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