diff options
Diffstat (limited to 'dom/workers')
-rw-r--r-- | dom/workers/ServiceWorkerEvents.cpp | 10 | ||||
-rw-r--r-- | dom/workers/ServiceWorkerPrivate.cpp | 45 | ||||
-rw-r--r-- | dom/workers/ServiceWorkerPrivate.h | 1 | ||||
-rw-r--r-- | dom/workers/test/serviceworkers/chrome.ini | 3 | ||||
-rw-r--r-- | dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html | 168 |
5 files changed, 224 insertions, 3 deletions
diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 780b2f5f8..1f79e2c92 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -12,6 +12,7 @@ #include "nsINetworkInterceptController.h" #include "nsIOutputStream.h" #include "nsIScriptError.h" +#include "nsITimedChannel.h" #include "nsIUnicodeDecoder.h" #include "nsIUnicodeEncoder.h" #include "nsContentPolicyUtils.h" @@ -108,6 +109,12 @@ NS_IMETHODIMP CancelChannelRunnable::Run() { MOZ_ASSERT(NS_IsMainThread()); + + // TODO: When bug 1204254 is implemented, this time marker should be moved to + // the point where the body of the network request is complete. + mChannel->SetHandleFetchEventEnd(TimeStamp::Now()); + mChannel->SaveTimeStampsToUnderlyingChannel(); + mChannel->Cancel(mStatus); mRegistration->MaybeScheduleUpdate(); return NS_OK; @@ -230,6 +237,9 @@ public: return NS_OK; } + mChannel->SetHandleFetchEventEnd(TimeStamp::Now()); + mChannel->SaveTimeStampsToUnderlyingChannel(); + nsCOMPtr<nsIObserverService> obsService = services::GetObserverService(); if (obsService) { obsService->NotifyObservers(underlyingChannel, "service-worker-synthesized-response", nullptr); diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp index eaa548f95..24b2e11e6 100644 --- a/dom/workers/ServiceWorkerPrivate.cpp +++ b/dom/workers/ServiceWorkerPrivate.cpp @@ -13,6 +13,7 @@ #include "nsINetworkInterceptController.h" #include "nsIPushErrorReporter.h" #include "nsISupportsImpl.h" +#include "nsITimedChannel.h" #include "nsIUploadChannel2.h" #include "nsNetUtil.h" #include "nsProxyRelease.h" @@ -1255,6 +1256,7 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable nsCString mMethod; nsString mClientId; bool mIsReload; + bool mMarkLaunchServiceWorkerEnd; RequestCache mCacheMode; RequestMode mRequestMode; RequestRedirect mRequestRedirect; @@ -1273,13 +1275,15 @@ public: const nsACString& aScriptSpec, nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration, const nsAString& aDocumentId, - bool aIsReload) + bool aIsReload, + bool aMarkLaunchServiceWorkerEnd) : ExtendableFunctionalEventWorkerRunnable( aWorkerPrivate, aKeepAliveToken, aRegistration) , mInterceptedChannel(aChannel) , mScriptSpec(aScriptSpec) , mClientId(aDocumentId) , mIsReload(aIsReload) + , mMarkLaunchServiceWorkerEnd(aMarkLaunchServiceWorkerEnd) , mCacheMode(RequestCache::Default) , mRequestMode(RequestMode::No_cors) , mRequestRedirect(RequestRedirect::Follow) @@ -1417,6 +1421,12 @@ public: WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { MOZ_ASSERT(aWorkerPrivate); + + if (mMarkLaunchServiceWorkerEnd) { + mInterceptedChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now()); + } + + mInterceptedChannel->SetDispatchFetchEventEnd(TimeStamp::Now()); return DispatchFetchEvent(aCx, aWorkerPrivate); } @@ -1445,6 +1455,10 @@ private: NS_IMETHOD Run() override { AssertIsOnMainThread(); + + mChannel->SetHandleFetchEventEnd(TimeStamp::Now()); + mChannel->SaveTimeStampsToUnderlyingChannel(); + nsresult rv = mChannel->ResetInterception(); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to resume intercepted network request"); @@ -1520,6 +1534,8 @@ private: event->PostInit(mInterceptedChannel, mRegistration, mScriptSpec); event->SetTrusted(true); + mInterceptedChannel->SetHandleFetchEventStart(TimeStamp::Now()); + RefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope()); nsresult rv2 = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr); if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) { @@ -1614,9 +1630,21 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, nsCOMPtr<nsIRunnable> failRunnable = NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception); - nsresult rv = SpawnWorkerIfNeeded(FetchEvent, failRunnable, aLoadGroup); + aChannel->SetLaunchServiceWorkerStart(TimeStamp::Now()); + aChannel->SetDispatchFetchEventStart(TimeStamp::Now()); + + bool newWorkerCreated = false; + nsresult rv = SpawnWorkerIfNeeded(FetchEvent, + failRunnable, + &newWorkerCreated, + aLoadGroup); + NS_ENSURE_SUCCESS(rv, rv); + if (!newWorkerCreated) { + aChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now()); + } + nsMainThreadPtrHandle<nsIInterceptedChannel> handle( new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false)); @@ -1646,7 +1674,7 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, RefPtr<FetchEventRunnable> r = new FetchEventRunnable(mWorkerPrivate, token, handle, mInfo->ScriptSpec(), regInfo, - aDocumentId, aIsReload); + aDocumentId, aIsReload, newWorkerCreated); rv = r->Init(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -1669,6 +1697,7 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, nsresult ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy, nsIRunnable* aLoadFailedRunnable, + bool* aNewWorkerCreated, nsILoadGroup* aLoadGroup) { AssertIsOnMainThread(); @@ -1679,6 +1708,12 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy, // the overriden load group when intercepting a fetch. MOZ_ASSERT_IF(aWhy == FetchEvent, aLoadGroup); + // Defaults to no new worker created, but if there is one, we'll set the value + // to true at the end of this function. + if (aNewWorkerCreated) { + *aNewWorkerCreated = false; + } + if (mWorkerPrivate) { mWorkerPrivate->UpdateOverridenLoadGroup(aLoadGroup); RenewKeepAliveToken(aWhy); @@ -1762,6 +1797,10 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy, RenewKeepAliveToken(aWhy); + if (aNewWorkerCreated) { + *aNewWorkerCreated = true; + } + return NS_OK; } diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h index 8d59ea1d0..911b07a11 100644 --- a/dom/workers/ServiceWorkerPrivate.h +++ b/dom/workers/ServiceWorkerPrivate.h @@ -189,6 +189,7 @@ private: nsresult SpawnWorkerIfNeeded(WakeUpReason aWhy, nsIRunnable* aLoadFailedRunnable, + bool* aNewWorkerCreated = nullptr, nsILoadGroup* aLoadGroup = nullptr); ~ServiceWorkerPrivate(); diff --git a/dom/workers/test/serviceworkers/chrome.ini b/dom/workers/test/serviceworkers/chrome.ini index e064e7fd0..6d7dbebd0 100644 --- a/dom/workers/test/serviceworkers/chrome.ini +++ b/dom/workers/test/serviceworkers/chrome.ini @@ -3,6 +3,8 @@ skip-if = os == 'android' support-files = chrome_helpers.js empty.js + fetch.js + hello.html serviceworker.html serviceworkerinfo_iframe.html serviceworkermanager_iframe.html @@ -10,6 +12,7 @@ support-files = worker.js worker2.js +[test_devtools_serviceworker_interception.html] [test_privateBrowsing.html] [test_serviceworkerinfo.xul] [test_serviceworkermanager.xul] diff --git a/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html b/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html new file mode 100644 index 000000000..d49ebb2c9 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html @@ -0,0 +1,168 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1168875 - test devtools serviceworker interception.</title> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" + type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + +// Constants +const Ci = Components.interfaces; +const workerScope = "http://mochi.test:8888/chrome/dom/workers/test/serviceworkers/"; +const workerURL = workerScope + "fetch.js"; +const contentPage = workerScope + "hello.html"; + +function createTestWindow(aURL) { + var mainwindow = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + var win = mainwindow.OpenBrowserWindow(contentPage); + + return new Promise(aResolve => { + win.addEventListener("DOMContentLoaded", function callback() { + if (win.content.location.href != aURL) { + win.gBrowser.loadURI(aURL); + return; + } + + win.removeEventListener("DOMContentLoaded", callback); + aResolve(win.content); + }); + }); +} + +function executeTest(aWindow) { + var registration; + + return Promise.resolve() + // Should not be intercepted. + .then(_ => fetchAndCheckTimedChannel(aWindow, false, true, "hello.html")) + + // Regist a service worker. + .then(_ => register(aWindow, workerURL, workerScope)) + .then(r => registration = r) + + // Should be intercpeted and synthesized. + .then(_ => fetchAndCheckTimedChannel(aWindow, true, false, "fake.html")) + + // Should be intercepted but still fetch from network. + .then(_ => fetchAndCheckTimedChannel(aWindow, true, true, + "hello.html?ForBypassingHttpCache")) + + // Tear down + .then(_ => registration.unregister()); +} + +function register(aWindow, aURL, aScope) { + return aWindow.navigator.serviceWorker.register(aURL, {scope: aScope}) + .then(r => { + var worker = r.installing; + return new Promise(function(aResolve) { + worker.onstatechange = function() { + if (worker.state == "activated") { + aResolve(r); + } + } + }); + }); +} + +function fetchAndCheckTimedChannel(aWindow, aIntercepted, aFetch, aURL) { + var resolveFunction; + var promise = new Promise(aResolve => resolveFunction = aResolve); + + var topic = aFetch ? "http-on-examine-response" + : "service-worker-synthesized-response"; + + function observer(aSubject) { + var channel = aSubject.QueryInterface(Ci.nsIChannel); + ok(channel.URI.spec.endsWith(aURL)); + + var tc = aSubject.QueryInterface(Ci.nsITimedChannel); + + // Check service worker related timings. + var serviceWorkerTimings = [{start: tc.launchServiceWorkerStartTime, + end: tc.launchServiceWorkerEndTime}, + {start: tc.dispatchFetchEventStartTime, + end: tc.dispatchFetchEventEndTime}, + {start: tc.handleFetchEventStartTime, + end: tc.handleFetchEventEndTime}]; + if (aIntercepted) { + serviceWorkerTimings.reduce((aPreviousTimings, aCurrentTimings) => { + ok(aPreviousTimings.start <= aCurrentTimings.start, + "Start time order check."); + ok(aPreviousTimings.end <= aCurrentTimings.end, + "End time order check."); + ok(aCurrentTimings.start <= aCurrentTimings.end, + "Start time should be smaller than end time."); + return aCurrentTimings; + }); + } else { + serviceWorkerTimings.forEach(aTimings => { + is(aTimings.start, 0); + is(aTimings.end, 0); + }); + } + + // Check network related timings. + var networkTimings = [tc.domainLookupStartTime, + tc.domainLookupEndTime, + tc.connectStartTime, + tc.connectEndTime, + tc.requestStartTime, + tc.responseStartTime, + tc.responseEndTime]; + if (aFetch) { + networkTimings.reduce((aPreviousTiming, aCurrentTiming) => { + ok(aPreviousTiming <= aCurrentTiming); + return aCurrentTiming; + }); + } else { + networkTimings.forEach(aTiming => is(aTiming, 0)); + } + + SpecialPowers.removeObserver(observer, topic); + resolveFunction(); + } + + SpecialPowers.addObserver(observer, topic, false); + + // return promise; + return Promise.all([aWindow.fetch(aURL), promise]); +} + +function runTest() { + return Promise.resolve() + .then(_ => createTestWindow(contentPage)) + .then(w => executeTest(w)) + .catch(e => ok(false, "Some test failed with error " + e)) + .then(_ => SimpleTest.finish()); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], +]}, runTest); + +</script> +</pre> +</body> +</html> + |