1/*
2 * Copyright (C) 2006, 2007, 2008, 2010 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 COMPUTER, 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 COMPUTER, 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 "AbstractDatabase.h"
31#include "BackForwardController.h"
32#include "BarInfo.h"
33#include "Base64.h"
34#include "BeforeUnloadEvent.h"
35#include "CSSComputedStyleDeclaration.h"
36#include "CSSRuleList.h"
37#include "CSSStyleSelector.h"
38#include "Chrome.h"
39#include "Console.h"
40#include "Crypto.h"
41#include "DOMApplicationCache.h"
42#include "DOMSelection.h"
43#include "DOMSettableTokenList.h"
44#include "DOMStringList.h"
45#include "DOMTimer.h"
46#include "DOMTokenList.h"
47#include "DOMURL.h"
48#include "Database.h"
49#include "DatabaseCallback.h"
50#include "DeviceMotionController.h"
51#include "DeviceOrientationController.h"
52#include "Document.h"
53#include "DocumentLoader.h"
54#include "Element.h"
55#include "EventException.h"
56#include "EventListener.h"
57#include "EventNames.h"
58#include "ExceptionCode.h"
59#include "FloatRect.h"
60#include "Frame.h"
61#include "FrameLoadRequest.h"
62#include "FrameLoader.h"
63#include "FrameTree.h"
64#include "FrameView.h"
65#include "HTMLFrameOwnerElement.h"
66#include "History.h"
67#include "IDBFactory.h"
68#include "IDBFactoryBackendInterface.h"
69#include "InspectorInstrumentation.h"
70#include "KURL.h"
71#include "Location.h"
72#include "MediaQueryList.h"
73#include "MediaQueryMatcher.h"
74#include "MessageEvent.h"
75#include "Navigator.h"
76#include "NotificationCenter.h"
77#include "Page.h"
78#include "PageGroup.h"
79#include "PageTransitionEvent.h"
80#include "Performance.h"
81#include "PlatformScreen.h"
82#include "PlatformString.h"
83#include "Screen.h"
84#include "SecurityOrigin.h"
85#include "SerializedScriptValue.h"
86#include "Settings.h"
87#include "Storage.h"
88#include "StorageArea.h"
89#include "StorageInfo.h"
90#include "StorageNamespace.h"
91#include "StyleMedia.h"
92#include "SuddenTermination.h"
93#include "WebKitPoint.h"
94#include "WindowFeatures.h"
95#include <algorithm>
96#include <wtf/CurrentTime.h>
97#include <wtf/MathExtras.h>
98#include <wtf/text/StringConcatenate.h>
99
100#if ENABLE(FILE_SYSTEM)
101#include "AsyncFileSystem.h"
102#include "DOMFileSystem.h"
103#include "DOMFileSystemBase.h"
104#include "EntryCallback.h"
105#include "ErrorCallback.h"
106#include "FileError.h"
107#include "FileSystemCallback.h"
108#include "FileSystemCallbacks.h"
109#include "LocalFileSystem.h"
110#endif
111
112#if ENABLE(REQUEST_ANIMATION_FRAME)
113#include "RequestAnimationFrameCallback.h"
114#endif
115
116using std::min;
117using std::max;
118
119namespace WebCore {
120
121class PostMessageTimer : public TimerBase {
122public:
123    PostMessageTimer(DOMWindow* window, PassRefPtr<SerializedScriptValue> message, const String& sourceOrigin, PassRefPtr<DOMWindow> source, PassOwnPtr<MessagePortChannelArray> channels, SecurityOrigin* targetOrigin)
124        : m_window(window)
125        , m_message(message)
126        , m_origin(sourceOrigin)
127        , m_source(source)
128        , m_channels(channels)
129        , m_targetOrigin(targetOrigin)
130    {
131    }
132
133    PassRefPtr<MessageEvent> event(ScriptExecutionContext* context)
134    {
135        OwnPtr<MessagePortArray> messagePorts = MessagePort::entanglePorts(*context, m_channels.release());
136        return MessageEvent::create(messagePorts.release(), m_message, m_origin, "", m_source);
137    }
138    SecurityOrigin* targetOrigin() const { return m_targetOrigin.get(); }
139
140private:
141    virtual void fired()
142    {
143        m_window->postMessageTimerFired(this);
144    }
145
146    RefPtr<DOMWindow> m_window;
147    RefPtr<SerializedScriptValue> m_message;
148    String m_origin;
149    RefPtr<DOMWindow> m_source;
150    OwnPtr<MessagePortChannelArray> m_channels;
151    RefPtr<SecurityOrigin> m_targetOrigin;
152};
153
154typedef HashCountedSet<DOMWindow*> DOMWindowSet;
155
156static DOMWindowSet& windowsWithUnloadEventListeners()
157{
158    DEFINE_STATIC_LOCAL(DOMWindowSet, windowsWithUnloadEventListeners, ());
159    return windowsWithUnloadEventListeners;
160}
161
162static DOMWindowSet& windowsWithBeforeUnloadEventListeners()
163{
164    DEFINE_STATIC_LOCAL(DOMWindowSet, windowsWithBeforeUnloadEventListeners, ());
165    return windowsWithBeforeUnloadEventListeners;
166}
167
168static void addUnloadEventListener(DOMWindow* domWindow)
169{
170    DOMWindowSet& set = windowsWithUnloadEventListeners();
171    if (set.isEmpty())
172        disableSuddenTermination();
173    set.add(domWindow);
174}
175
176static void removeUnloadEventListener(DOMWindow* domWindow)
177{
178    DOMWindowSet& set = windowsWithUnloadEventListeners();
179    DOMWindowSet::iterator it = set.find(domWindow);
180    if (it == set.end())
181        return;
182    set.remove(it);
183    if (set.isEmpty())
184        enableSuddenTermination();
185}
186
187static void removeAllUnloadEventListeners(DOMWindow* domWindow)
188{
189    DOMWindowSet& set = windowsWithUnloadEventListeners();
190    DOMWindowSet::iterator it = set.find(domWindow);
191    if (it == set.end())
192        return;
193    set.removeAll(it);
194    if (set.isEmpty())
195        enableSuddenTermination();
196}
197
198static void addBeforeUnloadEventListener(DOMWindow* domWindow)
199{
200    DOMWindowSet& set = windowsWithBeforeUnloadEventListeners();
201    if (set.isEmpty())
202        disableSuddenTermination();
203    set.add(domWindow);
204}
205
206static void removeBeforeUnloadEventListener(DOMWindow* domWindow)
207{
208    DOMWindowSet& set = windowsWithBeforeUnloadEventListeners();
209    DOMWindowSet::iterator it = set.find(domWindow);
210    if (it == set.end())
211        return;
212    set.remove(it);
213    if (set.isEmpty())
214        enableSuddenTermination();
215}
216
217static void removeAllBeforeUnloadEventListeners(DOMWindow* domWindow)
218{
219    DOMWindowSet& set = windowsWithBeforeUnloadEventListeners();
220    DOMWindowSet::iterator it = set.find(domWindow);
221    if (it == set.end())
222        return;
223    set.removeAll(it);
224    if (set.isEmpty())
225        enableSuddenTermination();
226}
227
228static bool allowsBeforeUnloadListeners(DOMWindow* window)
229{
230    ASSERT_ARG(window, window);
231    Frame* frame = window->frame();
232    if (!frame)
233        return false;
234    Page* page = frame->page();
235    if (!page)
236        return false;
237    return frame == page->mainFrame();
238}
239
240bool DOMWindow::dispatchAllPendingBeforeUnloadEvents()
241{
242    DOMWindowSet& set = windowsWithBeforeUnloadEventListeners();
243    if (set.isEmpty())
244        return true;
245
246    static bool alreadyDispatched = false;
247    ASSERT(!alreadyDispatched);
248    if (alreadyDispatched)
249        return true;
250
251    Vector<RefPtr<DOMWindow> > windows;
252    DOMWindowSet::iterator end = set.end();
253    for (DOMWindowSet::iterator it = set.begin(); it != end; ++it)
254        windows.append(it->first);
255
256    size_t size = windows.size();
257    for (size_t i = 0; i < size; ++i) {
258        DOMWindow* window = windows[i].get();
259        if (!set.contains(window))
260            continue;
261
262        Frame* frame = window->frame();
263        if (!frame)
264            continue;
265
266        if (!frame->loader()->shouldClose())
267            return false;
268    }
269
270    enableSuddenTermination();
271
272    alreadyDispatched = true;
273
274    return true;
275}
276
277unsigned DOMWindow::pendingUnloadEventListeners() const
278{
279    return windowsWithUnloadEventListeners().count(const_cast<DOMWindow*>(this));
280}
281
282void DOMWindow::dispatchAllPendingUnloadEvents()
283{
284    DOMWindowSet& set = windowsWithUnloadEventListeners();
285    if (set.isEmpty())
286        return;
287
288    static bool alreadyDispatched = false;
289    ASSERT(!alreadyDispatched);
290    if (alreadyDispatched)
291        return;
292
293    Vector<RefPtr<DOMWindow> > windows;
294    DOMWindowSet::iterator end = set.end();
295    for (DOMWindowSet::iterator it = set.begin(); it != end; ++it)
296        windows.append(it->first);
297
298    size_t size = windows.size();
299    for (size_t i = 0; i < size; ++i) {
300        DOMWindow* window = windows[i].get();
301        if (!set.contains(window))
302            continue;
303
304        window->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, false), window->document());
305        window->dispatchEvent(Event::create(eventNames().unloadEvent, false, false), window->document());
306    }
307
308    enableSuddenTermination();
309
310    alreadyDispatched = true;
311}
312
313// This function:
314// 1) Validates the pending changes are not changing to NaN
315// 2) Constrains the window rect to no smaller than 100 in each dimension and no
316//    bigger than the the float rect's dimensions.
317// 3) Constrain window rect to within the top and left boundaries of the screen rect
318// 4) Constraint the window rect to within the bottom and right boundaries of the
319//    screen rect.
320// 5) Translate the window rect coordinates to be within the coordinate space of
321//    the screen rect.
322void DOMWindow::adjustWindowRect(const FloatRect& screen, FloatRect& window, const FloatRect& pendingChanges)
323{
324    // Make sure we're in a valid state before adjusting dimensions.
325    ASSERT(isfinite(screen.x()));
326    ASSERT(isfinite(screen.y()));
327    ASSERT(isfinite(screen.width()));
328    ASSERT(isfinite(screen.height()));
329    ASSERT(isfinite(window.x()));
330    ASSERT(isfinite(window.y()));
331    ASSERT(isfinite(window.width()));
332    ASSERT(isfinite(window.height()));
333
334    // Update window values if new requested values are not NaN.
335    if (!isnan(pendingChanges.x()))
336        window.setX(pendingChanges.x());
337    if (!isnan(pendingChanges.y()))
338        window.setY(pendingChanges.y());
339    if (!isnan(pendingChanges.width()))
340        window.setWidth(pendingChanges.width());
341    if (!isnan(pendingChanges.height()))
342        window.setHeight(pendingChanges.height());
343
344    // Resize the window to between 100 and the screen width and height.
345    window.setWidth(min(max(100.0f, window.width()), screen.width()));
346    window.setHeight(min(max(100.0f, window.height()), screen.height()));
347
348    // Constrain the window position to the screen.
349    window.setX(max(screen.x(), min(window.x(), screen.maxX() - window.width())));
350    window.setY(max(screen.y(), min(window.y(), screen.maxY() - window.height())));
351}
352
353// FIXME: We can remove this function once V8 showModalDialog is changed to use DOMWindow.
354void DOMWindow::parseModalDialogFeatures(const String& string, HashMap<String, String>& map)
355{
356    WindowFeatures::parseDialogFeatures(string, map);
357}
358
359bool DOMWindow::allowPopUp(Frame* firstFrame)
360{
361    ASSERT(firstFrame);
362
363    if (ScriptController::processingUserGesture())
364        return true;
365
366    Settings* settings = firstFrame->settings();
367    return settings && settings->javaScriptCanOpenWindowsAutomatically();
368}
369
370bool DOMWindow::allowPopUp()
371{
372    return m_frame && allowPopUp(m_frame);
373}
374
375bool DOMWindow::canShowModalDialog(const Frame* frame)
376{
377    if (!frame)
378        return false;
379    Page* page = frame->page();
380    if (!page)
381        return false;
382    return page->chrome()->canRunModal();
383}
384
385bool DOMWindow::canShowModalDialogNow(const Frame* frame)
386{
387    if (!frame)
388        return false;
389    Page* page = frame->page();
390    if (!page)
391        return false;
392    return page->chrome()->canRunModalNow();
393}
394
395DOMWindow::DOMWindow(Frame* frame)
396    : m_shouldPrintWhenFinishedLoading(false)
397    , m_frame(frame)
398    , m_printTimer(this, &DOMWindow::printTimerFired)
399{
400}
401
402DOMWindow::~DOMWindow()
403{
404    if (m_frame)
405        m_frame->clearFormerDOMWindow(this);
406
407    removeAllUnloadEventListeners(this);
408    removeAllBeforeUnloadEventListeners(this);
409}
410
411ScriptExecutionContext* DOMWindow::scriptExecutionContext() const
412{
413    return document();
414}
415
416PassRefPtr<MediaQueryList> DOMWindow::matchMedia(const String& media)
417{
418    return document() ? document()->mediaQueryMatcher()->matchMedia(media) : 0;
419}
420
421void DOMWindow::disconnectFrame()
422{
423    m_frame = 0;
424    clear();
425}
426
427void DOMWindow::clear()
428{
429    if (m_screen)
430        m_screen->disconnectFrame();
431    m_screen = 0;
432
433    if (m_selection)
434        m_selection->disconnectFrame();
435    m_selection = 0;
436
437    if (m_history)
438        m_history->disconnectFrame();
439    m_history = 0;
440
441    m_crypto = 0;
442
443    if (m_locationbar)
444        m_locationbar->disconnectFrame();
445    m_locationbar = 0;
446
447    if (m_menubar)
448        m_menubar->disconnectFrame();
449    m_menubar = 0;
450
451    if (m_personalbar)
452        m_personalbar->disconnectFrame();
453    m_personalbar = 0;
454
455    if (m_scrollbars)
456        m_scrollbars->disconnectFrame();
457    m_scrollbars = 0;
458
459    if (m_statusbar)
460        m_statusbar->disconnectFrame();
461    m_statusbar = 0;
462
463    if (m_toolbar)
464        m_toolbar->disconnectFrame();
465    m_toolbar = 0;
466
467    if (m_console)
468        m_console->disconnectFrame();
469    m_console = 0;
470
471    if (m_navigator)
472        m_navigator->disconnectFrame();
473    m_navigator = 0;
474
475#if ENABLE(WEB_TIMING)
476    if (m_performance)
477        m_performance->disconnectFrame();
478    m_performance = 0;
479#endif
480
481    if (m_location)
482        m_location->disconnectFrame();
483    m_location = 0;
484
485    if (m_media)
486        m_media->disconnectFrame();
487    m_media = 0;
488
489#if ENABLE(DOM_STORAGE)
490    if (m_sessionStorage)
491        m_sessionStorage->disconnectFrame();
492    m_sessionStorage = 0;
493
494    if (m_localStorage)
495        m_localStorage->disconnectFrame();
496    m_localStorage = 0;
497#endif
498
499#if ENABLE(OFFLINE_WEB_APPLICATIONS)
500    if (m_applicationCache)
501        m_applicationCache->disconnectFrame();
502    m_applicationCache = 0;
503#endif
504
505#if ENABLE(NOTIFICATIONS)
506    if (m_notifications)
507        m_notifications->disconnectFrame();
508    m_notifications = 0;
509#endif
510
511#if ENABLE(INDEXED_DATABASE)
512    m_idbFactory = 0;
513#endif
514}
515
516#if ENABLE(ORIENTATION_EVENTS)
517int DOMWindow::orientation() const
518{
519    if (!m_frame)
520        return 0;
521
522    return m_frame->orientation();
523}
524#endif
525
526Screen* DOMWindow::screen() const
527{
528    if (!m_screen)
529        m_screen = Screen::create(m_frame);
530    return m_screen.get();
531}
532
533History* DOMWindow::history() const
534{
535    if (!m_history)
536        m_history = History::create(m_frame);
537    return m_history.get();
538}
539
540Crypto* DOMWindow::crypto() const
541{
542    if (!m_crypto)
543        m_crypto = Crypto::create();
544    return m_crypto.get();
545}
546
547BarInfo* DOMWindow::locationbar() const
548{
549    if (!m_locationbar)
550        m_locationbar = BarInfo::create(m_frame, BarInfo::Locationbar);
551    return m_locationbar.get();
552}
553
554BarInfo* DOMWindow::menubar() const
555{
556    if (!m_menubar)
557        m_menubar = BarInfo::create(m_frame, BarInfo::Menubar);
558    return m_menubar.get();
559}
560
561BarInfo* DOMWindow::personalbar() const
562{
563    if (!m_personalbar)
564        m_personalbar = BarInfo::create(m_frame, BarInfo::Personalbar);
565    return m_personalbar.get();
566}
567
568BarInfo* DOMWindow::scrollbars() const
569{
570    if (!m_scrollbars)
571        m_scrollbars = BarInfo::create(m_frame, BarInfo::Scrollbars);
572    return m_scrollbars.get();
573}
574
575BarInfo* DOMWindow::statusbar() const
576{
577    if (!m_statusbar)
578        m_statusbar = BarInfo::create(m_frame, BarInfo::Statusbar);
579    return m_statusbar.get();
580}
581
582BarInfo* DOMWindow::toolbar() const
583{
584    if (!m_toolbar)
585        m_toolbar = BarInfo::create(m_frame, BarInfo::Toolbar);
586    return m_toolbar.get();
587}
588
589Console* DOMWindow::console() const
590{
591    if (!m_console)
592        m_console = Console::create(m_frame);
593    return m_console.get();
594}
595
596#if ENABLE(OFFLINE_WEB_APPLICATIONS)
597DOMApplicationCache* DOMWindow::applicationCache() const
598{
599    if (!m_applicationCache)
600        m_applicationCache = DOMApplicationCache::create(m_frame);
601    return m_applicationCache.get();
602}
603#endif
604
605Navigator* DOMWindow::navigator() const
606{
607    if (!m_navigator)
608        m_navigator = Navigator::create(m_frame);
609    return m_navigator.get();
610}
611
612#if ENABLE(WEB_TIMING)
613Performance* DOMWindow::performance() const
614{
615    if (!m_performance)
616        m_performance = Performance::create(m_frame);
617    return m_performance.get();
618}
619#endif
620
621Location* DOMWindow::location() const
622{
623    if (!m_location)
624        m_location = Location::create(m_frame);
625    return m_location.get();
626}
627
628#if ENABLE(DOM_STORAGE)
629Storage* DOMWindow::sessionStorage(ExceptionCode& ec) const
630{
631    if (m_sessionStorage)
632        return m_sessionStorage.get();
633
634    Document* document = this->document();
635    if (!document)
636        return 0;
637
638    if (!document->securityOrigin()->canAccessLocalStorage()) {
639        ec = SECURITY_ERR;
640        return 0;
641    }
642
643    Page* page = document->page();
644    if (!page)
645        return 0;
646
647    RefPtr<StorageArea> storageArea = page->sessionStorage()->storageArea(document->securityOrigin());
648    InspectorInstrumentation::didUseDOMStorage(page, storageArea.get(), false, m_frame);
649
650    m_sessionStorage = Storage::create(m_frame, storageArea.release());
651    return m_sessionStorage.get();
652}
653
654Storage* DOMWindow::localStorage(ExceptionCode& ec) const
655{
656    if (m_localStorage)
657        return m_localStorage.get();
658
659    Document* document = this->document();
660    if (!document)
661        return 0;
662
663    if (!document->securityOrigin()->canAccessLocalStorage()) {
664        ec = SECURITY_ERR;
665        return 0;
666    }
667
668    Page* page = document->page();
669    if (!page)
670        return 0;
671
672    if (!page->settings()->localStorageEnabled())
673        return 0;
674
675    RefPtr<StorageArea> storageArea = page->group().localStorage()->storageArea(document->securityOrigin());
676    InspectorInstrumentation::didUseDOMStorage(page, storageArea.get(), true, m_frame);
677
678    m_localStorage = Storage::create(m_frame, storageArea.release());
679    return m_localStorage.get();
680}
681#endif
682
683#if ENABLE(NOTIFICATIONS)
684NotificationCenter* DOMWindow::webkitNotifications() const
685{
686    if (m_notifications)
687        return m_notifications.get();
688
689    Document* document = this->document();
690    if (!document)
691        return 0;
692
693    Page* page = document->page();
694    if (!page)
695        return 0;
696
697    NotificationPresenter* provider = page->chrome()->notificationPresenter();
698    if (provider)
699        m_notifications = NotificationCenter::create(document, provider);
700
701    return m_notifications.get();
702}
703#endif
704
705void DOMWindow::pageDestroyed()
706{
707#if ENABLE(NOTIFICATIONS)
708    // Clearing Notifications requests involves accessing the client so it must be done
709    // before the frame is detached.
710    if (m_notifications)
711        m_notifications->disconnectFrame();
712    m_notifications = 0;
713#endif
714}
715
716void DOMWindow::resetGeolocation()
717{
718    // Geolocation should cancel activities and permission requests when the page is detached.
719    if (m_navigator)
720        m_navigator->resetGeolocation();
721}
722
723#if ENABLE(INDEXED_DATABASE)
724IDBFactory* DOMWindow::webkitIndexedDB() const
725{
726    if (m_idbFactory)
727        return m_idbFactory.get();
728
729    Document* document = this->document();
730    if (!document)
731        return 0;
732
733    // FIXME: See if access is allowed.
734
735    Page* page = document->page();
736    if (!page)
737        return 0;
738
739    // FIXME: See if indexedDatabase access is allowed.
740
741    m_idbFactory = IDBFactory::create(page->group().idbFactory());
742    return m_idbFactory.get();
743}
744#endif
745
746#if ENABLE(FILE_SYSTEM)
747void DOMWindow::webkitRequestFileSystem(int type, long long size, PassRefPtr<FileSystemCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback)
748{
749    Document* document = this->document();
750    if (!document)
751        return;
752
753    if (!AsyncFileSystem::isAvailable() || !document->securityOrigin()->canAccessFileSystem()) {
754        DOMFileSystem::scheduleCallback(document, errorCallback, FileError::create(FileError::SECURITY_ERR));
755        return;
756    }
757
758    AsyncFileSystem::Type fileSystemType = static_cast<AsyncFileSystem::Type>(type);
759    if (fileSystemType != AsyncFileSystem::Temporary && fileSystemType != AsyncFileSystem::Persistent && fileSystemType != AsyncFileSystem::External) {
760        DOMFileSystem::scheduleCallback(document, errorCallback, FileError::create(FileError::INVALID_MODIFICATION_ERR));
761        return;
762    }
763
764    LocalFileSystem::localFileSystem().requestFileSystem(document, fileSystemType, size, FileSystemCallbacks::create(successCallback, errorCallback, document), false);
765}
766
767void DOMWindow::webkitResolveLocalFileSystemURL(const String& url, PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback)
768{
769    Document* document = this->document();
770    if (!document)
771        return;
772
773    SecurityOrigin* securityOrigin = document->securityOrigin();
774    KURL completedURL = document->completeURL(url);
775    if (!AsyncFileSystem::isAvailable() || !securityOrigin->canAccessFileSystem() || !securityOrigin->canRequest(completedURL)) {
776        DOMFileSystem::scheduleCallback(document, errorCallback, FileError::create(FileError::SECURITY_ERR));
777        return;
778    }
779
780    AsyncFileSystem::Type type;
781    String filePath;
782    if (!completedURL.isValid() || !DOMFileSystemBase::crackFileSystemURL(completedURL, type, filePath)) {
783        DOMFileSystem::scheduleCallback(document, errorCallback, FileError::create(FileError::ENCODING_ERR));
784        return;
785    }
786
787    LocalFileSystem::localFileSystem().readFileSystem(document, type, ResolveURICallbacks::create(successCallback, errorCallback, document, filePath));
788}
789
790COMPILE_ASSERT(static_cast<int>(DOMWindow::EXTERNAL) == static_cast<int>(AsyncFileSystem::External), enum_mismatch);
791
792COMPILE_ASSERT(static_cast<int>(DOMWindow::TEMPORARY) == static_cast<int>(AsyncFileSystem::Temporary), enum_mismatch);
793COMPILE_ASSERT(static_cast<int>(DOMWindow::PERSISTENT) == static_cast<int>(AsyncFileSystem::Persistent), enum_mismatch);
794
795#endif
796
797void DOMWindow::postMessage(PassRefPtr<SerializedScriptValue> message, MessagePort* port, const String& targetOrigin, DOMWindow* source, ExceptionCode& ec)
798{
799    MessagePortArray ports;
800    if (port)
801        ports.append(port);
802    postMessage(message, &ports, targetOrigin, source, ec);
803}
804
805void DOMWindow::postMessage(PassRefPtr<SerializedScriptValue> message, const MessagePortArray* ports, const String& targetOrigin, DOMWindow* source, ExceptionCode& ec)
806{
807    if (!m_frame)
808        return;
809
810    // Compute the target origin.  We need to do this synchronously in order
811    // to generate the SYNTAX_ERR exception correctly.
812    RefPtr<SecurityOrigin> target;
813    if (targetOrigin != "*") {
814        target = SecurityOrigin::createFromString(targetOrigin);
815        if (target->isEmpty()) {
816            ec = SYNTAX_ERR;
817            return;
818        }
819    }
820
821    OwnPtr<MessagePortChannelArray> channels = MessagePort::disentanglePorts(ports, ec);
822    if (ec)
823        return;
824
825    // Capture the source of the message.  We need to do this synchronously
826    // in order to capture the source of the message correctly.
827    Document* sourceDocument = source->document();
828    if (!sourceDocument)
829        return;
830    String sourceOrigin = sourceDocument->securityOrigin()->toString();
831
832    // Schedule the message.
833    PostMessageTimer* timer = new PostMessageTimer(this, message, sourceOrigin, source, channels.release(), target.get());
834    timer->startOneShot(0);
835}
836
837void DOMWindow::postMessageTimerFired(PostMessageTimer* t)
838{
839    OwnPtr<PostMessageTimer> timer(t);
840
841    if (!document())
842        return;
843
844    if (timer->targetOrigin()) {
845        // Check target origin now since the target document may have changed since the simer was scheduled.
846        if (!timer->targetOrigin()->isSameSchemeHostPort(document()->securityOrigin())) {
847            String message = makeString("Unable to post message to ", timer->targetOrigin()->toString(),
848                                        ". Recipient has origin ", document()->securityOrigin()->toString(), ".\n");
849            console()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, 0, String());
850            return;
851        }
852    }
853
854    dispatchEvent(timer->event(document()));
855}
856
857DOMSelection* DOMWindow::getSelection()
858{
859    if (!m_selection)
860        m_selection = DOMSelection::create(m_frame);
861    return m_selection.get();
862}
863
864Element* DOMWindow::frameElement() const
865{
866    if (!m_frame)
867        return 0;
868
869    return m_frame->ownerElement();
870}
871
872void DOMWindow::focus()
873{
874    if (!m_frame)
875        return;
876
877    Page* page = m_frame->page();
878    if (!page)
879        return;
880
881    // If we're a top level window, bring the window to the front.
882    if (m_frame == page->mainFrame())
883        page->chrome()->focus();
884
885    if (!m_frame)
886        return;
887
888    m_frame->eventHandler()->focusDocumentView();
889}
890
891void DOMWindow::blur()
892{
893    if (!m_frame)
894        return;
895
896    Page* page = m_frame->page();
897    if (!page)
898        return;
899
900    if (m_frame != page->mainFrame())
901        return;
902
903    page->chrome()->unfocus();
904}
905
906void DOMWindow::close(ScriptExecutionContext* context)
907{
908    if (!m_frame)
909        return;
910
911    Page* page = m_frame->page();
912    if (!page)
913        return;
914
915    if (m_frame != page->mainFrame())
916        return;
917
918    if (context) {
919        ASSERT(WTF::isMainThread());
920        Frame* activeFrame = static_cast<Document*>(context)->frame();
921        if (!activeFrame)
922            return;
923
924        if (!activeFrame->loader()->shouldAllowNavigation(m_frame))
925            return;
926    }
927
928    Settings* settings = m_frame->settings();
929    bool allowScriptsToCloseWindows = settings && settings->allowScriptsToCloseWindows();
930
931    if (!(page->openedByDOM() || page->backForward()->count() <= 1 || allowScriptsToCloseWindows))
932        return;
933
934    if (!m_frame->loader()->shouldClose())
935        return;
936
937    page->chrome()->closeWindowSoon();
938}
939
940void DOMWindow::print()
941{
942    if (!m_frame)
943        return;
944
945    Page* page = m_frame->page();
946    if (!page)
947        return;
948
949    if (m_frame->loader()->activeDocumentLoader()->isLoading()) {
950        m_shouldPrintWhenFinishedLoading = true;
951        return;
952    }
953    m_shouldPrintWhenFinishedLoading = false;
954    page->chrome()->print(m_frame);
955}
956
957void DOMWindow::printTimerFired(Timer<DOMWindow>* timer)
958{
959    ASSERT_UNUSED(timer, timer == &m_printTimer);
960    print();
961}
962
963void DOMWindow::stop()
964{
965    if (!m_frame)
966        return;
967
968    // We must check whether the load is complete asynchronously, because we might still be parsing
969    // the document until the callstack unwinds.
970    m_frame->loader()->stopForUserCancel(true);
971}
972
973void DOMWindow::alert(const String& message)
974{
975    if (!m_frame)
976        return;
977
978    m_frame->document()->updateStyleIfNeeded();
979
980    Page* page = m_frame->page();
981    if (!page)
982        return;
983
984    page->chrome()->runJavaScriptAlert(m_frame, message);
985}
986
987bool DOMWindow::confirm(const String& message)
988{
989    if (!m_frame)
990        return false;
991
992    m_frame->document()->updateStyleIfNeeded();
993
994    Page* page = m_frame->page();
995    if (!page)
996        return false;
997
998    return page->chrome()->runJavaScriptConfirm(m_frame, message);
999}
1000
1001String DOMWindow::prompt(const String& message, const String& defaultValue)
1002{
1003    if (!m_frame)
1004        return String();
1005
1006    m_frame->document()->updateStyleIfNeeded();
1007
1008    Page* page = m_frame->page();
1009    if (!page)
1010        return String();
1011
1012    String returnValue;
1013    if (page->chrome()->runJavaScriptPrompt(m_frame, message, defaultValue, returnValue))
1014        return returnValue;
1015
1016    return String();
1017}
1018
1019String DOMWindow::btoa(const String& stringToEncode, ExceptionCode& ec)
1020{
1021    if (stringToEncode.isNull())
1022        return String();
1023
1024    if (!stringToEncode.containsOnlyLatin1()) {
1025        ec = INVALID_CHARACTER_ERR;
1026        return String();
1027    }
1028
1029    return base64Encode(stringToEncode.latin1());
1030}
1031
1032String DOMWindow::atob(const String& encodedString, ExceptionCode& ec)
1033{
1034    if (encodedString.isNull())
1035        return String();
1036
1037    if (!encodedString.containsOnlyLatin1()) {
1038        ec = INVALID_CHARACTER_ERR;
1039        return String();
1040    }
1041
1042    Vector<char> out;
1043    if (!base64Decode(encodedString, out, FailOnInvalidCharacter)) {
1044        ec = INVALID_CHARACTER_ERR;
1045        return String();
1046    }
1047
1048    return String(out.data(), out.size());
1049}
1050
1051bool DOMWindow::find(const String& string, bool caseSensitive, bool backwards, bool wrap, bool /*wholeWord*/, bool /*searchInFrames*/, bool /*showDialog*/) const
1052{
1053    if (!m_frame)
1054        return false;
1055
1056    // FIXME (13016): Support wholeWord, searchInFrames and showDialog
1057    return m_frame->editor()->findString(string, !backwards, caseSensitive, wrap, false);
1058}
1059
1060bool DOMWindow::offscreenBuffering() const
1061{
1062    return true;
1063}
1064
1065int DOMWindow::outerHeight() const
1066{
1067    if (!m_frame)
1068        return 0;
1069
1070    Page* page = m_frame->page();
1071    if (!page)
1072        return 0;
1073
1074    return static_cast<int>(page->chrome()->windowRect().height());
1075}
1076
1077int DOMWindow::outerWidth() const
1078{
1079    if (!m_frame)
1080        return 0;
1081
1082    Page* page = m_frame->page();
1083    if (!page)
1084        return 0;
1085
1086    return static_cast<int>(page->chrome()->windowRect().width());
1087}
1088
1089int DOMWindow::innerHeight() const
1090{
1091    if (!m_frame)
1092        return 0;
1093
1094    FrameView* view = m_frame->view();
1095    if (!view)
1096        return 0;
1097
1098#if PLATFORM(ANDROID)
1099    return static_cast<int>(view->actualHeight() / m_frame->pageZoomFactor());
1100#else
1101    return static_cast<int>(view->height() / m_frame->pageZoomFactor());
1102#endif
1103}
1104
1105int DOMWindow::innerWidth() const
1106{
1107    if (!m_frame)
1108        return 0;
1109
1110    FrameView* view = m_frame->view();
1111    if (!view)
1112        return 0;
1113
1114#if PLATFORM(ANDROID)
1115    return static_cast<int>(view->actualWidth() / m_frame->pageZoomFactor());
1116#else
1117    return static_cast<int>(view->width() / m_frame->pageZoomFactor());
1118#endif
1119}
1120
1121int DOMWindow::screenX() const
1122{
1123    if (!m_frame)
1124        return 0;
1125
1126    Page* page = m_frame->page();
1127    if (!page)
1128        return 0;
1129
1130    return static_cast<int>(page->chrome()->windowRect().x());
1131}
1132
1133int DOMWindow::screenY() const
1134{
1135    if (!m_frame)
1136        return 0;
1137
1138    Page* page = m_frame->page();
1139    if (!page)
1140        return 0;
1141
1142    return static_cast<int>(page->chrome()->windowRect().y());
1143}
1144
1145int DOMWindow::scrollX() const
1146{
1147    if (!m_frame)
1148        return 0;
1149
1150    FrameView* view = m_frame->view();
1151    if (!view)
1152        return 0;
1153
1154    m_frame->document()->updateLayoutIgnorePendingStylesheets();
1155
1156#if PLATFORM(ANDROID)
1157    return static_cast<int>(view->actualScrollX() / m_frame->pageZoomFactor());
1158#else
1159    return static_cast<int>(view->scrollX() / m_frame->pageZoomFactor());
1160#endif
1161}
1162
1163int DOMWindow::scrollY() const
1164{
1165    if (!m_frame)
1166        return 0;
1167
1168    FrameView* view = m_frame->view();
1169    if (!view)
1170        return 0;
1171
1172    m_frame->document()->updateLayoutIgnorePendingStylesheets();
1173
1174#if PLATFORM(ANDROID)
1175    return static_cast<int>(view->actualScrollY() / m_frame->pageZoomFactor());
1176#else
1177    return static_cast<int>(view->scrollY() / m_frame->pageZoomFactor());
1178#endif
1179}
1180
1181bool DOMWindow::closed() const
1182{
1183    return !m_frame;
1184}
1185
1186unsigned DOMWindow::length() const
1187{
1188    if (!m_frame)
1189        return 0;
1190
1191    return m_frame->tree()->childCount();
1192}
1193
1194String DOMWindow::name() const
1195{
1196    if (!m_frame)
1197        return String();
1198
1199    return m_frame->tree()->name();
1200}
1201
1202void DOMWindow::setName(const String& string)
1203{
1204    if (!m_frame)
1205        return;
1206
1207    m_frame->tree()->setName(string);
1208}
1209
1210void DOMWindow::setStatus(const String& string)
1211{
1212    m_status = string;
1213
1214    if (!m_frame)
1215        return;
1216
1217    Page* page = m_frame->page();
1218    if (!page)
1219        return;
1220
1221    ASSERT(m_frame->document()); // Client calls shouldn't be made when the frame is in inconsistent state.
1222    page->chrome()->setStatusbarText(m_frame, m_status);
1223}
1224
1225void DOMWindow::setDefaultStatus(const String& string)
1226{
1227    m_defaultStatus = string;
1228
1229    if (!m_frame)
1230        return;
1231
1232    Page* page = m_frame->page();
1233    if (!page)
1234        return;
1235
1236    ASSERT(m_frame->document()); // Client calls shouldn't be made when the frame is in inconsistent state.
1237    page->chrome()->setStatusbarText(m_frame, m_defaultStatus);
1238}
1239
1240DOMWindow* DOMWindow::self() const
1241{
1242    if (!m_frame)
1243        return 0;
1244
1245    return m_frame->domWindow();
1246}
1247
1248DOMWindow* DOMWindow::opener() const
1249{
1250    if (!m_frame)
1251        return 0;
1252
1253    Frame* opener = m_frame->loader()->opener();
1254    if (!opener)
1255        return 0;
1256
1257    return opener->domWindow();
1258}
1259
1260DOMWindow* DOMWindow::parent() const
1261{
1262    if (!m_frame)
1263        return 0;
1264
1265    Frame* parent = m_frame->tree()->parent(true);
1266    if (parent)
1267        return parent->domWindow();
1268
1269    return m_frame->domWindow();
1270}
1271
1272DOMWindow* DOMWindow::top() const
1273{
1274    if (!m_frame)
1275        return 0;
1276
1277    Page* page = m_frame->page();
1278    if (!page)
1279        return 0;
1280
1281    return m_frame->tree()->top(true)->domWindow();
1282}
1283
1284Document* DOMWindow::document() const
1285{
1286    // FIXME: This function shouldn't need a frame to work.
1287    if (!m_frame)
1288        return 0;
1289
1290    // The m_frame pointer is not zeroed out when the window is put into b/f cache, so it can hold an unrelated document/window pair.
1291    // FIXME: We should always zero out the frame pointer on navigation to avoid accidentally accessing the new frame content.
1292    if (m_frame->domWindow() != this)
1293        return 0;
1294
1295    ASSERT(m_frame->document());
1296    return m_frame->document();
1297}
1298
1299PassRefPtr<StyleMedia> DOMWindow::styleMedia() const
1300{
1301    if (!m_media)
1302        m_media = StyleMedia::create(m_frame);
1303    return m_media.get();
1304}
1305
1306PassRefPtr<CSSStyleDeclaration> DOMWindow::getComputedStyle(Element* elt, const String& pseudoElt) const
1307{
1308    if (!elt)
1309        return 0;
1310
1311    return computedStyle(elt, false, pseudoElt);
1312}
1313
1314PassRefPtr<CSSRuleList> DOMWindow::getMatchedCSSRules(Element* elt, const String&, bool authorOnly) const
1315{
1316    if (!m_frame)
1317        return 0;
1318
1319    Settings* settings = m_frame->settings();
1320    return m_frame->document()->styleSelector()->styleRulesForElement(elt, authorOnly, false, settings && settings->crossOriginCheckInGetMatchedCSSRulesDisabled() ? AllCSSRules : SameOriginCSSRulesOnly);
1321}
1322
1323PassRefPtr<WebKitPoint> DOMWindow::webkitConvertPointFromNodeToPage(Node* node, const WebKitPoint* p) const
1324{
1325    if (!node || !p)
1326        return 0;
1327
1328    m_frame->document()->updateLayoutIgnorePendingStylesheets();
1329
1330    FloatPoint pagePoint(p->x(), p->y());
1331    pagePoint = node->convertToPage(pagePoint);
1332    return WebKitPoint::create(pagePoint.x(), pagePoint.y());
1333}
1334
1335PassRefPtr<WebKitPoint> DOMWindow::webkitConvertPointFromPageToNode(Node* node, const WebKitPoint* p) const
1336{
1337    if (!node || !p)
1338        return 0;
1339
1340    m_frame->document()->updateLayoutIgnorePendingStylesheets();
1341
1342    FloatPoint nodePoint(p->x(), p->y());
1343    nodePoint = node->convertFromPage(nodePoint);
1344    return WebKitPoint::create(nodePoint.x(), nodePoint.y());
1345}
1346
1347double DOMWindow::devicePixelRatio() const
1348{
1349    if (!m_frame)
1350        return 0.0;
1351
1352    Page* page = m_frame->page();
1353    if (!page)
1354        return 0.0;
1355
1356    return page->chrome()->scaleFactor();
1357}
1358
1359#if ENABLE(DATABASE)
1360PassRefPtr<Database> DOMWindow::openDatabase(const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec)
1361{
1362    RefPtr<Database> database = 0;
1363    if (m_frame && AbstractDatabase::isAvailable() && m_frame->document()->securityOrigin()->canAccessDatabase())
1364        database = Database::openDatabase(m_frame->document(), name, version, displayName, estimatedSize, creationCallback, ec);
1365
1366    if (!database && !ec)
1367        ec = SECURITY_ERR;
1368
1369    return database;
1370}
1371#endif
1372
1373void DOMWindow::scrollBy(int x, int y) const
1374{
1375    if (!m_frame)
1376        return;
1377
1378    m_frame->document()->updateLayoutIgnorePendingStylesheets();
1379
1380    RefPtr<FrameView> view = m_frame->view();
1381    if (!view)
1382        return;
1383
1384    view->scrollBy(IntSize(x, y));
1385}
1386
1387void DOMWindow::scrollTo(int x, int y) const
1388{
1389    if (!m_frame)
1390        return;
1391
1392    m_frame->document()->updateLayoutIgnorePendingStylesheets();
1393
1394    RefPtr<FrameView> view = m_frame->view();
1395    if (!view)
1396        return;
1397
1398    int zoomedX = static_cast<int>(x * m_frame->pageZoomFactor());
1399    int zoomedY = static_cast<int>(y * m_frame->pageZoomFactor());
1400    view->setScrollPosition(IntPoint(zoomedX, zoomedY));
1401}
1402
1403void DOMWindow::moveBy(float x, float y) const
1404{
1405    if (!m_frame)
1406        return;
1407
1408    Page* page = m_frame->page();
1409    if (!page)
1410        return;
1411
1412    if (m_frame != page->mainFrame())
1413        return;
1414
1415    FloatRect fr = page->chrome()->windowRect();
1416    FloatRect update = fr;
1417    update.move(x, y);
1418    // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1419    adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1420    page->chrome()->setWindowRect(fr);
1421}
1422
1423void DOMWindow::moveTo(float x, float y) const
1424{
1425    if (!m_frame)
1426        return;
1427
1428    Page* page = m_frame->page();
1429    if (!page)
1430        return;
1431
1432    if (m_frame != page->mainFrame())
1433        return;
1434
1435    FloatRect fr = page->chrome()->windowRect();
1436    FloatRect sr = screenAvailableRect(page->mainFrame()->view());
1437    fr.setLocation(sr.location());
1438    FloatRect update = fr;
1439    update.move(x, y);
1440    // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1441    adjustWindowRect(sr, fr, update);
1442    page->chrome()->setWindowRect(fr);
1443}
1444
1445void DOMWindow::resizeBy(float x, float y) const
1446{
1447    if (!m_frame)
1448        return;
1449
1450    Page* page = m_frame->page();
1451    if (!page)
1452        return;
1453
1454    if (m_frame != page->mainFrame())
1455        return;
1456
1457    FloatRect fr = page->chrome()->windowRect();
1458    FloatSize dest = fr.size() + FloatSize(x, y);
1459    FloatRect update(fr.location(), dest);
1460    adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1461    page->chrome()->setWindowRect(fr);
1462}
1463
1464void DOMWindow::resizeTo(float width, float height) const
1465{
1466    if (!m_frame)
1467        return;
1468
1469    Page* page = m_frame->page();
1470    if (!page)
1471        return;
1472
1473    if (m_frame != page->mainFrame())
1474        return;
1475
1476    FloatRect fr = page->chrome()->windowRect();
1477    FloatSize dest = FloatSize(width, height);
1478    FloatRect update(fr.location(), dest);
1479    adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1480    page->chrome()->setWindowRect(fr);
1481}
1482
1483int DOMWindow::setTimeout(PassOwnPtr<ScheduledAction> action, int timeout, ExceptionCode& ec)
1484{
1485    ScriptExecutionContext* context = scriptExecutionContext();
1486    if (!context) {
1487        ec = INVALID_ACCESS_ERR;
1488        return -1;
1489    }
1490    return DOMTimer::install(context, action, timeout, true);
1491}
1492
1493void DOMWindow::clearTimeout(int timeoutId)
1494{
1495    ScriptExecutionContext* context = scriptExecutionContext();
1496    if (!context)
1497        return;
1498    DOMTimer::removeById(context, timeoutId);
1499}
1500
1501int DOMWindow::setInterval(PassOwnPtr<ScheduledAction> action, int timeout, ExceptionCode& ec)
1502{
1503    ScriptExecutionContext* context = scriptExecutionContext();
1504    if (!context) {
1505        ec = INVALID_ACCESS_ERR;
1506        return -1;
1507    }
1508    return DOMTimer::install(context, action, timeout, false);
1509}
1510
1511void DOMWindow::clearInterval(int timeoutId)
1512{
1513    ScriptExecutionContext* context = scriptExecutionContext();
1514    if (!context)
1515        return;
1516    DOMTimer::removeById(context, timeoutId);
1517}
1518
1519#if ENABLE(REQUEST_ANIMATION_FRAME)
1520int DOMWindow::webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback, Element* e)
1521{
1522    if (Document* d = document())
1523        return d->webkitRequestAnimationFrame(callback, e);
1524    return 0;
1525}
1526
1527void DOMWindow::webkitCancelRequestAnimationFrame(int id)
1528{
1529    if (Document* d = document())
1530        d->webkitCancelRequestAnimationFrame(id);
1531}
1532#endif
1533
1534bool DOMWindow::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
1535{
1536    if (!EventTarget::addEventListener(eventType, listener, useCapture))
1537        return false;
1538
1539    if (Document* document = this->document())
1540        document->addListenerTypeIfNeeded(eventType);
1541
1542    if (eventType == eventNames().unloadEvent)
1543        addUnloadEventListener(this);
1544    else if (eventType == eventNames().beforeunloadEvent && allowsBeforeUnloadListeners(this))
1545        addBeforeUnloadEventListener(this);
1546#if ENABLE(DEVICE_ORIENTATION)
1547    else if (eventType == eventNames().devicemotionEvent && frame() && frame()->page() && frame()->page()->deviceMotionController())
1548        frame()->page()->deviceMotionController()->addListener(this);
1549    else if (eventType == eventNames().deviceorientationEvent && frame() && frame()->page() && frame()->page()->deviceOrientationController())
1550        frame()->page()->deviceOrientationController()->addListener(this);
1551#endif
1552
1553    return true;
1554}
1555
1556bool DOMWindow::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture)
1557{
1558    if (!EventTarget::removeEventListener(eventType, listener, useCapture))
1559        return false;
1560
1561    if (eventType == eventNames().unloadEvent)
1562        removeUnloadEventListener(this);
1563    else if (eventType == eventNames().beforeunloadEvent && allowsBeforeUnloadListeners(this))
1564        removeBeforeUnloadEventListener(this);
1565#if ENABLE(DEVICE_ORIENTATION)
1566    else if (eventType == eventNames().devicemotionEvent && frame() && frame()->page() && frame()->page()->deviceMotionController())
1567        frame()->page()->deviceMotionController()->removeListener(this);
1568    else if (eventType == eventNames().deviceorientationEvent && frame() && frame()->page() && frame()->page()->deviceOrientationController())
1569        frame()->page()->deviceOrientationController()->removeListener(this);
1570#endif
1571
1572    return true;
1573}
1574
1575void DOMWindow::dispatchLoadEvent()
1576{
1577    RefPtr<Event> loadEvent(Event::create(eventNames().loadEvent, false, false));
1578    if (m_frame && m_frame->loader()->documentLoader() && !m_frame->loader()->documentLoader()->timing()->loadEventStart) {
1579        // The DocumentLoader (and thus its DocumentLoadTiming) might get destroyed while dispatching
1580        // the event, so protect it to prevent writing the end time into freed memory.
1581        RefPtr<DocumentLoader> documentLoader = m_frame->loader()->documentLoader();
1582        DocumentLoadTiming* timing = documentLoader->timing();
1583        dispatchTimedEvent(loadEvent, document(), &timing->loadEventStart, &timing->loadEventEnd);
1584    } else
1585        dispatchEvent(loadEvent, document());
1586
1587    // For load events, send a separate load event to the enclosing frame only.
1588    // This is a DOM extension and is independent of bubbling/capturing rules of
1589    // the DOM.
1590    Element* ownerElement = m_frame ? m_frame->ownerElement() : 0;
1591    if (ownerElement)
1592        ownerElement->dispatchEvent(Event::create(eventNames().loadEvent, false, false));
1593
1594    InspectorInstrumentation::loadEventFired(frame(), url());
1595}
1596
1597bool DOMWindow::dispatchEvent(PassRefPtr<Event> prpEvent, PassRefPtr<EventTarget> prpTarget)
1598{
1599    RefPtr<EventTarget> protect = this;
1600    RefPtr<Event> event = prpEvent;
1601
1602    event->setTarget(prpTarget ? prpTarget : this);
1603    event->setCurrentTarget(this);
1604    event->setEventPhase(Event::AT_TARGET);
1605
1606    InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchEventOnWindow(frame(), *event, this);
1607
1608    bool result = fireEventListeners(event.get());
1609
1610    InspectorInstrumentation::didDispatchEventOnWindow(cookie);
1611
1612    return result;
1613}
1614
1615void DOMWindow::dispatchTimedEvent(PassRefPtr<Event> event, Document* target, double* startTime, double* endTime)
1616{
1617    ASSERT(startTime);
1618    ASSERT(endTime);
1619    *startTime = currentTime();
1620    dispatchEvent(event, target);
1621    *endTime = currentTime();
1622}
1623
1624void DOMWindow::removeAllEventListeners()
1625{
1626    EventTarget::removeAllEventListeners();
1627
1628#if ENABLE(DEVICE_ORIENTATION)
1629    if (frame() && frame()->page() && frame()->page()->deviceMotionController())
1630        frame()->page()->deviceMotionController()->removeAllListeners(this);
1631    if (frame() && frame()->page() && frame()->page()->deviceOrientationController())
1632        frame()->page()->deviceOrientationController()->removeAllListeners(this);
1633#endif
1634
1635    removeAllUnloadEventListeners(this);
1636    removeAllBeforeUnloadEventListeners(this);
1637}
1638
1639void DOMWindow::captureEvents()
1640{
1641    // Not implemented.
1642}
1643
1644void DOMWindow::releaseEvents()
1645{
1646    // Not implemented.
1647}
1648
1649void DOMWindow::finishedLoading()
1650{
1651    if (m_shouldPrintWhenFinishedLoading) {
1652        m_shouldPrintWhenFinishedLoading = false;
1653
1654        m_printTimer.stop();
1655        m_printTimer.startOneShot(0);
1656    }
1657}
1658
1659EventTargetData* DOMWindow::eventTargetData()
1660{
1661    return &m_eventTargetData;
1662}
1663
1664EventTargetData* DOMWindow::ensureEventTargetData()
1665{
1666    return &m_eventTargetData;
1667}
1668
1669#if ENABLE(DOM_STORAGE) && defined(ANDROID)
1670void DOMWindow::clearDOMStorage()
1671{
1672    if (m_sessionStorage)
1673        m_sessionStorage->disconnectFrame();
1674    m_sessionStorage = 0;
1675
1676    if (m_localStorage)
1677        m_localStorage->disconnectFrame();
1678    m_localStorage = 0;
1679}
1680#endif
1681
1682void DOMWindow::setLocation(const String& urlString, DOMWindow* activeWindow, DOMWindow* firstWindow, SetLocationLocking locking)
1683{
1684    if (!m_frame)
1685        return;
1686
1687    Frame* activeFrame = activeWindow->frame();
1688    if (!activeFrame)
1689        return;
1690
1691    if (!activeFrame->loader()->shouldAllowNavigation(m_frame))
1692        return;
1693
1694    Frame* firstFrame = firstWindow->frame();
1695    if (!firstFrame)
1696        return;
1697
1698    KURL completedURL = firstFrame->document()->completeURL(urlString);
1699    if (completedURL.isNull())
1700        return;
1701
1702    if (isInsecureScriptAccess(activeWindow, urlString))
1703        return;
1704
1705    // We want a new history item if we are processing a user gesture.
1706    m_frame->navigationScheduler()->scheduleLocationChange(activeFrame->document()->securityOrigin(),
1707        completedURL, activeFrame->loader()->outgoingReferrer(),
1708        locking != LockHistoryBasedOnGestureState || !activeFrame->script()->anyPageIsProcessingUserGesture(),
1709        locking != LockHistoryBasedOnGestureState);
1710}
1711
1712void DOMWindow::printErrorMessage(const String& message)
1713{
1714    if (message.isEmpty())
1715        return;
1716
1717    Settings* settings = m_frame->settings();
1718    if (!settings)
1719        return;
1720    if (settings->privateBrowsingEnabled())
1721        return;
1722
1723    // FIXME: Add arguments so that we can provide a correct source URL and line number.
1724    console()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String());
1725}
1726
1727String DOMWindow::crossDomainAccessErrorMessage(DOMWindow* activeWindow)
1728{
1729    const KURL& activeWindowURL = activeWindow->url();
1730    if (activeWindowURL.isNull())
1731        return String();
1732
1733    // FIXME: This error message should contain more specifics of why the same origin check has failed.
1734    // Perhaps we should involve the security origin object in composing it.
1735    // FIXME: This message, and other console messages, have extra newlines. Should remove them.
1736    return makeString("Unsafe JavaScript attempt to access frame with URL ", m_url.string(),
1737        " from frame with URL ", activeWindowURL.string(), ". Domains, protocols and ports must match.\n");
1738}
1739
1740bool DOMWindow::isInsecureScriptAccess(DOMWindow* activeWindow, const String& urlString)
1741{
1742    if (!protocolIsJavaScript(urlString))
1743        return false;
1744
1745    // If m_frame->domWindow() != this, then |this| isn't the DOMWindow that's
1746    // currently active in the frame and there's no way we should allow the
1747    // access.
1748    // FIXME: Remove this check if we're able to disconnect DOMWindow from
1749    // Frame on navigation: https://bugs.webkit.org/show_bug.cgi?id=62054
1750    if (m_frame->domWindow() == this) {
1751        // FIXME: Is there some way to eliminate the need for a separate "activeWindow == this" check?
1752        if (activeWindow == this)
1753            return false;
1754
1755        // FIXME: The name canAccess seems to be a roundabout way to ask "can execute script".
1756        // Can we name the SecurityOrigin function better to make this more clear?
1757        if (activeWindow->securityOrigin()->canAccess(securityOrigin()))
1758            return false;
1759    }
1760
1761    printErrorMessage(crossDomainAccessErrorMessage(activeWindow));
1762    return true;
1763}
1764
1765Frame* DOMWindow::createWindow(const String& urlString, const AtomicString& frameName, const WindowFeatures& windowFeatures,
1766    DOMWindow* activeWindow, Frame* firstFrame, Frame* openerFrame, PrepareDialogFunction function, void* functionContext)
1767{
1768    Frame* activeFrame = activeWindow->frame();
1769
1770    // For whatever reason, Firefox uses the first frame to determine the outgoingReferrer. We replicate that behavior here.
1771    String referrer = firstFrame->loader()->outgoingReferrer();
1772
1773    KURL completedURL = urlString.isEmpty() ? KURL(ParsedURLString, "") : firstFrame->document()->completeURL(urlString);
1774    ResourceRequest request(completedURL, referrer);
1775    FrameLoader::addHTTPOriginIfNeeded(request, firstFrame->loader()->outgoingOrigin());
1776    FrameLoadRequest frameRequest(activeWindow->securityOrigin(), request, frameName);
1777
1778    // We pass the opener frame for the lookupFrame in case the active frame is different from
1779    // the opener frame, and the name references a frame relative to the opener frame.
1780    bool created;
1781    Frame* newFrame = WebCore::createWindow(activeFrame, openerFrame, frameRequest, windowFeatures, created);
1782    if (!newFrame)
1783        return 0;
1784
1785    newFrame->loader()->setOpener(openerFrame);
1786    newFrame->page()->setOpenedByDOM();
1787
1788    if (newFrame->domWindow()->isInsecureScriptAccess(activeWindow, urlString))
1789        return newFrame;
1790
1791    if (function)
1792        function(newFrame->domWindow(), functionContext);
1793
1794    if (created)
1795        newFrame->loader()->changeLocation(activeWindow->securityOrigin(), completedURL, referrer, false, false);
1796    else if (!urlString.isEmpty()) {
1797        newFrame->navigationScheduler()->scheduleLocationChange(activeWindow->securityOrigin(), completedURL.string(), referrer,
1798            !activeFrame->script()->anyPageIsProcessingUserGesture(), false);
1799    }
1800
1801    return newFrame;
1802}
1803
1804PassRefPtr<DOMWindow> DOMWindow::open(const String& urlString, const AtomicString& frameName, const String& windowFeaturesString,
1805    DOMWindow* activeWindow, DOMWindow* firstWindow)
1806{
1807    if (!m_frame)
1808        return 0;
1809    Frame* activeFrame = activeWindow->frame();
1810    if (!activeFrame)
1811        return 0;
1812    Frame* firstFrame = firstWindow->frame();
1813    if (!firstFrame)
1814        return 0;
1815
1816    if (!firstWindow->allowPopUp()) {
1817        // Because FrameTree::find() returns true for empty strings, we must check for empty frame names.
1818        // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
1819        if (frameName.isEmpty() || !m_frame->tree()->find(frameName))
1820            return 0;
1821    }
1822
1823    // Get the target frame for the special cases of _top and _parent.
1824    // In those cases, we schedule a location change right now and return early.
1825    Frame* targetFrame = 0;
1826    if (frameName == "_top")
1827        targetFrame = m_frame->tree()->top();
1828    else if (frameName == "_parent") {
1829        if (Frame* parent = m_frame->tree()->parent())
1830            targetFrame = parent;
1831        else
1832            targetFrame = m_frame;
1833    }
1834    if (targetFrame) {
1835        if (!activeFrame->loader()->shouldAllowNavigation(targetFrame))
1836            return 0;
1837
1838        if (isInsecureScriptAccess(activeWindow, urlString))
1839            return targetFrame->domWindow();
1840
1841        if (urlString.isEmpty())
1842            return targetFrame->domWindow();
1843
1844        // For whatever reason, Firefox uses the first window rather than the active window to
1845        // determine the outgoing referrer. We replicate that behavior here.
1846        targetFrame->navigationScheduler()->scheduleLocationChange(activeFrame->document()->securityOrigin(),
1847            firstFrame->document()->completeURL(urlString).string(),
1848            firstFrame->loader()->outgoingReferrer(),
1849            !activeFrame->script()->anyPageIsProcessingUserGesture(), false);
1850
1851        return targetFrame->domWindow();
1852    }
1853
1854    WindowFeatures windowFeatures(windowFeaturesString);
1855    FloatRect windowRect(windowFeatures.xSet ? windowFeatures.x : 0, windowFeatures.ySet ? windowFeatures.y : 0,
1856        windowFeatures.widthSet ? windowFeatures.width : 0, windowFeatures.heightSet ? windowFeatures.height : 0);
1857    Page* page = m_frame->page();
1858    DOMWindow::adjustWindowRect(screenAvailableRect(page ? page->mainFrame()->view() : 0), windowRect, windowRect);
1859    windowFeatures.x = windowRect.x();
1860    windowFeatures.y = windowRect.y();
1861    windowFeatures.height = windowRect.height();
1862    windowFeatures.width = windowRect.width();
1863
1864    Frame* result = createWindow(urlString, frameName, windowFeatures, activeWindow, firstFrame, m_frame);
1865    return result ? result->domWindow() : 0;
1866}
1867
1868void DOMWindow::showModalDialog(const String& urlString, const String& dialogFeaturesString,
1869    DOMWindow* activeWindow, DOMWindow* firstWindow, PrepareDialogFunction function, void* functionContext)
1870{
1871    if (!m_frame)
1872        return;
1873    Frame* activeFrame = activeWindow->frame();
1874    if (!activeFrame)
1875        return;
1876    Frame* firstFrame = firstWindow->frame();
1877    if (!firstFrame)
1878        return;
1879
1880    if (m_frame->page())
1881        m_frame->page()->chrome()->willRunModalHTMLDialog(m_frame);
1882
1883    if (!canShowModalDialogNow(m_frame) || !firstWindow->allowPopUp())
1884        return;
1885
1886    Frame* dialogFrame = createWindow(urlString, emptyAtom, WindowFeatures(dialogFeaturesString, screenAvailableRect(m_frame->view())),
1887        activeWindow, firstFrame, m_frame, function, functionContext);
1888    if (!dialogFrame)
1889        return;
1890
1891    dialogFrame->page()->chrome()->runModal();
1892}
1893
1894#if ENABLE(BLOB)
1895DOMURL* DOMWindow::webkitURL() const
1896{
1897    if (!m_domURL)
1898        m_domURL = DOMURL::create(this->scriptExecutionContext());
1899    return m_domURL.get();
1900}
1901#endif
1902
1903#if ENABLE(QUOTA)
1904StorageInfo* DOMWindow::webkitStorageInfo() const
1905{
1906    if (!m_storageInfo)
1907        m_storageInfo = StorageInfo::create();
1908    return m_storageInfo.get();
1909}
1910#endif
1911
1912} // namespace WebCore
1913