2015-11-03 Geoffrey Garen <ggaren@apple.com>
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMWindowBase.cpp
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  *  Copyright (c) 2015 Canon Inc. All rights reserved.
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
21  *  USA
22  */
23
24 #include "config.h"
25 #include "JSDOMWindowBase.h"
26
27 #include "Chrome.h"
28 #include "DOMWindow.h"
29 #include "Frame.h"
30 #include "InspectorController.h"
31 #include "JSDOMGlobalObjectTask.h"
32 #include "JSDOMWindowCustom.h"
33 #include "JSModuleLoader.h"
34 #include "JSNode.h"
35 #include "Logging.h"
36 #include "Page.h"
37 #include "RuntimeApplicationChecks.h"
38 #include "ScriptController.h"
39 #include "SecurityOrigin.h"
40 #include "Settings.h"
41 #include "WebCoreJSClientData.h"
42 #include <runtime/JSInternalPromiseDeferred.h>
43 #include <runtime/Microtask.h>
44 #include <wtf/MainThread.h>
45
46 #if PLATFORM(IOS)
47 #include "ChromeClient.h"
48 #include "WebSafeGCActivityCallbackIOS.h"
49 #include "WebSafeIncrementalSweeperIOS.h"
50 #endif
51
52 #if ENABLE(STREAMS_API)
53 #include "JSReadableStreamPrivateConstructors.h"
54 #include "ReadableStreamInternalsBuiltins.h"
55 #include "StreamInternalsBuiltins.h"
56 #include "WritableStreamInternalsBuiltins.h"
57 #endif
58
59 using namespace JSC;
60
61 namespace WebCore {
62
63 static bool shouldAllowAccessFrom(const JSGlobalObject* thisObject, ExecState* exec)
64 {
65     return BindingSecurity::shouldAllowAccessToDOMWindow(exec, asJSDOMWindow(thisObject)->wrapped());
66 }
67
68 const ClassInfo JSDOMWindowBase::s_info = { "Window", &JSDOMGlobalObject::s_info, 0, CREATE_METHOD_TABLE(JSDOMWindowBase) };
69
70 const GlobalObjectMethodTable JSDOMWindowBase::s_globalObjectMethodTable = { &shouldAllowAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, &queueTaskToEventLoop, &shouldInterruptScriptBeforeTimeout, &moduleLoaderResolve, &moduleLoaderFetch, nullptr, nullptr, &moduleLoaderEvaluate };
71
72 JSDOMWindowBase::JSDOMWindowBase(VM& vm, Structure* structure, PassRefPtr<DOMWindow> window, JSDOMWindowShell* shell)
73     : JSDOMGlobalObject(vm, structure, &shell->world(), &s_globalObjectMethodTable)
74     , m_windowCloseWatchpoints((window && window->frame()) ? IsWatched : IsInvalidated)
75     , m_wrapped(window)
76     , m_shell(shell)
77     , m_privateFunctions(vm)
78 {
79 }
80
81 void JSDOMWindowBase::finishCreation(VM& vm, JSDOMWindowShell* shell)
82 {
83     Base::finishCreation(vm, shell);
84     ASSERT(inherits(info()));
85
86     m_privateFunctions.init(*this);
87
88 #if ENABLE(STREAMS_API)
89     JSVMClientData& clientData = *static_cast<JSVMClientData*>(vm.clientData);
90 #endif
91
92     GlobalPropertyInfo staticGlobals[] = {
93         GlobalPropertyInfo(vm.propertyNames->document, jsNull(), DontDelete | ReadOnly),
94         GlobalPropertyInfo(vm.propertyNames->window, m_shell, DontDelete | ReadOnly),
95 #if ENABLE(STREAMS_API)
96         GlobalPropertyInfo(clientData.builtinNames().streamClosedPrivateName(), jsNumber(1), DontDelete | ReadOnly),
97         GlobalPropertyInfo(clientData.builtinNames().streamClosingPrivateName(), jsNumber(2), DontDelete | ReadOnly),
98         GlobalPropertyInfo(clientData.builtinNames().streamErroredPrivateName(), jsNumber(3), DontDelete | ReadOnly),
99         GlobalPropertyInfo(clientData.builtinNames().streamReadablePrivateName(), jsNumber(4), DontDelete | ReadOnly),
100         GlobalPropertyInfo(clientData.builtinNames().streamWaitingPrivateName(), jsNumber(5), DontDelete | ReadOnly),
101         GlobalPropertyInfo(clientData.builtinNames().streamWritablePrivateName(), jsNumber(6), DontDelete | ReadOnly),
102         GlobalPropertyInfo(clientData.builtinNames().ReadableStreamControllerPrivateName(), createReadableStreamControllerPrivateConstructor(vm, *this), DontDelete | ReadOnly),
103         GlobalPropertyInfo(clientData.builtinNames().ReadableStreamReaderPrivateName(), createReadableStreamReaderPrivateConstructor(vm, *this), DontDelete | ReadOnly),
104 #define DECLARE_GLOBAL_STATIC(name)\
105         GlobalPropertyInfo(\
106             clientData.builtinFunctions().readableStreamInternalsBuiltins().name##PrivateName(), m_privateFunctions.readableStreamInternals().m_##name##Function.get() , DontDelete | ReadOnly),
107         WEBCORE_FOREACH_READABLESTREAMINTERNALS_BUILTIN_FUNCTION_NAME(DECLARE_GLOBAL_STATIC)
108 #undef DECLARE_GLOBAL_STATIC
109 #define DECLARE_GLOBAL_STATIC(name)\
110         GlobalPropertyInfo(\
111             clientData.builtinFunctions().streamInternalsBuiltins().name##PrivateName(), m_privateFunctions.streamInternals().m_##name##Function.get() , DontDelete | ReadOnly),
112         WEBCORE_FOREACH_STREAMINTERNALS_BUILTIN_FUNCTION_NAME(DECLARE_GLOBAL_STATIC)
113 #undef DECLARE_GLOBAL_STATIC
114 #define DECLARE_GLOBAL_STATIC(name)\
115         GlobalPropertyInfo(\
116             clientData.builtinFunctions().writableStreamInternalsBuiltins().name##PrivateName(), m_privateFunctions.writableStreamInternals().m_##name##Function.get() , DontDelete | ReadOnly),
117         WEBCORE_FOREACH_WRITABLESTREAMINTERNALS_BUILTIN_FUNCTION_NAME(DECLARE_GLOBAL_STATIC)
118 #undef DECLARE_GLOBAL_STATIC
119 #endif
120     };
121
122     addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals));
123 }
124
125 void JSDOMWindowBase::visitChildren(JSCell* cell, SlotVisitor& visitor)
126 {
127     JSDOMWindowBase* thisObject = jsCast<JSDOMWindowBase*>(cell);
128     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
129     Base::visitChildren(thisObject, visitor);
130     thisObject->m_privateFunctions.visit(visitor);
131 }
132
133 void JSDOMWindowBase::destroy(JSCell* cell)
134 {
135     static_cast<JSDOMWindowBase*>(cell)->JSDOMWindowBase::~JSDOMWindowBase();
136 }
137
138 void JSDOMWindowBase::updateDocument()
139 {
140     ASSERT(m_wrapped->document());
141     ExecState* exec = globalExec();
142     symbolTablePutWithAttributesTouchWatchpointSet(this, exec, exec->vm().propertyNames->document, toJS(exec, this, m_wrapped->document()), DontDelete | ReadOnly);
143 }
144
145 ScriptExecutionContext* JSDOMWindowBase::scriptExecutionContext() const
146 {
147     return m_wrapped->document();
148 }
149
150 void JSDOMWindowBase::printErrorMessage(const String& message) const
151 {
152     printErrorMessageForFrame(wrapped().frame(), message);
153 }
154
155 bool JSDOMWindowBase::supportsProfiling(const JSGlobalObject* object)
156 {
157     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
158     Frame* frame = thisObject->wrapped().frame();
159     if (!frame)
160         return false;
161
162     Page* page = frame->page();
163     if (!page)
164         return false;
165
166     return page->inspectorController().profilerEnabled();
167 }
168
169 bool JSDOMWindowBase::supportsRichSourceInfo(const JSGlobalObject* object)
170 {
171     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
172     Frame* frame = thisObject->wrapped().frame();
173     if (!frame)
174         return false;
175
176     Page* page = frame->page();
177     if (!page)
178         return false;
179
180     bool enabled = page->inspectorController().enabled();
181     ASSERT(enabled || !thisObject->debugger());
182     ASSERT(enabled || !supportsProfiling(thisObject));
183     return enabled;
184 }
185
186 static inline bool shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(Page* page)
187 {
188     // See <rdar://problem/5479443>. We don't think that page can ever be NULL
189     // in this case, but if it is, we've gotten into a state where we may have
190     // hung the UI, with no way to ask the client whether to cancel execution.
191     // For now, our solution is just to cancel execution no matter what,
192     // ensuring that we never hang. We might want to consider other solutions
193     // if we discover problems with this one.
194     ASSERT(page);
195     return !page;
196 }
197
198 bool JSDOMWindowBase::shouldInterruptScript(const JSGlobalObject* object)
199 {
200     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
201     ASSERT(thisObject->wrapped().frame());
202     Page* page = thisObject->wrapped().frame()->page();
203     return shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page);
204 }
205
206 bool JSDOMWindowBase::shouldInterruptScriptBeforeTimeout(const JSGlobalObject* object)
207 {
208     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
209     ASSERT(thisObject->wrapped().frame());
210     Page* page = thisObject->wrapped().frame()->page();
211
212     if (shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page))
213         return true;
214
215 #if PLATFORM(IOS)
216     if (page->chrome().client().isStopping())
217         return true;
218 #endif
219
220     return JSGlobalObject::shouldInterruptScriptBeforeTimeout(object);
221 }
222
223 RuntimeFlags JSDOMWindowBase::javaScriptRuntimeFlags(const JSGlobalObject* object)
224 {
225     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
226     Frame* frame = thisObject->wrapped().frame();
227     if (!frame)
228         return RuntimeFlags();
229     return frame->settings().javaScriptRuntimeFlags();
230 }
231
232 void JSDOMWindowBase::queueTaskToEventLoop(const JSGlobalObject* object, PassRefPtr<Microtask> task)
233 {
234     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
235     thisObject->scriptExecutionContext()->postTask(JSGlobalObjectTask((JSDOMWindowBase*)thisObject, task));
236 }
237
238 void JSDOMWindowBase::willRemoveFromWindowShell()
239 {
240     setCurrentEvent(0);
241 }
242
243 JSDOMWindowShell* JSDOMWindowBase::shell() const
244 {
245     return m_shell;
246 }
247
248 VM& JSDOMWindowBase::commonVM()
249 {
250     ASSERT(isMainThread());
251
252     static VM* vm = nullptr;
253     if (!vm) {
254         ScriptController::initializeThreading();
255         vm = &VM::createLeaked(LargeHeap).leakRef();
256 #if !PLATFORM(IOS)
257         vm->setExclusiveThread(std::this_thread::get_id());
258 #else
259         vm->heap.setFullActivityCallback(WebSafeFullGCActivityCallback::create(&vm->heap));
260         vm->heap.setEdenActivityCallback(WebSafeEdenGCActivityCallback::create(&vm->heap));
261
262         vm->heap.setIncrementalSweeper(std::make_unique<WebSafeIncrementalSweeper>(&vm->heap));
263         vm->heap.machineThreads().addCurrentThread();
264 #endif
265
266 #if PLATFORM(MAC)
267         if (applicationIsITunes() || applicationIsIBooks() || Settings::shouldRewriteConstAsVar())
268             vm->setShouldRewriteConstAsVar(true);
269 #endif
270
271         initNormalWorldClientData(vm);
272     }
273
274     return *vm;
275 }
276
277 // JSDOMGlobalObject* is ignored, accessing a window in any context will
278 // use that DOMWindow's prototype chain.
279 JSValue toJS(ExecState* exec, JSDOMGlobalObject*, DOMWindow* domWindow)
280 {
281     return toJS(exec, domWindow);
282 }
283
284 JSValue toJS(ExecState* exec, DOMWindow* domWindow)
285 {
286     if (!domWindow)
287         return jsNull();
288     Frame* frame = domWindow->frame();
289     if (!frame)
290         return jsNull();
291     return frame->script().windowShell(currentWorld(exec));
292 }
293
294 JSDOMWindow* toJSDOMWindow(Frame* frame, DOMWrapperWorld& world)
295 {
296     if (!frame)
297         return 0;
298     return frame->script().windowShell(world)->window();
299 }
300
301 JSDOMWindow* toJSDOMWindow(JSValue value)
302 {
303     if (!value.isObject())
304         return 0;
305     while (!value.isNull()) {
306         JSObject* object = asObject(value);
307         const ClassInfo* classInfo = object->classInfo();
308         if (classInfo == JSDOMWindow::info())
309             return jsCast<JSDOMWindow*>(object);
310         if (classInfo == JSDOMWindowShell::info())
311             return jsCast<JSDOMWindowShell*>(object)->window();
312         value = object->prototype();
313     }
314     return 0;
315 }
316
317 void JSDOMWindowBase::fireFrameClearedWatchpointsForWindow(DOMWindow* window)
318 {
319     JSC::VM& vm = JSDOMWindowBase::commonVM();
320     JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData);
321     Vector<Ref<DOMWrapperWorld>> wrapperWorlds;
322     clientData->getAllWorlds(wrapperWorlds);
323     for (unsigned i = 0; i < wrapperWorlds.size(); ++i) {
324         DOMObjectWrapperMap& wrappers = wrapperWorlds[i]->m_wrappers;
325         auto result = wrappers.find(window);
326         if (result == wrappers.end())
327             continue;
328         JSC::JSObject* wrapper = result->value.get();
329         if (!wrapper)
330             continue;
331         JSDOMWindowBase* jsWindow = JSC::jsCast<JSDOMWindowBase*>(wrapper);
332         jsWindow->m_windowCloseWatchpoints.fireAll("Frame cleared");
333     }
334 }
335
336
337 JSC::JSInternalPromise* JSDOMWindowBase::moduleLoaderResolve(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSValue moduleName, JSC::JSValue importerModuleKey)
338 {
339     JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject);
340     if (RefPtr<Document> document = thisObject->wrapped().document())
341         return document->moduleLoader()->resolve(globalObject, exec, moduleName, importerModuleKey);
342     JSC::JSInternalPromiseDeferred* deferred = JSC::JSInternalPromiseDeferred::create(exec, globalObject);
343     return deferred->reject(exec, jsUndefined());
344 }
345
346 JSC::JSInternalPromise* JSDOMWindowBase::moduleLoaderFetch(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSValue moduleKey)
347 {
348     JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject);
349     if (RefPtr<Document> document = thisObject->wrapped().document())
350         return document->moduleLoader()->fetch(globalObject, exec, moduleKey);
351     JSC::JSInternalPromiseDeferred* deferred = JSC::JSInternalPromiseDeferred::create(exec, globalObject);
352     return deferred->reject(exec, jsUndefined());
353 }
354
355 JSC::JSValue JSDOMWindowBase::moduleLoaderEvaluate(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSValue moduleKey, JSC::JSValue moduleRecord)
356 {
357     JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject);
358     if (RefPtr<Document> document = thisObject->wrapped().document())
359         return document->moduleLoader()->evaluate(globalObject, exec, moduleKey, moduleRecord);
360     return JSC::jsUndefined();
361 }
362
363 } // namespace WebCore