diff options
Diffstat (limited to 'dom/performance/PerformanceTiming.cpp')
-rw-r--r-- | dom/performance/PerformanceTiming.cpp | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/dom/performance/PerformanceTiming.cpp b/dom/performance/PerformanceTiming.cpp new file mode 100644 index 000000000..527cf9441 --- /dev/null +++ b/dom/performance/PerformanceTiming.cpp @@ -0,0 +1,361 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "PerformanceTiming.h" +#include "mozilla/dom/PerformanceTimingBinding.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceTiming, mPerformance) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PerformanceTiming, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PerformanceTiming, Release) + +PerformanceTiming::PerformanceTiming(Performance* aPerformance, + nsITimedChannel* aChannel, + nsIHttpChannel* aHttpChannel, + DOMHighResTimeStamp aZeroTime) + : mPerformance(aPerformance), + mFetchStart(0.0), + mZeroTime(aZeroTime), + mRedirectCount(0), + mTimingAllowed(true), + mAllRedirectsSameOrigin(true), + mInitialized(!!aChannel), + mReportCrossOriginRedirect(true) +{ + MOZ_ASSERT(aPerformance, "Parent performance object should be provided"); + + 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; + } + + 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->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin); + aChannel->GetRedirectCount(&mRedirectCount); + aChannel->GetRedirectStart(&mRedirectStart); + aChannel->GetRedirectEnd(&mRedirectEnd); + aChannel->GetDomainLookupStart(&mDomainLookupStart); + aChannel->GetDomainLookupEnd(&mDomainLookupEnd); + aChannel->GetConnectStart(&mConnectStart); + aChannel->GetConnectEnd(&mConnectEnd); + aChannel->GetRequestStart(&mRequestStart); + aChannel->GetResponseStart(&mResponseStart); + aChannel->GetCacheReadStart(&mCacheReadStart); + aChannel->GetResponseEnd(&mResponseEnd); + aChannel->GetCacheReadEnd(&mCacheReadEnd); + } +} + +PerformanceTiming::~PerformanceTiming() +{ +} + +DOMHighResTimeStamp +PerformanceTiming::FetchStartHighRes() +{ + if (!mFetchStart) { + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { + return mZeroTime; + } + MOZ_ASSERT(!mAsyncOpen.IsNull(), "The fetch start time stamp should always be " + "valid if the performance timing is enabled"); + mFetchStart = (!mAsyncOpen.IsNull()) + ? TimeStampToDOMHighRes(mAsyncOpen) + : 0.0; + } + return mFetchStart; +} + +DOMTimeMilliSec +PerformanceTiming::FetchStart() +{ + return static_cast<int64_t>(FetchStartHighRes()); +} + +bool +PerformanceTiming::CheckAllowedOrigin(nsIHttpChannel* aResourceChannel, + nsITimedChannel* aChannel) +{ + if (!IsInitialized()) { + return false; + } + + // Check that the current document passes the ckeck. + nsCOMPtr<nsILoadInfo> loadInfo; + aResourceChannel->GetLoadInfo(getter_AddRefs(loadInfo)); + if (!loadInfo) { + return false; + } + + // TYPE_DOCUMENT loads have no loadingPrincipal. And that's OK, because we + // never actually need to have a performance timing entry for TYPE_DOCUMENT + // loads. + if (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) { + return false; + } + + nsCOMPtr<nsIPrincipal> principal = loadInfo->LoadingPrincipal(); + + // Check if the resource is either same origin as the page that started + // the load, or if the response contains the proper Timing-Allow-Origin + // header with the domain of the page that started the load. + return aChannel->TimingAllowCheck(principal); +} + +bool +PerformanceTiming::TimingAllowed() const +{ + return mTimingAllowed; +} + +uint16_t +PerformanceTiming::GetRedirectCount() const +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { + return 0; + } + if (!mAllRedirectsSameOrigin) { + return 0; + } + return mRedirectCount; +} + +bool +PerformanceTiming::ShouldReportCrossOriginRedirect() const +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { + return false; + } + + // If the redirect count is 0, or if one of the cross-origin + // redirects doesn't have the proper Timing-Allow-Origin header, + // then RedirectStart and RedirectEnd will be set to zero + return (mRedirectCount != 0) && mReportCrossOriginRedirect; +} + +/** + * RedirectStartHighRes() is used by both the navigation timing and the + * resource timing. Since, navigation timing and resource timing check and + * interpret cross-domain redirects in a different manner, + * RedirectStartHighRes() will make no checks for cross-domain redirect. + * It's up to the consumers of this method (PerformanceTiming::RedirectStart() + * and PerformanceResourceTiming::RedirectStart() to make such verifications. + * + * @return a valid timing if the Performance Timing is enabled + */ +DOMHighResTimeStamp +PerformanceTiming::RedirectStartHighRes() +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { + return mZeroTime; + } + return TimeStampToDOMHighResOrFetchStart(mRedirectStart); +} + +DOMTimeMilliSec +PerformanceTiming::RedirectStart() +{ + if (!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<int64_t>(RedirectStartHighRes()); + } + return 0; +} + +/** + * RedirectEndHighRes() is used by both the navigation timing and the resource + * timing. Since, navigation timing and resource timing check and interpret + * cross-domain redirects in a different manner, RedirectEndHighRes() will make + * no checks for cross-domain redirect. It's up to the consumers of this method + * (PerformanceTiming::RedirectEnd() and + * PerformanceResourceTiming::RedirectEnd() to make such verifications. + * + * @return a valid timing if the Performance Timing is enabled + */ +DOMHighResTimeStamp +PerformanceTiming::RedirectEndHighRes() +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { + return mZeroTime; + } + return TimeStampToDOMHighResOrFetchStart(mRedirectEnd); +} + +DOMTimeMilliSec +PerformanceTiming::RedirectEnd() +{ + if (!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<int64_t>(RedirectEndHighRes()); + } + return 0; +} + +DOMHighResTimeStamp +PerformanceTiming::DomainLookupStartHighRes() +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { + return mZeroTime; + } + return TimeStampToDOMHighResOrFetchStart(mDomainLookupStart); +} + +DOMTimeMilliSec +PerformanceTiming::DomainLookupStart() +{ + return static_cast<int64_t>(DomainLookupStartHighRes()); +} + +DOMHighResTimeStamp +PerformanceTiming::DomainLookupEndHighRes() +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { + return mZeroTime; + } + // Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null + return mDomainLookupEnd.IsNull() ? DomainLookupStartHighRes() + : TimeStampToDOMHighRes(mDomainLookupEnd); +} + +DOMTimeMilliSec +PerformanceTiming::DomainLookupEnd() +{ + return static_cast<int64_t>(DomainLookupEndHighRes()); +} + +DOMHighResTimeStamp +PerformanceTiming::ConnectStartHighRes() +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { + return mZeroTime; + } + return mConnectStart.IsNull() ? DomainLookupEndHighRes() + : TimeStampToDOMHighRes(mConnectStart); +} + +DOMTimeMilliSec +PerformanceTiming::ConnectStart() +{ + return static_cast<int64_t>(ConnectStartHighRes()); +} + +DOMHighResTimeStamp +PerformanceTiming::ConnectEndHighRes() +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { + return mZeroTime; + } + // Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null + return mConnectEnd.IsNull() ? ConnectStartHighRes() + : TimeStampToDOMHighRes(mConnectEnd); +} + +DOMTimeMilliSec +PerformanceTiming::ConnectEnd() +{ + return static_cast<int64_t>(ConnectEndHighRes()); +} + +DOMHighResTimeStamp +PerformanceTiming::RequestStartHighRes() +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { + return mZeroTime; + } + return TimeStampToDOMHighResOrFetchStart(mRequestStart); +} + +DOMTimeMilliSec +PerformanceTiming::RequestStart() +{ + return static_cast<int64_t>(RequestStartHighRes()); +} + +DOMHighResTimeStamp +PerformanceTiming::ResponseStartHighRes() +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { + return mZeroTime; + } + if (mResponseStart.IsNull() || + (!mCacheReadStart.IsNull() && mCacheReadStart < mResponseStart)) { + mResponseStart = mCacheReadStart; + } + return TimeStampToDOMHighResOrFetchStart(mResponseStart); +} + +DOMTimeMilliSec +PerformanceTiming::ResponseStart() +{ + return static_cast<int64_t>(ResponseStartHighRes()); +} + +DOMHighResTimeStamp +PerformanceTiming::ResponseEndHighRes() +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { + return mZeroTime; + } + if (mResponseEnd.IsNull() || + (!mCacheReadEnd.IsNull() && mCacheReadEnd < mResponseEnd)) { + mResponseEnd = mCacheReadEnd; + } + // Bug 1155008 - nsHttpTransaction is racy. Return ResponseStart when null + return mResponseEnd.IsNull() ? ResponseStartHighRes() + : TimeStampToDOMHighRes(mResponseEnd); +} + +DOMTimeMilliSec +PerformanceTiming::ResponseEnd() +{ + return static_cast<int64_t>(ResponseEndHighRes()); +} + +bool +PerformanceTiming::IsInitialized() const +{ + return mInitialized; +} + +JSObject* +PerformanceTiming::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) +{ + return PerformanceTimingBinding::Wrap(cx, this, aGivenProto); +} + +} // dom namespace +} // mozilla namespace |