diff options
Diffstat (limited to 'src')
17 files changed, 1566 insertions, 43 deletions
diff --git a/src/bun.js/bindings/ScriptExecutionContext.h b/src/bun.js/bindings/ScriptExecutionContext.h index 271c7e205..b32435240 100644 --- a/src/bun.js/bindings/ScriptExecutionContext.h +++ b/src/bun.js/bindings/ScriptExecutionContext.h @@ -3,6 +3,7 @@ #include "root.h" #include "ActiveDOMObject.h" #include "ContextDestructionObserver.h" +#include "BunBroadcastChannelRegistry.h" #include <wtf/CrossThreadTask.h> #include <wtf/Function.h> #include <wtf/HashSet.h> @@ -81,6 +82,7 @@ public: : m_vm(vm) , m_globalObject(globalObject) , m_identifier(0) + , m_broadcastChannelRegistry(BunBroadcastChannelRegistry::create()) { regenerateIdentifier(); } @@ -89,6 +91,7 @@ public: : m_vm(vm) , m_globalObject(globalObject) , m_identifier(identifier) + , m_broadcastChannelRegistry(BunBroadcastChannelRegistry::create()) { addToContextsMap(); } @@ -210,6 +213,8 @@ public: m_vm = &globalObject->vm(); } + BunBroadcastChannelRegistry& broadcastChannelRegistry() { return m_broadcastChannelRegistry; } + private: JSC::VM* m_vm = nullptr; JSC::JSGlobalObject* m_globalObject = nullptr; @@ -219,6 +224,7 @@ private: HashSet<MessagePort*> m_messagePorts; HashSet<ContextDestructionObserver*> m_destructionObservers; Vector<CompletionHandler<void()>> m_processMessageWithMessagePortsSoonHandlers; + Ref<BunBroadcastChannelRegistry> m_broadcastChannelRegistry; bool m_willProcessMessageWithMessagePortsSoon { false }; diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 3776adb2b..7c1181921 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -121,7 +121,7 @@ #include "JSWorker.h" #include "JSMessageChannel.h" #include "JSMessagePort.h" -// #include "JSBroadcastChannel.h" +#include "JSBroadcastChannel.h" #if ENABLE(REMOTE_INSPECTOR) #include "JavaScriptCore/RemoteInspectorServer.h" @@ -942,8 +942,8 @@ WEBCORE_GENERATED_CONSTRUCTOR_SETTER(JSMessageChannel); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(JSMessagePort); WEBCORE_GENERATED_CONSTRUCTOR_SETTER(JSMessagePort); -// WEBCORE_GENERATED_CONSTRUCTOR_GETTER(JSBroadcastChannel); -// WEBCORE_GENERATED_CONSTRUCTOR_SETTER(JSBroadcastChannel); +WEBCORE_GENERATED_CONSTRUCTOR_GETTER(JSBroadcastChannel); +WEBCORE_GENERATED_CONSTRUCTOR_SETTER(JSBroadcastChannel); JSC_DECLARE_CUSTOM_GETTER(JSEvent_getter); @@ -4253,7 +4253,7 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm) PUT_WEBCORE_GENERATED_CONSTRUCTOR("Worker"_s, JSWorker); PUT_WEBCORE_GENERATED_CONSTRUCTOR("MessageChannel"_s, JSMessageChannel); PUT_WEBCORE_GENERATED_CONSTRUCTOR("MessagePort"_s, JSMessagePort); - // PUT_WEBCORE_GENERATED_CONSTRUCTOR("BroadcastChannel"_s, JSBroadcastChannel); + PUT_WEBCORE_GENERATED_CONSTRUCTOR("BroadcastChannel"_s, JSBroadcastChannel); putDirectCustomAccessor(vm, builtinNames.TransformStreamPublicName(), CustomGetterSetter::create(vm, jsServiceWorkerGlobalScope_TransformStreamConstructor, nullptr), attributesForStructure(static_cast<unsigned>(JSC::PropertyAttribute::DontEnum))); putDirectCustomAccessor(vm, builtinNames.TransformStreamPrivateName(), CustomGetterSetter::create(vm, jsServiceWorkerGlobalScope_TransformStreamConstructor, nullptr), attributesForStructure(static_cast<unsigned>(JSC::PropertyAttribute::DontEnum))); diff --git a/src/bun.js/bindings/webcore/BroadcastChannel.cpp b/src/bun.js/bindings/webcore/BroadcastChannel.cpp new file mode 100644 index 000000000..74ff150e6 --- /dev/null +++ b/src/bun.js/bindings/webcore/BroadcastChannel.cpp @@ -0,0 +1,330 @@ +/* + * 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 "BroadcastChannel.h" + +#include "BunClientData.h" +#include "BroadcastChannelRegistry.h" +#include "EventNames.h" +#include "EventTarget.h" +#include "MessageEvent.h" +// #include "Page.h" +// #include "PartitionedSecurityOrigin.h" +// #include "SecurityOrigin.h" +#include "SerializedScriptValue.h" +// #include "WorkerGlobalScope.h" +#include "BunWorkerGlobalScope.h" +// #include "WorkerLoaderProxy.h" +// #include "WorkerThread.h" +#include <wtf/CallbackAggregator.h> +#include <wtf/HashMap.h> +#include <wtf/IsoMallocInlines.h> +#include <wtf/MainThread.h> +#include <wtf/Scope.h> + +extern "C" void Bun__eventLoop__incrementRefConcurrently(void* bunVM, int delta); + +namespace WebCore { + +WTF_MAKE_ISO_ALLOCATED_IMPL(BroadcastChannel); + +static Lock allBroadcastChannelsLock; +static HashMap<BroadcastChannelIdentifier, BroadcastChannel*>& allBroadcastChannels() WTF_REQUIRES_LOCK(allBroadcastChannelsLock) +{ + static NeverDestroyed<HashMap<BroadcastChannelIdentifier, BroadcastChannel*>> map; + return map; +} + +static Lock channelToContextIdentifierLock; +static HashMap<BroadcastChannelIdentifier, ScriptExecutionContextIdentifier>& channelToContextIdentifier() +{ + ASSERT(isMainThread()); + static NeverDestroyed<HashMap<BroadcastChannelIdentifier, ScriptExecutionContextIdentifier>> map; + return map; +} + +// static PartitionedSecurityOrigin partitionedSecurityOriginFromContext(ScriptExecutionContext& context) +// { +// Ref securityOrigin { *context.securityOrigin() }; +// Ref topOrigin { context.settingsValues().broadcastChannelOriginPartitioningEnabled ? context.topOrigin() : securityOrigin.get() }; +// return { WTFMove(topOrigin), WTFMove(securityOrigin) }; +// } + +class BroadcastChannel::MainThreadBridge : public ThreadSafeRefCounted<MainThreadBridge, WTF::DestructionThread::Main> { +public: + static Ref<MainThreadBridge> create(BroadcastChannel& channel, const String& name, ScriptExecutionContext& context) + { + return adoptRef(*new MainThreadBridge(channel, name, context)); + } + + void registerChannel(ScriptExecutionContext&); + void unregisterChannel(); + void postMessage(Ref<SerializedScriptValue>&&); + + String name() const { return m_name.isolatedCopy(); } + BroadcastChannelIdentifier identifier() const { return m_identifier; } + ScriptExecutionContextIdentifier contextId() const { return m_contextId; } + +private: + MainThreadBridge(BroadcastChannel&, const String& name, ScriptExecutionContext&); + + void ensureOnMainThread(Function<void(void*)>&&); + + // WeakPtr<BroadcastChannel, WeakPtrImplWithEventTargetData> m_broadcastChannel; + WeakPtr<BroadcastChannel> m_broadcastChannel; + const BroadcastChannelIdentifier m_identifier; + const String m_name; // Main thread only. + ScriptExecutionContextIdentifier m_contextId; + // PartitionedSecurityOrigin m_origin; // Main thread only. +}; + +BroadcastChannel::MainThreadBridge::MainThreadBridge(BroadcastChannel& channel, const String& name, ScriptExecutionContext& context) + : m_broadcastChannel(channel) + , m_identifier(BroadcastChannelIdentifier::generate()) + , m_name(name.isolatedCopy()) + , m_contextId(context.identifier()) +// , m_origin(partitionedSecurityOriginFromContext(*channel.scriptExecutionContext()).isolatedCopy()) +{ +} + +void BroadcastChannel::MainThreadBridge::ensureOnMainThread(Function<void(void*)>&& task) +{ + ASSERT(m_broadcastChannel); + if (!m_broadcastChannel) + return; + + auto* context = m_broadcastChannel->scriptExecutionContext(); + if (!context) + return; + ASSERT(context->isContextThread()); + + Ref protectedThis { *this }; + + ScriptExecutionContext::ensureOnMainThread([protectedThis = WTFMove(protectedThis), task = WTFMove(task)](auto& context) { + task(nullptr); + }); +} + +void BroadcastChannel::MainThreadBridge::registerChannel(ScriptExecutionContext& context) +{ + Ref protectedThis { *this }; + + ScriptExecutionContext::ensureOnMainThread([protectedThis = WTFMove(protectedThis), contextId = context.identifier()](auto& context) mutable { + context.broadcastChannelRegistry().registerChannel(protectedThis->m_name, protectedThis->m_identifier); + channelToContextIdentifier().add(protectedThis->m_identifier, contextId); + }); +} + +void BroadcastChannel::MainThreadBridge::unregisterChannel() +{ + Ref protectedThis { *this }; + + ScriptExecutionContext::ensureOnMainThread([protectedThis = WTFMove(protectedThis)](auto& context) { + context.broadcastChannelRegistry().unregisterChannel(protectedThis->m_name, protectedThis->m_identifier); + channelToContextIdentifier().remove(protectedThis->m_identifier); + }); +} + +void BroadcastChannel::MainThreadBridge::postMessage(Ref<SerializedScriptValue>&& message) +{ + Ref protectedThis { *this }; + + ScriptExecutionContext::ensureOnMainThread([protectedThis = WTFMove(protectedThis), message = WTFMove(message)](auto& context) mutable { + context.broadcastChannelRegistry().postMessage(protectedThis->m_name, protectedThis->m_identifier, WTFMove(message)); + }); +} + +BroadcastChannel::BroadcastChannel(ScriptExecutionContext& context, const String& name) + // : ActiveDOMObject(&context) + : ContextDestructionObserver(&context) + , m_mainThreadBridge(MainThreadBridge::create(*this, name, context)) + , m_contextId(context.identifier()) +{ + { + Locker locker { allBroadcastChannelsLock }; + allBroadcastChannels().add(m_mainThreadBridge->identifier(), this); + } + m_mainThreadBridge->registerChannel(context); + jsRef(context.jsGlobalObject()); +} + +BroadcastChannel::~BroadcastChannel() +{ + close(); + { + Locker locker { allBroadcastChannelsLock }; + allBroadcastChannels().remove(m_mainThreadBridge->identifier()); + } +} + +BroadcastChannelIdentifier BroadcastChannel::identifier() const +{ + return m_mainThreadBridge->identifier(); +} + +String BroadcastChannel::name() const +{ + return m_mainThreadBridge->name(); +} + +ScriptExecutionContextIdentifier BroadcastChannel::contextIdForBroadcastChannelId(BroadcastChannelIdentifier identifier) +{ + Locker locker { channelToContextIdentifierLock }; + return channelToContextIdentifier().get(identifier); +} + +ScriptExecutionContext* BroadcastChannel::scriptExecutionContext() const +{ + return ScriptExecutionContext::getScriptExecutionContext(m_mainThreadBridge->contextId()); +} + +ExceptionOr<void> BroadcastChannel::postMessage(JSC::JSGlobalObject& globalObject, JSC::JSValue message) +{ + if (!isEligibleForMessaging()) + return {}; + + if (m_isClosed) + return Exception { InvalidStateError, "This BroadcastChannel is closed"_s }; + + Vector<RefPtr<MessagePort>> ports; + auto messageData = SerializedScriptValue::create(globalObject, message, {}, ports, SerializationForStorage::No, SerializationContext::WorkerPostMessage); + if (messageData.hasException()) + return messageData.releaseException(); + ASSERT(ports.isEmpty()); + + m_mainThreadBridge->postMessage(messageData.releaseReturnValue()); + return {}; +} + +void BroadcastChannel::close() +{ + if (m_isClosed) + return; + + m_isClosed = true; + m_mainThreadBridge->unregisterChannel(); +} + +void BroadcastChannel::dispatchMessageTo(BroadcastChannelIdentifier channelIdentifier, Ref<SerializedScriptValue>&& message) +{ + ASSERT(isMainThread()); + + auto contextIdentifier = channelToContextIdentifier().get(channelIdentifier); + if (!contextIdentifier) + return; + + ScriptExecutionContext::ensureOnContextThread(contextIdentifier, [channelIdentifier, message = WTFMove(message)](auto&) mutable { + RefPtr<BroadcastChannel> channel; + { + Locker locker { allBroadcastChannelsLock }; + channel = allBroadcastChannels().get(channelIdentifier); + } + if (channel) + channel->dispatchMessage(WTFMove(message)); + }); +} + +void BroadcastChannel::dispatchMessage(Ref<SerializedScriptValue>&& message) +{ + if (!isEligibleForMessaging()) + return; + + if (m_isClosed) + return; + + ScriptExecutionContext::postTaskTo(contextIdForBroadcastChannelId(m_mainThreadBridge->identifier()), [this, message = WTFMove(message)](ScriptExecutionContext& context) mutable { + if (m_isClosed) + return; + + auto* globalObject = context.jsGlobalObject(); + if (!globalObject) + return; + + auto& vm = globalObject->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + Vector<RefPtr<MessagePort>> dummyPorts; + auto event = MessageEvent::create(*globalObject, WTFMove(message), {}, {}, std::nullopt, WTFMove(dummyPorts)); + if (UNLIKELY(scope.exception())) { + // Currently, we assume that the only way we can get here is if we have a termination. + RELEASE_ASSERT(vm.hasPendingTerminationException()); + return; + } + + dispatchEvent(event.event); + }); +} + +// const char* BroadcastChannel::activeDOMObjectName() const +// { +// return "BroadcastChannel"; +// } + +void BroadcastChannel::eventListenersDidChange() +{ + m_hasRelevantEventListener = hasEventListeners(eventNames().messageEvent); +} + +// bool BroadcastChannel::virtualHasPendingActivity() const +// { +// return !m_isClosed && m_hasRelevantEventListener; +// } + +bool BroadcastChannel::hasPendingActivity() const +{ + return !m_isClosed && m_hasRelevantEventListener; +} + +// https://html.spec.whatwg.org/#eligible-for-messaging +bool BroadcastChannel::isEligibleForMessaging() const +{ + auto* context = scriptExecutionContext(); + if (!context) + return false; + + // if (auto document = dynamicDowncast<Document>(*context)) + // return document->isFullyActive(); + + return true; + // return !downcast<GlobalScope>(*context).isClosing(); +} + +void BroadcastChannel::jsRef(JSGlobalObject* lexicalGlobalObject) +{ + if (!m_hasRef) { + m_hasRef = true; + Bun__eventLoop__incrementRefConcurrently(WebCore::clientData(lexicalGlobalObject->vm())->bunVM, 1); + } +} + +void BroadcastChannel::jsUnref(JSGlobalObject* lexicalGlobalObject) +{ + if (m_hasRef) { + m_hasRef = false; + Bun__eventLoop__incrementRefConcurrently(WebCore::clientData(lexicalGlobalObject->vm())->bunVM, -1); + } +} + +} // namespace WebCore diff --git a/src/bun.js/bindings/webcore/BroadcastChannel.h b/src/bun.js/bindings/webcore/BroadcastChannel.h new file mode 100644 index 000000000..764f3db93 --- /dev/null +++ b/src/bun.js/bindings/webcore/BroadcastChannel.h @@ -0,0 +1,109 @@ +/* + * 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 "ActiveDOMObject.h" +#include "ContextDestructionObserver.h" +#include "BroadcastChannelIdentifier.h" +// #include "ClientOrigin.h" +#include "EventTarget.h" +#include "ExceptionOr.h" +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> + +namespace JSC { +class JSGlobalObject; +class JSValue; +} + +namespace WebCore { + +class SerializedScriptValue; + +class BroadcastChannel : public RefCounted<BroadcastChannel>, public EventTarget /*, public ActiveDOMObject*/, public ContextDestructionObserver { + WTF_MAKE_ISO_ALLOCATED(BroadcastChannel); + +public: + static Ref<BroadcastChannel> create(ScriptExecutionContext& context, const String& name) + { + auto channel = adoptRef(*new BroadcastChannel(context, name)); + // channel->suspendIfNeeded(); + return channel; + } + ~BroadcastChannel(); + + using RefCounted<BroadcastChannel>::ref; + using RefCounted<BroadcastChannel>::deref; + + BroadcastChannelIdentifier identifier() const; + String name() const; + + ExceptionOr<void> postMessage(JSC::JSGlobalObject&, JSC::JSValue message); + void close(); + + WEBCORE_EXPORT static void dispatchMessageTo(BroadcastChannelIdentifier, Ref<SerializedScriptValue>&&); + + static ScriptExecutionContextIdentifier contextIdForBroadcastChannelId(BroadcastChannelIdentifier); + + bool hasPendingActivity() const; + + void jsRef(JSGlobalObject*); + void jsUnref(JSGlobalObject*); + +private: + BroadcastChannel(ScriptExecutionContext&, const String& name); + + void dispatchMessage(Ref<SerializedScriptValue>&&); + + bool isEligibleForMessaging() const; + + // EventTarget + EventTargetInterface eventTargetInterface() const final { return BroadcastChannelEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const; + void refEventTarget() final { RefCounted<BroadcastChannel>::ref(); } + void derefEventTarget() final { RefCounted<BroadcastChannel>::deref(); } + void eventListenersDidChange() final; + + EventTargetData* eventTargetData() final { return &m_eventTargetData; } + EventTargetData* eventTargetDataConcurrently() final { return &m_eventTargetData; } + EventTargetData& ensureEventTargetData() final { return m_eventTargetData; } + + EventTargetData m_eventTargetData; + + // ActiveDOMObject + // const char* activeDOMObjectName() const final; + // bool virtualHasPendingActivity() const final; + // void stop() final { close(); } + + class MainThreadBridge; + Ref<MainThreadBridge> m_mainThreadBridge; + bool m_isClosed { false }; + bool m_hasRelevantEventListener { false }; + bool m_hasRef { false }; + ScriptExecutionContextIdentifier m_contextId; +}; + +} // namespace WebCore diff --git a/src/bun.js/bindings/webcore/BroadcastChannelIdentifier.h b/src/bun.js/bindings/webcore/BroadcastChannelIdentifier.h new file mode 100644 index 000000000..acddb06d5 --- /dev/null +++ b/src/bun.js/bindings/webcore/BroadcastChannelIdentifier.h @@ -0,0 +1,35 @@ +/* + * 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 <wtf/ObjectIdentifier.h> + +namespace WebCore { + +enum BroadcastChannelIdentifierType {}; +using BroadcastChannelIdentifier = AtomicObjectIdentifier<BroadcastChannelIdentifierType>; + +} // namespace WebCore diff --git a/src/bun.js/bindings/webcore/BroadcastChannelRegistry.h b/src/bun.js/bindings/webcore/BroadcastChannelRegistry.h new file mode 100644 index 000000000..70abf842a --- /dev/null +++ b/src/bun.js/bindings/webcore/BroadcastChannelRegistry.h @@ -0,0 +1,45 @@ +/* + * 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 "BroadcastChannelIdentifier.h" +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +struct PartitionedSecurityOrigin; +class SerializedScriptValue; + +class BroadcastChannelRegistry : public RefCounted<BroadcastChannelRegistry> { +public: + virtual ~BroadcastChannelRegistry() {} + virtual void registerChannel(const String& name, BroadcastChannelIdentifier) = 0; + virtual void unregisterChannel(const String& name, BroadcastChannelIdentifier) = 0; + virtual void postMessage(const String& name, BroadcastChannelIdentifier source, Ref<SerializedScriptValue>&&) = 0; +}; + +} // namespace WebCore diff --git a/src/bun.js/bindings/webcore/BunBroadcastChannelRegistry.cpp b/src/bun.js/bindings/webcore/BunBroadcastChannelRegistry.cpp new file mode 100644 index 000000000..258d29717 --- /dev/null +++ b/src/bun.js/bindings/webcore/BunBroadcastChannelRegistry.cpp @@ -0,0 +1,52 @@ +#include "config.h" + +#include "BunBroadcastChannelRegistry.h" +#include "webcore/BroadcastChannel.h" +#include "webcore/MessageWithMessagePorts.h" +#include <wtf/CallbackAggregator.h> + +namespace WebCore { + +void BunBroadcastChannelRegistry::registerChannel(const String& name, BroadcastChannelIdentifier identifier) +{ + auto& channels = m_channelsForName.ensure(name, [] { return Vector<BroadcastChannelIdentifier> {}; }).iterator->value; + channels.append(identifier); +} + +void BunBroadcastChannelRegistry::unregisterChannel(const String& name, BroadcastChannelIdentifier identifier) +{ + auto channels = m_channelsForName.find(name); + if (channels == m_channelsForName.end()) + return; + + auto& channelIds = channels->value; + channelIds.removeFirst(identifier); +} + +void BunBroadcastChannelRegistry::postMessage(const String& name, BroadcastChannelIdentifier source, Ref<SerializedScriptValue>&& message) +{ + postMessageLocally(name, source, message.copyRef()); +} + +void BunBroadcastChannelRegistry::postMessageLocally(const String& name, BroadcastChannelIdentifier sourceInProcess, Ref<SerializedScriptValue>&& message) +{ + auto channels = m_channelsForName.find(name); + if (channels == m_channelsForName.end()) + return; + + auto& channelIds = channels->value; + for (auto& channelId : channelIds) { + if (channelId == sourceInProcess) + continue; + + BroadcastChannel::dispatchMessageTo(channelId, message.copyRef()); + } +} + +void BunBroadcastChannelRegistry::postMessageToRemote(const String& name, MessageWithMessagePorts&& message) +{ + // auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler)); + // PartitionedSecurityOrigin origin { clientOrigin.topOrigin.securityOrigin(), clientOrigin.clientOrigin.securityOrigin() }; + // postMessageLocally(origin, name, std::nullopt, *message.message, callbackAggregator.copyRef()); +} +} diff --git a/src/bun.js/bindings/webcore/BunBroadcastChannelRegistry.h b/src/bun.js/bindings/webcore/BunBroadcastChannelRegistry.h new file mode 100644 index 000000000..5576b732e --- /dev/null +++ b/src/bun.js/bindings/webcore/BunBroadcastChannelRegistry.h @@ -0,0 +1,33 @@ +#pragma once + +#include "BroadcastChannelRegistry.h" +#include "wtf/CallbackAggregator.h" +#include "wtf/Vector.h" +#include "wtf/HashMap.h" + +namespace WebCore { + +struct MessageWithMessagePorts; + +class BunBroadcastChannelRegistry final : public BroadcastChannelRegistry { +public: + BunBroadcastChannelRegistry() = default; + static Ref<BunBroadcastChannelRegistry> create() + { + return adoptRef(*new BunBroadcastChannelRegistry); + } + + void registerChannel(const String& name, BroadcastChannelIdentifier) final; + void unregisterChannel(const String& name, BroadcastChannelIdentifier) final; + void postMessage(const String& name, BroadcastChannelIdentifier source, Ref<SerializedScriptValue>&&) final; + + // void didReceivedMessage(IPC::Connection&, IPC::Decoder&); + + HashMap<String, Vector<BroadcastChannelIdentifier>> m_channelsForName; + +private: + void postMessageToRemote(const String& name, MessageWithMessagePorts&&); + void postMessageLocally(const String& name, BroadcastChannelIdentifier sourceInProgress, Ref<SerializedScriptValue>&&); +}; + +} diff --git a/src/bun.js/bindings/webcore/EventTargetFactory.cpp b/src/bun.js/bindings/webcore/EventTargetFactory.cpp index 867d007be..809cceda9 100644 --- a/src/bun.js/bindings/webcore/EventTargetFactory.cpp +++ b/src/bun.js/bindings/webcore/EventTargetFactory.cpp @@ -57,8 +57,8 @@ JSC::JSValue toJS(JSC::JSGlobalObject* state, JSDOMGlobalObject* globalObject, E // case BaseAudioContextEventTargetInterfaceType: // return toJS(state, globalObject, static_cast<BaseAudioContext&>(impl)); // #endif - // case BroadcastChannelEventTargetInterfaceType: - // return toJS(state, globalObject, static_cast<BroadcastChannel&>(impl)); + case BroadcastChannelEventTargetInterfaceType: + return toJS(state, globalObject, static_cast<BroadcastChannel&>(impl)); // case ClipboardEventTargetInterfaceType: // return toJS(state, globalObject, static_cast<Clipboard&>(impl)); // case DOMApplicationCacheEventTargetInterfaceType: diff --git a/src/bun.js/bindings/webcore/EventTargetHeaders.h b/src/bun.js/bindings/webcore/EventTargetHeaders.h index 89c1f2393..6c81e59fd 100644 --- a/src/bun.js/bindings/webcore/EventTargetHeaders.h +++ b/src/bun.js/bindings/webcore/EventTargetHeaders.h @@ -46,7 +46,7 @@ // #include "BaseAudioContext.h" // #include "JSBaseAudioContext.h" // #endif -// #include "BroadcastChannel.h" +#include "BroadcastChannel.h" // #include "Clipboard.h" // #include "DOMApplicationCache.h" // #include "DOMWindow.h" @@ -59,7 +59,7 @@ // #include "IDBOpenDBRequest.h" // #include "IDBRequest.h" // #include "IDBTransaction.h" -// #include "JSBroadcastChannel.h" +#include "JSBroadcastChannel.h" // #include "JSClipboard.h" // #include "JSDOMApplicationCache.h" // #include "JSDOMWindow.h" diff --git a/src/bun.js/bindings/webcore/JSBroadcastChannel.cpp b/src/bun.js/bindings/webcore/JSBroadcastChannel.cpp new file mode 100644 index 000000000..694044cca --- /dev/null +++ b/src/bun.js/bindings/webcore/JSBroadcastChannel.cpp @@ -0,0 +1,426 @@ +/* + 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 "JSBroadcastChannel.h" + +#include "ActiveDOMObject.h" +#include "EventNames.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 "JSDOMConvertInterface.h" +#include "JSDOMConvertStrings.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMOperation.h" +#include "JSDOMWrapperCache.h" +#include "JSEventListener.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; + +// Functions + +static JSC_DECLARE_HOST_FUNCTION(jsBroadcastChannelPrototypeFunction_postMessage); +static JSC_DECLARE_HOST_FUNCTION(jsBroadcastChannelPrototypeFunction_close); +static JSC_DECLARE_HOST_FUNCTION(jsBroadcastChannelPrototypeFunction_ref); +static JSC_DECLARE_HOST_FUNCTION(jsBroadcastChannelPrototypeFunction_unref); + +// Attributes + +static JSC_DECLARE_CUSTOM_GETTER(jsBroadcastChannelConstructor); +static JSC_DECLARE_CUSTOM_GETTER(jsBroadcastChannel_name); +static JSC_DECLARE_CUSTOM_GETTER(jsBroadcastChannel_onmessage); +static JSC_DECLARE_CUSTOM_SETTER(setJSBroadcastChannel_onmessage); +static JSC_DECLARE_CUSTOM_GETTER(jsBroadcastChannel_onmessageerror); +static JSC_DECLARE_CUSTOM_SETTER(setJSBroadcastChannel_onmessageerror); + +class JSBroadcastChannelPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSBroadcastChannelPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSBroadcastChannelPrototype* ptr = new (NotNull, JSC::allocateCell<JSBroadcastChannelPrototype>(vm)) JSBroadcastChannelPrototype(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(JSBroadcastChannelPrototype, 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: + JSBroadcastChannelPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSBroadcastChannelPrototype, JSBroadcastChannelPrototype::Base); + +using JSBroadcastChannelDOMConstructor = JSDOMConstructor<JSBroadcastChannel>; + +template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSBroadcastChannelDOMConstructor::construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame) +{ + VM& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* castedThis = jsCast<JSBroadcastChannelDOMConstructor*>(callFrame->jsCallee()); + ASSERT(castedThis); + if (UNLIKELY(callFrame->argumentCount() < 1)) + return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); + auto* context = castedThis->scriptExecutionContext(); + if (UNLIKELY(!context)) + return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "BroadcastChannel"); + EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); + auto name = convert<IDLDOMString>(*lexicalGlobalObject, argument0.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + auto object = BroadcastChannel::create(*context, WTFMove(name)); + if constexpr (IsExceptionOr<decltype(object)>) + RETURN_IF_EXCEPTION(throwScope, {}); + static_assert(TypeOrExceptionOrUnderlyingType<decltype(object)>::isRef); + auto jsValue = toJSNewlyCreated<IDLInterface<BroadcastChannel>>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, WTFMove(object)); + if constexpr (IsExceptionOr<decltype(object)>) + RETURN_IF_EXCEPTION(throwScope, {}); + setSubclassStructureIfNeeded<BroadcastChannel>(lexicalGlobalObject, callFrame, asObject(jsValue)); + RETURN_IF_EXCEPTION(throwScope, {}); + return JSValue::encode(jsValue); +} +JSC_ANNOTATE_HOST_FUNCTION(JSBroadcastChannelDOMConstructorConstruct, JSBroadcastChannelDOMConstructor::construct); + +template<> const ClassInfo JSBroadcastChannelDOMConstructor::s_info = { "BroadcastChannel"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBroadcastChannelDOMConstructor) }; + +template<> JSValue JSBroadcastChannelDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + return JSEventTarget::getConstructor(vm, &globalObject); +} + +template<> void JSBroadcastChannelDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(1), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "BroadcastChannel"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSBroadcastChannel::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +/* Hash table for prototype */ + +static const HashTableValue JSBroadcastChannelPrototypeTableValues[] = { + { "constructor"_s, static_cast<unsigned>(PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsBroadcastChannelConstructor, 0 } }, + { "name"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute, NoIntrinsic, { HashTableValue::GetterSetterType, jsBroadcastChannel_name, 0 } }, + { "onmessage"_s, JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute, NoIntrinsic, { HashTableValue::GetterSetterType, jsBroadcastChannel_onmessage, setJSBroadcastChannel_onmessage } }, + { "onmessageerror"_s, JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute, NoIntrinsic, { HashTableValue::GetterSetterType, jsBroadcastChannel_onmessageerror, setJSBroadcastChannel_onmessageerror } }, + { "postMessage"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBroadcastChannelPrototypeFunction_postMessage, 1 } }, + { "close"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBroadcastChannelPrototypeFunction_close, 0 } }, + { "ref"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBroadcastChannelPrototypeFunction_ref, 0 } }, + { "unref"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBroadcastChannelPrototypeFunction_unref, 0 } }, +}; + +const ClassInfo JSBroadcastChannelPrototype::s_info = { "BroadcastChannel"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBroadcastChannelPrototype) }; + +void JSBroadcastChannelPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSBroadcastChannel::info(), JSBroadcastChannelPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSBroadcastChannel::s_info = { "BroadcastChannel"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBroadcastChannel) }; + +JSBroadcastChannel::JSBroadcastChannel(Structure* structure, JSDOMGlobalObject& globalObject, Ref<BroadcastChannel>&& impl) + : JSEventTarget(structure, globalObject, WTFMove(impl)) +{ +} + +// static_assert(std::is_base_of<ActiveDOMObject, BroadcastChannel>::value, "Interface is marked as [ActiveDOMObject] but implementation class does not subclass ActiveDOMObject."); + +JSObject* JSBroadcastChannel::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + auto* structure = JSBroadcastChannelPrototype::createStructure(vm, &globalObject, JSEventTarget::prototype(vm, globalObject)); + structure->setMayBePrototype(true); + return JSBroadcastChannelPrototype::create(vm, &globalObject, structure); +} + +JSObject* JSBroadcastChannel::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype<JSBroadcastChannel>(vm, globalObject); +} + +JSValue JSBroadcastChannel::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor<JSBroadcastChannelDOMConstructor, DOMConstructorID::BroadcastChannel>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject)); +} + +JSC_DEFINE_CUSTOM_GETTER(jsBroadcastChannelConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + VM& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast<JSBroadcastChannelPrototype*>(JSValue::decode(thisValue)); + if (UNLIKELY(!prototype)) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSBroadcastChannel::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject())); +} + +static inline JSValue jsBroadcastChannel_nameGetter(JSGlobalObject& lexicalGlobalObject, JSBroadcastChannel& 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.name()))); +} + +JSC_DEFINE_CUSTOM_GETTER(jsBroadcastChannel_name, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSBroadcastChannel>::get<jsBroadcastChannel_nameGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline JSValue jsBroadcastChannel_onmessageGetter(JSGlobalObject& lexicalGlobalObject, JSBroadcastChannel& thisObject) +{ + UNUSED_PARAM(lexicalGlobalObject); + return eventHandlerAttribute(thisObject.wrapped(), eventNames().messageEvent, worldForDOMObject(thisObject)); +} + +JSC_DEFINE_CUSTOM_GETTER(jsBroadcastChannel_onmessage, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSBroadcastChannel>::get<jsBroadcastChannel_onmessageGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline bool setJSBroadcastChannel_onmessageSetter(JSGlobalObject& lexicalGlobalObject, JSBroadcastChannel& thisObject, JSValue value) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + UNUSED_PARAM(vm); + setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().messageEvent, value, thisObject); + vm.writeBarrier(&thisObject, value); + ensureStillAliveHere(value); + + return true; +} + +JSC_DEFINE_CUSTOM_SETTER(setJSBroadcastChannel_onmessage, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) +{ + return IDLAttribute<JSBroadcastChannel>::set<setJSBroadcastChannel_onmessageSetter>(*lexicalGlobalObject, thisValue, encodedValue, attributeName); +} + +static inline JSValue jsBroadcastChannel_onmessageerrorGetter(JSGlobalObject& lexicalGlobalObject, JSBroadcastChannel& thisObject) +{ + UNUSED_PARAM(lexicalGlobalObject); + return eventHandlerAttribute(thisObject.wrapped(), eventNames().messageerrorEvent, worldForDOMObject(thisObject)); +} + +JSC_DEFINE_CUSTOM_GETTER(jsBroadcastChannel_onmessageerror, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + return IDLAttribute<JSBroadcastChannel>::get<jsBroadcastChannel_onmessageerrorGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName); +} + +static inline bool setJSBroadcastChannel_onmessageerrorSetter(JSGlobalObject& lexicalGlobalObject, JSBroadcastChannel& thisObject, JSValue value) +{ + auto& vm = JSC::getVM(&lexicalGlobalObject); + UNUSED_PARAM(vm); + setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().messageerrorEvent, value, thisObject); + vm.writeBarrier(&thisObject, value); + ensureStillAliveHere(value); + + return true; +} + +JSC_DEFINE_CUSTOM_SETTER(setJSBroadcastChannel_onmessageerror, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) +{ + return IDLAttribute<JSBroadcastChannel>::set<setJSBroadcastChannel_onmessageerrorSetter>(*lexicalGlobalObject, thisValue, encodedValue, attributeName); +} + +static inline JSC::EncodedJSValue jsBroadcastChannelPrototypeFunction_postMessageBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBroadcastChannel>::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 message = convert<IDLAny>(*lexicalGlobalObject, argument0.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)); }))); +} + +JSC_DEFINE_HOST_FUNCTION(jsBroadcastChannelPrototypeFunction_postMessage, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSBroadcastChannel>::call<jsBroadcastChannelPrototypeFunction_postMessageBody>(*lexicalGlobalObject, *callFrame, "postMessage"); +} + +static inline JSC::EncodedJSValue jsBroadcastChannelPrototypeFunction_closeBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBroadcastChannel>::ClassParameter castedThis) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(throwScope); + UNUSED_PARAM(callFrame); + auto& impl = castedThis->wrapped(); + impl.jsUnref(lexicalGlobalObject); + RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.close(); }))); +} + +JSC_DEFINE_HOST_FUNCTION(jsBroadcastChannelPrototypeFunction_close, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSBroadcastChannel>::call<jsBroadcastChannelPrototypeFunction_closeBody>(*lexicalGlobalObject, *callFrame, "close"); +} + +static inline JSC::EncodedJSValue jsBroadcastChannelPrototypeFunction_refBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBroadcastChannel>::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.jsRef(lexicalGlobalObject); }))); +} + +JSC_DEFINE_HOST_FUNCTION(jsBroadcastChannelPrototypeFunction_ref, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSBroadcastChannel>::call<jsBroadcastChannelPrototypeFunction_refBody>(*lexicalGlobalObject, *callFrame, "ref"); +} + +static inline JSC::EncodedJSValue jsBroadcastChannelPrototypeFunction_unrefBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBroadcastChannel>::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.jsUnref(lexicalGlobalObject); }))); +} + +JSC_DEFINE_HOST_FUNCTION(jsBroadcastChannelPrototypeFunction_unref, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSBroadcastChannel>::call<jsBroadcastChannelPrototypeFunction_unrefBody>(*lexicalGlobalObject, *callFrame, "unref"); +} + +JSC::GCClient::IsoSubspace* JSBroadcastChannel::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl<JSBroadcastChannel, UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForBroadcastChannel.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForBroadcastChannel = std::forward<decltype(space)>(space); }, + [](auto& spaces) { return spaces.m_subspaceForBroadcastChannel.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForBroadcastChannel = std::forward<decltype(space)>(space); }); +} + +void JSBroadcastChannel::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast<JSBroadcastChannel*>(cell); + analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); + if (thisObject->scriptExecutionContext()) + analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); + Base::analyzeHeap(cell, analyzer); +} + +bool JSBroadcastChannelOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, AbstractSlotVisitor& visitor, const char** reason) +{ + auto* jsBroadcastChannel = jsCast<JSBroadcastChannel*>(handle.slot()->asCell()); + auto& wrapped = jsBroadcastChannel->wrapped(); + if (/*!wrapped.isContextStopped() && */ wrapped.hasPendingActivity()) { + if (UNLIKELY(reason)) + *reason = "ActiveDOMObject with pending activity"; + return true; + } + UNUSED_PARAM(visitor); + UNUSED_PARAM(reason); + return false; +} + +void JSBroadcastChannelOwner::finalize(JSC::Handle<JSC::Unknown> handle, void* context) +{ + auto* jsBroadcastChannel = static_cast<JSBroadcastChannel*>(handle.slot()->asCell()); + auto& world = *static_cast<DOMWrapperWorld*>(context); + uncacheWrapper(world, &jsBroadcastChannel->wrapped(), jsBroadcastChannel); +} + +#if ENABLE(BINDING_INTEGRITY) +#if PLATFORM(WIN) +#pragma warning(disable : 4483) +extern "C" { +extern void (*const __identifier("??_7BroadcastChannel@WebCore@@6B@")[])(); +} +#else +extern "C" { +extern void* _ZTVN7WebCore16BroadcastChannelE[]; +} +#endif +#endif + +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref<BroadcastChannel>&& impl) +{ + + if constexpr (std::is_polymorphic_v<BroadcastChannel>) { +#if ENABLE(BINDING_INTEGRITY) + const void* actualVTablePointer = getVTablePointer(impl.ptr()); +#if PLATFORM(WIN) + void* expectedVTablePointer = __identifier("??_7BroadcastChannel@WebCore@@6B@"); +#else + void* expectedVTablePointer = &_ZTVN7WebCore16BroadcastChannelE[2]; +#endif + + // If you hit this assertion you either have a use after free bug, or + // BroadcastChannel has subclasses. If BroadcastChannel has subclasses that get passed + // to toJS() we currently require BroadcastChannel you to opt out of binding hardening + // by adding the SkipVTableValidation attribute to the interface IDL definition + RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); +#endif + } + return createWrapper<BroadcastChannel>(globalObject, WTFMove(impl)); +} + +JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, BroadcastChannel& impl) +{ + return wrap(lexicalGlobalObject, globalObject, impl); +} + +BroadcastChannel* JSBroadcastChannel::toWrapped(JSC::VM&, JSC::JSValue value) +{ + if (auto* wrapper = jsDynamicCast<JSBroadcastChannel*>(value)) + return &wrapper->wrapped(); + return nullptr; +} + +} diff --git a/src/bun.js/bindings/webcore/JSBroadcastChannel.h b/src/bun.js/bindings/webcore/JSBroadcastChannel.h new file mode 100644 index 000000000..9154d4991 --- /dev/null +++ b/src/bun.js/bindings/webcore/JSBroadcastChannel.h @@ -0,0 +1,99 @@ +/* + 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 "BroadcastChannel.h" +#include "JSDOMWrapper.h" +#include "JSEventTarget.h" +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +class JSBroadcastChannel : public JSEventTarget { +public: + using Base = JSEventTarget; + using DOMWrapped = BroadcastChannel; + static JSBroadcastChannel* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<BroadcastChannel>&& impl) + { + JSBroadcastChannel* ptr = new (NotNull, JSC::allocateCell<JSBroadcastChannel>(globalObject->vm())) JSBroadcastChannel(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 BroadcastChannel* 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); + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + BroadcastChannel& wrapped() const + { + return static_cast<BroadcastChannel&>(Base::wrapped()); + } + +protected: + JSBroadcastChannel(JSC::Structure*, JSDOMGlobalObject&, Ref<BroadcastChannel>&&); + + DECLARE_DEFAULT_FINISH_CREATION; +}; + +class JSBroadcastChannelOwner 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&, BroadcastChannel*) +{ + static NeverDestroyed<JSBroadcastChannelOwner> owner; + return &owner.get(); +} + +inline void* wrapperKey(BroadcastChannel* wrappableObject) +{ + return wrappableObject; +} + +JSC::JSValue toJS(JSC::JSGlobalObject*, JSDOMGlobalObject*, BroadcastChannel&); +inline JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, BroadcastChannel* impl) { return impl ? toJS(lexicalGlobalObject, globalObject, *impl) : JSC::jsNull(); } +JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, Ref<BroadcastChannel>&&); +inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, RefPtr<BroadcastChannel>&& impl) { return impl ? toJSNewlyCreated(lexicalGlobalObject, globalObject, impl.releaseNonNull()) : JSC::jsNull(); } + +template<> struct JSDOMWrapperConverterTraits<BroadcastChannel> { + using WrapperClass = JSBroadcastChannel; + using ToWrappedReturnType = BroadcastChannel*; +}; + +} // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSMessageEvent.cpp b/src/bun.js/bindings/webcore/JSMessageEvent.cpp index f6090bd91..68414fe46 100644 --- a/src/bun.js/bindings/webcore/JSMessageEvent.cpp +++ b/src/bun.js/bindings/webcore/JSMessageEvent.cpp @@ -352,7 +352,7 @@ JSC_DEFINE_CUSTOM_GETTER(jsMessageEvent_lastEventId, (JSGlobalObject * lexicalGl static inline JSValue jsMessageEvent_sourceGetter(JSGlobalObject& lexicalGlobalObject, JSMessageEvent& thisObject) { auto& vm = JSC::getVM(&lexicalGlobalObject); - return lexicalGlobalObject.globalThis(); + return jsNull(); // 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()))); diff --git a/src/bun.js/bindings/webcore/JSMessagePort.cpp b/src/bun.js/bindings/webcore/JSMessagePort.cpp index d957e32ce..b70167e55 100644 --- a/src/bun.js/bindings/webcore/JSMessagePort.cpp +++ b/src/bun.js/bindings/webcore/JSMessagePort.cpp @@ -62,6 +62,9 @@ using namespace JSC; static JSC_DECLARE_HOST_FUNCTION(jsMessagePortPrototypeFunction_postMessage); static JSC_DECLARE_HOST_FUNCTION(jsMessagePortPrototypeFunction_start); static JSC_DECLARE_HOST_FUNCTION(jsMessagePortPrototypeFunction_close); +static JSC_DECLARE_HOST_FUNCTION(jsMessagePortPrototypeFunction_ref); +static JSC_DECLARE_HOST_FUNCTION(jsMessagePortPrototypeFunction_unref); +static JSC_DECLARE_HOST_FUNCTION(jsMessagePortPrototypeFunction_hasRef); // Attributes @@ -130,6 +133,9 @@ static const HashTableValue JSMessagePortPrototypeTableValues[] = { { "postMessage"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMessagePortPrototypeFunction_postMessage, 1 } }, { "start"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMessagePortPrototypeFunction_start, 0 } }, { "close"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMessagePortPrototypeFunction_close, 0 } }, + { "ref"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMessagePortPrototypeFunction_ref, 0 } }, + { "unref"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMessagePortPrototypeFunction_unref, 0 } }, + { "hasRef"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMessagePortPrototypeFunction_hasRef, 0 } }, }; const ClassInfo JSMessagePortPrototype::s_info = { "MessagePort"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMessagePortPrototype) }; @@ -196,6 +202,8 @@ static inline bool setJSMessagePort_onmessageSetter(JSGlobalObject& lexicalGloba vm.writeBarrier(&thisObject, value); ensureStillAliveHere(value); + thisObject.wrapped().jsRef(&lexicalGlobalObject); + return true; } @@ -223,6 +231,8 @@ static inline bool setJSMessagePort_onmessageerrorSetter(JSGlobalObject& lexical vm.writeBarrier(&thisObject, value); ensureStillAliveHere(value); + thisObject.wrapped().jsRef(&lexicalGlobalObject); + return true; } @@ -318,6 +328,7 @@ static inline JSC::EncodedJSValue jsMessagePortPrototypeFunction_closeBody(JSC:: UNUSED_PARAM(throwScope); UNUSED_PARAM(callFrame); auto& impl = castedThis->wrapped(); + impl.jsUnref(lexicalGlobalObject); RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.close(); }))); } @@ -326,6 +337,51 @@ JSC_DEFINE_HOST_FUNCTION(jsMessagePortPrototypeFunction_close, (JSGlobalObject * return IDLOperation<JSMessagePort>::call<jsMessagePortPrototypeFunction_closeBody>(*lexicalGlobalObject, *callFrame, "close"); } +static inline JSC::EncodedJSValue jsMessagePortPrototypeFunction_refBody(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.jsRef(lexicalGlobalObject); }))); +} + +JSC_DEFINE_HOST_FUNCTION(jsMessagePortPrototypeFunction_ref, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSMessagePort>::call<jsMessagePortPrototypeFunction_refBody>(*lexicalGlobalObject, *callFrame, "ref"); +} + +static inline JSC::EncodedJSValue jsMessagePortPrototypeFunction_unrefBody(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.jsUnref(lexicalGlobalObject); }))); +} + +JSC_DEFINE_HOST_FUNCTION(jsMessagePortPrototypeFunction_unref, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSMessagePort>::call<jsMessagePortPrototypeFunction_unrefBody>(*lexicalGlobalObject, *callFrame, "unref"); +} + +static inline JSC::EncodedJSValue jsMessagePortPrototypeFunction_hasRefBody(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(); + return JSValue::encode(jsBoolean(impl.jsHasRef())); +} + +JSC_DEFINE_HOST_FUNCTION(jsMessagePortPrototypeFunction_hasRef, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation<JSMessagePort>::call<jsMessagePortPrototypeFunction_hasRefBody>(*lexicalGlobalObject, *callFrame, "hasRef"); +} + JSC::GCClient::IsoSubspace* JSMessagePort::subspaceForImpl(JSC::VM& vm) { return WebCore::subspaceForImpl<JSMessagePort, UseCustomHeapCellType::No>( diff --git a/src/bun.js/bindings/webcore/MessagePort.cpp b/src/bun.js/bindings/webcore/MessagePort.cpp index 956143c80..cdb3ea365 100644 --- a/src/bun.js/bindings/webcore/MessagePort.cpp +++ b/src/bun.js/bindings/webcore/MessagePort.cpp @@ -27,6 +27,7 @@ #include "config.h" #include "MessagePort.h" +#include "BunClientData.h" // #include "Document.h" #include "EventNames.h" // #include "Logging.h" @@ -44,6 +45,8 @@ #include <wtf/Lock.h> #include <wtf/Scope.h> +extern "C" void Bun__eventLoop__incrementRefConcurrently(void* bunVM, int delta); + namespace WebCore { WTF_MAKE_ISO_ALLOCATED_IMPL(MessagePort); @@ -416,4 +419,20 @@ WebCoreOpaqueRoot root(MessagePort* port) return WebCoreOpaqueRoot { port }; } +void MessagePort::jsRef(JSGlobalObject* lexicalGlobalObject) +{ + if (!m_hasRef) { + m_hasRef = true; + Bun__eventLoop__incrementRefConcurrently(WebCore::clientData(lexicalGlobalObject->vm())->bunVM, 1); + } +} + +void MessagePort::jsUnref(JSGlobalObject* lexicalGlobalObject) +{ + if (m_hasRef) { + m_hasRef = false; + Bun__eventLoop__incrementRefConcurrently(WebCore::clientData(lexicalGlobalObject->vm())->bunVM, -1); + } +} + } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/MessagePort.h b/src/bun.js/bindings/webcore/MessagePort.h index 18f0ba212..6a4e599be 100644 --- a/src/bun.js/bindings/webcore/MessagePort.h +++ b/src/bun.js/bindings/webcore/MessagePort.h @@ -103,6 +103,10 @@ public: static ScriptExecutionContextIdentifier contextIdForMessagePortId(MessagePortIdentifier); + void jsRef(JSGlobalObject*); + void jsUnref(JSGlobalObject*); + bool jsHasRef() { return m_hasRef; } + private: explicit MessagePort(ScriptExecutionContext&, const MessagePortIdentifier& local, const MessagePortIdentifier& remote); @@ -133,6 +137,8 @@ private: MessagePortIdentifier m_remoteIdentifier; mutable std::atomic<unsigned> m_refCount { 1 }; + + bool m_hasRef { false }; }; WebCoreOpaqueRoot root(MessagePort*); diff --git a/src/bun.js/bindings/webcore/SerializedScriptValue.cpp b/src/bun.js/bindings/webcore/SerializedScriptValue.cpp index 9eeee155a..9b9f04aa8 100644 --- a/src/bun.js/bindings/webcore/SerializedScriptValue.cpp +++ b/src/bun.js/bindings/webcore/SerializedScriptValue.cpp @@ -38,6 +38,7 @@ #include "CryptoKeyRaw.h" // #include "IDBValue.h" // #include "ImageBitmapBacking.h" +// #include "JSAudioWorkletGlobalScope.h" // #include "JSBlob.h" #include "JSCryptoKey.h" #include "JSDOMBinding.h" @@ -70,6 +71,7 @@ #include <JavaScriptCore/CatchScope.h> #include <JavaScriptCore/DateInstance.h> #include <JavaScriptCore/Error.h> +#include <JavaScriptCore/ErrorInstance.h> #include <JavaScriptCore/Exception.h> #include <JavaScriptCore/ExceptionHelpers.h> #include <JavaScriptCore/IterationKind.h> @@ -227,6 +229,7 @@ enum SerializationTag { WebCodecsVideoFrameTag = 53, #endif ResizableArrayBufferTag = 54, + ErrorInstanceTag = 55, Bun__BlobTag = 254, // bun types start at 254 and decrease with each addition @@ -249,6 +252,95 @@ enum ArrayBufferViewSubtag { BigUint64ArrayTag = 11, }; +// static bool isTypeExposedToGlobalObject(JSC::JSGlobalObject& globalObject, SerializationTag tag) +// { +// #if ENABLE(WEB_AUDIO) +// if (!jsDynamicCast<JSAudioWorkletGlobalScope*>(&globalObject)) +// return true; + +// // Only built-in JS types are exposed to audio worklets. +// switch (tag) { +// case ArrayTag: +// case ObjectTag: +// case UndefinedTag: +// case NullTag: +// case IntTag: +// case ZeroTag: +// case OneTag: +// case FalseTag: +// case TrueTag: +// case DoubleTag: +// case DateTag: +// case StringTag: +// case EmptyStringTag: +// case RegExpTag: +// case ObjectReferenceTag: +// case ArrayBufferTag: +// case ArrayBufferViewTag: +// case ArrayBufferTransferTag: +// case TrueObjectTag: +// case FalseObjectTag: +// case StringObjectTag: +// case EmptyStringObjectTag: +// case NumberObjectTag: +// case SetObjectTag: +// case MapObjectTag: +// case NonMapPropertiesTag: +// case NonSetPropertiesTag: +// case SharedArrayBufferTag: +// #if ENABLE(WEBASSEMBLY) +// case WasmModuleTag: +// #endif +// case BigIntTag: +// case BigIntObjectTag: +// #if ENABLE(WEBASSEMBLY) +// case WasmMemoryTag: +// #endif +// case ResizableArrayBufferTag: +// case ErrorInstanceTag: +// case ErrorTag: +// case MessagePortReferenceTag: +// return true; +// case FileTag: +// case FileListTag: +// case ImageDataTag: +// case BlobTag: +// #if ENABLE(WEB_CRYPTO) +// case CryptoKeyTag: +// #endif +// case DOMPointReadOnlyTag: +// case DOMPointTag: +// case DOMRectReadOnlyTag: +// case DOMRectTag: +// case DOMMatrixReadOnlyTag: +// case DOMMatrixTag: +// case DOMQuadTag: +// case ImageBitmapTransferTag: +// #if ENABLE(WEB_RTC) +// case RTCCertificateTag: +// #endif +// case ImageBitmapTag: +// #if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) +// case OffscreenCanvasTransferTag: +// #endif +// #if ENABLE(WEB_RTC) +// case RTCDataChannelTransferTag: +// #endif +// case DOMExceptionTag: +// #if ENABLE(WEB_CODECS) +// case WebCodecsEncodedVideoChunkTag: +// case WebCodecsVideoFrameTag: +// #endif +// break; +// } +// return false; +// #else +// UNUSED_PARAM(globalObject); +// UNUSED_PARAM(tag); +// return true; +// #endif +// } + static unsigned typedArrayElementSize(ArrayBufferViewSubtag tag) { switch (tag) { @@ -273,6 +365,55 @@ static unsigned typedArrayElementSize(ArrayBufferViewSubtag tag) } } +enum class SerializableErrorType : uint8_t { + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError, + Last = URIError +}; + +static SerializableErrorType errorNameToSerializableErrorType(const String& name) +{ + if (equalLettersIgnoringASCIICase(name, "evalerror"_s)) + return SerializableErrorType::EvalError; + if (equalLettersIgnoringASCIICase(name, "rangeerror"_s)) + return SerializableErrorType::RangeError; + if (equalLettersIgnoringASCIICase(name, "referenceerror"_s)) + return SerializableErrorType::ReferenceError; + if (equalLettersIgnoringASCIICase(name, "syntaxerror"_s)) + return SerializableErrorType::SyntaxError; + if (equalLettersIgnoringASCIICase(name, "typeerror"_s)) + return SerializableErrorType::TypeError; + if (equalLettersIgnoringASCIICase(name, "urierror"_s)) + return SerializableErrorType::URIError; + return SerializableErrorType::Error; +} + +static ErrorType toErrorType(SerializableErrorType value) +{ + switch (value) { + case SerializableErrorType::Error: + return ErrorType::Error; + case SerializableErrorType::EvalError: + return ErrorType::EvalError; + case SerializableErrorType::RangeError: + return ErrorType::RangeError; + case SerializableErrorType::ReferenceError: + return ErrorType::ReferenceError; + case SerializableErrorType::SyntaxError: + return ErrorType::SyntaxError; + case SerializableErrorType::TypeError: + return ErrorType::TypeError; + case SerializableErrorType::URIError: + return ErrorType::URIError; + } + return ErrorType::Error; +} + enum class PredefinedColorSpaceTag : uint8_t { SRGB = 0 #if ENABLE(PREDEFINED_COLOR_SPACE_DISPLAY_P3) @@ -396,14 +537,16 @@ const uint8_t cryptoKeyOKPOpNameTagMaximumValue = 1; * Version 10. changed the length (and offsets) of ArrayBuffers (and ArrayBufferViews) from 32 to 64 bits. * Version 11. added support for Blob's memory cost. * Version 12. added support for agent cluster ID. + * Version 13. added support for ErrorInstance objects. */ -static const unsigned CurrentVersion = 12; -static const unsigned TerminatorTag = 0xFFFFFFFF; -static const unsigned StringPoolTag = 0xFFFFFFFE; -static const unsigned NonIndexPropertiesTag = 0xFFFFFFFD; +static constexpr unsigned CurrentVersion = 13; +static constexpr unsigned TerminatorTag = 0xFFFFFFFF; +static constexpr unsigned StringPoolTag = 0xFFFFFFFE; +static constexpr unsigned NonIndexPropertiesTag = 0xFFFFFFFD; +static constexpr uint32_t ImageDataPoolTag = 0xFFFFFFFE; // The high bit of a StringData's length determines the character size. -static const unsigned StringDataIs8BitFlag = 0x80000000; +static constexpr unsigned StringDataIs8BitFlag = 0x80000000; /* * Object serialization is performed according to the following grammar, all tags @@ -1484,8 +1627,14 @@ private: // } // if (auto* data = JSImageData::toWrapped(vm, obj)) { // write(ImageDataTag); - // write(data->width()); - // write(data->height()); + // auto addResult = m_imageDataPool.add(*data, m_imageDataPool.size()); + // if (!addResult.isNewEntry) { + // write(ImageDataPoolTag); + // writeImageDataIndex(addResult.iterator->value); + // return true; + // } + // write(static_cast<uint32_t>(data->width())); + // write(static_cast<uint32_t>(data->height())); // CheckedUint32 dataLength = data->data().length(); // if (dataLength.hasOverflowed()) { // code = SerializationReturnCode::DataCloneError; @@ -1502,6 +1651,63 @@ private: write(String::fromLatin1(JSC::Yarr::flagsString(regExp->regExp()->flags()).data())); return true; } + if (auto* errorInstance = jsDynamicCast<ErrorInstance*>(obj)) { + auto& vm = m_lexicalGlobalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + auto errorTypeValue = errorInstance->get(m_lexicalGlobalObject, vm.propertyNames->name); + RETURN_IF_EXCEPTION(scope, false); + auto errorTypeString = errorTypeValue.toWTFString(m_lexicalGlobalObject); + RETURN_IF_EXCEPTION(scope, false); + + String message; + PropertyDescriptor messageDescriptor; + if (errorInstance->getOwnPropertyDescriptor(m_lexicalGlobalObject, vm.propertyNames->message, messageDescriptor) && messageDescriptor.isDataDescriptor()) { + EXCEPTION_ASSERT(!scope.exception()); + message = messageDescriptor.value().toWTFString(m_lexicalGlobalObject); + } + RETURN_IF_EXCEPTION(scope, false); + + unsigned line = 0; + PropertyDescriptor lineDescriptor; + if (errorInstance->getOwnPropertyDescriptor(m_lexicalGlobalObject, vm.propertyNames->line, lineDescriptor) && lineDescriptor.isDataDescriptor()) { + EXCEPTION_ASSERT(!scope.exception()); + line = lineDescriptor.value().toNumber(m_lexicalGlobalObject); + } + RETURN_IF_EXCEPTION(scope, false); + + unsigned column = 0; + PropertyDescriptor columnDescriptor; + if (errorInstance->getOwnPropertyDescriptor(m_lexicalGlobalObject, vm.propertyNames->column, columnDescriptor) && columnDescriptor.isDataDescriptor()) { + EXCEPTION_ASSERT(!scope.exception()); + column = columnDescriptor.value().toNumber(m_lexicalGlobalObject); + } + RETURN_IF_EXCEPTION(scope, false); + + String sourceURL; + PropertyDescriptor sourceURLDescriptor; + if (errorInstance->getOwnPropertyDescriptor(m_lexicalGlobalObject, vm.propertyNames->sourceURL, sourceURLDescriptor) && sourceURLDescriptor.isDataDescriptor()) { + EXCEPTION_ASSERT(!scope.exception()); + sourceURL = sourceURLDescriptor.value().toWTFString(m_lexicalGlobalObject); + } + RETURN_IF_EXCEPTION(scope, false); + + String stack; + PropertyDescriptor stackDescriptor; + if (errorInstance->getOwnPropertyDescriptor(m_lexicalGlobalObject, vm.propertyNames->stack, stackDescriptor) && stackDescriptor.isDataDescriptor()) { + EXCEPTION_ASSERT(!scope.exception()); + stack = stackDescriptor.value().toWTFString(m_lexicalGlobalObject); + } + RETURN_IF_EXCEPTION(scope, false); + + write(ErrorInstanceTag); + write(errorNameToSerializableErrorType(errorTypeString)); + writeNullableString(message); + write(line); + write(column); + writeNullableString(sourceURL); + writeNullableString(stack); + return true; + } if (obj->inherits<JSMessagePort>()) { auto index = m_transferredMessagePorts.find(obj); if (index != m_transferredMessagePorts.end()) { @@ -1765,6 +1971,11 @@ private: } #endif + void write(bool b) + { + writeLittleEndian(m_buffer, static_cast<int32_t>(b)); + } + void write(uint8_t c) { writeLittleEndian(m_buffer, c); @@ -1805,6 +2016,11 @@ private: writeConstantPoolIndex(m_constantPool, i); } + // void writeImageDataIndex(unsigned i) + // { + // writeConstantPoolIndex(m_imageDataPool, i); + // } + void writeObjectIndex(unsigned i) { writeConstantPoolIndex(m_objectPool, i); @@ -1863,6 +2079,14 @@ private: write(Identifier::fromString(m_lexicalGlobalObject->vm(), str)); } + void writeNullableString(const String& str) + { + bool isNull = str.isNull(); + write(isNull); + if (!isNull) + write(Identifier::fromString(m_lexicalGlobalObject->vm(), str)); + } + void write(const Vector<uint8_t>& vector) { uint32_t size = vector.size(); @@ -2074,6 +2298,11 @@ private: } } + void write(SerializableErrorType errorType) + { + write(enumToUnderlyingType(errorType)); + } + void write(const CryptoKey* key) { write(currentKeyFormatVersion); @@ -2172,6 +2401,8 @@ private: #endif typedef HashMap<RefPtr<UniquedStringImpl>, uint32_t, IdentifierRepHash> StringConstantPool; StringConstantPool m_constantPool; + // using ImageDataPool = HashMap<Ref<ImageData>, uint32_t>; + // ImageDataPool m_imageDataPool; Identifier m_emptyIdentifier; SerializationContext m_context; ArrayBufferContentsArray& m_sharedBuffers; @@ -2880,6 +3111,15 @@ private: } #endif + bool read(bool& b) + { + int32_t integer; + if (!readLittleEndian(integer) || integer > 1) + return false; + b = !!integer; + return true; + } + bool read(uint32_t& i) { return readLittleEndian(i); @@ -2917,28 +3157,34 @@ private: return readLittleEndian(i); } - bool readStringIndex(uint32_t& i) + std::optional<uint32_t> readStringIndex() { - return readConstantPoolIndex(m_constantPool, i); + return readConstantPoolIndex(m_constantPool); } - template<class T> bool readConstantPoolIndex(const T& constantPool, uint32_t& i) + // std::optional<uint32_t> readImageDataIndex() + // { + // return readConstantPoolIndex(m_imageDataPool); + // } + + template<typename T> std::optional<uint32_t> readConstantPoolIndex(const T& constantPool) { if (constantPool.size() <= 0xFF) { uint8_t i8; if (!read(i8)) - return false; - i = i8; - return true; + return std::nullopt; + return i8; } if (constantPool.size() <= 0xFFFF) { uint16_t i16; if (!read(i16)) - return false; - i = i16; - return true; + return std::nullopt; + return i16; } - return read(i); + uint32_t i; + if (!read(i)) + return std::nullopt; + return i; } static bool readString(const uint8_t*& ptr, const uint8_t* end, String& str, unsigned length, bool is8Bit) @@ -2973,6 +3219,20 @@ private: return true; } + bool readNullableString(String& nullableString) + { + bool isNull; + if (!read(isNull)) + return false; + if (isNull) + return true; + CachedStringRef stringData; + if (!readStringData(stringData)) + return false; + nullableString = stringData->string(); + return true; + } + static bool readIdentifier(JSC::VM& vm, const uint8_t*& ptr, const uint8_t* end, Identifier& str, unsigned length, bool is8Bit) { if (length >= std::numeric_limits<int32_t>::max() / sizeof(UChar)) @@ -3023,16 +3283,12 @@ private: return false; } if (length == StringPoolTag) { - unsigned index = 0; - if (!readStringIndex(index)) { + auto index = readStringIndex(); + if (!index || *index >= m_constantPool.size()) { fail(); return false; } - if (index >= m_constantPool.size()) { - fail(); - return false; - } - cachedString = CachedStringRef(&m_constantPool, index); + cachedString = CachedStringRef(&m_constantPool, *index); return true; } bool is8Bit = length & StringDataIs8BitFlag; @@ -3059,16 +3315,12 @@ private: return false; } if (length == StringPoolTag) { - unsigned index = 0; - if (!readStringIndex(index)) { - fail(); - return false; - } - if (index >= m_constantPool.size()) { + auto index = readStringIndex(); + if (!index || *index >= m_constantPool.size()) { fail(); return false; } - cachedString = CachedStringRef(&m_constantPool, index); + cachedString = CachedStringRef(&m_constantPool, *index); return true; } bool is8Bit = length & StringDataIs8BitFlag; @@ -3759,6 +4011,16 @@ private: } #endif + bool read(SerializableErrorType& errorType) + { + std::underlying_type_t<SerializableErrorType> errorTypeInt; + if (!read(errorTypeInt) || errorTypeInt > enumToUnderlyingType(SerializableErrorType::Last)) + return false; + + errorType = static_cast<SerializableErrorType>(errorTypeInt); + return true; + } + template<class T> JSValue getJSValue(T&& nativeObj) { @@ -4194,6 +4456,8 @@ private: JSValue readTerminal() { SerializationTag tag = readTag(); + // if (!isTypeExposedToGlobalObject(*m_globalObject, tag)) + // return JSValue(); // read bun types if (auto value = StructuredCloneableDeserialize::fromTagDeserialize(tag, m_lexicalGlobalObject, m_ptr, m_end)) { @@ -4296,6 +4560,14 @@ private: // uint32_t width; // if (!read(width)) // return JSValue(); + // if (width == ImageDataPoolTag) { + // auto index = readImageDataIndex(); + // if (!index || *index >= m_imageDataPool.size()) { + // fail(); + // return JSValue(); + // } + // return getJSValue(m_imageDataPool[*index]); + // } // uint32_t height; // if (!read(height)) // return JSValue(); @@ -4332,6 +4604,7 @@ private: // memcpy(result.returnValue()->data().data(), bufferStart, length); // else // result.returnValue()->data().zeroFill(); + // m_imageDataPool.append(result.returnValue().copyRef()); // return getJSValue(result.releaseReturnValue()); // } // case BlobTag: { @@ -4386,13 +4659,46 @@ private: RegExp* regExp = RegExp::create(vm, pattern->string(), reFlags.value()); return RegExpObject::create(vm, m_globalObject->regExpStructure(), regExp); } + case ErrorInstanceTag: { + SerializableErrorType serializedErrorType; + if (!read(serializedErrorType)) { + fail(); + return JSValue(); + } + String message; + if (!readNullableString(message)) { + fail(); + return JSValue(); + } + uint32_t line; + if (!read(line)) { + fail(); + return JSValue(); + } + uint32_t column; + if (!read(column)) { + fail(); + return JSValue(); + } + String sourceURL; + if (!readNullableString(sourceURL)) { + fail(); + return JSValue(); + } + String stackString; + if (!readNullableString(stackString)) { + fail(); + return JSValue(); + } + return ErrorInstance::create(m_lexicalGlobalObject, WTFMove(message), toErrorType(serializedErrorType), line, column, WTFMove(sourceURL), WTFMove(stackString)); + } case ObjectReferenceTag: { - unsigned index = 0; - if (!readConstantPoolIndex(m_gcBuffer, index)) { + auto index = readConstantPoolIndex(m_gcBuffer); + if (!index) { fail(); return JSValue(); } - return m_gcBuffer.at(index); + return m_gcBuffer.at(*index); } case MessagePortReferenceTag: { uint32_t index; @@ -4628,6 +4934,7 @@ private: const uint8_t* const m_end; unsigned m_version; Vector<CachedString> m_constantPool; + // Vector<Ref<ImageData>> m_imageDataPool; const Vector<RefPtr<MessagePort>>& m_messagePorts; ArrayBufferContentsArray* m_arrayBufferContents; Vector<RefPtr<JSC::ArrayBuffer>> m_arrayBuffers; |