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