1/*
2 *  Copyright (C) 2000 Harri Porten (porten@kde.org)
3 *  Copyright (C) 2006 Jon Shier (jshier@iastate.edu)
4 *  Copyright (C) 2003-2009, 2014 Apple Inc. All rights reseved.
5 *  Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6 *
7 *  This library is free software; you can redistribute it and/or
8 *  modify it under the terms of the GNU Lesser General Public
9 *  License as published by the Free Software Foundation; either
10 *  version 2 of the License, or (at your option) any later version.
11 *
12 *  This library is distributed in the hope that it will be useful,
13 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 *  Lesser General Public License for more details.
16 *
17 *  You should have received a copy of the GNU Lesser General Public
18 *  License along with this library; if not, write to the Free Software
19 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
20 *  USA
21 */
22
23#include "config.h"
24#include "JSDOMWindowBase.h"
25
26#include "Chrome.h"
27#include "DOMWindow.h"
28#include "Frame.h"
29#include "InspectorController.h"
30#include "JSDOMGlobalObjectTask.h"
31#include "JSDOMWindowCustom.h"
32#include "JSNode.h"
33#include "Logging.h"
34#include "Page.h"
35#include "ScriptController.h"
36#include "SecurityOrigin.h"
37#include "Settings.h"
38#include "WebCoreJSClientData.h"
39#include <runtime/Microtask.h>
40#include <wtf/MainThread.h>
41
42#if PLATFORM(IOS)
43#include "ChromeClient.h"
44#include "WebSafeGCActivityCallbackIOS.h"
45#include "WebSafeIncrementalSweeperIOS.h"
46#endif
47
48using namespace JSC;
49
50namespace WebCore {
51
52static bool shouldAllowAccessFrom(const JSGlobalObject* thisObject, ExecState* exec)
53{
54    return BindingSecurity::shouldAllowAccessToDOMWindow(exec, asJSDOMWindow(thisObject)->impl());
55}
56
57const ClassInfo JSDOMWindowBase::s_info = { "Window", &JSDOMGlobalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSDOMWindowBase) };
58
59const GlobalObjectMethodTable JSDOMWindowBase::s_globalObjectMethodTable = { &shouldAllowAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptExperimentsEnabled, &queueTaskToEventLoop, &shouldInterruptScriptBeforeTimeout };
60
61JSDOMWindowBase::JSDOMWindowBase(VM& vm, Structure* structure, PassRefPtr<DOMWindow> window, JSDOMWindowShell* shell)
62    : JSDOMGlobalObject(vm, structure, &shell->world(), &s_globalObjectMethodTable)
63    , m_windowCloseWatchpoints((window && window->frame()) ? IsWatched : IsInvalidated)
64    , m_impl(window)
65    , m_shell(shell)
66{
67}
68
69void JSDOMWindowBase::finishCreation(VM& vm, JSDOMWindowShell* shell)
70{
71    Base::finishCreation(vm, shell);
72    ASSERT(inherits(info()));
73
74    GlobalPropertyInfo staticGlobals[] = {
75        GlobalPropertyInfo(vm.propertyNames->document, jsNull(), DontDelete | ReadOnly),
76        GlobalPropertyInfo(vm.propertyNames->window, m_shell, DontDelete | ReadOnly)
77    };
78
79    addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals));
80}
81
82void JSDOMWindowBase::destroy(JSCell* cell)
83{
84    static_cast<JSDOMWindowBase*>(cell)->JSDOMWindowBase::~JSDOMWindowBase();
85}
86
87void JSDOMWindowBase::updateDocument()
88{
89    ASSERT(m_impl->document());
90    ExecState* exec = globalExec();
91    symbolTablePutWithAttributes(this, exec->vm(), exec->vm().propertyNames->document, toJS(exec, this, m_impl->document()), DontDelete | ReadOnly);
92}
93
94ScriptExecutionContext* JSDOMWindowBase::scriptExecutionContext() const
95{
96    return m_impl->document();
97}
98
99void JSDOMWindowBase::printErrorMessage(const String& message) const
100{
101    printErrorMessageForFrame(impl().frame(), message);
102}
103
104bool JSDOMWindowBase::supportsProfiling(const JSGlobalObject* object)
105{
106#if !ENABLE(INSPECTOR)
107    UNUSED_PARAM(object);
108    return false;
109#else
110    const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
111    Frame* frame = thisObject->impl().frame();
112    if (!frame)
113        return false;
114
115    Page* page = frame->page();
116    if (!page)
117        return false;
118
119    return page->inspectorController().profilerEnabled();
120#endif // ENABLE(INSPECTOR)
121}
122
123bool JSDOMWindowBase::supportsRichSourceInfo(const JSGlobalObject* object)
124{
125#if !ENABLE(INSPECTOR)
126    UNUSED_PARAM(object);
127    return false;
128#else
129    const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
130    Frame* frame = thisObject->impl().frame();
131    if (!frame)
132        return false;
133
134    Page* page = frame->page();
135    if (!page)
136        return false;
137
138    bool enabled = page->inspectorController().enabled();
139    ASSERT(enabled || !thisObject->debugger());
140    ASSERT(enabled || !supportsProfiling(thisObject));
141    return enabled;
142#endif
143}
144
145static inline bool shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(Page* page)
146{
147    // See <rdar://problem/5479443>. We don't think that page can ever be NULL
148    // in this case, but if it is, we've gotten into a state where we may have
149    // hung the UI, with no way to ask the client whether to cancel execution.
150    // For now, our solution is just to cancel execution no matter what,
151    // ensuring that we never hang. We might want to consider other solutions
152    // if we discover problems with this one.
153    ASSERT(page);
154    return !page;
155}
156
157bool JSDOMWindowBase::shouldInterruptScript(const JSGlobalObject* object)
158{
159    const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
160    ASSERT(thisObject->impl().frame());
161    Page* page = thisObject->impl().frame()->page();
162    return shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page) || page->chrome().shouldInterruptJavaScript();
163}
164
165bool JSDOMWindowBase::shouldInterruptScriptBeforeTimeout(const JSGlobalObject* object)
166{
167    const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
168    ASSERT(thisObject->impl().frame());
169    Page* page = thisObject->impl().frame()->page();
170
171    if (shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page))
172        return true;
173
174#if PLATFORM(IOS)
175    if (page->chrome().client().isStopping())
176        return true;
177#endif
178
179    return JSGlobalObject::shouldInterruptScriptBeforeTimeout(object);
180}
181
182bool JSDOMWindowBase::javaScriptExperimentsEnabled(const JSGlobalObject* object)
183{
184    const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
185    Frame* frame = thisObject->impl().frame();
186    if (!frame)
187        return false;
188    return frame->settings().javaScriptExperimentsEnabled();
189}
190
191void JSDOMWindowBase::queueTaskToEventLoop(const JSGlobalObject* object, PassRefPtr<Microtask> task)
192{
193    const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
194    thisObject->scriptExecutionContext()->postTask(JSGlobalObjectTask((JSDOMWindowBase*)thisObject, task));
195}
196
197void JSDOMWindowBase::willRemoveFromWindowShell()
198{
199    setCurrentEvent(0);
200}
201
202JSDOMWindowShell* JSDOMWindowBase::shell() const
203{
204    return m_shell;
205}
206
207VM& JSDOMWindowBase::commonVM()
208{
209    ASSERT(isMainThread());
210
211    static VM* vm = nullptr;
212    if (!vm) {
213        ScriptController::initializeThreading();
214        vm = VM::createLeaked(LargeHeap).leakRef();
215#if !PLATFORM(IOS)
216        vm->setExclusiveThread(std::this_thread::get_id());
217#else
218        vm->heap.setFullActivityCallback(WebSafeFullGCActivityCallback::create(&vm->heap));
219#if ENABLE(GGC)
220        vm->heap.setEdenActivityCallback(WebSafeEdenGCActivityCallback::create(&vm->heap));
221#else
222        vm->heap.setEdenActivityCallback(vm->heap.fullActivityCallback());
223#endif
224        vm->heap.setIncrementalSweeper(WebSafeIncrementalSweeper::create(&vm->heap));
225        vm->makeUsableFromMultipleThreads();
226        vm->heap.machineThreads().addCurrentThread();
227#endif
228        initNormalWorldClientData(vm);
229    }
230
231    return *vm;
232}
233
234// JSDOMGlobalObject* is ignored, accessing a window in any context will
235// use that DOMWindow's prototype chain.
236JSValue toJS(ExecState* exec, JSDOMGlobalObject*, DOMWindow* domWindow)
237{
238    return toJS(exec, domWindow);
239}
240
241JSValue toJS(ExecState* exec, DOMWindow* domWindow)
242{
243    if (!domWindow)
244        return jsNull();
245    Frame* frame = domWindow->frame();
246    if (!frame)
247        return jsNull();
248    return frame->script().windowShell(currentWorld(exec));
249}
250
251JSDOMWindow* toJSDOMWindow(Frame* frame, DOMWrapperWorld& world)
252{
253    if (!frame)
254        return 0;
255    return frame->script().windowShell(world)->window();
256}
257
258JSDOMWindow* toJSDOMWindow(JSValue value)
259{
260    if (!value.isObject())
261        return 0;
262    const ClassInfo* classInfo = asObject(value)->classInfo();
263    if (classInfo == JSDOMWindow::info())
264        return jsCast<JSDOMWindow*>(asObject(value));
265    if (classInfo == JSDOMWindowShell::info())
266        return jsCast<JSDOMWindowShell*>(asObject(value))->window();
267    return 0;
268}
269
270void JSDOMWindowBase::fireFrameClearedWatchpointsForWindow(DOMWindow* window)
271{
272    JSC::VM& vm = JSDOMWindowBase::commonVM();
273    WebCoreJSClientData* clientData = static_cast<WebCoreJSClientData*>(vm.clientData);
274    Vector<Ref<DOMWrapperWorld>> wrapperWorlds;
275    clientData->getAllWorlds(wrapperWorlds);
276    for (unsigned i = 0; i < wrapperWorlds.size(); ++i) {
277        DOMObjectWrapperMap& wrappers = wrapperWorlds[i]->m_wrappers;
278        auto result = wrappers.find(window);
279        if (result == wrappers.end())
280            continue;
281        JSC::JSObject* wrapper = result->value.get();
282        if (!wrapper)
283            continue;
284        JSDOMWindowBase* jsWindow = JSC::jsCast<JSDOMWindowBase*>(wrapper);
285        jsWindow->m_windowCloseWatchpoints.fireAll();
286    }
287}
288
289} // namespace WebCore
290