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