1/* 2 * Copyright (C) 2006, 2007, 2008, 2010, 2013 Apple Inc. All rights reserved. 3 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "DOMWindow.h" 29 30#include "BackForwardController.h" 31#include "BarProp.h" 32#include "BeforeUnloadEvent.h" 33#include "CSSComputedStyleDeclaration.h" 34#include "CSSRule.h" 35#include "CSSRuleList.h" 36#include "Chrome.h" 37#include "ChromeClient.h" 38#include "Crypto.h" 39#include "DOMApplicationCache.h" 40#include "DOMSelection.h" 41#include "DOMSettableTokenList.h" 42#include "DOMStringList.h" 43#include "DOMTimer.h" 44#include "DOMTokenList.h" 45#include "DOMURL.h" 46#include "DOMWindowCSS.h" 47#include "DOMWindowExtension.h" 48#include "DOMWindowNotifications.h" 49#include "DeviceMotionController.h" 50#include "DeviceOrientationController.h" 51#include "Document.h" 52#include "DocumentLoader.h" 53#include "Editor.h" 54#include "Element.h" 55#include "EventException.h" 56#include "EventHandler.h" 57#include "EventListener.h" 58#include "EventNames.h" 59#include "ExceptionCode.h" 60#include "ExceptionCodePlaceholder.h" 61#include "FloatRect.h" 62#include "FocusController.h" 63#include "FrameLoadRequest.h" 64#include "FrameLoader.h" 65#include "FrameLoaderClient.h" 66#include "FrameTree.h" 67#include "FrameView.h" 68#include "HTMLFrameOwnerElement.h" 69#include "History.h" 70#include "InspectorInstrumentation.h" 71#include "JSMainThreadExecState.h" 72#include "Location.h" 73#include "MainFrame.h" 74#include "MediaQueryList.h" 75#include "MediaQueryMatcher.h" 76#include "MessageEvent.h" 77#include "Navigator.h" 78#include "Page.h" 79#include "PageConsole.h" 80#include "PageGroup.h" 81#include "PageTransitionEvent.h" 82#include "Performance.h" 83#include "PlatformScreen.h" 84#include "RuntimeEnabledFeatures.h" 85#include "ScheduledAction.h" 86#include "Screen.h" 87#include "ScriptController.h" 88#include "SecurityOrigin.h" 89#include "SecurityPolicy.h" 90#include "SerializedScriptValue.h" 91#include "Settings.h" 92#include "Storage.h" 93#include "StorageArea.h" 94#include "StorageNamespace.h" 95#include "StyleMedia.h" 96#include "StyleResolver.h" 97#include "SuddenTermination.h" 98#include "URL.h" 99#include "WebKitPoint.h" 100#include "WindowFeatures.h" 101#include "WindowFocusAllowedIndicator.h" 102#include <algorithm> 103#include <inspector/ScriptCallStack.h> 104#include <inspector/ScriptCallStackFactory.h> 105#include <memory> 106#include <wtf/CurrentTime.h> 107#include <wtf/MainThread.h> 108#include <wtf/MathExtras.h> 109#include <wtf/Ref.h> 110#include <wtf/text/Base64.h> 111#include <wtf/text/WTFString.h> 112 113#if ENABLE(USER_MESSAGE_HANDLERS) 114#include "UserContentController.h" 115#include "UserMessageHandlerDescriptor.h" 116#include "WebKitNamespace.h" 117#endif 118 119#if ENABLE(PROXIMITY_EVENTS) 120#include "DeviceProximityController.h" 121#endif 122 123#if ENABLE(REQUEST_ANIMATION_FRAME) 124#include "RequestAnimationFrameCallback.h" 125#endif 126 127#if ENABLE(GAMEPAD) 128#include "GamepadManager.h" 129#endif 130 131#if PLATFORM(IOS) 132#if ENABLE(GEOLOCATION) 133#include "NavigatorGeolocation.h" 134#endif 135#include "WKContentObservation.h" 136#endif 137 138using namespace Inspector; 139 140namespace WebCore { 141 142class PostMessageTimer : public TimerBase { 143public: 144 PostMessageTimer(DOMWindow* window, PassRefPtr<SerializedScriptValue> message, const String& sourceOrigin, PassRefPtr<DOMWindow> source, std::unique_ptr<MessagePortChannelArray> channels, SecurityOrigin* targetOrigin, PassRefPtr<ScriptCallStack> stackTrace) 145 : m_window(window) 146 , m_message(message) 147 , m_origin(sourceOrigin) 148 , m_source(source) 149 , m_channels(WTF::move(channels)) 150 , m_targetOrigin(targetOrigin) 151 , m_stackTrace(stackTrace) 152 { 153 } 154 155 PassRefPtr<MessageEvent> event(ScriptExecutionContext* context) 156 { 157 std::unique_ptr<MessagePortArray> messagePorts = MessagePort::entanglePorts(*context, WTF::move(m_channels)); 158 return MessageEvent::create(WTF::move(messagePorts), m_message, m_origin, String(), m_source); 159 } 160 SecurityOrigin* targetOrigin() const { return m_targetOrigin.get(); } 161 ScriptCallStack* stackTrace() const { return m_stackTrace.get(); } 162 163private: 164 virtual void fired() 165 { 166 // This object gets deleted when std::unique_ptr falls out of scope.. 167 std::unique_ptr<PostMessageTimer> timer(this); 168 m_window->postMessageTimerFired(*timer); 169 } 170 171 RefPtr<DOMWindow> m_window; 172 RefPtr<SerializedScriptValue> m_message; 173 String m_origin; 174 RefPtr<DOMWindow> m_source; 175 std::unique_ptr<MessagePortChannelArray> m_channels; 176 RefPtr<SecurityOrigin> m_targetOrigin; 177 RefPtr<ScriptCallStack> m_stackTrace; 178}; 179 180typedef HashCountedSet<DOMWindow*> DOMWindowSet; 181 182static DOMWindowSet& windowsWithUnloadEventListeners() 183{ 184 DEPRECATED_DEFINE_STATIC_LOCAL(DOMWindowSet, windowsWithUnloadEventListeners, ()); 185 return windowsWithUnloadEventListeners; 186} 187 188static DOMWindowSet& windowsWithBeforeUnloadEventListeners() 189{ 190 DEPRECATED_DEFINE_STATIC_LOCAL(DOMWindowSet, windowsWithBeforeUnloadEventListeners, ()); 191 return windowsWithBeforeUnloadEventListeners; 192} 193 194static void addUnloadEventListener(DOMWindow* domWindow) 195{ 196 if (windowsWithUnloadEventListeners().add(domWindow).isNewEntry) 197 domWindow->disableSuddenTermination(); 198} 199 200static void removeUnloadEventListener(DOMWindow* domWindow) 201{ 202 if (windowsWithUnloadEventListeners().remove(domWindow)) 203 domWindow->enableSuddenTermination(); 204} 205 206static void removeAllUnloadEventListeners(DOMWindow* domWindow) 207{ 208 if (windowsWithUnloadEventListeners().removeAll(domWindow)) 209 domWindow->enableSuddenTermination(); 210} 211 212static void addBeforeUnloadEventListener(DOMWindow* domWindow) 213{ 214 if (windowsWithBeforeUnloadEventListeners().add(domWindow).isNewEntry) 215 domWindow->disableSuddenTermination(); 216} 217 218static void removeBeforeUnloadEventListener(DOMWindow* domWindow) 219{ 220 if (windowsWithBeforeUnloadEventListeners().remove(domWindow)) 221 domWindow->enableSuddenTermination(); 222} 223 224static void removeAllBeforeUnloadEventListeners(DOMWindow* domWindow) 225{ 226 if (windowsWithBeforeUnloadEventListeners().removeAll(domWindow)) 227 domWindow->enableSuddenTermination(); 228} 229 230static bool allowsBeforeUnloadListeners(DOMWindow* window) 231{ 232 ASSERT_ARG(window, window); 233 Frame* frame = window->frame(); 234 if (!frame) 235 return false; 236 if (!frame->page()) 237 return false; 238 return frame->isMainFrame(); 239} 240 241bool DOMWindow::dispatchAllPendingBeforeUnloadEvents() 242{ 243 DOMWindowSet& set = windowsWithBeforeUnloadEventListeners(); 244 if (set.isEmpty()) 245 return true; 246 247 static bool alreadyDispatched = false; 248 ASSERT(!alreadyDispatched); 249 if (alreadyDispatched) 250 return true; 251 252 Vector<Ref<DOMWindow>> windows; 253 windows.reserveInitialCapacity(set.size()); 254 for (auto it = set.begin(), end = set.end(); it != end; ++it) 255 windows.uncheckedAppend(*it->key); 256 257 for (unsigned i = 0; i < windows.size(); ++i) { 258 DOMWindow& window = windows[i].get(); 259 if (!set.contains(&window)) 260 continue; 261 262 Frame* frame = window.frame(); 263 if (!frame) 264 continue; 265 266 if (!frame->loader().shouldClose()) 267 return false; 268 269 window.enableSuddenTermination(); 270 } 271 272 alreadyDispatched = true; 273 return true; 274} 275 276unsigned DOMWindow::pendingUnloadEventListeners() const 277{ 278 return windowsWithUnloadEventListeners().count(const_cast<DOMWindow*>(this)); 279} 280 281void DOMWindow::dispatchAllPendingUnloadEvents() 282{ 283 DOMWindowSet& set = windowsWithUnloadEventListeners(); 284 if (set.isEmpty()) 285 return; 286 287 static bool alreadyDispatched = false; 288 ASSERT(!alreadyDispatched); 289 if (alreadyDispatched) 290 return; 291 292 Vector<Ref<DOMWindow>> windows; 293 windows.reserveInitialCapacity(set.size()); 294 for (auto it = set.begin(), end = set.end(); it != end; ++it) 295 windows.uncheckedAppend(*it->key); 296 297 for (unsigned i = 0; i < windows.size(); ++i) { 298 DOMWindow& window = windows[i].get(); 299 if (!set.contains(&window)) 300 continue; 301 302 window.dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, false), window.document()); 303 window.dispatchEvent(Event::create(eventNames().unloadEvent, false, false), window.document()); 304 305 window.enableSuddenTermination(); 306 } 307 308 alreadyDispatched = true; 309} 310 311// This function: 312// 1) Validates the pending changes are not changing any value to NaN; in that case keep original value. 313// 2) Constrains the window rect to the minimum window size and no bigger than the float rect's dimensions. 314// 3) Constrains the window rect to within the top and left boundaries of the available screen rect. 315// 4) Constrains the window rect to within the bottom and right boundaries of the available screen rect. 316// 5) Translate the window rect coordinates to be within the coordinate space of the screen. 317FloatRect DOMWindow::adjustWindowRect(Page* page, const FloatRect& pendingChanges) 318{ 319 ASSERT(page); 320 321 FloatRect screen = screenAvailableRect(page->mainFrame().view()); 322 FloatRect window = page->chrome().windowRect(); 323 324 // Make sure we're in a valid state before adjusting dimensions. 325 ASSERT(std::isfinite(screen.x())); 326 ASSERT(std::isfinite(screen.y())); 327 ASSERT(std::isfinite(screen.width())); 328 ASSERT(std::isfinite(screen.height())); 329 ASSERT(std::isfinite(window.x())); 330 ASSERT(std::isfinite(window.y())); 331 ASSERT(std::isfinite(window.width())); 332 ASSERT(std::isfinite(window.height())); 333 334 // Update window values if new requested values are not NaN. 335 if (!std::isnan(pendingChanges.x())) 336 window.setX(pendingChanges.x()); 337 if (!std::isnan(pendingChanges.y())) 338 window.setY(pendingChanges.y()); 339 if (!std::isnan(pendingChanges.width())) 340 window.setWidth(pendingChanges.width()); 341 if (!std::isnan(pendingChanges.height())) 342 window.setHeight(pendingChanges.height()); 343 344 FloatSize minimumSize = page->chrome().client().minimumWindowSize(); 345 window.setWidth(std::min(std::max(minimumSize.width(), window.width()), screen.width())); 346 window.setHeight(std::min(std::max(minimumSize.height(), window.height()), screen.height())); 347 348 // Constrain the window position within the valid screen area. 349 window.setX(std::max(screen.x(), std::min(window.x(), screen.maxX() - window.width()))); 350 window.setY(std::max(screen.y(), std::min(window.y(), screen.maxY() - window.height()))); 351 352 return window; 353} 354 355bool DOMWindow::allowPopUp(Frame* firstFrame) 356{ 357 ASSERT(firstFrame); 358 359 if (ScriptController::processingUserGesture()) 360 return true; 361 362 return firstFrame->settings().javaScriptCanOpenWindowsAutomatically(); 363} 364 365bool DOMWindow::allowPopUp() 366{ 367 return m_frame && allowPopUp(m_frame); 368} 369 370bool DOMWindow::canShowModalDialog(const Frame* frame) 371{ 372 if (!frame) 373 return false; 374 Page* page = frame->page(); 375 if (!page) 376 return false; 377 return page->chrome().canRunModal(); 378} 379 380bool DOMWindow::canShowModalDialogNow(const Frame* frame) 381{ 382 if (!frame) 383 return false; 384 Page* page = frame->page(); 385 if (!page) 386 return false; 387 return page->chrome().canRunModalNow(); 388} 389 390DOMWindow::DOMWindow(Document* document) 391 : ContextDestructionObserver(document) 392 , FrameDestructionObserver(document->frame()) 393 , m_shouldPrintWhenFinishedLoading(false) 394 , m_suspendedForPageCache(false) 395 , m_lastPageStatus(PageStatusNone) 396#if PLATFORM(IOS) 397 , m_scrollEventListenerCount(0) 398#endif 399#if ENABLE(IOS_TOUCH_EVENTS) || ENABLE(IOS_GESTURE_EVENTS) 400 , m_touchEventListenerCount(0) 401#endif 402#if ENABLE(GAMEPAD) 403 , m_gamepadEventListenerCount(0) 404#endif 405{ 406 ASSERT(frame()); 407 ASSERT(DOMWindow::document()); 408} 409 410void DOMWindow::didSecureTransitionTo(Document* document) 411{ 412 observeContext(document); 413} 414 415DOMWindow::~DOMWindow() 416{ 417#ifndef NDEBUG 418 if (!m_suspendedForPageCache) { 419 ASSERT(!m_screen); 420 ASSERT(!m_history); 421 ASSERT(!m_crypto); 422 ASSERT(!m_locationbar); 423 ASSERT(!m_menubar); 424 ASSERT(!m_personalbar); 425 ASSERT(!m_scrollbars); 426 ASSERT(!m_statusbar); 427 ASSERT(!m_toolbar); 428 ASSERT(!m_navigator); 429#if ENABLE(WEB_TIMING) 430 ASSERT(!m_performance); 431#endif 432 ASSERT(!m_location); 433 ASSERT(!m_media); 434 ASSERT(!m_sessionStorage); 435 ASSERT(!m_localStorage); 436 ASSERT(!m_applicationCache); 437 } 438#endif 439 440 if (m_suspendedForPageCache) 441 willDestroyCachedFrame(); 442 else 443 willDestroyDocumentInFrame(); 444 445 // As the ASSERTs above indicate, this reset should only be necessary if this DOMWindow is suspended for the page cache. 446 // But we don't want to risk any of these objects hanging around after we've been destroyed. 447 resetDOMWindowProperties(); 448 449 removeAllUnloadEventListeners(this); 450 removeAllBeforeUnloadEventListeners(this); 451 452#if ENABLE(GAMEPAD) 453 if (m_gamepadEventListenerCount) 454 GamepadManager::shared().unregisterDOMWindow(this); 455#endif 456} 457 458DOMWindow* DOMWindow::toDOMWindow() 459{ 460 return this; 461} 462 463PassRefPtr<MediaQueryList> DOMWindow::matchMedia(const String& media) 464{ 465 return document() ? document()->mediaQueryMatcher().matchMedia(media) : 0; 466} 467 468Page* DOMWindow::page() 469{ 470 return frame() ? frame()->page() : 0; 471} 472 473void DOMWindow::frameDestroyed() 474{ 475 willDestroyDocumentInFrame(); 476 FrameDestructionObserver::frameDestroyed(); 477 resetDOMWindowProperties(); 478 JSDOMWindowBase::fireFrameClearedWatchpointsForWindow(this); 479} 480 481void DOMWindow::willDetachPage() 482{ 483 InspectorInstrumentation::frameWindowDiscarded(m_frame, this); 484} 485 486void DOMWindow::willDestroyCachedFrame() 487{ 488 // It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may 489 // unregister themselves from the DOMWindow as a result of the call to willDestroyGlobalObjectInCachedFrame. 490 Vector<DOMWindowProperty*> properties; 491 copyToVector(m_properties, properties); 492 for (size_t i = 0; i < properties.size(); ++i) 493 properties[i]->willDestroyGlobalObjectInCachedFrame(); 494} 495 496void DOMWindow::willDestroyDocumentInFrame() 497{ 498 // It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may 499 // unregister themselves from the DOMWindow as a result of the call to willDestroyGlobalObjectInFrame. 500 Vector<DOMWindowProperty*> properties; 501 copyToVector(m_properties, properties); 502 for (size_t i = 0; i < properties.size(); ++i) 503 properties[i]->willDestroyGlobalObjectInFrame(); 504} 505 506void DOMWindow::willDetachDocumentFromFrame() 507{ 508 // It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may 509 // unregister themselves from the DOMWindow as a result of the call to willDetachGlobalObjectFromFrame. 510 Vector<DOMWindowProperty*> properties; 511 copyToVector(m_properties, properties); 512 for (size_t i = 0; i < properties.size(); ++i) 513 properties[i]->willDetachGlobalObjectFromFrame(); 514} 515 516#if ENABLE(GAMEPAD) 517void DOMWindow::incrementGamepadEventListenerCount() 518{ 519 if (++m_gamepadEventListenerCount == 1) 520 GamepadManager::shared().registerDOMWindow(this); 521} 522 523void DOMWindow::decrementGamepadEventListenerCount() 524{ 525 ASSERT(m_gamepadEventListenerCount); 526 527 if (!--m_gamepadEventListenerCount) 528 GamepadManager::shared().unregisterDOMWindow(this); 529} 530#endif 531 532void DOMWindow::registerProperty(DOMWindowProperty* property) 533{ 534 m_properties.add(property); 535} 536 537void DOMWindow::unregisterProperty(DOMWindowProperty* property) 538{ 539 m_properties.remove(property); 540} 541 542void DOMWindow::resetUnlessSuspendedForPageCache() 543{ 544 if (m_suspendedForPageCache) 545 return; 546 willDestroyDocumentInFrame(); 547 resetDOMWindowProperties(); 548} 549 550void DOMWindow::suspendForPageCache() 551{ 552 disconnectDOMWindowProperties(); 553 m_suspendedForPageCache = true; 554} 555 556void DOMWindow::resumeFromPageCache() 557{ 558 reconnectDOMWindowProperties(); 559 m_suspendedForPageCache = false; 560} 561 562void DOMWindow::disconnectDOMWindowProperties() 563{ 564 // It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may 565 // unregister themselves from the DOMWindow as a result of the call to disconnectFrameForPageCache. 566 Vector<DOMWindowProperty*> properties; 567 copyToVector(m_properties, properties); 568 for (size_t i = 0; i < properties.size(); ++i) 569 properties[i]->disconnectFrameForPageCache(); 570} 571 572void DOMWindow::reconnectDOMWindowProperties() 573{ 574 ASSERT(m_suspendedForPageCache); 575 // It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may 576 // unregister themselves from the DOMWindow as a result of the call to reconnectFromPageCache. 577 Vector<DOMWindowProperty*> properties; 578 copyToVector(m_properties, properties); 579 for (size_t i = 0; i < properties.size(); ++i) 580 properties[i]->reconnectFrameFromPageCache(m_frame); 581} 582 583void DOMWindow::resetDOMWindowProperties() 584{ 585 m_properties.clear(); 586 587 m_screen = 0; 588 m_history = 0; 589 m_crypto = 0; 590 m_locationbar = 0; 591 m_menubar = 0; 592 m_personalbar = 0; 593 m_scrollbars = 0; 594 m_statusbar = 0; 595 m_toolbar = 0; 596 m_navigator = 0; 597#if ENABLE(WEB_TIMING) 598 m_performance = 0; 599#endif 600 m_location = 0; 601 m_media = 0; 602 m_sessionStorage = 0; 603 m_localStorage = 0; 604 m_applicationCache = 0; 605} 606 607bool DOMWindow::isCurrentlyDisplayedInFrame() const 608{ 609 return m_frame && m_frame->document()->domWindow() == this; 610} 611 612#if ENABLE(ORIENTATION_EVENTS) 613int DOMWindow::orientation() const 614{ 615 if (!m_frame) 616 return 0; 617 618 return m_frame->orientation(); 619} 620#endif 621 622Screen* DOMWindow::screen() const 623{ 624 if (!isCurrentlyDisplayedInFrame()) 625 return 0; 626 if (!m_screen) 627 m_screen = Screen::create(m_frame); 628 return m_screen.get(); 629} 630 631History* DOMWindow::history() const 632{ 633 if (!isCurrentlyDisplayedInFrame()) 634 return 0; 635 if (!m_history) 636 m_history = History::create(m_frame); 637 return m_history.get(); 638} 639 640Crypto* DOMWindow::crypto() const 641{ 642 // FIXME: Why is crypto not available when the window is not currently displayed in a frame? 643 if (!isCurrentlyDisplayedInFrame()) 644 return 0; 645 if (!m_crypto) 646 m_crypto = Crypto::create(*document()); 647 return m_crypto.get(); 648} 649 650BarProp* DOMWindow::locationbar() const 651{ 652 if (!isCurrentlyDisplayedInFrame()) 653 return 0; 654 if (!m_locationbar) 655 m_locationbar = BarProp::create(m_frame, BarProp::Locationbar); 656 return m_locationbar.get(); 657} 658 659BarProp* DOMWindow::menubar() const 660{ 661 if (!isCurrentlyDisplayedInFrame()) 662 return 0; 663 if (!m_menubar) 664 m_menubar = BarProp::create(m_frame, BarProp::Menubar); 665 return m_menubar.get(); 666} 667 668BarProp* DOMWindow::personalbar() const 669{ 670 if (!isCurrentlyDisplayedInFrame()) 671 return 0; 672 if (!m_personalbar) 673 m_personalbar = BarProp::create(m_frame, BarProp::Personalbar); 674 return m_personalbar.get(); 675} 676 677BarProp* DOMWindow::scrollbars() const 678{ 679 if (!isCurrentlyDisplayedInFrame()) 680 return 0; 681 if (!m_scrollbars) 682 m_scrollbars = BarProp::create(m_frame, BarProp::Scrollbars); 683 return m_scrollbars.get(); 684} 685 686BarProp* DOMWindow::statusbar() const 687{ 688 if (!isCurrentlyDisplayedInFrame()) 689 return 0; 690 if (!m_statusbar) 691 m_statusbar = BarProp::create(m_frame, BarProp::Statusbar); 692 return m_statusbar.get(); 693} 694 695BarProp* DOMWindow::toolbar() const 696{ 697 if (!isCurrentlyDisplayedInFrame()) 698 return 0; 699 if (!m_toolbar) 700 m_toolbar = BarProp::create(m_frame, BarProp::Toolbar); 701 return m_toolbar.get(); 702} 703 704PageConsole* DOMWindow::pageConsole() const 705{ 706 if (!isCurrentlyDisplayedInFrame()) 707 return 0; 708 return m_frame->page() ? &m_frame->page()->console() : 0; 709} 710 711DOMApplicationCache* DOMWindow::applicationCache() const 712{ 713 if (!isCurrentlyDisplayedInFrame()) 714 return 0; 715 if (!m_applicationCache) 716 m_applicationCache = DOMApplicationCache::create(m_frame); 717 return m_applicationCache.get(); 718} 719 720Navigator* DOMWindow::navigator() const 721{ 722 if (!isCurrentlyDisplayedInFrame()) 723 return 0; 724 if (!m_navigator) 725 m_navigator = Navigator::create(m_frame); 726 return m_navigator.get(); 727} 728 729#if ENABLE(WEB_TIMING) 730Performance* DOMWindow::performance() const 731{ 732 if (!isCurrentlyDisplayedInFrame()) 733 return 0; 734 if (!m_performance) 735 m_performance = Performance::create(m_frame); 736 return m_performance.get(); 737} 738#endif 739 740Location* DOMWindow::location() const 741{ 742 if (!isCurrentlyDisplayedInFrame()) 743 return 0; 744 if (!m_location) 745 m_location = Location::create(m_frame); 746 return m_location.get(); 747} 748 749#if ENABLE(USER_MESSAGE_HANDLERS) 750bool DOMWindow::shouldHaveWebKitNamespaceForWorld(DOMWrapperWorld& world) 751{ 752 if (!m_frame) 753 return false; 754 755 auto* page = m_frame->page(); 756 if (!page) 757 return false; 758 759 auto* userContentController = page->userContentController(); 760 if (!userContentController) 761 return false; 762 763 auto* descriptorMap = userContentController->userMessageHandlerDescriptors(); 764 if (!descriptorMap) 765 return false; 766 767 for (auto& descriptor : descriptorMap->values()) { 768 if (&descriptor->world() == &world) 769 return true; 770 } 771 772 return false; 773} 774 775WebKitNamespace* DOMWindow::webkitNamespace() const 776{ 777 if (!isCurrentlyDisplayedInFrame()) 778 return nullptr; 779 if (!m_webkitNamespace) 780 m_webkitNamespace = WebKitNamespace::create(*m_frame); 781 return m_webkitNamespace.get(); 782} 783#endif 784 785Storage* DOMWindow::sessionStorage(ExceptionCode& ec) const 786{ 787 if (!isCurrentlyDisplayedInFrame()) 788 return 0; 789 790 Document* document = this->document(); 791 if (!document) 792 return 0; 793 794 if (!document->securityOrigin()->canAccessSessionStorage(document->topOrigin())) { 795 ec = SECURITY_ERR; 796 return 0; 797 } 798 799 if (m_sessionStorage) { 800 if (!m_sessionStorage->area().canAccessStorage(m_frame)) { 801 ec = SECURITY_ERR; 802 return 0; 803 } 804 return m_sessionStorage.get(); 805 } 806 807 Page* page = document->page(); 808 if (!page) 809 return 0; 810 811 RefPtr<StorageArea> storageArea = page->sessionStorage()->storageArea(document->securityOrigin()); 812 if (!storageArea->canAccessStorage(m_frame)) { 813 ec = SECURITY_ERR; 814 return 0; 815 } 816 817 m_sessionStorage = Storage::create(m_frame, storageArea.release()); 818 return m_sessionStorage.get(); 819} 820 821Storage* DOMWindow::localStorage(ExceptionCode& ec) const 822{ 823 if (!isCurrentlyDisplayedInFrame()) 824 return nullptr; 825 826 Document* document = this->document(); 827 if (!document) 828 return nullptr; 829 830 if (!document->securityOrigin()->canAccessLocalStorage(0)) { 831 ec = SECURITY_ERR; 832 return nullptr; 833 } 834 835 Page* page = document->page(); 836 // FIXME: We should consider supporting access/modification to local storage 837 // after calling window.close(). See <https://bugs.webkit.org/show_bug.cgi?id=135330>. 838 if (!page || !page->isClosing()) { 839 if (m_localStorage) { 840 if (!m_localStorage->area().canAccessStorage(m_frame)) { 841 ec = SECURITY_ERR; 842 return nullptr; 843 } 844 return m_localStorage.get(); 845 } 846 } 847 848 if (!page) 849 return nullptr; 850 851 if (page->isClosing()) 852 return nullptr; 853 854 if (!page->settings().localStorageEnabled()) 855 return nullptr; 856 857 RefPtr<StorageArea> storageArea; 858 if (!document->securityOrigin()->canAccessLocalStorage(document->topOrigin())) 859 storageArea = page->group().transientLocalStorage(document->topOrigin())->storageArea(document->securityOrigin()); 860 else 861 storageArea = page->group().localStorage()->storageArea(document->securityOrigin()); 862 863 if (!storageArea->canAccessStorage(m_frame)) { 864 ec = SECURITY_ERR; 865 return nullptr; 866 } 867 868 m_localStorage = Storage::create(m_frame, storageArea.release()); 869 return m_localStorage.get(); 870} 871 872void DOMWindow::postMessage(PassRefPtr<SerializedScriptValue> message, MessagePort* port, const String& targetOrigin, DOMWindow& source, ExceptionCode& ec) 873{ 874 MessagePortArray ports; 875 if (port) 876 ports.append(port); 877 postMessage(message, &ports, targetOrigin, source, ec); 878} 879 880void DOMWindow::postMessage(PassRefPtr<SerializedScriptValue> message, const MessagePortArray* ports, const String& targetOrigin, DOMWindow& source, ExceptionCode& ec) 881{ 882 if (!isCurrentlyDisplayedInFrame()) 883 return; 884 885 Document* sourceDocument = source.document(); 886 887 // Compute the target origin. We need to do this synchronously in order 888 // to generate the SYNTAX_ERR exception correctly. 889 RefPtr<SecurityOrigin> target; 890 if (targetOrigin == "/") { 891 if (!sourceDocument) 892 return; 893 target = sourceDocument->securityOrigin(); 894 } else if (targetOrigin != "*") { 895 target = SecurityOrigin::createFromString(targetOrigin); 896 // It doesn't make sense target a postMessage at a unique origin 897 // because there's no way to represent a unique origin in a string. 898 if (target->isUnique()) { 899 ec = SYNTAX_ERR; 900 return; 901 } 902 } 903 904 std::unique_ptr<MessagePortChannelArray> channels = MessagePort::disentanglePorts(ports, ec); 905 if (ec) 906 return; 907 908 // Capture the source of the message. We need to do this synchronously 909 // in order to capture the source of the message correctly. 910 if (!sourceDocument) 911 return; 912 String sourceOrigin = sourceDocument->securityOrigin()->toString(); 913 914 // Capture stack trace only when inspector front-end is loaded as it may be time consuming. 915 RefPtr<ScriptCallStack> stackTrace; 916 if (InspectorInstrumentation::consoleAgentEnabled(sourceDocument)) 917 stackTrace = createScriptCallStack(JSMainThreadExecState::currentState(), ScriptCallStack::maxCallStackSizeToCapture); 918 919 // Schedule the message. 920 PostMessageTimer* timer = new PostMessageTimer(this, message, sourceOrigin, &source, WTF::move(channels), target.get(), stackTrace.release()); 921 timer->startOneShot(0); 922} 923 924void DOMWindow::postMessageTimerFired(PostMessageTimer& timer) 925{ 926 if (!document() || !isCurrentlyDisplayedInFrame()) 927 return; 928 929 dispatchMessageEventWithOriginCheck(timer.targetOrigin(), timer.event(document()), timer.stackTrace()); 930} 931 932void DOMWindow::dispatchMessageEventWithOriginCheck(SecurityOrigin* intendedTargetOrigin, PassRefPtr<Event> event, PassRefPtr<ScriptCallStack> stackTrace) 933{ 934 if (intendedTargetOrigin) { 935 // Check target origin now since the target document may have changed since the timer was scheduled. 936 if (!intendedTargetOrigin->isSameSchemeHostPort(document()->securityOrigin())) { 937 String message = "Unable to post message to " + intendedTargetOrigin->toString() + 938 ". Recipient has origin " + document()->securityOrigin()->toString() + ".\n"; 939 pageConsole()->addMessage(MessageSource::Security, MessageLevel::Error, message, stackTrace); 940 return; 941 } 942 } 943 944 dispatchEvent(event); 945} 946 947DOMSelection* DOMWindow::getSelection() 948{ 949 if (!isCurrentlyDisplayedInFrame() || !m_frame) 950 return 0; 951 952 return m_frame->document()->getSelection(); 953} 954 955Element* DOMWindow::frameElement() const 956{ 957 if (!m_frame) 958 return 0; 959 960 return m_frame->ownerElement(); 961} 962 963void DOMWindow::focus(ScriptExecutionContext* context) 964{ 965 if (!m_frame) 966 return; 967 968 Page* page = m_frame->page(); 969 if (!page) 970 return; 971 972 bool allowFocus = WindowFocusAllowedIndicator::windowFocusAllowed() || !m_frame->settings().windowFocusRestricted(); 973 if (context) { 974 ASSERT(isMainThread()); 975 Document* activeDocument = toDocument(context); 976 if (opener() && opener() != this && activeDocument->domWindow() == opener()) 977 allowFocus = true; 978 } 979 980 // If we're a top level window, bring the window to the front. 981 if (m_frame->isMainFrame() && allowFocus) 982 page->chrome().focus(); 983 984 if (!m_frame) 985 return; 986 987 // Clear the current frame's focused node if a new frame is about to be focused. 988 Frame* focusedFrame = page->focusController().focusedFrame(); 989 if (focusedFrame && focusedFrame != m_frame) 990 focusedFrame->document()->setFocusedElement(0); 991 992 m_frame->eventHandler().focusDocumentView(); 993} 994 995void DOMWindow::blur() 996{ 997 if (!m_frame) 998 return; 999 1000 Page* page = m_frame->page(); 1001 if (!page) 1002 return; 1003 1004 if (m_frame->settings().windowFocusRestricted()) 1005 return; 1006 1007 if (!m_frame->isMainFrame()) 1008 return; 1009 1010 page->chrome().unfocus(); 1011} 1012 1013void DOMWindow::close(ScriptExecutionContext* context) 1014{ 1015 if (!m_frame) 1016 return; 1017 1018 Page* page = m_frame->page(); 1019 if (!page) 1020 return; 1021 1022 if (!m_frame->isMainFrame()) 1023 return; 1024 1025 if (context) { 1026 ASSERT(isMainThread()); 1027 Document* activeDocument = toDocument(context); 1028 if (!activeDocument) 1029 return; 1030 1031 if (!activeDocument->canNavigate(m_frame)) 1032 return; 1033 } 1034 1035 bool allowScriptsToCloseWindows = m_frame->settings().allowScriptsToCloseWindows(); 1036 1037 if (!(page->openedByDOM() || page->backForward().count() <= 1 || allowScriptsToCloseWindows)) { 1038 pageConsole()->addMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("Can't close the window since it was not opened by JavaScript")); 1039 return; 1040 } 1041 1042 if (!m_frame->loader().shouldClose()) 1043 return; 1044 1045 page->setIsClosing(); 1046 page->chrome().closeWindowSoon(); 1047} 1048 1049void DOMWindow::print() 1050{ 1051 if (!m_frame) 1052 return; 1053 1054 Page* page = m_frame->page(); 1055 if (!page) 1056 return; 1057 1058 // Pages are not allowed to bring up a modal print dialog during BeforeUnload dispatch. 1059 if (page->isAnyFrameHandlingBeforeUnloadEvent()) { 1060 printErrorMessage("Use of window.print is not allowed during beforeunload event dispatch."); 1061 return; 1062 } 1063 1064 if (m_frame->loader().activeDocumentLoader()->isLoading()) { 1065 m_shouldPrintWhenFinishedLoading = true; 1066 return; 1067 } 1068 m_shouldPrintWhenFinishedLoading = false; 1069 page->chrome().print(m_frame); 1070} 1071 1072void DOMWindow::stop() 1073{ 1074 if (!m_frame) 1075 return; 1076 1077 // We must check whether the load is complete asynchronously, because we might still be parsing 1078 // the document until the callstack unwinds. 1079 m_frame->loader().stopForUserCancel(true); 1080} 1081 1082void DOMWindow::alert(const String& message) 1083{ 1084 if (!m_frame) 1085 return; 1086 1087 // Pages are not allowed to cause modal alerts during BeforeUnload dispatch. 1088 if (page() && page()->isAnyFrameHandlingBeforeUnloadEvent()) { 1089 printErrorMessage("Use of window.alert is not allowed during beforeunload event dispatch."); 1090 return; 1091 } 1092 1093 m_frame->document()->updateStyleIfNeeded(); 1094 1095 Page* page = m_frame->page(); 1096 if (!page) 1097 return; 1098 1099 page->chrome().runJavaScriptAlert(m_frame, message); 1100} 1101 1102bool DOMWindow::confirm(const String& message) 1103{ 1104 if (!m_frame) 1105 return false; 1106 1107 // Pages are not allowed to cause modal alerts during BeforeUnload dispatch. 1108 if (page() && page()->isAnyFrameHandlingBeforeUnloadEvent()) { 1109 printErrorMessage("Use of window.confirm is not allowed during beforeunload event dispatch."); 1110 return false; 1111 } 1112 1113 m_frame->document()->updateStyleIfNeeded(); 1114 1115 Page* page = m_frame->page(); 1116 if (!page) 1117 return false; 1118 1119 return page->chrome().runJavaScriptConfirm(m_frame, message); 1120} 1121 1122String DOMWindow::prompt(const String& message, const String& defaultValue) 1123{ 1124 if (!m_frame) 1125 return String(); 1126 1127 // Pages are not allowed to cause modal alerts during BeforeUnload dispatch. 1128 if (page() && page()->isAnyFrameHandlingBeforeUnloadEvent()) { 1129 printErrorMessage("Use of window.prompt is not allowed during beforeunload event dispatch."); 1130 return String(); 1131 } 1132 1133 m_frame->document()->updateStyleIfNeeded(); 1134 1135 Page* page = m_frame->page(); 1136 if (!page) 1137 return String(); 1138 1139 String returnValue; 1140 if (page->chrome().runJavaScriptPrompt(m_frame, message, defaultValue, returnValue)) 1141 return returnValue; 1142 1143 return String(); 1144} 1145 1146String DOMWindow::btoa(const String& stringToEncode, ExceptionCode& ec) 1147{ 1148 if (stringToEncode.isNull()) 1149 return String(); 1150 1151 if (!stringToEncode.containsOnlyLatin1()) { 1152 ec = INVALID_CHARACTER_ERR; 1153 return String(); 1154 } 1155 1156 return base64Encode(stringToEncode.latin1()); 1157} 1158 1159String DOMWindow::atob(const String& encodedString, ExceptionCode& ec) 1160{ 1161 if (encodedString.isNull()) 1162 return String(); 1163 1164 if (!encodedString.containsOnlyLatin1()) { 1165 ec = INVALID_CHARACTER_ERR; 1166 return String(); 1167 } 1168 1169 Vector<char> out; 1170 if (!base64Decode(encodedString, out, Base64FailOnInvalidCharacterOrExcessPadding)) { 1171 ec = INVALID_CHARACTER_ERR; 1172 return String(); 1173 } 1174 1175 return String(out.data(), out.size()); 1176} 1177 1178bool DOMWindow::find(const String& string, bool caseSensitive, bool backwards, bool wrap, bool /*wholeWord*/, bool /*searchInFrames*/, bool /*showDialog*/) const 1179{ 1180 if (!isCurrentlyDisplayedInFrame()) 1181 return false; 1182 1183 // FIXME (13016): Support wholeWord, searchInFrames and showDialog. 1184 FindOptions options = (backwards ? Backwards : 0) | (caseSensitive ? 0 : CaseInsensitive) | (wrap ? WrapAround : 0); 1185 return m_frame->editor().findString(string, options); 1186} 1187 1188bool DOMWindow::offscreenBuffering() const 1189{ 1190 return true; 1191} 1192 1193int DOMWindow::outerHeight() const 1194{ 1195#if PLATFORM(IOS) 1196 return 0; 1197#else 1198 if (!m_frame) 1199 return 0; 1200 1201 Page* page = m_frame->page(); 1202 if (!page) 1203 return 0; 1204 1205 return static_cast<int>(page->chrome().windowRect().height()); 1206#endif 1207} 1208 1209int DOMWindow::outerWidth() const 1210{ 1211#if PLATFORM(IOS) 1212 return 0; 1213#else 1214 if (!m_frame) 1215 return 0; 1216 1217 Page* page = m_frame->page(); 1218 if (!page) 1219 return 0; 1220 1221 return static_cast<int>(page->chrome().windowRect().width()); 1222#endif 1223} 1224 1225int DOMWindow::innerHeight() const 1226{ 1227 if (!m_frame) 1228 return 0; 1229 1230 FrameView* view = m_frame->view(); 1231 if (!view) 1232 return 0; 1233 1234 return view->mapFromLayoutToCSSUnits(static_cast<int>(view->unobscuredContentRectIncludingScrollbars().height())); 1235} 1236 1237int DOMWindow::innerWidth() const 1238{ 1239 if (!m_frame) 1240 return 0; 1241 1242 FrameView* view = m_frame->view(); 1243 if (!view) 1244 return 0; 1245 1246 return view->mapFromLayoutToCSSUnits(static_cast<int>(view->unobscuredContentRectIncludingScrollbars().width())); 1247} 1248 1249int DOMWindow::screenX() const 1250{ 1251 if (!m_frame) 1252 return 0; 1253 1254 Page* page = m_frame->page(); 1255 if (!page) 1256 return 0; 1257 1258 return static_cast<int>(page->chrome().windowRect().x()); 1259} 1260 1261int DOMWindow::screenY() const 1262{ 1263 if (!m_frame) 1264 return 0; 1265 1266 Page* page = m_frame->page(); 1267 if (!page) 1268 return 0; 1269 1270 return static_cast<int>(page->chrome().windowRect().y()); 1271} 1272 1273int DOMWindow::scrollX() const 1274{ 1275 if (!m_frame) 1276 return 0; 1277 1278 FrameView* view = m_frame->view(); 1279 if (!view) 1280 return 0; 1281 1282 int scrollX = view->contentsScrollPosition().x(); 1283 if (!scrollX) 1284 return 0; 1285 1286 m_frame->document()->updateLayoutIgnorePendingStylesheets(); 1287 1288 return view->mapFromLayoutToCSSUnits(view->contentsScrollPosition().x()); 1289} 1290 1291int DOMWindow::scrollY() const 1292{ 1293 if (!m_frame) 1294 return 0; 1295 1296 FrameView* view = m_frame->view(); 1297 if (!view) 1298 return 0; 1299 1300 int scrollY = view->contentsScrollPosition().y(); 1301 if (!scrollY) 1302 return 0; 1303 1304 m_frame->document()->updateLayoutIgnorePendingStylesheets(); 1305 1306 return view->mapFromLayoutToCSSUnits(view->contentsScrollPosition().y()); 1307} 1308 1309bool DOMWindow::closed() const 1310{ 1311 return !m_frame; 1312} 1313 1314unsigned DOMWindow::length() const 1315{ 1316 if (!isCurrentlyDisplayedInFrame()) 1317 return 0; 1318 1319 return m_frame->tree().scopedChildCount(); 1320} 1321 1322String DOMWindow::name() const 1323{ 1324 if (!m_frame) 1325 return String(); 1326 1327 return m_frame->tree().name(); 1328} 1329 1330void DOMWindow::setName(const String& string) 1331{ 1332 if (!m_frame) 1333 return; 1334 1335 m_frame->tree().setName(string); 1336} 1337 1338void DOMWindow::setStatus(const String& string) 1339{ 1340 m_status = string; 1341 1342 if (!m_frame) 1343 return; 1344 1345 Page* page = m_frame->page(); 1346 if (!page) 1347 return; 1348 1349 ASSERT(m_frame->document()); // Client calls shouldn't be made when the frame is in inconsistent state. 1350 page->chrome().setStatusbarText(m_frame, m_status); 1351} 1352 1353void DOMWindow::setDefaultStatus(const String& string) 1354{ 1355 m_defaultStatus = string; 1356 1357 if (!m_frame) 1358 return; 1359 1360 Page* page = m_frame->page(); 1361 if (!page) 1362 return; 1363 1364 ASSERT(m_frame->document()); // Client calls shouldn't be made when the frame is in inconsistent state. 1365 page->chrome().setStatusbarText(m_frame, m_defaultStatus); 1366} 1367 1368DOMWindow* DOMWindow::self() const 1369{ 1370 if (!m_frame) 1371 return 0; 1372 1373 return m_frame->document()->domWindow(); 1374} 1375 1376DOMWindow* DOMWindow::opener() const 1377{ 1378 if (!m_frame) 1379 return 0; 1380 1381 Frame* opener = m_frame->loader().opener(); 1382 if (!opener) 1383 return 0; 1384 1385 return opener->document()->domWindow(); 1386} 1387 1388DOMWindow* DOMWindow::parent() const 1389{ 1390 if (!m_frame) 1391 return 0; 1392 1393 Frame* parent = m_frame->tree().parent(); 1394 if (parent) 1395 return parent->document()->domWindow(); 1396 1397 return m_frame->document()->domWindow(); 1398} 1399 1400DOMWindow* DOMWindow::top() const 1401{ 1402 if (!m_frame) 1403 return 0; 1404 1405 Page* page = m_frame->page(); 1406 if (!page) 1407 return 0; 1408 1409 return m_frame->tree().top().document()->domWindow(); 1410} 1411 1412Document* DOMWindow::document() const 1413{ 1414 ScriptExecutionContext* context = ContextDestructionObserver::scriptExecutionContext(); 1415 return toDocument(context); 1416} 1417 1418PassRefPtr<StyleMedia> DOMWindow::styleMedia() const 1419{ 1420 if (!isCurrentlyDisplayedInFrame()) 1421 return 0; 1422 if (!m_media) 1423 m_media = StyleMedia::create(m_frame); 1424 return m_media.get(); 1425} 1426 1427PassRefPtr<CSSStyleDeclaration> DOMWindow::getComputedStyle(Element* elt, const String& pseudoElt) const 1428{ 1429 if (!elt) 1430 return 0; 1431 1432 return CSSComputedStyleDeclaration::create(elt, false, pseudoElt); 1433} 1434 1435PassRefPtr<CSSRuleList> DOMWindow::getMatchedCSSRules(Element* element, const String& pseudoElement, bool authorOnly) const 1436{ 1437 if (!isCurrentlyDisplayedInFrame()) 1438 return 0; 1439 1440 unsigned colonStart = pseudoElement[0] == ':' ? (pseudoElement[1] == ':' ? 2 : 1) : 0; 1441 CSSSelector::PseudoElementType pseudoType = CSSSelector::parsePseudoElementType(pseudoElement.substringSharingImpl(colonStart)); 1442 if (pseudoType == CSSSelector::PseudoElementUnknown && !pseudoElement.isEmpty()) 1443 return 0; 1444 1445 unsigned rulesToInclude = StyleResolver::AuthorCSSRules; 1446 if (!authorOnly) 1447 rulesToInclude |= StyleResolver::UAAndUserCSSRules; 1448 if (m_frame->settings().crossOriginCheckInGetMatchedCSSRulesDisabled()) 1449 rulesToInclude |= StyleResolver::CrossOriginCSSRules; 1450 1451 PseudoId pseudoId = CSSSelector::pseudoId(pseudoType); 1452 1453 auto matchedRules = m_frame->document()->ensureStyleResolver().pseudoStyleRulesForElement(element, pseudoId, rulesToInclude); 1454 if (matchedRules.isEmpty()) 1455 return 0; 1456 1457 RefPtr<StaticCSSRuleList> ruleList = StaticCSSRuleList::create(); 1458 for (unsigned i = 0; i < matchedRules.size(); ++i) 1459 ruleList->rules().append(matchedRules[i]->createCSSOMWrapper()); 1460 1461 return ruleList.release(); 1462} 1463 1464PassRefPtr<WebKitPoint> DOMWindow::webkitConvertPointFromNodeToPage(Node* node, const WebKitPoint* p) const 1465{ 1466 if (!node || !p) 1467 return 0; 1468 1469 if (!document()) 1470 return 0; 1471 1472 document()->updateLayoutIgnorePendingStylesheets(); 1473 1474 FloatPoint pagePoint(p->x(), p->y()); 1475 pagePoint = node->convertToPage(pagePoint); 1476 return WebKitPoint::create(pagePoint.x(), pagePoint.y()); 1477} 1478 1479PassRefPtr<WebKitPoint> DOMWindow::webkitConvertPointFromPageToNode(Node* node, const WebKitPoint* p) const 1480{ 1481 if (!node || !p) 1482 return 0; 1483 1484 if (!document()) 1485 return 0; 1486 1487 document()->updateLayoutIgnorePendingStylesheets(); 1488 1489 FloatPoint nodePoint(p->x(), p->y()); 1490 nodePoint = node->convertFromPage(nodePoint); 1491 return WebKitPoint::create(nodePoint.x(), nodePoint.y()); 1492} 1493 1494double DOMWindow::devicePixelRatio() const 1495{ 1496 if (!m_frame) 1497 return 0.0; 1498 1499 Page* page = m_frame->page(); 1500 if (!page) 1501 return 0.0; 1502 1503 return page->deviceScaleFactor(); 1504} 1505 1506void DOMWindow::scrollBy(int x, int y) const 1507{ 1508 if (!isCurrentlyDisplayedInFrame()) 1509 return; 1510 1511 document()->updateLayoutIgnorePendingStylesheets(); 1512 1513 FrameView* view = m_frame->view(); 1514 if (!view) 1515 return; 1516 1517 IntSize scaledOffset(view->mapFromCSSToLayoutUnits(x), view->mapFromCSSToLayoutUnits(y)); 1518 view->setContentsScrollPosition(view->contentsScrollPosition() + scaledOffset); 1519} 1520 1521void DOMWindow::scrollTo(int x, int y) const 1522{ 1523 if (!isCurrentlyDisplayedInFrame()) 1524 return; 1525 1526 RefPtr<FrameView> view = m_frame->view(); 1527 if (!view) 1528 return; 1529 1530 if (!x && !y && view->contentsScrollPosition() == IntPoint(0, 0)) 1531 return; 1532 1533 document()->updateLayoutIgnorePendingStylesheets(); 1534 1535 IntPoint layoutPos(view->mapFromCSSToLayoutUnits(x), view->mapFromCSSToLayoutUnits(y)); 1536 view->setContentsScrollPosition(layoutPos); 1537} 1538 1539bool DOMWindow::allowedToChangeWindowGeometry() const 1540{ 1541 if (!m_frame) 1542 return false; 1543 if (!m_frame->page()) 1544 return false; 1545 if (!m_frame->isMainFrame()) 1546 return false; 1547 // Prevent web content from tricking the user into initiating a drag. 1548 if (m_frame->eventHandler().mousePressed()) 1549 return false; 1550 return true; 1551} 1552 1553void DOMWindow::moveBy(float x, float y) const 1554{ 1555 if (!allowedToChangeWindowGeometry()) 1556 return; 1557 1558 Page* page = m_frame->page(); 1559 FloatRect fr = page->chrome().windowRect(); 1560 FloatRect update = fr; 1561 update.move(x, y); 1562 // Security check (the spec talks about UniversalBrowserWrite to disable this check...) 1563 page->chrome().setWindowRect(adjustWindowRect(page, update)); 1564} 1565 1566void DOMWindow::moveTo(float x, float y) const 1567{ 1568 if (!allowedToChangeWindowGeometry()) 1569 return; 1570 1571 Page* page = m_frame->page(); 1572 FloatRect fr = page->chrome().windowRect(); 1573 FloatRect sr = screenAvailableRect(page->mainFrame().view()); 1574 fr.setLocation(sr.location()); 1575 FloatRect update = fr; 1576 update.move(x, y); 1577 // Security check (the spec talks about UniversalBrowserWrite to disable this check...) 1578 page->chrome().setWindowRect(adjustWindowRect(page, update)); 1579} 1580 1581void DOMWindow::resizeBy(float x, float y) const 1582{ 1583 if (!allowedToChangeWindowGeometry()) 1584 return; 1585 1586 Page* page = m_frame->page(); 1587 FloatRect fr = page->chrome().windowRect(); 1588 FloatSize dest = fr.size() + FloatSize(x, y); 1589 FloatRect update(fr.location(), dest); 1590 page->chrome().setWindowRect(adjustWindowRect(page, update)); 1591} 1592 1593void DOMWindow::resizeTo(float width, float height) const 1594{ 1595 if (!allowedToChangeWindowGeometry()) 1596 return; 1597 1598 Page* page = m_frame->page(); 1599 FloatRect fr = page->chrome().windowRect(); 1600 FloatSize dest = FloatSize(width, height); 1601 FloatRect update(fr.location(), dest); 1602 page->chrome().setWindowRect(adjustWindowRect(page, update)); 1603} 1604 1605int DOMWindow::setTimeout(std::unique_ptr<ScheduledAction> action, int timeout, ExceptionCode& ec) 1606{ 1607 ScriptExecutionContext* context = scriptExecutionContext(); 1608 if (!context) { 1609 ec = INVALID_ACCESS_ERR; 1610 return -1; 1611 } 1612 return DOMTimer::install(context, WTF::move(action), timeout, true); 1613} 1614 1615void DOMWindow::clearTimeout(int timeoutId) 1616{ 1617#if PLATFORM(IOS) 1618 if (m_frame) { 1619 Document* document = m_frame->document(); 1620 if (timeoutId > 0 && document) { 1621 DOMTimer* timer = document->findTimeout(timeoutId); 1622 if (timer && WebThreadContainsObservedContentModifier(timer)) { 1623 WebThreadRemoveObservedContentModifier(timer); 1624 1625 if (!WebThreadCountOfObservedContentModifiers()) { 1626 if (Page* page = m_frame->page()) 1627 page->chrome().client().observedContentChange(m_frame); 1628 } 1629 } 1630 } 1631 } 1632#endif 1633 ScriptExecutionContext* context = scriptExecutionContext(); 1634 if (!context) 1635 return; 1636 DOMTimer::removeById(context, timeoutId); 1637} 1638 1639int DOMWindow::setInterval(std::unique_ptr<ScheduledAction> action, int timeout, ExceptionCode& ec) 1640{ 1641 ScriptExecutionContext* context = scriptExecutionContext(); 1642 if (!context) { 1643 ec = INVALID_ACCESS_ERR; 1644 return -1; 1645 } 1646 return DOMTimer::install(context, WTF::move(action), timeout, false); 1647} 1648 1649void DOMWindow::clearInterval(int timeoutId) 1650{ 1651 ScriptExecutionContext* context = scriptExecutionContext(); 1652 if (!context) 1653 return; 1654 DOMTimer::removeById(context, timeoutId); 1655} 1656 1657#if ENABLE(REQUEST_ANIMATION_FRAME) 1658int DOMWindow::requestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback) 1659{ 1660 callback->m_useLegacyTimeBase = false; 1661 if (Document* d = document()) 1662 return d->requestAnimationFrame(callback); 1663 return 0; 1664} 1665 1666int DOMWindow::webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback) 1667{ 1668 callback->m_useLegacyTimeBase = true; 1669 if (Document* d = document()) 1670 return d->requestAnimationFrame(callback); 1671 return 0; 1672} 1673 1674void DOMWindow::cancelAnimationFrame(int id) 1675{ 1676 if (Document* d = document()) 1677 d->cancelAnimationFrame(id); 1678} 1679#endif 1680 1681#if ENABLE(CSS3_CONDITIONAL_RULES) 1682DOMWindowCSS* DOMWindow::css() 1683{ 1684 if (!m_css) 1685 m_css = DOMWindowCSS::create(); 1686 return m_css.get(); 1687} 1688#endif 1689 1690static void didAddStorageEventListener(DOMWindow* window) 1691{ 1692 // Creating these WebCore::Storage objects informs the system that we'd like to receive 1693 // notifications about storage events that might be triggered in other processes. Rather 1694 // than subscribe to these notifications explicitly, we subscribe to them implicitly to 1695 // simplify the work done by the system. 1696 window->localStorage(IGNORE_EXCEPTION); 1697 window->sessionStorage(IGNORE_EXCEPTION); 1698} 1699 1700bool DOMWindow::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) 1701{ 1702 if (!EventTarget::addEventListener(eventType, listener, useCapture)) 1703 return false; 1704 1705 if (Document* document = this->document()) { 1706 document->addListenerTypeIfNeeded(eventType); 1707 if (eventType == eventNames().wheelEvent || eventType == eventNames().mousewheelEvent) 1708 document->didAddWheelEventHandler(); 1709 else if (eventNames().isTouchEventType(eventType)) 1710 document->didAddTouchEventHandler(document); 1711 else if (eventType == eventNames().storageEvent) 1712 didAddStorageEventListener(this); 1713 } 1714 1715 if (eventType == eventNames().unloadEvent) 1716 addUnloadEventListener(this); 1717 else if (eventType == eventNames().beforeunloadEvent && allowsBeforeUnloadListeners(this)) 1718 addBeforeUnloadEventListener(this); 1719#if ENABLE(DEVICE_ORIENTATION) 1720#if PLATFORM(IOS) 1721 else if (eventType == eventNames().devicemotionEvent && document()) 1722 document()->deviceMotionController()->addDeviceEventListener(this); 1723 else if (eventType == eventNames().deviceorientationEvent && document()) 1724 document()->deviceOrientationController()->addDeviceEventListener(this); 1725#else 1726 else if (eventType == eventNames().devicemotionEvent && RuntimeEnabledFeatures::sharedFeatures().deviceMotionEnabled()) { 1727 if (DeviceMotionController* controller = DeviceMotionController::from(page())) 1728 controller->addDeviceEventListener(this); 1729 } else if (eventType == eventNames().deviceorientationEvent && RuntimeEnabledFeatures::sharedFeatures().deviceOrientationEnabled()) { 1730 if (DeviceOrientationController* controller = DeviceOrientationController::from(page())) 1731 controller->addDeviceEventListener(this); 1732 } 1733#endif // PLATFORM(IOS) 1734#endif // ENABLE(DEVICE_ORIENTATION) 1735#if PLATFORM(IOS) 1736 else if (eventType == eventNames().scrollEvent) 1737 incrementScrollEventListenersCount(); 1738#endif 1739#if ENABLE(IOS_TOUCH_EVENTS) 1740 else if (eventNames().isTouchEventType(eventType)) 1741 ++m_touchEventListenerCount; 1742#endif 1743#if ENABLE(IOS_GESTURE_EVENTS) 1744 else if (eventNames().isGestureEventType(eventType)) 1745 ++m_touchEventListenerCount; 1746#endif 1747#if ENABLE(GAMEPAD) 1748 else if (eventNames().isGamepadEventType(eventType)) 1749 incrementGamepadEventListenerCount(); 1750#endif 1751#if ENABLE(PROXIMITY_EVENTS) 1752 else if (eventType == eventNames().webkitdeviceproximityEvent) { 1753 if (DeviceProximityController* controller = DeviceProximityController::from(page())) 1754 controller->addDeviceEventListener(this); 1755 } 1756#endif 1757 1758 return true; 1759} 1760 1761#if PLATFORM(IOS) 1762void DOMWindow::incrementScrollEventListenersCount() 1763{ 1764 Document* document = this->document(); 1765 if (++m_scrollEventListenerCount == 1 && document == &document->topDocument()) { 1766 Frame* frame = this->frame(); 1767 if (frame && frame->page()) 1768 frame->page()->chrome().client().setNeedsScrollNotifications(frame, true); 1769 } 1770} 1771 1772void DOMWindow::decrementScrollEventListenersCount() 1773{ 1774 Document* document = this->document(); 1775 if (!--m_scrollEventListenerCount && document == &document->topDocument()) { 1776 Frame* frame = this->frame(); 1777 if (frame && frame->page() && !document->inPageCache()) 1778 frame->page()->chrome().client().setNeedsScrollNotifications(frame, false); 1779 } 1780} 1781#endif 1782 1783void DOMWindow::resetAllGeolocationPermission() 1784{ 1785 // FIXME: Remove PLATFORM(IOS)-guard once we upstream the iOS changes to Geolocation.cpp. 1786#if ENABLE(GEOLOCATION) && PLATFORM(IOS) 1787 if (m_navigator) 1788 NavigatorGeolocation::from(m_navigator.get())->resetAllGeolocationPermission(); 1789#endif 1790} 1791 1792bool DOMWindow::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) 1793{ 1794 if (!EventTarget::removeEventListener(eventType, listener, useCapture)) 1795 return false; 1796 1797 if (Document* document = this->document()) { 1798 if (eventType == eventNames().wheelEvent || eventType == eventNames().mousewheelEvent) 1799 document->didRemoveWheelEventHandler(); 1800 else if (eventNames().isTouchEventType(eventType)) 1801 document->didRemoveTouchEventHandler(document); 1802 } 1803 1804 if (eventType == eventNames().unloadEvent) 1805 removeUnloadEventListener(this); 1806 else if (eventType == eventNames().beforeunloadEvent && allowsBeforeUnloadListeners(this)) 1807 removeBeforeUnloadEventListener(this); 1808#if ENABLE(DEVICE_ORIENTATION) 1809#if PLATFORM(IOS) 1810 else if (eventType == eventNames().devicemotionEvent && document()) 1811 document()->deviceMotionController()->removeDeviceEventListener(this); 1812 else if (eventType == eventNames().deviceorientationEvent && document()) 1813 document()->deviceOrientationController()->removeDeviceEventListener(this); 1814#else 1815 else if (eventType == eventNames().devicemotionEvent) { 1816 if (DeviceMotionController* controller = DeviceMotionController::from(page())) 1817 controller->removeDeviceEventListener(this); 1818 } else if (eventType == eventNames().deviceorientationEvent) { 1819 if (DeviceOrientationController* controller = DeviceOrientationController::from(page())) 1820 controller->removeDeviceEventListener(this); 1821 } 1822#endif // PLATFORM(IOS) 1823#endif // ENABLE(DEVICE_ORIENTATION) 1824#if PLATFORM(IOS) 1825 else if (eventType == eventNames().scrollEvent) 1826 decrementScrollEventListenersCount(); 1827#endif 1828#if ENABLE(IOS_TOUCH_EVENTS) 1829 else if (eventNames().isTouchEventType(eventType)) { 1830 ASSERT(m_touchEventListenerCount > 0); 1831 --m_touchEventListenerCount; 1832 } 1833#endif 1834#if ENABLE(IOS_GESTURE_EVENTS) 1835 else if (eventNames().isGestureEventType(eventType)) { 1836 ASSERT(m_touchEventListenerCount > 0); 1837 --m_touchEventListenerCount; 1838 } 1839#endif 1840#if ENABLE(GAMEPAD) 1841 else if (eventNames().isGamepadEventType(eventType)) 1842 decrementGamepadEventListenerCount(); 1843#endif 1844#if ENABLE(PROXIMITY_EVENTS) 1845 else if (eventType == eventNames().webkitdeviceproximityEvent) { 1846 if (DeviceProximityController* controller = DeviceProximityController::from(page())) 1847 controller->removeDeviceEventListener(this); 1848 } 1849#endif 1850 1851 return true; 1852} 1853 1854void DOMWindow::dispatchLoadEvent() 1855{ 1856 RefPtr<Event> loadEvent(Event::create(eventNames().loadEvent, false, false)); 1857 if (m_frame && m_frame->loader().documentLoader() && !m_frame->loader().documentLoader()->timing()->loadEventStart()) { 1858 // The DocumentLoader (and thus its DocumentLoadTiming) might get destroyed while dispatching 1859 // the event, so protect it to prevent writing the end time into freed memory. 1860 RefPtr<DocumentLoader> documentLoader = m_frame->loader().documentLoader(); 1861 DocumentLoadTiming* timing = documentLoader->timing(); 1862 timing->markLoadEventStart(); 1863 dispatchEvent(loadEvent, document()); 1864 timing->markLoadEventEnd(); 1865 } else 1866 dispatchEvent(loadEvent, document()); 1867 1868 // For load events, send a separate load event to the enclosing frame only. 1869 // This is a DOM extension and is independent of bubbling/capturing rules of 1870 // the DOM. 1871 Element* ownerElement = m_frame ? m_frame->ownerElement() : 0; 1872 if (ownerElement) 1873 ownerElement->dispatchEvent(Event::create(eventNames().loadEvent, false, false)); 1874 1875 InspectorInstrumentation::loadEventFired(frame()); 1876} 1877 1878bool DOMWindow::dispatchEvent(PassRefPtr<Event> prpEvent, PassRefPtr<EventTarget> prpTarget) 1879{ 1880 Ref<EventTarget> protect(*this); 1881 RefPtr<Event> event = prpEvent; 1882 1883 // Pausing a page may trigger pagehide and pageshow events. WebCore also implicitly fires these 1884 // events when closing a WebView. Here we keep track of the state of the page to prevent duplicate, 1885 // unbalanced events per the definition of the pageshow event: 1886 // <http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#event-pageshow>. 1887 if (event->eventInterface() == PageTransitionEventInterfaceType) { 1888 if (event->type() == eventNames().pageshowEvent) { 1889 if (m_lastPageStatus == PageStatusShown) 1890 return true; // Event was previously dispatched; do not fire a duplicate event. 1891 m_lastPageStatus = PageStatusShown; 1892 } else if (event->type() == eventNames().pagehideEvent) { 1893 if (m_lastPageStatus == PageStatusHidden) 1894 return true; // Event was previously dispatched; do not fire a duplicate event. 1895 m_lastPageStatus = PageStatusHidden; 1896 } 1897 } 1898 1899 event->setTarget(prpTarget ? prpTarget : this); 1900 event->setCurrentTarget(this); 1901 event->setEventPhase(Event::AT_TARGET); 1902 1903 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchEventOnWindow(frame(), *event, this); 1904 1905 bool result = fireEventListeners(event.get()); 1906 1907 InspectorInstrumentation::didDispatchEventOnWindow(cookie); 1908 1909 return result; 1910} 1911 1912void DOMWindow::removeAllEventListeners() 1913{ 1914 EventTarget::removeAllEventListeners(); 1915 1916#if ENABLE(DEVICE_ORIENTATION) 1917#if PLATFORM(IOS) 1918 if (Document* document = this->document()) { 1919 document->deviceMotionController()->removeAllDeviceEventListeners(this); 1920 document->deviceOrientationController()->removeAllDeviceEventListeners(this); 1921 } 1922#else 1923 if (DeviceMotionController* controller = DeviceMotionController::from(page())) 1924 controller->removeAllDeviceEventListeners(this); 1925 if (DeviceOrientationController* controller = DeviceOrientationController::from(page())) 1926 controller->removeAllDeviceEventListeners(this); 1927#endif // PLATFORM(IOS) 1928#endif // ENABLE(DEVICE_ORIENTATION) 1929 1930#if PLATFORM(IOS) 1931 if (m_scrollEventListenerCount) { 1932 m_scrollEventListenerCount = 1; 1933 decrementScrollEventListenersCount(); 1934 } 1935#endif 1936 1937#if ENABLE(IOS_TOUCH_EVENTS) || ENABLE(IOS_GESTURE_EVENTS) 1938 m_touchEventListenerCount = 0; 1939#endif 1940 1941#if ENABLE(TOUCH_EVENTS) 1942 if (Document* document = this->document()) 1943 document->didRemoveEventTargetNode(document); 1944#endif 1945 1946#if ENABLE(PROXIMITY_EVENTS) 1947 if (DeviceProximityController* controller = DeviceProximityController::from(page())) 1948 controller->removeAllDeviceEventListeners(this); 1949#endif 1950 1951 removeAllUnloadEventListeners(this); 1952 removeAllBeforeUnloadEventListeners(this); 1953} 1954 1955void DOMWindow::captureEvents() 1956{ 1957 // Not implemented. 1958} 1959 1960void DOMWindow::releaseEvents() 1961{ 1962 // Not implemented. 1963} 1964 1965void DOMWindow::finishedLoading() 1966{ 1967 if (m_shouldPrintWhenFinishedLoading) { 1968 m_shouldPrintWhenFinishedLoading = false; 1969 print(); 1970 } 1971} 1972 1973void DOMWindow::setLocation(const String& urlString, DOMWindow& activeWindow, DOMWindow& firstWindow, SetLocationLocking locking) 1974{ 1975 if (!isCurrentlyDisplayedInFrame()) 1976 return; 1977 1978 Document* activeDocument = activeWindow.document(); 1979 if (!activeDocument) 1980 return; 1981 1982 if (!activeDocument->canNavigate(m_frame)) 1983 return; 1984 1985 Frame* firstFrame = firstWindow.frame(); 1986 if (!firstFrame) 1987 return; 1988 1989 URL completedURL = firstFrame->document()->completeURL(urlString); 1990 if (completedURL.isNull()) 1991 return; 1992 1993 if (isInsecureScriptAccess(activeWindow, completedURL)) 1994 return; 1995 1996 // We want a new history item if we are processing a user gesture. 1997 LockHistory lockHistory = (locking != LockHistoryBasedOnGestureState || !ScriptController::processingUserGesture()) ? LockHistory::Yes : LockHistory::No; 1998 LockBackForwardList lockBackForwardList = (locking != LockHistoryBasedOnGestureState) ? LockBackForwardList::Yes : LockBackForwardList::No; 1999 m_frame->navigationScheduler().scheduleLocationChange(activeDocument->securityOrigin(), 2000 // FIXME: What if activeDocument()->frame() is 0? 2001 completedURL, activeDocument->frame()->loader().outgoingReferrer(), 2002 lockHistory, lockBackForwardList); 2003} 2004 2005void DOMWindow::printErrorMessage(const String& message) 2006{ 2007 if (message.isEmpty()) 2008 return; 2009 2010 if (PageConsole* console = pageConsole()) 2011 console->addMessage(MessageSource::JS, MessageLevel::Error, message); 2012} 2013 2014String DOMWindow::crossDomainAccessErrorMessage(const DOMWindow& activeWindow) 2015{ 2016 const URL& activeWindowURL = activeWindow.document()->url(); 2017 if (activeWindowURL.isNull()) 2018 return String(); 2019 2020 ASSERT(!activeWindow.document()->securityOrigin()->canAccess(document()->securityOrigin())); 2021 2022 // FIXME: This message, and other console messages, have extra newlines. Should remove them. 2023 SecurityOrigin* activeOrigin = activeWindow.document()->securityOrigin(); 2024 SecurityOrigin* targetOrigin = document()->securityOrigin(); 2025 String message = "Blocked a frame with origin \"" + activeOrigin->toString() + "\" from accessing a frame with origin \"" + targetOrigin->toString() + "\". "; 2026 2027 // Sandbox errors: Use the origin of the frames' location, rather than their actual origin (since we know that at least one will be "null"). 2028 URL activeURL = activeWindow.document()->url(); 2029 URL targetURL = document()->url(); 2030 if (document()->isSandboxed(SandboxOrigin) || activeWindow.document()->isSandboxed(SandboxOrigin)) { 2031 message = "Blocked a frame at \"" + SecurityOrigin::create(activeURL)->toString() + "\" from accessing a frame at \"" + SecurityOrigin::create(targetURL)->toString() + "\". "; 2032 if (document()->isSandboxed(SandboxOrigin) && activeWindow.document()->isSandboxed(SandboxOrigin)) 2033 return "Sandbox access violation: " + message + " Both frames are sandboxed and lack the \"allow-same-origin\" flag."; 2034 if (document()->isSandboxed(SandboxOrigin)) 2035 return "Sandbox access violation: " + message + " The frame being accessed is sandboxed and lacks the \"allow-same-origin\" flag."; 2036 return "Sandbox access violation: " + message + " The frame requesting access is sandboxed and lacks the \"allow-same-origin\" flag."; 2037 } 2038 2039 // Protocol errors: Use the URL's protocol rather than the origin's protocol so that we get a useful message for non-heirarchal URLs like 'data:'. 2040 if (targetOrigin->protocol() != activeOrigin->protocol()) 2041 return message + " The frame requesting access has a protocol of \"" + activeURL.protocol() + "\", the frame being accessed has a protocol of \"" + targetURL.protocol() + "\". Protocols must match.\n"; 2042 2043 // 'document.domain' errors. 2044 if (targetOrigin->domainWasSetInDOM() && activeOrigin->domainWasSetInDOM()) 2045 return message + "The frame requesting access set \"document.domain\" to \"" + activeOrigin->domain() + "\", the frame being accessed set it to \"" + targetOrigin->domain() + "\". Both must set \"document.domain\" to the same value to allow access."; 2046 if (activeOrigin->domainWasSetInDOM()) 2047 return message + "The frame requesting access set \"document.domain\" to \"" + activeOrigin->domain() + "\", but the frame being accessed did not. Both must set \"document.domain\" to the same value to allow access."; 2048 if (targetOrigin->domainWasSetInDOM()) 2049 return message + "The frame being accessed set \"document.domain\" to \"" + targetOrigin->domain() + "\", but the frame requesting access did not. Both must set \"document.domain\" to the same value to allow access."; 2050 2051 // Default. 2052 return message + "Protocols, domains, and ports must match."; 2053} 2054 2055bool DOMWindow::isInsecureScriptAccess(DOMWindow& activeWindow, const String& urlString) 2056{ 2057 if (!protocolIsJavaScript(urlString)) 2058 return false; 2059 2060 // If this DOMWindow isn't currently active in the Frame, then there's no 2061 // way we should allow the access. 2062 // FIXME: Remove this check if we're able to disconnect DOMWindow from 2063 // Frame on navigation: https://bugs.webkit.org/show_bug.cgi?id=62054 2064 if (isCurrentlyDisplayedInFrame()) { 2065 // FIXME: Is there some way to eliminate the need for a separate "activeWindow == this" check? 2066 if (&activeWindow == this) 2067 return false; 2068 2069 // FIXME: The name canAccess seems to be a roundabout way to ask "can execute script". 2070 // Can we name the SecurityOrigin function better to make this more clear? 2071 if (activeWindow.document()->securityOrigin()->canAccess(document()->securityOrigin())) 2072 return false; 2073 } 2074 2075 printErrorMessage(crossDomainAccessErrorMessage(activeWindow)); 2076 return true; 2077} 2078 2079PassRefPtr<Frame> DOMWindow::createWindow(const String& urlString, const AtomicString& frameName, const WindowFeatures& windowFeatures, DOMWindow& activeWindow, Frame* firstFrame, Frame* openerFrame, std::function<void (DOMWindow&)> prepareDialogFunction) 2080{ 2081 Frame* activeFrame = activeWindow.frame(); 2082 2083 URL completedURL = urlString.isEmpty() ? URL(ParsedURLString, emptyString()) : firstFrame->document()->completeURL(urlString); 2084 if (!completedURL.isEmpty() && !completedURL.isValid()) { 2085 // Don't expose client code to invalid URLs. 2086 activeWindow.printErrorMessage("Unable to open a window with invalid URL '" + completedURL.string() + "'.\n"); 2087 return 0; 2088 } 2089 2090 // For whatever reason, Firefox uses the first frame to determine the outgoingReferrer. We replicate that behavior here. 2091 String referrer = SecurityPolicy::generateReferrerHeader(firstFrame->document()->referrerPolicy(), completedURL, firstFrame->loader().outgoingReferrer()); 2092 2093 ResourceRequest request(completedURL, referrer); 2094 FrameLoader::addHTTPOriginIfNeeded(request, firstFrame->loader().outgoingOrigin()); 2095 FrameLoadRequest frameRequest(activeWindow.document()->securityOrigin(), request, frameName); 2096 2097 // We pass the opener frame for the lookupFrame in case the active frame is different from 2098 // the opener frame, and the name references a frame relative to the opener frame. 2099 bool created; 2100 RefPtr<Frame> newFrame = WebCore::createWindow(activeFrame, openerFrame, frameRequest, windowFeatures, created); 2101 if (!newFrame) 2102 return 0; 2103 2104 newFrame->loader().setOpener(openerFrame); 2105 newFrame->page()->setOpenedByDOM(); 2106 2107 if (newFrame->document()->domWindow()->isInsecureScriptAccess(activeWindow, completedURL)) 2108 return newFrame.release(); 2109 2110 if (prepareDialogFunction) 2111 prepareDialogFunction(*newFrame->document()->domWindow()); 2112 2113 if (created) 2114 newFrame->loader().changeLocation(activeWindow.document()->securityOrigin(), completedURL, referrer, LockHistory::No, LockBackForwardList::No); 2115 else if (!urlString.isEmpty()) { 2116 LockHistory lockHistory = ScriptController::processingUserGesture() ? LockHistory::No : LockHistory::Yes; 2117 newFrame->navigationScheduler().scheduleLocationChange(activeWindow.document()->securityOrigin(), completedURL, referrer, lockHistory, LockBackForwardList::No); 2118 } 2119 2120 // Navigating the new frame could result in it being detached from its page by a navigation policy delegate. 2121 if (!newFrame->page()) 2122 return 0; 2123 2124 return newFrame.release(); 2125} 2126 2127PassRefPtr<DOMWindow> DOMWindow::open(const String& urlString, const AtomicString& frameName, const String& windowFeaturesString, 2128 DOMWindow& activeWindow, DOMWindow& firstWindow) 2129{ 2130 if (!isCurrentlyDisplayedInFrame()) 2131 return 0; 2132 Document* activeDocument = activeWindow.document(); 2133 if (!activeDocument) 2134 return 0; 2135 Frame* firstFrame = firstWindow.frame(); 2136 if (!firstFrame) 2137 return 0; 2138 2139 if (!firstWindow.allowPopUp()) { 2140 // Because FrameTree::find() returns true for empty strings, we must check for empty frame names. 2141 // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker. 2142 if (frameName.isEmpty() || !m_frame->tree().find(frameName)) 2143 return 0; 2144 } 2145 2146 // Get the target frame for the special cases of _top and _parent. 2147 // In those cases, we schedule a location change right now and return early. 2148 Frame* targetFrame = 0; 2149 if (frameName == "_top") 2150 targetFrame = &m_frame->tree().top(); 2151 else if (frameName == "_parent") { 2152 if (Frame* parent = m_frame->tree().parent()) 2153 targetFrame = parent; 2154 else 2155 targetFrame = m_frame; 2156 } 2157 if (targetFrame) { 2158 if (!activeDocument->canNavigate(targetFrame)) 2159 return 0; 2160 2161 URL completedURL = firstFrame->document()->completeURL(urlString); 2162 2163 if (targetFrame->document()->domWindow()->isInsecureScriptAccess(activeWindow, completedURL)) 2164 return targetFrame->document()->domWindow(); 2165 2166 if (urlString.isEmpty()) 2167 return targetFrame->document()->domWindow(); 2168 2169 // For whatever reason, Firefox uses the first window rather than the active window to 2170 // determine the outgoing referrer. We replicate that behavior here. 2171 LockHistory lockHistory = ScriptController::processingUserGesture() ? LockHistory::No : LockHistory::Yes; 2172 targetFrame->navigationScheduler().scheduleLocationChange(activeDocument->securityOrigin(), completedURL, firstFrame->loader().outgoingReferrer(), 2173 lockHistory, LockBackForwardList::No); 2174 return targetFrame->document()->domWindow(); 2175 } 2176 2177 WindowFeatures windowFeatures(windowFeaturesString); 2178 RefPtr<Frame> result = createWindow(urlString, frameName, windowFeatures, activeWindow, firstFrame, m_frame); 2179 return result ? result->document()->domWindow() : 0; 2180} 2181 2182void DOMWindow::showModalDialog(const String& urlString, const String& dialogFeaturesString, DOMWindow& activeWindow, DOMWindow& firstWindow, std::function<void (DOMWindow&)> prepareDialogFunction) 2183{ 2184 if (!isCurrentlyDisplayedInFrame()) 2185 return; 2186 Frame* activeFrame = activeWindow.frame(); 2187 if (!activeFrame) 2188 return; 2189 Frame* firstFrame = firstWindow.frame(); 2190 if (!firstFrame) 2191 return; 2192 2193 // Pages are not allowed to cause modal alerts during BeforeUnload dispatch. 2194 if (page() && page()->isAnyFrameHandlingBeforeUnloadEvent()) { 2195 printErrorMessage("Use of window.showModalDialog is not allowed during beforeunload event dispatch."); 2196 return; 2197 } 2198 2199 if (!canShowModalDialogNow(m_frame) || !firstWindow.allowPopUp()) 2200 return; 2201 2202 WindowFeatures windowFeatures(dialogFeaturesString, screenAvailableRect(m_frame->view())); 2203 RefPtr<Frame> dialogFrame = createWindow(urlString, emptyAtom, windowFeatures, activeWindow, firstFrame, m_frame, WTF::move(prepareDialogFunction)); 2204 if (!dialogFrame) 2205 return; 2206 dialogFrame->page()->chrome().runModal(); 2207} 2208 2209void DOMWindow::enableSuddenTermination() 2210{ 2211 if (Page* page = this->page()) 2212 page->chrome().enableSuddenTermination(); 2213} 2214 2215void DOMWindow::disableSuddenTermination() 2216{ 2217 if (Page* page = this->page()) 2218 page->chrome().disableSuddenTermination(); 2219} 2220 2221} // namespace WebCore 2222