Unreviewed, rolling out r194033 and r194267.
[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 "ActiveDOMCallbackMicrotask.h"
28 #include "Chrome.h"
29 #include "DOMWindow.h"
30 #include "Frame.h"
31 #include "InspectorController.h"
32 #include "JSDOMGlobalObjectTask.h"
33 #include "JSDOMWindowCustom.h"
34 #include "JSMainThreadExecState.h"
35 #include "JSModuleLoader.h"
36 #include "JSNode.h"
37 #include "Logging.h"
38 #include "Page.h"
39 #include "RuntimeApplicationChecks.h"
40 #include "ScriptController.h"
41 #include "SecurityOrigin.h"
42 #include "Settings.h"
43 #include "WebCoreJSClientData.h"
44 #include <heap/StrongInlines.h>
45 #include <runtime/JSInternalPromiseDeferred.h>
46 #include <runtime/Microtask.h>
47 #include <wtf/MainThread.h>
48
49 #if PLATFORM(IOS)
50 #include "ChromeClient.h"
51 #include "WebSafeGCActivityCallbackIOS.h"
52 #include "WebSafeIncrementalSweeperIOS.h"
53 #endif
54
55 #if ENABLE(STREAMS_API)
56 #include "JSReadableStreamPrivateConstructors.h"
57 #include "ReadableStreamInternalsBuiltins.h"
58 #include "StreamInternalsBuiltins.h"
59 #include "WritableStreamInternalsBuiltins.h"
60 #endif
61
62 #if ENABLE(MEDIA_STREAM)
63 #include "RTCPeerConnectionInternalsBuiltins.h"
64 #endif
65
66 using namespace JSC;
67
68 namespace WebCore {
69
70 static bool shouldAllowAccessFrom(const JSGlobalObject* thisObject, ExecState* exec)
71 {
72     return BindingSecurity::shouldAllowAccessToDOMWindow(exec, asJSDOMWindow(thisObject)->wrapped());
73 }
74
75 const ClassInfo JSDOMWindowBase::s_info = { "Window", &JSDOMGlobalObject::s_info, 0, CREATE_METHOD_TABLE(JSDOMWindowBase) };
76
77 const GlobalObjectMethodTable JSDOMWindowBase::s_globalObjectMethodTable = { &shouldAllowAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, &queueTaskToEventLoop, &shouldInterruptScriptBeforeTimeout, &moduleLoaderResolve, &moduleLoaderFetch, nullptr, nullptr, &moduleLoaderEvaluate };
78
79 JSDOMWindowBase::JSDOMWindowBase(VM& vm, Structure* structure, PassRefPtr<DOMWindow> window, JSDOMWindowShell* shell)
80     : JSDOMGlobalObject(vm, structure, &shell->world(), &s_globalObjectMethodTable)
81     , m_windowCloseWatchpoints((window && window->frame()) ? IsWatched : IsInvalidated)
82     , m_wrapped(window)
83     , m_shell(shell)
84     , m_privateFunctions(vm)
85 {
86 }
87
88 void JSDOMWindowBase::finishCreation(VM& vm, JSDOMWindowShell* shell)
89 {
90     Base::finishCreation(vm, shell);
91     ASSERT(inherits(info()));
92
93     m_privateFunctions.init(*this);
94
95 #if ENABLE(STREAMS_API) || ENABLE(MEDIA_STREAM)
96     JSVMClientData& clientData = *static_cast<JSVMClientData*>(vm.clientData);
97 #endif
98
99     GlobalPropertyInfo staticGlobals[] = {
100         GlobalPropertyInfo(vm.propertyNames->document, jsNull(), DontDelete | ReadOnly),
101         GlobalPropertyInfo(vm.propertyNames->window, m_shell, DontDelete | ReadOnly),
102 #if ENABLE(STREAMS_API)
103         GlobalPropertyInfo(clientData.builtinNames().streamClosedPrivateName(), jsNumber(1), DontDelete | ReadOnly),
104         GlobalPropertyInfo(clientData.builtinNames().streamClosingPrivateName(), jsNumber(2), DontDelete | ReadOnly),
105         GlobalPropertyInfo(clientData.builtinNames().streamErroredPrivateName(), jsNumber(3), DontDelete | ReadOnly),
106         GlobalPropertyInfo(clientData.builtinNames().streamReadablePrivateName(), jsNumber(4), DontDelete | ReadOnly),
107         GlobalPropertyInfo(clientData.builtinNames().streamWaitingPrivateName(), jsNumber(5), DontDelete | ReadOnly),
108         GlobalPropertyInfo(clientData.builtinNames().streamWritablePrivateName(), jsNumber(6), DontDelete | ReadOnly),
109         GlobalPropertyInfo(clientData.builtinNames().ReadableStreamControllerPrivateName(), createReadableStreamControllerPrivateConstructor(vm, *this), DontDelete | ReadOnly),
110         GlobalPropertyInfo(clientData.builtinNames().ReadableStreamReaderPrivateName(), createReadableStreamReaderPrivateConstructor(vm, *this), DontDelete | ReadOnly),
111 #define DECLARE_GLOBAL_STATIC(name)\
112         GlobalPropertyInfo(\
113             clientData.builtinFunctions().readableStreamInternalsBuiltins().name##PrivateName(), m_privateFunctions.readableStreamInternals().m_##name##Function.get() , DontDelete | ReadOnly),
114         WEBCORE_FOREACH_READABLESTREAMINTERNALS_BUILTIN_FUNCTION_NAME(DECLARE_GLOBAL_STATIC)
115 #undef DECLARE_GLOBAL_STATIC
116 #define DECLARE_GLOBAL_STATIC(name)\
117         GlobalPropertyInfo(\
118             clientData.builtinFunctions().streamInternalsBuiltins().name##PrivateName(), m_privateFunctions.streamInternals().m_##name##Function.get() , DontDelete | ReadOnly),
119         WEBCORE_FOREACH_STREAMINTERNALS_BUILTIN_FUNCTION_NAME(DECLARE_GLOBAL_STATIC)
120 #undef DECLARE_GLOBAL_STATIC
121 #define DECLARE_GLOBAL_STATIC(name)\
122         GlobalPropertyInfo(\
123             clientData.builtinFunctions().writableStreamInternalsBuiltins().name##PrivateName(), m_privateFunctions.writableStreamInternals().m_##name##Function.get() , DontDelete | ReadOnly),
124         WEBCORE_FOREACH_WRITABLESTREAMINTERNALS_BUILTIN_FUNCTION_NAME(DECLARE_GLOBAL_STATIC)
125 #undef DECLARE_GLOBAL_STATIC
126 #endif
127 #if ENABLE(MEDIA_STREAM)
128 #define DECLARE_GLOBAL_STATIC(name)\
129         GlobalPropertyInfo(\
130             clientData.builtinFunctions().rtcPeerConnectionInternalsBuiltins().name##PrivateName(), m_privateFunctions.rtcPeerConnectionInternals().m_##name##Function.get() , DontDelete | ReadOnly),
131         WEBCORE_FOREACH_RTCPEERCONNECTIONINTERNALS_BUILTIN_FUNCTION_NAME(DECLARE_GLOBAL_STATIC)
132 #undef DECLARE_GLOBAL_STATIC
133 #endif
134     };
135
136     addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals));
137 }
138
139 void JSDOMWindowBase::visitChildren(JSCell* cell, SlotVisitor& visitor)
140 {
141     JSDOMWindowBase* thisObject = jsCast<JSDOMWindowBase*>(cell);
142     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
143     Base::visitChildren(thisObject, visitor);
144     thisObject->m_privateFunctions.visit(visitor);
145 }
146
147 void JSDOMWindowBase::destroy(JSCell* cell)
148 {
149     static_cast<JSDOMWindowBase*>(cell)->JSDOMWindowBase::~JSDOMWindowBase();
150 }
151
152 void JSDOMWindowBase::updateDocument()
153 {
154     ASSERT(m_wrapped->document());
155     ExecState* exec = globalExec();
156     symbolTablePutWithAttributesTouchWatchpointSet(this, exec, exec->vm().propertyNames->document, toJS(exec, this, m_wrapped->document()), DontDelete | ReadOnly);
157 }
158
159 ScriptExecutionContext* JSDOMWindowBase::scriptExecutionContext() const
160 {
161     return m_wrapped->document();
162 }
163
164 void JSDOMWindowBase::printErrorMessage(const String& message) const
165 {
166     printErrorMessageForFrame(wrapped().frame(), message);
167 }
168
169 bool JSDOMWindowBase::supportsProfiling(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     return page->inspectorController().profilerEnabled();
181 }
182
183 bool JSDOMWindowBase::supportsRichSourceInfo(const JSGlobalObject* object)
184 {
185     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
186     Frame* frame = thisObject->wrapped().frame();
187     if (!frame)
188         return false;
189
190     Page* page = frame->page();
191     if (!page)
192         return false;
193
194     bool enabled = page->inspectorController().enabled();
195     ASSERT(enabled || !thisObject->debugger());
196     ASSERT(enabled || !supportsProfiling(thisObject));
197     return enabled;
198 }
199
200 static inline bool shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(Page* page)
201 {
202     // See <rdar://problem/5479443>. We don't think that page can ever be NULL
203     // in this case, but if it is, we've gotten into a state where we may have
204     // hung the UI, with no way to ask the client whether to cancel execution.
205     // For now, our solution is just to cancel execution no matter what,
206     // ensuring that we never hang. We might want to consider other solutions
207     // if we discover problems with this one.
208     ASSERT(page);
209     return !page;
210 }
211
212 bool JSDOMWindowBase::shouldInterruptScript(const JSGlobalObject* object)
213 {
214     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
215     ASSERT(thisObject->wrapped().frame());
216     Page* page = thisObject->wrapped().frame()->page();
217     return shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page);
218 }
219
220 bool JSDOMWindowBase::shouldInterruptScriptBeforeTimeout(const JSGlobalObject* object)
221 {
222     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
223     ASSERT(thisObject->wrapped().frame());
224     Page* page = thisObject->wrapped().frame()->page();
225
226     if (shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page))
227         return true;
228
229 #if PLATFORM(IOS)
230     if (page->chrome().client().isStopping())
231         return true;
232 #endif
233
234     return JSGlobalObject::shouldInterruptScriptBeforeTimeout(object);
235 }
236
237 RuntimeFlags JSDOMWindowBase::javaScriptRuntimeFlags(const JSGlobalObject* object)
238 {
239     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
240     Frame* frame = thisObject->wrapped().frame();
241     if (!frame)
242         return RuntimeFlags();
243     return frame->settings().javaScriptRuntimeFlags();
244 }
245
246 class JSDOMWindowMicrotaskCallback : public RefCounted<JSDOMWindowMicrotaskCallback> {
247 public:
248     static Ref<JSDOMWindowMicrotaskCallback> create(JSDOMWindowBase* globalObject, PassRefPtr<JSC::Microtask> task)
249     {
250         return adoptRef(*new JSDOMWindowMicrotaskCallback(globalObject, task));
251     }
252
253     void call()
254     {
255         Ref<JSDOMWindowMicrotaskCallback> protect(*this);
256         JSLockHolder lock(m_globalObject->vm());
257
258         ExecState* exec = m_globalObject->globalExec();
259
260         JSMainThreadExecState::runTask(exec, *m_task.get());
261
262         ASSERT(!exec->hadException());
263     }
264
265 private:
266     JSDOMWindowMicrotaskCallback(JSDOMWindowBase* globalObject, PassRefPtr<JSC::Microtask> task)
267         : m_globalObject(globalObject->vm(), globalObject)
268         , m_task(task)
269     {
270     }
271
272     Strong<JSDOMWindowBase> m_globalObject;
273     RefPtr<JSC::Microtask> m_task;
274 };
275
276 void JSDOMWindowBase::queueTaskToEventLoop(const JSGlobalObject* object, PassRefPtr<JSC::Microtask> task)
277 {
278     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
279
280     RefPtr<JSDOMWindowMicrotaskCallback> callback = JSDOMWindowMicrotaskCallback::create((JSDOMWindowBase*)thisObject, task);
281     auto microtask = std::make_unique<ActiveDOMCallbackMicrotask>(MicrotaskQueue::mainThreadQueue(), *thisObject->scriptExecutionContext(), [callback]() mutable {
282         callback->call();
283     });
284
285     MicrotaskQueue::mainThreadQueue().append(WTF::move(microtask));
286 }
287
288 void JSDOMWindowBase::willRemoveFromWindowShell()
289 {
290     setCurrentEvent(0);
291 }
292
293 JSDOMWindowShell* JSDOMWindowBase::shell() const
294 {
295     return m_shell;
296 }
297
298 VM& JSDOMWindowBase::commonVM()
299 {
300     ASSERT(isMainThread());
301
302     static VM* vm = nullptr;
303     if (!vm) {
304         ScriptController::initializeThreading();
305         vm = &VM::createLeaked(LargeHeap).leakRef();
306 #if !PLATFORM(IOS)
307         vm->setExclusiveThread(std::this_thread::get_id());
308 #else
309         vm->heap.setFullActivityCallback(WebSafeFullGCActivityCallback::create(&vm->heap));
310         vm->heap.setEdenActivityCallback(WebSafeEdenGCActivityCallback::create(&vm->heap));
311
312         vm->heap.setIncrementalSweeper(std::make_unique<WebSafeIncrementalSweeper>(&vm->heap));
313         vm->heap.machineThreads().addCurrentThread();
314 #endif
315
316 #if PLATFORM(MAC)
317         if (applicationIsITunes() || applicationIsIBooks() || Settings::shouldRewriteConstAsVar())
318             vm->setShouldRewriteConstAsVar(true);
319 #endif
320
321         initNormalWorldClientData(vm);
322     }
323
324     return *vm;
325 }
326
327 // JSDOMGlobalObject* is ignored, accessing a window in any context will
328 // use that DOMWindow's prototype chain.
329 JSValue toJS(ExecState* exec, JSDOMGlobalObject*, DOMWindow* domWindow)
330 {
331     return toJS(exec, domWindow);
332 }
333
334 JSValue toJS(ExecState* exec, DOMWindow* domWindow)
335 {
336     if (!domWindow)
337         return jsNull();
338     Frame* frame = domWindow->frame();
339     if (!frame)
340         return jsNull();
341     return frame->script().windowShell(currentWorld(exec));
342 }
343
344 JSDOMWindow* toJSDOMWindow(Frame* frame, DOMWrapperWorld& world)
345 {
346     if (!frame)
347         return 0;
348     return frame->script().windowShell(world)->window();
349 }
350
351 JSDOMWindow* toJSDOMWindow(JSValue value)
352 {
353     if (!value.isObject())
354         return 0;
355     while (!value.isNull()) {
356         JSObject* object = asObject(value);
357         const ClassInfo* classInfo = object->classInfo();
358         if (classInfo == JSDOMWindow::info())
359             return jsCast<JSDOMWindow*>(object);
360         if (classInfo == JSDOMWindowShell::info())
361             return jsCast<JSDOMWindowShell*>(object)->window();
362         value = object->prototype();
363     }
364     return 0;
365 }
366
367 void JSDOMWindowBase::fireFrameClearedWatchpointsForWindow(DOMWindow* window)
368 {
369     JSC::VM& vm = JSDOMWindowBase::commonVM();
370     JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData);
371     Vector<Ref<DOMWrapperWorld>> wrapperWorlds;
372     clientData->getAllWorlds(wrapperWorlds);
373     for (unsigned i = 0; i < wrapperWorlds.size(); ++i) {
374         DOMObjectWrapperMap& wrappers = wrapperWorlds[i]->m_wrappers;
375         auto result = wrappers.find(window);
376         if (result == wrappers.end())
377             continue;
378         JSC::JSObject* wrapper = result->value.get();
379         if (!wrapper)
380             continue;
381         JSDOMWindowBase* jsWindow = JSC::jsCast<JSDOMWindowBase*>(wrapper);
382         jsWindow->m_windowCloseWatchpoints.fireAll("Frame cleared");
383     }
384 }
385
386
387 JSC::JSInternalPromise* JSDOMWindowBase::moduleLoaderResolve(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSValue moduleName, JSC::JSValue importerModuleKey)
388 {
389     JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject);
390     if (RefPtr<Document> document = thisObject->wrapped().document())
391         return document->moduleLoader()->resolve(globalObject, exec, moduleName, importerModuleKey);
392     JSC::JSInternalPromiseDeferred* deferred = JSC::JSInternalPromiseDeferred::create(exec, globalObject);
393     return deferred->reject(exec, jsUndefined());
394 }
395
396 JSC::JSInternalPromise* JSDOMWindowBase::moduleLoaderFetch(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSValue moduleKey)
397 {
398     JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject);
399     if (RefPtr<Document> document = thisObject->wrapped().document())
400         return document->moduleLoader()->fetch(globalObject, exec, moduleKey);
401     JSC::JSInternalPromiseDeferred* deferred = JSC::JSInternalPromiseDeferred::create(exec, globalObject);
402     return deferred->reject(exec, jsUndefined());
403 }
404
405 JSC::JSValue JSDOMWindowBase::moduleLoaderEvaluate(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSValue moduleKey, JSC::JSValue moduleRecord)
406 {
407     JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject);
408     if (RefPtr<Document> document = thisObject->wrapped().document())
409         return document->moduleLoader()->evaluate(globalObject, exec, moduleKey, moduleRecord);
410     return JSC::jsUndefined();
411 }
412
413 } // namespace WebCore