Commit ed203455 authored by Matt Falkenhagen's avatar Matt Falkenhagen Committed by Commit Bot

service worker: Queue Client.postMessage() until DOMContentLoaded.

This introduces the enabling/disabling mechanism for the client
message queue as described in:
https://w3c.github.io/ServiceWorker/#dfn-client-message-queue

The client message queue should be disabled until one of the following
occurs:
* DOMContentLoaded is fired[1]
* startMessages() is called (not yet implemented)
* onmessage is assigned to (not yet implemented)

This CL enables the queue on DOMContentLoaded. This makes
partially makes the WPT test postmessage-to-client-message-queue.https.html
pass. The test case "Messages from ServiceWorker to Client only received
after DOMContentLoaded event." passes. But the test file still times out
because the other triggers are not implemented. It will also be flaky
until bug 924958 is fixed.

[1] At https://html.spec.whatwg.org/multipage/parsing.html#the-end:
1. Fire an event named DOMContentLoaded at the Document object, with its
bubbles attribute initialized to true.
2. Enable the client message queue of the ServiceWorkerContainer object
whose associated service worker client is the Document object's relevant
settings object.

Bug: 915685
Change-Id: Ifcd84687f70f98f1558b35b0e3044a7c4662a567
Reviewed-on: https://chromium-review.googlesource.com/c/1441657
Commit-Queue: Matt Falkenhagen <falken@chromium.org>
Reviewed-by: default avatarKenichi Ishibashi <bashi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#626918}
parent ce2a1206
......@@ -100,9 +100,9 @@ class MockWebServiceWorkerProviderClientImpl
was_set_controller_called_ = true;
}
void DispatchMessageEvent(blink::WebServiceWorkerObjectInfo info,
blink::TransferableMessage message) override {
was_dispatch_message_event_called_ = true;
void ReceiveMessage(blink::WebServiceWorkerObjectInfo info,
blink::TransferableMessage message) override {
was_receive_message_called_ = true;
}
void CountFeature(blink::mojom::WebFeature feature) override {
......@@ -111,8 +111,8 @@ class MockWebServiceWorkerProviderClientImpl
bool was_set_controller_called() const { return was_set_controller_called_; }
bool was_dispatch_message_event_called() const {
return was_dispatch_message_event_called_;
bool was_receive_message_called() const {
return was_receive_message_called_;
}
const std::set<blink::mojom::WebFeature>& used_features() const {
......@@ -121,7 +121,7 @@ class MockWebServiceWorkerProviderClientImpl
private:
bool was_set_controller_called_ = false;
bool was_dispatch_message_event_called_ = false;
bool was_receive_message_called_ = false;
std::set<blink::mojom::WebFeature> used_features_;
};
......@@ -658,7 +658,7 @@ TEST_F(ServiceWorkerProviderContextTest, PostMessageToClient) {
std::make_unique<WebServiceWorkerProviderImpl>(provider_context.get());
auto client = std::make_unique<MockWebServiceWorkerProviderClientImpl>();
provider_impl->SetClient(client.get());
ASSERT_FALSE(client->was_dispatch_message_event_called());
ASSERT_FALSE(client->was_receive_message_called());
container_ptr->PostMessageToClient(std::move(object_info),
blink::TransferableMessage());
......@@ -666,7 +666,7 @@ TEST_F(ServiceWorkerProviderContextTest, PostMessageToClient) {
// The passed reference should be owned by the provider client (but the
// reference is immediately released by the mock provider client).
EXPECT_TRUE(client->was_dispatch_message_event_called());
EXPECT_TRUE(client->was_receive_message_called());
EXPECT_EQ(0, mock_service_worker_object_host->GetBindingCount());
}
......
......@@ -201,7 +201,7 @@ void WebServiceWorkerProviderImpl::PostMessageToClient(
if (!provider_client_)
return;
provider_client_->DispatchMessageEvent(
provider_client_->ReceiveMessage(
source.To<blink::WebServiceWorkerObjectInfo>(), std::move(message));
}
......
......@@ -53,8 +53,8 @@ class WebServiceWorkerProviderClient {
virtual void SetController(WebServiceWorkerObjectInfo,
bool should_notify_controller_change) = 0;
virtual void DispatchMessageEvent(WebServiceWorkerObjectInfo,
TransferableMessage) = 0;
virtual void ReceiveMessage(WebServiceWorkerObjectInfo source,
TransferableMessage) = 0;
virtual void CountFeature(mojom::WebFeature) = 0;
};
......
......@@ -16,6 +16,8 @@ blink_modules_sources("service_worker") {
"fetch_respond_with_observer.h",
"install_event.cc",
"install_event.h",
"message_from_service_worker.cc",
"message_from_service_worker.h",
"navigation_preload_manager.cc",
"navigation_preload_manager.h",
"navigator_service_worker.cc",
......
// Copyright 2019 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/service_worker/message_from_service_worker.h"
#include <utility>
namespace blink {
MessageFromServiceWorker::MessageFromServiceWorker(
WebServiceWorkerObjectInfo source,
blink::TransferableMessage message)
: source(std::move(source)), message(std::move(message)) {}
MessageFromServiceWorker::~MessageFromServiceWorker() = default;
} // namespace blink
// Copyright 2019 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_MESSAGE_FROM_SERVICE_WORKER_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_MESSAGE_FROM_SERVICE_WORKER_H_
#include "third_party/blink/public/common/messaging/transferable_message.h"
#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_object_info.h"
namespace blink {
// Holds info for a message event destined for ServiceWorkerContainer.onmessage.
// https://w3c.github.io/ServiceWorker/#dom-serviceworkercontainer-onmessage
struct MessageFromServiceWorker {
MessageFromServiceWorker(WebServiceWorkerObjectInfo source,
blink::TransferableMessage message);
virtual ~MessageFromServiceWorker();
// The service worker that posted the message.
WebServiceWorkerObjectInfo source;
// The message.
blink::TransferableMessage message;
private:
DISALLOW_COPY_AND_ASSIGN(MessageFromServiceWorker);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_MESSAGE_FROM_SERVICE_WORKER_H_
......@@ -43,6 +43,7 @@
#include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
#include "third_party/blink/renderer/core/events/message_event.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
......@@ -67,6 +68,10 @@ namespace blink {
namespace {
bool HasFiredDomContentLoaded(const Document& document) {
return !document.GetTiming().DomContentLoadedEventStart().is_null();
}
mojom::ServiceWorkerUpdateViaCache ParseUpdateViaCache(const String& value) {
if (value == "imports")
return mojom::ServiceWorkerUpdateViaCache::kImports;
......@@ -122,6 +127,27 @@ class GetRegistrationCallback : public WebServiceWorkerProvider::
} // namespace
class ServiceWorkerContainer::DomContentLoadedListener final
: public NativeEventListener {
public:
void Invoke(ExecutionContext* execution_context, Event* event) override {
DCHECK_EQ(event->type(), "DOMContentLoaded");
Document& document = *To<Document>(execution_context);
DCHECK(HasFiredDomContentLoaded(document));
auto* container =
Supplement<Document>::From<ServiceWorkerContainer>(document);
if (!container) {
// There is no container for some reason, which means there's no message
// queue to start. Just abort.
return;
}
container->EnableClientMessageQueue();
}
};
class ServiceWorkerContainer::GetRegistrationForReadyCallback
: public WebServiceWorkerProvider::
WebServiceWorkerGetRegistrationForReadyCallbacks {
......@@ -196,6 +222,7 @@ void ServiceWorkerContainer::ContextDestroyed(ExecutionContext*) {
void ServiceWorkerContainer::Trace(blink::Visitor* visitor) {
visitor->Trace(controller_);
visitor->Trace(ready_);
visitor->Trace(dom_content_loaded_observer_);
visitor->Trace(service_worker_registration_objects_);
visitor->Trace(service_worker_objects_);
EventTargetWithInlineData::Trace(visitor);
......@@ -473,28 +500,44 @@ void ServiceWorkerContainer::SetController(
DispatchEvent(*Event::Create(event_type_names::kControllerchange));
}
void ServiceWorkerContainer::DispatchMessageEvent(
WebServiceWorkerObjectInfo info,
TransferableMessage message) {
if (!GetExecutionContext() || !GetExecutionContext()->ExecutingWindow())
void ServiceWorkerContainer::ReceiveMessage(WebServiceWorkerObjectInfo source,
TransferableMessage message) {
auto* context = GetExecutionContext();
if (!context || !context->ExecutingWindow())
return;
auto msg = ToBlinkTransferableMessage(std::move(message));
MessagePortArray* ports =
MessagePort::EntanglePorts(*GetExecutionContext(), std::move(msg.ports));
ServiceWorker* source =
ServiceWorker::From(GetExecutionContext(), std::move(info));
MessageEvent* event;
if (!msg.locked_agent_cluster_id ||
GetExecutionContext()->IsSameAgentCluster(*msg.locked_agent_cluster_id)) {
event = MessageEvent::Create(
ports, std::move(msg.message),
GetExecutionContext()->GetSecurityOrigin()->ToString(),
String() /* lastEventId */, source);
} else {
event = MessageEvent::CreateError(
GetExecutionContext()->GetSecurityOrigin()->ToString(), source);
// ServiceWorkerContainer is only supported on documents.
auto* document = DynamicTo<Document>(context);
DCHECK(document);
if (!is_client_message_queue_enabled_) {
if (!HasFiredDomContentLoaded(*document)) {
// Wait for DOMContentLoaded. This corresponds to the specification steps
// for "Parsing HTML documents": "The end" at
// https://html.spec.whatwg.org/multipage/parsing.html#the-end:
//
// 1. Fire an event named DOMContentLoaded at the Document object, with
// its bubbles attribute initialized to true.
// 2. Enable the client message queue of the ServiceWorkerContainer object
// whose associated service worker client is the Document object's
// relevant settings object.
if (!dom_content_loaded_observer_) {
dom_content_loaded_observer_ =
MakeGarbageCollected<DomContentLoadedListener>();
document->addEventListener(event_type_names::kDOMContentLoaded,
dom_content_loaded_observer_.Get(), false);
}
queued_messages_.emplace_back(std::make_unique<MessageFromServiceWorker>(
std::move(source), std::move(message)));
// The messages will be dispatched once EnableClientMessageQueue() is
// called.
return;
}
// DOMContentLoaded was fired already, so enable the queue.
EnableClientMessageQueue();
}
EnqueueEvent(*event, TaskType::kServiceWorkerClientMessage);
DispatchMessageEvent(std::move(source), std::move(message));
}
void ServiceWorkerContainer::CountFeature(mojom::WebFeature feature) {
......@@ -555,4 +598,41 @@ ServiceWorkerContainer::CreateReadyProperty() {
ReadyProperty::kReady);
}
void ServiceWorkerContainer::EnableClientMessageQueue() {
dom_content_loaded_observer_ = nullptr;
is_client_message_queue_enabled_ = true;
std::vector<std::unique_ptr<MessageFromServiceWorker>> messages;
messages.swap(queued_messages_);
for (auto& message : messages) {
DispatchMessageEvent(std::move(message->source),
std::move(message->message));
}
}
void ServiceWorkerContainer::DispatchMessageEvent(
WebServiceWorkerObjectInfo source,
TransferableMessage message) {
DCHECK(is_client_message_queue_enabled_);
auto msg = ToBlinkTransferableMessage(std::move(message));
MessagePortArray* ports =
MessagePort::EntanglePorts(*GetExecutionContext(), std::move(msg.ports));
ServiceWorker* service_worker =
ServiceWorker::From(GetExecutionContext(), std::move(source));
MessageEvent* event;
if (!msg.locked_agent_cluster_id ||
GetExecutionContext()->IsSameAgentCluster(*msg.locked_agent_cluster_id)) {
event = MessageEvent::Create(
ports, std::move(msg.message),
GetExecutionContext()->GetSecurityOrigin()->ToString(),
String() /* lastEventId */, service_worker);
} else {
event = MessageEvent::CreateError(
GetExecutionContext()->GetSecurityOrigin()->ToString(), service_worker);
}
// Schedule the event to be dispatched on the correct task source:
// https://w3c.github.io/ServiceWorker/#dfn-client-message-queue
EnqueueEvent(*event, TaskType::kServiceWorkerClientMessage);
}
} // namespace blink
......@@ -32,6 +32,8 @@
#define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_CONTAINER_H_
#include <memory>
#include <vector>
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-blink.h"
#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider.h"
#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h"
......@@ -41,6 +43,7 @@
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/events/event_target.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/modules/service_worker/message_from_service_worker.h"
#include "third_party/blink/renderer/modules/service_worker/registration_options.h"
#include "third_party/blink/renderer/modules/service_worker/service_worker.h"
#include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h"
......@@ -91,8 +94,8 @@ class MODULES_EXPORT ServiceWorkerContainer final
// WebServiceWorkerProviderClient implementation.
void SetController(WebServiceWorkerObjectInfo,
bool should_notify_controller_change) override;
void DispatchMessageEvent(WebServiceWorkerObjectInfo,
TransferableMessage) override;
void ReceiveMessage(WebServiceWorkerObjectInfo source,
TransferableMessage) override;
void CountFeature(mojom::WebFeature) override;
// EventTarget overrides.
......@@ -100,6 +103,8 @@ class MODULES_EXPORT ServiceWorkerContainer final
const AtomicString& InterfaceName() const override;
DEFINE_ATTRIBUTE_EVENT_LISTENER(controllerchange, kControllerchange);
// TODO(falken): Enable the client message queue when onmessage is assigned
// to.
DEFINE_ATTRIBUTE_EVENT_LISTENER(message, kMessage);
// Returns the ServiceWorkerRegistration object described by the given info.
......@@ -112,13 +117,19 @@ class MODULES_EXPORT ServiceWorkerContainer final
ServiceWorker* GetOrCreateServiceWorker(WebServiceWorkerObjectInfo);
private:
class DomContentLoadedListener;
class GetRegistrationForReadyCallback;
using ReadyProperty =
ScriptPromiseProperty<Member<ServiceWorkerContainer>,
Member<ServiceWorkerRegistration>,
Member<ServiceWorkerRegistration>>;
ReadyProperty* CreateReadyProperty();
void EnableClientMessageQueue();
void DispatchMessageEvent(WebServiceWorkerObjectInfo source,
TransferableMessage);
std::unique_ptr<WebServiceWorkerProvider> provider_;
Member<ServiceWorker> controller_;
Member<ReadyProperty> ready_;
......@@ -137,6 +148,14 @@ class MODULES_EXPORT ServiceWorkerContainer final
WTF::IntHash<int64_t>,
WTF::UnsignedWithZeroKeyHashTraits<int64_t>>
service_worker_objects_;
// For https://w3c.github.io/ServiceWorker/#dfn-client-message-queue
// Rather than pausing/resuming the task runner, we implement enabling the
// queue using this flag since the task runner is shared with other task
// sources.
bool is_client_message_queue_enabled_ = false;
std::vector<std::unique_ptr<MessageFromServiceWorker>> queued_messages_;
Member<DomContentLoadedListener> dom_content_loaded_observer_;
};
} // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment