tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsDOMNavigationTiming.cpp (22351B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsDOMNavigationTiming.h"
      8 
      9 #include "GeckoProfiler.h"
     10 #include "mozilla/ProfilerMarkers.h"
     11 #include "mozilla/TimeStamp.h"
     12 #include "mozilla/dom/Document.h"
     13 #include "mozilla/glean/DomMetrics.h"
     14 #include "mozilla/ipc/URIUtils.h"
     15 #include "nsCOMPtr.h"
     16 #include "nsContentUtils.h"
     17 #include "nsDocShell.h"
     18 #include "nsHttp.h"
     19 #include "nsIScriptSecurityManager.h"
     20 #include "nsIURI.h"
     21 #include "nsPrintfCString.h"
     22 #include "prtime.h"
     23 
     24 using namespace mozilla;
     25 
     26 namespace mozilla {
     27 
     28 LazyLogModule gPageLoadLog("PageLoad");
     29 #define PAGELOAD_LOG(args) MOZ_LOG(gPageLoadLog, LogLevel::Debug, args)
     30 #define PAGELOAD_LOG_ENABLED() MOZ_LOG_TEST(gPageLoadLog, LogLevel::Error)
     31 
     32 }  // namespace mozilla
     33 
     34 nsDOMNavigationTiming::nsDOMNavigationTiming(nsDocShell* aDocShell) {
     35  Clear();
     36 
     37  mDocShell = aDocShell;
     38 }
     39 
     40 nsDOMNavigationTiming::~nsDOMNavigationTiming() = default;
     41 
     42 void nsDOMNavigationTiming::Clear() {
     43  mNavigationType = TYPE_RESERVED;
     44  mNavigationStartHighRes = 0;
     45 
     46  mBeforeUnloadStart = TimeStamp();
     47  mUnloadStart = TimeStamp();
     48  mUnloadEnd = TimeStamp();
     49  mLoadEventStart = TimeStamp();
     50  mLoadEventEnd = TimeStamp();
     51  mDOMLoading = TimeStamp();
     52  mDOMInteractive = TimeStamp();
     53  mDOMContentLoadedEventStart = TimeStamp();
     54  mDOMContentLoadedEventEnd = TimeStamp();
     55  mDOMComplete = TimeStamp();
     56  mContentfulComposite = TimeStamp();
     57  mLargestContentfulRender = TimeStamp();
     58  mNonBlankPaint = TimeStamp();
     59 
     60  mDocShellHasBeenActiveSinceNavigationStart = false;
     61 }
     62 
     63 void nsDOMNavigationTiming::Anonymize(nsIURI* aFinalURI) {
     64  mLoadedURI = aFinalURI;
     65  mUnloadedURI = nullptr;
     66  mBeforeUnloadStart = TimeStamp();
     67  mUnloadStart = TimeStamp();
     68  mUnloadEnd = TimeStamp();
     69 }
     70 
     71 DOMTimeMilliSec nsDOMNavigationTiming::TimeStampToDOM(TimeStamp aStamp) const {
     72  if (aStamp.IsNull()) {
     73    return 0;
     74  }
     75 
     76  TimeDuration duration = aStamp - mNavigationStart;
     77  return GetNavigationStart() + static_cast<int64_t>(duration.ToMilliseconds());
     78 }
     79 
     80 void nsDOMNavigationTiming::NotifyNavigationStart(
     81    DocShellState aDocShellState) {
     82  mNavigationStartHighRes = (double)PR_Now() / PR_USEC_PER_MSEC;
     83  mNavigationStart = TimeStamp::Now();
     84  mDocShellHasBeenActiveSinceNavigationStart =
     85      (aDocShellState == DocShellState::eActive);
     86  PROFILER_MARKER_UNTYPED("Navigation::Start", DOM,
     87                          MarkerInnerWindowIdFromDocShell(mDocShell));
     88 }
     89 
     90 void nsDOMNavigationTiming::NotifyFetchStart(nsIURI* aURI,
     91                                             Type aNavigationType) {
     92  mNavigationType = aNavigationType;
     93  // At the unload event time we don't really know the loading uri.
     94  // Need it for later check for unload timing access.
     95  mLoadedURI = aURI;
     96 }
     97 
     98 void nsDOMNavigationTiming::NotifyRestoreStart() {
     99  mNavigationType = TYPE_BACK_FORWARD;
    100 }
    101 
    102 void nsDOMNavigationTiming::NotifyBeforeUnload() {
    103  mBeforeUnloadStart = TimeStamp::Now();
    104 }
    105 
    106 void nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* aOldURI) {
    107  mUnloadStart = mBeforeUnloadStart;
    108  mUnloadedURI = aOldURI;
    109 }
    110 
    111 void nsDOMNavigationTiming::NotifyUnloadEventStart() {
    112  mUnloadStart = TimeStamp::Now();
    113  PROFILER_MARKER("Unload", NETWORK,
    114                  MarkerOptions(MarkerTiming::IntervalStart(),
    115                                MarkerInnerWindowIdFromDocShell(mDocShell)),
    116                  Tracing, "Navigation");
    117 }
    118 
    119 void nsDOMNavigationTiming::NotifyUnloadEventEnd() {
    120  mUnloadEnd = TimeStamp::Now();
    121  PROFILER_MARKER("Unload", NETWORK,
    122                  MarkerOptions(MarkerTiming::IntervalEnd(),
    123                                MarkerInnerWindowIdFromDocShell(mDocShell)),
    124                  Tracing, "Navigation");
    125 }
    126 
    127 void nsDOMNavigationTiming::NotifyLoadEventStart() {
    128  if (!mLoadEventStart.IsNull()) {
    129    return;
    130  }
    131  mLoadEventStart = TimeStamp::Now();
    132 
    133  PROFILER_MARKER("Load", NETWORK,
    134                  MarkerOptions(MarkerTiming::IntervalStart(),
    135                                MarkerInnerWindowIdFromDocShell(mDocShell)),
    136                  Tracing, "Navigation");
    137 
    138  if (IsTopLevelContentDocumentInContentProcess()) {
    139    TimeStamp now = TimeStamp::Now();
    140 
    141    glean::performance_time::load_event_start.AccumulateRawDuration(
    142        now - mNavigationStart);
    143  }
    144 }
    145 
    146 void nsDOMNavigationTiming::NotifyLoadEventEnd() {
    147  if (!mLoadEventEnd.IsNull()) {
    148    return;
    149  }
    150  mLoadEventEnd = TimeStamp::Now();
    151 
    152  PROFILER_MARKER("Load", NETWORK,
    153                  MarkerOptions(MarkerTiming::IntervalEnd(),
    154                                MarkerInnerWindowIdFromDocShell(mDocShell)),
    155                  Tracing, "Navigation");
    156 
    157  if (IsTopLevelContentDocumentInContentProcess()) {
    158    if (profiler_thread_is_being_profiled_for_markers() ||
    159        PAGELOAD_LOG_ENABLED()) {
    160      TimeDuration elapsed = mLoadEventEnd - mNavigationStart;
    161      TimeDuration duration = mLoadEventEnd - mLoadEventStart;
    162      nsPrintfCString marker(
    163          "Document %s loaded after %dms, load event duration %dms",
    164          nsContentUtils::TruncatedURLForDisplay(mLoadedURI).get(),
    165          int(elapsed.ToMilliseconds()), int(duration.ToMilliseconds()));
    166      PAGELOAD_LOG(("%s", marker.get()));
    167      PROFILER_MARKER_TEXT(
    168          "DocumentLoad", DOM,
    169          MarkerOptions(MarkerTiming::Interval(mNavigationStart, mLoadEventEnd),
    170                        MarkerInnerWindowIdFromDocShell(mDocShell)),
    171          marker);
    172    }
    173    glean::performance_time::load_event_end.AccumulateRawDuration(
    174        TimeStamp::Now() - mNavigationStart);
    175  }
    176 }
    177 
    178 void nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI,
    179                                                   TimeStamp aValue) {
    180  if (!mDOMLoading.IsNull()) {
    181    return;
    182  }
    183  mLoadedURI = aURI;
    184  mDOMLoading = aValue;
    185 }
    186 
    187 void nsDOMNavigationTiming::NotifyDOMLoading(nsIURI* aURI) {
    188  if (!mDOMLoading.IsNull()) {
    189    return;
    190  }
    191  mLoadedURI = aURI;
    192  mDOMLoading = TimeStamp::Now();
    193 
    194  PROFILER_MARKER_UNTYPED("Navigation::DOMLoading", DOM,
    195                          MarkerInnerWindowIdFromDocShell(mDocShell));
    196 }
    197 
    198 void nsDOMNavigationTiming::NotifyDOMInteractive(nsIURI* aURI) {
    199  if (!mDOMInteractive.IsNull()) {
    200    return;
    201  }
    202  mLoadedURI = aURI;
    203  mDOMInteractive = TimeStamp::Now();
    204 
    205  PROFILER_MARKER_UNTYPED("Navigation::DOMInteractive", DOM,
    206                          MarkerInnerWindowIdFromDocShell(mDocShell));
    207 }
    208 
    209 void nsDOMNavigationTiming::NotifyDOMComplete(nsIURI* aURI) {
    210  if (!mDOMComplete.IsNull()) {
    211    return;
    212  }
    213  mLoadedURI = aURI;
    214  mDOMComplete = TimeStamp::Now();
    215 
    216  PROFILER_MARKER_UNTYPED("Navigation::DOMComplete", DOM,
    217                          MarkerInnerWindowIdFromDocShell(mDocShell));
    218 }
    219 
    220 void nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI) {
    221  if (!mDOMContentLoadedEventStart.IsNull()) {
    222    return;
    223  }
    224 
    225  mLoadedURI = aURI;
    226  mDOMContentLoadedEventStart = TimeStamp::Now();
    227 
    228  PROFILER_MARKER("DOMContentLoaded", NETWORK,
    229                  MarkerOptions(MarkerTiming::IntervalStart(),
    230                                MarkerInnerWindowIdFromDocShell(mDocShell)),
    231                  Tracing, "Navigation");
    232 
    233  if (IsTopLevelContentDocumentInContentProcess()) {
    234    TimeStamp now = TimeStamp::Now();
    235 
    236    glean::performance_time::dom_content_loaded_start.AccumulateRawDuration(
    237        now - mNavigationStart);
    238  }
    239 }
    240 
    241 void nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI) {
    242  if (!mDOMContentLoadedEventEnd.IsNull()) {
    243    return;
    244  }
    245 
    246  mLoadedURI = aURI;
    247  mDOMContentLoadedEventEnd = TimeStamp::Now();
    248 
    249  PROFILER_MARKER("DOMContentLoaded", NETWORK,
    250                  MarkerOptions(MarkerTiming::IntervalEnd(),
    251                                MarkerInnerWindowIdFromDocShell(mDocShell)),
    252                  Tracing, "Navigation");
    253 
    254  if (IsTopLevelContentDocumentInContentProcess()) {
    255    glean::performance_time::dom_content_loaded_end.AccumulateRawDuration(
    256        TimeStamp::Now() - mNavigationStart);
    257  }
    258 }
    259 
    260 // static
    261 void nsDOMNavigationTiming::TTITimeoutCallback(nsITimer* aTimer,
    262                                               void* aClosure) {
    263  nsDOMNavigationTiming* self = static_cast<nsDOMNavigationTiming*>(aClosure);
    264  self->TTITimeout(aTimer);
    265 }
    266 
    267 #define TTI_WINDOW_SIZE_MS (5 * 1000)
    268 
    269 void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) {
    270  // Check TTI: see if it's been 5 seconds since the last Long Task
    271  TimeStamp now = TimeStamp::Now();
    272  MOZ_RELEASE_ASSERT(!mContentfulComposite.IsNull(),
    273                     "TTI timeout with no contentful-composite?");
    274 
    275  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
    276  TimeStamp lastLongTaskEnded;
    277  mainThread->GetLastLongNonIdleTaskEnd(&lastLongTaskEnded);
    278  // Window starts at mContentfulComposite; any long task before that is ignored
    279  if (lastLongTaskEnded.IsNull() || lastLongTaskEnded < mContentfulComposite) {
    280    PAGELOAD_LOG(
    281        ("no longtask (last was %g ms before ContentfulComposite)",
    282         lastLongTaskEnded.IsNull()
    283             ? 0
    284             : (mContentfulComposite - lastLongTaskEnded).ToMilliseconds()));
    285    lastLongTaskEnded = mContentfulComposite;
    286  }
    287  TimeDuration delta = now - lastLongTaskEnded;
    288  PAGELOAD_LOG(("TTI delta: %g ms", delta.ToMilliseconds()));
    289  if (delta.ToMilliseconds() < TTI_WINDOW_SIZE_MS) {
    290    // Less than 5 seconds since the last long task or start of the window.
    291    // Schedule another check.
    292    PAGELOAD_LOG(("TTI: waiting additional %g ms",
    293                  (TTI_WINDOW_SIZE_MS + 100) - delta.ToMilliseconds()));
    294    aTimer->InitWithNamedFuncCallback(
    295        TTITimeoutCallback, this,
    296        (TTI_WINDOW_SIZE_MS + 100) -
    297            delta.ToMilliseconds(),  // slightly after the window ends
    298        nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
    299        "nsDOMNavigationTiming::TTITimeout"_ns);
    300    return;
    301  }
    302 
    303  // To correctly implement TTI/TTFI as proposed, we'd need to not
    304  // fire it until there are no more than 2 network loads.  By the
    305  // proposed definition, without that we're closer to
    306  // TimeToFirstInteractive.  There are also arguments about what sort
    307  // of loads should qualify.
    308 
    309  // XXX check number of network loads, and if > 2 mark to check if loads
    310  // decreases to 2 (or record that point and let the normal timer here
    311  // handle it)
    312 
    313  // TTI has occurred!  TTI is either FCP (if there are no longtasks and no
    314  // DCLEnd in the window that starts at FCP), or at the end of the last
    315  // Long Task or DOMContentLoadedEnd (whichever is later). lastLongTaskEnded
    316  // is >= FCP here.
    317 
    318  if (mTTFI.IsNull()) {
    319    // lastLongTaskEnded is >= mContentfulComposite
    320    mTTFI = (mDOMContentLoadedEventEnd.IsNull() ||
    321             lastLongTaskEnded > mDOMContentLoadedEventEnd)
    322                ? lastLongTaskEnded
    323                : mDOMContentLoadedEventEnd;
    324    PAGELOAD_LOG(
    325        ("TTFI after %dms (LongTask was at %dms, DCL was %dms)",
    326         int((mTTFI - mNavigationStart).ToMilliseconds()),
    327         lastLongTaskEnded.IsNull()
    328             ? 0
    329             : int((lastLongTaskEnded - mNavigationStart).ToMilliseconds()),
    330         mDOMContentLoadedEventEnd.IsNull()
    331             ? 0
    332             : int((mDOMContentLoadedEventEnd - mNavigationStart)
    333                       .ToMilliseconds())));
    334  }
    335  // XXX Implement TTI via check number of network loads, and if > 2 mark
    336  // to check if loads decreases to 2 (or record that point and let the
    337  // normal timer here handle it)
    338 
    339  mTTITimer = nullptr;
    340 
    341  if (profiler_thread_is_being_profiled_for_markers() ||
    342      PAGELOAD_LOG_ENABLED()) {
    343    TimeDuration elapsed = mTTFI - mNavigationStart;
    344    MOZ_ASSERT(elapsed.ToMilliseconds() > 0);
    345    TimeDuration elapsedLongTask =
    346        lastLongTaskEnded.IsNull() ? 0 : lastLongTaskEnded - mNavigationStart;
    347    nsPrintfCString marker(
    348        "TTFI after %dms (LongTask was at %dms) for URL %s",
    349        int(elapsed.ToMilliseconds()), int(elapsedLongTask.ToMilliseconds()),
    350        nsContentUtils::TruncatedURLForDisplay(mLoadedURI).get());
    351 
    352    PROFILER_MARKER_TEXT(
    353        "TimeToFirstInteractive (TTFI)", DOM,
    354        MarkerOptions(MarkerTiming::Interval(mNavigationStart, mTTFI),
    355                      MarkerInnerWindowIdFromDocShell(mDocShell)),
    356        marker);
    357  }
    358 }
    359 
    360 void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() {
    361  MOZ_ASSERT(NS_IsMainThread());
    362  MOZ_ASSERT(!mNavigationStart.IsNull());
    363 
    364  if (!mNonBlankPaint.IsNull()) {
    365    return;
    366  }
    367 
    368  mNonBlankPaint = TimeStamp::Now();
    369 
    370  if (profiler_thread_is_being_profiled_for_markers() ||
    371      PAGELOAD_LOG_ENABLED()) {
    372    TimeDuration elapsed = mNonBlankPaint - mNavigationStart;
    373    nsPrintfCString marker(
    374        "Non-blank paint after %dms for URL %s, %s",
    375        int(elapsed.ToMilliseconds()),
    376        nsContentUtils::TruncatedURLForDisplay(mLoadedURI).get(),
    377        mDocShellHasBeenActiveSinceNavigationStart
    378            ? "foreground tab"
    379            : "this tab was inactive some of the time between navigation start "
    380              "and first non-blank paint");
    381    PAGELOAD_LOG(("%s", marker.get()));
    382    PROFILER_MARKER_TEXT(
    383        "FirstNonBlankPaint", DOM,
    384        MarkerOptions(MarkerTiming::Interval(mNavigationStart, mNonBlankPaint),
    385                      MarkerInnerWindowIdFromDocShell(mDocShell)),
    386        marker);
    387  }
    388 
    389  if (mDocShellHasBeenActiveSinceNavigationStart) {
    390    glean::performance_page::non_blank_paint.AccumulateRawDuration(
    391        mNonBlankPaint - mNavigationStart);
    392  }
    393 }
    394 
    395 void nsDOMNavigationTiming::NotifyContentfulCompositeForRootContentDocument(
    396    const mozilla::TimeStamp& aCompositeEndTime) {
    397  MOZ_ASSERT(NS_IsMainThread());
    398  MOZ_ASSERT(!mNavigationStart.IsNull());
    399 
    400  if (!mContentfulComposite.IsNull()) {
    401    return;
    402  }
    403 
    404  mContentfulComposite = aCompositeEndTime;
    405 
    406  if (profiler_thread_is_being_profiled_for_markers() ||
    407      PAGELOAD_LOG_ENABLED()) {
    408    TimeDuration elapsed = mContentfulComposite - mNavigationStart;
    409    nsPrintfCString marker(
    410        "Contentful composite after %dms for URL %s, %s",
    411        int(elapsed.ToMilliseconds()),
    412        nsContentUtils::TruncatedURLForDisplay(mLoadedURI).get(),
    413        mDocShellHasBeenActiveSinceNavigationStart
    414            ? "foreground tab"
    415            : "this tab was inactive some of the time between navigation start "
    416              "and first non-blank paint");
    417    PAGELOAD_LOG(("%s", marker.get()));
    418    PROFILER_MARKER_TEXT(
    419        "FirstContentfulComposite", DOM,
    420        MarkerOptions(
    421            MarkerTiming::Interval(mNavigationStart, mContentfulComposite),
    422            MarkerInnerWindowIdFromDocShell(mDocShell)),
    423        marker);
    424  }
    425 
    426  if (!mTTITimer) {
    427    mTTITimer = NS_NewTimer();
    428  }
    429 
    430  // TTI is first checked 5 seconds after the FCP (non-blank-paint is very close
    431  // to FCP).
    432  mTTITimer->InitWithNamedFuncCallback(TTITimeoutCallback, this,
    433                                       TTI_WINDOW_SIZE_MS,
    434                                       nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
    435                                       "nsDOMNavigationTiming::TTITimeout"_ns);
    436 
    437  if (mDocShellHasBeenActiveSinceNavigationStart) {
    438    glean::performance_time::to_first_contentful_paint.AccumulateRawDuration(
    439        mContentfulComposite - mNavigationStart);
    440  }
    441 }
    442 
    443 void nsDOMNavigationTiming::NotifyLargestContentfulRenderForRootContentDocument(
    444    const DOMHighResTimeStamp& aRenderTime) {
    445  MOZ_ASSERT(NS_IsMainThread());
    446  MOZ_ASSERT(!mNavigationStart.IsNull());
    447 
    448  // This can get called multiple times and updates over time.
    449  mLargestContentfulRender =
    450      mNavigationStart + TimeDuration::FromMilliseconds(aRenderTime);
    451 }
    452 
    453 void nsDOMNavigationTiming::NotifyDocShellStateChanged(
    454    DocShellState aDocShellState) {
    455  mDocShellHasBeenActiveSinceNavigationStart &=
    456      (aDocShellState == DocShellState::eActive);
    457 }
    458 
    459 void nsDOMNavigationTiming::MaybeAddLCPProfilerMarker(
    460    MarkerInnerWindowId aInnerWindowID) {
    461  // This method might get called from outside of the main thread, so can't
    462  // check `profiler_thread_is_being_profiled_for_markers()` here.
    463  if (!profiler_is_active_and_unpaused()) {
    464    return;
    465  }
    466 
    467  TimeStamp navStartTime = GetNavigationStartTimeStamp();
    468  TimeStamp lcpTime = GetLargestContentfulRenderTimeStamp();
    469 
    470  if (!navStartTime || !lcpTime) {
    471    return;
    472  }
    473 
    474  TimeDuration elapsed = lcpTime - navStartTime;
    475  nsPrintfCString marker("Largest contentful paint after %dms",
    476                         int(elapsed.ToMilliseconds()));
    477  PROFILER_MARKER_TEXT(
    478      "LargestContentfulPaint", DOM,
    479      // Putting this marker to the main thread even if it's called from another
    480      // one.
    481      MarkerOptions(MarkerThreadId::MainThread(),
    482                    MarkerTiming::Interval(navStartTime, lcpTime),
    483                    std::move(aInnerWindowID)),
    484      marker);
    485 }
    486 
    487 mozilla::TimeStamp nsDOMNavigationTiming::GetUnloadEventStartTimeStamp() const {
    488  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
    489  // todo: if you intend to update CheckSameOriginURI to log the error to the
    490  // console you also need to update the 'aFromPrivateWindow' argument.
    491  nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false, false);
    492  if (NS_SUCCEEDED(rv)) {
    493    return mUnloadStart;
    494  }
    495  return mozilla::TimeStamp();
    496 }
    497 
    498 mozilla::TimeStamp nsDOMNavigationTiming::GetUnloadEventEndTimeStamp() const {
    499  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
    500  // todo: if you intend to update CheckSameOriginURI to log the error to the
    501  // console you also need to update the 'aFromPrivateWindow' argument.
    502  nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false, false);
    503  if (NS_SUCCEEDED(rv)) {
    504    return mUnloadEnd;
    505  }
    506  return mozilla::TimeStamp();
    507 }
    508 
    509 bool nsDOMNavigationTiming::IsTopLevelContentDocumentInContentProcess() const {
    510  if (!mDocShell) {
    511    return false;
    512  }
    513  if (!XRE_IsContentProcess()) {
    514    return false;
    515  }
    516  return mDocShell->GetBrowsingContext()->IsTopContent();
    517 }
    518 
    519 nsDOMNavigationTiming::nsDOMNavigationTiming(nsDocShell* aDocShell,
    520                                             nsDOMNavigationTiming* aOther)
    521    : mDocShell(aDocShell),
    522      mUnloadedURI(aOther->mUnloadedURI),
    523      mLoadedURI(aOther->mLoadedURI),
    524      mNavigationType(aOther->mNavigationType),
    525      mNavigationStartHighRes(aOther->mNavigationStartHighRes),
    526      mNavigationStart(aOther->mNavigationStart),
    527      mNonBlankPaint(aOther->mNonBlankPaint),
    528      mContentfulComposite(aOther->mContentfulComposite),
    529      mBeforeUnloadStart(aOther->mBeforeUnloadStart),
    530      mUnloadStart(aOther->mUnloadStart),
    531      mUnloadEnd(aOther->mUnloadEnd),
    532      mLoadEventStart(aOther->mLoadEventStart),
    533      mLoadEventEnd(aOther->mLoadEventEnd),
    534      mDOMLoading(aOther->mDOMLoading),
    535      mDOMInteractive(aOther->mDOMInteractive),
    536      mDOMContentLoadedEventStart(aOther->mDOMContentLoadedEventStart),
    537      mDOMContentLoadedEventEnd(aOther->mDOMContentLoadedEventEnd),
    538      mDOMComplete(aOther->mDOMComplete),
    539      mTTFI(aOther->mTTFI),
    540      mDocShellHasBeenActiveSinceNavigationStart(
    541          aOther->mDocShellHasBeenActiveSinceNavigationStart) {}
    542 
    543 /* static */
    544 void IPC::ParamTraits<nsDOMNavigationTiming*>::Write(
    545    MessageWriter* aWriter, nsDOMNavigationTiming* aParam) {
    546  bool isNull = !aParam;
    547  WriteParam(aWriter, isNull);
    548  if (isNull) {
    549    return;
    550  }
    551 
    552  RefPtr<nsIURI> unloadedURI = aParam->mUnloadedURI.get();
    553  RefPtr<nsIURI> loadedURI = aParam->mLoadedURI.get();
    554  WriteParam(aWriter, unloadedURI ? Some(unloadedURI) : Nothing());
    555  WriteParam(aWriter, loadedURI ? Some(loadedURI) : Nothing());
    556  WriteParam(aWriter, uint32_t(aParam->mNavigationType));
    557  WriteParam(aWriter, aParam->mNavigationStartHighRes);
    558  WriteParam(aWriter, aParam->mNavigationStart);
    559  WriteParam(aWriter, aParam->mNonBlankPaint);
    560  WriteParam(aWriter, aParam->mContentfulComposite);
    561  WriteParam(aWriter, aParam->mBeforeUnloadStart);
    562  WriteParam(aWriter, aParam->mUnloadStart);
    563  WriteParam(aWriter, aParam->mUnloadEnd);
    564  WriteParam(aWriter, aParam->mLoadEventStart);
    565  WriteParam(aWriter, aParam->mLoadEventEnd);
    566  WriteParam(aWriter, aParam->mDOMLoading);
    567  WriteParam(aWriter, aParam->mDOMInteractive);
    568  WriteParam(aWriter, aParam->mDOMContentLoadedEventStart);
    569  WriteParam(aWriter, aParam->mDOMContentLoadedEventEnd);
    570  WriteParam(aWriter, aParam->mDOMComplete);
    571  WriteParam(aWriter, aParam->mTTFI);
    572  WriteParam(aWriter, aParam->mDocShellHasBeenActiveSinceNavigationStart);
    573 }
    574 
    575 /* static */
    576 bool IPC::ParamTraits<nsDOMNavigationTiming*>::Read(
    577    IPC::MessageReader* aReader, RefPtr<nsDOMNavigationTiming>* aResult) {
    578  bool isNull;
    579  if (!ReadParam(aReader, &isNull)) {
    580    return false;
    581  }
    582  if (isNull) {
    583    *aResult = nullptr;
    584    return true;
    585  }
    586 
    587  auto timing = MakeRefPtr<nsDOMNavigationTiming>(nullptr);
    588  uint32_t type;
    589  Maybe<RefPtr<nsIURI>> unloadedURI;
    590  Maybe<RefPtr<nsIURI>> loadedURI;
    591  if (!ReadParam(aReader, &unloadedURI) || !ReadParam(aReader, &loadedURI) ||
    592      !ReadParam(aReader, &type) ||
    593      !ReadParam(aReader, &timing->mNavigationStartHighRes) ||
    594      !ReadParam(aReader, &timing->mNavigationStart) ||
    595      !ReadParam(aReader, &timing->mNonBlankPaint) ||
    596      !ReadParam(aReader, &timing->mContentfulComposite) ||
    597      !ReadParam(aReader, &timing->mBeforeUnloadStart) ||
    598      !ReadParam(aReader, &timing->mUnloadStart) ||
    599      !ReadParam(aReader, &timing->mUnloadEnd) ||
    600      !ReadParam(aReader, &timing->mLoadEventStart) ||
    601      !ReadParam(aReader, &timing->mLoadEventEnd) ||
    602      !ReadParam(aReader, &timing->mDOMLoading) ||
    603      !ReadParam(aReader, &timing->mDOMInteractive) ||
    604      !ReadParam(aReader, &timing->mDOMContentLoadedEventStart) ||
    605      !ReadParam(aReader, &timing->mDOMContentLoadedEventEnd) ||
    606      !ReadParam(aReader, &timing->mDOMComplete) ||
    607      !ReadParam(aReader, &timing->mTTFI) ||
    608      !ReadParam(aReader,
    609                 &timing->mDocShellHasBeenActiveSinceNavigationStart)) {
    610    return false;
    611  }
    612  timing->mNavigationType = nsDOMNavigationTiming::Type(type);
    613  if (unloadedURI) {
    614    timing->mUnloadedURI = std::move(*unloadedURI);
    615  }
    616  if (loadedURI) {
    617    timing->mLoadedURI = std::move(*loadedURI);
    618  }
    619  *aResult = std::move(timing);
    620  return true;
    621 }