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 | |
54 | namespace WebCore { |
55 | using namespace JSC; |
56 | |
57 | EncodedJSValue JSC_HOST_CALL makeThisTypeErrorForBuiltins(ExecState*); |
58 | EncodedJSValue JSC_HOST_CALL makeGetterTypeErrorForBuiltins(ExecState*); |
59 | EncodedJSValue JSC_HOST_CALL isReadableByteStreamAPIEnabled(ExecState*); |
60 | |
61 | const ClassInfo JSDOMGlobalObject::s_info = { "DOMGlobalObject" , &JSGlobalObject::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDOMGlobalObject) }; |
62 | |
63 | JSDOMGlobalObject::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 | |
71 | JSDOMGlobalObject::~JSDOMGlobalObject() = default; |
72 | |
73 | void JSDOMGlobalObject::destroy(JSCell* cell) |
74 | { |
75 | static_cast<JSDOMGlobalObject*>(cell)->JSDOMGlobalObject::~JSDOMGlobalObject(); |
76 | } |
77 | |
78 | EncodedJSValue 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 | |
92 | EncodedJSValue 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) |
110 | EncodedJSValue JSC_HOST_CALL isReadableByteStreamAPIEnabled(ExecState*) |
111 | { |
112 | return JSValue::encode(jsBoolean(RuntimeEnabledFeatures::sharedFeatures().readableByteStreamAPIEnabled())); |
113 | } |
114 | #endif |
115 | |
116 | void 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 | |
146 | void 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 | |
156 | void 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 | |
166 | ScriptExecutionContext* 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 | |
183 | void 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 | |
205 | void JSDOMGlobalObject::setCurrentEvent(Event* currentEvent) |
206 | { |
207 | m_currentEvent = currentEvent; |
208 | } |
209 | |
210 | Event* JSDOMGlobalObject::currentEvent() const |
211 | { |
212 | return m_currentEvent; |
213 | } |
214 | |
215 | void 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 | |
242 | JSDOMGlobalObject& 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 | |
284 | JSDOMGlobalObject* 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 | |