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