1/*
2 * Copyright (C) 2008-2018 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 */
26
27#include "config.h"
28#include "JSDOMGlobalObject.h"
29
30#include "DOMWindow.h"
31#include "Document.h"
32#include "JSDOMPromiseDeferred.h"
33#include "JSDOMWindow.h"
34#include "JSEventListener.h"
35#include "JSMediaStream.h"
36#include "JSMediaStreamTrack.h"
37#include "JSRTCIceCandidate.h"
38#include "JSRTCSessionDescription.h"
39#include "JSReadableStream.h"
40#include "JSRemoteDOMWindow.h"
41#include "JSWorkerGlobalScope.h"
42#include "JSWorkletGlobalScope.h"
43#include "RejectedPromiseTracker.h"
44#include "RuntimeEnabledFeatures.h"
45#include "StructuredClone.h"
46#include "WebCoreJSClientData.h"
47#include "WorkerGlobalScope.h"
48#include <JavaScriptCore/BuiltinNames.h>
49#include <JavaScriptCore/CodeBlock.h>
50#include <JavaScriptCore/JSInternalPromise.h>
51#include <JavaScriptCore/JSInternalPromiseDeferred.h>
52#include <JavaScriptCore/StructureInlines.h>
53
54namespace WebCore {
55using namespace JSC;
56
57EncodedJSValue JSC_HOST_CALL makeThisTypeErrorForBuiltins(ExecState*);
58EncodedJSValue JSC_HOST_CALL makeGetterTypeErrorForBuiltins(ExecState*);
59EncodedJSValue JSC_HOST_CALL isReadableByteStreamAPIEnabled(ExecState*);
60
61const ClassInfo JSDOMGlobalObject::s_info = { "DOMGlobalObject", &JSGlobalObject::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDOMGlobalObject) };
62
63JSDOMGlobalObject::JSDOMGlobalObject(VM& vm, Structure* structure, Ref<DOMWrapperWorld>&& world, const GlobalObjectMethodTable* globalObjectMethodTable)
64 : JSGlobalObject(vm, structure, globalObjectMethodTable)
65 , m_world(WTFMove(world))
66 , m_worldIsNormal(m_world->isNormal())
67 , m_builtinInternalFunctions(vm)
68{
69}
70
71JSDOMGlobalObject::~JSDOMGlobalObject() = default;
72
73void JSDOMGlobalObject::destroy(JSCell* cell)
74{
75 static_cast<JSDOMGlobalObject*>(cell)->JSDOMGlobalObject::~JSDOMGlobalObject();
76}
77
78EncodedJSValue JSC_HOST_CALL makeThisTypeErrorForBuiltins(ExecState* execState)
79{
80 ASSERT(execState);
81 ASSERT(execState->argumentCount() == 2);
82 VM& vm = execState->vm();
83 auto scope = DECLARE_CATCH_SCOPE(vm);
84
85 auto interfaceName = execState->uncheckedArgument(0).getString(execState);
86 scope.assertNoException();
87 auto functionName = execState->uncheckedArgument(1).getString(execState);
88 scope.assertNoException();
89 return JSValue::encode(createTypeError(execState, makeThisTypeErrorMessage(interfaceName.utf8().data(), functionName.utf8().data())));
90}
91
92EncodedJSValue JSC_HOST_CALL makeGetterTypeErrorForBuiltins(ExecState* execState)
93{
94 ASSERT(execState);
95 ASSERT(execState->argumentCount() == 2);
96 VM& vm = execState->vm();
97 auto scope = DECLARE_CATCH_SCOPE(vm);
98
99 auto interfaceName = execState->uncheckedArgument(0).getString(execState);
100 scope.assertNoException();
101 auto attributeName = execState->uncheckedArgument(1).getString(execState);
102 scope.assertNoException();
103
104 auto error = static_cast<ErrorInstance*>(createTypeError(execState, makeGetterTypeErrorMessage(interfaceName.utf8().data(), attributeName.utf8().data())));
105 error->setNativeGetterTypeError();
106 return JSValue::encode(error);
107}
108
109#if ENABLE(STREAMS_API)
110EncodedJSValue JSC_HOST_CALL isReadableByteStreamAPIEnabled(ExecState*)
111{
112 return JSValue::encode(jsBoolean(RuntimeEnabledFeatures::sharedFeatures().readableByteStreamAPIEnabled()));
113}
114#endif
115
116void JSDOMGlobalObject::addBuiltinGlobals(VM& vm)
117{
118 m_builtinInternalFunctions.initialize(*this);
119
120 JSVMClientData& clientData = *static_cast<JSVMClientData*>(vm.clientData);
121 JSDOMGlobalObject::GlobalPropertyInfo staticGlobals[] = {
122 JSDOMGlobalObject::GlobalPropertyInfo(clientData.builtinNames().makeThisTypeErrorPrivateName(),
123 JSFunction::create(vm, this, 2, String(), makeThisTypeErrorForBuiltins), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
124 JSDOMGlobalObject::GlobalPropertyInfo(clientData.builtinNames().makeGetterTypeErrorPrivateName(),
125 JSFunction::create(vm, this, 2, String(), makeGetterTypeErrorForBuiltins), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
126 JSDOMGlobalObject::GlobalPropertyInfo(clientData.builtinNames().cloneArrayBufferPrivateName(),
127 JSFunction::create(vm, this, 3, String(), cloneArrayBuffer), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
128 JSDOMGlobalObject::GlobalPropertyInfo(clientData.builtinNames().structuredCloneArrayBufferPrivateName(),
129 JSFunction::create(vm, this, 1, String(), structuredCloneArrayBuffer), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
130 JSDOMGlobalObject::GlobalPropertyInfo(clientData.builtinNames().structuredCloneArrayBufferViewPrivateName(),
131 JSFunction::create(vm, this, 1, String(), structuredCloneArrayBufferView), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
132 JSDOMGlobalObject::GlobalPropertyInfo(vm.propertyNames->builtinNames().ArrayBufferPrivateName(), arrayBufferConstructor(), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
133#if ENABLE(STREAMS_API)
134 JSDOMGlobalObject::GlobalPropertyInfo(clientData.builtinNames().streamClosedPrivateName(), jsNumber(1), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
135 JSDOMGlobalObject::GlobalPropertyInfo(clientData.builtinNames().streamClosingPrivateName(), jsNumber(2), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
136 JSDOMGlobalObject::GlobalPropertyInfo(clientData.builtinNames().streamErroredPrivateName(), jsNumber(3), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
137 JSDOMGlobalObject::GlobalPropertyInfo(clientData.builtinNames().streamReadablePrivateName(), jsNumber(4), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
138 JSDOMGlobalObject::GlobalPropertyInfo(clientData.builtinNames().streamWaitingPrivateName(), jsNumber(5), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
139 JSDOMGlobalObject::GlobalPropertyInfo(clientData.builtinNames().streamWritablePrivateName(), jsNumber(6), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
140 JSDOMGlobalObject::GlobalPropertyInfo(clientData.builtinNames().readableByteStreamAPIEnabledPrivateName(), JSFunction::create(vm, this, 0, String(), isReadableByteStreamAPIEnabled), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly),
141#endif
142 };
143 addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals));
144}
145
146void JSDOMGlobalObject::finishCreation(VM& vm)
147{
148 Base::finishCreation(vm);
149 ASSERT(inherits(vm, info()));
150
151 addBuiltinGlobals(vm);
152
153 RELEASE_ASSERT(classInfo());
154}
155
156void JSDOMGlobalObject::finishCreation(VM& vm, JSObject* thisValue)
157{
158 Base::finishCreation(vm, thisValue);
159 ASSERT(inherits(vm, info()));
160
161 addBuiltinGlobals(vm);
162
163 RELEASE_ASSERT(classInfo());
164}
165
166ScriptExecutionContext* JSDOMGlobalObject::scriptExecutionContext() const
167{
168 if (inherits<JSDOMWindowBase>(vm()))
169 return jsCast<const JSDOMWindowBase*>(this)->scriptExecutionContext();
170 if (inherits<JSRemoteDOMWindowBase>(vm()))
171 return nullptr;
172 if (inherits<JSWorkerGlobalScopeBase>(vm()))
173 return jsCast<const JSWorkerGlobalScopeBase*>(this)->scriptExecutionContext();
174#if ENABLE(CSS_PAINTING_API)
175 if (inherits<JSWorkletGlobalScopeBase>(vm()))
176 return jsCast<const JSWorkletGlobalScopeBase*>(this)->scriptExecutionContext();
177#endif
178 dataLog("Unexpected global object: ", JSValue(this), "\n");
179 RELEASE_ASSERT_NOT_REACHED();
180 return nullptr;
181}
182
183void JSDOMGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
184{
185 JSDOMGlobalObject* thisObject = jsCast<JSDOMGlobalObject*>(cell);
186 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
187 Base::visitChildren(thisObject, visitor);
188
189 {
190 auto locker = holdLock(thisObject->m_gcLock);
191
192 for (auto& structure : thisObject->structures(locker).values())
193 visitor.append(structure);
194
195 for (auto& constructor : thisObject->constructors(locker).values())
196 visitor.append(constructor);
197
198 for (auto& guarded : thisObject->guardedObjects(locker))
199 guarded->visitAggregate(visitor);
200 }
201
202 thisObject->m_builtinInternalFunctions.visit(visitor);
203}
204
205void JSDOMGlobalObject::setCurrentEvent(Event* currentEvent)
206{
207 m_currentEvent = currentEvent;
208}
209
210Event* JSDOMGlobalObject::currentEvent() const
211{
212 return m_currentEvent;
213}
214
215void JSDOMGlobalObject::promiseRejectionTracker(JSGlobalObject* jsGlobalObject, ExecState* exec, JSPromise* promise, JSPromiseRejectionOperation operation)
216{
217 // https://html.spec.whatwg.org/multipage/webappapis.html#the-hostpromiserejectiontracker-implementation
218
219 VM& vm = exec->vm();
220 auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(jsGlobalObject);
221 auto* context = globalObject.scriptExecutionContext();
222 if (!context)
223 return;
224
225 // InternalPromises should not be exposed to user scripts.
226 if (JSC::jsDynamicCast<JSC::JSInternalPromise*>(vm, promise))
227 return;
228
229 // FIXME: If script has muted errors (cross origin), terminate these steps.
230 // <https://webkit.org/b/171415> Implement the `muted-errors` property of Scripts to avoid onerror/onunhandledrejection for cross-origin scripts
231
232 switch (operation) {
233 case JSPromiseRejectionOperation::Reject:
234 context->ensureRejectedPromiseTracker().promiseRejected(*exec, globalObject, *promise);
235 break;
236 case JSPromiseRejectionOperation::Handle:
237 context->ensureRejectedPromiseTracker().promiseHandled(*exec, globalObject, *promise);
238 break;
239 }
240}
241
242JSDOMGlobalObject& callerGlobalObject(ExecState& state)
243{
244 class GetCallerGlobalObjectFunctor {
245 public:
246 GetCallerGlobalObjectFunctor() = default;
247
248 StackVisitor::Status operator()(StackVisitor& visitor) const
249 {
250 if (!m_hasSkippedFirstFrame) {
251 m_hasSkippedFirstFrame = true;
252 return StackVisitor::Continue;
253 }
254
255 if (auto* codeBlock = visitor->codeBlock())
256 m_globalObject = codeBlock->globalObject();
257 else {
258 ASSERT(visitor->callee().rawPtr());
259 // FIXME: Callee is not an object if the caller is Web Assembly.
260 // Figure out what to do here. We can probably get the global object
261 // from the top-most Wasm Instance. https://bugs.webkit.org/show_bug.cgi?id=165721
262 if (visitor->callee().isCell() && visitor->callee().asCell()->isObject())
263 m_globalObject = jsCast<JSObject*>(visitor->callee().asCell())->globalObject();
264 }
265 return StackVisitor::Done;
266 }
267
268 JSGlobalObject* globalObject() const { return m_globalObject; }
269
270 private:
271 mutable bool m_hasSkippedFirstFrame { false };
272 mutable JSGlobalObject* m_globalObject { nullptr };
273 };
274
275 GetCallerGlobalObjectFunctor iter;
276 state.iterate(iter);
277 if (iter.globalObject())
278 return *jsCast<JSDOMGlobalObject*>(iter.globalObject());
279
280 VM& vm = state.vm();
281 return *jsCast<JSDOMGlobalObject*>(vm.vmEntryGlobalObject(&state));
282}
283
284JSDOMGlobalObject* toJSDOMGlobalObject(ScriptExecutionContext& context, DOMWrapperWorld& world)
285{
286 if (is<Document>(context))
287 return toJSDOMWindow(downcast<Document>(context).frame(), world);
288
289 if (is<WorkerGlobalScope>(context))
290 return downcast<WorkerGlobalScope>(context).script()->workerGlobalScopeWrapper();
291
292 ASSERT_NOT_REACHED();
293 return nullptr;
294}
295
296} // namespace WebCore
297