Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsDOMNavigationTiming.h"
#include "GeckoProfiler.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/glean/GleanMetrics.h"
#include "mozilla/dom/Document.h"
#include "mozilla/ipc/IPDLParamTraits.h"
#include "mozilla/ipc/URIUtils.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsDocShell.h"
#include "nsHttp.h"
#include "nsIScriptSecurityManager.h"
#include "nsIURI.h"
#include "nsPrintfCString.h"
#include "prtime.h"
using namespace mozilla;
namespace mozilla {
LazyLogModule gPageLoadLog("PageLoad");
#define PAGELOAD_LOG(args) MOZ_LOG(gPageLoadLog, LogLevel::Debug, args)
#define PAGELOAD_LOG_ENABLED() MOZ_LOG_TEST(gPageLoadLog, LogLevel::Error)
} // namespace mozilla
nsDOMNavigationTiming::nsDOMNavigationTiming(nsDocShell* aDocShell) {
Clear();
mDocShell = aDocShell;
}
nsDOMNavigationTiming::~nsDOMNavigationTiming() = default;
void nsDOMNavigationTiming::Clear() {
mNavigationType = TYPE_RESERVED;
mNavigationStartHighRes = 0;
mBeforeUnloadStart = TimeStamp();
mUnloadStart = TimeStamp();
mUnloadEnd = TimeStamp();
mLoadEventStart = TimeStamp();
mLoadEventEnd = TimeStamp();
mDOMLoading = TimeStamp();
mDOMInteractive = TimeStamp();
mDOMContentLoadedEventStart = TimeStamp();
mDOMContentLoadedEventEnd = TimeStamp();
mDOMComplete = TimeStamp();
mContentfulComposite = TimeStamp();
mLargestContentfulRender = TimeStamp();
mNonBlankPaint = TimeStamp();
mDocShellHasBeenActiveSinceNavigationStart = false;
}
void nsDOMNavigationTiming::Anonymize(nsIURI* aFinalURI) {
mLoadedURI = aFinalURI;
mUnloadedURI = nullptr;
mBeforeUnloadStart = TimeStamp();
mUnloadStart = TimeStamp();
mUnloadEnd = TimeStamp();
}
DOMTimeMilliSec nsDOMNavigationTiming::TimeStampToDOM(TimeStamp aStamp) const {
if (aStamp.IsNull()) {
return 0;
}
TimeDuration duration = aStamp - mNavigationStart;
return GetNavigationStart() + static_cast<int64_t>(duration.ToMilliseconds());
}
void nsDOMNavigationTiming::NotifyNavigationStart(
DocShellState aDocShellState) {
mNavigationStartHighRes = (double)PR_Now() / PR_USEC_PER_MSEC;
mNavigationStart = TimeStamp::Now();
mDocShellHasBeenActiveSinceNavigationStart =
(aDocShellState == DocShellState::eActive);
PROFILER_MARKER_UNTYPED("Navigation::Start", DOM,
MarkerInnerWindowIdFromDocShell(mDocShell));
}
void nsDOMNavigationTiming::NotifyFetchStart(nsIURI* aURI,
Type aNavigationType) {
mNavigationType = aNavigationType;
// At the unload event time we don't really know the loading uri.
// Need it for later check for unload timing access.
mLoadedURI = aURI;
}
void nsDOMNavigationTiming::NotifyRestoreStart() {
mNavigationType = TYPE_BACK_FORWARD;
}
void nsDOMNavigationTiming::NotifyBeforeUnload() {
mBeforeUnloadStart = TimeStamp::Now();
}
void nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* aOldURI) {
mUnloadStart = mBeforeUnloadStart;
mUnloadedURI = aOldURI;
}
void nsDOMNavigationTiming::NotifyUnloadEventStart() {
mUnloadStart = TimeStamp::Now();
PROFILER_MARKER("Unload", NETWORK,
MarkerOptions(MarkerTiming::IntervalStart(),
MarkerInnerWindowIdFromDocShell(mDocShell)),
Tracing, "Navigation");
}
void nsDOMNavigationTiming::NotifyUnloadEventEnd() {
mUnloadEnd = TimeStamp::Now();
PROFILER_MARKER("Unload", NETWORK,
MarkerOptions(MarkerTiming::IntervalEnd(),
MarkerInnerWindowIdFromDocShell(mDocShell)),
Tracing, "Navigation");
}
void nsDOMNavigationTiming::NotifyLoadEventStart() {
if (!mLoadEventStart.IsNull()) {
return;
}
mLoadEventStart = TimeStamp::Now();
PROFILER_MARKER("Load", NETWORK,
MarkerOptions(MarkerTiming::IntervalStart(),
MarkerInnerWindowIdFromDocShell(mDocShell)),
Tracing, "Navigation");
if (IsTopLevelContentDocumentInContentProcess()) {
TimeStamp now = TimeStamp::Now();
glean::performance_time::load_event_start.AccumulateRawDuration(
now - mNavigationStart);
}
}
void nsDOMNavigationTiming::NotifyLoadEventEnd() {
if (!mLoadEventEnd.IsNull()) {
return;
}
mLoadEventEnd = TimeStamp::Now();
PROFILER_MARKER("Load", NETWORK,
MarkerOptions(MarkerTiming::IntervalEnd(),
MarkerInnerWindowIdFromDocShell(mDocShell)),
Tracing, "Navigation");
if (IsTopLevelContentDocumentInContentProcess()) {
if (profiler_thread_is_being_profiled_for_markers() ||
PAGELOAD_LOG_ENABLED()) {
TimeDuration elapsed = mLoadEventEnd - mNavigationStart;
TimeDuration duration = mLoadEventEnd - mLoadEventStart;
nsPrintfCString marker(
"Document %s loaded after %dms, load event duration %dms",
nsContentUtils::TruncatedURLForDisplay(mLoadedURI).get(),
int(elapsed.ToMilliseconds()), int(duration.ToMilliseconds()));
PAGELOAD_LOG(("%s", marker.get()));
PROFILER_MARKER_TEXT(
"DocumentLoad", DOM,
MarkerOptions(MarkerTiming::Interval(mNavigationStart, mLoadEventEnd),
MarkerInnerWindowIdFromDocShell(mDocShell)),
marker);
}
glean::performance_time::load_event_end.AccumulateRawDuration(
TimeStamp::Now() - mNavigationStart);
}
}
void nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI,
TimeStamp aValue) {
if (!mDOMLoading.IsNull()) {
return;
}
mLoadedURI = aURI;
mDOMLoading = aValue;
}
void nsDOMNavigationTiming::NotifyDOMLoading(nsIURI* aURI) {
if (!mDOMLoading.IsNull()) {
return;
}
mLoadedURI = aURI;
mDOMLoading = TimeStamp::Now();
PROFILER_MARKER_UNTYPED("Navigation::DOMLoading", DOM,
MarkerInnerWindowIdFromDocShell(mDocShell));
}
void nsDOMNavigationTiming::NotifyDOMInteractive(nsIURI* aURI) {
if (!mDOMInteractive.IsNull()) {
return;
}
mLoadedURI = aURI;
mDOMInteractive = TimeStamp::Now();
PROFILER_MARKER_UNTYPED("Navigation::DOMInteractive", DOM,
MarkerInnerWindowIdFromDocShell(mDocShell));
}
void nsDOMNavigationTiming::NotifyDOMComplete(nsIURI* aURI) {
if (!mDOMComplete.IsNull()) {
return;
}
mLoadedURI = aURI;
mDOMComplete = TimeStamp::Now();
PROFILER_MARKER_UNTYPED("Navigation::DOMComplete", DOM,
MarkerInnerWindowIdFromDocShell(mDocShell));
}
void nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI) {
if (!mDOMContentLoadedEventStart.IsNull()) {
return;
}
mLoadedURI = aURI;
mDOMContentLoadedEventStart = TimeStamp::Now();
PROFILER_MARKER("DOMContentLoaded", NETWORK,
MarkerOptions(MarkerTiming::IntervalStart(),
MarkerInnerWindowIdFromDocShell(mDocShell)),
Tracing, "Navigation");
if (IsTopLevelContentDocumentInContentProcess()) {
TimeStamp now = TimeStamp::Now();
glean::performance_time::dom_content_loaded_start.AccumulateRawDuration(
now - mNavigationStart);
}
}
void nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI) {
if (!mDOMContentLoadedEventEnd.IsNull()) {
return;
}
mLoadedURI = aURI;
mDOMContentLoadedEventEnd = TimeStamp::Now();
PROFILER_MARKER("DOMContentLoaded", NETWORK,
MarkerOptions(MarkerTiming::IntervalEnd(),
MarkerInnerWindowIdFromDocShell(mDocShell)),
Tracing, "Navigation");
if (IsTopLevelContentDocumentInContentProcess()) {
glean::performance_time::dom_content_loaded_end.AccumulateRawDuration(
TimeStamp::Now() - mNavigationStart);
}
}
// static
void nsDOMNavigationTiming::TTITimeoutCallback(nsITimer* aTimer,
void* aClosure) {
nsDOMNavigationTiming* self = static_cast<nsDOMNavigationTiming*>(aClosure);
self->TTITimeout(aTimer);
}
#define TTI_WINDOW_SIZE_MS (5 * 1000)
void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) {
// Check TTI: see if it's been 5 seconds since the last Long Task
TimeStamp now = TimeStamp::Now();
MOZ_RELEASE_ASSERT(!mContentfulComposite.IsNull(),
"TTI timeout with no contentful-composite?");
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
TimeStamp lastLongTaskEnded;
mainThread->GetLastLongNonIdleTaskEnd(&lastLongTaskEnded);
// Window starts at mContentfulComposite; any long task before that is ignored
if (lastLongTaskEnded.IsNull() || lastLongTaskEnded < mContentfulComposite) {
PAGELOAD_LOG(
("no longtask (last was %g ms before ContentfulComposite)",
lastLongTaskEnded.IsNull()
? 0
: (mContentfulComposite - lastLongTaskEnded).ToMilliseconds()));
lastLongTaskEnded = mContentfulComposite;
}
TimeDuration delta = now - lastLongTaskEnded;
PAGELOAD_LOG(("TTI delta: %g ms", delta.ToMilliseconds()));
if (delta.ToMilliseconds() < TTI_WINDOW_SIZE_MS) {
// Less than 5 seconds since the last long task or start of the window.
// Schedule another check.
PAGELOAD_LOG(("TTI: waiting additional %g ms",
(TTI_WINDOW_SIZE_MS + 100) - delta.ToMilliseconds()));
aTimer->InitWithNamedFuncCallback(
TTITimeoutCallback, this,
(TTI_WINDOW_SIZE_MS + 100) -
delta.ToMilliseconds(), // slightly after the window ends
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
"nsDOMNavigationTiming::TTITimeout");
return;
}
// To correctly implement TTI/TTFI as proposed, we'd need to not
// fire it until there are no more than 2 network loads. By the
// proposed definition, without that we're closer to
// TimeToFirstInteractive. There are also arguments about what sort
// of loads should qualify.
// XXX check number of network loads, and if > 2 mark to check if loads
// decreases to 2 (or record that point and let the normal timer here
// handle it)
// TTI has occurred! TTI is either FCP (if there are no longtasks and no
// DCLEnd in the window that starts at FCP), or at the end of the last
// Long Task or DOMContentLoadedEnd (whichever is later). lastLongTaskEnded
// is >= FCP here.
if (mTTFI.IsNull()) {
// lastLongTaskEnded is >= mContentfulComposite
mTTFI = (mDOMContentLoadedEventEnd.IsNull() ||
lastLongTaskEnded > mDOMContentLoadedEventEnd)
? lastLongTaskEnded
: mDOMContentLoadedEventEnd;
PAGELOAD_LOG(
("TTFI after %dms (LongTask was at %dms, DCL was %dms)",
int((mTTFI - mNavigationStart).ToMilliseconds()),
lastLongTaskEnded.IsNull()
? 0
: int((lastLongTaskEnded - mNavigationStart).ToMilliseconds()),
mDOMContentLoadedEventEnd.IsNull()
? 0
: int((mDOMContentLoadedEventEnd - mNavigationStart)
.ToMilliseconds())));
}
// XXX Implement TTI via check number of network loads, and if > 2 mark
// to check if loads decreases to 2 (or record that point and let the
// normal timer here handle it)
mTTITimer = nullptr;
if (profiler_thread_is_being_profiled_for_markers() ||
PAGELOAD_LOG_ENABLED()) {
TimeDuration elapsed = mTTFI - mNavigationStart;
MOZ_ASSERT(elapsed.ToMilliseconds() > 0);
TimeDuration elapsedLongTask =
lastLongTaskEnded.IsNull() ? 0 : lastLongTaskEnded - mNavigationStart;
nsPrintfCString marker(
"TTFI after %dms (LongTask was at %dms) for URL %s",
int(elapsed.ToMilliseconds()), int(elapsedLongTask.ToMilliseconds()),
nsContentUtils::TruncatedURLForDisplay(mLoadedURI).get());
PROFILER_MARKER_TEXT(
"TimeToFirstInteractive (TTFI)", DOM,
MarkerOptions(MarkerTiming::Interval(mNavigationStart, mTTFI),
MarkerInnerWindowIdFromDocShell(mDocShell)),
marker);
}
}
void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mNavigationStart.IsNull());
if (!mNonBlankPaint.IsNull()) {
return;
}
mNonBlankPaint = TimeStamp::Now();
if (profiler_thread_is_being_profiled_for_markers() ||
PAGELOAD_LOG_ENABLED()) {
TimeDuration elapsed = mNonBlankPaint - mNavigationStart;
nsPrintfCString marker(
"Non-blank paint after %dms for URL %s, %s",
int(elapsed.ToMilliseconds()),
nsContentUtils::TruncatedURLForDisplay(mLoadedURI).get(),
mDocShellHasBeenActiveSinceNavigationStart
? "foreground tab"
: "this tab was inactive some of the time between navigation start "
"and first non-blank paint");
PAGELOAD_LOG(("%s", marker.get()));
PROFILER_MARKER_TEXT(
"FirstNonBlankPaint", DOM,
MarkerOptions(MarkerTiming::Interval(mNavigationStart, mNonBlankPaint),
MarkerInnerWindowIdFromDocShell(mDocShell)),
marker);
}
if (mDocShellHasBeenActiveSinceNavigationStart) {
glean::performance_page::non_blank_paint.AccumulateRawDuration(
mNonBlankPaint - mNavigationStart);
}
}
void nsDOMNavigationTiming::NotifyContentfulCompositeForRootContentDocument(
const mozilla::TimeStamp& aCompositeEndTime) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mNavigationStart.IsNull());
if (!mContentfulComposite.IsNull()) {
return;
}
mContentfulComposite = aCompositeEndTime;
if (profiler_thread_is_being_profiled_for_markers() ||
PAGELOAD_LOG_ENABLED()) {
TimeDuration elapsed = mContentfulComposite - mNavigationStart;
nsPrintfCString marker(
"Contentful composite after %dms for URL %s, %s",
int(elapsed.ToMilliseconds()),
nsContentUtils::TruncatedURLForDisplay(mLoadedURI).get(),
mDocShellHasBeenActiveSinceNavigationStart
? "foreground tab"
: "this tab was inactive some of the time between navigation start "
"and first non-blank paint");
PAGELOAD_LOG(("%s", marker.get()));
PROFILER_MARKER_TEXT(
"FirstContentfulComposite", DOM,
MarkerOptions(
MarkerTiming::Interval(mNavigationStart, mContentfulComposite),
MarkerInnerWindowIdFromDocShell(mDocShell)),
marker);
}
if (!mTTITimer) {
mTTITimer = NS_NewTimer();
}
// TTI is first checked 5 seconds after the FCP (non-blank-paint is very close
// to FCP).
mTTITimer->InitWithNamedFuncCallback(TTITimeoutCallback, this,
TTI_WINDOW_SIZE_MS,
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
"nsDOMNavigationTiming::TTITimeout");
if (mDocShellHasBeenActiveSinceNavigationStart) {
Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_FIRST_CONTENTFUL_PAINT_MS,
mNavigationStart, mContentfulComposite);
}
}
void nsDOMNavigationTiming::NotifyLargestContentfulRenderForRootContentDocument(
const DOMHighResTimeStamp& aRenderTime) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mNavigationStart.IsNull());
// This can get called multiple times and updates over time.
mLargestContentfulRender =
mNavigationStart + TimeDuration::FromMilliseconds(aRenderTime);
}
void nsDOMNavigationTiming::NotifyDocShellStateChanged(
DocShellState aDocShellState) {
mDocShellHasBeenActiveSinceNavigationStart &=
(aDocShellState == DocShellState::eActive);
}
void nsDOMNavigationTiming::MaybeAddLCPProfilerMarker(
MarkerInnerWindowId aInnerWindowID) {
// This method might get called from outside of the main thread, so can't
// check `profiler_thread_is_being_profiled_for_markers()` here.
if (!profiler_is_active_and_unpaused()) {
return;
}
TimeStamp navStartTime = GetNavigationStartTimeStamp();
TimeStamp lcpTime = GetLargestContentfulRenderTimeStamp();
if (!navStartTime || !lcpTime) {
return;
}
TimeDuration elapsed = lcpTime - navStartTime;
nsPrintfCString marker("Largest contentful paint after %dms",
int(elapsed.ToMilliseconds()));
PROFILER_MARKER_TEXT(
"LargestContentfulPaint", DOM,
// Putting this marker to the main thread even if it's called from another
// one.
MarkerOptions(MarkerThreadId::MainThread(),
MarkerTiming::Interval(navStartTime, lcpTime),
std::move(aInnerWindowID)),
marker);
}
mozilla::TimeStamp nsDOMNavigationTiming::GetUnloadEventStartTimeStamp() const {
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
// todo: if you intend to update CheckSameOriginURI to log the error to the
// console you also need to update the 'aFromPrivateWindow' argument.
nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false, false);
if (NS_SUCCEEDED(rv)) {
return mUnloadStart;
}
return mozilla::TimeStamp();
}
mozilla::TimeStamp nsDOMNavigationTiming::GetUnloadEventEndTimeStamp() const {
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
// todo: if you intend to update CheckSameOriginURI to log the error to the
// console you also need to update the 'aFromPrivateWindow' argument.
nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false, false);
if (NS_SUCCEEDED(rv)) {
return mUnloadEnd;
}
return mozilla::TimeStamp();
}
bool nsDOMNavigationTiming::IsTopLevelContentDocumentInContentProcess() const {
if (!mDocShell) {
return false;
}
if (!XRE_IsContentProcess()) {
return false;
}
return mDocShell->GetBrowsingContext()->IsTopContent();
}
nsDOMNavigationTiming::nsDOMNavigationTiming(nsDocShell* aDocShell,
nsDOMNavigationTiming* aOther)
: mDocShell(aDocShell),
mUnloadedURI(aOther->mUnloadedURI),
mLoadedURI(aOther->mLoadedURI),
mNavigationType(aOther->mNavigationType),
mNavigationStartHighRes(aOther->mNavigationStartHighRes),
mNavigationStart(aOther->mNavigationStart),
mNonBlankPaint(aOther->mNonBlankPaint),
mContentfulComposite(aOther->mContentfulComposite),
mBeforeUnloadStart(aOther->mBeforeUnloadStart),
mUnloadStart(aOther->mUnloadStart),
mUnloadEnd(aOther->mUnloadEnd),
mLoadEventStart(aOther->mLoadEventStart),
mLoadEventEnd(aOther->mLoadEventEnd),
mDOMLoading(aOther->mDOMLoading),
mDOMInteractive(aOther->mDOMInteractive),
mDOMContentLoadedEventStart(aOther->mDOMContentLoadedEventStart),
mDOMContentLoadedEventEnd(aOther->mDOMContentLoadedEventEnd),
mDOMComplete(aOther->mDOMComplete),
mTTFI(aOther->mTTFI),
mDocShellHasBeenActiveSinceNavigationStart(
aOther->mDocShellHasBeenActiveSinceNavigationStart) {}
/* static */
void mozilla::ipc::IPDLParamTraits<nsDOMNavigationTiming*>::Write(
IPC::MessageWriter* aWriter, IProtocol* aActor,
nsDOMNavigationTiming* aParam) {
bool isNull = !aParam;
WriteIPDLParam(aWriter, aActor, isNull);
if (isNull) {
return;
}
RefPtr<nsIURI> unloadedURI = aParam->mUnloadedURI.get();
RefPtr<nsIURI> loadedURI = aParam->mLoadedURI.get();
WriteIPDLParam(aWriter, aActor, unloadedURI ? Some(unloadedURI) : Nothing());
WriteIPDLParam(aWriter, aActor, loadedURI ? Some(loadedURI) : Nothing());
WriteIPDLParam(aWriter, aActor, uint32_t(aParam->mNavigationType));
WriteIPDLParam(aWriter, aActor, aParam->mNavigationStartHighRes);
WriteIPDLParam(aWriter, aActor, aParam->mNavigationStart);
WriteIPDLParam(aWriter, aActor, aParam->mNonBlankPaint);
WriteIPDLParam(aWriter, aActor, aParam->mContentfulComposite);
WriteIPDLParam(aWriter, aActor, aParam->mBeforeUnloadStart);
WriteIPDLParam(aWriter, aActor, aParam->mUnloadStart);
WriteIPDLParam(aWriter, aActor, aParam->mUnloadEnd);
WriteIPDLParam(aWriter, aActor, aParam->mLoadEventStart);
WriteIPDLParam(aWriter, aActor, aParam->mLoadEventEnd);
WriteIPDLParam(aWriter, aActor, aParam->mDOMLoading);
WriteIPDLParam(aWriter, aActor, aParam->mDOMInteractive);
WriteIPDLParam(aWriter, aActor, aParam->mDOMContentLoadedEventStart);
WriteIPDLParam(aWriter, aActor, aParam->mDOMContentLoadedEventEnd);
WriteIPDLParam(aWriter, aActor, aParam->mDOMComplete);
WriteIPDLParam(aWriter, aActor, aParam->mTTFI);
WriteIPDLParam(aWriter, aActor,
aParam->mDocShellHasBeenActiveSinceNavigationStart);
}
/* static */
bool mozilla::ipc::IPDLParamTraits<nsDOMNavigationTiming*>::Read(
IPC::MessageReader* aReader, IProtocol* aActor,
RefPtr<nsDOMNavigationTiming>* aResult) {
bool isNull;
if (!ReadIPDLParam(aReader, aActor, &isNull)) {
return false;
}
if (isNull) {
*aResult = nullptr;
return true;
}
auto timing = MakeRefPtr<nsDOMNavigationTiming>(nullptr);
uint32_t type;
Maybe<RefPtr<nsIURI>> unloadedURI;
Maybe<RefPtr<nsIURI>> loadedURI;
if (!ReadIPDLParam(aReader, aActor, &unloadedURI) ||
!ReadIPDLParam(aReader, aActor, &loadedURI) ||
!ReadIPDLParam(aReader, aActor, &type) ||
!ReadIPDLParam(aReader, aActor, &timing->mNavigationStartHighRes) ||
!ReadIPDLParam(aReader, aActor, &timing->mNavigationStart) ||
!ReadIPDLParam(aReader, aActor, &timing->mNonBlankPaint) ||
!ReadIPDLParam(aReader, aActor, &timing->mContentfulComposite) ||
!ReadIPDLParam(aReader, aActor, &timing->mBeforeUnloadStart) ||
!ReadIPDLParam(aReader, aActor, &timing->mUnloadStart) ||
!ReadIPDLParam(aReader, aActor, &timing->mUnloadEnd) ||
!ReadIPDLParam(aReader, aActor, &timing->mLoadEventStart) ||
!ReadIPDLParam(aReader, aActor, &timing->mLoadEventEnd) ||
!ReadIPDLParam(aReader, aActor, &timing->mDOMLoading) ||
!ReadIPDLParam(aReader, aActor, &timing->mDOMInteractive) ||
!ReadIPDLParam(aReader, aActor, &timing->mDOMContentLoadedEventStart) ||
!ReadIPDLParam(aReader, aActor, &timing->mDOMContentLoadedEventEnd) ||
!ReadIPDLParam(aReader, aActor, &timing->mDOMComplete) ||
!ReadIPDLParam(aReader, aActor, &timing->mTTFI) ||
!ReadIPDLParam(aReader, aActor,
&timing->mDocShellHasBeenActiveSinceNavigationStart)) {
return false;
}
timing->mNavigationType = nsDOMNavigationTiming::Type(type);
if (unloadedURI) {
timing->mUnloadedURI = std::move(*unloadedURI);
}
if (loadedURI) {
timing->mLoadedURI = std::move(*loadedURI);
}
*aResult = std::move(timing);
return true;
}