| /* |
| * Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. |
| * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "core/frame/LocalDOMWindow.h" |
| |
| #include "bindings/core/v8/ScriptCallStack.h" |
| #include "bindings/core/v8/ScriptController.h" |
| #include "core/css/CSSComputedStyleDeclaration.h" |
| #include "core/css/CSSRuleList.h" |
| #include "core/css/DOMWindowCSS.h" |
| #include "core/css/MediaQueryList.h" |
| #include "core/css/MediaQueryMatcher.h" |
| #include "core/css/StyleMedia.h" |
| #include "core/css/resolver/StyleResolver.h" |
| #include "core/dom/DOMImplementation.h" |
| #include "core/dom/ExecutionContextTask.h" |
| #include "core/dom/FrameRequestCallback.h" |
| #include "core/dom/SandboxFlags.h" |
| #include "core/editing/Editor.h" |
| #include "core/events/DOMWindowEventQueue.h" |
| #include "core/events/HashChangeEvent.h" |
| #include "core/events/MessageEvent.h" |
| #include "core/events/PageTransitionEvent.h" |
| #include "core/events/PopStateEvent.h" |
| #include "core/events/ScopedEventQueue.h" |
| #include "core/frame/BarProp.h" |
| #include "core/frame/Console.h" |
| #include "core/frame/EventHandlerRegistry.h" |
| #include "core/frame/FrameConsole.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/History.h" |
| #include "core/frame/Navigator.h" |
| #include "core/frame/Screen.h" |
| #include "core/frame/ScrollToOptions.h" |
| #include "core/frame/Settings.h" |
| #include "core/frame/SuspendableTimer.h" |
| #include "core/html/HTMLFrameOwnerElement.h" |
| #include "core/input/EventHandler.h" |
| #include "core/inspector/ConsoleMessageStorage.h" |
| #include "core/inspector/InspectorInstrumentation.h" |
| #include "core/loader/DocumentLoader.h" |
| #include "core/loader/FrameLoaderClient.h" |
| #include "core/loader/SinkDocument.h" |
| #include "core/loader/appcache/ApplicationCache.h" |
| #include "core/page/ChromeClient.h" |
| #include "core/page/CreateWindow.h" |
| #include "core/page/Page.h" |
| #include "core/page/WindowFeatures.h" |
| #include "core/page/scrolling/ScrollingCoordinator.h" |
| #include "platform/EventDispatchForbiddenScope.h" |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebFrameScheduler.h" |
| #include "public/platform/WebScreenInfo.h" |
| |
| namespace blink { |
| |
| LocalDOMWindow::WindowFrameObserver::WindowFrameObserver(LocalDOMWindow* window, LocalFrame& frame) |
| : LocalFrameLifecycleObserver(&frame) |
| , m_window(window) |
| { |
| } |
| |
| PassOwnPtrWillBeRawPtr<LocalDOMWindow::WindowFrameObserver> LocalDOMWindow::WindowFrameObserver::create(LocalDOMWindow* window, LocalFrame& frame) |
| { |
| return adoptPtrWillBeNoop(new WindowFrameObserver(window, frame)); |
| } |
| |
| #if !ENABLE(OILPAN) |
| LocalDOMWindow::WindowFrameObserver::~WindowFrameObserver() |
| { |
| } |
| #endif |
| |
| DEFINE_TRACE(LocalDOMWindow::WindowFrameObserver) |
| { |
| visitor->trace(m_window); |
| LocalFrameLifecycleObserver::trace(visitor); |
| } |
| |
| void LocalDOMWindow::WindowFrameObserver::willDetachFrameHost() |
| { |
| m_window->willDetachFrameHost(); |
| } |
| |
| void LocalDOMWindow::WindowFrameObserver::contextDestroyed() |
| { |
| m_window->frameDestroyed(); |
| LocalFrameLifecycleObserver::contextDestroyed(); |
| } |
| |
| class PostMessageTimer final : public NoBaseWillBeGarbageCollectedFinalized<PostMessageTimer>, public SuspendableTimer { |
| WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(PostMessageTimer); |
| public: |
| PostMessageTimer(LocalDOMWindow& window, PassRefPtrWillBeRawPtr<MessageEvent> event, PassRefPtrWillBeRawPtr<LocalDOMWindow> source, SecurityOrigin* targetOrigin, PassRefPtr<ScriptCallStack> stackTrace, UserGestureToken* userGestureToken) |
| : SuspendableTimer(window.document()) |
| , m_event(event) |
| , m_window(&window) |
| , m_targetOrigin(targetOrigin) |
| , m_stackTrace(stackTrace) |
| , m_userGestureToken(userGestureToken) |
| , m_preventDestruction(false) |
| { |
| m_asyncOperationId = InspectorInstrumentation::traceAsyncOperationStarting(executionContext(), "postMessage"); |
| } |
| |
| PassRefPtrWillBeRawPtr<MessageEvent> event() const { return m_event.get(); } |
| SecurityOrigin* targetOrigin() const { return m_targetOrigin.get(); } |
| ScriptCallStack* stackTrace() const { return m_stackTrace.get(); } |
| UserGestureToken* userGestureToken() const { return m_userGestureToken.get(); } |
| void stop() override |
| { |
| SuspendableTimer::stop(); |
| |
| if (!m_preventDestruction) { |
| // Will destroy this object |
| m_window->removePostMessageTimer(this); |
| } |
| } |
| |
| // Eager finalization is needed to promptly stop this timer object. |
| // (see DOMTimer comment for more.) |
| EAGERLY_FINALIZE(); |
| DEFINE_INLINE_VIRTUAL_TRACE() |
| { |
| visitor->trace(m_event); |
| visitor->trace(m_window); |
| SuspendableTimer::trace(visitor); |
| } |
| |
| // TODO(alexclarke): Override timerTaskRunner() to pass in a document specific default task runner. |
| |
| private: |
| void fired() override |
| { |
| InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncOperationCompletedCallbackStarting(executionContext(), m_asyncOperationId); |
| // Prevent calls to stop triggered from the event handler to |
| // kill this object. |
| m_preventDestruction = true; |
| m_window->postMessageTimerFired(this); |
| // Will destroy this object |
| m_window->removePostMessageTimer(this); |
| InspectorInstrumentation::traceAsyncCallbackCompleted(cookie); |
| } |
| |
| RefPtrWillBeMember<MessageEvent> m_event; |
| RawPtrWillBeMember<LocalDOMWindow> m_window; |
| RefPtr<SecurityOrigin> m_targetOrigin; |
| RefPtr<ScriptCallStack> m_stackTrace; |
| RefPtr<UserGestureToken> m_userGestureToken; |
| int m_asyncOperationId; |
| bool m_preventDestruction; |
| }; |
| |
| static void updateSuddenTerminationStatus(LocalDOMWindow* domWindow, bool addedListener, FrameLoaderClient::SuddenTerminationDisablerType disablerType) |
| { |
| Platform::current()->suddenTerminationChanged(!addedListener); |
| if (domWindow->frame() && domWindow->frame()->loader().client()) |
| domWindow->frame()->loader().client()->suddenTerminationDisablerChanged(addedListener, disablerType); |
| } |
| |
| using DOMWindowSet = WillBePersistentHeapHashCountedSet<RawPtrWillBeWeakMember<LocalDOMWindow>>; |
| |
| static DOMWindowSet& windowsWithUnloadEventListeners() |
| { |
| DEFINE_STATIC_LOCAL(DOMWindowSet, windowsWithUnloadEventListeners, ()); |
| return windowsWithUnloadEventListeners; |
| } |
| |
| static DOMWindowSet& windowsWithBeforeUnloadEventListeners() |
| { |
| DEFINE_STATIC_LOCAL(DOMWindowSet, windowsWithBeforeUnloadEventListeners, ()); |
| return windowsWithBeforeUnloadEventListeners; |
| } |
| |
| static void addUnloadEventListener(LocalDOMWindow* domWindow) |
| { |
| DOMWindowSet& set = windowsWithUnloadEventListeners(); |
| if (set.isEmpty()) |
| updateSuddenTerminationStatus(domWindow, true, FrameLoaderClient::UnloadHandler); |
| |
| set.add(domWindow); |
| } |
| |
| static void removeUnloadEventListener(LocalDOMWindow* domWindow) |
| { |
| DOMWindowSet& set = windowsWithUnloadEventListeners(); |
| DOMWindowSet::iterator it = set.find(domWindow); |
| if (it == set.end()) |
| return; |
| set.remove(it); |
| if (set.isEmpty()) |
| updateSuddenTerminationStatus(domWindow, false, FrameLoaderClient::UnloadHandler); |
| } |
| |
| static void removeAllUnloadEventListeners(LocalDOMWindow* domWindow) |
| { |
| DOMWindowSet& set = windowsWithUnloadEventListeners(); |
| DOMWindowSet::iterator it = set.find(domWindow); |
| if (it == set.end()) |
| return; |
| set.removeAll(it); |
| if (set.isEmpty()) |
| updateSuddenTerminationStatus(domWindow, false, FrameLoaderClient::UnloadHandler); |
| } |
| |
| static void addBeforeUnloadEventListener(LocalDOMWindow* domWindow) |
| { |
| DOMWindowSet& set = windowsWithBeforeUnloadEventListeners(); |
| if (set.isEmpty()) |
| updateSuddenTerminationStatus(domWindow, true, FrameLoaderClient::BeforeUnloadHandler); |
| |
| set.add(domWindow); |
| } |
| |
| static void removeBeforeUnloadEventListener(LocalDOMWindow* domWindow) |
| { |
| DOMWindowSet& set = windowsWithBeforeUnloadEventListeners(); |
| DOMWindowSet::iterator it = set.find(domWindow); |
| if (it == set.end()) |
| return; |
| set.remove(it); |
| if (set.isEmpty()) |
| updateSuddenTerminationStatus(domWindow, false, FrameLoaderClient::BeforeUnloadHandler); |
| } |
| |
| static void removeAllBeforeUnloadEventListeners(LocalDOMWindow* domWindow) |
| { |
| DOMWindowSet& set = windowsWithBeforeUnloadEventListeners(); |
| DOMWindowSet::iterator it = set.find(domWindow); |
| if (it == set.end()) |
| return; |
| set.removeAll(it); |
| if (set.isEmpty()) |
| updateSuddenTerminationStatus(domWindow, false, FrameLoaderClient::BeforeUnloadHandler); |
| } |
| |
| static bool allowsBeforeUnloadListeners(LocalDOMWindow* window) |
| { |
| ASSERT_ARG(window, window); |
| LocalFrame* frame = window->frame(); |
| if (!frame) |
| return false; |
| return frame->isMainFrame(); |
| } |
| |
| unsigned LocalDOMWindow::pendingUnloadEventListeners() const |
| { |
| return windowsWithUnloadEventListeners().count(const_cast<LocalDOMWindow*>(this)); |
| } |
| |
| bool LocalDOMWindow::allowPopUp(LocalFrame& firstFrame) |
| { |
| if (UserGestureIndicator::processingUserGesture()) |
| return true; |
| |
| Settings* settings = firstFrame.settings(); |
| return settings && settings->javaScriptCanOpenWindowsAutomatically(); |
| } |
| |
| bool LocalDOMWindow::allowPopUp() |
| { |
| return frame() && allowPopUp(*frame()); |
| } |
| |
| LocalDOMWindow::LocalDOMWindow(LocalFrame& frame) |
| : m_frameObserver(WindowFrameObserver::create(this, frame)) |
| , m_shouldPrintWhenFinishedLoading(false) |
| #if ENABLE(ASSERT) |
| , m_hasBeenReset(false) |
| #endif |
| { |
| #if ENABLE(OILPAN) |
| ThreadState::current()->registerPreFinalizer(this); |
| #endif |
| } |
| |
| void LocalDOMWindow::clearDocument() |
| { |
| if (!m_document) |
| return; |
| |
| ASSERT(!m_document->isActive()); |
| |
| // FIXME: This should be part of ActiveDOMObject shutdown |
| clearEventQueue(); |
| |
| m_document->clearDOMWindow(); |
| m_document = nullptr; |
| } |
| |
| void LocalDOMWindow::clearEventQueue() |
| { |
| if (!m_eventQueue) |
| return; |
| m_eventQueue->close(); |
| m_eventQueue.clear(); |
| } |
| |
| void LocalDOMWindow::acceptLanguagesChanged() |
| { |
| if (m_navigator) |
| m_navigator->setLanguagesChanged(); |
| |
| dispatchEvent(Event::create(EventTypeNames::languagechange)); |
| } |
| |
| PassRefPtrWillBeRawPtr<Document> LocalDOMWindow::createDocument(const String& mimeType, const DocumentInit& init, bool forceXHTML) |
| { |
| RefPtrWillBeRawPtr<Document> document = nullptr; |
| if (forceXHTML) { |
| // This is a hack for XSLTProcessor. See XSLTProcessor::createDocumentFromSource(). |
| document = Document::create(init); |
| } else { |
| document = DOMImplementation::createDocument(mimeType, init, init.frame() ? init.frame()->inViewSourceMode() : false); |
| if (document->isPluginDocument() && document->isSandboxed(SandboxPlugins)) |
| document = SinkDocument::create(init); |
| } |
| |
| return document.release(); |
| } |
| |
| PassRefPtrWillBeRawPtr<Document> LocalDOMWindow::installNewDocument(const String& mimeType, const DocumentInit& init, bool forceXHTML) |
| { |
| ASSERT(init.frame() == frame()); |
| |
| clearDocument(); |
| |
| m_document = createDocument(mimeType, init, forceXHTML); |
| m_eventQueue = DOMWindowEventQueue::create(m_document.get()); |
| m_document->attach(); |
| |
| if (!frame()) |
| return m_document; |
| |
| frame()->script().updateDocument(); |
| m_document->updateViewportDescription(); |
| |
| if (frame()->page() && frame()->view()) { |
| if (ScrollingCoordinator* scrollingCoordinator = frame()->page()->scrollingCoordinator()) { |
| scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(frame()->view(), HorizontalScrollbar); |
| scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(frame()->view(), VerticalScrollbar); |
| scrollingCoordinator->scrollableAreaScrollLayerDidChange(frame()->view()); |
| } |
| } |
| |
| frame()->selection().updateSecureKeyboardEntryIfActive(); |
| return m_document; |
| } |
| |
| EventQueue* LocalDOMWindow::eventQueue() const |
| { |
| return m_eventQueue.get(); |
| } |
| |
| void LocalDOMWindow::enqueueWindowEvent(PassRefPtrWillBeRawPtr<Event> event) |
| { |
| if (!m_eventQueue) |
| return; |
| event->setTarget(this); |
| m_eventQueue->enqueueEvent(event); |
| } |
| |
| void LocalDOMWindow::enqueueDocumentEvent(PassRefPtrWillBeRawPtr<Event> event) |
| { |
| if (!m_eventQueue) |
| return; |
| event->setTarget(m_document.get()); |
| m_eventQueue->enqueueEvent(event); |
| } |
| |
| void LocalDOMWindow::dispatchWindowLoadEvent() |
| { |
| ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden()); |
| // Delay 'load' event if we are in EventQueueScope. This is a short-term |
| // workaround to avoid Editing code crashes. We should always dispatch |
| // 'load' event asynchronously. crbug.com/569511. |
| if (ScopedEventQueue::instance()->shouldQueueEvents() && m_document) { |
| m_document->postTask(BLINK_FROM_HERE, createSameThreadTask(&LocalDOMWindow::dispatchLoadEvent, PassRefPtrWillBeRawPtr<LocalDOMWindow>(this))); |
| return; |
| } |
| dispatchLoadEvent(); |
| } |
| |
| void LocalDOMWindow::documentWasClosed() |
| { |
| dispatchWindowLoadEvent(); |
| enqueuePageshowEvent(PageshowEventNotPersisted); |
| if (m_pendingStateObject) |
| enqueuePopstateEvent(m_pendingStateObject.release()); |
| } |
| |
| void LocalDOMWindow::enqueuePageshowEvent(PageshowEventPersistence persisted) |
| { |
| // FIXME: https://bugs.webkit.org/show_bug.cgi?id=36334 Pageshow event needs to fire asynchronously. |
| // As per spec pageshow must be triggered asynchronously. |
| // However to be compatible with other browsers blink fires pageshow synchronously. |
| dispatchEvent(PageTransitionEvent::create(EventTypeNames::pageshow, persisted), m_document.get()); |
| } |
| |
| void LocalDOMWindow::enqueueHashchangeEvent(const String& oldURL, const String& newURL) |
| { |
| enqueueWindowEvent(HashChangeEvent::create(oldURL, newURL)); |
| } |
| |
| void LocalDOMWindow::enqueuePopstateEvent(PassRefPtr<SerializedScriptValue> stateObject) |
| { |
| // FIXME: https://bugs.webkit.org/show_bug.cgi?id=36202 Popstate event needs to fire asynchronously |
| dispatchEvent(PopStateEvent::create(stateObject, history())); |
| } |
| |
| void LocalDOMWindow::statePopped(PassRefPtr<SerializedScriptValue> stateObject) |
| { |
| if (!frame()) |
| return; |
| |
| // Per step 11 of section 6.5.9 (history traversal) of the HTML5 spec, we |
| // defer firing of popstate until we're in the complete state. |
| if (document()->isLoadCompleted()) |
| enqueuePopstateEvent(stateObject); |
| else |
| m_pendingStateObject = stateObject; |
| } |
| |
| LocalDOMWindow::~LocalDOMWindow() |
| { |
| #if ENABLE(OILPAN) |
| // Cleared when detaching document. |
| ASSERT(!m_eventQueue); |
| #else |
| ASSERT(m_hasBeenReset); |
| ASSERT(m_document->isStopped()); |
| clearDocument(); |
| #endif |
| } |
| |
| void LocalDOMWindow::dispose() |
| { |
| // Oilpan: should the LocalDOMWindow be GCed along with its LocalFrame without the |
| // frame having first notified its observers of imminent destruction, the |
| // LocalDOMWindow will not have had an opportunity to remove event listeners. |
| // |
| // Arrange for that removal to happen using a prefinalizer action. Making LocalDOMWindow |
| // eager finalizable is problematic as other eagerly finalized objects may well |
| // want to access their associated LocalDOMWindow from their destructors. |
| // |
| // (Non-Oilpan, LocalDOMWindow::reset() will always be invoked, the last opportunity |
| // being via ~LocalFrame's setDOMWindow() call. Asserted for in the destructor.) |
| if (!frame()) |
| return; |
| |
| removeAllEventListeners(); |
| } |
| |
| ExecutionContext* LocalDOMWindow::executionContext() const |
| { |
| return m_document.get(); |
| } |
| |
| const LocalDOMWindow* LocalDOMWindow::toDOMWindow() const |
| { |
| return this; |
| } |
| |
| LocalDOMWindow* LocalDOMWindow::toDOMWindow() |
| { |
| return this; |
| } |
| |
| PassRefPtrWillBeRawPtr<MediaQueryList> LocalDOMWindow::matchMedia(const String& media) |
| { |
| return document() ? document()->mediaQueryMatcher().matchMedia(media) : nullptr; |
| } |
| |
| void LocalDOMWindow::willDetachFrameHost() |
| { |
| frame()->host()->eventHandlerRegistry().didRemoveAllEventHandlers(*this); |
| frame()->host()->consoleMessageStorage().frameWindowDiscarded(this); |
| LocalDOMWindow::notifyContextDestroyed(); |
| } |
| |
| void LocalDOMWindow::frameDestroyed() |
| { |
| willDestroyDocumentInFrame(); |
| resetLocation(); |
| m_properties.clear(); |
| removeAllEventListeners(); |
| } |
| |
| void LocalDOMWindow::willDestroyDocumentInFrame() |
| { |
| for (const auto& domWindowProperty : m_properties) |
| domWindowProperty->willDestroyGlobalObjectInFrame(); |
| } |
| |
| void LocalDOMWindow::willDetachDocumentFromFrame() |
| { |
| for (const auto& domWindowProperty : m_properties) |
| domWindowProperty->willDetachGlobalObjectFromFrame(); |
| } |
| |
| void LocalDOMWindow::registerProperty(DOMWindowProperty* property) |
| { |
| m_properties.add(property); |
| } |
| |
| void LocalDOMWindow::unregisterProperty(DOMWindowProperty* property) |
| { |
| m_properties.remove(property); |
| } |
| |
| void LocalDOMWindow::reset() |
| { |
| m_frameObserver->contextDestroyed(); |
| |
| m_screen = nullptr; |
| m_history = nullptr; |
| m_locationbar = nullptr; |
| m_menubar = nullptr; |
| m_personalbar = nullptr; |
| m_scrollbars = nullptr; |
| m_statusbar = nullptr; |
| m_toolbar = nullptr; |
| m_console = nullptr; |
| m_navigator = nullptr; |
| m_media = nullptr; |
| m_applicationCache = nullptr; |
| #if ENABLE(ASSERT) |
| m_hasBeenReset = true; |
| #endif |
| |
| LocalDOMWindow::notifyContextDestroyed(); |
| } |
| |
| void LocalDOMWindow::sendOrientationChangeEvent() |
| { |
| ASSERT(RuntimeEnabledFeatures::orientationEventEnabled()); |
| ASSERT(frame()->isMainFrame()); |
| |
| // Before dispatching the event, build a list of all frames in the page |
| // to send the event to, to mitigate side effects from event handlers |
| // potentially interfering with others. |
| WillBeHeapVector<RefPtrWillBeMember<Frame>> frames; |
| for (Frame* f = frame(); f; f = f->tree().traverseNext()) |
| frames.append(f); |
| |
| for (size_t i = 0; i < frames.size(); ++i) { |
| if (!frames[i]->isLocalFrame()) |
| continue; |
| toLocalFrame(frames[i].get())->localDOMWindow()->dispatchEvent(Event::create(EventTypeNames::orientationchange)); |
| } |
| } |
| |
| int LocalDOMWindow::orientation() const |
| { |
| ASSERT(RuntimeEnabledFeatures::orientationEventEnabled()); |
| |
| if (!frame() || !frame()->host()) |
| return 0; |
| |
| int orientation = frame()->host()->chromeClient().screenInfo().orientationAngle; |
| // For backward compatibility, we want to return a value in the range of |
| // [-90; 180] instead of [0; 360[ because window.orientation used to behave |
| // like that in WebKit (this is a WebKit proprietary API). |
| if (orientation == 270) |
| return -90; |
| return orientation; |
| } |
| |
| Screen* LocalDOMWindow::screen() const |
| { |
| if (!m_screen) |
| m_screen = Screen::create(frame()); |
| return m_screen.get(); |
| } |
| |
| History* LocalDOMWindow::history() const |
| { |
| if (!m_history) |
| m_history = History::create(frame()); |
| return m_history.get(); |
| } |
| |
| BarProp* LocalDOMWindow::locationbar() const |
| { |
| if (!m_locationbar) |
| m_locationbar = BarProp::create(frame(), BarProp::Locationbar); |
| return m_locationbar.get(); |
| } |
| |
| BarProp* LocalDOMWindow::menubar() const |
| { |
| if (!m_menubar) |
| m_menubar = BarProp::create(frame(), BarProp::Menubar); |
| return m_menubar.get(); |
| } |
| |
| BarProp* LocalDOMWindow::personalbar() const |
| { |
| if (!m_personalbar) |
| m_personalbar = BarProp::create(frame(), BarProp::Personalbar); |
| return m_personalbar.get(); |
| } |
| |
| BarProp* LocalDOMWindow::scrollbars() const |
| { |
| if (!m_scrollbars) |
| m_scrollbars = BarProp::create(frame(), BarProp::Scrollbars); |
| return m_scrollbars.get(); |
| } |
| |
| BarProp* LocalDOMWindow::statusbar() const |
| { |
| if (!m_statusbar) |
| m_statusbar = BarProp::create(frame(), BarProp::Statusbar); |
| return m_statusbar.get(); |
| } |
| |
| BarProp* LocalDOMWindow::toolbar() const |
| { |
| if (!m_toolbar) |
| m_toolbar = BarProp::create(frame(), BarProp::Toolbar); |
| return m_toolbar.get(); |
| } |
| |
| Console* LocalDOMWindow::console() const |
| { |
| if (!m_console) |
| m_console = Console::create(frame()); |
| return m_console.get(); |
| } |
| |
| FrameConsole* LocalDOMWindow::frameConsole() const |
| { |
| if (!isCurrentlyDisplayedInFrame()) |
| return nullptr; |
| return &frame()->console(); |
| } |
| |
| ApplicationCache* LocalDOMWindow::applicationCache() const |
| { |
| if (!isCurrentlyDisplayedInFrame()) |
| return nullptr; |
| if (!m_applicationCache) |
| m_applicationCache = ApplicationCache::create(frame()); |
| return m_applicationCache.get(); |
| } |
| |
| Navigator* LocalDOMWindow::navigator() const |
| { |
| if (!m_navigator) |
| m_navigator = Navigator::create(frame()); |
| return m_navigator.get(); |
| } |
| |
| void LocalDOMWindow::schedulePostMessage(PassRefPtrWillBeRawPtr<MessageEvent> event, LocalDOMWindow* source, SecurityOrigin* target, PassRefPtr<ScriptCallStack> stackTrace) |
| { |
| // Schedule the message. |
| OwnPtrWillBeRawPtr<PostMessageTimer> timer = adoptPtrWillBeNoop(new PostMessageTimer(*this, event, source, target, stackTrace, UserGestureIndicator::currentToken())); |
| timer->startOneShot(0, BLINK_FROM_HERE); |
| timer->suspendIfNeeded(); |
| m_postMessageTimers.add(timer.release()); |
| } |
| |
| void LocalDOMWindow::postMessageTimerFired(PostMessageTimer* timer) |
| { |
| if (!isCurrentlyDisplayedInFrame()) { |
| return; |
| } |
| |
| RefPtrWillBeRawPtr<MessageEvent> event = timer->event(); |
| |
| UserGestureIndicator gestureIndicator(timer->userGestureToken()); |
| |
| event->entangleMessagePorts(document()); |
| dispatchMessageEventWithOriginCheck(timer->targetOrigin(), event, timer->stackTrace()); |
| } |
| |
| void LocalDOMWindow::removePostMessageTimer(PostMessageTimer* timer) |
| { |
| m_postMessageTimers.remove(timer); |
| } |
| |
| void LocalDOMWindow::dispatchMessageEventWithOriginCheck(SecurityOrigin* intendedTargetOrigin, PassRefPtrWillBeRawPtr<Event> event, PassRefPtr<ScriptCallStack> stackTrace) |
| { |
| if (intendedTargetOrigin) { |
| // Check target origin now since the target document may have changed since the timer was scheduled. |
| if (!intendedTargetOrigin->isSameSchemeHostPortAndSuborigin(document()->securityOrigin())) { |
| String message = ExceptionMessages::failedToExecute("postMessage", "DOMWindow", "The target origin provided ('" + intendedTargetOrigin->toString() + "') does not match the recipient window's origin ('" + document()->securityOrigin()->toString() + "')."); |
| RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, message); |
| consoleMessage->setCallStack(stackTrace); |
| frameConsole()->addMessage(consoleMessage.release()); |
| return; |
| } |
| } |
| |
| dispatchEvent(event); |
| } |
| |
| DOMSelection* LocalDOMWindow::getSelection() |
| { |
| if (!isCurrentlyDisplayedInFrame()) |
| return nullptr; |
| |
| return frame()->document()->getSelection(); |
| } |
| |
| Element* LocalDOMWindow::frameElement() const |
| { |
| if (!(frame() && frame()->owner() && frame()->owner()->isLocal())) |
| return nullptr; |
| |
| return toHTMLFrameOwnerElement(frame()->owner()); |
| } |
| |
| void LocalDOMWindow::blur() |
| { |
| } |
| |
| void LocalDOMWindow::print() |
| { |
| if (!frame()) |
| return; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return; |
| |
| if (frame()->document()->isSandboxed(SandboxModals)) { |
| UseCounter::count(frame()->document(), UseCounter::DialogInSandboxedContext); |
| if (RuntimeEnabledFeatures::sandboxBlocksModalsEnabled()) { |
| frameConsole()->addMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, "Ignored call to 'print()'. The document is sandboxed, and the 'allow-modals' keyword is not set.")); |
| return; |
| } |
| } |
| |
| if (frame()->isLoading()) { |
| m_shouldPrintWhenFinishedLoading = true; |
| return; |
| } |
| m_shouldPrintWhenFinishedLoading = false; |
| host->chromeClient().print(frame()); |
| } |
| |
| void LocalDOMWindow::stop() |
| { |
| if (!frame()) |
| return; |
| frame()->loader().stopAllLoaders(); |
| } |
| |
| void LocalDOMWindow::alert(const String& message) |
| { |
| if (!frame()) |
| return; |
| |
| if (frame()->document()->isSandboxed(SandboxModals)) { |
| UseCounter::count(frame()->document(), UseCounter::DialogInSandboxedContext); |
| if (RuntimeEnabledFeatures::sandboxBlocksModalsEnabled()) { |
| frameConsole()->addMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, "Ignored call to 'alert()'. The document is sandboxed, and the 'allow-modals' keyword is not set.")); |
| return; |
| } |
| } |
| |
| frame()->document()->updateLayoutTreeIfNeeded(); |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return; |
| |
| host->chromeClient().openJavaScriptAlert(frame(), message); |
| } |
| |
| bool LocalDOMWindow::confirm(const String& message) |
| { |
| if (!frame()) |
| return false; |
| |
| if (frame()->document()->isSandboxed(SandboxModals)) { |
| UseCounter::count(frame()->document(), UseCounter::DialogInSandboxedContext); |
| if (RuntimeEnabledFeatures::sandboxBlocksModalsEnabled()) { |
| frameConsole()->addMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, "Ignored call to 'confirm()'. The document is sandboxed, and the 'allow-modals' keyword is not set.")); |
| return false; |
| } |
| } |
| |
| frame()->document()->updateLayoutTreeIfNeeded(); |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return false; |
| |
| return host->chromeClient().openJavaScriptConfirm(frame(), message); |
| } |
| |
| String LocalDOMWindow::prompt(const String& message, const String& defaultValue) |
| { |
| if (!frame()) |
| return String(); |
| |
| if (frame()->document()->isSandboxed(SandboxModals)) { |
| UseCounter::count(frame()->document(), UseCounter::DialogInSandboxedContext); |
| if (RuntimeEnabledFeatures::sandboxBlocksModalsEnabled()) { |
| frameConsole()->addMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, "Ignored call to 'prompt()'. The document is sandboxed, and the 'allow-modals' keyword is not set.")); |
| return String(); |
| } |
| } |
| |
| frame()->document()->updateLayoutTreeIfNeeded(); |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return String(); |
| |
| String returnValue; |
| if (host->chromeClient().openJavaScriptPrompt(frame(), message, defaultValue, returnValue)) |
| return returnValue; |
| |
| return String(); |
| } |
| |
| bool LocalDOMWindow::find(const String& string, bool caseSensitive, bool backwards, bool wrap, bool wholeWord, bool /*searchInFrames*/, bool /*showDialog*/) const |
| { |
| if (!isCurrentlyDisplayedInFrame()) |
| return false; |
| |
| // |frame()| can be destructed during |Editor::findString()| via |
| // |Document::updateLayout()|, e.g. event handler removes a frame. |
| RefPtrWillBeRawPtr<LocalFrame> protectFrame(frame()); |
| |
| // FIXME (13016): Support searchInFrames and showDialog |
| FindOptions options = (backwards ? Backwards : 0) | (caseSensitive ? 0 : CaseInsensitive) | (wrap ? WrapAround : 0) | (wholeWord ? WholeWord | AtWordStarts : 0); |
| return frame()->editor().findString(string, options); |
| } |
| |
| bool LocalDOMWindow::offscreenBuffering() const |
| { |
| return true; |
| } |
| |
| int LocalDOMWindow::outerHeight() const |
| { |
| if (!frame()) |
| return 0; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return 0; |
| |
| if (host->settings().reportScreenSizeInPhysicalPixelsQuirk()) |
| return lroundf(host->chromeClient().windowRect().height() * host->deviceScaleFactor()); |
| return host->chromeClient().windowRect().height(); |
| } |
| |
| int LocalDOMWindow::outerWidth() const |
| { |
| if (!frame()) |
| return 0; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return 0; |
| |
| if (host->settings().reportScreenSizeInPhysicalPixelsQuirk()) |
| return lroundf(host->chromeClient().windowRect().width() * host->deviceScaleFactor()); |
| return host->chromeClient().windowRect().width(); |
| } |
| |
| static FloatSize getViewportSize(LocalFrame* frame) |
| { |
| FrameView* view = frame->view(); |
| if (!view) |
| return FloatSize(); |
| |
| FrameHost* host = frame->host(); |
| if (!host) |
| return FloatSize(); |
| |
| // The main frame's viewport size depends on the page scale. Since the |
| // initial page scale depends on the content width and is set after a |
| // layout, perform one now so queries during page load will use the up to |
| // date viewport. |
| if (host->settings().viewportEnabled() && frame->isMainFrame()) |
| frame->document()->updateLayoutIgnorePendingStylesheets(); |
| |
| // FIXME: This is potentially too much work. We really only need to know the dimensions of the parent frame's layoutObject. |
| if (Frame* parent = frame->tree().parent()) { |
| if (parent && parent->isLocalFrame()) |
| toLocalFrame(parent)->document()->updateLayoutIgnorePendingStylesheets(); |
| } |
| |
| return frame->isMainFrame() && !host->settings().inertVisualViewport() |
| ? FloatSize(host->visualViewport().visibleRect().size()) |
| : FloatSize(view->visibleContentRect(IncludeScrollbars).size()); |
| } |
| |
| int LocalDOMWindow::innerHeight() const |
| { |
| if (!frame()) |
| return 0; |
| |
| FloatSize viewportSize = getViewportSize(frame()); |
| return adjustForAbsoluteZoom(expandedIntSize(viewportSize).height(), frame()->pageZoomFactor()); |
| } |
| |
| int LocalDOMWindow::innerWidth() const |
| { |
| if (!frame()) |
| return 0; |
| |
| FloatSize viewportSize = getViewportSize(frame()); |
| return adjustForAbsoluteZoom(expandedIntSize(viewportSize).width(), frame()->pageZoomFactor()); |
| } |
| |
| int LocalDOMWindow::screenX() const |
| { |
| if (!frame()) |
| return 0; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return 0; |
| |
| if (host->settings().reportScreenSizeInPhysicalPixelsQuirk()) |
| return lroundf(host->chromeClient().windowRect().x() * host->deviceScaleFactor()); |
| return host->chromeClient().windowRect().x(); |
| } |
| |
| int LocalDOMWindow::screenY() const |
| { |
| if (!frame()) |
| return 0; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return 0; |
| |
| if (host->settings().reportScreenSizeInPhysicalPixelsQuirk()) |
| return lroundf(host->chromeClient().windowRect().y() * host->deviceScaleFactor()); |
| return host->chromeClient().windowRect().y(); |
| } |
| |
| double LocalDOMWindow::scrollX() const |
| { |
| if (!frame()) |
| return 0; |
| |
| FrameView* view = frame()->view(); |
| if (!view) |
| return 0; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return 0; |
| |
| frame()->document()->updateLayoutIgnorePendingStylesheets(); |
| |
| ScrollableArea* viewport = host->settings().inertVisualViewport() ? view->layoutViewportScrollableArea() : view->scrollableArea(); |
| double viewportX = viewport->scrollPositionDouble().x(); |
| return adjustScrollForAbsoluteZoom(viewportX, frame()->pageZoomFactor()); |
| } |
| |
| double LocalDOMWindow::scrollY() const |
| { |
| if (!frame()) |
| return 0; |
| |
| FrameView* view = frame()->view(); |
| if (!view) |
| return 0; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return 0; |
| |
| frame()->document()->updateLayoutIgnorePendingStylesheets(); |
| |
| ScrollableArea* viewport = host->settings().inertVisualViewport() ? view->layoutViewportScrollableArea() : view->scrollableArea(); |
| double viewportY = viewport->scrollPositionDouble().y(); |
| return adjustScrollForAbsoluteZoom(viewportY, frame()->pageZoomFactor()); |
| } |
| |
| const AtomicString& LocalDOMWindow::name() const |
| { |
| if (!isCurrentlyDisplayedInFrame()) |
| return nullAtom; |
| |
| return frame()->tree().name(); |
| } |
| |
| void LocalDOMWindow::setName(const AtomicString& name) |
| { |
| if (!isCurrentlyDisplayedInFrame()) |
| return; |
| |
| frame()->tree().setName(name); |
| ASSERT(frame()->loader().client()); |
| frame()->loader().client()->didChangeName(name); |
| } |
| |
| void LocalDOMWindow::setStatus(const String& string) |
| { |
| m_status = string; |
| |
| if (!frame()) |
| return; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return; |
| |
| ASSERT(frame()->document()); // Client calls shouldn't be made when the frame is in inconsistent state. |
| host->chromeClient().setStatusbarText(m_status); |
| } |
| |
| void LocalDOMWindow::setDefaultStatus(const String& string) |
| { |
| m_defaultStatus = string; |
| |
| if (!frame()) |
| return; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return; |
| |
| ASSERT(frame()->document()); // Client calls shouldn't be made when the frame is in inconsistent state. |
| host->chromeClient().setStatusbarText(m_defaultStatus); |
| } |
| |
| Document* LocalDOMWindow::document() const |
| { |
| return m_document.get(); |
| } |
| |
| StyleMedia* LocalDOMWindow::styleMedia() const |
| { |
| if (!m_media) |
| m_media = StyleMedia::create(frame()); |
| return m_media.get(); |
| } |
| |
| PassRefPtrWillBeRawPtr<CSSStyleDeclaration> LocalDOMWindow::getComputedStyle(Element* elt, const String& pseudoElt) const |
| { |
| ASSERT(elt); |
| return CSSComputedStyleDeclaration::create(elt, false, pseudoElt); |
| } |
| |
| PassRefPtrWillBeRawPtr<CSSRuleList> LocalDOMWindow::getMatchedCSSRules(Element* element, const String& pseudoElement) const |
| { |
| if (!element) |
| return nullptr; |
| |
| if (!isCurrentlyDisplayedInFrame()) |
| return nullptr; |
| |
| unsigned colonStart = pseudoElement[0] == ':' ? (pseudoElement[1] == ':' ? 2 : 1) : 0; |
| CSSSelector::PseudoType pseudoType = CSSSelector::parsePseudoType(AtomicString(pseudoElement.substring(colonStart)), false); |
| if (pseudoType == CSSSelector::PseudoUnknown && !pseudoElement.isEmpty()) |
| return nullptr; |
| |
| unsigned rulesToInclude = StyleResolver::AuthorCSSRules; |
| PseudoId pseudoId = CSSSelector::pseudoId(pseudoType); |
| element->document().updateLayoutTreeIfNeeded(); |
| return frame()->document()->ensureStyleResolver().pseudoCSSRulesForElement(element, pseudoId, rulesToInclude); |
| } |
| |
| double LocalDOMWindow::devicePixelRatio() const |
| { |
| if (!frame()) |
| return 0.0; |
| |
| return frame()->devicePixelRatio(); |
| } |
| |
| void LocalDOMWindow::scrollBy(double x, double y, ScrollBehavior scrollBehavior) const |
| { |
| if (!isCurrentlyDisplayedInFrame()) |
| return; |
| |
| document()->updateLayoutIgnorePendingStylesheets(); |
| |
| FrameView* view = frame()->view(); |
| if (!view) |
| return; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return; |
| |
| x = ScrollableArea::normalizeNonFiniteScroll(x); |
| y = ScrollableArea::normalizeNonFiniteScroll(y); |
| |
| ScrollableArea* viewport = host->settings().inertVisualViewport() ? view->layoutViewportScrollableArea() : view->scrollableArea(); |
| |
| DoublePoint currentOffset = viewport->scrollPositionDouble(); |
| DoubleSize scaledDelta(x * frame()->pageZoomFactor(), y * frame()->pageZoomFactor()); |
| |
| viewport->setScrollPosition(currentOffset + scaledDelta, ProgrammaticScroll, scrollBehavior); |
| } |
| |
| void LocalDOMWindow::scrollBy(const ScrollToOptions& scrollToOptions) const |
| { |
| double x = 0.0; |
| double y = 0.0; |
| if (scrollToOptions.hasLeft()) |
| x = scrollToOptions.left(); |
| if (scrollToOptions.hasTop()) |
| y = scrollToOptions.top(); |
| ScrollBehavior scrollBehavior = ScrollBehaviorAuto; |
| ScrollableArea::scrollBehaviorFromString(scrollToOptions.behavior(), scrollBehavior); |
| scrollBy(x, y, scrollBehavior); |
| } |
| |
| void LocalDOMWindow::scrollTo(double x, double y) const |
| { |
| if (!isCurrentlyDisplayedInFrame()) |
| return; |
| |
| FrameView* view = frame()->view(); |
| if (!view) |
| return; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return; |
| |
| x = ScrollableArea::normalizeNonFiniteScroll(x); |
| y = ScrollableArea::normalizeNonFiniteScroll(y); |
| |
| // It is only necessary to have an up-to-date layout if the position may be clamped, |
| // which is never the case for (0, 0). |
| if (x || y) |
| document()->updateLayoutIgnorePendingStylesheets(); |
| |
| DoublePoint layoutPos(x * frame()->pageZoomFactor(), y * frame()->pageZoomFactor()); |
| ScrollableArea* viewport = host->settings().inertVisualViewport() ? view->layoutViewportScrollableArea() : view->scrollableArea(); |
| viewport->setScrollPosition(layoutPos, ProgrammaticScroll, ScrollBehaviorAuto); |
| } |
| |
| void LocalDOMWindow::scrollTo(const ScrollToOptions& scrollToOptions) const |
| { |
| if (!isCurrentlyDisplayedInFrame()) |
| return; |
| |
| FrameView* view = frame()->view(); |
| if (!view) |
| return; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return; |
| |
| // It is only necessary to have an up-to-date layout if the position may be clamped, |
| // which is never the case for (0, 0). |
| if (!scrollToOptions.hasLeft() |
| || !scrollToOptions.hasTop() |
| || scrollToOptions.left() |
| || scrollToOptions.top()) { |
| document()->updateLayoutIgnorePendingStylesheets(); |
| } |
| |
| double scaledX = 0.0; |
| double scaledY = 0.0; |
| |
| ScrollableArea* viewport = host->settings().inertVisualViewport() ? view->layoutViewportScrollableArea() : view->scrollableArea(); |
| |
| DoublePoint currentOffset = viewport->scrollPositionDouble(); |
| scaledX = currentOffset.x(); |
| scaledY = currentOffset.y(); |
| |
| if (scrollToOptions.hasLeft()) |
| scaledX = ScrollableArea::normalizeNonFiniteScroll(scrollToOptions.left()) * frame()->pageZoomFactor(); |
| |
| if (scrollToOptions.hasTop()) |
| scaledY = ScrollableArea::normalizeNonFiniteScroll(scrollToOptions.top()) * frame()->pageZoomFactor(); |
| |
| ScrollBehavior scrollBehavior = ScrollBehaviorAuto; |
| ScrollableArea::scrollBehaviorFromString(scrollToOptions.behavior(), scrollBehavior); |
| |
| viewport->setScrollPosition(DoublePoint(scaledX, scaledY), ProgrammaticScroll, scrollBehavior); |
| } |
| |
| void LocalDOMWindow::moveBy(int x, int y) const |
| { |
| if (!frame() || !frame()->isMainFrame()) |
| return; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return; |
| |
| IntRect windowRect = host->chromeClient().windowRect(); |
| windowRect.move(x, y); |
| // Security check (the spec talks about UniversalBrowserWrite to disable this check...) |
| host->chromeClient().setWindowRectWithAdjustment(windowRect); |
| } |
| |
| void LocalDOMWindow::moveTo(int x, int y) const |
| { |
| if (!frame() || !frame()->isMainFrame()) |
| return; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return; |
| |
| IntRect windowRect = host->chromeClient().windowRect(); |
| windowRect.setLocation(IntPoint(x, y)); |
| // Security check (the spec talks about UniversalBrowserWrite to disable this check...) |
| host->chromeClient().setWindowRectWithAdjustment(windowRect); |
| } |
| |
| void LocalDOMWindow::resizeBy(int x, int y) const |
| { |
| if (!frame() || !frame()->isMainFrame()) |
| return; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return; |
| |
| IntRect fr = host->chromeClient().windowRect(); |
| IntSize dest = fr.size() + IntSize(x, y); |
| IntRect update(fr.location(), dest); |
| host->chromeClient().setWindowRectWithAdjustment(update); |
| } |
| |
| void LocalDOMWindow::resizeTo(int width, int height) const |
| { |
| if (!frame() || !frame()->isMainFrame()) |
| return; |
| |
| FrameHost* host = frame()->host(); |
| if (!host) |
| return; |
| |
| IntRect fr = host->chromeClient().windowRect(); |
| IntSize dest = IntSize(width, height); |
| IntRect update(fr.location(), dest); |
| host->chromeClient().setWindowRectWithAdjustment(update); |
| } |
| |
| int LocalDOMWindow::requestAnimationFrame(FrameRequestCallback* callback) |
| { |
| callback->m_useLegacyTimeBase = false; |
| if (Document* doc = document()) |
| return doc->requestAnimationFrame(callback); |
| return 0; |
| } |
| |
| int LocalDOMWindow::webkitRequestAnimationFrame(FrameRequestCallback* callback) |
| { |
| callback->m_useLegacyTimeBase = true; |
| if (Document* document = this->document()) |
| return document->requestAnimationFrame(callback); |
| return 0; |
| } |
| |
| void LocalDOMWindow::cancelAnimationFrame(int id) |
| { |
| if (Document* document = this->document()) |
| document->cancelAnimationFrame(id); |
| } |
| |
| int LocalDOMWindow::requestIdleCallback(IdleRequestCallback* callback, const IdleRequestOptions& options) |
| { |
| if (Document* document = this->document()) |
| return document->requestIdleCallback(callback, options); |
| return 0; |
| } |
| |
| void LocalDOMWindow::cancelIdleCallback(int id) |
| { |
| if (Document* document = this->document()) |
| document->cancelIdleCallback(id); |
| } |
| |
| bool LocalDOMWindow::addEventListenerInternal(const AtomicString& eventType, PassRefPtrWillBeRawPtr<EventListener> prpListener, const EventListenerOptions& options) |
| { |
| RefPtrWillBeRawPtr<EventListener> listener = prpListener; |
| if (!EventTarget::addEventListenerInternal(eventType, listener, options)) |
| return false; |
| |
| if (frame() && frame()->host()) |
| frame()->host()->eventHandlerRegistry().didAddEventHandler(*this, eventType, options); |
| |
| if (Document* document = this->document()) { |
| document->addListenerTypeIfNeeded(eventType); |
| } |
| |
| notifyAddEventListener(this, eventType); |
| |
| if (eventType == EventTypeNames::unload) { |
| UseCounter::count(document(), UseCounter::DocumentUnloadRegistered); |
| addUnloadEventListener(this); |
| } else if (eventType == EventTypeNames::beforeunload) { |
| UseCounter::count(document(), UseCounter::DocumentBeforeUnloadRegistered); |
| if (allowsBeforeUnloadListeners(this)) { |
| // This is confusingly named. It doesn't actually add the listener. It just increments a count |
| // so that we know we have listeners registered for the purposes of determining if we can |
| // fast terminate the renderer process. |
| addBeforeUnloadEventListener(this); |
| } else { |
| // Subframes return false from allowsBeforeUnloadListeners. |
| UseCounter::count(document(), UseCounter::SubFrameBeforeUnloadRegistered); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool LocalDOMWindow::removeEventListenerInternal(const AtomicString& eventType, PassRefPtrWillBeRawPtr<EventListener> listener, const EventListenerOptions& options) |
| { |
| if (!EventTarget::removeEventListenerInternal(eventType, listener, options)) |
| return false; |
| |
| if (frame() && frame()->host()) |
| frame()->host()->eventHandlerRegistry().didRemoveEventHandler(*this, eventType, options); |
| |
| notifyRemoveEventListener(this, eventType); |
| |
| if (eventType == EventTypeNames::unload) { |
| removeUnloadEventListener(this); |
| } else if (eventType == EventTypeNames::beforeunload && allowsBeforeUnloadListeners(this)) { |
| removeBeforeUnloadEventListener(this); |
| } |
| |
| return true; |
| } |
| |
| void LocalDOMWindow::dispatchLoadEvent() |
| { |
| RefPtrWillBeRawPtr<Event> loadEvent(Event::create(EventTypeNames::load)); |
| if (frame() && frame()->loader().documentLoader() && !frame()->loader().documentLoader()->timing().loadEventStart()) { |
| // The DocumentLoader (and thus its DocumentLoadTiming) might get destroyed while dispatching |
| // the event, so protect it to prevent writing the end time into freed memory. |
| RefPtrWillBeRawPtr<DocumentLoader> documentLoader = frame()->loader().documentLoader(); |
| DocumentLoadTiming& timing = documentLoader->timing(); |
| timing.markLoadEventStart(); |
| dispatchEvent(loadEvent, document()); |
| timing.markLoadEventEnd(); |
| } else { |
| dispatchEvent(loadEvent, document()); |
| } |
| |
| // For load events, send a separate load event to the enclosing frame only. |
| // This is a DOM extension and is independent of bubbling/capturing rules of |
| // the DOM. |
| FrameOwner* owner = frame() ? frame()->owner() : nullptr; |
| if (owner) |
| owner->dispatchLoad(); |
| |
| TRACE_EVENT_INSTANT1("devtools.timeline", "MarkLoad", TRACE_EVENT_SCOPE_THREAD, "data", InspectorMarkLoadEvent::data(frame())); |
| InspectorInstrumentation::loadEventFired(frame()); |
| } |
| |
| bool LocalDOMWindow::dispatchEvent(PassRefPtrWillBeRawPtr<Event> prpEvent, PassRefPtrWillBeRawPtr<EventTarget> prpTarget) |
| { |
| ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden()); |
| |
| RefPtrWillBeRawPtr<EventTarget> protect(this); |
| RefPtrWillBeRawPtr<Event> event = prpEvent; |
| |
| event->setTrusted(true); |
| event->setTarget(prpTarget ? prpTarget : this); |
| event->setCurrentTarget(this); |
| event->setEventPhase(Event::AT_TARGET); |
| |
| TRACE_EVENT1("devtools.timeline", "EventDispatch", "data", InspectorEventDispatchEvent::data(*event)); |
| return fireEventListeners(event.get()); |
| } |
| |
| void LocalDOMWindow::removeAllEventListeners() |
| { |
| EventTarget::removeAllEventListeners(); |
| |
| notifyRemoveAllEventListeners(this); |
| if (frame() && frame()->host()) |
| frame()->host()->eventHandlerRegistry().didRemoveAllEventHandlers(*this); |
| |
| removeAllUnloadEventListeners(this); |
| removeAllBeforeUnloadEventListeners(this); |
| } |
| |
| void LocalDOMWindow::finishedLoading() |
| { |
| if (m_shouldPrintWhenFinishedLoading) { |
| m_shouldPrintWhenFinishedLoading = false; |
| print(); |
| } |
| } |
| |
| void LocalDOMWindow::printErrorMessage(const String& message) const |
| { |
| if (!isCurrentlyDisplayedInFrame()) |
| return; |
| |
| if (message.isEmpty()) |
| return; |
| |
| frameConsole()->addMessage(ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, message)); |
| } |
| |
| PassRefPtrWillBeRawPtr<DOMWindow> LocalDOMWindow::open(const String& urlString, const AtomicString& frameName, const String& windowFeaturesString, |
| LocalDOMWindow* callingWindow, LocalDOMWindow* enteredWindow) |
| { |
| if (!isCurrentlyDisplayedInFrame()) |
| return nullptr; |
| if (!callingWindow->frame()) |
| return nullptr; |
| Document* activeDocument = callingWindow->document(); |
| if (!activeDocument) |
| return nullptr; |
| LocalFrame* firstFrame = enteredWindow->frame(); |
| if (!firstFrame) |
| return nullptr; |
| |
| UseCounter::count(*activeDocument, UseCounter::DOMWindowOpen); |
| if (!windowFeaturesString.isEmpty()) |
| UseCounter::count(*activeDocument, UseCounter::DOMWindowOpenFeatures); |
| |
| if (!enteredWindow->allowPopUp()) { |
| // Because FrameTree::find() returns true for empty strings, we must check for empty frame names. |
| // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker. |
| if (frameName.isEmpty() || !frame()->tree().find(frameName)) |
| return nullptr; |
| } |
| |
| // Get the target frame for the special cases of _top and _parent. |
| // In those cases, we schedule a location change right now and return early. |
| Frame* targetFrame = nullptr; |
| if (frameName == "_top") { |
| targetFrame = frame()->tree().top(); |
| } else if (frameName == "_parent") { |
| if (Frame* parent = frame()->tree().parent()) |
| targetFrame = parent; |
| else |
| targetFrame = frame(); |
| } |
| |
| if (targetFrame) { |
| if (!activeDocument->frame() || !activeDocument->frame()->canNavigate(*targetFrame)) |
| return nullptr; |
| |
| KURL completedURL = firstFrame->document()->completeURL(urlString); |
| |
| if (targetFrame->domWindow()->isInsecureScriptAccess(*callingWindow, completedURL)) |
| return targetFrame->domWindow(); |
| |
| if (urlString.isEmpty()) |
| return targetFrame->domWindow(); |
| |
| targetFrame->navigate(*activeDocument, completedURL, false, UserGestureStatus::None); |
| return targetFrame->domWindow(); |
| } |
| |
| WindowFeatures features(windowFeaturesString); |
| RefPtrWillBeRawPtr<DOMWindow> newWindow = createWindow(urlString, frameName, features, *callingWindow, *firstFrame, *frame()); |
| return features.noopener ? nullptr : newWindow; |
| } |
| |
| DEFINE_TRACE(LocalDOMWindow) |
| { |
| #if ENABLE(OILPAN) |
| visitor->trace(m_frameObserver); |
| visitor->trace(m_document); |
| visitor->trace(m_properties); |
| visitor->trace(m_screen); |
| visitor->trace(m_history); |
| visitor->trace(m_locationbar); |
| visitor->trace(m_menubar); |
| visitor->trace(m_personalbar); |
| visitor->trace(m_scrollbars); |
| visitor->trace(m_statusbar); |
| visitor->trace(m_toolbar); |
| visitor->trace(m_console); |
| visitor->trace(m_navigator); |
| visitor->trace(m_media); |
| visitor->trace(m_applicationCache); |
| visitor->trace(m_eventQueue); |
| visitor->trace(m_postMessageTimers); |
| HeapSupplementable<LocalDOMWindow>::trace(visitor); |
| #endif |
| DOMWindow::trace(visitor); |
| DOMWindowLifecycleNotifier::trace(visitor); |
| } |
| |
| LocalFrame* LocalDOMWindow::frame() const |
| { |
| // If the LocalDOMWindow still has a frame reference, that frame must point |
| // back to this LocalDOMWindow: otherwise, it's easy to get into a situation |
| // where script execution leaks between different LocalDOMWindows. |
| if (m_frameObserver->frame()) |
| ASSERT_WITH_SECURITY_IMPLICATION(m_frameObserver->frame()->domWindow() == this); |
| return m_frameObserver->frame(); |
| } |
| |
| } // namespace blink |