blob: 582a091f27b3fef1e87103239b0ad70bba3ddedf [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.h"
#include "third_party/blink/public/platform/interface_provider.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
#include "third_party/blink/renderer/core/events/message_event.h"
#include "third_party/blink/renderer/platform/mojo/mojo_helper.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
namespace blink {
namespace {
// To ensure proper ordering of messages send to/from multiple BroadcastChannel
// instances in the same thread this uses one BroadcastChannelService
// connection as basis for all connections to channels from the same thread. The
// actual connections used to send/receive messages are then created using
// associated interfaces, ensuring proper message ordering.
mojom::blink::BroadcastChannelProviderPtr& GetThreadSpecificProvider() {
DEFINE_THREAD_SAFE_STATIC_LOCAL(
ThreadSpecific<mojom::blink::BroadcastChannelProviderPtr>, provider, ());
if (!provider.IsSet()) {
Platform::Current()->GetInterfaceProvider()->GetInterface(
mojo::MakeRequest(&*provider));
}
return *provider;
}
} // namespace
// static
BroadcastChannel* BroadcastChannel::Create(ExecutionContext* execution_context,
const String& name,
ExceptionState& exception_state) {
if (execution_context->GetSecurityOrigin()->IsOpaque()) {
// TODO(mek): Decide what to do here depending on
// https://github.com/whatwg/html/issues/1319
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
"Can't create BroadcastChannel in an opaque origin");
return nullptr;
}
return MakeGarbageCollected<BroadcastChannel>(execution_context, name);
}
BroadcastChannel::~BroadcastChannel() = default;
void BroadcastChannel::Dispose() {
close();
}
void BroadcastChannel::postMessage(const ScriptValue& message,
ExceptionState& exception_state) {
if (!binding_.is_bound()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Channel is closed");
return;
}
scoped_refptr<SerializedScriptValue> value = SerializedScriptValue::Serialize(
message.GetIsolate(), message.V8Value(),
SerializedScriptValue::SerializeOptions(), exception_state);
if (exception_state.HadException())
return;
BlinkCloneableMessage msg;
msg.message = std::move(value);
remote_client_->OnMessage(std::move(msg));
}
void BroadcastChannel::close() {
remote_client_.reset();
if (binding_.is_bound())
binding_.Close();
}
const AtomicString& BroadcastChannel::InterfaceName() const {
return event_target_names::kBroadcastChannel;
}
bool BroadcastChannel::HasPendingActivity() const {
return binding_.is_bound() && HasEventListeners(event_type_names::kMessage);
}
void BroadcastChannel::ContextDestroyed(ExecutionContext*) {
close();
}
void BroadcastChannel::Trace(blink::Visitor* visitor) {
ContextLifecycleObserver::Trace(visitor);
EventTargetWithInlineData::Trace(visitor);
}
void BroadcastChannel::OnMessage(BlinkCloneableMessage message) {
// Queue a task to dispatch the event.
MessageEvent* event;
if (!message.locked_agent_cluster_id ||
GetExecutionContext()->IsSameAgentCluster(
*message.locked_agent_cluster_id)) {
event = MessageEvent::Create(
nullptr, std::move(message.message),
GetExecutionContext()->GetSecurityOrigin()->ToString());
} else {
event = MessageEvent::CreateError(
GetExecutionContext()->GetSecurityOrigin()->ToString());
}
EnqueueEvent(*event, TaskType::kPostedMessage);
}
void BroadcastChannel::OnError() {
close();
}
BroadcastChannel::BroadcastChannel(ExecutionContext* execution_context,
const String& name)
: ContextLifecycleObserver(execution_context),
origin_(execution_context->GetSecurityOrigin()),
name_(name),
binding_(this) {
mojom::blink::BroadcastChannelProviderPtr& provider =
GetThreadSpecificProvider();
// Local BroadcastChannelClient for messages send from the browser to this
// channel.
mojom::blink::BroadcastChannelClientAssociatedPtrInfo local_client_info;
binding_.Bind(mojo::MakeRequest(&local_client_info));
binding_.set_connection_error_handler(
WTF::Bind(&BroadcastChannel::OnError, WrapWeakPersistent(this)));
// Remote BroadcastChannelClient for messages send from this channel to the
// browser.
auto remote_cient_request = mojo::MakeRequest(&remote_client_);
remote_client_.set_connection_error_handler(
WTF::Bind(&BroadcastChannel::OnError, WrapWeakPersistent(this)));
provider->ConnectToChannel(origin_, name_, std::move(local_client_info),
std::move(remote_cient_request));
}
} // namespace blink