diff options
Diffstat (limited to 'dom')
64 files changed, 1395 insertions, 531 deletions
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index ef87a250e..bc8cea35a 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -281,6 +281,7 @@ bool nsContentUtils::sIsCutCopyAllowed = true; bool nsContentUtils::sIsFrameTimingPrefEnabled = false; bool nsContentUtils::sIsPerformanceTimingEnabled = false; bool nsContentUtils::sIsResourceTimingEnabled = false; +bool nsContentUtils::sIsPerformanceNavigationTimingEnabled = false; bool nsContentUtils::sIsUserTimingLoggingEnabled = false; bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false; bool nsContentUtils::sEncodeDecodeURLHash = false; @@ -571,6 +572,9 @@ nsContentUtils::Init() Preferences::AddBoolVarCache(&sIsResourceTimingEnabled, "dom.enable_resource_timing", true); + Preferences::AddBoolVarCache(&sIsPerformanceNavigationTimingEnabled, + "dom.enable_performance_navigation_timing", true); + Preferences::AddBoolVarCache(&sIsUserTimingLoggingEnabled, "dom.performance.enable_user_timing_logging", false); diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 0932f451e..9ae6d2155 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2033,6 +2033,14 @@ public: } /* + * Returns true if the performance timing APIs are enabled. + */ + static bool IsPerformanceNavigationTimingEnabled() + { + return sIsPerformanceNavigationTimingEnabled; + } + + /* * Returns true if notification should be sent for peformance timing events. */ static bool SendPerformanceTimingNotifications() @@ -2825,6 +2833,7 @@ private: static uint32_t sHandlingInputTimeout; static bool sIsPerformanceTimingEnabled; static bool sIsResourceTimingEnabled; + static bool sIsPerformanceNavigationTimingEnabled; static bool sIsUserTimingLoggingEnabled; static bool sIsFrameTimingPrefEnabled; static bool sIsExperimentalAutocompleteEnabled; diff --git a/dom/base/nsDOMNavigationTiming.cpp b/dom/base/nsDOMNavigationTiming.cpp index 31b2932fb..32ce8a8cb 100644 --- a/dom/base/nsDOMNavigationTiming.cpp +++ b/dom/base/nsDOMNavigationTiming.cpp @@ -15,6 +15,9 @@ #include "nsPrintfCString.h" #include "mozilla/dom/PerformanceNavigation.h" #include "mozilla/TimeStamp.h" +#include "mozilla/Telemetry.h" + +using namespace mozilla; nsDOMNavigationTiming::nsDOMNavigationTiming() { @@ -30,47 +33,36 @@ nsDOMNavigationTiming::Clear() { mNavigationType = TYPE_RESERVED; mNavigationStartHighRes = 0; - mBeforeUnloadStart = 0; - mUnloadStart = 0; - mUnloadEnd = 0; - mLoadEventStart = 0; - mLoadEventEnd = 0; - mDOMLoading = 0; - mDOMInteractive = 0; - mDOMContentLoadedEventStart = 0; - mDOMContentLoadedEventEnd = 0; - mDOMComplete = 0; - - mLoadEventStartSet = false; - mLoadEventEndSet = false; - mDOMLoadingSet = false; - mDOMInteractiveSet = false; - mDOMContentLoadedEventStartSet = false; - mDOMContentLoadedEventEndSet = false; - mDOMCompleteSet = false; + mBeforeUnloadStart = TimeStamp(); + mUnloadStart = TimeStamp(); + mUnloadEnd = TimeStamp(); + mLoadEventStart = TimeStamp(); + mLoadEventEnd = TimeStamp(); + mDOMLoading = TimeStamp(); + mDOMInteractive = TimeStamp(); + mDOMContentLoadedEventStart = TimeStamp(); + mDOMContentLoadedEventEnd = TimeStamp(); + mDOMComplete = TimeStamp(); + mDocShellHasBeenActiveSinceNavigationStart = false; } DOMTimeMilliSec -nsDOMNavigationTiming::TimeStampToDOM(mozilla::TimeStamp aStamp) const +nsDOMNavigationTiming::TimeStampToDOM(TimeStamp aStamp) const { if (aStamp.IsNull()) { return 0; } - mozilla::TimeDuration duration = aStamp - mNavigationStartTimeStamp; - return GetNavigationStart() + static_cast<int64_t>(duration.ToMilliseconds()); -} -DOMTimeMilliSec nsDOMNavigationTiming::DurationFromStart() -{ - return TimeStampToDOM(mozilla::TimeStamp::Now()); + TimeDuration duration = aStamp - mNavigationStart; + return GetNavigationStart() + static_cast<int64_t>(duration.ToMilliseconds()); } void nsDOMNavigationTiming::NotifyNavigationStart(DocShellState aDocShellState) { mNavigationStartHighRes = (double)PR_Now() / PR_USEC_PER_MSEC; - mNavigationStartTimeStamp = mozilla::TimeStamp::Now(); + mNavigationStart = TimeStamp::Now(); mDocShellHasBeenActiveSinceNavigationStart = (aDocShellState == DocShellState::eActive); } @@ -86,7 +78,7 @@ nsDOMNavigationTiming::NotifyFetchStart(nsIURI* aURI, Type aNavigationType) void nsDOMNavigationTiming::NotifyBeforeUnload() { - mBeforeUnloadStart = DurationFromStart(); + mBeforeUnloadStart = TimeStamp::Now(); } void @@ -99,105 +91,107 @@ nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* aOldURI) void nsDOMNavigationTiming::NotifyUnloadEventStart() { - mUnloadStart = DurationFromStart(); + mUnloadStart = TimeStamp::Now(); } void nsDOMNavigationTiming::NotifyUnloadEventEnd() { - mUnloadEnd = DurationFromStart(); + mUnloadEnd = TimeStamp::Now(); } void nsDOMNavigationTiming::NotifyLoadEventStart() { - if (!mLoadEventStartSet) { - mLoadEventStart = DurationFromStart(); - mLoadEventStartSet = true; + if (!mLoadEventStart.IsNull()) { + return; } + mLoadEventStart = TimeStamp::Now(); } void nsDOMNavigationTiming::NotifyLoadEventEnd() { - if (!mLoadEventEndSet) { - mLoadEventEnd = DurationFromStart(); - mLoadEventEndSet = true; + if (!mLoadEventEnd.IsNull()) { + return; } + mLoadEventEnd = TimeStamp::Now(); } void -nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI, mozilla::TimeStamp aValue) +nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI, TimeStamp aValue) { - if (!mDOMLoadingSet) { - mLoadedURI = aURI; - mDOMLoading = TimeStampToDOM(aValue); - mDOMLoadingSet = true; + if (!mDOMLoading.IsNull()) { + return; } + mLoadedURI = aURI; + mDOMLoading = aValue; } void nsDOMNavigationTiming::NotifyDOMLoading(nsIURI* aURI) { - if (!mDOMLoadingSet) { - mLoadedURI = aURI; - mDOMLoading = DurationFromStart(); - mDOMLoadingSet = true; + if (!mDOMLoading.IsNull()) { + return; } + mLoadedURI = aURI; + mDOMLoading = TimeStamp::Now(); } void nsDOMNavigationTiming::NotifyDOMInteractive(nsIURI* aURI) { - if (!mDOMInteractiveSet) { - mLoadedURI = aURI; - mDOMInteractive = DurationFromStart(); - mDOMInteractiveSet = true; + if (!mDOMInteractive.IsNull()) { + return; } + mLoadedURI = aURI; + mDOMInteractive = TimeStamp::Now(); } void nsDOMNavigationTiming::NotifyDOMComplete(nsIURI* aURI) { - if (!mDOMCompleteSet) { - mLoadedURI = aURI; - mDOMComplete = DurationFromStart(); - mDOMCompleteSet = true; + if (!mDOMComplete.IsNull()) { + return; } + mLoadedURI = aURI; + mDOMComplete = TimeStamp::Now(); } void nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI) { - if (!mDOMContentLoadedEventStartSet) { - mLoadedURI = aURI; - mDOMContentLoadedEventStart = DurationFromStart(); - mDOMContentLoadedEventStartSet = true; + if (!mDOMContentLoadedEventStart.IsNull()) { + return; } + + mLoadedURI = aURI; + mDOMContentLoadedEventStart = TimeStamp::Now(); } void nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI) { - if (!mDOMContentLoadedEventEndSet) { - mLoadedURI = aURI; - mDOMContentLoadedEventEnd = DurationFromStart(); - mDOMContentLoadedEventEndSet = true; + if (!mDOMContentLoadedEventEnd.IsNull()) { + return; } + + mLoadedURI = aURI; + mDOMContentLoadedEventEnd = TimeStamp::Now(); } void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mNavigationStartTimeStamp.IsNull()); + MOZ_ASSERT(!mNavigationStart.IsNull()); - if (!mNonBlankPaintTimeStamp.IsNull()) { + if (!mNonBlankPaint.IsNull()) { return; } - mNonBlankPaintTimeStamp = TimeStamp::Now(); - TimeDuration elapsed = mNonBlankPaintTimeStamp - mNavigationStartTimeStamp; + mNonBlankPaint = TimeStamp::Now(); + TimeDuration elapsed = mNonBlankPaint - mNavigationStart; if (profiler_is_active()) { nsAutoCString spec; @@ -212,8 +206,8 @@ nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() if (mDocShellHasBeenActiveSinceNavigationStart) { Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_NON_BLANK_PAINT_MS, - mNavigationStartTimeStamp, - mNonBlankPaintTimeStamp); + mNavigationStart, + mNonBlankPaint); } } @@ -224,24 +218,24 @@ nsDOMNavigationTiming::NotifyDocShellStateChanged(DocShellState aDocShellState) (aDocShellState == DocShellState::eActive); } -DOMTimeMilliSec -nsDOMNavigationTiming::GetUnloadEventStart() +mozilla::TimeStamp +nsDOMNavigationTiming::GetUnloadEventStartTimeStamp() const { nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false); if (NS_SUCCEEDED(rv)) { return mUnloadStart; } - return 0; + return mozilla::TimeStamp(); } -DOMTimeMilliSec -nsDOMNavigationTiming::GetUnloadEventEnd() +mozilla::TimeStamp +nsDOMNavigationTiming::GetUnloadEventEndTimeStamp() const { nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false); if (NS_SUCCEEDED(rv)) { return mUnloadEnd; } - return 0; + return mozilla::TimeStamp(); } diff --git a/dom/base/nsDOMNavigationTiming.h b/dom/base/nsDOMNavigationTiming.h index 9babece96..3be2527ca 100644 --- a/dom/base/nsDOMNavigationTiming.h +++ b/dom/base/nsDOMNavigationTiming.h @@ -47,38 +47,91 @@ public: mozilla::TimeStamp GetNavigationStartTimeStamp() const { - return mNavigationStartTimeStamp; + return mNavigationStart; + } + + DOMTimeMilliSec GetUnloadEventStart() + { + return TimeStampToDOM(GetUnloadEventStartTimeStamp()); + } + + DOMTimeMilliSec GetUnloadEventEnd() + { + return TimeStampToDOM(GetUnloadEventEndTimeStamp()); } - DOMTimeMilliSec GetUnloadEventStart(); - DOMTimeMilliSec GetUnloadEventEnd(); DOMTimeMilliSec GetDomLoading() const { - return mDOMLoading; + return TimeStampToDOM(mDOMLoading); } DOMTimeMilliSec GetDomInteractive() const { - return mDOMInteractive; + return TimeStampToDOM(mDOMInteractive); } DOMTimeMilliSec GetDomContentLoadedEventStart() const { - return mDOMContentLoadedEventStart; + return TimeStampToDOM(mDOMContentLoadedEventStart); } DOMTimeMilliSec GetDomContentLoadedEventEnd() const { - return mDOMContentLoadedEventEnd; + return TimeStampToDOM(mDOMContentLoadedEventEnd); } DOMTimeMilliSec GetDomComplete() const { - return mDOMComplete; + return TimeStampToDOM(mDOMComplete); } DOMTimeMilliSec GetLoadEventStart() const { - return mLoadEventStart; + return TimeStampToDOM(mLoadEventStart); } DOMTimeMilliSec GetLoadEventEnd() const { - return mLoadEventEnd; + return TimeStampToDOM(mLoadEventEnd); + } + DOMTimeMilliSec GetTimeToNonBlankPaint() const + { + return TimeStampToDOM(mNonBlankPaint); + } + + DOMHighResTimeStamp GetUnloadEventStartHighRes() + { + mozilla::TimeStamp stamp = GetUnloadEventStartTimeStamp(); + if (stamp.IsNull()) { + return 0; + } + return TimeStampToDOMHighRes(stamp); + } + DOMHighResTimeStamp GetUnloadEventEndHighRes() + { + mozilla::TimeStamp stamp = GetUnloadEventEndTimeStamp(); + if (stamp.IsNull()) { + return 0; + } + return TimeStampToDOMHighRes(stamp); + } + DOMHighResTimeStamp GetDomInteractiveHighRes() const + { + return TimeStampToDOMHighRes(mDOMInteractive); + } + DOMHighResTimeStamp GetDomContentLoadedEventStartHighRes() const + { + return TimeStampToDOMHighRes(mDOMContentLoadedEventStart); + } + DOMHighResTimeStamp GetDomContentLoadedEventEndHighRes() const + { + return TimeStampToDOMHighRes(mDOMContentLoadedEventEnd); + } + DOMHighResTimeStamp GetDomCompleteHighRes() const + { + return TimeStampToDOMHighRes(mDOMComplete); + } + DOMHighResTimeStamp GetLoadEventStartHighRes() const + { + return TimeStampToDOMHighRes(mLoadEventStart); + } + DOMHighResTimeStamp GetLoadEventEndHighRes() const + { + return TimeStampToDOMHighRes(mLoadEventEnd); } enum class DocShellState : uint8_t { @@ -108,9 +161,13 @@ public: DOMTimeMilliSec TimeStampToDOM(mozilla::TimeStamp aStamp) const; - inline DOMHighResTimeStamp TimeStampToDOMHighRes(mozilla::TimeStamp aStamp) + inline DOMHighResTimeStamp TimeStampToDOMHighRes(mozilla::TimeStamp aStamp) const { - mozilla::TimeDuration duration = aStamp - mNavigationStartTimeStamp; + MOZ_ASSERT(!aStamp.IsNull(), "The timestamp should not be null"); + if (aStamp.IsNull()) { + return 0; + } + mozilla::TimeDuration duration = aStamp - mNavigationStart; return duration.ToMilliseconds(); } @@ -120,37 +177,29 @@ private: void Clear(); + mozilla::TimeStamp GetUnloadEventStartTimeStamp() const; + mozilla::TimeStamp GetUnloadEventEndTimeStamp() const; + nsCOMPtr<nsIURI> mUnloadedURI; nsCOMPtr<nsIURI> mLoadedURI; Type mNavigationType; DOMHighResTimeStamp mNavigationStartHighRes; - mozilla::TimeStamp mNavigationStartTimeStamp; - mozilla::TimeStamp mNonBlankPaintTimeStamp; - DOMTimeMilliSec DurationFromStart(); - - DOMTimeMilliSec mBeforeUnloadStart; - DOMTimeMilliSec mUnloadStart; - DOMTimeMilliSec mUnloadEnd; - DOMTimeMilliSec mLoadEventStart; - DOMTimeMilliSec mLoadEventEnd; - - DOMTimeMilliSec mDOMLoading; - DOMTimeMilliSec mDOMInteractive; - DOMTimeMilliSec mDOMContentLoadedEventStart; - DOMTimeMilliSec mDOMContentLoadedEventEnd; - DOMTimeMilliSec mDOMComplete; - - // Booleans to keep track of what things we've already been notified - // about. We don't update those once we've been notified about them - // once. - bool mLoadEventStartSet : 1; - bool mLoadEventEndSet : 1; - bool mDOMLoadingSet : 1; - bool mDOMInteractiveSet : 1; - bool mDOMContentLoadedEventStartSet : 1; - bool mDOMContentLoadedEventEndSet : 1; - bool mDOMCompleteSet : 1; + mozilla::TimeStamp mNavigationStart; + mozilla::TimeStamp mNonBlankPaint; + + mozilla::TimeStamp mBeforeUnloadStart; + mozilla::TimeStamp mUnloadStart; + mozilla::TimeStamp mUnloadEnd; + mozilla::TimeStamp mLoadEventStart; + mozilla::TimeStamp mLoadEventEnd; + + mozilla::TimeStamp mDOMLoading; + mozilla::TimeStamp mDOMInteractive; + mozilla::TimeStamp mDOMContentLoadedEventStart; + mozilla::TimeStamp mDOMContentLoadedEventEnd; + mozilla::TimeStamp mDOMComplete; + bool mDocShellHasBeenActiveSinceNavigationStart : 1; }; diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index e4ae7ede8..aa4ef2ca3 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -704,6 +704,7 @@ GK_ATOM(onattributechanged, "onattributechanged") GK_ATOM(onattributereadreq, "onattributereadreq") GK_ATOM(onattributewritereq, "onattributewritereq") GK_ATOM(onaudioprocess, "onaudioprocess") +GK_ATOM(onauxclick, "onauxclick") GK_ATOM(onbeforecopy, "onbeforecopy") GK_ATOM(onbeforecut, "onbeforecut") GK_ATOM(onbeforepaste, "onbeforepaste") diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 3d5c44a78..738703ef1 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3124,8 +3124,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, newInnerWindow->mPerformance = Performance::CreateForMainThread(newInnerWindow->AsInner(), currentInner->mPerformance->GetDOMTiming(), - currentInner->mPerformance->GetChannel(), - currentInner->mPerformance->GetParentPerformance()); + currentInner->mPerformance->GetChannel()); } } @@ -4339,22 +4338,7 @@ nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded() timedChannel = nullptr; } if (timing) { - // 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). - Performance* parentPerformance = nullptr; - nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetScriptableParentOrNull(); - if (parentWindow) { - nsPIDOMWindowInner* parentInnerWindow = nullptr; - if (parentWindow) { - parentInnerWindow = parentWindow->GetCurrentInnerWindow(); - } - if (parentInnerWindow) { - parentPerformance = parentInnerWindow->GetPerformance(); - } - } - mPerformance = - Performance::CreateForMainThread(this, timing, timedChannel, - parentPerformance); + mPerformance = Performance::CreateForMainThread(this, timing, timedChannel); } } diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 33f5f7a44..7056658a7 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -259,8 +259,8 @@ TErrorResult<CleanupPolicy>::ThrowJSException(JSContext* cx, JS::Handle<JS::Valu // Make sure mJSException is initialized _before_ we try to root it. But // don't set it to exn yet, because we don't want to do that until after we // root. - mJSException.setUndefined(); - if (!js::AddRawValueRoot(cx, &mJSException, "TErrorResult::mJSException")) { + mJSException.asValueRef().setUndefined(); + if (!js::AddRawValueRoot(cx, &mJSException.asValueRef(), "TErrorResult::mJSException")) { // Don't use NS_ERROR_DOM_JS_EXCEPTION, because that indicates we have // in fact rooted mJSException. mResult = NS_ERROR_OUT_OF_MEMORY; @@ -289,7 +289,7 @@ TErrorResult<CleanupPolicy>::SetPendingJSException(JSContext* cx) mJSException = exception; // If JS_WrapValue failed, not much we can do about it... No matter // what, go ahead and unroot mJSException. - js::RemoveRawValueRoot(cx, &mJSException); + js::RemoveRawValueRoot(cx, &mJSException.asValueRef()); mResult = NS_OK; #ifdef DEBUG @@ -395,8 +395,8 @@ TErrorResult<CleanupPolicy>::ClearUnionData() if (IsJSException()) { JSContext* cx = dom::danger::GetJSContext(); MOZ_ASSERT(cx); - mJSException.setUndefined(); - js::RemoveRawValueRoot(cx, &mJSException); + mJSException.asValueRef().setUndefined(); + js::RemoveRawValueRoot(cx, &mJSException.asValueRef()); #ifdef DEBUG mUnionState = HasNothing; #endif // DEBUG @@ -439,13 +439,13 @@ TErrorResult<CleanupPolicy>::operator=(TErrorResult<CleanupPolicy>&& aRHS) } else if (aRHS.IsJSException()) { JSContext* cx = dom::danger::GetJSContext(); MOZ_ASSERT(cx); - mJSException.setUndefined(); - if (!js::AddRawValueRoot(cx, &mJSException, "TErrorResult::mJSException")) { + mJSException.asValueRef().setUndefined(); + if (!js::AddRawValueRoot(cx, &mJSException.asValueRef(), "TErrorResult::mJSException")) { MOZ_CRASH("Could not root mJSException, we're about to OOM"); } mJSException = aRHS.mJSException; - aRHS.mJSException.setUndefined(); - js::RemoveRawValueRoot(cx, &aRHS.mJSException); + aRHS.mJSException.asValueRef().setUndefined(); + js::RemoveRawValueRoot(cx, &aRHS.mJSException.asValueRef()); } else if (aRHS.IsDOMException()) { mDOMExceptionInfo = aRHS.mDOMExceptionInfo; aRHS.mDOMExceptionInfo = nullptr; @@ -497,7 +497,7 @@ TErrorResult<CleanupPolicy>::CloneTo(TErrorResult& aRv) const aRv.mUnionState = HasJSException; #endif JSContext* cx = dom::danger::GetJSContext(); - JS::Rooted<JS::Value> exception(cx, mJSException); + JS::Rooted<JS::Value> exception(cx, mJSException.asValueRef()); aRv.ThrowJSException(cx, exception); } } diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 3174c37dd..7a6668687 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1069,6 +1069,20 @@ class CGHeaders(CGWrapper): if parent: ancestors.append(parent) interfaceDeps.extend(ancestors) + + # Include parent interface headers needed for jsonifier code. + jsonInterfaceParents = [] + for desc in descriptors: + if not desc.operations['Jsonifier']: + continue + parent = desc.interface.parent + while parent: + parentDesc = desc.getDescriptor(parent.identifier.name) + if parentDesc.operations['Jsonifier']: + jsonInterfaceParents.append(parentDesc.interface) + parent = parent.parent + interfaceDeps.extend(jsonInterfaceParents) + bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps) # Grab all the implementation declaration files we need. diff --git a/dom/bindings/ErrorResult.h b/dom/bindings/ErrorResult.h index c45e7ea3b..7c3fc9e2f 100644 --- a/dom/bindings/ErrorResult.h +++ b/dom/bindings/ErrorResult.h @@ -461,7 +461,7 @@ private: // (and deallocated) by SetPendingDOMException. union { Message* mMessage; // valid when IsErrorWithMessage() - JS::Value mJSException; // valid when IsJSException() + JS::UninitializedValue mJSException; // valid when IsJSException() DOMExceptionInfo* mDOMExceptionInfo; // valid when IsDOMException() }; diff --git a/dom/console/Console.cpp b/dom/console/Console.cpp index 79e3eadc5..ff5a92167 100755 --- a/dom/console/Console.cpp +++ b/dom/console/Console.cpp @@ -1336,10 +1336,7 @@ Console::MethodInternal(JSContext* aCx, MethodName aMethodName, WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(workerPrivate); - TimeDuration duration = - mozilla::TimeStamp::Now() - workerPrivate->NowBaseTimeStamp(); - - monotonicTimer = TimerClamping::ReduceMsTimeValue(duration.ToMilliseconds()); + monotonicTimer = workerPrivate->TimeStampToDOMHighRes(TimeStamp::Now()); } } diff --git a/dom/console/Console.h b/dom/console/Console.h index b334d79f9..2f375c8eb 100644 --- a/dom/console/Console.h +++ b/dom/console/Console.h @@ -258,9 +258,8 @@ private: // the max number of timers is reached. // * aCx - the JSContext rooting aName. // * aName - this is (should be) the name of the timer as JS::Value. - // * aTimestamp - the monotonicTimer for this context (taken from - // window->performance.now() or from Now() - - // workerPrivate->NowBaseTimeStamp() in workers. + // * aTimestamp - the monotonicTimer for this context taken from + // performance.now(). // * aTimerLabel - This label will be populated with the aName converted to a // string. // * aTimerValue - the StartTimer value stored into (or taken from) @@ -290,9 +289,8 @@ private: // the aName timer doesn't exist in the mTimerRegistry. // * aCx - the JSContext rooting aName. // * aName - this is (should be) the name of the timer as JS::Value. - // * aTimestamp - the monotonicTimer for this context (taken from - // window->performance.now() or from Now() - - // workerPrivate->NowBaseTimeStamp() in workers. + // * aTimestamp - the monotonicTimer for this context taken from + // performance.now(). // * aTimerLabel - This label will be populated with the aName converted to a // string. // * aTimerDuration - the difference between aTimestamp and when the timer diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp index 7e19cd74d..4b9776c0a 100755 --- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -1146,16 +1146,11 @@ Event::TimeStampImpl() const return perf->GetDOMTiming()->TimeStampToDOMHighRes(mEvent->mTimeStamp); } - // For dedicated workers, we should make times relative to the navigation - // start of the document that created the worker, which is the same as the - // timebase for performance.now(). workers::WorkerPrivate* workerPrivate = workers::GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(workerPrivate); - TimeDuration duration = - mEvent->mTimeStamp - workerPrivate->NowBaseTimeStamp(); - return duration.ToMilliseconds(); + return workerPrivate->TimeStampToDOMHighRes(mEvent->mTimeStamp); } bool diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index ba2427623..891035c43 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -164,6 +164,10 @@ EVENT(change, eFormChange, EventNameType_HTMLXUL, eBasicEventClass) +EVENT(auxclick, + eMouseAuxClick, + EventNameType_All, + eMouseEventClass) EVENT(click, eMouseClick, EventNameType_All, diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index c6b304183..7bbfe21b7 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -492,6 +492,7 @@ IsMessageMouseUserActivity(EventMessage aMessage) return aMessage == eMouseMove || aMessage == eMouseUp || aMessage == eMouseDown || + aMessage == eMouseAuxClick || aMessage == eMouseDoubleClick || aMessage == eMouseClick || aMessage == eMouseActivate || @@ -4633,6 +4634,32 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent, } nsresult +EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent, + nsEventStatus* aStatus, + EventMessage aMessage, + nsIPresShell* aPresShell, + nsIContent* aMouseTarget, + nsWeakFrame aCurrentTarget, + bool aNoContentDispatch) +{ + WidgetMouseEvent event(aEvent->IsTrusted(), aMessage, + aEvent->mWidget, WidgetMouseEvent::eReal); + + event.mRefPoint = aEvent->mRefPoint; + event.mClickCount = aEvent->mClickCount; + event.mModifiers = aEvent->mModifiers; + event.buttons = aEvent->buttons; + event.mTime = aEvent->mTime; + event.mTimeStamp = aEvent->mTimeStamp; + event.mFlags.mNoContentDispatch = aNoContentDispatch; + event.button = aEvent->button; + event.inputSource = aEvent->inputSource; + + return aPresShell->HandleEventWithTarget(&event, aCurrentTarget, + aMouseTarget, aStatus); +} + +nsresult EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent, nsEventStatus* aStatus) { @@ -4651,17 +4678,7 @@ EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent, (aEvent->button == WidgetMouseEvent::eMiddleButton || aEvent->button == WidgetMouseEvent::eRightButton); - WidgetMouseEvent event(aEvent->IsTrusted(), eMouseClick, - aEvent->mWidget, WidgetMouseEvent::eReal); - event.mRefPoint = aEvent->mRefPoint; - event.mClickCount = aEvent->mClickCount; - event.mModifiers = aEvent->mModifiers; - event.buttons = aEvent->buttons; - event.mTime = aEvent->mTime; - event.mTimeStamp = aEvent->mTimeStamp; - event.mFlags.mNoContentDispatch = notDispatchToContents; - event.button = aEvent->button; - event.inputSource = aEvent->inputSource; + bool fireAuxClick = notDispatchToContents; nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell(); if (presShell) { @@ -4680,23 +4697,22 @@ EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent, // HandleEvent clears out mCurrentTarget which we might need again nsWeakFrame currentTarget = mCurrentTarget; - ret = presShell->HandleEventWithTarget(&event, currentTarget, - mouseContent, aStatus); + ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseClick, + presShell, mouseContent, currentTarget, + notDispatchToContents); + if (NS_SUCCEEDED(ret) && aEvent->mClickCount == 2 && mouseContent && mouseContent->IsInComposedDoc()) { //fire double click - WidgetMouseEvent event2(aEvent->IsTrusted(), eMouseDoubleClick, - aEvent->mWidget, WidgetMouseEvent::eReal); - event2.mRefPoint = aEvent->mRefPoint; - event2.mClickCount = aEvent->mClickCount; - event2.mModifiers = aEvent->mModifiers; - event2.buttons = aEvent->buttons; - event2.mFlags.mNoContentDispatch = notDispatchToContents; - event2.button = aEvent->button; - event2.inputSource = aEvent->inputSource; - - ret = presShell->HandleEventWithTarget(&event2, currentTarget, - mouseContent, aStatus); + ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseDoubleClick, + presShell, mouseContent, currentTarget, + notDispatchToContents); + } + if (NS_SUCCEEDED(ret) && mouseContent && fireAuxClick && + mouseContent->IsInComposedDoc()) { + ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseAuxClick, + presShell, mouseContent, currentTarget, + false); } } } diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h index 49ecf0586..d0461e7fa 100644 --- a/dom/events/EventStateManager.h +++ b/dom/events/EventStateManager.h @@ -415,6 +415,13 @@ protected: */ void UpdateDragDataTransfer(WidgetDragEvent* dragEvent); + static nsresult InitAndDispatchClickEvent(WidgetMouseEvent* aEvent, + nsEventStatus* aStatus, + EventMessage aMessage, + nsIPresShell* aPresShell, + nsIContent* aMouseTarget, + nsWeakFrame aCurrentTarget, + bool aNoContentDispatch); nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus); nsresult CheckForAndDispatchClick(WidgetMouseEvent* aEvent, nsEventStatus* aStatus); @@ -1046,6 +1053,7 @@ private: #define NS_EVENT_NEEDS_FRAME(event) \ (!(event)->HasPluginActivationEventMessage() && \ (event)->mMessage != eMouseClick && \ - (event)->mMessage != eMouseDoubleClick) + (event)->mMessage != eMouseDoubleClick && \ + (event)->mMessage != eMouseAuxClick) #endif // mozilla_EventStateManager_h_ diff --git a/dom/events/WheelHandlingHelper.cpp b/dom/events/WheelHandlingHelper.cpp index 7665ee922..81f2b6bfa 100644 --- a/dom/events/WheelHandlingHelper.cpp +++ b/dom/events/WheelHandlingHelper.cpp @@ -257,6 +257,7 @@ WheelTransaction::OnEvent(WidgetEvent* aEvent) case eMouseUp: case eMouseDown: case eMouseDoubleClick: + case eMouseAuxClick: case eMouseClick: case eContextMenu: case eDrop: diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini index e100e60a1..0397487bb 100644 --- a/dom/events/test/mochitest.ini +++ b/dom/events/test/mochitest.ini @@ -184,3 +184,4 @@ skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM [test_wheel_default_action.html] [test_bug687787.html] [test_bug1298970.html] +[test_bug1304044.html] diff --git a/dom/events/test/test_bug1304044.html b/dom/events/test/test_bug1304044.html new file mode 100644 index 000000000..0911dcf73 --- /dev/null +++ b/dom/events/test/test_bug1304044.html @@ -0,0 +1,133 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1304044 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1304044</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + var eventsFired = []; + var target; + var eventsExpected; + + function GetNodeString(node) { + if (node == window) + return "window"; + if (node == document) + return "document"; + if (node.id) + return node.id; + if (node.nodeName) + return node.nodeName; + return node; + } + + function TargetAndListener(listener, target) { + this.listener = listener; + this.target = target; + } + + TargetAndListener.prototype.toString = function() { + var targetName = GetNodeString(this.target); + var listenerName = GetNodeString(this.listener); + return "(listener: " + listenerName + ", target: " + targetName + ")"; + } + + var tests = [ + TestAuxClickBubblesForEventListener, + TestAuxClickBubblesForOnAuxClick, + ]; + + function CompareEvents(evt, expected) { + return evt && expected && evt.listener == expected.listener && + evt.target == expected.target; + } + + function ResetEventsFired() { + eventsFired = []; + } + + function ClearEventListeners() { + for (i in arguments) { + arguments[i].removeEventListener("auxclick", log_event); + } + } + + function ClickTarget(tgt) { + synthesizeMouseAtCenter(tgt, {type : "mousedown", button: 2}, window); + synthesizeMouseAtCenter(tgt, {type : "mouseup", button: 2}, window); + } + + function log_event(e) { + eventsFired[eventsFired.length] = new TargetAndListener(this, e.target); + } + + function CompareEventsToExpected(expected, actual) { + for (var i = 0; i < expected.length || i < actual.length; i++) { + ok(CompareEvents(actual[i], expected[i]), + "Auxclick receiver's don't match: TargetAndListener " + + i + ": Expected: " + expected[i] + ", Actual: " + actual[i]); + } + } + + function TestAuxClickBubblesForEventListener() { + target.addEventListener("auxclick", log_event); + document.addEventListener("auxclick", log_event); + window.addEventListener("auxclick", log_event); + + ClickTarget(target) + CompareEventsToExpected(eventsExpected, eventsFired); + ResetEventsFired(); + ClearEventListeners(target, document, window); + } + + function TestAuxClickBubblesForOnAuxClick() { + target.onauxclick = log_event; + document.onauxclick = log_event; + window.onauxclick = log_event; + + ClickTarget(target); + CompareEventsToExpected(eventsExpected, eventsFired); + ResetEventsFired(); + } + + function RunTests(){ + for (var i = 0; i < tests.length; i++) { + tests[i](); + } + } + + function Begin() { + target = document.getElementById("target"); + eventsExpected = [ + new TargetAndListener(target, target), + new TargetAndListener(document, target), + new TargetAndListener(window, target), + ]; + RunTests(); + target.remove(); + SimpleTest.finish(); + } + + window.onload = function() { + SimpleTest.waitForExplicitFinish(); + SimpleTest.executeSoon(Begin); + } + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1304044">Mozilla Bug 1304044</a> +<p id="display"> + <div id="target">Target</div> +</p> +<div id="content" style:"display:none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/events/test/test_eventTimeStamp.html b/dom/events/test/test_eventTimeStamp.html index 107a21f87..056203e92 100644 --- a/dom/events/test/test_eventTimeStamp.html +++ b/dom/events/test/test_eventTimeStamp.html @@ -17,7 +17,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=77992 <script type="text/js-worker" id="worker-src"> // Simply returns the event timestamp onmessage = function(evt) { - postMessage(evt.timeStamp); + postMessage(evt.timeStamp + performance.timeOrigin); } </script> <script type="text/js-worker" id="shared-worker-src"> @@ -25,7 +25,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=77992 onconnect = function(evt) { var port = evt.ports[0]; port.onmessage = function(messageEvt) { - port.postMessage(messageEvt.timeStamp); + port.postMessage(messageEvt.timeStamp + performance.timeOrigin); }; }; </script> @@ -57,9 +57,9 @@ function testRegularEvents() { finishTests(); return; } - var timeBeforeEvent = window.performance.now(); - window.addEventListener("load", function(evt) { - var timeAfterEvent = window.performance.now(); + var timeBeforeEvent = performance.now(); + addEventListener("load", function(evt) { + var timeAfterEvent = performance.now(); ok(evt.timeStamp >= timeBeforeEvent && evt.timeStamp <= timeAfterEvent, "Event timestamp (" + evt.timeStamp + ") is in expected range: (" + @@ -71,19 +71,18 @@ function testRegularEvents() { function testWorkerEvents() { var blob = new Blob([ document.getElementById("worker-src").textContent ], { type: "text/javascript" }); - var worker = new Worker(window.URL.createObjectURL(blob)); + var worker = new Worker(URL.createObjectURL(blob)); worker.onmessage = function(evt) { - var timeAfterEvent = window.performance.now(); - // Comparing times across timelines may break now - // ok(evt.data >= timeBeforeEvent && - // evt.data <= timeAfterEvent, + var timeAfterEvent = performance.now() + performance.timeOrigin; + ok(evt.data >= timeBeforeEvent && + evt.data <= timeAfterEvent, // "Event timestamp in dedicated worker (" + evt.data + // ") is in expected range: (" + // timeBeforeEvent + ", " + timeAfterEvent + ")"); worker.terminate(); testSharedWorkerEvents(); }; - var timeBeforeEvent = window.performance.now(); + var timeBeforeEvent = performance.now() + performance.timeOrigin; worker.postMessage(""); } @@ -93,17 +92,16 @@ function testSharedWorkerEvents() { { type: "text/javascript" }); // Delay creation of worker slightly so it is easier to distinguish // shared worker creation time from this document's navigation start - window.setTimeout(function() { - var timeBeforeWorkerCreation = window.performance.now(); - var worker = new SharedWorker(window.URL.createObjectURL(blob)); + setTimeout(function() { + var timeBeforeEvent = performance.now() + performance.timeOrigin; + var worker = new SharedWorker(URL.createObjectURL(blob)); worker.port.onmessage = function(evt) { - var timeAfterEvent = window.performance.now(); - // Comparing times across timelines may break now - // ok(evt.data >= 0 && - // evt.data <= timeAfterEvent - timeBeforeWorkerCreation, + var timeAfterEvent = performance.now() + performance.timeOrigin; + ok(evt.data >= timeBeforeEvent && + evt.data <= timeAfterEvent, // "Event timestamp in shared worker (" + evt.data + // ") is in expected range: (0, " + - // (timeAfterEvent - timeBeforeWorkerCreation) + ")"); + // timeBeforeEvent + ", " + timeAfterEvent + ")"); worker.port.close(); finishTests(); }; diff --git a/dom/media/MediaStreamTrack.cpp b/dom/media/MediaStreamTrack.cpp index 8ccdeb90c..75cdeb1d1 100644 --- a/dom/media/MediaStreamTrack.cpp +++ b/dom/media/MediaStreamTrack.cpp @@ -165,11 +165,15 @@ MediaStreamTrack::Destroy() mPrincipalHandleListener->Forget(); mPrincipalHandleListener = nullptr; } - for (auto l : mTrackListeners) { - RemoveListener(l); + // Remove all listeners -- avoid iterating over the list we're removing from + const nsTArray<RefPtr<MediaStreamTrackListener>> trackListeners(mTrackListeners); + for (auto listener : trackListeners) { + RemoveListener(listener); } - for (auto l : mDirectTrackListeners) { - RemoveDirectListener(l); + // Do the same as above for direct listeners + const nsTArray<RefPtr<DirectMediaStreamTrackListener>> directTrackListeners(mDirectTrackListeners); + for (auto listener : directTrackListeners) { + RemoveDirectListener(listener); } } diff --git a/dom/performance/Performance.cpp b/dom/performance/Performance.cpp index 8dc239b05..93a6b7313 100755 --- a/dom/performance/Performance.cpp +++ b/dom/performance/Performance.cpp @@ -13,12 +13,14 @@ #include "PerformanceMeasure.h" #include "PerformanceObserver.h" #include "PerformanceResourceTiming.h" +#include "PerformanceService.h" #include "PerformanceWorker.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/PerformanceBinding.h" #include "mozilla/dom/PerformanceEntryEvent.h" #include "mozilla/dom/PerformanceNavigationBinding.h" #include "mozilla/dom/PerformanceObserverBinding.h" +#include "mozilla/dom/PerformanceNavigationTiming.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/Preferences.h" #include "mozilla/TimerClamping.h" @@ -38,27 +40,6 @@ using namespace workers; namespace { -// Helper classes -class MOZ_STACK_CLASS PerformanceEntryComparator final -{ -public: - bool Equals(const PerformanceEntry* aElem1, - const PerformanceEntry* aElem2) const - { - MOZ_ASSERT(aElem1 && aElem2, - "Trying to compare null performance entries"); - return aElem1->StartTime() == aElem2->StartTime(); - } - - bool LessThan(const PerformanceEntry* aElem1, - const PerformanceEntry* aElem2) const - { - MOZ_ASSERT(aElem1 && aElem2, - "Trying to compare null performance entries"); - return aElem1->StartTime() < aElem2->StartTime(); - } -}; - class PrefEnabledRunnable final : public WorkerCheckAPIExposureOnMainThreadRunnable { @@ -103,14 +84,12 @@ NS_IMPL_RELEASE_INHERITED(Performance, DOMEventTargetHelper) /* static */ already_AddRefed<Performance> Performance::CreateForMainThread(nsPIDOMWindowInner* aWindow, nsDOMNavigationTiming* aDOMTiming, - nsITimedChannel* aChannel, - Performance* aParentPerformance) + nsITimedChannel* aChannel) { MOZ_ASSERT(NS_IsMainThread()); RefPtr<Performance> performance = - new PerformanceMainThread(aWindow, aDOMTiming, aChannel, - aParentPerformance); + new PerformanceMainThread(aWindow, aDOMTiming, aChannel); return performance.forget(); } @@ -142,6 +121,24 @@ Performance::Performance(nsPIDOMWindowInner* aWindow) Performance::~Performance() {} +DOMHighResTimeStamp +Performance::Now() const +{ + TimeDuration duration = TimeStamp::Now() - CreationTimeStamp(); + return RoundTime(duration.ToMilliseconds()); +} + +DOMHighResTimeStamp +Performance::TimeOrigin() +{ + if (!mPerformanceService) { + mPerformanceService = PerformanceService::GetOrCreate(); + } + + MOZ_ASSERT(mPerformanceService); + return mPerformanceService->TimeOrigin(CreationTimeStamp()); +} + JSObject* Performance::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { @@ -266,7 +263,7 @@ Performance::ClearMarks(const Optional<nsAString>& aName) DOMHighResTimeStamp Performance::ResolveTimestampFromName(const nsAString& aName, - ErrorResult& aRv) + ErrorResult& aRv) { AutoTArray<RefPtr<PerformanceEntry>, 1> arr; DOMHighResTimeStamp ts; diff --git a/dom/performance/Performance.h b/dom/performance/Performance.h index bc70589a5..4debecc90 100644 --- a/dom/performance/Performance.h +++ b/dom/performance/Performance.h @@ -24,6 +24,7 @@ namespace dom { class PerformanceEntry; class PerformanceNavigation; class PerformanceObserver; +class PerformanceService; class PerformanceTiming; namespace workers { @@ -45,8 +46,7 @@ public: static already_AddRefed<Performance> CreateForMainThread(nsPIDOMWindowInner* aWindow, nsDOMNavigationTiming* aDOMTiming, - nsITimedChannel* aChannel, - Performance* aParentPerformance); + nsITimedChannel* aChannel); static already_AddRefed<Performance> CreateForWorker(workers::WorkerPrivate* aWorkerPrivate); @@ -54,21 +54,23 @@ public: JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; - void GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval); + virtual void GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval); - void GetEntriesByType(const nsAString& aEntryType, - nsTArray<RefPtr<PerformanceEntry>>& aRetval); + virtual void GetEntriesByType(const nsAString& aEntryType, + nsTArray<RefPtr<PerformanceEntry>>& aRetval); - void GetEntriesByName(const nsAString& aName, - const Optional<nsAString>& aEntryType, - nsTArray<RefPtr<PerformanceEntry>>& aRetval); + virtual void GetEntriesByName(const nsAString& aName, + const Optional<nsAString>& aEntryType, + nsTArray<RefPtr<PerformanceEntry>>& aRetval); virtual void AddEntry(nsIHttpChannel* channel, nsITimedChannel* timedChannel) = 0; void ClearResourceTimings(); - virtual DOMHighResTimeStamp Now() const = 0; + DOMHighResTimeStamp Now() const; + + DOMHighResTimeStamp TimeOrigin(); void Mark(const nsAString& aName, ErrorResult& aRv); @@ -101,8 +103,6 @@ public: virtual nsITimedChannel* GetChannel() const = 0; - virtual Performance* GetParentPerformance() const = 0; - protected: Performance(); explicit Performance(nsPIDOMWindowInner* aWindow); @@ -126,10 +126,16 @@ protected: virtual DOMHighResTimeStamp CreationTime() const = 0; - virtual bool IsPerformanceTimingAttribute(const nsAString& aName) = 0; + virtual bool IsPerformanceTimingAttribute(const nsAString& aName) + { + return false; + } virtual DOMHighResTimeStamp - GetPerformanceTimingFromString(const nsAString& aTimingName) = 0; + GetPerformanceTimingFromString(const nsAString& aTimingName) + { + return 0; + } bool IsResourceEntryLimitReached() const { @@ -147,13 +153,15 @@ protected: nsTObserverArray<PerformanceObserver*> mObservers; -private: +protected: nsTArray<RefPtr<PerformanceEntry>> mUserEntries; nsTArray<RefPtr<PerformanceEntry>> mResourceEntries; uint64_t mResourceTimingBufferSize; static const uint64_t kDefaultResourceTimingBufferSize = 150; bool mPendingNotificationObserversTask; + + RefPtr<PerformanceService> mPerformanceService; }; } // namespace dom diff --git a/dom/performance/PerformanceEntry.h b/dom/performance/PerformanceEntry.h index bc4f84f1c..0af9f669e 100644 --- a/dom/performance/PerformanceEntry.h +++ b/dom/performance/PerformanceEntry.h @@ -90,6 +90,27 @@ protected: nsString mEntryType; }; +// Helper classes +class MOZ_STACK_CLASS PerformanceEntryComparator final +{ +public: + bool Equals(const PerformanceEntry* aElem1, + const PerformanceEntry* aElem2) const + { + MOZ_ASSERT(aElem1 && aElem2, + "Trying to compare null performance entries"); + return aElem1->StartTime() == aElem2->StartTime(); + } + + bool LessThan(const PerformanceEntry* aElem1, + const PerformanceEntry* aElem2) const + { + MOZ_ASSERT(aElem1 && aElem2, + "Trying to compare null performance entries"); + return aElem1->StartTime() < aElem2->StartTime(); + } +}; + } // namespace dom } // namespace mozilla diff --git a/dom/performance/PerformanceMainThread.cpp b/dom/performance/PerformanceMainThread.cpp index 86d42c5f8..64c06d3ea 100644 --- a/dom/performance/PerformanceMainThread.cpp +++ b/dom/performance/PerformanceMainThread.cpp @@ -6,6 +6,7 @@ #include "PerformanceMainThread.h" #include "PerformanceNavigation.h" +#include "nsICacheInfoChannel.h" namespace mozilla { namespace dom { @@ -16,7 +17,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread, Performance) NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming, mNavigation, - mParentPerformance) + mDocEntry) tmp->mMozMemory = nullptr; mozilla::DropJSObjects(this); NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -25,7 +26,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMainThread, Performance) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTiming, mNavigation, - mParentPerformance) + mDocEntry) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -45,12 +46,10 @@ NS_INTERFACE_MAP_END_INHERITING(Performance) PerformanceMainThread::PerformanceMainThread(nsPIDOMWindowInner* aWindow, nsDOMNavigationTiming* aDOMTiming, - nsITimedChannel* aChannel, - Performance* aParentPerformance) + nsITimedChannel* aChannel) : Performance(aWindow) , mDOMTiming(aDOMTiming) , mChannel(aChannel) - , mParentPerformance(aParentPerformance) { MOZ_ASSERT(aWindow, "Parent window object should be provided"); } @@ -78,7 +77,7 @@ PerformanceTiming* PerformanceMainThread::Timing() { if (!mTiming) { - // For navigation timing, the third argument (an nsIHtttpChannel) is null + // For navigation timing, the third argument (an nsIHttpChannel) 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 PerformanceTiming(this, mChannel, nullptr, @@ -108,12 +107,6 @@ PerformanceMainThread::Navigation() return mNavigation; } -DOMHighResTimeStamp -PerformanceMainThread::Now() const -{ - return RoundTime(GetDOMTiming()->TimeStampToDOMHighRes(TimeStamp::Now())); -} - /** * 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. @@ -161,27 +154,7 @@ PerformanceMainThread::AddEntry(nsIHttpChannel* channel, // The PerformanceResourceTiming object will use the PerformanceTiming // object to get all the required timings. RefPtr<PerformanceResourceTiming> performanceEntry = - new PerformanceResourceTiming(performanceTiming, this, entryName); - - nsAutoCString protocol; - channel->GetProtocolVersion(protocol); - performanceEntry->SetNextHopProtocol(NS_ConvertUTF8toUTF16(protocol)); - - uint64_t encodedBodySize = 0; - channel->GetEncodedBodySize(&encodedBodySize); - performanceEntry->SetEncodedBodySize(encodedBodySize); - - uint64_t transferSize = 0; - channel->GetTransferSize(&transferSize); - performanceEntry->SetTransferSize(transferSize); - - uint64_t decodedBodySize = 0; - channel->GetDecodedBodySize(&decodedBodySize); - if (decodedBodySize == 0) { - decodedBodySize = encodedBodySize; - } - performanceEntry->SetDecodedBodySize(decodedBodySize); - + new PerformanceResourceTiming(performanceTiming, this, entryName, channel); // If the initiator type had no valid value, then set it to the default // ("other") value. if (initiatorType.IsEmpty()) { @@ -335,5 +308,65 @@ PerformanceMainThread::CreationTime() const return GetDOMTiming()->GetNavigationStart(); } +void +PerformanceMainThread::EnsureDocEntry() +{ + if (!mDocEntry && nsContentUtils::IsPerformanceNavigationTimingEnabled()) { + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel); + RefPtr<PerformanceTiming> timing = + new PerformanceTiming(this, mChannel, nullptr, 0); + mDocEntry = new PerformanceNavigationTiming(timing, this, + httpChannel); + } +} + + +void +PerformanceMainThread::GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval) +{ + aRetval = mResourceEntries; + aRetval.AppendElements(mUserEntries); + + EnsureDocEntry(); + if (mDocEntry) { + aRetval.AppendElement(mDocEntry); + } + + aRetval.Sort(PerformanceEntryComparator()); +} + +void +PerformanceMainThread::GetEntriesByType(const nsAString& aEntryType, + nsTArray<RefPtr<PerformanceEntry>>& aRetval) +{ + if (aEntryType.EqualsLiteral("navigation")) { + aRetval.Clear(); + EnsureDocEntry(); + if (mDocEntry) { + aRetval.AppendElement(mDocEntry); + } + return; + } + + Performance::GetEntriesByType(aEntryType, aRetval); +} + +void +PerformanceMainThread::GetEntriesByName(const nsAString& aName, + const Optional<nsAString>& aEntryType, + nsTArray<RefPtr<PerformanceEntry>>& aRetval) +{ + if (aName.EqualsLiteral("document")) { + aRetval.Clear(); + EnsureDocEntry(); + if (mDocEntry) { + aRetval.AppendElement(mDocEntry); + } + return; + } + + Performance::GetEntriesByName(aName, aEntryType, aRetval); +} + } // dom namespace } // mozilla namespace diff --git a/dom/performance/PerformanceMainThread.h b/dom/performance/PerformanceMainThread.h index 84773f29b..9f0e185fc 100644 --- a/dom/performance/PerformanceMainThread.h +++ b/dom/performance/PerformanceMainThread.h @@ -17,16 +17,12 @@ class PerformanceMainThread final : public Performance public: PerformanceMainThread(nsPIDOMWindowInner* aWindow, nsDOMNavigationTiming* aDOMTiming, - nsITimedChannel* aChannel, - Performance* aParentPerformance); + nsITimedChannel* aChannel); NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PerformanceMainThread, Performance) - // Performance WebIDL methods - DOMHighResTimeStamp Now() const override; - virtual PerformanceTiming* Timing() override; virtual PerformanceNavigation* Navigation() override; @@ -51,10 +47,14 @@ public: return mChannel; } - virtual Performance* GetParentPerformance() const override - { - return mParentPerformance; - } + // The GetEntries* methods need to be overriden in order to add the + // the document entry of type navigation. + virtual void GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval) override; + virtual void GetEntriesByType(const nsAString& aEntryType, + nsTArray<RefPtr<PerformanceEntry>>& aRetval) override; + virtual void GetEntriesByName(const nsAString& aName, + const Optional<nsAString>& aEntryType, + nsTArray<RefPtr<PerformanceEntry>>& aRetval) override; protected: ~PerformanceMainThread(); @@ -72,12 +72,13 @@ protected: GetPerformanceTimingFromString(const nsAString& aTimingName) override; void DispatchBufferFullEvent() override; + void EnsureDocEntry(); + RefPtr<PerformanceEntry> mDocEntry; RefPtr<nsDOMNavigationTiming> mDOMTiming; nsCOMPtr<nsITimedChannel> mChannel; RefPtr<PerformanceTiming> mTiming; RefPtr<PerformanceNavigation> mNavigation; - RefPtr<Performance> mParentPerformance; JS::Heap<JSObject*> mMozMemory; }; diff --git a/dom/performance/PerformanceNavigationTiming.cpp b/dom/performance/PerformanceNavigationTiming.cpp new file mode 100644 index 000000000..4e00b2bb2 --- /dev/null +++ b/dom/performance/PerformanceNavigationTiming.cpp @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/PerformanceNavigationTiming.h" +#include "mozilla/dom/PerformanceNavigationTimingBinding.h" + +using namespace mozilla::dom; + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceNavigationTiming) +NS_INTERFACE_MAP_END_INHERITING(PerformanceResourceTiming) + +NS_IMPL_ADDREF_INHERITED(PerformanceNavigationTiming, PerformanceResourceTiming) +NS_IMPL_RELEASE_INHERITED(PerformanceNavigationTiming, PerformanceResourceTiming) + +JSObject* +PerformanceNavigationTiming::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return PerformanceNavigationTimingBinding::Wrap(aCx, this, aGivenProto); +} + +DOMHighResTimeStamp +PerformanceNavigationTiming::UnloadEventStart() const +{ + return mTiming->GetDOMTiming()->GetUnloadEventStartHighRes(); +} + +DOMHighResTimeStamp +PerformanceNavigationTiming::UnloadEventEnd() const +{ + return mTiming->GetDOMTiming()->GetUnloadEventEndHighRes(); +} + +DOMHighResTimeStamp +PerformanceNavigationTiming::DomInteractive() const +{ + return mTiming->GetDOMTiming()->GetDomInteractiveHighRes(); +} + +DOMHighResTimeStamp +PerformanceNavigationTiming::DomContentLoadedEventStart() const +{ + return mTiming->GetDOMTiming()->GetDomContentLoadedEventStartHighRes(); +} + +DOMHighResTimeStamp +PerformanceNavigationTiming::DomContentLoadedEventEnd() const +{ + return mTiming->GetDOMTiming()->GetDomContentLoadedEventEndHighRes(); +} + +DOMHighResTimeStamp +PerformanceNavigationTiming::DomComplete() const +{ + return mTiming->GetDOMTiming()->GetDomCompleteHighRes(); +} + +DOMHighResTimeStamp +PerformanceNavigationTiming::LoadEventStart() const +{ + return mTiming->GetDOMTiming()->GetLoadEventStartHighRes(); +} + +DOMHighResTimeStamp +PerformanceNavigationTiming::LoadEventEnd() const +{ + return mTiming->GetDOMTiming()->GetLoadEventEndHighRes(); +} + +NavigationType +PerformanceNavigationTiming::Type() const +{ + switch(mTiming->GetDOMTiming()->GetType()) { + case nsDOMNavigationTiming::TYPE_NAVIGATE: + return NavigationType::Navigate; + break; + case nsDOMNavigationTiming::TYPE_RELOAD: + return NavigationType::Reload; + break; + case nsDOMNavigationTiming::TYPE_BACK_FORWARD: + return NavigationType::Back_forward; + break; + default: + // The type is TYPE_RESERVED or some other value that was later added. + // We fallback to the default of Navigate. + return NavigationType::Navigate; + } +} + +uint16_t +PerformanceNavigationTiming::RedirectCount() const +{ + return mTiming->GetRedirectCount(); +} diff --git a/dom/performance/PerformanceNavigationTiming.h b/dom/performance/PerformanceNavigationTiming.h new file mode 100644 index 000000000..8555f1987 --- /dev/null +++ b/dom/performance/PerformanceNavigationTiming.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_PerformanceNavigationTiming_h___ +#define mozilla_dom_PerformanceNavigationTiming_h___ + +#include "nsCOMPtr.h" +#include "nsIChannel.h" +#include "nsITimedChannel.h" +#include "mozilla/dom/PerformanceResourceTiming.h" +#include "mozilla/dom/PerformanceNavigationTimingBinding.h" +#include "nsIHttpChannel.h" + +namespace mozilla { +namespace dom { + +// https://www.w3.org/TR/navigation-timing-2/#sec-PerformanceNavigationTiming +class PerformanceNavigationTiming final + : public PerformanceResourceTiming +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + // Note that aPerformanceTiming must be initalized with zeroTime = 0 + // so that timestamps are relative to startTime, as opposed to the + // performance.timing object for which timestamps are absolute and has a + // zeroTime initialized to navigationStart + explicit PerformanceNavigationTiming(PerformanceTiming* aPerformanceTiming, + Performance* aPerformance, + nsIHttpChannel* aChannel) + : PerformanceResourceTiming(aPerformanceTiming, aPerformance, + NS_LITERAL_STRING("document"), aChannel) { + SetEntryType(NS_LITERAL_STRING("navigation")); + SetInitiatorType(NS_LITERAL_STRING("navigation")); + } + + DOMHighResTimeStamp Duration() const override + { + return LoadEventEnd() - StartTime(); + } + + DOMHighResTimeStamp StartTime() const override + { + return 0; + } + + JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + + DOMHighResTimeStamp UnloadEventStart() const; + DOMHighResTimeStamp UnloadEventEnd() const; + + DOMHighResTimeStamp DomInteractive() const; + DOMHighResTimeStamp DomContentLoadedEventStart() const; + DOMHighResTimeStamp DomContentLoadedEventEnd() const; + DOMHighResTimeStamp DomComplete() const; + DOMHighResTimeStamp LoadEventStart() const; + DOMHighResTimeStamp LoadEventEnd() const; + NavigationType Type() const; + uint16_t RedirectCount() const; + +private: + ~PerformanceNavigationTiming() {} +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_PerformanceNavigationTiming_h___ diff --git a/dom/performance/PerformanceObserver.cpp b/dom/performance/PerformanceObserver.cpp index 11dd30ac2..d02acfb09 100644 --- a/dom/performance/PerformanceObserver.cpp +++ b/dom/performance/PerformanceObserver.cpp @@ -114,12 +114,13 @@ PerformanceObserver::Notify() RefPtr<PerformanceObserverEntryList> list = new PerformanceObserverEntryList(this, mQueuedEntries); + mQueuedEntries.Clear(); + ErrorResult rv; mCallback->Call(this, *list, *this, rv); if (NS_WARN_IF(rv.Failed())) { rv.SuppressException(); } - mQueuedEntries.Clear(); } void @@ -170,6 +171,17 @@ PerformanceObserver::Observe(const PerformanceObserverInit& aOptions, mEntryTypes.SwapElements(validEntryTypes); mPerformance->AddObserver(this); + + if (aOptions.mBuffered) { + for (auto entryType : mEntryTypes) { + nsTArray<RefPtr<PerformanceEntry>> existingEntries; + mPerformance->GetEntriesByType(entryType, existingEntries); + if (!existingEntries.IsEmpty()) { + mQueuedEntries.AppendElements(existingEntries); + } + } + } + mConnected = true; } diff --git a/dom/performance/PerformanceObserverEntryList.cpp b/dom/performance/PerformanceObserverEntryList.cpp index 349103f08..20e818f3d 100644 --- a/dom/performance/PerformanceObserverEntryList.cpp +++ b/dom/performance/PerformanceObserverEntryList.cpp @@ -66,6 +66,7 @@ PerformanceObserverEntryList::GetEntries( aRetval.AppendElement(entry); } + aRetval.Sort(PerformanceEntryComparator()); } void @@ -79,6 +80,7 @@ PerformanceObserverEntryList::GetEntriesByType( aRetval.AppendElement(entry); } } + aRetval.Sort(PerformanceEntryComparator()); } void @@ -88,9 +90,18 @@ PerformanceObserverEntryList::GetEntriesByName( nsTArray<RefPtr<PerformanceEntry>>& aRetval) { aRetval.Clear(); + const bool typePassed = aEntryType.WasPassed(); for (const RefPtr<PerformanceEntry>& entry : mEntries) { - if (entry->GetName().Equals(aName)) { - aRetval.AppendElement(entry); + if (!entry->GetName().Equals(aName)) { + continue; } + + if (typePassed && + !entry->GetEntryType().Equals(aEntryType.Value())) { + continue; + } + + aRetval.AppendElement(entry); } + aRetval.Sort(PerformanceEntryComparator()); } diff --git a/dom/performance/PerformanceResourceTiming.cpp b/dom/performance/PerformanceResourceTiming.cpp index 60a20ca28..2eaa4eb9a 100644 --- a/dom/performance/PerformanceResourceTiming.cpp +++ b/dom/performance/PerformanceResourceTiming.cpp @@ -25,7 +25,8 @@ NS_IMPL_RELEASE_INHERITED(PerformanceResourceTiming, PerformanceEntry) PerformanceResourceTiming::PerformanceResourceTiming(PerformanceTiming* aPerformanceTiming, Performance* aPerformance, - const nsAString& aName) + const nsAString& aName, + nsIHttpChannel* aChannel) : PerformanceEntry(aPerformance, aName, NS_LITERAL_STRING("resource")), mTiming(aPerformanceTiming), mEncodedBodySize(0), @@ -33,6 +34,34 @@ PerformanceResourceTiming::PerformanceResourceTiming(PerformanceTiming* aPerform mDecodedBodySize(0) { MOZ_ASSERT(aPerformance, "Parent performance object should be provided"); + SetPropertiesFromChannel(aChannel); +} + +void +PerformanceResourceTiming::SetPropertiesFromChannel(nsIHttpChannel* aChannel) +{ + if (!aChannel) { + return; + } + + nsAutoCString protocol; + Unused << aChannel->GetProtocolVersion(protocol); + SetNextHopProtocol(NS_ConvertUTF8toUTF16(protocol)); + + uint64_t encodedBodySize = 0; + Unused << aChannel->GetEncodedBodySize(&encodedBodySize); + SetEncodedBodySize(encodedBodySize); + + uint64_t transferSize = 0; + Unused << aChannel->GetTransferSize(&transferSize); + SetTransferSize(transferSize); + + uint64_t decodedBodySize = 0; + Unused << aChannel->GetDecodedBodySize(&decodedBodySize); + if (decodedBodySize == 0) { + decodedBodySize = encodedBodySize; + } + SetDecodedBodySize(decodedBodySize); } PerformanceResourceTiming::~PerformanceResourceTiming() @@ -42,8 +71,22 @@ PerformanceResourceTiming::~PerformanceResourceTiming() DOMHighResTimeStamp PerformanceResourceTiming::StartTime() const { - DOMHighResTimeStamp startTime = mTiming->RedirectStartHighRes(); - return startTime ? startTime : mTiming->FetchStartHighRes(); + // Force the start time to be the earliest of: + // - RedirectStart + // - WorkerStart + // - AsyncOpen + // Ignore zero values. The RedirectStart and WorkerStart values + // can come from earlier redirected channels prior to the AsyncOpen + // time being recorded. + DOMHighResTimeStamp redirect = mTiming->RedirectStartHighRes(); + redirect = redirect ? redirect : DBL_MAX; + + DOMHighResTimeStamp worker = mTiming->WorkerStartHighRes(); + worker = worker ? worker : DBL_MAX; + + DOMHighResTimeStamp asyncOpen = mTiming->AsyncOpenHighRes(); + + return std::min(asyncOpen, std::min(redirect, worker)); } JSObject* diff --git a/dom/performance/PerformanceResourceTiming.h b/dom/performance/PerformanceResourceTiming.h index 2dd6b4a06..98a03327e 100644 --- a/dom/performance/PerformanceResourceTiming.h +++ b/dom/performance/PerformanceResourceTiming.h @@ -18,7 +18,7 @@ namespace mozilla { namespace dom { // http://www.w3.org/TR/resource-timing/#performanceresourcetiming -class PerformanceResourceTiming final : public PerformanceEntry +class PerformanceResourceTiming : public PerformanceEntry { public: typedef mozilla::TimeStamp TimeStamp; @@ -30,7 +30,8 @@ public: PerformanceResourceTiming(PerformanceTiming* aPerformanceTiming, Performance* aPerformance, - const nsAString& aName); + const nsAString& aName, + nsIHttpChannel* aChannel = nullptr); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; @@ -62,6 +63,12 @@ public: mNextHopProtocol = aNextHopProtocol; } + DOMHighResTimeStamp WorkerStart() const { + return mTiming && mTiming->TimingAllowed() + ? mTiming->WorkerStartHighRes() + : 0; + } + DOMHighResTimeStamp FetchStart() const { return mTiming ? mTiming->FetchStartHighRes() @@ -170,6 +177,7 @@ public: protected: virtual ~PerformanceResourceTiming(); + void SetPropertiesFromChannel(nsIHttpChannel* aChannel); nsString mInitiatorType; nsString mNextHopProtocol; diff --git a/dom/performance/PerformanceService.cpp b/dom/performance/PerformanceService.cpp new file mode 100644 index 000000000..cf119af89 --- /dev/null +++ b/dom/performance/PerformanceService.cpp @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "PerformanceService.h" + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/StaticMutex.h" +#include "mozilla/StaticPtr.h" + +namespace mozilla { +namespace dom { + +static StaticRefPtr<PerformanceService> gPerformanceService; +static StaticMutex gPerformanceServiceMutex; + +/* static */ PerformanceService* +PerformanceService::GetOrCreate() +{ + StaticMutexAutoLock al(gPerformanceServiceMutex); + + if (!gPerformanceService) { + gPerformanceService = new PerformanceService(); + ClearOnShutdown(&gPerformanceService); + } + + return gPerformanceService; +} + +DOMHighResTimeStamp +PerformanceService::TimeOrigin(const TimeStamp& aCreationTimeStamp) const +{ + return (aCreationTimeStamp - mCreationTimeStamp).ToMilliseconds() + + (mCreationEpochTime / PR_USEC_PER_MSEC); +} + +PerformanceService::PerformanceService() +{ + mCreationTimeStamp = TimeStamp::Now(); + mCreationEpochTime = PR_Now(); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/performance/PerformanceService.h b/dom/performance/PerformanceService.h new file mode 100644 index 000000000..9abbd674d --- /dev/null +++ b/dom/performance/PerformanceService.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef dom_performance_PerformanceService_h +#define dom_performance_PerformanceService_h + +#include "mozilla/TimeStamp.h" +#include "nsCOMPtr.h" +#include "nsDOMNavigationTiming.h" + +namespace mozilla { +namespace dom { + +// This class is thread-safe. + +// We use this singleton for having the correct value of performance.timeOrigin. +// This value must be calculated on top of the pair: +// - mCreationTimeStamp (monotonic clock) +// - mCreationEpochTime (unix epoch time) +// These 2 values must be taken "at the same time" in order to be used +// correctly. + +class PerformanceService +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PerformanceService) + + static PerformanceService* + GetOrCreate(); + + DOMHighResTimeStamp + TimeOrigin(const TimeStamp& aCreationTimeStamp) const; + +private: + PerformanceService(); + ~PerformanceService() = default; + + TimeStamp mCreationTimeStamp; + PRTime mCreationEpochTime; +}; + +} // dom namespace +} // mozilla namespace + +#endif // dom_performance_PerformanceService_h diff --git a/dom/performance/PerformanceTiming.cpp b/dom/performance/PerformanceTiming.cpp index e2f76a21f..887a23938 100755 --- a/dom/performance/PerformanceTiming.cpp +++ b/dom/performance/PerformanceTiming.cpp @@ -73,6 +73,7 @@ PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel) { if (aChannel) { aChannel->GetAsyncOpen(&mAsyncOpen); + aChannel->GetDispatchFetchEventStart(&mWorkerStart); aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin); aChannel->GetRedirectCount(&mRedirectCount); aChannel->GetRedirectStart(&mRedirectStart); @@ -88,31 +89,39 @@ PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel) aChannel->GetResponseEnd(&mResponseEnd); aChannel->GetCacheReadEnd(&mCacheReadEnd); - // the performance timing api essentially requires that the event timestamps - // are >= asyncOpen().. but in truth the browser engages in a number of - // speculative activities that sometimes mean connections and lookups begin - // earlier. Workaround that here by just using asyncOpen as the minimum - // timestamp for dns and connection info. + // The performance timing api essentially requires that the event timestamps + // have a strict relation with each other. The truth, however, is the browser + // engages in a number of speculative activities that sometimes mean connections + // and lookups begin at different times. Workaround that here by clamping + // these values to what we expect FetchStart to be. This means the later of + // AsyncOpen or WorkerStart times. if (!mAsyncOpen.IsNull()) { - if (!mDomainLookupStart.IsNull() && mDomainLookupStart < mAsyncOpen) { - mDomainLookupStart = mAsyncOpen; + // We want to clamp to the expected FetchStart value. This is later of + // the AsyncOpen and WorkerStart values. + const TimeStamp* clampTime = &mAsyncOpen; + if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) { + clampTime = &mWorkerStart; } - if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < mAsyncOpen) { - mDomainLookupEnd = mAsyncOpen; + if (!mDomainLookupStart.IsNull() && mDomainLookupStart < *clampTime) { + mDomainLookupStart = *clampTime; } - if (!mConnectStart.IsNull() && mConnectStart < mAsyncOpen) { - mConnectStart = mAsyncOpen; + if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < *clampTime) { + mDomainLookupEnd = *clampTime; + } + + if (!mConnectStart.IsNull() && mConnectStart < *clampTime) { + mConnectStart = *clampTime; } if (mSecureConnection && !mSecureConnectionStart.IsNull() && - mSecureConnectionStart < mAsyncOpen) { - mSecureConnectionStart = mAsyncOpen; + mSecureConnectionStart < *clampTime) { + mSecureConnectionStart = *clampTime; } - if (!mConnectEnd.IsNull() && mConnectEnd < mAsyncOpen) { - mConnectEnd = mAsyncOpen; + if (!mConnectEnd.IsNull() && mConnectEnd < *clampTime) { + mConnectEnd = *clampTime; } } } @@ -131,9 +140,13 @@ PerformanceTiming::FetchStartHighRes() } MOZ_ASSERT(!mAsyncOpen.IsNull(), "The fetch start time stamp should always be " "valid if the performance timing is enabled"); - mFetchStart = (!mAsyncOpen.IsNull()) - ? TimeStampToDOMHighRes(mAsyncOpen) - : 0.0; + if (!mAsyncOpen.IsNull()) { + if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) { + mFetchStart = TimeStampToDOMHighRes(mWorkerStart); + } else { + mFetchStart = TimeStampToDOMHighRes(mAsyncOpen); + } + } } return TimerClamping::ReduceMsTimeValue(mFetchStart); } @@ -180,7 +193,7 @@ PerformanceTiming::TimingAllowed() const return mTimingAllowed; } -uint16_t +uint8_t PerformanceTiming::GetRedirectCount() const { if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { @@ -205,6 +218,26 @@ PerformanceTiming::ShouldReportCrossOriginRedirect() const return (mRedirectCount != 0) && mReportCrossOriginRedirect; } +DOMHighResTimeStamp +PerformanceTiming::AsyncOpenHighRes() +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || + mAsyncOpen.IsNull()) { + return mZeroTime; + } + return TimeStampToReducedDOMHighResOrFetchStart(mAsyncOpen); +} + +DOMHighResTimeStamp +PerformanceTiming::WorkerStartHighRes() +{ + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() || + mWorkerStart.IsNull()) { + return mZeroTime; + } + return TimeStampToReducedDOMHighResOrFetchStart(mWorkerStart); +} + /** * RedirectStartHighRes() is used by both the navigation timing and the * resource timing. Since, navigation timing and resource timing check and diff --git a/dom/performance/PerformanceTiming.h b/dom/performance/PerformanceTiming.h index fc7e7d5bd..435e1bca1 100755 --- a/dom/performance/PerformanceTiming.h +++ b/dom/performance/PerformanceTiming.h @@ -139,7 +139,7 @@ public: return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetUnloadEventEnd()); } - uint16_t GetRedirectCount() const; + uint8_t GetRedirectCount() const; // Checks if the resource is either same origin as the page that started // the load, or if the response contains the Timing-Allow-Origin header @@ -155,7 +155,12 @@ public: // the timing-allow-origin check in HttpBaseChannel::TimingAllowCheck bool ShouldReportCrossOriginRedirect() const; + // The last channel's AsyncOpen time. This may occur before the FetchStart + // in some cases. + DOMHighResTimeStamp AsyncOpenHighRes(); + // High resolution (used by resource timing) + DOMHighResTimeStamp WorkerStartHighRes(); DOMHighResTimeStamp FetchStartHighRes(); DOMHighResTimeStamp RedirectStartHighRes(); DOMHighResTimeStamp RedirectEndHighRes(); @@ -237,6 +242,14 @@ public: return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetLoadEventEnd()); } + DOMTimeMilliSec TimeToNonBlankPaint() const + { + if (!nsContentUtils::IsPerformanceTimingEnabled()) { + return 0; + } + return TimerClamping::ReduceMsTimeValue(GetDOMTiming()->GetTimeToNonBlankPaint()); + } + private: ~PerformanceTiming(); @@ -253,6 +266,7 @@ private: DOMHighResTimeStamp mZeroTime; TimeStamp mAsyncOpen; + TimeStamp mWorkerStart; TimeStamp mRedirectStart; TimeStamp mRedirectEnd; TimeStamp mDomainLookupStart; @@ -265,7 +279,7 @@ private: TimeStamp mCacheReadStart; TimeStamp mResponseEnd; TimeStamp mCacheReadEnd; - uint16_t mRedirectCount; + uint8_t mRedirectCount; bool mTimingAllowed; bool mAllRedirectsSameOrigin; bool mInitialized; diff --git a/dom/performance/PerformanceWorker.cpp b/dom/performance/PerformanceWorker.cpp index 85ca2ccd8..f10c58446 100644 --- a/dom/performance/PerformanceWorker.cpp +++ b/dom/performance/PerformanceWorker.cpp @@ -23,37 +23,6 @@ PerformanceWorker::~PerformanceWorker() mWorkerPrivate->AssertIsOnWorkerThread(); } -DOMHighResTimeStamp -PerformanceWorker::Now() const -{ - TimeDuration duration = - TimeStamp::Now() - mWorkerPrivate->NowBaseTimeStamp(); - return RoundTime(duration.ToMilliseconds()); -} - -// To be removed once bug 1124165 lands -bool -PerformanceWorker::IsPerformanceTimingAttribute(const nsAString& aName) -{ - // In workers we just support navigationStart. - return aName.EqualsASCII("navigationStart"); -} - -DOMHighResTimeStamp -PerformanceWorker::GetPerformanceTimingFromString(const nsAString& aProperty) -{ - if (!IsPerformanceTimingAttribute(aProperty)) { - return 0; - } - - if (aProperty.EqualsLiteral("navigationStart")) { - return mWorkerPrivate->NowBaseTime(); - } - - MOZ_CRASH("IsPerformanceTimingAttribute and GetPerformanceTimingFromString are out of sync"); - return 0; -} - void PerformanceWorker::InsertUserEntry(PerformanceEntry* aEntry) { @@ -72,13 +41,13 @@ PerformanceWorker::InsertUserEntry(PerformanceEntry* aEntry) TimeStamp PerformanceWorker::CreationTimeStamp() const { - return mWorkerPrivate->NowBaseTimeStamp(); + return mWorkerPrivate->CreationTimeStamp(); } DOMHighResTimeStamp PerformanceWorker::CreationTime() const { - return mWorkerPrivate->NowBaseTime(); + return mWorkerPrivate->CreationTime(); } } // dom namespace diff --git a/dom/performance/PerformanceWorker.h b/dom/performance/PerformanceWorker.h index 7eef0d974..346bdd026 100644 --- a/dom/performance/PerformanceWorker.h +++ b/dom/performance/PerformanceWorker.h @@ -21,9 +21,6 @@ class PerformanceWorker final : public Performance public: explicit PerformanceWorker(workers::WorkerPrivate* aWorkerPrivate); - // Performance WebIDL methods - DOMHighResTimeStamp Now() const override; - virtual PerformanceTiming* Timing() override { MOZ_CRASH("This should not be called on workers."); @@ -64,12 +61,6 @@ public: return nullptr; } - virtual Performance* GetParentPerformance() const override - { - MOZ_CRASH("This should not be called on workers."); - return nullptr; - } - protected: ~PerformanceWorker(); @@ -80,11 +71,6 @@ protected: void InsertUserEntry(PerformanceEntry* aEntry) override; - bool IsPerformanceTimingAttribute(const nsAString& aName) override; - - DOMHighResTimeStamp - GetPerformanceTimingFromString(const nsAString& aTimingName) override; - void DispatchBufferFullEvent() override { MOZ_CRASH("This should not be called on workers."); diff --git a/dom/performance/moz.build b/dom/performance/moz.build index 3286a0a4c..e1f96fec8 100644 --- a/dom/performance/moz.build +++ b/dom/performance/moz.build @@ -10,9 +10,11 @@ EXPORTS.mozilla.dom += [ 'PerformanceMark.h', 'PerformanceMeasure.h', 'PerformanceNavigation.h', + 'PerformanceNavigationTiming.h', 'PerformanceObserver.h', 'PerformanceObserverEntryList.h', 'PerformanceResourceTiming.h', + 'PerformanceService.h', 'PerformanceTiming.h', ] @@ -23,9 +25,11 @@ UNIFIED_SOURCES += [ 'PerformanceMark.cpp', 'PerformanceMeasure.cpp', 'PerformanceNavigation.cpp', + 'PerformanceNavigationTiming.cpp', 'PerformanceObserver.cpp', 'PerformanceObserverEntryList.cpp', 'PerformanceResourceTiming.cpp', + 'PerformanceService.cpp', 'PerformanceTiming.cpp', 'PerformanceWorker.cpp', ] diff --git a/dom/performance/tests/mochitest.ini b/dom/performance/tests/mochitest.ini index 18f7f0e45..bee0b2e70 100644 --- a/dom/performance/tests/mochitest.ini +++ b/dom/performance/tests/mochitest.ini @@ -1,13 +1,11 @@ [DEFAULT] support-files = - performance_observer.html test_performance_observer.js test_performance_user_timing.js test_worker_performance_now.js worker_performance_user_timing.js worker_performance_observer.js sharedworker_performance_user_timing.js - worker_performance_observer.html [test_performance_observer.html] [test_performance_user_timing.html] @@ -15,3 +13,4 @@ support-files = [test_worker_observer.html] [test_sharedWorker_performance_user_timing.html] [test_worker_performance_now.html] +[test_timeOrigin.html] diff --git a/dom/performance/tests/performance_observer.html b/dom/performance/tests/performance_observer.html deleted file mode 100644 index b8ced9296..000000000 --- a/dom/performance/tests/performance_observer.html +++ /dev/null @@ -1,74 +0,0 @@ -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<!DOCTYPE html> -<meta charset=utf-8> -<html> -<head> -<title>Test for performance observer</title> -<script> -'use strict'; -[ - "promise_test", "test", "setup", - "assert_true", "assert_equals", "assert_array_equals", - "assert_throws", "assert_unreached" -].forEach(func => { - window[func] = opener[func].bind(opener); -}); -function done() { - opener.add_completion_callback(() => { - self.close(); - }); - opener.done(); -} - -</script> -<script src="test_performance_observer.js"></script> -</head> -<body> -<div id="log"></div> -<script> -function makeXHR(aUrl) { - var xmlhttp = new XMLHttpRequest(); - xmlhttp.open("get", aUrl, true); - xmlhttp.send(); -} - -promise_test(t => { - var promise = new Promise(resolve => { - performance.clearResourceTimings(); - - var observer = new PerformanceObserver(list => resolve(list)); - observer.observe({entryTypes: ['resource']}); - t.add_cleanup(() => observer.disconnect()); - }); - - makeXHR("test-data.json"); - - return promise.then(list => { - assert_equals(list.getEntries().length, 1); - assert_array_equals(list.getEntries(), - performance.getEntriesByType("resource"), - "Observed 'resource' entries should equal to entries obtained by getEntriesByType."); - - // getEntries filtering tests - assert_array_equals(list.getEntries({name: "http://mochi.test:8888/tests/dom/base/test/test-data.json"}), - performance.getEntriesByName("http://mochi.test:8888/tests/dom/base/test/test-data.json"), - "getEntries with name filter should return correct results."); - assert_array_equals(list.getEntries({entryType: "resource"}), - performance.getEntriesByType("resource"), - "getEntries with entryType filter should return correct results."); - assert_array_equals(list.getEntries({initiatorType: "xmlhttprequest"}), - performance.getEntriesByType("resource"), - "getEntries with initiatorType filter should return correct results."); - assert_array_equals(list.getEntries({initiatorType: "link"}), - [], - "getEntries with non-existent initiatorType filter should return an empty array."); - }); -}, "resource-timing test"); - -done(); - -</script> -</body> diff --git a/dom/performance/tests/test_performance_observer.html b/dom/performance/tests/test_performance_observer.html index d36878315..7df881bd4 100644 --- a/dom/performance/tests/test_performance_observer.html +++ b/dom/performance/tests/test_performance_observer.html @@ -3,15 +3,55 @@ http://creativecommons.org/publicdomain/zero/1.0/ --> <!DOCTYPE html> +<html> +<head> <meta charset=utf-8> <title>Test for performance observer</title> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<div id=log></div> +</head> +<body> +<div id="log"></div> +<script src="test_performance_observer.js"></script> <script> -'use strict'; -SpecialPowers.pushPrefEnv({"set": [["dom.enable_performance_observer", true]]}, - function() { - window.open("performance_observer.html"); - }); +function makeXHR(aUrl) { + var xmlhttp = new XMLHttpRequest(); + xmlhttp.open("get", aUrl, true); + xmlhttp.send(); +} + +promise_test(t => { + var promise = new Promise(resolve => { + performance.clearResourceTimings(); + + var observer = new PerformanceObserver(list => resolve(list)); + observer.observe({entryTypes: ['resource']}); + t.add_cleanup(() => observer.disconnect()); + }); + + makeXHR("test-data.json"); + + return promise.then(list => { + assert_equals(list.getEntries().length, 1); + assert_array_equals(list.getEntries(), + performance.getEntriesByType("resource"), + "Observed 'resource' entries should equal to entries obtained by getEntriesByType."); + + // getEntries filtering tests + assert_array_equals(list.getEntries({name: "http://mochi.test:8888/tests/dom/base/test/test-data.json"}), + performance.getEntriesByName("http://mochi.test:8888/tests/dom/base/test/test-data.json"), + "getEntries with name filter should return correct results."); + assert_array_equals(list.getEntries({entryType: "resource"}), + performance.getEntriesByType("resource"), + "getEntries with entryType filter should return correct results."); + assert_array_equals(list.getEntries({initiatorType: "xmlhttprequest"}), + performance.getEntriesByType("resource"), + "getEntries with initiatorType filter should return correct results."); + assert_array_equals(list.getEntries({initiatorType: "link"}), + [], + "getEntries with non-existent initiatorType filter should return an empty array."); + }); +}, "resource-timing test"); + </script> +</body> diff --git a/dom/performance/tests/test_performance_user_timing.js b/dom/performance/tests/test_performance_user_timing.js index cd8261bbd..a15dbebb6 100644 --- a/dom/performance/tests/test_performance_user_timing.js +++ b/dom/performance/tests/test_performance_user_timing.js @@ -126,15 +126,18 @@ var steps = [ }, // Test measure function () { - ok(true, "Running measure addition with no start/end time test"); - performance.measure("test"); - var measures = performance.getEntriesByType("measure"); - is(measures.length, 1, "number of measures should be 1"); - var measure = measures[0]; - is(measure.name, "test", "measure name should be 'test'"); - is(measure.entryType, "measure", "measure type should be 'measure'"); - is(measure.startTime, 0, "measure start time should be zero"); - ok(measure.duration >= 0, "measure duration should not be negative"); + // We don't have navigationStart in workers. + if ("window" in self) { + ok(true, "Running measure addition with no start/end time test"); + performance.measure("test", "navigationStart"); + var measures = performance.getEntriesByType("measure"); + is(measures.length, 1, "number of measures should be 1"); + var measure = measures[0]; + is(measure.name, "test", "measure name should be 'test'"); + is(measure.entryType, "measure", "measure type should be 'measure'"); + is(measure.startTime, 0, "measure start time should be zero"); + ok(measure.duration >= 0, "measure duration should not be negative"); + } }, function () { ok(true, "Running measure addition with only start time test"); diff --git a/dom/performance/tests/test_timeOrigin.html b/dom/performance/tests/test_timeOrigin.html new file mode 100644 index 000000000..5a8a461f3 --- /dev/null +++ b/dom/performance/tests/test_timeOrigin.html @@ -0,0 +1,68 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Test for performance.timeOrigin</title> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_performance_user_timing.js"></script> + </head> + <body> + <script type="text/js-worker" id="worker-src"> + postMessage({ now: performance.now(), timeOrigin: performance.timeOrigin }); + </script> + + <script type="text/js-worker" id="shared-worker-src"> + onconnect = function(evt) { + evt.ports[0].postMessage({ now: performance.now(), timeOrigin: performance.timeOrigin }); + }; + </script> + + <script class="testbody" type="text/javascript"> + +function testBasic() { + ok("timeOrigin" in performance, "Performance.timeOrigin exists."); + ok(performance.timeOrigin > 0, "TimeOrigin must be greater than 0."); + next(); +} + +function testWorker() { + var now = performance.now(); + + var blob = new Blob([ document.getElementById("worker-src").textContent ], + { type: "text/javascript" }); + var w = new Worker(URL.createObjectURL(blob)); + w.onmessage = function(e) { + ok (e.now + e.timeOrigin > now + performance.now, "Comparing worker.now and window.now"); + next(); + } +} + +function testSharedWorker() { + var now = performance.now(); + + var blob = new Blob([ document.getElementById("shared-worker-src").textContent ], + { type: "text/javascript" }); + var w = new SharedWorker(URL.createObjectURL(blob)); + w.port.onmessage = function(e) { + ok (e.now + e.timeOrigin > now + performance.now, "Comparing worker.now and window.now"); + next(); + } +} + +var tests = [ testBasic, testWorker, testSharedWorker ]; +function next() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); +} + +SimpleTest.waitForExplicitFinish(); +addLoadEvent(next); + </script> + </pre> + </body> +</html> diff --git a/dom/performance/tests/test_worker_observer.html b/dom/performance/tests/test_worker_observer.html index b9ed0c964..9a55ef1d5 100644 --- a/dom/performance/tests/test_worker_observer.html +++ b/dom/performance/tests/test_worker_observer.html @@ -3,15 +3,16 @@ http://creativecommons.org/publicdomain/zero/1.0/ --> <!DOCTYPE html> +<html> +<head> <meta charset=utf-8> <title>Test for performance observer in worker</title> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<div id=log></div> +</head> +<body> +<div id="log"></div> <script> -'use strict'; -SpecialPowers.pushPrefEnv({"set": [["dom.enable_performance_observer", true]]}, - function() { - window.open("worker_performance_observer.html"); - }); +fetch_tests_from_worker(new Worker("worker_performance_observer.js")); </script> +</body> diff --git a/dom/performance/tests/worker_performance_observer.html b/dom/performance/tests/worker_performance_observer.html deleted file mode 100644 index 613762f52..000000000 --- a/dom/performance/tests/worker_performance_observer.html +++ /dev/null @@ -1,32 +0,0 @@ -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<!DOCTYPE html> -<meta charset=utf-8> -<html> -<head> -<title>Test for performance observer in worker</title> -</head> -<body> -<div id="log"></div> -<script> -[ - "async_test", "test", "setup", - "assert_true", "assert_equals", "assert_array_equals", - "assert_throws", "fetch_tests_from_worker" -].forEach(func => { - window[func] = opener[func].bind(opener); -}); - -function done() { - opener.add_completion_callback(() => { - self.close(); - }); - opener.done(); -} - -fetch_tests_from_worker(new Worker("worker_performance_observer.js")); -done(); -</script> -</body> diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index b7651be1a..291ae576d 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -2532,6 +2532,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) NS_ASSERTION(anEvent.mMessage == eMouseDown || anEvent.mMessage == eMouseUp || anEvent.mMessage == eMouseDoubleClick || + anEvent.mMessage == eMouseAuxClick || anEvent.mMessage == eMouseOver || anEvent.mMessage == eMouseOut || anEvent.mMessage == eMouseMove || @@ -2594,6 +2595,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) switch (anEvent.mMessage) { case eMouseClick: case eMouseDoubleClick: + case eMouseAuxClick: // Button up/down events sent instead. return rv; default: @@ -2797,6 +2799,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) switch (anEvent.mMessage) { case eMouseClick: case eMouseDoubleClick: + case eMouseAuxClick: // Button up/down events sent instead. return rv; default: diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index acbc12e07..4b47d2b4f 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -751,9 +751,11 @@ var interfaceNamesInGlobalScope = // 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! - {name: "PerformanceObserver", nightly: true}, + "PerformanceNavigationTiming", // IMPORTANT: Do not change this list without review from a DOM peer! - {name: "PerformanceObserverEntryList", nightly: true}, + "PerformanceObserver" +// IMPORTANT: Do not change this list without review from a DOM peer! + "PerformanceObserverEntryList" // 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! diff --git a/dom/webidl/EventHandler.webidl b/dom/webidl/EventHandler.webidl index e65a75787..fce6d9b52 100644 --- a/dom/webidl/EventHandler.webidl +++ b/dom/webidl/EventHandler.webidl @@ -33,6 +33,7 @@ interface GlobalEventHandlers { // attribute OnErrorEventHandler onerror; attribute EventHandler onfocus; //(Not implemented)attribute EventHandler oncancel; + attribute EventHandler onauxclick; attribute EventHandler oncanplay; attribute EventHandler oncanplaythrough; attribute EventHandler onchange; diff --git a/dom/webidl/Performance.webidl b/dom/webidl/Performance.webidl index eaede253c..0bd2677df 100644 --- a/dom/webidl/Performance.webidl +++ b/dom/webidl/Performance.webidl @@ -17,6 +17,9 @@ typedef sequence <PerformanceEntry> PerformanceEntryList; interface Performance { [DependsOn=DeviceState, Affects=Nothing] DOMHighResTimeStamp now(); + + [Constant] + readonly attribute DOMHighResTimeStamp timeOrigin; }; [Exposed=Window] diff --git a/dom/webidl/PerformanceNavigationTiming.webidl b/dom/webidl/PerformanceNavigationTiming.webidl new file mode 100644 index 000000000..fa3ecaec4 --- /dev/null +++ b/dom/webidl/PerformanceNavigationTiming.webidl @@ -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 + * https://www.w3.org/TR/navigation-timing-2/#sec-PerformanceNavigationTiming + * + * Copyright © 2016 W3C® (MIT, ERCIM, Keio, Beihang). + * W3C liability, trademark and document use rules apply. + */ + +enum NavigationType { + "navigate", + "reload", + "back_forward", + "prerender" +}; + +interface PerformanceNavigationTiming : PerformanceResourceTiming { + readonly attribute DOMHighResTimeStamp unloadEventStart; + readonly attribute DOMHighResTimeStamp unloadEventEnd; + readonly attribute DOMHighResTimeStamp domInteractive; + readonly attribute DOMHighResTimeStamp domContentLoadedEventStart; + readonly attribute DOMHighResTimeStamp domContentLoadedEventEnd; + readonly attribute DOMHighResTimeStamp domComplete; + readonly attribute DOMHighResTimeStamp loadEventStart; + readonly attribute DOMHighResTimeStamp loadEventEnd; + readonly attribute NavigationType type; + readonly attribute unsigned short redirectCount; + + jsonifier; +}; diff --git a/dom/webidl/PerformanceObserver.webidl b/dom/webidl/PerformanceObserver.webidl index a3a14cb1e..4cebecbeb 100644 --- a/dom/webidl/PerformanceObserver.webidl +++ b/dom/webidl/PerformanceObserver.webidl @@ -9,6 +9,7 @@ dictionary PerformanceObserverInit { required sequence<DOMString> entryTypes; + boolean buffered = false; }; callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries, PerformanceObserver observer); diff --git a/dom/webidl/PerformanceResourceTiming.webidl b/dom/webidl/PerformanceResourceTiming.webidl index 021b84ae2..112228325 100644 --- a/dom/webidl/PerformanceResourceTiming.webidl +++ b/dom/webidl/PerformanceResourceTiming.webidl @@ -4,7 +4,7 @@ * 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 + * https://w3c.github.io/resource-timing/#performanceresourcetiming * * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C * liability, trademark and document use rules apply. @@ -12,14 +12,10 @@ 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 DOMString nextHopProtocol; + readonly attribute DOMHighResTimeStamp workerStart; readonly attribute DOMHighResTimeStamp redirectStart; readonly attribute DOMHighResTimeStamp redirectEnd; readonly attribute DOMHighResTimeStamp fetchStart; diff --git a/dom/webidl/PerformanceTiming.webidl b/dom/webidl/PerformanceTiming.webidl index e14201440..4aa403a50 100644 --- a/dom/webidl/PerformanceTiming.webidl +++ b/dom/webidl/PerformanceTiming.webidl @@ -33,5 +33,11 @@ interface PerformanceTiming { readonly attribute unsigned long long loadEventStart; readonly attribute unsigned long long loadEventEnd; + // This is a Chrome proprietary extension and not part of the + // performance/navigation timing specification. + // Returns 0 if a non-blank paint has not happened. + [Pref="dom.performance.time_to_non_blank_paint.enabled"] + readonly attribute unsigned long long timeToNonBlankPaint; + jsonifier; }; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 8682aee97..8469c9001 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -347,6 +347,7 @@ WEBIDL_FILES = [ 'PerformanceMark.webidl', 'PerformanceMeasure.webidl', 'PerformanceNavigation.webidl', + 'PerformanceNavigationTiming.webidl', 'PerformanceObserver.webidl', 'PerformanceObserverEntryList.webidl', 'PerformanceResourceTiming.webidl', diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 780b2f5f8..1f79e2c92 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -12,6 +12,7 @@ #include "nsINetworkInterceptController.h" #include "nsIOutputStream.h" #include "nsIScriptError.h" +#include "nsITimedChannel.h" #include "nsIUnicodeDecoder.h" #include "nsIUnicodeEncoder.h" #include "nsContentPolicyUtils.h" @@ -108,6 +109,12 @@ NS_IMETHODIMP CancelChannelRunnable::Run() { MOZ_ASSERT(NS_IsMainThread()); + + // TODO: When bug 1204254 is implemented, this time marker should be moved to + // the point where the body of the network request is complete. + mChannel->SetHandleFetchEventEnd(TimeStamp::Now()); + mChannel->SaveTimeStampsToUnderlyingChannel(); + mChannel->Cancel(mStatus); mRegistration->MaybeScheduleUpdate(); return NS_OK; @@ -230,6 +237,9 @@ public: return NS_OK; } + mChannel->SetHandleFetchEventEnd(TimeStamp::Now()); + mChannel->SaveTimeStampsToUnderlyingChannel(); + nsCOMPtr<nsIObserverService> obsService = services::GetObserverService(); if (obsService) { obsService->NotifyObservers(underlyingChannel, "service-worker-synthesized-response", nullptr); diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp index eaa548f95..24b2e11e6 100644 --- a/dom/workers/ServiceWorkerPrivate.cpp +++ b/dom/workers/ServiceWorkerPrivate.cpp @@ -13,6 +13,7 @@ #include "nsINetworkInterceptController.h" #include "nsIPushErrorReporter.h" #include "nsISupportsImpl.h" +#include "nsITimedChannel.h" #include "nsIUploadChannel2.h" #include "nsNetUtil.h" #include "nsProxyRelease.h" @@ -1255,6 +1256,7 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable nsCString mMethod; nsString mClientId; bool mIsReload; + bool mMarkLaunchServiceWorkerEnd; RequestCache mCacheMode; RequestMode mRequestMode; RequestRedirect mRequestRedirect; @@ -1273,13 +1275,15 @@ public: const nsACString& aScriptSpec, nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration, const nsAString& aDocumentId, - bool aIsReload) + bool aIsReload, + bool aMarkLaunchServiceWorkerEnd) : ExtendableFunctionalEventWorkerRunnable( aWorkerPrivate, aKeepAliveToken, aRegistration) , mInterceptedChannel(aChannel) , mScriptSpec(aScriptSpec) , mClientId(aDocumentId) , mIsReload(aIsReload) + , mMarkLaunchServiceWorkerEnd(aMarkLaunchServiceWorkerEnd) , mCacheMode(RequestCache::Default) , mRequestMode(RequestMode::No_cors) , mRequestRedirect(RequestRedirect::Follow) @@ -1417,6 +1421,12 @@ public: WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { MOZ_ASSERT(aWorkerPrivate); + + if (mMarkLaunchServiceWorkerEnd) { + mInterceptedChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now()); + } + + mInterceptedChannel->SetDispatchFetchEventEnd(TimeStamp::Now()); return DispatchFetchEvent(aCx, aWorkerPrivate); } @@ -1445,6 +1455,10 @@ private: NS_IMETHOD Run() override { AssertIsOnMainThread(); + + mChannel->SetHandleFetchEventEnd(TimeStamp::Now()); + mChannel->SaveTimeStampsToUnderlyingChannel(); + nsresult rv = mChannel->ResetInterception(); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to resume intercepted network request"); @@ -1520,6 +1534,8 @@ private: event->PostInit(mInterceptedChannel, mRegistration, mScriptSpec); event->SetTrusted(true); + mInterceptedChannel->SetHandleFetchEventStart(TimeStamp::Now()); + RefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope()); nsresult rv2 = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr); if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) { @@ -1614,9 +1630,21 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, nsCOMPtr<nsIRunnable> failRunnable = NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception); - nsresult rv = SpawnWorkerIfNeeded(FetchEvent, failRunnable, aLoadGroup); + aChannel->SetLaunchServiceWorkerStart(TimeStamp::Now()); + aChannel->SetDispatchFetchEventStart(TimeStamp::Now()); + + bool newWorkerCreated = false; + nsresult rv = SpawnWorkerIfNeeded(FetchEvent, + failRunnable, + &newWorkerCreated, + aLoadGroup); + NS_ENSURE_SUCCESS(rv, rv); + if (!newWorkerCreated) { + aChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now()); + } + nsMainThreadPtrHandle<nsIInterceptedChannel> handle( new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false)); @@ -1646,7 +1674,7 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, RefPtr<FetchEventRunnable> r = new FetchEventRunnable(mWorkerPrivate, token, handle, mInfo->ScriptSpec(), regInfo, - aDocumentId, aIsReload); + aDocumentId, aIsReload, newWorkerCreated); rv = r->Init(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -1669,6 +1697,7 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, nsresult ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy, nsIRunnable* aLoadFailedRunnable, + bool* aNewWorkerCreated, nsILoadGroup* aLoadGroup) { AssertIsOnMainThread(); @@ -1679,6 +1708,12 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy, // the overriden load group when intercepting a fetch. MOZ_ASSERT_IF(aWhy == FetchEvent, aLoadGroup); + // Defaults to no new worker created, but if there is one, we'll set the value + // to true at the end of this function. + if (aNewWorkerCreated) { + *aNewWorkerCreated = false; + } + if (mWorkerPrivate) { mWorkerPrivate->UpdateOverridenLoadGroup(aLoadGroup); RenewKeepAliveToken(aWhy); @@ -1762,6 +1797,10 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy, RenewKeepAliveToken(aWhy); + if (aNewWorkerCreated) { + *aNewWorkerCreated = true; + } + return NS_OK; } diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h index 8d59ea1d0..911b07a11 100644 --- a/dom/workers/ServiceWorkerPrivate.h +++ b/dom/workers/ServiceWorkerPrivate.h @@ -189,6 +189,7 @@ private: nsresult SpawnWorkerIfNeeded(WakeUpReason aWhy, nsIRunnable* aLoadFailedRunnable, + bool* aNewWorkerCreated = nullptr, nsILoadGroup* aLoadGroup = nullptr); ~ServiceWorkerPrivate(); diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index bd8a33032..8848e881a 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2419,8 +2419,6 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent( MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext); MOZ_ASSERT(IsDedicatedWorker()); - mNowBaseTimeStamp = aParent->NowBaseTimeStamp(); - mNowBaseTimeHighRes = aParent->NowBaseTime(); if (aParent->mParentFrozen) { Freeze(nullptr); @@ -2451,18 +2449,6 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent( .creationOptions().setSecureContext(true); } - if (IsDedicatedWorker() && mLoadInfo.mWindow && - mLoadInfo.mWindow->GetPerformance()) { - mNowBaseTimeStamp = mLoadInfo.mWindow->GetPerformance()->GetDOMTiming()-> - GetNavigationStartTimeStamp(); - mNowBaseTimeHighRes = - mLoadInfo.mWindow->GetPerformance()->GetDOMTiming()-> - GetNavigationStartHighRes(); - } else { - mNowBaseTimeStamp = CreationTimeStamp(); - mNowBaseTimeHighRes = CreationTime(); - } - // Our parent can get suspended after it initiates the async creation // of a new worker thread. In this case suspend the new worker as well. if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsSuspended()) { diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 465c0f9a3..28283bed7 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -216,8 +216,6 @@ private: WorkerType mWorkerType; TimeStamp mCreationTimeStamp; DOMHighResTimeStamp mCreationTimeHighRes; - TimeStamp mNowBaseTimeStamp; - DOMHighResTimeStamp mNowBaseTimeHighRes; protected: // The worker is owned by its thread, which is represented here. This is set @@ -579,14 +577,11 @@ public: return mCreationTimeHighRes; } - TimeStamp NowBaseTimeStamp() const + DOMHighResTimeStamp TimeStampToDOMHighRes(const TimeStamp& aTimeStamp) const { - return mNowBaseTimeStamp; - } - - DOMHighResTimeStamp NowBaseTime() const - { - return mNowBaseTimeHighRes; + MOZ_ASSERT(!aTimeStamp.IsNull()); + TimeDuration duration = aTimeStamp - mCreationTimeStamp; + return duration.ToMilliseconds(); } nsIPrincipal* diff --git a/dom/workers/test/serviceworkers/chrome.ini b/dom/workers/test/serviceworkers/chrome.ini index e064e7fd0..6d7dbebd0 100644 --- a/dom/workers/test/serviceworkers/chrome.ini +++ b/dom/workers/test/serviceworkers/chrome.ini @@ -3,6 +3,8 @@ skip-if = os == 'android' support-files = chrome_helpers.js empty.js + fetch.js + hello.html serviceworker.html serviceworkerinfo_iframe.html serviceworkermanager_iframe.html @@ -10,6 +12,7 @@ support-files = worker.js worker2.js +[test_devtools_serviceworker_interception.html] [test_privateBrowsing.html] [test_serviceworkerinfo.xul] [test_serviceworkermanager.xul] diff --git a/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html b/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html new file mode 100644 index 000000000..d49ebb2c9 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html @@ -0,0 +1,168 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug 1168875 - test devtools serviceworker interception.</title> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" + type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + +// Constants +const Ci = Components.interfaces; +const workerScope = "http://mochi.test:8888/chrome/dom/workers/test/serviceworkers/"; +const workerURL = workerScope + "fetch.js"; +const contentPage = workerScope + "hello.html"; + +function createTestWindow(aURL) { + var mainwindow = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + var win = mainwindow.OpenBrowserWindow(contentPage); + + return new Promise(aResolve => { + win.addEventListener("DOMContentLoaded", function callback() { + if (win.content.location.href != aURL) { + win.gBrowser.loadURI(aURL); + return; + } + + win.removeEventListener("DOMContentLoaded", callback); + aResolve(win.content); + }); + }); +} + +function executeTest(aWindow) { + var registration; + + return Promise.resolve() + // Should not be intercepted. + .then(_ => fetchAndCheckTimedChannel(aWindow, false, true, "hello.html")) + + // Regist a service worker. + .then(_ => register(aWindow, workerURL, workerScope)) + .then(r => registration = r) + + // Should be intercpeted and synthesized. + .then(_ => fetchAndCheckTimedChannel(aWindow, true, false, "fake.html")) + + // Should be intercepted but still fetch from network. + .then(_ => fetchAndCheckTimedChannel(aWindow, true, true, + "hello.html?ForBypassingHttpCache")) + + // Tear down + .then(_ => registration.unregister()); +} + +function register(aWindow, aURL, aScope) { + return aWindow.navigator.serviceWorker.register(aURL, {scope: aScope}) + .then(r => { + var worker = r.installing; + return new Promise(function(aResolve) { + worker.onstatechange = function() { + if (worker.state == "activated") { + aResolve(r); + } + } + }); + }); +} + +function fetchAndCheckTimedChannel(aWindow, aIntercepted, aFetch, aURL) { + var resolveFunction; + var promise = new Promise(aResolve => resolveFunction = aResolve); + + var topic = aFetch ? "http-on-examine-response" + : "service-worker-synthesized-response"; + + function observer(aSubject) { + var channel = aSubject.QueryInterface(Ci.nsIChannel); + ok(channel.URI.spec.endsWith(aURL)); + + var tc = aSubject.QueryInterface(Ci.nsITimedChannel); + + // Check service worker related timings. + var serviceWorkerTimings = [{start: tc.launchServiceWorkerStartTime, + end: tc.launchServiceWorkerEndTime}, + {start: tc.dispatchFetchEventStartTime, + end: tc.dispatchFetchEventEndTime}, + {start: tc.handleFetchEventStartTime, + end: tc.handleFetchEventEndTime}]; + if (aIntercepted) { + serviceWorkerTimings.reduce((aPreviousTimings, aCurrentTimings) => { + ok(aPreviousTimings.start <= aCurrentTimings.start, + "Start time order check."); + ok(aPreviousTimings.end <= aCurrentTimings.end, + "End time order check."); + ok(aCurrentTimings.start <= aCurrentTimings.end, + "Start time should be smaller than end time."); + return aCurrentTimings; + }); + } else { + serviceWorkerTimings.forEach(aTimings => { + is(aTimings.start, 0); + is(aTimings.end, 0); + }); + } + + // Check network related timings. + var networkTimings = [tc.domainLookupStartTime, + tc.domainLookupEndTime, + tc.connectStartTime, + tc.connectEndTime, + tc.requestStartTime, + tc.responseStartTime, + tc.responseEndTime]; + if (aFetch) { + networkTimings.reduce((aPreviousTiming, aCurrentTiming) => { + ok(aPreviousTiming <= aCurrentTiming); + return aCurrentTiming; + }); + } else { + networkTimings.forEach(aTiming => is(aTiming, 0)); + } + + SpecialPowers.removeObserver(observer, topic); + resolveFunction(); + } + + SpecialPowers.addObserver(observer, topic, false); + + // return promise; + return Promise.all([aWindow.fetch(aURL), promise]); +} + +function runTest() { + return Promise.resolve() + .then(_ => createTestWindow(contentPage)) + .then(w => executeTest(w)) + .catch(e => ok(false, "Some test failed with error " + e)) + .then(_ => SimpleTest.finish()); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({"set": [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], +]}, runTest); + +</script> +</pre> +</body> +</html> + diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js index 9dbfcc099..a4d498fb8 100644 --- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js +++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js @@ -172,9 +172,9 @@ var interfaceNamesInGlobalScope = // IMPORTANT: Do not change this list without review from a DOM peer! "PerformanceMeasure", // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PerformanceObserver", nightly: true }, + "PerformanceObserver", // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PerformanceObserverEntryList", nightly: true }, + "PerformanceObserverEntryList", // IMPORTANT: Do not change this list without review from a DOM peer! "Request", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/workers/test/test_worker_interfaces.js b/dom/workers/test/test_worker_interfaces.js index e0647682c..6fe5fcaff 100644 --- a/dom/workers/test/test_worker_interfaces.js +++ b/dom/workers/test/test_worker_interfaces.js @@ -163,9 +163,9 @@ var interfaceNamesInGlobalScope = // IMPORTANT: Do not change this list without review from a DOM peer! "PerformanceMeasure", // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PerformanceObserver", nightly: true }, + "PerformanceObserver", // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PerformanceObserverEntryList", nightly: true }, + "PerformanceObserverEntryList", // IMPORTANT: Do not change this list without review from a DOM peer! "Request", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/xslt/xpath/txXPCOMExtensionFunction.cpp b/dom/xslt/xpath/txXPCOMExtensionFunction.cpp index 4913702aa..032161722 100644 --- a/dom/xslt/xpath/txXPCOMExtensionFunction.cpp +++ b/dom/xslt/xpath/txXPCOMExtensionFunction.cpp @@ -322,7 +322,7 @@ public: void trace(JSTracer* trc) { for (uint8_t i = 0; i < mCount; ++i) { if (mArray[i].type == nsXPTType::T_JSVAL) { - JS::UnsafeTraceRoot(trc, &mArray[i].val.j, "txParam value"); + JS::UnsafeTraceRoot(trc, &mArray[i].val.j.asValueRef(), "txParam value"); } } } |