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 ...@@ -100,9 +100,9 @@ class MockWebServiceWorkerProviderClientImpl
was_set_controller_called_ = true; was_set_controller_called_ = true;
} }
void DispatchMessageEvent(blink::WebServiceWorkerObjectInfo info, void ReceiveMessage(blink::WebServiceWorkerObjectInfo info,
blink::TransferableMessage message) override { blink::TransferableMessage message) override {
was_dispatch_message_event_called_ = true; was_receive_message_called_ = true;
} }
void CountFeature(blink::mojom::WebFeature feature) override { void CountFeature(blink::mojom::WebFeature feature) override {
...@@ -111,8 +111,8 @@ class MockWebServiceWorkerProviderClientImpl ...@@ -111,8 +111,8 @@ class MockWebServiceWorkerProviderClientImpl
bool was_set_controller_called() const { return was_set_controller_called_; } bool was_set_controller_called() const { return was_set_controller_called_; }
bool was_dispatch_message_event_called() const { bool was_receive_message_called() const {
return was_dispatch_message_event_called_; return was_receive_message_called_;
} }
const std::set<blink::mojom::WebFeature>& used_features() const { const std::set<blink::mojom::WebFeature>& used_features() const {
...@@ -121,7 +121,7 @@ class MockWebServiceWorkerProviderClientImpl ...@@ -121,7 +121,7 @@ class MockWebServiceWorkerProviderClientImpl
private: private:
bool was_set_controller_called_ = false; 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_; std::set<blink::mojom::WebFeature> used_features_;
}; };
...@@ -658,7 +658,7 @@ TEST_F(ServiceWorkerProviderContextTest, PostMessageToClient) { ...@@ -658,7 +658,7 @@ TEST_F(ServiceWorkerProviderContextTest, PostMessageToClient) {
std::make_unique<WebServiceWorkerProviderImpl>(provider_context.get()); std::make_unique<WebServiceWorkerProviderImpl>(provider_context.get());
auto client = std::make_unique<MockWebServiceWorkerProviderClientImpl>(); auto client = std::make_unique<MockWebServiceWorkerProviderClientImpl>();
provider_impl->SetClient(client.get()); 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), container_ptr->PostMessageToClient(std::move(object_info),
blink::TransferableMessage()); blink::TransferableMessage());
...@@ -666,7 +666,7 @@ TEST_F(ServiceWorkerProviderContextTest, PostMessageToClient) { ...@@ -666,7 +666,7 @@ TEST_F(ServiceWorkerProviderContextTest, PostMessageToClient) {
// The passed reference should be owned by the provider client (but the // The passed reference should be owned by the provider client (but the
// reference is immediately released by the mock provider client). // 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()); EXPECT_EQ(0, mock_service_worker_object_host->GetBindingCount());
} }
......
...@@ -201,7 +201,7 @@ void WebServiceWorkerProviderImpl::PostMessageToClient( ...@@ -201,7 +201,7 @@ void WebServiceWorkerProviderImpl::PostMessageToClient(
if (!provider_client_) if (!provider_client_)
return; return;
provider_client_->DispatchMessageEvent( provider_client_->ReceiveMessage(
source.To<blink::WebServiceWorkerObjectInfo>(), std::move(message)); source.To<blink::WebServiceWorkerObjectInfo>(), std::move(message));
} }
......
...@@ -53,8 +53,8 @@ class WebServiceWorkerProviderClient { ...@@ -53,8 +53,8 @@ class WebServiceWorkerProviderClient {
virtual void SetController(WebServiceWorkerObjectInfo, virtual void SetController(WebServiceWorkerObjectInfo,
bool should_notify_controller_change) = 0; bool should_notify_controller_change) = 0;
virtual void DispatchMessageEvent(WebServiceWorkerObjectInfo, virtual void ReceiveMessage(WebServiceWorkerObjectInfo source,
TransferableMessage) = 0; TransferableMessage) = 0;
virtual void CountFeature(mojom::WebFeature) = 0; virtual void CountFeature(mojom::WebFeature) = 0;
}; };
......
...@@ -16,6 +16,8 @@ blink_modules_sources("service_worker") { ...@@ -16,6 +16,8 @@ blink_modules_sources("service_worker") {
"fetch_respond_with_observer.h", "fetch_respond_with_observer.h",
"install_event.cc", "install_event.cc",
"install_event.h", "install_event.h",
"message_from_service_worker.cc",
"message_from_service_worker.h",
"navigation_preload_manager.cc", "navigation_preload_manager.cc",
"navigation_preload_manager.h", "navigation_preload_manager.h",
"navigator_service_worker.cc", "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 @@ ...@@ -43,6 +43,7 @@
#include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h" #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/document.h"
#include "third_party/blink/renderer/core/dom/dom_exception.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/events/message_event.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
...@@ -67,6 +68,10 @@ namespace blink { ...@@ -67,6 +68,10 @@ namespace blink {
namespace { namespace {
bool HasFiredDomContentLoaded(const Document& document) {
return !document.GetTiming().DomContentLoadedEventStart().is_null();
}
mojom::ServiceWorkerUpdateViaCache ParseUpdateViaCache(const String& value) { mojom::ServiceWorkerUpdateViaCache ParseUpdateViaCache(const String& value) {
if (value == "imports") if (value == "imports")
return mojom::ServiceWorkerUpdateViaCache::kImports; return mojom::ServiceWorkerUpdateViaCache::kImports;
...@@ -122,6 +127,27 @@ class GetRegistrationCallback : public WebServiceWorkerProvider:: ...@@ -122,6 +127,27 @@ class GetRegistrationCallback : public WebServiceWorkerProvider::
} // namespace } // 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 class ServiceWorkerContainer::GetRegistrationForReadyCallback
: public WebServiceWorkerProvider:: : public WebServiceWorkerProvider::
WebServiceWorkerGetRegistrationForReadyCallbacks { WebServiceWorkerGetRegistrationForReadyCallbacks {
...@@ -196,6 +222,7 @@ void ServiceWorkerContainer::ContextDestroyed(ExecutionContext*) { ...@@ -196,6 +222,7 @@ void ServiceWorkerContainer::ContextDestroyed(ExecutionContext*) {
void ServiceWorkerContainer::Trace(blink::Visitor* visitor) { void ServiceWorkerContainer::Trace(blink::Visitor* visitor) {
visitor->Trace(controller_); visitor->Trace(controller_);
visitor->Trace(ready_); visitor->Trace(ready_);
visitor->Trace(dom_content_loaded_observer_);
visitor->Trace(service_worker_registration_objects_); visitor->Trace(service_worker_registration_objects_);
visitor->Trace(service_worker_objects_); visitor->Trace(service_worker_objects_);
EventTargetWithInlineData::Trace(visitor); EventTargetWithInlineData::Trace(visitor);
...@@ -473,28 +500,44 @@ void ServiceWorkerContainer::SetController( ...@@ -473,28 +500,44 @@ void ServiceWorkerContainer::SetController(
DispatchEvent(*Event::Create(event_type_names::kControllerchange)); DispatchEvent(*Event::Create(event_type_names::kControllerchange));
} }
void ServiceWorkerContainer::DispatchMessageEvent( void ServiceWorkerContainer::ReceiveMessage(WebServiceWorkerObjectInfo source,
WebServiceWorkerObjectInfo info, TransferableMessage message) {
TransferableMessage message) { auto* context = GetExecutionContext();
if (!GetExecutionContext() || !GetExecutionContext()->ExecutingWindow()) if (!context || !context->ExecutingWindow())
return; return;
auto msg = ToBlinkTransferableMessage(std::move(message)); // ServiceWorkerContainer is only supported on documents.
MessagePortArray* ports = auto* document = DynamicTo<Document>(context);
MessagePort::EntanglePorts(*GetExecutionContext(), std::move(msg.ports)); DCHECK(document);
ServiceWorker* source =
ServiceWorker::From(GetExecutionContext(), std::move(info)); if (!is_client_message_queue_enabled_) {
MessageEvent* event; if (!HasFiredDomContentLoaded(*document)) {
if (!msg.locked_agent_cluster_id || // Wait for DOMContentLoaded. This corresponds to the specification steps
GetExecutionContext()->IsSameAgentCluster(*msg.locked_agent_cluster_id)) { // for "Parsing HTML documents": "The end" at
event = MessageEvent::Create( // https://html.spec.whatwg.org/multipage/parsing.html#the-end:
ports, std::move(msg.message), //
GetExecutionContext()->GetSecurityOrigin()->ToString(), // 1. Fire an event named DOMContentLoaded at the Document object, with
String() /* lastEventId */, source); // its bubbles attribute initialized to true.
} else { // 2. Enable the client message queue of the ServiceWorkerContainer object
event = MessageEvent::CreateError( // whose associated service worker client is the Document object's
GetExecutionContext()->GetSecurityOrigin()->ToString(), source); // 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) { void ServiceWorkerContainer::CountFeature(mojom::WebFeature feature) {
...@@ -555,4 +598,41 @@ ServiceWorkerContainer::CreateReadyProperty() { ...@@ -555,4 +598,41 @@ ServiceWorkerContainer::CreateReadyProperty() {
ReadyProperty::kReady); 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 } // namespace blink
...@@ -32,6 +32,8 @@ ...@@ -32,6 +32,8 @@
#define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_CONTAINER_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_CONTAINER_H_
#include <memory> #include <memory>
#include <vector>
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-blink.h" #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.h"
#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h" #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h"
...@@ -41,6 +43,7 @@ ...@@ -41,6 +43,7 @@
#include "third_party/blink/renderer/core/dom/document.h" #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/core/dom/events/event_target.h"
#include "third_party/blink/renderer/modules/modules_export.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/registration_options.h"
#include "third_party/blink/renderer/modules/service_worker/service_worker.h" #include "third_party/blink/renderer/modules/service_worker/service_worker.h"
#include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h" #include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h"
...@@ -91,8 +94,8 @@ class MODULES_EXPORT ServiceWorkerContainer final ...@@ -91,8 +94,8 @@ class MODULES_EXPORT ServiceWorkerContainer final
// WebServiceWorkerProviderClient implementation. // WebServiceWorkerProviderClient implementation.
void SetController(WebServiceWorkerObjectInfo, void SetController(WebServiceWorkerObjectInfo,
bool should_notify_controller_change) override; bool should_notify_controller_change) override;
void DispatchMessageEvent(WebServiceWorkerObjectInfo, void ReceiveMessage(WebServiceWorkerObjectInfo source,
TransferableMessage) override; TransferableMessage) override;
void CountFeature(mojom::WebFeature) override; void CountFeature(mojom::WebFeature) override;
// EventTarget overrides. // EventTarget overrides.
...@@ -100,6 +103,8 @@ class MODULES_EXPORT ServiceWorkerContainer final ...@@ -100,6 +103,8 @@ class MODULES_EXPORT ServiceWorkerContainer final
const AtomicString& InterfaceName() const override; const AtomicString& InterfaceName() const override;
DEFINE_ATTRIBUTE_EVENT_LISTENER(controllerchange, kControllerchange); DEFINE_ATTRIBUTE_EVENT_LISTENER(controllerchange, kControllerchange);
// TODO(falken): Enable the client message queue when onmessage is assigned
// to.
DEFINE_ATTRIBUTE_EVENT_LISTENER(message, kMessage); DEFINE_ATTRIBUTE_EVENT_LISTENER(message, kMessage);
// Returns the ServiceWorkerRegistration object described by the given info. // Returns the ServiceWorkerRegistration object described by the given info.
...@@ -112,13 +117,19 @@ class MODULES_EXPORT ServiceWorkerContainer final ...@@ -112,13 +117,19 @@ class MODULES_EXPORT ServiceWorkerContainer final
ServiceWorker* GetOrCreateServiceWorker(WebServiceWorkerObjectInfo); ServiceWorker* GetOrCreateServiceWorker(WebServiceWorkerObjectInfo);
private: private:
class DomContentLoadedListener;
class GetRegistrationForReadyCallback; class GetRegistrationForReadyCallback;
using ReadyProperty = using ReadyProperty =
ScriptPromiseProperty<Member<ServiceWorkerContainer>, ScriptPromiseProperty<Member<ServiceWorkerContainer>,
Member<ServiceWorkerRegistration>, Member<ServiceWorkerRegistration>,
Member<ServiceWorkerRegistration>>; Member<ServiceWorkerRegistration>>;
ReadyProperty* CreateReadyProperty(); ReadyProperty* CreateReadyProperty();
void EnableClientMessageQueue();
void DispatchMessageEvent(WebServiceWorkerObjectInfo source,
TransferableMessage);
std::unique_ptr<WebServiceWorkerProvider> provider_; std::unique_ptr<WebServiceWorkerProvider> provider_;
Member<ServiceWorker> controller_; Member<ServiceWorker> controller_;
Member<ReadyProperty> ready_; Member<ReadyProperty> ready_;
...@@ -137,6 +148,14 @@ class MODULES_EXPORT ServiceWorkerContainer final ...@@ -137,6 +148,14 @@ class MODULES_EXPORT ServiceWorkerContainer final
WTF::IntHash<int64_t>, WTF::IntHash<int64_t>,
WTF::UnsignedWithZeroKeyHashTraits<int64_t>> WTF::UnsignedWithZeroKeyHashTraits<int64_t>>
service_worker_objects_; 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 } // 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