moebius#158: The Performance Resource Timing (added support for "workerStart")

https://github.com/MoonchildProductions/moebius/pull/158
This commit is contained in:
janekptacijarabaci 2018-04-29 11:49:50 +02:00 • committed by Roy Tam
commit 6885700c5b
26 changed files with 746 additions and 70 deletions

View file

@ -778,8 +778,8 @@ WebConsoleActor.prototype =
} }
// See `window` definition. It isn't always a DOM Window. // See `window` definition. It isn't always a DOM Window.
let requestStartTime = this.window && this.window.performance ? let winStartTime = this.window && this.window.performance ?
this.window.performance.timing.requestStart : 0; this.window.performance.timing.navigationStart : 0;
let cache = this.consoleAPIListener let cache = this.consoleAPIListener
.getCachedMessages(!this.parentActor.isRootActor); .getCachedMessages(!this.parentActor.isRootActor);
@ -787,7 +787,7 @@ WebConsoleActor.prototype =
// Filter out messages that came from a ServiceWorker but happened // Filter out messages that came from a ServiceWorker but happened
// before the page was requested. // before the page was requested.
if (aMessage.innerID === "ServiceWorker" && if (aMessage.innerID === "ServiceWorker" &&
requestStartTime > aMessage.timeStamp) { winStartTime > aMessage.timeStamp) {
return; return;
} }

View file

@ -42,8 +42,22 @@ PerformanceResourceTiming::~PerformanceResourceTiming()
DOMHighResTimeStamp DOMHighResTimeStamp
PerformanceResourceTiming::StartTime() const PerformanceResourceTiming::StartTime() const
{ {
DOMHighResTimeStamp startTime = mTiming->RedirectStartHighRes(); // Force the start time to be the earliest of:
return startTime ? startTime : mTiming->FetchStartHighRes(); // - RedirectStart
// - WorkerStart
// - AsyncOpen
// Ignore zero values. The RedirectStart and WorkerStart values
// can come from earlier redirected channels prior to the AsyncOpen
// time being recorded.
DOMHighResTimeStamp redirect = mTiming->RedirectStartHighRes();
redirect = redirect ? redirect : DBL_MAX;
DOMHighResTimeStamp worker = mTiming->WorkerStartHighRes();
worker = worker ? worker : DBL_MAX;
DOMHighResTimeStamp asyncOpen = mTiming->AsyncOpenHighRes();
return std::min(asyncOpen, std::min(redirect, worker));
} }
JSObject* JSObject*

View file

@ -62,6 +62,12 @@ public:
mNextHopProtocol = aNextHopProtocol; mNextHopProtocol = aNextHopProtocol;
} }
DOMHighResTimeStamp WorkerStart() const {
return mTiming && mTiming->TimingAllowed()
? mTiming->WorkerStartHighRes()
: 0;
}
DOMHighResTimeStamp FetchStart() const { DOMHighResTimeStamp FetchStart() const {
return mTiming return mTiming
? mTiming->FetchStartHighRes() ? mTiming->FetchStartHighRes()

View file

@ -73,6 +73,7 @@ PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel)
{ {
if (aChannel) { if (aChannel) {
aChannel->GetAsyncOpen(&mAsyncOpen); aChannel->GetAsyncOpen(&mAsyncOpen);
aChannel->GetDispatchFetchEventStart(&mWorkerStart);
aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin); aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin);
aChannel->GetRedirectCount(&mRedirectCount); aChannel->GetRedirectCount(&mRedirectCount);
aChannel->GetRedirectStart(&mRedirectStart); aChannel->GetRedirectStart(&mRedirectStart);
@ -88,31 +89,39 @@ PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel)
aChannel->GetResponseEnd(&mResponseEnd); aChannel->GetResponseEnd(&mResponseEnd);
aChannel->GetCacheReadEnd(&mCacheReadEnd); aChannel->GetCacheReadEnd(&mCacheReadEnd);
// the performance timing api essentially requires that the event timestamps // The performance timing api essentially requires that the event timestamps
// are >= asyncOpen().. but in truth the browser engages in a number of // have a strict relation with each other. The truth, however, is the browser
// speculative activities that sometimes mean connections and lookups begin // engages in a number of speculative activities that sometimes mean connections
// earlier. Workaround that here by just using asyncOpen as the minimum // and lookups begin at different times. Workaround that here by clamping
// timestamp for dns and connection info. // these values to what we expect FetchStart to be. This means the later of
// AsyncOpen or WorkerStart times.
if (!mAsyncOpen.IsNull()) { if (!mAsyncOpen.IsNull()) {
if (!mDomainLookupStart.IsNull() && mDomainLookupStart < mAsyncOpen) { // We want to clamp to the expected FetchStart value. This is later of
mDomainLookupStart = mAsyncOpen; // the AsyncOpen and WorkerStart values.
const TimeStamp* clampTime = &mAsyncOpen;
if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) {
clampTime = &mWorkerStart;
} }
if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < mAsyncOpen) { if (!mDomainLookupStart.IsNull() && mDomainLookupStart < *clampTime) {
mDomainLookupEnd = mAsyncOpen; mDomainLookupStart = *clampTime;
} }
if (!mConnectStart.IsNull() && mConnectStart < mAsyncOpen) { if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < *clampTime) {
mConnectStart = mAsyncOpen; mDomainLookupEnd = *clampTime;
}
if (!mConnectStart.IsNull() && mConnectStart < *clampTime) {
mConnectStart = *clampTime;
} }
if (mSecureConnection && !mSecureConnectionStart.IsNull() && if (mSecureConnection && !mSecureConnectionStart.IsNull() &&
mSecureConnectionStart < mAsyncOpen) { mSecureConnectionStart < *clampTime) {
mSecureConnectionStart = mAsyncOpen; mSecureConnectionStart = *clampTime;
} }
if (!mConnectEnd.IsNull() && mConnectEnd < mAsyncOpen) { if (!mConnectEnd.IsNull() && mConnectEnd < *clampTime) {
mConnectEnd = mAsyncOpen; mConnectEnd = *clampTime;
} }
} }
} }
@ -131,9 +140,13 @@ PerformanceTiming::FetchStartHighRes()
} }
MOZ_ASSERT(!mAsyncOpen.IsNull(), "The fetch start time stamp should always be " MOZ_ASSERT(!mAsyncOpen.IsNull(), "The fetch start time stamp should always be "
"valid if the performance timing is enabled"); "valid if the performance timing is enabled");
mFetchStart = (!mAsyncOpen.IsNull()) if (!mAsyncOpen.IsNull()) {
? TimeStampToDOMHighRes(mAsyncOpen) if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) {
: 0.0; mFetchStart = TimeStampToDOMHighRes(mWorkerStart);
} else {
mFetchStart = TimeStampToDOMHighRes(mAsyncOpen);
}
}
} }
return TimerClamping::ReduceMsTimeValue(mFetchStart); return TimerClamping::ReduceMsTimeValue(mFetchStart);
} }
@ -180,7 +193,7 @@ PerformanceTiming::TimingAllowed() const
return mTimingAllowed; return mTimingAllowed;
} }
uint16_t uint8_t
PerformanceTiming::GetRedirectCount() const PerformanceTiming::GetRedirectCount() const
{ {
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
@ -205,6 +218,26 @@ PerformanceTiming::ShouldReportCrossOriginRedirect() const
return (mRedirectCount != 0) && mReportCrossOriginRedirect; return (mRedirectCount != 0) && mReportCrossOriginRedirect;
} }
DOMHighResTimeStamp
PerformanceTiming::AsyncOpenHighRes()
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
mAsyncOpen.IsNull()) {
return mZeroTime;
}
return TimeStampToReducedDOMHighResOrFetchStart(mAsyncOpen);
}
DOMHighResTimeStamp
PerformanceTiming::WorkerStartHighRes()
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
mWorkerStart.IsNull()) {
return mZeroTime;
}
return TimeStampToReducedDOMHighResOrFetchStart(mWorkerStart);
}
/** /**
* RedirectStartHighRes() is used by both the navigation timing and the * RedirectStartHighRes() is used by both the navigation timing and the
* resource timing. Since, navigation timing and resource timing check and * resource timing. Since, navigation timing and resource timing check and

View file

@ -139,7 +139,7 @@ public:
return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetUnloadEventEnd()); return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetUnloadEventEnd());
} }
uint16_t GetRedirectCount() const; uint8_t GetRedirectCount() const;
// Checks if the resource is either same origin as the page that started // Checks if the resource is either same origin as the page that started
// the load, or if the response contains the Timing-Allow-Origin header // the load, or if the response contains the Timing-Allow-Origin header
@ -155,7 +155,12 @@ public:
// the timing-allow-origin check in HttpBaseChannel::TimingAllowCheck // the timing-allow-origin check in HttpBaseChannel::TimingAllowCheck
bool ShouldReportCrossOriginRedirect() const; bool ShouldReportCrossOriginRedirect() const;
// The last channel's AsyncOpen time. This may occur before the FetchStart
// in some cases.
DOMHighResTimeStamp AsyncOpenHighRes();
// High resolution (used by resource timing) // High resolution (used by resource timing)
DOMHighResTimeStamp WorkerStartHighRes();
DOMHighResTimeStamp FetchStartHighRes(); DOMHighResTimeStamp FetchStartHighRes();
DOMHighResTimeStamp RedirectStartHighRes(); DOMHighResTimeStamp RedirectStartHighRes();
DOMHighResTimeStamp RedirectEndHighRes(); DOMHighResTimeStamp RedirectEndHighRes();
@ -253,6 +258,7 @@ private:
DOMHighResTimeStamp mZeroTime; DOMHighResTimeStamp mZeroTime;
TimeStamp mAsyncOpen; TimeStamp mAsyncOpen;
TimeStamp mWorkerStart;
TimeStamp mRedirectStart; TimeStamp mRedirectStart;
TimeStamp mRedirectEnd; TimeStamp mRedirectEnd;
TimeStamp mDomainLookupStart; TimeStamp mDomainLookupStart;
@ -265,7 +271,7 @@ private:
TimeStamp mCacheReadStart; TimeStamp mCacheReadStart;
TimeStamp mResponseEnd; TimeStamp mResponseEnd;
TimeStamp mCacheReadEnd; TimeStamp mCacheReadEnd;
uint16_t mRedirectCount; uint8_t mRedirectCount;
bool mTimingAllowed; bool mTimingAllowed;
bool mAllRedirectsSameOrigin; bool mAllRedirectsSameOrigin;
bool mInitialized; bool mInitialized;

View file

@ -4,7 +4,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. * You can obtain one at http://mozilla.org/MPL/2.0/.
* *
* The origin of this IDL file is * The origin of this IDL file is
* http://w3c-test.org/webperf/specs/ResourceTiming/#performanceresourcetiming * https://w3c.github.io/resource-timing/#performanceresourcetiming
* *
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply. * liability, trademark and document use rules apply.
@ -12,14 +12,10 @@
interface PerformanceResourceTiming : PerformanceEntry interface PerformanceResourceTiming : PerformanceEntry
{ {
// A string with the name of that element that initiated the load.
// If the initiator is a CSS resource, the initiatorType attribute must return
// the string "css".
// If the initiator is an XMLHttpRequest object, the initiatorType attribute
// must return the string "xmlhttprequest".
readonly attribute DOMString initiatorType; readonly attribute DOMString initiatorType;
readonly attribute DOMString nextHopProtocol; readonly attribute DOMString nextHopProtocol;
readonly attribute DOMHighResTimeStamp workerStart;
readonly attribute DOMHighResTimeStamp redirectStart; readonly attribute DOMHighResTimeStamp redirectStart;
readonly attribute DOMHighResTimeStamp redirectEnd; readonly attribute DOMHighResTimeStamp redirectEnd;
readonly attribute DOMHighResTimeStamp fetchStart; readonly attribute DOMHighResTimeStamp fetchStart;

View file

@ -12,6 +12,7 @@
#include "nsINetworkInterceptController.h" #include "nsINetworkInterceptController.h"
#include "nsIOutputStream.h" #include "nsIOutputStream.h"
#include "nsIScriptError.h" #include "nsIScriptError.h"
#include "nsITimedChannel.h"
#include "nsIUnicodeDecoder.h" #include "nsIUnicodeDecoder.h"
#include "nsIUnicodeEncoder.h" #include "nsIUnicodeEncoder.h"
#include "nsContentPolicyUtils.h" #include "nsContentPolicyUtils.h"
@ -108,6 +109,12 @@ NS_IMETHODIMP
CancelChannelRunnable::Run() CancelChannelRunnable::Run()
{ {
MOZ_ASSERT(NS_IsMainThread()); 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); mChannel->Cancel(mStatus);
mRegistration->MaybeScheduleUpdate(); mRegistration->MaybeScheduleUpdate();
return NS_OK; return NS_OK;
@ -230,6 +237,9 @@ public:
return NS_OK; return NS_OK;
} }
mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
mChannel->SaveTimeStampsToUnderlyingChannel();
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService(); nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
if (obsService) { if (obsService) {
obsService->NotifyObservers(underlyingChannel, "service-worker-synthesized-response", nullptr); obsService->NotifyObservers(underlyingChannel, "service-worker-synthesized-response", nullptr);

View file

@ -13,6 +13,7 @@
#include "nsINetworkInterceptController.h" #include "nsINetworkInterceptController.h"
#include "nsIPushErrorReporter.h" #include "nsIPushErrorReporter.h"
#include "nsISupportsImpl.h" #include "nsISupportsImpl.h"
#include "nsITimedChannel.h"
#include "nsIUploadChannel2.h" #include "nsIUploadChannel2.h"
#include "nsNetUtil.h" #include "nsNetUtil.h"
#include "nsProxyRelease.h" #include "nsProxyRelease.h"
@ -1255,6 +1256,7 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable
nsCString mMethod; nsCString mMethod;
nsString mClientId; nsString mClientId;
bool mIsReload; bool mIsReload;
bool mMarkLaunchServiceWorkerEnd;
RequestCache mCacheMode; RequestCache mCacheMode;
RequestMode mRequestMode; RequestMode mRequestMode;
RequestRedirect mRequestRedirect; RequestRedirect mRequestRedirect;
@ -1273,13 +1275,15 @@ public:
const nsACString& aScriptSpec, const nsACString& aScriptSpec,
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration, nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
const nsAString& aDocumentId, const nsAString& aDocumentId,
bool aIsReload) bool aIsReload,
bool aMarkLaunchServiceWorkerEnd)
: ExtendableFunctionalEventWorkerRunnable( : ExtendableFunctionalEventWorkerRunnable(
aWorkerPrivate, aKeepAliveToken, aRegistration) aWorkerPrivate, aKeepAliveToken, aRegistration)
, mInterceptedChannel(aChannel) , mInterceptedChannel(aChannel)
, mScriptSpec(aScriptSpec) , mScriptSpec(aScriptSpec)
, mClientId(aDocumentId) , mClientId(aDocumentId)
, mIsReload(aIsReload) , mIsReload(aIsReload)
, mMarkLaunchServiceWorkerEnd(aMarkLaunchServiceWorkerEnd)
, mCacheMode(RequestCache::Default) , mCacheMode(RequestCache::Default)
, mRequestMode(RequestMode::No_cors) , mRequestMode(RequestMode::No_cors)
, mRequestRedirect(RequestRedirect::Follow) , mRequestRedirect(RequestRedirect::Follow)
@ -1417,6 +1421,12 @@ public:
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{ {
MOZ_ASSERT(aWorkerPrivate); MOZ_ASSERT(aWorkerPrivate);
if (mMarkLaunchServiceWorkerEnd) {
mInterceptedChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now());
}
mInterceptedChannel->SetDispatchFetchEventEnd(TimeStamp::Now());
return DispatchFetchEvent(aCx, aWorkerPrivate); return DispatchFetchEvent(aCx, aWorkerPrivate);
} }
@ -1445,6 +1455,10 @@ private:
NS_IMETHOD Run() override NS_IMETHOD Run() override
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
mChannel->SaveTimeStampsToUnderlyingChannel();
nsresult rv = mChannel->ResetInterception(); nsresult rv = mChannel->ResetInterception();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Failed to resume intercepted network request"); "Failed to resume intercepted network request");
@ -1520,6 +1534,8 @@ private:
event->PostInit(mInterceptedChannel, mRegistration, mScriptSpec); event->PostInit(mInterceptedChannel, mRegistration, mScriptSpec);
event->SetTrusted(true); event->SetTrusted(true);
mInterceptedChannel->SetHandleFetchEventStart(TimeStamp::Now());
RefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope()); RefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
nsresult rv2 = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr); nsresult rv2 = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) { if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) {
@ -1614,9 +1630,21 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
nsCOMPtr<nsIRunnable> failRunnable = nsCOMPtr<nsIRunnable> failRunnable =
NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception); 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); NS_ENSURE_SUCCESS(rv, rv);
if (!newWorkerCreated) {
aChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now());
}
nsMainThreadPtrHandle<nsIInterceptedChannel> handle( nsMainThreadPtrHandle<nsIInterceptedChannel> handle(
new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false)); new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false));
@ -1646,7 +1674,7 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
RefPtr<FetchEventRunnable> r = RefPtr<FetchEventRunnable> r =
new FetchEventRunnable(mWorkerPrivate, token, handle, new FetchEventRunnable(mWorkerPrivate, token, handle,
mInfo->ScriptSpec(), regInfo, mInfo->ScriptSpec(), regInfo,
aDocumentId, aIsReload); aDocumentId, aIsReload, newWorkerCreated);
rv = r->Init(); rv = r->Init();
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
@ -1669,6 +1697,7 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
nsresult nsresult
ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy, ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
nsIRunnable* aLoadFailedRunnable, nsIRunnable* aLoadFailedRunnable,
bool* aNewWorkerCreated,
nsILoadGroup* aLoadGroup) nsILoadGroup* aLoadGroup)
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
@ -1679,6 +1708,12 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
// the overriden load group when intercepting a fetch. // the overriden load group when intercepting a fetch.
MOZ_ASSERT_IF(aWhy == FetchEvent, aLoadGroup); 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) { if (mWorkerPrivate) {
mWorkerPrivate->UpdateOverridenLoadGroup(aLoadGroup); mWorkerPrivate->UpdateOverridenLoadGroup(aLoadGroup);
RenewKeepAliveToken(aWhy); RenewKeepAliveToken(aWhy);
@ -1762,6 +1797,10 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
RenewKeepAliveToken(aWhy); RenewKeepAliveToken(aWhy);
if (aNewWorkerCreated) {
*aNewWorkerCreated = true;
}
return NS_OK; return NS_OK;
} }

View file

@ -189,6 +189,7 @@ private:
nsresult nsresult
SpawnWorkerIfNeeded(WakeUpReason aWhy, SpawnWorkerIfNeeded(WakeUpReason aWhy,
nsIRunnable* aLoadFailedRunnable, nsIRunnable* aLoadFailedRunnable,
bool* aNewWorkerCreated = nullptr,
nsILoadGroup* aLoadGroup = nullptr); nsILoadGroup* aLoadGroup = nullptr);
~ServiceWorkerPrivate(); ~ServiceWorkerPrivate();

View file

@ -3,6 +3,8 @@ skip-if = os == 'android'
support-files = support-files =
chrome_helpers.js chrome_helpers.js
empty.js empty.js
fetch.js
hello.html
serviceworker.html serviceworker.html
serviceworkerinfo_iframe.html serviceworkerinfo_iframe.html
serviceworkermanager_iframe.html serviceworkermanager_iframe.html
@ -10,6 +12,7 @@ support-files =
worker.js worker.js
worker2.js worker2.js
[test_devtools_serviceworker_interception.html]
[test_privateBrowsing.html] [test_privateBrowsing.html]
[test_serviceworkerinfo.xul] [test_serviceworkerinfo.xul]
[test_serviceworkermanager.xul] [test_serviceworkermanager.xul]

View file

@ -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>

View file

@ -14,12 +14,16 @@ interface nsIURI;
%{C++ %{C++
#include "nsIConsoleReportCollector.h" #include "nsIConsoleReportCollector.h"
namespace mozilla { namespace mozilla {
class TimeStamp;
namespace dom { namespace dom {
class ChannelInfo; class ChannelInfo;
} }
} }
%} %}
native TimeStamp(mozilla::TimeStamp);
[ptr] native ChannelInfo(mozilla::dom::ChannelInfo); [ptr] native ChannelInfo(mozilla::dom::ChannelInfo);
/** /**
@ -97,6 +101,30 @@ interface nsIInterceptedChannel : nsISupports
[noscript] [noscript]
readonly attribute nsIConsoleReportCollector consoleReportCollector; readonly attribute nsIConsoleReportCollector consoleReportCollector;
/**
* Save the timestamps of various service worker interception phases.
*/
[noscript]
void SetLaunchServiceWorkerStart(in TimeStamp aTimeStamp);
[noscript]
void SetLaunchServiceWorkerEnd(in TimeStamp aTimeStamp);
[noscript]
void SetDispatchFetchEventStart(in TimeStamp aTimeStamp);
[noscript]
void SetDispatchFetchEventEnd(in TimeStamp aTimeStamp);
[noscript]
void SetHandleFetchEventStart(in TimeStamp aTimeStamp);
[noscript]
void SetHandleFetchEventEnd(in TimeStamp aTimeStamp);
[noscript]
void SaveTimeStampsToUnderlyingChannel();
%{C++ %{C++
already_AddRefed<nsIConsoleReportCollector> already_AddRefed<nsIConsoleReportCollector>
GetConsoleReportCollector() GetConsoleReportCollector()

View file

@ -21,7 +21,8 @@ interface nsITimedChannel : nsISupports {
attribute boolean timingEnabled; attribute boolean timingEnabled;
// The number of redirects // The number of redirects
attribute uint16_t redirectCount; attribute uint8_t redirectCount;
attribute uint8_t internalRedirectCount;
[noscript] readonly attribute TimeStamp channelCreation; [noscript] readonly attribute TimeStamp channelCreation;
[noscript] readonly attribute TimeStamp asyncOpen; [noscript] readonly attribute TimeStamp asyncOpen;
@ -37,6 +38,15 @@ interface nsITimedChannel : nsISupports {
[noscript] readonly attribute TimeStamp responseStart; [noscript] readonly attribute TimeStamp responseStart;
[noscript] readonly attribute TimeStamp responseEnd; [noscript] readonly attribute TimeStamp responseEnd;
// The following are only set when the request is intercepted by a service
// worker no matter the response is synthesized.
[noscript] attribute TimeStamp launchServiceWorkerStart;
[noscript] attribute TimeStamp launchServiceWorkerEnd;
[noscript] attribute TimeStamp dispatchFetchEventStart;
[noscript] attribute TimeStamp dispatchFetchEventEnd;
[noscript] attribute TimeStamp handleFetchEventStart;
[noscript] attribute TimeStamp handleFetchEventEnd;
// The redirect attributes timings must be writeble, se we can transfer // The redirect attributes timings must be writeble, se we can transfer
// the data from one channel to the redirected channel. // the data from one channel to the redirected channel.
[noscript] attribute TimeStamp redirectStart; [noscript] attribute TimeStamp redirectStart;
@ -67,6 +77,12 @@ interface nsITimedChannel : nsISupports {
// All following are PRTime versions of the above. // All following are PRTime versions of the above.
readonly attribute PRTime channelCreationTime; readonly attribute PRTime channelCreationTime;
readonly attribute PRTime asyncOpenTime; readonly attribute PRTime asyncOpenTime;
readonly attribute PRTime launchServiceWorkerStartTime;
readonly attribute PRTime launchServiceWorkerEndTime;
readonly attribute PRTime dispatchFetchEventStartTime;
readonly attribute PRTime dispatchFetchEventEndTime;
readonly attribute PRTime handleFetchEventStartTime;
readonly attribute PRTime handleFetchEventEndTime;
readonly attribute PRTime domainLookupStartTime; readonly attribute PRTime domainLookupStartTime;
readonly attribute PRTime domainLookupEndTime; readonly attribute PRTime domainLookupEndTime;
readonly attribute PRTime connectStartTime; readonly attribute PRTime connectStartTime;

View file

@ -20,6 +20,7 @@ using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
using RequestHeaderTuples from "mozilla/net/PHttpChannelParams.h"; using RequestHeaderTuples from "mozilla/net/PHttpChannelParams.h";
using struct nsHttpAtom from "nsHttp.h"; using struct nsHttpAtom from "nsHttp.h";
using class nsHttpResponseHead from "nsHttpResponseHead.h"; using class nsHttpResponseHead from "nsHttpResponseHead.h";
using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
namespace mozilla { namespace mozilla {
namespace net { namespace net {
@ -134,6 +135,12 @@ struct HttpChannelOpenArgs
nsCString channelId; nsCString channelId;
uint64_t contentWindowId; uint64_t contentWindowId;
nsCString preferredAlternativeType; nsCString preferredAlternativeType;
TimeStamp launchServiceWorkerStart;
TimeStamp launchServiceWorkerEnd;
TimeStamp dispatchFetchEventStart;
TimeStamp dispatchFetchEventEnd;
TimeStamp handleFetchEventStart;
TimeStamp handleFetchEventEnd;
}; };
struct HttpChannelConnectArgs struct HttpChannelConnectArgs

View file

@ -105,6 +105,7 @@ HttpBaseChannel::HttpBaseChannel()
, mHttpHandler(gHttpHandler) , mHttpHandler(gHttpHandler)
, mReferrerPolicy(REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE) , mReferrerPolicy(REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE)
, mRedirectCount(0) , mRedirectCount(0)
, mInternalRedirectCount(0)
, mForcePending(false) , mForcePending(false)
, mCorsIncludeCredentials(false) , mCorsIncludeCredentials(false)
, mCorsMode(nsIHttpChannelInternal::CORS_MODE_NO_CORS) , mCorsMode(nsIHttpChannelInternal::CORS_MODE_NO_CORS)
@ -3128,12 +3129,6 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
// convey the mAllowPipelining and mAllowSTS flags // convey the mAllowPipelining and mAllowSTS flags
httpChannel->SetAllowPipelining(mAllowPipelining); httpChannel->SetAllowPipelining(mAllowPipelining);
httpChannel->SetAllowSTS(mAllowSTS); httpChannel->SetAllowSTS(mAllowSTS);
// convey the new redirection limit
// make sure we don't underflow
uint32_t redirectionLimit = mRedirectionLimit
? mRedirectionLimit - 1
: 0;
httpChannel->SetRedirectionLimit(redirectionLimit);
// convey the Accept header value // convey the Accept header value
{ {
@ -3215,23 +3210,40 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
do_QueryInterface(static_cast<nsIHttpChannel*>(this))); do_QueryInterface(static_cast<nsIHttpChannel*>(this)));
if (oldTimedChannel && newTimedChannel) { if (oldTimedChannel && newTimedChannel) {
newTimedChannel->SetTimingEnabled(mTimingEnabled); newTimedChannel->SetTimingEnabled(mTimingEnabled);
newTimedChannel->SetRedirectCount(mRedirectCount + 1);
if (redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
int8_t newCount = mInternalRedirectCount + 1;
newTimedChannel->SetInternalRedirectCount(
std::max(newCount, mInternalRedirectCount));
} else {
int8_t newCount = mRedirectCount + 1;
newTimedChannel->SetRedirectCount(
std::max(newCount, mRedirectCount));
}
// If the RedirectStart is null, we will use the AsyncOpen value of the // If the RedirectStart is null, we will use the AsyncOpen value of the
// previous channel (this is the first redirect in the redirects chain). // previous channel (this is the first redirect in the redirects chain).
if (mRedirectStartTimeStamp.IsNull()) { if (mRedirectStartTimeStamp.IsNull()) {
TimeStamp asyncOpen; // Only do this for real redirects. Internal redirects should be hidden.
oldTimedChannel->GetAsyncOpen(&asyncOpen); if (!(redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
newTimedChannel->SetRedirectStart(asyncOpen); TimeStamp asyncOpen;
} oldTimedChannel->GetAsyncOpen(&asyncOpen);
else { newTimedChannel->SetRedirectStart(asyncOpen);
}
} else {
newTimedChannel->SetRedirectStart(mRedirectStartTimeStamp); newTimedChannel->SetRedirectStart(mRedirectStartTimeStamp);
} }
// The RedirectEnd timestamp is equal to the previous channel response end. // For internal redirects just propagate the last redirect end time
TimeStamp prevResponseEnd; // forward. Otherwise the new redirect end time is the last response
oldTimedChannel->GetResponseEnd(&prevResponseEnd); // end time.
newTimedChannel->SetRedirectEnd(prevResponseEnd); TimeStamp newRedirectEnd;
if (redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
oldTimedChannel->GetRedirectEnd(&newRedirectEnd);
} else {
oldTimedChannel->GetResponseEnd(&newRedirectEnd);
}
newTimedChannel->SetRedirectEnd(newRedirectEnd);
nsAutoString initiatorType; nsAutoString initiatorType;
oldTimedChannel->GetInitiatorType(initiatorType); oldTimedChannel->GetInitiatorType(initiatorType);
@ -3253,6 +3265,16 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
mAllRedirectsPassTimingAllowCheck && mAllRedirectsPassTimingAllowCheck &&
oldTimedChannel->TimingAllowCheck(principal)); oldTimedChannel->TimingAllowCheck(principal));
} }
// Propagate service worker measurements across redirects. The
// PeformanceResourceTiming.workerStart API expects to see the
// worker start time after a redirect.
newTimedChannel->SetLaunchServiceWorkerStart(mLaunchServiceWorkerStart);
newTimedChannel->SetLaunchServiceWorkerEnd(mLaunchServiceWorkerEnd);
newTimedChannel->SetDispatchFetchEventStart(mDispatchFetchEventStart);
newTimedChannel->SetDispatchFetchEventEnd(mDispatchFetchEventEnd);
newTimedChannel->SetHandleFetchEventStart(mHandleFetchEventStart);
newTimedChannel->SetHandleFetchEventEnd(mHandleFetchEventEnd);
} }
// Pass the preferred alt-data type on to the new channel. // Pass the preferred alt-data type on to the new channel.
@ -3318,19 +3340,33 @@ HttpBaseChannel::GetAsyncOpen(TimeStamp* _retval) {
* redirects. This check must be done by the consumers. * redirects. This check must be done by the consumers.
*/ */
NS_IMETHODIMP NS_IMETHODIMP
HttpBaseChannel::GetRedirectCount(uint16_t *aRedirectCount) HttpBaseChannel::GetRedirectCount(uint8_t *aRedirectCount)
{ {
*aRedirectCount = mRedirectCount; *aRedirectCount = mRedirectCount;
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
HttpBaseChannel::SetRedirectCount(uint16_t aRedirectCount) HttpBaseChannel::SetRedirectCount(uint8_t aRedirectCount)
{ {
mRedirectCount = aRedirectCount; mRedirectCount = aRedirectCount;
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
HttpBaseChannel::GetInternalRedirectCount(uint8_t *aRedirectCount)
{
*aRedirectCount = mInternalRedirectCount;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetInternalRedirectCount(uint8_t aRedirectCount)
{
mInternalRedirectCount = aRedirectCount;
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
HttpBaseChannel::GetRedirectStart(TimeStamp* _retval) HttpBaseChannel::GetRedirectStart(TimeStamp* _retval)
{ {
@ -3430,6 +3466,84 @@ HttpBaseChannel::TimingAllowCheck(nsIPrincipal *aOrigin, bool *_retval)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
HttpBaseChannel::GetLaunchServiceWorkerStart(TimeStamp* _retval) {
MOZ_ASSERT(_retval);
*_retval = mLaunchServiceWorkerStart;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetLaunchServiceWorkerStart(TimeStamp aTimeStamp) {
mLaunchServiceWorkerStart = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetLaunchServiceWorkerEnd(TimeStamp* _retval) {
MOZ_ASSERT(_retval);
*_retval = mLaunchServiceWorkerEnd;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetLaunchServiceWorkerEnd(TimeStamp aTimeStamp) {
mLaunchServiceWorkerEnd = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetDispatchFetchEventStart(TimeStamp* _retval) {
MOZ_ASSERT(_retval);
*_retval = mDispatchFetchEventStart;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetDispatchFetchEventStart(TimeStamp aTimeStamp) {
mDispatchFetchEventStart = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetDispatchFetchEventEnd(TimeStamp* _retval) {
MOZ_ASSERT(_retval);
*_retval = mDispatchFetchEventEnd;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetDispatchFetchEventEnd(TimeStamp aTimeStamp) {
mDispatchFetchEventEnd = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetHandleFetchEventStart(TimeStamp* _retval) {
MOZ_ASSERT(_retval);
*_retval = mHandleFetchEventStart;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetHandleFetchEventStart(TimeStamp aTimeStamp) {
mHandleFetchEventStart = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetHandleFetchEventEnd(TimeStamp* _retval) {
MOZ_ASSERT(_retval);
*_retval = mHandleFetchEventEnd;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetHandleFetchEventEnd(TimeStamp aTimeStamp) {
mHandleFetchEventEnd = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
HttpBaseChannel::GetDomainLookupStart(TimeStamp* _retval) { HttpBaseChannel::GetDomainLookupStart(TimeStamp* _retval) {
*_retval = mTransactionTimings.domainLookupStart; *_retval = mTransactionTimings.domainLookupStart;
@ -3520,6 +3634,12 @@ HttpBaseChannel::Get##name##Time(PRTime* _retval) { \
IMPL_TIMING_ATTR(ChannelCreation) IMPL_TIMING_ATTR(ChannelCreation)
IMPL_TIMING_ATTR(AsyncOpen) IMPL_TIMING_ATTR(AsyncOpen)
IMPL_TIMING_ATTR(LaunchServiceWorkerStart)
IMPL_TIMING_ATTR(LaunchServiceWorkerEnd)
IMPL_TIMING_ATTR(DispatchFetchEventStart)
IMPL_TIMING_ATTR(DispatchFetchEventEnd)
IMPL_TIMING_ATTR(HandleFetchEventStart)
IMPL_TIMING_ATTR(HandleFetchEventEnd)
IMPL_TIMING_ATTR(DomainLookupStart) IMPL_TIMING_ATTR(DomainLookupStart)
IMPL_TIMING_ATTR(DomainLookupEnd) IMPL_TIMING_ATTR(DomainLookupEnd)
IMPL_TIMING_ATTR(ConnectStart) IMPL_TIMING_ATTR(ConnectStart)

View file

@ -506,7 +506,9 @@ protected:
// the HTML file. // the HTML file.
nsString mInitiatorType; nsString mInitiatorType;
// Number of redirects that has occurred. // Number of redirects that has occurred.
int16_t mRedirectCount; int8_t mRedirectCount;
// Number of internal redirects that has occurred.
int8_t mInternalRedirectCount;
// A time value equal to the starting time of the fetch that initiates the // A time value equal to the starting time of the fetch that initiates the
// redirect. // redirect.
mozilla::TimeStamp mRedirectStartTimeStamp; mozilla::TimeStamp mRedirectStartTimeStamp;
@ -519,6 +521,12 @@ protected:
TimeStamp mAsyncOpenTime; TimeStamp mAsyncOpenTime;
TimeStamp mCacheReadStart; TimeStamp mCacheReadStart;
TimeStamp mCacheReadEnd; TimeStamp mCacheReadEnd;
TimeStamp mLaunchServiceWorkerStart;
TimeStamp mLaunchServiceWorkerEnd;
TimeStamp mDispatchFetchEventStart;
TimeStamp mDispatchFetchEventEnd;
TimeStamp mHandleFetchEventStart;
TimeStamp mHandleFetchEventEnd;
// copied from the transaction before we null out mTransaction // copied from the transaction before we null out mTransaction
// so that the timing can still be queried from OnStopRequest // so that the timing can still be queried from OnStopRequest
TimingStruct mTransactionTimings; TimingStruct mTransactionTimings;

View file

@ -2132,6 +2132,13 @@ HttpChannelChild::ContinueAsyncOpen()
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
openArgs.launchServiceWorkerStart() = mLaunchServiceWorkerStart;
openArgs.launchServiceWorkerEnd() = mLaunchServiceWorkerEnd;
openArgs.dispatchFetchEventStart() = mDispatchFetchEventStart;
openArgs.dispatchFetchEventEnd() = mDispatchFetchEventEnd;
openArgs.handleFetchEventStart() = mHandleFetchEventStart;
openArgs.handleFetchEventEnd() = mHandleFetchEventEnd;
// The socket transport in the chrome process now holds a logical ref to us // The socket transport in the chrome process now holds a logical ref to us
// until OnStopRequest, or we do a redirect, or we hit an IPDL error. // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
AddIPDLReference(); AddIPDLReference();

View file

@ -130,7 +130,13 @@ HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
a.initialRwin(), a.blockAuthPrompt(), a.initialRwin(), a.blockAuthPrompt(),
a.suspendAfterSynthesizeResponse(), a.suspendAfterSynthesizeResponse(),
a.allowStaleCacheContent(), a.contentTypeHint(), a.allowStaleCacheContent(), a.contentTypeHint(),
a.channelId(), a.contentWindowId(), a.preferredAlternativeType()); a.channelId(), a.contentWindowId(), a.preferredAlternativeType(),
a.launchServiceWorkerStart(),
a.launchServiceWorkerEnd(),
a.dispatchFetchEventStart(),
a.dispatchFetchEventEnd(),
a.handleFetchEventStart(),
a.handleFetchEventEnd());
} }
case HttpChannelCreationArgs::THttpChannelConnectArgs: case HttpChannelCreationArgs::THttpChannelConnectArgs:
{ {
@ -329,7 +335,13 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
const nsCString& aContentTypeHint, const nsCString& aContentTypeHint,
const nsCString& aChannelId, const nsCString& aChannelId,
const uint64_t& aContentWindowId, const uint64_t& aContentWindowId,
const nsCString& aPreferredAlternativeType) const nsCString& aPreferredAlternativeType,
const TimeStamp& aLaunchServiceWorkerStart,
const TimeStamp& aLaunchServiceWorkerEnd,
const TimeStamp& aDispatchFetchEventStart,
const TimeStamp& aDispatchFetchEventEnd,
const TimeStamp& aHandleFetchEventStart,
const TimeStamp& aHandleFetchEventEnd)
{ {
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI); nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
if (!uri) { if (!uri) {
@ -534,6 +546,13 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
mChannel->SetInitialRwin(aInitialRwin); mChannel->SetInitialRwin(aInitialRwin);
mChannel->SetBlockAuthPrompt(aBlockAuthPrompt); mChannel->SetBlockAuthPrompt(aBlockAuthPrompt);
mChannel->SetLaunchServiceWorkerStart(aLaunchServiceWorkerStart);
mChannel->SetLaunchServiceWorkerEnd(aLaunchServiceWorkerEnd);
mChannel->SetDispatchFetchEventStart(aDispatchFetchEventStart);
mChannel->SetDispatchFetchEventEnd(aDispatchFetchEventEnd);
mChannel->SetHandleFetchEventStart(aHandleFetchEventStart);
mChannel->SetHandleFetchEventEnd(aHandleFetchEventEnd);
nsCOMPtr<nsIApplicationCacheChannel> appCacheChan = nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
do_QueryObject(mChannel); do_QueryObject(mChannel);
nsCOMPtr<nsIApplicationCacheService> appCacheService = nsCOMPtr<nsIApplicationCacheService> appCacheService =
@ -1159,7 +1178,7 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
nsCString secInfoSerialization; nsCString secInfoSerialization;
UpdateAndSerializeSecurityInfo(secInfoSerialization); UpdateAndSerializeSecurityInfo(secInfoSerialization);
uint16_t redirectCount = 0; uint8_t redirectCount = 0;
chan->GetRedirectCount(&redirectCount); chan->GetRedirectCount(&redirectCount);
nsCOMPtr<nsISupports> cacheKey; nsCOMPtr<nsISupports> cacheKey;

View file

@ -143,7 +143,13 @@ protected:
const nsCString& aContentTypeHint, const nsCString& aContentTypeHint,
const nsCString& aChannelId, const nsCString& aChannelId,
const uint64_t& aContentWindowId, const uint64_t& aContentWindowId,
const nsCString& aPreferredAlternativeType); const nsCString& aPreferredAlternativeType,
const TimeStamp& aLaunchServiceWorkerStart,
const TimeStamp& aLaunchServiceWorkerEnd,
const TimeStamp& aDispatchFetchEventStart,
const TimeStamp& aDispatchFetchEventEnd,
const TimeStamp& aHandleFetchEventStart,
const TimeStamp& aHandleFetchEventEnd);
virtual bool RecvSetPriority(const uint16_t& priority) override; virtual bool RecvSetPriority(const uint16_t& priority) override;
virtual bool RecvSetClassOfService(const uint32_t& cos) override; virtual bool RecvSetClassOfService(const uint32_t& cos) override;

View file

@ -10,6 +10,7 @@
#include "nsInputStreamPump.h" #include "nsInputStreamPump.h"
#include "nsIPipe.h" #include "nsIPipe.h"
#include "nsIStreamListener.h" #include "nsIStreamListener.h"
#include "nsITimedChannel.h"
#include "nsHttpChannel.h" #include "nsHttpChannel.h"
#include "HttpChannelChild.h" #include "HttpChannelChild.h"
#include "nsHttpResponseHead.h" #include "nsHttpResponseHead.h"
@ -134,6 +135,40 @@ InterceptedChannelBase::SetReleaseHandle(nsISupports* aHandle)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
InterceptedChannelBase::SaveTimeStampsToUnderlyingChannel()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIChannel> underlyingChannel;
nsresult rv = GetChannel(getter_AddRefs(underlyingChannel));
MOZ_ASSERT(NS_SUCCEEDED(rv));
nsCOMPtr<nsITimedChannel> timedChannel =
do_QueryInterface(underlyingChannel);
MOZ_ASSERT(timedChannel);
rv = timedChannel->SetLaunchServiceWorkerStart(mLaunchServiceWorkerStart);
MOZ_ASSERT(NS_SUCCEEDED(rv));
rv = timedChannel->SetLaunchServiceWorkerEnd(mLaunchServiceWorkerEnd);
MOZ_ASSERT(NS_SUCCEEDED(rv));
rv = timedChannel->SetDispatchFetchEventStart(mDispatchFetchEventStart);
MOZ_ASSERT(NS_SUCCEEDED(rv));
rv = timedChannel->SetDispatchFetchEventEnd(mDispatchFetchEventEnd);
MOZ_ASSERT(NS_SUCCEEDED(rv));
rv = timedChannel->SetHandleFetchEventStart(mHandleFetchEventStart);
MOZ_ASSERT(NS_SUCCEEDED(rv));
rv = timedChannel->SetHandleFetchEventEnd(mHandleFetchEventEnd);
MOZ_ASSERT(NS_SUCCEEDED(rv));
return rv;
}
/* static */ /* static */
already_AddRefed<nsIURI> already_AddRefed<nsIURI>
InterceptedChannelBase::SecureUpgradeChannelURI(nsIChannel* aChannel) InterceptedChannelBase::SecureUpgradeChannelURI(nsIChannel* aChannel)

View file

@ -46,6 +46,13 @@ protected:
nsresult DoSynthesizeStatus(uint16_t aStatus, const nsACString& aReason); nsresult DoSynthesizeStatus(uint16_t aStatus, const nsACString& aReason);
nsresult DoSynthesizeHeader(const nsACString& aName, const nsACString& aValue); nsresult DoSynthesizeHeader(const nsACString& aName, const nsACString& aValue);
TimeStamp mLaunchServiceWorkerStart;
TimeStamp mLaunchServiceWorkerEnd;
TimeStamp mDispatchFetchEventStart;
TimeStamp mDispatchFetchEventEnd;
TimeStamp mHandleFetchEventStart;
TimeStamp mHandleFetchEventEnd;
virtual ~InterceptedChannelBase(); virtual ~InterceptedChannelBase();
public: public:
explicit InterceptedChannelBase(nsINetworkInterceptController* aController); explicit InterceptedChannelBase(nsINetworkInterceptController* aController);
@ -60,6 +67,50 @@ public:
NS_IMETHOD GetConsoleReportCollector(nsIConsoleReportCollector** aCollectorOut) override; NS_IMETHOD GetConsoleReportCollector(nsIConsoleReportCollector** aCollectorOut) override;
NS_IMETHOD SetReleaseHandle(nsISupports* aHandle) override; NS_IMETHOD SetReleaseHandle(nsISupports* aHandle) override;
NS_IMETHODIMP
SetLaunchServiceWorkerStart(TimeStamp aTimeStamp) override
{
mLaunchServiceWorkerStart = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
SetLaunchServiceWorkerEnd(TimeStamp aTimeStamp) override
{
mLaunchServiceWorkerEnd = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
SetDispatchFetchEventStart(TimeStamp aTimeStamp) override
{
mDispatchFetchEventStart = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
SetDispatchFetchEventEnd(TimeStamp aTimeStamp) override
{
mDispatchFetchEventEnd = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
SetHandleFetchEventStart(TimeStamp aTimeStamp) override
{
mHandleFetchEventStart = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
SetHandleFetchEventEnd(TimeStamp aTimeStamp) override
{
mHandleFetchEventEnd = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP SaveTimeStampsToUnderlyingChannel() override;
static already_AddRefed<nsIURI> static already_AddRefed<nsIURI>
SecureUpgradeChannelURI(nsIChannel* aChannel); SecureUpgradeChannelURI(nsIChannel* aChannel);
}; };

View file

@ -539,13 +539,25 @@ NullHttpChannel::SetTimingEnabled(bool aTimingEnabled)
} }
NS_IMETHODIMP NS_IMETHODIMP
NullHttpChannel::GetRedirectCount(uint16_t *aRedirectCount) NullHttpChannel::GetRedirectCount(uint8_t *aRedirectCount)
{ {
return NS_ERROR_NOT_IMPLEMENTED; return NS_ERROR_NOT_IMPLEMENTED;
} }
NS_IMETHODIMP NS_IMETHODIMP
NullHttpChannel::SetRedirectCount(uint16_t aRedirectCount) NullHttpChannel::SetRedirectCount(uint8_t aRedirectCount)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
NullHttpChannel::GetInternalRedirectCount(uint8_t *aRedirectCount)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
NullHttpChannel::SetInternalRedirectCount(uint8_t aRedirectCount)
{ {
return NS_ERROR_NOT_IMPLEMENTED; return NS_ERROR_NOT_IMPLEMENTED;
} }
@ -564,6 +576,90 @@ NullHttpChannel::GetAsyncOpen(mozilla::TimeStamp *aAsyncOpen)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
NullHttpChannel::GetLaunchServiceWorkerStart(mozilla::TimeStamp *_retval)
{
MOZ_ASSERT(_retval);
*_retval = mAsyncOpenTime;
return NS_OK;
}
NS_IMETHODIMP
NullHttpChannel::SetLaunchServiceWorkerStart(mozilla::TimeStamp aTimeStamp)
{
return NS_OK;
}
NS_IMETHODIMP
NullHttpChannel::GetLaunchServiceWorkerEnd(mozilla::TimeStamp *_retval)
{
MOZ_ASSERT(_retval);
*_retval = mAsyncOpenTime;
return NS_OK;
}
NS_IMETHODIMP
NullHttpChannel::SetLaunchServiceWorkerEnd(mozilla::TimeStamp aTimeStamp)
{
return NS_OK;
}
NS_IMETHODIMP
NullHttpChannel::GetDispatchFetchEventStart(mozilla::TimeStamp *_retval)
{
MOZ_ASSERT(_retval);
*_retval = mAsyncOpenTime;
return NS_OK;
}
NS_IMETHODIMP
NullHttpChannel::SetDispatchFetchEventStart(mozilla::TimeStamp aTimeStamp)
{
return NS_OK;
}
NS_IMETHODIMP
NullHttpChannel::GetDispatchFetchEventEnd(mozilla::TimeStamp *_retval)
{
MOZ_ASSERT(_retval);
*_retval = mAsyncOpenTime;
return NS_OK;
}
NS_IMETHODIMP
NullHttpChannel::SetDispatchFetchEventEnd(mozilla::TimeStamp aTimeStamp)
{
return NS_OK;
}
NS_IMETHODIMP
NullHttpChannel::GetHandleFetchEventStart(mozilla::TimeStamp *_retval)
{
MOZ_ASSERT(_retval);
*_retval = mAsyncOpenTime;
return NS_OK;
}
NS_IMETHODIMP
NullHttpChannel::SetHandleFetchEventStart(mozilla::TimeStamp aTimeStamp)
{
return NS_OK;
}
NS_IMETHODIMP
NullHttpChannel::GetHandleFetchEventEnd(mozilla::TimeStamp *_retval)
{
MOZ_ASSERT(_retval);
*_retval = mAsyncOpenTime;
return NS_OK;
}
NS_IMETHODIMP
NullHttpChannel::SetHandleFetchEventEnd(mozilla::TimeStamp aTimeStamp)
{
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
NullHttpChannel::GetDomainLookupStart(mozilla::TimeStamp *aDomainLookupStart) NullHttpChannel::GetDomainLookupStart(mozilla::TimeStamp *aDomainLookupStart)
{ {
@ -761,6 +857,12 @@ NullHttpChannel::Get##name##Time(PRTime* _retval) { \
IMPL_TIMING_ATTR(ChannelCreation) IMPL_TIMING_ATTR(ChannelCreation)
IMPL_TIMING_ATTR(AsyncOpen) IMPL_TIMING_ATTR(AsyncOpen)
IMPL_TIMING_ATTR(LaunchServiceWorkerStart)
IMPL_TIMING_ATTR(LaunchServiceWorkerEnd)
IMPL_TIMING_ATTR(DispatchFetchEventStart)
IMPL_TIMING_ATTR(DispatchFetchEventEnd)
IMPL_TIMING_ATTR(HandleFetchEventStart)
IMPL_TIMING_ATTR(HandleFetchEventEnd)
IMPL_TIMING_ATTR(DomainLookupStart) IMPL_TIMING_ATTR(DomainLookupStart)
IMPL_TIMING_ATTR(DomainLookupEnd) IMPL_TIMING_ATTR(DomainLookupEnd)
IMPL_TIMING_ATTR(ConnectStart) IMPL_TIMING_ATTR(ConnectStart)

View file

@ -5402,7 +5402,7 @@ nsHttpChannel::AsyncProcessRedirection(uint32_t redirectType)
if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII, locationBuf)) if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII, locationBuf))
location = locationBuf; location = locationBuf;
if (mRedirectionLimit == 0) { if (mRedirectCount >= mRedirectionLimit || mInternalRedirectCount >= mRedirectionLimit) {
LOG(("redirection limit reached!\n")); LOG(("redirection limit reached!\n"));
return NS_ERROR_REDIRECT_LOOP; return NS_ERROR_REDIRECT_LOOP;
} }

View file

@ -1,9 +1,2 @@
[resource-timing.https.html] [resource-timing.https.html]
type: testharness type: testharness
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1178713
[Controlled resource loads]
expected: FAIL
[Non-controlled resource loads]
expected: FAIL

View file

@ -13,6 +13,10 @@ function verify(performance, resource, description) {
assert_greater_than(entry.workerStart, 0, description); assert_greater_than(entry.workerStart, 0, description);
assert_greater_than_equal(entry.workerStart, entry.startTime, description); assert_greater_than_equal(entry.workerStart, entry.startTime, description);
assert_less_than_equal(entry.workerStart, entry.fetchStart, description); assert_less_than_equal(entry.workerStart, entry.fetchStart, description);
assert_greater_than_equal(entry.responseStart, entry.fetchStart, description);
assert_greater_than_equal(entry.responseEnd, entry.responseStart, description);
assert_greater_than(entry.responseEnd, entry.fetchStart, description);
assert_greater_than(entry.duration, 0, description);
if (resource.indexOf('redirect.py') != -1) { if (resource.indexOf('redirect.py') != -1) {
assert_less_than_equal(entry.workerStart, entry.redirectStart, assert_less_than_equal(entry.workerStart, entry.redirectStart,
description); description);

View file

@ -1,5 +1,9 @@
self.addEventListener('fetch', function(event) { self.addEventListener('fetch', function(event) {
if (event.request.url.indexOf('dummy.js') != -1) { if (event.request.url.indexOf('dummy.js') != -1) {
event.respondWith(new Response()); event.respondWith(new Promise(resolve => {
// Slightly delay the response so we ensure we get a non-zero
// duration.
setTimeout(_ => resolve(new Response()), 100);
}));
} }
}); });