7bde1cbb8b6371e7a7a08ba551e856b8192f60fc
[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 "JSExecState.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, Event::CanBubble::No, Event::IsCancelable::No), 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, scriptExecutionContext());
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(JSExecState::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     if (!(page->openedByDOM() || page->backForward().count() <= 1)) {
1090         console()->addMessage(MessageSource::JS, MessageLevel::Warning, "Can't close the window since it was not opened by JavaScript"_s);
1091         return;
1092     }
1093
1094     if (!m_frame->loader().shouldClose())
1095         return;
1096
1097     page->setIsClosing();
1098     page->chrome().closeWindowSoon();
1099 }
1100
1101 void DOMWindow::print()
1102 {
1103     if (!m_frame)
1104         return;
1105
1106     auto* page = m_frame->page();
1107     if (!page)
1108         return;
1109
1110     if (!page->arePromptsAllowed()) {
1111         printErrorMessage("Use of window.print is not allowed while unloading a page.");
1112         return;
1113     }
1114
1115     if (m_frame->loader().activeDocumentLoader()->isLoading()) {
1116         m_shouldPrintWhenFinishedLoading = true;
1117         return;
1118     }
1119     m_shouldPrintWhenFinishedLoading = false;
1120     page->chrome().print(*m_frame);
1121 }
1122
1123 void DOMWindow::stop()
1124 {
1125     if (!m_frame)
1126         return;
1127
1128     // We must check whether the load is complete asynchronously, because we might still be parsing
1129     // the document until the callstack unwinds.
1130     m_frame->loader().stopForUserCancel(true);
1131 }
1132
1133 void DOMWindow::alert(const String& message)
1134 {
1135     if (!m_frame)
1136         return;
1137
1138     if (document()->isSandboxed(SandboxModals)) {
1139         printErrorMessage("Use of window.alert is not allowed in a sandboxed frame when the allow-modals flag is not set.");
1140         return;
1141     }
1142
1143     auto* page = m_frame->page();
1144     if (!page)
1145         return;
1146
1147     if (!page->arePromptsAllowed()) {
1148         printErrorMessage("Use of window.alert is not allowed while unloading a page.");
1149         return;
1150     }
1151
1152     m_frame->document()->updateStyleIfNeeded();
1153 #if ENABLE(POINTER_LOCK)
1154     page->pointerLockController().requestPointerUnlock();
1155 #endif
1156
1157     page->chrome().runJavaScriptAlert(*m_frame, message);
1158 }
1159
1160 bool DOMWindow::confirm(const String& message)
1161 {
1162     if (!m_frame)
1163         return false;
1164     
1165     if (document()->isSandboxed(SandboxModals)) {
1166         printErrorMessage("Use of window.confirm is not allowed in a sandboxed frame when the allow-modals flag is not set.");
1167         return false;
1168     }
1169
1170     auto* page = m_frame->page();
1171     if (!page)
1172         return false;
1173
1174     if (!page->arePromptsAllowed()) {
1175         printErrorMessage("Use of window.confirm is not allowed while unloading a page.");
1176         return false;
1177     }
1178
1179     m_frame->document()->updateStyleIfNeeded();
1180 #if ENABLE(POINTER_LOCK)
1181     page->pointerLockController().requestPointerUnlock();
1182 #endif
1183
1184     return page->chrome().runJavaScriptConfirm(*m_frame, message);
1185 }
1186
1187 String DOMWindow::prompt(const String& message, const String& defaultValue)
1188 {
1189     if (!m_frame)
1190         return String();
1191
1192     if (document()->isSandboxed(SandboxModals)) {
1193         printErrorMessage("Use of window.prompt is not allowed in a sandboxed frame when the allow-modals flag is not set.");
1194         return String();
1195     }
1196
1197     auto* page = m_frame->page();
1198     if (!page)
1199         return String();
1200
1201     if (!page->arePromptsAllowed()) {
1202         printErrorMessage("Use of window.prompt is not allowed while unloading a page.");
1203         return String();
1204     }
1205
1206     m_frame->document()->updateStyleIfNeeded();
1207 #if ENABLE(POINTER_LOCK)
1208     page->pointerLockController().requestPointerUnlock();
1209 #endif
1210
1211     String returnValue;
1212     if (page->chrome().runJavaScriptPrompt(*m_frame, message, defaultValue, returnValue))
1213         return returnValue;
1214
1215     return String();
1216 }
1217
1218 bool DOMWindow::find(const String& string, bool caseSensitive, bool backwards, bool wrap, bool /*wholeWord*/, bool /*searchInFrames*/, bool /*showDialog*/) const
1219 {
1220     if (!isCurrentlyDisplayedInFrame())
1221         return false;
1222
1223     // FIXME (13016): Support wholeWord, searchInFrames and showDialog.    
1224     FindOptions options { DoNotTraverseFlatTree };
1225     if (backwards)
1226         options.add(Backwards);
1227     if (!caseSensitive)
1228         options.add(CaseInsensitive);
1229     if (wrap)
1230         options.add(WrapAround);
1231     return m_frame->editor().findString(string, options);
1232 }
1233
1234 bool DOMWindow::offscreenBuffering() const
1235 {
1236     return true;
1237 }
1238
1239 int DOMWindow::outerHeight() const
1240 {
1241 #if PLATFORM(IOS)
1242     return 0;
1243 #else
1244     if (!m_frame)
1245         return 0;
1246
1247     Page* page = m_frame->page();
1248     if (!page)
1249         return 0;
1250
1251     return static_cast<int>(page->chrome().windowRect().height());
1252 #endif
1253 }
1254
1255 int DOMWindow::outerWidth() const
1256 {
1257 #if PLATFORM(IOS)
1258     return 0;
1259 #else
1260     if (!m_frame)
1261         return 0;
1262
1263     Page* page = m_frame->page();
1264     if (!page)
1265         return 0;
1266
1267     return static_cast<int>(page->chrome().windowRect().width());
1268 #endif
1269 }
1270
1271 int DOMWindow::innerHeight() const
1272 {
1273     if (!m_frame)
1274         return 0;
1275
1276     // Force enough layout in the parent document to ensure that the FrameView has been resized.
1277     if (auto* frameElement = this->frameElement())
1278         frameElement->document().updateLayoutIfDimensionsOutOfDate(*frameElement, HeightDimensionsCheck);
1279
1280     FrameView* view = m_frame->view();
1281     if (!view)
1282         return 0;
1283
1284     return view->mapFromLayoutToCSSUnits(static_cast<int>(view->unobscuredContentRectIncludingScrollbars().height()));
1285 }
1286
1287 int DOMWindow::innerWidth() const
1288 {
1289     if (!m_frame)
1290         return 0;
1291
1292     // Force enough layout in the parent document to ensure that the FrameView has been resized.
1293     if (auto* frameElement = this->frameElement())
1294         frameElement->document().updateLayoutIfDimensionsOutOfDate(*frameElement, WidthDimensionsCheck);
1295
1296     FrameView* view = m_frame->view();
1297     if (!view)
1298         return 0;
1299
1300     return view->mapFromLayoutToCSSUnits(static_cast<int>(view->unobscuredContentRectIncludingScrollbars().width()));
1301 }
1302
1303 int DOMWindow::screenX() const
1304 {
1305     if (!m_frame)
1306         return 0;
1307
1308     Page* page = m_frame->page();
1309     if (!page)
1310         return 0;
1311
1312     return static_cast<int>(page->chrome().windowRect().x());
1313 }
1314
1315 int DOMWindow::screenY() const
1316 {
1317     if (!m_frame)
1318         return 0;
1319
1320     Page* page = m_frame->page();
1321     if (!page)
1322         return 0;
1323
1324     return static_cast<int>(page->chrome().windowRect().y());
1325 }
1326
1327 int DOMWindow::scrollX() const
1328 {
1329     if (!m_frame)
1330         return 0;
1331
1332     FrameView* view = m_frame->view();
1333     if (!view)
1334         return 0;
1335
1336     int scrollX = view->contentsScrollPosition().x();
1337     if (!scrollX)
1338         return 0;
1339
1340     m_frame->document()->updateLayoutIgnorePendingStylesheets();
1341
1342     return view->mapFromLayoutToCSSUnits(view->contentsScrollPosition().x());
1343 }
1344
1345 int DOMWindow::scrollY() const
1346 {
1347     if (!m_frame)
1348         return 0;
1349
1350     FrameView* view = m_frame->view();
1351     if (!view)
1352         return 0;
1353
1354     int scrollY = view->contentsScrollPosition().y();
1355     if (!scrollY)
1356         return 0;
1357
1358     m_frame->document()->updateLayoutIgnorePendingStylesheets();
1359
1360     return view->mapFromLayoutToCSSUnits(view->contentsScrollPosition().y());
1361 }
1362
1363 bool DOMWindow::closed() const
1364 {
1365     return !m_frame;
1366 }
1367
1368 unsigned DOMWindow::length() const
1369 {
1370     if (!isCurrentlyDisplayedInFrame())
1371         return 0;
1372
1373     return m_frame->tree().scopedChildCount();
1374 }
1375
1376 String DOMWindow::name() const
1377 {
1378     if (!m_frame)
1379         return String();
1380
1381     return m_frame->tree().name();
1382 }
1383
1384 void DOMWindow::setName(const String& string)
1385 {
1386     if (!m_frame)
1387         return;
1388
1389     m_frame->tree().setName(string);
1390 }
1391
1392 void DOMWindow::setStatus(const String& string) 
1393 {
1394     m_status = string;
1395
1396     if (!m_frame)
1397         return;
1398
1399     Page* page = m_frame->page();
1400     if (!page)
1401         return;
1402
1403     ASSERT(m_frame->document()); // Client calls shouldn't be made when the frame is in inconsistent state.
1404     page->chrome().setStatusbarText(*m_frame, m_status);
1405
1406     
1407 void DOMWindow::setDefaultStatus(const String& string) 
1408 {
1409     m_defaultStatus = string;
1410
1411     if (!m_frame)
1412         return;
1413
1414     Page* page = m_frame->page();
1415     if (!page)
1416         return;
1417
1418     ASSERT(m_frame->document()); // Client calls shouldn't be made when the frame is in inconsistent state.
1419     page->chrome().setStatusbarText(*m_frame, m_defaultStatus);
1420 }
1421
1422 WindowProxy* DOMWindow::self() const
1423 {
1424     if (!m_frame)
1425         return nullptr;
1426
1427     return &m_frame->windowProxy();
1428 }
1429
1430 WindowProxy* DOMWindow::opener() const
1431 {
1432     if (!m_frame)
1433         return nullptr;
1434
1435     auto* openerFrame = m_frame->loader().opener();
1436     if (!openerFrame)
1437         return nullptr;
1438
1439     return &openerFrame->windowProxy();
1440 }
1441
1442 void DOMWindow::disownOpener()
1443 {
1444     if (m_frame)
1445         m_frame->loader().setOpener(nullptr);
1446 }
1447
1448 WindowProxy* DOMWindow::parent() const
1449 {
1450     if (!m_frame)
1451         return nullptr;
1452
1453     auto* parentFrame = m_frame->tree().parent();
1454     if (parentFrame)
1455         return &parentFrame->windowProxy();
1456
1457     return &m_frame->windowProxy();
1458 }
1459
1460 WindowProxy* DOMWindow::top() const
1461 {
1462     if (!m_frame)
1463         return nullptr;
1464
1465     if (!m_frame->page())
1466         return nullptr;
1467
1468     return &m_frame->tree().top().windowProxy();
1469 }
1470
1471 String DOMWindow::origin() const
1472 {
1473     auto document = this->document();
1474     return document ? document->securityOrigin().toString() : emptyString();
1475 }
1476
1477 Document* DOMWindow::document() const
1478 {
1479     return downcast<Document>(ContextDestructionObserver::scriptExecutionContext());
1480 }
1481
1482 RefPtr<StyleMedia> DOMWindow::styleMedia() const
1483 {
1484     if (!isCurrentlyDisplayedInFrame())
1485         return nullptr;
1486     if (!m_media)
1487         m_media = StyleMedia::create(m_frame);
1488     return m_media;
1489 }
1490
1491 Ref<CSSStyleDeclaration> DOMWindow::getComputedStyle(Element& element, const String& pseudoElt) const
1492 {
1493     return CSSComputedStyleDeclaration::create(element, false, pseudoElt);
1494 }
1495
1496 RefPtr<CSSRuleList> DOMWindow::getMatchedCSSRules(Element* element, const String& pseudoElement, bool authorOnly) const
1497 {
1498     if (!isCurrentlyDisplayedInFrame())
1499         return nullptr;
1500
1501     unsigned colonStart = pseudoElement[0] == ':' ? (pseudoElement[1] == ':' ? 2 : 1) : 0;
1502     CSSSelector::PseudoElementType pseudoType = CSSSelector::parsePseudoElementType(pseudoElement.substringSharingImpl(colonStart));
1503     if (pseudoType == CSSSelector::PseudoElementUnknown && !pseudoElement.isEmpty())
1504         return nullptr;
1505
1506     m_frame->document()->styleScope().flushPendingUpdate();
1507
1508     unsigned rulesToInclude = StyleResolver::AuthorCSSRules;
1509     if (!authorOnly)
1510         rulesToInclude |= StyleResolver::UAAndUserCSSRules;
1511
1512     PseudoId pseudoId = CSSSelector::pseudoId(pseudoType);
1513
1514     auto matchedRules = m_frame->document()->styleScope().resolver().pseudoStyleRulesForElement(element, pseudoId, rulesToInclude);
1515     if (matchedRules.isEmpty())
1516         return nullptr;
1517
1518     bool allowCrossOrigin = m_frame->settings().crossOriginCheckInGetMatchedCSSRulesDisabled();
1519
1520     RefPtr<StaticCSSRuleList> ruleList = StaticCSSRuleList::create();
1521     for (auto& rule : matchedRules) {
1522         if (!allowCrossOrigin && !rule->hasDocumentSecurityOrigin())
1523             continue;
1524         ruleList->rules().append(rule->createCSSOMWrapper());
1525     }
1526
1527     if (ruleList->rules().isEmpty())
1528         return nullptr;
1529
1530     return ruleList;
1531 }
1532
1533 RefPtr<WebKitPoint> DOMWindow::webkitConvertPointFromNodeToPage(Node* node, const WebKitPoint* p) const
1534 {
1535     if (!node || !p)
1536         return nullptr;
1537
1538     if (!document())
1539         return nullptr;
1540
1541     document()->updateLayoutIgnorePendingStylesheets();
1542
1543     FloatPoint pagePoint(p->x(), p->y());
1544     pagePoint = node->convertToPage(pagePoint);
1545     return WebKitPoint::create(pagePoint.x(), pagePoint.y());
1546 }
1547
1548 RefPtr<WebKitPoint> DOMWindow::webkitConvertPointFromPageToNode(Node* node, const WebKitPoint* p) const
1549 {
1550     if (!node || !p)
1551         return nullptr;
1552
1553     if (!document())
1554         return nullptr;
1555
1556     document()->updateLayoutIgnorePendingStylesheets();
1557
1558     FloatPoint nodePoint(p->x(), p->y());
1559     nodePoint = node->convertFromPage(nodePoint);
1560     return WebKitPoint::create(nodePoint.x(), nodePoint.y());
1561 }
1562
1563 double DOMWindow::devicePixelRatio() const
1564 {
1565     if (!m_frame)
1566         return 0.0;
1567
1568     Page* page = m_frame->page();
1569     if (!page)
1570         return 0.0;
1571
1572     return page->deviceScaleFactor();
1573 }
1574
1575 void DOMWindow::scrollBy(double x, double y) const
1576 {
1577     scrollBy({ x, y });
1578 }
1579
1580 void DOMWindow::scrollBy(const ScrollToOptions& options) const
1581 {
1582     if (!isCurrentlyDisplayedInFrame())
1583         return;
1584
1585     document()->updateLayoutIgnorePendingStylesheets();
1586
1587     FrameView* view = m_frame->view();
1588     if (!view)
1589         return;
1590
1591     ScrollToOptions scrollToOptions = normalizeNonFiniteCoordinatesOrFallBackTo(options, 0, 0);
1592     scrollToOptions.left.value() += view->mapFromLayoutToCSSUnits(view->contentsScrollPosition().x());
1593     scrollToOptions.top.value() += view->mapFromLayoutToCSSUnits(view->contentsScrollPosition().y());
1594     scrollTo(scrollToOptions);
1595 }
1596
1597 void DOMWindow::scrollTo(double x, double y, ScrollClamping clamping) const
1598 {
1599     scrollTo({ x, y }, clamping);
1600 }
1601
1602 void DOMWindow::scrollTo(const ScrollToOptions& options, ScrollClamping) const
1603 {
1604     if (!isCurrentlyDisplayedInFrame())
1605         return;
1606
1607     RefPtr<FrameView> view = m_frame->view();
1608     if (!view)
1609         return;
1610
1611     ScrollToOptions scrollToOptions = normalizeNonFiniteCoordinatesOrFallBackTo(options,
1612         view->contentsScrollPosition().x(), view->contentsScrollPosition().y()
1613     );
1614
1615     if (!scrollToOptions.left.value() && !scrollToOptions.top.value() && view->contentsScrollPosition() == IntPoint(0, 0))
1616         return;
1617
1618     document()->updateLayoutIgnorePendingStylesheets();
1619
1620     IntPoint layoutPos(view->mapFromCSSToLayoutUnits(scrollToOptions.left.value()), view->mapFromCSSToLayoutUnits(scrollToOptions.top.value()));
1621     view->setContentsScrollPosition(layoutPos);
1622 }
1623
1624 bool DOMWindow::allowedToChangeWindowGeometry() const
1625 {
1626     if (!m_frame)
1627         return false;
1628     if (!m_frame->page())
1629         return false;
1630     if (!m_frame->isMainFrame())
1631         return false;
1632     // Prevent web content from tricking the user into initiating a drag.
1633     if (m_frame->eventHandler().mousePressed())
1634         return false;
1635     return true;
1636 }
1637
1638 void DOMWindow::moveBy(float x, float y) const
1639 {
1640     if (!allowedToChangeWindowGeometry())
1641         return;
1642
1643     Page* page = m_frame->page();
1644     FloatRect fr = page->chrome().windowRect();
1645     FloatRect update = fr;
1646     update.move(x, y);
1647     page->chrome().setWindowRect(adjustWindowRect(*page, update));
1648 }
1649
1650 void DOMWindow::moveTo(float x, float y) const
1651 {
1652     if (!allowedToChangeWindowGeometry())
1653         return;
1654
1655     Page* page = m_frame->page();
1656     FloatRect fr = page->chrome().windowRect();
1657     FloatRect sr = screenAvailableRect(page->mainFrame().view());
1658     fr.setLocation(sr.location());
1659     FloatRect update = fr;
1660     update.move(x, y);
1661     page->chrome().setWindowRect(adjustWindowRect(*page, update));
1662 }
1663
1664 void DOMWindow::resizeBy(float x, float y) const
1665 {
1666     if (!allowedToChangeWindowGeometry())
1667         return;
1668
1669     Page* page = m_frame->page();
1670     FloatRect fr = page->chrome().windowRect();
1671     FloatSize dest = fr.size() + FloatSize(x, y);
1672     FloatRect update(fr.location(), dest);
1673     page->chrome().setWindowRect(adjustWindowRect(*page, update));
1674 }
1675
1676 void DOMWindow::resizeTo(float width, float height) const
1677 {
1678     if (!allowedToChangeWindowGeometry())
1679         return;
1680
1681     Page* page = m_frame->page();
1682     FloatRect fr = page->chrome().windowRect();
1683     FloatSize dest = FloatSize(width, height);
1684     FloatRect update(fr.location(), dest);
1685     page->chrome().setWindowRect(adjustWindowRect(*page, update));
1686 }
1687
1688 ExceptionOr<int> DOMWindow::setTimeout(JSC::ExecState& state, std::unique_ptr<ScheduledAction> action, int timeout, Vector<JSC::Strong<JSC::Unknown>>&& arguments)
1689 {
1690     auto* context = scriptExecutionContext();
1691     if (!context)
1692         return Exception { InvalidAccessError };
1693
1694     // FIXME: Should this check really happen here? Or should it happen when code is about to eval?
1695     if (action->type() == ScheduledAction::Type::Code) {
1696         if (!context->contentSecurityPolicy()->allowEval(&state))
1697             return 0;
1698     }
1699
1700     action->addArguments(WTFMove(arguments));
1701
1702     return DOMTimer::install(*context, WTFMove(action), Seconds::fromMilliseconds(timeout), true);
1703 }
1704
1705 void DOMWindow::clearTimeout(int timeoutId)
1706 {
1707 #if PLATFORM(IOS)
1708     if (m_frame) {
1709         Document* document = m_frame->document();
1710         if (timeoutId > 0 && document) {
1711             DOMTimer* timer = document->findTimeout(timeoutId);
1712             if (timer && WebThreadContainsObservedContentModifier(timer)) {
1713                 WebThreadRemoveObservedContentModifier(timer);
1714
1715                 if (!WebThreadCountOfObservedContentModifiers()) {
1716                     if (Page* page = m_frame->page())
1717                         page->chrome().client().observedContentChange(*m_frame);
1718                 }
1719             }
1720         }
1721     }
1722 #endif
1723     ScriptExecutionContext* context = scriptExecutionContext();
1724     if (!context)
1725         return;
1726     DOMTimer::removeById(*context, timeoutId);
1727 }
1728
1729 ExceptionOr<int> DOMWindow::setInterval(JSC::ExecState& state, std::unique_ptr<ScheduledAction> action, int timeout, Vector<JSC::Strong<JSC::Unknown>>&& arguments)
1730 {
1731     auto* context = scriptExecutionContext();
1732     if (!context)
1733         return Exception { InvalidAccessError };
1734
1735     // FIXME: Should this check really happen here? Or should it happen when code is about to eval?
1736     if (action->type() == ScheduledAction::Type::Code) {
1737         if (!context->contentSecurityPolicy()->allowEval(&state))
1738             return 0;
1739     }
1740
1741     action->addArguments(WTFMove(arguments));
1742
1743     return DOMTimer::install(*context, WTFMove(action), Seconds::fromMilliseconds(timeout), false);
1744 }
1745
1746 void DOMWindow::clearInterval(int timeoutId)
1747 {
1748     ScriptExecutionContext* context = scriptExecutionContext();
1749     if (!context)
1750         return;
1751     DOMTimer::removeById(*context, timeoutId);
1752 }
1753
1754 int DOMWindow::requestAnimationFrame(Ref<RequestAnimationFrameCallback>&& callback)
1755 {
1756     callback->m_useLegacyTimeBase = false;
1757     auto* document = this->document();
1758     if (!document)
1759         return 0;
1760     return document->requestAnimationFrame(WTFMove(callback));
1761 }
1762
1763 int DOMWindow::webkitRequestAnimationFrame(Ref<RequestAnimationFrameCallback>&& callback)
1764 {
1765     callback->m_useLegacyTimeBase = true;
1766     auto* document = this->document();
1767     if (!document)
1768         return 0;
1769     return document->requestAnimationFrame(WTFMove(callback));
1770 }
1771
1772 void DOMWindow::cancelAnimationFrame(int id)
1773 {
1774     auto* document = this->document();
1775     if (!document)
1776         return;
1777     document->cancelAnimationFrame(id);
1778 }
1779
1780 void DOMWindow::createImageBitmap(ImageBitmap::Source&& source, ImageBitmapOptions&& options, ImageBitmap::Promise&& promise)
1781 {
1782     auto* document = this->document();
1783     if (!document) {
1784         promise.reject(InvalidStateError);
1785         return;
1786     }
1787     ImageBitmap::createPromise(*document, WTFMove(source), WTFMove(options), WTFMove(promise));
1788 }
1789
1790 void DOMWindow::createImageBitmap(ImageBitmap::Source&& source, int sx, int sy, int sw, int sh, ImageBitmapOptions&& options, ImageBitmap::Promise&& promise)
1791 {
1792     auto* document = this->document();
1793     if (!document) {
1794         promise.reject(InvalidStateError);
1795         return;
1796     }
1797     ImageBitmap::createPromise(*document, WTFMove(source), WTFMove(options), sx, sy, sw, sh, WTFMove(promise));
1798 }
1799
1800 bool DOMWindow::isSecureContext() const
1801 {
1802     auto* document = this->document();
1803     if (!document)
1804         return false;
1805     return document->isSecureContext();
1806 }
1807
1808 static void didAddStorageEventListener(DOMWindow& window)
1809 {
1810     // Creating these WebCore::Storage objects informs the system that we'd like to receive
1811     // notifications about storage events that might be triggered in other processes. Rather
1812     // than subscribe to these notifications explicitly, we subscribe to them implicitly to
1813     // simplify the work done by the system. 
1814     window.localStorage();
1815     window.sessionStorage();
1816 }
1817
1818 bool DOMWindow::isSameSecurityOriginAsMainFrame() const
1819 {
1820     if (!m_frame || !m_frame->page() || !document())
1821         return false;
1822
1823     if (m_frame->isMainFrame())
1824         return true;
1825
1826     Document* mainFrameDocument = m_frame->mainFrame().document();
1827
1828     if (mainFrameDocument && document()->securityOrigin().canAccess(mainFrameDocument->securityOrigin()))
1829         return true;
1830
1831     return false;
1832 }
1833
1834 bool DOMWindow::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
1835 {
1836     if (!EventTarget::addEventListener(eventType, WTFMove(listener), options))
1837         return false;
1838
1839     if (Document* document = this->document()) {
1840         document->addListenerTypeIfNeeded(eventType);
1841         if (eventNames().isWheelEventType(eventType))
1842             document->didAddWheelEventHandler(*document);
1843         else if (eventNames().isTouchEventType(eventType))
1844             document->didAddTouchEventHandler(*document);
1845         else if (eventType == eventNames().storageEvent)
1846             didAddStorageEventListener(*this);
1847     }
1848
1849     if (eventType == eventNames().unloadEvent)
1850         addUnloadEventListener(this);
1851     else if (eventType == eventNames().beforeunloadEvent && allowsBeforeUnloadListeners(this))
1852         addBeforeUnloadEventListener(this);
1853 #if ENABLE(DEVICE_ORIENTATION)
1854 #if PLATFORM(IOS)
1855     else if ((eventType == eventNames().devicemotionEvent || eventType == eventNames().deviceorientationEvent) && document()) {
1856         if (isSameSecurityOriginAsMainFrame()) {
1857             if (eventType == eventNames().deviceorientationEvent)
1858                 document()->deviceOrientationController()->addDeviceEventListener(this);
1859             else
1860                 document()->deviceMotionController()->addDeviceEventListener(this);
1861         } else if (document())
1862             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);
1863     }
1864 #else
1865     else if (eventType == eventNames().devicemotionEvent) {
1866         if (isSameSecurityOriginAsMainFrame()) {
1867             if (DeviceMotionController* controller = DeviceMotionController::from(page()))
1868                 controller->addDeviceEventListener(this);
1869         } else if (document())
1870             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);
1871     } else if (eventType == eventNames().deviceorientationEvent) {
1872         if (isSameSecurityOriginAsMainFrame()) {
1873             if (DeviceOrientationController* controller = DeviceOrientationController::from(page()))
1874                 controller->addDeviceEventListener(this);
1875         } else if (document())
1876             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);
1877     }
1878 #endif // PLATFORM(IOS)
1879 #endif // ENABLE(DEVICE_ORIENTATION)
1880 #if PLATFORM(IOS)
1881     else if (eventType == eventNames().scrollEvent)
1882         incrementScrollEventListenersCount();
1883 #endif
1884 #if ENABLE(IOS_TOUCH_EVENTS)
1885     else if (eventNames().isTouchEventType(eventType))
1886         ++m_touchAndGestureEventListenerCount;
1887 #endif
1888 #if ENABLE(IOS_GESTURE_EVENTS)
1889     else if (eventNames().isGestureEventType(eventType))
1890         ++m_touchAndGestureEventListenerCount;
1891 #endif
1892 #if ENABLE(GAMEPAD)
1893     else if (eventNames().isGamepadEventType(eventType))
1894         incrementGamepadEventListenerCount();
1895 #endif
1896
1897     return true;
1898 }
1899
1900 #if PLATFORM(IOS)
1901
1902 void DOMWindow::incrementScrollEventListenersCount()
1903 {
1904     Document* document = this->document();
1905     if (++m_scrollEventListenerCount == 1 && document == &document->topDocument()) {
1906         Frame* frame = this->frame();
1907         if (frame && frame->page())
1908             frame->page()->chrome().client().setNeedsScrollNotifications(*frame, true);
1909     }
1910 }
1911
1912 void DOMWindow::decrementScrollEventListenersCount()
1913 {
1914     Document* document = this->document();
1915     if (!--m_scrollEventListenerCount && document == &document->topDocument()) {
1916         Frame* frame = this->frame();
1917         if (frame && frame->page() && document->pageCacheState() == Document::NotInPageCache)
1918             frame->page()->chrome().client().setNeedsScrollNotifications(*frame, false);
1919     }
1920 }
1921
1922 #endif
1923
1924 void DOMWindow::resetAllGeolocationPermission()
1925 {
1926     // FIXME: Can we remove the PLATFORM(IOS)-guard?
1927 #if ENABLE(GEOLOCATION) && PLATFORM(IOS)
1928     if (m_navigator)
1929         NavigatorGeolocation::from(m_navigator.get())->resetAllGeolocationPermission();
1930 #endif
1931 }
1932
1933 bool DOMWindow::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
1934 {
1935     if (!EventTarget::removeEventListener(eventType, listener, options.capture))
1936         return false;
1937
1938     if (Document* document = this->document()) {
1939         if (eventNames().isWheelEventType(eventType))
1940             document->didRemoveWheelEventHandler(*document);
1941         else if (eventNames().isTouchEventType(eventType))
1942             document->didRemoveTouchEventHandler(*document);
1943     }
1944
1945     if (eventType == eventNames().unloadEvent)
1946         removeUnloadEventListener(this);
1947     else if (eventType == eventNames().beforeunloadEvent && allowsBeforeUnloadListeners(this))
1948         removeBeforeUnloadEventListener(this);
1949 #if ENABLE(DEVICE_ORIENTATION)
1950 #if PLATFORM(IOS)
1951     else if (eventType == eventNames().devicemotionEvent && document())
1952         document()->deviceMotionController()->removeDeviceEventListener(this);
1953     else if (eventType == eventNames().deviceorientationEvent && document())
1954         document()->deviceOrientationController()->removeDeviceEventListener(this);
1955 #else
1956     else if (eventType == eventNames().devicemotionEvent) {
1957         if (DeviceMotionController* controller = DeviceMotionController::from(page()))
1958             controller->removeDeviceEventListener(this);
1959     } else if (eventType == eventNames().deviceorientationEvent) {
1960         if (DeviceOrientationController* controller = DeviceOrientationController::from(page()))
1961             controller->removeDeviceEventListener(this);
1962     }
1963 #endif // PLATFORM(IOS)
1964 #endif // ENABLE(DEVICE_ORIENTATION)
1965 #if PLATFORM(IOS)
1966     else if (eventType == eventNames().scrollEvent)
1967         decrementScrollEventListenersCount();
1968 #endif
1969 #if ENABLE(IOS_TOUCH_EVENTS)
1970     else if (eventNames().isTouchEventType(eventType)) {
1971         ASSERT(m_touchAndGestureEventListenerCount > 0);
1972         --m_touchAndGestureEventListenerCount;
1973     }
1974 #endif
1975 #if ENABLE(IOS_GESTURE_EVENTS)
1976     else if (eventNames().isGestureEventType(eventType)) {
1977         ASSERT(m_touchAndGestureEventListenerCount > 0);
1978         --m_touchAndGestureEventListenerCount;
1979     }
1980 #endif
1981 #if ENABLE(GAMEPAD)
1982     else if (eventNames().isGamepadEventType(eventType))
1983         decrementGamepadEventListenerCount();
1984 #endif
1985
1986     return true;
1987 }
1988
1989 void DOMWindow::languagesChanged()
1990 {
1991     if (auto* document = this->document())
1992         document->enqueueWindowEvent(Event::create(eventNames().languagechangeEvent, Event::CanBubble::No, Event::IsCancelable::No));
1993 }
1994
1995 void DOMWindow::dispatchLoadEvent()
1996 {
1997     // If we did not protect it, the document loader and its timing subobject might get destroyed
1998     // as a side effect of what event handling code does.
1999     auto protectedThis = makeRef(*this);
2000     auto protectedLoader = makeRefPtr(frame() ? frame()->loader().documentLoader() : nullptr);
2001     bool shouldMarkLoadEventTimes = protectedLoader && !protectedLoader->timing().loadEventStart();
2002
2003     if (shouldMarkLoadEventTimes)
2004         protectedLoader->timing().markLoadEventStart();
2005
2006     dispatchEvent(Event::create(eventNames().loadEvent, Event::CanBubble::No, Event::IsCancelable::No), document());
2007
2008     if (shouldMarkLoadEventTimes)
2009         protectedLoader->timing().markLoadEventEnd();
2010
2011     // Send a separate load event to the element that owns this frame.
2012     if (frame()) {
2013         if (auto* owner = frame()->ownerElement())
2014             owner->dispatchEvent(Event::create(eventNames().loadEvent, Event::CanBubble::No, Event::IsCancelable::No));
2015     }
2016
2017     InspectorInstrumentation::loadEventFired(frame());
2018 }
2019
2020 void DOMWindow::dispatchEvent(Event& event, EventTarget* target)
2021 {
2022     // FIXME: It's confusing to have both the inherited EventTarget::dispatchEvent function
2023     // and this function, which does something nearly identical but subtly different if
2024     // called with a target of null. Most callers pass the document as the target, though.
2025     // Fixing this could allow us to remove the special case in DocumentEventQueue::dispatchEvent.
2026
2027     auto protectedThis = makeRef(*this);
2028
2029     // Pausing a page may trigger pagehide and pageshow events. WebCore also implicitly fires these
2030     // events when closing a WebView. Here we keep track of the state of the page to prevent duplicate,
2031     // unbalanced events per the definition of the pageshow event:
2032     // <http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#event-pageshow>.
2033     // FIXME: This code should go at call sites where pageshowEvent and pagehideEvents are
2034     // generated, not here inside the event dispatching process.
2035     if (event.eventInterface() == PageTransitionEventInterfaceType) {
2036         if (event.type() == eventNames().pageshowEvent) {
2037             if (m_lastPageStatus == PageStatus::Shown)
2038                 return; // Event was previously dispatched; do not fire a duplicate event.
2039             m_lastPageStatus = PageStatus::Shown;
2040         } else if (event.type() == eventNames().pagehideEvent) {
2041             if (m_lastPageStatus == PageStatus::Hidden)
2042                 return; // Event was previously dispatched; do not fire a duplicate event.
2043             m_lastPageStatus = PageStatus::Hidden;
2044         }
2045     }
2046
2047     // FIXME: It doesn't seem right to have the inspector instrumentation here since not all
2048     // events dispatched to the window object are guaranteed to flow through this function.
2049     // But the instrumentation prevents us from calling EventDispatcher::dispatchEvent here.
2050     event.setTarget(target ? target : this);
2051     event.setCurrentTarget(this);
2052     event.setEventPhase(Event::AT_TARGET);
2053     event.resetBeforeDispatch();
2054     auto cookie = InspectorInstrumentation::willDispatchEventOnWindow(frame(), event, *this);
2055     // FIXME: We should use EventDispatcher everywhere.
2056     fireEventListeners(event, EventInvokePhase::Capturing);
2057     fireEventListeners(event, EventInvokePhase::Bubbling);
2058     InspectorInstrumentation::didDispatchEventOnWindow(cookie);
2059     event.resetAfterDispatch();
2060 }
2061
2062 void DOMWindow::removeAllEventListeners()
2063 {
2064     EventTarget::removeAllEventListeners();
2065
2066 #if ENABLE(DEVICE_ORIENTATION)
2067 #if PLATFORM(IOS)
2068     if (Document* document = this->document()) {
2069         document->deviceMotionController()->removeAllDeviceEventListeners(this);
2070         document->deviceOrientationController()->removeAllDeviceEventListeners(this);
2071     }
2072 #else
2073     if (DeviceMotionController* controller = DeviceMotionController::from(page()))
2074         controller->removeAllDeviceEventListeners(this);
2075     if (DeviceOrientationController* controller = DeviceOrientationController::from(page()))
2076         controller->removeAllDeviceEventListeners(this);
2077 #endif // PLATFORM(IOS)
2078 #endif // ENABLE(DEVICE_ORIENTATION)
2079
2080 #if PLATFORM(IOS)
2081     if (m_scrollEventListenerCount) {
2082         m_scrollEventListenerCount = 1;
2083         decrementScrollEventListenersCount();
2084     }
2085 #endif
2086
2087 #if ENABLE(IOS_TOUCH_EVENTS) || ENABLE(IOS_GESTURE_EVENTS)
2088     m_touchAndGestureEventListenerCount = 0;
2089 #endif
2090
2091 #if ENABLE(TOUCH_EVENTS)
2092     if (Document* document = this->document())
2093         document->didRemoveEventTargetNode(*document);
2094 #endif
2095
2096     if (m_performance) {
2097         m_performance->removeAllEventListeners();
2098         m_performance->removeAllObservers();
2099     }
2100
2101     removeAllUnloadEventListeners(this);
2102     removeAllBeforeUnloadEventListeners(this);
2103 }
2104
2105 void DOMWindow::captureEvents()
2106 {
2107     // Not implemented.
2108 }
2109
2110 void DOMWindow::releaseEvents()
2111 {
2112     // Not implemented.
2113 }
2114
2115 void DOMWindow::finishedLoading()
2116 {
2117     if (m_shouldPrintWhenFinishedLoading) {
2118         m_shouldPrintWhenFinishedLoading = false;
2119         if (m_frame->loader().activeDocumentLoader()->mainDocumentError().isNull())
2120             print();
2121     }
2122 }
2123
2124 void DOMWindow::setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& urlString, SetLocationLocking locking)
2125 {
2126     if (!isCurrentlyDisplayedInFrame())
2127         return;
2128
2129     Document* activeDocument = activeWindow.document();
2130     if (!activeDocument)
2131         return;
2132
2133     if (!activeDocument->canNavigate(m_frame))
2134         return;
2135
2136     Frame* firstFrame = firstWindow.frame();
2137     if (!firstFrame)
2138         return;
2139
2140     URL completedURL = firstFrame->document()->completeURL(urlString);
2141     if (completedURL.isNull())
2142         return;
2143
2144     if (isInsecureScriptAccess(activeWindow, completedURL))
2145         return;
2146
2147     // We want a new history item if we are processing a user gesture.
2148     LockHistory lockHistory = (locking != LockHistoryBasedOnGestureState || !UserGestureIndicator::processingUserGesture()) ? LockHistory::Yes : LockHistory::No;
2149     LockBackForwardList lockBackForwardList = (locking != LockHistoryBasedOnGestureState) ? LockBackForwardList::Yes : LockBackForwardList::No;
2150     m_frame->navigationScheduler().scheduleLocationChange(*activeDocument, activeDocument->securityOrigin(),
2151         // FIXME: What if activeDocument()->frame() is 0?
2152         completedURL, activeDocument->frame()->loader().outgoingReferrer(),
2153         lockHistory, lockBackForwardList);
2154 }
2155
2156 void DOMWindow::printErrorMessage(const String& message)
2157 {
2158     if (message.isEmpty())
2159         return;
2160
2161     if (PageConsoleClient* pageConsole = console())
2162         pageConsole->addMessage(MessageSource::JS, MessageLevel::Error, message);
2163 }
2164
2165 String DOMWindow::crossDomainAccessErrorMessage(const DOMWindow& activeWindow, IncludeTargetOrigin includeTargetOrigin)
2166 {
2167     const URL& activeWindowURL = activeWindow.document()->url();
2168     if (activeWindowURL.isNull())
2169         return String();
2170
2171     ASSERT(!activeWindow.document()->securityOrigin().canAccess(document()->securityOrigin()));
2172
2173     // FIXME: This message, and other console messages, have extra newlines. Should remove them.
2174     SecurityOrigin& activeOrigin = activeWindow.document()->securityOrigin();
2175     SecurityOrigin& targetOrigin = document()->securityOrigin();
2176     String message;
2177     if (includeTargetOrigin == IncludeTargetOrigin::Yes)
2178         message = makeString("Blocked a frame with origin \"", activeOrigin.toString(), "\" from accessing a frame with origin \"", targetOrigin.toString(), "\". ");
2179     else
2180         message = makeString("Blocked a frame with origin \"", activeOrigin.toString(), "\" from accessing a cross-origin frame. ");
2181
2182     // Sandbox errors: Use the origin of the frames' location, rather than their actual origin (since we know that at least one will be "null").
2183     URL activeURL = activeWindow.document()->url();
2184     URL targetURL = document()->url();
2185     if (document()->isSandboxed(SandboxOrigin) || activeWindow.document()->isSandboxed(SandboxOrigin)) {
2186         if (includeTargetOrigin == IncludeTargetOrigin::Yes)
2187             message = makeString("Blocked a frame at \"", SecurityOrigin::create(activeURL).get().toString(), "\" from accessing a frame at \"", SecurityOrigin::create(targetURL).get().toString(), "\". ");
2188         else
2189             message = makeString("Blocked a frame at \"", SecurityOrigin::create(activeURL).get().toString(), "\" from accessing a cross-origin frame. ");
2190
2191         if (document()->isSandboxed(SandboxOrigin) && activeWindow.document()->isSandboxed(SandboxOrigin))
2192             return makeString("Sandbox access violation: ", message, " Both frames are sandboxed and lack the \"allow-same-origin\" flag.");
2193         if (document()->isSandboxed(SandboxOrigin))
2194             return makeString("Sandbox access violation: ", message, " The frame being accessed is sandboxed and lacks the \"allow-same-origin\" flag.");
2195         return makeString("Sandbox access violation: ", message, " The frame requesting access is sandboxed and lacks the \"allow-same-origin\" flag.");
2196     }
2197
2198     if (includeTargetOrigin == IncludeTargetOrigin::Yes) {
2199         // 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:'.
2200         if (targetOrigin.protocol() != activeOrigin.protocol())
2201             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";
2202
2203         // 'document.domain' errors.
2204         if (targetOrigin.domainWasSetInDOM() && activeOrigin.domainWasSetInDOM())
2205             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.";
2206         if (activeOrigin.domainWasSetInDOM())
2207             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.";
2208         if (targetOrigin.domainWasSetInDOM())
2209             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.";
2210     }
2211
2212     // Default.
2213     return message + "Protocols, domains, and ports must match.";
2214 }
2215
2216 bool DOMWindow::isInsecureScriptAccess(DOMWindow& activeWindow, const String& urlString)
2217 {
2218     if (!protocolIsJavaScript(urlString))
2219         return false;
2220
2221     // If this DOMWindow isn't currently active in the Frame, then there's no
2222     // way we should allow the access.
2223     // FIXME: Remove this check if we're able to disconnect DOMWindow from
2224     // Frame on navigation: https://bugs.webkit.org/show_bug.cgi?id=62054
2225     if (isCurrentlyDisplayedInFrame()) {
2226         // FIXME: Is there some way to eliminate the need for a separate "activeWindow == this" check?
2227         if (&activeWindow == this)
2228             return false;
2229
2230         // FIXME: The name canAccess seems to be a roundabout way to ask "can execute script".
2231         // Can we name the SecurityOrigin function better to make this more clear?
2232         if (activeWindow.document()->securityOrigin().canAccess(document()->securityOrigin()))
2233             return false;
2234     }
2235
2236     printErrorMessage(crossDomainAccessErrorMessage(activeWindow, IncludeTargetOrigin::Yes));
2237     return true;
2238 }
2239
2240 ExceptionOr<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)
2241 {
2242     Frame* activeFrame = activeWindow.frame();
2243     if (!activeFrame)
2244         return RefPtr<Frame> { nullptr };
2245
2246     Document* activeDocument = activeWindow.document();
2247     if (!activeDocument)
2248         return RefPtr<Frame> { nullptr };
2249
2250     URL completedURL = urlString.isEmpty() ? URL({ }, emptyString()) : firstFrame.document()->completeURL(urlString);
2251     if (!completedURL.isEmpty() && !completedURL.isValid())
2252         return Exception { SyntaxError };
2253
2254     // For whatever reason, Firefox uses the first frame to determine the outgoingReferrer. We replicate that behavior here.
2255     String referrer = SecurityPolicy::generateReferrerHeader(firstFrame.document()->referrerPolicy(), completedURL, firstFrame.loader().outgoingReferrer());
2256     auto initiatedByMainFrame = activeFrame->isMainFrame() ? InitiatedByMainFrame::Yes : InitiatedByMainFrame::Unknown;
2257
2258     ResourceRequest resourceRequest { completedURL, referrer };
2259     FrameLoader::addHTTPOriginIfNeeded(resourceRequest, firstFrame.loader().outgoingOrigin());
2260     FrameLoadRequest frameLoadRequest { *activeDocument, activeDocument->securityOrigin(), resourceRequest, frameName, LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, activeDocument->shouldOpenExternalURLsPolicyToPropagate(), initiatedByMainFrame };
2261
2262     // We pass the opener frame for the lookupFrame in case the active frame is different from
2263     // the opener frame, and the name references a frame relative to the opener frame.
2264     bool created;
2265     RefPtr<Frame> newFrame = WebCore::createWindow(*activeFrame, openerFrame, WTFMove(frameLoadRequest), windowFeatures, created);
2266     if (!newFrame)
2267         return RefPtr<Frame> { nullptr };
2268
2269     if (!windowFeatures.noopener) {
2270         newFrame->loader().setOpener(&openerFrame);
2271         newFrame->page()->setOpenedViaWindowOpenWithOpener();
2272     }
2273     newFrame->page()->setOpenedByDOM();
2274
2275     if (newFrame->document()->domWindow()->isInsecureScriptAccess(activeWindow, completedURL))
2276         return windowFeatures.noopener ? RefPtr<Frame> { nullptr } : newFrame;
2277
2278     if (prepareDialogFunction)
2279         prepareDialogFunction(*newFrame->document()->domWindow());
2280
2281     if (created) {
2282         ResourceRequest resourceRequest { completedURL, referrer, ResourceRequestCachePolicy::UseProtocolCachePolicy };
2283         FrameLoader::addSameSiteInfoToRequestIfNeeded(resourceRequest, openerFrame.document());
2284         FrameLoadRequest frameLoadRequest { *activeWindow.document(), activeWindow.document()->securityOrigin(), resourceRequest, "_self"_s, LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, activeDocument->shouldOpenExternalURLsPolicyToPropagate(), initiatedByMainFrame };
2285         newFrame->loader().changeLocation(WTFMove(frameLoadRequest));
2286     } else if (!urlString.isEmpty()) {
2287         LockHistory lockHistory = UserGestureIndicator::processingUserGesture() ? LockHistory::No : LockHistory::Yes;
2288         newFrame->navigationScheduler().scheduleLocationChange(*activeDocument, activeDocument->securityOrigin(), completedURL, referrer, lockHistory, LockBackForwardList::No);
2289     }
2290
2291     // Navigating the new frame could result in it being detached from its page by a navigation policy delegate.
2292     if (!newFrame->page())
2293         return RefPtr<Frame> { nullptr };
2294
2295     return windowFeatures.noopener ? RefPtr<Frame> { nullptr } : newFrame;
2296 }
2297
2298 ExceptionOr<RefPtr<WindowProxy>> DOMWindow::open(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& urlString, const AtomicString& frameName, const String& windowFeaturesString)
2299 {
2300     if (!isCurrentlyDisplayedInFrame())
2301         return RefPtr<WindowProxy> { nullptr };
2302
2303     auto* activeDocument = activeWindow.document();
2304     if (!activeDocument)
2305         return RefPtr<WindowProxy> { nullptr };
2306
2307     auto* firstFrame = firstWindow.frame();
2308     if (!firstFrame)
2309         return RefPtr<WindowProxy> { nullptr };
2310
2311 #if ENABLE(CONTENT_EXTENSIONS)
2312     if (firstFrame->document()
2313         && firstFrame->page()
2314         && firstFrame->mainFrame().document()
2315         && firstFrame->mainFrame().document()->loader()) {
2316         ResourceLoadInfo resourceLoadInfo { firstFrame->document()->completeURL(urlString), firstFrame->mainFrame().document()->url(), ResourceType::Popup };
2317         for (auto& action : firstFrame->page()->userContentProvider().actionsForResourceLoad(resourceLoadInfo, *firstFrame->mainFrame().document()->loader()).first) {
2318             if (action.type() == ContentExtensions::ActionType::BlockLoad)
2319                 return RefPtr<WindowProxy> { nullptr };
2320         }
2321     }
2322 #endif
2323
2324     if (!firstWindow.allowPopUp()) {
2325         // Because FrameTree::findFrameForNavigation() returns true for empty strings, we must check for empty frame names.
2326         // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
2327         if (frameName.isEmpty() || !m_frame->loader().findFrameForNavigation(frameName, activeDocument))
2328             return RefPtr<WindowProxy> { nullptr };
2329     }
2330
2331     // Get the target frame for the special cases of _top and _parent.
2332     // In those cases, we schedule a location change right now and return early.
2333     Frame* targetFrame = nullptr;
2334     if (equalIgnoringASCIICase(frameName, "_top"))
2335         targetFrame = &m_frame->tree().top();
2336     else if (equalIgnoringASCIICase(frameName, "_parent")) {
2337         if (Frame* parent = m_frame->tree().parent())
2338             targetFrame = parent;
2339         else
2340             targetFrame = m_frame;
2341     }
2342     if (targetFrame) {
2343         if (!activeDocument->canNavigate(targetFrame))
2344             return RefPtr<WindowProxy> { nullptr };
2345
2346         URL completedURL = firstFrame->document()->completeURL(urlString);
2347
2348         if (targetFrame->document()->domWindow()->isInsecureScriptAccess(activeWindow, completedURL))
2349             return &targetFrame->windowProxy();
2350
2351         if (urlString.isEmpty())
2352             return &targetFrame->windowProxy();
2353
2354         // For whatever reason, Firefox uses the first window rather than the active window to
2355         // determine the outgoing referrer. We replicate that behavior here.
2356         LockHistory lockHistory = UserGestureIndicator::processingUserGesture() ? LockHistory::No : LockHistory::Yes;
2357         targetFrame->navigationScheduler().scheduleLocationChange(*activeDocument, activeDocument->securityOrigin(), completedURL, firstFrame->loader().outgoingReferrer(),
2358             lockHistory, LockBackForwardList::No);
2359         return &targetFrame->windowProxy();
2360     }
2361
2362     auto newFrameOrException = createWindow(urlString, frameName, parseWindowFeatures(windowFeaturesString), activeWindow, *firstFrame, *m_frame);
2363     if (newFrameOrException.hasException())
2364         return newFrameOrException.releaseException();
2365
2366     auto newFrame = newFrameOrException.releaseReturnValue();
2367     return newFrame ? &newFrame->windowProxy() : RefPtr<WindowProxy> { nullptr };
2368 }
2369
2370 void DOMWindow::showModalDialog(const String& urlString, const String& dialogFeaturesString, DOMWindow& activeWindow, DOMWindow& firstWindow, const WTF::Function<void (DOMWindow&)>& prepareDialogFunction)
2371 {
2372     if (!isCurrentlyDisplayedInFrame())
2373         return;
2374     if (!activeWindow.frame())
2375         return;
2376     Frame* firstFrame = firstWindow.frame();
2377     if (!firstFrame)
2378         return;
2379
2380     auto* page = m_frame->page();
2381     if (!page)
2382         return;
2383
2384     if (!page->arePromptsAllowed()) {
2385         printErrorMessage("Use of window.showModalDialog is not allowed while unloading a page.");
2386         return;
2387     }
2388
2389     if (!canShowModalDialog(*m_frame) || !firstWindow.allowPopUp())
2390         return;
2391
2392     auto dialogFrameOrException = createWindow(urlString, emptyAtom(), parseDialogFeatures(dialogFeaturesString, screenAvailableRect(m_frame->view())), activeWindow, *firstFrame, *m_frame, prepareDialogFunction);
2393     if (dialogFrameOrException.hasException())
2394         return;
2395     RefPtr<Frame> dialogFrame = dialogFrameOrException.releaseReturnValue();
2396     if (!dialogFrame)
2397         return;
2398     dialogFrame->page()->chrome().runModal();
2399 }
2400
2401 void DOMWindow::enableSuddenTermination()
2402 {
2403     if (Page* page = this->page())
2404         page->chrome().enableSuddenTermination();
2405 }
2406
2407 void DOMWindow::disableSuddenTermination()
2408 {
2409     if (Page* page = this->page())
2410         page->chrome().disableSuddenTermination();
2411 }
2412
2413 } // namespace WebCore