[ServiceWorker] Fills SSLInfo of the response from a SW with the SSLInfo of the SW script.
After https://codereview.chromium.org/874833003/, every mixed-content requests from the ServiceWorker are blocked. So we can show the HTTPS padlock because every responses from the ServiceWorker to the page are created using the resources which are served through the secure protocol. To show the padlock we fill the SSLInfo of the response from the ServiceWorker using the SSLInfo of the ServiceWorker script. When we support fetching mixed-content requests from the ServiceWorker in the future, we have to check the security level of the responses. BUG=392409 Review URL: https://codereview.chromium.org/877623002 Cr-Commit-Position: refs/heads/master@{#314549}
This commit is contained in:
content
browser
service_worker
service_worker_browsertest.ccservice_worker_context_request_handler.ccservice_worker_read_from_cache_job.ccservice_worker_read_from_cache_job.hservice_worker_url_request_job.ccservice_worker_url_request_job.hservice_worker_url_request_job_unittest.ccservice_worker_version.ccservice_worker_version.hservice_worker_write_to_cache_job.cc
test
data
service_worker
@ -21,11 +21,14 @@
|
||||
#include "content/common/service_worker/service_worker_types.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/navigation_entry.h"
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "content/public/browser/storage_partition.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "content/public/common/referrer.h"
|
||||
#include "content/public/common/security_style.h"
|
||||
#include "content/public/common/ssl_status.h"
|
||||
#include "content/public/test/browser_test_utils.h"
|
||||
#include "content/public/test/content_browser_test.h"
|
||||
#include "content/public/test/content_browser_test_utils.h"
|
||||
@ -766,45 +769,110 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, SyncEventHandled) {
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, Reload) {
|
||||
const std::string kPageUrl = "/service_worker/reload.html";
|
||||
const std::string kWorkerUrl = "/service_worker/fetch_event_reload.js";
|
||||
{
|
||||
scoped_refptr<WorkerActivatedObserver> observer =
|
||||
new WorkerActivatedObserver(wrapper());
|
||||
observer->Init();
|
||||
public_context()->RegisterServiceWorker(
|
||||
embedded_test_server()->GetURL(kPageUrl),
|
||||
embedded_test_server()->GetURL(kWorkerUrl),
|
||||
base::Bind(&ExpectResultAndRun, true, base::Bind(&base::DoNothing)));
|
||||
observer->Wait();
|
||||
}
|
||||
{
|
||||
const base::string16 title = base::ASCIIToUTF16("reload=false");
|
||||
TitleWatcher title_watcher(shell()->web_contents(), title);
|
||||
NavigateToURL(shell(), embedded_test_server()->GetURL(kPageUrl));
|
||||
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
|
||||
}
|
||||
{
|
||||
const base::string16 title = base::ASCIIToUTF16("reload=true");
|
||||
TitleWatcher title_watcher(shell()->web_contents(), title);
|
||||
ReloadBlockUntilNavigationsComplete(shell(), 1);
|
||||
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
|
||||
}
|
||||
const char kPageUrl[] = "/service_worker/reload.html";
|
||||
const char kWorkerUrl[] = "/service_worker/fetch_event_reload.js";
|
||||
scoped_refptr<WorkerActivatedObserver> observer =
|
||||
new WorkerActivatedObserver(wrapper());
|
||||
observer->Init();
|
||||
public_context()->RegisterServiceWorker(
|
||||
embedded_test_server()->GetURL(kPageUrl),
|
||||
embedded_test_server()->GetURL(kWorkerUrl),
|
||||
base::Bind(&ExpectResultAndRun, true, base::Bind(&base::DoNothing)));
|
||||
observer->Wait();
|
||||
|
||||
const base::string16 title1 = base::ASCIIToUTF16("reload=false");
|
||||
TitleWatcher title_watcher1(shell()->web_contents(), title1);
|
||||
NavigateToURL(shell(), embedded_test_server()->GetURL(kPageUrl));
|
||||
EXPECT_EQ(title1, title_watcher1.WaitAndGetTitle());
|
||||
|
||||
const base::string16 title2 = base::ASCIIToUTF16("reload=true");
|
||||
TitleWatcher title_watcher2(shell()->web_contents(), title2);
|
||||
ReloadBlockUntilNavigationsComplete(shell(), 1);
|
||||
EXPECT_EQ(title2, title_watcher2.WaitAndGetTitle());
|
||||
|
||||
shell()->Close();
|
||||
{
|
||||
base::RunLoop run_loop;
|
||||
public_context()->UnregisterServiceWorker(
|
||||
embedded_test_server()->GetURL(kPageUrl),
|
||||
base::Bind(&ExpectResultAndRun, true, run_loop.QuitClosure()));
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
base::RunLoop run_loop;
|
||||
public_context()->UnregisterServiceWorker(
|
||||
embedded_test_server()->GetURL(kPageUrl),
|
||||
base::Bind(&ExpectResultAndRun, true, run_loop.QuitClosure()));
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest,
|
||||
ResponseFromHTTPSServiceWorkerIsMarkedAsSecure) {
|
||||
const char kPageUrl[] = "files/service_worker/fetch_event_blob.html";
|
||||
const char kWorkerUrl[] = "files/service_worker/fetch_event_blob.js";
|
||||
net::SpawnedTestServer https_server(
|
||||
net::SpawnedTestServer::TYPE_HTTPS,
|
||||
net::BaseTestServer::SSLOptions(
|
||||
net::BaseTestServer::SSLOptions::CERT_OK),
|
||||
base::FilePath(FILE_PATH_LITERAL("content/test/data/")));
|
||||
ASSERT_TRUE(https_server.Start());
|
||||
|
||||
scoped_refptr<WorkerActivatedObserver> observer =
|
||||
new WorkerActivatedObserver(wrapper());
|
||||
observer->Init();
|
||||
public_context()->RegisterServiceWorker(
|
||||
https_server.GetURL(kPageUrl),
|
||||
https_server.GetURL(kWorkerUrl),
|
||||
base::Bind(&ExpectResultAndRun, true, base::Bind(&base::DoNothing)));
|
||||
observer->Wait();
|
||||
|
||||
const base::string16 title = base::ASCIIToUTF16("Title");
|
||||
TitleWatcher title_watcher(shell()->web_contents(), title);
|
||||
NavigateToURL(shell(), https_server.GetURL(kPageUrl));
|
||||
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
|
||||
EXPECT_FALSE(shell()->web_contents()->DisplayedInsecureContent());
|
||||
NavigationEntry* entry =
|
||||
shell()->web_contents()->GetController().GetVisibleEntry();
|
||||
EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, entry->GetSSL().security_style);
|
||||
|
||||
shell()->Close();
|
||||
|
||||
base::RunLoop run_loop;
|
||||
public_context()->UnregisterServiceWorker(
|
||||
https_server.GetURL(kPageUrl),
|
||||
base::Bind(&ExpectResultAndRun, true, run_loop.QuitClosure()));
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest,
|
||||
ResponseFromHTTPServiceWorkerIsNotMarkedAsSecure) {
|
||||
const char kPageUrl[] = "/service_worker/fetch_event_blob.html";
|
||||
const char kWorkerUrl[] = "/service_worker/fetch_event_blob.js";
|
||||
scoped_refptr<WorkerActivatedObserver> observer =
|
||||
new WorkerActivatedObserver(wrapper());
|
||||
observer->Init();
|
||||
public_context()->RegisterServiceWorker(
|
||||
embedded_test_server()->GetURL(kPageUrl),
|
||||
embedded_test_server()->GetURL(kWorkerUrl),
|
||||
base::Bind(&ExpectResultAndRun, true, base::Bind(&base::DoNothing)));
|
||||
observer->Wait();
|
||||
|
||||
const base::string16 title = base::ASCIIToUTF16("Title");
|
||||
TitleWatcher title_watcher(shell()->web_contents(), title);
|
||||
NavigateToURL(shell(), embedded_test_server()->GetURL(kPageUrl));
|
||||
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
|
||||
EXPECT_FALSE(shell()->web_contents()->DisplayedInsecureContent());
|
||||
NavigationEntry* entry =
|
||||
shell()->web_contents()->GetController().GetVisibleEntry();
|
||||
EXPECT_EQ(SECURITY_STYLE_UNAUTHENTICATED, entry->GetSSL().security_style);
|
||||
|
||||
shell()->Close();
|
||||
|
||||
base::RunLoop run_loop;
|
||||
public_context()->UnregisterServiceWorker(
|
||||
embedded_test_server()->GetURL(kPageUrl),
|
||||
base::Bind(&ExpectResultAndRun, true, run_loop.QuitClosure()));
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, ImportsBustMemcache) {
|
||||
const std::string kScopeUrl = "/service_worker/imports_bust_memcache_scope/";
|
||||
const std::string kPageUrl = "/service_worker/imports_bust_memcache.html";
|
||||
const std::string kScriptUrl = "/service_worker/worker_with_one_import.js";
|
||||
const std::string kImportUrl = "/service_worker/long_lived_import.js";
|
||||
const char kScopeUrl[] = "/service_worker/imports_bust_memcache_scope/";
|
||||
const char kPageUrl[] = "/service_worker/imports_bust_memcache.html";
|
||||
const char kScriptUrl[] = "/service_worker/worker_with_one_import.js";
|
||||
const char kImportUrl[] = "/service_worker/long_lived_import.js";
|
||||
const base::string16 kOKTitle(base::ASCIIToUTF16("OK"));
|
||||
const base::string16 kFailTitle(base::ASCIIToUTF16("FAIL"));
|
||||
|
||||
@ -878,8 +946,8 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBlackBoxBrowserTest, MAYBE_Registration) {
|
||||
shell()->Close();
|
||||
EXPECT_EQ(0, CountRenderProcessHosts());
|
||||
|
||||
const std::string kWorkerUrl = "/service_worker/fetch_event.js";
|
||||
const std::string kScope = "/service_worker/";
|
||||
const char kWorkerUrl[] = "/service_worker/fetch_event.js";
|
||||
const char kScope[] = "/service_worker/";
|
||||
|
||||
// Unregistering nothing should return false.
|
||||
{
|
||||
@ -953,7 +1021,7 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBlackBoxBrowserTest, MAYBE_Registration) {
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, CrossSiteTransfer) {
|
||||
// The first page registers a service worker.
|
||||
const std::string kRegisterPageUrl = "/service_worker/cross_site_xfer.html";
|
||||
const char kRegisterPageUrl[] = "/service_worker/cross_site_xfer.html";
|
||||
const base::string16 kOKTitle1(base::ASCIIToUTF16("OK_1"));
|
||||
const base::string16 kFailTitle1(base::ASCIIToUTF16("FAIL_1"));
|
||||
content::TitleWatcher title_watcher1(shell()->web_contents(), kOKTitle1);
|
||||
@ -966,7 +1034,7 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, CrossSiteTransfer) {
|
||||
ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
|
||||
|
||||
// The second pages loads via the serviceworker including a subresource.
|
||||
const std::string kConfirmPageUrl =
|
||||
const char kConfirmPageUrl[] =
|
||||
"/service_worker/cross_site_xfer_scope/"
|
||||
"cross_site_xfer_confirm_via_serviceworker.html";
|
||||
const base::string16 kOKTitle2(base::ASCIIToUTF16("OK_2"));
|
||||
|
@ -83,7 +83,7 @@ net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJob(
|
||||
int64 response_id = kInvalidServiceWorkerResponseId;
|
||||
if (ShouldReadFromScriptCache(request->url(), &response_id)) {
|
||||
return new ServiceWorkerReadFromCacheJob(
|
||||
request, network_delegate, context_, response_id);
|
||||
request, network_delegate, context_, version_, response_id);
|
||||
}
|
||||
|
||||
// NULL means use the network.
|
||||
|
@ -26,9 +26,11 @@ ServiceWorkerReadFromCacheJob::ServiceWorkerReadFromCacheJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
base::WeakPtr<ServiceWorkerContextCore> context,
|
||||
const scoped_refptr<ServiceWorkerVersion>& version,
|
||||
int64 response_id)
|
||||
: net::URLRequestJob(request, network_delegate),
|
||||
context_(context),
|
||||
version_(version),
|
||||
response_id_(response_id),
|
||||
has_been_killed_(false),
|
||||
weak_factory_(this) {
|
||||
@ -164,6 +166,8 @@ void ServiceWorkerReadFromCacheJob::OnReadInfoComplete(int result) {
|
||||
if (is_range_request())
|
||||
SetupRangeResponse(http_info_io_buffer_->response_data_size);
|
||||
http_info_io_buffer_ = NULL;
|
||||
if (request_->url() == version_->script_url())
|
||||
version_->SetMainScriptHttpResponseInfo(*http_info_);
|
||||
TRACE_EVENT_ASYNC_END1("ServiceWorker",
|
||||
"ServiceWorkerReadFromCacheJob::ReadInfo",
|
||||
this,
|
||||
|
@ -18,6 +18,7 @@ namespace content {
|
||||
|
||||
class ServiceWorkerContextCore;
|
||||
class ServiceWorkerResponseReader;
|
||||
class ServiceWorkerVersion;
|
||||
|
||||
// A URLRequestJob derivative used to retrieve script resources
|
||||
// from the service workers script cache. It uses a response reader
|
||||
@ -29,6 +30,7 @@ class CONTENT_EXPORT ServiceWorkerReadFromCacheJob
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
base::WeakPtr<ServiceWorkerContextCore> context,
|
||||
const scoped_refptr<ServiceWorkerVersion>& version,
|
||||
int64 response_id);
|
||||
|
||||
private:
|
||||
@ -55,6 +57,7 @@ class CONTENT_EXPORT ServiceWorkerReadFromCacheJob
|
||||
void SetupRangeResponse(int response_data_size);
|
||||
|
||||
base::WeakPtr<ServiceWorkerContextCore> context_;
|
||||
scoped_refptr<ServiceWorkerVersion> version_;
|
||||
int64 response_id_;
|
||||
scoped_ptr<ServiceWorkerResponseReader> reader_;
|
||||
scoped_refptr<HttpResponseInfoIOBuffer> http_info_io_buffer_;
|
||||
|
@ -112,7 +112,9 @@ bool ServiceWorkerURLRequestJob::GetMimeType(std::string* mime_type) const {
|
||||
void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
|
||||
if (!http_info())
|
||||
return;
|
||||
const base::Time request_time = info->request_time;
|
||||
*info = *http_info();
|
||||
info->request_time = request_time;
|
||||
info->response_time = response_time_;
|
||||
}
|
||||
|
||||
@ -538,6 +540,16 @@ void ServiceWorkerURLRequestJob::DidDispatchFetchEvent(
|
||||
fetch_end_time_ = base::TimeTicks::Now();
|
||||
load_timing_info_.send_end = fetch_end_time_;
|
||||
|
||||
// Creates a new HttpResponseInfo using the the ServiceWorker script's
|
||||
// HttpResponseInfo to show HTTPS padlock.
|
||||
// TODO(horo): When we support mixed-content (HTTP) no-cors requests from a
|
||||
// ServiceWorker, we have to check the security level of the responses.
|
||||
DCHECK(!http_response_info_);
|
||||
const net::HttpResponseInfo* main_script_http_info =
|
||||
provider_host_->active_version()->GetMainScriptHttpResponseInfo();
|
||||
DCHECK(main_script_http_info);
|
||||
http_response_info_.reset(new net::HttpResponseInfo(*main_script_http_info));
|
||||
|
||||
// Set up a request for reading the stream.
|
||||
if (response.stream_url.is_valid()) {
|
||||
DCHECK(response.blob_uuid.empty());
|
||||
@ -610,8 +622,11 @@ void ServiceWorkerURLRequestJob::CreateResponseHeader(
|
||||
}
|
||||
|
||||
void ServiceWorkerURLRequestJob::CommitResponseHeader() {
|
||||
http_response_info_.reset(new net::HttpResponseInfo());
|
||||
if (!http_response_info_)
|
||||
http_response_info_.reset(new net::HttpResponseInfo());
|
||||
http_response_info_->headers.swap(http_response_headers_);
|
||||
http_response_info_->vary_data = net::HttpVaryData();
|
||||
http_response_info_->metadata = nullptr;
|
||||
NotifyHeadersComplete();
|
||||
}
|
||||
|
||||
|
@ -108,8 +108,6 @@ class CONTENT_EXPORT ServiceWorkerURLRequestJob
|
||||
// StreamRegisterObserver override:
|
||||
void OnStreamRegistered(Stream* stream) override;
|
||||
|
||||
const net::HttpResponseInfo* http_info() const;
|
||||
|
||||
void GetExtraResponseInfo(
|
||||
bool* was_fetched_via_service_worker,
|
||||
bool* was_fallback_required_by_service_worker,
|
||||
@ -163,6 +161,8 @@ class CONTENT_EXPORT ServiceWorkerURLRequestJob
|
||||
// Releases the resources for streaming.
|
||||
void ClearStream();
|
||||
|
||||
const net::HttpResponseInfo* http_info() const;
|
||||
|
||||
base::WeakPtr<ServiceWorkerProviderHost> provider_host_;
|
||||
|
||||
// Timing info to show on the popup in Devtools' Network tab.
|
||||
|
@ -31,8 +31,11 @@
|
||||
#include "content/public/test/test_browser_context.h"
|
||||
#include "content/public/test/test_browser_thread_bundle.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/base/test_data_directory.h"
|
||||
#include "net/http/http_request_headers.h"
|
||||
#include "net/http/http_response_headers.h"
|
||||
#include "net/ssl/ssl_info.h"
|
||||
#include "net/test/cert_test_util.h"
|
||||
#include "net/url_request/url_request.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
#include "net/url_request/url_request_job_factory_impl.h"
|
||||
@ -125,6 +128,15 @@ class ServiceWorkerURLRequestJobTest : public testing::Test {
|
||||
GURL("http://example.com/service_worker.js"),
|
||||
1L,
|
||||
helper_->context()->AsWeakPtr());
|
||||
net::HttpResponseInfo http_info;
|
||||
http_info.ssl_info.cert =
|
||||
net::ImportCertFromFile(net::GetTestCertsDirectory(),
|
||||
"ok_cert.pem");
|
||||
EXPECT_TRUE(http_info.ssl_info.is_valid());
|
||||
http_info.ssl_info.security_bits = 0x100;
|
||||
// SSL3 TLS_DHE_RSA_WITH_AES_256_CBC_SHA
|
||||
http_info.ssl_info.connection_status = 0x300039;
|
||||
version_->SetMainScriptHttpResponseInfo(http_info);
|
||||
|
||||
scoped_ptr<ServiceWorkerProviderHost> provider_host(
|
||||
new ServiceWorkerProviderHost(
|
||||
@ -180,6 +192,10 @@ class ServiceWorkerURLRequestJobTest : public testing::Test {
|
||||
EXPECT_EQ(expected_status_text,
|
||||
request_->response_headers()->GetStatusText());
|
||||
EXPECT_EQ(expected_response, url_request_delegate_.response_data());
|
||||
const net::SSLInfo& ssl_info = request_->response_info().ssl_info;
|
||||
EXPECT_TRUE(ssl_info.is_valid());
|
||||
EXPECT_EQ(ssl_info.security_bits, 0x100);
|
||||
EXPECT_EQ(ssl_info.connection_status, 0x300039);
|
||||
}
|
||||
|
||||
bool HasInflightRequests() {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "content/common/service_worker/service_worker_messages.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "net/http/http_response_info.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
@ -676,6 +677,16 @@ void ServiceWorkerVersion::SetDevToolsAttached(bool attached) {
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceWorkerVersion::SetMainScriptHttpResponseInfo(
|
||||
const net::HttpResponseInfo& http_info) {
|
||||
main_script_http_info_.reset(new net::HttpResponseInfo(http_info));
|
||||
}
|
||||
|
||||
const net::HttpResponseInfo*
|
||||
ServiceWorkerVersion::GetMainScriptHttpResponseInfo() {
|
||||
return main_script_http_info_.get();
|
||||
}
|
||||
|
||||
void ServiceWorkerVersion::OnStarted() {
|
||||
DCHECK_EQ(RUNNING, running_status());
|
||||
DCHECK(cache_listener_.get());
|
||||
|
@ -33,6 +33,10 @@ namespace blink {
|
||||
struct WebCircularGeofencingRegion;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
class HttpResponseInfo;
|
||||
}
|
||||
|
||||
namespace content {
|
||||
|
||||
class EmbeddedWorkerRegistry;
|
||||
@ -292,6 +296,13 @@ class CONTENT_EXPORT ServiceWorkerVersion
|
||||
|
||||
void SetDevToolsAttached(bool attached);
|
||||
|
||||
// Sets the HttpResponseInfo used to load the main script.
|
||||
// This HttpResponseInfo will be used for all responses sent back from the
|
||||
// service worker, as the effective security of these responses is equivalent
|
||||
// to that of the ServiceWorker.
|
||||
void SetMainScriptHttpResponseInfo(const net::HttpResponseInfo& http_info);
|
||||
const net::HttpResponseInfo* GetMainScriptHttpResponseInfo();
|
||||
|
||||
private:
|
||||
class GetClientDocumentsCallback;
|
||||
|
||||
@ -411,6 +422,7 @@ class CONTENT_EXPORT ServiceWorkerVersion
|
||||
bool is_doomed_;
|
||||
std::vector<int> pending_skip_waiting_requests_;
|
||||
bool skip_waiting_;
|
||||
scoped_ptr<net::HttpResponseInfo> main_script_http_info_;
|
||||
|
||||
base::WeakPtrFactory<ServiceWorkerVersion> weak_factory_;
|
||||
|
||||
|
@ -418,6 +418,8 @@ void ServiceWorkerWriteToCacheJob::OnResponseStarted(
|
||||
|
||||
if (!CheckPathRestriction(request))
|
||||
return;
|
||||
|
||||
version_->SetMainScriptHttpResponseInfo(net_request_->response_info());
|
||||
}
|
||||
|
||||
WriteHeadersToCache();
|
||||
|
@ -6,6 +6,7 @@
|
||||
['OS=="android" or OS=="linux" or OS=="mac" or OS=="win"', {
|
||||
'variables': {
|
||||
'files': [
|
||||
'../net/data/ssl/certificates/',
|
||||
'../media/test/data/',
|
||||
'test/data/',
|
||||
],
|
||||
|
9
content/test/data/service_worker/fetch_event_blob.js
Normal file
9
content/test/data/service_worker/fetch_event_blob.js
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
this.onfetch = function(event) {
|
||||
var blob = new Blob(['<title>Title</title>']);
|
||||
var response = new Response(blob);
|
||||
event.respondWith(response);
|
||||
};
|
@ -0,0 +1,2 @@
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/javascript
|
Reference in New Issue
Block a user