Convert Background Fetch' input to a WebServiceWorkerRequest vector
Developers can pass in either an individual USVString or Request object,
or a sequence of either. Convert the input provided by the developer to
a sequence of WebServiceWorkerRequest objects, which can (soon) be send
over Mojo to the browser process.
BUG=692534, 692535
Review-Url: https://codereview.chromium.org/2762243002
Cr-Original-Commit-Position: refs/heads/master@{#459465}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 9b34b21e479145817c93e1e4870ed2e87376eac0
diff --git a/WebKit/LayoutTests/http/tests/background_fetch/background-fetch-manager-fetch.https.html b/WebKit/LayoutTests/http/tests/background_fetch/background-fetch-manager-fetch.https.html
index 365995f..40286c4 100644
--- a/WebKit/LayoutTests/http/tests/background_fetch/background-fetch-manager-fetch.https.html
+++ b/WebKit/LayoutTests/http/tests/background_fetch/background-fetch-manager-fetch.https.html
@@ -8,13 +8,15 @@
<h1>BackgroundFetchManager.fetch()</h1>
<p>This test validates the behaviour of the fetch() method.</p>
-<!-- TODO(peter): Move this to the WPT directory when it's merged. -->
+<!-- TODO(peter): Move this to the WPT directory when it's merged and the
+ behaviour of fetch() for null and empty sequences is defined. -->
<script>
'use strict';
const workerUrl = 'resources/empty-worker.js';
const scope = 'resources/scope/' + location.pathname;
+const tag = 'my-background-fetch';
promise_test(function(test) {
return service_worker_unregister_and_register(test, workerUrl, scope)
@@ -29,7 +31,42 @@
}, 'BackgroundFetchManager.fetch() requires an activated Service Worker.');
promise_test(function(test) {
- const tag = 'my-background-fetch';
+ let registration = null;
+ return service_worker_unregister_and_register(test, workerUrl, scope)
+ .then(r => {
+ registration = r;
+ return wait_for_state(test, r.installing, 'activated');
+ })
+ .then(() => registration.backgroundFetch.fetch(tag, null))
+ .then(() => unreached_fulfillment(test), () => true /* pass */);
+
+}, 'BackgroundFetchManager.fetch() throws when given a null request.');
+
+promise_test(function(test) {
+ let registration = null;
+ return service_worker_unregister_and_register(test, workerUrl, scope)
+ .then(r => {
+ registration = r;
+ return wait_for_state(test, r.installing, 'activated');
+ })
+ .then(() => registration.backgroundFetch.fetch(tag, []))
+ .then(() => unreached_fulfillment(test), () => true /* pass */);
+
+}, 'BackgroundFetchManager.fetch() throws when given an empty sequence.');
+
+promise_test(function(test) {
+ let registration = null;
+ return service_worker_unregister_and_register(test, workerUrl, scope)
+ .then(r => {
+ registration = r;
+ return wait_for_state(test, r.installing, 'activated');
+ })
+ .then(() => registration.backgroundFetch.fetch(tag, ['resources/non-existing-file.png', null]))
+ .then(() => unreached_fulfillment(test), () => true /* pass */);
+
+}, 'BackgroundFetchManager.fetch() throws when given a sequence with a null request.');
+
+promise_test(function(test) {
const options = {
icons: [
{
diff --git a/WebKit/Source/modules/BUILD.gn b/WebKit/Source/modules/BUILD.gn
index 91e8181..59c50ca 100644
--- a/WebKit/Source/modules/BUILD.gn
+++ b/WebKit/Source/modules/BUILD.gn
@@ -234,6 +234,7 @@
sources = [
"accessibility/AXObjectTest.cpp",
+ "background_fetch/BackgroundFetchManagerTest.cpp",
"cachestorage/CacheTest.cpp",
"canvas/HTMLCanvasElementModuleTest.cpp",
"canvas2d/CanvasRenderingContext2DAPITest.cpp",
diff --git a/WebKit/Source/modules/background_fetch/BackgroundFetchBridge.cpp b/WebKit/Source/modules/background_fetch/BackgroundFetchBridge.cpp
index c65ff59..a0b283e 100644
--- a/WebKit/Source/modules/background_fetch/BackgroundFetchBridge.cpp
+++ b/WebKit/Source/modules/background_fetch/BackgroundFetchBridge.cpp
@@ -12,6 +12,7 @@
#include "public/platform/InterfaceProvider.h"
#include "public/platform/Platform.h"
#include "public/platform/modules/serviceworker/WebServiceWorkerRegistration.h"
+#include "public/platform/modules/serviceworker/WebServiceWorkerRequest.h"
namespace blink {
@@ -46,8 +47,10 @@
void BackgroundFetchBridge::fetch(
const String& tag,
+ Vector<WebServiceWorkerRequest> requests,
const BackgroundFetchOptions& options,
std::unique_ptr<RegistrationCallback> callback) {
+ // TODO(peter): Include |requests| in the Mojo call.
getService()->Fetch(
supplementable()->webRegistration()->registrationId(), tag,
mojom::blink::BackgroundFetchOptions::From(options),
diff --git a/WebKit/Source/modules/background_fetch/BackgroundFetchBridge.h b/WebKit/Source/modules/background_fetch/BackgroundFetchBridge.h
index c8c1067..4d67561 100644
--- a/WebKit/Source/modules/background_fetch/BackgroundFetchBridge.h
+++ b/WebKit/Source/modules/background_fetch/BackgroundFetchBridge.h
@@ -18,6 +18,7 @@
class BackgroundFetchOptions;
class BackgroundFetchRegistration;
+class WebServiceWorkerRequest;
// The bridge is responsible for establishing and maintaining the Mojo
// connection to the BackgroundFetchService. It's keyed on an active Service
@@ -45,6 +46,7 @@
// given |options| for the sequence of |requests|. The |callback| will be
// invoked when the registration has been created.
void fetch(const String& tag,
+ Vector<WebServiceWorkerRequest> requests,
const BackgroundFetchOptions&,
std::unique_ptr<RegistrationCallback>);
diff --git a/WebKit/Source/modules/background_fetch/BackgroundFetchManager.cpp b/WebKit/Source/modules/background_fetch/BackgroundFetchManager.cpp
index d1018f8..66a39ed 100644
--- a/WebKit/Source/modules/background_fetch/BackgroundFetchManager.cpp
+++ b/WebKit/Source/modules/background_fetch/BackgroundFetchManager.cpp
@@ -7,15 +7,30 @@
#include "bindings/core/v8/ScriptPromiseResolver.h"
#include "bindings/core/v8/ScriptState.h"
#include "bindings/core/v8/V8ThrowException.h"
+#include "bindings/modules/v8/RequestOrUSVString.h"
+#include "bindings/modules/v8/RequestOrUSVStringOrRequestOrUSVStringSequence.h"
#include "core/dom/DOMException.h"
#include "core/dom/ExceptionCode.h"
#include "modules/background_fetch/BackgroundFetchBridge.h"
#include "modules/background_fetch/BackgroundFetchOptions.h"
#include "modules/background_fetch/BackgroundFetchRegistration.h"
+#include "modules/fetch/Request.h"
#include "modules/serviceworkers/ServiceWorkerRegistration.h"
+#include "public/platform/modules/serviceworker/WebServiceWorkerRequest.h"
namespace blink {
+namespace {
+
+// Message for the TypeError thrown when an empty request sequence is seen.
+const char kEmptyRequestSequenceErrorMessage[] =
+ "At least one request must be given.";
+
+// Message for the TypeError thrown when a null request is seen.
+const char kNullRequestErrorMessage[] = "Requests must not be null.";
+
+} // namespace
+
BackgroundFetchManager::BackgroundFetchManager(
ServiceWorkerRegistration* registration)
: m_registration(registration) {
@@ -27,7 +42,8 @@
ScriptState* scriptState,
const String& tag,
const RequestOrUSVStringOrRequestOrUSVStringSequence& requests,
- const BackgroundFetchOptions& options) {
+ const BackgroundFetchOptions& options,
+ ExceptionState& exceptionState) {
if (!m_registration->active()) {
return ScriptPromise::reject(
scriptState,
@@ -36,12 +52,15 @@
"the ServiceWorkerRegistration."));
}
+ Vector<WebServiceWorkerRequest> webRequests =
+ createWebRequestVector(scriptState, requests, exceptionState);
+ if (exceptionState.hadException())
+ return ScriptPromise();
+
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise();
- // TODO(peter): Include the |requests| in the Mojo call.
-
- m_bridge->fetch(tag, options,
+ m_bridge->fetch(tag, std::move(webRequests), options,
WTF::bind(&BackgroundFetchManager::didFetch,
wrapPersistent(this), wrapPersistent(resolver)));
@@ -91,6 +110,65 @@
return promise;
}
+// static
+Vector<WebServiceWorkerRequest> BackgroundFetchManager::createWebRequestVector(
+ ScriptState* scriptState,
+ const RequestOrUSVStringOrRequestOrUSVStringSequence& requests,
+ ExceptionState& exceptionState) {
+ Vector<WebServiceWorkerRequest> webRequests;
+
+ if (requests.isRequestOrUSVStringSequence()) {
+ HeapVector<RequestOrUSVString> requestVector =
+ requests.getAsRequestOrUSVStringSequence();
+
+ // Throw a TypeError when the developer has passed an empty sequence.
+ if (!requestVector.size()) {
+ exceptionState.throwTypeError(kEmptyRequestSequenceErrorMessage);
+ return Vector<WebServiceWorkerRequest>();
+ }
+
+ webRequests.resize(requestVector.size());
+
+ for (size_t i = 0; i < requestVector.size(); ++i) {
+ const RequestOrUSVString& requestOrUrl = requestVector[i];
+
+ Request* request = nullptr;
+ if (requestOrUrl.isRequest()) {
+ request = requestOrUrl.getAsRequest();
+ } else if (requestOrUrl.isUSVString()) {
+ request = Request::create(scriptState, requestOrUrl.getAsUSVString(),
+ exceptionState);
+ if (exceptionState.hadException())
+ return Vector<WebServiceWorkerRequest>();
+ } else {
+ exceptionState.throwTypeError(kNullRequestErrorMessage);
+ return Vector<WebServiceWorkerRequest>();
+ }
+
+ DCHECK(request);
+ request->populateWebServiceWorkerRequest(webRequests[i]);
+ }
+ } else if (requests.isRequest()) {
+ DCHECK(requests.getAsRequest());
+ webRequests.resize(1);
+ requests.getAsRequest()->populateWebServiceWorkerRequest(webRequests[0]);
+ } else if (requests.isUSVString()) {
+ Request* request =
+ Request::create(scriptState, requests.getAsUSVString(), exceptionState);
+ if (exceptionState.hadException())
+ return Vector<WebServiceWorkerRequest>();
+
+ DCHECK(request);
+ webRequests.resize(1);
+ request->populateWebServiceWorkerRequest(webRequests[0]);
+ } else {
+ exceptionState.throwTypeError(kNullRequestErrorMessage);
+ return Vector<WebServiceWorkerRequest>();
+ }
+
+ return webRequests;
+}
+
void BackgroundFetchManager::didGetRegistration(
ScriptPromiseResolver* resolver,
mojom::blink::BackgroundFetchError error,
diff --git a/WebKit/Source/modules/background_fetch/BackgroundFetchManager.h b/WebKit/Source/modules/background_fetch/BackgroundFetchManager.h
index b524fd5..ad1c4f4 100644
--- a/WebKit/Source/modules/background_fetch/BackgroundFetchManager.h
+++ b/WebKit/Source/modules/background_fetch/BackgroundFetchManager.h
@@ -7,6 +7,7 @@
#include "bindings/core/v8/ScriptPromise.h"
#include "bindings/core/v8/ScriptWrappable.h"
+#include "modules/ModulesExport.h"
#include "platform/heap/GarbageCollected.h"
#include "platform/heap/Handle.h"
#include "public/platform/modules/background_fetch/background_fetch.mojom-blink.h"
@@ -16,14 +17,16 @@
class BackgroundFetchBridge;
class BackgroundFetchOptions;
class BackgroundFetchRegistration;
+class ExceptionState;
class RequestOrUSVStringOrRequestOrUSVStringSequence;
class ScriptPromiseResolver;
class ScriptState;
class ServiceWorkerRegistration;
+class WebServiceWorkerRequest;
// Implementation of the BackgroundFetchManager JavaScript object, accessible
// by developers through ServiceWorkerRegistration.backgroundFetch.
-class BackgroundFetchManager final
+class MODULES_EXPORT BackgroundFetchManager final
: public GarbageCollected<BackgroundFetchManager>,
public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
@@ -39,15 +42,25 @@
ScriptState*,
const String& tag,
const RequestOrUSVStringOrRequestOrUSVStringSequence& requests,
- const BackgroundFetchOptions&);
+ const BackgroundFetchOptions&,
+ ExceptionState&);
ScriptPromise get(ScriptState*, const String& tag);
ScriptPromise getTags(ScriptState*);
DECLARE_TRACE();
private:
+ friend class BackgroundFetchManagerTest;
+
explicit BackgroundFetchManager(ServiceWorkerRegistration*);
+ // Creates a vector of WebServiceWorkerRequest objects for the given set of
+ // |requests|, which can be either Request objects or URL strings.
+ static Vector<WebServiceWorkerRequest> createWebRequestVector(
+ ScriptState*,
+ const RequestOrUSVStringOrRequestOrUSVStringSequence& requests,
+ ExceptionState&);
+
void didFetch(ScriptPromiseResolver*,
mojom::blink::BackgroundFetchError,
BackgroundFetchRegistration*);
diff --git a/WebKit/Source/modules/background_fetch/BackgroundFetchManager.idl b/WebKit/Source/modules/background_fetch/BackgroundFetchManager.idl
index a30ad00..855ab3b 100644
--- a/WebKit/Source/modules/background_fetch/BackgroundFetchManager.idl
+++ b/WebKit/Source/modules/background_fetch/BackgroundFetchManager.idl
@@ -8,7 +8,7 @@
Exposed=(Window,Worker),
RuntimeEnabled=BackgroundFetch
] interface BackgroundFetchManager {
- [CallWith=ScriptState] Promise<BackgroundFetchRegistration> fetch(DOMString tag, (RequestInfo or sequence<RequestInfo>) requests, optional BackgroundFetchOptions options);
+ [CallWith=ScriptState, RaisesException] Promise<BackgroundFetchRegistration> fetch(DOMString tag, (RequestInfo or sequence<RequestInfo>) requests, optional BackgroundFetchOptions options);
[CallWith=ScriptState] Promise<BackgroundFetchRegistration?> get(DOMString tag);
[CallWith=ScriptState] Promise<FrozenArray<DOMString>> getTags();
};
diff --git a/WebKit/Source/modules/background_fetch/BackgroundFetchManagerTest.cpp b/WebKit/Source/modules/background_fetch/BackgroundFetchManagerTest.cpp
new file mode 100644
index 0000000..7bd0a0d
--- /dev/null
+++ b/WebKit/Source/modules/background_fetch/BackgroundFetchManagerTest.cpp
@@ -0,0 +1,185 @@
+// Copyright 2017 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 "modules/background_fetch/BackgroundFetchManager.h"
+
+#include "bindings/core/v8/Dictionary.h"
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/ScriptState.h"
+#include "bindings/core/v8/V8Binding.h"
+#include "bindings/core/v8/V8BindingForTesting.h"
+#include "bindings/modules/v8/RequestOrUSVString.h"
+#include "bindings/modules/v8/RequestOrUSVStringOrRequestOrUSVStringSequence.h"
+#include "core/dom/ExceptionCode.h"
+#include "modules/fetch/Request.h"
+#include "public/platform/modules/serviceworker/WebServiceWorkerRequest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class BackgroundFetchManagerTest : public ::testing::Test {
+ protected:
+ // Creates a vector of WebServiceWorkerRequest entries for the given
+ // |requests| based on the |scope|. Proxied in the fixture to reduce the
+ // number of friend declarations necessary in the BackgroundFetchManager.
+ Vector<WebServiceWorkerRequest> createWebRequestVector(
+ V8TestingScope& scope,
+ const RequestOrUSVStringOrRequestOrUSVStringSequence& requests) {
+ return BackgroundFetchManager::createWebRequestVector(
+ scope.getScriptState(), requests, scope.getExceptionState());
+ }
+
+ // Returns a Dictionary object that represents a JavaScript dictionary with
+ // a single key-value pair, where the key always is "method" with the value
+ // set to |method|.
+ Dictionary getDictionaryForMethod(V8TestingScope& scope, const char* method) {
+ v8::Isolate* isolate = scope.isolate();
+ v8::Local<v8::Object> data = v8::Object::New(isolate);
+
+ data->Set(isolate->GetCurrentContext(), v8String(isolate, "method"),
+ v8String(isolate, method))
+ .ToChecked();
+
+ return Dictionary(scope.isolate(), data, scope.getExceptionState());
+ }
+};
+
+TEST_F(BackgroundFetchManagerTest, NullValue) {
+ V8TestingScope scope;
+
+ RequestOrUSVStringOrRequestOrUSVStringSequence requests;
+
+ Vector<WebServiceWorkerRequest> webRequests =
+ createWebRequestVector(scope, requests);
+ ASSERT_TRUE(scope.getExceptionState().hadException());
+ EXPECT_EQ(scope.getExceptionState().code(), V8TypeError);
+}
+
+TEST_F(BackgroundFetchManagerTest, SingleUSVString) {
+ V8TestingScope scope;
+
+ KURL imageUrl(ParsedURLString, "https://www.example.com/my_image.png");
+
+ RequestOrUSVStringOrRequestOrUSVStringSequence requests =
+ RequestOrUSVStringOrRequestOrUSVStringSequence::fromUSVString(
+ imageUrl.getString());
+
+ Vector<WebServiceWorkerRequest> webRequests =
+ createWebRequestVector(scope, requests);
+ ASSERT_FALSE(scope.getExceptionState().hadException());
+
+ ASSERT_EQ(webRequests.size(), 1u);
+
+ WebServiceWorkerRequest& webRequest = webRequests[0];
+ EXPECT_EQ(webRequest.url(), WebURL(imageUrl));
+ EXPECT_EQ(webRequest.method(), "GET");
+}
+
+TEST_F(BackgroundFetchManagerTest, SingleRequest) {
+ V8TestingScope scope;
+
+ KURL imageUrl(ParsedURLString, "https://www.example.com/my_image.png");
+
+ Request* request = Request::create(
+ scope.getScriptState(), imageUrl.getString(),
+ getDictionaryForMethod(scope, "POST"), scope.getExceptionState());
+ ASSERT_FALSE(scope.getExceptionState().hadException());
+ ASSERT_TRUE(request);
+
+ RequestOrUSVStringOrRequestOrUSVStringSequence requests =
+ RequestOrUSVStringOrRequestOrUSVStringSequence::fromRequest(request);
+
+ Vector<WebServiceWorkerRequest> webRequests =
+ createWebRequestVector(scope, requests);
+ ASSERT_FALSE(scope.getExceptionState().hadException());
+
+ ASSERT_EQ(webRequests.size(), 1u);
+
+ WebServiceWorkerRequest& webRequest = webRequests[0];
+ EXPECT_EQ(webRequest.url(), WebURL(imageUrl));
+ EXPECT_EQ(webRequest.method(), "POST");
+}
+
+TEST_F(BackgroundFetchManagerTest, Sequence) {
+ V8TestingScope scope;
+
+ KURL imageUrl(ParsedURLString, "https://www.example.com/my_image.png");
+ KURL iconUrl(ParsedURLString, "https://www.example.com/my_icon.jpg");
+ KURL catVideoUrl(ParsedURLString, "https://www.example.com/my_cat_video.avi");
+
+ RequestOrUSVString imageRequest =
+ RequestOrUSVString::fromUSVString(imageUrl.getString());
+ RequestOrUSVString iconRequest =
+ RequestOrUSVString::fromUSVString(iconUrl.getString());
+
+ Request* request = Request::create(
+ scope.getScriptState(), catVideoUrl.getString(),
+ getDictionaryForMethod(scope, "DELETE"), scope.getExceptionState());
+ ASSERT_FALSE(scope.getExceptionState().hadException());
+ ASSERT_TRUE(request);
+
+ RequestOrUSVString catVideoRequest = RequestOrUSVString::fromRequest(request);
+
+ HeapVector<RequestOrUSVString> requestSequence;
+ requestSequence.push_back(imageRequest);
+ requestSequence.push_back(iconRequest);
+ requestSequence.push_back(catVideoRequest);
+
+ RequestOrUSVStringOrRequestOrUSVStringSequence requests =
+ RequestOrUSVStringOrRequestOrUSVStringSequence::
+ fromRequestOrUSVStringSequence(requestSequence);
+
+ Vector<WebServiceWorkerRequest> webRequests =
+ createWebRequestVector(scope, requests);
+ ASSERT_FALSE(scope.getExceptionState().hadException());
+
+ ASSERT_EQ(webRequests.size(), 3u);
+ EXPECT_EQ(webRequests[0].url(), WebURL(imageUrl));
+ EXPECT_EQ(webRequests[0].method(), "GET");
+
+ EXPECT_EQ(webRequests[1].url(), WebURL(iconUrl));
+ EXPECT_EQ(webRequests[1].method(), "GET");
+
+ EXPECT_EQ(webRequests[2].url(), WebURL(catVideoUrl));
+ EXPECT_EQ(webRequests[2].method(), "DELETE");
+}
+
+TEST_F(BackgroundFetchManagerTest, SequenceEmpty) {
+ V8TestingScope scope;
+
+ HeapVector<RequestOrUSVString> requestSequence;
+ RequestOrUSVStringOrRequestOrUSVStringSequence requests =
+ RequestOrUSVStringOrRequestOrUSVStringSequence::
+ fromRequestOrUSVStringSequence(requestSequence);
+
+ Vector<WebServiceWorkerRequest> webRequests =
+ createWebRequestVector(scope, requests);
+ ASSERT_TRUE(scope.getExceptionState().hadException());
+ EXPECT_EQ(scope.getExceptionState().code(), V8TypeError);
+}
+
+TEST_F(BackgroundFetchManagerTest, SequenceWithNullValue) {
+ V8TestingScope scope;
+
+ KURL imageUrl(ParsedURLString, "https://www.example.com/my_image.png");
+
+ RequestOrUSVString nullRequest;
+ RequestOrUSVString imageRequest =
+ RequestOrUSVString::fromUSVString(imageUrl.getString());
+
+ HeapVector<RequestOrUSVString> requestSequence;
+ requestSequence.push_back(imageRequest);
+ requestSequence.push_back(nullRequest);
+
+ RequestOrUSVStringOrRequestOrUSVStringSequence requests =
+ RequestOrUSVStringOrRequestOrUSVStringSequence::
+ fromRequestOrUSVStringSequence(requestSequence);
+
+ Vector<WebServiceWorkerRequest> webRequests =
+ createWebRequestVector(scope, requests);
+ ASSERT_TRUE(scope.getExceptionState().hadException());
+ EXPECT_EQ(scope.getExceptionState().code(), V8TypeError);
+}
+
+} // namespace blink