Source/JavaScriptCore:
[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  *
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 "JSModuleLoader.h"
33 #include "JSNode.h"
34 #include "Logging.h"
35 #include "Page.h"
36 #include "ScriptController.h"
37 #include "SecurityOrigin.h"
38 #include "Settings.h"
39 #include "WebCoreJSClientData.h"
40 #include <runtime/JSInternalPromiseDeferred.h>
41 #include <runtime/Microtask.h>
42 #include <wtf/MainThread.h>
43
44 #if PLATFORM(IOS)
45 #include "ChromeClient.h"
46 #include "WebSafeGCActivityCallbackIOS.h"
47 #include "WebSafeIncrementalSweeperIOS.h"
48 #endif
49
50 #if ENABLE(STREAMS_API)
51 #include "JSReadableStreamPrivateConstructors.h"
52 #include "ReadableStreamInternalsBuiltins.h"
53 #endif
54
55 using namespace JSC;
56
57 namespace WebCore {
58
59 static bool shouldAllowAccessFrom(const JSGlobalObject* thisObject, ExecState* exec)
60 {
61     return BindingSecurity::shouldAllowAccessToDOMWindow(exec, asJSDOMWindow(thisObject)->impl());
62 }
63
64 const ClassInfo JSDOMWindowBase::s_info = { "Window", &JSDOMGlobalObject::s_info, 0, CREATE_METHOD_TABLE(JSDOMWindowBase) };
65
66 const GlobalObjectMethodTable JSDOMWindowBase::s_globalObjectMethodTable = { &shouldAllowAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, &queueTaskToEventLoop, &shouldInterruptScriptBeforeTimeout, &moduleLoaderResolve, &moduleLoaderFetch, nullptr, nullptr, &moduleLoaderEvaluate };
67
68 JSDOMWindowBase::JSDOMWindowBase(VM& vm, Structure* structure, PassRefPtr<DOMWindow> window, JSDOMWindowShell* shell)
69     : JSDOMGlobalObject(vm, structure, &shell->world(), &s_globalObjectMethodTable)
70     , m_windowCloseWatchpoints((window && window->frame()) ? IsWatched : IsInvalidated)
71     , m_impl(window)
72     , m_shell(shell)
73     , m_privateFunctions(vm)
74 {
75 }
76
77 void JSDOMWindowBase::finishCreation(VM& vm, JSDOMWindowShell* shell)
78 {
79     Base::finishCreation(vm, shell);
80     ASSERT(inherits(info()));
81
82     m_privateFunctions.init(*this);
83
84     GlobalPropertyInfo staticGlobals[] = {
85         GlobalPropertyInfo(vm.propertyNames->document, jsNull(), DontDelete | ReadOnly),
86         GlobalPropertyInfo(vm.propertyNames->window, m_shell, DontDelete | ReadOnly),
87 #if ENABLE(STREAMS_API)
88         GlobalPropertyInfo(static_cast<JSVMClientData*>(vm.clientData)->builtinNames().readableStreamClosedPrivateName(), jsNumber(1), DontDelete | ReadOnly),
89         GlobalPropertyInfo(static_cast<JSVMClientData*>(vm.clientData)->builtinNames().readableStreamErroredPrivateName(), jsNumber(2), DontDelete | ReadOnly),
90         GlobalPropertyInfo(static_cast<JSVMClientData*>(vm.clientData)->builtinNames().readableStreamReadablePrivateName(), jsNumber(3), DontDelete | ReadOnly),
91         GlobalPropertyInfo(static_cast<JSVMClientData*>(vm.clientData)->builtinNames().ReadableStreamControllerPrivateName(), createReadableStreamControllerPrivateConstructor(vm, *this), DontDelete | ReadOnly),
92         GlobalPropertyInfo(static_cast<JSVMClientData*>(vm.clientData)->builtinNames().ReadableStreamReaderPrivateName(), createReadableStreamReaderPrivateConstructor(vm, *this), DontDelete | ReadOnly),
93 #define DECLARE_GLOBAL_STATIC(name)\
94         GlobalPropertyInfo(\
95             static_cast<JSVMClientData*>(vm.clientData)->builtinFunctions().readableStreamInternalsBuiltins().name##PrivateName(), \
96             m_privateFunctions.readableStreamInternals().m_##name##Function.get() , DontDelete | ReadOnly),
97         WEBCOREREADABLESTREAMINTERNALS_FOREACH_BUILTIN_FUNCTION_NAME(DECLARE_GLOBAL_STATIC)
98 #undef DECLARE_GLOBAL_STATIC
99 #endif
100     };
101
102     addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals));
103 }
104
105 void JSDOMWindowBase::visitChildren(JSCell* cell, SlotVisitor& visitor)
106 {
107     JSDOMWindowBase* thisObject = jsCast<JSDOMWindowBase*>(cell);
108     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
109     Base::visitChildren(thisObject, visitor);
110     thisObject->m_privateFunctions.visit(visitor);
111 }
112
113 void JSDOMWindowBase::destroy(JSCell* cell)
114 {
115     static_cast<JSDOMWindowBase*>(cell)->JSDOMWindowBase::~JSDOMWindowBase();
116 }
117
118 void JSDOMWindowBase::updateDocument()
119 {
120     ASSERT(m_impl->document());
121     ExecState* exec = globalExec();
122     symbolTablePutWithAttributesTouchWatchpointSet(this, exec, exec->vm().propertyNames->document, toJS(exec, this, m_impl->document()), DontDelete | ReadOnly);
123 }
124
125 ScriptExecutionContext* JSDOMWindowBase::scriptExecutionContext() const
126 {
127     return m_impl->document();
128 }
129
130 void JSDOMWindowBase::printErrorMessage(const String& message) const
131 {
132     printErrorMessageForFrame(impl().frame(), message);
133 }
134
135 bool JSDOMWindowBase::supportsProfiling(const JSGlobalObject* object)
136 {
137     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
138     Frame* frame = thisObject->impl().frame();
139     if (!frame)
140         return false;
141
142     Page* page = frame->page();
143     if (!page)
144         return false;
145
146     return page->inspectorController().profilerEnabled();
147 }
148
149 bool JSDOMWindowBase::supportsRichSourceInfo(const JSGlobalObject* object)
150 {
151     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
152     Frame* frame = thisObject->impl().frame();
153     if (!frame)
154         return false;
155
156     Page* page = frame->page();
157     if (!page)
158         return false;
159
160     bool enabled = page->inspectorController().enabled();
161     ASSERT(enabled || !thisObject->debugger());
162     ASSERT(enabled || !supportsProfiling(thisObject));
163     return enabled;
164 }
165
166 static inline bool shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(Page* page)
167 {
168     // See <rdar://problem/5479443>. We don't think that page can ever be NULL
169     // in this case, but if it is, we've gotten into a state where we may have
170     // hung the UI, with no way to ask the client whether to cancel execution.
171     // For now, our solution is just to cancel execution no matter what,
172     // ensuring that we never hang. We might want to consider other solutions
173     // if we discover problems with this one.
174     ASSERT(page);
175     return !page;
176 }
177
178 bool JSDOMWindowBase::shouldInterruptScript(const JSGlobalObject* object)
179 {
180     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
181     ASSERT(thisObject->impl().frame());
182     Page* page = thisObject->impl().frame()->page();
183     return shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page);
184 }
185
186 bool JSDOMWindowBase::shouldInterruptScriptBeforeTimeout(const JSGlobalObject* object)
187 {
188     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
189     ASSERT(thisObject->impl().frame());
190     Page* page = thisObject->impl().frame()->page();
191
192     if (shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page))
193         return true;
194
195 #if PLATFORM(IOS)
196     if (page->chrome().client().isStopping())
197         return true;
198 #endif
199
200     return JSGlobalObject::shouldInterruptScriptBeforeTimeout(object);
201 }
202
203 RuntimeFlags JSDOMWindowBase::javaScriptRuntimeFlags(const JSGlobalObject* object)
204 {
205     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
206     Frame* frame = thisObject->impl().frame();
207     if (!frame)
208         return RuntimeFlags();
209     return frame->settings().javaScriptRuntimeFlags();
210 }
211
212 void JSDOMWindowBase::queueTaskToEventLoop(const JSGlobalObject* object, PassRefPtr<Microtask> task)
213 {
214     const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object);
215     thisObject->scriptExecutionContext()->postTask(JSGlobalObjectTask((JSDOMWindowBase*)thisObject, task));
216 }
217
218 void JSDOMWindowBase::willRemoveFromWindowShell()
219 {
220     setCurrentEvent(0);
221 }
222
223 JSDOMWindowShell* JSDOMWindowBase::shell() const
224 {
225     return m_shell;
226 }
227
228 VM& JSDOMWindowBase::commonVM()
229 {
230     ASSERT(isMainThread());
231
232     static VM* vm = nullptr;
233     if (!vm) {
234         ScriptController::initializeThreading();
235         vm = &VM::createLeaked(LargeHeap).leakRef();
236 #if !PLATFORM(IOS)
237         vm->setExclusiveThread(std::this_thread::get_id());
238 #else
239         vm->heap.setFullActivityCallback(WebSafeFullGCActivityCallback::create(&vm->heap));
240         vm->heap.setEdenActivityCallback(WebSafeEdenGCActivityCallback::create(&vm->heap));
241
242         vm->heap.setIncrementalSweeper(std::make_unique<WebSafeIncrementalSweeper>(&vm->heap));
243         vm->heap.machineThreads().addCurrentThread();
244 #endif
245         initNormalWorldClientData(vm);
246     }
247
248     return *vm;
249 }
250
251 // JSDOMGlobalObject* is ignored, accessing a window in any context will
252 // use that DOMWindow's prototype chain.
253 JSValue toJS(ExecState* exec, JSDOMGlobalObject*, DOMWindow* domWindow)
254 {
255     return toJS(exec, domWindow);
256 }
257
258 JSValue toJS(ExecState* exec, DOMWindow* domWindow)
259 {
260     if (!domWindow)
261         return jsNull();
262     Frame* frame = domWindow->frame();
263     if (!frame)
264         return jsNull();
265     return frame->script().windowShell(currentWorld(exec));
266 }
267
268 JSDOMWindow* toJSDOMWindow(Frame* frame, DOMWrapperWorld& world)
269 {
270     if (!frame)
271         return 0;
272     return frame->script().windowShell(world)->window();
273 }
274
275 JSDOMWindow* toJSDOMWindow(JSValue value)
276 {
277     if (!value.isObject())
278         return 0;
279     while (!value.isNull()) {
280         JSObject* object = asObject(value);
281         const ClassInfo* classInfo = object->classInfo();
282         if (classInfo == JSDOMWindow::info())
283             return jsCast<JSDOMWindow*>(object);
284         if (classInfo == JSDOMWindowShell::info())
285             return jsCast<JSDOMWindowShell*>(object)->window();
286         value = object->prototype();
287     }
288     return 0;
289 }
290
291 void JSDOMWindowBase::fireFrameClearedWatchpointsForWindow(DOMWindow* window)
292 {
293     JSC::VM& vm = JSDOMWindowBase::commonVM();
294     JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData);
295     Vector<Ref<DOMWrapperWorld>> wrapperWorlds;
296     clientData->getAllWorlds(wrapperWorlds);
297     for (unsigned i = 0; i < wrapperWorlds.size(); ++i) {
298         DOMObjectWrapperMap& wrappers = wrapperWorlds[i]->m_wrappers;
299         auto result = wrappers.find(window);
300         if (result == wrappers.end())
301             continue;
302         JSC::JSObject* wrapper = result->value.get();
303         if (!wrapper)
304             continue;
305         JSDOMWindowBase* jsWindow = JSC::jsCast<JSDOMWindowBase*>(wrapper);
306         jsWindow->m_windowCloseWatchpoints.fireAll("Frame cleared");
307     }
308 }
309
310
311 JSC::JSInternalPromise* JSDOMWindowBase::moduleLoaderResolve(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSValue moduleName, JSC::JSValue importerModuleKey)
312 {
313     JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject);
314     if (RefPtr<Document> document = thisObject->impl().document())
315         return document->moduleLoader()->resolve(globalObject, exec, moduleName, importerModuleKey);
316     JSC::JSInternalPromiseDeferred* deferred = JSC::JSInternalPromiseDeferred::create(exec, globalObject);
317     return deferred->reject(exec, jsUndefined());
318 }
319
320 JSC::JSInternalPromise* JSDOMWindowBase::moduleLoaderFetch(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSValue moduleKey)
321 {
322     JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject);
323     if (RefPtr<Document> document = thisObject->impl().document())
324         return document->moduleLoader()->fetch(globalObject, exec, moduleKey);
325     JSC::JSInternalPromiseDeferred* deferred = JSC::JSInternalPromiseDeferred::create(exec, globalObject);
326     return deferred->reject(exec, jsUndefined());
327 }
328
329 JSC::JSValue JSDOMWindowBase::moduleLoaderEvaluate(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSValue moduleKey, JSC::JSValue moduleRecord)
330 {
331     JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject);
332     if (RefPtr<Document> document = thisObject->impl().document())
333         return document->moduleLoader()->evaluate(globalObject, exec, moduleKey, moduleRecord);
334     return JSC::jsUndefined();
335 }
336
337 } // namespace WebCore