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