diff --git a/dom/performance/PerformanceMainThread.cpp b/dom/performance/PerformanceMainThread.cpp index bbfdaf3cf822..c38c616d955d 100644 --- a/dom/performance/PerformanceMainThread.cpp +++ b/dom/performance/PerformanceMainThread.cpp @@ -147,15 +147,18 @@ void PerformanceMainThread::AddEntry(nsIHttpChannel* channel, if (!performanceTimingData) { return; } + AddRawEntry(std::move(performanceTimingData), initiatorType, entryName); +} +void PerformanceMainThread::AddRawEntry(UniquePtr aData, + const nsAString& aInitiatorType, + const nsAString& aEntryName) { // The PerformanceResourceTiming object will use the PerformanceTimingData // object to get all the required timings. - RefPtr performanceEntry = - new PerformanceResourceTiming(std::move(performanceTimingData), this, - entryName); - - performanceEntry->SetInitiatorType(initiatorType); - InsertResourceEntry(performanceEntry); + auto entry = + MakeRefPtr(std::move(aData), this, aEntryName); + entry->SetInitiatorType(aInitiatorType); + InsertResourceEntry(entry); } // To be removed once bug 1124165 lands diff --git a/dom/performance/PerformanceMainThread.h b/dom/performance/PerformanceMainThread.h index 989bc3f01749..4b55ab555159 100644 --- a/dom/performance/PerformanceMainThread.h +++ b/dom/performance/PerformanceMainThread.h @@ -35,6 +35,10 @@ class PerformanceMainThread final : public Performance, virtual void AddEntry(nsIHttpChannel* channel, nsITimedChannel* timedChannel) override; + void AddRawEntry(UniquePtr, + const nsAString& aInitiatorType, + const nsAString& aEntryName); + TimeStamp CreationTimeStamp() const override; DOMHighResTimeStamp CreationTime() const override; diff --git a/dom/performance/PerformanceTiming.cpp b/dom/performance/PerformanceTiming.cpp index 8cccc2e94233..f77d3df4abe5 100644 --- a/dom/performance/PerformanceTiming.cpp +++ b/dom/performance/PerformanceTiming.cpp @@ -205,6 +205,14 @@ PerformanceTimingData::PerformanceTimingData(nsITimedChannel* aChannel, } } +void PerformanceTimingData::InitializeForMemoryCacheHit() { + auto now = TimeStamp::Now(); + mInitialized = true; + mAsyncOpen = mRequestStart = mResponseStart = mResponseEnd = + mDomainLookupStart = mDomainLookupEnd = mConnectStart = mConnectEnd = + mCacheReadStart = mCacheReadEnd = now; +} + void PerformanceTimingData::SetPropertiesFromHttpChannel( nsIHttpChannel* aHttpChannel, nsITimedChannel* aChannel) { MOZ_ASSERT(aHttpChannel); diff --git a/dom/performance/PerformanceTiming.h b/dom/performance/PerformanceTiming.h index d893818f722b..657234a7a6ce 100644 --- a/dom/performance/PerformanceTiming.h +++ b/dom/performance/PerformanceTiming.h @@ -112,6 +112,18 @@ class PerformanceTimingData final { return duration.ToMilliseconds() + mZeroTime; } + /** + * Initializes the timestamps to represent a synchronous cache hit in an + * in-memory cache. + * + * This is used right now for the shared stylesheet cache, which doesn't + * always go through necko and thus there isn't an HTTP channel, etc. + * + * We fill the values with the current timestamp to represent an "instant" + * resource fetch. + */ + void InitializeForMemoryCacheHit(); + // The last channel's AsyncOpen time. This may occur before the FetchStart // in some cases. DOMHighResTimeStamp AsyncOpenHighRes(Performance* aPerformance); diff --git a/dom/performance/moz.build b/dom/performance/moz.build index 0678920c53db..df7f7dc72fc2 100644 --- a/dom/performance/moz.build +++ b/dom/performance/moz.build @@ -10,6 +10,7 @@ with Files("**"): EXPORTS.mozilla.dom += [ 'Performance.h', 'PerformanceEntry.h', + 'PerformanceMainThread.h', 'PerformanceMark.h', 'PerformanceMeasure.h', 'PerformanceNavigation.h', diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index f548f3ea5506..e020bd140d99 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -28,6 +28,8 @@ #include "nsIContent.h" #include "nsIContentInlines.h" #include "mozilla/dom/Document.h" +#include "mozilla/dom/PerformanceTiming.h" +#include "mozilla/dom/PerformanceMainThread.h" #include "nsIURI.h" #include "nsNetUtil.h" #include "nsContentUtils.h" @@ -263,10 +265,6 @@ SheetLoadData::SheetLoadData(Loader* aLoader, nsIURI* aURI, StyleSheet* aSheet, mCompatMode(aLoader->mCompatMode) { MOZ_ASSERT(mLoader, "Must have a loader!"); MOZ_ASSERT(mTriggeringPrincipal); - if (mParentData) { - ++mParentData->mPendingChildren; - } - MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad, "Shouldn't use system principal for async loads"); } @@ -648,18 +646,14 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus, mSheet->SetPrincipal(principal); - if (mSheet->GetCORSMode() == CORS_NONE) { - bool subsumed; - result = mTriggeringPrincipal->Subsumes(principal, &subsumed); - if (NS_FAILED(result) || !subsumed) { - mIsCrossOriginNoCORS = true; - } + if (mSheet->GetCORSMode() == CORS_NONE && + !mTriggeringPrincipal->Subsumes(principal)) { + mIsCrossOriginNoCORS = true; } // If it's an HTTP channel, we want to make sure this is not an // error document we got. - nsCOMPtr httpChannel(do_QueryInterface(aChannel)); - if (httpChannel) { + if (nsCOMPtr httpChannel = do_QueryInterface(aChannel)) { bool requestSucceeded; result = httpChannel->GetRequestSucceeded(&requestSucceeded); if (NS_SUCCEEDED(result) && !requestSucceeded) { @@ -829,6 +823,48 @@ nsresult Loader::CheckContentPolicy(nsIPrincipal* aLoadingPrincipal, return NS_OK; } +void Loader::MaybeNotifyOfResourceTiming(SheetLoadData& aData) { + SheetLoadDataHashKey key(aData); + if (!mLoadsPerformed.EnsureInserted(key)) { + // We've already reported this subresource load. + return; + } + + if (!mDocument) { + // No document means no performance object to report to. + return; + } + + nsPIDOMWindowInner* win = mDocument->GetInnerWindow(); + if (!win) { + return; + } + + auto* perf = static_cast(win->GetPerformance()); + if (!perf) { + return; + } + + // If any ancestor is cross-origin, then we don't report it, see + // mIsCrossOriginNoCORS and so. + for (auto* parent = aData.mSheet->GetParentSheet(); parent; + parent = parent->GetParentSheet()) { + if (parent->GetCORSMode() == CORS_NONE && + !aData.mTriggeringPrincipal->Subsumes(parent->Principal())) { + return; + } + } + + nsLiteralString initiatorType = aData.mSheet->GetParentSheet() + ? NS_LITERAL_STRING("css") + : NS_LITERAL_STRING("link"); + nsAutoCString entryName; + aData.mSheet->GetOriginalURI()->GetSpec(entryName); + auto data = MakeUnique(nullptr, nullptr, 0); + data->InitializeForMemoryCacheHit(); + perf->AddRawEntry(std::move(data), initiatorType, + NS_ConvertUTF8toUTF16(entryName)); +} /** * CreateSheet() creates a StyleSheet object for the given URI. * @@ -1048,6 +1084,9 @@ nsresult Loader::LoadSheet(SheetLoadData& aLoadData, SheetState aSheetState) { LOG_URI(" Load from: '%s'", aLoadData.mURI); ++mOngoingLoadCount; + if (aLoadData.mParentData) { + ++aLoadData.mParentData->mPendingChildren; + } nsresult rv = NS_OK; @@ -1306,6 +1345,8 @@ nsresult Loader::LoadSheet(SheetLoadData& aLoadData, SheetState aSheetState) { // sub-resource and so we set the flag on ourself too to propagate it // on down. // + // If you add more conditions here make sure to add them to + // MaybeNotifyResourceTiming too. if (aLoadData.mParentData->mIsCrossOriginNoCORS || aLoadData.mParentData->mBlockResourceTiming) { // Set a flag so any other stylesheet triggered by this one will @@ -1684,6 +1725,7 @@ Result Loader::LoadStyleLink( aInfo.mReferrerInfo, context); if (state == SheetState::Complete) { LOG((" Sheet already complete: 0x%p", sheet.get())); + MaybeNotifyOfResourceTiming(*data); if (aObserver || !mObservers.IsEmpty() || aInfo.mContent) { rv = PostLoadEvent(std::move(data)); if (NS_FAILED(rv)) { @@ -1833,18 +1875,22 @@ nsresult Loader::LoadChildSheet(StyleSheet& aParentSheet, MOZ_ASSERT(sheet); InsertChildSheet(*sheet, aParentSheet); - if (state == SheetState::Complete) { - LOG((" Sheet already complete")); - // We're completely done. No need to notify, even, since the - // @import rule addition/modification will trigger the right style - // changes automatically. - return NS_OK; - } - auto data = MakeRefPtr( this, aURL, sheet, aParentData, observer, principal, aParentSheet.GetReferrerInfo(), context); + if (state == SheetState::Complete) { + LOG((" Sheet already complete")); + MaybeNotifyOfResourceTiming(*data); + // We're completely done. No need to notify, even, since the + // @import rule addition/modification will trigger the right style + // changes automatically. +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED + data->mIntentionallyDropped = true; +#endif + return NS_OK; + } + bool syncLoad = data->mSyncLoad; // Load completion will release the data @@ -1925,6 +1971,7 @@ Result, nsresult> Loader::InternalLoadNonDocumentSheet( mDocument); if (state == SheetState::Complete) { LOG((" Sheet already complete")); + MaybeNotifyOfResourceTiming(*data); if (aObserver || !mObservers.IsEmpty()) { rv = PostLoadEvent(std::move(data)); if (NS_FAILED(rv)) { diff --git a/layout/style/Loader.h b/layout/style/Loader.h index dcfeb802b10b..7673908f2842 100644 --- a/layout/style/Loader.h +++ b/layout/style/Loader.h @@ -358,6 +358,11 @@ class Loader final { nsIURI* aURL, dom::MediaList* aMedia, LoaderReusableStyleSheets* aSavedSheets); + // If we hit the shared cache and this is the first load for a given resource, + // we still post a resource timing entry, because tests expect this, and other + // browsers behave like this too. + void MaybeNotifyOfResourceTiming(SheetLoadData&); + enum class UseSystemPrincipal { No, Yes }; /**