Commit fc05b522 authored by Ting Shao's avatar Ting Shao Committed by Commit Bot

ServiceWorker: Support FetchEvent.handled API

This change implements the FetchEvent.handled API which gives
developers the ability to intercept when the event is handled.

Chrome Status: https://chromestatus.com/features/5654467158474752

Bug: 1039558
Change-Id: I8033d85d57ea6c331401006789713c0cb899aeac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2114932
Commit-Queue: Ting Shao <ting.shao@intel.com>
Reviewed-by: default avatarMakoto Shimazu <shimazu@chromium.org>
Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797335}
parent c8c2ecf2
...@@ -68,6 +68,19 @@ ScriptPromise FetchEvent::preloadResponse(ScriptState* script_state) { ...@@ -68,6 +68,19 @@ ScriptPromise FetchEvent::preloadResponse(ScriptState* script_state) {
return preload_response_property_->Promise(script_state->World()); return preload_response_property_->Promise(script_state->World());
} }
ScriptPromise FetchEvent::handled(ScriptState* script_state) {
return handled_property_->Promise(script_state->World());
}
void FetchEvent::ResolveHandledPromise() {
handled_property_->ResolveWithUndefined();
}
void FetchEvent::RejectHandledPromise(const String& error_message) {
handled_property_->Reject(ServiceWorkerError::GetException(
nullptr, mojom::blink::ServiceWorkerErrorType::kNetwork, error_message));
}
const AtomicString& FetchEvent::InterfaceName() const { const AtomicString& FetchEvent::InterfaceName() const {
return event_interface_names::kFetchEvent; return event_interface_names::kFetchEvent;
} }
...@@ -96,6 +109,10 @@ FetchEvent::FetchEvent(ScriptState* script_state, ...@@ -96,6 +109,10 @@ FetchEvent::FetchEvent(ScriptState* script_state,
observer_(respond_with_observer), observer_(respond_with_observer),
preload_response_property_(MakeGarbageCollected<PreloadResponseProperty>( preload_response_property_(MakeGarbageCollected<PreloadResponseProperty>(
ExecutionContext::From(script_state))), ExecutionContext::From(script_state))),
handled_property_(
MakeGarbageCollected<ScriptPromiseProperty<ToV8UndefinedGenerator,
Member<DOMException>>>(
ExecutionContext::From(script_state))),
worker_timing_remote_(ExecutionContext::From(script_state)) { worker_timing_remote_(ExecutionContext::From(script_state)) {
worker_timing_remote_.Bind(std::move(worker_timing_remote), worker_timing_remote_.Bind(std::move(worker_timing_remote),
ExecutionContext::From(script_state) ExecutionContext::From(script_state)
...@@ -231,6 +248,7 @@ void FetchEvent::Trace(Visitor* visitor) const { ...@@ -231,6 +248,7 @@ void FetchEvent::Trace(Visitor* visitor) const {
visitor->Trace(request_); visitor->Trace(request_);
visitor->Trace(preload_response_property_); visitor->Trace(preload_response_property_);
visitor->Trace(body_completion_notifier_); visitor->Trace(body_completion_notifier_);
visitor->Trace(handled_property_);
visitor->Trace(worker_timing_remote_); visitor->Trace(worker_timing_remote_);
ExtendableEvent::Trace(visitor); ExtendableEvent::Trace(visitor);
ExecutionContextClient::Trace(visitor); ExecutionContextClient::Trace(visitor);
......
...@@ -71,6 +71,11 @@ class MODULES_EXPORT FetchEvent final ...@@ -71,6 +71,11 @@ class MODULES_EXPORT FetchEvent final
void respondWith(ScriptState*, ScriptPromise, ExceptionState&); void respondWith(ScriptState*, ScriptPromise, ExceptionState&);
ScriptPromise preloadResponse(ScriptState*); ScriptPromise preloadResponse(ScriptState*);
ScriptPromise handled(ScriptState*);
void ResolveHandledPromise();
void RejectHandledPromise(const String& error_message);
void addPerformanceEntry(PerformanceMark*); void addPerformanceEntry(PerformanceMark*);
void addPerformanceEntry(PerformanceMeasure*); void addPerformanceEntry(PerformanceMeasure*);
...@@ -98,6 +103,8 @@ class MODULES_EXPORT FetchEvent final ...@@ -98,6 +103,8 @@ class MODULES_EXPORT FetchEvent final
Member<PreloadResponseProperty> preload_response_property_; Member<PreloadResponseProperty> preload_response_property_;
std::unique_ptr<WebURLResponse> preload_response_; std::unique_ptr<WebURLResponse> preload_response_;
Member<DataPipeBytesConsumer::CompletionNotifier> body_completion_notifier_; Member<DataPipeBytesConsumer::CompletionNotifier> body_completion_notifier_;
Member<ScriptPromiseProperty<ToV8UndefinedGenerator, Member<DOMException>>>
handled_property_;
// This is currently null for navigation while https://crbug.com/900700 is // This is currently null for navigation while https://crbug.com/900700 is
// being implemented. // being implemented.
HeapMojoRemote<mojom::blink::WorkerTimingContainer, HeapMojoRemote<mojom::blink::WorkerTimingContainer,
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
[CallWith=ScriptState, RaisesException] void respondWith(Promise<Response> r); [CallWith=ScriptState, RaisesException] void respondWith(Promise<Response> r);
[CallWith=ScriptState] readonly attribute Promise<any> preloadResponse; [CallWith=ScriptState] readonly attribute Promise<any> preloadResponse;
[CallWith=ScriptState] readonly attribute Promise<void> handled;
// FetchEvent#addPerformanceEntry // FetchEvent#addPerformanceEntry
// https://github.com/wanderview/fetchevent-worker-timing/blob/master/explainer.md // https://github.com/wanderview/fetchevent-worker-timing/blob/master/explainer.md
[RuntimeEnabled=ServiceWorkerFetchEventWorkerTiming] [RuntimeEnabled=ServiceWorkerFetchEventWorkerTiming]
......
...@@ -198,19 +198,23 @@ class FetchLoaderClient final : public GarbageCollected<FetchLoaderClient>, ...@@ -198,19 +198,23 @@ class FetchLoaderClient final : public GarbageCollected<FetchLoaderClient>,
void FetchRespondWithObserver::OnResponseRejected( void FetchRespondWithObserver::OnResponseRejected(
ServiceWorkerResponseError error) { ServiceWorkerResponseError error) {
DCHECK(GetExecutionContext()); DCHECK(GetExecutionContext());
const String error_message = GetMessageForResponseError(error, request_url_);
GetExecutionContext()->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( GetExecutionContext()->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kJavaScript, mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kWarning, mojom::ConsoleMessageLevel::kWarning, error_message));
GetMessageForResponseError(error, request_url_)));
// The default value of FetchAPIResponse's status is 0, which maps to a // The default value of FetchAPIResponse's status is 0, which maps to a
// network error. // network error.
auto response = mojom::blink::FetchAPIResponse::New(); auto response = mojom::blink::FetchAPIResponse::New();
response->status_text = ""; response->status_text = "";
response->error = error; response->error = error;
To<ServiceWorkerGlobalScope>(GetExecutionContext()) ServiceWorkerGlobalScope* service_worker_global_scope =
->RespondToFetchEvent(event_id_, request_url_, std::move(response), To<ServiceWorkerGlobalScope>(GetExecutionContext());
event_dispatch_time_, base::TimeTicks::Now()); service_worker_global_scope->RespondToFetchEvent(
event_id_, request_url_, std::move(response), event_dispatch_time_,
base::TimeTicks::Now());
service_worker_global_scope->RejectFetchEventHandledPromise(event_id_,
error_message);
} }
void FetchRespondWithObserver::OnResponseFulfilled( void FetchRespondWithObserver::OnResponseFulfilled(
...@@ -333,6 +337,7 @@ void FetchRespondWithObserver::OnResponseFulfilled( ...@@ -333,6 +337,7 @@ void FetchRespondWithObserver::OnResponseFulfilled(
service_worker_global_scope->RespondToFetchEvent( service_worker_global_scope->RespondToFetchEvent(
event_id_, request_url_, std::move(fetch_api_response), event_id_, request_url_, std::move(fetch_api_response),
event_dispatch_time_, base::TimeTicks::Now()); event_dispatch_time_, base::TimeTicks::Now());
service_worker_global_scope->ResolveFetchEventHandledPromise(event_id_);
return; return;
} }
...@@ -359,19 +364,22 @@ void FetchRespondWithObserver::OnResponseFulfilled( ...@@ -359,19 +364,22 @@ void FetchRespondWithObserver::OnResponseFulfilled(
service_worker_global_scope->RespondToFetchEventWithResponseStream( service_worker_global_scope->RespondToFetchEventWithResponseStream(
event_id_, request_url_, std::move(fetch_api_response), event_id_, request_url_, std::move(fetch_api_response),
std::move(stream_handle), event_dispatch_time_, base::TimeTicks::Now()); std::move(stream_handle), event_dispatch_time_, base::TimeTicks::Now());
service_worker_global_scope->ResolveFetchEventHandledPromise(event_id_);
return; return;
} }
service_worker_global_scope->RespondToFetchEvent( service_worker_global_scope->RespondToFetchEvent(
event_id_, request_url_, std::move(fetch_api_response), event_id_, request_url_, std::move(fetch_api_response),
event_dispatch_time_, base::TimeTicks::Now()); event_dispatch_time_, base::TimeTicks::Now());
service_worker_global_scope->ResolveFetchEventHandledPromise(event_id_);
} }
void FetchRespondWithObserver::OnNoResponse() { void FetchRespondWithObserver::OnNoResponse() {
DCHECK(GetExecutionContext()); DCHECK(GetExecutionContext());
To<ServiceWorkerGlobalScope>(GetExecutionContext()) ServiceWorkerGlobalScope* service_worker_global_scope =
->RespondToFetchEventWithNoResponse(event_id_, request_url_, To<ServiceWorkerGlobalScope>(GetExecutionContext());
event_dispatch_time_, service_worker_global_scope->RespondToFetchEventWithNoResponse(
base::TimeTicks::Now()); event_id_, request_url_, event_dispatch_time_, base::TimeTicks::Now());
service_worker_global_scope->ResolveFetchEventHandledPromise(event_id_);
} }
FetchRespondWithObserver::FetchRespondWithObserver( FetchRespondWithObserver::FetchRespondWithObserver(
......
...@@ -808,6 +808,7 @@ void ServiceWorkerGlobalScope::Trace(Visitor* visitor) const { ...@@ -808,6 +808,7 @@ void ServiceWorkerGlobalScope::Trace(Visitor* visitor) const {
visitor->Trace(payment_response_callbacks_); visitor->Trace(payment_response_callbacks_);
visitor->Trace(fetch_response_callbacks_); visitor->Trace(fetch_response_callbacks_);
visitor->Trace(pending_preload_fetch_events_); visitor->Trace(pending_preload_fetch_events_);
visitor->Trace(pending_fetch_events_);
visitor->Trace(controller_receivers_); visitor->Trace(controller_receivers_);
WorkerGlobalScope::Trace(visitor); WorkerGlobalScope::Trace(visitor);
} }
...@@ -822,6 +823,20 @@ int ServiceWorkerGlobalScope::GetOutstandingThrottledLimit() const { ...@@ -822,6 +823,20 @@ int ServiceWorkerGlobalScope::GetOutstandingThrottledLimit() const {
return features::kInstallingServiceWorkerOutstandingThrottledLimit.Get(); return features::kInstallingServiceWorkerOutstandingThrottledLimit.Get();
} }
void ServiceWorkerGlobalScope::ResolveFetchEventHandledPromise(int event_id) {
FetchEvent* fetch_event = pending_fetch_events_.Take(event_id);
DCHECK(fetch_event);
fetch_event->ResolveHandledPromise();
}
void ServiceWorkerGlobalScope::RejectFetchEventHandledPromise(
int event_id,
const String& error_message) {
FetchEvent* fetch_event = pending_fetch_events_.Take(event_id);
DCHECK(fetch_event);
fetch_event->RejectHandledPromise(error_message);
}
void ServiceWorkerGlobalScope::importScripts(const Vector<String>& urls, void ServiceWorkerGlobalScope::importScripts(const Vector<String>& urls,
ExceptionState& exception_state) { ExceptionState& exception_state) {
for (const String& string_url : urls) { for (const String& string_url : urls) {
...@@ -1528,6 +1543,8 @@ void ServiceWorkerGlobalScope::StartFetchEvent( ...@@ -1528,6 +1543,8 @@ void ServiceWorkerGlobalScope::StartFetchEvent(
pending_preload_fetch_events_.insert(event_id, fetch_event); pending_preload_fetch_events_.insert(event_id, fetch_event);
} }
pending_fetch_events_.insert(event_id, fetch_event);
NoteNewFetchEvent(request->url()); NoteNewFetchEvent(request->url());
DispatchExtendableEventWithRespondWith(fetch_event, wait_until_observer, DispatchExtendableEventWithRespondWith(fetch_event, wait_until_observer,
......
...@@ -316,6 +316,9 @@ class MODULES_EXPORT ServiceWorkerGlobalScope final ...@@ -316,6 +316,9 @@ class MODULES_EXPORT ServiceWorkerGlobalScope final
int GetOutstandingThrottledLimit() const override; int GetOutstandingThrottledLimit() const override;
const ServiceWorkerToken& token() const { return token_; } const ServiceWorkerToken& token() const { return token_; }
void ResolveFetchEventHandledPromise(int event_id);
void RejectFetchEventHandledPromise(int event_id,
const String& error_message);
protected: protected:
// EventTarget // EventTarget
...@@ -688,6 +691,10 @@ class MODULES_EXPORT ServiceWorkerGlobalScope final ...@@ -688,6 +691,10 @@ class MODULES_EXPORT ServiceWorkerGlobalScope final
HeapHashMap<int, Member<FetchEvent>> pending_preload_fetch_events_; HeapHashMap<int, Member<FetchEvent>> pending_preload_fetch_events_;
// Fetch events that are being handled are stored here and will be removed
// after being handled.
HeapHashMap<int, Member<FetchEvent>> pending_fetch_events_;
// Track outstanding FetchEvent objects still waiting for a response by // Track outstanding FetchEvent objects still waiting for a response by
// request URL. This information can be used as a hint that cache_storage // request URL. This information can be used as a hint that cache_storage
// or fetch requests to the same URL is likely to be used to satisfy a // or fetch requests to the same URL is likely to be used to satisfy a
......
This is a testharness.js-based test. This is a testharness.js-based test.
Found 296 tests; 252 PASS, 44 FAIL, 0 TIMEOUT, 0 NOTRUN. Found 296 tests; 253 PASS, 43 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS idl_test setup PASS idl_test setup
PASS idl_test validation PASS idl_test validation
PASS Partial interface Navigator: original interface defined PASS Partial interface Navigator: original interface defined
...@@ -216,7 +216,7 @@ PASS FetchEvent interface: attribute preloadResponse ...@@ -216,7 +216,7 @@ PASS FetchEvent interface: attribute preloadResponse
PASS FetchEvent interface: attribute clientId PASS FetchEvent interface: attribute clientId
PASS FetchEvent interface: attribute resultingClientId PASS FetchEvent interface: attribute resultingClientId
FAIL FetchEvent interface: attribute replacesClientId assert_true: The prototype object must have a property "replacesClientId" expected true got false FAIL FetchEvent interface: attribute replacesClientId assert_true: The prototype object must have a property "replacesClientId" expected true got false
FAIL FetchEvent interface: attribute handled assert_true: The prototype object must have a property "handled" expected true got false PASS FetchEvent interface: attribute handled
PASS FetchEvent interface: operation respondWith(Promise<Response>) PASS FetchEvent interface: operation respondWith(Promise<Response>)
FAIL FetchEvent must be primary interface of new FetchEvent("type") assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'FetchEvent': 2 arguments required, but only 1 present." FAIL FetchEvent must be primary interface of new FetchEvent("type") assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'FetchEvent': 2 arguments required, but only 1 present."
FAIL Stringification of new FetchEvent("type") assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'FetchEvent': 2 arguments required, but only 1 present." FAIL Stringification of new FetchEvent("type") assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'FetchEvent': 2 arguments required, but only 1 present."
......
This is a testharness.js-based test.
PASS global setup
FAIL FetchEvent.handled should resolve when respondWith() is not called for a navigation request assert_equals: expected "RESOLVED" but got "FAILED"
FAIL FetchEvent.handled should resolve when respondWith() is not called for a sub-resource request assert_equals: expected "RESOLVED" but got "FAILED"
FAIL FetchEvent.handled should reject when respondWith() is not called and the event is canceled assert_equals: expected "REJECTED" but got "FAILED"
FAIL FetchEvent.handled should resolve when the promise provided to respondWith() is resolved assert_equals: expected "RESOLVED" but got "FAILED"
FAIL FetchEvent.handled should reject when the promise provided to respondWith() is resolved to an invalid response assert_equals: expected "REJECTED" but got "FAILED"
FAIL FetchEvent.handled should reject when the promise provided to respondWith() is rejected assert_equals: expected "REJECTED" but got "FAILED"
PASS global cleanup
Harness: the test ran to completion.
...@@ -511,6 +511,7 @@ interface FaceDetector ...@@ -511,6 +511,7 @@ interface FaceDetector
interface FetchEvent : ExtendableEvent interface FetchEvent : ExtendableEvent
attribute @@toStringTag attribute @@toStringTag
getter clientId getter clientId
getter handled
getter isReload getter isReload
getter preloadResponse getter preloadResponse
getter request getter request
......
...@@ -446,6 +446,7 @@ interface ExtendableMessageEvent : ExtendableEvent ...@@ -446,6 +446,7 @@ interface ExtendableMessageEvent : ExtendableEvent
interface FetchEvent : ExtendableEvent interface FetchEvent : ExtendableEvent
attribute @@toStringTag attribute @@toStringTag
getter clientId getter clientId
getter handled
getter isReload getter isReload
getter preloadResponse getter preloadResponse
getter request getter request
......
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