dom/base/nsPerformance.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsPerformance.h"
     7 #include "nsCOMPtr.h"
     8 #include "nsIHttpChannel.h"
     9 #include "nsITimedChannel.h"
    10 #include "nsDOMNavigationTiming.h"
    11 #include "nsContentUtils.h"
    12 #include "nsIScriptSecurityManager.h"
    13 #include "nsIDOMWindow.h"
    14 #include "nsIURI.h"
    15 #include "PerformanceEntry.h"
    16 #include "PerformanceResourceTiming.h"
    17 #include "mozilla/dom/PerformanceBinding.h"
    18 #include "mozilla/dom/PerformanceTimingBinding.h"
    19 #include "mozilla/dom/PerformanceNavigationBinding.h"
    20 #include "mozilla/TimeStamp.h"
    21 #include "nsThreadUtils.h"
    23 using namespace mozilla;
    25 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsPerformanceTiming, mPerformance)
    27 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsPerformanceTiming, AddRef)
    28 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsPerformanceTiming, Release)
    30 nsPerformanceTiming::nsPerformanceTiming(nsPerformance* aPerformance,
    31                                          nsITimedChannel* aChannel,
    32                                          nsIHttpChannel* aHttpChannel,
    33                                          DOMHighResTimeStamp aZeroTime)
    34   : mPerformance(aPerformance),
    35     mChannel(aChannel),
    36     mFetchStart(0.0),
    37     mZeroTime(aZeroTime),
    38     mReportCrossOriginResources(true)
    39 {
    40   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
    41   SetIsDOMBinding();
    43   if (!nsContentUtils::IsPerformanceTimingEnabled()) {
    44     mZeroTime = 0;
    45   }
    47  // The aHttpChannel argument is null if this nsPerformanceTiming object
    48   // is being used for the navigation timing (document) and has a non-null
    49   // value for the resource timing (any resources within the page).
    50   if (aHttpChannel) {
    51     CheckRedirectCrossOrigin(aHttpChannel);
    52   }
    53 }
    55 nsPerformanceTiming::~nsPerformanceTiming()
    56 {
    57 }
    59 DOMHighResTimeStamp
    60 nsPerformanceTiming::FetchStartHighRes()
    61 {
    62   if (!mFetchStart) {
    63     if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
    64       return mZeroTime;
    65     }
    66     TimeStamp stamp;
    67     mChannel->GetAsyncOpen(&stamp);
    68     MOZ_ASSERT(!stamp.IsNull(), "The fetch start time stamp should always be "
    69         "valid if the performance timing is enabled");
    70     mFetchStart = (!stamp.IsNull())
    71         ? TimeStampToDOMHighRes(stamp)
    72         : 0.0;
    73   }
    74   return mFetchStart;
    75 }
    77 DOMTimeMilliSec
    78 nsPerformanceTiming::FetchStart()
    79 {
    80   return static_cast<int64_t>(FetchStartHighRes());
    81 }
    83 // This method will implement the timing allow check algorithm
    84 // http://w3c-test.org/webperf/specs/ResourceTiming/#timing-allow-check
    85 // https://bugzilla.mozilla.org/show_bug.cgi?id=936814
    86 void
    87 nsPerformanceTiming::CheckRedirectCrossOrigin(nsIHttpChannel* aResourceChannel)
    88 {
    89   if (!IsInitialized()) {
    90     return;
    91   }
    92   uint16_t redirectCount;
    93   mChannel->GetRedirectCount(&redirectCount);
    94   if (redirectCount == 0) {
    95     return;
    96   }
    97   nsCOMPtr<nsIURI> resourceURI, referrerURI;
    98   aResourceChannel->GetReferrer(getter_AddRefs(referrerURI));
    99   aResourceChannel->GetURI(getter_AddRefs(resourceURI));
   100   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   101   nsresult rv = ssm->CheckSameOriginURI(resourceURI, referrerURI, false);
   102   if (!NS_SUCCEEDED(rv)) {
   103     mReportCrossOriginResources = false;
   104   }
   105 }
   107 bool
   108 nsPerformanceTiming::IsSameOriginAsReferral() const
   109 {
   110   return mReportCrossOriginResources;
   111 }
   113 uint16_t
   114 nsPerformanceTiming::GetRedirectCount() const
   115 {
   116   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
   117     return 0;
   118   }
   119   bool sameOrigin;
   120   mChannel->GetAllRedirectsSameOrigin(&sameOrigin);
   121   if (!sameOrigin) {
   122     return 0;
   123   }
   124   uint16_t redirectCount;
   125   mChannel->GetRedirectCount(&redirectCount);
   126   return redirectCount;
   127 }
   129 /**
   130  * RedirectStartHighRes() is used by both the navigation timing and the
   131  * resource timing. Since, navigation timing and resource timing check and
   132  * interpret cross-domain redirects in a different manner,
   133  * RedirectStartHighRes() will make no checks for cross-domain redirect.
   134  * It's up to the consumers of this method (nsPerformanceTiming::RedirectStart()
   135  * and PerformanceResourceTiming::RedirectStart() to make such verifications.
   136  *
   137  * @return a valid timing if the Performance Timing is enabled
   138  */
   139 DOMHighResTimeStamp
   140 nsPerformanceTiming::RedirectStartHighRes()
   141 {
   142   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
   143     return mZeroTime;
   144   }
   145   mozilla::TimeStamp stamp;
   146   mChannel->GetRedirectStart(&stamp);
   147   return TimeStampToDOMHighResOrFetchStart(stamp);
   148 }
   150 DOMTimeMilliSec
   151 nsPerformanceTiming::RedirectStart()
   152 {
   153   if (!IsInitialized()) {
   154     return mZeroTime;
   155   }
   156   // We have to check if all the redirect URIs had the same origin (since there
   157   // is no check in RedirectStartHighRes())
   158   bool sameOrigin;
   159   mChannel->GetAllRedirectsSameOrigin(&sameOrigin);
   160   if (sameOrigin) {
   161     return static_cast<int64_t>(RedirectStartHighRes());
   162   }
   163   return 0;
   164 }
   166 /**
   167  * RedirectEndHighRes() is used by both the navigation timing and the resource
   168  * timing. Since, navigation timing and resource timing check and interpret
   169  * cross-domain redirects in a different manner, RedirectEndHighRes() will make
   170  * no checks for cross-domain redirect. It's up to the consumers of this method
   171  * (nsPerformanceTiming::RedirectEnd() and
   172  * PerformanceResourceTiming::RedirectEnd() to make such verifications.
   173  *
   174  * @return a valid timing if the Performance Timing is enabled
   175  */
   176 DOMHighResTimeStamp
   177 nsPerformanceTiming::RedirectEndHighRes()
   178 {
   179   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
   180     return mZeroTime;
   181   }
   182   mozilla::TimeStamp stamp;
   183   mChannel->GetRedirectEnd(&stamp);
   184   return TimeStampToDOMHighResOrFetchStart(stamp);
   185 }
   187 DOMTimeMilliSec
   188 nsPerformanceTiming::RedirectEnd()
   189 {
   190   if (!IsInitialized()) {
   191     return mZeroTime;
   192   }
   193   // We have to check if all the redirect URIs had the same origin (since there
   194   // is no check in RedirectEndHighRes())
   195   bool sameOrigin;
   196   mChannel->GetAllRedirectsSameOrigin(&sameOrigin);
   197   if (sameOrigin) {
   198     return static_cast<int64_t>(RedirectEndHighRes());
   199   }
   200   return 0;
   201 }
   203 DOMHighResTimeStamp
   204 nsPerformanceTiming::DomainLookupStartHighRes()
   205 {
   206   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
   207     return mZeroTime;
   208   }
   209   mozilla::TimeStamp stamp;
   210   mChannel->GetDomainLookupStart(&stamp);
   211   return TimeStampToDOMHighResOrFetchStart(stamp);
   212 }
   214 DOMTimeMilliSec
   215 nsPerformanceTiming::DomainLookupStart()
   216 {
   217   return static_cast<int64_t>(DomainLookupStartHighRes());
   218 }
   220 DOMHighResTimeStamp
   221 nsPerformanceTiming::DomainLookupEndHighRes()
   222 {
   223   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
   224     return mZeroTime;
   225   }
   226   mozilla::TimeStamp stamp;
   227   mChannel->GetDomainLookupEnd(&stamp);
   228   return TimeStampToDOMHighResOrFetchStart(stamp);
   229 }
   231 DOMTimeMilliSec
   232 nsPerformanceTiming::DomainLookupEnd()
   233 {
   234   return static_cast<int64_t>(DomainLookupEndHighRes());
   235 }
   237 DOMHighResTimeStamp
   238 nsPerformanceTiming::ConnectStartHighRes()
   239 {
   240   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
   241     return mZeroTime;
   242   }
   243   mozilla::TimeStamp stamp;
   244   mChannel->GetConnectStart(&stamp);
   245   return TimeStampToDOMHighResOrFetchStart(stamp);
   246 }
   248 DOMTimeMilliSec
   249 nsPerformanceTiming::ConnectStart()
   250 {
   251   return static_cast<int64_t>(ConnectStartHighRes());
   252 }
   254 DOMHighResTimeStamp
   255 nsPerformanceTiming::ConnectEndHighRes()
   256 {
   257   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
   258     return mZeroTime;
   259   }
   260   mozilla::TimeStamp stamp;
   261   mChannel->GetConnectEnd(&stamp);
   262   return TimeStampToDOMHighResOrFetchStart(stamp);
   263 }
   265 DOMTimeMilliSec
   266 nsPerformanceTiming::ConnectEnd()
   267 {
   268   return static_cast<int64_t>(ConnectEndHighRes());
   269 }
   271 DOMHighResTimeStamp
   272 nsPerformanceTiming::RequestStartHighRes()
   273 {
   274   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
   275     return mZeroTime;
   276   }
   277   mozilla::TimeStamp stamp;
   278   mChannel->GetRequestStart(&stamp);
   279   return TimeStampToDOMHighResOrFetchStart(stamp);
   280 }
   282 DOMTimeMilliSec
   283 nsPerformanceTiming::RequestStart()
   284 {
   285   return static_cast<int64_t>(RequestStartHighRes());
   286 }
   288 DOMHighResTimeStamp
   289 nsPerformanceTiming::ResponseStartHighRes()
   290 {
   291   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
   292     return mZeroTime;
   293   }
   294   mozilla::TimeStamp stamp;
   295   mChannel->GetResponseStart(&stamp);
   296   mozilla::TimeStamp cacheStamp;
   297   mChannel->GetCacheReadStart(&cacheStamp);
   298   if (stamp.IsNull() || (!cacheStamp.IsNull() && cacheStamp < stamp)) {
   299     stamp = cacheStamp;
   300   }
   301   return TimeStampToDOMHighResOrFetchStart(stamp);
   302 }
   304 DOMTimeMilliSec
   305 nsPerformanceTiming::ResponseStart()
   306 {
   307   return static_cast<int64_t>(ResponseStartHighRes());
   308 }
   310 DOMHighResTimeStamp
   311 nsPerformanceTiming::ResponseEndHighRes()
   312 {
   313   if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
   314     return mZeroTime;
   315   }
   316   mozilla::TimeStamp stamp;
   317   mChannel->GetResponseEnd(&stamp);
   318   mozilla::TimeStamp cacheStamp;
   319   mChannel->GetCacheReadEnd(&cacheStamp);
   320   if (stamp.IsNull() || (!cacheStamp.IsNull() && cacheStamp < stamp)) {
   321     stamp = cacheStamp;
   322   }
   323   return TimeStampToDOMHighResOrFetchStart(stamp);
   324 }
   326 DOMTimeMilliSec
   327 nsPerformanceTiming::ResponseEnd()
   328 {
   329   return static_cast<int64_t>(ResponseEndHighRes());
   330 }
   332 bool
   333 nsPerformanceTiming::IsInitialized() const
   334 {
   335   return !!mChannel;
   336 }
   338 JSObject*
   339 nsPerformanceTiming::WrapObject(JSContext *cx)
   340 {
   341   return dom::PerformanceTimingBinding::Wrap(cx, this);
   342 }
   345 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsPerformanceNavigation, mPerformance)
   347 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsPerformanceNavigation, AddRef)
   348 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsPerformanceNavigation, Release)
   350 nsPerformanceNavigation::nsPerformanceNavigation(nsPerformance* aPerformance)
   351   : mPerformance(aPerformance)
   352 {
   353   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
   354   SetIsDOMBinding();
   355 }
   357 nsPerformanceNavigation::~nsPerformanceNavigation()
   358 {
   359 }
   361 JSObject*
   362 nsPerformanceNavigation::WrapObject(JSContext *cx)
   363 {
   364   return dom::PerformanceNavigationBinding::Wrap(cx, this);
   365 }
   368 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_5(nsPerformance,
   369                                         mWindow, mTiming,
   370                                         mNavigation, mEntries,
   371                                         mParentPerformance)
   372 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPerformance)
   373 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPerformance)
   375 nsPerformance::nsPerformance(nsIDOMWindow* aWindow,
   376                              nsDOMNavigationTiming* aDOMTiming,
   377                              nsITimedChannel* aChannel,
   378                              nsPerformance* aParentPerformance)
   379   : mWindow(aWindow),
   380     mDOMTiming(aDOMTiming),
   381     mChannel(aChannel),
   382     mParentPerformance(aParentPerformance),
   383     mBufferSizeSet(kDefaultBufferSize),
   384     mPrimaryBufferSize(kDefaultBufferSize)
   385 {
   386   MOZ_ASSERT(aWindow, "Parent window object should be provided");
   387   SetIsDOMBinding();
   388 }
   390 nsPerformance::~nsPerformance()
   391 {
   392 }
   394 // QueryInterface implementation for nsPerformance
   395 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPerformance)
   396   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   397   NS_INTERFACE_MAP_ENTRY(nsISupports)
   398 NS_INTERFACE_MAP_END
   401 nsPerformanceTiming*
   402 nsPerformance::Timing()
   403 {
   404   if (!mTiming) {
   405     // For navigation timing, the third argument (an nsIHtttpChannel) is null
   406     // since the cross-domain redirect were already checked.
   407     // The last argument (zero time) for performance.timing is the navigation
   408     // start value.
   409     mTiming = new nsPerformanceTiming(this, mChannel, nullptr,
   410         mDOMTiming->GetNavigationStart());
   411   }
   412   return mTiming;
   413 }
   415 nsPerformanceNavigation*
   416 nsPerformance::Navigation()
   417 {
   418   if (!mNavigation) {
   419     mNavigation = new nsPerformanceNavigation(this);
   420   }
   421   return mNavigation;
   422 }
   424 DOMHighResTimeStamp
   425 nsPerformance::Now()
   426 {
   427   return GetDOMTiming()->TimeStampToDOMHighRes(mozilla::TimeStamp::Now());
   428 }
   430 JSObject*
   431 nsPerformance::WrapObject(JSContext *cx)
   432 {
   433   return dom::PerformanceBinding::Wrap(cx, this);
   434 }
   436 void
   437 nsPerformance::GetEntries(nsTArray<nsRefPtr<PerformanceEntry> >& retval)
   438 {
   439   MOZ_ASSERT(NS_IsMainThread());
   441   retval.Clear();
   442   uint32_t count = mEntries.Length();
   443   if (count > mPrimaryBufferSize) {
   444     count = mPrimaryBufferSize;
   445   }
   446   retval.AppendElements(mEntries.Elements(), count);
   447 }
   449 void
   450 nsPerformance::GetEntriesByType(const nsAString& entryType,
   451                                 nsTArray<nsRefPtr<PerformanceEntry> >& retval)
   452 {
   453   MOZ_ASSERT(NS_IsMainThread());
   455   retval.Clear();
   456   uint32_t count = mEntries.Length();
   457   for (uint32_t i = 0 ; i < count && i < mPrimaryBufferSize ; i++) {
   458     if (mEntries[i]->GetEntryType().Equals(entryType)) {
   459       retval.AppendElement(mEntries[i]);
   460     }
   461   }
   462 }
   464 void
   465 nsPerformance::GetEntriesByName(const nsAString& name,
   466                                 const mozilla::dom::Optional<nsAString>& entryType,
   467                                 nsTArray<nsRefPtr<PerformanceEntry> >& retval)
   468 {
   469   MOZ_ASSERT(NS_IsMainThread());
   471   retval.Clear();
   472   uint32_t count = mEntries.Length();
   473   for (uint32_t i = 0 ; i < count && i < mPrimaryBufferSize ; i++) {
   474     if (mEntries[i]->GetName().Equals(name) &&
   475         (!entryType.WasPassed() ||
   476          mEntries[i]->GetEntryType().Equals(entryType.Value()))) {
   477       retval.AppendElement(mEntries[i]);
   478     }
   479   }
   480 }
   482 void
   483 nsPerformance::ClearResourceTimings()
   484 {
   485   MOZ_ASSERT(NS_IsMainThread());
   486   mPrimaryBufferSize = mBufferSizeSet;
   487   mEntries.Clear();
   488 }
   490 void
   491 nsPerformance::SetResourceTimingBufferSize(uint64_t maxSize)
   492 {
   493   MOZ_ASSERT(NS_IsMainThread());
   494   mBufferSizeSet = maxSize;
   495   if (mBufferSizeSet < mEntries.Length()) {
   496     // call onresourcetimingbufferfull
   497     // https://bugzilla.mozilla.org/show_bug.cgi?id=936813
   498   }
   499 }
   501 /**
   502  * An entry should be added only after the resource is loaded.
   503  * This method is not thread safe and can only be called on the main thread.
   504  */
   505 void
   506 nsPerformance::AddEntry(nsIHttpChannel* channel,
   507                         nsITimedChannel* timedChannel)
   508 {
   509   MOZ_ASSERT(NS_IsMainThread());
   510   // Check if resource timing is prefed off.
   511   if (!nsContentUtils::IsResourceTimingEnabled()) {
   512     return;
   513   }
   514   if (channel && timedChannel) {
   515     nsAutoCString name;
   516     nsAutoString initiatorType;
   517     nsCOMPtr<nsIURI> originalURI;
   519     timedChannel->GetInitiatorType(initiatorType);
   521     // According to the spec, "The name attribute must return the resolved URL
   522     // of the requested resource. This attribute must not change even if the
   523     // fetch redirected to a different URL."
   524     channel->GetOriginalURI(getter_AddRefs(originalURI));
   525     originalURI->GetSpec(name);
   526     NS_ConvertUTF8toUTF16 entryName(name);
   528     // The nsITimedChannel argument will be used to gather all the timings.
   529     // The nsIHttpChannel argument will be used to check if any cross-origin
   530     // redirects occurred.
   531     // The last argument is the "zero time" (offset). Since we don't want
   532     // any offset for the resource timing, this will be set to "0" - the
   533     // resource timing returns a relative timing (no offset).
   534     nsRefPtr<nsPerformanceTiming> performanceTiming =
   535         new nsPerformanceTiming(this, timedChannel, channel,
   536             0);
   538     // The PerformanceResourceTiming object will use the nsPerformanceTiming
   539     // object to get all the required timings.
   540     nsRefPtr<dom::PerformanceResourceTiming> performanceEntry =
   541         new dom::PerformanceResourceTiming(performanceTiming, this);
   543     performanceEntry->SetName(entryName);
   544     performanceEntry->SetEntryType(NS_LITERAL_STRING("resource"));
   545     // If the initiator type had no valid value, then set it to the default
   546     // ("other") value.
   547     if (initiatorType.IsEmpty()) {
   548       initiatorType = NS_LITERAL_STRING("other");
   549     }
   550     performanceEntry->SetInitiatorType(initiatorType);
   552     mEntries.InsertElementSorted(performanceEntry,
   553         PerformanceEntryComparator());
   554     if (mEntries.Length() > mPrimaryBufferSize) {
   555       // call onresourcetimingbufferfull
   556       // https://bugzilla.mozilla.org/show_bug.cgi?id=936813
   557     }
   558   }
   559 }
   561 bool
   562 nsPerformance::PerformanceEntryComparator::Equals(
   563     const PerformanceEntry* aElem1,
   564     const PerformanceEntry* aElem2) const
   565 {
   566   NS_ABORT_IF_FALSE(aElem1 && aElem2,
   567       "Trying to compare null performance entries");
   568   return aElem1->StartTime() == aElem2->StartTime();
   569 }
   571 bool
   572 nsPerformance::PerformanceEntryComparator::LessThan(
   573     const PerformanceEntry* aElem1,
   574     const PerformanceEntry* aElem2) const
   575 {
   576   NS_ABORT_IF_FALSE(aElem1 && aElem2,
   577       "Trying to compare null performance entries");
   578   return aElem1->StartTime() < aElem2->StartTime();
   579 }

mercurial