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