From 398486a64bdd0c3ae7df24719dd434d13e58895a Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 20 Oct 2023 04:21:49 -0500 Subject: Issue #2282 - Align the Performance Observer navigation and resource with the spec. This now passes the conformance tests. https://bugzilla.mozilla.org/show_bug.cgi?id=1425458 Resource timing entries Workers - part 2 - PerformanceTimingData. https://bugzilla.mozilla.org/show_bug.cgi?id=1462605 PerformanceNavigationTiming.name must be the value of the address of the current document. https://bugzilla.mozilla.org/show_bug.cgi?id=1462883 Update PerformanceTimingData::mReportCrossOriginRedirect in SetPropertiesFromHttpChannel. https://bugzilla.mozilla.org/show_bug.cgi?id=1462879 PerformanceNavigationTiming must be notified correctly - part 1 - notify. --- dom/base/nsGlobalWindow.cpp | 9 + dom/base/nsPIDOMWindow.h | 2 + dom/performance/Performance.h | 2 + dom/performance/PerformanceMainThread.cpp | 70 ++++-- dom/performance/PerformanceMainThread.h | 5 +- dom/performance/PerformanceNavigation.cpp | 2 +- dom/performance/PerformanceNavigationTiming.cpp | 20 +- dom/performance/PerformanceNavigationTiming.h | 16 +- dom/performance/PerformanceResourceTiming.cpp | 49 +--- dom/performance/PerformanceResourceTiming.h | 99 +++----- dom/performance/PerformanceTiming.cpp | 255 +++++++++++++-------- dom/performance/PerformanceTiming.h | 288 +++++++++++++++--------- dom/performance/PerformanceWorker.h | 5 + layout/base/nsDocumentViewer.cpp | 5 + 14 files changed, 484 insertions(+), 343 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index ae84de3a19..a69b5e177a 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -4222,6 +4222,15 @@ nsGlobalWindow::GetPerformance() return AsInner()->GetPerformance(); } +void +nsPIDOMWindowInner::QueuePerformanceNavigationTiming() +{ + CreatePerformanceObjectIfNeeded(); + if (mPerformance) { + mPerformance->QueueNavigationTimingEntry(); + } +} + void nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded() { diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index ffebf6570d..110da38604 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -727,6 +727,8 @@ public: mozilla::dom::Performance* GetPerformance(); + void QueuePerformanceNavigationTiming(); + bool HasMutationListeners(uint32_t aMutationEventType) const { if (!mOuterWindow) { diff --git a/dom/performance/Performance.h b/dom/performance/Performance.h index 7a60eb85a9..2e1b63c45b 100644 --- a/dom/performance/Performance.h +++ b/dom/performance/Performance.h @@ -124,6 +124,8 @@ public: return false; } + virtual void QueueNavigationTimingEntry() = 0; + protected: Performance(); explicit Performance(nsPIDOMWindowInner* aWindow); diff --git a/dom/performance/PerformanceMainThread.cpp b/dom/performance/PerformanceMainThread.cpp index a4dbf88799..b5ca6ca1be 100644 --- a/dom/performance/PerformanceMainThread.cpp +++ b/dom/performance/PerformanceMainThread.cpp @@ -15,6 +15,34 @@ namespace mozilla { namespace dom { +namespace { + +void +GetURLSpecFromChannel(nsITimedChannel* aChannel, nsAString& aSpec) +{ + aSpec.AssignLiteral("document"); + + nsCOMPtr channel = do_QueryInterface(aChannel); + if (!channel) { + return; + } + + nsCOMPtr uri; + nsresult rv = channel->GetURI(getter_AddRefs(uri)); + if (NS_WARN_IF(NS_FAILED(rv)) || !uri) { + return; + } + + nsAutoCString spec; + rv = uri->GetSpec(spec); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + aSpec = NS_ConvertUTF8toUTF16(spec); +} + +} // anonymous NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread) @@ -60,6 +88,7 @@ PerformanceMainThread::PerformanceMainThread(nsPIDOMWindowInner* aWindow, , mChannel(aChannel) { MOZ_ASSERT(aWindow, "Parent window object should be provided"); + CreateNavigationTimingEntry(); } PerformanceMainThread::~PerformanceMainThread() @@ -159,14 +188,14 @@ PerformanceMainThread::AddEntry(nsIHttpChannel* channel, // The last argument is the "zero time" (offset). Since we don't want // any offset for the resource timing, this will be set to "0" - the // resource timing returns a relative timing (no offset). - RefPtr performanceTiming = - new PerformanceTiming(this, timedChannel, channel, - 0); + UniquePtr performanceTimingData( + new PerformanceTimingData(timedChannel, channel, 0)); // The PerformanceResourceTiming object will use the PerformanceTiming // object to get all the required timings. RefPtr performanceEntry = - new PerformanceResourceTiming(performanceTiming, this, entryName, channel); + new PerformanceResourceTiming(std::move(performanceTimingData), this, entryName); + // If the initiator type had no valid value, then set it to the default // ("other") value. if (initiatorType.IsEmpty()) { @@ -299,17 +328,31 @@ PerformanceMainThread::CreationTime() const } void -PerformanceMainThread::EnsureDocEntry() +PerformanceMainThread::CreateNavigationTimingEntry() { - if (!mDocEntry && nsContentUtils::IsPerformanceNavigationTimingEnabled()) { - nsCOMPtr httpChannel = do_QueryInterface(mChannel); - RefPtr timing = - new PerformanceTiming(this, mChannel, nullptr, 0); - mDocEntry = new PerformanceNavigationTiming(timing, this, - httpChannel); + MOZ_ASSERT(!mDocEntry, "mDocEntry should be null."); + + nsAutoString name; + GetURLSpecFromChannel(mChannel, name); + + UniquePtr timing( + new PerformanceTimingData(mChannel, nullptr, 0)); + + nsCOMPtr httpChannel = do_QueryInterface(mChannel); + if (httpChannel) { + timing->SetPropertiesFromHttpChannel(httpChannel, mChannel); } + + mDocEntry = new PerformanceNavigationTiming(std::move(timing), this, name); } +void +PerformanceMainThread::QueueNavigationTimingEntry() +{ + if (mDocEntry) { + QueueEntry(mDocEntry); + } +} void PerformanceMainThread::GetEntries(nsTArray>& aRetval) @@ -317,7 +360,6 @@ PerformanceMainThread::GetEntries(nsTArray>& aRetval) aRetval = mResourceEntries; aRetval.AppendElements(mUserEntries); - EnsureDocEntry(); if (mDocEntry) { aRetval.AppendElement(mDocEntry); } @@ -331,7 +373,7 @@ PerformanceMainThread::GetEntriesByType(const nsAString& aEntryType, { if (aEntryType.EqualsLiteral("navigation")) { aRetval.Clear(); - EnsureDocEntry(); + if (mDocEntry) { aRetval.AppendElement(mDocEntry); } @@ -348,7 +390,7 @@ PerformanceMainThread::GetEntriesByName(const nsAString& aName, { if (aName.EqualsLiteral("document")) { aRetval.Clear(); - EnsureDocEntry(); + if (mDocEntry) { aRetval.AppendElement(mDocEntry); } diff --git a/dom/performance/PerformanceMainThread.h b/dom/performance/PerformanceMainThread.h index 702483e9de..fc81c94e2a 100644 --- a/dom/performance/PerformanceMainThread.h +++ b/dom/performance/PerformanceMainThread.h @@ -62,9 +62,13 @@ public: return true; } + void QueueNavigationTimingEntry() override; + protected: ~PerformanceMainThread(); + void CreateNavigationTimingEntry(); + nsISupports* GetAsISupports() override { return this; @@ -76,7 +80,6 @@ protected: GetPerformanceTimingFromString(const nsAString& aTimingName) override; void DispatchBufferFullEvent() override; - void EnsureDocEntry(); RefPtr mDocEntry; RefPtr mDOMTiming; diff --git a/dom/performance/PerformanceNavigation.cpp b/dom/performance/PerformanceNavigation.cpp index 24b3af11f8..1b96de1cf4 100644 --- a/dom/performance/PerformanceNavigation.cpp +++ b/dom/performance/PerformanceNavigation.cpp @@ -35,7 +35,7 @@ PerformanceNavigation::WrapObject(JSContext *cx, uint16_t PerformanceNavigation::RedirectCount() const { - return GetPerformanceTiming()->GetRedirectCount(); + return GetPerformanceTiming()->Data()->GetRedirectCount(); } } // dom namespace diff --git a/dom/performance/PerformanceNavigationTiming.cpp b/dom/performance/PerformanceNavigationTiming.cpp index 37d54366c6..1c4a30b569 100644 --- a/dom/performance/PerformanceNavigationTiming.cpp +++ b/dom/performance/PerformanceNavigationTiming.cpp @@ -24,55 +24,55 @@ PerformanceNavigationTiming::WrapObject(JSContext* aCx, JS::Handle aG DOMHighResTimeStamp PerformanceNavigationTiming::UnloadEventStart() const { - return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetUnloadEventStartHighRes()); + return mPerformance->GetDOMTiming()->GetUnloadEventStartHighRes(); } DOMHighResTimeStamp PerformanceNavigationTiming::UnloadEventEnd() const { - return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetUnloadEventEndHighRes()); + return mPerformance->GetDOMTiming()->GetUnloadEventEndHighRes(); } DOMHighResTimeStamp PerformanceNavigationTiming::DomInteractive() const { - return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetDomInteractiveHighRes()); + return mPerformance->GetDOMTiming()->GetDomInteractiveHighRes(); } DOMHighResTimeStamp PerformanceNavigationTiming::DomContentLoadedEventStart() const { - return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetDomContentLoadedEventStartHighRes()); + return mPerformance->GetDOMTiming()->GetDomContentLoadedEventStartHighRes(); } DOMHighResTimeStamp PerformanceNavigationTiming::DomContentLoadedEventEnd() const { - return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetDomContentLoadedEventEndHighRes()); + return mPerformance->GetDOMTiming()->GetDomContentLoadedEventEndHighRes(); } DOMHighResTimeStamp PerformanceNavigationTiming::DomComplete() const { - return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetDomCompleteHighRes()); + return mPerformance->GetDOMTiming()->GetDomCompleteHighRes(); } DOMHighResTimeStamp PerformanceNavigationTiming::LoadEventStart() const { - return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetLoadEventStartHighRes()); + return mPerformance->GetDOMTiming()->GetLoadEventStartHighRes(); } DOMHighResTimeStamp PerformanceNavigationTiming::LoadEventEnd() const { - return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetLoadEventEndHighRes()); + return mPerformance->GetDOMTiming()->GetLoadEventEndHighRes(); } NavigationType PerformanceNavigationTiming::Type() const { - switch(mTiming->GetDOMTiming()->GetType()) { + switch(mPerformance->GetDOMTiming()->GetType()) { case nsDOMNavigationTiming::TYPE_NAVIGATE: return NavigationType::Navigate; break; @@ -92,5 +92,5 @@ PerformanceNavigationTiming::Type() const uint16_t PerformanceNavigationTiming::RedirectCount() const { - return mTiming->GetRedirectCount(); + return mTimingData->GetRedirectCount(); } diff --git a/dom/performance/PerformanceNavigationTiming.h b/dom/performance/PerformanceNavigationTiming.h index f528c5337e..93471e17de 100644 --- a/dom/performance/PerformanceNavigationTiming.h +++ b/dom/performance/PerformanceNavigationTiming.h @@ -27,14 +27,14 @@ public: // so that timestamps are relative to startTime, as opposed to the // performance.timing object for which timestamps are absolute and has a // zeroTime initialized to navigationStart - explicit PerformanceNavigationTiming(PerformanceTiming* aPerformanceTiming, - Performance* aPerformance, - nsIHttpChannel* aChannel) - : PerformanceResourceTiming(aPerformanceTiming, aPerformance, - NS_LITERAL_STRING("document"), aChannel) { - SetEntryType(NS_LITERAL_STRING("navigation")); - SetInitiatorType(NS_LITERAL_STRING("navigation")); - } + PerformanceNavigationTiming(UniquePtr&& aPerformanceTiming, + Performance* aPerformance, + const nsAString& aName) + : PerformanceResourceTiming(Move(aPerformanceTiming), aPerformance, aName) + { + SetEntryType(NS_LITERAL_STRING("navigation")); + SetInitiatorType(NS_LITERAL_STRING("navigation")); + } DOMHighResTimeStamp Duration() const override { diff --git a/dom/performance/PerformanceResourceTiming.cpp b/dom/performance/PerformanceResourceTiming.cpp index 6f84896af1..cb08b39b65 100644 --- a/dom/performance/PerformanceResourceTiming.cpp +++ b/dom/performance/PerformanceResourceTiming.cpp @@ -11,7 +11,7 @@ using namespace mozilla::dom; NS_IMPL_CYCLE_COLLECTION_INHERITED(PerformanceResourceTiming, PerformanceEntry, - mTiming) + mPerformance) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceResourceTiming, PerformanceEntry) @@ -23,45 +23,14 @@ NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry) NS_IMPL_ADDREF_INHERITED(PerformanceResourceTiming, PerformanceEntry) NS_IMPL_RELEASE_INHERITED(PerformanceResourceTiming, PerformanceEntry) -PerformanceResourceTiming::PerformanceResourceTiming(PerformanceTiming* aPerformanceTiming, +PerformanceResourceTiming::PerformanceResourceTiming(UniquePtr&& aPerformanceTiming, Performance* aPerformance, - const nsAString& aName, - nsIHttpChannel* aChannel) -: PerformanceEntry(aPerformance, aName, NS_LITERAL_STRING("resource")), - mTiming(aPerformanceTiming), - mEncodedBodySize(0), - mTransferSize(0), - mDecodedBodySize(0) + const nsAString& aName) + : PerformanceEntry(aPerformance->GetParentObject(), aName, NS_LITERAL_STRING("resource")) + , mTimingData(Move(aPerformanceTiming)) + , mPerformance(aPerformance) { MOZ_ASSERT(aPerformance, "Parent performance object should be provided"); - SetPropertiesFromChannel(aChannel); -} - -void -PerformanceResourceTiming::SetPropertiesFromChannel(nsIHttpChannel* aChannel) -{ - if (!aChannel) { - return; - } - - nsAutoCString protocol; - Unused << aChannel->GetProtocolVersion(protocol); - SetNextHopProtocol(NS_ConvertUTF8toUTF16(protocol)); - - uint64_t encodedBodySize = 0; - Unused << aChannel->GetEncodedBodySize(&encodedBodySize); - SetEncodedBodySize(encodedBodySize); - - uint64_t transferSize = 0; - Unused << aChannel->GetTransferSize(&transferSize); - SetTransferSize(transferSize); - - uint64_t decodedBodySize = 0; - Unused << aChannel->GetDecodedBodySize(&decodedBodySize); - if (decodedBodySize == 0) { - decodedBodySize = encodedBodySize; - } - SetDecodedBodySize(decodedBodySize); } PerformanceResourceTiming::~PerformanceResourceTiming() @@ -78,13 +47,13 @@ PerformanceResourceTiming::StartTime() const // 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(); + DOMHighResTimeStamp redirect = mTimingData->RedirectStartHighRes(mPerformance); redirect = redirect ? redirect : DBL_MAX; - DOMHighResTimeStamp worker = mTiming->WorkerStartHighRes(); + DOMHighResTimeStamp worker = mTimingData->WorkerStartHighRes(mPerformance); worker = worker ? worker : DBL_MAX; - DOMHighResTimeStamp asyncOpen = mTiming->AsyncOpenHighRes(); + DOMHighResTimeStamp asyncOpen = mTimingData->AsyncOpenHighRes(mPerformance); return std::min(asyncOpen, std::min(redirect, worker)); } diff --git a/dom/performance/PerformanceResourceTiming.h b/dom/performance/PerformanceResourceTiming.h index 63a8c24149..fd085b120a 100644 --- a/dom/performance/PerformanceResourceTiming.h +++ b/dom/performance/PerformanceResourceTiming.h @@ -6,9 +6,8 @@ #ifndef mozilla_dom_PerformanceResourceTiming_h___ #define mozilla_dom_PerformanceResourceTiming_h___ +#include "mozilla/UniquePtr.h" #include "nsCOMPtr.h" -#include "nsIChannel.h" -#include "nsITimedChannel.h" #include "Performance.h" #include "PerformanceEntry.h" #include "PerformanceTiming.h" @@ -27,10 +26,9 @@ public: PerformanceResourceTiming, PerformanceEntry) - PerformanceResourceTiming(PerformanceTiming* aPerformanceTiming, + PerformanceResourceTiming(UniquePtr&& aPerformanceTimingData, Performance* aPerformance, - const nsAString& aName, - nsIHttpChannel* aChannel = nullptr); + const nsAString& aName); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; @@ -54,93 +52,85 @@ public: void GetNextHopProtocol(nsAString& aNextHopProtocol) const { - if (mTiming && mTiming->TimingAllowed()) { - aNextHopProtocol = mNextHopProtocol; + if (mTimingData) { + aNextHopProtocol = mTimingData->NextHopProtocol(); } } - void SetNextHopProtocol(const nsAString& aNextHopProtocol) - { - mNextHopProtocol = aNextHopProtocol; - } - DOMHighResTimeStamp WorkerStart() const { - return mTiming && mTiming->TimingAllowed() - ? mTiming->WorkerStartHighRes() + return mTimingData + ? mTimingData->WorkerStartHighRes(mPerformance) : 0; } DOMHighResTimeStamp FetchStart() const { - if (mTiming) { - return mTiming->TimingAllowed() - ? mTiming->FetchStartHighRes() - : StartTime(); - } - return 0; + return mTimingData + ? mTimingData->FetchStartHighRes(mPerformance) + : 0; } DOMHighResTimeStamp RedirectStart() const { // We have to check if all the redirect URIs had the same origin (since // there is no check in RedirectEndHighRes()) - return mTiming && mTiming->ShouldReportCrossOriginRedirect() - ? mTiming->RedirectStartHighRes() + return mTimingData && mTimingData->ShouldReportCrossOriginRedirect() + ? mTimingData->RedirectStartHighRes(mPerformance) : 0; } DOMHighResTimeStamp RedirectEnd() const { // We have to check if all the redirect URIs had the same origin (since // there is no check in RedirectEndHighRes()) - return mTiming && mTiming->ShouldReportCrossOriginRedirect() - ? mTiming->RedirectEndHighRes() + return mTimingData && mTimingData->ShouldReportCrossOriginRedirect() + ? mTimingData->RedirectEndHighRes(mPerformance) : 0; } DOMHighResTimeStamp DomainLookupStart() const { - return mTiming && mTiming->TimingAllowed() - ? mTiming->DomainLookupStartHighRes() + return mTimingData && mTimingData->TimingAllowed() + ? mTimingData->DomainLookupStartHighRes(mPerformance) : 0; } DOMHighResTimeStamp DomainLookupEnd() const { - return mTiming && mTiming->TimingAllowed() - ? mTiming->DomainLookupEndHighRes() + return mTimingData && mTimingData->TimingAllowed() + ? mTimingData->DomainLookupEndHighRes(mPerformance) : 0; } DOMHighResTimeStamp ConnectStart() const { - return mTiming && mTiming->TimingAllowed() - ? mTiming->ConnectStartHighRes() + return mTimingData && mTimingData->TimingAllowed() + ? mTimingData->ConnectStartHighRes(mPerformance) : 0; } DOMHighResTimeStamp ConnectEnd() const { - return mTiming && mTiming->TimingAllowed() - ? mTiming->ConnectEndHighRes() + return mTimingData && mTimingData->TimingAllowed() + ? mTimingData->ConnectEndHighRes(mPerformance) : 0; } DOMHighResTimeStamp RequestStart() const { - return mTiming && mTiming->TimingAllowed() - ? mTiming->RequestStartHighRes() + return mTimingData && mTimingData->TimingAllowed() + ? mTimingData->RequestStartHighRes(mPerformance) : 0; } DOMHighResTimeStamp ResponseStart() const { - return mTiming && mTiming->TimingAllowed() - ? mTiming->ResponseStartHighRes() + return mTimingData && mTimingData->TimingAllowed() + ? mTimingData->ResponseStartHighRes(mPerformance) : 0; } DOMHighResTimeStamp ResponseEnd() const { - return mTiming - ? mTiming->ResponseEndHighRes() + return mTimingData + ? mTimingData->ResponseEndHighRes(mPerformance) : 0; } DOMHighResTimeStamp SecureConnectionStart() const { - return mTiming && mTiming->TimingAllowed() - ? mTiming->SecureConnectionStartHighRes() + return mTimingData && mTimingData->TimingAllowed() + ? mTimingData->SecureConnectionStartHighRes(mPerformance) : 0; } @@ -151,44 +141,25 @@ public: uint64_t TransferSize() const { - return mTiming && mTiming->TimingAllowed() ? mTransferSize : 0; + return mTimingData ? mTimingData->TransferSize() : 0; } uint64_t EncodedBodySize() const { - return mTiming && mTiming->TimingAllowed() ? mEncodedBodySize : 0; + return mTimingData ? mTimingData->EncodedBodySize() : 0; } uint64_t DecodedBodySize() const { - return mTiming && mTiming->TimingAllowed() ? mDecodedBodySize : 0; - } - - void SetEncodedBodySize(uint64_t aEncodedBodySize) - { - mEncodedBodySize = aEncodedBodySize; - } - - void SetTransferSize(uint64_t aTransferSize) - { - mTransferSize = aTransferSize; - } - - void SetDecodedBodySize(uint64_t aDecodedBodySize) - { - mDecodedBodySize = aDecodedBodySize; + return mTimingData ? mTimingData->DecodedBodySize() : 0; } protected: virtual ~PerformanceResourceTiming(); - void SetPropertiesFromChannel(nsIHttpChannel* aChannel); nsString mInitiatorType; - nsString mNextHopProtocol; - RefPtr mTiming; - uint64_t mEncodedBodySize; - uint64_t mTransferSize; - uint64_t mDecodedBodySize; + UniquePtr mTimingData; + RefPtr mPerformance; }; } // namespace dom diff --git a/dom/performance/PerformanceTiming.cpp b/dom/performance/PerformanceTiming.cpp index 5c33457d0c..7e10a7edda 100755 --- a/dom/performance/PerformanceTiming.cpp +++ b/dom/performance/PerformanceTiming.cpp @@ -19,34 +19,39 @@ PerformanceTiming::PerformanceTiming(Performance* aPerformance, nsITimedChannel* aChannel, nsIHttpChannel* aHttpChannel, DOMHighResTimeStamp aZeroTime) - : mPerformance(aPerformance), - mFetchStart(0.0), - mZeroTime(TimerClamping::ReduceMsTimeValue(aZeroTime)), - mRedirectCount(0), - mTimingAllowed(true), - mAllRedirectsSameOrigin(true), - mInitialized(!!aChannel), - mReportCrossOriginRedirect(true) + : mPerformance(aPerformance) { MOZ_ASSERT(aPerformance, "Parent performance object should be provided"); + mTimingData.reset(new PerformanceTimingData(aChannel, aHttpChannel, + aZeroTime)); +} + +// Copy the timing info from the channel so we don't need to keep the channel +// alive just to get the timestamps. +PerformanceTimingData::PerformanceTimingData(nsITimedChannel* aChannel, + nsIHttpChannel* aHttpChannel, + DOMHighResTimeStamp aZeroTime) + : mZeroTime(0.0) + , mFetchStart(0.0) + , mEncodedBodySize(0) + , mTransferSize(0) + , mDecodedBodySize(0) + , mRedirectCount(0) + , mAllRedirectsSameOrigin(true) + , mReportCrossOriginRedirect(true) + , mSecureConnection(false) + , mTimingAllowed(true) + , mInitialized(false) +{ + mInitialized = !!aChannel; + + mZeroTime = TimerClamping::ReduceMsTimeValue(aZeroTime); + if (!nsContentUtils::IsPerformanceTimingEnabled()) { mZeroTime = 0; } - // The aHttpChannel argument is null if this PerformanceTiming object is - // being used for navigation timing (which is only relevant for documents). - // It has a non-null value if this PerformanceTiming object is being used - // for resource timing, which can include document loads, both toplevel and - // in subframes, and resources linked from a document. - if (aHttpChannel) { - mTimingAllowed = CheckAllowedOrigin(aHttpChannel, aChannel); - bool redirectsPassCheck = false; - aChannel->GetAllRedirectsPassTimingAllowCheck(&redirectsPassCheck); - mReportCrossOriginRedirect = mTimingAllowed && redirectsPassCheck; - } - - mSecureConnection = false; nsCOMPtr uri; if (aHttpChannel) { aHttpChannel->GetURI(getter_AddRefs(uri)); @@ -63,14 +68,7 @@ PerformanceTiming::PerformanceTiming(Performance* aPerformance, mSecureConnection = false; } } - InitializeTimingInfo(aChannel); -} -// Copy the timing info from the channel so we don't need to keep the channel -// alive just to get the timestamps. -void -PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel) -{ if (aChannel) { aChannel->GetAsyncOpen(&mAsyncOpen); aChannel->GetDispatchFetchEventStart(&mWorkerStart); @@ -90,11 +88,11 @@ PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel) aChannel->GetCacheReadEnd(&mCacheReadEnd); // The performance timing api essentially requires that the event timestamps - // have a strict relation with each other. The truth, however, is the browser - // engages in a number of speculative activities that sometimes mean connections - // and lookups begin at different times. Workaround that here by clamping - // these values to what we expect FetchStart to be. This means the later of - // AsyncOpen or WorkerStart times. + // have a strict relation with each other. The truth, however, is the + // browser engages in a number of speculative activities that sometimes mean + // connections and lookups begin at different times. Workaround that here by + // clamping these values to what we expect FetchStart to be. This means the + // later of AsyncOpen or WorkerStart times. if (!mAsyncOpen.IsNull()) { // We want to clamp to the expected FetchStart value. This is later of // the AsyncOpen and WorkerStart values. @@ -125,6 +123,43 @@ PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel) } } } + + // The aHttpChannel argument is null if this PerformanceTiming object is + // being used for navigation timing (which is only relevant for documents). + // It has a non-null value if this PerformanceTiming object is being used + // for resource timing, which can include document loads, both toplevel and + // in subframes, and resources linked from a document. + if (aHttpChannel) { + mTimingAllowed = CheckAllowedOrigin(aHttpChannel, aChannel); + bool redirectsPassCheck = false; + aChannel->GetAllRedirectsPassTimingAllowCheck(&redirectsPassCheck); + mReportCrossOriginRedirect = mTimingAllowed && redirectsPassCheck; + + SetPropertiesFromHttpChannel(aHttpChannel, aChannel); + } +} + +void +PerformanceTimingData::SetPropertiesFromHttpChannel(nsIHttpChannel* aHttpChannel, + nsITimedChannel* aChannel) +{ + MOZ_ASSERT(aHttpChannel); + + nsAutoCString protocol; + Unused << aHttpChannel->GetProtocolVersion(protocol); + mNextHopProtocol = NS_ConvertUTF8toUTF16(protocol); + + Unused << aHttpChannel->GetEncodedBodySize(&mEncodedBodySize); + Unused << aHttpChannel->GetTransferSize(&mTransferSize); + Unused << aHttpChannel->GetDecodedBodySize(&mDecodedBodySize); + if (mDecodedBodySize == 0) { + mDecodedBodySize = mEncodedBodySize; + } + + mTimingAllowed = CheckAllowedOrigin(aHttpChannel, aChannel); + bool redirectsPassCheck = false; + aChannel->GetAllRedirectsPassTimingAllowCheck(&redirectsPassCheck); + mReportCrossOriginRedirect = mTimingAllowed && redirectsPassCheck; } PerformanceTiming::~PerformanceTiming() @@ -132,8 +167,10 @@ PerformanceTiming::~PerformanceTiming() } DOMHighResTimeStamp -PerformanceTiming::FetchStartHighRes() +PerformanceTimingData::FetchStartHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!mFetchStart) { if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return mZeroTime; @@ -142,9 +179,9 @@ PerformanceTiming::FetchStartHighRes() "valid if the performance timing is enabled"); if (!mAsyncOpen.IsNull()) { if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) { - mFetchStart = TimeStampToDOMHighRes(mWorkerStart); + mFetchStart = TimeStampToDOMHighRes(aPerformance, mWorkerRequestStart); } else { - mFetchStart = TimeStampToDOMHighRes(mAsyncOpen); + mFetchStart = TimeStampToDOMHighRes(aPerformance, mAsyncOpen); } } } @@ -154,12 +191,12 @@ PerformanceTiming::FetchStartHighRes() DOMTimeMilliSec PerformanceTiming::FetchStart() { - return static_cast(FetchStartHighRes()); + return static_cast(mTimingData->FetchStartHighRes(mPerformance)); } bool -PerformanceTiming::CheckAllowedOrigin(nsIHttpChannel* aResourceChannel, - nsITimedChannel* aChannel) +PerformanceTimingData::CheckAllowedOrigin(nsIHttpChannel* aResourceChannel, + nsITimedChannel* aChannel) { if (!IsInitialized()) { return false; @@ -187,14 +224,8 @@ PerformanceTiming::CheckAllowedOrigin(nsIHttpChannel* aResourceChannel, return aChannel->TimingAllowCheck(principal); } -bool -PerformanceTiming::TimingAllowed() const -{ - return mTimingAllowed; -} - uint8_t -PerformanceTiming::GetRedirectCount() const +PerformanceTimingData::GetRedirectCount() const { if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return 0; @@ -206,7 +237,7 @@ PerformanceTiming::GetRedirectCount() const } bool -PerformanceTiming::ShouldReportCrossOriginRedirect() const +PerformanceTimingData::ShouldReportCrossOriginRedirect() const { if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return false; @@ -219,23 +250,27 @@ PerformanceTiming::ShouldReportCrossOriginRedirect() const } DOMHighResTimeStamp -PerformanceTiming::AsyncOpenHighRes() +PerformanceTimingData::AsyncOpenHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || mAsyncOpen.IsNull()) { return mZeroTime; } - return TimeStampToReducedDOMHighResOrFetchStart(mAsyncOpen); + return TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(aPerformance, mAsyncOpen)); } DOMHighResTimeStamp -PerformanceTiming::WorkerStartHighRes() +PerformanceTimingData::WorkerStartHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || mWorkerStart.IsNull()) { return mZeroTime; } - return TimeStampToReducedDOMHighResOrFetchStart(mWorkerStart); + return TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(aPerformance, mWorkerStart)); } /** @@ -249,24 +284,27 @@ PerformanceTiming::WorkerStartHighRes() * @return a valid timing if the Performance Timing is enabled */ DOMHighResTimeStamp -PerformanceTiming::RedirectStartHighRes() +PerformanceTimingData::RedirectStartHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return mZeroTime; } - return TimeStampToReducedDOMHighResOrFetchStart(mRedirectStart); + return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRedirectStart); } DOMTimeMilliSec PerformanceTiming::RedirectStart() { - if (!IsInitialized()) { + if (!mTimingData->IsInitialized()) { return 0; } // We have to check if all the redirect URIs had the same origin (since there // is no check in RedirectStartHighRes()) - if (mAllRedirectsSameOrigin && mRedirectCount) { - return static_cast(RedirectStartHighRes()); + if (mTimingData->AllRedirectsSameOrigin() && + mTimingData->RedirectCountReal()) { + return static_cast(mTimingData->RedirectStartHighRes(mPerformance)); } return 0; } @@ -282,130 +320,155 @@ PerformanceTiming::RedirectStart() * @return a valid timing if the Performance Timing is enabled */ DOMHighResTimeStamp -PerformanceTiming::RedirectEndHighRes() +PerformanceTimingData::RedirectEndHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return mZeroTime; } - return TimeStampToReducedDOMHighResOrFetchStart(mRedirectEnd); + return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRedirectEnd); } DOMTimeMilliSec PerformanceTiming::RedirectEnd() { - if (!IsInitialized()) { + if (!mTimingData->IsInitialized()) { return 0; } // We have to check if all the redirect URIs had the same origin (since there // is no check in RedirectEndHighRes()) - if (mAllRedirectsSameOrigin && mRedirectCount) { - return static_cast(RedirectEndHighRes()); + if (mTimingData->AllRedirectsSameOrigin() && + mTimingData->RedirectCountReal()) { + return static_cast(mTimingData->RedirectEndHighRes(mPerformance)); } return 0; } DOMHighResTimeStamp -PerformanceTiming::DomainLookupStartHighRes() +PerformanceTimingData::DomainLookupStartHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return mZeroTime; } - return TimeStampToReducedDOMHighResOrFetchStart(mDomainLookupStart); + return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, + mDomainLookupStart); } DOMTimeMilliSec PerformanceTiming::DomainLookupStart() { - return static_cast(DomainLookupStartHighRes()); + return static_cast(mTimingData->DomainLookupStartHighRes(mPerformance)); } DOMHighResTimeStamp -PerformanceTiming::DomainLookupEndHighRes() +PerformanceTimingData::DomainLookupEndHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return mZeroTime; } - // Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null - return mDomainLookupEnd.IsNull() ? DomainLookupStartHighRes() - : TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(mDomainLookupEnd)); + return mDomainLookupEnd.IsNull() + ? DomainLookupStartHighRes(aPerformance) + : TimerClamping::ReduceMsTimeValue( + TimeStampToDOMHighRes(aPerformance, mDomainLookupEnd)); } DOMTimeMilliSec PerformanceTiming::DomainLookupEnd() { - return static_cast(DomainLookupEndHighRes()); + return static_cast(mTimingData->DomainLookupEndHighRes(mPerformance)); } DOMHighResTimeStamp -PerformanceTiming::ConnectStartHighRes() +PerformanceTimingData::ConnectStartHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return mZeroTime; } - return mConnectStart.IsNull() ? DomainLookupEndHighRes() - : TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(mConnectStart)); + return mConnectStart.IsNull() + ? DomainLookupEndHighRes(aPerformance) + : TimerClamping::ReduceMsTimeValue( + TimeStampToDOMHighRes(aPerformance, mConnectStart)); } DOMTimeMilliSec PerformanceTiming::ConnectStart() { - return static_cast(ConnectStartHighRes()); + return static_cast(mTimingData->ConnectStartHighRes(mPerformance)); } DOMHighResTimeStamp -PerformanceTiming::SecureConnectionStartHighRes() +PerformanceTimingData::SecureConnectionStartHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return mZeroTime; } return !mSecureConnection ? 0 // We use 0 here, because mZeroTime is sometimes set to the navigation // start time. - : (mSecureConnectionStart.IsNull() ? mZeroTime - : TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(mSecureConnectionStart))); + : (mSecureConnectionStart.IsNull() + ? mZeroTime + : TimerClamping::ReduceMsTimeValue( + TimeStampToDOMHighRes(aPerformance, mSecureConnectionStart))); } DOMTimeMilliSec PerformanceTiming::SecureConnectionStart() { - return static_cast(SecureConnectionStartHighRes()); + return static_cast(mTimingData->SecureConnectionStartHighRes(mPerformance)); } DOMHighResTimeStamp -PerformanceTiming::ConnectEndHighRes() +PerformanceTimingData::ConnectEndHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return mZeroTime; } // Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null - return mConnectEnd.IsNull() ? ConnectStartHighRes() - : TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(mConnectEnd)); + return mConnectEnd.IsNull() + ? ConnectStartHighRes(aPerformance) + : TimerClamping::ReduceMsTimeValue( + TimeStampToDOMHighRes(aPerformance, mConnectEnd)); } DOMTimeMilliSec PerformanceTiming::ConnectEnd() { - return static_cast(ConnectEndHighRes()); + return static_cast(mTimingData->ConnectEndHighRes(mPerformance)); } DOMHighResTimeStamp -PerformanceTiming::RequestStartHighRes() +PerformanceTimingData::RequestStartHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return mZeroTime; } - return TimeStampToReducedDOMHighResOrFetchStart(mRequestStart); + return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mRequestStart); } DOMTimeMilliSec PerformanceTiming::RequestStart() { - return static_cast(RequestStartHighRes()); + return static_cast(mTimingData->RequestStartHighRes(mPerformance)); } DOMHighResTimeStamp -PerformanceTiming::ResponseStartHighRes() +PerformanceTimingData::ResponseStartHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return mZeroTime; } @@ -413,18 +476,20 @@ PerformanceTiming::ResponseStartHighRes() (!mCacheReadStart.IsNull() && mCacheReadStart < mResponseStart)) { mResponseStart = mCacheReadStart; } - return TimeStampToReducedDOMHighResOrFetchStart(mResponseStart); + return TimeStampToReducedDOMHighResOrFetchStart(aPerformance, mResponseStart); } DOMTimeMilliSec PerformanceTiming::ResponseStart() { - return static_cast(ResponseStartHighRes()); + return static_cast(mTimingData->ResponseStartHighRes(mPerformance)); } DOMHighResTimeStamp -PerformanceTiming::ResponseEndHighRes() +PerformanceTimingData::ResponseEndHighRes(Performance* aPerformance) { + MOZ_ASSERT(aPerformance); + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return mZeroTime; } @@ -433,20 +498,16 @@ PerformanceTiming::ResponseEndHighRes() mResponseEnd = mCacheReadEnd; } // Bug 1155008 - nsHttpTransaction is racy. Return ResponseStart when null - return mResponseEnd.IsNull() ? ResponseStartHighRes() - : TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(mResponseEnd)); + return mResponseEnd.IsNull() + ? ResponseStartHighRes(aPerformance) + : TimerClamping::ReduceMsTimeValue( + TimeStampToDOMHighRes(aPerformance, mResponseEnd)); } DOMTimeMilliSec PerformanceTiming::ResponseEnd() { - return static_cast(ResponseEndHighRes()); -} - -bool -PerformanceTiming::IsInitialized() const -{ - return mInitialized; + return static_cast(mTimingData->ResponseEndHighRes(mPerformance)); } JSObject* diff --git a/dom/performance/PerformanceTiming.h b/dom/performance/PerformanceTiming.h index c0d4b6237b..91bd14f0d7 100755 --- a/dom/performance/PerformanceTiming.h +++ b/dom/performance/PerformanceTiming.h @@ -19,44 +19,38 @@ class nsITimedChannel; namespace mozilla { namespace dom { -// Script "performance.timing" object -class PerformanceTiming final : public nsWrapperCache +class PerformanceTimingData final { public: -/** - * @param aPerformance - * The performance object (the JS parent). - * This will allow access to "window.performance.timing" attribute for - * the navigation timing (can't be null). - * @param aChannel - * An nsITimedChannel used to gather all the networking timings by both - * the navigation timing and the resource timing (can't be null). - * @param aHttpChannel - * An nsIHttpChannel (the resource's http channel). - * This will be used by the resource timing cross-domain check - * algorithm. - * Argument is null for the navigation timing (navigation timing uses - * another algorithm for the cross-domain redirects). - * @param aZeroTime - * The offset that will be added to the timestamp of each event. This - * argument should be equal to performance.navigationStart for - * navigation timing and "0" for the resource timing. - */ - PerformanceTiming(Performance* aPerformance, - nsITimedChannel* aChannel, - nsIHttpChannel* aHttpChannel, - DOMHighResTimeStamp aZeroTime); - NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PerformanceTiming) - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PerformanceTiming) + PerformanceTimingData(nsITimedChannel* aChannel, + nsIHttpChannel* aHttpChannel, + DOMHighResTimeStamp aZeroTime); - nsDOMNavigationTiming* GetDOMTiming() const + void SetPropertiesFromHttpChannel(nsIHttpChannel* aHttpChannel, nsITimedChannel* aChannel); + + bool IsInitialized() const { - return mPerformance->GetDOMTiming(); + return mInitialized; } - Performance* GetParentObject() const + const nsString& NextHopProtocol() const { - return mPerformance; + return mNextHopProtocol; + } + + uint64_t TransferSize() const + { + return mTimingAllowed ? mTransferSize : 0; + } + + uint64_t EncodedBodySize() const + { + return mTimingAllowed ? mEncodedBodySize : 0; + } + + uint64_t DecodedBodySize() const + { + return mTimingAllowed ? mDecodedBodySize : 0; } /** @@ -68,11 +62,16 @@ public: * page), if the given TimeStamp is valid. Otherwise, it will return * the FetchStart timing value. */ - inline DOMHighResTimeStamp TimeStampToReducedDOMHighResOrFetchStart(TimeStamp aStamp) + inline DOMHighResTimeStamp + TimeStampToReducedDOMHighResOrFetchStart(Performance* aPerformance, + TimeStamp aStamp) { + MOZ_ASSERT(aPerformance); + return (!aStamp.IsNull()) - ? TimerClamping::ReduceMsTimeValue(TimeStampToDOMHighRes(aStamp)) - : FetchStartHighRes(); + ? TimerClamping::ReduceMsTimeValue( + TimeStampToDOMHighRes(aPerformance, aStamp)) + : FetchStartHighRes(aPerformance); } /** @@ -83,7 +82,7 @@ public: * * The algorithm operates in 2 steps: * 1. The first step is to subtract the two timestamps: the argument (the - * envet's timesramp) and the navigation start timestamp. This will result in + * event's timestamp) and the navigation start timestamp. This will result in * a relative timestamp of the event (relative to the navigation start - * window.performance.timing.navigationStart). * 2. The second step is to add any required offset (the mZeroTime). For now, @@ -99,17 +98,154 @@ public: * be null. * @return number of milliseconds value as one of: * - relative to the navigation start time, time the user has landed on the - * page + * page * - an absolute wall clock time since the unix epoch */ - inline DOMHighResTimeStamp TimeStampToDOMHighRes(TimeStamp aStamp) const + inline DOMHighResTimeStamp + TimeStampToDOMHighRes(Performance* aPerformance, TimeStamp aStamp) const { + MOZ_ASSERT(aPerformance); MOZ_ASSERT(!aStamp.IsNull()); + TimeDuration duration = - aStamp - GetDOMTiming()->GetNavigationStartTimeStamp(); + aStamp - aPerformance->GetDOMTiming()->GetNavigationStartTimeStamp(); return duration.ToMilliseconds() + mZeroTime; } + // The last channel's AsyncOpen time. This may occur before the FetchStart + // in some cases. + DOMHighResTimeStamp AsyncOpenHighRes(Performance* aPerformance); + + // High resolution (used by resource timing) + DOMHighResTimeStamp WorkerStartHighRes(Performance* aPerformance); + DOMHighResTimeStamp FetchStartHighRes(Performance* aPerformance); + DOMHighResTimeStamp RedirectStartHighRes(Performance* aPerformance); + DOMHighResTimeStamp RedirectEndHighRes(Performance* aPerformance); + DOMHighResTimeStamp DomainLookupStartHighRes(Performance* aPerformance); + DOMHighResTimeStamp DomainLookupEndHighRes(Performance* aPerformance); + DOMHighResTimeStamp ConnectStartHighRes(Performance* aPerformance); + DOMHighResTimeStamp SecureConnectionStartHighRes(Performance* aPerformance); + DOMHighResTimeStamp ConnectEndHighRes(Performance* aPerformance); + DOMHighResTimeStamp RequestStartHighRes(Performance* aPerformance); + DOMHighResTimeStamp ResponseStartHighRes(Performance* aPerformance); + DOMHighResTimeStamp ResponseEndHighRes(Performance* aPerformance); + + DOMHighResTimeStamp ZeroTime() const { return mZeroTime; } + + uint8_t RedirectCountReal() const { return mRedirectCount; } + uint8_t GetRedirectCount() const; + + bool AllRedirectsSameOrigin() const { return mAllRedirectsSameOrigin; } + + // If this is false the values of redirectStart/End will be 0 This is false if + // no redirects occured, or if any of the responses failed the + // timing-allow-origin check in HttpBaseChannel::TimingAllowCheck + bool ShouldReportCrossOriginRedirect() const; + + // Cached result of CheckAllowedOrigin. If false, security sensitive + // attributes of the resourceTiming object will be set to 0 + bool TimingAllowed() const + { + return mTimingAllowed; + } + +private: + // 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 + // with a value of * or matching the domain of the loading Principal + bool CheckAllowedOrigin(nsIHttpChannel* aResourceChannel, + nsITimedChannel* aChannel); + + nsString mNextHopProtocol; + + TimeStamp mAsyncOpen; + TimeStamp mRedirectStart; + TimeStamp mRedirectEnd; + TimeStamp mDomainLookupStart; + TimeStamp mDomainLookupEnd; + TimeStamp mConnectStart; + TimeStamp mSecureConnectionStart; + TimeStamp mConnectEnd; + TimeStamp mRequestStart; + TimeStamp mResponseStart; + TimeStamp mCacheReadStart; + TimeStamp mResponseEnd; + TimeStamp mCacheReadEnd; + + // ServiceWorker interception timing information + TimeStamp mWorkerStart; + TimeStamp mWorkerRequestStart; + TimeStamp mWorkerResponseEnd; + + // This is an offset that will be added to each timing ([ms] resolution). + // There are only 2 possible values: (1) logicaly equal to navigationStart + // TimeStamp (results are absolute timstamps - wallclock); (2) "0" (results + // are relative to the navigation start). + DOMHighResTimeStamp mZeroTime; + + DOMHighResTimeStamp mFetchStart; + + uint64_t mEncodedBodySize; + uint64_t mTransferSize; + uint64_t mDecodedBodySize; + + uint8_t mRedirectCount; + + bool mAllRedirectsSameOrigin; + + // If the resourceTiming object should have non-zero redirectStart and + // redirectEnd attributes. It is false if there were no redirects, or if any + // of the responses didn't pass the timing-allow-check + bool mReportCrossOriginRedirect; + + bool mSecureConnection; + + bool mTimingAllowed; + + bool mInitialized; + +}; + +// Script "performance.timing" object +class PerformanceTiming final : public nsWrapperCache +{ +public: +/** + * @param aPerformance + * The performance object (the JS parent). + * This will allow access to "window.performance.timing" attribute for + * the navigation timing (can't be null). + * @param aChannel + * An nsITimedChannel used to gather all the networking timings by both + * the navigation timing and the resource timing (can't be null). + * @param aHttpChannel + * An nsIHttpChannel (the resource's http channel). + * This will be used by the resource timing cross-domain check + * algorithm. + * Argument is null for the navigation timing (navigation timing uses + * another algorithm for the cross-domain redirects). + * @param aZeroTime + * The offset that will be added to the timestamp of each event. This + * argument should be equal to performance.navigationStart for + * navigation timing and "0" for the resource timing. + */ + PerformanceTiming(Performance* aPerformance, + nsITimedChannel* aChannel, + nsIHttpChannel* aHttpChannel, + DOMHighResTimeStamp aZeroTime); + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PerformanceTiming) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PerformanceTiming) + + nsDOMNavigationTiming* GetDOMTiming() const + { + return mPerformance->GetDOMTiming(); + } + + Performance* GetParentObject() const + { + return mPerformance; + } + virtual JSObject* WrapObject(JSContext *cx, JS::Handle aGivenProto) override; @@ -138,40 +274,6 @@ public: return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetUnloadEventEnd()); } - uint8_t GetRedirectCount() const; - - // 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 - // with a value of * or matching the domain of the loading Principal - bool CheckAllowedOrigin(nsIHttpChannel* aResourceChannel, nsITimedChannel* aChannel); - - // Cached result of CheckAllowedOrigin. If false, security sensitive - // attributes of the resourceTiming object will be set to 0 - bool TimingAllowed() const; - - // If this is false the values of redirectStart/End will be 0 - // This is false if no redirects occured, or if any of the responses failed - // the timing-allow-origin check in HttpBaseChannel::TimingAllowCheck - 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) - DOMHighResTimeStamp WorkerStartHighRes(); - DOMHighResTimeStamp FetchStartHighRes(); - DOMHighResTimeStamp RedirectStartHighRes(); - DOMHighResTimeStamp RedirectEndHighRes(); - DOMHighResTimeStamp DomainLookupStartHighRes(); - DOMHighResTimeStamp DomainLookupEndHighRes(); - DOMHighResTimeStamp ConnectStartHighRes(); - DOMHighResTimeStamp SecureConnectionStartHighRes(); - DOMHighResTimeStamp ConnectEndHighRes(); - DOMHighResTimeStamp RequestStartHighRes(); - DOMHighResTimeStamp ResponseStartHighRes(); - DOMHighResTimeStamp ResponseEndHighRes(); - // Low resolution (used by navigation timing) DOMTimeMilliSec FetchStart(); DOMTimeMilliSec RedirectStart(); @@ -249,46 +351,16 @@ public: return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetTimeToNonBlankPaint()); } + PerformanceTimingData* Data() const + { + return mTimingData.get(); + } + private: ~PerformanceTiming(); - bool IsInitialized() const; - void InitializeTimingInfo(nsITimedChannel* aChannel); - RefPtr mPerformance; - DOMHighResTimeStamp mFetchStart; - - // This is an offset that will be added to each timing ([ms] resolution). - // There are only 2 possible values: (1) logicaly equal to navigationStart - // TimeStamp (results are absolute timstamps - wallclock); (2) "0" (results - // are relative to the navigation start). - DOMHighResTimeStamp mZeroTime; - - TimeStamp mAsyncOpen; - TimeStamp mWorkerStart; - TimeStamp mRedirectStart; - TimeStamp mRedirectEnd; - TimeStamp mDomainLookupStart; - TimeStamp mDomainLookupEnd; - TimeStamp mConnectStart; - TimeStamp mSecureConnectionStart; - TimeStamp mConnectEnd; - TimeStamp mRequestStart; - TimeStamp mResponseStart; - TimeStamp mCacheReadStart; - TimeStamp mResponseEnd; - TimeStamp mCacheReadEnd; - uint8_t mRedirectCount; - bool mTimingAllowed; - bool mAllRedirectsSameOrigin; - bool mInitialized; - - // If the resourceTiming object should have non-zero redirectStart and - // redirectEnd attributes. It is false if there were no redirects, or if - // any of the responses didn't pass the timing-allow-check - bool mReportCrossOriginRedirect; - - bool mSecureConnection; + UniquePtr mTimingData; }; } // namespace dom diff --git a/dom/performance/PerformanceWorker.h b/dom/performance/PerformanceWorker.h index 3604e6874f..cdd3a521dd 100644 --- a/dom/performance/PerformanceWorker.h +++ b/dom/performance/PerformanceWorker.h @@ -62,6 +62,11 @@ public: return nullptr; } + void QueueNavigationTimingEntry() override + { + MOZ_CRASH("This should not be called on workers."); + } + protected: ~PerformanceWorker(); diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 879f47b206..a3577fdd12 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -1070,6 +1070,11 @@ nsDocumentViewer::LoadComplete(nsresult aStatus) if (timing) { timing->NotifyLoadEventEnd(); } + + nsPIDOMWindowInner* innerWindow = window->GetCurrentInnerWindow(); + if (innerWindow) { + innerWindow->QueuePerformanceNavigationTiming(); + } } } else { // XXX: Should fire error event to the document... -- cgit v1.2.3