Skip to content
Permalink
Browse files
BroadcastChannel is not always computing correctly its origin
https://bugs.webkit.org/show_bug.cgi?id=248869
rdar://102519898

Reviewed by Chris Dumez.

Shared Workers document is also its own top document.
This makes Document::topOrigin() computation wrong in case of third-party Shared Workers.
Update BroadcastChannel to directly use ScriptExecutionContext::topOrigin instead.
As a follow-up, we should probably update third-party SharedWorker/ServiceWorker document creation to ensure this mistake does not pop up again.

* LayoutTests/http/wpt/shared-workers/resources/third-party-shared-worker-broadcast-channel-iframe.html: Added.
* LayoutTests/http/wpt/shared-workers/resources/third-party-shared-worker-broadcast-channel-sharedworker.js: Added.
* LayoutTests/http/wpt/shared-workers/third-party-shared-worker-broadcast-channel-expected.txt: Added.
* LayoutTests/http/wpt/shared-workers/third-party-shared-worker-broadcast-channel.html: Added.
* Source/WebCore/dom/BroadcastChannel.cpp:
(WebCore::shouldPartitionOrigin):
(WebCore::BroadcastChannel::MainThreadBridge::ensureOnMainThread):
(WebCore::BroadcastChannel::MainThreadBridge::registerChannel):
(WebCore::BroadcastChannel::MainThreadBridge::unregisterChannel):
(WebCore::BroadcastChannel::MainThreadBridge::postMessage):
* Source/WebCore/dom/BroadcastChannel.h:
* Source/WebCore/page/PartitionedSecurityOrigin.h:
(WebCore::PartitionedSecurityOrigin::isolatedCopy const):
* Source/WebKit/NetworkProcess/NetworkBroadcastChannelRegistry.cpp:
(WebKit::NetworkBroadcastChannelRegistry::registerChannel):
(WebKit::NetworkBroadcastChannelRegistry::unregisterChannel):
(WebKit::NetworkBroadcastChannelRegistry::postMessage):
* Source/WebKit/WebProcess/WebCoreSupport/WebBroadcastChannelRegistry.cpp:
(WebKit::WebBroadcastChannelRegistry::registerChannel):
(WebKit::WebBroadcastChannelRegistry::unregisterChannel):
(WebKit::WebBroadcastChannelRegistry::postMessage):
(WebKit::WebBroadcastChannelRegistry::postMessageLocally):
(WebKit::WebBroadcastChannelRegistry::postMessageToRemote):

Canonical link: https://commits.webkit.org/257551@main
  • Loading branch information
youennf committed Dec 8, 2022
1 parent 65071a6 commit b5f2a68531f7fe3718d62f999d770dc9594e82fc
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 23 deletions.
@@ -0,0 +1,17 @@
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<script>
const channel = new BroadcastChannel('test');

channel.onmessage = function (e) {
parent.postMessage(e.data, "*");
}

const worker = new SharedWorker("third-party-shared-worker-broadcast-channel-sharedworker.js");
</script>
</body>
</html>
@@ -0,0 +1,2 @@
const channel = new BroadcastChannel('test');
channel.postMessage('hello');
@@ -0,0 +1,3 @@

PASS BroadcastChannel between third-party iframe and shared worker should work properly

@@ -0,0 +1,27 @@
<html><!-- webkit-test-runner [ BroadcastChannelOriginPartitioningEnabled=true ] -->
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
</head>
<body>
<script>
function with_iframe(url) {
return new Promise(function(resolve) {
var frame = document.createElement('iframe');
frame.className = 'test-iframe';
frame.src = url;
frame.onload = function() { resolve(frame); };
document.body.appendChild(frame);
});
}

promise_test(async (t) => {
const frame = await with_iframe(get_host_info().HTTP_REMOTE_ORIGIN + "/WebKit/shared-workers/resources/third-party-shared-worker-broadcast-channel-iframe.html");
const data = await new Promise(resolve => window.onmessage = (e) => resolve(e.data));
assert_equals(data, "hello");
frame.remove();
}, "BroadcastChannel between third-party iframe and shared worker should work properly");
</script>
</body>
</html>
@@ -60,9 +60,11 @@ static HashMap<BroadcastChannelIdentifier, ScriptExecutionContextIdentifier>& ch
return map;
}

static bool shouldPartitionOrigin(Document& document)
static PartitionedSecurityOrigin partitionedSecurityOriginFromContext(ScriptExecutionContext& context)
{
return document.settings().broadcastChannelOriginPartitioningEnabled();
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> {
@@ -82,22 +84,23 @@ class BroadcastChannel::MainThreadBridge : public ThreadSafeRefCounted<MainThrea
private:
MainThreadBridge(BroadcastChannel&, const String& name);

void ensureOnMainThread(Function<void(Document&)>&&);
void ensureOnMainThread(Function<void(Page*)>&&);

WeakPtr<BroadcastChannel, WeakPtrImplWithEventTargetData> m_broadcastChannel;
const BroadcastChannelIdentifier m_identifier;
const String m_name; // Main thread only.
std::optional<PartitionedSecurityOrigin> m_origin; // Main thread only.
PartitionedSecurityOrigin m_origin; // Main thread only.
};

BroadcastChannel::MainThreadBridge::MainThreadBridge(BroadcastChannel& channel, const String& name)
: m_broadcastChannel(channel)
, m_identifier(BroadcastChannelIdentifier::generateThreadSafe())
, m_name(name.isolatedCopy())
, m_origin(partitionedSecurityOriginFromContext(*channel.scriptExecutionContext()).isolatedCopy())
{
}

void BroadcastChannel::MainThreadBridge::ensureOnMainThread(Function<void(Document&)>&& task)
void BroadcastChannel::MainThreadBridge::ensureOnMainThread(Function<void(Page*)>&& task)
{
ASSERT(m_broadcastChannel);
if (!m_broadcastChannel)
@@ -109,44 +112,42 @@ void BroadcastChannel::MainThreadBridge::ensureOnMainThread(Function<void(Docume
ASSERT(context->isContextThread());

Ref protectedThis { *this };
if (auto document = dynamicDowncast<Document>(*context))
task(*document);
else {
downcast<WorkerGlobalScope>(*context).thread().workerLoaderProxy().postTaskToLoader([protectedThis = WTFMove(protectedThis), task = WTFMove(task)](auto& context) {
task(downcast<Document>(context));
});
if (auto* document = dynamicDowncast<Document>(*context)) {
task(document->page());
return;
}

downcast<WorkerGlobalScope>(*context).thread().workerLoaderProxy().postTaskToLoader([protectedThis = WTFMove(protectedThis), task = WTFMove(task)](auto& context) {
task(downcast<Document>(context).page());
});
}

void BroadcastChannel::MainThreadBridge::registerChannel()
{
auto securityOrigin = m_broadcastChannel->scriptExecutionContext()->securityOrigin()->isolatedCopy();
ensureOnMainThread([this, contextIdentifier = m_broadcastChannel->scriptExecutionContext()->identifier(), securityOrigin = WTFMove(securityOrigin)](auto& document) {
m_origin = PartitionedSecurityOrigin { shouldPartitionOrigin(document) ? Ref { document.topOrigin() } : securityOrigin.copyRef(), securityOrigin.copyRef() };
if (auto* page = document.page())
page->broadcastChannelRegistry().registerChannel(*m_origin, m_name, m_identifier);
ensureOnMainThread([this, contextIdentifier = m_broadcastChannel->scriptExecutionContext()->identifier()](auto* page) mutable {
if (page)
page->broadcastChannelRegistry().registerChannel(m_origin, m_name, m_identifier);
channelToContextIdentifier().add(m_identifier, contextIdentifier);
});
}

void BroadcastChannel::MainThreadBridge::unregisterChannel()
{
ensureOnMainThread([this](auto& document) {
if (auto* page = document.page())
page->broadcastChannelRegistry().unregisterChannel(*m_origin, m_name, m_identifier);
ensureOnMainThread([this](auto* page) {
if (page)
page->broadcastChannelRegistry().unregisterChannel(m_origin, m_name, m_identifier);
channelToContextIdentifier().remove(m_identifier);
});
}

void BroadcastChannel::MainThreadBridge::postMessage(Ref<SerializedScriptValue>&& message)
{
ensureOnMainThread([this, message = WTFMove(message)](auto& document) mutable {
auto* page = document.page();
ensureOnMainThread([this, message = WTFMove(message)](auto* page) mutable {
if (!page)
return;

auto blobHandles = message->blobHandles();
page->broadcastChannelRegistry().postMessage(*m_origin, m_name, m_identifier, WTFMove(message), [blobHandles = WTFMove(blobHandles)] {
page->broadcastChannelRegistry().postMessage(m_origin, m_name, m_identifier, WTFMove(message), [blobHandles = WTFMove(blobHandles)] {
// Keeps Blob data inside messageData alive until the message has been delivered.
});
});
@@ -68,7 +68,6 @@ class BroadcastChannel : public RefCounted<BroadcastChannel>, public EventTarget
BroadcastChannel(ScriptExecutionContext&, const String& name);

void dispatchMessage(Ref<SerializedScriptValue>&&);
void ensureOnMainThread(Function<void(Document&)>&&);

bool isEligibleForMessaging() const;

@@ -51,6 +51,8 @@ struct PartitionedSecurityOrigin {
bool isHashTableDeletedValue() const { return topOrigin.isHashTableDeletedValue(); }
bool isHashTableEmptyValue() const { return topOrigin.isHashTableEmptyValue(); }

PartitionedSecurityOrigin isolatedCopy() const { return { topOrigin->isolatedCopy(), clientOrigin->isolatedCopy() }; }

Ref<SecurityOrigin> topOrigin;
Ref<SecurityOrigin> clientOrigin;
};

0 comments on commit b5f2a68

Please sign in to comment.