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