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