author | Will Hawkins <whawkins@mozilla.com> |
Thu, 11 Jul 2019 04:00:41 +0000 | |
changeset 482330 | 3f3048189adbaef8933a5da5600e88b8a1aadc9b |
parent 482329 | 012d1b8802007b5d0dab4e741b1eead6186944c3 |
child 482331 | 9c274e3d473bdd3bb917ff34fabefd555db2d2cd |
push id | 89700 |
push user | csabou@mozilla.com |
push date | Thu, 11 Jul 2019 04:20:53 +0000 |
treeherder | autoland@3f3048189adb [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | Ehsan, barret |
bugs | 1559657 |
milestone | 70.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -3489,16 +3489,39 @@ NS_IMETHODIMP BrowserChild::OnStateChang Maybe<WebProgressData> webProgressData; Maybe<WebProgressStateChangeData> stateChangeData; RequestData requestData; MOZ_TRY(PrepareProgressListenerData(aWebProgress, aRequest, webProgressData, requestData)); + /* + * If + * 1) this is a document, + * 2) the document is top-level, + * 3) the document is completely loaded (STATE_STOP), and + * 4) this is the end of activity for the document + * (STATE_IS_WINDOW, STATE_IS_NETWORK), + * then record the elapsed time that it took to load. + */ + if (document && webProgressData->isTopLevel() && + (aStateFlags & nsIWebProgressListener::STATE_STOP) && + (aStateFlags & nsIWebProgressListener::STATE_IS_WINDOW) && + (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)) { + RefPtr<nsDOMNavigationTiming> navigationTiming = + document->GetNavigationTiming(); + if (navigationTiming) { + TimeDuration elapsedLoadTimeMS = + TimeStamp::Now() - navigationTiming->GetNavigationStartTimeStamp(); + requestData.elapsedLoadTimeMS() = + Some(elapsedLoadTimeMS.ToMilliseconds()); + } + } + if (webProgressData->isTopLevel()) { stateChangeData.emplace(); stateChangeData->isNavigating() = docShell->GetIsNavigating(); stateChangeData->mayEnableCharacterEncodingMenu() = docShell->GetMayEnableCharacterEncodingMenu(); stateChangeData->charsetAutodetected() = docShell->GetCharsetAutodetected();
--- a/dom/ipc/BrowserParent.cpp +++ b/dom/ipc/BrowserParent.cpp @@ -2618,17 +2618,17 @@ void BrowserParent::ReconstructWebProgre } else { webProgress = new RemoteWebProgress(aManager, 0, 0, 0, false, false); } webProgress.forget(aOutWebProgress); if (aRequestData.requestURI()) { nsCOMPtr<nsIRequest> request = MakeAndAddRef<RemoteWebProgressRequest>( aRequestData.requestURI(), aRequestData.originalRequestURI(), - aRequestData.matchedList()); + aRequestData.matchedList(), aRequestData.elapsedLoadTimeMS()); request.forget(aOutRequest); } else { *aOutRequest = nullptr; } } mozilla::ipc::IPCResult BrowserParent::RecvSessionStoreUpdate( const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
--- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -113,16 +113,22 @@ struct WebProgressData uint64_t innerDOMWindowID; }; struct RequestData { nsIURI requestURI; nsIURI originalRequestURI; nsCString matchedList; + // The elapsedLoadTimeMS is only set when the request has finished loading. + // In other words, this field is set only during and |OnStateChange| event + // where |aStateFlags| contains |nsIWebProgressListener::STATE_STOP| and + // |nsIWebProgressListener::STATE_IS_NETWORK| and + // |nsIWebProgressListener::STATE_IS_WINDOW|, and the document is top level. + uint64_t? elapsedLoadTimeMS; }; struct WebProgressStateChangeData { bool isNavigating; bool mayEnableCharacterEncodingMenu; bool charsetAutodetected;
--- a/dom/ipc/RemoteWebProgressRequest.cpp +++ b/dom/ipc/RemoteWebProgressRequest.cpp @@ -13,16 +13,27 @@ NS_IMPL_ISUPPORTS(RemoteWebProgressReque NS_IMETHODIMP RemoteWebProgressRequest::Init(nsIURI* aURI, nsIURI* aOriginalURI) { mURI = aURI; mOriginalURI = aOriginalURI; return NS_OK; } +NS_IMETHODIMP RemoteWebProgressRequest::GetElapsedLoadTimeMS( + uint64_t* aElapsedLoadTimeMS) { + NS_ENSURE_ARG_POINTER(aElapsedLoadTimeMS); + if (mMaybeElapsedLoadTimeMS) { + *aElapsedLoadTimeMS = *mMaybeElapsedLoadTimeMS; + return NS_OK; + } + *aElapsedLoadTimeMS = 0; + return NS_ERROR_NOT_AVAILABLE; +} + // nsIChannel methods NS_IMETHODIMP RemoteWebProgressRequest::GetOriginalURI(nsIURI** aOriginalURI) { NS_ENSURE_ARG_POINTER(aOriginalURI); NS_ADDREF(*aOriginalURI = mOriginalURI); return NS_OK; }
--- a/dom/ipc/RemoteWebProgressRequest.h +++ b/dom/ipc/RemoteWebProgressRequest.h @@ -21,24 +21,35 @@ class RemoteWebProgressRequest final : p NS_DECL_NSICHANNEL NS_DECL_NSICLASSIFIEDCHANNEL NS_DECL_NSIREQUEST RemoteWebProgressRequest() : mURI(nullptr), mOriginalURI(nullptr), mMatchedList(VoidCString()) {} RemoteWebProgressRequest(nsIURI* aURI, nsIURI* aOriginalURI, - const nsACString& aMatchedList) - : mURI(aURI), mOriginalURI(aOriginalURI), mMatchedList(aMatchedList) {} + const nsACString& aMatchedList, + const Maybe<uint64_t>& aMaybeElapsedLoadTimeMS) + : mURI(aURI), + mOriginalURI(aOriginalURI), + mMatchedList(aMatchedList), + mMaybeElapsedLoadTimeMS(aMaybeElapsedLoadTimeMS) {} protected: ~RemoteWebProgressRequest() = default; private: nsCOMPtr<nsIURI> mURI; nsCOMPtr<nsIURI> mOriginalURI; nsCString mMatchedList; + + // This field is only Some(...) when the RemoteWebProgressRequest + // is created at a time that the document whose progress is being + // described by this request is top level and its status changes + // from loading to completely loaded. + // See BrowserChild::OnStateChange. + Maybe<uint64_t> mMaybeElapsedLoadTimeMS; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_RemoteWebProgressRequest_h
--- a/dom/ipc/nsIRemoteWebProgressRequest.idl +++ b/dom/ipc/nsIRemoteWebProgressRequest.idl @@ -5,9 +5,16 @@ #include "nsISupports.idl" interface nsIURI; [scriptable, uuid(e14ff4c2-7e26-4748-8755-dfd074b9c746)] interface nsIRemoteWebProgressRequest : nsISupports { void init(in nsIURI aURI, in nsIURI aOriginalURI); + + // This field is available to users in |OnStateChange| methods only + // when the document whose progress is being described by this progress + // request is top level and its status has just changed from loading to + // completely loaded; for invocations of |OnStateChange| before or after + // that transition, this field will throw |NS_ERROR_UNAVAILABLE|. + readonly attribute uint64_t elapsedLoadTimeMS; };
--- a/dom/ipc/tests/browser.ini +++ b/dom/ipc/tests/browser.ini @@ -4,8 +4,10 @@ support-files = file_domainPolicy_base.html file_cancel_content_js.html [browser_domainPolicy.js] [browser_memory_distribution_telemetry.js] skip-if = !e10 # This is an e10s only probe. [browser_cancel_content_js.js] skip-if = !e10s # This is an e10s only probe. +[browser_ElapsedTime.js] +support-files = elapsed_time.sjs
new file mode 100644 --- /dev/null +++ b/dom/ipc/tests/browser_ElapsedTime.js @@ -0,0 +1,63 @@ +"use strict"; + +/* + * Synchronize DELAY_MS with DELAY_MS from elapsed_time.sjs + */ +const DELAY_MS = 200; +const SLOW_PAGE = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" + ) + "elapsed_time.sjs"; + +add_task(async function testLongElapsedTime() { + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function(tabBrowser) { + const flags = Ci.nsIWebProgress.NOTIFY_STATE_NETWORK; + let listener; + + let stateChangeWaiter = new Promise(resolve => { + listener = { + onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { + if (!aWebProgress.isTopLevel) { + return; + } + const isTopLevel = aWebProgress.isTopLevel; + const isStop = aStateFlags & Ci.nsIWebProgressListener.STATE_STOP; + const isNetwork = + aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK; + const isWindow = + aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW; + const isLoadingDocument = aWebProgress.isLoadingDocument; + + if ( + isTopLevel && + isStop && + isWindow && + isNetwork && + !isLoadingDocument + ) { + aRequest.QueryInterface(Ci.nsIRemoteWebProgressRequest); + if (aRequest.elapsedLoadTimeMS >= DELAY_MS) { + resolve(true); + } + } + }, + }; + }); + tabBrowser.addProgressListener(listener, flags); + + BrowserTestUtils.loadURI(tabBrowser, SLOW_PAGE); + await BrowserTestUtils.browserLoaded(tabBrowser); + let pass = await stateChangeWaiter; + + tabBrowser.removeProgressListener(listener); + + ok( + pass, + "Bug 1559657: Check that the elapsedLoadTimeMS in RemoteWebProgress meets expectations." + ); + } + ); +});
new file mode 100644 --- /dev/null +++ b/dom/ipc/tests/elapsed_time.sjs @@ -0,0 +1,31 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const DELAY_MS = 200; + +const HTML = `<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf8"> + </head> + <body> + </body> +</html>`; + +/* + * Keep timer as a global so that it is not GC'd + * between the time that handleRequest() completes + * and it expires. + */ +var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); +function handleRequest(req, resp) { + resp.processAsync(); + resp.setHeader("Cache-Control", "no-cache", false); + resp.setHeader("Content-Type", "text/html;charset=utf-8", false); + + resp.write(HTML); + timer.init(() => { + resp.write(""); + resp.finish(); + }, DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT); +}