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 }