1 /* 2 * Copyright (C) 2006, 2007, 2008, 2010 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 COMPUTER, 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 COMPUTER, 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 "core/page/DOMWindow.h" 29 30 #include "wtf/MainThread.h" 31 #include "wtf/MathExtras.h" 32 #include "wtf/text/WTFString.h" 33 #include <algorithm> 34 #include "RuntimeEnabledFeatures.h" 35 #include "bindings/v8/ExceptionState.h" 36 #include "bindings/v8/ExceptionStatePlaceholder.h" 37 #include "bindings/v8/ScriptCallStackFactory.h" 38 #include "bindings/v8/ScriptController.h" 39 #include "bindings/v8/SerializedScriptValue.h" 40 #include "core/css/CSSComputedStyleDeclaration.h" 41 #include "core/css/CSSRuleList.h" 42 #include "core/css/DOMWindowCSS.h" 43 #include "core/css/MediaQueryList.h" 44 #include "core/css/MediaQueryMatcher.h" 45 #include "core/css/StyleMedia.h" 46 #include "core/css/resolver/StyleResolver.h" 47 #include "core/dom/DeviceOrientationController.h" 48 #include "core/dom/Document.h" 49 #include "core/dom/Element.h" 50 #include "core/dom/EventListener.h" 51 #include "core/dom/EventNames.h" 52 #include "core/dom/ExceptionCode.h" 53 #include "core/dom/MessageEvent.h" 54 #include "core/dom/PageTransitionEvent.h" 55 #include "core/dom/RequestAnimationFrameCallback.h" 56 #include "core/dom/ScriptExecutionContext.h" 57 #include "core/editing/Editor.h" 58 #include "core/history/BackForwardController.h" 59 #include "core/html/HTMLFrameOwnerElement.h" 60 #include "core/inspector/InspectorInstrumentation.h" 61 #include "core/inspector/ScriptCallStack.h" 62 #include "core/loader/DocumentLoader.h" 63 #include "core/loader/FrameLoadRequest.h" 64 #include "core/loader/FrameLoader.h" 65 #include "core/loader/FrameLoaderClient.h" 66 #include "core/loader/appcache/DOMApplicationCache.h" 67 #include "core/page/BarProp.h" 68 #include "core/page/Chrome.h" 69 #include "core/page/ChromeClient.h" 70 #include "core/page/Console.h" 71 #include "core/page/CreateWindow.h" 72 #include "core/page/DOMPoint.h" 73 #include "core/page/EventHandler.h" 74 #include "core/page/Frame.h" 75 #include "core/page/FrameTree.h" 76 #include "core/page/FrameView.h" 77 #include "core/page/History.h" 78 #include "core/page/Location.h" 79 #include "core/page/Navigator.h" 80 #include "core/page/Page.h" 81 #include "core/page/PageConsole.h" 82 #include "core/page/PageGroup.h" 83 #include "core/page/Performance.h" 84 #include "core/page/Screen.h" 85 #include "core/page/Settings.h" 86 #include "core/page/WindowFeatures.h" 87 #include "core/page/WindowFocusAllowedIndicator.h" 88 #include "core/page/scrolling/ScrollingCoordinator.h" 89 #include "core/platform/PlatformScreen.h" 90 #include "core/platform/SuddenTermination.h" 91 #include "core/platform/graphics/FloatRect.h" 92 #include "core/platform/graphics/MediaPlayer.h" 93 #include "core/storage/Storage.h" 94 #include "core/storage/StorageArea.h" 95 #include "core/storage/StorageNamespace.h" 96 #include "modules/device_orientation/DeviceMotionController.h" 97 #include "weborigin/KURL.h" 98 #include "weborigin/SecurityOrigin.h" 99 #include "weborigin/SecurityPolicy.h" 100 101 using std::min; 102 using std::max; 103 104 namespace WebCore { 105 106 class PostMessageTimer : public TimerBase { 107 public: 108 PostMessageTimer(DOMWindow* window, PassRefPtr<SerializedScriptValue> message, const String& sourceOrigin, PassRefPtr<DOMWindow> source, PassOwnPtr<MessagePortChannelArray> channels, SecurityOrigin* targetOrigin, PassRefPtr<ScriptCallStack> stackTrace) 109 : m_window(window) 110 , m_message(message) 111 , m_origin(sourceOrigin) 112 , m_source(source) 113 , m_channels(channels) 114 , m_targetOrigin(targetOrigin) 115 , m_stackTrace(stackTrace) 116 { 117 } 118 119 PassRefPtr<MessageEvent> event(ScriptExecutionContext* context) 120 { 121 OwnPtr<MessagePortArray> messagePorts = MessagePort::entanglePorts(*context, m_channels.release()); 122 return MessageEvent::create(messagePorts.release(), m_message, m_origin, "", m_source); 123 } 124 SecurityOrigin* targetOrigin() const { return m_targetOrigin.get(); } 125 ScriptCallStack* stackTrace() const { return m_stackTrace.get(); } 126 127 private: 128 virtual void fired() 129 { 130 m_window->postMessageTimerFired(adoptPtr(this)); 131 // This object is deleted now. 132 } 133 134 RefPtr<DOMWindow> m_window; 135 RefPtr<SerializedScriptValue> m_message; 136 String m_origin; 137 RefPtr<DOMWindow> m_source; 138 OwnPtr<MessagePortChannelArray> m_channels; 139 RefPtr<SecurityOrigin> m_targetOrigin; 140 RefPtr<ScriptCallStack> m_stackTrace; 141 }; 142 143 typedef HashCountedSet<DOMWindow*> DOMWindowSet; 144 145 static DOMWindowSet& windowsWithUnloadEventListeners() 146 { 147 DEFINE_STATIC_LOCAL(DOMWindowSet, windowsWithUnloadEventListeners, ()); 148 return windowsWithUnloadEventListeners; 149 } 150 151 static DOMWindowSet& windowsWithBeforeUnloadEventListeners() 152 { 153 DEFINE_STATIC_LOCAL(DOMWindowSet, windowsWithBeforeUnloadEventListeners, ()); 154 return windowsWithBeforeUnloadEventListeners; 155 } 156 157 static void addUnloadEventListener(DOMWindow* domWindow) 158 { 159 DOMWindowSet& set = windowsWithUnloadEventListeners(); 160 if (set.isEmpty()) 161 disableSuddenTermination(); 162 set.add(domWindow); 163 } 164 165 static void removeUnloadEventListener(DOMWindow* domWindow) 166 { 167 DOMWindowSet& set = windowsWithUnloadEventListeners(); 168 DOMWindowSet::iterator it = set.find(domWindow); 169 if (it == set.end()) 170 return; 171 set.remove(it); 172 if (set.isEmpty()) 173 enableSuddenTermination(); 174 } 175 176 static void removeAllUnloadEventListeners(DOMWindow* domWindow) 177 { 178 DOMWindowSet& set = windowsWithUnloadEventListeners(); 179 DOMWindowSet::iterator it = set.find(domWindow); 180 if (it == set.end()) 181 return; 182 set.removeAll(it); 183 if (set.isEmpty()) 184 enableSuddenTermination(); 185 } 186 187 static void addBeforeUnloadEventListener(DOMWindow* domWindow) 188 { 189 DOMWindowSet& set = windowsWithBeforeUnloadEventListeners(); 190 if (set.isEmpty()) 191 disableSuddenTermination(); 192 set.add(domWindow); 193 } 194 195 static void removeBeforeUnloadEventListener(DOMWindow* domWindow) 196 { 197 DOMWindowSet& set = windowsWithBeforeUnloadEventListeners(); 198 DOMWindowSet::iterator it = set.find(domWindow); 199 if (it == set.end()) 200 return; 201 set.remove(it); 202 if (set.isEmpty()) 203 enableSuddenTermination(); 204 } 205 206 static void removeAllBeforeUnloadEventListeners(DOMWindow* domWindow) 207 { 208 DOMWindowSet& set = windowsWithBeforeUnloadEventListeners(); 209 DOMWindowSet::iterator it = set.find(domWindow); 210 if (it == set.end()) 211 return; 212 set.removeAll(it); 213 if (set.isEmpty()) 214 enableSuddenTermination(); 215 } 216 217 static bool allowsBeforeUnloadListeners(DOMWindow* window) 218 { 219 ASSERT_ARG(window, window); 220 Frame* frame = window->frame(); 221 if (!frame) 222 return false; 223 Page* page = frame->page(); 224 if (!page) 225 return false; 226 return frame == page->mainFrame(); 227 } 228 229 unsigned DOMWindow::pendingUnloadEventListeners() const 230 { 231 return windowsWithUnloadEventListeners().count(const_cast<DOMWindow*>(this)); 232 } 233 234 // This function: 235 // 1) Validates the pending changes are not changing any value to NaN; in that case keep original value. 236 // 2) Constrains the window rect to the minimum window size and no bigger than the float rect's dimensions. 237 // 3) Constrains the window rect to within the top and left boundaries of the available screen rect. 238 // 4) Constrains the window rect to within the bottom and right boundaries of the available screen rect. 239 // 5) Translate the window rect coordinates to be within the coordinate space of the screen. 240 FloatRect DOMWindow::adjustWindowRect(Page* page, const FloatRect& pendingChanges) 241 { 242 ASSERT(page); 243 244 FloatRect screen = screenAvailableRect(page->mainFrame()->view()); 245 FloatRect window = page->chrome().windowRect(); 246 247 // Make sure we're in a valid state before adjusting dimensions. 248 ASSERT(std::isfinite(screen.x())); 249 ASSERT(std::isfinite(screen.y())); 250 ASSERT(std::isfinite(screen.width())); 251 ASSERT(std::isfinite(screen.height())); 252 ASSERT(std::isfinite(window.x())); 253 ASSERT(std::isfinite(window.y())); 254 ASSERT(std::isfinite(window.width())); 255 ASSERT(std::isfinite(window.height())); 256 257 // Update window values if new requested values are not NaN. 258 if (!std::isnan(pendingChanges.x())) 259 window.setX(pendingChanges.x()); 260 if (!std::isnan(pendingChanges.y())) 261 window.setY(pendingChanges.y()); 262 if (!std::isnan(pendingChanges.width())) 263 window.setWidth(pendingChanges.width()); 264 if (!std::isnan(pendingChanges.height())) 265 window.setHeight(pendingChanges.height()); 266 267 FloatSize minimumSize = page->chrome().client()->minimumWindowSize(); 268 // Let size 0 pass through, since that indicates default size, not minimum size. 269 if (window.width()) 270 window.setWidth(min(max(minimumSize.width(), window.width()), screen.width())); 271 if (window.height()) 272 window.setHeight(min(max(minimumSize.height(), window.height()), screen.height())); 273 274 // Constrain the window position within the valid screen area. 275 window.setX(max(screen.x(), min(window.x(), screen.maxX() - window.width()))); 276 window.setY(max(screen.y(), min(window.y(), screen.maxY() - window.height()))); 277 278 return window; 279 } 280 281 bool DOMWindow::allowPopUp(Frame* firstFrame) 282 { 283 ASSERT(firstFrame); 284 285 if (ScriptController::processingUserGesture()) 286 return true; 287 288 Settings* settings = firstFrame->settings(); 289 return settings && settings->javaScriptCanOpenWindowsAutomatically(); 290 } 291 292 bool DOMWindow::allowPopUp() 293 { 294 return m_frame && allowPopUp(m_frame); 295 } 296 297 bool DOMWindow::canShowModalDialog(const Frame* frame) 298 { 299 if (!frame) 300 return false; 301 Page* page = frame->page(); 302 if (!page) 303 return false; 304 return page->chrome().canRunModal(); 305 } 306 307 bool DOMWindow::canShowModalDialogNow(const Frame* frame) 308 { 309 if (!frame) 310 return false; 311 Page* page = frame->page(); 312 if (!page) 313 return false; 314 return page->chrome().canRunModalNow(); 315 } 316 317 DOMWindow::DOMWindow(Frame* frame) 318 : FrameDestructionObserver(frame) 319 , m_shouldPrintWhenFinishedLoading(false) 320 { 321 ASSERT(frame); 322 ScriptWrappable::init(this); 323 } 324 325 void DOMWindow::setDocument(PassRefPtr<Document> document) 326 { 327 ASSERT(!document || document->frame() == m_frame); 328 if (m_document) { 329 if (m_document->attached()) { 330 // FIXME: We don't call willRemove here. Why is that OK? 331 // This detach() call is also mostly redundant. Most of the calls to 332 // this function come via DocumentLoader::createWriterFor, which 333 // always detaches the previous Document first. Only XSLTProcessor 334 // depends on this detach() call, so it seems like there's some room 335 // for cleanup. 336 m_document->detach(); 337 } 338 m_document->setDOMWindow(0); 339 } 340 341 m_document = document; 342 343 if (!m_document) 344 return; 345 346 m_document->setDOMWindow(this); 347 if (!m_document->attached()) 348 m_document->attach(); 349 350 if (!m_frame) 351 return; 352 353 m_frame->script()->updateDocument(); 354 m_document->updateViewportArguments(); 355 356 if (m_frame->page() && m_frame->view()) { 357 if (ScrollingCoordinator* scrollingCoordinator = m_frame->page()->scrollingCoordinator()) { 358 scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_frame->view(), HorizontalScrollbar); 359 scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_frame->view(), VerticalScrollbar); 360 scrollingCoordinator->scrollableAreaScrollLayerDidChange(m_frame->view()); 361 } 362 } 363 364 m_frame->selection()->updateSecureKeyboardEntryIfActive(); 365 366 if (m_frame->page() && m_frame->page()->mainFrame() == m_frame) { 367 m_frame->page()->mainFrame()->notifyChromeClientWheelEventHandlerCountChanged(); 368 if (m_document->hasTouchEventHandlers()) 369 m_frame->page()->chrome().client()->needTouchEvents(true); 370 } 371 } 372 373 DOMWindow::~DOMWindow() 374 { 375 ASSERT(!m_screen); 376 ASSERT(!m_history); 377 ASSERT(!m_locationbar); 378 ASSERT(!m_menubar); 379 ASSERT(!m_personalbar); 380 ASSERT(!m_scrollbars); 381 ASSERT(!m_statusbar); 382 ASSERT(!m_toolbar); 383 ASSERT(!m_console); 384 ASSERT(!m_navigator); 385 ASSERT(!m_performance); 386 ASSERT(!m_location); 387 ASSERT(!m_media); 388 ASSERT(!m_sessionStorage); 389 ASSERT(!m_localStorage); 390 ASSERT(!m_applicationCache); 391 392 reset(); 393 394 removeAllEventListeners(); 395 396 ASSERT(!m_document->attached()); 397 setDocument(0); 398 } 399 400 const AtomicString& DOMWindow::interfaceName() const 401 { 402 return eventNames().interfaceForDOMWindow; 403 } 404 405 ScriptExecutionContext* DOMWindow::scriptExecutionContext() const 406 { 407 return m_document.get(); 408 } 409 410 DOMWindow* DOMWindow::toDOMWindow() 411 { 412 return this; 413 } 414 415 PassRefPtr<MediaQueryList> DOMWindow::matchMedia(const String& media) 416 { 417 return document() ? document()->mediaQueryMatcher()->matchMedia(media) : 0; 418 } 419 420 Page* DOMWindow::page() 421 { 422 return frame() ? frame()->page() : 0; 423 } 424 425 void DOMWindow::frameDestroyed() 426 { 427 FrameDestructionObserver::frameDestroyed(); 428 reset(); 429 } 430 431 void DOMWindow::willDetachPage() 432 { 433 InspectorInstrumentation::frameWindowDiscarded(m_frame, this); 434 // FIXME: Once DeviceOrientationController is a ScriptExecutionContext 435 // Supplement, this will no longer be needed. 436 if (DeviceOrientationController* controller = DeviceOrientationController::from(page())) 437 controller->removeAllDeviceEventListeners(this); 438 } 439 440 void DOMWindow::willDestroyDocumentInFrame() 441 { 442 // It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may 443 // unregister themselves from the DOMWindow as a result of the call to willDestroyGlobalObjectInFrame. 444 Vector<DOMWindowProperty*> properties; 445 copyToVector(m_properties, properties); 446 for (size_t i = 0; i < properties.size(); ++i) 447 properties[i]->willDestroyGlobalObjectInFrame(); 448 } 449 450 void DOMWindow::willDetachDocumentFromFrame() 451 { 452 // It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may 453 // unregister themselves from the DOMWindow as a result of the call to willDetachGlobalObjectFromFrame. 454 Vector<DOMWindowProperty*> properties; 455 copyToVector(m_properties, properties); 456 for (size_t i = 0; i < properties.size(); ++i) 457 properties[i]->willDetachGlobalObjectFromFrame(); 458 } 459 460 void DOMWindow::registerProperty(DOMWindowProperty* property) 461 { 462 m_properties.add(property); 463 } 464 465 void DOMWindow::unregisterProperty(DOMWindowProperty* property) 466 { 467 m_properties.remove(property); 468 } 469 470 void DOMWindow::reset() 471 { 472 willDestroyDocumentInFrame(); 473 resetDOMWindowProperties(); 474 } 475 476 void DOMWindow::resetDOMWindowProperties() 477 { 478 m_properties.clear(); 479 480 m_screen = 0; 481 m_history = 0; 482 m_locationbar = 0; 483 m_menubar = 0; 484 m_personalbar = 0; 485 m_scrollbars = 0; 486 m_statusbar = 0; 487 m_toolbar = 0; 488 m_console = 0; 489 m_navigator = 0; 490 m_performance = 0; 491 m_location = 0; 492 m_media = 0; 493 m_sessionStorage = 0; 494 m_localStorage = 0; 495 m_applicationCache = 0; 496 } 497 498 bool DOMWindow::isCurrentlyDisplayedInFrame() const 499 { 500 return m_frame && m_frame->domWindow() == this; 501 } 502 503 #if ENABLE(ORIENTATION_EVENTS) 504 int DOMWindow::orientation() const 505 { 506 if (!m_frame) 507 return 0; 508 509 return m_frame->orientation(); 510 } 511 #endif 512 513 Screen* DOMWindow::screen() const 514 { 515 if (!isCurrentlyDisplayedInFrame()) 516 return 0; 517 if (!m_screen) 518 m_screen = Screen::create(m_frame); 519 return m_screen.get(); 520 } 521 522 History* DOMWindow::history() const 523 { 524 if (!isCurrentlyDisplayedInFrame()) 525 return 0; 526 if (!m_history) 527 m_history = History::create(m_frame); 528 return m_history.get(); 529 } 530 531 BarProp* DOMWindow::locationbar() const 532 { 533 if (!isCurrentlyDisplayedInFrame()) 534 return 0; 535 if (!m_locationbar) 536 m_locationbar = BarProp::create(m_frame, BarProp::Locationbar); 537 return m_locationbar.get(); 538 } 539 540 BarProp* DOMWindow::menubar() const 541 { 542 if (!isCurrentlyDisplayedInFrame()) 543 return 0; 544 if (!m_menubar) 545 m_menubar = BarProp::create(m_frame, BarProp::Menubar); 546 return m_menubar.get(); 547 } 548 549 BarProp* DOMWindow::personalbar() const 550 { 551 if (!isCurrentlyDisplayedInFrame()) 552 return 0; 553 if (!m_personalbar) 554 m_personalbar = BarProp::create(m_frame, BarProp::Personalbar); 555 return m_personalbar.get(); 556 } 557 558 BarProp* DOMWindow::scrollbars() const 559 { 560 if (!isCurrentlyDisplayedInFrame()) 561 return 0; 562 if (!m_scrollbars) 563 m_scrollbars = BarProp::create(m_frame, BarProp::Scrollbars); 564 return m_scrollbars.get(); 565 } 566 567 BarProp* DOMWindow::statusbar() const 568 { 569 if (!isCurrentlyDisplayedInFrame()) 570 return 0; 571 if (!m_statusbar) 572 m_statusbar = BarProp::create(m_frame, BarProp::Statusbar); 573 return m_statusbar.get(); 574 } 575 576 BarProp* DOMWindow::toolbar() const 577 { 578 if (!isCurrentlyDisplayedInFrame()) 579 return 0; 580 if (!m_toolbar) 581 m_toolbar = BarProp::create(m_frame, BarProp::Toolbar); 582 return m_toolbar.get(); 583 } 584 585 Console* DOMWindow::console() const 586 { 587 if (!isCurrentlyDisplayedInFrame()) 588 return 0; 589 if (!m_console) 590 m_console = Console::create(m_frame); 591 return m_console.get(); 592 } 593 594 PageConsole* DOMWindow::pageConsole() const 595 { 596 if (!isCurrentlyDisplayedInFrame()) 597 return 0; 598 return m_frame->page() ? m_frame->page()->console() : 0; 599 } 600 601 DOMApplicationCache* DOMWindow::applicationCache() const 602 { 603 if (!isCurrentlyDisplayedInFrame()) 604 return 0; 605 if (!m_applicationCache) 606 m_applicationCache = DOMApplicationCache::create(m_frame); 607 return m_applicationCache.get(); 608 } 609 610 Navigator* DOMWindow::navigator() const 611 { 612 if (!isCurrentlyDisplayedInFrame()) 613 return 0; 614 if (!m_navigator) 615 m_navigator = Navigator::create(m_frame); 616 return m_navigator.get(); 617 } 618 619 Performance* DOMWindow::performance() const 620 { 621 if (!isCurrentlyDisplayedInFrame()) 622 return 0; 623 if (!m_performance) 624 m_performance = Performance::create(m_frame); 625 return m_performance.get(); 626 } 627 628 Location* DOMWindow::location() const 629 { 630 if (!isCurrentlyDisplayedInFrame()) 631 return 0; 632 if (!m_location) 633 m_location = Location::create(m_frame); 634 return m_location.get(); 635 } 636 637 Storage* DOMWindow::sessionStorage(ExceptionState& es) const 638 { 639 if (!isCurrentlyDisplayedInFrame()) 640 return 0; 641 642 Document* document = this->document(); 643 if (!document) 644 return 0; 645 646 if (!document->securityOrigin()->canAccessLocalStorage()) { 647 es.throwDOMException(SecurityError); 648 return 0; 649 } 650 651 if (m_sessionStorage) { 652 if (!m_sessionStorage->area()->canAccessStorage(m_frame)) { 653 es.throwDOMException(SecurityError); 654 return 0; 655 } 656 return m_sessionStorage.get(); 657 } 658 659 Page* page = document->page(); 660 if (!page) 661 return 0; 662 663 OwnPtr<StorageArea> storageArea = page->sessionStorage()->storageArea(document->securityOrigin()); 664 if (!storageArea->canAccessStorage(m_frame)) { 665 es.throwDOMException(SecurityError); 666 return 0; 667 } 668 669 m_sessionStorage = Storage::create(m_frame, storageArea.release()); 670 return m_sessionStorage.get(); 671 } 672 673 Storage* DOMWindow::localStorage(ExceptionState& es) const 674 { 675 if (!isCurrentlyDisplayedInFrame()) 676 return 0; 677 678 Document* document = this->document(); 679 if (!document) 680 return 0; 681 682 if (!document->securityOrigin()->canAccessLocalStorage()) { 683 es.throwDOMException(SecurityError); 684 return 0; 685 } 686 687 if (m_localStorage) { 688 if (!m_localStorage->area()->canAccessStorage(m_frame)) { 689 es.throwDOMException(SecurityError); 690 return 0; 691 } 692 return m_localStorage.get(); 693 } 694 695 Page* page = document->page(); 696 if (!page) 697 return 0; 698 699 if (!page->settings()->localStorageEnabled()) 700 return 0; 701 702 OwnPtr<StorageArea> storageArea = StorageNamespace::localStorageArea(document->securityOrigin()); 703 if (!storageArea->canAccessStorage(m_frame)) { 704 es.throwDOMException(SecurityError); 705 return 0; 706 } 707 708 m_localStorage = Storage::create(m_frame, storageArea.release()); 709 return m_localStorage.get(); 710 } 711 712 void DOMWindow::postMessage(PassRefPtr<SerializedScriptValue> message, const MessagePortArray* ports, const String& targetOrigin, DOMWindow* source, ExceptionState& es) 713 { 714 if (!isCurrentlyDisplayedInFrame()) 715 return; 716 717 Document* sourceDocument = source->document(); 718 719 // Compute the target origin. We need to do this synchronously in order 720 // to generate the SyntaxError exception correctly. 721 RefPtr<SecurityOrigin> target; 722 if (targetOrigin == "/") { 723 if (!sourceDocument) 724 return; 725 target = sourceDocument->securityOrigin(); 726 } else if (targetOrigin != "*") { 727 target = SecurityOrigin::createFromString(targetOrigin); 728 // It doesn't make sense target a postMessage at a unique origin 729 // because there's no way to represent a unique origin in a string. 730 if (target->isUnique()) { 731 es.throwDOMException(SyntaxError); 732 return; 733 } 734 } 735 736 OwnPtr<MessagePortChannelArray> channels = MessagePort::disentanglePorts(ports, es); 737 if (es.hadException()) 738 return; 739 740 // Capture the source of the message. We need to do this synchronously 741 // in order to capture the source of the message correctly. 742 if (!sourceDocument) 743 return; 744 String sourceOrigin = sourceDocument->securityOrigin()->toString(); 745 746 // Capture stack trace only when inspector front-end is loaded as it may be time consuming. 747 RefPtr<ScriptCallStack> stackTrace; 748 if (InspectorInstrumentation::consoleAgentEnabled(sourceDocument)) 749 stackTrace = createScriptCallStack(ScriptCallStack::maxCallStackSizeToCapture, true); 750 751 // Schedule the message. 752 PostMessageTimer* timer = new PostMessageTimer(this, message, sourceOrigin, source, channels.release(), target.get(), stackTrace.release()); 753 timer->startOneShot(0); 754 } 755 756 void DOMWindow::postMessageTimerFired(PassOwnPtr<PostMessageTimer> t) 757 { 758 OwnPtr<PostMessageTimer> timer(t); 759 760 if (!document() || !isCurrentlyDisplayedInFrame()) 761 return; 762 763 RefPtr<MessageEvent> event = timer->event(document()); 764 765 // Give the embedder a chance to intercept this postMessage because this 766 // DOMWindow might be a proxy for another in browsers that support 767 // postMessage calls across WebKit instances. 768 if (m_frame->loader()->client()->willCheckAndDispatchMessageEvent(timer->targetOrigin(), event.get())) 769 return; 770 771 dispatchMessageEventWithOriginCheck(timer->targetOrigin(), event, timer->stackTrace()); 772 } 773 774 void DOMWindow::dispatchMessageEventWithOriginCheck(SecurityOrigin* intendedTargetOrigin, PassRefPtr<Event> event, PassRefPtr<ScriptCallStack> stackTrace) 775 { 776 if (intendedTargetOrigin) { 777 // Check target origin now since the target document may have changed since the timer was scheduled. 778 if (!intendedTargetOrigin->isSameSchemeHostPort(document()->securityOrigin())) { 779 String message = "Unable to post message to " + intendedTargetOrigin->toString() + 780 ". Recipient has origin " + document()->securityOrigin()->toString() + ".\n"; 781 pageConsole()->addMessage(SecurityMessageSource, ErrorMessageLevel, message, stackTrace); 782 return; 783 } 784 } 785 786 dispatchEvent(event); 787 } 788 789 DOMSelection* DOMWindow::getSelection() 790 { 791 if (!isCurrentlyDisplayedInFrame() || !m_frame) 792 return 0; 793 794 return m_frame->document()->getSelection(); 795 } 796 797 Element* DOMWindow::frameElement() const 798 { 799 if (!m_frame) 800 return 0; 801 802 return m_frame->ownerElement(); 803 } 804 805 void DOMWindow::focus(ScriptExecutionContext* context) 806 { 807 if (!m_frame) 808 return; 809 810 Page* page = m_frame->page(); 811 if (!page) 812 return; 813 814 bool allowFocus = WindowFocusAllowedIndicator::windowFocusAllowed(); 815 if (context) { 816 ASSERT(isMainThread()); 817 Document* activeDocument = toDocument(context); 818 if (opener() && opener() != this && activeDocument->domWindow() == opener()) 819 allowFocus = true; 820 } 821 822 // If we're a top level window, bring the window to the front. 823 if (m_frame == page->mainFrame() && allowFocus) 824 page->chrome().focus(); 825 826 if (!m_frame) 827 return; 828 829 m_frame->eventHandler()->focusDocumentView(); 830 } 831 832 void DOMWindow::blur() 833 { 834 } 835 836 void DOMWindow::close(ScriptExecutionContext* context) 837 { 838 if (!m_frame) 839 return; 840 841 Page* page = m_frame->page(); 842 if (!page) 843 return; 844 845 if (m_frame != page->mainFrame()) 846 return; 847 848 if (context) { 849 ASSERT(isMainThread()); 850 Document* activeDocument = toDocument(context); 851 if (!activeDocument) 852 return; 853 854 if (!activeDocument->canNavigate(m_frame)) 855 return; 856 } 857 858 Settings* settings = m_frame->settings(); 859 bool allowScriptsToCloseWindows = settings && settings->allowScriptsToCloseWindows(); 860 861 if (!(page->openedByDOM() || page->backForward()->count() <= 1 || allowScriptsToCloseWindows)) 862 return; 863 864 if (!m_frame->loader()->shouldClose()) 865 return; 866 867 page->chrome().closeWindowSoon(); 868 } 869 870 void DOMWindow::print() 871 { 872 if (!m_frame) 873 return; 874 875 Page* page = m_frame->page(); 876 if (!page) 877 return; 878 879 if (m_frame->loader()->activeDocumentLoader()->isLoading()) { 880 m_shouldPrintWhenFinishedLoading = true; 881 return; 882 } 883 m_shouldPrintWhenFinishedLoading = false; 884 page->chrome().print(m_frame); 885 } 886 887 void DOMWindow::stop() 888 { 889 if (!m_frame) 890 return; 891 892 // We must check whether the load is complete asynchronously, because we might still be parsing 893 // the document until the callstack unwinds. 894 m_frame->loader()->stopForUserCancel(true); 895 } 896 897 void DOMWindow::alert(const String& message) 898 { 899 if (!m_frame) 900 return; 901 902 m_frame->document()->updateStyleIfNeeded(); 903 904 Page* page = m_frame->page(); 905 if (!page) 906 return; 907 908 page->chrome().runJavaScriptAlert(m_frame, message); 909 } 910 911 bool DOMWindow::confirm(const String& message) 912 { 913 if (!m_frame) 914 return false; 915 916 m_frame->document()->updateStyleIfNeeded(); 917 918 Page* page = m_frame->page(); 919 if (!page) 920 return false; 921 922 return page->chrome().runJavaScriptConfirm(m_frame, message); 923 } 924 925 String DOMWindow::prompt(const String& message, const String& defaultValue) 926 { 927 if (!m_frame) 928 return String(); 929 930 m_frame->document()->updateStyleIfNeeded(); 931 932 Page* page = m_frame->page(); 933 if (!page) 934 return String(); 935 936 String returnValue; 937 if (page->chrome().runJavaScriptPrompt(m_frame, message, defaultValue, returnValue)) 938 return returnValue; 939 940 return String(); 941 } 942 943 bool DOMWindow::find(const String& string, bool caseSensitive, bool backwards, bool wrap, bool /*wholeWord*/, bool /*searchInFrames*/, bool /*showDialog*/) const 944 { 945 if (!isCurrentlyDisplayedInFrame()) 946 return false; 947 948 // FIXME (13016): Support wholeWord, searchInFrames and showDialog 949 return m_frame->editor()->findString(string, !backwards, caseSensitive, wrap, false); 950 } 951 952 bool DOMWindow::offscreenBuffering() const 953 { 954 return true; 955 } 956 957 int DOMWindow::outerHeight() const 958 { 959 if (!m_frame) 960 return 0; 961 962 Page* page = m_frame->page(); 963 if (!page) 964 return 0; 965 966 if (page->settings()->reportScreenSizeInPhysicalPixelsQuirk()) 967 return lroundf(page->chrome().windowRect().height() * page->deviceScaleFactor()); 968 return static_cast<int>(page->chrome().windowRect().height()); 969 } 970 971 int DOMWindow::outerWidth() const 972 { 973 if (!m_frame) 974 return 0; 975 976 Page* page = m_frame->page(); 977 if (!page) 978 return 0; 979 980 if (page->settings()->reportScreenSizeInPhysicalPixelsQuirk()) 981 return lroundf(page->chrome().windowRect().width() * page->deviceScaleFactor()); 982 return static_cast<int>(page->chrome().windowRect().width()); 983 } 984 985 int DOMWindow::innerHeight() const 986 { 987 if (!m_frame) 988 return 0; 989 990 FrameView* view = m_frame->view(); 991 if (!view) 992 return 0; 993 994 // FIXME: This is potentially too much work. We really only need to know the dimensions of the parent frame's renderer. 995 if (Frame* parent = m_frame->tree()->parent()) 996 parent->document()->updateLayoutIgnorePendingStylesheets(); 997 998 // If the device height is overridden, do not include the horizontal scrollbar into the innerHeight (since it is absent on the real device). 999 bool includeScrollbars = !InspectorInstrumentation::shouldApplyScreenHeightOverride(m_frame); 1000 return view->mapFromLayoutToCSSUnits(static_cast<int>(view->visibleContentRect(includeScrollbars ? ScrollableArea::IncludeScrollbars : ScrollableArea::ExcludeScrollbars).height())); 1001 } 1002 1003 int DOMWindow::innerWidth() const 1004 { 1005 if (!m_frame) 1006 return 0; 1007 1008 FrameView* view = m_frame->view(); 1009 if (!view) 1010 return 0; 1011 1012 // FIXME: This is potentially too much work. We really only need to know the dimensions of the parent frame's renderer. 1013 if (Frame* parent = m_frame->tree()->parent()) 1014 parent->document()->updateLayoutIgnorePendingStylesheets(); 1015 1016 // If the device width is overridden, do not include the vertical scrollbar into the innerWidth (since it is absent on the real device). 1017 bool includeScrollbars = !InspectorInstrumentation::shouldApplyScreenWidthOverride(m_frame); 1018 return view->mapFromLayoutToCSSUnits(static_cast<int>(view->visibleContentRect(includeScrollbars ? ScrollableArea::IncludeScrollbars : ScrollableArea::ExcludeScrollbars).width())); 1019 } 1020 1021 int DOMWindow::screenX() const 1022 { 1023 if (!m_frame) 1024 return 0; 1025 1026 Page* page = m_frame->page(); 1027 if (!page) 1028 return 0; 1029 1030 if (page->settings()->reportScreenSizeInPhysicalPixelsQuirk()) 1031 return lroundf(page->chrome().windowRect().x() * page->deviceScaleFactor()); 1032 return static_cast<int>(page->chrome().windowRect().x()); 1033 } 1034 1035 int DOMWindow::screenY() const 1036 { 1037 if (!m_frame) 1038 return 0; 1039 1040 Page* page = m_frame->page(); 1041 if (!page) 1042 return 0; 1043 1044 if (page->settings()->reportScreenSizeInPhysicalPixelsQuirk()) 1045 return lroundf(page->chrome().windowRect().y() * page->deviceScaleFactor()); 1046 return static_cast<int>(page->chrome().windowRect().y()); 1047 } 1048 1049 int DOMWindow::scrollX() const 1050 { 1051 if (!m_frame) 1052 return 0; 1053 1054 FrameView* view = m_frame->view(); 1055 if (!view) 1056 return 0; 1057 1058 m_frame->document()->updateLayoutIgnorePendingStylesheets(); 1059 1060 return view->mapFromLayoutToCSSUnits(view->scrollX()); 1061 } 1062 1063 int DOMWindow::scrollY() const 1064 { 1065 if (!m_frame) 1066 return 0; 1067 1068 FrameView* view = m_frame->view(); 1069 if (!view) 1070 return 0; 1071 1072 m_frame->document()->updateLayoutIgnorePendingStylesheets(); 1073 1074 return view->mapFromLayoutToCSSUnits(view->scrollY()); 1075 } 1076 1077 bool DOMWindow::closed() const 1078 { 1079 return !m_frame; 1080 } 1081 1082 unsigned DOMWindow::length() const 1083 { 1084 if (!isCurrentlyDisplayedInFrame()) 1085 return 0; 1086 1087 return m_frame->tree()->scopedChildCount(); 1088 } 1089 1090 String DOMWindow::name() const 1091 { 1092 if (!m_frame) 1093 return String(); 1094 1095 return m_frame->tree()->name(); 1096 } 1097 1098 void DOMWindow::setName(const String& string) 1099 { 1100 if (!m_frame) 1101 return; 1102 1103 m_frame->tree()->setName(string); 1104 m_frame->loader()->client()->didChangeName(string); 1105 } 1106 1107 void DOMWindow::setStatus(const String& string) 1108 { 1109 m_status = string; 1110 1111 if (!m_frame) 1112 return; 1113 1114 Page* page = m_frame->page(); 1115 if (!page) 1116 return; 1117 1118 ASSERT(m_frame->document()); // Client calls shouldn't be made when the frame is in inconsistent state. 1119 page->chrome().setStatusbarText(m_frame, m_status); 1120 } 1121 1122 void DOMWindow::setDefaultStatus(const String& string) 1123 { 1124 m_defaultStatus = string; 1125 1126 if (!m_frame) 1127 return; 1128 1129 Page* page = m_frame->page(); 1130 if (!page) 1131 return; 1132 1133 ASSERT(m_frame->document()); // Client calls shouldn't be made when the frame is in inconsistent state. 1134 page->chrome().setStatusbarText(m_frame, m_defaultStatus); 1135 } 1136 1137 DOMWindow* DOMWindow::self() const 1138 { 1139 if (!m_frame) 1140 return 0; 1141 1142 return m_frame->domWindow(); 1143 } 1144 1145 DOMWindow* DOMWindow::opener() const 1146 { 1147 if (!m_frame) 1148 return 0; 1149 1150 Frame* opener = m_frame->loader()->opener(); 1151 if (!opener) 1152 return 0; 1153 1154 return opener->domWindow(); 1155 } 1156 1157 DOMWindow* DOMWindow::parent() const 1158 { 1159 if (!m_frame) 1160 return 0; 1161 1162 Frame* parent = m_frame->tree()->parent(); 1163 if (parent) 1164 return parent->domWindow(); 1165 1166 return m_frame->domWindow(); 1167 } 1168 1169 DOMWindow* DOMWindow::top() const 1170 { 1171 if (!m_frame) 1172 return 0; 1173 1174 Page* page = m_frame->page(); 1175 if (!page) 1176 return 0; 1177 1178 return m_frame->tree()->top()->domWindow(); 1179 } 1180 1181 Document* DOMWindow::document() const 1182 { 1183 return m_document.get(); 1184 } 1185 1186 PassRefPtr<StyleMedia> DOMWindow::styleMedia() const 1187 { 1188 if (!isCurrentlyDisplayedInFrame()) 1189 return 0; 1190 if (!m_media) 1191 m_media = StyleMedia::create(m_frame); 1192 return m_media.get(); 1193 } 1194 1195 PassRefPtr<CSSStyleDeclaration> DOMWindow::getComputedStyle(Element* elt, const String& pseudoElt) const 1196 { 1197 if (!elt) 1198 return 0; 1199 1200 return CSSComputedStyleDeclaration::create(elt, false, pseudoElt); 1201 } 1202 1203 PassRefPtr<CSSRuleList> DOMWindow::getMatchedCSSRules(Element* element, const String& pseudoElement, bool authorOnly) const 1204 { 1205 if (!isCurrentlyDisplayedInFrame()) 1206 return 0; 1207 1208 unsigned colonStart = pseudoElement[0] == ':' ? (pseudoElement[1] == ':' ? 2 : 1) : 0; 1209 CSSSelector::PseudoType pseudoType = CSSSelector::parsePseudoType(AtomicString(pseudoElement.substring(colonStart))); 1210 if (pseudoType == CSSSelector::PseudoUnknown && !pseudoElement.isEmpty()) 1211 return 0; 1212 1213 unsigned rulesToInclude = StyleResolver::AuthorCSSRules; 1214 if (!authorOnly) 1215 rulesToInclude |= StyleResolver::UAAndUserCSSRules; 1216 1217 PseudoId pseudoId = CSSSelector::pseudoId(pseudoType); 1218 1219 return m_frame->document()->styleResolver()->pseudoStyleRulesForElement(element, pseudoId, rulesToInclude); 1220 } 1221 1222 PassRefPtr<DOMPoint> DOMWindow::webkitConvertPointFromNodeToPage(Node* node, const DOMPoint* p) const 1223 { 1224 if (!node || !p) 1225 return 0; 1226 1227 if (!document()) 1228 return 0; 1229 1230 document()->updateLayoutIgnorePendingStylesheets(); 1231 1232 FloatPoint pagePoint(p->x(), p->y()); 1233 pagePoint = node->convertToPage(pagePoint); 1234 return DOMPoint::create(pagePoint.x(), pagePoint.y()); 1235 } 1236 1237 PassRefPtr<DOMPoint> DOMWindow::webkitConvertPointFromPageToNode(Node* node, const DOMPoint* p) const 1238 { 1239 if (!node || !p) 1240 return 0; 1241 1242 if (!document()) 1243 return 0; 1244 1245 document()->updateLayoutIgnorePendingStylesheets(); 1246 1247 FloatPoint nodePoint(p->x(), p->y()); 1248 nodePoint = node->convertFromPage(nodePoint); 1249 return DOMPoint::create(nodePoint.x(), nodePoint.y()); 1250 } 1251 1252 double DOMWindow::devicePixelRatio() const 1253 { 1254 if (!m_frame) 1255 return 0.0; 1256 1257 Page* page = m_frame->page(); 1258 if (!page) 1259 return 0.0; 1260 1261 return page->deviceScaleFactor(); 1262 } 1263 1264 void DOMWindow::scrollBy(int x, int y) const 1265 { 1266 if (!isCurrentlyDisplayedInFrame()) 1267 return; 1268 1269 document()->updateLayoutIgnorePendingStylesheets(); 1270 1271 FrameView* view = m_frame->view(); 1272 if (!view) 1273 return; 1274 1275 IntSize scaledOffset(view->mapFromCSSToLayoutUnits(x), view->mapFromCSSToLayoutUnits(y)); 1276 view->scrollBy(scaledOffset); 1277 } 1278 1279 void DOMWindow::scrollTo(int x, int y) const 1280 { 1281 if (!isCurrentlyDisplayedInFrame()) 1282 return; 1283 1284 document()->updateLayoutIgnorePendingStylesheets(); 1285 1286 RefPtr<FrameView> view = m_frame->view(); 1287 if (!view) 1288 return; 1289 1290 IntPoint layoutPos(view->mapFromCSSToLayoutUnits(x), view->mapFromCSSToLayoutUnits(y)); 1291 view->setScrollPosition(layoutPos); 1292 } 1293 1294 void DOMWindow::moveBy(float x, float y) const 1295 { 1296 if (!m_frame) 1297 return; 1298 1299 Page* page = m_frame->page(); 1300 if (!page) 1301 return; 1302 1303 if (m_frame != page->mainFrame()) 1304 return; 1305 1306 FloatRect fr = page->chrome().windowRect(); 1307 FloatRect update = fr; 1308 update.move(x, y); 1309 // Security check (the spec talks about UniversalBrowserWrite to disable this check...) 1310 page->chrome().setWindowRect(adjustWindowRect(page, update)); 1311 } 1312 1313 void DOMWindow::moveTo(float x, float y) const 1314 { 1315 if (!m_frame) 1316 return; 1317 1318 Page* page = m_frame->page(); 1319 if (!page) 1320 return; 1321 1322 if (m_frame != page->mainFrame()) 1323 return; 1324 1325 FloatRect fr = page->chrome().windowRect(); 1326 FloatRect sr = screenAvailableRect(page->mainFrame()->view()); 1327 fr.setLocation(sr.location()); 1328 FloatRect update = fr; 1329 update.move(x, y); 1330 // Security check (the spec talks about UniversalBrowserWrite to disable this check...) 1331 page->chrome().setWindowRect(adjustWindowRect(page, update)); 1332 } 1333 1334 void DOMWindow::resizeBy(float x, float y) const 1335 { 1336 if (!m_frame) 1337 return; 1338 1339 Page* page = m_frame->page(); 1340 if (!page) 1341 return; 1342 1343 if (m_frame != page->mainFrame()) 1344 return; 1345 1346 FloatRect fr = page->chrome().windowRect(); 1347 FloatSize dest = fr.size() + FloatSize(x, y); 1348 FloatRect update(fr.location(), dest); 1349 page->chrome().setWindowRect(adjustWindowRect(page, update)); 1350 } 1351 1352 void DOMWindow::resizeTo(float width, float height) const 1353 { 1354 if (!m_frame) 1355 return; 1356 1357 Page* page = m_frame->page(); 1358 if (!page) 1359 return; 1360 1361 if (m_frame != page->mainFrame()) 1362 return; 1363 1364 FloatRect fr = page->chrome().windowRect(); 1365 FloatSize dest = FloatSize(width, height); 1366 FloatRect update(fr.location(), dest); 1367 page->chrome().setWindowRect(adjustWindowRect(page, update)); 1368 } 1369 1370 int DOMWindow::requestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback) 1371 { 1372 callback->m_useLegacyTimeBase = false; 1373 if (Document* d = document()) 1374 return d->requestAnimationFrame(callback); 1375 return 0; 1376 } 1377 1378 int DOMWindow::webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback) 1379 { 1380 callback->m_useLegacyTimeBase = true; 1381 if (Document* d = document()) 1382 return d->requestAnimationFrame(callback); 1383 return 0; 1384 } 1385 1386 void DOMWindow::cancelAnimationFrame(int id) 1387 { 1388 if (Document* d = document()) 1389 d->cancelAnimationFrame(id); 1390 } 1391 1392 DOMWindowCSS* DOMWindow::css() 1393 { 1394 if (!m_css) 1395 m_css = DOMWindowCSS::create(); 1396 return m_css.get(); 1397 } 1398 1399 static void didAddStorageEventListener(DOMWindow* window) 1400 { 1401 // Creating these WebCore::Storage objects informs the system that we'd like to receive 1402 // notifications about storage events that might be triggered in other processes. Rather 1403 // than subscribe to these notifications explicitly, we subscribe to them implicitly to 1404 // simplify the work done by the system. 1405 window->localStorage(IGNORE_EXCEPTION); 1406 window->sessionStorage(IGNORE_EXCEPTION); 1407 } 1408 1409 bool DOMWindow::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) 1410 { 1411 if (!EventTarget::addEventListener(eventType, listener, useCapture)) 1412 return false; 1413 1414 if (Document* document = this->document()) { 1415 document->addListenerTypeIfNeeded(eventType); 1416 if (eventType == eventNames().mousewheelEvent) 1417 document->didAddWheelEventHandler(); 1418 else if (eventNames().isTouchEventType(eventType)) 1419 document->didAddTouchEventHandler(document); 1420 else if (eventType == eventNames().storageEvent) 1421 didAddStorageEventListener(this); 1422 } 1423 1424 if (eventType == eventNames().unloadEvent) { 1425 addUnloadEventListener(this); 1426 } else if (eventType == eventNames().beforeunloadEvent) { 1427 if (allowsBeforeUnloadListeners(this)) { 1428 // This is confusingly named. It doesn't actually add the listener. It just increments a count 1429 // so that we know we have listeners registered for the purposes of determining if we can 1430 // fast terminate the renderer process. 1431 addBeforeUnloadEventListener(this); 1432 } else { 1433 // Subframes return false from allowsBeforeUnloadListeners. 1434 UseCounter::count(this, UseCounter::SubFrameBeforeUnloadRegistered); 1435 } 1436 } else if (eventType == eventNames().devicemotionEvent && RuntimeEnabledFeatures::deviceMotionEnabled()) { 1437 if (DeviceMotionController* controller = DeviceMotionController::from(document())) 1438 controller->startUpdating(); 1439 } else if (eventType == eventNames().deviceorientationEvent && RuntimeEnabledFeatures::deviceOrientationEnabled()) { 1440 if (DeviceOrientationController* controller = DeviceOrientationController::from(page())) 1441 controller->addDeviceEventListener(this); 1442 } 1443 1444 return true; 1445 } 1446 1447 bool DOMWindow::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) 1448 { 1449 if (!EventTarget::removeEventListener(eventType, listener, useCapture)) 1450 return false; 1451 1452 if (Document* document = this->document()) { 1453 if (eventType == eventNames().mousewheelEvent) 1454 document->didRemoveWheelEventHandler(); 1455 else if (eventNames().isTouchEventType(eventType)) 1456 document->didRemoveTouchEventHandler(document); 1457 } 1458 1459 if (eventType == eventNames().unloadEvent) 1460 removeUnloadEventListener(this); 1461 else if (eventType == eventNames().beforeunloadEvent && allowsBeforeUnloadListeners(this)) 1462 removeBeforeUnloadEventListener(this); 1463 else if (eventType == eventNames().devicemotionEvent) { 1464 if (DeviceMotionController* controller = DeviceMotionController::from(document())) 1465 controller->stopUpdating(); 1466 } else if (eventType == eventNames().deviceorientationEvent) { 1467 if (DeviceOrientationController* controller = DeviceOrientationController::from(page())) 1468 controller->removeDeviceEventListener(this); 1469 } 1470 1471 return true; 1472 } 1473 1474 void DOMWindow::dispatchLoadEvent() 1475 { 1476 RefPtr<Event> loadEvent(Event::create(eventNames().loadEvent, false, false)); 1477 if (m_frame && m_frame->loader()->documentLoader() && !m_frame->loader()->documentLoader()->timing()->loadEventStart()) { 1478 // The DocumentLoader (and thus its DocumentLoadTiming) might get destroyed while dispatching 1479 // the event, so protect it to prevent writing the end time into freed memory. 1480 RefPtr<DocumentLoader> documentLoader = m_frame->loader()->documentLoader(); 1481 DocumentLoadTiming* timing = documentLoader->timing(); 1482 timing->markLoadEventStart(); 1483 dispatchEvent(loadEvent, document()); 1484 timing->markLoadEventEnd(); 1485 } else 1486 dispatchEvent(loadEvent, document()); 1487 1488 // For load events, send a separate load event to the enclosing frame only. 1489 // This is a DOM extension and is independent of bubbling/capturing rules of 1490 // the DOM. 1491 Element* ownerElement = m_frame ? m_frame->ownerElement() : 0; 1492 if (ownerElement) 1493 ownerElement->dispatchEvent(Event::create(eventNames().loadEvent, false, false)); 1494 1495 InspectorInstrumentation::loadEventFired(frame()); 1496 } 1497 1498 bool DOMWindow::dispatchEvent(PassRefPtr<Event> prpEvent, PassRefPtr<EventTarget> prpTarget) 1499 { 1500 RefPtr<EventTarget> protect = this; 1501 RefPtr<Event> event = prpEvent; 1502 1503 event->setTarget(prpTarget ? prpTarget : this); 1504 event->setCurrentTarget(this); 1505 event->setEventPhase(Event::AT_TARGET); 1506 1507 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchEventOnWindow(frame(), *event, this); 1508 1509 bool result = fireEventListeners(event.get()); 1510 1511 InspectorInstrumentation::didDispatchEventOnWindow(cookie); 1512 1513 return result; 1514 } 1515 1516 void DOMWindow::removeAllEventListeners() 1517 { 1518 EventTarget::removeAllEventListeners(); 1519 1520 if (DeviceMotionController* controller = DeviceMotionController::from(document())) 1521 controller->stopUpdating(); 1522 if (DeviceOrientationController* controller = DeviceOrientationController::from(page())) 1523 controller->removeAllDeviceEventListeners(this); 1524 if (Document* document = this->document()) 1525 document->didRemoveEventTargetNode(document); 1526 1527 removeAllUnloadEventListeners(this); 1528 removeAllBeforeUnloadEventListeners(this); 1529 } 1530 1531 void DOMWindow::finishedLoading() 1532 { 1533 if (m_shouldPrintWhenFinishedLoading) { 1534 m_shouldPrintWhenFinishedLoading = false; 1535 print(); 1536 } 1537 } 1538 1539 EventTargetData* DOMWindow::eventTargetData() 1540 { 1541 return &m_eventTargetData; 1542 } 1543 1544 EventTargetData* DOMWindow::ensureEventTargetData() 1545 { 1546 return &m_eventTargetData; 1547 } 1548 1549 void DOMWindow::setLocation(const String& urlString, DOMWindow* activeWindow, DOMWindow* firstWindow, SetLocationLocking locking) 1550 { 1551 if (!isCurrentlyDisplayedInFrame()) 1552 return; 1553 1554 Document* activeDocument = activeWindow->document(); 1555 if (!activeDocument) 1556 return; 1557 1558 if (!activeDocument->canNavigate(m_frame)) 1559 return; 1560 1561 Frame* firstFrame = firstWindow->frame(); 1562 if (!firstFrame) 1563 return; 1564 1565 KURL completedURL = firstFrame->document()->completeURL(urlString); 1566 if (firstFrame->loader()->client()->shouldAbortNavigationAfterUrlResolve(firstFrame->document()->baseURI(), urlString, completedURL)) 1567 return; 1568 1569 if (completedURL.isNull()) 1570 return; 1571 1572 if (isInsecureScriptAccess(activeWindow, completedURL)) 1573 return; 1574 1575 // We want a new history item if we are processing a user gesture. 1576 m_frame->navigationScheduler()->scheduleLocationChange(activeDocument->securityOrigin(), 1577 // FIXME: What if activeDocument()->frame() is 0? 1578 completedURL, activeDocument->frame()->loader()->outgoingReferrer(), 1579 locking != LockHistoryBasedOnGestureState); 1580 } 1581 1582 void DOMWindow::printErrorMessage(const String& message) 1583 { 1584 if (message.isEmpty()) 1585 return; 1586 1587 pageConsole()->addMessage(JSMessageSource, ErrorMessageLevel, message); 1588 } 1589 1590 String DOMWindow::crossDomainAccessErrorMessage(DOMWindow* activeWindow) 1591 { 1592 const KURL& activeWindowURL = activeWindow->document()->url(); 1593 if (activeWindowURL.isNull()) 1594 return String(); 1595 1596 ASSERT(!activeWindow->document()->securityOrigin()->canAccess(document()->securityOrigin())); 1597 1598 // FIXME: This message, and other console messages, have extra newlines. Should remove them. 1599 SecurityOrigin* activeOrigin = activeWindow->document()->securityOrigin(); 1600 SecurityOrigin* targetOrigin = document()->securityOrigin(); 1601 String message = "Blocked a frame with origin \"" + activeOrigin->toString() + "\" from accessing a frame with origin \"" + targetOrigin->toString() + "\". "; 1602 1603 // Sandbox errors: Use the origin of the frames' location, rather than their actual origin (since we know that at least one will be "null"). 1604 KURL activeURL = activeWindow->document()->url(); 1605 KURL targetURL = document()->url(); 1606 if (document()->isSandboxed(SandboxOrigin) || activeWindow->document()->isSandboxed(SandboxOrigin)) { 1607 message = "Blocked a frame at \"" + SecurityOrigin::create(activeURL)->toString() + "\" from accessing a frame at \"" + SecurityOrigin::create(targetURL)->toString() + "\". "; 1608 if (document()->isSandboxed(SandboxOrigin) && activeWindow->document()->isSandboxed(SandboxOrigin)) 1609 return "Sandbox access violation: " + message + " Both frames are sandboxed and lack the \"allow-same-origin\" flag."; 1610 if (document()->isSandboxed(SandboxOrigin)) 1611 return "Sandbox access violation: " + message + " The frame being accessed is sandboxed and lacks the \"allow-same-origin\" flag."; 1612 return "Sandbox access violation: " + message + " The frame requesting access is sandboxed and lacks the \"allow-same-origin\" flag."; 1613 } 1614 1615 // 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:'. 1616 if (targetOrigin->protocol() != activeOrigin->protocol()) 1617 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"; 1618 1619 // 'document.domain' errors. 1620 if (targetOrigin->domainWasSetInDOM() && activeOrigin->domainWasSetInDOM()) 1621 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."; 1622 if (activeOrigin->domainWasSetInDOM()) 1623 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."; 1624 if (targetOrigin->domainWasSetInDOM()) 1625 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."; 1626 1627 // Default. 1628 return message + "Protocols, domains, and ports must match."; 1629 } 1630 1631 bool DOMWindow::isInsecureScriptAccess(DOMWindow* activeWindow, const String& urlString) 1632 { 1633 if (!protocolIsJavaScript(urlString)) 1634 return false; 1635 1636 // If this DOMWindow isn't currently active in the Frame, then there's no 1637 // way we should allow the access. 1638 // FIXME: Remove this check if we're able to disconnect DOMWindow from 1639 // Frame on navigation: https://bugs.webkit.org/show_bug.cgi?id=62054 1640 if (isCurrentlyDisplayedInFrame()) { 1641 // FIXME: Is there some way to eliminate the need for a separate "activeWindow == this" check? 1642 if (activeWindow == this) 1643 return false; 1644 1645 // FIXME: The name canAccess seems to be a roundabout way to ask "can execute script". 1646 // Can we name the SecurityOrigin function better to make this more clear? 1647 if (activeWindow->document()->securityOrigin()->canAccess(document()->securityOrigin())) 1648 return false; 1649 } 1650 1651 printErrorMessage(crossDomainAccessErrorMessage(activeWindow)); 1652 return true; 1653 } 1654 1655 PassRefPtr<DOMWindow> DOMWindow::open(const String& urlString, const AtomicString& frameName, const String& windowFeaturesString, 1656 DOMWindow* activeWindow, DOMWindow* firstWindow) 1657 { 1658 if (!isCurrentlyDisplayedInFrame()) 1659 return 0; 1660 Document* activeDocument = activeWindow->document(); 1661 if (!activeDocument) 1662 return 0; 1663 Frame* firstFrame = firstWindow->frame(); 1664 if (!firstFrame) 1665 return 0; 1666 1667 if (!firstWindow->allowPopUp()) { 1668 // Because FrameTree::find() returns true for empty strings, we must check for empty frame names. 1669 // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker. 1670 if (frameName.isEmpty() || !m_frame->tree()->find(frameName)) 1671 return 0; 1672 } 1673 1674 // Get the target frame for the special cases of _top and _parent. 1675 // In those cases, we schedule a location change right now and return early. 1676 Frame* targetFrame = 0; 1677 if (frameName == "_top") 1678 targetFrame = m_frame->tree()->top(); 1679 else if (frameName == "_parent") { 1680 if (Frame* parent = m_frame->tree()->parent()) 1681 targetFrame = parent; 1682 else 1683 targetFrame = m_frame; 1684 } 1685 if (targetFrame) { 1686 if (!activeDocument->canNavigate(targetFrame)) 1687 return 0; 1688 1689 KURL completedURL = firstFrame->document()->completeURL(urlString); 1690 1691 if (targetFrame->domWindow()->isInsecureScriptAccess(activeWindow, completedURL)) 1692 return targetFrame->domWindow(); 1693 1694 if (urlString.isEmpty()) 1695 return targetFrame->domWindow(); 1696 1697 // For whatever reason, Firefox uses the first window rather than the active window to 1698 // determine the outgoing referrer. We replicate that behavior here. 1699 targetFrame->navigationScheduler()->scheduleLocationChange( 1700 activeDocument->securityOrigin(), 1701 completedURL, 1702 firstFrame->loader()->outgoingReferrer(), 1703 false); 1704 return targetFrame->domWindow(); 1705 } 1706 1707 WindowFeatures windowFeatures(windowFeaturesString); 1708 Frame* result = createWindow(urlString, frameName, windowFeatures, activeWindow, firstFrame, m_frame); 1709 return result ? result->domWindow() : 0; 1710 } 1711 1712 void DOMWindow::showModalDialog(const String& urlString, const String& dialogFeaturesString, 1713 DOMWindow* activeWindow, DOMWindow* firstWindow, PrepareDialogFunction function, void* functionContext) 1714 { 1715 if (!isCurrentlyDisplayedInFrame()) 1716 return; 1717 Frame* activeFrame = activeWindow->frame(); 1718 if (!activeFrame) 1719 return; 1720 Frame* firstFrame = firstWindow->frame(); 1721 if (!firstFrame) 1722 return; 1723 1724 if (!canShowModalDialogNow(m_frame) || !firstWindow->allowPopUp()) 1725 return; 1726 1727 WindowFeatures windowFeatures(dialogFeaturesString, screenAvailableRect(m_frame->view())); 1728 Frame* dialogFrame = createWindow(urlString, emptyAtom, windowFeatures, 1729 activeWindow, firstFrame, m_frame, function, functionContext); 1730 if (!dialogFrame) 1731 return; 1732 UserGestureIndicatorDisabler disabler; 1733 dialogFrame->page()->chrome().runModal(); 1734 } 1735 1736 DOMWindow* DOMWindow::anonymousIndexedGetter(uint32_t index) 1737 { 1738 Frame* frame = this->frame(); 1739 if (!frame) 1740 return 0; 1741 1742 Frame* child = frame->tree()->scopedChild(index); 1743 if (child) 1744 return child->domWindow(); 1745 1746 return 0; 1747 } 1748 1749 1750 } // namespace WebCore 1751