michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #ifndef nsPerformance_h___ michael@0: #define nsPerformance_h___ michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsWrapperCache.h" michael@0: #include "nsDOMNavigationTiming.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "js/TypeDecls.h" michael@0: #include "mozilla/dom/BindingDeclarations.h" michael@0: michael@0: class nsITimedChannel; michael@0: class nsPerformance; michael@0: class nsIHttpChannel; michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: class PerformanceEntry; michael@0: } michael@0: } michael@0: michael@0: // Script "performance.timing" object michael@0: class nsPerformanceTiming MOZ_FINAL : public nsWrapperCache michael@0: { michael@0: public: michael@0: typedef mozilla::TimeStamp TimeStamp; michael@0: michael@0: /** michael@0: * @param aPerformance michael@0: * The performance object (the JS parent). michael@0: * This will allow access to "window.performance.timing" attribute for michael@0: * the navigation timing (can't be null). michael@0: * @param aChannel michael@0: * An nsITimedChannel used to gather all the networking timings by both michael@0: * the navigation timing and the resource timing (can't be null). michael@0: * @param aHttpChannel michael@0: * An nsIHttpChannel (the resource's http channel). michael@0: * This will be used by the resource timing cross-domain check michael@0: * algorithm. michael@0: * Argument is null for the navigation timing (navigation timing uses michael@0: * another algorithm for the cross-domain redirects). michael@0: * @param aZeroTime michael@0: * The offset that will be added to the timestamp of each event. This michael@0: * argument should be equal to performance.navigationStart for michael@0: * navigation timing and "0" for the resource timing. michael@0: */ michael@0: nsPerformanceTiming(nsPerformance* aPerformance, michael@0: nsITimedChannel* aChannel, michael@0: nsIHttpChannel* aHttpChannel, michael@0: DOMHighResTimeStamp aZeroTime); michael@0: NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsPerformanceTiming) michael@0: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(nsPerformanceTiming) michael@0: michael@0: nsDOMNavigationTiming* GetDOMTiming() const; michael@0: michael@0: nsPerformance* GetParentObject() const michael@0: { michael@0: return mPerformance; michael@0: } michael@0: michael@0: /** michael@0: * @param aStamp michael@0: * The TimeStamp recorded for a specific event. This TimeStamp can michael@0: * be null. michael@0: * @return the duration of an event with a given TimeStamp, relative to the michael@0: * navigationStart TimeStamp (the moment the user landed on the michael@0: * page), if the given TimeStamp is valid. Otherwise, it will return michael@0: * the FetchStart timing value. michael@0: */ michael@0: inline DOMHighResTimeStamp TimeStampToDOMHighResOrFetchStart(TimeStamp aStamp) michael@0: { michael@0: return (!aStamp.IsNull()) michael@0: ? TimeStampToDOMHighRes(aStamp) michael@0: : FetchStartHighRes(); michael@0: } michael@0: michael@0: /** michael@0: * The nsITimedChannel records an absolute timestamp for each event. michael@0: * The nsDOMNavigationTiming will record the moment when the user landed on michael@0: * the page. This is a window.performance unique timestamp, so it can be used michael@0: * for all the events (navigation timing and resource timing events). michael@0: * michael@0: * The algorithm operates in 2 steps: michael@0: * 1. The first step is to subtract the two timestamps: the argument (the michael@0: * envet's timesramp) and the navigation start timestamp. This will result in michael@0: * a relative timestamp of the event (relative to the navigation start - michael@0: * window.performance.timing.navigationStart). michael@0: * 2. The second step is to add any required offset (the mZeroTime). For now, michael@0: * this offset value is either 0 (for the resource timing), or equal to michael@0: * "performance.navigationStart" (for navigation timing). michael@0: * For the resource timing, mZeroTime is set to 0, causing the result to be a michael@0: * relative time. michael@0: * For the navigation timing, mZeroTime is set to "performance.navigationStart" michael@0: * causing the result be an absolute time. michael@0: * michael@0: * @param aStamp michael@0: * The TimeStamp recorded for a specific event. This TimeStamp can't michael@0: * be null. michael@0: * @return number of milliseconds value as one of: michael@0: * - relative to the navigation start time, time the user has landed on the michael@0: * page michael@0: * - an absolute wall clock time since the unix epoch michael@0: */ michael@0: inline DOMHighResTimeStamp TimeStampToDOMHighRes(TimeStamp aStamp) const michael@0: { michael@0: MOZ_ASSERT(!aStamp.IsNull()); michael@0: mozilla::TimeDuration duration = michael@0: aStamp - GetDOMTiming()->GetNavigationStartTimeStamp(); michael@0: return duration.ToMilliseconds() + mZeroTime; michael@0: } michael@0: michael@0: virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE; michael@0: michael@0: // PerformanceNavigation WebIDL methods michael@0: DOMTimeMilliSec NavigationStart() const { michael@0: if (!nsContentUtils::IsPerformanceTimingEnabled()) { michael@0: return 0; michael@0: } michael@0: return GetDOMTiming()->GetNavigationStart(); michael@0: } michael@0: DOMTimeMilliSec UnloadEventStart() { michael@0: if (!nsContentUtils::IsPerformanceTimingEnabled()) { michael@0: return 0; michael@0: } michael@0: return GetDOMTiming()->GetUnloadEventStart(); michael@0: } michael@0: DOMTimeMilliSec UnloadEventEnd() { michael@0: if (!nsContentUtils::IsPerformanceTimingEnabled()) { michael@0: return 0; michael@0: } michael@0: return GetDOMTiming()->GetUnloadEventEnd(); michael@0: } michael@0: michael@0: uint16_t GetRedirectCount() const; michael@0: bool IsSameOriginAsReferral() const; michael@0: void CheckRedirectCrossOrigin(nsIHttpChannel* aResourceChannel); michael@0: michael@0: // High resolution (used by resource timing) michael@0: DOMHighResTimeStamp FetchStartHighRes(); michael@0: DOMHighResTimeStamp RedirectStartHighRes(); michael@0: DOMHighResTimeStamp RedirectEndHighRes(); michael@0: DOMHighResTimeStamp DomainLookupStartHighRes(); michael@0: DOMHighResTimeStamp DomainLookupEndHighRes(); michael@0: DOMHighResTimeStamp ConnectStartHighRes(); michael@0: DOMHighResTimeStamp ConnectEndHighRes(); michael@0: DOMHighResTimeStamp RequestStartHighRes(); michael@0: DOMHighResTimeStamp ResponseStartHighRes(); michael@0: DOMHighResTimeStamp ResponseEndHighRes(); michael@0: michael@0: // Low resolution (used by navigation timing) michael@0: DOMTimeMilliSec FetchStart(); michael@0: DOMTimeMilliSec RedirectStart(); michael@0: DOMTimeMilliSec RedirectEnd(); michael@0: DOMTimeMilliSec DomainLookupStart(); michael@0: DOMTimeMilliSec DomainLookupEnd(); michael@0: DOMTimeMilliSec ConnectStart(); michael@0: DOMTimeMilliSec ConnectEnd(); michael@0: DOMTimeMilliSec RequestStart(); michael@0: DOMTimeMilliSec ResponseStart(); michael@0: DOMTimeMilliSec ResponseEnd(); michael@0: michael@0: DOMTimeMilliSec DomLoading() { michael@0: if (!nsContentUtils::IsPerformanceTimingEnabled()) { michael@0: return 0; michael@0: } michael@0: return GetDOMTiming()->GetDomLoading(); michael@0: } michael@0: DOMTimeMilliSec DomInteractive() const { michael@0: if (!nsContentUtils::IsPerformanceTimingEnabled()) { michael@0: return 0; michael@0: } michael@0: return GetDOMTiming()->GetDomInteractive(); michael@0: } michael@0: DOMTimeMilliSec DomContentLoadedEventStart() const { michael@0: if (!nsContentUtils::IsPerformanceTimingEnabled()) { michael@0: return 0; michael@0: } michael@0: return GetDOMTiming()->GetDomContentLoadedEventStart(); michael@0: } michael@0: DOMTimeMilliSec DomContentLoadedEventEnd() const { michael@0: if (!nsContentUtils::IsPerformanceTimingEnabled()) { michael@0: return 0; michael@0: } michael@0: return GetDOMTiming()->GetDomContentLoadedEventEnd(); michael@0: } michael@0: DOMTimeMilliSec DomComplete() const { michael@0: if (!nsContentUtils::IsPerformanceTimingEnabled()) { michael@0: return 0; michael@0: } michael@0: return GetDOMTiming()->GetDomComplete(); michael@0: } michael@0: DOMTimeMilliSec LoadEventStart() const { michael@0: if (!nsContentUtils::IsPerformanceTimingEnabled()) { michael@0: return 0; michael@0: } michael@0: return GetDOMTiming()->GetLoadEventStart(); michael@0: } michael@0: DOMTimeMilliSec LoadEventEnd() const { michael@0: if (!nsContentUtils::IsPerformanceTimingEnabled()) { michael@0: return 0; michael@0: } michael@0: return GetDOMTiming()->GetLoadEventEnd(); michael@0: } michael@0: michael@0: private: michael@0: ~nsPerformanceTiming(); michael@0: bool IsInitialized() const; michael@0: nsRefPtr mPerformance; michael@0: nsCOMPtr mChannel; michael@0: DOMHighResTimeStamp mFetchStart; michael@0: // This is an offset that will be added to each timing ([ms] resolution). michael@0: // There are only 2 possible values: (1) logicaly equal to navigationStart michael@0: // TimeStamp (results are absolute timstamps - wallclock); (2) "0" (results michael@0: // are relative to the navigation start). michael@0: DOMHighResTimeStamp mZeroTime; michael@0: bool mReportCrossOriginResources; michael@0: }; michael@0: michael@0: // Script "performance.navigation" object michael@0: class nsPerformanceNavigation MOZ_FINAL : public nsWrapperCache michael@0: { michael@0: public: michael@0: explicit nsPerformanceNavigation(nsPerformance* aPerformance); michael@0: NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsPerformanceNavigation) michael@0: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(nsPerformanceNavigation) michael@0: michael@0: nsDOMNavigationTiming* GetDOMTiming() const; michael@0: nsPerformanceTiming* GetPerformanceTiming() const; michael@0: michael@0: nsPerformance* GetParentObject() const michael@0: { michael@0: return mPerformance; michael@0: } michael@0: michael@0: virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE; michael@0: michael@0: // PerformanceNavigation WebIDL methods michael@0: uint16_t Type() const { michael@0: return GetDOMTiming()->GetType(); michael@0: } michael@0: uint16_t RedirectCount() const { michael@0: return GetPerformanceTiming()->GetRedirectCount(); michael@0: } michael@0: michael@0: private: michael@0: ~nsPerformanceNavigation(); michael@0: nsRefPtr mPerformance; michael@0: }; michael@0: michael@0: // Script "performance" object michael@0: class nsPerformance MOZ_FINAL : public nsISupports, michael@0: public nsWrapperCache michael@0: { michael@0: public: michael@0: typedef mozilla::dom::PerformanceEntry PerformanceEntry; michael@0: nsPerformance(nsIDOMWindow* aWindow, michael@0: nsDOMNavigationTiming* aDOMTiming, michael@0: nsITimedChannel* aChannel, michael@0: nsPerformance* aParentPerformance); michael@0: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsPerformance) michael@0: michael@0: nsDOMNavigationTiming* GetDOMTiming() const michael@0: { michael@0: return mDOMTiming; michael@0: } michael@0: michael@0: nsITimedChannel* GetChannel() const michael@0: { michael@0: return mChannel; michael@0: } michael@0: michael@0: nsPerformance* GetParentPerformance() const michael@0: { michael@0: return mParentPerformance; michael@0: } michael@0: michael@0: nsIDOMWindow* GetParentObject() const michael@0: { michael@0: return mWindow.get(); michael@0: } michael@0: michael@0: virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE; michael@0: michael@0: // Performance WebIDL methods michael@0: DOMHighResTimeStamp Now(); michael@0: nsPerformanceTiming* Timing(); michael@0: nsPerformanceNavigation* Navigation(); michael@0: michael@0: void GetEntries(nsTArray >& retval); michael@0: void GetEntriesByType(const nsAString& entryType, michael@0: nsTArray >& retval); michael@0: void GetEntriesByName(const nsAString& name, michael@0: const mozilla::dom::Optional< nsAString >& entryType, michael@0: nsTArray >& retval); michael@0: void AddEntry(nsIHttpChannel* channel, michael@0: nsITimedChannel* timedChannel); michael@0: void ClearResourceTimings(); michael@0: void SetResourceTimingBufferSize(uint64_t maxSize); michael@0: michael@0: private: michael@0: ~nsPerformance(); michael@0: michael@0: nsCOMPtr mWindow; michael@0: nsRefPtr mDOMTiming; michael@0: nsCOMPtr mChannel; michael@0: nsRefPtr mTiming; michael@0: nsRefPtr mNavigation; michael@0: nsTArray > mEntries; michael@0: nsRefPtr mParentPerformance; michael@0: uint64_t mBufferSizeSet; michael@0: uint64_t mPrimaryBufferSize; michael@0: michael@0: static const uint64_t kDefaultBufferSize = 150; michael@0: michael@0: // Helper classes michael@0: class PerformanceEntryComparator { michael@0: public: michael@0: bool Equals(const PerformanceEntry* aElem1, michael@0: const PerformanceEntry* aElem2) const; michael@0: bool LessThan(const PerformanceEntry* aElem1, michael@0: const PerformanceEntry* aElem2) const; michael@0: }; michael@0: }; michael@0: michael@0: inline nsDOMNavigationTiming* michael@0: nsPerformanceNavigation::GetDOMTiming() const michael@0: { michael@0: return mPerformance->GetDOMTiming(); michael@0: } michael@0: michael@0: inline nsPerformanceTiming* michael@0: nsPerformanceNavigation::GetPerformanceTiming() const michael@0: { michael@0: return mPerformance->Timing(); michael@0: } michael@0: michael@0: inline nsDOMNavigationTiming* michael@0: nsPerformanceTiming::GetDOMTiming() const michael@0: { michael@0: return mPerformance->GetDOMTiming(); michael@0: } michael@0: michael@0: #endif /* nsPerformance_h___ */ michael@0: