diff options
author | 2022-04-09 19:53:17 -0700 | |
---|---|---|
committer | 2022-04-09 19:53:17 -0700 | |
commit | c5d637fbb15874caa0d9669ac8cc79912ed4d5d9 (patch) | |
tree | f20a9b0047ba763b0003a60032d6c4ae1f9dcb47 /src/javascript | |
parent | fb82e2bf86a13211f08b407c3ec80fc0db45a77c (diff) | |
download | bun-c5d637fbb15874caa0d9669ac8cc79912ed4d5d9.tar.gz bun-c5d637fbb15874caa0d9669ac8cc79912ed4d5d9.tar.zst bun-c5d637fbb15874caa0d9669ac8cc79912ed4d5d9.zip |
Diffstat (limited to 'src/javascript')
154 files changed, 19232 insertions, 38 deletions
diff --git a/src/javascript/jsc/bindings/DOMWrapperWorld.h b/src/javascript/jsc/bindings/DOMWrapperWorld.h index c03b5ed41..4d6b77ae7 100644 --- a/src/javascript/jsc/bindings/DOMWrapperWorld.h +++ b/src/javascript/jsc/bindings/DOMWrapperWorld.h @@ -44,8 +44,7 @@ inline DOMWrapperWorld& worldForDOMObject(JSC::JSObject& object); inline bool isWorldCompatible(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value) { - return true; - // return !value.isObject() || &worldForDOMObject(*value.getObject()) == ¤tWorld(lexicalGlobalObject); + return !value.isObject() || &worldForDOMObject(*value.getObject()) == ¤tWorld(lexicalGlobalObject); } inline DOMWrapperWorld& currentWorld(JSC::JSGlobalObject& lexicalGlobalObject) diff --git a/src/javascript/jsc/bindings/FetchOptions.h b/src/javascript/jsc/bindings/FetchOptions.h new file mode 100644 index 000000000..dad88d7dd --- /dev/null +++ b/src/javascript/jsc/bindings/FetchOptions.h @@ -0,0 +1,44 @@ +#pragma once + +namespace WebCore { + +struct FetchOptions { + enum class Destination : uint8_t { EmptyString, + Audio, + Audioworklet, + Document, + Embed, + Font, + Image, + Iframe, + Manifest, + Model, + Object, + Paintworklet, + Report, + Script, + Serviceworker, + Sharedworker, + Style, + Track, + Video, + Worker, + Xslt }; + enum class Mode : uint8_t { Navigate, + SameOrigin, + NoCors, + Cors }; + enum class Credentials : uint8_t { Omit, + SameOrigin, + Include }; + enum class Cache : uint8_t { Default, + NoStore, + Reload, + NoCache, + ForceCache, + OnlyIfCached }; + enum class Redirect : uint8_t { Follow, + Error, + Manual }; +}; +}
\ No newline at end of file diff --git a/src/javascript/jsc/bindings/JSDOMExceptionHandling.cpp b/src/javascript/jsc/bindings/JSDOMExceptionHandling.cpp index 494bf9e3a..e682cca6a 100644 --- a/src/javascript/jsc/bindings/JSDOMExceptionHandling.cpp +++ b/src/javascript/jsc/bindings/JSDOMExceptionHandling.cpp @@ -24,6 +24,7 @@ #include "DOMException.h" #include "JSDOMException.h" #include "JSDOMExceptionHandling.h" +#include "JSDOMPromiseDeferred.h" #include "JavaScriptCore/ErrorHandlingScope.h" #include "JavaScriptCore/Exception.h" @@ -275,10 +276,10 @@ void throwNonFiniteTypeError(JSGlobalObject& lexicalGlobalObject, JSC::ThrowScop throwTypeError(&lexicalGlobalObject, scope, "The provided value is non-finite"_s); } -// JSC::EncodedJSValue rejectPromiseWithGetterTypeError(JSC::JSGlobalObject& lexicalGlobalObject, const JSC::ClassInfo* classInfo, JSC::PropertyName attributeName) -// { -// return createRejectedPromiseWithTypeError(lexicalGlobalObject, JSC::makeDOMAttributeGetterTypeErrorMessage(classInfo->className, String(attributeName.uid())), RejectedPromiseWithTypeErrorCause::NativeGetter); -// } +JSC::EncodedJSValue rejectPromiseWithGetterTypeError(JSC::JSGlobalObject& lexicalGlobalObject, const JSC::ClassInfo* classInfo, JSC::PropertyName attributeName) +{ + return createRejectedPromiseWithTypeError(lexicalGlobalObject, JSC::makeDOMAttributeGetterTypeErrorMessage(classInfo->className, String(attributeName.uid())), RejectedPromiseWithTypeErrorCause::NativeGetter); +} String makeThisTypeErrorMessage(const char* interfaceName, const char* functionName) { @@ -295,16 +296,16 @@ EncodedJSValue throwThisTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC: return throwTypeError(lexicalGlobalObject, scope, makeThisTypeErrorMessage(interfaceName, functionName)); } -// JSC::EncodedJSValue rejectPromiseWithThisTypeError(DeferredPromise& promise, const char* interfaceName, const char* methodName) -// { -// promise.reject(TypeError, makeThisTypeErrorMessage(interfaceName, methodName)); -// return JSValue::encode(jsUndefined()); -// } +JSC::EncodedJSValue rejectPromiseWithThisTypeError(DeferredPromise& promise, const char* interfaceName, const char* methodName) +{ + promise.reject(TypeError, makeThisTypeErrorMessage(interfaceName, methodName)); + return JSValue::encode(jsUndefined()); +} -// JSC::EncodedJSValue rejectPromiseWithThisTypeError(JSC::JSGlobalObject& lexicalGlobalObject, const char* interfaceName, const char* methodName) -// { -// return createRejectedPromiseWithTypeError(lexicalGlobalObject, makeThisTypeErrorMessage(interfaceName, methodName), RejectedPromiseWithTypeErrorCause::InvalidThis); -// } +JSC::EncodedJSValue rejectPromiseWithThisTypeError(JSC::JSGlobalObject& lexicalGlobalObject, const char* interfaceName, const char* methodName) +{ + return createRejectedPromiseWithTypeError(lexicalGlobalObject, makeThisTypeErrorMessage(interfaceName, methodName), RejectedPromiseWithTypeErrorCause::InvalidThis); +} void throwDOMSyntaxError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, ASCIILiteral message) { diff --git a/src/javascript/jsc/bindings/JSDOMExceptionHandling.h b/src/javascript/jsc/bindings/JSDOMExceptionHandling.h index a130dfcde..01dc44790 100644 --- a/src/javascript/jsc/bindings/JSDOMExceptionHandling.h +++ b/src/javascript/jsc/bindings/JSDOMExceptionHandling.h @@ -60,9 +60,9 @@ String makeUnsupportedIndexedSetterErrorMessage(const char* interfaceName); WEBCORE_EXPORT JSC::EncodedJSValue throwThisTypeError(JSC::JSGlobalObject&, JSC::ThrowScope&, const char* interfaceName, const char* functionName); -// WEBCORE_EXPORT JSC::EncodedJSValue rejectPromiseWithGetterTypeError(JSC::JSGlobalObject&, const JSC::ClassInfo*, JSC::PropertyName attributeName); -// WEBCORE_EXPORT JSC::EncodedJSValue rejectPromiseWithThisTypeError(DeferredPromise&, const char* interfaceName, const char* operationName); -// WEBCORE_EXPORT JSC::EncodedJSValue rejectPromiseWithThisTypeError(JSC::JSGlobalObject&, const char* interfaceName, const char* operationName); +WEBCORE_EXPORT JSC::EncodedJSValue rejectPromiseWithGetterTypeError(JSC::JSGlobalObject&, const JSC::ClassInfo*, JSC::PropertyName attributeName); +WEBCORE_EXPORT JSC::EncodedJSValue rejectPromiseWithThisTypeError(DeferredPromise&, const char* interfaceName, const char* operationName); +WEBCORE_EXPORT JSC::EncodedJSValue rejectPromiseWithThisTypeError(JSC::JSGlobalObject&, const char* interfaceName, const char* operationName); String retrieveErrorMessageWithoutName(JSC::JSGlobalObject&, JSC::VM&, JSC::JSValue exception, JSC::CatchScope&); String retrieveErrorMessage(JSC::JSGlobalObject&, JSC::VM&, JSC::JSValue exception, JSC::CatchScope&); diff --git a/src/javascript/jsc/bindings/ScriptExecutionContext.h b/src/javascript/jsc/bindings/ScriptExecutionContext.h index 4265ffccd..0d3eaddcf 100644 --- a/src/javascript/jsc/bindings/ScriptExecutionContext.h +++ b/src/javascript/jsc/bindings/ScriptExecutionContext.h @@ -1,7 +1,6 @@ #pragma once #include "root.h" -#include "ActiveDOMObject.h" #include <wtf/CrossThreadTask.h> #include <wtf/Function.h> #include <wtf/HashSet.h> @@ -65,12 +64,37 @@ public: bool isDocument() { return false; } bool isWorkerGlobalScope() { return true; } bool isJSExecutionForbidden() { return false; } + + EventLoopTaskGroup& eventLoop() { return m_eventLoop; } + void reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, JSC::Exception* exception, RefPtr<void*>&&, CachedScript* = nullptr, bool = false) { } - // void reportUnhandledPromiseRejection(JSC::JSGlobalObject&, JSC::JSPromise&, RefPtr<Inspector::ScriptCallStack>&&) - // { - // } + void reportUnhandledPromiseRejection(JSC::JSGlobalObject&, JSC::JSPromise&, RefPtr<Inspector::ScriptCallStack>&&) + { + } + // Called from the constructor and destructors of ActiveDOMObject. + void didCreateActiveDOMObject(ActiveDOMObject&); + void willDestroyActiveDOMObject(ActiveDOMObject&); + + // Called after the construction of an ActiveDOMObject to synchronize suspend state. + void suspendActiveDOMObjectIfNeeded(ActiveDOMObject&); + + void didCreateDestructionObserver(ContextDestructionObserver&); + void willDestroyDestructionObserver(ContextDestructionObserver&); + + // MessagePort is conceptually a kind of ActiveDOMObject, but it needs to be tracked separately for message dispatch. + void processMessageWithMessagePortsSoon(); + void dispatchMessagePortEvents(); + void createdMessagePort(MessagePort&); + void destroyedMessagePort(MessagePort&); + + ReasonForSuspension reasonForSuspendingActiveDOMObjects() const { return m_reasonForSuspendingActiveDOMObjects; } + + bool hasPendingActivity() const; + void removeFromContextsMap(); + void removeRejectedPromiseTracker(); + void regenerateIdentifier(); void postTask(Task&&) { @@ -91,5 +115,21 @@ private: JSC::VM* m_vm = nullptr; JSC::JSGlobalObject* m_globalObject = nullptr; WTF::URL m_url = WTF::URL(); + + enum class ShouldContinue { No, + Yes }; + void forEachActiveDOMObject(const Function<ShouldContinue(ActiveDOMObject&)>&) const; + RejectedPromiseTracker& ensureRejectedPromiseTrackerSlow(); + HashSet<MessagePort*> m_messagePorts; + HashSet<ContextDestructionObserver*> m_destructionObservers; + HashSet<ActiveDOMObject*> m_activeDOMObjects; + std::unique_ptr<RejectedPromiseTracker> m_rejectedPromiseTracker; + + ReasonForSuspension m_reasonForSuspendingActiveDOMObjects { static_cast<ReasonForSuspension>(-1) }; + bool m_activeDOMObjectsAreSuspended { false }; + bool m_activeDOMObjectsAreStopped { false }; + bool m_inDispatchErrorEvent { false }; + mutable bool m_activeDOMObjectAdditionForbidden { false }; + bool m_willprocessMessageWithMessagePortsSoon { false }; }; }
\ No newline at end of file diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp index a2e4fc42a..1bc03ff50 100644 --- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp +++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp @@ -87,6 +87,7 @@ #include "Process.h" #include "JavaScriptCore/RemoteInspectorServer.h" +#include "JSDOMGuardedObject.h" using JSGlobalObject = JSC::JSGlobalObject; using Exception = JSC::Exception; @@ -287,6 +288,7 @@ GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure) , m_constructors(makeUnique<WebCore::DOMConstructors>()) , m_world(WebCore::DOMWrapperWorld::create(vm, WebCore::DOMWrapperWorld::Type::Normal)) , m_worldIsNormal(true) + , m_guardedObjects() { m_scriptExecutionContext = new WebCore::ScriptExecutionContext(&vm, this); @@ -482,6 +484,16 @@ JSC_DEFINE_CUSTOM_GETTER(property_lazyProcessGetter, return JSC::JSValue::encode(JSC::JSValue(process)); } +void JSDOMGlobalObject::clearDOMGuardedObjects() const +{ + // No locking is necessary here since we are not directly modifying the returned container. + // Calling JSDOMGuardedObject::clear() will however modify the guarded objects container but + // it will grab the lock as needed. + auto guardedObjectsCopy = guardedObjects(); + for (auto& guarded : guardedObjectsCopy) + guarded->clear(); +} + static JSC_DECLARE_HOST_FUNCTION(functionQueueMicrotask); static JSC_DEFINE_HOST_FUNCTION(functionQueueMicrotask, @@ -896,8 +908,8 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) for (auto& structure : thisObject->m_structures.values()) visitor.append(structure); - // for (auto& guarded : thisObject->m_guardedObjects) - // guarded->visitAggregate(visitor); + for (auto& guarded : thisObject->m_guardedObjects) + guarded->visitAggregate(visitor); } for (auto& constructor : thisObject->constructors().array()) diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.h b/src/javascript/jsc/bindings/ZigGlobalObject.h index da67f93c1..ed3ce7156 100644 --- a/src/javascript/jsc/bindings/ZigGlobalObject.h +++ b/src/javascript/jsc/bindings/ZigGlobalObject.h @@ -11,6 +11,7 @@ class Identifier; namespace WebCore { class ScriptExecutionContext; +class DOMGuardedObject; } #include "root.h" @@ -30,6 +31,7 @@ class ScriptExecutionContext; #include "DOMIsoSubspaces.h" namespace Zig { +using DOMGuardedObjectSet = HashSet<WebCore::DOMGuardedObject*>; using JSDOMStructureMap = HashMap<const JSC::ClassInfo*, JSC::WriteBarrier<JSC::Structure>>; @@ -83,6 +85,20 @@ public: return *m_constructors; } + Zig::DOMGuardedObjectSet& guardedObjects() WTF_REQUIRES_LOCK(m_gcLock) { return m_guardedObjects; } + + const Zig::DOMGuardedObjectSet& guardedObjects() const WTF_IGNORES_THREAD_SAFETY_ANALYSIS + { + ASSERT(!Thread::mayBeGCThread()); + return m_guardedObjects; + } + + Zig::DOMGuardedObjectSet& guardedObjects(NoLockingNecessaryTag) WTF_IGNORES_THREAD_SAFETY_ANALYSIS + { + ASSERT(!vm().heap.mutatorShouldBeFenced()); + return m_guardedObjects; + } + WebCore::DOMWrapperWorld& world() { return m_world.get(); } DECLARE_VISIT_CHILDREN; @@ -90,6 +106,8 @@ public: bool worldIsNormal() const { return m_worldIsNormal; } static ptrdiff_t offsetOfWorldIsNormal() { return OBJECT_OFFSETOF(GlobalObject, m_worldIsNormal); } + void clearDOMGuardedObjects(); + WebCore::ScriptExecutionContext* scriptExecutionContext(); WebCore::ScriptExecutionContext* scriptExecutionContext() const; @@ -134,6 +152,7 @@ private: Lock m_gcLock; WebCore::ScriptExecutionContext* m_scriptExecutionContext; Ref<WebCore::DOMWrapperWorld> m_world; + Zig::DOMGuardedObjectSet m_guardedObjects WTF_GUARDED_BY_LOCK(m_gcLock); }; class JSMicrotaskCallback : public RefCounted<JSMicrotaskCallback> { diff --git a/src/javascript/jsc/bindings/not-implemented.h b/src/javascript/jsc/bindings/not-implemented.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/not-implemented.h diff --git a/src/javascript/jsc/bindings/root.h b/src/javascript/jsc/bindings/root.h index ec2d228f7..f77589df9 100644 --- a/src/javascript/jsc/bindings/root.h +++ b/src/javascript/jsc/bindings/root.h @@ -78,4 +78,7 @@ #define WTF_MAKE_ISO_ALLOCATED_EXPORT(className, a) WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(className); #define WTF_MAKE_ISO_ALLOCATED_IMPL(className) + +#define JSC_NOT_IMPLEMENTED_GETTER_BODY return JSC::JSValue::encode(JSC::jsUndefined()) + #endif
\ No newline at end of file diff --git a/src/javascript/jsc/bindings/webcore/AbstractWorker.cpp b/src/javascript/jsc/bindings/webcore/AbstractWorker.cpp new file mode 100644 index 000000000..e32653a81 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/AbstractWorker.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "AbstractWorker.h" + +// #include "ContentSecurityPolicy.h" +#include "ScriptExecutionContext.h" +// #include "SecurityOrigin.h" +#include "WorkerOptions.h" +#include <wtf/IsoMallocInlines.h> + +namespace WebCore { + +WTF_MAKE_ISO_ALLOCATED_IMPL(AbstractWorker); + +FetchOptions AbstractWorker::workerFetchOptions(const WorkerOptions& options, FetchOptions::Destination destination) +{ + FetchOptions fetchOptions; + fetchOptions.mode = FetchOptions::Mode::SameOrigin; + if (options.type == WorkerType::Module) + fetchOptions.credentials = options.credentials; + else + fetchOptions.credentials = FetchOptions::Credentials::SameOrigin; + fetchOptions.cache = FetchOptions::Cache::Default; + fetchOptions.redirect = FetchOptions::Redirect::Follow; + fetchOptions.destination = destination; + return fetchOptions; +} + +ExceptionOr<URL> AbstractWorker::resolveURL(const String& url) +{ + auto& context = *scriptExecutionContext(); + + // FIXME: This should use the dynamic global scope (bug #27887). + URL scriptURL = context.completeURL(url); + if (!scriptURL.isValid()) + return Exception { SyntaxError }; + + // if (!context.securityOrigin()->canRequest(scriptURL) && !scriptURL.protocolIsData()) + // return Exception { SecurityError }; + + // ASSERT(context.contentSecurityPolicy()); + // if (!context.contentSecurityPolicy()->allowWorkerFromSource(scriptURL)) + // return Exception { SecurityError }; + + return scriptURL; +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/AbstractWorker.h b/src/javascript/jsc/bindings/webcore/AbstractWorker.h new file mode 100644 index 000000000..c595dcee2 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/AbstractWorker.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "EventTarget.h" +#include "ExceptionOr.h" +#include "FetchOptions.h" + +namespace WebCore { + +struct FetchOptions; +struct WorkerOptions; + +class AbstractWorker : public RefCounted<AbstractWorker>, public EventTargetWithInlineData { + WTF_MAKE_ISO_ALLOCATED(AbstractWorker); +public: + using RefCounted::ref; + using RefCounted::deref; + + static FetchOptions workerFetchOptions(const WorkerOptions&, FetchOptions::Destination); + +protected: + AbstractWorker() = default; + + // Helper function that converts a URL to an absolute URL and checks the result for validity. + ExceptionOr<URL> resolveURL(const String& url); + + intptr_t asID() const { return reinterpret_cast<intptr_t>(this); } + +private: + void refEventTarget() final { ref(); } + void derefEventTarget() final { deref(); } +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/AbstractWorker.idl b/src/javascript/jsc/bindings/webcore/AbstractWorker.idl new file mode 100644 index 000000000..fe0debc38 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/AbstractWorker.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2011, 2020 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// https://html.spec.whatwg.org/multipage/workers.html#abstractworker +interface mixin AbstractWorker { + attribute EventHandler onerror; +}; + diff --git a/src/javascript/jsc/bindings/webcore/ActiveDOMCallback.cpp b/src/javascript/jsc/bindings/webcore/ActiveDOMCallback.cpp new file mode 100644 index 000000000..2f7936aef --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/ActiveDOMCallback.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010. 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ActiveDOMCallback.h" + +#include "ScriptExecutionContext.h" + +namespace WebCore { + +ActiveDOMCallback::ActiveDOMCallback(ScriptExecutionContext* context) + : ContextDestructionObserver(context) +{ +} + +ActiveDOMCallback::~ActiveDOMCallback() = default; + +bool ActiveDOMCallback::canInvokeCallback() const +{ + ScriptExecutionContext* context = scriptExecutionContext(); + return context && !context->activeDOMObjectsAreSuspended() && !context->activeDOMObjectsAreStopped(); +} + +bool ActiveDOMCallback::activeDOMObjectsAreSuspended() const +{ + auto* context = scriptExecutionContext(); + return context && context->activeDOMObjectsAreSuspended(); +} + +bool ActiveDOMCallback::activeDOMObjectAreStopped() const +{ + auto* context = scriptExecutionContext(); + return !context || context->activeDOMObjectsAreStopped(); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/ActiveDOMCallback.h b/src/javascript/jsc/bindings/webcore/ActiveDOMCallback.h new file mode 100644 index 000000000..1117b952f --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/ActiveDOMCallback.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010, 2012 Google Inc. All rights reserved. + * Copyright (C) 2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ContextDestructionObserver.h" + +namespace JSC { +class AbstractSlotVisitor; +class SlotVisitor; +} + +namespace WebCore { + +class ScriptExecutionContext; + +// A base class that prevents binding callbacks from executing when +// active dom objects are stopped or suspended. +// +// Should only be created, used, and destroyed on the script execution +// context thread. +class ActiveDOMCallback : public ContextDestructionObserver { +public: + WEBCORE_EXPORT ActiveDOMCallback(ScriptExecutionContext*); + WEBCORE_EXPORT virtual ~ActiveDOMCallback(); + + WEBCORE_EXPORT bool canInvokeCallback() const; + + WEBCORE_EXPORT bool activeDOMObjectsAreSuspended() const; + WEBCORE_EXPORT bool activeDOMObjectAreStopped() const; + + virtual void visitJSFunction(JSC::AbstractSlotVisitor&) { } + virtual void visitJSFunction(JSC::SlotVisitor&) { } +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/ActiveDOMObject.cpp b/src/javascript/jsc/bindings/webcore/ActiveDOMObject.cpp new file mode 100644 index 000000000..059b8039c --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/ActiveDOMObject.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "ActiveDOMObject.h" + +// #include "Document.h" +#include "Event.h" +#include "EventTarget.h" +#include "EventLoop.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +static inline ScriptExecutionContext* suitableScriptExecutionContext(ScriptExecutionContext* scriptExecutionContext) +{ + // For detached documents, make sure we observe their context document instead. + // return is<Document>(scriptExecutionContext) ? &downcast<Document>(*scriptExecutionContext).contextDocument() : scriptExecutionContext; + return scriptExecutionContext; +} + +inline ActiveDOMObject::ActiveDOMObject(ScriptExecutionContext* context, CheckedScriptExecutionContextType) + : ContextDestructionObserver(context) +{ + // ASSERT(!is<Document>(context) || &downcast<Document>(context)->contextDocument() == downcast<Document>(context)); + if (!context) + return; + + ASSERT(context->isContextThread()); + context->didCreateActiveDOMObject(*this); +} + +ActiveDOMObject::ActiveDOMObject(ScriptExecutionContext* scriptExecutionContext) + : ActiveDOMObject(suitableScriptExecutionContext(scriptExecutionContext), CheckedScriptExecutionContext) +{ +} + +// ActiveDOMObject::ActiveDOMObject(Document* document) +// : ActiveDOMObject(document ? &document->contextDocument() : nullptr, CheckedScriptExecutionContext) +// { +// } + +// ActiveDOMObject::ActiveDOMObject(Document& document) +// : ActiveDOMObject(&document.contextDocument(), CheckedScriptExecutionContext) +// { +// } + +ActiveDOMObject::~ActiveDOMObject() +{ + ASSERT(canCurrentThreadAccessThreadLocalData(m_creationThread)); + + // ActiveDOMObject may be inherited by a sub-class whose life-cycle + // exceeds that of the associated ScriptExecutionContext. In those cases, + // m_scriptExecutionContext would/should have been nullified by + // ContextDestructionObserver::contextDestroyed() (which we implement / + // inherit). Hence, we should ensure that this is not 0 before use it + // here. + auto* context = scriptExecutionContext(); + if (!context) + return; + + ASSERT(m_suspendIfNeededWasCalled); + ASSERT(context->isContextThread()); + context->willDestroyActiveDOMObject(*this); +} + +void ActiveDOMObject::suspendIfNeeded() +{ +#if ASSERT_ENABLED + ASSERT(!m_suspendIfNeededWasCalled); + m_suspendIfNeededWasCalled = true; +#endif + if (auto* context = scriptExecutionContext()) + context->suspendActiveDOMObjectIfNeeded(*this); +} + +#if ASSERT_ENABLED + +void ActiveDOMObject::assertSuspendIfNeededWasCalled() const +{ + if (!m_suspendIfNeededWasCalled) + WTFLogAlways("Failed to call suspendIfNeeded() for %s", activeDOMObjectName()); + ASSERT(m_suspendIfNeededWasCalled); +} + +#endif // ASSERT_ENABLED + +void ActiveDOMObject::suspend(ReasonForSuspension) +{ +} + +void ActiveDOMObject::resume() +{ +} + +void ActiveDOMObject::stop() +{ +} + +bool ActiveDOMObject::isContextStopped() const +{ + return !scriptExecutionContext() || scriptExecutionContext()->activeDOMObjectsAreStopped(); +} + +bool ActiveDOMObject::isAllowedToRunScript() const +{ + return scriptExecutionContext() && !scriptExecutionContext()->activeDOMObjectsAreStopped() && !scriptExecutionContext()->activeDOMObjectsAreSuspended(); +} + +void ActiveDOMObject::queueTaskInEventLoop(TaskSource source, Function<void()>&& function) +{ + auto* context = scriptExecutionContext(); + if (!context) + return; + context->eventLoop().queueTask(source, WTFMove(function)); +} + +class ActiveDOMObjectEventDispatchTask : public EventLoopTask { +public: + ActiveDOMObjectEventDispatchTask(TaskSource source, EventLoopTaskGroup& group, ActiveDOMObject& object, Function<void()>&& dispatchEvent) + : EventLoopTask(source, group) + , m_object(object) + , m_dispatchEvent(WTFMove(dispatchEvent)) + { + ++m_object.m_pendingActivityInstanceCount; + } + + ~ActiveDOMObjectEventDispatchTask() + { + ASSERT(m_object.m_pendingActivityInstanceCount); + --m_object.m_pendingActivityInstanceCount; + } + + void execute() final + { + // If this task executes after the script execution context has been stopped, don't + // actually dispatch the event. + if (m_object.isAllowedToRunScript()) + m_dispatchEvent(); + } + +private: + ActiveDOMObject& m_object; + Function<void()> m_dispatchEvent; +}; + +void ActiveDOMObject::queueTaskToDispatchEventInternal(EventTarget& target, TaskSource source, Ref<Event>&& event) +{ + ASSERT(!event->target() || &target == event->target()); + auto* context = scriptExecutionContext(); + if (!context) + return; + auto& eventLoopTaskGroup = context->eventLoop(); + auto task = makeUnique<ActiveDOMObjectEventDispatchTask>(source, eventLoopTaskGroup, *this, [target = Ref { target }, event = WTFMove(event)] { + target->dispatchEvent(event); + }); + eventLoopTaskGroup.queueTask(WTFMove(task)); +} + +void ActiveDOMObject::queueCancellableTaskToDispatchEventInternal(EventTarget& target, TaskSource source, TaskCancellationGroup& cancellationGroup, Ref<Event>&& event) +{ + ASSERT(!event->target() || &target == event->target()); + auto* context = scriptExecutionContext(); + if (!context) + return; + auto& eventLoopTaskGroup = context->eventLoop(); + auto task = makeUnique<ActiveDOMObjectEventDispatchTask>(source, eventLoopTaskGroup, *this, CancellableTask(cancellationGroup, [target = Ref { target }, event = WTFMove(event)] { + target->dispatchEvent(event); + })); + eventLoopTaskGroup.queueTask(WTFMove(task)); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/ActiveDOMObject.h b/src/javascript/jsc/bindings/webcore/ActiveDOMObject.h index e69de29bb..0712e3511 100644 --- a/src/javascript/jsc/bindings/webcore/ActiveDOMObject.h +++ b/src/javascript/jsc/bindings/webcore/ActiveDOMObject.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#include "root.h" + +#include "ScriptExecutionContext.h" +#include "ContextDestructionObserver.h" +#include "TaskSource.h" +#include <wtf/Assertions.h> +#include <wtf/CancellableTask.h> +#include <wtf/Forward.h> +#include <wtf/Function.h> +#include <wtf/RefCounted.h> +#include <wtf/Threading.h> + +namespace WebCore { + +// class Document; +class Event; +class EventLoopTaskGroup; +class EventTarget; + +enum class ReasonForSuspension { + JavaScriptDebuggerPaused, + WillDeferLoading, + BackForwardCache, + PageWillBeSuspended, +}; + +class WEBCORE_EXPORT ActiveDOMObject : public ContextDestructionObserver { +public: + // The suspendIfNeeded must be called exactly once after object construction to update + // the suspended state to match that of the ScriptExecutionContext. + void suspendIfNeeded(); + void assertSuspendIfNeededWasCalled() const; + + // This function is used by JS bindings to determine if the JS wrapper should be kept alive or not. + bool hasPendingActivity() const { return m_pendingActivityInstanceCount || virtualHasPendingActivity(); } + + // However, the suspend function will sometimes be called even if canSuspendForDocumentSuspension() returns false. + // That happens in step-by-step JS debugging for example - in this case it would be incorrect + // to stop the object. Exact semantics of suspend is up to the object in cases like that. + + virtual const char* activeDOMObjectName() const = 0; + + // These functions must not have a side effect of creating or destroying + // any ActiveDOMObject. That means they must not result in calls to arbitrary JavaScript. + virtual void suspend(ReasonForSuspension); + virtual void resume(); + + // This function must not have a side effect of creating an ActiveDOMObject. + // That means it must not result in calls to arbitrary JavaScript. + // It can, however, have a side effect of deleting an ActiveDOMObject. + virtual void stop(); + + template<class T> + class PendingActivity : public RefCounted<PendingActivity<T>> { + public: + explicit PendingActivity(T& thisObject) + : m_thisObject(thisObject) + { + ++(m_thisObject->m_pendingActivityInstanceCount); + } + + ~PendingActivity() + { + ASSERT(m_thisObject->m_pendingActivityInstanceCount > 0); + --(m_thisObject->m_pendingActivityInstanceCount); + } + + private: + Ref<T> m_thisObject; + }; + + template<class T> Ref<PendingActivity<T>> makePendingActivity(T& thisObject) + { + ASSERT(&thisObject == this); + return adoptRef(*new PendingActivity<T>(thisObject)); + } + + bool isContextStopped() const; + bool isAllowedToRunScript() const; + + template<typename T> + static void queueTaskKeepingObjectAlive(T& object, TaskSource source, Function<void()>&& task) + { + object.queueTaskInEventLoop(source, [protectedObject = Ref { object }, activity = object.ActiveDOMObject::makePendingActivity(object), task = WTFMove(task)]() { + task(); + }); + } + + template<typename T> + static void queueCancellableTaskKeepingObjectAlive(T& object, TaskSource source, TaskCancellationGroup& cancellationGroup, Function<void()>&& task) + { + CancellableTask cancellableTask(cancellationGroup, WTFMove(task)); + object.queueTaskInEventLoop(source, [protectedObject = Ref { object }, activity = object.ActiveDOMObject::makePendingActivity(object), cancellableTask = WTFMove(cancellableTask)]() mutable { + cancellableTask(); + }); + } + + template<typename EventTargetType> + static void queueTaskToDispatchEvent(EventTargetType& target, TaskSource source, Ref<Event>&& event) + { + target.queueTaskToDispatchEventInternal(target, source, WTFMove(event)); + } + + template<typename EventTargetType> + static void queueCancellableTaskToDispatchEvent(EventTargetType& target, TaskSource source, TaskCancellationGroup& cancellationGroup, Ref<Event>&& event) + { + target.queueCancellableTaskToDispatchEventInternal(target, source, cancellationGroup, WTFMove(event)); + } + +protected: + explicit ActiveDOMObject(ScriptExecutionContext*); + // explicit ActiveDOMObject(Document*); + // explicit ActiveDOMObject(Document&); + virtual ~ActiveDOMObject(); + +private: + enum CheckedScriptExecutionContextType { CheckedScriptExecutionContext }; + ActiveDOMObject(ScriptExecutionContext*, CheckedScriptExecutionContextType); + + // This is used by subclasses to indicate that they have pending activity, meaning that they would + // like the JS wrapper to stay alive (because they may still fire JS events). + virtual bool virtualHasPendingActivity() const { return false; } + + void queueTaskInEventLoop(TaskSource, Function<void()>&&); + void queueTaskToDispatchEventInternal(EventTarget&, TaskSource, Ref<Event>&&); + void queueCancellableTaskToDispatchEventInternal(EventTarget&, TaskSource, TaskCancellationGroup&, Ref<Event>&&); + + uint64_t m_pendingActivityInstanceCount { 0 }; +#if ASSERT_ENABLED + bool m_suspendIfNeededWasCalled { false }; + Ref<Thread> m_creationThread { Thread::current() }; +#endif + + friend class ActiveDOMObjectEventDispatchTask; +}; + +#if !ASSERT_ENABLED + +inline void ActiveDOMObject::assertSuspendIfNeededWasCalled() const +{ +} + +#endif + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/Blob.h b/src/javascript/jsc/bindings/webcore/Blob.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/Blob.h diff --git a/src/javascript/jsc/bindings/webcore/ContentSecurityPolicyResponseHeaders.h b/src/javascript/jsc/bindings/webcore/ContentSecurityPolicyResponseHeaders.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/ContentSecurityPolicyResponseHeaders.h diff --git a/src/javascript/jsc/bindings/webcore/DOMClientIsoSubspaces.h b/src/javascript/jsc/bindings/webcore/DOMClientIsoSubspaces.h index ad1b6a32b..8caf414e4 100644 --- a/src/javascript/jsc/bindings/webcore/DOMClientIsoSubspaces.h +++ b/src/javascript/jsc/bindings/webcore/DOMClientIsoSubspaces.h @@ -416,9 +416,9 @@ public: // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForIdleDeadline; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForInputEvent; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForKeyboardEvent; - // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMessageChannel; - // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMessageEvent; - // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMessagePort; + std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMessageChannel; + std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMessageEvent; + std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMessagePort; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMouseEvent; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMutationEvent; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMutationObserver; @@ -434,7 +434,7 @@ public: // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPopStateEvent; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForProcessingInstruction; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForProgressEvent; - // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPromiseRejectionEvent; + std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPromiseRejectionEvent; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForRange; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForSecurityPolicyViolationEvent; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForShadowRoot; @@ -671,7 +671,7 @@ public: // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForVisualViewport; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWebKitNamespace; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWebKitPoint; - // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWorkerNavigator; + std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWorkerNavigator; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDOMMimeType; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDOMMimeTypeArray; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDOMPlugin; @@ -835,7 +835,7 @@ public: // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDedicatedWorkerGlobalScope; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWorker; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWorkerGlobalScope; - // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWorkerLocation; + std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWorkerLocation; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForExtendableEvent; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForExtendableMessageEvent; // std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForFetchEvent; diff --git a/src/javascript/jsc/bindings/webcore/DOMIsoSubspaces.h b/src/javascript/jsc/bindings/webcore/DOMIsoSubspaces.h index 75d91c0a2..ccfde3a0f 100644 --- a/src/javascript/jsc/bindings/webcore/DOMIsoSubspaces.h +++ b/src/javascript/jsc/bindings/webcore/DOMIsoSubspaces.h @@ -407,9 +407,9 @@ public: // std::unique_ptr<IsoSubspace> m_subspaceForIdleDeadline; // std::unique_ptr<IsoSubspace> m_subspaceForInputEvent; // std::unique_ptr<IsoSubspace> m_subspaceForKeyboardEvent; - // std::unique_ptr<IsoSubspace> m_subspaceForMessageChannel; - // std::unique_ptr<IsoSubspace> m_subspaceForMessageEvent; - // std::unique_ptr<IsoSubspace> m_subspaceForMessagePort; + std::unique_ptr<IsoSubspace> m_subspaceForMessageChannel; + std::unique_ptr<IsoSubspace> m_subspaceForMessageEvent; + std::unique_ptr<IsoSubspace> m_subspaceForMessagePort; // std::unique_ptr<IsoSubspace> m_subspaceForMouseEvent; // std::unique_ptr<IsoSubspace> m_subspaceForMutationEvent; // std::unique_ptr<IsoSubspace> m_subspaceForMutationObserver; @@ -425,7 +425,7 @@ public: // std::unique_ptr<IsoSubspace> m_subspaceForPopStateEvent; // std::unique_ptr<IsoSubspace> m_subspaceForProcessingInstruction; // std::unique_ptr<IsoSubspace> m_subspaceForProgressEvent; - // std::unique_ptr<IsoSubspace> m_subspaceForPromiseRejectionEvent; + std::unique_ptr<IsoSubspace> m_subspaceForPromiseRejectionEvent; // std::unique_ptr<IsoSubspace> m_subspaceForRange; // std::unique_ptr<IsoSubspace> m_subspaceForSecurityPolicyViolationEvent; // std::unique_ptr<IsoSubspace> m_subspaceForShadowRoot; @@ -662,7 +662,7 @@ public: // std::unique_ptr<IsoSubspace> m_subspaceForVisualViewport; // std::unique_ptr<IsoSubspace> m_subspaceForWebKitNamespace; // std::unique_ptr<IsoSubspace> m_subspaceForWebKitPoint; - // std::unique_ptr<IsoSubspace> m_subspaceForWorkerNavigator; + std::unique_ptr<IsoSubspace> m_subspaceForWorkerNavigator; // std::unique_ptr<IsoSubspace> m_subspaceForDOMMimeType; // std::unique_ptr<IsoSubspace> m_subspaceForDOMMimeTypeArray; // std::unique_ptr<IsoSubspace> m_subspaceForDOMPlugin; @@ -826,7 +826,7 @@ public: // std::unique_ptr<IsoSubspace> m_subspaceForDedicatedWorkerGlobalScope; // std::unique_ptr<IsoSubspace> m_subspaceForWorker; std::unique_ptr<IsoSubspace> m_subspaceForWorkerGlobalScope; - // std::unique_ptr<IsoSubspace> m_subspaceForWorkerLocation; + std::unique_ptr<IsoSubspace> m_subspaceForWorkerLocation; // std::unique_ptr<IsoSubspace> m_subspaceForExtendableEvent; // std::unique_ptr<IsoSubspace> m_subspaceForExtendableMessageEvent; // std::unique_ptr<IsoSubspace> m_subspaceForFetchEvent; diff --git a/src/javascript/jsc/bindings/webcore/DOMPromiseProxy.h b/src/javascript/jsc/bindings/webcore/DOMPromiseProxy.h new file mode 100644 index 000000000..2b8706e76 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/DOMPromiseProxy.h @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2017-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ExceptionOr.h" +#include "JSDOMGlobalObject.h" +#include "JSDOMPromiseDeferred.h" +#include <wtf/Function.h> +#include <wtf/Vector.h> + +namespace WebCore { + +template<typename IDLType> +class DOMPromiseProxy { + WTF_MAKE_FAST_ALLOCATED; +public: + using Value = typename IDLType::StorageType; + + DOMPromiseProxy() = default; + ~DOMPromiseProxy() = default; + + JSC::JSValue promise(JSC::JSGlobalObject&, JSDOMGlobalObject&); + + void clear(); + + bool isFulfilled() const; + + void resolve(typename IDLType::StorageType); + void resolveWithNewlyCreated(typename IDLType::StorageType); + void reject(Exception, RejectAsHandled = RejectAsHandled::No); + +private: + JSC::JSValue resolvePromise(JSC::JSGlobalObject&, JSDOMGlobalObject&, const Function<void(DeferredPromise&)>&); + + std::optional<ExceptionOr<Value>> m_valueOrException; + Vector<Ref<DeferredPromise>, 1> m_deferredPromises; +}; + +template<> +class DOMPromiseProxy<IDLUndefined> { + WTF_MAKE_FAST_ALLOCATED; +public: + DOMPromiseProxy() = default; + ~DOMPromiseProxy() = default; + + JSC::JSValue promise(JSC::JSGlobalObject&, JSDOMGlobalObject&); + + void clear(); + + bool isFulfilled() const; + + void resolve(); + void reject(Exception, RejectAsHandled = RejectAsHandled::No); + +private: + std::optional<ExceptionOr<void>> m_valueOrException; + Vector<Ref<DeferredPromise>, 1> m_deferredPromises; +}; + +// Instead of storing the value of the resolution directly, DOMPromiseProxyWithResolveCallback +// allows the owner to specify callback to be called when the resolved value is needed. This is +// needed to avoid reference cycles when the resolved value is the owner, such as is the case with +// FontFace and FontFaceSet. +template<typename IDLType> +class DOMPromiseProxyWithResolveCallback { + WTF_MAKE_FAST_ALLOCATED; +public: + using ResolveCallback = Function<typename IDLType::ParameterType()>; + + template <typename Class, typename BaseClass> + DOMPromiseProxyWithResolveCallback(Class&, typename IDLType::ParameterType (BaseClass::*)()); + DOMPromiseProxyWithResolveCallback(ResolveCallback&&); + ~DOMPromiseProxyWithResolveCallback() = default; + + JSC::JSValue promise(JSC::JSGlobalObject&, JSDOMGlobalObject&); + + void clear(); + + bool isFulfilled() const; + + void resolve(typename IDLType::ParameterType); + void resolveWithNewlyCreated(typename IDLType::ParameterType); + void reject(Exception, RejectAsHandled = RejectAsHandled::No); + +private: + ResolveCallback m_resolveCallback; + std::optional<ExceptionOr<void>> m_valueOrException; + Vector<Ref<DeferredPromise>, 1> m_deferredPromises; +}; + +// MARK: - DOMPromiseProxy<IDLType> generic implementation + +template<typename IDLType> +inline JSC::JSValue DOMPromiseProxy<IDLType>::resolvePromise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject, const Function<void(DeferredPromise&)>& resolvePromiseCallback) +{ + UNUSED_PARAM(lexicalGlobalObject); + for (auto& deferredPromise : m_deferredPromises) { + if (deferredPromise->globalObject() == &globalObject) + return deferredPromise->promise(); + } + + // DeferredPromise can fail construction during worker abrupt termination. + auto deferredPromise = DeferredPromise::create(globalObject, DeferredPromise::Mode::RetainPromiseOnResolve); + if (!deferredPromise) + return JSC::jsUndefined(); + + if (m_valueOrException) { + if (m_valueOrException->hasException()) + deferredPromise->reject(m_valueOrException->exception()); + else + resolvePromiseCallback(*deferredPromise); + } + + auto result = deferredPromise->promise(); + m_deferredPromises.append(deferredPromise.releaseNonNull()); + return result; +} + +template<typename IDLType> +inline JSC::JSValue DOMPromiseProxy<IDLType>::promise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject) +{ + return resolvePromise(lexicalGlobalObject, globalObject, [this](auto& deferredPromise) { + deferredPromise.template resolve<IDLType>(m_valueOrException->returnValue()); + }); +} + +template<> +inline JSC::JSValue DOMPromiseProxy<IDLAny>::promise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject) +{ + return resolvePromise(lexicalGlobalObject, globalObject, [this](auto& deferredPromise) { + deferredPromise.resolveWithJSValue(m_valueOrException->returnValue().get()); + }); +} + +template<typename IDLType> +inline void DOMPromiseProxy<IDLType>::clear() +{ + m_valueOrException = std::nullopt; + m_deferredPromises.clear(); +} + +template<typename IDLType> +inline bool DOMPromiseProxy<IDLType>::isFulfilled() const +{ + return m_valueOrException.has_value(); +} + +template<typename IDLType> +inline void DOMPromiseProxy<IDLType>::resolve(typename IDLType::StorageType value) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr<Value> { std::forward<typename IDLType::StorageType>(value) }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->template resolve<IDLType>(m_valueOrException->returnValue()); +} + +template<> +inline void DOMPromiseProxy<IDLAny>::resolve(typename IDLAny::StorageType value) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr<Value> { std::forward<typename IDLAny::StorageType>(value) }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->resolveWithJSValue(m_valueOrException->returnValue().get()); +} + +template<typename IDLType> +inline void DOMPromiseProxy<IDLType>::resolveWithNewlyCreated(typename IDLType::StorageType value) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr<Value> { std::forward<typename IDLType::StorageType>(value) }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->template resolveWithNewlyCreated<IDLType>(m_valueOrException->returnValue()); +} + +template<typename IDLType> +inline void DOMPromiseProxy<IDLType>::reject(Exception exception, RejectAsHandled rejectAsHandled) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr<Value> { WTFMove(exception) }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->reject(m_valueOrException->exception(), rejectAsHandled); +} + + +// MARK: - DOMPromiseProxy<IDLUndefined> specialization + +inline JSC::JSValue DOMPromiseProxy<IDLUndefined>::promise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(lexicalGlobalObject); + for (auto& deferredPromise : m_deferredPromises) { + if (deferredPromise->globalObject() == &globalObject) + return deferredPromise->promise(); + } + + // DeferredPromise can fail construction during worker abrupt termination. + auto deferredPromise = DeferredPromise::create(globalObject, DeferredPromise::Mode::RetainPromiseOnResolve); + if (!deferredPromise) + return JSC::jsUndefined(); + + if (m_valueOrException) { + if (m_valueOrException->hasException()) + deferredPromise->reject(m_valueOrException->exception()); + else + deferredPromise->resolve(); + } + + auto result = deferredPromise->promise(); + m_deferredPromises.append(deferredPromise.releaseNonNull()); + return result; +} + +inline void DOMPromiseProxy<IDLUndefined>::clear() +{ + m_valueOrException = std::nullopt; + m_deferredPromises.clear(); +} + +inline bool DOMPromiseProxy<IDLUndefined>::isFulfilled() const +{ + return m_valueOrException.has_value(); +} + +inline void DOMPromiseProxy<IDLUndefined>::resolve() +{ + ASSERT(!m_valueOrException); + m_valueOrException = ExceptionOr<void> { }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->resolve(); +} + +inline void DOMPromiseProxy<IDLUndefined>::reject(Exception exception, RejectAsHandled rejectAsHandled) +{ + ASSERT(!m_valueOrException); + m_valueOrException = ExceptionOr<void> { WTFMove(exception) }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->reject(m_valueOrException->exception(), rejectAsHandled); +} + +// MARK: - DOMPromiseProxyWithResolveCallback<IDLType> implementation + +template<typename IDLType> +template <typename Class, typename BaseClass> +inline DOMPromiseProxyWithResolveCallback<IDLType>::DOMPromiseProxyWithResolveCallback(Class& object, typename IDLType::ParameterType (BaseClass::*function)()) + : m_resolveCallback(std::bind(function, &object)) +{ +} + +template<typename IDLType> +inline DOMPromiseProxyWithResolveCallback<IDLType>::DOMPromiseProxyWithResolveCallback(ResolveCallback&& function) + : m_resolveCallback(WTFMove(function)) +{ +} + +template<typename IDLType> +inline JSC::JSValue DOMPromiseProxyWithResolveCallback<IDLType>::promise(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(lexicalGlobalObject); + for (auto& deferredPromise : m_deferredPromises) { + if (deferredPromise->globalObject() == &globalObject) + return deferredPromise->promise(); + } + + // DeferredPromise can fail construction during worker abrupt termination. + auto deferredPromise = DeferredPromise::create(globalObject, DeferredPromise::Mode::RetainPromiseOnResolve); + if (!deferredPromise) + return JSC::jsUndefined(); + + if (m_valueOrException) { + if (m_valueOrException->hasException()) + deferredPromise->reject(m_valueOrException->exception()); + else + deferredPromise->template resolve<IDLType>(m_resolveCallback()); + } + + auto result = deferredPromise->promise(); + m_deferredPromises.append(deferredPromise.releaseNonNull()); + return result; +} + +template<typename IDLType> +inline void DOMPromiseProxyWithResolveCallback<IDLType>::clear() +{ + m_valueOrException = std::nullopt; + m_deferredPromises.clear(); +} + +template<typename IDLType> +inline bool DOMPromiseProxyWithResolveCallback<IDLType>::isFulfilled() const +{ + return m_valueOrException.has_value(); +} + +template<typename IDLType> +inline void DOMPromiseProxyWithResolveCallback<IDLType>::resolve(typename IDLType::ParameterType value) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr<void> { }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->template resolve<IDLType>(value); +} + +template<typename IDLType> +inline void DOMPromiseProxyWithResolveCallback<IDLType>::resolveWithNewlyCreated(typename IDLType::ParameterType value) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr<void> { }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->template resolveWithNewlyCreated<IDLType>(value); +} + +template<typename IDLType> +inline void DOMPromiseProxyWithResolveCallback<IDLType>::reject(Exception exception, RejectAsHandled rejectAsHandled) +{ + ASSERT(!m_valueOrException); + + m_valueOrException = ExceptionOr<void> { WTFMove(exception) }; + for (auto& deferredPromise : m_deferredPromises) + deferredPromise->reject(m_valueOrException->exception(), rejectAsHandled); +} + +} diff --git a/src/javascript/jsc/bindings/webcore/DOMWindow.h b/src/javascript/jsc/bindings/webcore/DOMWindow.h new file mode 100644 index 000000000..d067d0b5a --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/DOMWindow.h @@ -0,0 +1 @@ +// stub
\ No newline at end of file diff --git a/src/javascript/jsc/bindings/webcore/DedicatedWorkerGlobalScope.cpp b/src/javascript/jsc/bindings/webcore/DedicatedWorkerGlobalScope.cpp new file mode 100644 index 000000000..d3fbfe6cd --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/DedicatedWorkerGlobalScope.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DedicatedWorkerGlobalScope.h" + +#include "ContentSecurityPolicyResponseHeaders.h" +#include "DOMWindow.h" +#include "DedicatedWorkerThread.h" +#include "EventNames.h" +#include "JSRTCRtpScriptTransformer.h" +#include "MessageEvent.h" +#include "RTCTransformEvent.h" +#include "RequestAnimationFrameCallback.h" +#include "SecurityOrigin.h" +#include "StructuredSerializeOptions.h" +#include "Worker.h" +#if ENABLE(OFFSCREEN_CANVAS) +#include "WorkerAnimationController.h" +#endif +#include "WorkerObjectProxy.h" +#include <wtf/IsoMallocInlines.h> + +namespace WebCore { + +WTF_MAKE_ISO_ALLOCATED_IMPL(DedicatedWorkerGlobalScope); + +Ref<DedicatedWorkerGlobalScope> DedicatedWorkerGlobalScope::create(const WorkerParameters& params, Ref<SecurityOrigin>&& origin, DedicatedWorkerThread& thread, Ref<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider) +{ + auto context = adoptRef(*new DedicatedWorkerGlobalScope(params, WTFMove(origin), thread, WTFMove(topOrigin), connectionProxy, socketProvider)); + if (!params.shouldBypassMainWorldContentSecurityPolicy) + context->applyContentSecurityPolicyResponseHeaders(params.contentSecurityPolicyResponseHeaders); + return context; +} + +DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(const WorkerParameters& params, Ref<SecurityOrigin>&& origin, DedicatedWorkerThread& thread, Ref<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider) + : WorkerGlobalScope(WorkerThreadType::DedicatedWorker, params, WTFMove(origin), thread, WTFMove(topOrigin), connectionProxy, socketProvider) + , m_name(params.name) +{ +} + +DedicatedWorkerGlobalScope::~DedicatedWorkerGlobalScope() = default; + +EventTargetInterface DedicatedWorkerGlobalScope::eventTargetInterface() const +{ + return DedicatedWorkerGlobalScopeEventTargetInterfaceType; +} + +void DedicatedWorkerGlobalScope::prepareForDestruction() +{ + WorkerGlobalScope::prepareForDestruction(); +} + +ExceptionOr<void> DedicatedWorkerGlobalScope::postMessage(JSC::JSGlobalObject& state, JSC::JSValue messageValue, StructuredSerializeOptions&& options) +{ + Vector<RefPtr<MessagePort>> ports; + auto message = SerializedScriptValue::create(state, messageValue, WTFMove(options.transfer), ports, SerializationContext::WorkerPostMessage); + if (message.hasException()) + return message.releaseException(); + + // Disentangle the port in preparation for sending it to the remote context. + auto channels = MessagePort::disentanglePorts(WTFMove(ports)); + if (channels.hasException()) + return channels.releaseException(); + + thread().workerObjectProxy().postMessageToWorkerObject({ message.releaseReturnValue(), channels.releaseReturnValue() }); + return { }; +} + +ExceptionOr<void> DedicatedWorkerGlobalScope::importScripts(const FixedVector<String>& urls) +{ + auto result = Base::importScripts(urls); + thread().workerObjectProxy().reportPendingActivity(hasPendingActivity()); + return result; +} + +DedicatedWorkerThread& DedicatedWorkerGlobalScope::thread() +{ + return static_cast<DedicatedWorkerThread&>(Base::thread()); +} + +#if ENABLE(OFFSCREEN_CANVAS) +CallbackId DedicatedWorkerGlobalScope::requestAnimationFrame(Ref<RequestAnimationFrameCallback>&& callback) +{ + if (!m_workerAnimationController) + m_workerAnimationController = WorkerAnimationController::create(*this); + return m_workerAnimationController->requestAnimationFrame(WTFMove(callback)); +} + +void DedicatedWorkerGlobalScope::cancelAnimationFrame(CallbackId callbackId) +{ + if (m_workerAnimationController) + m_workerAnimationController->cancelAnimationFrame(callbackId); +} +#endif + +#if ENABLE(WEB_RTC) +RefPtr<RTCRtpScriptTransformer> DedicatedWorkerGlobalScope::createRTCRtpScriptTransformer(MessageWithMessagePorts&& options) +{ + auto transformerOrException = RTCRtpScriptTransformer::create(*this, WTFMove(options)); + if (transformerOrException.hasException()) + return nullptr; + auto transformer = transformerOrException.releaseReturnValue(); + dispatchEvent(RTCTransformEvent::create(eventNames().rtctransformEvent, transformer.copyRef(), Event::IsTrusted::Yes)); + return transformer; +} +#endif + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/DedicatedWorkerGlobalScope.h b/src/javascript/jsc/bindings/webcore/DedicatedWorkerGlobalScope.h new file mode 100644 index 000000000..f352a6dba --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/DedicatedWorkerGlobalScope.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "MessagePort.h" +#include "WorkerGlobalScope.h" + +namespace JSC { +class CallFrame; +class JSObject; +class JSValue; +} + +namespace WebCore { + +class ContentSecurityPolicyResponseHeaders; +class DedicatedWorkerThread; +class JSRTCRtpScriptTransformerConstructor; +class RTCRtpScriptTransformer; +class RequestAnimationFrameCallback; +class SerializedScriptValue; + +struct StructuredSerializeOptions; + +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) +class WorkerAnimationController; + +using CallbackId = int; +#endif + +using TransferredMessagePort = std::pair<WebCore::MessagePortIdentifier, WebCore::MessagePortIdentifier>; + +class DedicatedWorkerGlobalScope final : public WorkerGlobalScope { + WTF_MAKE_ISO_ALLOCATED(DedicatedWorkerGlobalScope); + +public: + static Ref<DedicatedWorkerGlobalScope> create(const WorkerParameters&, Ref<SecurityOrigin>&&, DedicatedWorkerThread&, Ref<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*); + virtual ~DedicatedWorkerGlobalScope(); + + const String& name() const { return m_name; } + + ExceptionOr<void> postMessage(JSC::JSGlobalObject&, JSC::JSValue message, StructuredSerializeOptions&&); + + DedicatedWorkerThread& thread(); + + // #if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + // CallbackId requestAnimationFrame(Ref<RequestAnimationFrameCallback>&&); + // void cancelAnimationFrame(CallbackId); + // #endif + + // #if ENABLE(WEB_RTC) + // RefPtr<RTCRtpScriptTransformer> createRTCRtpScriptTransformer(MessageWithMessagePorts&&); + // #endif + + FetchOptions::Destination destination() const final { return FetchOptions::Destination::Worker; } + +private: + using Base = WorkerGlobalScope; + + DedicatedWorkerGlobalScope(const WorkerParameters&, Ref<SecurityOrigin>&&, DedicatedWorkerThread&, Ref<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*); + + Type type() const final { return Type::DedicatedWorker; } + + ExceptionOr<void> importScripts(const FixedVector<String>& urls) final; + EventTargetInterface eventTargetInterface() const final; + + void prepareForDestruction() final; + + String m_name; + + // #if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + // RefPtr<WorkerAnimationController> m_workerAnimationController; + // #endif +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::DedicatedWorkerGlobalScope) +static bool isType(const WebCore::ScriptExecutionContext& context) { return is<WebCore::WorkerGlobalScope>(context) && downcast<WebCore::WorkerGlobalScope>(context).type() == WebCore::WorkerGlobalScope::Type::DedicatedWorker; } +static bool isType(const WebCore::WorkerGlobalScope& context) { return context.type() == WebCore::WorkerGlobalScope::Type::DedicatedWorker; } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/src/javascript/jsc/bindings/webcore/DedicatedWorkerGlobalScope.idl b/src/javascript/jsc/bindings/webcore/DedicatedWorkerGlobalScope.idl new file mode 100644 index 000000000..690848b6c --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/DedicatedWorkerGlobalScope.idl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009, 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Exposed=DedicatedWorker, + Global=(Worker,DedicatedWorker), + JSGenerateToNativeObject, + IsImmutablePrototypeExoticObject, + IsImmutablePrototypeExoticObjectOnPrototype, +] interface DedicatedWorkerGlobalScope : WorkerGlobalScope { + [Replaceable] readonly attribute DOMString name; + + [CallWith=CurrentGlobalObject] undefined postMessage(any message, sequence<object> transfer); + [CallWith=CurrentGlobalObject] undefined postMessage(any message, optional StructuredSerializeOptions options); + + undefined close(); + + attribute EventHandler onmessage; +}; + +[Conditional=OFFSCREEN_CANVAS_IN_WORKERS, EnabledAtRuntime=OffscreenCanvasInWorkersEnabled] DedicatedWorkerGlobalScope includes AnimationFrameProvider; +[Conditional=WEB_RTC, EnabledBySetting=WebRTCEncodedTransformEnabled] DedicatedWorkerGlobalScope includes RTCRtpScriptTransformProvider; diff --git a/src/javascript/jsc/bindings/webcore/DedicatedWorkerThread.cpp b/src/javascript/jsc/bindings/webcore/DedicatedWorkerThread.cpp new file mode 100644 index 000000000..c157dbebb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/DedicatedWorkerThread.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DedicatedWorkerThread.h" + +#include "DedicatedWorkerGlobalScope.h" +#include "SecurityOrigin.h" +#include "WorkerObjectProxy.h" + +namespace WebCore { + +DedicatedWorkerThread::DedicatedWorkerThread(const WorkerParameters& params, const ScriptBuffer& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerDebuggerProxy& workerDebuggerProxy, WorkerObjectProxy& workerObjectProxy, WorkerThreadStartMode startMode, const SecurityOrigin& topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider, JSC::RuntimeFlags runtimeFlags) + : WorkerThread(params, sourceCode, workerLoaderProxy, workerDebuggerProxy, workerObjectProxy, startMode, topOrigin, connectionProxy, socketProvider, runtimeFlags) + , m_workerObjectProxy(workerObjectProxy) +{ +} + +DedicatedWorkerThread::~DedicatedWorkerThread() = default; + +Ref<WorkerGlobalScope> DedicatedWorkerThread::createWorkerGlobalScope(const WorkerParameters& params, /*Ref<SecurityOrigin>&& origin,*/ Ref<SecurityOrigin>&& topOrigin) +{ + return DedicatedWorkerGlobalScope::create(params, WTFMove(origin), *this, WTFMove(topOrigin), idbConnectionProxy(), socketProvider()); +} + +void DedicatedWorkerThread::runEventLoop() +{ + // Notify the parent object of our current active state before calling the superclass to run the event loop. + m_workerObjectProxy.reportPendingActivity(globalScope()->hasPendingActivity()); + WorkerThread::runEventLoop(); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/DedicatedWorkerThread.h b/src/javascript/jsc/bindings/webcore/DedicatedWorkerThread.h new file mode 100644 index 000000000..1300ca01b --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/DedicatedWorkerThread.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "WorkerThread.h" + +namespace WebCore { + +class ContentSecurityPolicyResponseHeaders; +class ScriptBuffer; +class WorkerObjectProxy; + +class DedicatedWorkerThread : public WorkerThread { +public: + template<typename... Args> static Ref<DedicatedWorkerThread> create(Args&&... args) + { + return adoptRef(*new DedicatedWorkerThread(std::forward<Args>(args)...)); + } + virtual ~DedicatedWorkerThread(); + + WorkerObjectProxy& workerObjectProxy() const { return m_workerObjectProxy; } + void start() { WorkerThread::start(nullptr); } + +protected: + Ref<WorkerGlobalScope> createWorkerGlobalScope(const WorkerParameters&, Ref<SecurityOrigin>&&, Ref<SecurityOrigin>&& topOrigin) override; + void runEventLoop() override; + +private: + DedicatedWorkerThread(const WorkerParameters&, const ScriptBuffer& sourceCode, WorkerLoaderProxy&, WorkerDebuggerProxy&, WorkerObjectProxy&, WorkerThreadStartMode, const SecurityOrigin& topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*, JSC::RuntimeFlags); + + ASCIILiteral threadName() const final { return "WebCore: Worker"_s; } + + WorkerObjectProxy& m_workerObjectProxy; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/DetachedRTCDataChannel.h b/src/javascript/jsc/bindings/webcore/DetachedRTCDataChannel.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/DetachedRTCDataChannel.h diff --git a/src/javascript/jsc/bindings/webcore/EventLoop.cpp b/src/javascript/jsc/bindings/webcore/EventLoop.cpp new file mode 100644 index 000000000..6782ffe42 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/EventLoop.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "EventLoop.h" + +#include "Microtasks.h" + +namespace WebCore { + +void EventLoop::queueTask(std::unique_ptr<EventLoopTask>&& task) +{ + ASSERT(task->taskSource() != TaskSource::Microtask); + ASSERT(task->group()); + ASSERT(isContextThread()); + scheduleToRunIfNeeded(); + m_tasks.append(WTFMove(task)); +} + +void EventLoop::queueMicrotask(std::unique_ptr<EventLoopTask>&& microtask) +{ + ASSERT(microtask->taskSource() == TaskSource::Microtask); + microtaskQueue().append(WTFMove(microtask)); + scheduleToRunIfNeeded(); // FIXME: Remove this once everything is integrated with the event loop. +} + +void EventLoop::performMicrotaskCheckpoint() +{ + microtaskQueue().performMicrotaskCheckpoint(); +} + +void EventLoop::resumeGroup(EventLoopTaskGroup& group) +{ + ASSERT(isContextThread()); + if (!m_groupsWithSuspendedTasks.contains(group)) + return; + scheduleToRunIfNeeded(); +} + +void EventLoop::registerGroup(EventLoopTaskGroup& group) +{ + ASSERT(isContextThread()); + m_associatedGroups.add(group); +} + +void EventLoop::unregisterGroup(EventLoopTaskGroup& group) +{ + ASSERT(isContextThread()); + if (m_associatedGroups.remove(group)) + stopAssociatedGroupsIfNecessary(); +} + +void EventLoop::stopAssociatedGroupsIfNecessary() +{ + ASSERT(isContextThread()); + for (auto& group : m_associatedGroups) { + if (!group.isReadyToStop()) + return; + } + auto associatedGroups = std::exchange(m_associatedGroups, { }); + for (auto& group : associatedGroups) + group.stopAndDiscardAllTasks(); +} + +void EventLoop::stopGroup(EventLoopTaskGroup& group) +{ + ASSERT(isContextThread()); + m_tasks.removeAllMatching([&group] (auto& task) { + return group.matchesTask(*task); + }); +} + +void EventLoop::scheduleToRunIfNeeded() +{ + if (m_isScheduledToRun) + return; + m_isScheduledToRun = true; + scheduleToRun(); +} + +void EventLoop::run() +{ + m_isScheduledToRun = false; + bool didPerformMicrotaskCheckpoint = false; + + if (!m_tasks.isEmpty()) { + auto tasks = std::exchange(m_tasks, { }); + m_groupsWithSuspendedTasks.clear(); + Vector<std::unique_ptr<EventLoopTask>> remainingTasks; + for (auto& task : tasks) { + auto* group = task->group(); + if (!group || group->isStoppedPermanently()) + continue; + + if (group->isSuspended()) { + m_groupsWithSuspendedTasks.add(*group); + remainingTasks.append(WTFMove(task)); + continue; + } + + task->execute(); + didPerformMicrotaskCheckpoint = true; + microtaskQueue().performMicrotaskCheckpoint(); + } + for (auto& task : m_tasks) + remainingTasks.append(WTFMove(task)); + m_tasks = WTFMove(remainingTasks); + } + + // FIXME: Remove this once everything is integrated with the event loop. + if (!didPerformMicrotaskCheckpoint) + microtaskQueue().performMicrotaskCheckpoint(); +} + +void EventLoop::clearAllTasks() +{ + m_tasks.clear(); + m_groupsWithSuspendedTasks.clear(); +} + +void EventLoopTaskGroup::queueTask(std::unique_ptr<EventLoopTask>&& task) +{ + if (m_state == State::Stopped || !m_eventLoop) + return; + ASSERT(task->group() == this); + m_eventLoop->queueTask(WTFMove(task)); +} + +class EventLoopFunctionDispatchTask : public EventLoopTask { +public: + EventLoopFunctionDispatchTask(TaskSource source, EventLoopTaskGroup& group, EventLoop::TaskFunction&& function) + : EventLoopTask(source, group) + , m_function(WTFMove(function)) + { + } + + void execute() final { m_function(); } + +private: + EventLoop::TaskFunction m_function; +}; + +void EventLoopTaskGroup::queueTask(TaskSource source, EventLoop::TaskFunction&& function) +{ + return queueTask(makeUnique<EventLoopFunctionDispatchTask>(source, *this, WTFMove(function))); +} + +void EventLoopTaskGroup::queueMicrotask(EventLoop::TaskFunction&& function) +{ + if (m_state == State::Stopped || !m_eventLoop) + return; + m_eventLoop->queueMicrotask(makeUnique<EventLoopFunctionDispatchTask>(TaskSource::Microtask, *this, WTFMove(function))); +} + +void EventLoopTaskGroup::performMicrotaskCheckpoint() +{ + if (m_eventLoop) + m_eventLoop->performMicrotaskCheckpoint(); +} + +void EventLoopTaskGroup::runAtEndOfMicrotaskCheckpoint(EventLoop::TaskFunction&& function) +{ + if (m_state == State::Stopped || !m_eventLoop) + return; + + microtaskQueue().addCheckpointTask(makeUnique<EventLoopFunctionDispatchTask>(TaskSource::IndexedDB, *this, WTFMove(function))); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/EventLoop.h b/src/javascript/jsc/bindings/webcore/EventLoop.h new file mode 100644 index 000000000..dd5f522be --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/EventLoop.h @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "TaskSource.h" +#include <wtf/Function.h> +#include <wtf/RefCounted.h> +#include <wtf/StdLibExtras.h> +#include <wtf/WeakHashSet.h> +#include <wtf/WeakPtr.h> + +namespace WebCore { + +class ActiveDOMCallbackMicrotask; +class EventLoopTaskGroup; +class EventTarget; +class MicrotaskQueue; +class ScriptExecutionContext; + +class EventLoopTask { + WTF_MAKE_NONCOPYABLE(EventLoopTask); + WTF_MAKE_FAST_ALLOCATED; + +public: + virtual ~EventLoopTask() = default; + + TaskSource taskSource() { return m_taskSource; } + virtual void execute() = 0; + + EventLoopTaskGroup* group() const { return m_group.get(); } + +protected: + EventLoopTask(TaskSource, EventLoopTaskGroup&); + +private: + const TaskSource m_taskSource; + WeakPtr<EventLoopTaskGroup> m_group; +}; + +// https://html.spec.whatwg.org/multipage/webappapis.html#event-loop +class EventLoop : public RefCounted<EventLoop>, public CanMakeWeakPtr<EventLoop> { +public: + virtual ~EventLoop() = default; + + typedef Function<void ()> TaskFunction; + void queueTask(std::unique_ptr<EventLoopTask>&&); + + // https://html.spec.whatwg.org/multipage/webappapis.html#queue-a-microtask + void queueMicrotask(std::unique_ptr<EventLoopTask>&&); + + // https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint + void performMicrotaskCheckpoint(); + virtual MicrotaskQueue& microtaskQueue() = 0; + + void resumeGroup(EventLoopTaskGroup&); + void stopGroup(EventLoopTaskGroup&); + + void registerGroup(EventLoopTaskGroup&); + void unregisterGroup(EventLoopTaskGroup&); + void stopAssociatedGroupsIfNecessary(); + +protected: + EventLoop() = default; + void run(); + void clearAllTasks(); + +private: + void scheduleToRunIfNeeded(); + virtual void scheduleToRun() = 0; + virtual bool isContextThread() const = 0; + + // Use a global queue instead of multiple task queues since HTML5 spec allows UA to pick arbitrary queue. + Vector<std::unique_ptr<EventLoopTask>> m_tasks; + WeakHashSet<EventLoopTaskGroup> m_associatedGroups; + WeakHashSet<EventLoopTaskGroup> m_groupsWithSuspendedTasks; + bool m_isScheduledToRun { false }; +}; + +class EventLoopTaskGroup : public CanMakeWeakPtr<EventLoopTaskGroup> { + WTF_MAKE_NONCOPYABLE(EventLoopTaskGroup); + WTF_MAKE_FAST_ALLOCATED; + +public: + EventLoopTaskGroup(EventLoop& eventLoop) + : m_eventLoop(eventLoop) + { + eventLoop.registerGroup(*this); + } + + ~EventLoopTaskGroup() + { + if (auto* eventLoop = m_eventLoop.get()) + eventLoop->unregisterGroup(*this); + } + + bool hasSameEventLoopAs(EventLoopTaskGroup& otherGroup) + { + ASSERT(m_eventLoop); + return m_eventLoop == otherGroup.m_eventLoop; + } + + bool matchesTask(EventLoopTask& task) const + { + auto* group = task.group(); + return group == this; + } + + // Marks the group as ready to stop but it won't actually be stopped + // until all groups in this event loop are ready to stop. + void markAsReadyToStop() + { + if (isReadyToStop() || isStoppedPermanently()) + return; + + bool wasSuspended = isSuspended(); + m_state = State::ReadyToStop; + if (auto* eventLoop = m_eventLoop.get()) + eventLoop->stopAssociatedGroupsIfNecessary(); + + if (wasSuspended && !isStoppedPermanently()) { + // We we get marked as ready to stop while suspended (happens when a CachedPage gets destroyed) then the + // queued tasks will never be able to run (since tasks don't run while suspended and we will never resume). + // As a result, we can simply discard our tasks and stop permanently. + stopAndDiscardAllTasks(); + } + } + + // This gets called by the event loop when all groups in the EventLoop as ready to stop. + void stopAndDiscardAllTasks() + { + ASSERT(isReadyToStop()); + m_state = State::Stopped; + if (auto* eventLoop = m_eventLoop.get()) + eventLoop->stopGroup(*this); + } + + void suspend() + { + ASSERT(!isStoppedPermanently()); + ASSERT(!isReadyToStop()); + m_state = State::Suspended; + // We don't remove suspended tasks to preserve the ordering. + // EventLoop::run checks whether each task's group is suspended or not. + } + + void resume() + { + ASSERT(!isStoppedPermanently()); + ASSERT(!isReadyToStop()); + m_state = State::Running; + if (auto* eventLoop = m_eventLoop.get()) + eventLoop->resumeGroup(*this); + } + + bool isStoppedPermanently() const { return m_state == State::Stopped; } + bool isSuspended() const { return m_state == State::Suspended; } + bool isReadyToStop() const { return m_state == State::ReadyToStop; } + + void queueTask(std::unique_ptr<EventLoopTask>&&); + WEBCORE_EXPORT void queueTask(TaskSource, EventLoop::TaskFunction&&); + + // https://html.spec.whatwg.org/multipage/webappapis.html#queue-a-microtask + WEBCORE_EXPORT void queueMicrotask(EventLoop::TaskFunction&&); + MicrotaskQueue& microtaskQueue() { return m_eventLoop->microtaskQueue(); } + + // https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint + void performMicrotaskCheckpoint(); + + void runAtEndOfMicrotaskCheckpoint(EventLoop::TaskFunction&&); + +private: + enum class State : uint8_t { Running, Suspended, ReadyToStop, Stopped }; + + WeakPtr<EventLoop> m_eventLoop; + State m_state { State::Running }; +}; + +inline EventLoopTask::EventLoopTask(TaskSource source, EventLoopTaskGroup& group) + : m_taskSource(source) + , m_group(group) +{ } + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/EventNames.h b/src/javascript/jsc/bindings/webcore/EventNames.h index 3938d62ca..eb0f94ef4 100644 --- a/src/javascript/jsc/bindings/webcore/EventNames.h +++ b/src/javascript/jsc/bindings/webcore/EventNames.h @@ -31,8 +31,10 @@ namespace WebCore { #define DOM_EVENT_NAMES_FOR_EACH(macro) \ macro(error) \ - macro(abort) -// + macro(abort) \ + macro(unhandledrejection) \ + macro(message) +// end of DOM_EVENT_NAMES_FOR_EACH // macro(DOMActivate) \ // macro(DOMCharacterDataModified) \ diff --git a/src/javascript/jsc/bindings/webcore/FetchRequestCredentials.h b/src/javascript/jsc/bindings/webcore/FetchRequestCredentials.h new file mode 100644 index 000000000..5bc479b79 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/FetchRequestCredentials.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "FetchOptions.h" + +namespace WebCore { + +using FetchRequestCredentials = FetchOptions::Credentials; + +} + diff --git a/src/javascript/jsc/bindings/webcore/FetchRequestCredentials.idl b/src/javascript/jsc/bindings/webcore/FetchRequestCredentials.idl new file mode 100644 index 000000000..f5dba1110 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/FetchRequestCredentials.idl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +enum FetchRequestCredentials { "omit", "same-origin", "include" }; diff --git a/src/javascript/jsc/bindings/webcore/JSDOMGuardedObject.cpp b/src/javascript/jsc/bindings/webcore/JSDOMGuardedObject.cpp new file mode 100644 index 000000000..76fc86415 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSDOMGuardedObject.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JSDOMGuardedObject.h" + + +namespace WebCore { +using namespace JSC; + +DOMGuardedObject::DOMGuardedObject(JSDOMGlobalObject& globalObject, JSCell& guarded) + : ActiveDOMCallback(globalObject.scriptExecutionContext()) + , m_guarded(&guarded) + , m_globalObject(&globalObject) +{ + globalObject.vm().writeBarrier(&globalObject, &guarded); + if (globalObject.vm().heap.mutatorShouldBeFenced()) { + Locker locker { globalObject.gcLock() }; + globalObject.guardedObjects().add(this); + return; + } + globalObject.guardedObjects(NoLockingNecessary).add(this); +} + +DOMGuardedObject::~DOMGuardedObject() +{ + clear(); +} + +void DOMGuardedObject::clear() +{ + ASSERT(!m_guarded || m_globalObject); + removeFromGlobalObject(); + m_guarded.clear(); + m_globalObject.clear(); +} + +void DOMGuardedObject::removeFromGlobalObject() +{ + if (!m_guarded || !m_globalObject) + return; + + if (m_globalObject->vm().heap.mutatorShouldBeFenced()) { + Locker locker { m_globalObject->gcLock() }; + m_globalObject->guardedObjects().remove(this); + } else + m_globalObject->guardedObjects(NoLockingNecessary).remove(this); +} + +void DOMGuardedObject::contextDestroyed() +{ + ActiveDOMCallback::contextDestroyed(); + clear(); +} + +} diff --git a/src/javascript/jsc/bindings/webcore/JSDOMGuardedObject.h b/src/javascript/jsc/bindings/webcore/JSDOMGuardedObject.h new file mode 100644 index 000000000..e576ff1a6 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSDOMGuardedObject.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ActiveDOMCallback.h" +#include "JSDOMGlobalObject.h" +#include <JavaScriptCore/HeapInlines.h> +#include <JavaScriptCore/JSCell.h> +#include <JavaScriptCore/SlotVisitorInlines.h> +#include <JavaScriptCore/StrongInlines.h> + +namespace WebCore { + +class WEBCORE_EXPORT DOMGuardedObject : public RefCounted<DOMGuardedObject>, public ActiveDOMCallback { +public: + ~DOMGuardedObject(); + + bool isSuspended() const { return !m_guarded || !canInvokeCallback(); } // The wrapper world has gone away or active DOM objects have been suspended. + + template<typename Visitor> void visitAggregate(Visitor& visitor) { visitor.append(m_guarded); } + + JSC::JSValue guardedObject() const { return m_guarded.get(); } + JSDOMGlobalObject* globalObject() const { return m_globalObject.get(); } + + void clear(); + +protected: + DOMGuardedObject(JSDOMGlobalObject&, JSC::JSCell&); + + void contextDestroyed() override; + bool isEmpty() const { return !m_guarded; } + + JSC::Weak<JSC::JSCell> m_guarded; + JSC::Weak<JSDOMGlobalObject> m_globalObject; + +private: + void removeFromGlobalObject(); +}; + +template <typename T> class DOMGuarded : public DOMGuardedObject { +protected: + DOMGuarded(JSDOMGlobalObject& globalObject, T& guarded) : DOMGuardedObject(globalObject, guarded) { } + T* guarded() const { return JSC::jsDynamicCast<T*>(globalObject()->vm(), guardedObject()); } +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSDOMPromise.cpp b/src/javascript/jsc/bindings/webcore/JSDOMPromise.cpp new file mode 100644 index 000000000..544d61b5d --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSDOMPromise.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JSDOMPromise.h" + +// #include "DOMWindow.h" +// #include "JSDOMWindow.h" +#include <JavaScriptCore/BuiltinNames.h> +#include <JavaScriptCore/CatchScope.h> +#include <JavaScriptCore/Exception.h> +#include <JavaScriptCore/JSNativeStdFunction.h> +#include <JavaScriptCore/JSPromiseConstructor.h> + +using namespace JSC; + +namespace WebCore { + +auto DOMPromise::whenSettled(std::function<void()>&& callback) -> IsCallbackRegistered +{ + return whenPromiseIsSettled(globalObject(), promise(), WTFMove(callback)); +} + +auto DOMPromise::whenPromiseIsSettled(JSDOMGlobalObject* globalObject, JSC::JSObject* promise, Function<void()>&& callback) -> IsCallbackRegistered +{ + auto& lexicalGlobalObject = *globalObject; + auto& vm = lexicalGlobalObject.vm(); + JSLockHolder lock(vm); + auto* handler = JSC::JSNativeStdFunction::create(vm, globalObject, 1, String {}, [callback = WTFMove(callback)](JSGlobalObject*, CallFrame*) mutable { + callback(); + return JSC::JSValue::encode(JSC::jsUndefined()); + }); + + auto scope = DECLARE_THROW_SCOPE(vm); + const JSC::Identifier& privateName = vm.propertyNames->builtinNames().thenPrivateName(); + auto thenFunction = promise->get(&lexicalGlobalObject, privateName); + + EXCEPTION_ASSERT(!scope.exception() || vm.hasPendingTerminationException()); + if (scope.exception()) + return IsCallbackRegistered::No; + + ASSERT(thenFunction.isCallable(vm)); + + JSC::MarkedArgumentBuffer arguments; + arguments.append(handler); + arguments.append(handler); + + auto callData = JSC::getCallData(vm, thenFunction); + ASSERT(callData.type != JSC::CallData::Type::None); + call(&lexicalGlobalObject, thenFunction, callData, promise, arguments); + + EXCEPTION_ASSERT(!scope.exception() || vm.hasPendingTerminationException()); + return scope.exception() ? IsCallbackRegistered::No : IsCallbackRegistered::Yes; +} + +JSC::JSValue DOMPromise::result() const +{ + return promise()->result(m_globalObject->vm()); +} + +DOMPromise::Status DOMPromise::status() const +{ + switch (promise()->status(m_globalObject->vm())) { + case JSC::JSPromise::Status::Pending: + return Status::Pending; + case JSC::JSPromise::Status::Fulfilled: + return Status::Fulfilled; + case JSC::JSPromise::Status::Rejected: + return Status::Rejected; + }; + ASSERT_NOT_REACHED(); + return Status::Rejected; +} + +} diff --git a/src/javascript/jsc/bindings/webcore/JSDOMPromise.h b/src/javascript/jsc/bindings/webcore/JSDOMPromise.h new file mode 100644 index 000000000..97ebafa74 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSDOMPromise.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013-2017 Apple Inc. All rights reserved. + * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "JSDOMGuardedObject.h" +#include <JavaScriptCore/JSPromise.h> + +namespace WebCore { + +class DOMPromise : public DOMGuarded<JSC::JSPromise> { +public: + static Ref<DOMPromise> create(JSDOMGlobalObject& globalObject, JSC::JSPromise& promise) + { + return adoptRef(*new DOMPromise(globalObject, promise)); + } + + JSC::JSPromise* promise() const + { + ASSERT(!isSuspended()); + return guarded(); + } + + enum class IsCallbackRegistered { No, Yes }; + IsCallbackRegistered whenSettled(std::function<void()>&&); + JSC::JSValue result() const; + + enum class Status { Pending, Fulfilled, Rejected }; + Status status() const; + + static IsCallbackRegistered whenPromiseIsSettled(JSDOMGlobalObject*, JSC::JSObject* promise, Function<void()>&&); + +private: + DOMPromise(JSDOMGlobalObject& globalObject, JSC::JSPromise& promise) + : DOMGuarded<JSC::JSPromise>(globalObject, promise) + { + } +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSDOMPromiseDeferred.cpp b/src/javascript/jsc/bindings/webcore/JSDOMPromiseDeferred.cpp new file mode 100644 index 000000000..1ea86920c --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSDOMPromiseDeferred.cpp @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2013-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JSDOMPromiseDeferred.h" + +#include "DOMWindow.h" +#include "EventLoop.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMPromise.h" +#include "JSDOMWindow.h" +// #include "ScriptController.h" +#include "WorkerGlobalScope.h" +#include <JavaScriptCore/BuiltinNames.h> +#include <JavaScriptCore/Exception.h> +#include <JavaScriptCore/JSONObject.h> +#include <JavaScriptCore/JSPromiseConstructor.h> +#include <JavaScriptCore/Strong.h> + +namespace WebCore { +using namespace JSC; + +JSC::JSValue DeferredPromise::promise() const +{ + if (isEmpty()) + return jsUndefined(); + + ASSERT(deferred()); + return deferred(); +} + +void DeferredPromise::callFunction(JSGlobalObject& lexicalGlobalObject, ResolveMode mode, JSValue resolution) +{ + if (shouldIgnoreRequestToFulfill()) + return; + + if (activeDOMObjectsAreSuspended()) { + JSC::Strong<JSC::Unknown, ShouldStrongDestructorGrabLock::Yes> strongResolution(lexicalGlobalObject.vm(), resolution); + ASSERT(scriptExecutionContext()->eventLoop().isSuspended()); + scriptExecutionContext()->eventLoop().queueTask(TaskSource::Networking, [this, protectedThis = Ref { *this }, mode, strongResolution = WTFMove(strongResolution)]() mutable { + if (shouldIgnoreRequestToFulfill()) + return; + + JSC::JSGlobalObject* lexicalGlobalObject = globalObject(); + JSC::JSLockHolder locker(lexicalGlobalObject); + callFunction(*globalObject(), mode, strongResolution.get()); + }); + return; + } + + // FIXME: We could have error since any JS call can throw stack-overflow errors. + // https://bugs.webkit.org/show_bug.cgi?id=203402 + switch (mode) { + case ResolveMode::Resolve: + deferred()->resolve(&lexicalGlobalObject, resolution); + break; + case ResolveMode::Reject: + deferred()->reject(&lexicalGlobalObject, resolution); + break; + case ResolveMode::RejectAsHandled: + deferred()->rejectAsHandled(&lexicalGlobalObject, resolution); + break; + } + + if (m_mode == Mode::ClearPromiseOnResolve) + clear(); +} + +void DeferredPromise::whenSettled(Function<void()>&& callback) +{ + if (shouldIgnoreRequestToFulfill()) + return; + + if (activeDOMObjectsAreSuspended()) { + scriptExecutionContext()->eventLoop().queueTask(TaskSource::Networking, [this, protectedThis = Ref { *this }, callback = WTFMove(callback)]() mutable { + whenSettled(WTFMove(callback)); + }); + return; + } + + DOMPromise::whenPromiseIsSettled(globalObject(), deferred(), WTFMove(callback)); +} + +void DeferredPromise::reject(RejectAsHandled rejectAsHandled) +{ + if (shouldIgnoreRequestToFulfill()) + return; + + ASSERT(deferred()); + ASSERT(m_globalObject); + auto& lexicalGlobalObject = *m_globalObject; + JSC::JSLockHolder locker(&lexicalGlobalObject); + reject(lexicalGlobalObject, JSC::jsUndefined(), rejectAsHandled); +} + +void DeferredPromise::reject(std::nullptr_t, RejectAsHandled rejectAsHandled) +{ + if (shouldIgnoreRequestToFulfill()) + return; + + ASSERT(deferred()); + ASSERT(m_globalObject); + auto& lexicalGlobalObject = *m_globalObject; + JSC::JSLockHolder locker(&lexicalGlobalObject); + reject(lexicalGlobalObject, JSC::jsNull(), rejectAsHandled); +} + +void DeferredPromise::reject(Exception exception, RejectAsHandled rejectAsHandled) +{ + if (shouldIgnoreRequestToFulfill()) + return; + + Ref protectedThis(*this); + ASSERT(deferred()); + ASSERT(m_globalObject); + auto& lexicalGlobalObject = *m_globalObject; + JSC::VM& vm = lexicalGlobalObject.vm(); + JSC::JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + + if (exception.code() == ExistingExceptionError) { + EXCEPTION_ASSERT(scope.exception()); + auto error = scope.exception()->value(); + bool isTerminating = handleTerminationExceptionIfNeeded(scope, lexicalGlobalObject); + scope.clearException(); + + if (!isTerminating) + reject<IDLAny>(error, rejectAsHandled); + return; + } + + auto error = createDOMException(lexicalGlobalObject, WTFMove(exception)); + if (UNLIKELY(scope.exception())) { + handleUncaughtException(scope, lexicalGlobalObject); + return; + } + + reject(lexicalGlobalObject, error, rejectAsHandled); + if (UNLIKELY(scope.exception())) + handleUncaughtException(scope, lexicalGlobalObject); +} + +void DeferredPromise::reject(ExceptionCode ec, const String& message, RejectAsHandled rejectAsHandled) +{ + if (shouldIgnoreRequestToFulfill()) + return; + + Ref protectedThis(*this); + ASSERT(deferred()); + ASSERT(m_globalObject); + auto& lexicalGlobalObject = *m_globalObject; + JSC::VM& vm = lexicalGlobalObject.vm(); + JSC::JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + + if (ec == ExistingExceptionError) { + EXCEPTION_ASSERT(scope.exception()); + auto error = scope.exception()->value(); + bool isTerminating = handleTerminationExceptionIfNeeded(scope, lexicalGlobalObject); + scope.clearException(); + + if (!isTerminating) + reject<IDLAny>(error, rejectAsHandled); + return; + } + + auto error = createDOMException(&lexicalGlobalObject, ec, message); + if (UNLIKELY(scope.exception())) { + handleUncaughtException(scope, lexicalGlobalObject); + return; + } + + reject(lexicalGlobalObject, error, rejectAsHandled); + if (UNLIKELY(scope.exception())) + handleUncaughtException(scope, lexicalGlobalObject); +} + +void DeferredPromise::reject(const JSC::PrivateName& privateName, RejectAsHandled rejectAsHandled) +{ + if (shouldIgnoreRequestToFulfill()) + return; + + ASSERT(deferred()); + ASSERT(m_globalObject); + JSC::JSGlobalObject* lexicalGlobalObject = m_globalObject.get(); + JSC::JSLockHolder locker(lexicalGlobalObject); + reject(*lexicalGlobalObject, JSC::Symbol::create(lexicalGlobalObject->vm(), privateName.uid()), rejectAsHandled); +} + +void rejectPromiseWithExceptionIfAny(JSC::JSGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& globalObject, JSPromise& promise, JSC::CatchScope& catchScope) +{ + UNUSED_PARAM(lexicalGlobalObject); + if (LIKELY(!catchScope.exception())) + return; + + JSValue error = catchScope.exception()->value(); + catchScope.clearException(); + + DeferredPromise::create(globalObject, promise)->reject<IDLAny>(error); +} + +JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::JSGlobalObject& lexicalGlobalObject, const String& errorMessage, RejectedPromiseWithTypeErrorCause cause) +{ + auto& globalObject = lexicalGlobalObject; + auto& vm = lexicalGlobalObject.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto promiseConstructor = globalObject.promiseConstructor(); + auto rejectFunction = promiseConstructor->get(&lexicalGlobalObject, vm.propertyNames->builtinNames().rejectPrivateName()); + RETURN_IF_EXCEPTION(scope, {}); + auto* rejectionValue = static_cast<ErrorInstance*>(createTypeError(&lexicalGlobalObject, errorMessage)); + if (cause == RejectedPromiseWithTypeErrorCause::NativeGetter) + rejectionValue->setNativeGetterTypeError(); + + auto callData = getCallData(vm, rejectFunction); + ASSERT(callData.type != CallData::Type::None); + + MarkedArgumentBuffer arguments; + arguments.append(rejectionValue); + ASSERT(!arguments.hasOverflowed()); + + RELEASE_AND_RETURN(scope, JSValue::encode(call(&lexicalGlobalObject, rejectFunction, callData, promiseConstructor, arguments))); +} + +static inline JSC::JSValue parseAsJSON(JSC::JSGlobalObject* lexicalGlobalObject, const String& data) +{ + JSC::JSLockHolder lock(lexicalGlobalObject); + return JSC::JSONParse(lexicalGlobalObject, data); +} + +void fulfillPromiseWithJSON(Ref<DeferredPromise>&& promise, const String& data) +{ + JSC::JSValue value = parseAsJSON(promise->globalObject(), data); + if (!value) + promise->reject(SyntaxError); + else + promise->resolve<IDLAny>(value); +} + +void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&& promise, ArrayBuffer* arrayBuffer) +{ + if (!arrayBuffer) { + promise->reject<IDLAny>(createOutOfMemoryError(promise->globalObject())); + return; + } + promise->resolve<IDLInterface<ArrayBuffer>>(*arrayBuffer); +} + +void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&& promise, const void* data, size_t length) +{ + fulfillPromiseWithArrayBuffer(WTFMove(promise), ArrayBuffer::tryCreate(data, length).get()); +} + +bool DeferredPromise::handleTerminationExceptionIfNeeded(CatchScope& scope, JSDOMGlobalObject& lexicalGlobalObject) +{ + auto* exception = scope.exception(); + VM& vm = scope.vm(); + + auto& scriptExecutionContext = *lexicalGlobalObject.scriptExecutionContext(); + if (is<WorkerGlobalScope>(scriptExecutionContext)) { + auto* scriptController = downcast<WorkerGlobalScope>(scriptExecutionContext).script(); + bool terminatorCausedException = vm.isTerminationException(exception); + if (terminatorCausedException || (scriptController && scriptController->isTerminatingExecution())) { + scriptController->forbidExecution(); + return true; + } + } + return false; +} + +void DeferredPromise::handleUncaughtException(CatchScope& scope, JSDOMGlobalObject& lexicalGlobalObject) +{ + auto* exception = scope.exception(); + handleTerminationExceptionIfNeeded(scope, lexicalGlobalObject); + reportException(&lexicalGlobalObject, exception); +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSDOMPromiseDeferred.h b/src/javascript/jsc/bindings/webcore/JSDOMPromiseDeferred.h new file mode 100644 index 000000000..b874f2a04 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSDOMPromiseDeferred.h @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2013-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ExceptionOr.h" +#include "JSDOMConvert.h" +#include "JSDOMGuardedObject.h" +#include "ScriptExecutionContext.h" +#include <JavaScriptCore/CatchScope.h> +#include <JavaScriptCore/JSPromise.h> + +namespace WebCore { + +class JSDOMWindow; +enum class RejectAsHandled : uint8_t { No, Yes }; + +class DeferredPromise : public DOMGuarded<JSC::JSPromise> { +public: + enum class Mode { + ClearPromiseOnResolve, + RetainPromiseOnResolve + }; + + static RefPtr<DeferredPromise> create(JSDOMGlobalObject& globalObject, Mode mode = Mode::ClearPromiseOnResolve) + { + JSC::VM& vm = JSC::getVM(&globalObject); + auto* promise = JSC::JSPromise::create(vm, globalObject.promiseStructure()); + ASSERT(promise); + return adoptRef(new DeferredPromise(globalObject, *promise, mode)); + } + + static Ref<DeferredPromise> create(JSDOMGlobalObject& globalObject, JSC::JSPromise& deferred, Mode mode = Mode::ClearPromiseOnResolve) + { + return adoptRef(*new DeferredPromise(globalObject, deferred, mode)); + } + + template<class IDLType> + void resolve(typename IDLType::ParameterType value) + { + if (shouldIgnoreRequestToFulfill()) + return; + + ASSERT(deferred()); + ASSERT(globalObject()); + JSC::JSGlobalObject* lexicalGlobalObject = globalObject(); + JSC::JSLockHolder locker(lexicalGlobalObject); + resolve(*lexicalGlobalObject, toJS<IDLType>(*lexicalGlobalObject, *globalObject(), std::forward<typename IDLType::ParameterType>(value))); + } + + void resolveWithJSValue(JSC::JSValue resolution) + { + if (shouldIgnoreRequestToFulfill()) + return; + + ASSERT(deferred()); + ASSERT(globalObject()); + JSC::JSGlobalObject* lexicalGlobalObject = globalObject(); + JSC::JSLockHolder locker(lexicalGlobalObject); + resolve(*lexicalGlobalObject, resolution); + } + + void resolve() + { + if (shouldIgnoreRequestToFulfill()) + return; + + ASSERT(deferred()); + ASSERT(globalObject()); + JSC::JSGlobalObject* lexicalGlobalObject = globalObject(); + JSC::JSLockHolder locker(lexicalGlobalObject); + resolve(*lexicalGlobalObject, JSC::jsUndefined()); + } + + template<class IDLType> + void resolveWithNewlyCreated(typename IDLType::ParameterType value) + { + if (shouldIgnoreRequestToFulfill()) + return; + + ASSERT(deferred()); + ASSERT(globalObject()); + JSC::JSGlobalObject* lexicalGlobalObject = globalObject(); + JSC::JSLockHolder locker(lexicalGlobalObject); + resolve(*lexicalGlobalObject, toJSNewlyCreated<IDLType>(*lexicalGlobalObject, *globalObject(), std::forward<typename IDLType::ParameterType>(value))); + } + + template<class IDLType> + void resolveCallbackValueWithNewlyCreated(const Function<typename IDLType::InnerParameterType(ScriptExecutionContext&)>& createValue) + { + if (shouldIgnoreRequestToFulfill()) + return; + + ASSERT(deferred()); + ASSERT(globalObject()); + auto* lexicalGlobalObject = globalObject(); + JSC::JSLockHolder locker(lexicalGlobalObject); + resolve(*lexicalGlobalObject, toJSNewlyCreated<IDLType>(*lexicalGlobalObject, *globalObject(), createValue(*globalObject()->scriptExecutionContext()))); + } + + template<class IDLType> + void reject(typename IDLType::ParameterType value, RejectAsHandled rejectAsHandled = RejectAsHandled::No) + { + if (shouldIgnoreRequestToFulfill()) + return; + + ASSERT(deferred()); + ASSERT(globalObject()); + JSC::JSGlobalObject* lexicalGlobalObject = globalObject(); + JSC::JSLockHolder locker(lexicalGlobalObject); + reject(*lexicalGlobalObject, toJS<IDLType>(*lexicalGlobalObject, *globalObject(), std::forward<typename IDLType::ParameterType>(value)), rejectAsHandled); + } + + void reject(RejectAsHandled = RejectAsHandled::No); + void reject(std::nullptr_t, RejectAsHandled = RejectAsHandled::No); + WEBCORE_EXPORT void reject(Exception, RejectAsHandled = RejectAsHandled::No); + WEBCORE_EXPORT void reject(ExceptionCode, const String& = { }, RejectAsHandled = RejectAsHandled::No); + void reject(const JSC::PrivateName&, RejectAsHandled = RejectAsHandled::No); + + template<typename Callback> + void resolveWithCallback(Callback callback) + { + if (shouldIgnoreRequestToFulfill()) + return; + + ASSERT(deferred()); + ASSERT(globalObject()); + auto* lexicalGlobalObject = globalObject(); + JSC::VM& vm = lexicalGlobalObject->vm(); + JSC::JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + resolve(*lexicalGlobalObject, callback(*globalObject())); + if (UNLIKELY(scope.exception())) + handleUncaughtException(scope, *lexicalGlobalObject); + } + + template<typename Callback> + void rejectWithCallback(Callback callback, RejectAsHandled rejectAsHandled = RejectAsHandled::No) + { + if (shouldIgnoreRequestToFulfill()) + return; + + ASSERT(deferred()); + ASSERT(globalObject()); + auto* lexicalGlobalObject = globalObject(); + JSC::VM& vm = lexicalGlobalObject->vm(); + JSC::JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + reject(*lexicalGlobalObject, callback(*globalObject()), rejectAsHandled); + if (UNLIKELY(scope.exception())) + handleUncaughtException(scope, *lexicalGlobalObject); + } + + JSC::JSValue promise() const; + + void whenSettled(Function<void()>&&); + +private: + DeferredPromise(JSDOMGlobalObject& globalObject, JSC::JSPromise& deferred, Mode mode) + : DOMGuarded<JSC::JSPromise>(globalObject, deferred) + , m_mode(mode) + { + } + + bool shouldIgnoreRequestToFulfill() const { return isEmpty(); } + + JSC::JSPromise* deferred() const { return guarded(); } + + enum class ResolveMode { Resolve, Reject, RejectAsHandled }; + WEBCORE_EXPORT void callFunction(JSC::JSGlobalObject&, ResolveMode, JSC::JSValue resolution); + + void resolve(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue resolution) { callFunction(lexicalGlobalObject, ResolveMode::Resolve, resolution); } + void reject(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue resolution, RejectAsHandled rejectAsHandled) + { + callFunction(lexicalGlobalObject, rejectAsHandled == RejectAsHandled::Yes ? ResolveMode::RejectAsHandled : ResolveMode::Reject, resolution); + } + + bool handleTerminationExceptionIfNeeded(JSC::CatchScope&, JSDOMGlobalObject& lexicalGlobalObject); + void handleUncaughtException(JSC::CatchScope&, JSDOMGlobalObject& lexicalGlobalObject); + + Mode m_mode; +}; + +class DOMPromiseDeferredBase { + WTF_MAKE_FAST_ALLOCATED; +public: + DOMPromiseDeferredBase(Ref<DeferredPromise>&& genericPromise) + : m_promise(WTFMove(genericPromise)) + { + } + + DOMPromiseDeferredBase(DOMPromiseDeferredBase&& promise) + : m_promise(WTFMove(promise.m_promise)) + { + } + + DOMPromiseDeferredBase(const DOMPromiseDeferredBase& other) + : m_promise(other.m_promise.copyRef()) + { + } + + DOMPromiseDeferredBase& operator=(const DOMPromiseDeferredBase& other) + { + m_promise = other.m_promise.copyRef(); + return *this; + } + + DOMPromiseDeferredBase& operator=(DOMPromiseDeferredBase&& other) + { + m_promise = WTFMove(other.m_promise); + return *this; + } + + void reject(RejectAsHandled rejectAsHandled = RejectAsHandled::No) + { + m_promise->reject(rejectAsHandled); + } + + template<typename... ErrorType> + void reject(ErrorType&&... error) + { + m_promise->reject(std::forward<ErrorType>(error)...); + } + + template<typename IDLType> + void rejectType(typename IDLType::ParameterType value, RejectAsHandled rejectAsHandled = RejectAsHandled::No) + { + m_promise->reject<IDLType>(std::forward<typename IDLType::ParameterType>(value), rejectAsHandled); + } + + JSC::JSValue promise() const { return m_promise->promise(); }; + + void whenSettled(Function<void()>&& function) + { + m_promise->whenSettled(WTFMove(function)); + } + +protected: + Ref<DeferredPromise> m_promise; +}; + +template<typename IDLType> +class DOMPromiseDeferred : public DOMPromiseDeferredBase { +public: + using DOMPromiseDeferredBase::DOMPromiseDeferredBase; + using DOMPromiseDeferredBase::operator=; + using DOMPromiseDeferredBase::promise; + using DOMPromiseDeferredBase::reject; + + void resolve(typename IDLType::ParameterType value) + { + m_promise->resolve<IDLType>(std::forward<typename IDLType::ParameterType>(value)); + } + + template<typename U> + void settle(ExceptionOr<U>&& result) + { + if (result.hasException()) { + reject(result.releaseException()); + return; + } + resolve(result.releaseReturnValue()); + } +}; + +template<> class DOMPromiseDeferred<void> : public DOMPromiseDeferredBase { +public: + using DOMPromiseDeferredBase::DOMPromiseDeferredBase; + using DOMPromiseDeferredBase::operator=; + using DOMPromiseDeferredBase::promise; + using DOMPromiseDeferredBase::reject; + + void resolve() + { + m_promise->resolve(); + } + + void settle(ExceptionOr<void>&& result) + { + if (result.hasException()) { + reject(result.releaseException()); + return; + } + resolve(); + } +}; + +void fulfillPromiseWithJSON(Ref<DeferredPromise>&&, const String&); +void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, ArrayBuffer*); +void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, const void*, size_t); +WEBCORE_EXPORT void rejectPromiseWithExceptionIfAny(JSC::JSGlobalObject&, JSDOMGlobalObject&, JSC::JSPromise&, JSC::CatchScope&); + +enum class RejectedPromiseWithTypeErrorCause { NativeGetter, InvalidThis }; +JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::JSGlobalObject&, const String&, RejectedPromiseWithTypeErrorCause); + +using PromiseFunction = void(JSC::JSGlobalObject&, JSC::CallFrame&, Ref<DeferredPromise>&&); + +template<PromiseFunction promiseFunction> +inline JSC::JSValue callPromiseFunction(JSC::JSGlobalObject& lexicalGlobalObject, JSC::CallFrame& callFrame) +{ + JSC::VM& vm = JSC::getVM(&lexicalGlobalObject); + auto catchScope = DECLARE_CATCH_SCOPE(vm); + + auto& globalObject = *JSC::jsSecureCast<JSDOMGlobalObject*>(vm, &lexicalGlobalObject); + auto* promise = JSC::JSPromise::create(vm, globalObject.promiseStructure()); + ASSERT(promise); + + promiseFunction(lexicalGlobalObject, callFrame, DeferredPromise::create(globalObject, *promise)); + + rejectPromiseWithExceptionIfAny(lexicalGlobalObject, globalObject, *promise, catchScope); + // FIXME: We could have error since any JS call can throw stack-overflow errors. + // https://bugs.webkit.org/show_bug.cgi?id=203402 + RETURN_IF_EXCEPTION(catchScope, JSC::jsUndefined()); + return promise; +} + +template<typename PromiseFunctor> +inline JSC::JSValue callPromiseFunction(JSC::JSGlobalObject& lexicalGlobalObject, JSC::CallFrame& callFrame, PromiseFunctor functor) +{ + JSC::VM& vm = JSC::getVM(&lexicalGlobalObject); + auto catchScope = DECLARE_CATCH_SCOPE(vm); + + auto& globalObject = *JSC::jsSecureCast<JSDOMGlobalObject*>(vm, &lexicalGlobalObject); + auto* promise = JSC::JSPromise::create(vm, globalObject.promiseStructure()); + ASSERT(promise); + + functor(lexicalGlobalObject, callFrame, DeferredPromise::create(globalObject, *promise)); + + rejectPromiseWithExceptionIfAny(lexicalGlobalObject, globalObject, *promise, catchScope); + // FIXME: We could have error since any JS call can throw stack-overflow errors. + // https://bugs.webkit.org/show_bug.cgi?id=203402 + RETURN_IF_EXCEPTION(catchScope, JSC::jsUndefined()); + return promise; +} + +using BindingPromiseFunction = JSC::EncodedJSValue(JSC::JSGlobalObject*, JSC::CallFrame*, Ref<DeferredPromise>&&); +template<BindingPromiseFunction bindingFunction> +inline void bindingPromiseFunctionAdapter(JSC::JSGlobalObject& lexicalGlobalObject, JSC::CallFrame& callFrame, Ref<DeferredPromise>&& promise) +{ + bindingFunction(&lexicalGlobalObject, &callFrame, WTFMove(promise)); +} + +template<BindingPromiseFunction bindingPromiseFunction> +inline JSC::JSValue callPromiseFunction(JSC::JSGlobalObject& lexicalGlobalObject, JSC::CallFrame& callFrame) +{ + return callPromiseFunction<bindingPromiseFunctionAdapter<bindingPromiseFunction>>(lexicalGlobalObject, callFrame); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSDOMWindow.h b/src/javascript/jsc/bindings/webcore/JSDOMWindow.h new file mode 100644 index 000000000..d067d0b5a --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSDOMWindow.h @@ -0,0 +1 @@ +// stub
\ No newline at end of file diff --git a/src/javascript/jsc/bindings/webcore/JSFetchRequestCredentials.cpp b/src/javascript/jsc/bindings/webcore/JSFetchRequestCredentials.cpp new file mode 100644 index 000000000..37f00d64b --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSFetchRequestCredentials.cpp @@ -0,0 +1,68 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSFetchRequestCredentials.h" + +#include <JavaScriptCore/JSCInlines.h> +#include <JavaScriptCore/JSString.h> +#include <wtf/NeverDestroyed.h> + + +namespace WebCore { +using namespace JSC; + +String convertEnumerationToString(FetchRequestCredentials enumerationValue) +{ + static const NeverDestroyed<String> values[] = { + MAKE_STATIC_STRING_IMPL("omit"), + MAKE_STATIC_STRING_IMPL("same-origin"), + MAKE_STATIC_STRING_IMPL("include"), + }; + static_assert(static_cast<size_t>(FetchRequestCredentials::Omit) == 0, "FetchRequestCredentials::Omit is not 0 as expected"); + static_assert(static_cast<size_t>(FetchRequestCredentials::SameOrigin) == 1, "FetchRequestCredentials::SameOrigin is not 1 as expected"); + static_assert(static_cast<size_t>(FetchRequestCredentials::Include) == 2, "FetchRequestCredentials::Include is not 2 as expected"); + ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values)); + return values[static_cast<size_t>(enumerationValue)]; +} + +template<> JSString* convertEnumerationToJS(JSGlobalObject& lexicalGlobalObject, FetchRequestCredentials enumerationValue) +{ + return jsStringWithCache(lexicalGlobalObject.vm(), convertEnumerationToString(enumerationValue)); +} + +template<> std::optional<FetchRequestCredentials> parseEnumeration<FetchRequestCredentials>(JSGlobalObject& lexicalGlobalObject, JSValue value) +{ + auto stringValue = value.toWTFString(&lexicalGlobalObject); + if (stringValue == "omit") + return FetchRequestCredentials::Omit; + if (stringValue == "same-origin") + return FetchRequestCredentials::SameOrigin; + if (stringValue == "include") + return FetchRequestCredentials::Include; + return std::nullopt; +} + +template<> const char* expectedEnumerationValues<FetchRequestCredentials>() +{ + return "\"omit\", \"same-origin\", \"include\""; +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSFetchRequestCredentials.h b/src/javascript/jsc/bindings/webcore/JSFetchRequestCredentials.h new file mode 100644 index 000000000..74030df84 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSFetchRequestCredentials.h @@ -0,0 +1,34 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "FetchRequestCredentials.h" +#include "JSDOMConvertEnumeration.h" + +namespace WebCore { + +String convertEnumerationToString(FetchRequestCredentials); +template<> JSC::JSString* convertEnumerationToJS(JSC::JSGlobalObject&, FetchRequestCredentials); + +template<> std::optional<FetchRequestCredentials> parseEnumeration<FetchRequestCredentials>(JSC::JSGlobalObject&, JSC::JSValue); +template<> const char* expectedEnumerationValues<FetchRequestCredentials>(); + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSGPU.h b/src/javascript/jsc/bindings/webcore/JSGPU.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSGPU.h diff --git a/src/javascript/jsc/bindings/webcore/JSMediaCapabilities.h b/src/javascript/jsc/bindings/webcore/JSMediaCapabilities.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSMediaCapabilities.h diff --git a/src/javascript/jsc/bindings/webcore/JSMessageChannel.cpp b/src/javascript/jsc/bindings/webcore/JSMessageChannel.cpp new file mode 100644 index 000000000..0421bc9d3 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSMessageChannel.cpp @@ -0,0 +1,326 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#if ENABLE(CHANNEL_MESSAGING) + +#include "JSMessageChannel.h" + +#include "ActiveDOMObject.h" +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMConstructor.h" +#include "JSDOMConvertInterface.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObject.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMWrapperCache.h" +#include "JSMessagePort.h" +#include "ScriptExecutionContext.h" +#include "WebCoreJSClientData.h" +#include <JavaScriptCore/FunctionPrototype.h> +#include <JavaScriptCore/HeapAnalyzer.h> +#include <JavaScriptCore/JSCInlines.h> +#include <JavaScriptCore/JSDestructibleObjectHeapCellType.h> +#include <JavaScriptCore/SlotVisitorMacros.h> +#include <JavaScriptCore/SubspaceInlines.h> +#include <wtf/GetPtr.h> +#include <wtf/PointerPreparations.h> +#include <wtf/URL.h> + + +namespace WebCore { +using namespace JSC; + +// Attributes + +static JSC_DECLARE_CUSTOM_GETTER(jsMessageChannelConstructor); +static JSC_DECLARE_CUSTOM_GETTER(jsMessageChannel_port1); +static JSC_DECLARE_CUSTOM_GETTER(jsMessageChannel_port2); + +class JSMessageChannelPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSMessageChannelPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSMessageChannelPrototype* ptr = new (NotNull, JSC::allocateCell<JSMessageChannelPrototype>(vm)) JSMessageChannelPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSMessageChannelPrototype, Base); + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSMessageChannelPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSMessageChannelPrototype, JSMessageChannelPrototype::Base); + +using JSMessageChannelDOMConstructor = JSDOMConstructor<JSMessageChannel>; + +template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSMessageChannelDOMConstructor::construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame) +{ + VM& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* castedThis = jsCast<JSMessageChannelDOMConstructor*>(callFrame->jsCallee()); + ASSERT(castedThis); + auto* context = castedThis->scriptExecutionContext(); + if (UNLIKELY(!context)) + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "MessageChannel"); + auto object = MessageChannel::create(*context); + if constexpr (IsExceptionOr<decltype(object)>) + RETURN_IF_EXCEPTION(throwScope, { }); + static_assert(TypeOrExceptionOrUnderlyingType<decltype(object)>::isRef); + auto jsValue = toJSNewlyCreated<IDLInterface<MessageChannel>>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, WTFMove(object)); + if constexpr (IsExceptionOr<decltype(object)>) + RETURN_IF_EXCEPTION(throwScope, { }); + setSubclassStructureIfNeeded<MessageChannel>(lexicalGlobalObject, callFrame, asObject(jsValue)); + RETURN_IF_EXCEPTION(throwScope, { }); + return JSValue::encode(jsValue); +} +JSC_ANNOTATE_HOST_FUNCTION(JSMessageChannelDOMConstructorConstruct, JSMessageChannelDOMConstructor::construct); + +template<> const ClassInfo JSMessageChannelDOMConstructor::s_info = { "MessageChannel"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMessageChannelDOMConstructor) }; + +template<> JSValue JSMessageChannelDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(vm); + return globalObject.functionPrototype(); +} + +template<> void JSMessageChannelDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "MessageChannel"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSMessageChannel::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +/* Hash table for prototype */ + +static const HashTableValue JSMessageChannelPrototypeTableValues[] = +{ + { "constructor", static_cast<unsigned>(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsMessageChannelConstructor), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "port1", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsMessageChannel_port1), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "port2", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsMessageChannel_port2), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, +}; + +const ClassInfo JSMessageChannelPrototype::s_info = { "MessageChannel"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMessageChannelPrototype) }; + +void JSMessageChannelPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSMessageChannel::info(), JSMessageChannelPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSMessageChannel::s_info = { "MessageChannel"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMessageChannel) }; + +JSMessageChannel::JSMessageChannel(Structure* structure, JSDOMGlobalObject& globalObject, Ref<MessageChannel>&& impl) + : JSDOMWrapper<MessageChannel>(structure, globalObject, WTFMove(impl)) +{ +} + +void JSMessageChannel::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); + + static_assert(!std::is_base_of<ActiveDOMObject, MessageChannel>::value, "Interface is not marked as [ActiveDOMObject] even though implementation class subclasses ActiveDOMObject."); + +} + +JSObject* JSMessageChannel::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return JSMessageChannelPrototype::create(vm, &globalObject, JSMessageChannelPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype())); +} + +JSObject* JSMessageChannel::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype<JSMessageChannel>(vm, globalObject); +} + +JSValue JSMessageChannel::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor<JSMessageChannelDOMConstructor, DOMConstructorID::MessageChannel>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject)); +} + +void JSMessageChannel::destroy(JSC::JSCell* cell) +{ + JSMessageChannel* thisObject = static_cast<JSMessageChannel*>(cell); + thisObject->JSMessageChannel::~JSMessageChannel(); +} + +JSC_DEFINE_CUSTOM_GETTER(jsMessageChannelConstructor, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + VM& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast<JSMessageChannelPrototype*>(vm, JSValue::decode(thisValue)); + if (UNLIKELY(!prototype)) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSMessageChannel::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject())); +} + +static inline JSValue jsMessageChannel_port1Getter(JSGlobalObject& lexicalGlobalObject, JSMessageChannel& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLInterface<MessagePort>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, impl.port1()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsMessageChannel_port1, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSMessageChannel>::get<jsMessageChannel_port1Getter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsMessageChannel_port2Getter(JSGlobalObject& lexicalGlobalObject, JSMessageChannel& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLInterface<MessagePort>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, impl.port2()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsMessageChannel_port2, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSMessageChannel>::get<jsMessageChannel_port2Getter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +JSC::GCClient::IsoSubspace* JSMessageChannel::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl<JSMessageChannel, UseCustomHeapCellType::No>(vm, + [] (auto& spaces) { return spaces.m_clientSubspaceForMessageChannel.get(); }, + [] (auto& spaces, auto&& space) { spaces.m_clientSubspaceForMessageChannel = WTFMove(space); }, + [] (auto& spaces) { return spaces.m_subspaceForMessageChannel.get(); }, + [] (auto& spaces, auto&& space) { spaces.m_subspaceForMessageChannel = WTFMove(space); } + ); +} + +template<typename Visitor> +void JSMessageChannel::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<JSMessageChannel*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + thisObject->visitAdditionalChildren(visitor); +} + +DEFINE_VISIT_CHILDREN(JSMessageChannel); + +template<typename Visitor> +void JSMessageChannel::visitOutputConstraints(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<JSMessageChannel*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitOutputConstraints(thisObject, visitor); + thisObject->visitAdditionalChildren(visitor); +} + +template void JSMessageChannel::visitOutputConstraints(JSCell*, AbstractSlotVisitor&); +template void JSMessageChannel::visitOutputConstraints(JSCell*, SlotVisitor&); +void JSMessageChannel::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast<JSMessageChannel*>(cell); + analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); + if (thisObject->scriptExecutionContext()) + analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + Base::analyzeHeap(cell, analyzer); +} + +bool JSMessageChannelOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, AbstractSlotVisitor& visitor, const char** reason) +{ + UNUSED_PARAM(handle); + UNUSED_PARAM(visitor); + UNUSED_PARAM(reason); + return false; +} + +void JSMessageChannelOwner::finalize(JSC::Handle<JSC::Unknown> handle, void* context) +{ + auto* jsMessageChannel = static_cast<JSMessageChannel*>(handle.slot()->asCell()); + auto& world = *static_cast<DOMWrapperWorld*>(context); + uncacheWrapper(world, &jsMessageChannel->wrapped(), jsMessageChannel); +} + +#if ENABLE(BINDING_INTEGRITY) +#if PLATFORM(WIN) +#pragma warning(disable: 4483) +extern "C" { extern void (*const __identifier("??_7MessageChannel@WebCore@@6B@")[])(); } +#else +extern "C" { extern void* _ZTVN7WebCore14MessageChannelE[]; } +#endif +#endif + +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref<MessageChannel>&& impl) +{ + + if constexpr (std::is_polymorphic_v<MessageChannel>) { +#if ENABLE(BINDING_INTEGRITY) + const void* actualVTablePointer = getVTablePointer(impl.ptr()); +#if PLATFORM(WIN) + void* expectedVTablePointer = __identifier("??_7MessageChannel@WebCore@@6B@"); +#else + void* expectedVTablePointer = &_ZTVN7WebCore14MessageChannelE[2]; +#endif + + // If you hit this assertion you either have a use after free bug, or + // MessageChannel has subclasses. If MessageChannel has subclasses that get passed + // to toJS() we currently require MessageChannel you to opt out of binding hardening + // by adding the SkipVTableValidation attribute to the interface IDL definition + RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); +#endif + } + return createWrapper<MessageChannel>(globalObject, WTFMove(impl)); +} + +JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, MessageChannel& impl) +{ + return wrap(lexicalGlobalObject, globalObject, impl); +} + +MessageChannel* JSMessageChannel::toWrapped(JSC::VM& vm, JSC::JSValue value) +{ + if (auto* wrapper = jsDynamicCast<JSMessageChannel*>(vm, value)) + return &wrapper->wrapped(); + return nullptr; +} + +} + +#endif // ENABLE(CHANNEL_MESSAGING) diff --git a/src/javascript/jsc/bindings/webcore/JSMessageChannel.dep b/src/javascript/jsc/bindings/webcore/JSMessageChannel.dep new file mode 100644 index 000000000..ce0ee3b9f --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSMessageChannel.dep @@ -0,0 +1 @@ +JSMessageChannel.h : diff --git a/src/javascript/jsc/bindings/webcore/JSMessageChannel.h b/src/javascript/jsc/bindings/webcore/JSMessageChannel.h new file mode 100644 index 000000000..8ac4f8170 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSMessageChannel.h @@ -0,0 +1,101 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#if ENABLE(CHANNEL_MESSAGING) + +#include "JSDOMWrapper.h" +#include "MessageChannel.h" +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +class JSMessageChannel : public JSDOMWrapper<MessageChannel> { +public: + using Base = JSDOMWrapper<MessageChannel>; + static JSMessageChannel* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<MessageChannel>&& impl) + { + JSMessageChannel* ptr = new (NotNull, JSC::allocateCell<JSMessageChannel>(globalObject->vm())) JSMessageChannel(structure, *globalObject, WTFMove(impl)); + ptr->finishCreation(globalObject->vm()); + return ptr; + } + + static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&); + static MessageChannel* toWrapped(JSC::VM&, JSC::JSValue); + static void destroy(JSC::JSCell*); + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray); + } + + static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*); + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + DECLARE_VISIT_CHILDREN; + template<typename Visitor> void visitAdditionalChildren(Visitor&); + + template<typename Visitor> static void visitOutputConstraints(JSCell*, Visitor&); + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); +protected: + JSMessageChannel(JSC::Structure*, JSDOMGlobalObject&, Ref<MessageChannel>&&); + + void finishCreation(JSC::VM&); +}; + +class JSMessageChannelOwner final : public JSC::WeakHandleOwner { +public: + bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::AbstractSlotVisitor&, const char**) final; + void finalize(JSC::Handle<JSC::Unknown>, void* context) final; +}; + +inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, MessageChannel*) +{ + static NeverDestroyed<JSMessageChannelOwner> owner; + return &owner.get(); +} + +inline void* wrapperKey(MessageChannel* wrappableObject) +{ + return wrappableObject; +} + +JSC::JSValue toJS(JSC::JSGlobalObject*, JSDOMGlobalObject*, MessageChannel&); +inline JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, MessageChannel* impl) { return impl ? toJS(lexicalGlobalObject, globalObject, *impl) : JSC::jsNull(); } +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, Ref<MessageChannel>&&); +inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, RefPtr<MessageChannel>&& impl) { return impl ? toJSNewlyCreated(lexicalGlobalObject, globalObject, impl.releaseNonNull()) : JSC::jsNull(); } + +template<> struct JSDOMWrapperConverterTraits<MessageChannel> { + using WrapperClass = JSMessageChannel; + using ToWrappedReturnType = MessageChannel*; +}; + +} // namespace WebCore + +#endif // ENABLE(CHANNEL_MESSAGING) diff --git a/src/javascript/jsc/bindings/webcore/JSMessageEvent.cpp b/src/javascript/jsc/bindings/webcore/JSMessageEvent.cpp new file mode 100644 index 000000000..cb50ac507 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSMessageEvent.cpp @@ -0,0 +1,515 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSMessageEvent.h" + +#include "ActiveDOMObject.h" +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "IDLTypes.h" +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMConstructor.h" +#include "JSDOMConvertAny.h" +#include "JSDOMConvertBase.h" +#include "JSDOMConvertBoolean.h" +#include "JSDOMConvertInterface.h" +#include "JSDOMConvertNullable.h" +#include "JSDOMConvertSequences.h" +#include "JSDOMConvertStrings.h" +#include "JSDOMConvertUnion.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObject.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMOperation.h" +#include "JSDOMWrapperCache.h" +#include "JSMessagePort.h" +#include "JSServiceWorker.h" +#include "JSWindowProxy.h" +#include "ScriptExecutionContext.h" +#include "WebCoreJSClientData.h" +#include <JavaScriptCore/HeapAnalyzer.h> +#include <JavaScriptCore/JSArray.h> +#include <JavaScriptCore/JSCInlines.h> +#include <JavaScriptCore/JSDestructibleObjectHeapCellType.h> +#include <JavaScriptCore/SlotVisitorMacros.h> +#include <JavaScriptCore/SubspaceInlines.h> +#include <variant> +#include <wtf/GetPtr.h> +#include <wtf/PointerPreparations.h> +#include <wtf/URL.h> + + +namespace WebCore { +using namespace JSC; + +template<> MessageEvent::Init convertDictionary<MessageEvent::Init>(JSGlobalObject& lexicalGlobalObject, JSValue value) +{ + VM& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + bool isNullOrUndefined = value.isUndefinedOrNull(); + auto* object = isNullOrUndefined ? nullptr : value.getObject(); + if (UNLIKELY(!isNullOrUndefined && !object)) { + throwTypeError(&lexicalGlobalObject, throwScope); + return { }; + } + MessageEvent::Init result; + JSValue bubblesValue; + if (isNullOrUndefined) + bubblesValue = jsUndefined(); + else { + bubblesValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "bubbles")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!bubblesValue.isUndefined()) { + result.bubbles = convert<IDLBoolean>(lexicalGlobalObject, bubblesValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.bubbles = false; + JSValue cancelableValue; + if (isNullOrUndefined) + cancelableValue = jsUndefined(); + else { + cancelableValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "cancelable")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!cancelableValue.isUndefined()) { + result.cancelable = convert<IDLBoolean>(lexicalGlobalObject, cancelableValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.cancelable = false; + JSValue composedValue; + if (isNullOrUndefined) + composedValue = jsUndefined(); + else { + composedValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "composed")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!composedValue.isUndefined()) { + result.composed = convert<IDLBoolean>(lexicalGlobalObject, composedValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.composed = false; + JSValue dataValue; + if (isNullOrUndefined) + dataValue = jsUndefined(); + else { + dataValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "data")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!dataValue.isUndefined()) { + result.data = convert<IDLAny>(lexicalGlobalObject, dataValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.data = jsNull(); + JSValue lastEventIdValue; + if (isNullOrUndefined) + lastEventIdValue = jsUndefined(); + else { + lastEventIdValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "lastEventId")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!lastEventIdValue.isUndefined()) { + result.lastEventId = convert<IDLDOMString>(lexicalGlobalObject, lastEventIdValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.lastEventId = emptyString(); + JSValue originValue; + if (isNullOrUndefined) + originValue = jsUndefined(); + else { + originValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "origin")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!originValue.isUndefined()) { + result.origin = convert<IDLUSVString>(lexicalGlobalObject, originValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.origin = emptyString(); + JSValue portsValue; + if (isNullOrUndefined) + portsValue = jsUndefined(); + else { + portsValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "ports")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!portsValue.isUndefined()) { + result.ports = convert<IDLSequence<IDLInterface<MessagePort>>>(lexicalGlobalObject, portsValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.ports = Converter<IDLSequence<IDLInterface<MessagePort>>>::ReturnType{ }; + JSValue sourceValue; + if (isNullOrUndefined) + sourceValue = jsUndefined(); + else { + sourceValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "source")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!sourceValue.isUndefined()) { + result.source = convert<IDLNullable<IDLUnion<IDLInterface<WindowProxy>, IDLInterface<MessagePort>, IDLInterface<ServiceWorker>>>>(lexicalGlobalObject, sourceValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.source = std::nullopt; + return result; +} + +// Functions + +static JSC_DECLARE_HOST_FUNCTION(jsMessageEventPrototypeFunction_initMessageEvent); + +// Attributes + +static JSC_DECLARE_CUSTOM_GETTER(jsMessageEventConstructor); +static JSC_DECLARE_CUSTOM_GETTER(jsMessageEvent_origin); +static JSC_DECLARE_CUSTOM_GETTER(jsMessageEvent_lastEventId); +static JSC_DECLARE_CUSTOM_GETTER(jsMessageEvent_source); +static JSC_DECLARE_CUSTOM_GETTER(jsMessageEvent_data); +static JSC_DECLARE_CUSTOM_GETTER(jsMessageEvent_ports); + +class JSMessageEventPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSMessageEventPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSMessageEventPrototype* ptr = new (NotNull, JSC::allocateCell<JSMessageEventPrototype>(vm)) JSMessageEventPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSMessageEventPrototype, Base); + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSMessageEventPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSMessageEventPrototype, JSMessageEventPrototype::Base); + +using JSMessageEventDOMConstructor = JSDOMConstructor<JSMessageEvent>; + +template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSMessageEventDOMConstructor::construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame) +{ + VM& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* castedThis = jsCast<JSMessageEventDOMConstructor*>(callFrame->jsCallee()); + ASSERT(castedThis); + if (UNLIKELY(callFrame->argumentCount() < 1)) + return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); + EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); + auto type = convert<IDLDOMString>(*lexicalGlobalObject, argument0.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + EnsureStillAliveScope argument1 = callFrame->argument(1); + auto eventInitDict = convert<IDLDictionary<MessageEvent::Init>>(*lexicalGlobalObject, argument1.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + auto object = MessageEvent::create(WTFMove(type), WTFMove(eventInitDict)); + if constexpr (IsExceptionOr<decltype(object)>) + RETURN_IF_EXCEPTION(throwScope, { }); + static_assert(TypeOrExceptionOrUnderlyingType<decltype(object)>::isRef); + auto jsValue = toJSNewlyCreated<IDLInterface<MessageEvent>>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, WTFMove(object)); + if constexpr (IsExceptionOr<decltype(object)>) + RETURN_IF_EXCEPTION(throwScope, { }); + setSubclassStructureIfNeeded<MessageEvent>(lexicalGlobalObject, callFrame, asObject(jsValue)); + RETURN_IF_EXCEPTION(throwScope, { }); + return JSValue::encode(jsValue); +} +JSC_ANNOTATE_HOST_FUNCTION(JSMessageEventDOMConstructorConstruct, JSMessageEventDOMConstructor::construct); + +template<> const ClassInfo JSMessageEventDOMConstructor::s_info = { "MessageEvent"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMessageEventDOMConstructor) }; + +template<> JSValue JSMessageEventDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + return JSEvent::getConstructor(vm, &globalObject); +} + +template<> void JSMessageEventDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(1), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "MessageEvent"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSMessageEvent::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +/* Hash table for prototype */ + +static const HashTableValue JSMessageEventPrototypeTableValues[] = +{ + { "constructor", static_cast<unsigned>(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsMessageEventConstructor), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "origin", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsMessageEvent_origin), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "lastEventId", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsMessageEvent_lastEventId), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "source", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsMessageEvent_source), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "data", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsMessageEvent_data), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "ports", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsMessageEvent_ports), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "initMessageEvent", static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t)static_cast<RawNativeFunction>(jsMessageEventPrototypeFunction_initMessageEvent), (intptr_t) (1) } }, +}; + +const ClassInfo JSMessageEventPrototype::s_info = { "MessageEvent"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMessageEventPrototype) }; + +void JSMessageEventPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSMessageEvent::info(), JSMessageEventPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSMessageEvent::s_info = { "MessageEvent"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMessageEvent) }; + +JSMessageEvent::JSMessageEvent(Structure* structure, JSDOMGlobalObject& globalObject, Ref<MessageEvent>&& impl) + : JSEvent(structure, globalObject, WTFMove(impl)) +{ +} + +void JSMessageEvent::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); + + static_assert(!std::is_base_of<ActiveDOMObject, MessageEvent>::value, "Interface is not marked as [ActiveDOMObject] even though implementation class subclasses ActiveDOMObject."); + + vm.heap.reportExtraMemoryAllocated(wrapped().memoryCost()); +} + +JSObject* JSMessageEvent::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return JSMessageEventPrototype::create(vm, &globalObject, JSMessageEventPrototype::createStructure(vm, &globalObject, JSEvent::prototype(vm, globalObject))); +} + +JSObject* JSMessageEvent::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype<JSMessageEvent>(vm, globalObject); +} + +JSValue JSMessageEvent::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor<JSMessageEventDOMConstructor, DOMConstructorID::MessageEvent>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject)); +} + +JSC_DEFINE_CUSTOM_GETTER(jsMessageEventConstructor, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + VM& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast<JSMessageEventPrototype*>(vm, JSValue::decode(thisValue)); + if (UNLIKELY(!prototype)) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSMessageEvent::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject())); +} + +static inline JSValue jsMessageEvent_originGetter(JSGlobalObject& lexicalGlobalObject, JSMessageEvent& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLUSVString>(lexicalGlobalObject, throwScope, impl.origin()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsMessageEvent_origin, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSMessageEvent>::get<jsMessageEvent_originGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsMessageEvent_lastEventIdGetter(JSGlobalObject& lexicalGlobalObject, JSMessageEvent& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLDOMString>(lexicalGlobalObject, throwScope, impl.lastEventId()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsMessageEvent_lastEventId, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSMessageEvent>::get<jsMessageEvent_lastEventIdGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsMessageEvent_sourceGetter(JSGlobalObject& lexicalGlobalObject, JSMessageEvent& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLNullable<IDLUnion<IDLInterface<WindowProxy>, IDLInterface<MessagePort>, IDLInterface<ServiceWorker>>>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, impl.source()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsMessageEvent_source, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSMessageEvent>::get<jsMessageEvent_sourceGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsMessageEvent_dataGetter(JSGlobalObject& lexicalGlobalObject, JSMessageEvent& thisObject) +{ + UNUSED_PARAM(lexicalGlobalObject); + return thisObject.data(lexicalGlobalObject); +} + +JSC_DEFINE_CUSTOM_GETTER(jsMessageEvent_data, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSMessageEvent>::get<jsMessageEvent_dataGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsMessageEvent_portsGetter(JSGlobalObject& lexicalGlobalObject, JSMessageEvent& thisObject) +{ + UNUSED_PARAM(lexicalGlobalObject); + return thisObject.ports(lexicalGlobalObject); +} + +JSC_DEFINE_CUSTOM_GETTER(jsMessageEvent_ports, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSMessageEvent>::get<jsMessageEvent_portsGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSC::EncodedJSValue jsMessageEventPrototypeFunction_initMessageEventBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSMessageEvent>::ClassParameter castedThis) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(throwScope); + UNUSED_PARAM(callFrame); + auto& impl = castedThis->wrapped(); + if (UNLIKELY(callFrame->argumentCount() < 1)) + return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); + EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); + auto type = convert<IDLDOMString>(*lexicalGlobalObject, argument0.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + EnsureStillAliveScope argument1 = callFrame->argument(1); + auto bubbles = convert<IDLBoolean>(*lexicalGlobalObject, argument1.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + EnsureStillAliveScope argument2 = callFrame->argument(2); + auto cancelable = convert<IDLBoolean>(*lexicalGlobalObject, argument2.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + EnsureStillAliveScope argument3 = callFrame->argument(3); + auto data = argument3.value().isUndefined() ? jsNull() : convert<IDLAny>(*lexicalGlobalObject, argument3.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + EnsureStillAliveScope argument4 = callFrame->argument(4); + auto originArg = argument4.value().isUndefined() ? emptyString() : convert<IDLUSVString>(*lexicalGlobalObject, argument4.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + EnsureStillAliveScope argument5 = callFrame->argument(5); + auto lastEventId = argument5.value().isUndefined() ? emptyString() : convert<IDLDOMString>(*lexicalGlobalObject, argument5.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + EnsureStillAliveScope argument6 = callFrame->argument(6); + auto source = argument6.value().isUndefined() ? std::nullopt : convert<IDLNullable<IDLUnion<IDLInterface<WindowProxy>, IDLInterface<MessagePort>, IDLInterface<ServiceWorker>>>>(*lexicalGlobalObject, argument6.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + EnsureStillAliveScope argument7 = callFrame->argument(7); + auto messagePorts = argument7.value().isUndefined() ? Converter<IDLSequence<IDLInterface<MessagePort>>>::ReturnType{ } : convert<IDLSequence<IDLInterface<MessagePort>>>(*lexicalGlobalObject, argument7.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.initMessageEvent(WTFMove(type), WTFMove(bubbles), WTFMove(cancelable), WTFMove(data), WTFMove(originArg), WTFMove(lastEventId), WTFMove(source), WTFMove(messagePorts)); }))); +} + +JSC_DEFINE_HOST_FUNCTION(jsMessageEventPrototypeFunction_initMessageEvent, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSMessageEvent>::call<jsMessageEventPrototypeFunction_initMessageEventBody>(*lexicalGlobalObject, *callFrame, "initMessageEvent"); +} + +JSC::GCClient::IsoSubspace* JSMessageEvent::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl<JSMessageEvent, UseCustomHeapCellType::No>(vm, + [] (auto& spaces) { return spaces.m_clientSubspaceForMessageEvent.get(); }, + [] (auto& spaces, auto&& space) { spaces.m_clientSubspaceForMessageEvent = WTFMove(space); }, + [] (auto& spaces) { return spaces.m_subspaceForMessageEvent.get(); }, + [] (auto& spaces, auto&& space) { spaces.m_subspaceForMessageEvent = WTFMove(space); } + ); +} + +template<typename Visitor> +void JSMessageEvent::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<JSMessageEvent*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + thisObject->visitAdditionalChildren(visitor); + visitor.reportExtraMemoryVisited(thisObject->wrapped().memoryCost()); +} + +DEFINE_VISIT_CHILDREN(JSMessageEvent); + +template<typename Visitor> +void JSMessageEvent::visitOutputConstraints(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<JSMessageEvent*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitOutputConstraints(thisObject, visitor); + thisObject->visitAdditionalChildren(visitor); +} + +template void JSMessageEvent::visitOutputConstraints(JSCell*, AbstractSlotVisitor&); +template void JSMessageEvent::visitOutputConstraints(JSCell*, SlotVisitor&); +size_t JSMessageEvent::estimatedSize(JSCell* cell, VM& vm) +{ + auto* thisObject = jsCast<JSMessageEvent*>(cell); + return Base::estimatedSize(thisObject, vm) + thisObject->wrapped().memoryCost(); +} + +void JSMessageEvent::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast<JSMessageEvent*>(cell); + analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); + if (thisObject->scriptExecutionContext()) + analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + Base::analyzeHeap(cell, analyzer); +} + +#if ENABLE(BINDING_INTEGRITY) +#if PLATFORM(WIN) +#pragma warning(disable: 4483) +extern "C" { extern void (*const __identifier("??_7MessageEvent@WebCore@@6B@")[])(); } +#else +extern "C" { extern void* _ZTVN7WebCore12MessageEventE[]; } +#endif +#endif + +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref<MessageEvent>&& impl) +{ + + if constexpr (std::is_polymorphic_v<MessageEvent>) { +#if ENABLE(BINDING_INTEGRITY) + const void* actualVTablePointer = getVTablePointer(impl.ptr()); +#if PLATFORM(WIN) + void* expectedVTablePointer = __identifier("??_7MessageEvent@WebCore@@6B@"); +#else + void* expectedVTablePointer = &_ZTVN7WebCore12MessageEventE[2]; +#endif + + // If you hit this assertion you either have a use after free bug, or + // MessageEvent has subclasses. If MessageEvent has subclasses that get passed + // to toJS() we currently require MessageEvent you to opt out of binding hardening + // by adding the SkipVTableValidation attribute to the interface IDL definition + RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); +#endif + } + return createWrapper<MessageEvent>(globalObject, WTFMove(impl)); +} + +JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, MessageEvent& impl) +{ + return wrap(lexicalGlobalObject, globalObject, impl); +} + + +} diff --git a/src/javascript/jsc/bindings/webcore/JSMessageEvent.dep b/src/javascript/jsc/bindings/webcore/JSMessageEvent.dep new file mode 100644 index 000000000..da20f76fa --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSMessageEvent.dep @@ -0,0 +1,3 @@ +JSMessageEvent.h : Event.idl EventInit.idl +Event.idl : +EventInit.idl : diff --git a/src/javascript/jsc/bindings/webcore/JSMessageEvent.h b/src/javascript/jsc/bindings/webcore/JSMessageEvent.h new file mode 100644 index 000000000..85424d9ec --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSMessageEvent.h @@ -0,0 +1,91 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "JSDOMConvertDictionary.h" +#include "JSDOMWrapper.h" +#include "JSEvent.h" +#include "MessageEvent.h" + +namespace WebCore { + +class JSMessageEvent : public JSEvent { +public: + using Base = JSEvent; + using DOMWrapped = MessageEvent; + static JSMessageEvent* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<MessageEvent>&& impl) + { + JSMessageEvent* ptr = new (NotNull, JSC::allocateCell<JSMessageEvent>(globalObject->vm())) JSMessageEvent(structure, *globalObject, WTFMove(impl)); + ptr->finishCreation(globalObject->vm()); + return ptr; + } + + static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&); + static size_t estimatedSize(JSCell*, JSC::VM&); + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType(JSEventType), StructureFlags), info(), JSC::NonArray); + } + + static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*); + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + DECLARE_VISIT_CHILDREN; + template<typename Visitor> void visitAdditionalChildren(Visitor&); + + template<typename Visitor> static void visitOutputConstraints(JSCell*, Visitor&); + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + + // Custom attributes + JSC::JSValue data(JSC::JSGlobalObject&) const; + JSC::JSValue ports(JSC::JSGlobalObject&) const; + MessageEvent& wrapped() const + { + return static_cast<MessageEvent&>(Base::wrapped()); + } +protected: + JSMessageEvent(JSC::Structure*, JSDOMGlobalObject&, Ref<MessageEvent>&&); + + void finishCreation(JSC::VM&); +}; + +JSC::JSValue toJS(JSC::JSGlobalObject*, JSDOMGlobalObject*, MessageEvent&); +inline JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, MessageEvent* impl) { return impl ? toJS(lexicalGlobalObject, globalObject, *impl) : JSC::jsNull(); } +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, Ref<MessageEvent>&&); +inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, RefPtr<MessageEvent>&& impl) { return impl ? toJSNewlyCreated(lexicalGlobalObject, globalObject, impl.releaseNonNull()) : JSC::jsNull(); } + +template<> struct JSDOMWrapperConverterTraits<MessageEvent> { + using WrapperClass = JSMessageEvent; + using ToWrappedReturnType = MessageEvent*; +}; +template<> MessageEvent::Init convertDictionary<MessageEvent::Init>(JSC::JSGlobalObject&, JSC::JSValue); + + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSMessagePort.cpp b/src/javascript/jsc/bindings/webcore/JSMessagePort.cpp new file mode 100644 index 000000000..055ad9364 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSMessagePort.cpp @@ -0,0 +1,415 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSMessagePort.h" + +#include "ActiveDOMObject.h" +#include "EventNames.h" +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "IDLTypes.h" +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMConstructorNotConstructable.h" +#include "JSDOMConvertAny.h" +#include "JSDOMConvertBase.h" +#include "JSDOMConvertDictionary.h" +#include "JSDOMConvertObject.h" +#include "JSDOMConvertSequences.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMOperation.h" +#include "JSDOMWrapperCache.h" +#include "JSEventListener.h" +#include "JSStructuredSerializeOptions.h" +#include "ScriptExecutionContext.h" +#include "WebCoreJSClientData.h" +#include <JavaScriptCore/HeapAnalyzer.h> +#include <JavaScriptCore/IteratorOperations.h> +#include <JavaScriptCore/JSArray.h> +#include <JavaScriptCore/JSCInlines.h> +#include <JavaScriptCore/JSDestructibleObjectHeapCellType.h> +#include <JavaScriptCore/SlotVisitorMacros.h> +#include <JavaScriptCore/SubspaceInlines.h> +#include <wtf/GetPtr.h> +#include <wtf/PointerPreparations.h> +#include <wtf/URL.h> + + +namespace WebCore { +using namespace JSC; + +// Functions + +static JSC_DECLARE_HOST_FUNCTION(jsMessagePortPrototypeFunction_postMessage); +static JSC_DECLARE_HOST_FUNCTION(jsMessagePortPrototypeFunction_start); +static JSC_DECLARE_HOST_FUNCTION(jsMessagePortPrototypeFunction_close); + +// Attributes + +static JSC_DECLARE_CUSTOM_GETTER(jsMessagePortConstructor); +static JSC_DECLARE_CUSTOM_GETTER(jsMessagePort_onmessage); +static JSC_DECLARE_CUSTOM_SETTER(setJSMessagePort_onmessage); + +class JSMessagePortPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSMessagePortPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSMessagePortPrototype* ptr = new (NotNull, JSC::allocateCell<JSMessagePortPrototype>(vm)) JSMessagePortPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSMessagePortPrototype, Base); + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSMessagePortPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSMessagePortPrototype, JSMessagePortPrototype::Base); + +using JSMessagePortDOMConstructor = JSDOMConstructorNotConstructable<JSMessagePort>; + +template<> const ClassInfo JSMessagePortDOMConstructor::s_info = { "MessagePort"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMessagePortDOMConstructor) }; + +template<> JSValue JSMessagePortDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + return JSEventTarget::getConstructor(vm, &globalObject); +} + +template<> void JSMessagePortDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "MessagePort"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSMessagePort::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +/* Hash table for prototype */ + +static const HashTableValue JSMessagePortPrototypeTableValues[] = +{ + { "constructor", static_cast<unsigned>(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsMessagePortConstructor), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "onmessage", static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsMessagePort_onmessage), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSMessagePort_onmessage) } }, + { "postMessage", static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t)static_cast<RawNativeFunction>(jsMessagePortPrototypeFunction_postMessage), (intptr_t) (1) } }, + { "start", static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t)static_cast<RawNativeFunction>(jsMessagePortPrototypeFunction_start), (intptr_t) (0) } }, + { "close", static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t)static_cast<RawNativeFunction>(jsMessagePortPrototypeFunction_close), (intptr_t) (0) } }, +}; + +const ClassInfo JSMessagePortPrototype::s_info = { "MessagePort"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMessagePortPrototype) }; + +void JSMessagePortPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSMessagePort::info(), JSMessagePortPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSMessagePort::s_info = { "MessagePort"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMessagePort) }; + +JSMessagePort::JSMessagePort(Structure* structure, JSDOMGlobalObject& globalObject, Ref<MessagePort>&& impl) + : JSEventTarget(structure, globalObject, WTFMove(impl)) +{ +} + +void JSMessagePort::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); + + static_assert(std::is_base_of<ActiveDOMObject, MessagePort>::value, "Interface is marked as [ActiveDOMObject] but implementation class does not subclass ActiveDOMObject."); + +} + +JSObject* JSMessagePort::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return JSMessagePortPrototype::create(vm, &globalObject, JSMessagePortPrototype::createStructure(vm, &globalObject, JSEventTarget::prototype(vm, globalObject))); +} + +JSObject* JSMessagePort::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype<JSMessagePort>(vm, globalObject); +} + +JSValue JSMessagePort::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor<JSMessagePortDOMConstructor, DOMConstructorID::MessagePort>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject)); +} + +JSC_DEFINE_CUSTOM_GETTER(jsMessagePortConstructor, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + VM& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast<JSMessagePortPrototype*>(vm, JSValue::decode(thisValue)); + if (UNLIKELY(!prototype)) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSMessagePort::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject())); +} + +static inline JSValue jsMessagePort_onmessageGetter(JSGlobalObject& lexicalGlobalObject, JSMessagePort& thisObject) +{ + UNUSED_PARAM(lexicalGlobalObject); + return eventHandlerAttribute(thisObject.wrapped(), eventNames().messageEvent, worldForDOMObject(thisObject)); +} + +JSC_DEFINE_CUSTOM_GETTER(jsMessagePort_onmessage, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSMessagePort>::get<jsMessagePort_onmessageGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline bool setJSMessagePort_onmessageSetter(JSGlobalObject& lexicalGlobalObject, JSMessagePort& thisObject, JSValue value) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().messageEvent, value, thisObject); + vm.writeBarrier(&thisObject, value); + ensureStillAliveHere(value); + + return true; +} + +JSC_DEFINE_CUSTOM_SETTER(setJSMessagePort_onmessage, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) +{ + return IDLAttribute<JSMessagePort>::set<setJSMessagePort_onmessageSetter>(*lexicalGlobalObject, thisValue, encodedValue, attributeName); +} + +static inline JSC::EncodedJSValue jsMessagePortPrototypeFunction_postMessage1Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSMessagePort>::ClassParameter castedThis) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(throwScope); + UNUSED_PARAM(callFrame); + auto& impl = castedThis->wrapped(); + EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); + auto message = convert<IDLAny>(*lexicalGlobalObject, argument0.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); + auto transfer = convert<IDLSequence<IDLObject>>(*lexicalGlobalObject, argument1.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.postMessage(*jsCast<JSDOMGlobalObject*>(lexicalGlobalObject), WTFMove(message), WTFMove(transfer)); }))); +} + +static inline JSC::EncodedJSValue jsMessagePortPrototypeFunction_postMessage2Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSMessagePort>::ClassParameter castedThis) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(throwScope); + UNUSED_PARAM(callFrame); + auto& impl = castedThis->wrapped(); + EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); + auto message = convert<IDLAny>(*lexicalGlobalObject, argument0.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + EnsureStillAliveScope argument1 = callFrame->argument(1); + auto options = convert<IDLDictionary<StructuredSerializeOptions>>(*lexicalGlobalObject, argument1.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.postMessage(*jsCast<JSDOMGlobalObject*>(lexicalGlobalObject), WTFMove(message), WTFMove(options)); }))); +} + +static inline JSC::EncodedJSValue jsMessagePortPrototypeFunction_postMessageOverloadDispatcher(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSMessagePort>::ClassParameter castedThis) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(throwScope); + UNUSED_PARAM(callFrame); + size_t argsCount = std::min<size_t>(2, callFrame->argumentCount()); + if (argsCount == 1) { + RELEASE_AND_RETURN(throwScope, (jsMessagePortPrototypeFunction_postMessage2Body(lexicalGlobalObject, callFrame, castedThis))); + } + if (argsCount == 2) { + JSValue distinguishingArg = callFrame->uncheckedArgument(1); + if (distinguishingArg.isUndefined()) + RELEASE_AND_RETURN(throwScope, (jsMessagePortPrototypeFunction_postMessage2Body(lexicalGlobalObject, callFrame, castedThis))); + if (distinguishingArg.isUndefinedOrNull()) + RELEASE_AND_RETURN(throwScope, (jsMessagePortPrototypeFunction_postMessage2Body(lexicalGlobalObject, callFrame, castedThis))); + { + bool success = hasIteratorMethod(lexicalGlobalObject, distinguishingArg); + RETURN_IF_EXCEPTION(throwScope, { }); + if (success) + RELEASE_AND_RETURN(throwScope, (jsMessagePortPrototypeFunction_postMessage1Body(lexicalGlobalObject, callFrame, castedThis))); + } + if (distinguishingArg.isObject()) + RELEASE_AND_RETURN(throwScope, (jsMessagePortPrototypeFunction_postMessage2Body(lexicalGlobalObject, callFrame, castedThis))); + } + return argsCount < 1 ? throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)) : throwVMTypeError(lexicalGlobalObject, throwScope); +} + +JSC_DEFINE_HOST_FUNCTION(jsMessagePortPrototypeFunction_postMessage, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSMessagePort>::call<jsMessagePortPrototypeFunction_postMessageOverloadDispatcher>(*lexicalGlobalObject, *callFrame, "postMessage"); +} + +static inline JSC::EncodedJSValue jsMessagePortPrototypeFunction_startBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSMessagePort>::ClassParameter castedThis) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(throwScope); + UNUSED_PARAM(callFrame); + auto& impl = castedThis->wrapped(); + RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.start(); }))); +} + +JSC_DEFINE_HOST_FUNCTION(jsMessagePortPrototypeFunction_start, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSMessagePort>::call<jsMessagePortPrototypeFunction_startBody>(*lexicalGlobalObject, *callFrame, "start"); +} + +static inline JSC::EncodedJSValue jsMessagePortPrototypeFunction_closeBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSMessagePort>::ClassParameter castedThis) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(throwScope); + UNUSED_PARAM(callFrame); + auto& impl = castedThis->wrapped(); + RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.close(); }))); +} + +JSC_DEFINE_HOST_FUNCTION(jsMessagePortPrototypeFunction_close, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSMessagePort>::call<jsMessagePortPrototypeFunction_closeBody>(*lexicalGlobalObject, *callFrame, "close"); +} + +JSC::GCClient::IsoSubspace* JSMessagePort::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl<JSMessagePort, UseCustomHeapCellType::No>(vm, + [] (auto& spaces) { return spaces.m_clientSubspaceForMessagePort.get(); }, + [] (auto& spaces, auto&& space) { spaces.m_clientSubspaceForMessagePort = WTFMove(space); }, + [] (auto& spaces) { return spaces.m_subspaceForMessagePort.get(); }, + [] (auto& spaces, auto&& space) { spaces.m_subspaceForMessagePort = WTFMove(space); } + ); +} + +template<typename Visitor> +void JSMessagePort::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<JSMessagePort*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + thisObject->visitAdditionalChildren(visitor); +} + +DEFINE_VISIT_CHILDREN(JSMessagePort); + +template<typename Visitor> +void JSMessagePort::visitOutputConstraints(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<JSMessagePort*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitOutputConstraints(thisObject, visitor); + thisObject->visitAdditionalChildren(visitor); +} + +template void JSMessagePort::visitOutputConstraints(JSCell*, AbstractSlotVisitor&); +template void JSMessagePort::visitOutputConstraints(JSCell*, SlotVisitor&); +void JSMessagePort::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast<JSMessagePort*>(cell); + analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); + if (thisObject->scriptExecutionContext()) + analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + Base::analyzeHeap(cell, analyzer); +} + +bool JSMessagePortOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, AbstractSlotVisitor& visitor, const char** reason) +{ + auto* jsMessagePort = jsCast<JSMessagePort*>(handle.slot()->asCell()); + auto& wrapped = jsMessagePort->wrapped(); + if (!wrapped.isContextStopped() && wrapped.hasPendingActivity()) { + if (UNLIKELY(reason)) + *reason = "ActiveDOMObject with pending activity"; + return true; + } + if (jsMessagePort->wrapped().isFiringEventListeners()) { + if (UNLIKELY(reason)) + *reason = "EventTarget firing event listeners"; + return true; + } + MessagePort* root = &jsMessagePort->wrapped(); + if (UNLIKELY(reason)) + *reason = "Reachable from MessagePort"; + return visitor.containsOpaqueRoot(root); +} + +void JSMessagePortOwner::finalize(JSC::Handle<JSC::Unknown> handle, void* context) +{ + auto* jsMessagePort = static_cast<JSMessagePort*>(handle.slot()->asCell()); + auto& world = *static_cast<DOMWrapperWorld*>(context); + uncacheWrapper(world, &jsMessagePort->wrapped(), jsMessagePort); +} + +#if ENABLE(BINDING_INTEGRITY) +#if PLATFORM(WIN) +#pragma warning(disable: 4483) +extern "C" { extern void (*const __identifier("??_7MessagePort@WebCore@@6B@")[])(); } +#else +extern "C" { extern void* _ZTVN7WebCore11MessagePortE[]; } +#endif +#endif + +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref<MessagePort>&& impl) +{ + + if constexpr (std::is_polymorphic_v<MessagePort>) { +#if ENABLE(BINDING_INTEGRITY) + const void* actualVTablePointer = getVTablePointer(impl.ptr()); +#if PLATFORM(WIN) + void* expectedVTablePointer = __identifier("??_7MessagePort@WebCore@@6B@"); +#else + void* expectedVTablePointer = &_ZTVN7WebCore11MessagePortE[2]; +#endif + + // If you hit this assertion you either have a use after free bug, or + // MessagePort has subclasses. If MessagePort has subclasses that get passed + // to toJS() we currently require MessagePort you to opt out of binding hardening + // by adding the SkipVTableValidation attribute to the interface IDL definition + RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); +#endif + } + return createWrapper<MessagePort>(globalObject, WTFMove(impl)); +} + +JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, MessagePort& impl) +{ + return wrap(lexicalGlobalObject, globalObject, impl); +} + +MessagePort* JSMessagePort::toWrapped(JSC::VM& vm, JSC::JSValue value) +{ + if (auto* wrapper = jsDynamicCast<JSMessagePort*>(vm, value)) + return &wrapper->wrapped(); + return nullptr; +} + +} diff --git a/src/javascript/jsc/bindings/webcore/JSMessagePort.dep b/src/javascript/jsc/bindings/webcore/JSMessagePort.dep new file mode 100644 index 000000000..b8540bfcb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSMessagePort.dep @@ -0,0 +1,2 @@ +JSMessagePort.h : EventTarget.idl +EventTarget.idl : diff --git a/src/javascript/jsc/bindings/webcore/JSMessagePort.h b/src/javascript/jsc/bindings/webcore/JSMessagePort.h new file mode 100644 index 000000000..50cce617c --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSMessagePort.h @@ -0,0 +1,102 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "JSDOMWrapper.h" +#include "JSEventTarget.h" +#include "MessagePort.h" +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +class JSMessagePort : public JSEventTarget { +public: + using Base = JSEventTarget; + using DOMWrapped = MessagePort; + static JSMessagePort* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<MessagePort>&& impl) + { + JSMessagePort* ptr = new (NotNull, JSC::allocateCell<JSMessagePort>(globalObject->vm())) JSMessagePort(structure, *globalObject, WTFMove(impl)); + ptr->finishCreation(globalObject->vm()); + return ptr; + } + + static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&); + static MessagePort* toWrapped(JSC::VM&, JSC::JSValue); + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray); + } + + static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*); + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + DECLARE_VISIT_CHILDREN; + template<typename Visitor> void visitAdditionalChildren(Visitor&); + + template<typename Visitor> static void visitOutputConstraints(JSCell*, Visitor&); + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + MessagePort& wrapped() const + { + return static_cast<MessagePort&>(Base::wrapped()); + } +protected: + JSMessagePort(JSC::Structure*, JSDOMGlobalObject&, Ref<MessagePort>&&); + + void finishCreation(JSC::VM&); +}; + +class JSMessagePortOwner final : public JSC::WeakHandleOwner { +public: + bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::AbstractSlotVisitor&, const char**) final; + void finalize(JSC::Handle<JSC::Unknown>, void* context) final; +}; + +inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, MessagePort*) +{ + static NeverDestroyed<JSMessagePortOwner> owner; + return &owner.get(); +} + +inline void* wrapperKey(MessagePort* wrappableObject) +{ + return wrappableObject; +} + +JSC::JSValue toJS(JSC::JSGlobalObject*, JSDOMGlobalObject*, MessagePort&); +inline JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, MessagePort* impl) { return impl ? toJS(lexicalGlobalObject, globalObject, *impl) : JSC::jsNull(); } +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, Ref<MessagePort>&&); +inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, RefPtr<MessagePort>&& impl) { return impl ? toJSNewlyCreated(lexicalGlobalObject, globalObject, impl.releaseNonNull()) : JSC::jsNull(); } + +template<> struct JSDOMWrapperConverterTraits<MessagePort> { + using WrapperClass = JSMessagePort; + using ToWrappedReturnType = MessagePort*; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSPromiseRejectionEvent.cpp b/src/javascript/jsc/bindings/webcore/JSPromiseRejectionEvent.cpp new file mode 100644 index 000000000..92bbb352d --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSPromiseRejectionEvent.cpp @@ -0,0 +1,377 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSPromiseRejectionEvent.h" + +#include "ActiveDOMObject.h" +#include "DOMPromiseProxy.h" +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMConstructor.h" +#include "JSDOMConvertAny.h" +#include "JSDOMConvertBoolean.h" +#include "JSDOMConvertInterface.h" +#include "JSDOMConvertPromise.h" +#include "JSDOMConvertStrings.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObject.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMWrapperCache.h" +#include "ScriptExecutionContext.h" +#include "WebCoreJSClientData.h" +#include <JavaScriptCore/HeapAnalyzer.h> +#include <JavaScriptCore/JSCInlines.h> +#include <JavaScriptCore/JSDestructibleObjectHeapCellType.h> +#include <JavaScriptCore/SlotVisitorMacros.h> +#include <JavaScriptCore/SubspaceInlines.h> +#include <wtf/GetPtr.h> +#include <wtf/PointerPreparations.h> +#include <wtf/URL.h> + + +namespace WebCore { +using namespace JSC; + +template<> PromiseRejectionEvent::Init convertDictionary<PromiseRejectionEvent::Init>(JSGlobalObject& lexicalGlobalObject, JSValue value) +{ + VM& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + bool isNullOrUndefined = value.isUndefinedOrNull(); + auto* object = isNullOrUndefined ? nullptr : value.getObject(); + if (UNLIKELY(!isNullOrUndefined && !object)) { + throwTypeError(&lexicalGlobalObject, throwScope); + return { }; + } + PromiseRejectionEvent::Init result; + JSValue bubblesValue; + if (isNullOrUndefined) + bubblesValue = jsUndefined(); + else { + bubblesValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "bubbles")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!bubblesValue.isUndefined()) { + result.bubbles = convert<IDLBoolean>(lexicalGlobalObject, bubblesValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.bubbles = false; + JSValue cancelableValue; + if (isNullOrUndefined) + cancelableValue = jsUndefined(); + else { + cancelableValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "cancelable")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!cancelableValue.isUndefined()) { + result.cancelable = convert<IDLBoolean>(lexicalGlobalObject, cancelableValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.cancelable = false; + JSValue composedValue; + if (isNullOrUndefined) + composedValue = jsUndefined(); + else { + composedValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "composed")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!composedValue.isUndefined()) { + result.composed = convert<IDLBoolean>(lexicalGlobalObject, composedValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.composed = false; + JSValue promiseValue; + if (isNullOrUndefined) + promiseValue = jsUndefined(); + else { + promiseValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "promise")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!promiseValue.isUndefined()) { + result.promise = convert<IDLPromise<IDLAny>>(lexicalGlobalObject, promiseValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else { + throwRequiredMemberTypeError(lexicalGlobalObject, throwScope, "promise", "PromiseRejectionEventInit", "Promise"); + return { }; + } + JSValue reasonValue; + if (isNullOrUndefined) + reasonValue = jsUndefined(); + else { + reasonValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "reason")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!reasonValue.isUndefined()) { + result.reason = convert<IDLAny>(lexicalGlobalObject, reasonValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.reason = jsUndefined(); + return result; +} + +// Attributes + +static JSC_DECLARE_CUSTOM_GETTER(jsPromiseRejectionEventConstructor); +static JSC_DECLARE_CUSTOM_GETTER(jsPromiseRejectionEvent_promise); +static JSC_DECLARE_CUSTOM_GETTER(jsPromiseRejectionEvent_reason); + +class JSPromiseRejectionEventPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSPromiseRejectionEventPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSPromiseRejectionEventPrototype* ptr = new (NotNull, JSC::allocateCell<JSPromiseRejectionEventPrototype>(vm)) JSPromiseRejectionEventPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSPromiseRejectionEventPrototype, Base); + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSPromiseRejectionEventPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSPromiseRejectionEventPrototype, JSPromiseRejectionEventPrototype::Base); + +using JSPromiseRejectionEventDOMConstructor = JSDOMConstructor<JSPromiseRejectionEvent>; + +template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSPromiseRejectionEventDOMConstructor::construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame) +{ + VM& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* castedThis = jsCast<JSPromiseRejectionEventDOMConstructor*>(callFrame->jsCallee()); + ASSERT(castedThis); + if (UNLIKELY(callFrame->argumentCount() < 2)) + return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); + EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); + auto type = convert<IDLDOMString>(*lexicalGlobalObject, argument0.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); + auto eventInitDict = convert<IDLDictionary<PromiseRejectionEvent::Init>>(*lexicalGlobalObject, argument1.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + auto object = PromiseRejectionEvent::create(WTFMove(type), WTFMove(eventInitDict)); + if constexpr (IsExceptionOr<decltype(object)>) + RETURN_IF_EXCEPTION(throwScope, { }); + static_assert(TypeOrExceptionOrUnderlyingType<decltype(object)>::isRef); + auto jsValue = toJSNewlyCreated<IDLInterface<PromiseRejectionEvent>>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, WTFMove(object)); + if constexpr (IsExceptionOr<decltype(object)>) + RETURN_IF_EXCEPTION(throwScope, { }); + setSubclassStructureIfNeeded<PromiseRejectionEvent>(lexicalGlobalObject, callFrame, asObject(jsValue)); + RETURN_IF_EXCEPTION(throwScope, { }); + return JSValue::encode(jsValue); +} +JSC_ANNOTATE_HOST_FUNCTION(JSPromiseRejectionEventDOMConstructorConstruct, JSPromiseRejectionEventDOMConstructor::construct); + +template<> const ClassInfo JSPromiseRejectionEventDOMConstructor::s_info = { "PromiseRejectionEvent"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSPromiseRejectionEventDOMConstructor) }; + +template<> JSValue JSPromiseRejectionEventDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + return JSEvent::getConstructor(vm, &globalObject); +} + +template<> void JSPromiseRejectionEventDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(2), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "PromiseRejectionEvent"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSPromiseRejectionEvent::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +/* Hash table for prototype */ + +static const HashTableValue JSPromiseRejectionEventPrototypeTableValues[] = +{ + { "constructor", static_cast<unsigned>(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsPromiseRejectionEventConstructor), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "promise", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsPromiseRejectionEvent_promise), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "reason", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsPromiseRejectionEvent_reason), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, +}; + +const ClassInfo JSPromiseRejectionEventPrototype::s_info = { "PromiseRejectionEvent"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSPromiseRejectionEventPrototype) }; + +void JSPromiseRejectionEventPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSPromiseRejectionEvent::info(), JSPromiseRejectionEventPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSPromiseRejectionEvent::s_info = { "PromiseRejectionEvent"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSPromiseRejectionEvent) }; + +JSPromiseRejectionEvent::JSPromiseRejectionEvent(Structure* structure, JSDOMGlobalObject& globalObject, Ref<PromiseRejectionEvent>&& impl) + : JSEvent(structure, globalObject, WTFMove(impl)) +{ +} + +void JSPromiseRejectionEvent::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); + + static_assert(!std::is_base_of<ActiveDOMObject, PromiseRejectionEvent>::value, "Interface is not marked as [ActiveDOMObject] even though implementation class subclasses ActiveDOMObject."); + +} + +JSObject* JSPromiseRejectionEvent::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return JSPromiseRejectionEventPrototype::create(vm, &globalObject, JSPromiseRejectionEventPrototype::createStructure(vm, &globalObject, JSEvent::prototype(vm, globalObject))); +} + +JSObject* JSPromiseRejectionEvent::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype<JSPromiseRejectionEvent>(vm, globalObject); +} + +JSValue JSPromiseRejectionEvent::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor<JSPromiseRejectionEventDOMConstructor, DOMConstructorID::PromiseRejectionEvent>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject)); +} + +JSC_DEFINE_CUSTOM_GETTER(jsPromiseRejectionEventConstructor, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + VM& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast<JSPromiseRejectionEventPrototype*>(vm, JSValue::decode(thisValue)); + if (UNLIKELY(!prototype)) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSPromiseRejectionEvent::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject())); +} + +static inline JSValue jsPromiseRejectionEvent_promiseGetter(JSGlobalObject& lexicalGlobalObject, JSPromiseRejectionEvent& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLPromise<IDLAny>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, [&]() -> decltype(auto) { return impl.promise(); }))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsPromiseRejectionEvent_promise, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSPromiseRejectionEvent>::get<jsPromiseRejectionEvent_promiseGetter, CastedThisErrorBehavior::RejectPromise>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsPromiseRejectionEvent_reasonGetter(JSGlobalObject& lexicalGlobalObject, JSPromiseRejectionEvent& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLAny>(lexicalGlobalObject, throwScope, impl.reason()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsPromiseRejectionEvent_reason, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSPromiseRejectionEvent>::get<jsPromiseRejectionEvent_reasonGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +JSC::GCClient::IsoSubspace* JSPromiseRejectionEvent::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl<JSPromiseRejectionEvent, UseCustomHeapCellType::No>(vm, + [] (auto& spaces) { return spaces.m_clientSubspaceForPromiseRejectionEvent.get(); }, + [] (auto& spaces, auto&& space) { spaces.m_clientSubspaceForPromiseRejectionEvent = WTFMove(space); }, + [] (auto& spaces) { return spaces.m_subspaceForPromiseRejectionEvent.get(); }, + [] (auto& spaces, auto&& space) { spaces.m_subspaceForPromiseRejectionEvent = WTFMove(space); } + ); +} + +template<typename Visitor> +void JSPromiseRejectionEvent::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<JSPromiseRejectionEvent*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + thisObject->visitAdditionalChildren(visitor); +} + +DEFINE_VISIT_CHILDREN(JSPromiseRejectionEvent); + +template<typename Visitor> +void JSPromiseRejectionEvent::visitOutputConstraints(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<JSPromiseRejectionEvent*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitOutputConstraints(thisObject, visitor); + thisObject->visitAdditionalChildren(visitor); +} + +template void JSPromiseRejectionEvent::visitOutputConstraints(JSCell*, AbstractSlotVisitor&); +template void JSPromiseRejectionEvent::visitOutputConstraints(JSCell*, SlotVisitor&); +void JSPromiseRejectionEvent::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast<JSPromiseRejectionEvent*>(cell); + analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); + if (thisObject->scriptExecutionContext()) + analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + Base::analyzeHeap(cell, analyzer); +} + +#if ENABLE(BINDING_INTEGRITY) +#if PLATFORM(WIN) +#pragma warning(disable: 4483) +extern "C" { extern void (*const __identifier("??_7PromiseRejectionEvent@WebCore@@6B@")[])(); } +#else +extern "C" { extern void* _ZTVN7WebCore21PromiseRejectionEventE[]; } +#endif +#endif + +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref<PromiseRejectionEvent>&& impl) +{ + + if constexpr (std::is_polymorphic_v<PromiseRejectionEvent>) { +#if ENABLE(BINDING_INTEGRITY) + const void* actualVTablePointer = getVTablePointer(impl.ptr()); +#if PLATFORM(WIN) + void* expectedVTablePointer = __identifier("??_7PromiseRejectionEvent@WebCore@@6B@"); +#else + void* expectedVTablePointer = &_ZTVN7WebCore21PromiseRejectionEventE[2]; +#endif + + // If you hit this assertion you either have a use after free bug, or + // PromiseRejectionEvent has subclasses. If PromiseRejectionEvent has subclasses that get passed + // to toJS() we currently require PromiseRejectionEvent you to opt out of binding hardening + // by adding the SkipVTableValidation attribute to the interface IDL definition + RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); +#endif + } + return createWrapper<PromiseRejectionEvent>(globalObject, WTFMove(impl)); +} + +JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, PromiseRejectionEvent& impl) +{ + return wrap(lexicalGlobalObject, globalObject, impl); +} + + +} diff --git a/src/javascript/jsc/bindings/webcore/JSPromiseRejectionEvent.dep b/src/javascript/jsc/bindings/webcore/JSPromiseRejectionEvent.dep new file mode 100644 index 000000000..b92e83044 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSPromiseRejectionEvent.dep @@ -0,0 +1,3 @@ +JSPromiseRejectionEvent.h : Event.idl EventInit.idl +Event.idl : +EventInit.idl : diff --git a/src/javascript/jsc/bindings/webcore/JSPromiseRejectionEvent.h b/src/javascript/jsc/bindings/webcore/JSPromiseRejectionEvent.h new file mode 100644 index 000000000..578a6992d --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSPromiseRejectionEvent.h @@ -0,0 +1,86 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "JSDOMConvertDictionary.h" +#include "JSDOMWrapper.h" +#include "JSEvent.h" +#include "PromiseRejectionEvent.h" + +namespace WebCore { + +class JSPromiseRejectionEvent : public JSEvent { +public: + using Base = JSEvent; + using DOMWrapped = PromiseRejectionEvent; + static JSPromiseRejectionEvent* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<PromiseRejectionEvent>&& impl) + { + JSPromiseRejectionEvent* ptr = new (NotNull, JSC::allocateCell<JSPromiseRejectionEvent>(globalObject->vm())) JSPromiseRejectionEvent(structure, *globalObject, WTFMove(impl)); + ptr->finishCreation(globalObject->vm()); + return ptr; + } + + static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&); + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType(JSEventType), StructureFlags), info(), JSC::NonArray); + } + + static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*); + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + DECLARE_VISIT_CHILDREN; + template<typename Visitor> void visitAdditionalChildren(Visitor&); + + template<typename Visitor> static void visitOutputConstraints(JSCell*, Visitor&); + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + PromiseRejectionEvent& wrapped() const + { + return static_cast<PromiseRejectionEvent&>(Base::wrapped()); + } +protected: + JSPromiseRejectionEvent(JSC::Structure*, JSDOMGlobalObject&, Ref<PromiseRejectionEvent>&&); + + void finishCreation(JSC::VM&); +}; + +JSC::JSValue toJS(JSC::JSGlobalObject*, JSDOMGlobalObject*, PromiseRejectionEvent&); +inline JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, PromiseRejectionEvent* impl) { return impl ? toJS(lexicalGlobalObject, globalObject, *impl) : JSC::jsNull(); } +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, Ref<PromiseRejectionEvent>&&); +inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, RefPtr<PromiseRejectionEvent>&& impl) { return impl ? toJSNewlyCreated(lexicalGlobalObject, globalObject, impl.releaseNonNull()) : JSC::jsNull(); } + +template<> struct JSDOMWrapperConverterTraits<PromiseRejectionEvent> { + using WrapperClass = JSPromiseRejectionEvent; + using ToWrappedReturnType = PromiseRejectionEvent*; +}; +template<> PromiseRejectionEvent::Init convertDictionary<PromiseRejectionEvent::Init>(JSC::JSGlobalObject&, JSC::JSValue); + + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSServiceWorker.h b/src/javascript/jsc/bindings/webcore/JSServiceWorker.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSServiceWorker.h diff --git a/src/javascript/jsc/bindings/webcore/JSStorageManager.h b/src/javascript/jsc/bindings/webcore/JSStorageManager.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSStorageManager.h diff --git a/src/javascript/jsc/bindings/webcore/JSStructuredSerializeOptions.cpp b/src/javascript/jsc/bindings/webcore/JSStructuredSerializeOptions.cpp new file mode 100644 index 000000000..e0aa01f86 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSStructuredSerializeOptions.cpp @@ -0,0 +1,59 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSStructuredSerializeOptions.h" + +#include "JSDOMConvertObject.h" +#include "JSDOMConvertSequences.h" +#include <JavaScriptCore/JSArray.h> +#include <JavaScriptCore/JSCInlines.h> + + +namespace WebCore { +using namespace JSC; + +template<> StructuredSerializeOptions convertDictionary<StructuredSerializeOptions>(JSGlobalObject& lexicalGlobalObject, JSValue value) +{ + VM& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + bool isNullOrUndefined = value.isUndefinedOrNull(); + auto* object = isNullOrUndefined ? nullptr : value.getObject(); + if (UNLIKELY(!isNullOrUndefined && !object)) { + throwTypeError(&lexicalGlobalObject, throwScope); + return { }; + } + StructuredSerializeOptions result; + JSValue transferValue; + if (isNullOrUndefined) + transferValue = jsUndefined(); + else { + transferValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "transfer")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!transferValue.isUndefined()) { + result.transfer = convert<IDLSequence<IDLObject>>(lexicalGlobalObject, transferValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.transfer = Converter<IDLSequence<IDLObject>>::ReturnType{ }; + return result; +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSStructuredSerializeOptions.dep b/src/javascript/jsc/bindings/webcore/JSStructuredSerializeOptions.dep new file mode 100644 index 000000000..d6a73bdae --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSStructuredSerializeOptions.dep @@ -0,0 +1 @@ +StructuredSerializeOptions.h : diff --git a/src/javascript/jsc/bindings/webcore/JSStructuredSerializeOptions.h b/src/javascript/jsc/bindings/webcore/JSStructuredSerializeOptions.h new file mode 100644 index 000000000..8fc8fddd0 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSStructuredSerializeOptions.h @@ -0,0 +1,30 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "JSDOMConvertDictionary.h" +#include "StructuredSerializeOptions.h" + +namespace WebCore { + +template<> StructuredSerializeOptions convertDictionary<StructuredSerializeOptions>(JSC::JSGlobalObject&, JSC::JSValue); + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSWebLockManager.h b/src/javascript/jsc/bindings/webcore/JSWebLockManager.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWebLockManager.h diff --git a/src/javascript/jsc/bindings/webcore/JSWindowProxy.h b/src/javascript/jsc/bindings/webcore/JSWindowProxy.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWindowProxy.h diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerLocation.cpp b/src/javascript/jsc/bindings/webcore/JSWorkerLocation.cpp new file mode 100644 index 000000000..a982e552d --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerLocation.cpp @@ -0,0 +1,402 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSWorkerLocation.h" + +#include "ActiveDOMObject.h" +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMConstructorNotConstructable.h" +#include "JSDOMConvertStrings.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMOperation.h" +#include "JSDOMWrapperCache.h" +#include "ScriptExecutionContext.h" +#include "WebCoreJSClientData.h" +#include <JavaScriptCore/FunctionPrototype.h> +#include <JavaScriptCore/HeapAnalyzer.h> +#include <JavaScriptCore/JSCInlines.h> +#include <JavaScriptCore/JSDestructibleObjectHeapCellType.h> +#include <JavaScriptCore/SlotVisitorMacros.h> +#include <JavaScriptCore/SubspaceInlines.h> +#include <wtf/GetPtr.h> +#include <wtf/PointerPreparations.h> +#include <wtf/URL.h> + + +namespace WebCore { +using namespace JSC; + +// Functions + +static JSC_DECLARE_HOST_FUNCTION(jsWorkerLocationPrototypeFunction_toString); + +// Attributes + +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerLocationConstructor); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerLocation_href); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerLocation_protocol); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerLocation_host); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerLocation_hostname); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerLocation_port); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerLocation_pathname); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerLocation_search); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerLocation_hash); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerLocation_origin); + +class JSWorkerLocationPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSWorkerLocationPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSWorkerLocationPrototype* ptr = new (NotNull, JSC::allocateCell<JSWorkerLocationPrototype>(vm)) JSWorkerLocationPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSWorkerLocationPrototype, Base); + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSWorkerLocationPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSWorkerLocationPrototype, JSWorkerLocationPrototype::Base); + +using JSWorkerLocationDOMConstructor = JSDOMConstructorNotConstructable<JSWorkerLocation>; + +template<> const ClassInfo JSWorkerLocationDOMConstructor::s_info = { "WorkerLocation"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWorkerLocationDOMConstructor) }; + +template<> JSValue JSWorkerLocationDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(vm); + return globalObject.functionPrototype(); +} + +template<> void JSWorkerLocationDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "WorkerLocation"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSWorkerLocation::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +/* Hash table for prototype */ + +static const HashTableValue JSWorkerLocationPrototypeTableValues[] = +{ + { "constructor", static_cast<unsigned>(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsWorkerLocationConstructor), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "href", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsWorkerLocation_href), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "protocol", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsWorkerLocation_protocol), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "host", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsWorkerLocation_host), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "hostname", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsWorkerLocation_hostname), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "port", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsWorkerLocation_port), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "pathname", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsWorkerLocation_pathname), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "search", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsWorkerLocation_search), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "hash", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsWorkerLocation_hash), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "origin", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsWorkerLocation_origin), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "toString", static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t)static_cast<RawNativeFunction>(jsWorkerLocationPrototypeFunction_toString), (intptr_t) (0) } }, +}; + +const ClassInfo JSWorkerLocationPrototype::s_info = { "WorkerLocation"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWorkerLocationPrototype) }; + +void JSWorkerLocationPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSWorkerLocation::info(), JSWorkerLocationPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSWorkerLocation::s_info = { "WorkerLocation"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWorkerLocation) }; + +JSWorkerLocation::JSWorkerLocation(Structure* structure, JSDOMGlobalObject& globalObject, Ref<WorkerLocation>&& impl) + : JSDOMWrapper<WorkerLocation>(structure, globalObject, WTFMove(impl)) +{ +} + +void JSWorkerLocation::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); + + static_assert(!std::is_base_of<ActiveDOMObject, WorkerLocation>::value, "Interface is not marked as [ActiveDOMObject] even though implementation class subclasses ActiveDOMObject."); + +} + +JSObject* JSWorkerLocation::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return JSWorkerLocationPrototype::create(vm, &globalObject, JSWorkerLocationPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype())); +} + +JSObject* JSWorkerLocation::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype<JSWorkerLocation>(vm, globalObject); +} + +JSValue JSWorkerLocation::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor<JSWorkerLocationDOMConstructor, DOMConstructorID::WorkerLocation>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject)); +} + +void JSWorkerLocation::destroy(JSC::JSCell* cell) +{ + JSWorkerLocation* thisObject = static_cast<JSWorkerLocation*>(cell); + thisObject->JSWorkerLocation::~JSWorkerLocation(); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerLocationConstructor, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + VM& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast<JSWorkerLocationPrototype*>(vm, JSValue::decode(thisValue)); + if (UNLIKELY(!prototype)) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSWorkerLocation::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject())); +} + +static inline JSValue jsWorkerLocation_hrefGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerLocation& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLUSVString>(lexicalGlobalObject, throwScope, impl.href()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerLocation_href, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerLocation>::get<jsWorkerLocation_hrefGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerLocation_protocolGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerLocation& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLUSVString>(lexicalGlobalObject, throwScope, impl.protocol()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerLocation_protocol, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerLocation>::get<jsWorkerLocation_protocolGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerLocation_hostGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerLocation& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLUSVString>(lexicalGlobalObject, throwScope, impl.host()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerLocation_host, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerLocation>::get<jsWorkerLocation_hostGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerLocation_hostnameGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerLocation& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLUSVString>(lexicalGlobalObject, throwScope, impl.hostname()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerLocation_hostname, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerLocation>::get<jsWorkerLocation_hostnameGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerLocation_portGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerLocation& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLUSVString>(lexicalGlobalObject, throwScope, impl.port()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerLocation_port, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerLocation>::get<jsWorkerLocation_portGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerLocation_pathnameGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerLocation& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLUSVString>(lexicalGlobalObject, throwScope, impl.pathname()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerLocation_pathname, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerLocation>::get<jsWorkerLocation_pathnameGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerLocation_searchGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerLocation& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLUSVString>(lexicalGlobalObject, throwScope, impl.search()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerLocation_search, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerLocation>::get<jsWorkerLocation_searchGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerLocation_hashGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerLocation& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLUSVString>(lexicalGlobalObject, throwScope, impl.hash()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerLocation_hash, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerLocation>::get<jsWorkerLocation_hashGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerLocation_originGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerLocation& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLUSVString>(lexicalGlobalObject, throwScope, impl.origin()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerLocation_origin, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerLocation>::get<jsWorkerLocation_originGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSC::EncodedJSValue jsWorkerLocationPrototypeFunction_toStringBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSWorkerLocation>::ClassParameter castedThis) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(throwScope); + UNUSED_PARAM(callFrame); + auto& impl = castedThis->wrapped(); + RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUSVString>(*lexicalGlobalObject, throwScope, impl.href()))); +} + +JSC_DEFINE_HOST_FUNCTION(jsWorkerLocationPrototypeFunction_toString, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSWorkerLocation>::call<jsWorkerLocationPrototypeFunction_toStringBody>(*lexicalGlobalObject, *callFrame, "toString"); +} + +JSC::GCClient::IsoSubspace* JSWorkerLocation::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl<JSWorkerLocation, UseCustomHeapCellType::No>(vm, + [] (auto& spaces) { return spaces.m_clientSubspaceForWorkerLocation.get(); }, + [] (auto& spaces, auto&& space) { spaces.m_clientSubspaceForWorkerLocation = WTFMove(space); }, + [] (auto& spaces) { return spaces.m_subspaceForWorkerLocation.get(); }, + [] (auto& spaces, auto&& space) { spaces.m_subspaceForWorkerLocation = WTFMove(space); } + ); +} + +void JSWorkerLocation::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast<JSWorkerLocation*>(cell); + analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); + if (thisObject->scriptExecutionContext()) + analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + Base::analyzeHeap(cell, analyzer); +} + +bool JSWorkerLocationOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, AbstractSlotVisitor& visitor, const char** reason) +{ + auto* jsWorkerLocation = jsCast<JSWorkerLocation*>(handle.slot()->asCell()); + WorkerLocation* root = &jsWorkerLocation->wrapped(); + if (UNLIKELY(reason)) + *reason = "Reachable from WorkerLocation"; + return visitor.containsOpaqueRoot(root); +} + +void JSWorkerLocationOwner::finalize(JSC::Handle<JSC::Unknown> handle, void* context) +{ + auto* jsWorkerLocation = static_cast<JSWorkerLocation*>(handle.slot()->asCell()); + auto& world = *static_cast<DOMWrapperWorld*>(context); + uncacheWrapper(world, &jsWorkerLocation->wrapped(), jsWorkerLocation); +} + +#if ENABLE(BINDING_INTEGRITY) +#if PLATFORM(WIN) +#pragma warning(disable: 4483) +extern "C" { extern void (*const __identifier("??_7WorkerLocation@WebCore@@6B@")[])(); } +#else +extern "C" { extern void* _ZTVN7WebCore14WorkerLocationE[]; } +#endif +#endif + +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref<WorkerLocation>&& impl) +{ + + if constexpr (std::is_polymorphic_v<WorkerLocation>) { +#if ENABLE(BINDING_INTEGRITY) + const void* actualVTablePointer = getVTablePointer(impl.ptr()); +#if PLATFORM(WIN) + void* expectedVTablePointer = __identifier("??_7WorkerLocation@WebCore@@6B@"); +#else + void* expectedVTablePointer = &_ZTVN7WebCore14WorkerLocationE[2]; +#endif + + // If you hit this assertion you either have a use after free bug, or + // WorkerLocation has subclasses. If WorkerLocation has subclasses that get passed + // to toJS() we currently require WorkerLocation you to opt out of binding hardening + // by adding the SkipVTableValidation attribute to the interface IDL definition + RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); +#endif + } + return createWrapper<WorkerLocation>(globalObject, WTFMove(impl)); +} + +JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, WorkerLocation& impl) +{ + return wrap(lexicalGlobalObject, globalObject, impl); +} + +WorkerLocation* JSWorkerLocation::toWrapped(JSC::VM& vm, JSC::JSValue value) +{ + if (auto* wrapper = jsDynamicCast<JSWorkerLocation*>(vm, value)) + return &wrapper->wrapped(); + return nullptr; +} + +} diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerLocation.dep b/src/javascript/jsc/bindings/webcore/JSWorkerLocation.dep new file mode 100644 index 000000000..3133412cb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerLocation.dep @@ -0,0 +1 @@ +JSWorkerLocation.h : diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerLocation.h b/src/javascript/jsc/bindings/webcore/JSWorkerLocation.h new file mode 100644 index 000000000..e124d44b3 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerLocation.h @@ -0,0 +1,93 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "JSDOMWrapper.h" +#include "WorkerLocation.h" +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +class JSWorkerLocation : public JSDOMWrapper<WorkerLocation> { +public: + using Base = JSDOMWrapper<WorkerLocation>; + static JSWorkerLocation* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<WorkerLocation>&& impl) + { + JSWorkerLocation* ptr = new (NotNull, JSC::allocateCell<JSWorkerLocation>(globalObject->vm())) JSWorkerLocation(structure, *globalObject, WTFMove(impl)); + ptr->finishCreation(globalObject->vm()); + return ptr; + } + + static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&); + static WorkerLocation* toWrapped(JSC::VM&, JSC::JSValue); + static void destroy(JSC::JSCell*); + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray); + } + + static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*); + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); +protected: + JSWorkerLocation(JSC::Structure*, JSDOMGlobalObject&, Ref<WorkerLocation>&&); + + void finishCreation(JSC::VM&); +}; + +class JSWorkerLocationOwner final : public JSC::WeakHandleOwner { +public: + bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::AbstractSlotVisitor&, const char**) final; + void finalize(JSC::Handle<JSC::Unknown>, void* context) final; +}; + +inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, WorkerLocation*) +{ + static NeverDestroyed<JSWorkerLocationOwner> owner; + return &owner.get(); +} + +inline void* wrapperKey(WorkerLocation* wrappableObject) +{ + return wrappableObject; +} + +JSC::JSValue toJS(JSC::JSGlobalObject*, JSDOMGlobalObject*, WorkerLocation&); +inline JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, WorkerLocation* impl) { return impl ? toJS(lexicalGlobalObject, globalObject, *impl) : JSC::jsNull(); } +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, Ref<WorkerLocation>&&); +inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, RefPtr<WorkerLocation>&& impl) { return impl ? toJSNewlyCreated(lexicalGlobalObject, globalObject, impl.releaseNonNull()) : JSC::jsNull(); } + +template<> struct JSDOMWrapperConverterTraits<WorkerLocation> { + using WrapperClass = JSWorkerLocation; + using ToWrappedReturnType = WorkerLocation*; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerNavigator+MediaCapabilities.cpp b/src/javascript/jsc/bindings/webcore/JSWorkerNavigator+MediaCapabilities.cpp new file mode 100644 index 000000000..10e46595f --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerNavigator+MediaCapabilities.cpp @@ -0,0 +1,5 @@ +/* + This file is generated to inform build scripts that JSWorkerNavigator+MediaCapabilities.h and + JSWorkerNavigator+MediaCapabilities.cpp were created for WorkerNavigator+MediaCapabilities.idl, and prevent the build + scripts from trying to regenerate JSWorkerNavigator+MediaCapabilities.h and JSWorkerNavigator+MediaCapabilities.cpp on every build. +*/ diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerNavigator+MediaCapabilities.h b/src/javascript/jsc/bindings/webcore/JSWorkerNavigator+MediaCapabilities.h new file mode 100644 index 000000000..10e46595f --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerNavigator+MediaCapabilities.h @@ -0,0 +1,5 @@ +/* + This file is generated to inform build scripts that JSWorkerNavigator+MediaCapabilities.h and + JSWorkerNavigator+MediaCapabilities.cpp were created for WorkerNavigator+MediaCapabilities.idl, and prevent the build + scripts from trying to regenerate JSWorkerNavigator+MediaCapabilities.h and JSWorkerNavigator+MediaCapabilities.cpp on every build. +*/ diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerNavigator.cpp b/src/javascript/jsc/bindings/webcore/JSWorkerNavigator.cpp new file mode 100644 index 000000000..7e4e302e6 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerNavigator.cpp @@ -0,0 +1,569 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSWorkerNavigator.h" + +#include "ActiveDOMObject.h" +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMConstructorNotConstructable.h" +#include "JSDOMConvertBoolean.h" +#include "JSDOMConvertInterface.h" +#include "JSDOMConvertNumbers.h" +#include "JSDOMConvertSequences.h" +#include "JSDOMConvertStrings.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObject.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMWrapperCache.h" +#include "JSGPU.h" +#include "JSMediaCapabilities.h" +#include "JSStorageManager.h" +#include "JSWebLockManager.h" +#include "ScriptExecutionContext.h" +#include "WebCoreJSClientData.h" +// #include "WorkerNavigatorMediaCapabilities.h" +#include <JavaScriptCore/FunctionPrototype.h> +#include <JavaScriptCore/HeapAnalyzer.h> +#include <JavaScriptCore/JSArray.h> +#include <JavaScriptCore/JSCInlines.h> +#include <JavaScriptCore/JSDestructibleObjectHeapCellType.h> +#include <JavaScriptCore/SlotVisitorMacros.h> +#include <JavaScriptCore/SubspaceInlines.h> +#include <wtf/GetPtr.h> +#include <wtf/PointerPreparations.h> +#include <wtf/URL.h> + +#if ENABLE(SERVICE_WORKER) +#include "JSServiceWorkerContainer.h" +#endif + +namespace WebCore { +using namespace JSC; + +// Attributes + +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigatorConstructor); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_hardwareConcurrency); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_gpu); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_mediaCapabilities); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_locks); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_appCodeName); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_appName); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_appVersion); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_platform); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_product); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_userAgent); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_language); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_languages); +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_onLine); +#if ENABLE(SERVICE_WORKER) +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_serviceWorker); +#endif +static JSC_DECLARE_CUSTOM_GETTER(jsWorkerNavigator_storage); + +class JSWorkerNavigatorPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSWorkerNavigatorPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSWorkerNavigatorPrototype* ptr = new (NotNull, JSC::allocateCell<JSWorkerNavigatorPrototype>(vm)) JSWorkerNavigatorPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSWorkerNavigatorPrototype, Base); + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSWorkerNavigatorPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSWorkerNavigatorPrototype, JSWorkerNavigatorPrototype::Base); + +using JSWorkerNavigatorDOMConstructor = JSDOMConstructorNotConstructable<JSWorkerNavigator>; + +template<> const ClassInfo JSWorkerNavigatorDOMConstructor::s_info = { "WorkerNavigator"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWorkerNavigatorDOMConstructor) }; + +template<> JSValue JSWorkerNavigatorDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(vm); + return globalObject.functionPrototype(); +} + +template<> void JSWorkerNavigatorDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "WorkerNavigator"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSWorkerNavigator::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +/* Hash table for prototype */ + +static const HashTableValue JSWorkerNavigatorPrototypeTableValues[] = { + { "constructor", static_cast<unsigned>(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigatorConstructor), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "hardwareConcurrency", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_hardwareConcurrency), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "gpu", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_gpu), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "mediaCapabilities", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_mediaCapabilities), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "locks", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_locks), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "appCodeName", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_appCodeName), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "appName", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_appName), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "appVersion", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_appVersion), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "platform", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_platform), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "product", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_product), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "userAgent", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_userAgent), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "language", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_language), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "languages", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_languages), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, + { "onLine", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_onLine), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, +#if ENABLE(SERVICE_WORKER) + { "serviceWorker", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_serviceWorker), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, +#else + { 0, 0, NoIntrinsic, { 0, 0 } }, +#endif + { "storage", static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsWorkerNavigator_storage), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } }, +}; + +const ClassInfo JSWorkerNavigatorPrototype::s_info = { "WorkerNavigator"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWorkerNavigatorPrototype) }; + +void JSWorkerNavigatorPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSWorkerNavigator::info(), JSWorkerNavigatorPrototypeTableValues, *this); + bool hasDisabledRuntimeProperties = false; + if (true) { + // if (!(jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->isSecureContext() && jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->settingsValues().webGPU)) { + hasDisabledRuntimeProperties = true; + auto propertyName = Identifier::fromString(vm, reinterpret_cast<const LChar*>("gpu"), strlen("gpu")); + VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable); + DeletePropertySlot slot; + JSObject::deleteProperty(this, globalObject(), propertyName, slot); + } + if (true) { + // if (!jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->settingsValues().mediaCapabilitiesEnabled) { + hasDisabledRuntimeProperties = true; + auto propertyName = Identifier::fromString(vm, reinterpret_cast<const LChar*>("mediaCapabilities"), strlen("mediaCapabilities")); + VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable); + DeletePropertySlot slot; + JSObject::deleteProperty(this, globalObject(), propertyName, slot); + } + if (true) { + // if (!(jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->isSecureContext() && jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->settingsValues().webLocksAPIEnabled)) { + hasDisabledRuntimeProperties = true; + auto propertyName = Identifier::fromString(vm, reinterpret_cast<const LChar*>("locks"), strlen("locks")); + VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable); + DeletePropertySlot slot; + JSObject::deleteProperty(this, globalObject(), propertyName, slot); + } +#if ENABLE(SERVICE_WORKER) + if (!(jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->isSecureContext() && jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->settingsValues().serviceWorkersEnabled)) { + hasDisabledRuntimeProperties = true; + auto propertyName = Identifier::fromString(vm, reinterpret_cast<const LChar*>("serviceWorker"), strlen("serviceWorker")); + VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable); + DeletePropertySlot slot; + JSObject::deleteProperty(this, globalObject(), propertyName, slot); + } +#endif + if (true) { + // if (!(jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->isSecureContext() && jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->settingsValues().storageAPIEnabled)) { + hasDisabledRuntimeProperties = true; + auto propertyName = Identifier::fromString(vm, reinterpret_cast<const LChar*>("storage"), strlen("storage")); + VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable); + DeletePropertySlot slot; + JSObject::deleteProperty(this, globalObject(), propertyName, slot); + } + if (hasDisabledRuntimeProperties && structure()->isDictionary()) + flattenDictionaryObject(vm); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSWorkerNavigator::s_info = { "WorkerNavigator"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWorkerNavigator) }; + +JSWorkerNavigator::JSWorkerNavigator(Structure* structure, JSDOMGlobalObject& globalObject, Ref<WorkerNavigator>&& impl) + : JSDOMWrapper<WorkerNavigator>(structure, globalObject, WTFMove(impl)) +{ +} + +void JSWorkerNavigator::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); + + static_assert(!std::is_base_of<ActiveDOMObject, WorkerNavigator>::value, "Interface is not marked as [ActiveDOMObject] even though implementation class subclasses ActiveDOMObject."); +} + +JSObject* JSWorkerNavigator::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return JSWorkerNavigatorPrototype::create(vm, &globalObject, JSWorkerNavigatorPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype())); +} + +JSObject* JSWorkerNavigator::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype<JSWorkerNavigator>(vm, globalObject); +} + +JSValue JSWorkerNavigator::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor<JSWorkerNavigatorDOMConstructor, DOMConstructorID::WorkerNavigator>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject)); +} + +void JSWorkerNavigator::destroy(JSC::JSCell* cell) +{ + JSWorkerNavigator* thisObject = static_cast<JSWorkerNavigator*>(cell); + thisObject->JSWorkerNavigator::~JSWorkerNavigator(); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigatorConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + VM& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast<JSWorkerNavigatorPrototype*>(vm, JSValue::decode(thisValue)); + if (UNLIKELY(!prototype)) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSWorkerNavigator::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject())); +} + +static inline JSValue jsWorkerNavigator_hardwareConcurrencyGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLUnsignedLongLong>(lexicalGlobalObject, throwScope, impl.hardwareConcurrency()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_hardwareConcurrency, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_hardwareConcurrencyGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +// static inline JSValue jsWorkerNavigator_gpuGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +// { +// auto& vm = JSC::getVM(&lexicalGlobalObject); +// auto throwScope = DECLARE_THROW_SCOPE(vm); +// auto& impl = thisObject.wrapped(); +// RELEASE_AND_RETURN(throwScope, (toJS<IDLInterface<GPU>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, impl.gpu()))); +// } + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_gpu, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + JSC_NOT_IMPLEMENTED_GETTER_BODY; + // return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_gpuGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +// static inline JSValue jsWorkerNavigator_mediaCapabilitiesGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +// { +// // auto& vm = JSC::getVM(&lexicalGlobalObject); +// // auto throwScope = DECLARE_THROW_SCOPE(vm); +// // auto& impl = thisObject.wrapped(); +// // RELEASE_AND_RETURN(throwScope, (toJS<IDLInterface<MediaCapabilities>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, WebCore::WorkerNavigatorMediaCapabilities::mediaCapabilities(impl)))); +// } + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_mediaCapabilities, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + JSC_NOT_IMPLEMENTED_GETTER_BODY; + // return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_mediaCapabilitiesGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +// static inline JSValue jsWorkerNavigator_locksGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +// { +// auto& vm = JSC::getVM(&lexicalGlobalObject); +// auto throwScope = DECLARE_THROW_SCOPE(vm); +// auto& impl = thisObject.wrapped(); +// RELEASE_AND_RETURN(throwScope, (toJS<IDLInterface<WebLockManager>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, impl.locks()))); +// } + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_locks, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + JSC_NOT_IMPLEMENTED_GETTER_BODY; + // return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_locksGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerNavigator_appCodeNameGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLDOMString>(lexicalGlobalObject, throwScope, impl.appCodeName()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_appCodeName, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_appCodeNameGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerNavigator_appNameGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLDOMString>(lexicalGlobalObject, throwScope, impl.appName()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_appName, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_appNameGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerNavigator_appVersionGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLDOMString>(lexicalGlobalObject, throwScope, impl.appVersion()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_appVersion, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_appVersionGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerNavigator_platformGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLDOMString>(lexicalGlobalObject, throwScope, impl.platform()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_platform, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_platformGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerNavigator_productGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLDOMString>(lexicalGlobalObject, throwScope, impl.product()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_product, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_productGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerNavigator_userAgentGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLDOMString>(lexicalGlobalObject, throwScope, impl.userAgent()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_userAgent, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_userAgentGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerNavigator_languageGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLDOMString>(lexicalGlobalObject, throwScope, impl.language()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_language, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_languageGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerNavigator_languagesGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLFrozenArray<IDLDOMString>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, impl.languages()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_languages, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_languagesGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsWorkerNavigator_onLineGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLBoolean>(lexicalGlobalObject, throwScope, impl.onLine()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_onLine, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_onLineGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +#if ENABLE(SERVICE_WORKER) +static inline JSValue jsWorkerNavigator_serviceWorkerGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* context = jsCast<JSDOMGlobalObject*>(&lexicalGlobalObject)->scriptExecutionContext(); + if (UNLIKELY(!context)) + return jsUndefined(); + auto& impl = thisObject.wrapped(); + RELEASE_AND_RETURN(throwScope, (toJS<IDLInterface<ServiceWorkerContainer>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, impl.serviceWorker(*context)))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_serviceWorker, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_serviceWorkerGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +#endif + +// static inline JSValue jsWorkerNavigator_storageGetter(JSGlobalObject& lexicalGlobalObject, JSWorkerNavigator& thisObject) +// { +// auto& vm = JSC::getVM(&lexicalGlobalObject); +// auto throwScope = DECLARE_THROW_SCOPE(vm); +// auto& impl = thisObject.wrapped(); +// RELEASE_AND_RETURN(throwScope, (toJS<IDLInterface<StorageManager>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, impl.storage()))); +// } + +JSC_DEFINE_CUSTOM_GETTER(jsWorkerNavigator_storage, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + JSC_NOT_IMPLEMENTED_GETTER_BODY; + // return IDLAttribute<JSWorkerNavigator>::get<jsWorkerNavigator_storageGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +JSC::GCClient::IsoSubspace* JSWorkerNavigator::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl<JSWorkerNavigator, UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForWorkerNavigator.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForWorkerNavigator = WTFMove(space); }, + [](auto& spaces) { return spaces.m_subspaceForWorkerNavigator.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForWorkerNavigator = WTFMove(space); }); +} + +template<typename Visitor> +void JSWorkerNavigator::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<JSWorkerNavigator*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + thisObject->visitAdditionalChildren(visitor); +} + +DEFINE_VISIT_CHILDREN(JSWorkerNavigator); + +template<typename Visitor> +void JSWorkerNavigator::visitOutputConstraints(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<JSWorkerNavigator*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitOutputConstraints(thisObject, visitor); + thisObject->visitAdditionalChildren(visitor); +} + +template void JSWorkerNavigator::visitOutputConstraints(JSCell*, AbstractSlotVisitor&); +template void JSWorkerNavigator::visitOutputConstraints(JSCell*, SlotVisitor&); +void JSWorkerNavigator::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast<JSWorkerNavigator*>(cell); + analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); + if (thisObject->scriptExecutionContext()) + analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + Base::analyzeHeap(cell, analyzer); +} + +bool JSWorkerNavigatorOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, AbstractSlotVisitor& visitor, const char** reason) +{ + auto* jsWorkerNavigator = jsCast<JSWorkerNavigator*>(handle.slot()->asCell()); + WorkerNavigator* root = &jsWorkerNavigator->wrapped(); + if (UNLIKELY(reason)) + *reason = "Reachable from WorkerNavigator"; + return visitor.containsOpaqueRoot(root); +} + +void JSWorkerNavigatorOwner::finalize(JSC::Handle<JSC::Unknown> handle, void* context) +{ + auto* jsWorkerNavigator = static_cast<JSWorkerNavigator*>(handle.slot()->asCell()); + auto& world = *static_cast<DOMWrapperWorld*>(context); + uncacheWrapper(world, &jsWorkerNavigator->wrapped(), jsWorkerNavigator); +} + +#if ENABLE(BINDING_INTEGRITY) +#if PLATFORM(WIN) +#pragma warning(disable : 4483) +extern "C" { +extern void (*const __identifier("??_7WorkerNavigator@WebCore@@6B@")[])(); +} +#else +extern "C" { +extern void* _ZTVN7WebCore15WorkerNavigatorE[]; +} +#endif +#endif + +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref<WorkerNavigator>&& impl) +{ + + if constexpr (std::is_polymorphic_v<WorkerNavigator>) { +#if ENABLE(BINDING_INTEGRITY) + const void* actualVTablePointer = getVTablePointer(impl.ptr()); +#if PLATFORM(WIN) + void* expectedVTablePointer = __identifier("??_7WorkerNavigator@WebCore@@6B@"); +#else + void* expectedVTablePointer = &_ZTVN7WebCore15WorkerNavigatorE[2]; +#endif + + // If you hit this assertion you either have a use after free bug, or + // WorkerNavigator has subclasses. If WorkerNavigator has subclasses that get passed + // to toJS() we currently require WorkerNavigator you to opt out of binding hardening + // by adding the SkipVTableValidation attribute to the interface IDL definition + RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); +#endif + } + return createWrapper<WorkerNavigator>(globalObject, WTFMove(impl)); +} + +JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, WorkerNavigator& impl) +{ + return wrap(lexicalGlobalObject, globalObject, impl); +} + +WorkerNavigator* JSWorkerNavigator::toWrapped(JSC::VM& vm, JSC::JSValue value) +{ + if (auto* wrapper = jsDynamicCast<JSWorkerNavigator*>(vm, value)) + return &wrapper->wrapped(); + return nullptr; +} + +} diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerNavigator.dep b/src/javascript/jsc/bindings/webcore/JSWorkerNavigator.dep new file mode 100644 index 000000000..2cac28c40 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerNavigator.dep @@ -0,0 +1 @@ +JSWorkerNavigator.h : diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerNavigator.h b/src/javascript/jsc/bindings/webcore/JSWorkerNavigator.h new file mode 100644 index 000000000..549f94510 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerNavigator.h @@ -0,0 +1,97 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "JSDOMWrapper.h" +#include "WorkerNavigator.h" +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +class JSWorkerNavigator : public JSDOMWrapper<WorkerNavigator> { +public: + using Base = JSDOMWrapper<WorkerNavigator>; + static JSWorkerNavigator* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<WorkerNavigator>&& impl) + { + JSWorkerNavigator* ptr = new (NotNull, JSC::allocateCell<JSWorkerNavigator>(globalObject->vm())) JSWorkerNavigator(structure, *globalObject, WTFMove(impl)); + ptr->finishCreation(globalObject->vm()); + return ptr; + } + + static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&); + static WorkerNavigator* toWrapped(JSC::VM&, JSC::JSValue); + static void destroy(JSC::JSCell*); + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray); + } + + static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*); + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + DECLARE_VISIT_CHILDREN; + template<typename Visitor> void visitAdditionalChildren(Visitor&); + + template<typename Visitor> static void visitOutputConstraints(JSCell*, Visitor&); + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); +protected: + JSWorkerNavigator(JSC::Structure*, JSDOMGlobalObject&, Ref<WorkerNavigator>&&); + + void finishCreation(JSC::VM&); +}; + +class JSWorkerNavigatorOwner final : public JSC::WeakHandleOwner { +public: + bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::AbstractSlotVisitor&, const char**) final; + void finalize(JSC::Handle<JSC::Unknown>, void* context) final; +}; + +inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, WorkerNavigator*) +{ + static NeverDestroyed<JSWorkerNavigatorOwner> owner; + return &owner.get(); +} + +inline void* wrapperKey(WorkerNavigator* wrappableObject) +{ + return wrappableObject; +} + +JSC::JSValue toJS(JSC::JSGlobalObject*, JSDOMGlobalObject*, WorkerNavigator&); +inline JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, WorkerNavigator* impl) { return impl ? toJS(lexicalGlobalObject, globalObject, *impl) : JSC::jsNull(); } +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, Ref<WorkerNavigator>&&); +inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, RefPtr<WorkerNavigator>&& impl) { return impl ? toJSNewlyCreated(lexicalGlobalObject, globalObject, impl.releaseNonNull()) : JSC::jsNull(); } + +template<> struct JSDOMWrapperConverterTraits<WorkerNavigator> { + using WrapperClass = JSWorkerNavigator; + using ToWrappedReturnType = WorkerNavigator*; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerOptions.cpp b/src/javascript/jsc/bindings/webcore/JSWorkerOptions.cpp new file mode 100644 index 000000000..235d72b2e --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerOptions.cpp @@ -0,0 +1,84 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSWorkerOptions.h" + +#include "JSDOMConvertEnumeration.h" +#include "JSDOMConvertStrings.h" +#include "JSFetchRequestCredentials.h" +#include "JSWorkerType.h" +#include <JavaScriptCore/JSCInlines.h> + + +namespace WebCore { +using namespace JSC; + +template<> WorkerOptions convertDictionary<WorkerOptions>(JSGlobalObject& lexicalGlobalObject, JSValue value) +{ + VM& vm = JSC::getVM(&lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + bool isNullOrUndefined = value.isUndefinedOrNull(); + auto* object = isNullOrUndefined ? nullptr : value.getObject(); + if (UNLIKELY(!isNullOrUndefined && !object)) { + throwTypeError(&lexicalGlobalObject, throwScope); + return { }; + } + WorkerOptions result; + JSValue credentialsValue; + if (isNullOrUndefined) + credentialsValue = jsUndefined(); + else { + credentialsValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "credentials")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!credentialsValue.isUndefined()) { + result.credentials = convert<IDLEnumeration<FetchRequestCredentials>>(lexicalGlobalObject, credentialsValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.credentials = FetchRequestCredentials::SameOrigin; + JSValue nameValue; + if (isNullOrUndefined) + nameValue = jsUndefined(); + else { + nameValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "name")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!nameValue.isUndefined()) { + result.name = convert<IDLDOMString>(lexicalGlobalObject, nameValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.name = emptyString(); + JSValue typeValue; + if (isNullOrUndefined) + typeValue = jsUndefined(); + else { + typeValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "type")); + RETURN_IF_EXCEPTION(throwScope, { }); + } + if (!typeValue.isUndefined()) { + result.type = convert<IDLEnumeration<WorkerType>>(lexicalGlobalObject, typeValue); + RETURN_IF_EXCEPTION(throwScope, { }); + } else + result.type = WorkerType::Classic; + return result; +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerOptions.dep b/src/javascript/jsc/bindings/webcore/JSWorkerOptions.dep new file mode 100644 index 000000000..631dbd4c7 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerOptions.dep @@ -0,0 +1 @@ +WorkerOptions.h : diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerOptions.h b/src/javascript/jsc/bindings/webcore/JSWorkerOptions.h new file mode 100644 index 000000000..afb5f3656 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerOptions.h @@ -0,0 +1,30 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "JSDOMConvertDictionary.h" +#include "WorkerOptions.h" + +namespace WebCore { + +template<> WorkerOptions convertDictionary<WorkerOptions>(JSC::JSGlobalObject&, JSC::JSValue); + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerType.cpp b/src/javascript/jsc/bindings/webcore/JSWorkerType.cpp new file mode 100644 index 000000000..3ef12ab34 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerType.cpp @@ -0,0 +1,64 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "JSWorkerType.h" + +#include <JavaScriptCore/JSCInlines.h> +#include <JavaScriptCore/JSString.h> +#include <wtf/NeverDestroyed.h> + + +namespace WebCore { +using namespace JSC; + +String convertEnumerationToString(WorkerType enumerationValue) +{ + static const NeverDestroyed<String> values[] = { + MAKE_STATIC_STRING_IMPL("classic"), + MAKE_STATIC_STRING_IMPL("module"), + }; + static_assert(static_cast<size_t>(WorkerType::Classic) == 0, "WorkerType::Classic is not 0 as expected"); + static_assert(static_cast<size_t>(WorkerType::Module) == 1, "WorkerType::Module is not 1 as expected"); + ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values)); + return values[static_cast<size_t>(enumerationValue)]; +} + +template<> JSString* convertEnumerationToJS(JSGlobalObject& lexicalGlobalObject, WorkerType enumerationValue) +{ + return jsStringWithCache(lexicalGlobalObject.vm(), convertEnumerationToString(enumerationValue)); +} + +template<> std::optional<WorkerType> parseEnumeration<WorkerType>(JSGlobalObject& lexicalGlobalObject, JSValue value) +{ + auto stringValue = value.toWTFString(&lexicalGlobalObject); + if (stringValue == "classic") + return WorkerType::Classic; + if (stringValue == "module") + return WorkerType::Module; + return std::nullopt; +} + +template<> const char* expectedEnumerationValues<WorkerType>() +{ + return "\"classic\", \"module\""; +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/JSWorkerType.h b/src/javascript/jsc/bindings/webcore/JSWorkerType.h new file mode 100644 index 000000000..e907f39c1 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/JSWorkerType.h @@ -0,0 +1,34 @@ +/* + This file is part of the WebKit open source project. + This file has been generated by generate-bindings.pl. DO NOT MODIFY! + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include "JSDOMConvertEnumeration.h" +#include "WorkerType.h" + +namespace WebCore { + +String convertEnumerationToString(WorkerType); +template<> JSC::JSString* convertEnumerationToJS(JSC::JSGlobalObject&, WorkerType); + +template<> std::optional<WorkerType> parseEnumeration<WorkerType>(JSC::JSGlobalObject&, JSC::JSValue); +template<> const char* expectedEnumerationValues<WorkerType>(); + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessageChannel.cpp b/src/javascript/jsc/bindings/webcore/MessageChannel.cpp new file mode 100644 index 000000000..7f7a12c3e --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessageChannel.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "MessageChannel.h" + +#include "MessagePort.h" +#include "MessagePortChannelProvider.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +static std::pair<Ref<MessagePort>, Ref<MessagePort>> generateMessagePorts(ScriptExecutionContext& context) +{ + MessagePortIdentifier id1 = { Process::identifier(), ObjectIdentifier<MessagePortIdentifier::PortIdentifierType>::generate() }; + MessagePortIdentifier id2 = { Process::identifier(), ObjectIdentifier<MessagePortIdentifier::PortIdentifierType>::generate() }; + + return { MessagePort::create(context, id1, id2), MessagePort::create(context, id2, id1) }; +} + +Ref<MessageChannel> MessageChannel::create(ScriptExecutionContext& context) +{ + return adoptRef(*new MessageChannel(context)); +} + +MessageChannel::MessageChannel(ScriptExecutionContext& context) + : m_ports(generateMessagePorts(context)) +{ + if (!context.activeDOMObjectsAreStopped()) { + ASSERT(!port1().closed()); + ASSERT(!port2().closed()); + MessagePortChannelProvider::fromContext(context).createNewMessagePortChannel(port1().identifier(), port2().identifier()); + } else { + ASSERT(port1().closed()); + ASSERT(port2().closed()); + } +} + +MessageChannel::~MessageChannel() = default; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessageChannel.h b/src/javascript/jsc/bindings/webcore/MessageChannel.h new file mode 100644 index 000000000..15317c37c --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessageChannel.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class MessagePort; +class ScriptExecutionContext; + +class MessageChannel : public RefCounted<MessageChannel> { +public: + static Ref<MessageChannel> create(ScriptExecutionContext&); + ~MessageChannel(); + + MessagePort& port1() const { return m_ports.first; } + MessagePort& port2() const { return m_ports.second; } + +private: + explicit MessageChannel(ScriptExecutionContext&); + + std::pair<Ref<MessagePort>, Ref<MessagePort>> m_ports; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessageChannel.idl b/src/javascript/jsc/bindings/webcore/MessageChannel.idl new file mode 100644 index 000000000..a02bef5a0 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessageChannel.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +[ + Exposed=(Window,Worker), + Conditional=CHANNEL_MESSAGING, + JSCustomMarkFunction, +] interface MessageChannel { + [CallWith=CurrentScriptExecutionContext] constructor(); + + readonly attribute MessagePort port1; + readonly attribute MessagePort port2; + +}; + diff --git a/src/javascript/jsc/bindings/webcore/MessageEvent.cpp b/src/javascript/jsc/bindings/webcore/MessageEvent.cpp new file mode 100644 index 000000000..6a3279b52 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessageEvent.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2007 Henry Mason (hmason@mac.com) + * Copyright (C) 2003-2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "MessageEvent.h" + +#include "Blob.h" +#include "EventNames.h" +#include <JavaScriptCore/JSCInlines.h> +#include <wtf/IsoMallocInlines.h> + +namespace WebCore { + +using namespace JSC; + +WTF_MAKE_ISO_ALLOCATED_IMPL(MessageEvent); + +MessageEvent::MessageEvent() = default; + +inline MessageEvent::MessageEvent(const AtomString& type, Init&& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) + , m_data(JSValueTag { }) + , m_origin(initializer.origin) + , m_lastEventId(initializer.lastEventId) + , m_source(WTFMove(initializer.source)) + , m_ports(WTFMove(initializer.ports)) + , m_jsData(initializer.data) +{ +} + +inline MessageEvent::MessageEvent(const AtomString& type, DataType&& data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&& source, Vector<RefPtr<MessagePort>>&& ports) + : Event(type, CanBubble::No, IsCancelable::No) + , m_data(WTFMove(data)) + , m_origin(origin) + , m_lastEventId(lastEventId) + , m_source(WTFMove(source)) + , m_ports(WTFMove(ports)) +{ +} + +Ref<MessageEvent> MessageEvent::create(const AtomString& type, DataType&& data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&& source, Vector<RefPtr<MessagePort>>&& ports) +{ + return adoptRef(*new MessageEvent(type, WTFMove(data), origin, lastEventId, WTFMove(source), WTFMove(ports))); +} + +Ref<MessageEvent> MessageEvent::create(DataType&& data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&& source, Vector<RefPtr<MessagePort>>&& ports) +{ + return create(eventNames().messageEvent, WTFMove(data), origin, lastEventId, WTFMove(source), WTFMove(ports)); +} + +Ref<MessageEvent> MessageEvent::createForBindings() +{ + return adoptRef(*new MessageEvent); +} + +Ref<MessageEvent> MessageEvent::create(const AtomString& type, Init&& initializer, IsTrusted isTrusted) +{ + return adoptRef(*new MessageEvent(type, WTFMove(initializer), isTrusted)); +} + +MessageEvent::~MessageEvent() = default; + +void MessageEvent::initMessageEvent(const AtomString& type, bool canBubble, bool cancelable, JSValue data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&& source, Vector<RefPtr<MessagePort>>&& ports) +{ + if (isBeingDispatched()) + return; + + initEvent(type, canBubble, cancelable); + + { + Locker { m_concurrentDataAccessLock }; + m_data = JSValueTag { }; + } + // FIXME: This code is wrong: we should emit a write-barrier. Otherwise, GC can collect it. + // https://bugs.webkit.org/show_bug.cgi?id=236353 + m_jsData.setWeakly(data); + m_cachedData.clear(); + m_origin = origin; + m_lastEventId = lastEventId; + m_source = WTFMove(source); + m_ports = WTFMove(ports); + m_cachedPorts.clear(); +} + +EventInterface MessageEvent::eventInterface() const +{ + return MessageEventInterfaceType; +} + +size_t MessageEvent::memoryCost() const +{ + Locker { m_concurrentDataAccessLock }; + return WTF::switchOn(m_data, [] (JSValueTag) -> size_t { + return 0; + }, [] (const Ref<SerializedScriptValue>& data) -> size_t { + return data->memoryCost(); + }, [] (const String& string) -> size_t { + return string.sizeInBytes(); + }, [] (const Ref<Blob>& blob) -> size_t { + return blob->size(); + }, [] (const Ref<ArrayBuffer>& buffer) -> size_t { + return buffer->byteLength(); + }); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessageEvent.h b/src/javascript/jsc/bindings/webcore/MessageEvent.h new file mode 100644 index 000000000..4f26eef8b --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessageEvent.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2007 Henry Mason (hmason@mac.com) + * Copyright (C) 2003-2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#include "Event.h" +#include "JSValueInWrappedObject.h" +#include "MessagePort.h" +#include "SerializedScriptValue.h" +#include "ServiceWorker.h" +#include "WindowProxy.h" +#include <variant> + +namespace WebCore { + +// class Blob; + +// #if ENABLE(SERVICE_WORKER) +// using MessageEventSource = std::variant<RefPtr<WindowProxy>, RefPtr<MessagePort>, RefPtr<ServiceWorker>>; +// #else +// using MessageEventSource = std::variant<RefPtr<WindowProxy>, RefPtr<MessagePort>>; +// #endif +using MessageEventSource = std::variant<RefPtr<MessagePort>>; + +class MessageEvent final : public Event { + WTF_MAKE_ISO_ALLOCATED(MessageEvent); + +public: + struct JSValueTag { + }; + using DataType = std::variant<JSValueTag, Ref<SerializedScriptValue>, String, /*Ref<Blob>,*/ Ref<ArrayBuffer>>; + static Ref<MessageEvent> create(const AtomString& type, DataType&&, const String& origin = {}, const String& lastEventId = {}, std::optional<MessageEventSource>&& = std::nullopt, Vector<RefPtr<MessagePort>>&& = {}); + static Ref<MessageEvent> create(DataType&&, const String& origin = {}, const String& lastEventId = {}, std::optional<MessageEventSource>&& = std::nullopt, Vector<RefPtr<MessagePort>>&& = {}); + static Ref<MessageEvent> createForBindings(); + + struct Init : EventInit { + JSC::JSValue data; + String origin; + String lastEventId; + std::optional<MessageEventSource> source; + Vector<RefPtr<MessagePort>> ports; + }; + static Ref<MessageEvent> create(const AtomString& type, Init&&, IsTrusted = IsTrusted::No); + + virtual ~MessageEvent(); + + void initMessageEvent(const AtomString& type, bool canBubble, bool cancelable, JSC::JSValue data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&&, Vector<RefPtr<MessagePort>>&&); + + const String& origin() const { return m_origin; } + const String& lastEventId() const { return m_lastEventId; } + const std::optional<MessageEventSource>& source() const { return m_source; } + const Vector<RefPtr<MessagePort>>& ports() const { return m_ports; } + + const DataType& data() const { return m_data; } + + JSValueInWrappedObject& jsData() { return m_jsData; } + JSValueInWrappedObject& cachedData() { return m_cachedData; } + JSValueInWrappedObject& cachedPorts() { return m_cachedPorts; } + + size_t memoryCost() const; + +private: + MessageEvent(); + MessageEvent(const AtomString& type, Init&&, IsTrusted); + MessageEvent(const AtomString& type, DataType&&, const String& origin, const String& lastEventId = {}, std::optional<MessageEventSource>&& = std::nullopt, Vector<RefPtr<MessagePort>>&& = {}); + + EventInterface eventInterface() const final; + + DataType m_data; + String m_origin; + String m_lastEventId; + std::optional<MessageEventSource> m_source; + Vector<RefPtr<MessagePort>> m_ports; + + JSValueInWrappedObject m_jsData; + JSValueInWrappedObject m_cachedData; + JSValueInWrappedObject m_cachedPorts; + + mutable Lock m_concurrentDataAccessLock; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessageEvent.idl b/src/javascript/jsc/bindings/webcore/MessageEvent.idl new file mode 100644 index 000000000..67e4e21bc --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessageEvent.idl @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2007 Henry Mason <hmason@mac.com> + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#if defined(ENABLE_SERVICE_WORKER) && ENABLE_SERVICE_WORKER +typedef (WindowProxy or MessagePort or ServiceWorker) MessageEventSource; +#else +typedef (WindowProxy or MessagePort) MessageEventSource; +#endif + +[ + Exposed=(Window,Worker,AudioWorklet), + JSCustomMarkFunction, + ReportExtraMemoryCost, +] interface MessageEvent : Event { + constructor(DOMString type, optional MessageEventInit eventInitDict); + + readonly attribute USVString origin; + readonly attribute DOMString lastEventId; + readonly attribute MessageEventSource? source; + [CustomGetter] readonly attribute any data; + [CustomGetter] readonly attribute FrozenArray<MessagePort> ports; + + undefined initMessageEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false, + optional any data = null, optional USVString originArg = "", optional DOMString lastEventId = "", optional MessageEventSource? source = null, + optional sequence<MessagePort> messagePorts = []); +}; + +dictionary MessageEventInit : EventInit { + any data = null; + USVString origin = ""; + DOMString lastEventId = ""; + MessageEventSource? source = null; + sequence<MessagePort> ports = []; +}; diff --git a/src/javascript/jsc/bindings/webcore/MessagePort.cpp b/src/javascript/jsc/bindings/webcore/MessagePort.cpp new file mode 100644 index 000000000..9251e8466 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessagePort.cpp @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "MessagePort.h" + +#include "Document.h" +#include "EventNames.h" +#include "Logging.h" +#include "MessageEvent.h" +#include "MessagePortChannelProvider.h" +#include "MessageWithMessagePorts.h" +#include "StructuredSerializeOptions.h" +#include "WorkerGlobalScope.h" +#include "WorkerThread.h" +#include <wtf/CompletionHandler.h> +#include <wtf/IsoMallocInlines.h> +#include <wtf/Lock.h> +#include <wtf/Scope.h> + +namespace WebCore { + +WTF_MAKE_ISO_ALLOCATED_IMPL(MessagePort); + +static Lock allMessagePortsLock; +static HashMap<MessagePortIdentifier, MessagePort*>& allMessagePorts() WTF_REQUIRES_LOCK(allMessagePortsLock) +{ + static NeverDestroyed<HashMap<MessagePortIdentifier, MessagePort*>> map; + return map; +} + +void MessagePort::ref() const +{ + ++m_refCount; +} + +void MessagePort::deref() const +{ + // This custom deref() function ensures that as long as the lock to allMessagePortsLock is taken, no MessagePort will be destroyed. + // This allows isExistingMessagePortLocallyReachable and notifyMessageAvailable to easily query the map and manipulate MessagePort instances. + + if (!--m_refCount) { + Locker locker { allMessagePortsLock }; + + if (m_refCount) + return; + + auto iterator = allMessagePorts().find(m_identifier); + if (iterator != allMessagePorts().end() && iterator->value == this) + allMessagePorts().remove(iterator); + + delete this; + } +} + +bool MessagePort::isExistingMessagePortLocallyReachable(const MessagePortIdentifier& identifier) +{ + Locker locker { allMessagePortsLock }; + auto* port = allMessagePorts().get(identifier); + return port && port->isLocallyReachable(); +} + +void MessagePort::notifyMessageAvailable(const MessagePortIdentifier& identifier) +{ + Locker locker { allMessagePortsLock }; + if (auto* port = allMessagePorts().get(identifier)) + port->messageAvailable(); + +} + +Ref<MessagePort> MessagePort::create(ScriptExecutionContext& scriptExecutionContext, const MessagePortIdentifier& local, const MessagePortIdentifier& remote) +{ + auto messagePort = adoptRef(*new MessagePort(scriptExecutionContext, local, remote)); + messagePort->suspendIfNeeded(); + return messagePort; +} + +MessagePort::MessagePort(ScriptExecutionContext& scriptExecutionContext, const MessagePortIdentifier& local, const MessagePortIdentifier& remote) + : ActiveDOMObject(&scriptExecutionContext) + , m_identifier(local) + , m_remoteIdentifier(remote) +{ + LOG(MessagePorts, "Created MessagePort %s (%p) in process %" PRIu64, m_identifier.logString().utf8().data(), this, Process::identifier().toUInt64()); + + Locker locker { allMessagePortsLock }; + allMessagePorts().set(m_identifier, this); + + // Make sure the WeakPtrFactory gets initialized eagerly on the thread the MessagePort gets constructed on for thread-safety reasons. + initializeWeakPtrFactory(); + + scriptExecutionContext.createdMessagePort(*this); + + // Don't need to call processMessageWithMessagePortsSoon() here, because the port will not be opened until start() is invoked. +} + +MessagePort::~MessagePort() +{ + LOG(MessagePorts, "Destroyed MessagePort %s (%p) in process %" PRIu64, m_identifier.logString().utf8().data(), this, Process::identifier().toUInt64()); + + ASSERT(allMessagePortsLock.isLocked()); + + if (m_entangled) + close(); + + if (auto* context = scriptExecutionContext()) + context->destroyedMessagePort(*this); +} + +void MessagePort::entangle() +{ + MessagePortChannelProvider::fromContext(*scriptExecutionContext()).entangleLocalPortInThisProcessToRemote(m_identifier, m_remoteIdentifier); +} + +ExceptionOr<void> MessagePort::postMessage(JSC::JSGlobalObject& state, JSC::JSValue messageValue, StructuredSerializeOptions&& options) +{ + LOG(MessagePorts, "Attempting to post message to port %s (to be received by port %s)", m_identifier.logString().utf8().data(), m_remoteIdentifier.logString().utf8().data()); + + registerLocalActivity(); + + Vector<RefPtr<MessagePort>> ports; + auto messageData = SerializedScriptValue::create(state, messageValue, WTFMove(options.transfer), ports); + if (messageData.hasException()) + return messageData.releaseException(); + + if (!isEntangled()) + return { }; + ASSERT(scriptExecutionContext()); + + Vector<TransferredMessagePort> transferredPorts; + // Make sure we aren't connected to any of the passed-in ports. + if (!ports.isEmpty()) { + for (auto& port : ports) { + if (port->identifier() == m_identifier || port->identifier() == m_remoteIdentifier) + return Exception { DataCloneError }; + } + + auto disentangleResult = MessagePort::disentanglePorts(WTFMove(ports)); + if (disentangleResult.hasException()) + return disentangleResult.releaseException(); + transferredPorts = disentangleResult.releaseReturnValue(); + } + + MessageWithMessagePorts message { messageData.releaseReturnValue(), WTFMove(transferredPorts) }; + + LOG(MessagePorts, "Actually posting message to port %s (to be received by port %s)", m_identifier.logString().utf8().data(), m_remoteIdentifier.logString().utf8().data()); + + MessagePortChannelProvider::fromContext(*scriptExecutionContext()).postMessageToRemote(WTFMove(message), m_remoteIdentifier); + return { }; +} + +TransferredMessagePort MessagePort::disentangle() +{ + ASSERT(m_entangled); + m_entangled = false; + + registerLocalActivity(); + + auto& context = *scriptExecutionContext(); + MessagePortChannelProvider::fromContext(context).messagePortDisentangled(m_identifier); + + // We can't receive any messages or generate any events after this, so remove ourselves from the list of active ports. + context.destroyedMessagePort(*this); + context.willDestroyActiveDOMObject(*this); + context.willDestroyDestructionObserver(*this); + + observeContext(nullptr); + + return { identifier(), remoteIdentifier() }; +} + +void MessagePort::registerLocalActivity() +{ + // Any time certain local operations happen, we dirty our own state to delay GC. + m_hasHadLocalActivitySinceLastCheck = true; + m_mightBeEligibleForGC = false; +} + +// Invoked to notify us that there are messages available for this port. +// This code may be called from another thread, and so should not call any non-threadsafe APIs (i.e. should not call into the entangled channel or access mutable variables). +void MessagePort::messageAvailable() +{ + // This MessagePort object might be disentangled because the port is being transferred, + // in which case we'll notify it that messages are available once a new end point is created. + auto* context = scriptExecutionContext(); + if (!context || context->activeDOMObjectsAreSuspended()) + return; + + context->processMessageWithMessagePortsSoon(); +} + +void MessagePort::start() +{ + // Do nothing if we've been cloned or closed. + if (!isEntangled()) + return; + + registerLocalActivity(); + + ASSERT(scriptExecutionContext()); + if (m_started) + return; + + m_started = true; + scriptExecutionContext()->processMessageWithMessagePortsSoon(); +} + +void MessagePort::close() +{ + m_mightBeEligibleForGC = true; + + if (m_closed) + return; + m_closed = true; + + ensureOnMainThread([identifier = m_identifier] { + MessagePortChannelProvider::singleton().messagePortClosed(identifier); + }); + + removeAllEventListeners(); +} + +void MessagePort::contextDestroyed() +{ + ASSERT(scriptExecutionContext()); + + close(); + ActiveDOMObject::contextDestroyed(); +} + +void MessagePort::dispatchMessages() +{ + // Messages for contexts that are not fully active get dispatched too, but JSAbstractEventListener::handleEvent() doesn't call handlers for these. + // The HTML5 spec specifies that any messages sent to a document that is not fully active should be dropped, so this behavior is OK. + ASSERT(started()); + + auto* context = scriptExecutionContext(); + if (!context || context->activeDOMObjectsAreSuspended() || !isEntangled()) + return; + + auto messagesTakenHandler = [this, weakThis = WeakPtr { *this }](Vector<MessageWithMessagePorts>&& messages, CompletionHandler<void()>&& completionCallback) mutable { + auto scopeExit = makeScopeExit(WTFMove(completionCallback)); + + if (!weakThis) + return; + + LOG(MessagePorts, "MessagePort %s (%p) dispatching %zu messages", m_identifier.logString().utf8().data(), this, messages.size()); + + auto* context = scriptExecutionContext(); + if (!context) + return; + + if (!messages.isEmpty()) + registerLocalActivity(); + + ASSERT(context->isContextThread()); + + bool contextIsWorker = is<WorkerGlobalScope>(*context); + for (auto& message : messages) { + // close() in Worker onmessage handler should prevent next message from dispatching. + if (contextIsWorker && downcast<WorkerGlobalScope>(*context).isClosing()) + return; + auto ports = MessagePort::entanglePorts(*context, WTFMove(message.transferredPorts)); + // Per specification, each MessagePort object has a task source called the port message queue. + queueTaskToDispatchEvent(*this, TaskSource::PostedMessageQueue, MessageEvent::create(message.message.releaseNonNull(), { }, { }, { }, WTFMove(ports))); + } + }; + + MessagePortChannelProvider::fromContext(*scriptExecutionContext()).takeAllMessagesForPort(m_identifier, WTFMove(messagesTakenHandler)); +} + +void MessagePort::dispatchEvent(Event& event) +{ + if (m_closed) + return; + + auto* context = scriptExecutionContext(); + if (is<WorkerGlobalScope>(*context) && downcast<WorkerGlobalScope>(*context).isClosing()) + return; + + EventTarget::dispatchEvent(event); +} + +void MessagePort::updateActivity(MessagePortChannelProvider::HasActivity hasActivity) +{ + bool hasHadLocalActivity = m_hasHadLocalActivitySinceLastCheck; + m_hasHadLocalActivitySinceLastCheck = false; + + if (hasActivity == MessagePortChannelProvider::HasActivity::No && !hasHadLocalActivity) + m_isRemoteEligibleForGC = true; + + if (hasActivity == MessagePortChannelProvider::HasActivity::Yes) + m_isRemoteEligibleForGC = false; + + m_isAskingRemoteAboutGC = false; +} + +bool MessagePort::virtualHasPendingActivity() const +{ + m_mightBeEligibleForGC = true; + + // If the ScriptExecutionContext has been shut down on this object close()'ed, we can GC. + auto* context = scriptExecutionContext(); + if (!context || m_closed) + return false; + + // If this object has been idle since the remote port declared itself elgibile for GC, we can GC. + if (!m_hasHadLocalActivitySinceLastCheck && m_isRemoteEligibleForGC) + return false; + + // If this MessagePort has no message event handler then the existence of remote activity cannot keep it alive. + if (!m_hasMessageEventListener) + return false; + + // If we're not in the middle of asking the remote port about collectability, do so now. + if (!m_isAskingRemoteAboutGC) { + RefPtr<WorkerOrWorkletThread> workerOrWorkletThread; + if (is<WorkerOrWorkletGlobalScope>(*context)) + workerOrWorkletThread = downcast<WorkerOrWorkletGlobalScope>(*context).workerOrWorkletThread(); + + callOnMainThread([remoteIdentifier = m_remoteIdentifier, weakThis = WeakPtr { *this }, workerOrWorkletThread = WTFMove(workerOrWorkletThread)]() mutable { + MessagePortChannelProvider::singleton().checkRemotePortForActivity(remoteIdentifier, [weakThis = WTFMove(weakThis), workerOrWorkletThread = WTFMove(workerOrWorkletThread)](auto hasActivity) mutable { + if (!workerOrWorkletThread) { + if (weakThis) + weakThis->updateActivity(hasActivity); + return; + } + + workerOrWorkletThread->runLoop().postTaskForMode([weakThis = WTFMove(weakThis), hasActivity](auto&) mutable { + if (weakThis) + weakThis->updateActivity(hasActivity); + }, WorkerRunLoop::defaultMode()); + }); + }); + m_isAskingRemoteAboutGC = true; + } + + // Since we need an answer from the remote object, we have to pretend we have pending activity for now. + return true; +} + +bool MessagePort::isLocallyReachable() const +{ + return !m_mightBeEligibleForGC; +} + +MessagePort* MessagePort::locallyEntangledPort() const +{ + // FIXME: As the header describes, this is an optional optimization. + // Even in the new async model we should be able to get it right. + return nullptr; +} + +ExceptionOr<Vector<TransferredMessagePort>> MessagePort::disentanglePorts(Vector<RefPtr<MessagePort>>&& ports) +{ + if (ports.isEmpty()) + return Vector<TransferredMessagePort> { }; + + // Walk the incoming array - if there are any duplicate ports, or null ports or cloned ports, throw an error (per section 8.3.3 of the HTML5 spec). + HashSet<MessagePort*> portSet; + for (auto& port : ports) { + if (!port || !port->m_entangled || !portSet.add(port.get()).isNewEntry) + return Exception { DataCloneError }; + } + + // Passed-in ports passed validity checks, so we can disentangle them. + return WTF::map(ports, [](auto& port) { + return port->disentangle(); + }); +} + +Vector<RefPtr<MessagePort>> MessagePort::entanglePorts(ScriptExecutionContext& context, Vector<TransferredMessagePort>&& transferredPorts) +{ + LOG(MessagePorts, "Entangling %zu transferred ports to ScriptExecutionContext %s (%p)", transferredPorts.size(), context.url().string().utf8().data(), &context); + + if (transferredPorts.isEmpty()) + return { }; + + return WTF::map(transferredPorts, [&](auto& port) -> RefPtr<MessagePort> { + return MessagePort::entangle(context, WTFMove(port)); + }); +} + +Ref<MessagePort> MessagePort::entangle(ScriptExecutionContext& context, TransferredMessagePort&& transferredPort) +{ + auto port = MessagePort::create(context, transferredPort.first, transferredPort.second); + port->entangle(); + return port; +} + +bool MessagePort::addEventListener(const AtomString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options) +{ + if (eventType == eventNames().messageEvent) { + if (listener->isAttribute()) + start(); + m_hasMessageEventListener = true; + registerLocalActivity(); + } + + return EventTargetWithInlineData::addEventListener(eventType, WTFMove(listener), options); +} + +bool MessagePort::removeEventListener(const AtomString& eventType, EventListener& listener, const EventListenerOptions& options) +{ + auto result = EventTargetWithInlineData::removeEventListener(eventType, listener, options); + + if (!hasEventListeners(eventNames().messageEvent)) + m_hasMessageEventListener = false; + + return result; +} + +const char* MessagePort::activeDOMObjectName() const +{ + return "MessagePort"; +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessagePort.h b/src/javascript/jsc/bindings/webcore/MessagePort.h new file mode 100644 index 000000000..3c6dc7f6c --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessagePort.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#include "ActiveDOMObject.h" +#include "EventTarget.h" +#include "ExceptionOr.h" +#include "MessagePortChannel.h" +#include "MessagePortIdentifier.h" +#include "MessageWithMessagePorts.h" +#include <wtf/WeakPtr.h> + +namespace JSC { +class CallFrame; +class JSObject; +class JSValue; +} + +namespace WebCore { + +class Frame; + +struct StructuredSerializeOptions; + +class MessagePort final : public ActiveDOMObject, public EventTargetWithInlineData { + WTF_MAKE_NONCOPYABLE(MessagePort); + WTF_MAKE_ISO_ALLOCATED(MessagePort); +public: + static Ref<MessagePort> create(ScriptExecutionContext&, const MessagePortIdentifier& local, const MessagePortIdentifier& remote); + virtual ~MessagePort(); + + ExceptionOr<void> postMessage(JSC::JSGlobalObject&, JSC::JSValue message, StructuredSerializeOptions&&); + + void start(); + void close(); + void entangle(); + + // Returns nullptr if the passed-in vector is empty. + static ExceptionOr<Vector<TransferredMessagePort>> disentanglePorts(Vector<RefPtr<MessagePort>>&&); + static Vector<RefPtr<MessagePort>> entanglePorts(ScriptExecutionContext&, Vector<TransferredMessagePort>&&); + + WEBCORE_EXPORT static bool isExistingMessagePortLocallyReachable(const MessagePortIdentifier&); + WEBCORE_EXPORT static void notifyMessageAvailable(const MessagePortIdentifier&); + + WEBCORE_EXPORT void messageAvailable(); + bool started() const { return m_started; } + bool closed() const { return m_closed; } + + void dispatchMessages(); + + // Returns null if there is no entangled port, or if the entangled port is run by a different thread. + // This is used solely to enable a GC optimization. Some platforms may not be able to determine ownership + // of the remote port (since it may live cross-process) - those platforms may always return null. + MessagePort* locallyEntangledPort() const; + + const MessagePortIdentifier& identifier() const { return m_identifier; } + const MessagePortIdentifier& remoteIdentifier() const { return m_remoteIdentifier; } + + WEBCORE_EXPORT void ref() const; + WEBCORE_EXPORT void deref() const; + + WEBCORE_EXPORT bool isLocallyReachable() const; + + // EventTargetWithInlineData. + EventTargetInterface eventTargetInterface() const final { return MessagePortEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); } + void refEventTarget() final { ref(); } + void derefEventTarget() final { deref(); } + + void dispatchEvent(Event&) final; + + TransferredMessagePort disentangle(); + static Ref<MessagePort> entangle(ScriptExecutionContext&, TransferredMessagePort&&); + +private: + explicit MessagePort(ScriptExecutionContext&, const MessagePortIdentifier& local, const MessagePortIdentifier& remote); + + bool addEventListener(const AtomString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) final; + bool removeEventListener(const AtomString& eventType, EventListener&, const EventListenerOptions&) final; + + // ActiveDOMObject + const char* activeDOMObjectName() const final; + void contextDestroyed() final; + void stop() final { close(); } + bool virtualHasPendingActivity() const final; + + void registerLocalActivity(); + + // A port starts out its life entangled, and remains entangled until it is closed or is cloned. + bool isEntangled() const { return !m_closed && m_entangled; } + + void updateActivity(MessagePortChannelProvider::HasActivity); + + bool m_started { false }; + bool m_closed { false }; + bool m_entangled { true }; + + // Flags to manage querying the remote port for GC purposes + mutable bool m_mightBeEligibleForGC { false }; + mutable bool m_hasHadLocalActivitySinceLastCheck { false }; + mutable bool m_isRemoteEligibleForGC { false }; + mutable bool m_isAskingRemoteAboutGC { false }; + bool m_hasMessageEventListener { false }; + + MessagePortIdentifier m_identifier; + MessagePortIdentifier m_remoteIdentifier; + + mutable std::atomic<unsigned> m_refCount { 1 }; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessagePort.idl b/src/javascript/jsc/bindings/webcore/MessagePort.idl new file mode 100644 index 000000000..5b2c55294 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessagePort.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2011 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +[ + ActiveDOMObject, + Exposed=(Window,Worker,AudioWorklet), + GenerateIsReachable=Impl, + JSCustomMarkFunction, +] interface MessagePort : EventTarget { + [CallWith=CurrentGlobalObject] undefined postMessage(any message, sequence<object> transfer); + [CallWith=CurrentGlobalObject] undefined postMessage(any message, optional StructuredSerializeOptions options); + undefined start(); + undefined close(); + + attribute EventHandler onmessage; +}; diff --git a/src/javascript/jsc/bindings/webcore/MessagePortChannel.cpp b/src/javascript/jsc/bindings/webcore/MessagePortChannel.cpp new file mode 100644 index 000000000..befa546a4 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessagePortChannel.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MessagePortChannel.h" + +#include "Logging.h" +#include "MessagePortChannelRegistry.h" +#include <wtf/CompletionHandler.h> +#include <wtf/MainThread.h> + +namespace WebCore { + +Ref<MessagePortChannel> MessagePortChannel::create(MessagePortChannelRegistry& registry, const MessagePortIdentifier& port1, const MessagePortIdentifier& port2) +{ + return adoptRef(*new MessagePortChannel(registry, port1, port2)); +} + +MessagePortChannel::MessagePortChannel(MessagePortChannelRegistry& registry, const MessagePortIdentifier& port1, const MessagePortIdentifier& port2) + : m_registry(registry) +{ + ASSERT(isMainThread()); + + relaxAdoptionRequirement(); + + m_ports[0] = port1; + m_processes[0] = port1.processIdentifier; + m_entangledToProcessProtectors[0] = this; + m_ports[1] = port2; + m_processes[1] = port2.processIdentifier; + m_entangledToProcessProtectors[1] = this; + + m_registry.messagePortChannelCreated(*this); +} + +MessagePortChannel::~MessagePortChannel() +{ + m_registry.messagePortChannelDestroyed(*this); +} + +std::optional<ProcessIdentifier> MessagePortChannel::processForPort(const MessagePortIdentifier& port) +{ + ASSERT(isMainThread()); + ASSERT(port == m_ports[0] || port == m_ports[1]); + size_t i = port == m_ports[0] ? 0 : 1; + return m_processes[i]; +} + +bool MessagePortChannel::includesPort(const MessagePortIdentifier& port) +{ + ASSERT(isMainThread()); + + return m_ports[0] == port || m_ports[1] == port; +} + +void MessagePortChannel::entanglePortWithProcess(const MessagePortIdentifier& port, ProcessIdentifier process) +{ + ASSERT(isMainThread()); + + ASSERT(port == m_ports[0] || port == m_ports[1]); + size_t i = port == m_ports[0] ? 0 : 1; + + LOG(MessagePorts, "MessagePortChannel %s (%p) entangling port %s (that port has %zu messages available)", logString().utf8().data(), this, port.logString().utf8().data(), m_pendingMessages[i].size()); + + ASSERT(!m_processes[i] || *m_processes[i] == process); + m_processes[i] = process; + m_entangledToProcessProtectors[i] = this; + m_pendingMessagePortTransfers[i].remove(this); +} + +void MessagePortChannel::disentanglePort(const MessagePortIdentifier& port) +{ + ASSERT(isMainThread()); + + LOG(MessagePorts, "MessagePortChannel %s (%p) disentangling port %s", logString().utf8().data(), this, port.logString().utf8().data()); + + ASSERT(port == m_ports[0] || port == m_ports[1]); + size_t i = port == m_ports[0] ? 0 : 1; + + ASSERT(m_processes[i] || m_isClosed[i]); + m_processes[i] = std::nullopt; + m_pendingMessagePortTransfers[i].add(this); + + // This set of steps is to guarantee that the lock is unlocked before the + // last ref to this object is released. + auto protectedThis = WTFMove(m_entangledToProcessProtectors[i]); +} + +void MessagePortChannel::closePort(const MessagePortIdentifier& port) +{ + ASSERT(isMainThread()); + + ASSERT(port == m_ports[0] || port == m_ports[1]); + size_t i = port == m_ports[0] ? 0 : 1; + + m_processes[i] = std::nullopt; + m_isClosed[i] = true; + + // This set of steps is to guarantee that the lock is unlocked before the + // last ref to this object is released. + Ref protectedThis { *this }; + + m_pendingMessages[i].clear(); + m_pendingMessagePortTransfers[i].clear(); + m_pendingMessageProtectors[i] = nullptr; + m_entangledToProcessProtectors[i] = nullptr; +} + +bool MessagePortChannel::postMessageToRemote(MessageWithMessagePorts&& message, const MessagePortIdentifier& remoteTarget) +{ + ASSERT(isMainThread()); + + ASSERT(remoteTarget == m_ports[0] || remoteTarget == m_ports[1]); + size_t i = remoteTarget == m_ports[0] ? 0 : 1; + + m_pendingMessages[i].append(WTFMove(message)); + LOG(MessagePorts, "MessagePortChannel %s (%p) now has %zu messages pending on port %s", logString().utf8().data(), this, m_pendingMessages[i].size(), remoteTarget.logString().utf8().data()); + + if (m_pendingMessages[i].size() == 1) { + m_pendingMessageProtectors[i] = this; + return true; + } + + ASSERT(m_pendingMessageProtectors[i] == this); + return false; +} + +void MessagePortChannel::takeAllMessagesForPort(const MessagePortIdentifier& port, CompletionHandler<void(Vector<MessageWithMessagePorts>&&, CompletionHandler<void()>&&)>&& callback) +{ + ASSERT(isMainThread()); + + LOG(MessagePorts, "MessagePortChannel %p taking all messages for port %s", this, port.logString().utf8().data()); + + ASSERT(port == m_ports[0] || port == m_ports[1]); + size_t i = port == m_ports[0] ? 0 : 1; + + if (m_pendingMessages[i].isEmpty()) { + callback({ }, [] { }); + return; + } + + ASSERT(m_pendingMessageProtectors[i]); + + Vector<MessageWithMessagePorts> result; + result.swap(m_pendingMessages[i]); + + ++m_messageBatchesInFlight; + + LOG(MessagePorts, "There are %zu messages to take for port %s. Taking them now, messages in flight is now %" PRIu64, result.size(), port.logString().utf8().data(), m_messageBatchesInFlight); + + auto size = result.size(); + callback(WTFMove(result), [size, this, port, protectedThis = WTFMove(m_pendingMessageProtectors[i])] { + UNUSED_PARAM(port); +#if LOG_DISABLED + UNUSED_PARAM(size); +#endif + --m_messageBatchesInFlight; + LOG(MessagePorts, "Message port channel %s was notified that a batch of %zu message port messages targeted for port %s just completed dispatch, in flight is now %" PRIu64, logString().utf8().data(), size, port.logString().utf8().data(), m_messageBatchesInFlight); + + }); +} + +void MessagePortChannel::checkRemotePortForActivity(const MessagePortIdentifier& remotePort, CompletionHandler<void(MessagePortChannelProvider::HasActivity)>&& callback) +{ + ASSERT(isMainThread()); + ASSERT(remotePort == m_ports[0] || remotePort == m_ports[1]); + + // If the remote port is closed there is no pending activity. + size_t i = remotePort == m_ports[0] ? 0 : 1; + if (m_isClosed[i]) { + callback(MessagePortChannelProvider::HasActivity::No); + return; + } + + // If there are any messages in flight between the ports, there is pending activity. + if (hasAnyMessagesPendingOrInFlight()) { + callback(MessagePortChannelProvider::HasActivity::Yes); + return; + } + + // If the port is not currently in a process then it's being transferred as part of a postMessage. + // We treat these ports as if they do have activity since they will be revived when the message is delivered. + if (!m_processes[i]) { + callback(MessagePortChannelProvider::HasActivity::Yes); + return; + } + + CompletionHandler<void(MessagePortChannelProvider::HasActivity)> outerCallback = [this, protectedThis = Ref { *this }, callback = WTFMove(callback)](auto hasActivity) mutable { + if (hasActivity == MessagePortChannelProvider::HasActivity::Yes) { + callback(hasActivity); + return; + } + + // If the remote port said it had no activity, check again for any messages that might be in flight. + // This is because it might have asynchronously sent a message just before it was asked about local activity. + if (hasAnyMessagesPendingOrInFlight()) + hasActivity = MessagePortChannelProvider::HasActivity::Yes; + + callback(hasActivity); + }; + + m_registry.checkProcessLocalPortForActivity(remotePort, *m_processes[i], WTFMove(outerCallback)); +} + +bool MessagePortChannel::hasAnyMessagesPendingOrInFlight() const +{ + ASSERT(isMainThread()); + return m_messageBatchesInFlight || !m_pendingMessages[0].isEmpty() || !m_pendingMessages[1].isEmpty(); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessagePortChannel.h b/src/javascript/jsc/bindings/webcore/MessagePortChannel.h new file mode 100644 index 000000000..b8606195d --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessagePortChannel.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "MessagePortChannelProvider.h" +#include "MessagePortIdentifier.h" +#include "MessageWithMessagePorts.h" +#include "ProcessIdentifier.h" +#include <wtf/HashSet.h> +#include <wtf/RefCounted.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class MessagePortChannelRegistry; + +class MessagePortChannel : public RefCounted<MessagePortChannel> { +public: + static Ref<MessagePortChannel> create(MessagePortChannelRegistry&, const MessagePortIdentifier& port1, const MessagePortIdentifier& port2); + + ~MessagePortChannel(); + + const MessagePortIdentifier& port1() const { return m_ports[0]; } + const MessagePortIdentifier& port2() const { return m_ports[1]; } + + WEBCORE_EXPORT std::optional<ProcessIdentifier> processForPort(const MessagePortIdentifier&); + bool includesPort(const MessagePortIdentifier&); + void entanglePortWithProcess(const MessagePortIdentifier&, ProcessIdentifier); + void disentanglePort(const MessagePortIdentifier&); + void closePort(const MessagePortIdentifier&); + bool postMessageToRemote(MessageWithMessagePorts&&, const MessagePortIdentifier& remoteTarget); + + void takeAllMessagesForPort(const MessagePortIdentifier&, CompletionHandler<void(Vector<MessageWithMessagePorts>&&, CompletionHandler<void()>&&)>&&); + void checkRemotePortForActivity(const MessagePortIdentifier&, CompletionHandler<void(MessagePortChannelProvider::HasActivity)>&& callback); + + WEBCORE_EXPORT bool hasAnyMessagesPendingOrInFlight() const; + + uint64_t beingTransferredCount(); + +#if !LOG_DISABLED + String logString() const { return makeString(m_ports[0].logString(), ":", m_ports[1].logString()); } +#endif + +private: + MessagePortChannel(MessagePortChannelRegistry&, const MessagePortIdentifier& port1, const MessagePortIdentifier& port2); + + MessagePortIdentifier m_ports[2]; + bool m_isClosed[2] { false, false }; + std::optional<ProcessIdentifier> m_processes[2]; + RefPtr<MessagePortChannel> m_entangledToProcessProtectors[2]; + Vector<MessageWithMessagePorts> m_pendingMessages[2]; + HashSet<RefPtr<MessagePortChannel>> m_pendingMessagePortTransfers[2]; + RefPtr<MessagePortChannel> m_pendingMessageProtectors[2]; + uint64_t m_messageBatchesInFlight { 0 }; + + MessagePortChannelRegistry& m_registry; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessagePortChannelProvider.cpp b/src/javascript/jsc/bindings/webcore/MessagePortChannelProvider.cpp new file mode 100644 index 000000000..341c03cef --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessagePortChannelProvider.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MessagePortChannelProvider.h" + +#include "Document.h" +#include "MessagePortChannelProviderImpl.h" +#include "WorkerGlobalScope.h" +#include "WorkletGlobalScope.h" +#include <wtf/MainThread.h> + +namespace WebCore { + +static MessagePortChannelProvider* globalProvider; + +MessagePortChannelProvider& MessagePortChannelProvider::singleton() +{ + ASSERT(isMainThread()); + static std::once_flag onceFlag; + std::call_once(onceFlag, [] { + if (!globalProvider) + globalProvider = new MessagePortChannelProviderImpl; + }); + + return *globalProvider; +} + +void MessagePortChannelProvider::setSharedProvider(MessagePortChannelProvider& provider) +{ + RELEASE_ASSERT(isMainThread()); + RELEASE_ASSERT(!globalProvider); + globalProvider = &provider; +} + +MessagePortChannelProvider& MessagePortChannelProvider::fromContext(ScriptExecutionContext& context) +{ + if (auto document = dynamicDowncast<Document>(context)) + return document->messagePortChannelProvider(); + + if (auto workletScope = dynamicDowncast<WorkletGlobalScope>(context)) + return workletScope->messagePortChannelProvider(); + + return downcast<WorkerGlobalScope>(context).messagePortChannelProvider(); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessagePortChannelProvider.h b/src/javascript/jsc/bindings/webcore/MessagePortChannelProvider.h new file mode 100644 index 000000000..19dff9369 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessagePortChannelProvider.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ProcessIdentifier.h" +#include <wtf/CompletionHandler.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class ScriptExecutionContext; +struct MessagePortIdentifier; +struct MessageWithMessagePorts; + +class MessagePortChannelProvider { +public: + static MessagePortChannelProvider& fromContext(ScriptExecutionContext&); + static MessagePortChannelProvider& singleton(); + WEBCORE_EXPORT static void setSharedProvider(MessagePortChannelProvider&); + + virtual ~MessagePortChannelProvider() { } + + // Operations that WebProcesses perform + virtual void createNewMessagePortChannel(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) = 0; + virtual void entangleLocalPortInThisProcessToRemote(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) = 0; + virtual void messagePortDisentangled(const MessagePortIdentifier& local) = 0; + virtual void messagePortClosed(const MessagePortIdentifier& local) = 0; + + virtual void takeAllMessagesForPort(const MessagePortIdentifier&, CompletionHandler<void(Vector<MessageWithMessagePorts>&&, CompletionHandler<void()>&&)>&&) = 0; + + virtual void postMessageToRemote(MessageWithMessagePorts&&, const MessagePortIdentifier& remoteTarget) = 0; + + enum class HasActivity { + Yes, + No, + }; + virtual void checkRemotePortForActivity(const MessagePortIdentifier& remoteTarget, CompletionHandler<void(HasActivity)>&& callback) = 0; + +private: + +}; + +} // namespace WebCore + +namespace WTF { + +template<> struct EnumTraits<WebCore::MessagePortChannelProvider::HasActivity> { + using values = EnumValues< + WebCore::MessagePortChannelProvider::HasActivity, + WebCore::MessagePortChannelProvider::HasActivity::No, + WebCore::MessagePortChannelProvider::HasActivity::Yes + >; +}; + +} diff --git a/src/javascript/jsc/bindings/webcore/MessagePortChannelProviderImpl.cpp b/src/javascript/jsc/bindings/webcore/MessagePortChannelProviderImpl.cpp new file mode 100644 index 000000000..f9eb7f593 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessagePortChannelProviderImpl.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MessagePortChannelProviderImpl.h" + +#include "MessagePort.h" +#include <wtf/MainThread.h> +#include <wtf/RunLoop.h> + +namespace WebCore { + +static inline MessagePortChannelRegistry::CheckProcessLocalPortForActivityCallback checkActivityCallback() +{ + return [](auto& messagePortIdentifier, auto, auto&& callback) { + ASSERT(isMainThread()); + callback(MessagePort::isExistingMessagePortLocallyReachable(messagePortIdentifier) ? MessagePortChannelProvider::HasActivity::Yes : MessagePortChannelProvider::HasActivity::No); + }; +} + +MessagePortChannelProviderImpl::MessagePortChannelProviderImpl() + : m_registry(checkActivityCallback()) +{ +} + +MessagePortChannelProviderImpl::~MessagePortChannelProviderImpl() +{ + ASSERT_NOT_REACHED(); +} + +void MessagePortChannelProviderImpl::createNewMessagePortChannel(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) +{ + ensureOnMainThread([registry = &m_registry, local, remote] { + registry->didCreateMessagePortChannel(local, remote); + }); +} + +void MessagePortChannelProviderImpl::entangleLocalPortInThisProcessToRemote(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) +{ + ensureOnMainThread([registry = &m_registry, local, remote] { + registry->didEntangleLocalToRemote(local, remote, Process::identifier()); + }); +} + +void MessagePortChannelProviderImpl::messagePortDisentangled(const MessagePortIdentifier& local) +{ + ensureOnMainThread([registry = &m_registry, local] { + registry->didDisentangleMessagePort(local); + }); +} + +void MessagePortChannelProviderImpl::messagePortClosed(const MessagePortIdentifier& local) +{ + ensureOnMainThread([registry = &m_registry, local] { + registry->didCloseMessagePort(local); + }); +} + +void MessagePortChannelProviderImpl::postMessageToRemote(MessageWithMessagePorts&& message, const MessagePortIdentifier& remoteTarget) +{ + ensureOnMainThread([registry = &m_registry, message = WTFMove(message), remoteTarget]() mutable { + if (registry->didPostMessageToRemote(WTFMove(message), remoteTarget)) + MessagePort::notifyMessageAvailable(remoteTarget); + }); +} + +void MessagePortChannelProviderImpl::takeAllMessagesForPort(const MessagePortIdentifier& port, CompletionHandler<void(Vector<MessageWithMessagePorts>&&, CompletionHandler<void()>&&)>&& outerCallback) +{ + // It is the responsibility of outerCallback to get itself to the appropriate thread (e.g. WebWorker thread) + auto callback = [outerCallback = WTFMove(outerCallback)](Vector<MessageWithMessagePorts>&& messages, CompletionHandler<void()>&& messageDeliveryCallback) mutable { + ASSERT(isMainThread()); + outerCallback(WTFMove(messages), WTFMove(messageDeliveryCallback)); + }; + + ensureOnMainThread([registry = &m_registry, port, callback = WTFMove(callback)]() mutable { + registry->takeAllMessagesForPort(port, WTFMove(callback)); + }); +} + +void MessagePortChannelProviderImpl::checkRemotePortForActivity(const MessagePortIdentifier& remoteTarget, CompletionHandler<void(HasActivity)>&& outerCallback) +{ + auto callback = Function<void(HasActivity)> { [outerCallback = WTFMove(outerCallback)](HasActivity hasActivity) mutable { + ASSERT(isMainThread()); + outerCallback(hasActivity); + } }; + + ensureOnMainThread([registry = &m_registry, remoteTarget, callback = WTFMove(callback)]() mutable { + registry->checkRemotePortForActivity(remoteTarget, WTFMove(callback)); + }); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessagePortChannelProviderImpl.h b/src/javascript/jsc/bindings/webcore/MessagePortChannelProviderImpl.h new file mode 100644 index 000000000..24b8c7b1c --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessagePortChannelProviderImpl.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "MessagePortChannelProvider.h" +#include "MessagePortChannelRegistry.h" + +namespace WebCore { + +class MessagePortChannelProviderImpl final : public MessagePortChannelProvider { +public: + MessagePortChannelProviderImpl(); + ~MessagePortChannelProviderImpl() final; + +private: + void createNewMessagePortChannel(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) final; + void entangleLocalPortInThisProcessToRemote(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) final; + void messagePortDisentangled(const MessagePortIdentifier& local) final; + void messagePortClosed(const MessagePortIdentifier& local) final; + void postMessageToRemote(MessageWithMessagePorts&&, const MessagePortIdentifier& remoteTarget) final; + void takeAllMessagesForPort(const MessagePortIdentifier&, CompletionHandler<void(Vector<MessageWithMessagePorts>&&, CompletionHandler<void()>&&)>&&) final; + void checkRemotePortForActivity(const MessagePortIdentifier& remoteTarget, CompletionHandler<void(HasActivity)>&& callback) final; + + MessagePortChannelRegistry m_registry; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessagePortChannelRegistry.cpp b/src/javascript/jsc/bindings/webcore/MessagePortChannelRegistry.cpp new file mode 100644 index 000000000..98be42a8f --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessagePortChannelRegistry.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MessagePortChannelRegistry.h" + +#include "Logging.h" +#include <wtf/CompletionHandler.h> +#include <wtf/MainThread.h> + +namespace WebCore { + +MessagePortChannelRegistry::MessagePortChannelRegistry(CheckProcessLocalPortForActivityCallback&& checkProcessLocalPortForActivityCallback) + : m_checkProcessLocalPortForActivityCallback(WTFMove(checkProcessLocalPortForActivityCallback)) +{ +} + +MessagePortChannelRegistry::~MessagePortChannelRegistry() +{ + ASSERT(m_openChannels.isEmpty()); +} + +void MessagePortChannelRegistry::didCreateMessagePortChannel(const MessagePortIdentifier& port1, const MessagePortIdentifier& port2) +{ + LOG(MessagePorts, "Registry: Creating MessagePortChannel %p linking %s and %s", this, port1.logString().utf8().data(), port2.logString().utf8().data()); + ASSERT(isMainThread()); + + MessagePortChannel::create(*this, port1, port2); +} + +void MessagePortChannelRegistry::messagePortChannelCreated(MessagePortChannel& channel) +{ + ASSERT(isMainThread()); + + auto result = m_openChannels.ensure(channel.port1(), [channel = &channel] { + return channel; + }); + ASSERT(result.isNewEntry); + + result = m_openChannels.ensure(channel.port2(), [channel = &channel] { + return channel; + }); + ASSERT(result.isNewEntry); +} + +void MessagePortChannelRegistry::messagePortChannelDestroyed(MessagePortChannel& channel) +{ + ASSERT(isMainThread()); + + ASSERT(m_openChannels.get(channel.port1()) == &channel); + ASSERT(m_openChannels.get(channel.port2()) == &channel); + + m_openChannels.remove(channel.port1()); + m_openChannels.remove(channel.port2()); + + LOG(MessagePorts, "Registry: After removing channel %s there are %u channels left in the registry:", channel.logString().utf8().data(), m_openChannels.size()); +} + +void MessagePortChannelRegistry::didEntangleLocalToRemote(const MessagePortIdentifier& local, const MessagePortIdentifier& remote, ProcessIdentifier process) +{ + ASSERT(isMainThread()); + + // The channel might be gone if the remote side was closed. + auto* channel = m_openChannels.get(local); + if (!channel) + return; + + ASSERT_UNUSED(remote, channel->includesPort(remote)); + + channel->entanglePortWithProcess(local, process); +} + +void MessagePortChannelRegistry::didDisentangleMessagePort(const MessagePortIdentifier& port) +{ + ASSERT(isMainThread()); + + // The channel might be gone if the remote side was closed. + auto* channel = m_openChannels.get(port); + if (!channel) + return; + + channel->disentanglePort(port); +} + +void MessagePortChannelRegistry::didCloseMessagePort(const MessagePortIdentifier& port) +{ + ASSERT(isMainThread()); + + LOG(MessagePorts, "Registry: MessagePort %s closed in registry", port.logString().utf8().data()); + + auto* channel = m_openChannels.get(port); + if (!channel) + return; + +#ifndef NDEBUG + if (channel && channel->hasAnyMessagesPendingOrInFlight()) + LOG(MessagePorts, "Registry: (Note) The channel closed for port %s had messages pending or in flight", port.logString().utf8().data()); +#endif + + channel->closePort(port); + + // FIXME: When making message ports be multi-process, this should probably push a notification + // to the remaining port to tell it this port closed. +} + +bool MessagePortChannelRegistry::didPostMessageToRemote(MessageWithMessagePorts&& message, const MessagePortIdentifier& remoteTarget) +{ + ASSERT(isMainThread()); + + LOG(MessagePorts, "Registry: Posting message to MessagePort %s in registry", remoteTarget.logString().utf8().data()); + + // The channel might be gone if the remote side was closed. + auto* channel = m_openChannels.get(remoteTarget); + if (!channel) { + LOG(MessagePorts, "Registry: Could not find MessagePortChannel for port %s; It was probably closed. Message will be dropped.", remoteTarget.logString().utf8().data()); + return false; + } + + return channel->postMessageToRemote(WTFMove(message), remoteTarget); +} + +void MessagePortChannelRegistry::takeAllMessagesForPort(const MessagePortIdentifier& port, CompletionHandler<void(Vector<MessageWithMessagePorts>&&, CompletionHandler<void()>&&)>&& callback) +{ + ASSERT(isMainThread()); + + LOG(MessagePorts, "Registry: Taking all messages for MessagePort %s", port.logString().utf8().data()); + + // The channel might be gone if the remote side was closed. + auto* channel = m_openChannels.get(port); + if (!channel) { + callback({ }, [] { }); + return; + } + + channel->takeAllMessagesForPort(port, WTFMove(callback)); +} + +void MessagePortChannelRegistry::checkRemotePortForActivity(const MessagePortIdentifier& remoteTarget, CompletionHandler<void(MessagePortChannelProvider::HasActivity)>&& callback) +{ + ASSERT(isMainThread()); + + // The channel might be gone if the remote side was closed. + auto* channel = m_openChannels.get(remoteTarget); + if (!channel) { + callback(MessagePortChannelProvider::HasActivity::No); + return; + } + + channel->checkRemotePortForActivity(remoteTarget, WTFMove(callback)); +} + +MessagePortChannel* MessagePortChannelRegistry::existingChannelContainingPort(const MessagePortIdentifier& port) +{ + ASSERT(isMainThread()); + + return m_openChannels.get(port); +} + +void MessagePortChannelRegistry::checkProcessLocalPortForActivity(const MessagePortIdentifier& messagePortIdentifier, ProcessIdentifier processIdentifier, CompletionHandler<void(MessagePortChannelProvider::HasActivity)>&& callback) +{ + m_checkProcessLocalPortForActivityCallback(messagePortIdentifier, processIdentifier, WTFMove(callback)); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessagePortChannelRegistry.h b/src/javascript/jsc/bindings/webcore/MessagePortChannelRegistry.h new file mode 100644 index 000000000..962fc8f02 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessagePortChannelRegistry.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "MessagePortChannel.h" +#include "MessagePortChannelProvider.h" +#include "MessagePortIdentifier.h" +#include "ProcessIdentifier.h" +#include <wtf/HashMap.h> + +namespace WebCore { + +class MessagePortChannelRegistry { +public: + using CheckProcessLocalPortForActivityCallback = Function<void(const MessagePortIdentifier&, ProcessIdentifier, CompletionHandler<void(MessagePortChannelProvider::HasActivity)>&&)>; + WEBCORE_EXPORT explicit MessagePortChannelRegistry(CheckProcessLocalPortForActivityCallback&&); + + WEBCORE_EXPORT ~MessagePortChannelRegistry(); + + WEBCORE_EXPORT void didCreateMessagePortChannel(const MessagePortIdentifier& port1, const MessagePortIdentifier& port2); + WEBCORE_EXPORT void didEntangleLocalToRemote(const MessagePortIdentifier& local, const MessagePortIdentifier& remote, ProcessIdentifier); + WEBCORE_EXPORT void didDisentangleMessagePort(const MessagePortIdentifier& local); + WEBCORE_EXPORT void didCloseMessagePort(const MessagePortIdentifier& local); + WEBCORE_EXPORT bool didPostMessageToRemote(MessageWithMessagePorts&&, const MessagePortIdentifier& remoteTarget); + WEBCORE_EXPORT void takeAllMessagesForPort(const MessagePortIdentifier&, CompletionHandler<void(Vector<MessageWithMessagePorts>&&, CompletionHandler<void()>&&)>&&); + WEBCORE_EXPORT void checkRemotePortForActivity(const MessagePortIdentifier& remoteTarget, CompletionHandler<void(MessagePortChannelProvider::HasActivity)>&& callback); + + WEBCORE_EXPORT MessagePortChannel* existingChannelContainingPort(const MessagePortIdentifier&); + + WEBCORE_EXPORT void messagePortChannelCreated(MessagePortChannel&); + WEBCORE_EXPORT void messagePortChannelDestroyed(MessagePortChannel&); + + void checkProcessLocalPortForActivity(const MessagePortIdentifier&, ProcessIdentifier, CompletionHandler<void(MessagePortChannelProvider::HasActivity)>&&); + +private: + HashMap<MessagePortIdentifier, MessagePortChannel*> m_openChannels; + CheckProcessLocalPortForActivityCallback m_checkProcessLocalPortForActivityCallback; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/MessagePortIdentifier.h b/src/javascript/jsc/bindings/webcore/MessagePortIdentifier.h new file mode 100644 index 000000000..ab4ff37d1 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessagePortIdentifier.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ProcessIdentifier.h" +#include <wtf/Hasher.h> +#include <wtf/text/StringConcatenateNumbers.h> + +namespace WebCore { + +struct MessagePortIdentifier { + ProcessIdentifier processIdentifier; + enum PortIdentifierType { }; + ObjectIdentifier<PortIdentifierType> portIdentifier; + + template<class Encoder> void encode(Encoder&) const; + template<class Decoder> static std::optional<MessagePortIdentifier> decode(Decoder&); + +#if !LOG_DISABLED + String logString() const; +#endif +}; + +inline void add(Hasher& hasher, const MessagePortIdentifier& identifier) +{ + add(hasher, identifier.processIdentifier, identifier.portIdentifier); +} + +inline bool operator==(const MessagePortIdentifier& a, const MessagePortIdentifier& b) +{ + return a.processIdentifier == b.processIdentifier && a.portIdentifier == b.portIdentifier; +} + +template<class Encoder> +void MessagePortIdentifier::encode(Encoder& encoder) const +{ + encoder << processIdentifier << portIdentifier; +} + +template<class Decoder> +std::optional<MessagePortIdentifier> MessagePortIdentifier::decode(Decoder& decoder) +{ + std::optional<ProcessIdentifier> processIdentifier; + decoder >> processIdentifier; + if (!processIdentifier) + return std::nullopt; + + std::optional<ObjectIdentifier<PortIdentifierType>> portIdentifier; + decoder >> portIdentifier; + if (!portIdentifier) + return std::nullopt; + + return { { WTFMove(*processIdentifier), WTFMove(*portIdentifier) } }; +} + +#if !LOG_DISABLED + +inline String MessagePortIdentifier::logString() const +{ + return makeString(processIdentifier.toUInt64(), '-', portIdentifier.toUInt64()); +} + +#endif + +} // namespace WebCore + +namespace WTF { + +struct MessagePortIdentifierHash { + static unsigned hash(const WebCore::MessagePortIdentifier& key) { return computeHash(key); } + static bool equal(const WebCore::MessagePortIdentifier& a, const WebCore::MessagePortIdentifier& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +template<> struct HashTraits<WebCore::MessagePortIdentifier> : GenericHashTraits<WebCore::MessagePortIdentifier> { + static WebCore::MessagePortIdentifier emptyValue() { return { }; } + + static void constructDeletedValue(WebCore::MessagePortIdentifier& slot) { new (NotNull, &slot.processIdentifier) WebCore::ProcessIdentifier(WTF::HashTableDeletedValue); } + + static bool isDeletedValue(const WebCore::MessagePortIdentifier& slot) { return slot.processIdentifier.isHashTableDeletedValue(); } +}; + +template<> struct DefaultHash<WebCore::MessagePortIdentifier> : MessagePortIdentifierHash { }; + +} // namespace WTF diff --git a/src/javascript/jsc/bindings/webcore/MessageWithMessagePorts.h b/src/javascript/jsc/bindings/webcore/MessageWithMessagePorts.h new file mode 100644 index 000000000..c1db070aa --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/MessageWithMessagePorts.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "SerializedScriptValue.h" +#include "TransferredMessagePort.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +struct MessageWithMessagePorts { + RefPtr<SerializedScriptValue> message; + Vector<TransferredMessagePort> transferredPorts; + + template<class Encoder> void encode(Encoder&) const; + template<class Decoder> static std::optional<MessageWithMessagePorts> decode(Decoder&); +}; + + +template<class Encoder> +void MessageWithMessagePorts::encode(Encoder& encoder) const +{ + ASSERT(message); + encoder << *message << transferredPorts; +} + +template<class Decoder> +std::optional<MessageWithMessagePorts> MessageWithMessagePorts::decode(Decoder& decoder) +{ + MessageWithMessagePorts result; + + result.message = SerializedScriptValue::decode(decoder); + if (!result.message) + return std::nullopt; + + if (!decoder.decode(result.transferredPorts)) + return std::nullopt; + + return result; +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/Microtasks.cpp b/src/javascript/jsc/bindings/webcore/Microtasks.cpp new file mode 100644 index 000000000..c3f61166c --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/Microtasks.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2014 Yoav Weiss (yoav@yoav.ws) + * Copyright (C) 2015 Akamai Technologies Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "Microtasks.h" + +// #include "CommonVM.h" +#include "EventLoop.h" +#include "WorkerGlobalScope.h" +#include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/SetForScope.h> + +namespace WebCore { + +MicrotaskQueue::MicrotaskQueue(JSC::VM& vm) + : m_vm(vm) +{ +} + +MicrotaskQueue::~MicrotaskQueue() = default; + +void MicrotaskQueue::append(std::unique_ptr<EventLoopTask>&& task) +{ + m_microtaskQueue.append(WTFMove(task)); +} + +void MicrotaskQueue::performMicrotaskCheckpoint() +{ + if (m_performingMicrotaskCheckpoint) + return; + + SetForScope change(m_performingMicrotaskCheckpoint, true); + JSC::JSLockHolder locker(vm()); + + Vector<std::unique_ptr<EventLoopTask>> toKeep; + while (!m_microtaskQueue.isEmpty()) { + Vector<std::unique_ptr<EventLoopTask>> queue = WTFMove(m_microtaskQueue); + for (auto& task : queue) { + auto* group = task->group(); + if (!group || group->isStoppedPermanently()) + continue; + if (group->isSuspended()) + toKeep.append(WTFMove(task)); + else + task->execute(); + } + } + + vm().finalizeSynchronousJSExecution(); + m_microtaskQueue = WTFMove(toKeep); + + auto checkpointTasks = std::exchange(m_checkpointTasks, {}); + for (auto& checkpointTask : checkpointTasks) { + auto* group = checkpointTask->group(); + if (!group || group->isStoppedPermanently()) + continue; + + if (group->isSuspended()) { + m_checkpointTasks.append(WTFMove(checkpointTask)); + continue; + } + + checkpointTask->execute(); + } +} + +void MicrotaskQueue::addCheckpointTask(std::unique_ptr<EventLoopTask>&& task) +{ + m_checkpointTasks.append(WTFMove(task)); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/Microtasks.h b/src/javascript/jsc/bindings/webcore/Microtasks.h new file mode 100644 index 000000000..be173b0e8 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/Microtasks.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 Yoav Weiss (yoav@yoav.ws) + * Copyright (C) 2015 Akamai Technologies Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#pragma once + +#include <wtf/Forward.h> +#include <wtf/Vector.h> + +namespace JSC { +class VM; +} // namespace JSC + +namespace WebCore { + +class EventLoopTask; + +class MicrotaskQueue final { + WTF_MAKE_FAST_ALLOCATED; +public: + WEBCORE_EXPORT MicrotaskQueue(JSC::VM&); + WEBCORE_EXPORT ~MicrotaskQueue(); + + WEBCORE_EXPORT void append(std::unique_ptr<EventLoopTask>&&); + WEBCORE_EXPORT void performMicrotaskCheckpoint(); + + WEBCORE_EXPORT void addCheckpointTask(std::unique_ptr<EventLoopTask>&&); + +private: + JSC::VM& vm() const { return m_vm.get(); } + + bool m_performingMicrotaskCheckpoint { false }; + Vector<std::unique_ptr<EventLoopTask>> m_microtaskQueue; + // For the main thread the VM lives forever. For workers it's lifetime is tied to our owning WorkerGlobalScope. Regardless, we retain the VM here to be safe. + Ref<JSC::VM> m_vm; + + Vector<std::unique_ptr<EventLoopTask>> m_checkpointTasks; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/NavigatorBase.cpp b/src/javascript/jsc/bindings/webcore/NavigatorBase.cpp new file mode 100644 index 000000000..7b10c6356 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/NavigatorBase.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "NavigatorBase.h" + +#include "Document.h" +#include "GPU.h" +#include "RuntimeEnabledFeatures.h" +#include "ServiceWorkerContainer.h" +#include "StorageManager.h" +#include "WebLockManager.h" +#include <mutex> +#include <wtf/Language.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/NumberOfCores.h> +#include <wtf/UniqueRef.h> +#include <wtf/text/WTFString.h> + +#if OS(LINUX) +#include "sys/utsname.h" +#include <wtf/StdLibExtras.h> +#endif + +#if PLATFORM(IOS_FAMILY) +#include "Device.h" +#endif + +#ifndef WEBCORE_NAVIGATOR_PRODUCT +#define WEBCORE_NAVIGATOR_PRODUCT "Gecko"_s +#endif // ifndef WEBCORE_NAVIGATOR_PRODUCT + +#ifndef WEBCORE_NAVIGATOR_PRODUCT_SUB +#define WEBCORE_NAVIGATOR_PRODUCT_SUB "20030107"_s +#endif // ifndef WEBCORE_NAVIGATOR_PRODUCT_SUB + +#ifndef WEBCORE_NAVIGATOR_VENDOR +#define WEBCORE_NAVIGATOR_VENDOR "Apple Computer, Inc."_s +#endif // ifndef WEBCORE_NAVIGATOR_VENDOR + +#ifndef WEBCORE_NAVIGATOR_VENDOR_SUB +#define WEBCORE_NAVIGATOR_VENDOR_SUB emptyString() +#endif // ifndef WEBCORE_NAVIGATOR_VENDOR_SUB + +namespace WebCore { + +NavigatorBase::NavigatorBase(ScriptExecutionContext* context) + : ContextDestructionObserver(context) +{ +} + +NavigatorBase::~NavigatorBase() = default; + +String NavigatorBase::appName() +{ + return "Netscape"_s; +} + +String NavigatorBase::appVersion() const +{ + // Version is everything in the user agent string past the "Mozilla/" prefix. + const String& agent = userAgent(); + return agent.substring(agent.find('/') + 1); +} + +String NavigatorBase::platform() const +{ +#if OS(LINUX) + static LazyNeverDestroyed<String> platformName; + static std::once_flag onceKey; + std::call_once(onceKey, [] { + struct utsname osname; + platformName.construct(uname(&osname) >= 0 ? String(osname.sysname) + " "_str + String(osname.machine) : String(""_s)); + }); + return platformName->isolatedCopy(); +#elif PLATFORM(IOS_FAMILY) + return deviceName(); +#elif OS(MAC_OS_X) + return "MacIntel"_s; +#elif OS(WINDOWS) + return "Win32"_s; +#else + return ""_s; +#endif +} + +String NavigatorBase::appCodeName() +{ + return "Mozilla"_s; +} + +String NavigatorBase::product() +{ + return WEBCORE_NAVIGATOR_PRODUCT; +} + +String NavigatorBase::productSub() +{ + return WEBCORE_NAVIGATOR_PRODUCT_SUB; +} + +String NavigatorBase::vendor() +{ + return WEBCORE_NAVIGATOR_VENDOR; +} + +String NavigatorBase::vendorSub() +{ + return WEBCORE_NAVIGATOR_VENDOR_SUB; +} + +String NavigatorBase::language() +{ + return defaultLanguage(); +} + +Vector<String> NavigatorBase::languages() +{ + // We intentionally expose only the primary language for privacy reasons. + return { defaultLanguage() }; +} + +StorageManager& NavigatorBase::storage() +{ + if (!m_storageManager) + m_storageManager = StorageManager::create(*this); + + return *m_storageManager; +} + +WebLockManager& NavigatorBase::locks() +{ + if (!m_webLockManager) + m_webLockManager = WebLockManager::create(*this); + + return *m_webLockManager; +} + +#if ENABLE(SERVICE_WORKER) +ServiceWorkerContainer& NavigatorBase::serviceWorker() +{ + ASSERT(!scriptExecutionContext() || scriptExecutionContext()->settingsValues().serviceWorkersEnabled); + if (!m_serviceWorkerContainer) + m_serviceWorkerContainer = ServiceWorkerContainer::create(scriptExecutionContext(), *this).moveToUniquePtr(); + return *m_serviceWorkerContainer; +} + +ExceptionOr<ServiceWorkerContainer&> NavigatorBase::serviceWorker(ScriptExecutionContext& context) +{ + if (is<Document>(context) && downcast<Document>(context).isSandboxed(SandboxOrigin)) + return Exception { SecurityError, "Service Worker is disabled because the context is sandboxed and lacks the 'allow-same-origin' flag" }; + return serviceWorker(); +} +#endif + +int NavigatorBase::hardwareConcurrency() +{ + static int numberOfCores; + + static std::once_flag once; + std::call_once(once, [] { + // Enforce a maximum for the number of cores reported to mitigate + // fingerprinting for the minority of machines with large numbers of cores. + // If machines with more than 8 cores become commonplace, we should bump this number. + // see https://bugs.webkit.org/show_bug.cgi?id=132588 for the + // rationale behind this decision. + if (WTF::numberOfProcessorCores() < 8) + numberOfCores = 4; + else + numberOfCores = 8; + }); + + return numberOfCores; +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/NavigatorBase.h b/src/javascript/jsc/bindings/webcore/NavigatorBase.h new file mode 100644 index 000000000..5b0f99a74 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/NavigatorBase.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ContextDestructionObserver.h" +#include "ExceptionOr.h" +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> +#include <wtf/UniqueRef.h> +#include <wtf/Vector.h> +#include <wtf/WeakPtr.h> + +namespace WebCore { + +// class GPU; +class ScriptExecutionContext; +class ServiceWorkerContainer; +// class StorageManager; +// class WebLockManager; + +class NavigatorBase : public RefCounted<NavigatorBase>, public ContextDestructionObserver, public CanMakeWeakPtr<NavigatorBase> { +public: + virtual ~NavigatorBase(); + + static String appName(); + String appVersion() const; + virtual const String& userAgent() const = 0; + virtual String platform() const; + + static String appCodeName(); + static String product(); + static String productSub(); + static String vendor(); + static String vendorSub(); + + virtual bool onLine() const = 0; + + static String language(); + static Vector<String> languages(); + + // StorageManager& storage(); + // WebLockManager& locks(); + + static int hardwareConcurrency(); + +protected: + explicit NavigatorBase(ScriptExecutionContext*); + +private: + // RefPtr<StorageManager> m_storageManager; + // RefPtr<WebLockManager> m_webLockManager; + + // #if ENABLE(SERVICE_WORKER) + // public: + // ServiceWorkerContainer& serviceWorker(); + // ExceptionOr<ServiceWorkerContainer&> serviceWorker(ScriptExecutionContext&); + + // private: + // std::unique_ptr<ServiceWorkerContainer> m_serviceWorkerContainer; + // #endif +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/ProcessIdentifier.cpp b/src/javascript/jsc/bindings/webcore/ProcessIdentifier.cpp new file mode 100644 index 000000000..89e7a4d01 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/ProcessIdentifier.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ProcessIdentifier.h" + +#include <wtf/MainThread.h> + +namespace WebCore { +namespace Process { + +static std::optional<ProcessIdentifier> globalIdentifier; + +void setIdentifier(ProcessIdentifier processIdentifier) +{ + ASSERT(isUIThread()); + globalIdentifier = processIdentifier; +} + +ProcessIdentifier identifier() +{ + static std::once_flag onceFlag; + std::call_once(onceFlag, [] { + if (!globalIdentifier) + globalIdentifier = ProcessIdentifier::generate(); + }); + + return *globalIdentifier; +} + +} // namespace Process +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/ProcessIdentifier.h b/src/javascript/jsc/bindings/webcore/ProcessIdentifier.h new file mode 100644 index 000000000..3e1000934 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/ProcessIdentifier.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <wtf/ObjectIdentifier.h> + +namespace WebCore { + +enum ProcessIdentifierType { }; +using ProcessIdentifier = ObjectIdentifier<ProcessIdentifierType>; + +namespace Process { + +WEBCORE_EXPORT void setIdentifier(ProcessIdentifier); +WEBCORE_EXPORT ProcessIdentifier identifier(); + +} // namespace Process +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/ProcessIdentity.cpp b/src/javascript/jsc/bindings/webcore/ProcessIdentity.cpp new file mode 100644 index 000000000..de8d32ab4 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/ProcessIdentity.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ProcessIdentity.h" + +#include "Logging.h" + +#if HAVE(TASK_IDENTITY_TOKEN) +#include <mach/mach.h> +#endif + +namespace WebCore { + +ProcessIdentity::ProcessIdentity(CurrentProcessTag) +{ +#if HAVE(TASK_IDENTITY_TOKEN) + task_id_token_t identityToken; + kern_return_t kr = task_create_identity_token(mach_task_self(), &identityToken); + if (kr == KERN_SUCCESS) + m_taskIdToken = MachSendRight::adopt(identityToken); + else + RELEASE_LOG_ERROR(Process, "task_create_identity_token() failed: %{private}s (%x)", mach_error_string(kr), kr); +#endif +} + +ProcessIdentity::operator bool() const +{ +#if HAVE(TASK_IDENTITY_TOKEN) + return static_cast<bool>(m_taskIdToken); +#else + return false; +#endif +} + +#if HAVE(TASK_IDENTITY_TOKEN) +ProcessIdentity::ProcessIdentity(MachSendRight&& taskIdToken) + : m_taskIdToken(WTFMove(taskIdToken)) +{ +} +#endif + +} diff --git a/src/javascript/jsc/bindings/webcore/ProcessIdentity.h b/src/javascript/jsc/bindings/webcore/ProcessIdentity.h new file mode 100644 index 000000000..952e5648e --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/ProcessIdentity.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <optional> + +#if HAVE(TASK_IDENTITY_TOKEN) +#include <wtf/MachSendRight.h> +#else +#include <variant> +#endif + +namespace WebCore { + +// Object to access proof of process identity. +// ProcessIdentifier identifies a process. +// ProcessIdentity grants access to the identity. +// Empty ProcessIdentity does not do anything. +class ProcessIdentity { +public: + // Creates an process identity for current process or empty on error. + enum CurrentProcessTag { CurrentProcess }; + WEBCORE_EXPORT explicit ProcessIdentity(CurrentProcessTag); + + // Creates an empty process identity that does not grant any access. + ProcessIdentity() = default; + + // Returns true for a process identity or false on empty identity. + WEBCORE_EXPORT operator bool() const; + +#if HAVE(TASK_IDENTITY_TOKEN) + task_id_token_t taskIdToken() const { return m_taskIdToken.sendRight(); } +#endif + + template<typename Encoder> void encode(Encoder&) const; + template<typename Decoder> static std::optional<ProcessIdentity> decode(Decoder&); + +private: +#if HAVE(TASK_IDENTITY_TOKEN) + WEBCORE_EXPORT ProcessIdentity(MachSendRight&& taskIdToken); + MachSendRight m_taskIdToken; +#endif +}; + +template<typename Encoder> void ProcessIdentity::encode(Encoder& encoder) const +{ +#if HAVE(TASK_IDENTITY_TOKEN) + encoder << m_taskIdToken; +#else + UNUSED_PARAM(encoder); +#endif +} + +template<typename Decoder> std::optional<ProcessIdentity> ProcessIdentity::decode(Decoder& decoder) +{ +#if HAVE(TASK_IDENTITY_TOKEN) + std::optional<MachSendRight> identitySendRight; + decoder >> identitySendRight; + if (identitySendRight) + return ProcessIdentity { WTFMove(*identitySendRight) }; + return std::nullopt; +#else + UNUSED_PARAM(decoder); + return ProcessIdentity { }; +#endif +} + +} diff --git a/src/javascript/jsc/bindings/webcore/PromiseRejectionEvent.cpp b/src/javascript/jsc/bindings/webcore/PromiseRejectionEvent.cpp new file mode 100644 index 000000000..8c394ff0d --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/PromiseRejectionEvent.cpp @@ -0,0 +1,49 @@ +/* +* Copyright (C) 2017 Apple Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "config.h" +#include "PromiseRejectionEvent.h" + +#include "DOMWrapperWorld.h" +#include "JSDOMPromise.h" +#include <JavaScriptCore/HeapInlines.h> +#include <JavaScriptCore/StrongInlines.h> +#include <wtf/IsoMallocInlines.h> + +namespace WebCore { +using namespace JSC; + +WTF_MAKE_ISO_ALLOCATED_IMPL(PromiseRejectionEvent); + +PromiseRejectionEvent::PromiseRejectionEvent(const AtomString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) + , m_promise(*(initializer.promise)) + , m_reason(initializer.reason) +{ +} + +PromiseRejectionEvent::~PromiseRejectionEvent() = default; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/PromiseRejectionEvent.h b/src/javascript/jsc/bindings/webcore/PromiseRejectionEvent.h new file mode 100644 index 000000000..254bd0ca6 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/PromiseRejectionEvent.h @@ -0,0 +1,62 @@ +/* +* Copyright (C) 2017 Apple Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "Event.h" +#include "JSValueInWrappedObject.h" + +namespace WebCore { + +class DOMPromise; + +class PromiseRejectionEvent final : public Event { + WTF_MAKE_ISO_ALLOCATED(PromiseRejectionEvent); +public: + struct Init : EventInit { + RefPtr<DOMPromise> promise; + JSC::JSValue reason; + }; + + static Ref<PromiseRejectionEvent> create(const AtomString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) + { + return adoptRef(*new PromiseRejectionEvent(type, initializer, isTrusted)); + } + + virtual ~PromiseRejectionEvent(); + + DOMPromise& promise() const { return m_promise.get(); } + const JSValueInWrappedObject& reason() const { return m_reason; } + + EventInterface eventInterface() const override { return PromiseRejectionEventInterfaceType; } + +private: + PromiseRejectionEvent(const AtomString&, const Init&, IsTrusted); + + Ref<DOMPromise> m_promise; + JSValueInWrappedObject m_reason; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/PromiseRejectionEvent.idl b/src/javascript/jsc/bindings/webcore/PromiseRejectionEvent.idl new file mode 100644 index 000000000..40cfc3d04 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/PromiseRejectionEvent.idl @@ -0,0 +1,39 @@ +/* +* Copyright (C) 2017 Apple Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +[ + Exposed=*, + JSCustomMarkFunction, +] interface PromiseRejectionEvent : Event { + constructor(DOMString type, PromiseRejectionEventInit eventInitDict); + + readonly attribute Promise<any> promise; + readonly attribute any reason; +}; + +dictionary PromiseRejectionEventInit : EventInit { + required Promise<any> promise; + any reason; +}; diff --git a/src/javascript/jsc/bindings/webcore/SecurityOrigin.h b/src/javascript/jsc/bindings/webcore/SecurityOrigin.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/SecurityOrigin.h diff --git a/src/javascript/jsc/bindings/webcore/SerializedScriptValue.cpp b/src/javascript/jsc/bindings/webcore/SerializedScriptValue.cpp new file mode 100644 index 000000000..ec6d040f9 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/SerializedScriptValue.cpp @@ -0,0 +1,4617 @@ +/* + * Copyright (C) 2009-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "SerializedScriptValue.h" + +// #include "BlobRegistry.h" +// #include "CryptoKeyAES.h" +// #include "CryptoKeyEC.h" +// #include "CryptoKeyHMAC.h" +// #include "CryptoKeyRSA.h" +// #include "CryptoKeyRSAComponents.h" +// #include "CryptoKeyRaw.h" +// #include "IDBValue.h" +// #include "ImageBitmapBacking.h" +// #include "JSBlob.h" +// #include "JSCryptoKey.h" +#include "JSDOMBinding.h" +#include "JSDOMConvertBufferSource.h" +#include "JSDOMException.h" +#include "JSDOMGlobalObject.h" +#include "JSDOMMatrix.h" +#include "JSDOMPoint.h" +#include "JSDOMQuad.h" +#include "JSDOMRect.h" +#include "JSExecState.h" +#include "JSFile.h" +#include "JSFileList.h" +// #include "JSIDBSerializationGlobalObject.h" +// #include "JSImageBitmap.h" +// #include "JSImageData.h" +// #include "JSMessagePort.h" +#include "JSNavigator.h" +#include "JSRTCCertificate.h" +#include "JSRTCDataChannel.h" +#include "ScriptExecutionContext.h" +#include "SharedBuffer.h" +#include "WebCoreJSClientData.h" +#include <JavaScriptCore/APICast.h> +#include <JavaScriptCore/BigIntObject.h> +#include <JavaScriptCore/BooleanObject.h> +#include <JavaScriptCore/CatchScope.h> +#include <JavaScriptCore/DateInstance.h> +#include <JavaScriptCore/Error.h> +#include <JavaScriptCore/Exception.h> +#include <JavaScriptCore/ExceptionHelpers.h> +#include <JavaScriptCore/IterationKind.h> +#include <JavaScriptCore/JSArrayBuffer.h> +#include <JavaScriptCore/JSArrayBufferView.h> +#include <JavaScriptCore/JSCInlines.h> +#include <JavaScriptCore/JSDataView.h> +#include <JavaScriptCore/JSMapInlines.h> +#include <JavaScriptCore/JSMapIterator.h> +#include <JavaScriptCore/JSSetInlines.h> +#include <JavaScriptCore/JSSetIterator.h> +#include <JavaScriptCore/JSTypedArrays.h> +#include <JavaScriptCore/JSWebAssemblyMemory.h> +#include <JavaScriptCore/JSWebAssemblyModule.h> +#include <JavaScriptCore/ObjectConstructor.h> +#include <JavaScriptCore/PropertyNameArray.h> +#include <JavaScriptCore/RegExp.h> +#include <JavaScriptCore/RegExpObject.h> +#include <JavaScriptCore/TypedArrayInlines.h> +#include <JavaScriptCore/TypedArrays.h> +#include <JavaScriptCore/WasmModule.h> +#include <JavaScriptCore/YarrFlags.h> +#include <limits> +#include <wtf/CheckedArithmetic.h> +#include <wtf/CompletionHandler.h> +#include <wtf/MainThread.h> +#include <wtf/RunLoop.h> +#include <wtf/Vector.h> +#include <wtf/threads/BinarySemaphore.h> + +#if USE(CG) +#include <CoreGraphics/CoreGraphics.h> +#endif + +#if PLATFORM(COCOA) +#include <CoreFoundation/CoreFoundation.h> +#endif + +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) +#include "JSOffscreenCanvas.h" +#include "OffscreenCanvas.h" +#endif + +#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) || CPU(NEEDS_ALIGNED_ACCESS) +#define ASSUME_LITTLE_ENDIAN 0 +#else +#define ASSUME_LITTLE_ENDIAN 1 +#endif + +namespace WebCore { + +using namespace JSC; + +DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(SerializedScriptValue); + +static const unsigned maximumFilterRecursion = 40000; + +enum class SerializationReturnCode { + SuccessfullyCompleted, + StackOverflowError, + InterruptedExecutionError, + ValidationError, + ExistingExceptionError, + DataCloneError, + UnspecifiedError +}; + +enum WalkerState { StateUnknown, + ArrayStartState, + ArrayStartVisitMember, + ArrayEndVisitMember, + ObjectStartState, + ObjectStartVisitMember, + ObjectEndVisitMember, + MapDataStartVisitEntry, + MapDataEndVisitKey, + MapDataEndVisitValue, + SetDataStartVisitEntry, + SetDataEndVisitKey }; + +// These can't be reordered, and any new types must be added to the end of the list +// When making changes to these lists please cover your new type(s) in the API test "IndexedDB.StructuredCloneBackwardCompatibility" +enum SerializationTag { + ArrayTag = 1, + ObjectTag = 2, + UndefinedTag = 3, + NullTag = 4, + IntTag = 5, + ZeroTag = 6, + OneTag = 7, + FalseTag = 8, + TrueTag = 9, + DoubleTag = 10, + DateTag = 11, + FileTag = 12, + FileListTag = 13, + ImageDataTag = 14, + BlobTag = 15, + StringTag = 16, + EmptyStringTag = 17, + RegExpTag = 18, + ObjectReferenceTag = 19, + MessagePortReferenceTag = 20, + ArrayBufferTag = 21, + ArrayBufferViewTag = 22, + ArrayBufferTransferTag = 23, + TrueObjectTag = 24, + FalseObjectTag = 25, + StringObjectTag = 26, + EmptyStringObjectTag = 27, + NumberObjectTag = 28, + SetObjectTag = 29, + MapObjectTag = 30, + NonMapPropertiesTag = 31, + NonSetPropertiesTag = 32, +#if ENABLE(WEB_CRYPTO) + CryptoKeyTag = 33, +#endif + SharedArrayBufferTag = 34, +#if ENABLE(WEBASSEMBLY) + WasmModuleTag = 35, +#endif + DOMPointReadOnlyTag = 36, + DOMPointTag = 37, + DOMRectReadOnlyTag = 38, + DOMRectTag = 39, + DOMMatrixReadOnlyTag = 40, + DOMMatrixTag = 41, + DOMQuadTag = 42, + ImageBitmapTransferTag = 43, +#if ENABLE(WEB_RTC) + RTCCertificateTag = 44, +#endif + ImageBitmapTag = 45, +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + OffscreenCanvasTransferTag = 46, +#endif + BigIntTag = 47, + BigIntObjectTag = 48, +#if ENABLE(WEBASSEMBLY) + WasmMemoryTag = 49, +#endif +#if ENABLE(WEB_RTC) + RTCDataChannelTransferTag = 50, +#endif + DOMExceptionTag = 51, + ErrorTag = 255 +}; + +enum ArrayBufferViewSubtag { + DataViewTag = 0, + Int8ArrayTag = 1, + Uint8ArrayTag = 2, + Uint8ClampedArrayTag = 3, + Int16ArrayTag = 4, + Uint16ArrayTag = 5, + Int32ArrayTag = 6, + Uint32ArrayTag = 7, + Float32ArrayTag = 8, + Float64ArrayTag = 9, + BigInt64ArrayTag = 10, + BigUint64ArrayTag = 11, +}; + +static unsigned typedArrayElementSize(ArrayBufferViewSubtag tag) +{ + switch (tag) { + case DataViewTag: + case Int8ArrayTag: + case Uint8ArrayTag: + case Uint8ClampedArrayTag: + return 1; + case Int16ArrayTag: + case Uint16ArrayTag: + return 2; + case Int32ArrayTag: + case Uint32ArrayTag: + case Float32ArrayTag: + return 4; + case Float64ArrayTag: + case BigInt64ArrayTag: + case BigUint64ArrayTag: + return 8; + default: + return 0; + } +} + +enum class PredefinedColorSpaceTag : uint8_t { + SRGB = 0 +#if ENABLE(PREDEFINED_COLOR_SPACE_DISPLAY_P3) + , + DisplayP3 = 1 +#endif +}; + +enum DestinationColorSpaceTag { + DestinationColorSpaceSRGBTag = 0, +#if ENABLE(DESTINATION_COLOR_SPACE_LINEAR_SRGB) + DestinationColorSpaceLinearSRGBTag = 1, +#endif +#if ENABLE(DESTINATION_COLOR_SPACE_DISPLAY_P3) + DestinationColorSpaceDisplayP3Tag = 2, +#endif +#if PLATFORM(COCOA) + DestinationColorSpaceCGColorSpaceNameTag = 3, + DestinationColorSpaceCGColorSpacePropertyListTag = 4, +#endif +}; + +#if ENABLE(WEB_CRYPTO) + +const uint32_t currentKeyFormatVersion = 1; + +enum class CryptoKeyClassSubtag { + HMAC = 0, + AES = 1, + RSA = 2, + EC = 3, + Raw = 4, +}; +const uint8_t cryptoKeyClassSubtagMaximumValue = 4; + +enum class CryptoKeyAsymmetricTypeSubtag { + Public = 0, + Private = 1 +}; +const uint8_t cryptoKeyAsymmetricTypeSubtagMaximumValue = 1; + +enum class CryptoKeyUsageTag { + Encrypt = 0, + Decrypt = 1, + Sign = 2, + Verify = 3, + DeriveKey = 4, + DeriveBits = 5, + WrapKey = 6, + UnwrapKey = 7 +}; +const uint8_t cryptoKeyUsageTagMaximumValue = 7; + +enum class CryptoAlgorithmIdentifierTag { + RSAES_PKCS1_v1_5 = 0, + RSASSA_PKCS1_v1_5 = 1, + RSA_PSS = 2, + RSA_OAEP = 3, + ECDSA = 4, + ECDH = 5, + AES_CTR = 6, + AES_CBC = 7, + AES_GCM = 9, + AES_CFB = 10, + AES_KW = 11, + HMAC = 12, + SHA_1 = 14, + SHA_224 = 15, + SHA_256 = 16, + SHA_384 = 17, + SHA_512 = 18, + HKDF = 20, + PBKDF2 = 21, +}; +const uint8_t cryptoAlgorithmIdentifierTagMaximumValue = 21; + +static unsigned countUsages(CryptoKeyUsageBitmap usages) +{ + // Fast bit count algorithm for sparse bit maps. + unsigned count = 0; + while (usages) { + usages = usages & (usages - 1); + ++count; + } + return count; +} + +#endif + +/* CurrentVersion tracks the serialization version so that persistent stores + * are able to correctly bail out in the case of encountering newer formats. + * + * Initial version was 1. + * Version 2. added the ObjectReferenceTag and support for serialization of cyclic graphs. + * Version 3. added the FalseObjectTag, TrueObjectTag, NumberObjectTag, StringObjectTag + * and EmptyStringObjectTag for serialization of Boolean, Number and String objects. + * Version 4. added support for serializing non-index properties of arrays. + * Version 5. added support for Map and Set types. + * Version 6. added support for 8-bit strings. + * Version 7. added support for File's lastModified attribute. + * Version 8. added support for ImageData's colorSpace attribute. + * Version 9. added support for ImageBitmap color space. + * Version 10. changed the length (and offsets) of ArrayBuffers (and ArrayBufferViews) from 32 to 64 bits + */ +static const unsigned CurrentVersion = 10; +static const unsigned TerminatorTag = 0xFFFFFFFF; +static const unsigned StringPoolTag = 0xFFFFFFFE; +static const unsigned NonIndexPropertiesTag = 0xFFFFFFFD; + +// The high bit of a StringData's length determines the character size. +static const unsigned StringDataIs8BitFlag = 0x80000000; + +/* + * Object serialization is performed according to the following grammar, all tags + * are recorded as a single uint8_t. + * + * IndexType (used for the object pool and StringData's constant pool) is the + * minimum sized unsigned integer type required to represent the maximum index + * in the constant pool. + * + * SerializedValue :- <CurrentVersion:uint32_t> Value + * Value :- Array | Object | Map | Set | Terminal + * + * Array :- + * ArrayTag <length:uint32_t>(<index:uint32_t><value:Value>)* TerminatorTag + * + * Object :- + * ObjectTag (<name:StringData><value:Value>)* TerminatorTag + * + * Map :- MapObjectTag MapData + * + * Set :- SetObjectTag SetData + * + * MapData :- (<key:Value><value:Value>)* NonMapPropertiesTag (<name:StringData><value:Value>)* TerminatorTag + * SetData :- (<key:Value>)* NonSetPropertiesTag (<name:StringData><value:Value>)* TerminatorTag + * + * Terminal :- + * UndefinedTag + * | NullTag + * | IntTag <value:int32_t> + * | ZeroTag + * | OneTag + * | FalseTag + * | TrueTag + * | FalseObjectTag + * | TrueObjectTag + * | DoubleTag <value:double> + * | NumberObjectTag <value:double> + * | DateTag <value:double> + * | String + * | EmptyStringTag + * | EmptyStringObjectTag + * | BigInt + * | File + * | FileList + * | ImageData + * | Blob + * | ObjectReference + * | MessagePortReferenceTag <value:uint32_t> + * | ArrayBuffer + * | ArrayBufferViewTag ArrayBufferViewSubtag <byteOffset:uint64_t> <byteLength:uint64_t> (ArrayBuffer | ObjectReference) + * | CryptoKeyTag <wrappedKeyLength:uint32_t> <factor:byte{wrappedKeyLength}> + * | DOMPoint + * | DOMRect + * | DOMMatrix + * | DOMQuad + * | ImageBitmapTransferTag <value:uint32_t> + * | RTCCertificateTag + * | ImageBitmapTag <originClean:uint8_t> <logicalWidth:int32_t> <logicalHeight:int32_t> <resolutionScale:double> DestinationColorSpace <byteLength:uint32_t>(<imageByteData:uint8_t>) + * | OffscreenCanvasTransferTag <value:uint32_t> + * | WasmMemoryTag <value:uint32_t> + * | RTCDataChannelTransferTag <processIdentifier:uint64_t><rtcDataChannelIdentifier:uint64_t><label:String> + * | DOMExceptionTag <message:String> <name:String> + * + * Inside certificate, data is serialized in this format as per spec: + * + * <expires:double> <certificate:StringData> <origin:StringData> <keyingMaterial:StringData> + * We also add fingerprints to make sure we expose to JavaScript the same information. + * + * Inside wrapped crypto key, data is serialized in this format: + * + * <keyFormatVersion:uint32_t> <extractable:int32_t> <usagesCount:uint32_t> <usages:byte{usagesCount}> CryptoKeyClassSubtag (CryptoKeyHMAC | CryptoKeyAES | CryptoKeyRSA) + * + * String :- + * EmptyStringTag + * StringTag StringData + * + * StringObject: + * EmptyStringObjectTag + * StringObjectTag StringData + * + * StringData :- + * StringPoolTag <cpIndex:IndexType> + * (not (TerminatorTag | StringPoolTag))<is8Bit:uint32_t:1><length:uint32_t:31><characters:CharType{length}> // Added to constant pool when seen, string length 0xFFFFFFFF is disallowed + * + * BigInt :- + * BigIntTag BigIntData + * BigIntObjectTag BigIntData + * + * BigIntData :- + * <sign:uint8_t> <lengthInUint64:uint32_t> <contents:uint64_t{lengthInUint64}> + * + * File :- + * FileTag FileData + * + * FileData :- + * <path:StringData> <url:StringData> <type:StringData> <name:StringData> <lastModified:double> + * + * FileList :- + * FileListTag <length:uint32_t>(<file:FileData>){length} + * + * ImageData :- + * ImageDataTag <width:int32_t> <height:int32_t> <length:uint32_t> <data:uint8_t{length}> <colorSpace:PredefinedColorSpaceTag> + * + * Blob :- + * BlobTag <url:StringData><type:StringData><size:long long> + * + * RegExp :- + * RegExpTag <pattern:StringData><flags:StringData> + * + * ObjectReference :- + * ObjectReferenceTag <opIndex:IndexType> + * + * ArrayBuffer :- + * ArrayBufferTag <length:uint64_t> <contents:byte{length}> + * ArrayBufferTransferTag <value:uint32_t> + * SharedArrayBufferTag <value:uint32_t> + * + * CryptoKeyHMAC :- + * <keySize:uint32_t> <keyData:byte{keySize}> CryptoAlgorithmIdentifierTag // Algorithm tag inner hash function. + * + * CryptoKeyAES :- + * CryptoAlgorithmIdentifierTag <keySize:uint32_t> <keyData:byte{keySize}> + * + * CryptoKeyRSA :- + * CryptoAlgorithmIdentifierTag <isRestrictedToHash:int32_t> CryptoAlgorithmIdentifierTag? CryptoKeyAsymmetricTypeSubtag CryptoKeyRSAPublicComponents CryptoKeyRSAPrivateComponents? + * + * CryptoKeyRSAPublicComponents :- + * <modulusSize:uint32_t> <modulus:byte{modulusSize}> <exponentSize:uint32_t> <exponent:byte{exponentSize}> + * + * CryptoKeyRSAPrivateComponents :- + * <privateExponentSize:uint32_t> <privateExponent:byte{privateExponentSize}> <primeCount:uint32_t> FirstPrimeInfo? PrimeInfo{primeCount - 1} + * + * // CRT data could be computed from prime factors. It is only serialized to reuse a code path that's needed for JWK. + * FirstPrimeInfo :- + * <factorSize:uint32_t> <factor:byte{factorSize}> <crtExponentSize:uint32_t> <crtExponent:byte{crtExponentSize}> + * + * PrimeInfo :- + * <factorSize:uint32_t> <factor:byte{factorSize}> <crtExponentSize:uint32_t> <crtExponent:byte{crtExponentSize}> <crtCoefficientSize:uint32_t> <crtCoefficient:byte{crtCoefficientSize}> + * + * CryptoKeyEC :- + * CryptoAlgorithmIdentifierTag <namedCurve:StringData> CryptoKeyAsymmetricTypeSubtag <keySize:uint32_t> <keyData:byte{keySize}> + * + * CryptoKeyRaw :- + * CryptoAlgorithmIdentifierTag <keySize:uint32_t> <keyData:byte{keySize}> + * + * DOMPoint :- + * DOMPointReadOnlyTag DOMPointData + * | DOMPointTag DOMPointData + * + * DOMPointData :- + * <x:double> <y:double> <z:double> <w:double> + * + * DOMRect :- + * DOMRectReadOnlyTag DOMRectData + * | DOMRectTag DOMRectData + * + * DOMRectData :- + * <x:double> <y:double> <width:double> <height:double> + * + * DOMMatrix :- + * DOMMatrixReadOnlyTag DOMMatrixData + * | DOMMatrixTag DOMMatrixData + * + * DOMMatrixData :- + * <is2D:uint8_t:true> <m11:double> <m12:double> <m21:double> <m22:double> <m41:double> <m42:double> + * | <is2D:uint8_t:false> <m11:double> <m12:double> <m13:double> <m14:double> <m21:double> <m22:double> <m23:double> <m24:double> <m31:double> <m32:double> <m33:double> <m34:double> <m41:double> <m42:double> <m43:double> <m44:double> + * + * DOMQuad :- + * DOMQuadTag DOMQuadData + * + * DOMQuadData :- + * <p1:DOMPointData> <p2:DOMPointData> <p3:DOMPointData> <p4:DOMPointData> + * + * DestinationColorSpace :- + * DestinationColorSpaceSRGBTag + * | DestinationColorSpaceLinearSRGBTag + * | DestinationColorSpaceDisplayP3Tag + * | DestinationColorSpaceCGColorSpaceNameTag <nameDataLength:uint32_t> <nameData:uint8_t>{nameDataLength} + * | DestinationColorSpaceCGColorSpacePropertyListTag <propertyListDataLength:uint32_t> <propertyListData:uint8_t>{propertyListDataLength} + */ + +using DeserializationResult = std::pair<JSC::JSValue, SerializationReturnCode>; + +class CloneBase { +protected: + CloneBase(JSGlobalObject* lexicalGlobalObject) + : m_lexicalGlobalObject(lexicalGlobalObject) + , m_failed(false) + { + } + + void fail() + { + m_failed = true; + } + + JSGlobalObject* m_lexicalGlobalObject; + bool m_failed; + MarkedArgumentBuffer m_gcBuffer; +}; + +#if ENABLE(WEB_CRYPTO) +static bool wrapCryptoKey(JSGlobalObject* lexicalGlobalObject, const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey) +{ + auto context = executionContext(lexicalGlobalObject); + return context && context->wrapCryptoKey(key, wrappedKey); +} + +static bool unwrapCryptoKey(JSGlobalObject* lexicalGlobalObject, const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key) +{ + auto context = executionContext(lexicalGlobalObject); + return context && context->unwrapCryptoKey(wrappedKey, key); +} +#endif + +#if ASSUME_LITTLE_ENDIAN +template<typename T> static void writeLittleEndian(Vector<uint8_t>& buffer, T value) +{ + buffer.append(reinterpret_cast<uint8_t*>(&value), sizeof(value)); +} +#else +template<typename T> static void writeLittleEndian(Vector<uint8_t>& buffer, T value) +{ + for (unsigned i = 0; i < sizeof(T); i++) { + buffer.append(value & 0xFF); + value >>= 8; + } +} +#endif + +template<> void writeLittleEndian<uint8_t>(Vector<uint8_t>& buffer, uint8_t value) +{ + buffer.append(value); +} + +template<typename T> static bool writeLittleEndian(Vector<uint8_t>& buffer, const T* values, uint32_t length) +{ + if (length > std::numeric_limits<uint32_t>::max() / sizeof(T)) + return false; + +#if ASSUME_LITTLE_ENDIAN + buffer.append(reinterpret_cast<const uint8_t*>(values), length * sizeof(T)); +#else + for (unsigned i = 0; i < length; i++) { + T value = values[i]; + for (unsigned j = 0; j < sizeof(T); j++) { + buffer.append(static_cast<uint8_t>(value & 0xFF)); + value >>= 8; + } + } +#endif + return true; +} + +template<> bool writeLittleEndian<uint8_t>(Vector<uint8_t>& buffer, const uint8_t* values, uint32_t length) +{ + buffer.append(values, length); + return true; +} + +class CloneSerializer : CloneBase { +public: + static SerializationReturnCode serialize(JSGlobalObject* lexicalGlobalObject, JSValue value, Vector<RefPtr<MessagePort>>& messagePorts, Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers, const Vector<RefPtr<ImageBitmap>>& imageBitmaps, +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + const Vector<RefPtr<OffscreenCanvas>>& offscreenCanvases, +#endif +#if ENABLE(WEB_RTC) + const Vector<Ref<RTCDataChannel>>& rtcDataChannels, +#endif +#if ENABLE(WEBASSEMBLY) + WasmModuleArray& wasmModules, + WasmMemoryHandleArray& wasmMemoryHandles, +#endif + /* Vector<BlobURLHandle>& blobHandles,*/ Vector<uint8_t>& out, SerializationContext context, ArrayBufferContentsArray& sharedBuffers) + { + CloneSerializer serializer(lexicalGlobalObject, messagePorts, arrayBuffers, imageBitmaps, +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + offscreenCanvases, +#endif +#if ENABLE(WEB_RTC) + rtcDataChannels, +#endif +#if ENABLE(WEBASSEMBLY) + wasmModules, + wasmMemoryHandles, +#endif + blobHandles, out, context, sharedBuffers); + return serializer.serialize(value); + } + + static bool serialize(StringView string, Vector<uint8_t>& out) + { + writeLittleEndian(out, CurrentVersion); + if (string.isEmpty()) { + writeLittleEndian<uint8_t>(out, EmptyStringTag); + return true; + } + writeLittleEndian<uint8_t>(out, StringTag); + if (string.is8Bit()) { + writeLittleEndian(out, string.length() | StringDataIs8BitFlag); + return writeLittleEndian(out, string.characters8(), string.length()); + } + writeLittleEndian(out, string.length()); + return writeLittleEndian(out, string.characters16(), string.length()); + } + +private: + typedef HashMap<JSObject*, uint32_t> ObjectPool; + + CloneSerializer(JSGlobalObject* lexicalGlobalObject, Vector<RefPtr<MessagePort>>& messagePorts, Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers, /*const Vector<RefPtr<ImageBitmap>>& imageBitmaps,*/ +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + const Vector<RefPtr<OffscreenCanvas>>& offscreenCanvases, +#endif +#if ENABLE(WEB_RTC) + const Vector<Ref<RTCDataChannel>>& rtcDataChannels, +#endif +#if ENABLE(WEBASSEMBLY) + WasmModuleArray& wasmModules, + WasmMemoryHandleArray& wasmMemoryHandles, +#endif + /*Vector<BlobURLHandle>& blobHandles,*/ Vector<uint8_t>& out, SerializationContext context, ArrayBufferContentsArray& sharedBuffers) + : CloneBase(lexicalGlobalObject) + , m_buffer(out) + // , m_blobHandles(blobHandles) + , m_emptyIdentifier(Identifier::fromString(lexicalGlobalObject->vm(), emptyString())) + , m_context(context) + , m_sharedBuffers(sharedBuffers) +#if ENABLE(WEBASSEMBLY) + , m_wasmModules(wasmModules) + , m_wasmMemoryHandles(wasmMemoryHandles) +#endif + { + write(CurrentVersion); + fillTransferMap(messagePorts, m_transferredMessagePorts); + fillTransferMap(arrayBuffers, m_transferredArrayBuffers); + // fillTransferMap(imageBitmaps, m_transferredImageBitmaps); + // #if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + // fillTransferMap(offscreenCanvases, m_transferredOffscreenCanvases); + // #endif + // #if ENABLE(WEB_RTC) + // fillTransferMap(rtcDataChannels, m_transferredRTCDataChannels); + // #endif + } + + template<class T> + void fillTransferMap(const Vector<RefPtr<T>>& input, ObjectPool& result) + { + if (input.isEmpty()) + return; + JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(m_lexicalGlobalObject); + for (size_t i = 0; i < input.size(); i++) { + JSC::JSValue value = toJS(m_lexicalGlobalObject, globalObject, input[i].get()); + JSC::JSObject* obj = value.getObject(); + if (obj && !result.contains(obj)) + result.add(obj, i); + } + } + template<class T> + void fillTransferMap(const Vector<Ref<T>>& input, ObjectPool& result) + { + if (input.isEmpty()) + return; + JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(m_lexicalGlobalObject); + for (size_t i = 0; i < input.size(); i++) { + JSC::JSValue value = toJS(m_lexicalGlobalObject, globalObject, input[i].get()); + JSC::JSObject* obj = value.getObject(); + if (obj && !result.contains(obj)) + result.add(obj, i); + } + } + + SerializationReturnCode serialize(JSValue in); + + bool isArray(VM& vm, JSValue value) + { + if (!value.isObject()) + return false; + JSObject* object = asObject(value); + return object->inherits<JSArray>(vm); + } + + bool isMap(VM& vm, JSValue value) + { + if (!value.isObject()) + return false; + JSObject* object = asObject(value); + return object->inherits<JSMap>(vm); + } + bool isSet(VM& vm, JSValue value) + { + if (!value.isObject()) + return false; + JSObject* object = asObject(value); + return object->inherits<JSSet>(vm); + } + + bool checkForDuplicate(JSObject* object) + { + // Record object for graph reconstruction + ObjectPool::const_iterator found = m_objectPool.find(object); + + // Handle duplicate references + if (found != m_objectPool.end()) { + write(ObjectReferenceTag); + ASSERT(found->value < m_objectPool.size()); + writeObjectIndex(found->value); + return true; + } + + return false; + } + + void recordObject(JSObject* object) + { + m_objectPool.add(object, m_objectPool.size()); + m_gcBuffer.appendWithCrashOnOverflow(object); + } + + bool startObjectInternal(JSObject* object) + { + if (checkForDuplicate(object)) + return false; + recordObject(object); + return true; + } + + bool startObject(JSObject* object) + { + if (!startObjectInternal(object)) + return false; + write(ObjectTag); + return true; + } + + bool startArray(JSArray* array) + { + if (!startObjectInternal(array)) + return false; + + unsigned length = array->length(); + write(ArrayTag); + write(length); + return true; + } + + bool startSet(JSSet* set) + { + if (!startObjectInternal(set)) + return false; + + write(SetObjectTag); + return true; + } + + bool startMap(JSMap* map) + { + if (!startObjectInternal(map)) + return false; + + write(MapObjectTag); + return true; + } + + void endObject() + { + write(TerminatorTag); + } + + JSValue getProperty(VM& vm, JSObject* object, const Identifier& propertyName) + { + PropertySlot slot(object, PropertySlot::InternalMethodType::Get); + if (object->methodTable(vm)->getOwnPropertySlot(object, m_lexicalGlobalObject, propertyName, slot)) + return slot.getValue(m_lexicalGlobalObject, propertyName); + return JSValue(); + } + + void dumpImmediate(JSValue value, SerializationReturnCode& code) + { + if (value.isNull()) { + write(NullTag); + return; + } + if (value.isUndefined()) { + write(UndefinedTag); + return; + } + if (value.isNumber()) { + if (value.isInt32()) { + if (!value.asInt32()) + write(ZeroTag); + else if (value.asInt32() == 1) + write(OneTag); + else { + write(IntTag); + write(static_cast<uint32_t>(value.asInt32())); + } + } else { + write(DoubleTag); + write(value.asDouble()); + } + return; + } + if (value.isBoolean()) { + if (value.isTrue()) + write(TrueTag); + else + write(FalseTag); + return; + } +#if USE(BIGINT32) + if (value.isBigInt32()) { + write(BigIntTag); + dumpBigIntData(value); + return; + } +#endif + + // Make any new primitive extension safe by throwing an error. + code = SerializationReturnCode::DataCloneError; + } + + void dumpString(const String& string) + { + if (string.isEmpty()) + write(EmptyStringTag); + else { + write(StringTag); + write(string); + } + } + + void dumpStringObject(const String& string) + { + if (string.isEmpty()) + write(EmptyStringObjectTag); + else { + write(StringObjectTag); + write(string); + } + } + + void dumpBigIntData(JSValue value) + { + ASSERT(value.isBigInt()); +#if USE(BIGINT32) + if (value.isBigInt32()) { + dumpBigInt32Data(value.bigInt32AsInt32()); + return; + } +#endif + dumpHeapBigIntData(jsCast<JSBigInt*>(value)); + } + +#if USE(BIGINT32) + void dumpBigInt32Data(int32_t integer) + { + write(static_cast<uint8_t>(integer < 0)); + if (!integer) { + write(static_cast<uint32_t>(0)); // Length-in-uint64_t + return; + } + write(static_cast<uint32_t>(1)); // Length-in-uint64_t + int64_t value = static_cast<int64_t>(integer); + if (value < 0) + value = -value; + write(static_cast<uint64_t>(value)); + } +#endif + + void dumpHeapBigIntData(JSBigInt* bigInt) + { + write(static_cast<uint8_t>(bigInt->sign())); + if constexpr (sizeof(JSBigInt::Digit) == sizeof(uint64_t)) { + write(static_cast<uint32_t>(bigInt->length())); + for (unsigned index = 0; index < bigInt->length(); ++index) + write(static_cast<uint64_t>(bigInt->digit(index))); + } else { + ASSERT(sizeof(JSBigInt::Digit) == sizeof(uint32_t)); + uint32_t lengthInUint64 = bigInt->length() / 2; + if (bigInt->length() & 0x1) + ++lengthInUint64; + write(lengthInUint64); + uint64_t value = 0; + for (unsigned index = 0; index < bigInt->length(); ++index) { + if (!(index & 0x1)) + value = bigInt->digit(index); + else { + value = (static_cast<uint64_t>(bigInt->digit(index)) << 32) | value; + write(static_cast<uint64_t>(value)); + value = 0; + } + } + if (bigInt->length() & 0x1) + write(static_cast<uint64_t>(value)); + } + } + + JSC::JSValue toJSArrayBuffer(ArrayBuffer& arrayBuffer) + { + auto& vm = m_lexicalGlobalObject->vm(); + auto* globalObject = m_lexicalGlobalObject; + if (globalObject->inherits<JSDOMGlobalObject>(vm)) + return toJS(globalObject, jsCast<JSDOMGlobalObject*>(globalObject), &arrayBuffer); + + if (auto* buffer = arrayBuffer.m_wrapper.get()) + return buffer; + + return JSC::JSArrayBuffer::create(vm, globalObject->arrayBufferStructure(arrayBuffer.sharingMode()), &arrayBuffer); + } + + bool dumpArrayBufferView(JSObject* obj, SerializationReturnCode& code) + { + VM& vm = m_lexicalGlobalObject->vm(); + write(ArrayBufferViewTag); + if (obj->inherits<JSDataView>(vm)) + write(DataViewTag); + else if (obj->inherits<JSUint8ClampedArray>(vm)) + write(Uint8ClampedArrayTag); + else if (obj->inherits<JSInt8Array>(vm)) + write(Int8ArrayTag); + else if (obj->inherits<JSUint8Array>(vm)) + write(Uint8ArrayTag); + else if (obj->inherits<JSInt16Array>(vm)) + write(Int16ArrayTag); + else if (obj->inherits<JSUint16Array>(vm)) + write(Uint16ArrayTag); + else if (obj->inherits<JSInt32Array>(vm)) + write(Int32ArrayTag); + else if (obj->inherits<JSUint32Array>(vm)) + write(Uint32ArrayTag); + else if (obj->inherits<JSFloat32Array>(vm)) + write(Float32ArrayTag); + else if (obj->inherits<JSFloat64Array>(vm)) + write(Float64ArrayTag); + else if (obj->inherits<JSBigInt64Array>(vm)) + write(BigInt64ArrayTag); + else if (obj->inherits<JSBigUint64Array>(vm)) + write(BigUint64ArrayTag); + else + return false; + + RefPtr<ArrayBufferView> arrayBufferView = toPossiblySharedArrayBufferView(vm, obj); + uint64_t byteOffset = arrayBufferView->byteOffset(); + write(byteOffset); + uint64_t byteLength = arrayBufferView->byteLength(); + write(byteLength); + RefPtr<ArrayBuffer> arrayBuffer = arrayBufferView->possiblySharedBuffer(); + if (!arrayBuffer) { + code = SerializationReturnCode::ValidationError; + return true; + } + + return dumpIfTerminal(toJSArrayBuffer(*arrayBuffer), code); + } + + // void dumpDOMPoint(const DOMPointReadOnly& point) + // { + // write(point.x()); + // write(point.y()); + // write(point.z()); + // write(point.w()); + // } + + // void dumpDOMPoint(JSObject* obj) + // { + // VM& vm = m_lexicalGlobalObject->vm(); + // if (obj->inherits<JSDOMPoint>(vm)) + // write(DOMPointTag); + // else + // write(DOMPointReadOnlyTag); + + // dumpDOMPoint(jsCast<JSDOMPointReadOnly*>(obj)->wrapped()); + // } + + // void dumpDOMRect(JSObject* obj) + // { + // VM& vm = m_lexicalGlobalObject->vm(); + // if (obj->inherits<JSDOMRect>(vm)) + // write(DOMRectTag); + // else + // write(DOMRectReadOnlyTag); + + // auto& rect = jsCast<JSDOMRectReadOnly*>(obj)->wrapped(); + // write(rect.x()); + // write(rect.y()); + // write(rect.width()); + // write(rect.height()); + // } + + // void dumpDOMMatrix(JSObject* obj) + // { + // VM& vm = m_lexicalGlobalObject->vm(); + // if (obj->inherits<JSDOMMatrix>(vm)) + // write(DOMMatrixTag); + // else + // write(DOMMatrixReadOnlyTag); + + // auto& matrix = jsCast<JSDOMMatrixReadOnly*>(obj)->wrapped(); + // bool is2D = matrix.is2D(); + // write(static_cast<uint8_t>(is2D)); + // if (is2D) { + // write(matrix.m11()); + // write(matrix.m12()); + // write(matrix.m21()); + // write(matrix.m22()); + // write(matrix.m41()); + // write(matrix.m42()); + // } else { + // write(matrix.m11()); + // write(matrix.m12()); + // write(matrix.m13()); + // write(matrix.m14()); + // write(matrix.m21()); + // write(matrix.m22()); + // write(matrix.m23()); + // write(matrix.m24()); + // write(matrix.m31()); + // write(matrix.m32()); + // write(matrix.m33()); + // write(matrix.m34()); + // write(matrix.m41()); + // write(matrix.m42()); + // write(matrix.m43()); + // write(matrix.m44()); + // } + // } + + // void dumpDOMQuad(JSObject* obj) + // { + // write(DOMQuadTag); + + // auto& quad = jsCast<JSDOMQuad*>(obj)->wrapped(); + // dumpDOMPoint(quad.p1()); + // dumpDOMPoint(quad.p2()); + // dumpDOMPoint(quad.p3()); + // dumpDOMPoint(quad.p4()); + // } + + // void dumpImageBitmap(JSObject* obj, SerializationReturnCode& code) + // { + // auto index = m_transferredImageBitmaps.find(obj); + // if (index != m_transferredImageBitmaps.end()) { + // write(ImageBitmapTransferTag); + // write(index->value); + // return; + // } + + // auto& imageBitmap = jsCast<JSImageBitmap*>(obj)->wrapped(); + // auto* buffer = imageBitmap.buffer(); + + // if (!buffer) { + // code = SerializationReturnCode::ValidationError; + // return; + // } + + // // FIXME: We should try to avoid converting pixel format. + // PixelBufferFormat format { AlphaPremultiplication::Premultiplied, PixelFormat::RGBA8, buffer->colorSpace() }; + // const IntSize& logicalSize = buffer->truncatedLogicalSize(); + // auto pixelBuffer = buffer->getPixelBuffer(format, { IntPoint::zero(), logicalSize }); + // if (!pixelBuffer) { + // code = SerializationReturnCode::ValidationError; + // return; + // } + + // auto arrayBuffer = pixelBuffer->data().possiblySharedBuffer(); + // if (!arrayBuffer) { + // code = SerializationReturnCode::ValidationError; + // return; + // } + + // write(ImageBitmapTag); + // write(static_cast<uint8_t>(imageBitmap.serializationState().toRaw())); + // write(static_cast<int32_t>(logicalSize.width())); + // write(static_cast<int32_t>(logicalSize.height())); + // write(static_cast<double>(buffer->resolutionScale())); + // write(buffer->colorSpace()); + + // CheckedUint32 byteLength = arrayBuffer->byteLength(); + // if (byteLength.hasOverflowed()) { + // code = SerializationReturnCode::ValidationError; + // return; + // } + // write(byteLength); + // write(static_cast<const uint8_t*>(arrayBuffer->data()), byteLength); + // } + +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + void dumpOffscreenCanvas(JSObject* obj, SerializationReturnCode& code) + { + auto index = m_transferredOffscreenCanvases.find(obj); + if (index != m_transferredOffscreenCanvases.end()) { + write(OffscreenCanvasTransferTag); + write(index->value); + return; + } + + code = SerializationReturnCode::DataCloneError; + } +#endif + +#if ENABLE(WEB_RTC) + void dumpRTCDataChannel(JSObject* obj, SerializationReturnCode& code) + { + auto index = m_transferredRTCDataChannels.find(obj); + if (index != m_transferredRTCDataChannels.end()) { + write(RTCDataChannelTransferTag); + write(index->value); + return; + } + + code = SerializationReturnCode::DataCloneError; + } +#endif + + void dumpDOMException(JSObject* obj, SerializationReturnCode& code) + { + if (auto* exception = JSDOMException::toWrapped(m_lexicalGlobalObject->vm(), obj)) { + write(DOMExceptionTag); + write(exception->message()); + write(exception->name()); + return; + } + + code = SerializationReturnCode::DataCloneError; + } + + bool dumpIfTerminal(JSValue value, SerializationReturnCode& code) + { + if (!value.isCell()) { + dumpImmediate(value, code); + return true; + } + ASSERT(value.isCell()); + + if (value.isString()) { + dumpString(asString(value)->value(m_lexicalGlobalObject)); + return true; + } + + if (value.isHeapBigInt()) { + write(BigIntTag); + dumpBigIntData(value); + return true; + } + + if (value.isSymbol()) { + code = SerializationReturnCode::DataCloneError; + return true; + } + + VM& vm = m_lexicalGlobalObject->vm(); + if (isArray(vm, value)) + return false; + + if (value.isObject()) { + auto* obj = asObject(value); + if (auto* dateObject = jsDynamicCast<DateInstance*>(vm, obj)) { + write(DateTag); + write(dateObject->internalNumber()); + return true; + } + if (auto* booleanObject = jsDynamicCast<BooleanObject*>(vm, obj)) { + if (!startObjectInternal(booleanObject)) // handle duplicates + return true; + write(booleanObject->internalValue().toBoolean(m_lexicalGlobalObject) ? TrueObjectTag : FalseObjectTag); + return true; + } + if (auto* stringObject = jsDynamicCast<StringObject*>(vm, obj)) { + if (!startObjectInternal(stringObject)) // handle duplicates + return true; + String str = asString(stringObject->internalValue())->value(m_lexicalGlobalObject); + dumpStringObject(str); + return true; + } + if (auto* numberObject = jsDynamicCast<NumberObject*>(vm, obj)) { + if (!startObjectInternal(numberObject)) // handle duplicates + return true; + write(NumberObjectTag); + write(numberObject->internalValue().asNumber()); + return true; + } + if (auto* bigIntObject = jsDynamicCast<BigIntObject*>(vm, obj)) { + if (!startObjectInternal(bigIntObject)) // handle duplicates + return true; + JSValue bigIntValue = bigIntObject->internalValue(); + ASSERT(bigIntValue.isBigInt()); + write(BigIntObjectTag); + dumpBigIntData(bigIntValue); + return true; + } + if (auto* file = JSFile::toWrapped(vm, obj)) { + write(FileTag); + write(*file); + return true; + } + if (auto* list = JSFileList::toWrapped(vm, obj)) { + write(FileListTag); + write(list->length()); + for (auto& file : list->files()) + write(file.get()); + return true; + } + // if (auto* blob = JSBlob::toWrapped(vm, obj)) { + // write(BlobTag); + // m_blobHandles.append(blob->handle()); + // write(blob->url().string()); + // write(blob->type()); + // static_assert(sizeof(uint64_t) == sizeof(decltype(blob->size()))); + // uint64_t size = blob->size(); + // write(size); + // return true; + // } + if (auto* data = JSImageData::toWrapped(vm, obj)) { + write(ImageDataTag); + write(data->width()); + write(data->height()); + CheckedUint32 dataLength = data->data().length(); + if (dataLength.hasOverflowed()) { + code = SerializationReturnCode::DataCloneError; + return true; + } + write(dataLength); + write(data->data().data(), dataLength); + write(data->colorSpace()); + return true; + } + if (auto* regExp = jsDynamicCast<RegExpObject*>(vm, obj)) { + write(RegExpTag); + write(regExp->regExp()->pattern()); + write(String(JSC::Yarr::flagsString(regExp->regExp()->flags()).data())); + return true; + } + if (obj->inherits<JSMessagePort>(vm)) { + auto index = m_transferredMessagePorts.find(obj); + if (index != m_transferredMessagePorts.end()) { + write(MessagePortReferenceTag); + write(index->value); + return true; + } + // MessagePort object could not be found in transferred message ports + code = SerializationReturnCode::ValidationError; + return true; + } + if (auto* arrayBuffer = toPossiblySharedArrayBuffer(vm, obj)) { + if (arrayBuffer->isDetached()) { + code = SerializationReturnCode::ValidationError; + return true; + } + auto index = m_transferredArrayBuffers.find(obj); + if (index != m_transferredArrayBuffers.end()) { + write(ArrayBufferTransferTag); + write(index->value); + return true; + } + if (!startObjectInternal(obj)) // handle duplicates + return true; + + if (arrayBuffer->isShared() && m_context == SerializationContext::WorkerPostMessage) { + uint32_t index = m_sharedBuffers.size(); + ArrayBufferContents contents; + if (arrayBuffer->shareWith(contents)) { + write(SharedArrayBufferTag); + m_sharedBuffers.append(WTFMove(contents)); + write(index); + return true; + } + } + + write(ArrayBufferTag); + uint64_t byteLength = arrayBuffer->byteLength(); + write(byteLength); + write(static_cast<const uint8_t*>(arrayBuffer->data()), byteLength); + return true; + } + if (obj->inherits<JSArrayBufferView>(vm)) { + if (checkForDuplicate(obj)) + return true; + bool success = dumpArrayBufferView(obj, code); + recordObject(obj); + return success; + } +#if ENABLE(WEB_CRYPTO) + if (auto* key = JSCryptoKey::toWrapped(vm, obj)) { + write(CryptoKeyTag); + Vector<uint8_t> serializedKey; + Vector<BlobURLHandle> dummyBlobHandles; + Vector<RefPtr<MessagePort>> dummyMessagePorts; + Vector<RefPtr<JSC::ArrayBuffer>> dummyArrayBuffers; +#if ENABLE(WEBASSEMBLY) + WasmModuleArray dummyModules; + WasmMemoryHandleArray dummyMemoryHandles; +#endif + ArrayBufferContentsArray dummySharedBuffers; + CloneSerializer rawKeySerializer(m_lexicalGlobalObject, dummyMessagePorts, dummyArrayBuffers, {}, +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + {}, +#endif +#if ENABLE(WEB_RTC) + {}, +#endif +#if ENABLE(WEBASSEMBLY) + dummyModules, + dummyMemoryHandles, +#endif + dummyBlobHandles, serializedKey, SerializationContext::Default, dummySharedBuffers); + rawKeySerializer.write(key); + Vector<uint8_t> wrappedKey; + if (!wrapCryptoKey(m_lexicalGlobalObject, serializedKey, wrappedKey)) + return false; + write(wrappedKey); + return true; + } +#endif +#if ENABLE(WEB_RTC) + if (auto* rtcCertificate = JSRTCCertificate::toWrapped(vm, obj)) { + write(RTCCertificateTag); + write(rtcCertificate->expires()); + write(rtcCertificate->pemCertificate()); + write(rtcCertificate->origin().toString()); + write(rtcCertificate->pemPrivateKey()); + write(static_cast<unsigned>(rtcCertificate->getFingerprints().size())); + for (const auto& fingerprint : rtcCertificate->getFingerprints()) { + write(fingerprint.algorithm); + write(fingerprint.value); + } + return true; + } +#endif +#if ENABLE(WEBASSEMBLY) + if (JSWebAssemblyModule* module = jsDynamicCast<JSWebAssemblyModule*>(vm, obj)) { + if (m_context != SerializationContext::WorkerPostMessage && m_context != SerializationContext::WindowPostMessage) + return false; + + uint32_t index = m_wasmModules.size(); + m_wasmModules.append(&module->module()); + write(WasmModuleTag); + write(index); + return true; + } + if (JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(vm, obj)) { + if (memory->memory().sharingMode() != JSC::Wasm::MemorySharingMode::Shared) { + code = SerializationReturnCode::DataCloneError; + return true; + } + if (m_context != SerializationContext::WorkerPostMessage) { + code = SerializationReturnCode::DataCloneError; + return true; + } + uint32_t index = m_wasmMemoryHandles.size(); + m_wasmMemoryHandles.append(&memory->memory().handle()); + write(WasmMemoryTag); + write(index); + return true; + } +#endif + // if (obj->inherits<JSDOMPointReadOnly>(vm)) { + // dumpDOMPoint(obj); + // return true; + // } + // if (obj->inherits<JSDOMRectReadOnly>(vm)) { + // dumpDOMRect(obj); + // return true; + // } + // if (obj->inherits<JSDOMMatrixReadOnly>(vm)) { + // dumpDOMMatrix(obj); + // return true; + // } + // if (obj->inherits<JSDOMQuad>(vm)) { + // dumpDOMQuad(obj); + // return true; + // } + // if (obj->inherits(vm, JSImageBitmap::info())) { + // dumpImageBitmap(obj, code); + // return true; + // } +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + if (obj->inherits(vm, JSOffscreenCanvas::info())) { + dumpOffscreenCanvas(obj, code); + return true; + } +#endif +#if ENABLE(WEB_RTC) + if (obj->inherits(vm, JSRTCDataChannel::info())) { + dumpRTCDataChannel(obj, code); + return true; + } +#endif + if (obj->inherits(vm, JSDOMException::info())) { + dumpDOMException(obj, code); + return true; + } + + return false; + } + // Any other types are expected to serialize as null. + write(NullTag); + return true; + } + + void write(SerializationTag tag) + { + writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag)); + } + + void write(ArrayBufferViewSubtag tag) + { + writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag)); + } + + // void write(DestinationColorSpaceTag tag) + // { + // writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag)); + // } + +#if ENABLE(WEB_CRYPTO) + void write(CryptoKeyClassSubtag tag) + { + writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag)); + } + + void write(CryptoKeyAsymmetricTypeSubtag tag) + { + writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag)); + } + + void write(CryptoKeyUsageTag tag) + { + writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag)); + } + + void write(CryptoAlgorithmIdentifierTag tag) + { + writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag)); + } +#endif + + void write(uint8_t c) + { + writeLittleEndian(m_buffer, c); + } + + void write(uint32_t i) + { + writeLittleEndian(m_buffer, i); + } + + void write(double d) + { + union { + double d; + int64_t i; + } u; + u.d = d; + writeLittleEndian(m_buffer, u.i); + } + + void write(int32_t i) + { + writeLittleEndian(m_buffer, i); + } + + void write(uint64_t i) + { + writeLittleEndian(m_buffer, i); + } + + void write(uint16_t ch) + { + writeLittleEndian(m_buffer, ch); + } + + void writeStringIndex(unsigned i) + { + writeConstantPoolIndex(m_constantPool, i); + } + + void writeObjectIndex(unsigned i) + { + writeConstantPoolIndex(m_objectPool, i); + } + + template<class T> void writeConstantPoolIndex(const T& constantPool, unsigned i) + { + ASSERT(i < constantPool.size()); + if (constantPool.size() <= 0xFF) + write(static_cast<uint8_t>(i)); + else if (constantPool.size() <= 0xFFFF) + write(static_cast<uint16_t>(i)); + else + write(static_cast<uint32_t>(i)); + } + + void write(const Identifier& ident) + { + const String& str = ident.string(); + StringConstantPool::AddResult addResult = m_constantPool.add(ident.impl(), m_constantPool.size()); + if (!addResult.isNewEntry) { + write(StringPoolTag); + writeStringIndex(addResult.iterator->value); + return; + } + + unsigned length = str.length(); + + // Guard against overflow + if (length > (std::numeric_limits<uint32_t>::max() - sizeof(uint32_t)) / sizeof(UChar)) { + fail(); + return; + } + + if (str.is8Bit()) + writeLittleEndian<uint32_t>(m_buffer, length | StringDataIs8BitFlag); + else + writeLittleEndian<uint32_t>(m_buffer, length); + + if (!length) + return; + if (str.is8Bit()) { + if (!writeLittleEndian(m_buffer, str.characters8(), length)) + fail(); + return; + } + if (!writeLittleEndian(m_buffer, str.characters16(), length)) + fail(); + } + + void write(const String& str) + { + if (str.isNull()) + write(m_emptyIdentifier); + else + write(Identifier::fromString(m_lexicalGlobalObject->vm(), str)); + } + + void write(const Vector<uint8_t>& vector) + { + uint32_t size = vector.size(); + write(size); + writeLittleEndian(m_buffer, vector.data(), size); + } + + // void write(const File& file) + // { + // // m_blobHandles.append(file.handle()); + // write(file.path()); + // write(file.url().string()); + // write(file.type()); + // write(file.name()); + // write(static_cast<double>(file.lastModifiedOverride().value_or(-1))); + // } + + // void write(PredefinedColorSpace colorSpace) + // { + // switch (colorSpace) { + // case PredefinedColorSpace::SRGB: + // writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(PredefinedColorSpaceTag::SRGB)); + // break; + // #if ENABLE(PREDEFINED_COLOR_SPACE_DISPLAY_P3) + // case PredefinedColorSpace::DisplayP3: + // writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(PredefinedColorSpaceTag::DisplayP3)); + // break; + // #endif + // } + // } + +#if PLATFORM(COCOA) + void write(const RetainPtr<CFDataRef>& data) + { + uint32_t dataLength = CFDataGetLength(data.get()); + write(dataLength); + write(CFDataGetBytePtr(data.get()), dataLength); + } +#endif + + // void write(DestinationColorSpace destinationColorSpace) + // { + // if (destinationColorSpace == DestinationColorSpace::SRGB()) { + // write(DestinationColorSpaceSRGBTag); + // return; + // } + + // #if ENABLE(DESTINATION_COLOR_SPACE_LINEAR_SRGB) + // if (destinationColorSpace == DestinationColorSpace::LinearSRGB()) { + // write(DestinationColorSpaceLinearSRGBTag); + // return; + // } + // #endif + + // #if ENABLE(DESTINATION_COLOR_SPACE_DISPLAY_P3) + // if (destinationColorSpace == DestinationColorSpace::DisplayP3()) { + // write(DestinationColorSpaceDisplayP3Tag); + // return; + // } + // #endif + + // #if PLATFORM(COCOA) + // auto colorSpace = destinationColorSpace.platformColorSpace(); + + // if (auto name = CGColorSpaceGetName(colorSpace)) { + // auto data = adoptCF(CFStringCreateExternalRepresentation(nullptr, name, kCFStringEncodingUTF8, 0)); + // if (!data) { + // write(DestinationColorSpaceSRGBTag); + // return; + // } + + // write(DestinationColorSpaceCGColorSpaceNameTag); + // write(data); + // return; + // } + + // if (auto propertyList = adoptCF(CGColorSpaceCopyPropertyList(colorSpace))) { + // auto data = adoptCF(CFPropertyListCreateData(nullptr, propertyList.get(), kCFPropertyListBinaryFormat_v1_0, 0, nullptr)); + // if (!data) { + // write(DestinationColorSpaceSRGBTag); + // return; + // } + + // write(DestinationColorSpaceCGColorSpacePropertyListTag); + // write(data); + // return; + // } + // #endif + + // ASSERT_NOT_REACHED(); + // write(DestinationColorSpaceSRGBTag); + // } + +#if ENABLE(WEB_CRYPTO) + void write(CryptoAlgorithmIdentifier algorithm) + { + switch (algorithm) { + case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: + write(CryptoAlgorithmIdentifierTag::RSAES_PKCS1_v1_5); + break; + case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: + write(CryptoAlgorithmIdentifierTag::RSASSA_PKCS1_v1_5); + break; + case CryptoAlgorithmIdentifier::RSA_PSS: + write(CryptoAlgorithmIdentifierTag::RSA_PSS); + break; + case CryptoAlgorithmIdentifier::RSA_OAEP: + write(CryptoAlgorithmIdentifierTag::RSA_OAEP); + break; + case CryptoAlgorithmIdentifier::ECDSA: + write(CryptoAlgorithmIdentifierTag::ECDSA); + break; + case CryptoAlgorithmIdentifier::ECDH: + write(CryptoAlgorithmIdentifierTag::ECDH); + break; + case CryptoAlgorithmIdentifier::AES_CTR: + write(CryptoAlgorithmIdentifierTag::AES_CTR); + break; + case CryptoAlgorithmIdentifier::AES_CBC: + write(CryptoAlgorithmIdentifierTag::AES_CBC); + break; + case CryptoAlgorithmIdentifier::AES_GCM: + write(CryptoAlgorithmIdentifierTag::AES_GCM); + break; + case CryptoAlgorithmIdentifier::AES_CFB: + write(CryptoAlgorithmIdentifierTag::AES_CFB); + break; + case CryptoAlgorithmIdentifier::AES_KW: + write(CryptoAlgorithmIdentifierTag::AES_KW); + break; + case CryptoAlgorithmIdentifier::HMAC: + write(CryptoAlgorithmIdentifierTag::HMAC); + break; + case CryptoAlgorithmIdentifier::SHA_1: + write(CryptoAlgorithmIdentifierTag::SHA_1); + break; + case CryptoAlgorithmIdentifier::SHA_224: + write(CryptoAlgorithmIdentifierTag::SHA_224); + break; + case CryptoAlgorithmIdentifier::SHA_256: + write(CryptoAlgorithmIdentifierTag::SHA_256); + break; + case CryptoAlgorithmIdentifier::SHA_384: + write(CryptoAlgorithmIdentifierTag::SHA_384); + break; + case CryptoAlgorithmIdentifier::SHA_512: + write(CryptoAlgorithmIdentifierTag::SHA_512); + break; + case CryptoAlgorithmIdentifier::HKDF: + write(CryptoAlgorithmIdentifierTag::HKDF); + break; + case CryptoAlgorithmIdentifier::PBKDF2: + write(CryptoAlgorithmIdentifierTag::PBKDF2); + break; + } + } + + void write(CryptoKeyRSAComponents::Type type) + { + switch (type) { + case CryptoKeyRSAComponents::Type::Public: + write(CryptoKeyAsymmetricTypeSubtag::Public); + return; + case CryptoKeyRSAComponents::Type::Private: + write(CryptoKeyAsymmetricTypeSubtag::Private); + return; + } + } + + void write(const CryptoKeyRSAComponents& key) + { + write(key.type()); + write(key.modulus()); + write(key.exponent()); + if (key.type() == CryptoKeyRSAComponents::Type::Public) + return; + + write(key.privateExponent()); + + unsigned primeCount = key.hasAdditionalPrivateKeyParameters() ? key.otherPrimeInfos().size() + 2 : 0; + write(primeCount); + if (!primeCount) + return; + + write(key.firstPrimeInfo().primeFactor); + write(key.firstPrimeInfo().factorCRTExponent); + write(key.secondPrimeInfo().primeFactor); + write(key.secondPrimeInfo().factorCRTExponent); + write(key.secondPrimeInfo().factorCRTCoefficient); + for (unsigned i = 2; i < primeCount; ++i) { + write(key.otherPrimeInfos()[i].primeFactor); + write(key.otherPrimeInfos()[i].factorCRTExponent); + write(key.otherPrimeInfos()[i].factorCRTCoefficient); + } + } + + void write(const CryptoKey* key) + { + write(currentKeyFormatVersion); + + write(key->extractable()); + + CryptoKeyUsageBitmap usages = key->usagesBitmap(); + write(countUsages(usages)); + if (usages & CryptoKeyUsageEncrypt) + write(CryptoKeyUsageTag::Encrypt); + if (usages & CryptoKeyUsageDecrypt) + write(CryptoKeyUsageTag::Decrypt); + if (usages & CryptoKeyUsageSign) + write(CryptoKeyUsageTag::Sign); + if (usages & CryptoKeyUsageVerify) + write(CryptoKeyUsageTag::Verify); + if (usages & CryptoKeyUsageDeriveKey) + write(CryptoKeyUsageTag::DeriveKey); + if (usages & CryptoKeyUsageDeriveBits) + write(CryptoKeyUsageTag::DeriveBits); + if (usages & CryptoKeyUsageWrapKey) + write(CryptoKeyUsageTag::WrapKey); + if (usages & CryptoKeyUsageUnwrapKey) + write(CryptoKeyUsageTag::UnwrapKey); + + switch (key->keyClass()) { + case CryptoKeyClass::HMAC: + write(CryptoKeyClassSubtag::HMAC); + write(downcast<CryptoKeyHMAC>(*key).key()); + write(downcast<CryptoKeyHMAC>(*key).hashAlgorithmIdentifier()); + break; + case CryptoKeyClass::AES: + write(CryptoKeyClassSubtag::AES); + write(key->algorithmIdentifier()); + write(downcast<CryptoKeyAES>(*key).key()); + break; + case CryptoKeyClass::EC: + write(CryptoKeyClassSubtag::EC); + write(key->algorithmIdentifier()); + write(downcast<CryptoKeyEC>(*key).namedCurveString()); + switch (key->type()) { + case CryptoKey::Type::Public: { + write(CryptoKeyAsymmetricTypeSubtag::Public); + auto result = downcast<CryptoKeyEC>(*key).exportRaw(); + ASSERT(!result.hasException()); + write(result.releaseReturnValue()); + break; + } + case CryptoKey::Type::Private: { + write(CryptoKeyAsymmetricTypeSubtag::Private); + // Use the standard complied method is not very efficient, but simple/reliable. + auto result = downcast<CryptoKeyEC>(*key).exportPkcs8(); + ASSERT(!result.hasException()); + write(result.releaseReturnValue()); + break; + } + default: + ASSERT_NOT_REACHED(); + } + break; + case CryptoKeyClass::Raw: + write(CryptoKeyClassSubtag::Raw); + write(key->algorithmIdentifier()); + write(downcast<CryptoKeyRaw>(*key).key()); + break; + case CryptoKeyClass::RSA: + write(CryptoKeyClassSubtag::RSA); + write(key->algorithmIdentifier()); + CryptoAlgorithmIdentifier hash; + bool isRestrictedToHash = downcast<CryptoKeyRSA>(*key).isRestrictedToHash(hash); + write(isRestrictedToHash); + if (isRestrictedToHash) + write(hash); + write(*downcast<CryptoKeyRSA>(*key).exportData()); + break; + } + } +#endif + + void write(const uint8_t* data, unsigned length) + { + m_buffer.append(data, length); + } + + Vector<uint8_t>& m_buffer; + // Vector<BlobURLHandle>& m_blobHandles; + ObjectPool m_objectPool; + ObjectPool m_transferredMessagePorts; + ObjectPool m_transferredArrayBuffers; + // ObjectPool m_transferredImageBitmaps; + // #if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + // ObjectPool m_transferredOffscreenCanvases; + // #endif + // #if ENABLE(WEB_RTC) + // ObjectPool m_transferredRTCDataChannels; + // #endif + typedef HashMap<RefPtr<UniquedStringImpl>, uint32_t, IdentifierRepHash> StringConstantPool; + StringConstantPool m_constantPool; + Identifier m_emptyIdentifier; + SerializationContext m_context; + ArrayBufferContentsArray& m_sharedBuffers; +#if ENABLE(WEBASSEMBLY) + WasmModuleArray& m_wasmModules; + WasmMemoryHandleArray& m_wasmMemoryHandles; +#endif +}; + +SerializationReturnCode CloneSerializer::serialize(JSValue in) +{ + VM& vm = m_lexicalGlobalObject->vm(); + Vector<uint32_t, 16> indexStack; + Vector<uint32_t, 16> lengthStack; + Vector<PropertyNameArray, 16> propertyStack; + Vector<JSObject*, 32> inputObjectStack; + Vector<JSMapIterator*, 4> mapIteratorStack; + Vector<JSSetIterator*, 4> setIteratorStack; + Vector<JSValue, 4> mapIteratorValueStack; + Vector<WalkerState, 16> stateStack; + WalkerState lexicalGlobalObject = StateUnknown; + JSValue inValue = in; + auto scope = DECLARE_THROW_SCOPE(vm); + while (1) { + switch (lexicalGlobalObject) { + arrayStartState: + case ArrayStartState: { + ASSERT(isArray(vm, inValue)); + if (inputObjectStack.size() > maximumFilterRecursion) + return SerializationReturnCode::StackOverflowError; + + JSArray* inArray = asArray(inValue); + unsigned length = inArray->length(); + if (!startArray(inArray)) + break; + inputObjectStack.append(inArray); + indexStack.append(0); + lengthStack.append(length); + } + arrayStartVisitMember: + FALLTHROUGH; + case ArrayStartVisitMember: { + JSObject* array = inputObjectStack.last(); + uint32_t index = indexStack.last(); + if (index == lengthStack.last()) { + indexStack.removeLast(); + lengthStack.removeLast(); + + propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude)); + array->getOwnNonIndexPropertyNames(m_lexicalGlobalObject, propertyStack.last(), DontEnumPropertiesMode::Exclude); + if (UNLIKELY(scope.exception())) + return SerializationReturnCode::ExistingExceptionError; + if (propertyStack.last().size()) { + write(NonIndexPropertiesTag); + indexStack.append(0); + goto objectStartVisitMember; + } + propertyStack.removeLast(); + + endObject(); + inputObjectStack.removeLast(); + break; + } + inValue = array->getDirectIndex(m_lexicalGlobalObject, index); + if (UNLIKELY(scope.exception())) + return SerializationReturnCode::ExistingExceptionError; + if (!inValue) { + indexStack.last()++; + goto arrayStartVisitMember; + } + + write(index); + auto terminalCode = SerializationReturnCode::SuccessfullyCompleted; + if (dumpIfTerminal(inValue, terminalCode)) { + if (terminalCode != SerializationReturnCode::SuccessfullyCompleted) + return terminalCode; + indexStack.last()++; + goto arrayStartVisitMember; + } + stateStack.append(ArrayEndVisitMember); + goto stateUnknown; + } + case ArrayEndVisitMember: { + indexStack.last()++; + goto arrayStartVisitMember; + } + objectStartState: + case ObjectStartState: { + ASSERT(inValue.isObject()); + if (inputObjectStack.size() > maximumFilterRecursion) + return SerializationReturnCode::StackOverflowError; + JSObject* inObject = asObject(inValue); + if (!startObject(inObject)) + break; + // At this point, all supported objects other than Object + // objects have been handled. If we reach this point and + // the input is not an Object object then we should throw + // a DataCloneError. + if (inObject->classInfo(vm) != JSFinalObject::info()) + return SerializationReturnCode::DataCloneError; + inputObjectStack.append(inObject); + indexStack.append(0); + propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude)); + inObject->methodTable(vm)->getOwnPropertyNames(inObject, m_lexicalGlobalObject, propertyStack.last(), DontEnumPropertiesMode::Exclude); + if (UNLIKELY(scope.exception())) + return SerializationReturnCode::ExistingExceptionError; + } + objectStartVisitMember: + FALLTHROUGH; + case ObjectStartVisitMember: { + JSObject* object = inputObjectStack.last(); + uint32_t index = indexStack.last(); + PropertyNameArray& properties = propertyStack.last(); + if (index == properties.size()) { + endObject(); + inputObjectStack.removeLast(); + indexStack.removeLast(); + propertyStack.removeLast(); + break; + } + inValue = getProperty(vm, object, properties[index]); + if (UNLIKELY(scope.exception())) + return SerializationReturnCode::ExistingExceptionError; + + if (!inValue) { + // Property was removed during serialisation + indexStack.last()++; + goto objectStartVisitMember; + } + write(properties[index]); + + if (UNLIKELY(scope.exception())) + return SerializationReturnCode::ExistingExceptionError; + + auto terminalCode = SerializationReturnCode::SuccessfullyCompleted; + if (!dumpIfTerminal(inValue, terminalCode)) { + stateStack.append(ObjectEndVisitMember); + goto stateUnknown; + } + if (terminalCode != SerializationReturnCode::SuccessfullyCompleted) + return terminalCode; + FALLTHROUGH; + } + case ObjectEndVisitMember: { + if (UNLIKELY(scope.exception())) + return SerializationReturnCode::ExistingExceptionError; + + indexStack.last()++; + goto objectStartVisitMember; + } + mapStartState : { + ASSERT(inValue.isObject()); + if (inputObjectStack.size() > maximumFilterRecursion) + return SerializationReturnCode::StackOverflowError; + JSMap* inMap = jsCast<JSMap*>(inValue); + if (!startMap(inMap)) + break; + JSMapIterator* iterator = JSMapIterator::create(vm, m_lexicalGlobalObject->mapIteratorStructure(), inMap, IterationKind::Entries); + m_gcBuffer.appendWithCrashOnOverflow(inMap); + m_gcBuffer.appendWithCrashOnOverflow(iterator); + mapIteratorStack.append(iterator); + inputObjectStack.append(inMap); + goto mapDataStartVisitEntry; + } + mapDataStartVisitEntry: + case MapDataStartVisitEntry: { + JSMapIterator* iterator = mapIteratorStack.last(); + JSValue key, value; + if (!iterator->nextKeyValue(m_lexicalGlobalObject, key, value)) { + mapIteratorStack.removeLast(); + JSObject* object = inputObjectStack.last(); + ASSERT(jsDynamicCast<JSMap*>(vm, object)); + propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude)); + object->methodTable(vm)->getOwnPropertyNames(object, m_lexicalGlobalObject, propertyStack.last(), DontEnumPropertiesMode::Exclude); + if (UNLIKELY(scope.exception())) + return SerializationReturnCode::ExistingExceptionError; + write(NonMapPropertiesTag); + indexStack.append(0); + goto objectStartVisitMember; + } + inValue = key; + m_gcBuffer.appendWithCrashOnOverflow(value); + mapIteratorValueStack.append(value); + stateStack.append(MapDataEndVisitKey); + goto stateUnknown; + } + case MapDataEndVisitKey: { + inValue = mapIteratorValueStack.last(); + mapIteratorValueStack.removeLast(); + stateStack.append(MapDataEndVisitValue); + goto stateUnknown; + } + case MapDataEndVisitValue: { + goto mapDataStartVisitEntry; + } + + setStartState : { + ASSERT(inValue.isObject()); + if (inputObjectStack.size() > maximumFilterRecursion) + return SerializationReturnCode::StackOverflowError; + JSSet* inSet = jsCast<JSSet*>(inValue); + if (!startSet(inSet)) + break; + JSSetIterator* iterator = JSSetIterator::create(vm, m_lexicalGlobalObject->setIteratorStructure(), inSet, IterationKind::Keys); + m_gcBuffer.appendWithCrashOnOverflow(inSet); + m_gcBuffer.appendWithCrashOnOverflow(iterator); + setIteratorStack.append(iterator); + inputObjectStack.append(inSet); + goto setDataStartVisitEntry; + } + setDataStartVisitEntry: + case SetDataStartVisitEntry: { + JSSetIterator* iterator = setIteratorStack.last(); + JSValue key; + if (!iterator->next(m_lexicalGlobalObject, key)) { + setIteratorStack.removeLast(); + JSObject* object = inputObjectStack.last(); + ASSERT(jsDynamicCast<JSSet*>(vm, object)); + propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude)); + object->methodTable(vm)->getOwnPropertyNames(object, m_lexicalGlobalObject, propertyStack.last(), DontEnumPropertiesMode::Exclude); + if (UNLIKELY(scope.exception())) + return SerializationReturnCode::ExistingExceptionError; + write(NonSetPropertiesTag); + indexStack.append(0); + goto objectStartVisitMember; + } + inValue = key; + stateStack.append(SetDataEndVisitKey); + goto stateUnknown; + } + case SetDataEndVisitKey: { + goto setDataStartVisitEntry; + } + + stateUnknown: + case StateUnknown: { + auto terminalCode = SerializationReturnCode::SuccessfullyCompleted; + if (dumpIfTerminal(inValue, terminalCode)) { + if (terminalCode != SerializationReturnCode::SuccessfullyCompleted) + return terminalCode; + break; + } + + if (isArray(vm, inValue)) + goto arrayStartState; + if (isMap(vm, inValue)) + goto mapStartState; + if (isSet(vm, inValue)) + goto setStartState; + goto objectStartState; + } + } + if (stateStack.isEmpty()) + break; + + lexicalGlobalObject = stateStack.last(); + stateStack.removeLast(); + } + if (m_failed) + return SerializationReturnCode::UnspecifiedError; + + return SerializationReturnCode::SuccessfullyCompleted; +} + +class CloneDeserializer : CloneBase { +public: + static String deserializeString(const Vector<uint8_t>& buffer) + { + if (buffer.isEmpty()) + return String(); + const uint8_t* ptr = buffer.begin(); + const uint8_t* end = buffer.end(); + uint32_t version; + if (!readLittleEndian(ptr, end, version) || version > CurrentVersion) + return String(); + uint8_t tag; + if (!readLittleEndian(ptr, end, tag) || tag != StringTag) + return String(); + uint32_t length; + if (!readLittleEndian(ptr, end, length)) + return String(); + bool is8Bit = length & StringDataIs8BitFlag; + length &= ~StringDataIs8BitFlag; + String str; + if (!readString(ptr, end, str, length, is8Bit)) + return String(); + return str; + } + + static DeserializationResult deserialize(JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, /*Vector<std::optional<ImageBitmapBacking>>&& backingStores*/ +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + , + Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& detachedOffscreenCanvases +#endif +#if ENABLE(WEB_RTC) + , + Vector<std::unique_ptr<DetachedRTCDataChannel>>&& detachedRTCDataChannels +#endif + , + ArrayBufferContentsArray* arrayBufferContentsArray, const Vector<uint8_t>& buffer, const Vector<String>& blobURLs, const Vector<String> blobFilePaths, ArrayBufferContentsArray* sharedBuffers +#if ENABLE(WEBASSEMBLY) + , + WasmModuleArray* wasmModules, WasmMemoryHandleArray* wasmMemoryHandles +#endif + ) + { + if (!buffer.size()) + return std::make_pair(jsNull(), SerializationReturnCode::UnspecifiedError); + CloneDeserializer deserializer(lexicalGlobalObject, globalObject, messagePorts, arrayBufferContentsArray, buffer, blobURLs, blobFilePaths, sharedBuffers, WTFMove(backingStores) +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + , + WTFMove(detachedOffscreenCanvases) +#endif +#if ENABLE(WEB_RTC) + , + WTFMove(detachedRTCDataChannels) +#endif +#if ENABLE(WEBASSEMBLY) + , + wasmModules, wasmMemoryHandles +#endif + ); + if (!deserializer.isValid()) + return std::make_pair(JSValue(), SerializationReturnCode::ValidationError); + return deserializer.deserialize(); + } + +private: + struct CachedString { + CachedString(const String& string) + : m_string(string) + { + } + + JSValue jsString(JSGlobalObject* lexicalGlobalObject) + { + if (!m_jsString) + m_jsString = JSC::jsString(lexicalGlobalObject->vm(), m_string); + return m_jsString; + } + const String& string() { return m_string; } + String takeString() { return WTFMove(m_string); } + + private: + String m_string; + JSValue m_jsString; + }; + + struct CachedStringRef { + CachedStringRef() + : m_base(0) + , m_index(0) + { + } + CachedStringRef(Vector<CachedString>* base, size_t index) + : m_base(base) + , m_index(index) + { + } + + CachedString* operator->() + { + ASSERT(m_base); + return &m_base->at(m_index); + } + + private: + Vector<CachedString>* m_base; + size_t m_index; + }; + + CloneDeserializer( + JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, ArrayBufferContentsArray* arrayBufferContents, /*Vector<std::optional<ImageBitmapBacking>>&& backingStores,*/ const Vector<uint8_t>& buffer +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + , + Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& detachedOffscreenCanvases = {} +#endif +#if ENABLE(WEB_RTC) + , + Vector<std::unique_ptr<DetachedRTCDataChannel>>&& detachedRTCDataChannels = {} +#endif +#if ENABLE(WEBASSEMBLY) + , + WasmModuleArray* wasmModules = nullptr, WasmMemoryHandleArray* wasmMemoryHandles = nullptr +#endif + ) + : CloneBase(lexicalGlobalObject) + , m_globalObject(globalObject) + , m_isDOMGlobalObject(globalObject->inherits<JSDOMGlobalObject>(globalObject->vm())) + , m_canCreateDOMObject(m_isDOMGlobalObject && !globalObject->inherits<JSIDBSerializationGlobalObject>(globalObject->vm())) + , m_ptr(buffer.data()) + , m_end(buffer.data() + buffer.size()) + , m_version(0xFFFFFFFF) + , m_messagePorts(messagePorts) + , m_arrayBufferContents(arrayBufferContents) + , m_arrayBuffers(arrayBufferContents ? arrayBufferContents->size() : 0) + , m_backingStores(WTFMove(backingStores)) + , m_imageBitmaps(m_backingStores.size()) +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + , m_detachedOffscreenCanvases(WTFMove(detachedOffscreenCanvases)) + , m_offscreenCanvases(m_detachedOffscreenCanvases.size()) +#endif +#if ENABLE(WEB_RTC) + , m_detachedRTCDataChannels(WTFMove(detachedRTCDataChannels)) + , m_rtcDataChannels(m_detachedRTCDataChannels.size()) +#endif +#if ENABLE(WEBASSEMBLY) + , m_wasmModules(wasmModules) + , m_wasmMemoryHandles(wasmMemoryHandles) +#endif + { + if (!read(m_version)) + m_version = 0xFFFFFFFF; + } + + CloneDeserializer(JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, ArrayBufferContentsArray* arrayBufferContents, const Vector<uint8_t>& buffer, const Vector<String>& blobURLs, const Vector<String> blobFilePaths, ArrayBufferContentsArray* sharedBuffers, /*Vector<std::optional<ImageBitmapBacking>>&& backingStores*/ +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + , + Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& detachedOffscreenCanvases +#endif +#if ENABLE(WEB_RTC) + , + Vector<std::unique_ptr<DetachedRTCDataChannel>>&& detachedRTCDataChannels +#endif +#if ENABLE(WEBASSEMBLY) + , + WasmModuleArray* wasmModules, WasmMemoryHandleArray* wasmMemoryHandles +#endif + ) + : CloneBase(lexicalGlobalObject) + , m_globalObject(globalObject) + , m_isDOMGlobalObject(globalObject->inherits<JSDOMGlobalObject>(globalObject->vm())) + , m_canCreateDOMObject(m_isDOMGlobalObject && !globalObject->inherits<JSIDBSerializationGlobalObject>(globalObject->vm())) + , m_ptr(buffer.data()) + , m_end(buffer.data() + buffer.size()) + , m_version(0xFFFFFFFF) + , m_messagePorts(messagePorts) + , m_arrayBufferContents(arrayBufferContents) + , m_arrayBuffers(arrayBufferContents ? arrayBufferContents->size() : 0) + // , m_blobURLs(blobURLs) + // , m_blobFilePaths(blobFilePaths) + , m_sharedBuffers(sharedBuffers) + // , m_backingStores(WTFMove(backingStores)) + // , m_imageBitmaps(m_backingStores.size()) +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + , m_detachedOffscreenCanvases(WTFMove(detachedOffscreenCanvases)) + , m_offscreenCanvases(m_detachedOffscreenCanvases.size()) +#endif +#if ENABLE(WEB_RTC) + , m_detachedRTCDataChannels(WTFMove(detachedRTCDataChannels)) + , m_rtcDataChannels(m_detachedRTCDataChannels.size()) +#endif +#if ENABLE(WEBASSEMBLY) + , m_wasmModules(wasmModules) + , m_wasmMemoryHandles(wasmMemoryHandles) +#endif + { + if (!read(m_version)) + m_version = 0xFFFFFFFF; + } + + DeserializationResult deserialize(); + + bool isValid() const { return m_version <= CurrentVersion; } + + template<typename T> bool readLittleEndian(T& value) + { + if (m_failed || !readLittleEndian(m_ptr, m_end, value)) { + fail(); + return false; + } + return true; + } +#if ASSUME_LITTLE_ENDIAN + template<typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value) + { + if (ptr > end - sizeof(value)) + return false; + + if (sizeof(T) == 1) + value = *ptr++; + else { + value = *reinterpret_cast<const T*>(ptr); + ptr += sizeof(T); + } + return true; + } +#else + template<typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value) + { + if (ptr > end - sizeof(value)) + return false; + + if (sizeof(T) == 1) + value = *ptr++; + else { + value = 0; + for (unsigned i = 0; i < sizeof(T); i++) + value += ((T)*ptr++) << (i * 8); + } + return true; + } +#endif + + bool read(uint32_t& i) + { + return readLittleEndian(i); + } + + bool read(int32_t& i) + { + return readLittleEndian(*reinterpret_cast<uint32_t*>(&i)); + } + + bool read(uint16_t& i) + { + return readLittleEndian(i); + } + + bool read(uint8_t& i) + { + return readLittleEndian(i); + } + + bool read(double& d) + { + union { + double d; + uint64_t i64; + } u; + if (!readLittleEndian(u.i64)) + return false; + d = u.d; + return true; + } + + bool read(uint64_t& i) + { + return readLittleEndian(i); + } + + bool readStringIndex(uint32_t& i) + { + return readConstantPoolIndex(m_constantPool, i); + } + + template<class T> bool readConstantPoolIndex(const T& constantPool, uint32_t& i) + { + if (constantPool.size() <= 0xFF) { + uint8_t i8; + if (!read(i8)) + return false; + i = i8; + return true; + } + if (constantPool.size() <= 0xFFFF) { + uint16_t i16; + if (!read(i16)) + return false; + i = i16; + return true; + } + return read(i); + } + + static bool readString(const uint8_t*& ptr, const uint8_t* end, String& str, unsigned length, bool is8Bit) + { + if (length >= std::numeric_limits<int32_t>::max() / sizeof(UChar)) + return false; + + if (is8Bit) { + if ((end - ptr) < static_cast<int>(length)) + return false; + str = String { ptr, length }; + ptr += length; + return true; + } + + unsigned size = length * sizeof(UChar); + if ((end - ptr) < static_cast<int>(size)) + return false; + +#if ASSUME_LITTLE_ENDIAN + str = String(reinterpret_cast<const UChar*>(ptr), length); + ptr += length * sizeof(UChar); +#else + UChar* characters; + str = String::createUninitialized(length, characters); + for (unsigned i = 0; i < length; ++i) { + uint16_t c; + readLittleEndian(ptr, end, c); + characters[i] = c; + } +#endif + return true; + } + + bool readStringData(CachedStringRef& cachedString) + { + bool scratch; + return readStringData(cachedString, scratch); + } + + bool readStringData(CachedStringRef& cachedString, bool& wasTerminator) + { + if (m_failed) + return false; + uint32_t length = 0; + if (!read(length)) + return false; + if (length == TerminatorTag) { + wasTerminator = true; + return false; + } + if (length == StringPoolTag) { + unsigned index = 0; + if (!readStringIndex(index)) { + fail(); + return false; + } + if (index >= m_constantPool.size()) { + fail(); + return false; + } + cachedString = CachedStringRef(&m_constantPool, index); + return true; + } + bool is8Bit = length & StringDataIs8BitFlag; + length &= ~StringDataIs8BitFlag; + String str; + if (!readString(m_ptr, m_end, str, length, is8Bit)) { + fail(); + return false; + } + m_constantPool.append(str); + cachedString = CachedStringRef(&m_constantPool, m_constantPool.size() - 1); + return true; + } + + SerializationTag readTag() + { + if (m_ptr >= m_end) + return ErrorTag; + return static_cast<SerializationTag>(*m_ptr++); + } + + bool readArrayBufferViewSubtag(ArrayBufferViewSubtag& tag) + { + if (m_ptr >= m_end) + return false; + tag = static_cast<ArrayBufferViewSubtag>(*m_ptr++); + return true; + } + + void putProperty(JSObject* object, unsigned index, JSValue value) + { + object->putDirectIndex(m_lexicalGlobalObject, index, value); + } + + void putProperty(JSObject* object, const Identifier& property, JSValue value) + { + object->putDirectMayBeIndex(m_lexicalGlobalObject, property, value); + } + + // bool readFile(RefPtr<File>& file) + // { + // CachedStringRef path; + // if (!readStringData(path)) + // return false; + // CachedStringRef url; + // if (!readStringData(url)) + // return false; + // CachedStringRef type; + // if (!readStringData(type)) + // return false; + // CachedStringRef name; + // if (!readStringData(name)) + // return false; + // std::optional<int64_t> optionalLastModified; + // if (m_version > 6) { + // double lastModified; + // if (!read(lastModified)) + // return false; + // if (lastModified >= 0) + // optionalLastModified = lastModified; + // } + + // // If the blob URL for this file has an associated blob file path, prefer that one over the "built-in" path. + // String filePath = blobFilePathForBlobURL(url->string()); + // if (filePath.isEmpty()) + // filePath = path->string(); + + // if (!m_canCreateDOMObject) + // return true; + + // file = File::deserialize(executionContext(m_lexicalGlobalObject), filePath, URL { url->string() }, type->string(), name->string(), optionalLastModified); + // return true; + // } + + template<typename LengthType> + bool readArrayBufferImpl(RefPtr<ArrayBuffer>& arrayBuffer) + { + LengthType length; + if (!read(length)) + return false; + if (m_ptr + length > m_end) + return false; + arrayBuffer = ArrayBuffer::tryCreate(m_ptr, length); + if (!arrayBuffer) + return false; + m_ptr += length; + return true; + } + + bool readArrayBuffer(RefPtr<ArrayBuffer>& arrayBuffer) + { + if (m_version < 10) + return readArrayBufferImpl<uint32_t>(arrayBuffer); + return readArrayBufferImpl<uint64_t>(arrayBuffer); + } + + template<typename LengthType> + bool readArrayBufferViewImpl(VM& vm, JSValue& arrayBufferView) + { + ArrayBufferViewSubtag arrayBufferViewSubtag; + if (!readArrayBufferViewSubtag(arrayBufferViewSubtag)) + return false; + LengthType byteOffset; + if (!read(byteOffset)) + return false; + LengthType byteLength; + if (!read(byteLength)) + return false; + JSObject* arrayBufferObj = asObject(readTerminal()); + if (!arrayBufferObj || !arrayBufferObj->inherits<JSArrayBuffer>(vm)) + return false; + + unsigned elementSize = typedArrayElementSize(arrayBufferViewSubtag); + if (!elementSize) + return false; + LengthType length = byteLength / elementSize; + if (length * elementSize != byteLength) + return false; + + RefPtr<ArrayBuffer> arrayBuffer = toPossiblySharedArrayBuffer(vm, arrayBufferObj); + switch (arrayBufferViewSubtag) { + case DataViewTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, DataView::create(WTFMove(arrayBuffer), byteOffset, length).get()); + return true; + case Int8ArrayTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Int8Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get()); + return true; + case Uint8ArrayTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint8Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get()); + return true; + case Uint8ClampedArrayTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint8ClampedArray::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get()); + return true; + case Int16ArrayTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Int16Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get()); + return true; + case Uint16ArrayTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint16Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get()); + return true; + case Int32ArrayTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Int32Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get()); + return true; + case Uint32ArrayTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint32Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get()); + return true; + case Float32ArrayTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Float32Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get()); + return true; + case Float64ArrayTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Float64Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get()); + return true; + case BigInt64ArrayTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, BigInt64Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get()); + return true; + case BigUint64ArrayTag: + arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, BigUint64Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get()); + return true; + default: + return false; + } + } + + bool readArrayBufferView(VM& vm, JSValue& arrayBufferView) + { + if (m_version < 10) + return readArrayBufferViewImpl<uint32_t>(vm, arrayBufferView); + return readArrayBufferViewImpl<uint64_t>(vm, arrayBufferView); + } + + bool read(Vector<uint8_t>& result) + { + ASSERT(result.isEmpty()); + uint32_t size; + if (!read(size)) + return false; + if (m_ptr + size > m_end) + return false; + result.append(m_ptr, size); + m_ptr += size; + return true; + } + + // bool read(PredefinedColorSpace& result) + // { + // uint8_t tag; + // if (!read(tag)) + // return false; + + // switch (static_cast<PredefinedColorSpaceTag>(tag)) { + // case PredefinedColorSpaceTag::SRGB: + // result = PredefinedColorSpace::SRGB; + // return true; + // #if ENABLE(PREDEFINED_COLOR_SPACE_DISPLAY_P3) + // case PredefinedColorSpaceTag::DisplayP3: + // result = PredefinedColorSpace::DisplayP3; + // return true; + // #endif + // default: + // return false; + // } + // } + + // bool read(DestinationColorSpaceTag& tag) + // { + // if (m_ptr >= m_end) + // return false; + // tag = static_cast<DestinationColorSpaceTag>(*m_ptr++); + // return true; + // } + +#if PLATFORM(COCOA) + bool read(RetainPtr<CFDataRef>& data) + { + uint32_t dataLength; + if (!read(dataLength) || static_cast<uint32_t>(m_end - m_ptr) < dataLength) + return false; + + data = adoptCF(CFDataCreateWithBytesNoCopy(nullptr, m_ptr, dataLength, kCFAllocatorNull)); + if (!data) + return false; + + m_ptr += dataLength; + return true; + } +#endif + + bool read(DestinationColorSpace& destinationColorSpace) + { + DestinationColorSpaceTag tag; + if (!read(tag)) + return false; + + switch (tag) { + case DestinationColorSpaceSRGBTag: + destinationColorSpace = DestinationColorSpace::SRGB(); + return true; +#if ENABLE(DESTINATION_COLOR_SPACE_LINEAR_SRGB) + case DestinationColorSpaceLinearSRGBTag: + destinationColorSpace = DestinationColorSpace::LinearSRGB(); + return true; +#endif +#if ENABLE(DESTINATION_COLOR_SPACE_DISPLAY_P3) + case DestinationColorSpaceDisplayP3Tag: + destinationColorSpace = DestinationColorSpace::DisplayP3(); + return true; +#endif +#if PLATFORM(COCOA) + case DestinationColorSpaceCGColorSpaceNameTag: { + RetainPtr<CFDataRef> data; + if (!read(data)) + return false; + + auto name = adoptCF(CFStringCreateFromExternalRepresentation(nullptr, data.get(), kCFStringEncodingUTF8)); + if (!name) + return false; + + auto colorSpace = adoptCF(CGColorSpaceCreateWithName(name.get())); + if (!colorSpace) + return false; + + destinationColorSpace = DestinationColorSpace(colorSpace.get()); + return true; + } + case DestinationColorSpaceCGColorSpacePropertyListTag: { + RetainPtr<CFDataRef> data; + if (!read(data)) + return false; + + auto propertyList = adoptCF(CFPropertyListCreateWithData(nullptr, data.get(), kCFPropertyListImmutable, nullptr, nullptr)); + if (!propertyList) + return false; + + auto colorSpace = adoptCF(CGColorSpaceCreateWithPropertyList(propertyList.get())); + if (!colorSpace) + return false; + + destinationColorSpace = DestinationColorSpace(colorSpace.get()); + return true; + } +#endif + } + + ASSERT_NOT_REACHED(); + return false; + } + +#if ENABLE(WEB_CRYPTO) + bool read(CryptoAlgorithmIdentifier& result) + { + uint8_t algorithmTag; + if (!read(algorithmTag)) + return false; + if (algorithmTag > cryptoAlgorithmIdentifierTagMaximumValue) + return false; + switch (static_cast<CryptoAlgorithmIdentifierTag>(algorithmTag)) { + case CryptoAlgorithmIdentifierTag::RSAES_PKCS1_v1_5: + result = CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5; + break; + case CryptoAlgorithmIdentifierTag::RSASSA_PKCS1_v1_5: + result = CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5; + break; + case CryptoAlgorithmIdentifierTag::RSA_PSS: + result = CryptoAlgorithmIdentifier::RSA_PSS; + break; + case CryptoAlgorithmIdentifierTag::RSA_OAEP: + result = CryptoAlgorithmIdentifier::RSA_OAEP; + break; + case CryptoAlgorithmIdentifierTag::ECDSA: + result = CryptoAlgorithmIdentifier::ECDSA; + break; + case CryptoAlgorithmIdentifierTag::ECDH: + result = CryptoAlgorithmIdentifier::ECDH; + break; + case CryptoAlgorithmIdentifierTag::AES_CTR: + result = CryptoAlgorithmIdentifier::AES_CTR; + break; + case CryptoAlgorithmIdentifierTag::AES_CBC: + result = CryptoAlgorithmIdentifier::AES_CBC; + break; + case CryptoAlgorithmIdentifierTag::AES_GCM: + result = CryptoAlgorithmIdentifier::AES_GCM; + break; + case CryptoAlgorithmIdentifierTag::AES_CFB: + result = CryptoAlgorithmIdentifier::AES_CFB; + break; + case CryptoAlgorithmIdentifierTag::AES_KW: + result = CryptoAlgorithmIdentifier::AES_KW; + break; + case CryptoAlgorithmIdentifierTag::HMAC: + result = CryptoAlgorithmIdentifier::HMAC; + break; + case CryptoAlgorithmIdentifierTag::SHA_1: + result = CryptoAlgorithmIdentifier::SHA_1; + break; + case CryptoAlgorithmIdentifierTag::SHA_224: + result = CryptoAlgorithmIdentifier::SHA_224; + break; + case CryptoAlgorithmIdentifierTag::SHA_256: + result = CryptoAlgorithmIdentifier::SHA_256; + break; + case CryptoAlgorithmIdentifierTag::SHA_384: + result = CryptoAlgorithmIdentifier::SHA_384; + break; + case CryptoAlgorithmIdentifierTag::SHA_512: + result = CryptoAlgorithmIdentifier::SHA_512; + break; + case CryptoAlgorithmIdentifierTag::HKDF: + result = CryptoAlgorithmIdentifier::HKDF; + break; + case CryptoAlgorithmIdentifierTag::PBKDF2: + result = CryptoAlgorithmIdentifier::PBKDF2; + break; + } + return true; + } + + bool read(CryptoKeyClassSubtag& result) + { + uint8_t tag; + if (!read(tag)) + return false; + if (tag > cryptoKeyClassSubtagMaximumValue) + return false; + result = static_cast<CryptoKeyClassSubtag>(tag); + return true; + } + + bool read(CryptoKeyUsageTag& result) + { + uint8_t tag; + if (!read(tag)) + return false; + if (tag > cryptoKeyUsageTagMaximumValue) + return false; + result = static_cast<CryptoKeyUsageTag>(tag); + return true; + } + + bool read(CryptoKeyAsymmetricTypeSubtag& result) + { + uint8_t tag; + if (!read(tag)) + return false; + if (tag > cryptoKeyAsymmetricTypeSubtagMaximumValue) + return false; + result = static_cast<CryptoKeyAsymmetricTypeSubtag>(tag); + return true; + } + + bool readHMACKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result) + { + Vector<uint8_t> keyData; + if (!read(keyData)) + return false; + CryptoAlgorithmIdentifier hash; + if (!read(hash)) + return false; + result = CryptoKeyHMAC::importRaw(0, hash, WTFMove(keyData), extractable, usages); + return true; + } + + bool readAESKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result) + { + CryptoAlgorithmIdentifier algorithm; + if (!read(algorithm)) + return false; + if (!CryptoKeyAES::isValidAESAlgorithm(algorithm)) + return false; + Vector<uint8_t> keyData; + if (!read(keyData)) + return false; + result = CryptoKeyAES::importRaw(algorithm, WTFMove(keyData), extractable, usages); + return true; + } + + bool readRSAKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result) + { + CryptoAlgorithmIdentifier algorithm; + if (!read(algorithm)) + return false; + + int32_t isRestrictedToHash; + CryptoAlgorithmIdentifier hash = CryptoAlgorithmIdentifier::SHA_1; + if (!read(isRestrictedToHash)) + return false; + if (isRestrictedToHash && !read(hash)) + return false; + + CryptoKeyAsymmetricTypeSubtag type; + if (!read(type)) + return false; + + Vector<uint8_t> modulus; + if (!read(modulus)) + return false; + Vector<uint8_t> exponent; + if (!read(exponent)) + return false; + + if (type == CryptoKeyAsymmetricTypeSubtag::Public) { + auto keyData = CryptoKeyRSAComponents::createPublic(modulus, exponent); + auto key = CryptoKeyRSA::create(algorithm, hash, isRestrictedToHash, *keyData, extractable, usages); + result = WTFMove(key); + return true; + } + + Vector<uint8_t> privateExponent; + if (!read(privateExponent)) + return false; + + uint32_t primeCount; + if (!read(primeCount)) + return false; + + if (!primeCount) { + auto keyData = CryptoKeyRSAComponents::createPrivate(modulus, exponent, privateExponent); + auto key = CryptoKeyRSA::create(algorithm, hash, isRestrictedToHash, *keyData, extractable, usages); + result = WTFMove(key); + return true; + } + + if (primeCount < 2) + return false; + + CryptoKeyRSAComponents::PrimeInfo firstPrimeInfo; + CryptoKeyRSAComponents::PrimeInfo secondPrimeInfo; + Vector<CryptoKeyRSAComponents::PrimeInfo> otherPrimeInfos(primeCount - 2); + + if (!read(firstPrimeInfo.primeFactor)) + return false; + if (!read(firstPrimeInfo.factorCRTExponent)) + return false; + if (!read(secondPrimeInfo.primeFactor)) + return false; + if (!read(secondPrimeInfo.factorCRTExponent)) + return false; + if (!read(secondPrimeInfo.factorCRTCoefficient)) + return false; + for (unsigned i = 2; i < primeCount; ++i) { + if (!read(otherPrimeInfos[i].primeFactor)) + return false; + if (!read(otherPrimeInfos[i].factorCRTExponent)) + return false; + if (!read(otherPrimeInfos[i].factorCRTCoefficient)) + return false; + } + + auto keyData = CryptoKeyRSAComponents::createPrivateWithAdditionalData(modulus, exponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos); + auto key = CryptoKeyRSA::create(algorithm, hash, isRestrictedToHash, *keyData, extractable, usages); + result = WTFMove(key); + return true; + } + + bool readECKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result) + { + CryptoAlgorithmIdentifier algorithm; + if (!read(algorithm)) + return false; + if (!CryptoKeyEC::isValidECAlgorithm(algorithm)) + return false; + CachedStringRef curve; + if (!readStringData(curve)) + return false; + CryptoKeyAsymmetricTypeSubtag type; + if (!read(type)) + return false; + Vector<uint8_t> keyData; + if (!read(keyData)) + return false; + + switch (type) { + case CryptoKeyAsymmetricTypeSubtag::Public: + result = CryptoKeyEC::importRaw(algorithm, curve->string(), WTFMove(keyData), extractable, usages); + break; + case CryptoKeyAsymmetricTypeSubtag::Private: + result = CryptoKeyEC::importPkcs8(algorithm, curve->string(), WTFMove(keyData), extractable, usages); + break; + } + + return true; + } + + bool readRawKey(CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result) + { + CryptoAlgorithmIdentifier algorithm; + if (!read(algorithm)) + return false; + Vector<uint8_t> keyData; + if (!read(keyData)) + return false; + result = CryptoKeyRaw::create(algorithm, WTFMove(keyData), usages); + return true; + } + + bool readCryptoKey(JSValue& cryptoKey) + { + uint32_t keyFormatVersion; + if (!read(keyFormatVersion) || keyFormatVersion > currentKeyFormatVersion) + return false; + + int32_t extractable; + if (!read(extractable)) + return false; + + uint32_t usagesCount; + if (!read(usagesCount)) + return false; + + CryptoKeyUsageBitmap usages = 0; + for (uint32_t i = 0; i < usagesCount; ++i) { + CryptoKeyUsageTag usage; + if (!read(usage)) + return false; + switch (usage) { + case CryptoKeyUsageTag::Encrypt: + usages |= CryptoKeyUsageEncrypt; + break; + case CryptoKeyUsageTag::Decrypt: + usages |= CryptoKeyUsageDecrypt; + break; + case CryptoKeyUsageTag::Sign: + usages |= CryptoKeyUsageSign; + break; + case CryptoKeyUsageTag::Verify: + usages |= CryptoKeyUsageVerify; + break; + case CryptoKeyUsageTag::DeriveKey: + usages |= CryptoKeyUsageDeriveKey; + break; + case CryptoKeyUsageTag::DeriveBits: + usages |= CryptoKeyUsageDeriveBits; + break; + case CryptoKeyUsageTag::WrapKey: + usages |= CryptoKeyUsageWrapKey; + break; + case CryptoKeyUsageTag::UnwrapKey: + usages |= CryptoKeyUsageUnwrapKey; + break; + } + } + + CryptoKeyClassSubtag cryptoKeyClass; + if (!read(cryptoKeyClass)) + return false; + RefPtr<CryptoKey> result; + switch (cryptoKeyClass) { + case CryptoKeyClassSubtag::HMAC: + if (!readHMACKey(extractable, usages, result)) + return false; + break; + case CryptoKeyClassSubtag::AES: + if (!readAESKey(extractable, usages, result)) + return false; + break; + case CryptoKeyClassSubtag::RSA: + if (!readRSAKey(extractable, usages, result)) + return false; + break; + case CryptoKeyClassSubtag::EC: + if (!readECKey(extractable, usages, result)) + return false; + break; + case CryptoKeyClassSubtag::Raw: + if (!readRawKey(usages, result)) + return false; + break; + } + cryptoKey = getJSValue(result.get()); + return true; + } +#endif + + template<class T> + JSValue getJSValue(T&& nativeObj) + { + return toJS(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), std::forward<T>(nativeObj)); + } + + // template<class T> + // JSValue readDOMPoint() + // { + // double x; + // if (!read(x)) + // return {}; + // double y; + // if (!read(y)) + // return {}; + // double z; + // if (!read(z)) + // return {}; + // double w; + // if (!read(w)) + // return {}; + + // return toJSNewlyCreated(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), T::create(x, y, z, w)); + // } + + // template<class T> + // JSValue readDOMMatrix() + // { + // uint8_t is2D; + // if (!read(is2D)) + // return {}; + + // if (is2D) { + // double m11; + // if (!read(m11)) + // return {}; + // double m12; + // if (!read(m12)) + // return {}; + // double m21; + // if (!read(m21)) + // return {}; + // double m22; + // if (!read(m22)) + // return {}; + // double m41; + // if (!read(m41)) + // return {}; + // double m42; + // if (!read(m42)) + // return {}; + + // TransformationMatrix matrix(m11, m12, m21, m22, m41, m42); + // return toJSNewlyCreated(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), T::create(WTFMove(matrix), DOMMatrixReadOnly::Is2D::Yes)); + // } else { + // double m11; + // if (!read(m11)) + // return {}; + // double m12; + // if (!read(m12)) + // return {}; + // double m13; + // if (!read(m13)) + // return {}; + // double m14; + // if (!read(m14)) + // return {}; + // double m21; + // if (!read(m21)) + // return {}; + // double m22; + // if (!read(m22)) + // return {}; + // double m23; + // if (!read(m23)) + // return {}; + // double m24; + // if (!read(m24)) + // return {}; + // double m31; + // if (!read(m31)) + // return {}; + // double m32; + // if (!read(m32)) + // return {}; + // double m33; + // if (!read(m33)) + // return {}; + // double m34; + // if (!read(m34)) + // return {}; + // double m41; + // if (!read(m41)) + // return {}; + // double m42; + // if (!read(m42)) + // return {}; + // double m43; + // if (!read(m43)) + // return {}; + // double m44; + // if (!read(m44)) + // return {}; + + // TransformationMatrix matrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44); + // return toJSNewlyCreated(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), T::create(WTFMove(matrix), DOMMatrixReadOnly::Is2D::No)); + // } + // } + + // template<class T> + // JSValue readDOMRect() + // { + // double x; + // if (!read(x)) + // return {}; + // double y; + // if (!read(y)) + // return {}; + // double width; + // if (!read(width)) + // return {}; + // double height; + // if (!read(height)) + // return {}; + + // return toJSNewlyCreated(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), T::create(x, y, width, height)); + // } + + // std::optional<DOMPointInit> readDOMPointInit() + // { + // DOMPointInit point; + // if (!read(point.x)) + // return std::nullopt; + // if (!read(point.y)) + // return std::nullopt; + // if (!read(point.z)) + // return std::nullopt; + // if (!read(point.w)) + // return std::nullopt; + + // return point; + // } + + // JSValue readDOMQuad() + // { + // auto p1 = readDOMPointInit(); + // if (!p1) + // return JSValue(); + // auto p2 = readDOMPointInit(); + // if (!p2) + // return JSValue(); + // auto p3 = readDOMPointInit(); + // if (!p3) + // return JSValue(); + // auto p4 = readDOMPointInit(); + // if (!p4) + // return JSValue(); + + // return toJSNewlyCreated(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), DOMQuad::create(p1.value(), p2.value(), p3.value(), p4.value())); + // } + + // JSValue readTransferredImageBitmap() + // { + // uint32_t index; + // bool indexSuccessfullyRead = read(index); + // if (!indexSuccessfullyRead || index >= m_backingStores.size()) { + // fail(); + // return JSValue(); + // } + + // if (!m_imageBitmaps[index]) + // m_imageBitmaps[index] = ImageBitmap::create(WTFMove(m_backingStores.at(index))); + + // auto bitmap = m_imageBitmaps[index].get(); + // return getJSValue(bitmap); + // } + +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + JSValue readOffscreenCanvas() + { + uint32_t index; + bool indexSuccessfullyRead = read(index); + if (!indexSuccessfullyRead || index >= m_detachedOffscreenCanvases.size()) { + fail(); + return JSValue(); + } + + if (!m_offscreenCanvases[index]) + m_offscreenCanvases[index] = OffscreenCanvas::create(*executionContext(m_lexicalGlobalObject), WTFMove(m_detachedOffscreenCanvases.at(index))); + + auto offscreenCanvas = m_offscreenCanvases[index].get(); + return getJSValue(offscreenCanvas); + } +#endif + +#if ENABLE(WEB_RTC) + JSValue readRTCCertificate() + { + double expires; + if (!read(expires)) { + fail(); + return JSValue(); + } + CachedStringRef certificate; + if (!readStringData(certificate)) { + fail(); + return JSValue(); + } + CachedStringRef origin; + if (!readStringData(origin)) { + fail(); + return JSValue(); + } + CachedStringRef keyedMaterial; + if (!readStringData(keyedMaterial)) { + fail(); + return JSValue(); + } + unsigned size = 0; + if (!read(size)) + return JSValue(); + + Vector<RTCCertificate::DtlsFingerprint> fingerprints; + fingerprints.reserveInitialCapacity(size); + for (unsigned i = 0; i < size; i++) { + CachedStringRef algorithm; + if (!readStringData(algorithm)) + return JSValue(); + CachedStringRef value; + if (!readStringData(value)) + return JSValue(); + fingerprints.uncheckedAppend(RTCCertificate::DtlsFingerprint { algorithm->string(), value->string() }); + } + + if (!m_canCreateDOMObject) + return constructEmptyObject(m_lexicalGlobalObject, m_globalObject->objectPrototype()); + + auto rtcCertificate = RTCCertificate::create(SecurityOrigin::createFromString(origin->string()), expires, WTFMove(fingerprints), certificate->takeString(), keyedMaterial->takeString()); + return toJSNewlyCreated(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), WTFMove(rtcCertificate)); + } + + JSValue readRTCDataChannel() + { + uint32_t index; + bool indexSuccessfullyRead = read(index); + if (!indexSuccessfullyRead || index >= m_detachedRTCDataChannels.size()) { + fail(); + return JSValue(); + } + + if (!m_rtcDataChannels[index]) { + auto detachedChannel = WTFMove(m_detachedRTCDataChannels.at(index)); + m_rtcDataChannels[index] = RTCDataChannel::create(*executionContext(m_lexicalGlobalObject), detachedChannel->identifier, WTFMove(detachedChannel->label), WTFMove(detachedChannel->options), detachedChannel->state); + } + + return getJSValue(m_rtcDataChannels[index].get()); + } +#endif + + JSValue readImageBitmap() + { + uint8_t serializationState; + int32_t logicalWidth; + int32_t logicalHeight; + double resolutionScale; + auto colorSpace = DestinationColorSpace::SRGB(); + RefPtr<ArrayBuffer> arrayBuffer; + + if (!read(serializationState) || !read(logicalWidth) || !read(logicalHeight) || !read(resolutionScale) || (m_version > 8 && !read(colorSpace)) || !readArrayBufferImpl<uint32_t>(arrayBuffer)) { + fail(); + return JSValue(); + } + + auto logicalSize = IntSize(logicalWidth, logicalHeight); + auto imageDataSize = logicalSize; + imageDataSize.scale(resolutionScale); + + auto buffer = ImageBitmap::createImageBuffer(*executionContext(m_lexicalGlobalObject), logicalSize, RenderingMode::Unaccelerated, colorSpace, resolutionScale); + if (!buffer) { + fail(); + return JSValue(); + } + + PixelBufferFormat format { AlphaPremultiplication::Premultiplied, PixelFormat::RGBA8, colorSpace }; + auto pixelBuffer = PixelBuffer::tryCreate(format, imageDataSize, arrayBuffer.releaseNonNull()); + if (!pixelBuffer) { + fail(); + return JSValue(); + } + + buffer->putPixelBuffer(WTFMove(*pixelBuffer), { IntPoint::zero(), logicalSize }); + + auto bitmap = ImageBitmap::create(ImageBitmapBacking(WTFMove(buffer), OptionSet<SerializationState>::fromRaw(serializationState))); + return getJSValue(bitmap); + } + + JSValue readDOMException() + { + CachedStringRef message; + if (!readStringData(message)) + return JSValue(); + CachedStringRef name; + if (!readStringData(name)) + return JSValue(); + auto exception = DOMException::create(message->string(), name->string()); + return getJSValue(exception); + } + + JSValue readBigInt() + { + uint8_t sign = 0; + if (!read(sign)) + return JSValue(); + uint32_t lengthInUint64 = 0; + if (!read(lengthInUint64)) + return JSValue(); + + if (!lengthInUint64) { +#if USE(BIGINT32) + return jsBigInt32(0); +#else + JSBigInt* bigInt = JSBigInt::tryCreateZero(m_lexicalGlobalObject->vm()); + if (UNLIKELY(!bigInt)) { + fail(); + return JSValue(); + } + m_gcBuffer.appendWithCrashOnOverflow(bigInt); + return bigInt; +#endif + } + +#if USE(BIGINT32) + static_assert(sizeof(JSBigInt::Digit) == sizeof(uint64_t)); + if (lengthInUint64 == 1) { + uint64_t digit64 = 0; + if (!read(digit64)) + return JSValue(); + if (sign) { + if (digit64 <= static_cast<uint64_t>(-static_cast<int64_t>(INT32_MIN))) + return jsBigInt32(static_cast<int32_t>(-static_cast<int64_t>(digit64))); + } else { + if (digit64 <= INT32_MAX) + return jsBigInt32(static_cast<int32_t>(digit64)); + } + ASSERT(digit64 != 0); + JSBigInt* bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject->vm(), 1); + if (!bigInt) { + fail(); + return JSValue(); + } + bigInt->setDigit(0, digit64); + bigInt->setSign(sign); + bigInt = bigInt->tryRightTrim(m_lexicalGlobalObject->vm()); + if (!bigInt) { + fail(); + return JSValue(); + } + m_gcBuffer.appendWithCrashOnOverflow(bigInt); + return tryConvertToBigInt32(bigInt); + } +#endif + JSBigInt* bigInt = nullptr; + if constexpr (sizeof(JSBigInt::Digit) == sizeof(uint64_t)) { + bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject->vm(), lengthInUint64); + if (!bigInt) { + fail(); + return JSValue(); + } + for (uint32_t index = 0; index < lengthInUint64; ++index) { + uint64_t digit64 = 0; + if (!read(digit64)) + return JSValue(); + bigInt->setDigit(index, digit64); + } + } else { + ASSERT(sizeof(JSBigInt::Digit) == sizeof(uint32_t)); + bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject->vm(), lengthInUint64 * 2); + if (!bigInt) { + fail(); + return JSValue(); + } + for (uint32_t index = 0; index < lengthInUint64; ++index) { + uint64_t digit64 = 0; + if (!read(digit64)) + return JSValue(); + bigInt->setDigit(index * 2, static_cast<uint32_t>(digit64)); + bigInt->setDigit(index * 2 + 1, static_cast<uint32_t>(digit64 >> 32)); + } + } + bigInt->setSign(sign); + bigInt = bigInt->tryRightTrim(m_lexicalGlobalObject->vm()); + if (!bigInt) { + fail(); + return JSValue(); + } + m_gcBuffer.appendWithCrashOnOverflow(bigInt); + return tryConvertToBigInt32(bigInt); + } + + JSValue readTerminal() + { + SerializationTag tag = readTag(); + switch (tag) { + case UndefinedTag: + return jsUndefined(); + case NullTag: + return jsNull(); + case IntTag: { + int32_t i; + if (!read(i)) + return JSValue(); + return jsNumber(i); + } + case ZeroTag: + return jsNumber(0); + case OneTag: + return jsNumber(1); + case FalseTag: + return jsBoolean(false); + case TrueTag: + return jsBoolean(true); + case FalseObjectTag: { + BooleanObject* obj = BooleanObject::create(m_lexicalGlobalObject->vm(), m_globalObject->booleanObjectStructure()); + obj->setInternalValue(m_lexicalGlobalObject->vm(), jsBoolean(false)); + m_gcBuffer.appendWithCrashOnOverflow(obj); + return obj; + } + case TrueObjectTag: { + BooleanObject* obj = BooleanObject::create(m_lexicalGlobalObject->vm(), m_globalObject->booleanObjectStructure()); + obj->setInternalValue(m_lexicalGlobalObject->vm(), jsBoolean(true)); + m_gcBuffer.appendWithCrashOnOverflow(obj); + return obj; + } + case DoubleTag: { + double d; + if (!read(d)) + return JSValue(); + return jsNumber(d); + } + case BigIntTag: + return readBigInt(); + case NumberObjectTag: { + double d; + if (!read(d)) + return JSValue(); + NumberObject* obj = constructNumber(m_globalObject, jsNumber(d)); + m_gcBuffer.appendWithCrashOnOverflow(obj); + return obj; + } + case BigIntObjectTag: { + JSValue bigInt = readBigInt(); + if (!bigInt) + return JSValue(); + ASSERT(bigInt.isBigInt()); + BigIntObject* obj = BigIntObject::create(m_lexicalGlobalObject->vm(), m_globalObject, bigInt); + m_gcBuffer.appendWithCrashOnOverflow(obj); + return obj; + } + case DateTag: { + double d; + if (!read(d)) + return JSValue(); + return DateInstance::create(m_lexicalGlobalObject->vm(), m_globalObject->dateStructure(), d); + } + case FileTag: { + RefPtr<File> file; + if (!readFile(file)) + return JSValue(); + if (!m_canCreateDOMObject) + return jsNull(); + return toJS(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), file.get()); + } + case FileListTag: { + unsigned length = 0; + if (!read(length)) + return JSValue(); + ASSERT(m_globalObject->inherits<JSDOMGlobalObject>(m_globalObject->vm())); + Vector<Ref<File>> files; + for (unsigned i = 0; i < length; i++) { + RefPtr<File> file; + if (!readFile(file)) + return JSValue(); + if (m_canCreateDOMObject) + files.append(file.releaseNonNull()); + } + if (!m_canCreateDOMObject) + return jsNull(); + return getJSValue(FileList::create(WTFMove(files)).get()); + } + case ImageDataTag: { + uint32_t width; + if (!read(width)) + return JSValue(); + uint32_t height; + if (!read(height)) + return JSValue(); + uint32_t length; + if (!read(length)) + return JSValue(); + if (static_cast<uint32_t>(m_end - m_ptr) < length) { + fail(); + return JSValue(); + } + auto bufferStart = m_ptr; + m_ptr += length; + + auto resultColorSpace = PredefinedColorSpace::SRGB; + if (m_version > 7) { + if (!read(resultColorSpace)) + return JSValue(); + } + + if (length && (IntSize(width, height).area() * 4) != length) { + fail(); + return JSValue(); + } + + if (!m_isDOMGlobalObject) + return jsNull(); + + auto result = ImageData::createUninitialized(width, height, resultColorSpace); + if (result.hasException()) { + fail(); + return JSValue(); + } + if (length) + memcpy(result.returnValue()->data().data(), bufferStart, length); + else + result.returnValue()->data().zeroFill(); + return getJSValue(result.releaseReturnValue()); + } + case BlobTag: { + CachedStringRef url; + if (!readStringData(url)) + return JSValue(); + CachedStringRef type; + if (!readStringData(type)) + return JSValue(); + uint64_t size = 0; + if (!read(size)) + return JSValue(); + if (!m_canCreateDOMObject) + return jsNull(); + return getJSValue(Blob::deserialize(executionContext(m_lexicalGlobalObject), URL { url->string() }, type->string(), size, blobFilePathForBlobURL(url->string())).get()); + } + case StringTag: { + CachedStringRef cachedString; + if (!readStringData(cachedString)) + return JSValue(); + return cachedString->jsString(m_lexicalGlobalObject); + } + case EmptyStringTag: + return jsEmptyString(m_lexicalGlobalObject->vm()); + case StringObjectTag: { + CachedStringRef cachedString; + if (!readStringData(cachedString)) + return JSValue(); + StringObject* obj = constructString(m_lexicalGlobalObject->vm(), m_globalObject, cachedString->jsString(m_lexicalGlobalObject)); + m_gcBuffer.appendWithCrashOnOverflow(obj); + return obj; + } + case EmptyStringObjectTag: { + VM& vm = m_lexicalGlobalObject->vm(); + StringObject* obj = constructString(vm, m_globalObject, jsEmptyString(vm)); + m_gcBuffer.appendWithCrashOnOverflow(obj); + return obj; + } + case RegExpTag: { + CachedStringRef pattern; + if (!readStringData(pattern)) + return JSValue(); + CachedStringRef flags; + if (!readStringData(flags)) + return JSValue(); + auto reFlags = Yarr::parseFlags(flags->string()); + ASSERT(reFlags.has_value()); + VM& vm = m_lexicalGlobalObject->vm(); + RegExp* regExp = RegExp::create(vm, pattern->string(), reFlags.value()); + return RegExpObject::create(vm, m_globalObject->regExpStructure(), regExp); + } + case ObjectReferenceTag: { + unsigned index = 0; + if (!readConstantPoolIndex(m_gcBuffer, index)) { + fail(); + return JSValue(); + } + return m_gcBuffer.at(index); + } + case MessagePortReferenceTag: { + uint32_t index; + bool indexSuccessfullyRead = read(index); + if (!indexSuccessfullyRead || index >= m_messagePorts.size()) { + fail(); + return JSValue(); + } + return getJSValue(m_messagePorts[index].get()); + } +#if ENABLE(WEBASSEMBLY) + case WasmModuleTag: { + uint32_t index; + bool indexSuccessfullyRead = read(index); + if (!indexSuccessfullyRead || !m_wasmModules || index >= m_wasmModules->size()) { + fail(); + return JSValue(); + } + auto scope = DECLARE_THROW_SCOPE(m_lexicalGlobalObject->vm()); + JSValue result = JSC::JSWebAssemblyModule::createStub(m_lexicalGlobalObject->vm(), m_lexicalGlobalObject, m_globalObject->webAssemblyModuleStructure(), m_wasmModules->at(index)); + // Since we are cloning a JSWebAssemblyModule, it's impossible for that + // module to not have been a valid module. Therefore, createStub should + // not throw. + scope.releaseAssertNoException(); + m_gcBuffer.appendWithCrashOnOverflow(result); + return result; + } + case WasmMemoryTag: { + uint32_t index; + bool indexSuccessfullyRead = read(index); + if (!indexSuccessfullyRead || !m_wasmMemoryHandles || index >= m_wasmMemoryHandles->size()) { + fail(); + return JSValue(); + } + RefPtr<Wasm::MemoryHandle> handle = m_wasmMemoryHandles->at(index); + if (!handle) { + fail(); + return JSValue(); + } + auto& vm = m_lexicalGlobalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSWebAssemblyMemory* result = JSC::JSWebAssemblyMemory::tryCreate(m_lexicalGlobalObject, vm, m_globalObject->webAssemblyMemoryStructure()); + // Since we are cloning a JSWebAssemblyMemory, it's impossible for that + // module to not have been a valid module. Therefore, createStub should + // not throw. + scope.releaseAssertNoException(); + Ref<Wasm::Memory> memory = Wasm::Memory::create( + handle.releaseNonNull(), + [&vm](Wasm::Memory::NotifyPressure) { vm.heap.collectAsync(CollectionScope::Full); }, + [&vm](Wasm::Memory::SyncTryToReclaim) { vm.heap.collectSync(CollectionScope::Full); }, + [&vm, result](Wasm::Memory::GrowSuccess, Wasm::PageCount oldPageCount, Wasm::PageCount newPageCount) { result->growSuccessCallback(vm, oldPageCount, newPageCount); }); + result->adopt(WTFMove(memory)); + m_gcBuffer.appendWithCrashOnOverflow(result); + return result; + } +#endif + case ArrayBufferTag: { + RefPtr<ArrayBuffer> arrayBuffer; + if (!readArrayBuffer(arrayBuffer)) { + fail(); + return JSValue(); + } + Structure* structure = m_globalObject->arrayBufferStructure(arrayBuffer->sharingMode()); + // A crazy RuntimeFlags mismatch could mean that we are not equipped to handle shared + // array buffers while the sender is. In that case, we would see a null structure here. + if (!structure) { + fail(); + return JSValue(); + } + JSValue result = JSArrayBuffer::create(m_lexicalGlobalObject->vm(), structure, WTFMove(arrayBuffer)); + m_gcBuffer.appendWithCrashOnOverflow(result); + return result; + } + case ArrayBufferTransferTag: { + uint32_t index; + bool indexSuccessfullyRead = read(index); + if (!indexSuccessfullyRead || index >= m_arrayBuffers.size()) { + fail(); + return JSValue(); + } + + if (!m_arrayBuffers[index]) + m_arrayBuffers[index] = ArrayBuffer::create(WTFMove(m_arrayBufferContents->at(index))); + + return getJSValue(m_arrayBuffers[index].get()); + } + case SharedArrayBufferTag: { + uint32_t index = UINT_MAX; + bool indexSuccessfullyRead = read(index); + if (!indexSuccessfullyRead || !m_sharedBuffers || index >= m_sharedBuffers->size()) { + fail(); + return JSValue(); + } + + RELEASE_ASSERT(m_sharedBuffers->at(index)); + auto buffer = ArrayBuffer::create(WTFMove(m_sharedBuffers->at(index))); + JSValue result = getJSValue(buffer.get()); + m_gcBuffer.appendWithCrashOnOverflow(result); + return result; + } + case ArrayBufferViewTag: { + JSValue arrayBufferView; + if (!readArrayBufferView(m_lexicalGlobalObject->vm(), arrayBufferView)) { + fail(); + return JSValue(); + } + m_gcBuffer.appendWithCrashOnOverflow(arrayBufferView); + return arrayBufferView; + } +#if ENABLE(WEB_CRYPTO) + case CryptoKeyTag: { + Vector<uint8_t> wrappedKey; + if (!read(wrappedKey)) { + fail(); + return JSValue(); + } + Vector<uint8_t> serializedKey; + if (!unwrapCryptoKey(m_lexicalGlobalObject, wrappedKey, serializedKey)) { + fail(); + return JSValue(); + } + JSValue cryptoKey; + Vector<RefPtr<MessagePort>> dummyMessagePorts; + CloneDeserializer rawKeyDeserializer(m_lexicalGlobalObject, m_globalObject, dummyMessagePorts, nullptr, {}, serializedKey); + if (!rawKeyDeserializer.readCryptoKey(cryptoKey)) { + fail(); + return JSValue(); + } + m_gcBuffer.appendWithCrashOnOverflow(cryptoKey); + return cryptoKey; + } +#endif + // case DOMPointReadOnlyTag: + // return readDOMPoint<DOMPointReadOnly>(); + // case DOMPointTag: + // return readDOMPoint<DOMPoint>(); + // case DOMRectReadOnlyTag: + // return readDOMRect<DOMRectReadOnly>(); + // case DOMRectTag: + // return readDOMRect<DOMRect>(); + // case DOMMatrixReadOnlyTag: + // return readDOMMatrix<DOMMatrixReadOnly>(); + // case DOMMatrixTag: + // return readDOMMatrix<DOMMatrix>(); + // case DOMQuadTag: + // return readDOMQuad(); + // case ImageBitmapTransferTag: + // return readTransferredImageBitmap(); +#if ENABLE(WEB_RTC) + case RTCCertificateTag: + return readRTCCertificate(); + +#endif + // case ImageBitmapTag: + // return readImageBitmap(); +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + case OffscreenCanvasTransferTag: + return readOffscreenCanvas(); +#endif +#if ENABLE(WEB_RTC) + case RTCDataChannelTransferTag: + return readRTCDataChannel(); +#endif + case DOMExceptionTag: + return readDOMException(); + + default: + m_ptr--; // Push the tag back + return JSValue(); + } + } + + template<SerializationTag Tag> + bool consumeCollectionDataTerminationIfPossible() + { + if (readTag() == Tag) + return true; + m_ptr--; + return false; + } + + JSGlobalObject* m_globalObject; + bool m_isDOMGlobalObject; + bool m_canCreateDOMObject; + const uint8_t* m_ptr; + const uint8_t* m_end; + unsigned m_version; + Vector<CachedString> m_constantPool; + const Vector<RefPtr<MessagePort>>& m_messagePorts; + ArrayBufferContentsArray* m_arrayBufferContents; + Vector<RefPtr<JSC::ArrayBuffer>> m_arrayBuffers; + // Vector<String> m_blobURLs; + // Vector<String> m_blobFilePaths; + ArrayBufferContentsArray* m_sharedBuffers; + // Vector<std::optional<ImageBitmapBacking>> m_backingStores; + // Vector<RefPtr<ImageBitmap>> m_imageBitmaps; +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + Vector<std::unique_ptr<DetachedOffscreenCanvas>> m_detachedOffscreenCanvases; + Vector<RefPtr<OffscreenCanvas>> m_offscreenCanvases; +#endif +#if ENABLE(WEB_RTC) + Vector<std::unique_ptr<DetachedRTCDataChannel>> m_detachedRTCDataChannels; + Vector<RefPtr<RTCDataChannel>> m_rtcDataChannels; +#endif +#if ENABLE(WEBASSEMBLY) + WasmModuleArray* m_wasmModules; + WasmMemoryHandleArray* m_wasmMemoryHandles; +#endif + + // String blobFilePathForBlobURL(const String& blobURL) + // { + // size_t i = 0; + // for (; i < m_blobURLs.size(); ++i) { + // if (m_blobURLs[i] == blobURL) + // break; + // } + + // return i < m_blobURLs.size() ? m_blobFilePaths[i] : String(); + // } +}; + +DeserializationResult CloneDeserializer::deserialize() +{ + VM& vm = m_lexicalGlobalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + Vector<uint32_t, 16> indexStack; + Vector<Identifier, 16> propertyNameStack; + Vector<JSObject*, 32> outputObjectStack; + Vector<JSValue, 4> mapKeyStack; + Vector<JSMap*, 4> mapStack; + Vector<JSSet*, 4> setStack; + Vector<WalkerState, 16> stateStack; + WalkerState lexicalGlobalObject = StateUnknown; + JSValue outValue; + + while (1) { + switch (lexicalGlobalObject) { + arrayStartState: + case ArrayStartState: { + uint32_t length; + if (!read(length)) { + fail(); + goto error; + } + JSArray* outArray = constructEmptyArray(m_globalObject, static_cast<JSC::ArrayAllocationProfile*>(nullptr), length); + if (UNLIKELY(scope.exception())) + goto error; + m_gcBuffer.appendWithCrashOnOverflow(outArray); + outputObjectStack.append(outArray); + } + arrayStartVisitMember: + FALLTHROUGH; + case ArrayStartVisitMember: { + uint32_t index; + if (!read(index)) { + fail(); + goto error; + } + if (index == TerminatorTag) { + JSObject* outArray = outputObjectStack.last(); + outValue = outArray; + outputObjectStack.removeLast(); + break; + } else if (index == NonIndexPropertiesTag) { + goto objectStartVisitMember; + } + + if (JSValue terminal = readTerminal()) { + putProperty(outputObjectStack.last(), index, terminal); + goto arrayStartVisitMember; + } + if (m_failed) + goto error; + indexStack.append(index); + stateStack.append(ArrayEndVisitMember); + goto stateUnknown; + } + case ArrayEndVisitMember: { + JSObject* outArray = outputObjectStack.last(); + putProperty(outArray, indexStack.last(), outValue); + indexStack.removeLast(); + goto arrayStartVisitMember; + } + objectStartState: + case ObjectStartState: { + if (outputObjectStack.size() > maximumFilterRecursion) + return std::make_pair(JSValue(), SerializationReturnCode::StackOverflowError); + JSObject* outObject = constructEmptyObject(m_lexicalGlobalObject, m_globalObject->objectPrototype()); + m_gcBuffer.appendWithCrashOnOverflow(outObject); + outputObjectStack.append(outObject); + } + objectStartVisitMember: + FALLTHROUGH; + case ObjectStartVisitMember: { + CachedStringRef cachedString; + bool wasTerminator = false; + if (!readStringData(cachedString, wasTerminator)) { + if (!wasTerminator) + goto error; + + JSObject* outObject = outputObjectStack.last(); + outValue = outObject; + outputObjectStack.removeLast(); + break; + } + + if (JSValue terminal = readTerminal()) { + putProperty(outputObjectStack.last(), Identifier::fromString(vm, cachedString->string()), terminal); + goto objectStartVisitMember; + } + stateStack.append(ObjectEndVisitMember); + propertyNameStack.append(Identifier::fromString(vm, cachedString->string())); + goto stateUnknown; + } + case ObjectEndVisitMember: { + putProperty(outputObjectStack.last(), propertyNameStack.last(), outValue); + propertyNameStack.removeLast(); + goto objectStartVisitMember; + } + mapObjectStartState : { + if (outputObjectStack.size() > maximumFilterRecursion) + return std::make_pair(JSValue(), SerializationReturnCode::StackOverflowError); + JSMap* map = JSMap::create(m_lexicalGlobalObject, m_lexicalGlobalObject->vm(), m_globalObject->mapStructure()); + if (UNLIKELY(scope.exception())) + goto error; + m_gcBuffer.appendWithCrashOnOverflow(map); + outputObjectStack.append(map); + mapStack.append(map); + goto mapDataStartVisitEntry; + } + mapDataStartVisitEntry: + case MapDataStartVisitEntry: { + if (consumeCollectionDataTerminationIfPossible<NonMapPropertiesTag>()) { + mapStack.removeLast(); + goto objectStartVisitMember; + } + stateStack.append(MapDataEndVisitKey); + goto stateUnknown; + } + case MapDataEndVisitKey: { + mapKeyStack.append(outValue); + stateStack.append(MapDataEndVisitValue); + goto stateUnknown; + } + case MapDataEndVisitValue: { + mapStack.last()->set(m_lexicalGlobalObject, mapKeyStack.last(), outValue); + mapKeyStack.removeLast(); + goto mapDataStartVisitEntry; + } + + setObjectStartState : { + if (outputObjectStack.size() > maximumFilterRecursion) + return std::make_pair(JSValue(), SerializationReturnCode::StackOverflowError); + JSSet* set = JSSet::create(m_lexicalGlobalObject, m_lexicalGlobalObject->vm(), m_globalObject->setStructure()); + if (UNLIKELY(scope.exception())) + goto error; + m_gcBuffer.appendWithCrashOnOverflow(set); + outputObjectStack.append(set); + setStack.append(set); + goto setDataStartVisitEntry; + } + setDataStartVisitEntry: + case SetDataStartVisitEntry: { + if (consumeCollectionDataTerminationIfPossible<NonSetPropertiesTag>()) { + setStack.removeLast(); + goto objectStartVisitMember; + } + stateStack.append(SetDataEndVisitKey); + goto stateUnknown; + } + case SetDataEndVisitKey: { + JSSet* set = setStack.last(); + set->add(m_lexicalGlobalObject, outValue); + goto setDataStartVisitEntry; + } + + stateUnknown: + case StateUnknown: + if (JSValue terminal = readTerminal()) { + outValue = terminal; + break; + } + SerializationTag tag = readTag(); + if (tag == ArrayTag) + goto arrayStartState; + if (tag == ObjectTag) + goto objectStartState; + if (tag == MapObjectTag) + goto mapObjectStartState; + if (tag == SetObjectTag) + goto setObjectStartState; + goto error; + } + if (stateStack.isEmpty()) + break; + + lexicalGlobalObject = stateStack.last(); + stateStack.removeLast(); + } + ASSERT(outValue); + ASSERT(!m_failed); + return std::make_pair(outValue, SerializationReturnCode::SuccessfullyCompleted); +error: + fail(); + return std::make_pair(JSValue(), SerializationReturnCode::ValidationError); +} + +SerializedScriptValue::~SerializedScriptValue() = default; + +SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, std::unique_ptr<ArrayBufferContentsArray>&& arrayBufferContentsArray +#if ENABLE(WEB_RTC) + , + Vector<std::unique_ptr<DetachedRTCDataChannel>>&& detachedRTCDataChannels +#endif + ) + : m_data(WTFMove(buffer)) + , m_arrayBufferContentsArray(WTFMove(arrayBufferContentsArray)) +#if ENABLE(WEB_RTC) + , m_detachedRTCDataChannels(WTFMove(detachedRTCDataChannels)) +#endif +{ + m_memoryCost = computeMemoryCost(); +} + +SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, /* const Vector<BlobURLHandle>& blobHandles, */ std::unique_ptr<ArrayBufferContentsArray> arrayBufferContentsArray, std::unique_ptr<ArrayBufferContentsArray> sharedBufferContentsArray, Vector<std::optional<ImageBitmapBacking>>&& backingStores +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + , + Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& detachedOffscreenCanvases +#endif +#if ENABLE(WEB_RTC) + , + Vector<std::unique_ptr<DetachedRTCDataChannel>>&& detachedRTCDataChannels +#endif +#if ENABLE(WEBASSEMBLY) + , + std::unique_ptr<WasmModuleArray> wasmModulesArray, std::unique_ptr<WasmMemoryHandleArray> wasmMemoryHandlesArray +#endif + ) + : m_data(WTFMove(buffer)) + , m_arrayBufferContentsArray(WTFMove(arrayBufferContentsArray)) + , m_sharedBufferContentsArray(WTFMove(sharedBufferContentsArray)) + , m_backingStores(WTFMove(backingStores)) +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + , m_detachedOffscreenCanvases(WTFMove(detachedOffscreenCanvases)) +#endif +#if ENABLE(WEB_RTC) + , m_detachedRTCDataChannels(WTFMove(detachedRTCDataChannels)) +#endif +#if ENABLE(WEBASSEMBLY) + , m_wasmModulesArray(WTFMove(wasmModulesArray)) + , m_wasmMemoryHandlesArray(WTFMove(wasmMemoryHandlesArray)) +#endif +// , m_blobHandles(blobHandles) +{ + m_memoryCost = computeMemoryCost(); +} + +size_t SerializedScriptValue::computeMemoryCost() const +{ + size_t cost = m_data.size(); + + if (m_arrayBufferContentsArray) { + for (auto& content : *m_arrayBufferContentsArray) + cost += content.sizeInBytes(); + } + + if (m_sharedBufferContentsArray) { + for (auto& content : *m_sharedBufferContentsArray) + cost += content.sizeInBytes(); + } + + for (auto& backingStore : m_backingStores) { + if (auto buffer = backingStore ? backingStore->buffer() : nullptr) + cost += buffer->memoryCost(); + } + +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + for (auto& canvas : m_detachedOffscreenCanvases) { + if (canvas) + cost += canvas->memoryCost(); + } +#endif +#if ENABLE(WEB_RTC) + for (auto& channel : m_detachedRTCDataChannels) { + if (channel) + cost += channel->memoryCost(); + } +#endif + +#if ENABLE(WEBASSEMBLY) + // We are not supporting WebAssembly Module memory estimation yet. + if (m_wasmMemoryHandlesArray) { + for (auto& content : *m_wasmMemoryHandlesArray) + cost += content->size(); + } +#endif + + // for (auto& handle : m_blobHandles) + // cost += handle.url().string().sizeInBytes(); + + return cost; +} + +static ExceptionOr<std::unique_ptr<ArrayBufferContentsArray>> transferArrayBuffers(VM& vm, const Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers) +{ + if (arrayBuffers.isEmpty()) + return nullptr; + + auto contents = makeUnique<ArrayBufferContentsArray>(arrayBuffers.size()); + + HashSet<JSC::ArrayBuffer*> visited; + for (size_t arrayBufferIndex = 0; arrayBufferIndex < arrayBuffers.size(); arrayBufferIndex++) { + if (visited.contains(arrayBuffers[arrayBufferIndex].get())) + continue; + visited.add(arrayBuffers[arrayBufferIndex].get()); + + bool result = arrayBuffers[arrayBufferIndex]->transferTo(vm, contents->at(arrayBufferIndex)); + if (!result) + return Exception { TypeError }; + } + + return contents; +} + +static void maybeThrowExceptionIfSerializationFailed(JSGlobalObject& lexicalGlobalObject, SerializationReturnCode code) +{ + auto& vm = lexicalGlobalObject.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + switch (code) { + case SerializationReturnCode::SuccessfullyCompleted: + break; + case SerializationReturnCode::StackOverflowError: + throwException(&lexicalGlobalObject, scope, createStackOverflowError(&lexicalGlobalObject)); + break; + case SerializationReturnCode::ValidationError: + throwTypeError(&lexicalGlobalObject, scope, "Unable to deserialize data."_s); + break; + case SerializationReturnCode::DataCloneError: + throwDataCloneError(lexicalGlobalObject, scope); + break; + case SerializationReturnCode::ExistingExceptionError: + case SerializationReturnCode::UnspecifiedError: + break; + case SerializationReturnCode::InterruptedExecutionError: + ASSERT_NOT_REACHED(); + } +} + +static Exception exceptionForSerializationFailure(SerializationReturnCode code) +{ + ASSERT(code != SerializationReturnCode::SuccessfullyCompleted); + + switch (code) { + case SerializationReturnCode::StackOverflowError: + return Exception { StackOverflowError }; + case SerializationReturnCode::ValidationError: + return Exception { TypeError }; + case SerializationReturnCode::DataCloneError: + return Exception { DataCloneError }; + case SerializationReturnCode::ExistingExceptionError: + return Exception { ExistingExceptionError }; + case SerializationReturnCode::UnspecifiedError: + return Exception { TypeError }; + case SerializationReturnCode::SuccessfullyCompleted: + case SerializationReturnCode::InterruptedExecutionError: + ASSERT_NOT_REACHED(); + return Exception { TypeError }; + } + ASSERT_NOT_REACHED(); + return Exception { TypeError }; +} + +static bool containsDuplicates(const Vector<RefPtr<ImageBitmap>>& imageBitmaps) +{ + HashSet<ImageBitmap*> visited; + for (auto& imageBitmap : imageBitmaps) { + if (!visited.add(imageBitmap.get())) + return true; + } + return false; +} + +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) +static bool canOffscreenCanvasesDetach(const Vector<RefPtr<OffscreenCanvas>>& offscreenCanvases) +{ + HashSet<OffscreenCanvas*> visited; + for (auto& offscreenCanvas : offscreenCanvases) { + if (!offscreenCanvas->canDetach()) + return false; + // Check the return value of add, we should not encounter duplicates. + if (!visited.add(offscreenCanvas.get())) + return false; + } + return true; +} +#endif + +#if ENABLE(WEB_RTC) +static bool canDetachRTCDataChannels(const Vector<Ref<RTCDataChannel>>& channels) +{ + HashSet<RTCDataChannel*> visited; + for (auto& channel : channels) { + if (!channel->canDetach()) + return false; + // Check the return value of add, we should not encounter duplicates. + if (!visited.add(channel.ptr())) + return false; + } + return true; +} +#endif + +RefPtr<SerializedScriptValue> SerializedScriptValue::create(JSC::JSGlobalObject& globalObject, JSC::JSValue value, SerializationErrorMode throwExceptions, SerializationContext serializationContext) +{ + Vector<RefPtr<MessagePort>> dummyPorts; + auto result = create(globalObject, value, {}, dummyPorts, throwExceptions, serializationContext); + if (result.hasException()) + return nullptr; + return result.releaseReturnValue(); +} + +ExceptionOr<Ref<SerializedScriptValue>> SerializedScriptValue::create(JSGlobalObject& globalObject, JSValue value, Vector<JSC::Strong<JSC::JSObject>>&& transferList, Vector<RefPtr<MessagePort>>& messagePorts, SerializationContext serializationContext) +{ + return create(globalObject, value, WTFMove(transferList), messagePorts, SerializationErrorMode::NonThrowing, serializationContext); +} + +ExceptionOr<Ref<SerializedScriptValue>> SerializedScriptValue::create(JSGlobalObject& lexicalGlobalObject, JSValue value, Vector<JSC::Strong<JSC::JSObject>>&& transferList, Vector<RefPtr<MessagePort>>& messagePorts, SerializationErrorMode throwExceptions, SerializationContext context) +{ + VM& vm = lexicalGlobalObject.vm(); + Vector<RefPtr<JSC::ArrayBuffer>> arrayBuffers; + Vector<RefPtr<ImageBitmap>> imageBitmaps; +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + Vector<RefPtr<OffscreenCanvas>> offscreenCanvases; +#endif +#if ENABLE(WEB_RTC) + Vector<Ref<RTCDataChannel>> dataChannels; +#endif + HashSet<JSC::JSObject*> uniqueTransferables; + for (auto& transferable : transferList) { + if (!uniqueTransferables.add(transferable.get()).isNewEntry) + return Exception { DataCloneError, "Duplicate transferable for structured clone"_s }; + + if (auto arrayBuffer = toPossiblySharedArrayBuffer(vm, transferable.get())) { + if (arrayBuffer->isDetached() || arrayBuffer->isShared()) + return Exception { DataCloneError }; + if (arrayBuffer->isLocked()) { + auto scope = DECLARE_THROW_SCOPE(vm); + throwVMTypeError(&lexicalGlobalObject, scope, errorMesasgeForTransfer(arrayBuffer)); + return Exception { ExistingExceptionError }; + } + arrayBuffers.append(WTFMove(arrayBuffer)); + continue; + } + if (auto port = JSMessagePort::toWrapped(vm, transferable.get())) { + // FIXME: This should check if the port is detached as per https://html.spec.whatwg.org/multipage/infrastructure.html#istransferable. + messagePorts.append(WTFMove(port)); + continue; + } + + if (auto imageBitmap = JSImageBitmap::toWrapped(vm, transferable.get())) { + if (imageBitmap->isDetached()) + return Exception { DataCloneError }; + + imageBitmaps.append(WTFMove(imageBitmap)); + continue; + } + +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + if (auto offscreenCanvas = JSOffscreenCanvas::toWrapped(vm, transferable.get())) { + offscreenCanvases.append(WTFMove(offscreenCanvas)); + continue; + } +#endif + +#if ENABLE(WEB_RTC) + if (auto channel = JSRTCDataChannel::toWrapped(vm, transferable.get())) { + dataChannels.append(*channel); + continue; + } +#endif + + return Exception { DataCloneError }; + } + + if (containsDuplicates(imageBitmaps)) + return Exception { DataCloneError }; +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + if (!canOffscreenCanvasesDetach(offscreenCanvases)) + return Exception { InvalidStateError }; +#endif +#if ENABLE(WEB_RTC) + if (!canDetachRTCDataChannels(dataChannels)) + return Exception { DataCloneError }; +#endif + + Vector<uint8_t> buffer; + // Vector<BlobURLHandle> blobHandles; +#if ENABLE(WEBASSEMBLY) + WasmModuleArray wasmModules; + WasmMemoryHandleArray wasmMemoryHandles; +#endif + std::unique_ptr<ArrayBufferContentsArray> sharedBuffers = makeUnique<ArrayBufferContentsArray>(); + auto code = CloneSerializer::serialize(&lexicalGlobalObject, value, messagePorts, arrayBuffers, imageBitmaps, +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + offscreenCanvases, +#endif +#if ENABLE(WEB_RTC) + dataChannels, +#endif +#if ENABLE(WEBASSEMBLY) + wasmModules, + wasmMemoryHandles, +#endif + /*blobHandles,*/ buffer, context, *sharedBuffers); + + if (throwExceptions == SerializationErrorMode::Throwing) + maybeThrowExceptionIfSerializationFailed(lexicalGlobalObject, code); + + if (code != SerializationReturnCode::SuccessfullyCompleted) + return exceptionForSerializationFailure(code); + + auto arrayBufferContentsArray = transferArrayBuffers(vm, arrayBuffers); + if (arrayBufferContentsArray.hasException()) + return arrayBufferContentsArray.releaseException(); + + auto backingStores = ImageBitmap::detachBitmaps(WTFMove(imageBitmaps)); + +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + Vector<std::unique_ptr<DetachedOffscreenCanvas>> detachedCanvases; + for (auto offscreenCanvas : offscreenCanvases) + detachedCanvases.append(offscreenCanvas->detach()); +#endif +#if ENABLE(WEB_RTC) + Vector<std::unique_ptr<DetachedRTCDataChannel>> detachedRTCDataChannels; + for (auto& channel : dataChannels) + detachedRTCDataChannels.append(channel->detach()); +#endif + + return adoptRef(*new SerializedScriptValue(WTFMove(buffer), /*blobHandles,*/ arrayBufferContentsArray.releaseReturnValue(), context == SerializationContext::WorkerPostMessage ? WTFMove(sharedBuffers) : nullptr, WTFMove(backingStores) +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + , + WTFMove(detachedCanvases) +#endif +#if ENABLE(WEB_RTC) + , + WTFMove(detachedRTCDataChannels) +#endif +#if ENABLE(WEBASSEMBLY) + , + makeUnique<WasmModuleArray>(wasmModules), context == SerializationContext::WorkerPostMessage ? makeUnique<WasmMemoryHandleArray>(wasmMemoryHandles) : nullptr +#endif + )); +} + +RefPtr<SerializedScriptValue> SerializedScriptValue::create(StringView string) +{ + Vector<uint8_t> buffer; + if (!CloneSerializer::serialize(string, buffer)) + return nullptr; + return adoptRef(*new SerializedScriptValue(WTFMove(buffer))); +} + +RefPtr<SerializedScriptValue> SerializedScriptValue::create(JSContextRef originContext, JSValueRef apiValue, JSValueRef* exception) +{ + JSGlobalObject* lexicalGlobalObject = toJS(originContext); + VM& vm = lexicalGlobalObject->vm(); + JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + + JSValue value = toJS(lexicalGlobalObject, apiValue); + auto serializedValue = SerializedScriptValue::create(*lexicalGlobalObject, value); + if (UNLIKELY(scope.exception())) { + if (exception) + *exception = toRef(lexicalGlobalObject, scope.exception()->value()); + scope.clearException(); + return nullptr; + } + ASSERT(serializedValue); + return serializedValue; +} + +String SerializedScriptValue::toString() const +{ + return CloneDeserializer::deserializeString(m_data); +} + +JSValue SerializedScriptValue::deserialize(JSGlobalObject& lexicalGlobalObject, JSGlobalObject* globalObject, SerializationErrorMode throwExceptions) +{ + return deserialize(lexicalGlobalObject, globalObject, {}, throwExceptions); +} + +JSValue SerializedScriptValue::deserialize(JSGlobalObject& lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, SerializationErrorMode throwExceptions) +{ + Vector<String> dummyBlobs; + Vector<String> dummyPaths; + return deserialize(lexicalGlobalObject, globalObject, messagePorts, dummyBlobs, dummyPaths, throwExceptions); +} + +JSValue SerializedScriptValue::deserialize(JSGlobalObject& lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths, SerializationErrorMode throwExceptions) +{ + DeserializationResult result = CloneDeserializer::deserialize(&lexicalGlobalObject, globalObject, messagePorts, WTFMove(m_backingStores) +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + , + WTFMove(m_detachedOffscreenCanvases) +#endif +#if ENABLE(WEB_RTC) + , + WTFMove(m_detachedRTCDataChannels) +#endif + , + m_arrayBufferContentsArray.get(), m_data, blobURLs, blobFilePaths, m_sharedBufferContentsArray.get() +#if ENABLE(WEBASSEMBLY) + , + m_wasmModulesArray.get(), m_wasmMemoryHandlesArray.get() +#endif + ); + if (throwExceptions == SerializationErrorMode::Throwing) + maybeThrowExceptionIfSerializationFailed(lexicalGlobalObject, result.second); + return result.first ? result.first : jsNull(); +} + +JSValueRef SerializedScriptValue::deserialize(JSContextRef destinationContext, JSValueRef* exception) +{ + JSGlobalObject* lexicalGlobalObject = toJS(destinationContext); + VM& vm = lexicalGlobalObject->vm(); + JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + + JSValue value = deserialize(*lexicalGlobalObject, lexicalGlobalObject); + if (UNLIKELY(scope.exception())) { + if (exception) + *exception = toRef(lexicalGlobalObject, scope.exception()->value()); + scope.clearException(); + return nullptr; + } + ASSERT(value); + return toRef(lexicalGlobalObject, value); +} + +Ref<SerializedScriptValue> SerializedScriptValue::nullValue() +{ + return adoptRef(*new SerializedScriptValue(Vector<uint8_t>())); +} + +uint32_t SerializedScriptValue::wireFormatVersion() +{ + return CurrentVersion; +} + +// Vector<String> SerializedScriptValue::blobURLs() const +// { +// return m_blobHandles.map([](auto& handle) { +// return handle.url().string(); +// }); +// } + +// void SerializedScriptValue::writeBlobsToDiskForIndexedDB(CompletionHandler<void(IDBValue&&)>&& completionHandler) +// { +// ASSERT(isMainThread()); +// ASSERT(hasBlobURLs()); + +// blobRegistry().writeBlobsToTemporaryFilesForIndexedDB(blobURLs(), [completionHandler = WTFMove(completionHandler), this, protectedThis = Ref { *this }](auto&& blobFilePaths) mutable { +// ASSERT(isMainThread()); + +// if (blobFilePaths.isEmpty()) { +// // We should have successfully written blobs to temporary files. +// // If we failed, then we can't successfully store this record. +// completionHandler({}); +// return; +// } + +// ASSERT(m_blobHandles.size() == blobFilePaths.size()); + +// completionHandler({ *this, blobURLs(), blobFilePaths }); +// }); +// } + +// IDBValue SerializedScriptValue::writeBlobsToDiskForIndexedDBSynchronously() +// { +// ASSERT(!isMainThread()); + +// BinarySemaphore semaphore; +// IDBValue value; +// callOnMainThread([this, &semaphore, &value] { +// writeBlobsToDiskForIndexedDB([&semaphore, &value](IDBValue&& result) { +// ASSERT(isMainThread()); +// value.setAsIsolatedCopy(result); + +// semaphore.signal(); +// }); +// }); +// semaphore.wait(); + +// return value; +// } + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/SerializedScriptValue.h b/src/javascript/jsc/bindings/webcore/SerializedScriptValue.h new file mode 100644 index 000000000..6562fb238 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/SerializedScriptValue.h @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2009, 2013, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#include "Blob.h" +#include "DetachedRTCDataChannel.h" +#include "ExceptionOr.h" +#include <JavaScriptCore/ArrayBuffer.h> +#include <JavaScriptCore/JSCJSValue.h> +#include <JavaScriptCore/Strong.h> +#include <wtf/Forward.h> +#include <wtf/Function.h> +#include <wtf/Gigacage.h> +#include <wtf/text/WTFString.h> + +typedef const struct OpaqueJSContext* JSContextRef; +typedef const struct OpaqueJSValue* JSValueRef; + +#if ENABLE(WEBASSEMBLY) +namespace JSC { +namespace Wasm { +class Module; +class MemoryHandle; +} +} +#endif + +namespace WebCore { + +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) +class DetachedOffscreenCanvas; +#endif +class IDBValue; +class MessagePort; +class ImageBitmapBacking; +class FragmentedSharedBuffer; +enum class SerializationReturnCode; + +enum class SerializationErrorMode { NonThrowing, + Throwing }; +enum class SerializationContext { Default, + WorkerPostMessage, + WindowPostMessage }; + +using ArrayBufferContentsArray = Vector<JSC::ArrayBufferContents>; +#if ENABLE(WEBASSEMBLY) +using WasmModuleArray = Vector<RefPtr<JSC::Wasm::Module>>; +using WasmMemoryHandleArray = Vector<RefPtr<JSC::Wasm::MemoryHandle>>; +#endif + +DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(SerializedScriptValue); +class SerializedScriptValue : public ThreadSafeRefCounted<SerializedScriptValue> { + WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(SerializedScriptValue); + +public: + WEBCORE_EXPORT static ExceptionOr<Ref<SerializedScriptValue>> create(JSC::JSGlobalObject&, JSC::JSValue, Vector<JSC::Strong<JSC::JSObject>>&& transfer, Vector<RefPtr<MessagePort>>&, SerializationContext = SerializationContext::Default); + WEBCORE_EXPORT static RefPtr<SerializedScriptValue> create(JSC::JSGlobalObject&, JSC::JSValue, SerializationErrorMode = SerializationErrorMode::Throwing, SerializationContext = SerializationContext::Default); + + WEBCORE_EXPORT static RefPtr<SerializedScriptValue> create(StringView); + + static Ref<SerializedScriptValue> nullValue(); + + WEBCORE_EXPORT JSC::JSValue deserialize(JSC::JSGlobalObject&, JSC::JSGlobalObject*, SerializationErrorMode = SerializationErrorMode::Throwing); + WEBCORE_EXPORT JSC::JSValue deserialize(JSC::JSGlobalObject&, JSC::JSGlobalObject*, const Vector<RefPtr<MessagePort>>&, SerializationErrorMode = SerializationErrorMode::Throwing); + JSC::JSValue deserialize(JSC::JSGlobalObject&, JSC::JSGlobalObject*, const Vector<RefPtr<MessagePort>>&, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths, SerializationErrorMode = SerializationErrorMode::Throwing); + + static uint32_t wireFormatVersion(); + + String toString() const; + + // API implementation helpers. These don't expose special behavior for ArrayBuffers or MessagePorts. + WEBCORE_EXPORT static RefPtr<SerializedScriptValue> create(JSContextRef, JSValueRef, JSValueRef* exception); + WEBCORE_EXPORT JSValueRef deserialize(JSContextRef, JSValueRef* exception); + + // bool hasBlobURLs() const { return !m_blobHandles.isEmpty(); } + + // Vector<String> blobURLs() const; + // const Vector<BlobURLHandle>& blobHandles() const { return m_blobHandles; } + // void writeBlobsToDiskForIndexedDB(CompletionHandler<void(IDBValue&&)>&&); + // IDBValue writeBlobsToDiskForIndexedDBSynchronously(); + static Ref<SerializedScriptValue> createFromWireBytes(Vector<uint8_t>&& data) + { + return adoptRef(*new SerializedScriptValue(WTFMove(data))); + } + const Vector<uint8_t>& wireBytes() const { return m_data; } + + template<class Encoder> void encode(Encoder&) const; + template<class Decoder> static RefPtr<SerializedScriptValue> decode(Decoder&); + + size_t memoryCost() const { return m_memoryCost; } + + WEBCORE_EXPORT ~SerializedScriptValue(); + +private: + static ExceptionOr<Ref<SerializedScriptValue>> create(JSC::JSGlobalObject&, JSC::JSValue, Vector<JSC::Strong<JSC::JSObject>>&& transfer, Vector<RefPtr<MessagePort>>&, SerializationErrorMode, SerializationContext); + WEBCORE_EXPORT SerializedScriptValue( + Vector<unsigned char>&&, std::unique_ptr<ArrayBufferContentsArray>&& = nullptr +#if ENABLE(WEB_RTC) + , + Vector<std::unique_ptr<DetachedRTCDataChannel>>&& = {} +#endif + ); + + SerializedScriptValue( + Vector<unsigned char>&&, /*const Vector<BlobURLHandle>& blobHandles,*/ std::unique_ptr<ArrayBufferContentsArray>, std::unique_ptr<ArrayBufferContentsArray> sharedBuffers /*,Vector<std::optional<ImageBitmapBacking>>&& backingStores*/ +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + , + Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& = {} +#endif +#if ENABLE(WEB_RTC) + , + Vector<std::unique_ptr<DetachedRTCDataChannel>>&& = {} +#endif +#if ENABLE(WEBASSEMBLY) + , + std::unique_ptr<WasmModuleArray> = nullptr, std::unique_ptr<WasmMemoryHandleArray> = nullptr +#endif + ); + + size_t computeMemoryCost() const; + + Vector<unsigned char> m_data; + std::unique_ptr<ArrayBufferContentsArray> m_arrayBufferContentsArray; + std::unique_ptr<ArrayBufferContentsArray> m_sharedBufferContentsArray; + // Vector<std::optional<ImageBitmapBacking>> m_backingStores; +#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) + Vector<std::unique_ptr<DetachedOffscreenCanvas>> m_detachedOffscreenCanvases; +#endif +#if ENABLE(WEB_RTC) + Vector<std::unique_ptr<DetachedRTCDataChannel>> m_detachedRTCDataChannels; +#endif +#if ENABLE(WEBASSEMBLY) + std::unique_ptr<WasmModuleArray> m_wasmModulesArray; + std::unique_ptr<WasmMemoryHandleArray> m_wasmMemoryHandlesArray; +#endif + // Vector<BlobURLHandle> m_blobHandles; + size_t m_memoryCost { 0 }; +}; + +template<class Encoder> +void SerializedScriptValue::encode(Encoder& encoder) const +{ + encoder << m_data; + + auto hasArray = m_arrayBufferContentsArray && m_arrayBufferContentsArray->size(); + encoder << hasArray; + + if (hasArray) { + encoder << static_cast<uint64_t>(m_arrayBufferContentsArray->size()); + for (const auto& arrayBufferContents : *m_arrayBufferContentsArray) { + encoder << static_cast<uint64_t>(arrayBufferContents.sizeInBytes()); + encoder.encodeFixedLengthData(static_cast<const uint8_t*>(arrayBufferContents.data()), arrayBufferContents.sizeInBytes(), 1); + } + } + +#if ENABLE(WEB_RTC) + encoder << static_cast<uint64_t>(m_detachedRTCDataChannels.size()); + for (const auto& channel : m_detachedRTCDataChannels) + encoder << *channel; +#endif +} + +template<class Decoder> +RefPtr<SerializedScriptValue> SerializedScriptValue::decode(Decoder& decoder) +{ + Vector<uint8_t> data; + if (!decoder.decode(data)) + return nullptr; + + bool hasArray; + if (!decoder.decode(hasArray)) + return nullptr; + + std::unique_ptr<ArrayBufferContentsArray> arrayBufferContentsArray; + if (hasArray) { + uint64_t arrayLength; + if (!decoder.decode(arrayLength)) + return nullptr; + ASSERT(arrayLength); + + arrayBufferContentsArray = makeUnique<ArrayBufferContentsArray>(); + while (arrayLength--) { + uint64_t bufferSize; + if (!decoder.decode(bufferSize)) + return nullptr; + CheckedSize checkedBufferSize = bufferSize; + if (checkedBufferSize.hasOverflowed()) + return nullptr; + if (!decoder.template bufferIsLargeEnoughToContain<uint8_t>(bufferSize)) + return nullptr; + + auto buffer = Gigacage::tryMalloc(Gigacage::Primitive, bufferSize); + if (!buffer) + return nullptr; + if (!decoder.decodeFixedLengthData(static_cast<uint8_t*>(buffer), bufferSize, 1)) { + Gigacage::free(Gigacage::Primitive, buffer); + return nullptr; + } + arrayBufferContentsArray->append({ buffer, checkedBufferSize, ArrayBuffer::primitiveGigacageDestructor() }); + } + } + +#if ENABLE(WEB_RTC) + uint64_t detachedRTCDataChannelsSize; + if (!decoder.decode(detachedRTCDataChannelsSize)) + return nullptr; + + Vector<std::unique_ptr<DetachedRTCDataChannel>> detachedRTCDataChannels; + while (detachedRTCDataChannelsSize--) { + auto detachedRTCDataChannel = DetachedRTCDataChannel::decode(decoder); + if (!detachedRTCDataChannel) + return nullptr; + detachedRTCDataChannels.append(WTFMove(detachedRTCDataChannel)); + } +#endif + + return adoptRef(*new SerializedScriptValue(WTFMove(data), WTFMove(arrayBufferContentsArray) +#if ENABLE(WEB_RTC) + , + WTFMove(detachedRTCDataChannels) +#endif + )); +} + +} diff --git a/src/javascript/jsc/bindings/webcore/ServiceWorker.h b/src/javascript/jsc/bindings/webcore/ServiceWorker.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/ServiceWorker.h diff --git a/src/javascript/jsc/bindings/webcore/StructuredSerializeOptions.h b/src/javascript/jsc/bindings/webcore/StructuredSerializeOptions.h new file mode 100644 index 000000000..f21da5ab0 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/StructuredSerializeOptions.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <JavaScriptCore/JSObject.h> +#include <JavaScriptCore/Strong.h> +#include <wtf/Vector.h> + +namespace WebCore { + +struct StructuredSerializeOptions { + StructuredSerializeOptions() = default; + StructuredSerializeOptions(Vector<JSC::Strong<JSC::JSObject>>&& transfer) + : transfer(WTFMove(transfer)) + { } + + Vector<JSC::Strong<JSC::JSObject>> transfer; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/StructuredSerializeOptions.idl b/src/javascript/jsc/bindings/webcore/StructuredSerializeOptions.idl new file mode 100644 index 000000000..229d18ec7 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/StructuredSerializeOptions.idl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +dictionary StructuredSerializeOptions { + sequence<object> transfer = []; +}; diff --git a/src/javascript/jsc/bindings/webcore/Supplementable.h b/src/javascript/jsc/bindings/webcore/Supplementable.h new file mode 100644 index 000000000..933b58f52 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/Supplementable.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2012 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Supplementable_h +#define Supplementable_h + +#include <wtf/Assertions.h> +#include <wtf/HashMap.h> +#include <wtf/MainThread.h> + +#if ASSERT_ENABLED +#include <wtf/Threading.h> +#endif + +namespace WebCore { + +// What you should know about Supplementable and Supplement +// ======================================================== +// Supplementable and Supplement instances are meant to be thread local. They +// should only be accessed from within the thread that created them. The +// 2 classes are not designed for safe access from another thread. Violating +// this design assumption can result in memory corruption and unpredictable +// behavior. +// +// What you should know about the Supplement keys +// ============================================== +// The Supplement is expected to use the same const char* string instance +// as its key. The Supplementable's SupplementMap will use the address of the +// string as the key and not the characters themselves. Hence, 2 strings with +// the same characters will be treated as 2 different keys. +// +// In practice, it is recommended that Supplements implements a static method +// for returning its key to use. For example: +// +// class MyClass : public Supplement<MySupplementable> { +// ... +// static const char* supplementName(); +// } +// +// const char* MyClass::supplementName() +// { +// return "MyClass"; +// } +// +// An example of the using the key: +// +// MyClass* MyClass::from(MySupplementable* host) +// { +// return reinterpret_cast<MyClass*>(Supplement<MySupplementable>::from(host, supplementName())); +// } + +template<typename T> +class Supplementable; + +template<typename T> +class Supplement { +public: + virtual ~Supplement() = default; +#if ASSERT_ENABLED || ENABLE(SECURITY_ASSERTIONS) + virtual bool isRefCountedWrapper() const { return false; } +#endif + + static void provideTo(Supplementable<T>* host, const char* key, std::unique_ptr<Supplement<T>> supplement) + { + host->provideSupplement(key, WTFMove(supplement)); + } + + static Supplement<T>* from(Supplementable<T>* host, const char* key) + { + return host ? host->requireSupplement(key) : 0; + } +}; + +template<typename T> +class Supplementable { +public: + void provideSupplement(const char* key, std::unique_ptr<Supplement<T>> supplement) + { + ASSERT(canCurrentThreadAccessThreadLocalData(m_thread)); + ASSERT(!m_supplements.get(key)); + m_supplements.set(key, WTFMove(supplement)); + } + + void removeSupplement(const char* key) + { + ASSERT(canCurrentThreadAccessThreadLocalData(m_thread)); + m_supplements.remove(key); + } + + Supplement<T>* requireSupplement(const char* key) + { + ASSERT(canCurrentThreadAccessThreadLocalData(m_thread)); + return m_supplements.get(key); + } + +#if ASSERT_ENABLED +protected: + Supplementable() = default; +#endif + +private: + typedef HashMap<const char*, std::unique_ptr<Supplement<T>>, PtrHash<const char*>> SupplementMap; + SupplementMap m_supplements; +#if ASSERT_ENABLED + Ref<Thread> m_thread { Thread::current() }; +#endif +}; + +} // namespace WebCore + +#endif // Supplementable_h + diff --git a/src/javascript/jsc/bindings/webcore/TaskSource.h b/src/javascript/jsc/bindings/webcore/TaskSource.h new file mode 100644 index 000000000..79c135031 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/TaskSource.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +enum class TaskSource : uint8_t { + DOMManipulation, + DatabaseAccess, + FileReading, + FontLoading, + Geolocation, + IdleTask, + IndexedDB, + MediaElement, + Microtask, + Networking, + PerformanceTimeline, + Permission, + PostedMessageQueue, + Speech, + UserInteraction, + WebGL, + WebXR, + WebSocket, + + // Internal to WebCore + InternalAsyncTask, // Safe to re-order or delay. +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/TransferredMessagePort.h b/src/javascript/jsc/bindings/webcore/TransferredMessagePort.h new file mode 100644 index 000000000..19aa63746 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/TransferredMessagePort.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018-2022 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "MessagePortIdentifier.h" + +namespace WebCore { + +// When a message port is transferred, it is represented by a pair of identifiers. +// The first identifier is the port being transferred and the second is its remote port. +using TransferredMessagePort = std::pair<MessagePortIdentifier, MessagePortIdentifier>; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WindowOrWorkerGlobalScope.cpp b/src/javascript/jsc/bindings/webcore/WindowOrWorkerGlobalScope.cpp new file mode 100644 index 000000000..3a5fb7ecf --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WindowOrWorkerGlobalScope.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WindowOrWorkerGlobalScope.h" + +#include "ExceptionOr.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObject.h" +#include "MessagePort.h" +#include "SerializedScriptValue.h" +#include "StructuredSerializeOptions.h" + +namespace WebCore { + +void WindowOrWorkerGlobalScope::reportError(JSDOMGlobalObject& globalObject, JSC::JSValue error) +{ + auto& vm = globalObject.vm(); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); + auto* exception = JSC::jsDynamicCast<JSC::Exception*>(vm, error); + if (!exception) + exception = JSC::Exception::create(vm, error); + + reportException(&globalObject, exception); +} + +ExceptionOr<JSC::JSValue> WindowOrWorkerGlobalScope::structuredClone(JSDOMGlobalObject& lexicalGlobalObject, JSDOMGlobalObject& relevantGlobalObject, JSC::JSValue value, StructuredSerializeOptions&& options) +{ + Vector<RefPtr<MessagePort>> ports; + auto messageData = SerializedScriptValue::create(lexicalGlobalObject, value, WTFMove(options.transfer), ports, SerializationContext::WindowPostMessage); + if (messageData.hasException()) + return messageData.releaseException(); + + auto disentangledPorts = MessagePort::disentanglePorts(WTFMove(ports)); + if (disentangledPorts.hasException()) + return disentangledPorts.releaseException(); + + Vector<RefPtr<MessagePort>> entangledPorts; + if (auto* scriptExecutionContext = relevantGlobalObject.scriptExecutionContext()) + entangledPorts = MessagePort::entanglePorts(*scriptExecutionContext, disentangledPorts.releaseReturnValue()); + + return messageData.returnValue()->deserialize(lexicalGlobalObject, &relevantGlobalObject, WTFMove(entangledPorts)); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WindowOrWorkerGlobalScope.h b/src/javascript/jsc/bindings/webcore/WindowOrWorkerGlobalScope.h new file mode 100644 index 000000000..108ddc2c7 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WindowOrWorkerGlobalScope.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace Zig { +class GlobalObject; +} + +namespace JSC { +class JSValue; +} + +namespace WebCore { + +template<typename> class ExceptionOr; + +struct StructuredSerializeOptions; + +class WindowOrWorkerGlobalScope { +public: + static void reportError(Zig::GlobalObject&, JSC::JSValue); + static ExceptionOr<JSC::JSValue> structuredClone(Zig::GlobalObject& lexicalGlobalObject, Zig::GlobalObject& relevantGlobalObject, JSC::JSValue, StructuredSerializeOptions&&); +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WindowOrWorkerGlobalScope.idl b/src/javascript/jsc/bindings/webcore/WindowOrWorkerGlobalScope.idl new file mode 100644 index 000000000..b5fb09567 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WindowOrWorkerGlobalScope.idl @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2006-2009, 2013, 1016 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2013 Samsung Electronics. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// FIXME: This should include SVGImageElement. +typedef (HTMLImageElement +#if defined(ENABLE_VIDEO) && ENABLE_VIDEO + or HTMLVideoElement +#endif + or HTMLCanvasElement + or ImageBitmap +#if defined(ENABLE_OFFSCREEN_CANVAS) && ENABLE_OFFSCREEN_CANVAS + or OffscreenCanvas +#endif +#if defined(ENABLE_CSS_TYPED_OM) && ENABLE_CSS_TYPED_OM + or CSSStyleImageValue +#endif +) CanvasImageSource; + +typedef (CanvasImageSource or Blob or ImageData) ImageBitmapSource; + +// https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope-mixin +interface mixin WindowOrWorkerGlobalScope { + [Replaceable] readonly attribute USVString origin; + readonly attribute boolean isSecureContext; + + [EnabledBySetting=CrossOriginOpenerPolicyEnabled] readonly attribute boolean crossOriginIsolated; + + [CallWith=RelevantGlobalObject] undefined reportError(any error); + + // Base64 utility methods. + DOMString atob(DOMString string); + DOMString btoa(DOMString string); + + // Timers. + // FIXME: This should take a TimerHandler (a.k.a. (DOMString or Function)) rather than a ScheduledAction. + long setTimeout(ScheduledAction handler, optional long timeout = 0, any... arguments); + undefined clearTimeout(optional long handle = 0); + // FIXME: This should take a TimerHandler (a.k.a. (DOMString or Function)) rather than a ScheduledAction. + long setInterval(ScheduledAction handler, optional long timeout = 0, any... arguments); + undefined clearInterval(optional long handle = 0); + + // microtask queuing. + [Custom] undefined queueMicrotask(VoidCallback callback); + + // ImageBitmap. + [EnabledAtRuntime=ImageBitmapEnabled] Promise<ImageBitmap> createImageBitmap(ImageBitmapSource image, optional ImageBitmapOptions options); + [EnabledAtRuntime=ImageBitmapEnabled] Promise<ImageBitmap> createImageBitmap(ImageBitmapSource image, long sx, long sy, long sw, long sh, optional ImageBitmapOptions options); + + // structured cloning + [CallWith=CurrentGlobalObject&RelevantGlobalObject] any structuredClone(any value, optional StructuredSerializeOptions options); +}; diff --git a/src/javascript/jsc/bindings/webcore/WindowProxy.h b/src/javascript/jsc/bindings/webcore/WindowProxy.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WindowProxy.h diff --git a/src/javascript/jsc/bindings/webcore/WindowSessionStorage.idl b/src/javascript/jsc/bindings/webcore/WindowSessionStorage.idl new file mode 100644 index 000000000..a6b981539 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WindowSessionStorage.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +// https://html.spec.whatwg.org/multipage/webstorage.html#windowsessionstorage +interface mixin WindowSessionStorage { + readonly attribute Storage sessionStorage; +}; diff --git a/src/javascript/jsc/bindings/webcore/WorkerDebugger.cpp b/src/javascript/jsc/bindings/webcore/WorkerDebugger.cpp new file mode 100644 index 000000000..b52088bc3 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerDebugger.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2011 Google Inc. All rights reserved. + * Copyright (c) 2013-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WorkerDebugger.h" + +#include "JSDOMExceptionHandling.h" +#include "Timer.h" +#include "WorkerOrWorkletGlobalScope.h" +#include "WorkerOrWorkletScriptController.h" +#include "WorkerRunLoop.h" +#include "WorkerThread.h" +#include <JavaScriptCore/VM.h> + +namespace WebCore { + +using namespace Inspector; + +WorkerDebugger::WorkerDebugger(WorkerOrWorkletGlobalScope& context) + : Debugger(context.script()->vm()) + , m_globalScope(context) +{ +} + +void WorkerDebugger::attachDebugger() +{ + JSC::Debugger::attachDebugger(); + + m_globalScope.script()->attachDebugger(this); +} + +void WorkerDebugger::detachDebugger(bool isBeingDestroyed) +{ + JSC::Debugger::detachDebugger(isBeingDestroyed); + + if (m_globalScope.script()) + m_globalScope.script()->detachDebugger(this); + if (!isBeingDestroyed) + recompileAllJSFunctions(); +} + +void WorkerDebugger::recompileAllJSFunctions() +{ + JSC::JSLockHolder lock(vm()); + JSC::Debugger::recompileAllJSFunctions(); +} + +void WorkerDebugger::runEventLoopWhilePaused() +{ + JSC::Debugger::runEventLoopWhilePaused(); + + TimerBase::fireTimersInNestedEventLoop(); + + // FIXME: Add support for pausing workers running on the main thread. + if (!is<WorkerDedicatedRunLoop>(m_globalScope.workerOrWorkletThread()->runLoop())) + return; + + MessageQueueWaitResult result; + do { + result = downcast<WorkerDedicatedRunLoop>(m_globalScope.workerOrWorkletThread()->runLoop()).runInDebuggerMode(m_globalScope); + } while (result != MessageQueueTerminated && !doneProcessingDebuggerEvents()); +} + +void WorkerDebugger::reportException(JSC::JSGlobalObject* exec, JSC::Exception* exception) const +{ + JSC::Debugger::reportException(exec, exception); + + WebCore::reportException(exec, exception); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerDebugger.h b/src/javascript/jsc/bindings/webcore/WorkerDebugger.h new file mode 100644 index 000000000..88a55e9e3 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerDebugger.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Google Inc. All rights reserved. + * Copyright (c) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <JavaScriptCore/Debugger.h> + +namespace WebCore { + +class WorkerOrWorkletGlobalScope; + +class WorkerDebugger final : public JSC::Debugger { + WTF_MAKE_NONCOPYABLE(WorkerDebugger); + WTF_MAKE_FAST_ALLOCATED; +public: + WorkerDebugger(WorkerOrWorkletGlobalScope&); + ~WorkerDebugger() override = default; + + +private: + // JSC::Debugger + void attachDebugger() final; + void detachDebugger(bool isBeingDestroyed) final; + void recompileAllJSFunctions() final; + void runEventLoopWhilePaused() final; + void reportException(JSC::JSGlobalObject*, JSC::Exception*) const final; + + WorkerOrWorkletGlobalScope& m_globalScope; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerEventLoop.cpp b/src/javascript/jsc/bindings/webcore/WorkerEventLoop.cpp new file mode 100644 index 000000000..654f10d87 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerEventLoop.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WorkerEventLoop.h" + +#include "Microtasks.h" +#include "WorkerOrWorkletGlobalScope.h" + +namespace WebCore { + +Ref<WorkerEventLoop> WorkerEventLoop::create(WorkerOrWorkletGlobalScope& context) +{ + return adoptRef(*new WorkerEventLoop(context)); +} + +WorkerEventLoop::WorkerEventLoop(WorkerOrWorkletGlobalScope& context) + : ContextDestructionObserver(&context) +{ +} + +WorkerEventLoop::~WorkerEventLoop() +{ +} + +void WorkerEventLoop::scheduleToRun() +{ + ASSERT(scriptExecutionContext()); + scriptExecutionContext()->postTask([eventLoop = Ref { *this }] (ScriptExecutionContext&) { + eventLoop->run(); + }); +} + +bool WorkerEventLoop::isContextThread() const +{ + return scriptExecutionContext()->isContextThread(); +} + +MicrotaskQueue& WorkerEventLoop::microtaskQueue() +{ + ASSERT(scriptExecutionContext()); + if (!m_microtaskQueue) + m_microtaskQueue = makeUnique<MicrotaskQueue>(scriptExecutionContext()->vm()); + return *m_microtaskQueue; +} + +void WorkerEventLoop::clearMicrotaskQueue() +{ + m_microtaskQueue = nullptr; +} + +} // namespace WebCore + diff --git a/src/javascript/jsc/bindings/webcore/WorkerEventLoop.h b/src/javascript/jsc/bindings/webcore/WorkerEventLoop.h new file mode 100644 index 000000000..df70b9ee2 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerEventLoop.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ActiveDOMObject.h" +#include "EventLoop.h" + +namespace WebCore { + +class WorkerOrWorkletGlobalScope; + +class WorkerEventLoop final : public EventLoop, private ContextDestructionObserver { +public: + static Ref<WorkerEventLoop> create(WorkerOrWorkletGlobalScope&); + + virtual ~WorkerEventLoop(); + + // FIXME: This should be removed once MicrotaskQueue is integrated with EventLoopTaskGroup. + void clearMicrotaskQueue(); + +private: + explicit WorkerEventLoop(WorkerOrWorkletGlobalScope&); + + void scheduleToRun() final; + bool isContextThread() const; + MicrotaskQueue& microtaskQueue() final; + + std::unique_ptr<MicrotaskQueue> m_microtaskQueue; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerGlobalScope.cpp b/src/javascript/jsc/bindings/webcore/WorkerGlobalScope.cpp new file mode 100644 index 000000000..f2f298e78 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerGlobalScope.cpp @@ -0,0 +1,655 @@ +/* + * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. + * Copyright (C) 2009, 2011 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "WorkerGlobalScope.h" + +#include "BlobURL.h" +#include "CSSFontSelector.h" +#include "CSSValueList.h" +#include "CSSValuePool.h" +#include "CommonVM.h" +#include "ContentSecurityPolicy.h" +#include "Crypto.h" +#include "FontCustomPlatformData.h" +#include "FontFaceSet.h" +#include "IDBConnectionProxy.h" +#include "ImageBitmapOptions.h" +#include "InspectorInstrumentation.h" +#include "JSDOMExceptionHandling.h" +#include "Performance.h" +#include "RTCDataChannelRemoteHandlerConnection.h" +#include "RuntimeEnabledFeatures.h" +#include "ScheduledAction.h" +#include "ScriptSourceCode.h" +#include "SecurityOrigin.h" +#include "SecurityOriginPolicy.h" +#include "ServiceWorkerGlobalScope.h" +#include "SocketProvider.h" +#include "WorkerCacheStorageConnection.h" +#include "WorkerFileSystemStorageConnection.h" +#include "WorkerFontLoadRequest.h" +#include "WorkerLoaderProxy.h" +#include "WorkerLocation.h" +#include "WorkerMessagePortChannelProvider.h" +#include "WorkerMessagingProxy.h" +#include "WorkerNavigator.h" +#include "WorkerOrWorkletGlobalScope.h" +#include "WorkerReportingProxy.h" +#include "WorkerSWClientConnection.h" +#include "WorkerScriptLoader.h" +#include "WorkerStorageConnection.h" +#include "WorkerThread.h" +#include <JavaScriptCore/ScriptArguments.h> +#include <JavaScriptCore/ScriptCallStack.h> +#include <wtf/IsoMallocInlines.h> +#include <wtf/Lock.h> +#include <wtf/WorkQueue.h> +#include <wtf/threads/BinarySemaphore.h> + +namespace WebCore { +using namespace Inspector; + +static Lock allWorkerGlobalScopeIdentifiersLock; +static HashSet<ScriptExecutionContextIdentifier>& allWorkerGlobalScopeIdentifiers() WTF_REQUIRES_LOCK(allWorkerGlobalScopeIdentifiersLock) +{ + static NeverDestroyed<HashSet<ScriptExecutionContextIdentifier>> identifiers; + ASSERT(allWorkerGlobalScopeIdentifiersLock.isLocked()); + return identifiers; +} + +static WorkQueue& sharedFileSystemStorageQueue() +{ + static NeverDestroyed<Ref<WorkQueue>> queue(WorkQueue::create("Shared File System Storage Queue", WorkQueue::QOS::Default)); + return queue.get(); +} + +WTF_MAKE_ISO_ALLOCATED_IMPL(WorkerGlobalScope); + +WorkerGlobalScope::WorkerGlobalScope(WorkerThreadType type, const WorkerParameters& params, Ref<SecurityOrigin>&& origin, WorkerThread& thread, Ref<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider) + : WorkerOrWorkletGlobalScope(type, isMainThread() ? Ref { commonVM() } : JSC::VM::create(), &thread) + , m_url(params.scriptURL) + , m_inspectorIdentifier(params.inspectorIdentifier) + , m_userAgent(params.userAgent) + , m_isOnline(params.isOnline) + , m_shouldBypassMainWorldContentSecurityPolicy(params.shouldBypassMainWorldContentSecurityPolicy) + , m_topOrigin(WTFMove(topOrigin)) + , m_connectionProxy(connectionProxy) + , m_socketProvider(socketProvider) + , m_performance(Performance::create(this, params.timeOrigin)) + , m_referrerPolicy(params.referrerPolicy) + , m_settingsValues(params.settingsValues) + , m_workerType(params.workerType) + , m_credentials(params.credentials) +{ + { + Locker locker { allWorkerGlobalScopeIdentifiersLock }; + allWorkerGlobalScopeIdentifiers().add(identifier()); + } + + if (m_topOrigin->hasUniversalAccess()) + origin->grantUniversalAccess(); + if (m_topOrigin->needsStorageAccessFromFileURLsQuirk()) + origin->grantStorageAccessFromFileURLsQuirk(); + + setSecurityOriginPolicy(SecurityOriginPolicy::create(WTFMove(origin))); + setContentSecurityPolicy(makeUnique<ContentSecurityPolicy>(URL { m_url }, *this)); + setCrossOriginEmbedderPolicy(params.crossOriginEmbedderPolicy); +} + +WorkerGlobalScope::~WorkerGlobalScope() +{ + ASSERT(thread().thread() == &Thread::current()); + // We need to remove from the contexts map very early in the destructor so that calling postTask() on this WorkerGlobalScope from another thread is safe. + removeFromContextsMap(); + + { + Locker locker { allWorkerGlobalScopeIdentifiersLock }; + allWorkerGlobalScopeIdentifiers().remove(identifier()); + } + + m_performance = nullptr; + m_crypto = nullptr; + + // Notify proxy that we are going away. This can free the WorkerThread object, so do not access it after this. + thread().workerReportingProxy().workerGlobalScopeDestroyed(); +} + +String WorkerGlobalScope::origin() const +{ + auto* securityOrigin = this->securityOrigin(); + return securityOrigin ? securityOrigin->toString() : emptyString(); +} + +void WorkerGlobalScope::prepareForDestruction() +{ + WorkerOrWorkletGlobalScope::prepareForDestruction(); + + stopIndexedDatabase(); + + if (m_cacheStorageConnection) + m_cacheStorageConnection->clearPendingRequests(); + + if (m_storageConnection) + m_storageConnection->scopeClosed(); + + if (m_fileSystemStorageConnection) + m_fileSystemStorageConnection->scopeClosed(); +} + +void WorkerGlobalScope::removeAllEventListeners() +{ + WorkerOrWorkletGlobalScope::removeAllEventListeners(); + m_performance->removeAllEventListeners(); + m_performance->removeAllObservers(); +} + +bool WorkerGlobalScope::isSecureContext() const +{ + if (!RuntimeEnabledFeatures::sharedFeatures().secureContextChecksEnabled()) + return true; + + return securityOrigin() && securityOrigin()->isPotentiallyTrustworthy(); +} + +void WorkerGlobalScope::applyContentSecurityPolicyResponseHeaders(const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders) +{ + contentSecurityPolicy()->didReceiveHeaders(contentSecurityPolicyResponseHeaders, String { }); +} + +URL WorkerGlobalScope::completeURL(const String& url, ForceUTF8) const +{ + // Always return a null URL when passed a null string. + // FIXME: Should we change the URL constructor to have this behavior? + if (url.isNull()) + return URL(); + // Always use UTF-8 in Workers. + return URL(m_url, url); +} + +String WorkerGlobalScope::userAgent(const URL&) const +{ + return m_userAgent; +} + +SocketProvider* WorkerGlobalScope::socketProvider() +{ + return m_socketProvider.get(); +} + +RefPtr<RTCDataChannelRemoteHandlerConnection> WorkerGlobalScope::createRTCDataChannelRemoteHandlerConnection() +{ + RefPtr<RTCDataChannelRemoteHandlerConnection> connection; + callOnMainThreadAndWait([workerThread = Ref { thread() }, &connection]() mutable { + connection = workerThread->workerLoaderProxy().createRTCDataChannelRemoteHandlerConnection(); + }); + ASSERT(connection); + + return connection; +} + +IDBClient::IDBConnectionProxy* WorkerGlobalScope::idbConnectionProxy() +{ + return m_connectionProxy.get(); +} + +void WorkerGlobalScope::stopIndexedDatabase() +{ + if (m_connectionProxy) + m_connectionProxy->forgetActivityForCurrentThread(); +} + +void WorkerGlobalScope::suspend() +{ + if (m_connectionProxy) + m_connectionProxy->setContextSuspended(*this, true); +} + +void WorkerGlobalScope::resume() +{ + if (m_connectionProxy) + m_connectionProxy->setContextSuspended(*this, false); +} + +WorkerStorageConnection& WorkerGlobalScope::storageConnection() +{ + if (!m_storageConnection) + m_storageConnection = WorkerStorageConnection::create(*this); + + return *m_storageConnection; +} + +void WorkerGlobalScope::postFileSystemStorageTask(Function<void()>&& task) +{ + sharedFileSystemStorageQueue().dispatch(WTFMove(task)); +} + +WorkerFileSystemStorageConnection& WorkerGlobalScope::getFileSystemStorageConnection(Ref<FileSystemStorageConnection>&& mainThreadConnection) +{ + if (!m_fileSystemStorageConnection) + m_fileSystemStorageConnection = WorkerFileSystemStorageConnection::create(*this, WTFMove(mainThreadConnection)); + else if (m_fileSystemStorageConnection->mainThreadConnection() != mainThreadConnection.ptr()) { + m_fileSystemStorageConnection->connectionClosed(); + m_fileSystemStorageConnection = WorkerFileSystemStorageConnection::create(*this, WTFMove(mainThreadConnection)); + } + + return *m_fileSystemStorageConnection; +} + +WorkerFileSystemStorageConnection* WorkerGlobalScope::fileSystemStorageConnection() +{ + return m_fileSystemStorageConnection.get(); +} + +WorkerLocation& WorkerGlobalScope::location() const +{ + if (!m_location) + m_location = WorkerLocation::create(URL { m_url }, origin()); + return *m_location; +} + +void WorkerGlobalScope::close() +{ + if (isClosing()) + return; + + // Let current script run to completion but prevent future script evaluations. + // After m_closing is set, all the tasks in the queue continue to be fetched but only + // tasks with isCleanupTask()==true will be executed. + markAsClosing(); + postTask({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context) { + ASSERT_WITH_SECURITY_IMPLICATION(is<WorkerGlobalScope>(context)); + WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context); + // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop(). + workerGlobalScope.thread().workerReportingProxy().workerGlobalScopeClosed(); + } }); +} + +WorkerNavigator& WorkerGlobalScope::navigator() +{ + if (!m_navigator) + m_navigator = WorkerNavigator::create(*this, m_userAgent, m_isOnline); + return *m_navigator; +} + +void WorkerGlobalScope::setIsOnline(bool isOnline) +{ + m_isOnline = isOnline; + if (m_navigator) + m_navigator->setIsOnline(isOnline); +} + +ExceptionOr<int> WorkerGlobalScope::setTimeout(std::unique_ptr<ScheduledAction> action, int timeout, FixedVector<JSC::Strong<JSC::Unknown>>&& arguments) +{ + // FIXME: Should this check really happen here? Or should it happen when code is about to eval? + if (action->type() == ScheduledAction::Type::Code) { + if (!contentSecurityPolicy()->allowEval(globalObject(), LogToConsole::Yes, action->code())) + return 0; + } + + action->addArguments(WTFMove(arguments)); + + return DOMTimer::install(*this, WTFMove(action), Seconds::fromMilliseconds(timeout), true); +} + +void WorkerGlobalScope::clearTimeout(int timeoutId) +{ + DOMTimer::removeById(*this, timeoutId); +} + +ExceptionOr<int> WorkerGlobalScope::setInterval(std::unique_ptr<ScheduledAction> action, int timeout, FixedVector<JSC::Strong<JSC::Unknown>>&& arguments) +{ + // FIXME: Should this check really happen here? Or should it happen when code is about to eval? + if (action->type() == ScheduledAction::Type::Code) { + if (!contentSecurityPolicy()->allowEval(globalObject(), LogToConsole::Yes, action->code())) + return 0; + } + + action->addArguments(WTFMove(arguments)); + + return DOMTimer::install(*this, WTFMove(action), Seconds::fromMilliseconds(timeout), false); +} + +void WorkerGlobalScope::clearInterval(int timeoutId) +{ + DOMTimer::removeById(*this, timeoutId); +} + +ExceptionOr<void> WorkerGlobalScope::importScripts(const FixedVector<String>& urls) +{ + ASSERT(contentSecurityPolicy()); + + // https://html.spec.whatwg.org/multipage/workers.html#importing-scripts-and-libraries + // 1. If worker global scope's type is "module", throw a TypeError exception. + if (m_workerType == WorkerType::Module) + return Exception { TypeError, "importScripts cannot be used if worker type is \"module\""_s }; + + Vector<URL> completedURLs; + Vector<BlobURLHandle> protectedBlobURLs; + completedURLs.reserveInitialCapacity(urls.size()); + for (auto& entry : urls) { + URL url = completeURL(entry); + if (!url.isValid()) + return Exception { SyntaxError }; + if (url.protocolIsBlob()) + protectedBlobURLs.append(BlobURLHandle { url }); + completedURLs.uncheckedAppend(WTFMove(url)); + } + + FetchOptions::Cache cachePolicy = FetchOptions::Cache::Default; + +#if ENABLE(SERVICE_WORKER) + bool isServiceWorkerGlobalScope = is<ServiceWorkerGlobalScope>(*this); + if (isServiceWorkerGlobalScope) { + // FIXME: We need to add support for the 'imported scripts updated' flag as per: + // https://w3c.github.io/ServiceWorker/#importscripts + auto& serviceWorkerGlobalScope = downcast<ServiceWorkerGlobalScope>(*this); + auto& registration = serviceWorkerGlobalScope.registration(); + if (registration.updateViaCache() == ServiceWorkerUpdateViaCache::None || registration.needsUpdate()) + cachePolicy = FetchOptions::Cache::NoCache; + } +#endif + + for (auto& url : completedURLs) { + // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. + bool shouldBypassMainWorldContentSecurityPolicy = this->shouldBypassMainWorldContentSecurityPolicy(); + if (!shouldBypassMainWorldContentSecurityPolicy && !contentSecurityPolicy()->allowScriptFromSource(url)) + return Exception { NetworkError }; + + auto scriptLoader = WorkerScriptLoader::create(); + auto cspEnforcement = shouldBypassMainWorldContentSecurityPolicy ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceScriptSrcDirective; + if (auto exception = scriptLoader->loadSynchronously(this, url, WorkerScriptLoader::Source::ClassicWorkerImport, FetchOptions::Mode::NoCors, cachePolicy, cspEnforcement, resourceRequestIdentifier())) + return WTFMove(*exception); + + InspectorInstrumentation::scriptImported(*this, scriptLoader->identifier(), scriptLoader->script().toString()); + + WeakPtr<ScriptBufferSourceProvider> sourceProvider; + { + NakedPtr<JSC::Exception> exception; + ScriptSourceCode sourceCode(scriptLoader->script(), URL(scriptLoader->responseURL())); + sourceProvider = static_cast<ScriptBufferSourceProvider&>(sourceCode.provider()); + script()->evaluate(sourceCode, exception); + if (exception) { + script()->setException(exception); + return { }; + } + } + if (sourceProvider) + addImportedScriptSourceProvider(url, *sourceProvider); + } + + return { }; +} + +EventTarget* WorkerGlobalScope::errorEventTarget() +{ + return this; +} + +void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, RefPtr<ScriptCallStack>&&) +{ + thread().workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, columnNumber, sourceURL); +} + +void WorkerGlobalScope::addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&& message) +{ + if (!isContextThread()) { + postTask(AddConsoleMessageTask(message->source(), message->level(), message->message())); + return; + } + + InspectorInstrumentation::addMessageToConsole(*this, WTFMove(message)); +} + +void WorkerGlobalScope::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier) +{ + addMessage(source, level, message, { }, 0, 0, nullptr, nullptr, requestIdentifier); +} + +void WorkerGlobalScope::addMessage(MessageSource source, MessageLevel level, const String& messageText, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, RefPtr<ScriptCallStack>&& callStack, JSC::JSGlobalObject* state, unsigned long requestIdentifier) +{ + if (!isContextThread()) { + postTask(AddConsoleMessageTask(source, level, messageText)); + return; + } + + std::unique_ptr<Inspector::ConsoleMessage> message; + if (callStack) + message = makeUnique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, callStack.releaseNonNull(), requestIdentifier); + else + message = makeUnique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, sourceURL, lineNumber, columnNumber, state, requestIdentifier); + InspectorInstrumentation::addMessageToConsole(*this, WTFMove(message)); +} + +#if ENABLE(WEB_CRYPTO) + +bool WorkerGlobalScope::wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey) +{ + Ref protectedThis { *this }; + bool success = false; + BinarySemaphore semaphore; + thread().workerLoaderProxy().postTaskToLoader([&semaphore, &success, &key, &wrappedKey](auto& context) { + success = context.wrapCryptoKey(key, wrappedKey); + semaphore.signal(); + }); + semaphore.wait(); + return success; +} + +bool WorkerGlobalScope::unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key) +{ + Ref protectedThis { *this }; + bool success = false; + BinarySemaphore semaphore; + thread().workerLoaderProxy().postTaskToLoader([&semaphore, &success, &key, &wrappedKey](auto& context) { + success = context.unwrapCryptoKey(wrappedKey, key); + semaphore.signal(); + }); + semaphore.wait(); + return success; +} + +#endif // ENABLE(WEB_CRYPTO) + +Crypto& WorkerGlobalScope::crypto() +{ + if (!m_crypto) + m_crypto = Crypto::create(this); + return *m_crypto; +} + +Performance& WorkerGlobalScope::performance() const +{ + return *m_performance; +} + +WorkerCacheStorageConnection& WorkerGlobalScope::cacheStorageConnection() +{ + if (!m_cacheStorageConnection) + m_cacheStorageConnection = WorkerCacheStorageConnection::create(*this); + return *m_cacheStorageConnection; +} + +MessagePortChannelProvider& WorkerGlobalScope::messagePortChannelProvider() +{ + if (!m_messagePortChannelProvider) + m_messagePortChannelProvider = makeUnique<WorkerMessagePortChannelProvider>(*this); + return *m_messagePortChannelProvider; +} + +#if ENABLE(SERVICE_WORKER) +WorkerSWClientConnection& WorkerGlobalScope::swClientConnection() +{ + if (!m_swClientConnection) + m_swClientConnection = WorkerSWClientConnection::create(*this); + return *m_swClientConnection; +} +#endif + +void WorkerGlobalScope::createImageBitmap(ImageBitmap::Source&& source, ImageBitmapOptions&& options, ImageBitmap::Promise&& promise) +{ + ImageBitmap::createPromise(*this, WTFMove(source), WTFMove(options), WTFMove(promise)); +} + +void WorkerGlobalScope::createImageBitmap(ImageBitmap::Source&& source, int sx, int sy, int sw, int sh, ImageBitmapOptions&& options, ImageBitmap::Promise&& promise) +{ + ImageBitmap::createPromise(*this, WTFMove(source), WTFMove(options), sx, sy, sw, sh, WTFMove(promise)); +} + +CSSValuePool& WorkerGlobalScope::cssValuePool() +{ + if (!m_cssValuePool) + m_cssValuePool = makeUnique<CSSValuePool>(); + return *m_cssValuePool; +} + +CSSFontSelector* WorkerGlobalScope::cssFontSelector() +{ + if (!m_cssFontSelector) + m_cssFontSelector = CSSFontSelector::create(*this); + return m_cssFontSelector.get(); +} + +Ref<FontFaceSet> WorkerGlobalScope::fonts() +{ + ASSERT(cssFontSelector()); + return cssFontSelector()->fontFaceSet(); +} + +std::unique_ptr<FontLoadRequest> WorkerGlobalScope::fontLoadRequest(String& url, bool, bool, LoadedFromOpaqueSource loadedFromOpaqueSource) +{ + return makeUnique<WorkerFontLoadRequest>(completeURL(url), loadedFromOpaqueSource); +} + +void WorkerGlobalScope::beginLoadingFontSoon(FontLoadRequest& request) +{ + ASSERT(is<WorkerFontLoadRequest>(request)); + downcast<WorkerFontLoadRequest>(request).load(*this); +} + +ReferrerPolicy WorkerGlobalScope::referrerPolicy() const +{ + return m_referrerPolicy; +} + +WorkerThread& WorkerGlobalScope::thread() const +{ + return *static_cast<WorkerThread*>(workerOrWorkletThread()); +} + +void WorkerGlobalScope::releaseMemory(Synchronous synchronous) +{ + ASSERT(isContextThread()); + deleteJSCodeAndGC(synchronous); + clearDecodedScriptData(); +} + +void WorkerGlobalScope::deleteJSCodeAndGC(Synchronous synchronous) +{ + ASSERT(isContextThread()); + + JSC::JSLockHolder lock(vm()); + vm().deleteAllCode(JSC::DeleteAllCodeIfNotCollecting); + + if (synchronous == Synchronous::Yes) { + if (!vm().heap.currentThreadIsDoingGCWork()) { + vm().heap.collectNow(JSC::Sync, JSC::CollectionScope::Full); + WTF::releaseFastMallocFreeMemory(); + return; + } + } +#if PLATFORM(IOS_FAMILY) + if (!vm().heap.currentThreadIsDoingGCWork()) { + vm().heap.collectNowFullIfNotDoneRecently(JSC::Async); + return; + } +#endif +#if USE(CF) || USE(GLIB) + vm().heap.reportAbandonedObjectGraph(); +#else + vm().heap.collectNow(JSC::Async, JSC::CollectionScope::Full); +#endif +} + +void WorkerGlobalScope::releaseMemoryInWorkers(Synchronous synchronous) +{ + Locker locker { allWorkerGlobalScopeIdentifiersLock }; + for (auto& globalScopeIdentifier : allWorkerGlobalScopeIdentifiers()) { + postTaskTo(globalScopeIdentifier, [synchronous](auto& context) { + downcast<WorkerGlobalScope>(context).releaseMemory(synchronous); + }); + } +} + +void WorkerGlobalScope::setMainScriptSourceProvider(ScriptBufferSourceProvider& provider) +{ + ASSERT(!m_mainScriptSourceProvider); + m_mainScriptSourceProvider = provider; +} + +void WorkerGlobalScope::addImportedScriptSourceProvider(const URL& url, ScriptBufferSourceProvider& provider) +{ + m_importedScriptsSourceProviders.ensure(url, [] { + return WeakHashSet<ScriptBufferSourceProvider> { }; + }).iterator->value.add(provider); +} + +void WorkerGlobalScope::clearDecodedScriptData() +{ + ASSERT(isContextThread()); + + if (m_mainScriptSourceProvider) + m_mainScriptSourceProvider->clearDecodedData(); + + for (auto& sourceProviders : m_importedScriptsSourceProviders.values()) { + for (auto& sourceProvider : sourceProviders) + sourceProvider.clearDecodedData(); + } +} + +bool WorkerGlobalScope::crossOriginIsolated() const +{ + return ScriptExecutionContext::crossOriginMode() == CrossOriginMode::Isolated; +} + +void WorkerGlobalScope::updateSourceProviderBuffers(const ScriptBuffer& mainScript, const HashMap<URL, ScriptBuffer>& importedScripts) +{ + ASSERT(isContextThread()); + + if (mainScript && m_mainScriptSourceProvider) + m_mainScriptSourceProvider->tryReplaceScriptBuffer(mainScript); + + for (auto& pair : importedScripts) { + auto it = m_importedScriptsSourceProviders.find(pair.key); + if (it == m_importedScriptsSourceProviders.end()) + continue; + for (auto& sourceProvider : it->value) + sourceProvider.tryReplaceScriptBuffer(pair.value); + } +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerGlobalScope.h b/src/javascript/jsc/bindings/webcore/WorkerGlobalScope.h new file mode 100644 index 000000000..7c08e8f62 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerGlobalScope.h @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2008-2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +// #include "Base64Utilities.h" +// #include "CacheStorageConnection.h" +// #include "ImageBitmap.h" +#include "ScriptExecutionContext.h" +#include "Supplementable.h" +#include "WindowOrWorkerGlobalScope.h" +#include "WorkerOrWorkletGlobalScope.h" +// #include "WorkerOrWorkletScriptController.h" +#include "WorkerType.h" +#include <JavaScriptCore/ConsoleMessage.h> +#include <memory> +#include <wtf/FixedVector.h> +#include <wtf/HashMap.h> +#include <wtf/MemoryPressureHandler.h> +#include <wtf/URL.h> +#include <wtf/URLHash.h> +#include <wtf/WeakHashSet.h> + +namespace WebCore { + +class CSSFontSelector; +class CSSValuePool; +class ContentSecurityPolicyResponseHeaders; +class Crypto; +class FileSystemStorageConnection; +class FontFaceSet; +class MessagePortChannelProvider; +class Performance; +class ScheduledAction; +class ScriptBuffer; +class ScriptBufferSourceProvider; +class WorkerCacheStorageConnection; +class WorkerFileSystemStorageConnection; +class WorkerLocation; +class WorkerMessagePortChannelProvider; +class WorkerNavigator; +class WorkerSWClientConnection; +class WorkerStorageConnection; +class WorkerStorageConnection; +class WorkerThread; +struct WorkerParameters; + +namespace IDBClient { +class IDBConnectionProxy; +} + +class WorkerGlobalScope : public Supplementable<WorkerGlobalScope>, /*public Base64Utilities,*/ public WindowOrWorkerGlobalScope, public WorkerOrWorkletGlobalScope { + WTF_MAKE_ISO_ALLOCATED(WorkerGlobalScope); + +public: + virtual ~WorkerGlobalScope(); + + enum class Type : uint8_t { DedicatedWorker, + ServiceWorker, + SharedWorker }; + virtual Type type() const = 0; + + const URL& url() const final { return m_url; } + String origin() const; + const String& inspectorIdentifier() const { return m_inspectorIdentifier; } + + // IDBClient::IDBConnectionProxy* idbConnectionProxy() final; + void suspend() final; + void resume() final; + + using WeakValueType = EventTarget::WeakValueType; + using EventTarget::weakPtrFactory; + // WorkerStorageConnection& storageConnection(); + // static void postFileSystemStorageTask(Function<void()>&&); + // WorkerFileSystemStorageConnection& getFileSystemStorageConnection(Ref<FileSystemStorageConnection>&&); + // WEBCORE_EXPORT WorkerFileSystemStorageConnection* fileSystemStorageConnection(); + // WorkerCacheStorageConnection& cacheStorageConnection(); + MessagePortChannelProvider& messagePortChannelProvider(); + // #if ENABLE(SERVICE_WORKER) + // WorkerSWClientConnection& swClientConnection(); + // #endif + + WorkerThread& thread() const; + + using ScriptExecutionContext::hasPendingActivity; + + WorkerGlobalScope& self() { return *this; } + WorkerLocation& location() const; + void close(); + + virtual ExceptionOr<void> importScripts(const FixedVector<String>& urls); + WorkerNavigator& navigator(); + + // void setIsOnline(bool); + + // ExceptionOr<int> setTimeout(std::unique_ptr<ScheduledAction>, int timeout, FixedVector<JSC::Strong<JSC::Unknown>>&& arguments); + // void clearTimeout(int timeoutId); + // ExceptionOr<int> setInterval(std::unique_ptr<ScheduledAction>, int timeout, FixedVector<JSC::Strong<JSC::Unknown>>&& arguments); + // void clearInterval(int timeoutId); + + bool isSecureContext() const final; + bool crossOriginIsolated() const; + + // WorkerNavigator* optionalNavigator() const { return m_navigator.get(); } + // WorkerLocation* optionalLocation() const { return m_location.get(); } + + // void addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&&) final; + + // SecurityOrigin& topOrigin() const final { return m_topOrigin.get(); } + + // Crypto& crypto(); + // Performance& performance() const; + + void prepareForDestruction() override; + + void removeAllEventListeners() final; + + // void createImageBitmap(ImageBitmap::Source&&, ImageBitmapOptions&&, ImageBitmap::Promise&&); + // void createImageBitmap(ImageBitmap::Source&&, int sx, int sy, int sw, int sh, ImageBitmapOptions&&, ImageBitmap::Promise&&); + + // CSSValuePool& cssValuePool() final; + // CSSFontSelector* cssFontSelector() final; + // Ref<FontFaceSet> fonts(); + // std::unique_ptr<FontLoadRequest> fontLoadRequest(String& url, bool isSVG, bool isInitiatingElementInUserAgentShadowTree, LoadedFromOpaqueSource) final; + // void beginLoadingFontSoon(FontLoadRequest&) final; + + // ReferrerPolicy referrerPolicy() const final; + + const Settings::Values& settingsValues() const final { return m_settingsValues; } + + // FetchOptions::Credentials credentials() const { return m_credentials; } + + void releaseMemory(Synchronous); + static void releaseMemoryInWorkers(Synchronous); + + void setMainScriptSourceProvider(ScriptBufferSourceProvider&); + // void addImportedScriptSourceProvider(const URL&, ScriptBufferSourceProvider&); + +protected: + WorkerGlobalScope(WorkerThreadType, const WorkerParameters&, /*Ref<SecurityOrigin>&&,*/ WorkerThread& /*,Ref<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*/); + + // void applyContentSecurityPolicyResponseHeaders(const ContentSecurityPolicyResponseHeaders&); + // void updateSourceProviderBuffers(const ScriptBuffer& mainScript, const HashMap<URL, ScriptBuffer>& importedScripts); + +private: + void logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, RefPtr<Inspector::ScriptCallStack>&&) final; + + // The following addMessage and addConsoleMessage functions are deprecated. + // Callers should try to create the ConsoleMessage themselves. + void addMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, RefPtr<Inspector::ScriptCallStack>&&, JSC::JSGlobalObject*, unsigned long requestIdentifier) final; + void addConsoleMessage(MessageSource, MessageLevel, const String& message, unsigned long requestIdentifier) final; + + bool isWorkerGlobalScope() const final { return true; } + + void deleteJSCodeAndGC(Synchronous); + void clearDecodedScriptData(); + + URL completeURL(const String&, ForceUTF8 = ForceUTF8::No) const final; + String userAgent(const URL&) const final; + + EventTarget* errorEventTarget() final; + String resourceRequestIdentifier() const final { return m_inspectorIdentifier; } + SocketProvider* socketProvider() final; + RefPtr<RTCDataChannelRemoteHandlerConnection> createRTCDataChannelRemoteHandlerConnection() final; + + // bool shouldBypassMainWorldContentSecurityPolicy() const final { return m_shouldBypassMainWorldContentSecurityPolicy; } + + // #if ENABLE(WEB_CRYPTO) + // bool wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey) final; + // bool unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key) final; + // #endif + + // void stopIndexedDatabase(); + + URL m_url; + String m_inspectorIdentifier; + // String m_userAgent; + + mutable RefPtr<WorkerLocation> m_location; + mutable RefPtr<WorkerNavigator> m_navigator; + + // bool m_isOnline; + // bool m_shouldBypassMainWorldContentSecurityPolicy; + + // Ref<SecurityOrigin> m_topOrigin; + + // RefPtr<IDBClient::IDBConnectionProxy> m_connectionProxy; + + // RefPtr<SocketProvider> m_socketProvider; + + // RefPtr<Performance> m_performance; + // mutable RefPtr<Crypto> m_crypto; + + WeakPtr<ScriptBufferSourceProvider> m_mainScriptSourceProvider; + // HashMap<URL, WeakHashSet<ScriptBufferSourceProvider>> m_importedScriptsSourceProviders; + + // RefPtr<WorkerCacheStorageConnection> m_cacheStorageConnection; + std::unique_ptr<WorkerMessagePortChannelProvider> m_messagePortChannelProvider; + // #if ENABLE(SERVICE_WORKER) + // RefPtr<WorkerSWClientConnection> m_swClientConnection; + // #endif + // std::unique_ptr<CSSValuePool> m_cssValuePool; + // RefPtr<CSSFontSelector> m_cssFontSelector; + // ReferrerPolicy m_referrerPolicy; + // Settings::Values m_settingsValues; + // WorkerType m_workerType; + // FetchOptions::Credentials m_credentials; + // RefPtr<WorkerStorageConnection> m_storageConnection; + // RefPtr<WorkerFileSystemStorageConnection> m_fileSystemStorageConnection; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::WorkerGlobalScope) +static bool isType(const WebCore::ScriptExecutionContext& context) { return context.isWorkerGlobalScope(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/src/javascript/jsc/bindings/webcore/WorkerInspectorController.cpp b/src/javascript/jsc/bindings/webcore/WorkerInspectorController.cpp new file mode 100644 index 000000000..6361048e6 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerInspectorController.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WorkerInspectorController.h" + +#include "CommandLineAPIHost.h" +#include "InstrumentingAgents.h" +#include "JSExecState.h" +#include "WebHeapAgent.h" +#include "WebInjectedScriptHost.h" +#include "WebInjectedScriptManager.h" +#include "WorkerAuditAgent.h" +#include "WorkerConsoleAgent.h" +#include "WorkerDOMDebuggerAgent.h" +#include "WorkerDebuggerAgent.h" +#include "WorkerNetworkAgent.h" +#include "WorkerOrWorkletGlobalScope.h" +#include "WorkerRuntimeAgent.h" +#include "WorkerThread.h" +#include "WorkerToPageFrontendChannel.h" +#include <JavaScriptCore/InspectorAgentBase.h> +#include <JavaScriptCore/InspectorBackendDispatcher.h> +#include <JavaScriptCore/InspectorFrontendChannel.h> +#include <JavaScriptCore/InspectorFrontendDispatchers.h> +#include <JavaScriptCore/InspectorFrontendRouter.h> + +#if ENABLE(SERVICE_WORKER) +#include "InspectorClient.h" +#include "InspectorController.h" +#include "Page.h" +#include "ServiceWorkerAgent.h" +#include "ServiceWorkerGlobalScope.h" +#endif + +namespace WebCore { + +using namespace JSC; +using namespace Inspector; + +WorkerInspectorController::WorkerInspectorController(WorkerOrWorkletGlobalScope& globalScope) + : m_instrumentingAgents(InstrumentingAgents::create(*this)) + , m_injectedScriptManager(makeUnique<WebInjectedScriptManager>(*this, WebInjectedScriptHost::create())) + , m_frontendRouter(FrontendRouter::create()) + , m_backendDispatcher(BackendDispatcher::create(m_frontendRouter.copyRef())) + , m_executionStopwatch(Stopwatch::create()) + , m_debugger(globalScope) + , m_globalScope(globalScope) +{ + ASSERT(globalScope.isContextThread()); + + auto workerContext = workerAgentContext(); + + auto consoleAgent = makeUnique<WorkerConsoleAgent>(workerContext); + m_instrumentingAgents->setWebConsoleAgent(consoleAgent.get()); + m_agents.append(WTFMove(consoleAgent)); +} + +WorkerInspectorController::~WorkerInspectorController() +{ + ASSERT(!m_frontendRouter->hasFrontends()); + ASSERT(!m_forwardingChannel); + + m_instrumentingAgents->reset(); +} + +void WorkerInspectorController::workerTerminating() +{ + m_injectedScriptManager->disconnect(); + + disconnectFrontend(Inspector::DisconnectReason::InspectedTargetDestroyed); + + m_agents.discardValues(); +} + +void WorkerInspectorController::connectFrontend() +{ + ASSERT(!m_frontendRouter->hasFrontends()); + ASSERT(!m_forwardingChannel); + + createLazyAgents(); + + callOnMainThread([] { + InspectorInstrumentation::frontendCreated(); + }); + + m_executionStopwatch->reset(); + m_executionStopwatch->start(); + + m_forwardingChannel = makeUnique<WorkerToPageFrontendChannel>(m_globalScope); + m_frontendRouter->connectFrontend(*m_forwardingChannel.get()); + m_agents.didCreateFrontendAndBackend(&m_frontendRouter.get(), &m_backendDispatcher.get()); + +#if ENABLE(SERVICE_WORKER) + updateServiceWorkerPageFrontendCount(); +#endif +} + +void WorkerInspectorController::disconnectFrontend(Inspector::DisconnectReason reason) +{ + if (!m_frontendRouter->hasFrontends()) + return; + + ASSERT(m_forwardingChannel); + + callOnMainThread([] { + InspectorInstrumentation::frontendDeleted(); + }); + + m_agents.willDestroyFrontendAndBackend(reason); + m_frontendRouter->disconnectFrontend(*m_forwardingChannel.get()); + m_forwardingChannel = nullptr; + +#if ENABLE(SERVICE_WORKER) + updateServiceWorkerPageFrontendCount(); +#endif +} + +#if ENABLE(SERVICE_WORKER) +void WorkerInspectorController::updateServiceWorkerPageFrontendCount() +{ + if (!is<ServiceWorkerGlobalScope>(m_globalScope)) + return; + + auto serviceWorkerPage = downcast<ServiceWorkerGlobalScope>(m_globalScope).serviceWorkerPage(); + if (!serviceWorkerPage) + return; + + ASSERT(isMainThread()); + + // When a service worker is loaded in a Page, we need to report its inspector frontend count + // up to the page's inspectorController so the client knows about it. + auto inspectorClient = serviceWorkerPage->inspectorController().inspectorClient(); + if (!inspectorClient) + return; + + inspectorClient->frontendCountChanged(m_frontendRouter->frontendCount()); +} +#endif + +void WorkerInspectorController::dispatchMessageFromFrontend(const String& message) +{ + m_backendDispatcher->dispatch(message); +} + +WorkerAgentContext WorkerInspectorController::workerAgentContext() +{ + AgentContext baseContext = { + *this, + *m_injectedScriptManager, + m_frontendRouter.get(), + m_backendDispatcher.get(), + }; + + WebAgentContext webContext = { + baseContext, + m_instrumentingAgents.get(), + }; + + WorkerAgentContext workerContext = { + webContext, + m_globalScope, + }; + + return workerContext; +} + +void WorkerInspectorController::createLazyAgents() +{ + if (m_didCreateLazyAgents) + return; + + m_didCreateLazyAgents = true; + + m_injectedScriptManager->connect(); + + auto workerContext = workerAgentContext(); + + m_agents.append(makeUnique<WorkerRuntimeAgent>(workerContext)); + +#if ENABLE(SERVICE_WORKER) + if (is<ServiceWorkerGlobalScope>(m_globalScope)) { + m_agents.append(makeUnique<ServiceWorkerAgent>(workerContext)); + m_agents.append(makeUnique<WorkerNetworkAgent>(workerContext)); + } +#endif + + m_agents.append(makeUnique<WebHeapAgent>(workerContext)); + + auto debuggerAgent = makeUnique<WorkerDebuggerAgent>(workerContext); + auto debuggerAgentPtr = debuggerAgent.get(); + m_agents.append(WTFMove(debuggerAgent)); + + m_agents.append(makeUnique<WorkerDOMDebuggerAgent>(workerContext, debuggerAgentPtr)); + m_agents.append(makeUnique<WorkerAuditAgent>(workerContext)); + + if (auto& commandLineAPIHost = m_injectedScriptManager->commandLineAPIHost()) + commandLineAPIHost->init(m_instrumentingAgents.copyRef()); +} + +InspectorFunctionCallHandler WorkerInspectorController::functionCallHandler() const +{ + return WebCore::functionCallHandlerFromAnyThread; +} + +InspectorEvaluateHandler WorkerInspectorController::evaluateHandler() const +{ + return WebCore::evaluateHandlerFromAnyThread; +} + +VM& WorkerInspectorController::vm() +{ + return m_globalScope.vm(); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerInspectorController.h b/src/javascript/jsc/bindings/webcore/WorkerInspectorController.h new file mode 100644 index 000000000..c3946beaf --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerInspectorController.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "WorkerDebugger.h" +#include <JavaScriptCore/InspectorAgentRegistry.h> +#include <JavaScriptCore/InspectorEnvironment.h> +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> +#include <wtf/Stopwatch.h> + +namespace Inspector { +class FrontendChannel; +class FrontendRouter; +}; + +namespace WebCore { + +class InstrumentingAgents; +class WebInjectedScriptManager; +class WorkerOrWorkletGlobalScope; +struct WorkerAgentContext; + +class WorkerInspectorController final : public Inspector::InspectorEnvironment { + WTF_MAKE_NONCOPYABLE(WorkerInspectorController); + WTF_MAKE_FAST_ALLOCATED; +public: + explicit WorkerInspectorController(WorkerOrWorkletGlobalScope&); + ~WorkerInspectorController() override; + + void workerTerminating(); + + void connectFrontend(); + void disconnectFrontend(Inspector::DisconnectReason); + + void dispatchMessageFromFrontend(const String&); + + // InspectorEnvironment + bool developerExtrasEnabled() const override { return true; } + bool canAccessInspectedScriptState(JSC::JSGlobalObject*) const override { return true; } + Inspector::InspectorFunctionCallHandler functionCallHandler() const override; + Inspector::InspectorEvaluateHandler evaluateHandler() const override; + void frontendInitialized() override { } + Stopwatch& executionStopwatch() const final { return m_executionStopwatch; } + WorkerDebugger& debugger() override { return m_debugger; } + JSC::VM& vm() override; + +private: + friend class InspectorInstrumentation; + + WorkerAgentContext workerAgentContext(); + void createLazyAgents(); + +#if ENABLE(SERVICE_WORKER) + void updateServiceWorkerPageFrontendCount(); +#endif + + Ref<InstrumentingAgents> m_instrumentingAgents; + std::unique_ptr<WebInjectedScriptManager> m_injectedScriptManager; + Ref<Inspector::FrontendRouter> m_frontendRouter; + Ref<Inspector::BackendDispatcher> m_backendDispatcher; + Ref<Stopwatch> m_executionStopwatch; + WorkerDebugger m_debugger; + Inspector::AgentRegistry m_agents; + WorkerOrWorkletGlobalScope& m_globalScope; + std::unique_ptr<Inspector::FrontendChannel> m_forwardingChannel; + bool m_didCreateLazyAgents { false }; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerLocation.cpp b/src/javascript/jsc/bindings/webcore/WorkerLocation.cpp new file mode 100644 index 000000000..300a0237d --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerLocation.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WorkerLocation.h" + +// #include "SecurityOrigin.h" +#include <wtf/text/WTFString.h> + +namespace WebCore { + +String WorkerLocation::href() const +{ + return m_url.string(); +} + +String WorkerLocation::protocol() const +{ + return makeString(m_url.protocol(), ":"); +} + +String WorkerLocation::host() const +{ + return m_url.hostAndPort(); +} + +String WorkerLocation::hostname() const +{ + return m_url.host().toString(); +} + +String WorkerLocation::port() const +{ + auto port = m_url.port(); + return port ? String::number(*port) : emptyString(); +} + +String WorkerLocation::pathname() const +{ + auto path = m_url.path(); + return path.isEmpty() ? "/"_s : path.toString(); +} + +String WorkerLocation::search() const +{ + return m_url.query().isEmpty() ? emptyString() : m_url.queryWithLeadingQuestionMark().toString(); +} + +String WorkerLocation::hash() const +{ + return m_url.fragmentIdentifier().isEmpty() ? emptyString() : m_url.fragmentIdentifierWithLeadingNumberSign().toString(); +} + +String WorkerLocation::origin() const +{ + return m_origin; +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerLocation.h b/src/javascript/jsc/bindings/webcore/WorkerLocation.h new file mode 100644 index 000000000..dfad302ea --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerLocation.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <wtf/RefCounted.h> +#include <wtf/URL.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + + class WorkerLocation : public RefCounted<WorkerLocation> { + public: + static Ref<WorkerLocation> create(URL&& url, String&& origin) { return adoptRef(*new WorkerLocation(WTFMove(url), WTFMove(origin))); } + + const URL& url() const { return m_url; } + String href() const; + + // URI decomposition attributes + String protocol() const; + String host() const; + String hostname() const; + String port() const; + String pathname() const; + String search() const; + String hash() const; + String origin() const; + + private: + WorkerLocation(URL&& url, String&& origin) + : m_url(WTFMove(url)) + , m_origin(WTFMove(origin)) + { + } + + URL m_url; + String m_origin; + }; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerLocation.idl b/src/javascript/jsc/bindings/webcore/WorkerLocation.idl new file mode 100644 index 000000000..b253528b6 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerLocation.idl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Exposed=Worker, + GenerateIsReachable=Impl, +] interface WorkerLocation { + stringifier readonly attribute USVString href; + readonly attribute USVString protocol; + readonly attribute USVString host; + readonly attribute USVString hostname; + readonly attribute USVString port; + readonly attribute USVString pathname; + readonly attribute USVString search; + readonly attribute USVString hash; + readonly attribute USVString origin; +}; + diff --git a/src/javascript/jsc/bindings/webcore/WorkerMessagePortChannelProvider.cpp b/src/javascript/jsc/bindings/webcore/WorkerMessagePortChannelProvider.cpp new file mode 100644 index 000000000..24e2269f2 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerMessagePortChannelProvider.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WorkerMessagePortChannelProvider.h" + +#include "MessagePort.h" +#include "WorkerOrWorkletGlobalScope.h" +#include "WorkerThread.h" +#include <wtf/MainThread.h> +#include <wtf/RunLoop.h> + +namespace WebCore { + +WorkerMessagePortChannelProvider::WorkerMessagePortChannelProvider(WorkerOrWorkletGlobalScope& scope) + : m_scope(scope) +{ +} + +WorkerMessagePortChannelProvider::~WorkerMessagePortChannelProvider() +{ + while (!m_takeAllMessagesCallbacks.isEmpty()) { + auto first = m_takeAllMessagesCallbacks.begin(); + first->value({ }, [] { }); + m_takeAllMessagesCallbacks.remove(first); + } + while (!m_activityCallbacks.isEmpty()) { + auto first = m_activityCallbacks.begin(); + first->value(HasActivity::No); + m_activityCallbacks.remove(first); + } +} + +void WorkerMessagePortChannelProvider::createNewMessagePortChannel(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) +{ + callOnMainThread([local, remote] { + MessagePortChannelProvider::singleton().createNewMessagePortChannel(local, remote); + }); +} + +void WorkerMessagePortChannelProvider::entangleLocalPortInThisProcessToRemote(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) +{ + callOnMainThread([local, remote] { + MessagePortChannelProvider::singleton().entangleLocalPortInThisProcessToRemote(local, remote); + }); +} + +void WorkerMessagePortChannelProvider::messagePortDisentangled(const MessagePortIdentifier& local) +{ + callOnMainThread([local] { + MessagePortChannelProvider::singleton().messagePortDisentangled(local); + }); +} + +void WorkerMessagePortChannelProvider::messagePortClosed(const MessagePortIdentifier&) +{ + ASSERT_NOT_REACHED(); +} + +void WorkerMessagePortChannelProvider::postMessageToRemote(MessageWithMessagePorts&& message, const MessagePortIdentifier& remoteTarget) +{ + callOnMainThread([message = WTFMove(message), remoteTarget]() mutable { + MessagePortChannelProvider::singleton().postMessageToRemote(WTFMove(message), remoteTarget); + }); +} + +class MainThreadCompletionHandler { +public: + explicit MainThreadCompletionHandler(CompletionHandler<void()>&& completionHandler) + : m_completionHandler(WTFMove(completionHandler)) + { + } + MainThreadCompletionHandler(MainThreadCompletionHandler&&) = default; + MainThreadCompletionHandler& operator=(MainThreadCompletionHandler&&) = default; + + ~MainThreadCompletionHandler() + { + if (m_completionHandler) + complete(); + } + + void complete() + { + callOnMainThread(WTFMove(m_completionHandler)); + } + +private: + CompletionHandler<void()> m_completionHandler; +}; + +void WorkerMessagePortChannelProvider::takeAllMessagesForPort(const MessagePortIdentifier& identifier, CompletionHandler<void(Vector<MessageWithMessagePorts>&&, CompletionHandler<void()>&&)>&& callback) +{ + uint64_t callbackIdentifier = ++m_lastCallbackIdentifier; + m_takeAllMessagesCallbacks.add(callbackIdentifier, WTFMove(callback)); + + callOnMainThread([this, workerThread = RefPtr { m_scope.workerOrWorkletThread() }, callbackIdentifier, identifier]() mutable { + MessagePortChannelProvider::singleton().takeAllMessagesForPort(identifier, [this, workerThread = WTFMove(workerThread), callbackIdentifier](Vector<MessageWithMessagePorts>&& messages, Function<void()>&& completionHandler) { + workerThread->runLoop().postTaskForMode([this, callbackIdentifier, messages = WTFMove(messages), completionHandler = MainThreadCompletionHandler(WTFMove(completionHandler))](auto&) mutable { + m_takeAllMessagesCallbacks.take(callbackIdentifier)(WTFMove(messages), [completionHandler = WTFMove(completionHandler)]() mutable { + completionHandler.complete(); + }); + }, WorkerRunLoop::defaultMode()); + }); + }); +} + +void WorkerMessagePortChannelProvider::checkRemotePortForActivity(const MessagePortIdentifier& remoteTarget, CompletionHandler<void(HasActivity)>&& callback) +{ + uint64_t callbackIdentifier = ++m_lastCallbackIdentifier; + m_activityCallbacks.add(callbackIdentifier, WTFMove(callback)); + + callOnMainThread([this, workerThread = RefPtr { m_scope.workerOrWorkletThread() }, callbackIdentifier, remoteTarget]() mutable { + MessagePortChannelProvider::singleton().checkRemotePortForActivity(remoteTarget, [this, workerThread = WTFMove(workerThread), callbackIdentifier](auto hasActivity) { + workerThread->runLoop().postTaskForMode([this, callbackIdentifier, hasActivity](auto&) mutable { + m_activityCallbacks.take(callbackIdentifier)(hasActivity); + }, WorkerRunLoop::defaultMode()); + }); + }); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerMessagePortChannelProvider.h b/src/javascript/jsc/bindings/webcore/WorkerMessagePortChannelProvider.h new file mode 100644 index 000000000..fe94a845b --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerMessagePortChannelProvider.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "MessagePortChannelProvider.h" +#include <wtf/CompletionHandler.h> +#include <wtf/HashMap.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class WorkerOrWorkletGlobalScope; + +class WorkerMessagePortChannelProvider final : public MessagePortChannelProvider { + WTF_MAKE_FAST_ALLOCATED; +public: + explicit WorkerMessagePortChannelProvider(WorkerOrWorkletGlobalScope&); + ~WorkerMessagePortChannelProvider(); + +private: + void createNewMessagePortChannel(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) final; + void entangleLocalPortInThisProcessToRemote(const MessagePortIdentifier& local, const MessagePortIdentifier& remote) final; + void messagePortDisentangled(const MessagePortIdentifier& local) final; + void messagePortClosed(const MessagePortIdentifier& local) final; + void postMessageToRemote(MessageWithMessagePorts&&, const MessagePortIdentifier& remoteTarget) final; + void takeAllMessagesForPort(const MessagePortIdentifier&, CompletionHandler<void(Vector<MessageWithMessagePorts>&&, CompletionHandler<void()>&&)>&&) final; + void checkRemotePortForActivity(const MessagePortIdentifier& remoteTarget, CompletionHandler<void(HasActivity)>&& callback) final; + + WorkerOrWorkletGlobalScope& m_scope; + + uint64_t m_lastCallbackIdentifier { 0 }; + HashMap<uint64_t, CompletionHandler<void(Vector<MessageWithMessagePorts>&&, Function<void()>&&)>> m_takeAllMessagesCallbacks; + HashMap<uint64_t, CompletionHandler<void(HasActivity)>> m_activityCallbacks; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerMessagingProxy.cpp b/src/javascript/jsc/bindings/webcore/WorkerMessagingProxy.cpp new file mode 100644 index 000000000..cb6c622bf --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerMessagingProxy.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "WorkerMessagingProxy.h" + +#include "CacheStorageProvider.h" +#include "ContentSecurityPolicy.h" +#include "DOMWindow.h" +#include "DedicatedWorkerGlobalScope.h" +#include "DedicatedWorkerThread.h" +#include "Document.h" +#include "ErrorEvent.h" +#include "EventNames.h" +#include "FetchRequestCredentials.h" +#include "LibWebRTCProvider.h" +#include "MessageEvent.h" +#include "Page.h" +#include "ScriptExecutionContext.h" +#include "Settings.h" +#include "Worker.h" +#include "WorkerInspectorProxy.h" +#include <JavaScriptCore/ConsoleTypes.h> +#include <JavaScriptCore/ScriptCallStack.h> +#include <wtf/MainThread.h> +#include <wtf/RunLoop.h> + +namespace WebCore { + +WorkerGlobalScopeProxy& WorkerGlobalScopeProxy::create(Worker& worker) +{ + return *new WorkerMessagingProxy(worker); +} + +WorkerMessagingProxy::WorkerMessagingProxy(Worker& workerObject) + : m_scriptExecutionContext(workerObject.scriptExecutionContext()) + , m_inspectorProxy(WorkerInspectorProxy::create(workerObject.identifier())) + , m_workerObject(&workerObject) +{ + ASSERT((is<Document>(*m_scriptExecutionContext) && isMainThread()) + || (is<WorkerGlobalScope>(*m_scriptExecutionContext) && downcast<WorkerGlobalScope>(*m_scriptExecutionContext).thread().thread() == &Thread::current())); + + // Nobody outside this class ref counts this object. The original ref + // is balanced by the deref in workerGlobalScopeDestroyedInternal. +} + +WorkerMessagingProxy::~WorkerMessagingProxy() +{ + ASSERT(!m_workerObject); + ASSERT((is<Document>(*m_scriptExecutionContext) && isMainThread()) + || (is<WorkerGlobalScope>(*m_scriptExecutionContext) && downcast<WorkerGlobalScope>(*m_scriptExecutionContext).thread().thread() == &Thread::current())); +} + +void WorkerMessagingProxy::startWorkerGlobalScope(const URL& scriptURL, const String& name, const String& userAgent, bool isOnline, const ScriptBuffer& sourceCode, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders, bool shouldBypassMainWorldContentSecurityPolicy, const CrossOriginEmbedderPolicy& crossOriginEmbedderPolicy, MonotonicTime timeOrigin, ReferrerPolicy referrerPolicy, WorkerType workerType, FetchRequestCredentials credentials, JSC::RuntimeFlags runtimeFlags) +{ + // FIXME: This need to be revisited when we support nested worker one day + ASSERT(m_scriptExecutionContext); + Document& document = downcast<Document>(*m_scriptExecutionContext); + WorkerThreadStartMode startMode = m_inspectorProxy->workerStartMode(*m_scriptExecutionContext.get()); + String identifier = m_inspectorProxy->identifier(); + + IDBClient::IDBConnectionProxy* proxy = document.idbConnectionProxy(); + + SocketProvider* socketProvider = document.socketProvider(); + + WorkerParameters params = { scriptURL, name, identifier, userAgent, isOnline, contentSecurityPolicyResponseHeaders, shouldBypassMainWorldContentSecurityPolicy, crossOriginEmbedderPolicy, timeOrigin, referrerPolicy, workerType, credentials, document.settingsValues() }; + auto thread = DedicatedWorkerThread::create(params, sourceCode, *this, *this, *this, startMode, document.topOrigin(), proxy, socketProvider, runtimeFlags); + + workerThreadCreated(thread.get()); + thread->start(); + + m_inspectorProxy->workerStarted(m_scriptExecutionContext.get(), thread.ptr(), scriptURL, name); +} + +void WorkerMessagingProxy::postMessageToWorkerObject(MessageWithMessagePorts&& message) +{ + m_scriptExecutionContext->postTask([this, message = WTFMove(message)] (ScriptExecutionContext& context) mutable { + Worker* workerObject = this->workerObject(); + if (!workerObject || askedToTerminate()) + return; + + auto ports = MessagePort::entanglePorts(context, WTFMove(message.transferredPorts)); + ActiveDOMObject::queueTaskToDispatchEvent(*workerObject, TaskSource::PostedMessageQueue, MessageEvent::create(message.message.releaseNonNull(), { }, { }, std::nullopt, WTFMove(ports))); + }); +} + +void WorkerMessagingProxy::postTaskToWorkerObject(Function<void(Worker&)>&& function) +{ + m_scriptExecutionContext->postTask([this, function = WTFMove(function)](auto&) mutable { + auto* workerObject = this->workerObject(); + if (!workerObject || askedToTerminate()) + return; + function(*workerObject); + }); +} + +void WorkerMessagingProxy::postMessageToWorkerGlobalScope(MessageWithMessagePorts&& message) +{ + postTaskToWorkerGlobalScope([message = WTFMove(message)](auto& scriptContext) mutable { + ASSERT_WITH_SECURITY_IMPLICATION(scriptContext.isWorkerGlobalScope()); + auto& context = static_cast<DedicatedWorkerGlobalScope&>(scriptContext); + auto ports = MessagePort::entanglePorts(scriptContext, WTFMove(message.transferredPorts)); + context.dispatchEvent(MessageEvent::create(message.message.releaseNonNull(), { }, { }, std::nullopt, WTFMove(ports))); + context.thread().workerObjectProxy().confirmMessageFromWorkerObject(context.hasPendingActivity()); + }); +} + +void WorkerMessagingProxy::postTaskToWorkerGlobalScope(Function<void(ScriptExecutionContext&)>&& task) +{ + if (m_askedToTerminate) + return; + + if (!m_workerThread) { + m_queuedEarlyTasks.append(makeUnique<ScriptExecutionContext::Task>(WTFMove(task))); + return; + } + ++m_unconfirmedMessageCount; + m_workerThread->runLoop().postTask(WTFMove(task)); +} + +void WorkerMessagingProxy::suspendForBackForwardCache() +{ + if (m_workerThread) + m_workerThread->suspend(); + else + m_askedToSuspend = true; +} + +void WorkerMessagingProxy::resumeForBackForwardCache() +{ + if (m_workerThread) + m_workerThread->resume(); + else + m_askedToSuspend = false; +} + +void WorkerMessagingProxy::postTaskToLoader(ScriptExecutionContext::Task&& task) +{ + // FIXME: In case of nested workers, this should go directly to the root Document context. + ASSERT(m_scriptExecutionContext->isDocument()); + m_scriptExecutionContext->postTask(WTFMove(task)); +} + +RefPtr<CacheStorageConnection> WorkerMessagingProxy::createCacheStorageConnection() +{ + ASSERT(isMainThread()); + auto& document = downcast<Document>(*m_scriptExecutionContext); + return document.page()->cacheStorageProvider().createCacheStorageConnection(); +} + +StorageConnection* WorkerMessagingProxy::storageConnection() +{ + ASSERT(isMainThread()); + auto& document = downcast<Document>(*m_scriptExecutionContext); + return document.storageConnection(); +} + +RefPtr<RTCDataChannelRemoteHandlerConnection> WorkerMessagingProxy::createRTCDataChannelRemoteHandlerConnection() +{ + ASSERT(isMainThread()); + auto& document = downcast<Document>(*m_scriptExecutionContext); + if (!document.page()) + return nullptr; + return document.page()->libWebRTCProvider().createRTCDataChannelRemoteHandlerConnection(); +} + +bool WorkerMessagingProxy::postTaskForModeToWorkerOrWorkletGlobalScope(ScriptExecutionContext::Task&& task, const String& mode) +{ + if (m_askedToTerminate) + return false; + + ASSERT(m_workerThread); + m_workerThread->runLoop().postTaskForMode(WTFMove(task), mode); + return true; +} + +void WorkerMessagingProxy::postExceptionToWorkerObject(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL) +{ + m_scriptExecutionContext->postTask([this, errorMessage = errorMessage.isolatedCopy(), sourceURL = sourceURL.isolatedCopy(), lineNumber, columnNumber] (ScriptExecutionContext&) { + Worker* workerObject = this->workerObject(); + if (!workerObject) + return; + + // We don't bother checking the askedToTerminate() flag here, because exceptions should *always* be reported even if the thread is terminated. + // This is intentionally different than the behavior in MessageWorkerTask, because terminated workers no longer deliver messages (section 4.6 of the WebWorker spec), but they do report exceptions. + ActiveDOMObject::queueTaskToDispatchEvent(*workerObject, TaskSource::DOMManipulation, ErrorEvent::create(errorMessage, sourceURL, lineNumber, columnNumber, { })); + }); +} + +void WorkerMessagingProxy::postMessageToDebugger(const String& message) +{ + RunLoop::main().dispatch([this, protectedThis = Ref { *this }, message = message.isolatedCopy()]() mutable { + if (!m_mayBeDestroyed) + m_inspectorProxy->sendMessageFromWorkerToFrontend(WTFMove(message)); + }); +} + +void WorkerMessagingProxy::setResourceCachingDisabledByWebInspector(bool disabled) +{ + postTaskToLoader([disabled] (ScriptExecutionContext& context) { + ASSERT(isMainThread()); + if (auto* page = downcast<Document>(context).page()) + page->setResourceCachingDisabledByWebInspector(disabled); + }); +} + +void WorkerMessagingProxy::workerThreadCreated(DedicatedWorkerThread& workerThread) +{ + m_workerThread = &workerThread; + + if (m_askedToTerminate) { + // Worker.terminate() could be called from JS before the thread was created. + m_workerThread->stop(nullptr); + } else { + if (m_askedToSuspend) { + m_askedToSuspend = false; + m_workerThread->suspend(); + } + + ASSERT(!m_unconfirmedMessageCount); + m_unconfirmedMessageCount = m_queuedEarlyTasks.size(); + m_workerThreadHadPendingActivity = true; // Worker initialization means a pending activity. + + auto queuedEarlyTasks = WTFMove(m_queuedEarlyTasks); + for (auto& task : queuedEarlyTasks) + m_workerThread->runLoop().postTask(WTFMove(*task)); + } +} + +void WorkerMessagingProxy::workerObjectDestroyed() +{ + m_workerObject = nullptr; + m_scriptExecutionContext->postTask([this] (ScriptExecutionContext&) { + m_mayBeDestroyed = true; + if (m_workerThread) + terminateWorkerGlobalScope(); + else + workerGlobalScopeDestroyedInternal(); + }); +} + +void WorkerMessagingProxy::notifyNetworkStateChange(bool isOnline) +{ + if (m_askedToTerminate) + return; + + if (!m_workerThread) + return; + + m_workerThread->runLoop().postTask([isOnline] (ScriptExecutionContext& context) { + auto& globalScope = downcast<WorkerGlobalScope>(context); + globalScope.setIsOnline(isOnline); + globalScope.dispatchEvent(Event::create(isOnline ? eventNames().onlineEvent : eventNames().offlineEvent, Event::CanBubble::No, Event::IsCancelable::No)); + }); +} + +void WorkerMessagingProxy::workerGlobalScopeDestroyed() +{ + m_scriptExecutionContext->postTask([this] (ScriptExecutionContext&) { + workerGlobalScopeDestroyedInternal(); + }); +} + +void WorkerMessagingProxy::workerGlobalScopeClosed() +{ + m_scriptExecutionContext->postTask([this] (ScriptExecutionContext&) { + terminateWorkerGlobalScope(); + }); +} + +void WorkerMessagingProxy::workerGlobalScopeDestroyedInternal() +{ + // This is always the last task to be performed, so the proxy is not needed for communication + // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too. + m_askedToTerminate = true; + m_workerThread = nullptr; + + m_inspectorProxy->workerTerminated(); + + // This balances the original ref in construction. + if (m_mayBeDestroyed) + deref(); +} + +void WorkerMessagingProxy::terminateWorkerGlobalScope() +{ + if (m_askedToTerminate) + return; + m_askedToTerminate = true; + + m_inspectorProxy->workerTerminated(); + + if (m_workerThread) + m_workerThread->stop(nullptr); +} + +void WorkerMessagingProxy::confirmMessageFromWorkerObject(bool hasPendingActivity) +{ + m_scriptExecutionContext->postTask([this, hasPendingActivity] (ScriptExecutionContext&) { + reportPendingActivityInternal(true, hasPendingActivity); + }); +} + +void WorkerMessagingProxy::reportPendingActivity(bool hasPendingActivity) +{ + m_scriptExecutionContext->postTask([this, hasPendingActivity] (ScriptExecutionContext&) { + reportPendingActivityInternal(false, hasPendingActivity); + }); +} + +void WorkerMessagingProxy::reportPendingActivityInternal(bool confirmingMessage, bool hasPendingActivity) +{ + if (confirmingMessage && !m_askedToTerminate) { + ASSERT(m_unconfirmedMessageCount); + --m_unconfirmedMessageCount; + } + + m_workerThreadHadPendingActivity = hasPendingActivity; +} + +bool WorkerMessagingProxy::hasPendingActivity() const +{ + return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate; +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerMessagingProxy.h b/src/javascript/jsc/bindings/webcore/WorkerMessagingProxy.h new file mode 100644 index 000000000..226e861c5 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerMessagingProxy.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "WorkerGlobalScopeProxy.h" +#include "WorkerDebuggerProxy.h" +#include "WorkerLoaderProxy.h" +#include "WorkerObjectProxy.h" +#include <wtf/MonotonicTime.h> +#include <wtf/ThreadSafeRefCounted.h> + +namespace WebCore { + +class DedicatedWorkerThread; +class WorkerInspectorProxy; + +class WorkerMessagingProxy final : public ThreadSafeRefCounted<WorkerMessagingProxy>, public WorkerGlobalScopeProxy, public WorkerObjectProxy, public WorkerLoaderProxy, public WorkerDebuggerProxy { + WTF_MAKE_FAST_ALLOCATED; +public: + explicit WorkerMessagingProxy(Worker&); + virtual ~WorkerMessagingProxy(); + + // Implementation of WorkerLoaderProxy. + // This method is used in the main thread to post task back to the worker thread. + bool postTaskForModeToWorkerOrWorkletGlobalScope(ScriptExecutionContext::Task&&, const String& mode) final; + +private: + // Implementations of WorkerGlobalScopeProxy. + // (Only use these functions in the worker object thread.) + void startWorkerGlobalScope(const URL& scriptURL, const String& name, const String& userAgent, bool isOnline, const ScriptBuffer& sourceCode, const ContentSecurityPolicyResponseHeaders&, bool shouldBypassMainWorldContentSecurityPolicy, const CrossOriginEmbedderPolicy&, MonotonicTime timeOrigin, ReferrerPolicy, WorkerType, FetchRequestCredentials, JSC::RuntimeFlags) final; + void terminateWorkerGlobalScope() final; + void postMessageToWorkerGlobalScope(MessageWithMessagePorts&&) final; + void postTaskToWorkerGlobalScope(Function<void(ScriptExecutionContext&)>&&) final; + bool hasPendingActivity() const final; + void workerObjectDestroyed() final; + void notifyNetworkStateChange(bool isOnline) final; + void suspendForBackForwardCache() final; + void resumeForBackForwardCache() final; + + // Implementation of WorkerObjectProxy. + // (Only use these functions in the worker context thread.) + void postMessageToWorkerObject(MessageWithMessagePorts&&) final; + void postTaskToWorkerObject(Function<void(Worker&)>&&) final; + void postExceptionToWorkerObject(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL) final; + void confirmMessageFromWorkerObject(bool hasPendingActivity) final; + void reportPendingActivity(bool hasPendingActivity) final; + void workerGlobalScopeClosed() final; + void workerGlobalScopeDestroyed() final; + + // Implementation of WorkerDebuggerProxy. + // (Only use these functions in the worker context thread.) + void postMessageToDebugger(const String&) final; + void setResourceCachingDisabledByWebInspector(bool) final; + + // Implementation of WorkerLoaderProxy. + // These functions are called on different threads to schedule loading + // requests and to send callbacks back to WorkerGlobalScope. + bool isWorkerMessagingProxy() const final { return true; } + void postTaskToLoader(ScriptExecutionContext::Task&&) final; + RefPtr<CacheStorageConnection> createCacheStorageConnection() final; + StorageConnection* storageConnection() final; + RefPtr<RTCDataChannelRemoteHandlerConnection> createRTCDataChannelRemoteHandlerConnection() final; + + void workerThreadCreated(DedicatedWorkerThread&); + + // Only use this method on the worker object thread. + bool askedToTerminate() const { return m_askedToTerminate; } + + void workerGlobalScopeDestroyedInternal(); + void reportPendingActivityInternal(bool confirmingMessage, bool hasPendingActivity); + Worker* workerObject() const { return m_workerObject; } + + RefPtr<ScriptExecutionContext> m_scriptExecutionContext; + RefPtr<WorkerInspectorProxy> m_inspectorProxy; + Worker* m_workerObject; + bool m_mayBeDestroyed { false }; + RefPtr<DedicatedWorkerThread> m_workerThread; + + unsigned m_unconfirmedMessageCount { 0 }; // Unconfirmed messages from worker object to worker thread. + bool m_workerThreadHadPendingActivity { false }; // The latest confirmation from worker thread reported that it was still active. + + bool m_askedToSuspend { false }; + bool m_askedToTerminate { false }; + + Vector<std::unique_ptr<ScriptExecutionContext::Task>> m_queuedEarlyTasks; // Tasks are queued here until there's a thread object created. +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::WorkerMessagingProxy) + static bool isType(const WebCore::WorkerLoaderProxy& proxy) { return proxy.isWorkerMessagingProxy(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/src/javascript/jsc/bindings/webcore/WorkerNavigator.cpp b/src/javascript/jsc/bindings/webcore/WorkerNavigator.cpp new file mode 100644 index 000000000..c72e150af --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerNavigator.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "WorkerNavigator.h" + +namespace WebCore { + +WorkerNavigator::WorkerNavigator(ScriptExecutionContext& context, const String& userAgent, bool isOnline) + : NavigatorBase(&context) + , m_userAgent(userAgent) + , m_isOnline(isOnline) +{ +} + +const String& WorkerNavigator::userAgent() const +{ + return m_userAgent; +} + +bool WorkerNavigator::onLine() const +{ + return m_isOnline; +} + +// GPU* WorkerNavigator::gpu() +// { +// // FIXME: https://bugs.webkit.org/show_bug.cgi?id=233622 Implement this. +// return nullptr; +// } + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerNavigator.h b/src/javascript/jsc/bindings/webcore/WorkerNavigator.h new file mode 100644 index 000000000..847a05ad0 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerNavigator.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "NavigatorBase.h" +#include "Supplementable.h" +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class WorkerNavigator final : public NavigatorBase, public Supplementable<WorkerNavigator> { +public: + static Ref<WorkerNavigator> create(ScriptExecutionContext& context, const String& userAgent, bool isOnline) { return adoptRef(*new WorkerNavigator(context, userAgent, isOnline)); } + + const String& userAgent() const final; + bool onLine() const final; + void setIsOnline(bool isOnline) { m_isOnline = isOnline; } + + // GPU* gpu(); + +private: + explicit WorkerNavigator(ScriptExecutionContext&, const String&, bool isOnline); + + String m_userAgent; + bool m_isOnline; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerNavigator.idl b/src/javascript/jsc/bindings/webcore/WorkerNavigator.idl new file mode 100644 index 000000000..4f19ed621 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerNavigator.idl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Exposed=Worker, + GenerateIsReachable=Impl, + JSCustomMarkFunction, +] interface WorkerNavigator { + readonly attribute unsigned long long hardwareConcurrency; +}; + +WorkerNavigator includes NavigatorID; +WorkerNavigator includes NavigatorLanguage; +WorkerNavigator includes NavigatorLocks; +WorkerNavigator includes NavigatorOnLine; +WorkerNavigator includes NavigatorServiceWorker; +WorkerNavigator includes NavigatorStorage; +WorkerNavigator includes NavigatorGPU; diff --git a/src/javascript/jsc/bindings/webcore/WorkerOptions.h b/src/javascript/jsc/bindings/webcore/WorkerOptions.h new file mode 100644 index 000000000..d4ca6fea3 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerOptions.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021-2022 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "FetchRequestCredentials.h" +#include "WorkerType.h" + +namespace WebCore { + +struct WorkerOptions { + WorkerType type { WorkerType::Classic }; + FetchRequestCredentials credentials { FetchRequestCredentials::SameOrigin }; + String name; + + template<class Encoder> void encode(Encoder&) const; + template<class Decoder> static std::optional<WorkerOptions> decode(Decoder&); +}; + +template<class Encoder> +void WorkerOptions::encode(Encoder& encoder) const +{ + encoder << type << credentials << name; +} + +template<class Decoder> +std::optional<WorkerOptions> WorkerOptions::decode(Decoder& decoder) +{ + std::optional<WorkerType> workerType; + decoder >> workerType; + if (!workerType) + return std::nullopt; + + std::optional<FetchRequestCredentials> credentials; + decoder >> credentials; + if (!credentials) + return std::nullopt; + + std::optional<String> name; + decoder >> name; + if (!name) + return std::nullopt; + + return { { *workerType, *credentials, WTFMove(*name) } }; +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerOptions.idl b/src/javascript/jsc/bindings/webcore/WorkerOptions.idl new file mode 100644 index 000000000..b4c6e089d --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerOptions.idl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +dictionary WorkerOptions { + WorkerType type = "classic"; + FetchRequestCredentials credentials = "same-origin"; // credentials is only used if type is "module" + DOMString name = ""; +}; diff --git a/src/javascript/jsc/bindings/webcore/WorkerOrWorkletGlobalScope.cpp b/src/javascript/jsc/bindings/webcore/WorkerOrWorkletGlobalScope.cpp new file mode 100644 index 000000000..9b0ad8b76 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerOrWorkletGlobalScope.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2020 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WorkerOrWorkletGlobalScope.h" + +// #include "ScriptModuleLoader.h" +// #include "ServiceWorkerGlobalScope.h" +#include "WorkerEventLoop.h" +#include "WorkerInspectorController.h" +#include "WorkerOrWorkletScriptController.h" +#include "WorkerOrWorkletThread.h" +#include "WorkerRunLoop.h" +#include "WorkletGlobalScope.h" +#include <wtf/IsoMallocInlines.h> + +namespace WebCore { + +WTF_MAKE_ISO_ALLOCATED_IMPL(WorkerOrWorkletGlobalScope); + +WorkerOrWorkletGlobalScope::WorkerOrWorkletGlobalScope(WorkerThreadType type, Ref<JSC::VM>&& vm, WorkerOrWorkletThread* thread) + // : m_script(makeUnique<WorkerOrWorkletScriptController>(type, WTFMove(vm), this)) + // , m_moduleLoader(makeUnique<ScriptModuleLoader>(*this, ScriptModuleLoader::OwnerType::WorkerOrWorklet)) + , + m_thread(thread), m_inspectorController(makeUnique<WorkerInspectorController>(*this)) +{ +} + +WorkerOrWorkletGlobalScope::~WorkerOrWorkletGlobalScope() = default; + +void WorkerOrWorkletGlobalScope::prepareForDestruction() +{ + if (m_defaultTaskGroup) { + m_defaultTaskGroup->markAsReadyToStop(); + ASSERT(m_defaultTaskGroup->isStoppedPermanently()); + } + + stopActiveDOMObjects(); + + // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects, + // which become dangling once Heap is destroyed. + removeAllEventListeners(); + + // MicrotaskQueue and RejectedPromiseTracker reference Heap. + if (m_eventLoop) + m_eventLoop->clearMicrotaskQueue(); + removeRejectedPromiseTracker(); + + m_inspectorController->workerTerminating(); +} + +void WorkerOrWorkletGlobalScope::clearScript() +{ + m_script = nullptr; +} + +JSC::VM& WorkerOrWorkletGlobalScope::vm() +{ + return script()->vm(); +} + +void WorkerOrWorkletGlobalScope::disableEval(const String& errorMessage) +{ + m_script->disableEval(errorMessage); +} + +void WorkerOrWorkletGlobalScope::disableWebAssembly(const String& errorMessage) +{ + m_script->disableWebAssembly(errorMessage); +} + +bool WorkerOrWorkletGlobalScope::isJSExecutionForbidden() const +{ + return !m_script || m_script->isExecutionForbidden(); +} + +EventLoopTaskGroup& WorkerOrWorkletGlobalScope::eventLoop() +{ + ASSERT(isContextThread()); + if (UNLIKELY(!m_defaultTaskGroup)) { + m_eventLoop = WorkerEventLoop::create(*this); + m_defaultTaskGroup = makeUnique<EventLoopTaskGroup>(*m_eventLoop); + if (activeDOMObjectsAreStopped()) + m_defaultTaskGroup->stopAndDiscardAllTasks(); + } + return *m_defaultTaskGroup; +} + +bool WorkerOrWorkletGlobalScope::isContextThread() const +{ + auto* thread = workerOrWorkletThread(); + return thread && thread->thread() ? thread->thread() == &Thread::current() : isMainThread(); +} + +void WorkerOrWorkletGlobalScope::postTask(Task&& task) +{ + ASSERT(workerOrWorkletThread()); + workerOrWorkletThread()->runLoop().postTask(WTFMove(task)); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerOrWorkletGlobalScope.h b/src/javascript/jsc/bindings/webcore/WorkerOrWorkletGlobalScope.h new file mode 100644 index 000000000..7c9ba0554 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerOrWorkletGlobalScope.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2020 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "EventTarget.h" +#include "FetchOptions.h" +#include "ScriptExecutionContext.h" +#include "WorkerThreadType.h" + +namespace WebCore { + +class EventLoopTaskGroup; +class ScriptModuleLoader; +class WorkerEventLoop; +class WorkerInspectorController; +class WorkerOrWorkletScriptController; +class WorkerOrWorkletThread; + +class WorkerOrWorkletGlobalScope : public ScriptExecutionContext, public RefCounted<WorkerOrWorkletGlobalScope>, public EventTargetWithInlineData { + WTF_MAKE_ISO_ALLOCATED(WorkerOrWorkletGlobalScope); + WTF_MAKE_NONCOPYABLE(WorkerOrWorkletGlobalScope); + +public: + ~WorkerOrWorkletGlobalScope(); + + using ScriptExecutionContext::weakPtrFactory; + using WeakValueType = ScriptExecutionContext::WeakValueType; + + bool isClosing() const { return m_isClosing; } + WorkerOrWorkletThread* workerOrWorkletThread() const { return m_thread; } + + // WorkerOrWorkletScriptController* script() const { return m_script.get(); } + void clearScript(); + + JSC::VM& vm(); + WorkerInspectorController& inspectorController() const { return *m_inspectorController; } + + // ScriptModuleLoader& moduleLoader() { return *m_moduleLoader; } + + // ScriptExecutionContext. + ScriptExecutionContext* scriptExecutionContext() const { return const_cast<WorkerOrWorkletGlobalScope*>(this); } + EventLoopTaskGroup& eventLoop(); + bool isContextThread() const; + void postTask(Task&&); // Executes the task on context's thread asynchronously. + + virtual void prepareForDestruction(); + + using RefCounted::deref; + using RefCounted::ref; + + void suspend() {} + void resume() {} + + // virtual FetchOptions::Destination destination() const = 0; + +protected: + WorkerOrWorkletGlobalScope(WorkerThreadType, Ref<JSC::VM>&&, WorkerOrWorkletThread*); + + // ScriptExecutionContext. + bool isJSExecutionForbidden() const; + + void markAsClosing() { m_isClosing = true; } + +private: + // ScriptExecutionContext. + void disableEval(const String& errorMessage); + void disableWebAssembly(const String& errorMessage); + void refScriptExecutionContext() { ref(); } + void derefScriptExecutionContext() { deref(); } + + // EventTarget. + void refEventTarget() { ref(); } + void derefEventTarget() { deref(); } + + // #if ENABLE(NOTIFICATIONS) + // NotificationClient* notificationClient() override + // { + // return nullptr; + // } + // #endif + + // std::unique_ptr<WorkerOrWorkletScriptController> m_script; + // std::unique_ptr<ScriptModuleLoader> m_moduleLoader; + WorkerOrWorkletThread* m_thread; + RefPtr<WorkerEventLoop> m_eventLoop; + std::unique_ptr<EventLoopTaskGroup> m_defaultTaskGroup; + std::unique_ptr<WorkerInspectorController> m_inspectorController; + bool m_isClosing { false }; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::WorkerOrWorkletGlobalScope) +static bool isType(const WebCore::ScriptExecutionContext& context) { return context.isWorkerGlobalScope() || context.isWorkletGlobalScope(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/src/javascript/jsc/bindings/webcore/WorkerOrWorkletScriptController.h b/src/javascript/jsc/bindings/webcore/WorkerOrWorkletScriptController.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerOrWorkletScriptController.h diff --git a/src/javascript/jsc/bindings/webcore/WorkerOrWorkletThread.cpp b/src/javascript/jsc/bindings/webcore/WorkerOrWorkletThread.cpp new file mode 100644 index 000000000..0a926c0cd --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerOrWorkletThread.cpp @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2008-2020 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WorkerOrWorkletThread.h" + +#include "ThreadGlobalData.h" +#include "WorkerEventLoop.h" +#include "WorkerOrWorkletGlobalScope.h" +#include "WorkerOrWorkletScriptController.h" + +#if PLATFORM(IOS_FAMILY) +#include "FloatingPointEnvironment.h" +#endif + +#if USE(GLIB) +#include <wtf/glib/GRefPtr.h> +#endif + +namespace WebCore { + +Lock WorkerOrWorkletThread::s_workerOrWorkletThreadsLock; + +Lock& WorkerOrWorkletThread::workerOrWorkletThreadsLock() +{ + return s_workerOrWorkletThreadsLock; +} + +HashSet<WorkerOrWorkletThread*>& WorkerOrWorkletThread::workerOrWorkletThreads() +{ + ASSERT(workerOrWorkletThreadsLock().isHeld()); + static NeverDestroyed<HashSet<WorkerOrWorkletThread*>> workerOrWorkletThreads; + return workerOrWorkletThreads; +} + +static UniqueRef<WorkerRunLoop> constructRunLoop(WorkerThreadMode workerThreadMode) +{ + switch (workerThreadMode) { + case WorkerThreadMode::UseMainThread: + return makeUniqueRef<WorkerMainRunLoop>(); + case WorkerThreadMode::CreateNewThread: + break; + } + return makeUniqueRef<WorkerDedicatedRunLoop>(); +} + +WorkerOrWorkletThread::WorkerOrWorkletThread(const String& inspectorIdentifier, WorkerThreadMode workerThreadMode) + : m_inspectorIdentifier(inspectorIdentifier) + , m_runLoop(constructRunLoop(workerThreadMode)) +{ + Locker locker { workerOrWorkletThreadsLock() }; + workerOrWorkletThreads().add(this); +} + +WorkerOrWorkletThread::~WorkerOrWorkletThread() +{ + Locker locker { workerOrWorkletThreadsLock() }; + ASSERT(workerOrWorkletThreads().contains(this)); + workerOrWorkletThreads().remove(this); +} + +void WorkerOrWorkletThread::startRunningDebuggerTasks() +{ + ASSERT(!m_pausedForDebugger); + m_pausedForDebugger = true; + + // FIXME: Add support for debugging workers running on the main thread. + if (!is<WorkerDedicatedRunLoop>(m_runLoop.get())) + return; + + MessageQueueWaitResult result; + do { + result = downcast<WorkerDedicatedRunLoop>(m_runLoop.get()).runInDebuggerMode(*m_globalScope); + } while (result != MessageQueueTerminated && m_pausedForDebugger); +} + +void WorkerOrWorkletThread::stopRunningDebuggerTasks() +{ + m_pausedForDebugger = false; +} + +void WorkerOrWorkletThread::runEventLoop() +{ + // Does not return until terminated. + if (is<WorkerDedicatedRunLoop>(m_runLoop.get())) + downcast<WorkerDedicatedRunLoop>(m_runLoop.get()).run(m_globalScope.get()); +} + +void WorkerOrWorkletThread::workerOrWorkletThread() +{ + Ref protectedThis { *this }; + + if (isMainThread()) { + m_globalScope = createGlobalScope(); + if (!m_globalScope) + return; + + downcast<WorkerMainRunLoop>(m_runLoop.get()).setGlobalScope(*m_globalScope); + + String exceptionMessage; + evaluateScriptIfNecessary(exceptionMessage); + + callOnMainThread([evaluateCallback = WTFMove(m_evaluateCallback), message = WTFMove(exceptionMessage)] { + if (evaluateCallback) + evaluateCallback(message); + }); + return; + } + + // Propagate the mainThread's fenv to workers. +#if PLATFORM(IOS_FAMILY) + FloatingPointEnvironment::singleton().propagateMainThreadEnvironment(); +#endif + +#if USE(GLIB) + GRefPtr<GMainContext> mainContext = adoptGRef(g_main_context_new()); + g_main_context_push_thread_default(mainContext.get()); +#endif + + WorkerOrWorkletScriptController* scriptController; + { + // Mutex protection is necessary to ensure that we don't change m_globalScope + // while WorkerThread::stop() is accessing it. Note that WorkerThread::stop() can + // be called before we've finished creating the WorkerGlobalScope. + Locker locker { m_threadCreationAndGlobalScopeLock }; + m_globalScope = createGlobalScope(); + + // When running out of memory, createGlobalScope() may return null because we could not allocate a JSC::VM. + if (!m_globalScope) { + WTFLogAlways("Error: Failed to create a WorkerOrWorkerGlobalScope."); + return; + } + + scriptController = m_globalScope->script(); + + if (m_runLoop->terminated()) { + // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet, + // forbidExecution() couldn't be called from stop(). + scriptController->scheduleExecutionTermination(); + scriptController->forbidExecution(); + } + } + + if (shouldWaitForWebInspectorOnStartup()) { + startRunningDebuggerTasks(); + + // If the worker was somehow terminated while processing debugger commands. + if (m_runLoop->terminated()) + scriptController->forbidExecution(); + } + + String exceptionMessage; + evaluateScriptIfNecessary(exceptionMessage); + + callOnMainThread([evaluateCallback = WTFMove(m_evaluateCallback), message = exceptionMessage.isolatedCopy()] { + if (evaluateCallback) + evaluateCallback(message); + }); + + runEventLoop(); + +#if USE(GLIB) + g_main_context_pop_thread_default(mainContext.get()); +#endif + + RefPtr<Thread> protector = m_thread; + + ASSERT(m_globalScope->hasOneRef()); + + RefPtr<WorkerOrWorkletGlobalScope> workerGlobalScopeToDelete; + { + // Mutex protection is necessary to ensure that we don't change m_globalScope + // while WorkerThread::stop is accessing it. + Locker locker { m_threadCreationAndGlobalScopeLock }; + + // Delay the destruction of the WorkerGlobalScope context until after we've unlocked the + // m_threadCreationAndWorkerGlobalScopeMutex. This is needed because destructing the + // context will trigger the main thread to race against us to delete the WorkerThread + // object, and the WorkerThread object owns the mutex we need to unlock after this. + workerGlobalScopeToDelete = std::exchange(m_globalScope, nullptr); + + if (m_stoppedCallback) + callOnMainThread(WTFMove(m_stoppedCallback)); + } + + // The below assignment will destroy the context, which will in turn notify messaging proxy. + // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them. + workerGlobalScopeToDelete = nullptr; + + // Clean up WebCore::ThreadGlobalData before WTF::Thread goes away! + threadGlobalData().destroy(); + + // Send the last WorkerThread Ref to be Deref'ed on the main thread. + callOnMainThread([protectedThis = WTFMove(protectedThis)] { }); + + // The thread object may be already destroyed from notification now, don't try to access "this". + protector->detach(); +} + +void WorkerOrWorkletThread::start(Function<void(const String&)>&& evaluateCallback) +{ + // Mutex protection is necessary to ensure that m_thread is initialized when the thread starts. + Locker locker { m_threadCreationAndGlobalScopeLock }; + + if (m_thread) + return; + + m_evaluateCallback = WTFMove(evaluateCallback); + + auto thread = createThread(); + + // Force the Thread object to be initialized fully before storing it to m_thread (and becoming visible to other threads). + WTF::storeStoreFence(); + + m_thread = WTFMove(thread); +} + +void WorkerOrWorkletThread::stop(Function<void()>&& stoppedCallback) +{ + // Mutex protection is necessary to ensure that m_workerGlobalScope isn't changed by + // WorkerThread::workerThread() while we're accessing it. Note also that stop() can + // be called before m_workerGlobalScope is fully created. + if (!m_threadCreationAndGlobalScopeLock.tryLock()) { + // The thread is still starting, spin the runloop and try again to avoid deadlocks if the worker thread + // needs to interact with the main thread during startup. + callOnMainThread([this, stoppedCallback = WTFMove(stoppedCallback)]() mutable { + stop(WTFMove(stoppedCallback)); + }); + return; + } + Locker locker { AdoptLock, m_threadCreationAndGlobalScopeLock }; + + // If the thread is suspended, resume it now so that we can dispatch the cleanup tasks below. + if (m_isSuspended) + resume(); + + ASSERT(!m_stoppedCallback); + m_stoppedCallback = WTFMove(stoppedCallback); + + // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever. + if (globalScope()) { + globalScope()->script()->scheduleExecutionTermination(); + + if (is<WorkerMainRunLoop>(m_runLoop.get())) { + auto globalScope = std::exchange(m_globalScope, nullptr); + globalScope->prepareForDestruction(); + globalScope->clearScript(); + m_runLoop->terminate(); + + if (m_stoppedCallback) + callOnMainThread(std::exchange(m_stoppedCallback, nullptr)); + return; + } + + m_runLoop->postTaskAndTerminate({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context ) { + auto& globalScope = downcast<WorkerOrWorkletGlobalScope>(context); + + globalScope.prepareForDestruction(); + + // Stick a shutdown command at the end of the queue, so that we deal + // with all the cleanup tasks the databases post first. + globalScope.postTask({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext& context) { + auto& globalScope = downcast<WorkerOrWorkletGlobalScope>(context); + // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed. + globalScope.clearScript(); + } }); + + } }); + return; + } + m_runLoop->terminate(); +} + +void WorkerOrWorkletThread::suspend() +{ + m_isSuspended = true; + if (is<WorkerMainRunLoop>(m_runLoop.get())) + return; + + m_runLoop->postTask([&](ScriptExecutionContext&) { + if (globalScope()) + globalScope()->suspend(); + + m_suspensionSemaphore.wait(); + + if (globalScope()) + globalScope()->resume(); + }); +} + +void WorkerOrWorkletThread::resume() +{ + ASSERT(m_isSuspended); + m_isSuspended = false; + if (is<WorkerMainRunLoop>(m_runLoop.get())) + return; + + m_suspensionSemaphore.signal(); +} + +void WorkerOrWorkletThread::releaseFastMallocFreeMemoryInAllThreads() +{ + Locker locker { workerOrWorkletThreadsLock() }; + for (auto* workerOrWorkletThread : workerOrWorkletThreads()) { + workerOrWorkletThread->runLoop().postTask([] (ScriptExecutionContext&) { + WTF::releaseFastMallocFreeMemory(); + }); + } +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerOrWorkletThread.h b/src/javascript/jsc/bindings/webcore/WorkerOrWorkletThread.h new file mode 100644 index 000000000..e35fcdcdd --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerOrWorkletThread.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2008-2020 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "WorkerRunLoop.h" +#include "WorkerThreadMode.h" +#include <wtf/Forward.h> +#include <wtf/Function.h> +#include <wtf/Lock.h> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/threads/BinarySemaphore.h> + +namespace WTF { +class Thread; +} + +namespace WebCore { + +class WorkerDebuggerProxy; +class WorkerLoaderProxy; + +class WorkerOrWorkletThread : public ThreadSafeRefCounted<WorkerOrWorkletThread> { +public: + virtual ~WorkerOrWorkletThread(); + + Thread* thread() const { return m_thread.get(); } + + virtual WorkerDebuggerProxy* workerDebuggerProxy() const = 0; + virtual WorkerLoaderProxy& workerLoaderProxy() = 0; + + WorkerOrWorkletGlobalScope* globalScope() const { return m_globalScope.get(); } + WorkerRunLoop& runLoop() { return m_runLoop; } + + void start(Function<void(const String&)>&& evaluateCallback = { }); + void stop(Function<void()>&& terminatedCallback = { }); + + void startRunningDebuggerTasks(); + void stopRunningDebuggerTasks(); + + void suspend(); + void resume(); + + const String& inspectorIdentifier() const { return m_inspectorIdentifier; } + + static HashSet<WorkerOrWorkletThread*>& workerOrWorkletThreads() WTF_REQUIRES_LOCK(workerOrWorkletThreadsLock()); + static Lock& workerOrWorkletThreadsLock() WTF_RETURNS_LOCK(s_workerOrWorkletThreadsLock); + static void releaseFastMallocFreeMemoryInAllThreads(); + +protected: + explicit WorkerOrWorkletThread(const String& inspectorIdentifier, WorkerThreadMode = WorkerThreadMode::CreateNewThread); + void workerOrWorkletThread(); + + // Executes the event loop for the worker thread. Derived classes can override to perform actions before/after entering the event loop. + virtual void runEventLoop(); + +private: + virtual Ref<Thread> createThread() = 0; + virtual RefPtr<WorkerOrWorkletGlobalScope> createGlobalScope() = 0; + virtual void evaluateScriptIfNecessary(String&) { } + virtual bool shouldWaitForWebInspectorOnStartup() const { return false; } + + static Lock s_workerOrWorkletThreadsLock; + + String m_inspectorIdentifier; + Lock m_threadCreationAndGlobalScopeLock; + RefPtr<WorkerOrWorkletGlobalScope> m_globalScope; + RefPtr<Thread> m_thread; + UniqueRef<WorkerRunLoop> m_runLoop; + Function<void(const String&)> m_evaluateCallback; + Function<void()> m_stoppedCallback; + BinarySemaphore m_suspensionSemaphore; + bool m_isSuspended { false }; + bool m_pausedForDebugger { false }; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerReportingProxy.h b/src/javascript/jsc/bindings/webcore/WorkerReportingProxy.h new file mode 100644 index 000000000..fdae2f8ce --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerReportingProxy.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +class WorkerReportingProxy { +public: + virtual ~WorkerReportingProxy() = default; + + virtual void postExceptionToWorkerObject(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL) = 0; + + // Invoked when close() is invoked on the worker context. + virtual void workerGlobalScopeClosed() = 0; + + // Invoked when the thread has stopped. + virtual void workerGlobalScopeDestroyed() = 0; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerRunLoop.cpp b/src/javascript/jsc/bindings/webcore/WorkerRunLoop.cpp new file mode 100644 index 000000000..fce5ce132 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerRunLoop.cpp @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2016-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WorkerRunLoop.h" + +#include "ScriptExecutionContext.h" +#include "SharedTimer.h" +#include "ThreadGlobalData.h" +#include "ThreadTimers.h" +#include "WorkerOrWorkletGlobalScope.h" +#include "WorkerOrWorkletScriptController.h" +#include "WorkerThread.h" +#include <JavaScriptCore/JSRunLoopTimer.h> + +#if USE(GLIB) +#include <glib.h> +#endif + +namespace WebCore { + +class WorkerSharedTimer final : public SharedTimer { +public: + // SharedTimer interface. + void setFiredFunction(Function<void()>&& function) final { m_sharedTimerFunction = WTFMove(function); } + void setFireInterval(Seconds interval) final { m_nextFireTime = MonotonicTime::now() + interval; } + void stop() final { m_nextFireTime = MonotonicTime { }; } + + bool isActive() { return m_sharedTimerFunction && m_nextFireTime; } + Seconds fireTimeDelay() { return std::max(0_s, m_nextFireTime - MonotonicTime::now()); } + void fire() { m_sharedTimerFunction(); } + +private: + Function<void()> m_sharedTimerFunction; + MonotonicTime m_nextFireTime; +}; + +class ModePredicate { +public: + explicit ModePredicate(String&& mode) + : m_mode(WTFMove(mode)) + , m_defaultMode(m_mode == WorkerRunLoop::defaultMode()) + { + } + + bool isDefaultMode() const + { + return m_defaultMode; + } + + bool operator()(const WorkerDedicatedRunLoop::Task& task) const + { + return m_defaultMode || m_mode == task.mode(); + } + +private: + String m_mode; + bool m_defaultMode; +}; + +WorkerDedicatedRunLoop::WorkerDedicatedRunLoop() + : m_sharedTimer(makeUnique<WorkerSharedTimer>()) +{ +} + +WorkerDedicatedRunLoop::~WorkerDedicatedRunLoop() +{ + ASSERT(!m_nestedCount); +} + +String WorkerRunLoop::defaultMode() +{ + return String(); +} + +static String debuggerMode() +{ + return "debugger"_s; +} + +class RunLoopSetup { + WTF_MAKE_NONCOPYABLE(RunLoopSetup); +public: + enum class IsForDebugging { No, Yes }; + RunLoopSetup(WorkerDedicatedRunLoop& runLoop, IsForDebugging isForDebugging) + : m_runLoop(runLoop) + , m_isForDebugging(isForDebugging) + { + if (!m_runLoop.m_nestedCount) + threadGlobalData().threadTimers().setSharedTimer(m_runLoop.m_sharedTimer.get()); + m_runLoop.m_nestedCount++; + if (m_isForDebugging == IsForDebugging::Yes) + m_runLoop.m_debugCount++; + } + + ~RunLoopSetup() + { + m_runLoop.m_nestedCount--; + if (!m_runLoop.m_nestedCount) + threadGlobalData().threadTimers().setSharedTimer(nullptr); + if (m_isForDebugging == IsForDebugging::Yes) + m_runLoop.m_debugCount--; + } +private: + WorkerDedicatedRunLoop& m_runLoop; + IsForDebugging m_isForDebugging { IsForDebugging::No }; +}; + +void WorkerDedicatedRunLoop::run(WorkerOrWorkletGlobalScope* context) +{ + RunLoopSetup setup(*this, RunLoopSetup::IsForDebugging::No); + ModePredicate modePredicate(defaultMode()); + MessageQueueWaitResult result; + do { + result = runInMode(context, modePredicate); + } while (result != MessageQueueTerminated); + runCleanupTasks(context); +} + +MessageQueueWaitResult WorkerDedicatedRunLoop::runInDebuggerMode(WorkerOrWorkletGlobalScope& context) +{ + RunLoopSetup setup(*this, RunLoopSetup::IsForDebugging::Yes); + return runInMode(&context, ModePredicate { debuggerMode() }); +} + +bool WorkerDedicatedRunLoop::runInMode(WorkerOrWorkletGlobalScope* context, const String& mode) +{ + ASSERT(mode != debuggerMode()); + RunLoopSetup setup(*this, RunLoopSetup::IsForDebugging::No); + ModePredicate modePredicate(String { mode }); + return runInMode(context, modePredicate) != MessageQueueWaitResult::MessageQueueTerminated; +} + +MessageQueueWaitResult WorkerDedicatedRunLoop::runInMode(WorkerOrWorkletGlobalScope* context, const ModePredicate& predicate) +{ + ASSERT(context); + ASSERT(context->workerOrWorkletThread()->thread() == &Thread::current()); + + JSC::JSRunLoopTimer::TimerNotificationCallback timerAddedTask = createSharedTask<JSC::JSRunLoopTimer::TimerNotificationType>([this] { + // We don't actually do anything here, we just want to loop around runInMode + // to both recalculate our deadline and to potentially run the run loop. + this->postTask([](ScriptExecutionContext&) { }); + }); + +#if USE(GLIB) + GMainContext* mainContext = g_main_context_get_thread_default(); + if (g_main_context_pending(mainContext)) + g_main_context_iteration(mainContext, FALSE); +#endif + + Seconds timeoutDelay = Seconds::infinity(); + +#if USE(CF) + CFAbsoluteTime nextCFRunLoopTimerFireDate = CFRunLoopGetNextTimerFireDate(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + double timeUntilNextCFRunLoopTimerInSeconds = nextCFRunLoopTimerFireDate - CFAbsoluteTimeGetCurrent(); + timeoutDelay = std::max(0_s, Seconds(timeUntilNextCFRunLoopTimerInSeconds)); +#endif + + if (predicate.isDefaultMode() && m_sharedTimer->isActive()) + timeoutDelay = std::min(timeoutDelay, m_sharedTimer->fireTimeDelay()); + + if (auto* script = context->script()) { + script->releaseHeapAccess(); + script->addTimerSetNotification(timerAddedTask); + } + MessageQueueWaitResult result; + auto task = m_messageQueue.waitForMessageFilteredWithTimeout(result, predicate, timeoutDelay); + if (auto* script = context->script()) { + script->acquireHeapAccess(); + script->removeTimerSetNotification(timerAddedTask); + } + + // If the context is closing, don't execute any further JavaScript tasks (per section 4.1.1 of the Web Workers spec). However, there may be implementation cleanup tasks in the queue, so keep running through it. + + switch (result) { + case MessageQueueTerminated: + break; + + case MessageQueueMessageReceived: + task->performTask(context); + break; + + case MessageQueueTimeout: + if (!context->isClosing() && !isBeingDebugged()) + m_sharedTimer->fire(); + break; + } + +#if USE(CF) + if (result != MessageQueueTerminated) { + if (nextCFRunLoopTimerFireDate <= CFAbsoluteTimeGetCurrent()) + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, /*returnAfterSourceHandled*/ false); + } +#endif + + return result; +} + +void WorkerDedicatedRunLoop::runCleanupTasks(WorkerOrWorkletGlobalScope* context) +{ + ASSERT(context); + ASSERT(context->workerOrWorkletThread()->thread() == &Thread::current()); + ASSERT(m_messageQueue.killed()); + + while (true) { + auto task = m_messageQueue.tryGetMessageIgnoringKilled(); + if (!task) + return; + task->performTask(context); + } +} + +void WorkerDedicatedRunLoop::terminate() +{ + m_messageQueue.kill(); +} + +void WorkerRunLoop::postTask(ScriptExecutionContext::Task&& task) +{ + postTaskForMode(WTFMove(task), defaultMode()); +} + +void WorkerDedicatedRunLoop::postTaskAndTerminate(ScriptExecutionContext::Task&& task) +{ + m_messageQueue.appendAndKill(makeUnique<Task>(WTFMove(task), defaultMode())); +} + +void WorkerDedicatedRunLoop::postTaskForMode(ScriptExecutionContext::Task&& task, const String& mode) +{ + m_messageQueue.append(makeUnique<Task>(WTFMove(task), mode)); +} + +void WorkerRunLoop::postDebuggerTask(ScriptExecutionContext::Task&& task) +{ + postTaskForMode(WTFMove(task), debuggerMode()); +} + +void WorkerDedicatedRunLoop::Task::performTask(WorkerOrWorkletGlobalScope* context) +{ + if ((!context->isClosing() && context->script() && !context->script()->isTerminatingExecution()) || m_task.isCleanupTask()) + m_task.performTask(*context); +} + +WorkerDedicatedRunLoop::Task::Task(ScriptExecutionContext::Task&& task, const String& mode) + : m_task(WTFMove(task)) + , m_mode(mode.isolatedCopy()) +{ +} + +WorkerMainRunLoop::WorkerMainRunLoop() +{ +} + +void WorkerMainRunLoop::setGlobalScope(WorkerOrWorkletGlobalScope& globalScope) +{ + m_workerOrWorkletGlobalScope = globalScope; +} + +void WorkerMainRunLoop::postTaskAndTerminate(ScriptExecutionContext::Task&& task) +{ + if (m_terminated) + return; + + RunLoop::main().dispatch([weakThis = WeakPtr { *this }, task = WTFMove(task)]() mutable { + if (!weakThis || !weakThis->m_workerOrWorkletGlobalScope || weakThis->m_terminated) + return; + + weakThis->m_terminated = true; + task.performTask(*weakThis->m_workerOrWorkletGlobalScope); + }); +} + +void WorkerMainRunLoop::postTaskForMode(ScriptExecutionContext::Task&& task, const String& /*mode*/) +{ + if (m_terminated) + return; + + RunLoop::main().dispatch([weakThis = WeakPtr { *this }, task = WTFMove(task)]() mutable { + if (!weakThis || !weakThis->m_workerOrWorkletGlobalScope || weakThis->m_terminated) + return; + + task.performTask(*weakThis->m_workerOrWorkletGlobalScope); + }); +} + +bool WorkerMainRunLoop::runInMode(WorkerOrWorkletGlobalScope*, const String&) +{ + RunLoop::main().cycle(); + return true; +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerRunLoop.h b/src/javascript/jsc/bindings/webcore/WorkerRunLoop.h new file mode 100644 index 000000000..33fe24189 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerRunLoop.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2017-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ScriptExecutionContext.h" +#include <memory> +#include <wtf/MessageQueue.h> + +namespace WebCore { + +class ModePredicate; +class WorkerOrWorkletGlobalScope; +class WorkerSharedTimer; + +class WorkerRunLoop { + WTF_MAKE_FAST_ALLOCATED; +public: + enum class Type : bool { WorkerDedicatedRunLoop, WorkerMainRunLoop }; + + virtual ~WorkerRunLoop() = default; + + virtual bool runInMode(WorkerOrWorkletGlobalScope*, const String& mode) = 0; + virtual void postTaskAndTerminate(ScriptExecutionContext::Task&&) = 0; + virtual void postTaskForMode(ScriptExecutionContext::Task&&, const String& mode) = 0; + virtual void terminate() = 0; + virtual bool terminated() const = 0; + virtual Type type() const = 0; + + void postTask(ScriptExecutionContext::Task&&); + void postDebuggerTask(ScriptExecutionContext::Task&&); + + WEBCORE_EXPORT static String defaultMode(); + + unsigned long createUniqueId() { return ++m_uniqueId; } + +private: + unsigned long m_uniqueId { 0 }; +}; + +class WorkerDedicatedRunLoop final : public WorkerRunLoop { +public: + WorkerDedicatedRunLoop(); + ~WorkerDedicatedRunLoop(); + + // Blocking call. Waits for tasks and timers, invokes the callbacks. + void run(WorkerOrWorkletGlobalScope*); + + // Waits for a single task and returns. + bool runInMode(WorkerOrWorkletGlobalScope*, const String& mode) final; + MessageQueueWaitResult runInDebuggerMode(WorkerOrWorkletGlobalScope&); + + void terminate() final; + bool terminated() const final { return m_messageQueue.killed(); } + Type type() const final { return Type::WorkerDedicatedRunLoop; } + + void postTaskAndTerminate(ScriptExecutionContext::Task&&) final; + WEBCORE_EXPORT void postTaskForMode(ScriptExecutionContext::Task&&, const String& mode) final; + + class Task { + WTF_MAKE_NONCOPYABLE(Task); WTF_MAKE_FAST_ALLOCATED; + public: + Task(ScriptExecutionContext::Task&&, const String& mode); + const String& mode() const { return m_mode; } + + private: + void performTask(WorkerOrWorkletGlobalScope*); + + ScriptExecutionContext::Task m_task; + String m_mode; + + friend class WorkerDedicatedRunLoop; + }; + +private: + friend class RunLoopSetup; + MessageQueueWaitResult runInMode(WorkerOrWorkletGlobalScope*, const ModePredicate&); + + // Runs any clean up tasks that are currently in the queue and returns. + // This should only be called when the context is closed or loop has been terminated. + void runCleanupTasks(WorkerOrWorkletGlobalScope*); + + bool isBeingDebugged() const { return m_debugCount >= 1; } + + MessageQueue<Task> m_messageQueue; + std::unique_ptr<WorkerSharedTimer> m_sharedTimer; + int m_nestedCount { 0 }; + int m_debugCount { 0 }; +}; + +class WorkerMainRunLoop final : public WorkerRunLoop, public CanMakeWeakPtr<WorkerMainRunLoop> { +public: + WorkerMainRunLoop(); + + void setGlobalScope(WorkerOrWorkletGlobalScope&); + + void terminate() final { m_terminated = true; } + bool terminated() const final { return m_terminated; } + + bool runInMode(WorkerOrWorkletGlobalScope*, const String& mode); + void postTaskAndTerminate(ScriptExecutionContext::Task&&) final; + void postTaskForMode(ScriptExecutionContext::Task&&, const String& mode) final; + Type type() const final { return Type::WorkerMainRunLoop; } + +private: + WeakPtr<WorkerOrWorkletGlobalScope> m_workerOrWorkletGlobalScope; + bool m_terminated { false }; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::WorkerDedicatedRunLoop) + static bool isType(const WebCore::WorkerRunLoop& runLoop) { return runLoop.type() == WebCore::WorkerRunLoop::Type::WorkerDedicatedRunLoop; } +SPECIALIZE_TYPE_TRAITS_END() + +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::WorkerMainRunLoop) + static bool isType(const WebCore::WorkerRunLoop& runLoop) { return runLoop.type() == WebCore::WorkerRunLoop::Type::WorkerMainRunLoop; } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/src/javascript/jsc/bindings/webcore/WorkerThread.cpp b/src/javascript/jsc/bindings/webcore/WorkerThread.cpp new file mode 100644 index 000000000..111141ff6 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerThread.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "WorkerThread.h" + +#include "IDBConnectionProxy.h" +#include "ScriptSourceCode.h" +#include "SecurityOrigin.h" +#include "SocketProvider.h" +#include "WorkerGlobalScope.h" +#include "WorkerScriptFetcher.h" +#include <JavaScriptCore/ScriptCallStack.h> +#include <wtf/SetForScope.h> +#include <wtf/Threading.h> + +namespace WebCore { + +static std::atomic<unsigned> workerThreadCounter { 0 }; + +unsigned WorkerThread::workerThreadCount() +{ + return workerThreadCounter; +} + +WorkerParameters WorkerParameters::isolatedCopy() const +{ + return { + scriptURL.isolatedCopy(), + name.isolatedCopy(), + inspectorIdentifier.isolatedCopy(), + userAgent.isolatedCopy(), + isOnline, + contentSecurityPolicyResponseHeaders.isolatedCopy(), + shouldBypassMainWorldContentSecurityPolicy, + crossOriginEmbedderPolicy.isolatedCopy(), + timeOrigin, + referrerPolicy, + workerType, + credentials, + settingsValues.isolatedCopy(), + workerThreadMode, + sessionID, + }; +} + +struct WorkerThreadStartupData { + WTF_MAKE_NONCOPYABLE(WorkerThreadStartupData); + WTF_MAKE_FAST_ALLOCATED; + +public: + WorkerThreadStartupData(const WorkerParameters& params, const ScriptBuffer& sourceCode, WorkerThreadStartMode /*,const SecurityOrigin& topOrigin*/); + + WorkerParameters params; + // Ref<SecurityOrigin> origin; + ScriptBuffer sourceCode; + WorkerThreadStartMode startMode; + // Ref<SecurityOrigin> topOrigin; +}; + +WorkerThreadStartupData::WorkerThreadStartupData(const WorkerParameters& other, const ScriptBuffer& sourceCode, WorkerThreadStartMode startMode, /*const SecurityOrigin& topOrigin*/ + ) + : params(other.isolatedCopy()) + // , origin(SecurityOrigin::create(other.scriptURL)->isolatedCopy()) + , sourceCode(sourceCode.isolatedCopy()) + , startMode(startMode) +// , topOrigin(topOrigin.isolatedCopy()) +{ +} + +WorkerThread::WorkerThread(const WorkerParameters& params, const ScriptBuffer& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerDebuggerProxy& workerDebuggerProxy, WorkerReportingProxy& workerReportingProxy, WorkerThreadStartMode startMode, const SecurityOrigin& topOrigin, IDBClient::IDBConnectionProxy* connectionProxy, SocketProvider* socketProvider, JSC::RuntimeFlags runtimeFlags) + : WorkerOrWorkletThread(params.inspectorIdentifier.isolatedCopy(), params.workerThreadMode) + , m_workerLoaderProxy(workerLoaderProxy) + , m_workerDebuggerProxy(workerDebuggerProxy) + , m_workerReportingProxy(workerReportingProxy) + , m_runtimeFlags(runtimeFlags) + , m_startupData(makeUnique<WorkerThreadStartupData>(params, sourceCode, startMode, topOrigin)) + , m_idbConnectionProxy(connectionProxy) + , m_socketProvider(socketProvider) +{ + ++workerThreadCounter; +} + +WorkerThread::~WorkerThread() +{ + ASSERT(workerThreadCounter); + --workerThreadCounter; +} + +Ref<Thread> WorkerThread::createThread() +{ + if (is<WorkerMainRunLoop>(runLoop())) { + // This worker should run on the main thread. + RunLoop::main().dispatch([protectedThis = Ref { *this }] { + protectedThis->workerOrWorkletThread(); + }); + ASSERT(isMainThread()); + return Thread::current(); + } + + return Thread::create( + threadName(), [this] { + workerOrWorkletThread(); + }, + ThreadType::JavaScript); +} + +RefPtr<WorkerOrWorkletGlobalScope> WorkerThread::createGlobalScope() +{ + return createWorkerGlobalScope(m_startupData->params, WTFMove(m_startupData->origin), WTFMove(m_startupData->topOrigin)); +} + +bool WorkerThread::shouldWaitForWebInspectorOnStartup() const +{ + return m_startupData->startMode == WorkerThreadStartMode::WaitForInspector; +} + +void WorkerThread::evaluateScriptIfNecessary(String& exceptionMessage) +{ + SetForScope isInStaticScriptEvaluation(m_isInStaticScriptEvaluation, true); + + // We are currently holding only the initial script code. If the WorkerType is Module, we should fetch the entire graph before executing the rest of this. + // We invoke module loader as if we are executing inline module script tag in Document. + + WeakPtr<ScriptBufferSourceProvider> sourceProvider; + if (m_startupData->params.workerType == WorkerType::Classic) { + ScriptSourceCode sourceCode(m_startupData->sourceCode, URL(m_startupData->params.scriptURL)); + sourceProvider = static_cast<ScriptBufferSourceProvider&>(sourceCode.provider()); + globalScope()->script()->evaluate(sourceCode, &exceptionMessage); + finishedEvaluatingScript(); + } else { + auto scriptFetcher = WorkerScriptFetcher::create(globalScope()->credentials(), globalScope()->destination(), globalScope()->referrerPolicy()); + ScriptSourceCode sourceCode(m_startupData->sourceCode, URL(m_startupData->params.scriptURL), {}, JSC::SourceProviderSourceType::Module, scriptFetcher.copyRef()); + sourceProvider = static_cast<ScriptBufferSourceProvider&>(sourceCode.provider()); + bool success = globalScope()->script()->loadModuleSynchronously(scriptFetcher.get(), sourceCode); + if (success) { + if (std::optional<LoadableScript::Error> error = scriptFetcher->error()) { + if (std::optional<LoadableScript::ConsoleMessage> message = error->consoleMessage) + exceptionMessage = message->message; + else + exceptionMessage = "Importing a module script failed."_s; + globalScope()->reportException(exceptionMessage, {}, {}, {}, {}, {}); + } else if (!scriptFetcher->wasCanceled()) { + globalScope()->script()->linkAndEvaluateModule(scriptFetcher.get(), sourceCode, &exceptionMessage); + finishedEvaluatingScript(); + } + } + } + if (sourceProvider) + globalScope()->setMainScriptSourceProvider(*sourceProvider); + + // Free the startup data to cause its member variable deref's happen on the worker's thread (since + // all ref/derefs of these objects are happening on the thread at this point). Note that + // WorkerThread::~WorkerThread happens on a different thread where it was created. + m_startupData = nullptr; +} + +// IDBClient::IDBConnectionProxy* WorkerThread::idbConnectionProxy() +// { +// return m_idbConnectionProxy.get(); +// } + +// SocketProvider* WorkerThread::socketProvider() +// { +// return m_socketProvider.get(); +// } + +WorkerGlobalScope* WorkerThread::globalScope() +{ + return downcast<WorkerGlobalScope>(WorkerOrWorkletThread::globalScope()); +} + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerThread.h b/src/javascript/jsc/bindings/webcore/WorkerThread.h new file mode 100644 index 000000000..0f9bde2e2 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerThread.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +// #include "ContentSecurityPolicyResponseHeaders.h" +// #include "CrossOriginEmbedderPolicy.h" +#include "FetchRequestCredentials.h" +// #include "NotificationPermission.h" +#include "WorkerOrWorkletThread.h" +#include "WorkerRunLoop.h" +#include "WorkerType.h" +#include <JavaScriptCore/RuntimeFlags.h> +#include <memory> +// #include <pal/SessionID.h> +#include <wtf/URL.h> + +namespace WebCore { + +class NotificationClient; +class ScriptBuffer; +class SecurityOrigin; +class SocketProvider; +class WorkerGlobalScope; +class WorkerLoaderProxy; +class WorkerDebuggerProxy; +class WorkerReportingProxy; + +enum class WorkerThreadStartMode { + Normal, + WaitForInspector, +}; + +namespace IDBClient { +class IDBConnectionProxy; +} + +struct WorkerThreadStartupData; + +struct WorkerParameters { +public: + URL scriptURL; + String name; + String inspectorIdentifier; + String userAgent; + bool isOnline; + // ContentSecurityPolicyResponseHeaders contentSecurityPolicyResponseHeaders; + bool shouldBypassMainWorldContentSecurityPolicy; + // CrossOriginEmbedderPolicy crossOriginEmbedderPolicy; + MonotonicTime timeOrigin; + // ReferrerPolicy referrerPolicy; + WorkerType workerType; + // FetchRequestCredentials credentials; + Settings::Values settingsValues; + WorkerThreadMode workerThreadMode { WorkerThreadMode::CreateNewThread }; + // std::optional<PAL::SessionID> sessionID { std::nullopt }; + + WorkerParameters isolatedCopy() const; +}; + +class WorkerThread : public WorkerOrWorkletThread { +public: + virtual ~WorkerThread(); + + WorkerLoaderProxy& workerLoaderProxy() final { return m_workerLoaderProxy; } + WorkerDebuggerProxy* workerDebuggerProxy() const final { return &m_workerDebuggerProxy; } + WorkerReportingProxy& workerReportingProxy() const { return m_workerReportingProxy; } + + // Number of active worker threads. + WEBCORE_EXPORT static unsigned workerThreadCount(); + + // #if ENABLE(NOTIFICATIONS) + // NotificationClient* getNotificationClient() { return m_notificationClient; } + // void setNotificationClient(NotificationClient* client) { m_notificationClient = client; } + // #endif + + JSC::RuntimeFlags runtimeFlags() const { return m_runtimeFlags; } + bool isInStaticScriptEvaluation() const { return m_isInStaticScriptEvaluation; } + +protected: + WorkerThread(const WorkerParameters&, const ScriptBuffer& sourceCode, WorkerLoaderProxy&, WorkerDebuggerProxy&, WorkerReportingProxy&, WorkerThreadStartMode, const SecurityOrigin& topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*, JSC::RuntimeFlags); + + // Factory method for creating a new worker context for the thread. + virtual Ref<WorkerGlobalScope> createWorkerGlobalScope(const WorkerParameters& /*,Ref<SecurityOrigin>&&, Ref<SecurityOrigin>&& topOrigin*/) = 0; + + WorkerGlobalScope* globalScope(); + + // IDBClient::IDBConnectionProxy* idbConnectionProxy(); + // SocketProvider* socketProvider(); + +private: + virtual ASCIILiteral threadName() const = 0; + + virtual void finishedEvaluatingScript() {} + + // WorkerOrWorkletThread. + Ref<Thread> createThread() final; + RefPtr<WorkerOrWorkletGlobalScope> createGlobalScope() final; + void evaluateScriptIfNecessary(String& exceptionMessage) final; + bool shouldWaitForWebInspectorOnStartup() const final; + + WorkerLoaderProxy& m_workerLoaderProxy; + WorkerDebuggerProxy& m_workerDebuggerProxy; + WorkerReportingProxy& m_workerReportingProxy; + JSC::RuntimeFlags m_runtimeFlags; + + std::unique_ptr<WorkerThreadStartupData> m_startupData; + + // #if ENABLE(NOTIFICATIONS) + // NotificationClient* m_notificationClient { nullptr }; + // #endif + + // RefPtr<IDBClient::IDBConnectionProxy> m_idbConnectionProxy; + // RefPtr<SocketProvider> m_socketProvider; + bool m_isInStaticScriptEvaluation { false }; +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerThreadMode.h b/src/javascript/jsc/bindings/webcore/WorkerThreadMode.h new file mode 100644 index 000000000..6a8672f1d --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerThreadMode.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +enum class WorkerThreadMode : bool { UseMainThread, CreateNewThread }; + +} diff --git a/src/javascript/jsc/bindings/webcore/WorkerThreadType.h b/src/javascript/jsc/bindings/webcore/WorkerThreadType.h new file mode 100644 index 000000000..d25568b31 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerThreadType.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +enum class WorkerThreadType : uint8_t { + Main, + Worklet, + DedicatedWorker, + ServiceWorker, + SharedWorker +}; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerType.h b/src/javascript/jsc/bindings/webcore/WorkerType.h new file mode 100644 index 000000000..168ca8c1e --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerType.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +enum class WorkerType : bool { Classic, Module }; + +} // namespace WebCore diff --git a/src/javascript/jsc/bindings/webcore/WorkerType.idl b/src/javascript/jsc/bindings/webcore/WorkerType.idl new file mode 100644 index 000000000..ae9fffb56 --- /dev/null +++ b/src/javascript/jsc/bindings/webcore/WorkerType.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +enum WorkerType { + "classic", + "module" +}; |