Bug 822480 - Add in the Resource Timing API. r=honzab, r=jst

This commit is contained in:
Adrian Lungu 2013-10-15 18:35:44 -07:00
parent bfd9a489d3
commit 864ffca9e7
47 changed files with 1581 additions and 209 deletions

View File

@ -654,6 +654,7 @@ public:
nsIURI* aReferrer,
imgINotificationObserver* aObserver,
int32_t aLoadFlags,
const nsAString& initiatorType,
imgRequestProxy** aRequest);
/**
@ -1847,6 +1848,14 @@ public:
return sIsPerformanceTimingEnabled;
}
/*
* Returns true if the performance timing APIs are enabled.
*/
static bool IsResourceTimingEnabled()
{
return sIsResourceTimingEnabled;
}
/**
* Returns true if the doc tree branch which contains aDoc contains any
* plugins which we don't control event dispatch for, i.e. do any plugins
@ -2221,6 +2230,7 @@ private:
static uint32_t sHandlingInputTimeout;
static bool sIsIdleObserverAPIEnabled;
static bool sIsPerformanceTimingEnabled;
static bool sIsResourceTimingEnabled;
static nsHtml5StringParser* sHTMLFragmentParser;
static nsIParser* sXMLFragmentParser;

View File

@ -238,6 +238,7 @@ bool nsContentUtils::sTrustedFullScreenOnly = true;
bool nsContentUtils::sFullscreenApiIsContentOnly = false;
bool nsContentUtils::sIsIdleObserverAPIEnabled = false;
bool nsContentUtils::sIsPerformanceTimingEnabled = false;
bool nsContentUtils::sIsResourceTimingEnabled = false;
uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
@ -439,6 +440,9 @@ nsContentUtils::Init()
Preferences::AddBoolVarCache(&sIsPerformanceTimingEnabled,
"dom.enable_performance", true);
Preferences::AddBoolVarCache(&sIsResourceTimingEnabled,
"dom.enable_resource_timing", true);
Preferences::AddUintVarCache(&sHandlingInputTimeout,
"dom.event.handling-user-input-time-limit",
1000);
@ -2711,6 +2715,7 @@ nsresult
nsContentUtils::LoadImage(nsIURI* aURI, nsIDocument* aLoadingDocument,
nsIPrincipal* aLoadingPrincipal, nsIURI* aReferrer,
imgINotificationObserver* aObserver, int32_t aLoadFlags,
const nsAString& initiatorType,
imgRequestProxy** aRequest)
{
NS_PRECONDITION(aURI, "Must have a URI");
@ -2760,6 +2765,7 @@ nsContentUtils::LoadImage(nsIURI* aURI, nsIDocument* aLoadingDocument,
aLoadFlags, /* load flags */
nullptr, /* cache key */
channelPolicy, /* CSP info */
initiatorType, /* the load initiator */
aRequest);
}

View File

@ -9274,6 +9274,7 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr)
mDocumentURI, // uri of document used as referrer
nullptr, // no observer
loadFlags,
NS_LITERAL_STRING("img"),
getter_AddRefs(request));
// Pin image-reference to avoid evicting it from the img-cache before

View File

@ -822,12 +822,16 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
// Not blocked. Do the load.
nsRefPtr<imgRequestProxy>& req = PrepareNextRequest();
nsCOMPtr<nsIContent> content =
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
nsresult rv;
rv = nsContentUtils::LoadImage(aNewURI, aDocument,
aDocument->NodePrincipal(),
aDocument->GetDocumentURI(),
this, loadFlags,
content->LocalName(),
getter_AddRefs(req));
if (NS_SUCCEEDED(rv)) {
TrackImage(req);
ResetAnimationIfNeeded();

View File

@ -2323,6 +2323,12 @@ nsObjectLoadingContent::OpenChannel()
nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
if (httpChan) {
httpChan->SetReferrer(doc->GetDocumentURI());
// Set the initiator type
nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChan));
if (timedChannel) {
timedChannel->SetInitiatorType(thisContent->LocalName());
}
}
// Set up the channel's principal and such, like nsDocShell::DoURILoad does.

View File

@ -28,6 +28,7 @@
#include "nsContentPolicyUtils.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsITimedChannel.h"
#include "nsIScriptElement.h"
#include "nsIDOMHTMLScriptElement.h"
#include "nsIDocShell.h"
@ -338,6 +339,12 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
mozilla::net::SeerLearn(aRequest->mURI, mDocument->GetDocumentURI(),
nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, loadContext);
// Set the initiator type
nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
if (timedChannel) {
timedChannel->SetInitiatorType(NS_LITERAL_STRING("script"));
}
nsCOMPtr<nsIStreamLoader> loader;
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -68,6 +68,7 @@
#include "nsFormData.h"
#include "nsStreamListenerWrapper.h"
#include "xpcjsid.h"
#include "nsITimedChannel.h"
#include "nsWrapperCacheInlines.h"
@ -1703,6 +1704,12 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
if (httpChannel) {
rv = httpChannel->SetRequestMethod(method);
NS_ENSURE_SUCCESS(rv, rv);
// Set the initiator type
nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
if (timedChannel) {
timedChannel->SetInitiatorType(NS_LITERAL_STRING("xmlhttprequest"));
}
}
ChangeState(XML_HTTP_REQUEST_OPENED);

View File

@ -6818,14 +6818,6 @@ nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
}
}
// On session restore we get a redirect from page to itself. Don't count it.
bool equals = false;
if (mTiming &&
!(mLoadType == LOAD_HISTORY &&
NS_SUCCEEDED(newURI->Equals(oldURI, &equals)) && equals)) {
mTiming->NotifyRedirect(oldURI, newURI);
}
// Below a URI visit is saved (see AddURIVisit method doc).
// The visit chain looks something like:
// ...
@ -10033,6 +10025,9 @@ nsDocShell::DoURILoad(nsIURI * aURI,
nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(channel));
if (timedChannel) {
timedChannel->SetTimingEnabled(true);
if (IsFrame()) {
timedChannel->SetInitiatorType(NS_LITERAL_STRING("subdocument"));
}
}
rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);

View File

@ -0,0 +1,37 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "PerformanceEntry.h"
#include "nsIURI.h"
#include "mozilla/dom/PerformanceEntryBinding.h"
using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(PerformanceEntry, mPerformance)
NS_IMPL_CYCLE_COLLECTING_ADDREF(PerformanceEntry)
NS_IMPL_CYCLE_COLLECTING_RELEASE(PerformanceEntry)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceEntry)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
PerformanceEntry::PerformanceEntry(nsPerformance* aPerformance)
: mPerformance(aPerformance)
{
MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
SetIsDOMBinding();
}
PerformanceEntry::~PerformanceEntry()
{
}
JSObject*
PerformanceEntry::WrapObject(JSContext* aCx)
{
return mozilla::dom::PerformanceEntryBinding::Wrap(aCx, this);
}

View File

@ -0,0 +1,82 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef mozilla_dom_PerformanceEntry_h___
#define mozilla_dom_PerformanceEntry_h___
#include "nsPerformance.h"
#include "nsDOMNavigationTiming.h"
namespace mozilla {
namespace dom {
// http://www.w3.org/TR/performance-timeline/#performanceentry
class PerformanceEntry : public nsISupports,
public nsWrapperCache
{
public:
PerformanceEntry(nsPerformance* aPerformance);
virtual ~PerformanceEntry();
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PerformanceEntry)
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
nsPerformance* GetParentObject() const
{
return mPerformance;
}
void GetName(nsAString& aName) const
{
aName = mName;
}
const nsAString& GetName() const
{
return mName;
}
void SetName(const nsAString& aName)
{
mName = aName;
}
void GetEntryType(nsAString& aEntryType) const
{
aEntryType = mEntryType;
}
const nsAString& GetEntryType()
{
return mEntryType;
}
void SetEntryType(const nsAString& aEntryType)
{
mEntryType = aEntryType;
}
virtual DOMHighResTimeStamp StartTime() const
{
return 0;
}
virtual DOMHighResTimeStamp Duration() const
{
return 0;
}
protected:
nsRefPtr<nsPerformance> mPerformance;
nsString mName;
nsString mEntryType;
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_PerformanceEntry_h___ */

View File

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "PerformanceResourceTiming.h"
#include "mozilla/dom/PerformanceResourceTimingBinding.h"
using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTION_INHERITED_1(PerformanceResourceTiming,
PerformanceEntry,
mTiming)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceResourceTiming,
PerformanceEntry)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PerformanceResourceTiming)
NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry)
NS_IMPL_ADDREF_INHERITED(PerformanceResourceTiming, PerformanceEntry)
NS_IMPL_RELEASE_INHERITED(PerformanceResourceTiming, PerformanceEntry)
PerformanceResourceTiming::PerformanceResourceTiming(nsPerformanceTiming* aPerformanceTiming,
nsPerformance* aPerformance)
: PerformanceEntry(aPerformance),
mTiming(aPerformanceTiming)
{
MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
}
PerformanceResourceTiming::~PerformanceResourceTiming()
{
}
DOMHighResTimeStamp
PerformanceResourceTiming::StartTime() const
{
DOMHighResTimeStamp startTime = mTiming->RedirectStartHighRes();
return startTime ? startTime : mTiming->FetchStartHighRes();
}
JSObject*
PerformanceResourceTiming::WrapObject(JSContext* aCx)
{
return PerformanceResourceTimingBinding::Wrap(aCx, this);
}

View File

@ -0,0 +1,133 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef mozilla_dom_PerformanceResourceTiming_h___
#define mozilla_dom_PerformanceResourceTiming_h___
#include "nsCOMPtr.h"
#include "nsPerformance.h"
#include "nsIChannel.h"
#include "nsITimedChannel.h"
#include "nsDOMNavigationTiming.h"
#include "PerformanceEntry.h"
namespace mozilla {
namespace dom {
// http://www.w3.org/TR/resource-timing/#performanceresourcetiming
class PerformanceResourceTiming MOZ_FINAL : public PerformanceEntry
{
public:
typedef mozilla::TimeStamp TimeStamp;
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
PerformanceResourceTiming,
PerformanceEntry)
PerformanceResourceTiming(nsPerformanceTiming* aPerformanceTiming,
nsPerformance* aPerformance);
virtual ~PerformanceResourceTiming();
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
virtual DOMHighResTimeStamp StartTime() const;
virtual DOMHighResTimeStamp Duration() const
{
return ResponseEnd() - StartTime();
}
void GetInitiatorType(nsAString& aInitiatorType) const
{
aInitiatorType = mInitiatorType;
}
void SetInitiatorType(const nsAString& aInitiatorType)
{
mInitiatorType = aInitiatorType;
}
DOMHighResTimeStamp FetchStart() const {
return mTiming
? mTiming->FetchStartHighRes()
: 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->IsSameOriginAsReferral()
? mTiming->RedirectStartHighRes()
: 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->IsSameOriginAsReferral()
? mTiming->RedirectEndHighRes()
: 0;
}
DOMHighResTimeStamp DomainLookupStart() const {
return mTiming && mTiming->IsSameOriginAsReferral()
? mTiming->DomainLookupStartHighRes()
: 0;
}
DOMHighResTimeStamp DomainLookupEnd() const {
return mTiming && mTiming->IsSameOriginAsReferral()
? mTiming->DomainLookupEndHighRes()
: 0;
}
DOMHighResTimeStamp ConnectStart() const {
return mTiming && mTiming->IsSameOriginAsReferral()
? mTiming->ConnectStartHighRes()
: 0;
}
DOMHighResTimeStamp ConnectEnd() const {
return mTiming && mTiming->IsSameOriginAsReferral()
? mTiming->ConnectEndHighRes()
: 0;
}
DOMHighResTimeStamp RequestStart() const {
return mTiming && mTiming->IsSameOriginAsReferral()
? mTiming->RequestStartHighRes()
: 0;
}
DOMHighResTimeStamp ResponseStart() const {
return mTiming && mTiming->IsSameOriginAsReferral()
? mTiming->ResponseStartHighRes()
: 0;
}
DOMHighResTimeStamp ResponseEnd() const {
return mTiming
? mTiming->ResponseEndHighRes()
: 0;
}
DOMHighResTimeStamp SecureConnectionStart() const
{
// This measurement is not available for Navigation Timing either.
// There is a different bug submitted for it.
return 0;
}
protected:
nsString mInitiatorType;
nsRefPtr<nsPerformanceTiming> mTiming;
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_PerformanceResourceTiming_h___ */

View File

@ -59,6 +59,8 @@ EXPORTS.mozilla.dom += [
'MessagePort.h',
'MessagePortList.h',
'Navigator.h',
'PerformanceEntry.h',
'PerformanceResourceTiming.h',
'ScreenOrientation.h',
'ScriptSettings.h',
'StructuredCloneTags.h',
@ -99,6 +101,8 @@ UNIFIED_SOURCES += [
'nsWindowMemoryReporter.cpp',
'nsWindowRoot.cpp',
'nsWrapperCache.cpp',
'PerformanceEntry.cpp',
'PerformanceResourceTiming.cpp',
'ScriptSettings.cpp',
'URL.cpp',
'URLSearchParams.cpp',

View File

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsDOMNavigationTiming.h"
#include "nsPerformance.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsIScriptSecurityManager.h"
@ -24,11 +25,7 @@ void
nsDOMNavigationTiming::Clear()
{
mNavigationType = mozilla::dom::PerformanceNavigation::TYPE_RESERVED;
mNavigationStart = 0;
mFetchStart = 0;
mRedirectStart = 0;
mRedirectEnd = 0;
mRedirectCount = 0;
mNavigationStartHighRes = 0;
mBeforeUnloadStart = 0;
mUnloadStart = 0;
mUnloadEnd = 0;
@ -39,7 +36,6 @@ nsDOMNavigationTiming::Clear()
mDOMContentLoadedEventStart = 0;
mDOMContentLoadedEventEnd = 0;
mDOMComplete = 0;
mRedirectCheck = NOT_CHECKED;
mLoadEventStartSet = false;
mLoadEventEndSet = false;
@ -57,17 +53,7 @@ nsDOMNavigationTiming::TimeStampToDOM(mozilla::TimeStamp aStamp) const
return 0;
}
mozilla::TimeDuration duration = aStamp - mNavigationStartTimeStamp;
return mNavigationStart + static_cast<int32_t>(duration.ToMilliseconds());
}
DOMTimeMilliSec
nsDOMNavigationTiming::TimeStampToDOMOrFetchStart(mozilla::TimeStamp aStamp) const
{
if (!aStamp.IsNull()) {
return TimeStampToDOM(aStamp);
} else {
return GetFetchStart();
}
return GetNavigationStart() + static_cast<int64_t>(duration.ToMilliseconds());
}
DOMTimeMilliSec nsDOMNavigationTiming::DurationFromStart(){
@ -77,36 +63,19 @@ DOMTimeMilliSec nsDOMNavigationTiming::DurationFromStart(){
void
nsDOMNavigationTiming::NotifyNavigationStart()
{
mNavigationStart = PR_Now() / PR_USEC_PER_MSEC;
mNavigationStartHighRes = (double)PR_Now() / PR_USEC_PER_MSEC;
mNavigationStartTimeStamp = mozilla::TimeStamp::Now();
}
void
nsDOMNavigationTiming::NotifyFetchStart(nsIURI* aURI, nsDOMPerformanceNavigationType aNavigationType)
{
mFetchStart = DurationFromStart();
mNavigationType = aNavigationType;
// At the unload event time we don't really know the loading uri.
// Need it for later check for unload timing access.
mLoadedURI = aURI;
}
void
nsDOMNavigationTiming::NotifyRedirect(nsIURI* aOldURI, nsIURI* aNewURI)
{
if (mRedirects.Count() == 0) {
mRedirectStart = mFetchStart;
}
mFetchStart = DurationFromStart();
mRedirectEnd = mFetchStart;
// At the unload event time we don't really know the loading uri.
// Need it for later check for unload timing access.
mLoadedURI = aNewURI;
mRedirects.AppendObject(aOldURI);
}
void
nsDOMNavigationTiming::NotifyBeforeUnload()
{
@ -150,32 +119,6 @@ nsDOMNavigationTiming::NotifyLoadEventEnd()
}
}
bool
nsDOMNavigationTiming::ReportRedirects()
{
if (mRedirectCheck == NOT_CHECKED) {
mRedirectCount = mRedirects.Count();
if (mRedirects.Count() == 0) {
mRedirectCheck = NO_REDIRECTS;
} else {
mRedirectCheck = CHECK_PASSED;
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
for (int i = mRedirects.Count() - 1; i >= 0; --i) {
nsIURI * curr = mRedirects[i];
nsresult rv = ssm->CheckSameOriginURI(curr, mLoadedURI, false);
if (!NS_SUCCEEDED(rv)) {
mRedirectCheck = CHECK_FAILED;
mRedirectCount = 0;
break;
}
}
// All we need to know is in mRedirectCheck now. Clear history.
mRedirects.Clear();
}
}
return mRedirectCheck == CHECK_PASSED;
}
void
nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI, mozilla::TimeStamp aValue)
{
@ -236,33 +179,6 @@ nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI)
}
}
uint16_t
nsDOMNavigationTiming::GetRedirectCount()
{
if (ReportRedirects()) {
return mRedirectCount;
}
return 0;
}
DOMTimeMilliSec
nsDOMNavigationTiming::GetRedirectStart()
{
if (ReportRedirects()) {
return mRedirectStart;
}
return 0;
}
DOMTimeMilliSec
nsDOMNavigationTiming::GetRedirectEnd()
{
if (ReportRedirects()) {
return mRedirectEnd;
}
return 0;
}
DOMTimeMilliSec
nsDOMNavigationTiming::GetUnloadEventStart()
{

View File

@ -37,18 +37,17 @@ public:
nsDOMPerformanceNavigationType GetType() const {
return mNavigationType;
}
uint16_t GetRedirectCount();
DOMTimeMilliSec GetRedirectStart();
DOMTimeMilliSec GetRedirectEnd();
inline DOMHighResTimeStamp GetNavigationStartHighRes() const {
return mNavigationStartHighRes;
}
DOMTimeMilliSec GetNavigationStart() const {
return mNavigationStart;
return static_cast<int64_t>(GetNavigationStartHighRes());
}
mozilla::TimeStamp GetNavigationStartTimeStamp() const {
return mNavigationStartTimeStamp;
}
DOMTimeMilliSec GetUnloadEventStart();
DOMTimeMilliSec GetUnloadEventEnd();
DOMTimeMilliSec GetFetchStart() const {
return mFetchStart;
}
DOMTimeMilliSec GetDomLoading() const {
return mDOMLoading;
}
@ -73,7 +72,6 @@ public:
void NotifyNavigationStart();
void NotifyFetchStart(nsIURI* aURI, nsDOMPerformanceNavigationType aNavigationType);
void NotifyRedirect(nsIURI* aOldURI, nsIURI* aNewURI);
void NotifyBeforeUnload();
void NotifyUnloadAccepted(nsIURI* aOldURI);
void NotifyUnloadEventStart();
@ -89,7 +87,6 @@ public:
void NotifyDOMContentLoadedStart(nsIURI* aURI);
void NotifyDOMContentLoadedEnd(nsIURI* aURI);
DOMTimeMilliSec TimeStampToDOM(mozilla::TimeStamp aStamp) const;
DOMTimeMilliSec TimeStampToDOMOrFetchStart(mozilla::TimeStamp aStamp) const;
inline DOMHighResTimeStamp TimeStampToDOMHighRes(mozilla::TimeStamp aStamp)
{
@ -102,27 +99,15 @@ private:
~nsDOMNavigationTiming();
void Clear();
bool ReportRedirects();
nsCOMPtr<nsIURI> mUnloadedURI;
nsCOMPtr<nsIURI> mLoadedURI;
nsCOMArray<nsIURI> mRedirects;
typedef enum { NOT_CHECKED,
CHECK_PASSED,
NO_REDIRECTS,
CHECK_FAILED} RedirectCheckState;
RedirectCheckState mRedirectCheck;
int16_t mRedirectCount;
nsDOMPerformanceNavigationType mNavigationType;
DOMTimeMilliSec mNavigationStart;
DOMHighResTimeStamp mNavigationStartHighRes;
mozilla::TimeStamp mNavigationStartTimeStamp;
DOMTimeMilliSec DurationFromStart();
DOMTimeMilliSec mFetchStart;
DOMTimeMilliSec mRedirectStart;
DOMTimeMilliSec mRedirectEnd;
DOMTimeMilliSec mBeforeUnloadStart;
DOMTimeMilliSec mUnloadStart;
DOMTimeMilliSec mUnloadEnd;

View File

@ -2454,7 +2454,8 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
newInnerWindow->mPerformance =
new nsPerformance(newInnerWindow,
currentInner->mPerformance->GetDOMTiming(),
currentInner->mPerformance->GetChannel());
currentInner->mPerformance->GetChannel(),
currentInner->mPerformance->GetParentPerformance());
}
}
@ -3567,7 +3568,22 @@ nsPIDOMWindow::CreatePerformanceObjectIfNeeded()
timedChannel = nullptr;
}
if (timing) {
mPerformance = new nsPerformance(this, timing, timedChannel);
// If we are dealing with an iframe, we will need the parent's performance
// object (so we can add the iframe as a resource of that page).
nsPerformance* parentPerformance = nullptr;
nsCOMPtr<nsIDOMWindow> parentWindow;
GetScriptableParent(getter_AddRefs(parentWindow));
nsCOMPtr<nsPIDOMWindow> parentPWindow = do_GetInterface(parentWindow);
if (GetOuterWindow() != parentPWindow) {
if (parentPWindow && !parentPWindow->IsInnerWindow()) {
parentPWindow = parentPWindow->GetCurrentInnerWindow();
}
if (parentPWindow) {
parentPerformance = parentPWindow->GetPerformance();
}
}
mPerformance =
new nsPerformance(this, timing, timedChannel, parentPerformance);
}
}

View File

@ -5,14 +5,20 @@
#include "nsPerformance.h"
#include "nsCOMPtr.h"
#include "nsIHttpChannel.h"
#include "nsITimedChannel.h"
#include "nsDOMNavigationTiming.h"
#include "nsContentUtils.h"
#include "nsIScriptSecurityManager.h"
#include "nsIDOMWindow.h"
#include "nsIURI.h"
#include "PerformanceEntry.h"
#include "PerformanceResourceTiming.h"
#include "mozilla/dom/PerformanceBinding.h"
#include "mozilla/dom/PerformanceTimingBinding.h"
#include "mozilla/dom/PerformanceNavigationBinding.h"
#include "mozilla/TimeStamp.h"
#include "nsThreadUtils.h"
using namespace mozilla;
@ -22,96 +28,256 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsPerformanceTiming, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsPerformanceTiming, Release)
nsPerformanceTiming::nsPerformanceTiming(nsPerformance* aPerformance,
nsITimedChannel* aChannel)
nsITimedChannel* aChannel,
nsIHttpChannel* aHttpChannel,
DOMHighResTimeStamp aZeroTime)
: mPerformance(aPerformance),
mChannel(aChannel)
mChannel(aChannel),
mFetchStart(0.0),
mZeroTime(aZeroTime),
mReportCrossOriginResources(true)
{
MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
SetIsDOMBinding();
// The aHttpChannel argument is null if this nsPerformanceTiming object
// is being used for the navigation timing (document) and has a non-null
// value for the resource timing (any resources within the page).
if (aHttpChannel) {
CheckRedirectCrossOrigin(aHttpChannel);
}
}
nsPerformanceTiming::~nsPerformanceTiming()
{
}
DOMTimeMilliSec
nsPerformanceTiming::DomainLookupStart() const
DOMHighResTimeStamp
nsPerformanceTiming::FetchStartHighRes()
{
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
if (!mFetchStart) {
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return 0;
}
if (!mChannel) {
return FetchStart();
TimeStamp stamp;
mChannel->GetAsyncOpen(&stamp);
MOZ_ASSERT(!stamp.IsNull(), "The fetch start time stamp should always be "
"valid if the performance timing is enabled");
mFetchStart = (!stamp.IsNull())
? TimeStampToDOMHighRes(stamp)
: 0.0;
}
return mFetchStart;
}
DOMTimeMilliSec
nsPerformanceTiming::FetchStart()
{
return static_cast<int64_t>(FetchStartHighRes());
}
// This method will implement the timing allow check algorithm
// http://w3c-test.org/webperf/specs/ResourceTiming/#timing-allow-check
// https://bugzilla.mozilla.org/show_bug.cgi?id=936814
void
nsPerformanceTiming::CheckRedirectCrossOrigin(nsIHttpChannel* aResourceChannel)
{
MOZ_ASSERT(mChannel, "The resource channel should not be null for any"
"valid resource");
uint16_t redirectCount;
mChannel->GetRedirectCount(&redirectCount);
if (redirectCount == 0) {
return;
}
nsCOMPtr<nsIURI> resourceURI, referrerURI;
aResourceChannel->GetReferrer(getter_AddRefs(referrerURI));
aResourceChannel->GetURI(getter_AddRefs(resourceURI));
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
nsresult rv = ssm->CheckSameOriginURI(resourceURI, referrerURI, false);
if (!NS_SUCCEEDED(rv)) {
mReportCrossOriginResources = false;
}
}
bool
nsPerformanceTiming::IsSameOriginAsReferral() const
{
return mReportCrossOriginResources;
}
uint16_t
nsPerformanceTiming::GetRedirectCount() const
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return 0;
}
bool sameOrigin;
mChannel->GetAllRedirectsSameOrigin(&sameOrigin);
if (!sameOrigin) {
return 0;
}
uint16_t redirectCount;
mChannel->GetRedirectCount(&redirectCount);
return redirectCount;
}
/**
* 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 (nsPerformanceTiming::RedirectStart()
* and PerformanceResourceTiming::RedirectStart() to make such verifications.
*
* @return a valid timing if the Performance Timing is enabled
*/
DOMHighResTimeStamp
nsPerformanceTiming::RedirectStartHighRes()
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return 0;
}
mozilla::TimeStamp stamp;
mChannel->GetRedirectStart(&stamp);
return TimeStampToDOMHighResOrFetchStart(stamp);
}
DOMTimeMilliSec
nsPerformanceTiming::RedirectStart()
{
// We have to check if all the redirect URIs had the same origin (since there
// is no check in RedirectStartHighRes())
bool sameOrigin;
mChannel->GetAllRedirectsSameOrigin(&sameOrigin);
if (sameOrigin) {
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
* (nsPerformanceTiming::RedirectEnd() and
* PerformanceResourceTiming::RedirectEnd() to make such verifications.
*
* @return a valid timing if the Performance Timing is enabled
*/
DOMHighResTimeStamp
nsPerformanceTiming::RedirectEndHighRes()
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return 0;
}
mozilla::TimeStamp stamp;
mChannel->GetRedirectEnd(&stamp);
return TimeStampToDOMHighResOrFetchStart(stamp);
}
DOMTimeMilliSec
nsPerformanceTiming::RedirectEnd()
{
// We have to check if all the redirect URIs had the same origin (since there
// is no check in RedirectEndHighRes())
bool sameOrigin;
mChannel->GetAllRedirectsSameOrigin(&sameOrigin);
if (sameOrigin) {
return static_cast<int64_t>(RedirectEndHighRes());
}
return 0;
}
DOMHighResTimeStamp
nsPerformanceTiming::DomainLookupStartHighRes()
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return 0;
}
mozilla::TimeStamp stamp;
mChannel->GetDomainLookupStart(&stamp);
return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
return TimeStampToDOMHighResOrFetchStart(stamp);
}
DOMTimeMilliSec
nsPerformanceTiming::DomainLookupEnd() const
nsPerformanceTiming::DomainLookupStart()
{
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
return 0;
return static_cast<int64_t>(DomainLookupStartHighRes());
}
if (!mChannel) {
return FetchStart();
DOMHighResTimeStamp
nsPerformanceTiming::DomainLookupEndHighRes()
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return 0;
}
mozilla::TimeStamp stamp;
mChannel->GetDomainLookupEnd(&stamp);
return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
return TimeStampToDOMHighResOrFetchStart(stamp);
}
DOMTimeMilliSec
nsPerformanceTiming::ConnectStart() const
nsPerformanceTiming::DomainLookupEnd()
{
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
return 0;
return static_cast<int64_t>(DomainLookupEndHighRes());
}
if (!mChannel) {
return FetchStart();
DOMHighResTimeStamp
nsPerformanceTiming::ConnectStartHighRes()
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return 0;
}
mozilla::TimeStamp stamp;
mChannel->GetConnectStart(&stamp);
return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
return TimeStampToDOMHighResOrFetchStart(stamp);
}
DOMTimeMilliSec
nsPerformanceTiming::ConnectEnd() const
nsPerformanceTiming::ConnectStart()
{
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
return 0;
return static_cast<int64_t>(ConnectStartHighRes());
}
if (!mChannel) {
return FetchStart();
DOMHighResTimeStamp
nsPerformanceTiming::ConnectEndHighRes()
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return 0;
}
mozilla::TimeStamp stamp;
mChannel->GetConnectEnd(&stamp);
return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
return TimeStampToDOMHighResOrFetchStart(stamp);
}
DOMTimeMilliSec
nsPerformanceTiming::RequestStart() const
nsPerformanceTiming::ConnectEnd()
{
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
return 0;
return static_cast<int64_t>(ConnectEndHighRes());
}
if (!mChannel) {
return FetchStart();
DOMHighResTimeStamp
nsPerformanceTiming::RequestStartHighRes()
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return 0;
}
mozilla::TimeStamp stamp;
mChannel->GetRequestStart(&stamp);
return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
return TimeStampToDOMHighResOrFetchStart(stamp);
}
DOMTimeMilliSec
nsPerformanceTiming::ResponseStart() const
nsPerformanceTiming::RequestStart()
{
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
return 0;
return static_cast<int64_t>(RequestStartHighRes());
}
if (!mChannel) {
return FetchStart();
DOMHighResTimeStamp
nsPerformanceTiming::ResponseStartHighRes()
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return 0;
}
mozilla::TimeStamp stamp;
mChannel->GetResponseStart(&stamp);
@ -120,17 +286,20 @@ nsPerformanceTiming::ResponseStart() const
if (stamp.IsNull() || (!cacheStamp.IsNull() && cacheStamp < stamp)) {
stamp = cacheStamp;
}
return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
return TimeStampToDOMHighResOrFetchStart(stamp);
}
DOMTimeMilliSec
nsPerformanceTiming::ResponseEnd() const
nsPerformanceTiming::ResponseStart()
{
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
return 0;
return static_cast<int64_t>(ResponseStartHighRes());
}
if (!mChannel) {
return FetchStart();
DOMHighResTimeStamp
nsPerformanceTiming::ResponseEndHighRes()
{
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
return 0;
}
mozilla::TimeStamp stamp;
mChannel->GetResponseEnd(&stamp);
@ -139,7 +308,21 @@ nsPerformanceTiming::ResponseEnd() const
if (stamp.IsNull() || (!cacheStamp.IsNull() && cacheStamp < stamp)) {
stamp = cacheStamp;
}
return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
return TimeStampToDOMHighResOrFetchStart(stamp);
}
DOMTimeMilliSec
nsPerformanceTiming::ResponseEnd()
{
return static_cast<int64_t>(ResponseEndHighRes());
}
bool
nsPerformanceTiming::IsInitialized() const
{
MOZ_ASSERT(mChannel, "The timed channel should not be null for any "
"valid resource");
return !!mChannel;
}
JSObject*
@ -172,18 +355,23 @@ nsPerformanceNavigation::WrapObject(JSContext *cx)
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_3(nsPerformance,
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_5(nsPerformance,
mWindow, mTiming,
mNavigation)
mNavigation, mEntries,
mParentPerformance)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPerformance)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPerformance)
nsPerformance::nsPerformance(nsIDOMWindow* aWindow,
nsDOMNavigationTiming* aDOMTiming,
nsITimedChannel* aChannel)
nsITimedChannel* aChannel,
nsPerformance* aParentPerformance)
: mWindow(aWindow),
mDOMTiming(aDOMTiming),
mChannel(aChannel)
mChannel(aChannel),
mParentPerformance(aParentPerformance),
mBufferSizeSet(kDefaultBufferSize),
mPrimaryBufferSize(kDefaultBufferSize)
{
MOZ_ASSERT(aWindow, "Parent window object should be provided");
SetIsDOMBinding();
@ -204,7 +392,12 @@ nsPerformanceTiming*
nsPerformance::Timing()
{
if (!mTiming) {
mTiming = new nsPerformanceTiming(this, mChannel);
// For navigation timing, the third argument (an nsIHtttpChannel) is null
// since the cross-domain redirect were already checked.
// The last argument (zero time) for performance.timing is the navigation
// start value.
mTiming = new nsPerformanceTiming(this, mChannel, nullptr,
mDOMTiming->GetNavigationStart());
}
return mTiming;
}
@ -230,3 +423,147 @@ nsPerformance::WrapObject(JSContext *cx)
return dom::PerformanceBinding::Wrap(cx, this);
}
void
nsPerformance::GetEntries(nsTArray<nsRefPtr<PerformanceEntry> >& retval)
{
MOZ_ASSERT(NS_IsMainThread());
retval.Clear();
uint32_t count = mEntries.Length();
if (count > mPrimaryBufferSize) {
count = mPrimaryBufferSize;
}
retval.AppendElements(mEntries.Elements(), count);
}
void
nsPerformance::GetEntriesByType(const nsAString& entryType,
nsTArray<nsRefPtr<PerformanceEntry> >& retval)
{
MOZ_ASSERT(NS_IsMainThread());
retval.Clear();
uint32_t count = mEntries.Length();
for (uint32_t i = 0 ; i < count && i < mPrimaryBufferSize ; i++) {
if (mEntries[i]->GetEntryType().Equals(entryType)) {
retval.AppendElement(mEntries[i]);
}
}
}
void
nsPerformance::GetEntriesByName(const nsAString& name,
const mozilla::dom::Optional<nsAString>& entryType,
nsTArray<nsRefPtr<PerformanceEntry> >& retval)
{
MOZ_ASSERT(NS_IsMainThread());
retval.Clear();
uint32_t count = mEntries.Length();
for (uint32_t i = 0 ; i < count && i < mPrimaryBufferSize ; i++) {
if (mEntries[i]->GetName().Equals(name) &&
(!entryType.WasPassed() ||
mEntries[i]->GetEntryType().Equals(entryType.Value()))) {
retval.AppendElement(mEntries[i]);
}
}
}
void
nsPerformance::ClearResourceTimings()
{
MOZ_ASSERT(NS_IsMainThread());
mPrimaryBufferSize = mBufferSizeSet;
mEntries.Clear();
}
void
nsPerformance::SetResourceTimingBufferSize(uint64_t maxSize)
{
MOZ_ASSERT(NS_IsMainThread());
mBufferSizeSet = maxSize;
if (mBufferSizeSet < mEntries.Length()) {
// call onresourcetimingbufferfull
// https://bugzilla.mozilla.org/show_bug.cgi?id=936813
}
}
/**
* An entry should be added only after the resource is loaded.
* This method is not thread safe and can only be called on the main thread.
*/
void
nsPerformance::AddEntry(nsIHttpChannel* channel,
nsITimedChannel* timedChannel)
{
MOZ_ASSERT(NS_IsMainThread());
// Check if resource timing is prefed off.
if (!nsContentUtils::IsResourceTimingEnabled()) {
return;
}
if (channel && timedChannel) {
nsAutoCString name;
nsAutoString initiatorType;
nsCOMPtr<nsIURI> originalURI;
timedChannel->GetInitiatorType(initiatorType);
// According to the spec, "The name attribute must return the resolved URL
// of the requested resource. This attribute must not change even if the
// fetch redirected to a different URL."
channel->GetOriginalURI(getter_AddRefs(originalURI));
originalURI->GetSpec(name);
NS_ConvertUTF8toUTF16 entryName(name);
// The nsITimedChannel argument will be used to gather all the timings.
// The nsIHttpChannel argument will be used to check if any cross-origin
// redirects occurred.
// 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).
nsRefPtr<nsPerformanceTiming> performanceTiming =
new nsPerformanceTiming(this, timedChannel, channel,
0);
// The PerformanceResourceTiming object will use the nsPerformanceTiming
// object to get all the required timings.
nsRefPtr<dom::PerformanceResourceTiming> performanceEntry =
new dom::PerformanceResourceTiming(performanceTiming, this);
performanceEntry->SetName(entryName);
performanceEntry->SetEntryType(NS_LITERAL_STRING("resource"));
// If the initiator type had no valid value, then set it to the default
// ("other") value.
if (initiatorType.IsEmpty()) {
initiatorType = NS_LITERAL_STRING("other");
}
performanceEntry->SetInitiatorType(initiatorType);
mEntries.InsertElementSorted(performanceEntry,
PerformanceEntryComparator());
if (mEntries.Length() > mPrimaryBufferSize) {
// call onresourcetimingbufferfull
// https://bugzilla.mozilla.org/show_bug.cgi?id=936813
}
}
}
bool
nsPerformance::PerformanceEntryComparator::Equals(
const PerformanceEntry* aElem1,
const PerformanceEntry* aElem2) const
{
NS_ABORT_IF_FALSE(aElem1 && aElem2,
"Trying to compare null performance entries");
return aElem1->StartTime() == aElem2->StartTime();
}
bool
nsPerformance::PerformanceEntryComparator::LessThan(
const PerformanceEntry* aElem1,
const PerformanceEntry* aElem2) const
{
NS_ABORT_IF_FALSE(aElem1 && aElem2,
"Trying to compare null performance entries");
return aElem1->StartTime() < aElem2->StartTime();
}

View File

@ -13,16 +13,47 @@
#include "nsContentUtils.h"
#include "nsIDOMWindow.h"
#include "js/TypeDecls.h"
#include "mozilla/dom/BindingDeclarations.h"
class nsITimedChannel;
class nsPerformance;
class nsIHttpChannel;
namespace mozilla {
namespace dom {
class PerformanceEntry;
}
}
// Script "performance.timing" object
class nsPerformanceTiming MOZ_FINAL : public nsWrapperCache
{
public:
typedef mozilla::TimeStamp TimeStamp;
/**
* @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.
*/
nsPerformanceTiming(nsPerformance* aPerformance,
nsITimedChannel* aChannel);
nsITimedChannel* aChannel,
nsIHttpChannel* aHttpChannel,
DOMHighResTimeStamp aZeroTime);
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsPerformanceTiming)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(nsPerformanceTiming)
@ -33,6 +64,57 @@ public:
return mPerformance;
}
/**
* @param aStamp
* The TimeStamp recorded for a specific event. This TimeStamp can
* be null.
* @return the duration of an event with a given TimeStamp, relative to the
* navigationStart TimeStamp (the moment the user landed on the
* page), if the given TimeStamp is valid. Otherwise, it will return
* the FetchStart timing value.
*/
inline DOMHighResTimeStamp TimeStampToDOMHighResOrFetchStart(TimeStamp aStamp)
{
return (!aStamp.IsNull())
? TimeStampToDOMHighRes(aStamp)
: FetchStartHighRes();
}
/**
* The nsITimedChannel records an absolute timestamp for each event.
* The nsDOMNavigationTiming will record the moment when the user landed on
* the page. This is a window.performance unique timestamp, so it can be used
* for all the events (navigation timing and resource timing events).
*
* 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
* 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,
* this offset value is either 0 (for the resource timing), or equal to
* "performance.navigationStart" (for navigation timing).
* For the resource timing, mZeroTime is set to 0, causing the result to be a
* relative time.
* For the navigation timing, mZeroTime is set to "performance.navigationStart"
* causing the result be an absolute time.
*
* @param aStamp
* The TimeStamp recorded for a specific event. This TimeStamp can't
* be null.
* @return number of milliseconds value as one of:
* - relative to the navigation start time, time the user has landed on the
* page
* - an absolute wall clock time since the unix epoch
*/
inline DOMHighResTimeStamp TimeStampToDOMHighRes(TimeStamp aStamp) const
{
MOZ_ASSERT(!aStamp.IsNull());
mozilla::TimeDuration duration =
aStamp - GetDOMTiming()->GetNavigationStartTimeStamp();
return duration.ToMilliseconds() + mZeroTime;
}
virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE;
// PerformanceNavigation WebIDL methods
@ -54,32 +136,36 @@ public:
}
return GetDOMTiming()->GetUnloadEventEnd();
}
DOMTimeMilliSec RedirectStart() {
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
return 0;
}
return GetDOMTiming()->GetRedirectStart();
}
DOMTimeMilliSec RedirectEnd() {
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
return 0;
}
return GetDOMTiming()->GetRedirectEnd();
}
DOMTimeMilliSec FetchStart() const {
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
return 0;
}
return GetDOMTiming()->GetFetchStart();
}
DOMTimeMilliSec DomainLookupStart() const;
DOMTimeMilliSec DomainLookupEnd() const;
DOMTimeMilliSec ConnectStart() const;
DOMTimeMilliSec ConnectEnd() const;
DOMTimeMilliSec RequestStart() const;
DOMTimeMilliSec ResponseStart() const;
DOMTimeMilliSec ResponseEnd() const;
DOMTimeMilliSec DomLoading() const {
uint16_t GetRedirectCount() const;
bool IsSameOriginAsReferral() const;
void CheckRedirectCrossOrigin(nsIHttpChannel* aResourceChannel);
// High resolution (used by resource timing)
DOMHighResTimeStamp FetchStartHighRes();
DOMHighResTimeStamp RedirectStartHighRes();
DOMHighResTimeStamp RedirectEndHighRes();
DOMHighResTimeStamp DomainLookupStartHighRes();
DOMHighResTimeStamp DomainLookupEndHighRes();
DOMHighResTimeStamp ConnectStartHighRes();
DOMHighResTimeStamp ConnectEndHighRes();
DOMHighResTimeStamp RequestStartHighRes();
DOMHighResTimeStamp ResponseStartHighRes();
DOMHighResTimeStamp ResponseEndHighRes();
// Low resolution (used by navigation timing)
DOMTimeMilliSec FetchStart();
DOMTimeMilliSec RedirectStart();
DOMTimeMilliSec RedirectEnd();
DOMTimeMilliSec DomainLookupStart();
DOMTimeMilliSec DomainLookupEnd();
DOMTimeMilliSec ConnectStart();
DOMTimeMilliSec ConnectEnd();
DOMTimeMilliSec RequestStart();
DOMTimeMilliSec ResponseStart();
DOMTimeMilliSec ResponseEnd();
DOMTimeMilliSec DomLoading() {
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
return 0;
}
@ -124,8 +210,16 @@ public:
private:
~nsPerformanceTiming();
bool IsInitialized() const;
nsRefPtr<nsPerformance> mPerformance;
nsCOMPtr<nsITimedChannel> mChannel;
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;
bool mReportCrossOriginResources;
};
// Script "performance.navigation" object
@ -137,6 +231,7 @@ public:
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(nsPerformanceNavigation)
nsDOMNavigationTiming* GetDOMTiming() const;
nsPerformanceTiming* GetPerformanceTiming() const;
nsPerformance* GetParentObject() const
{
@ -150,7 +245,7 @@ public:
return GetDOMTiming()->GetType();
}
uint16_t RedirectCount() const {
return GetDOMTiming()->GetRedirectCount();
return GetPerformanceTiming()->GetRedirectCount();
}
private:
@ -163,9 +258,11 @@ class nsPerformance MOZ_FINAL : public nsISupports,
public nsWrapperCache
{
public:
typedef mozilla::dom::PerformanceEntry PerformanceEntry;
nsPerformance(nsIDOMWindow* aWindow,
nsDOMNavigationTiming* aDOMTiming,
nsITimedChannel* aChannel);
nsITimedChannel* aChannel,
nsPerformance* aParentPerformance);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsPerformance)
@ -180,6 +277,11 @@ public:
return mChannel;
}
nsPerformance* GetParentPerformance() const
{
return mParentPerformance;
}
nsIDOMWindow* GetParentObject() const
{
return mWindow.get();
@ -192,6 +294,17 @@ public:
nsPerformanceTiming* Timing();
nsPerformanceNavigation* Navigation();
void GetEntries(nsTArray<nsRefPtr<PerformanceEntry> >& retval);
void GetEntriesByType(const nsAString& entryType,
nsTArray<nsRefPtr<PerformanceEntry> >& retval);
void GetEntriesByName(const nsAString& name,
const mozilla::dom::Optional< nsAString >& entryType,
nsTArray<nsRefPtr<PerformanceEntry> >& retval);
void AddEntry(nsIHttpChannel* channel,
nsITimedChannel* timedChannel);
void ClearResourceTimings();
void SetResourceTimingBufferSize(uint64_t maxSize);
private:
~nsPerformance();
@ -200,6 +313,21 @@ private:
nsCOMPtr<nsITimedChannel> mChannel;
nsRefPtr<nsPerformanceTiming> mTiming;
nsRefPtr<nsPerformanceNavigation> mNavigation;
nsTArray<nsRefPtr<PerformanceEntry> > mEntries;
nsRefPtr<nsPerformance> mParentPerformance;
uint64_t mBufferSizeSet;
uint64_t mPrimaryBufferSize;
static const uint64_t kDefaultBufferSize = 150;
// Helper classes
class PerformanceEntryComparator {
public:
bool Equals(const PerformanceEntry* aElem1,
const PerformanceEntry* aElem2) const;
bool LessThan(const PerformanceEntry* aElem1,
const PerformanceEntry* aElem2) const;
};
};
inline nsDOMNavigationTiming*
@ -208,6 +336,12 @@ nsPerformanceNavigation::GetDOMTiming() const
return mPerformance->GetDOMTiming();
}
inline nsPerformanceTiming*
nsPerformanceNavigation::GetPerformanceTiming() const
{
return mPerformance->Timing();
}
inline nsDOMNavigationTiming*
nsPerformanceTiming::GetDOMTiming() const
{

View File

@ -12,6 +12,8 @@ support-files =
file_moving_xhr.html
file_showModalDialog.html
historyframes.html
resource_timing_iframe.html
resource_timing_main_test.html
[test_497898.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage
@ -48,6 +50,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec
[test_outerHTML.html]
[test_outerHTML.xhtml]
[test_paste_selection.html]
[test_resource_timing.html]
skip-if = buildapp == 'b2g' # b2g(No clipboard) b2g-debug(No clipboard) b2g-desktop(No clipboard)
[test_performance_now.html]
[test_showModalDialog.html]

View File

@ -0,0 +1,48 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!--
This file is a sub-test file for the Resource Timing and Performance Timeline
APIs.
These tests are focused on the iframe corner case.
The first step is to check that the image from this document was added as
an entry to this window.performance object.
The second step is to check that this iframe was not added as an entry to its
own window.performance object.
As a final step, we do a double checking: no ifrmes were added as entries
to this window.performance object.
-->
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for Bug 822480 - Add in the Resource Timing API</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<script>
function doTest() {
window.parent.ok(!!window.performance.getEntriesByName(
"http://example.com/tests/image/test/mochitest/damon.jpg").length,
"http://example.com/tests/image/test/mochitest/damon.jpg should be a valid entry name");
window.parent.ok(!window.performance.getEntriesByName(
"http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing_iframe.html").length,
"This iframe should NOT contain itself as an entry");
// Check that there are no iframes added as entries
for (var i = 0 ; i < window.performance.getEntries().length ; i++) {
var entry = window.performance.getEntries()[i];
if (entry.initiatorType === "subdocument") {
ok(false, "unexpected iframe " + entry.name);
}
}
window.parent.iframeTestsCompleted();
}
</script>
<body onLoad="doTest()">
<img src="http://example.com/tests/image/test/mochitest/damon.jpg"/>
</body>
</html>

View File

@ -0,0 +1,258 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!--
This file contains test for the Resource Timing and Performance Timeline APIs.
The test starts by checking that all the entries were added to the performance
object.
The next step is to check that the "performance" object and its "getEntries()"
methods are available. We check all the 3 methods: getEntries,
getEntriesByName() and getEntriesByType().
As a next step, we check that the entries contain the correct information
("checkEntries()" method).
The test checks that the entries contain all the required members, that the
timings are sorted properly and that the entries were returned in
chronological order with respect to startTime. In "checkEntries()", it is also
checked if the order of the entries is the expected order (the expected order
is hard-coded here).
The last test from the "checkEntries()" method will verify the iframe case:
the iframe must be added as an entry to this window's performance object,
while the image from the iframe should not be added here.
Next tests will check the Performance API extensions introduced by the
resource timing: window.performance.setResourceTimingBufferSize(1) and
window.performance.clearResourceTimings();
The last tests will verify that the xhr resources are also added as entries
to our performance object.
Meanwhile, the iframe from the page will get loaded
(resource_timing_iframe.html).
The iframe contains a second script that will do some tests, as well, plus
an image - its own resource.
The script from the iframe will check that the iframe itself was not added
as an entry (to itself). Also, it will check that its image was added as
entry to the iframe's performance object.
The last check is a double check: check that no subdocuments were added as
entries for this iframe's performance object.
The parent's (this window) "ok_wrapper()" method will be called once the tests
are completed.
-->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
var mainWindowTestsDone = false;
var iframeTestsDone = false;
function ok(cond, message) {
window.opener.ok(cond, message)
}
function is(received, expected, message) {
window.opener.is(received, expected, message);
}
function isnot(received, notExpected, message) {
window.opener.isnot(received, notExpected, message);
}
window.onload = function() {
ok(!!window.performance, "Performance object should exist");
ok(!!window.performance.getEntries, "Performance.getEntries() should exist");
ok(!!window.performance.getEntriesByName, "Performance.getEntriesByName() should exist");
ok(!!window.performance.getEntriesByType, "Performance.getEntriesByType() should exist");
// Here, we should have 6 entries (1 css, 3 png, 1 html) since the image was loaded.
is(window.performance.getEntries().length, 5, "Performance.getEntries() returned wrong number of entries.");
ok(!!window.performance.getEntriesByType("resource").length,
"Performance.getEntriesByType() should return some results");
ok(!!window.performance.getEntriesByName("http://example.com/tests/image/test/mochitest/blue.png").length,
"Performance.getEntriesByName() should return some results");
// Checks that two calls for "getEntries()" return a different array with the same
// entries.
ok(window.performance.getEntries() !== window.performance.getEntries(),
"getEntries() should return a different array object every time.");
ok(function (array1, array2) {
if (array1.length != array2.length) {
return false;
}
for (var i = 0 ; i < array1.length ; i++) {
if (array1[i] !== array2[i]) {
return false;
}
}
return true;
}(window.performance.getEntries(), window.performance.getEntries()),
"The arrays should have the same entries.");
checkEntries(window.performance.getEntries());
window.performance.setResourceTimingBufferSize(1);
is(window.performance.getEntries().length, 5, "No entries should be " +
"removed when setResourceTimingBufferSize is called.");
window.performance.setResourceTimingBufferSize(4);
is(window.performance.getEntries().length, 5, "No entries should be " +
"removed when setResourceTimingBufferSize is called.");
window.performance.setResourceTimingBufferSize(1);
window.performance.clearResourceTimings();
is(window.performance.getEntries().length, 0, "All the entries should " +
"be removed when when clearResourceTimings is being called.");
makeXhr("test-data.json", firstCheck);
}
function checkEntries(anEntryList) {
// Check that all the entries have all the properties.
for (var i = 0 ; i < anEntryList.length ; i++) {
var entry = anEntryList[i];
ok(!!entry, "PerformanceEntry should not be null");
ok(!!entry.name, "PerformanceEntry.name should be valid.");
ok(entry.startTime > 0, "PerformanceEntry.startTime should be grater than 0");
// The entries list should be in chronological order with respect to startTime
if (i > 0) {
ok(anEntryList[i - 1].startTime <= anEntryList[i].startTime,
"Entries list should be in chronological order with respect to startTime.");
}
// Check that each entry has all the properties and that the timings were
// returned in the expected order.
if ("initiatorType" in entry) {
ok("redirectStart" in entry, "PerformanceEntry.redirectStart should be part of PerformanceEntry");
ok("redirectEnd" in entry, "PerformanceEntry.redirectEnd should be part of PerformanceEntry");
ok("fetchStart" in entry, "PerformanceEntry.fetchStart should be part of PerformanceEntry");
ok("domainLookupStart" in entry, "PerformanceEntry.domainLookupStart should be part of PerformanceEntry");
ok("domainLookupEnd" in entry, "PerformanceEntry.domainLookupEnd should be part of PerformanceEntry");
ok("connectStart" in entry, "PerformanceEntry.connectStart should be part of PerformanceEntry");
ok("connectEnd" in entry, "PerformanceEntry.connectEnd should be part of PerformanceEntry");
ok("secureConnectionStart" in entry, "PerformanceEntry.secureConnectionStart should be part of PerformanceEntry");
ok("requestStart" in entry, "PerformanceEntry.requestStart should be part of PerformanceEntry");
ok("responseStart" in entry, "PerformanceEntry.responseStart should be part of PerformanceEntry");
ok("responseEnd" in entry, "PerformanceEntry.responseEnd should be part of PerformanceEntry");
// Check that timings are in proper order
sequence = ['startTime', 'redirectStart', 'redirectEnd', 'fetchStart',
'domainLookupStart', 'domainLookupEnd', 'connectStart',
'connectEnd', 'requestStart', 'responseStart', 'responseEnd'];
for (var j = 1; j < sequence.length; ++j) {
var prop = sequence[j];
var prevProp = sequence[j-1];
ok(entry[prevProp] <= entry[prop],
['Expected ', prevProp, ' to happen before ', prop,
', got ', prevProp, ' = ', entry[prevProp],
', ', prop, ' = ', entry[prop]].join(''));
}
}
}
// Check that the entries have the expected initiator type. We can't check
// the order (the order might depend on the platform the tests are running).
allResources = {
"http://mochi.test:8888/tests/SimpleTest/test.css" : "link",
"http://example.com/tests/image/test/mochitest/blue.png" : "img",
"http://example.com/tests/image/test/mochitest/red.png" : "object",
"http://example.com/tests/image/test/mochitest/big.png" : "embed",
"http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing_iframe.html" : "subdocument"};
for (resourceName in allResources) {
// Check that we have a resource with the specific name.
namedEntries = window.performance.getEntriesByName(resourceName);
ok (!!namedEntries && (namedEntries.length == 1),
"An entry with the name '" + resourceName + "' should be available");
// Double check for the entry name.
is (namedEntries[0].name, resourceName, "The resource name is invalid");
// Check the initiator type.
is (namedEntries[0].initiatorType, allResources[resourceName],
"The initiator type for " + resourceName + " is invalid");
}
// Check that the iframe's image was NOT added as an entry to this window's performance entry.
ok(!window.performance.getEntriesByName("http://example.com/tests/image/test/mochitest/damon.jpg").length,
"http://example.com/tests/image/test/mochitest/damon.jpg should be a valid entry name");
}
function firstCheck() {
is(window.performance.getEntries().length, 1, "The first xhr entry was not added.");
is(window.performance.getEntries()[0].initiatorType, "xmlhttprequest",
"The initiatorType is incorect for this entry");
makeXhr("test-data2.json", secondCheck);
}
function secondCheck() {
// Since the buffer max size was set to '1', 'peformance.getEntries()' should
// return only '1' entry (first xhr results).
is(window.performance.getEntries().length, 1, "The second xhr entry should not be " +
"returned since the buffer size was set to 1.");
isnot(window.performance.getEntries()[0].name, "http://mochi.test:8888/tests/dom/tests/mochitest/general/test-data2.json",
"We returned the second xhr instead of the first one");
finishTest();
}
function finishTest() {
// Check if all the tests are completed.
if (iframeTestsDone) {
window.opener.finishTests();
} else {
mainWindowTestsDone = true;
}
}
function makeXhr(aUrl, aCallback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onload = aCallback;
xmlhttp.open("get", aUrl, true);
xmlhttp.send();
}
function checkArraysHaveSameElementsInSameOrder(array1, array2) {
if (array1.length != array2.length) {
return false;
}
for (var i = 0 ; i < array1.length ; i++) {
if (array1[i] !== array2[i]) {
return false;
}
}
return true;
}
function iframeTestsCompleted() {
if (mainWindowTestsDone) {
window.opener.finishTests();
}
else {
iframeTestsDone = true;
}
}
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=822480"
title="Add resource timing API.">
Bug #822480 - Add in the Resource Timing API
</a>
<p id="display"></p>
<div id="content">
<img src="http://example.com/tests/image/test/mochitest/blue.png">
<object data="http://example.com/tests/image/test/mochitest/red.png" type="image/png"/>
<embed src="http://example.com/tests/image/test/mochitest/big.png" type="image/png"/>
<iframe sandbox="allow-same-origin allow-scripts" id="if_2" src="resource_timing_iframe.html" height="10" width="10"></iframe>
</div>
</body>
</html>

View File

@ -0,0 +1 @@
{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }

View File

@ -0,0 +1 @@
{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }

View File

@ -747,8 +747,12 @@ var interfaceNamesInGlobalScope =
{ name: "Path2D", pref: "canvas.path.enabled" },
// IMPORTANT: Do not change this list without review from a DOM peer!
"Performance",
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceEntry",
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceNavigation",
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceResourceTiming",
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceTiming",
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -0,0 +1,35 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=822480
-->
<head>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
// Resource timing is prefed off by default, so we had to use this workaround
SpecialPowers.setBoolPref("dom.enable_resource_timing", true);
var subwindow = window.open("resource_timing_main_test.html");
function finishTests() {
subwindow.close();
SpecialPowers.setBoolPref("dom.enable_resource_timing", false);
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

View File

@ -11,6 +11,7 @@
*/
typedef double DOMHighResTimeStamp;
typedef sequence <PerformanceEntry> PerformanceEntryList;
interface Performance {
DOMHighResTimeStamp now();
@ -22,3 +23,17 @@ interface Performance {
jsonifier;
};
// http://www.w3c-test.org/webperf/specs/PerformanceTimeline/#sec-window.performance-attribute
partial interface Performance {
PerformanceEntryList getEntries();
PerformanceEntryList getEntriesByType(DOMString entryType);
PerformanceEntryList getEntriesByName(DOMString name, optional DOMString
entryType);
};
// http://w3c-test.org/webperf/specs/ResourceTiming/#extensions-performance-interface
partial interface Performance {
void clearResourceTimings();
void setResourceTimingBufferSize(unsigned long maxSize);
};

View File

@ -0,0 +1,19 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* http://www.w3c-test.org/webperf/specs/PerformanceTimeline/#sec-PerformanceEntry-interface
*
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
*/
interface PerformanceEntry
{
readonly attribute DOMString name;
readonly attribute DOMString entryType;
readonly attribute DOMHighResTimeStamp startTime;
readonly attribute DOMHighResTimeStamp duration;
};

View File

@ -0,0 +1,33 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* http://w3c-test.org/webperf/specs/ResourceTiming/#performanceresourcetiming
*
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
*/
interface PerformanceResourceTiming : PerformanceEntry
{
// A string with the name of that element that initiated the load.
// If the initiator is a CSS resource, the initiatorType attribute must return
// the string "css".
// If the initiator is an XMLHttpRequest object, the initiatorType attribute
// must return the string "xmlhttprequest".
readonly attribute DOMString initiatorType;
readonly attribute DOMHighResTimeStamp redirectStart;
readonly attribute DOMHighResTimeStamp redirectEnd;
readonly attribute DOMHighResTimeStamp fetchStart;
readonly attribute DOMHighResTimeStamp domainLookupStart;
readonly attribute DOMHighResTimeStamp domainLookupEnd;
readonly attribute DOMHighResTimeStamp connectStart;
readonly attribute DOMHighResTimeStamp connectEnd;
readonly attribute DOMHighResTimeStamp secureConnectionStart;
readonly attribute DOMHighResTimeStamp requestStart;
readonly attribute DOMHighResTimeStamp responseStart;
readonly attribute DOMHighResTimeStamp responseEnd;
};

View File

@ -269,7 +269,9 @@ WEBIDL_FILES = [
'PannerNode.webidl',
'ParentNode.webidl',
'Performance.webidl',
'PerformanceEntry.webidl',
'PerformanceNavigation.webidl',
'PerformanceResourceTiming.webidl',
'PerformanceTiming.webidl',
'PeriodicWave.webidl',
'PermissionSettings.webidl',

View File

@ -119,7 +119,7 @@ nsXBLResourceLoader::LoadResources(bool* aResult)
// XXX: initialDocumentURI is nullptr!
nsRefPtr<imgRequestProxy> req;
nsContentUtils::LoadImage(url, doc, docPrincipal, docURL, nullptr,
nsIRequest::LOAD_BACKGROUND,
nsIRequest::LOAD_BACKGROUND, EmptyString(),
getter_AddRefs(req));
}
else if (curr->mType == nsGkAtoms::stylesheet) {

View File

@ -310,7 +310,7 @@ nsContextMenuInfo::GetBackgroundImageRequestInternal(nsIDOMNode *aDOMNode, imgRe
return il->LoadImage(bgUri, nullptr, nullptr, principal, nullptr,
nullptr, nullptr, nsIRequest::LOAD_NORMAL,
nullptr, channelPolicy, aRequest);
nullptr, channelPolicy, EmptyString(), aRequest);
}
}

View File

@ -1562,6 +1562,7 @@ NS_IMETHODIMP imgLoader::LoadImageXPCOM(nsIURI *aURI,
aLoadFlags,
aCacheKey,
aPolicy,
EmptyString(),
&proxy);
*_retval = proxy;
return result;
@ -1581,6 +1582,7 @@ nsresult imgLoader::LoadImage(nsIURI *aURI,
nsLoadFlags aLoadFlags,
nsISupports *aCacheKey,
nsIChannelPolicy *aPolicy,
const nsAString& initiatorType,
imgRequestProxy **_retval)
{
VerifyCacheSizes();
@ -1718,6 +1720,12 @@ nsresult imgLoader::LoadImage(nsIURI *aURI,
request->Init(aURI, aURI, channelLoadGroup, newChannel, entry, aCX,
aLoadingPrincipal, corsmode);
// Add the initiator type for this image load
nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(newChannel);
if (timedChannel) {
timedChannel->SetInitiatorType(initiatorType);
}
// Pass the inner window ID of the loading document, if possible.
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX);
if (doc) {

View File

@ -255,6 +255,7 @@ public:
nsLoadFlags aLoadFlags,
nsISupports *aCacheKey,
nsIChannelPolicy *aPolicy,
const nsAString& initiatorType,
imgRequestProxy **_retval);
nsresult LoadImageWithChannel(nsIChannel *channel,
imgINotificationObserver *aObserver,

View File

@ -17,6 +17,7 @@
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
#include "mozilla/TimeStamp.h"
#include "imgRequest.h"

View File

@ -1842,6 +1842,7 @@ nsImageFrame::LoadIcon(const nsAString& aSpec,
loadFlags,
nullptr,
nullptr, /* channel policy not needed */
EmptyString(),
aRequest);
}

View File

@ -259,6 +259,7 @@ ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
nsRefPtr<imgRequestProxy> request;
nsContentUtils::LoadImage(aURI, mDocument, aOriginPrincipal, aReferrer,
nullptr, nsIRequest::LOAD_NORMAL,
NS_LITERAL_STRING("css"),
getter_AddRefs(request));
if (!request) {

View File

@ -1546,6 +1546,16 @@ Loader::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI();
if (referrerURI)
httpChannel->SetReferrer(referrerURI);
// Set the initiator type
nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
if (timedChannel) {
if (aLoadData->mParentData) {
timedChannel->SetInitiatorType(NS_LITERAL_STRING("css"));
} else {
timedChannel->SetInitiatorType(NS_LITERAL_STRING("link"));
}
}
}
// Now tell the channel we expect text/css data back.... We do

View File

@ -236,7 +236,7 @@ nsImageBoxFrame::UpdateImage()
mContent->NodePrincipal())) {
nsContentUtils::LoadImage(uri, doc, mContent->NodePrincipal(),
doc->GetDocumentURI(), mListener, mLoadFlags,
getter_AddRefs(mImageRequest));
EmptyString(), getter_AddRefs(mImageRequest));
if (mImageRequest) {
nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,

View File

@ -2199,6 +2199,7 @@ nsTreeBodyFrame::GetImage(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContex
doc->GetDocumentURI(),
imgNotificationObserver,
nsIRequest::LOAD_NORMAL,
EmptyString(),
getter_AddRefs(imageRequest));
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -105,6 +105,9 @@ pref("dom.workers.sharedWorkers.enabled", true);
// Whether nonzero values can be returned from performance.timing.*
pref("dom.enable_performance", true);
// Whether resource timing will be gathered and returned by performance.GetEntries*
pref("dom.enable_resource_timing", false);
// Whether the Gamepad API is enabled
pref("dom.gamepad.enabled", true);
#ifdef RELEASE_BUILD

View File

@ -12,13 +12,16 @@ class TimeStamp;
native TimeStamp(mozilla::TimeStamp);
// All properties return zero if the value is not available
[scriptable, uuid(c259b593-a9bf-4d08-8149-ef89e1977dc4)]
[scriptable, uuid(E94AB245-B40D-4154-8B7F-B6E0F2461031)]
interface nsITimedChannel : nsISupports {
// Set this attribute to true to enable collection of timing data.
// channelCreationTime will be available even with this attribute set to
// false.
attribute boolean timingEnabled;
// The number of redirects
attribute uint16_t redirectCount;
[noscript] readonly attribute TimeStamp channelCreation;
[noscript] readonly attribute TimeStamp asyncOpen;
@ -32,6 +35,17 @@ interface nsITimedChannel : nsISupports {
[noscript] readonly attribute TimeStamp responseStart;
[noscript] readonly attribute TimeStamp responseEnd;
// The redirect attributes timings must be writeble, se we can transfer
// the data from one channel to the redirected channel.
[noscript] attribute TimeStamp redirectStart;
[noscript] attribute TimeStamp redirectEnd;
// The initiator type
[noscript] attribute AString initiatorType;
// This flag should be set to false only if a cross-domain redirect occurred
[noscript] attribute boolean allRedirectsSameOrigin;
// The following are only set if the document is (partially) read from the
// cache
[noscript] readonly attribute TimeStamp cacheReadStart;
@ -49,4 +63,6 @@ interface nsITimedChannel : nsISupports {
readonly attribute PRTime responseEndTime;
readonly attribute PRTime cacheReadStartTime;
readonly attribute PRTime cacheReadEndTime;
readonly attribute PRTime redirectStartTime;
readonly attribute PRTime redirectEndTime;
};

View File

@ -26,6 +26,8 @@
#include "nsICookieService.h"
#include "nsIStreamConverterService.h"
#include "nsCRT.h"
#include "nsContentUtils.h"
#include "nsIScriptSecurityManager.h"
#include "nsIObserverService.h"
#include <algorithm>
@ -59,10 +61,12 @@ HttpBaseChannel::HttpBaseChannel()
, mLoadAsBlocking(false)
, mLoadUnblocked(false)
, mResponseTimeoutEnabled(true)
, mAllRedirectsSameOrigin(true)
, mSuspendCount(0)
, mProxyResolveFlags(0)
, mContentDispositionHint(UINT32_MAX)
, mHttpHandler(gHttpHandler)
, mRedirectCount(0)
{
LOG(("Creating HttpBaseChannel @%x\n", this));
@ -1906,14 +1910,47 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
if (bag)
mPropertyHash.EnumerateRead(CopyProperties, bag.get());
// transfer timed channel enabled status
nsCOMPtr<nsITimedChannel> timed(do_QueryInterface(newChannel));
if (timed)
timed->SetTimingEnabled(mTimingEnabled);
// Transfer the timing data (if we are dealing with an nsITimedChannel).
nsCOMPtr<nsITimedChannel> newTimedChannel(do_QueryInterface(newChannel));
nsCOMPtr<nsITimedChannel> oldTimedChannel(
do_QueryInterface(static_cast<nsIHttpChannel*>(this)));
if (oldTimedChannel && newTimedChannel) {
newTimedChannel->SetTimingEnabled(mTimingEnabled);
newTimedChannel->SetRedirectCount(mRedirectCount + 1);
// If the RedirectStart is null, we will use the AsyncOpen value of the
// previous channel (this is the first redirect in the redirects chain).
if (mRedirectStartTimeStamp.IsNull()) {
TimeStamp asyncOpen;
oldTimedChannel->GetAsyncOpen(&asyncOpen);
newTimedChannel->SetRedirectStart(asyncOpen);
}
else {
newTimedChannel->SetRedirectStart(mRedirectStartTimeStamp);
}
// The RedirectEnd timestamp is equal to the previous channel response end.
TimeStamp prevResponseEnd;
oldTimedChannel->GetResponseEnd(&prevResponseEnd);
newTimedChannel->SetRedirectEnd(prevResponseEnd);
// Check whether or not this was a cross-domain redirect.
newTimedChannel->SetAllRedirectsSameOrigin(
mAllRedirectsSameOrigin && SameOriginWithOriginalUri(newURI));
}
return NS_OK;
}
// Redirect Tracking
bool
HttpBaseChannel::SameOriginWithOriginalUri(nsIURI *aURI)
{
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
nsresult rv = ssm->CheckSameOriginURI(aURI, mOriginalURI, false);
return (NS_SUCCEEDED(rv));
}
//------------------------------------------------------------------------------
} // namespace net

View File

@ -243,6 +243,10 @@ protected:
getter_AddRefs(aResult));
}
// Redirect tracking
// Checks whether or not aURI and mOriginalURI share the same domain.
bool SameOriginWithOriginalUri(nsIURI *aURI);
friend class PrivateBrowsingChannel<HttpBaseChannel>;
nsCOMPtr<nsIURI> mURI;
@ -306,6 +310,8 @@ protected:
uint32_t mLoadAsBlocking : 1;
uint32_t mLoadUnblocked : 1;
uint32_t mResponseTimeoutEnabled : 1;
// A flag that should be false only if a cross-domain redirect occurred
uint32_t mAllRedirectsSameOrigin : 1;
// Current suspension depth for this channel object
uint32_t mSuspendCount;
@ -320,6 +326,19 @@ protected:
nsAutoPtr<nsString> mContentDispositionFilename;
nsRefPtr<nsHttpHandler> mHttpHandler; // keep gHttpHandler alive
// Performance tracking
// The initiator type (for this resource) - how was the resource referenced in
// the HTML file.
nsString mInitiatorType;
// Number of redirects that has occurred.
int16_t mRedirectCount;
// A time value equal to the starting time of the fetch that initiates the
// redirect.
mozilla::TimeStamp mRedirectStartTimeStamp;
// A time value equal to the time immediately after receiving the last byte of
// the response of the last redirect.
mozilla::TimeStamp mRedirectEndTimeStamp;
};
// Share some code while working around C++'s absurd inability to handle casting

View File

@ -62,6 +62,8 @@
#include "nsIStreamConverterService.h"
#include "nsISiteSecurityService.h"
#include "nsCRT.h"
#include "nsPIDOMWindow.h"
#include "nsPerformance.h"
#include "CacheObserver.h"
#include "mozilla/Telemetry.h"
@ -4774,6 +4776,66 @@ nsHttpChannel::GetAsyncOpen(TimeStamp* _retval) {
return NS_OK;
}
/**
* @return the number of redirects. There is no check for cross-domain
* redirects. This check must be done by the consumers.
*/
NS_IMETHODIMP
nsHttpChannel::GetRedirectCount(uint16_t *aRedirectCount)
{
*aRedirectCount = mRedirectCount;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::SetRedirectCount(uint16_t aRedirectCount)
{
mRedirectCount = aRedirectCount;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetRedirectStart(TimeStamp* _retval)
{
*_retval = mRedirectStartTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::SetRedirectStart(TimeStamp aRedirectStart)
{
mRedirectStartTimeStamp = aRedirectStart;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetRedirectEnd(TimeStamp* _retval)
{
*_retval = mRedirectEndTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::SetRedirectEnd(TimeStamp aRedirectEnd)
{
mRedirectEndTimeStamp = aRedirectEnd;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetAllRedirectsSameOrigin(bool *aAllRedirectsSameOrigin)
{
*aAllRedirectsSameOrigin = mAllRedirectsSameOrigin;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::SetAllRedirectsSameOrigin(bool aAllRedirectsSameOrigin)
{
mAllRedirectsSameOrigin = aAllRedirectsSameOrigin;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetDomainLookupStart(TimeStamp* _retval) {
if (mDNSPrefetch && mDNSPrefetch->TimingsValid())
@ -4853,6 +4915,20 @@ nsHttpChannel::GetCacheReadEnd(TimeStamp* _retval) {
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetInitiatorType(nsAString & aInitiatorType)
{
aInitiatorType = mInitiatorType;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::SetInitiatorType(const nsAString & aInitiatorType)
{
mInitiatorType = aInitiatorType;
return NS_OK;
}
#define IMPL_TIMING_ATTR(name) \
NS_IMETHODIMP \
nsHttpChannel::Get##name##Time(PRTime* _retval) { \
@ -4878,6 +4954,8 @@ IMPL_TIMING_ATTR(ResponseStart)
IMPL_TIMING_ATTR(ResponseEnd)
IMPL_TIMING_ATTR(CacheReadStart)
IMPL_TIMING_ATTR(CacheReadEnd)
IMPL_TIMING_ATTR(RedirectStart)
IMPL_TIMING_ATTR(RedirectEnd)
#undef IMPL_TIMING_ATTR
@ -5240,6 +5318,12 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st
}
}
// Register entry to the Performance resource timing
nsPerformance* documentPerformance = GetPerformance();
if (documentPerformance) {
documentPerformance->AddEntry(this, this);
}
if (mListener) {
LOG((" calling OnStopRequest\n"));
mListener->OnStopRequest(this, mListenerContext, status);
@ -6218,6 +6302,46 @@ nsHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
return rv;
}
nsPerformance*
nsHttpChannel::GetPerformance()
{
// If performance timing is disabled, there is no need for the nsPerformance
// object anymore.
if (!mTimingEnabled) {
return nullptr;
}
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(this, loadContext);
if (!loadContext) {
return nullptr;
}
nsCOMPtr<nsIDOMWindow> domWindow;
loadContext->GetAssociatedWindow(getter_AddRefs(domWindow));
if (!domWindow) {
return nullptr;
}
nsCOMPtr<nsPIDOMWindow> pDomWindow = do_QueryInterface(domWindow);
if (!pDomWindow) {
return nullptr;
}
if (!pDomWindow->IsInnerWindow()) {
pDomWindow = pDomWindow->GetCurrentInnerWindow();
if (!pDomWindow) {
return nullptr;
}
}
nsPerformance* docPerformance = pDomWindow->GetPerformance();
if (!docPerformance) {
return nullptr;
}
// iframes should be added to the parent's entries list.
if (mLoadFlags & LOAD_DOCUMENT_URI) {
return docPerformance->GetParentPerformance();
}
return docPerformance;
}
void
nsHttpChannel::ForcePending(bool aForcePending)
{

View File

@ -30,6 +30,7 @@ class nsICacheEntryDescriptor;
class nsICancelable;
class nsIHttpChannelAuthProvider;
class nsInputStreamPump;
class nsPerformance;
namespace mozilla { namespace net {
@ -420,6 +421,7 @@ private:
protected:
virtual void DoNotifyListenerCleanup();
nsPerformance* GetPerformance();
private: // cache telemetry
bool mDidReval;

View File

@ -241,7 +241,8 @@ OSXNotificationCenter::ShowAlertNotification(const nsAString & aImageUrl, const
if (imageUri) {
nsresult rv = il->LoadImage(imageUri, nullptr, nullptr, aPrincipal, nullptr,
this, nullptr, nsIRequest::LOAD_NORMAL, nullptr,
nullptr, getter_AddRefs(osxni->mIconRequest));
nullptr, EmptyString(),
getter_AddRefs(osxni->mIconRequest));
if (NS_SUCCEEDED(rv)) {
// Set a timer for six seconds. If we don't have an icon by the time this
// goes off then we go ahead without an icon.

View File

@ -316,7 +316,7 @@ nsMenuItemIconX::LoadIcon(nsIURI* aIconURI)
// not exposed to web content
nsresult rv = loader->LoadImage(aIconURI, nullptr, nullptr, nullptr, loadGroup, this,
nullptr, nsIRequest::LOAD_NORMAL, nullptr,
nullptr, getter_AddRefs(mIconRequest));
nullptr, EmptyString(), getter_AddRefs(mIconRequest));
if (NS_FAILED(rv)) return rv;
// We need to request the icon be decoded (bug 573583, bug 705516).