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