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