mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-05 14:57:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
536
dom/performance/Performance.cpp
Normal file
536
dom/performance/Performance.cpp
Normal file
|
|
@ -0,0 +1,536 @@
|
|||
/* -*- 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 "Performance.h"
|
||||
|
||||
#include "GeckoProfiler.h"
|
||||
#include "PerformanceEntry.h"
|
||||
#include "PerformanceMainThread.h"
|
||||
#include "PerformanceMark.h"
|
||||
#include "PerformanceMeasure.h"
|
||||
#include "PerformanceObserver.h"
|
||||
#include "PerformanceResourceTiming.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/IntegerPrintfMacros.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#define PERFLOG(msg, ...) __android_log_print(ANDROID_LOG_INFO, "PerformanceTiming", msg, ##__VA_ARGS__)
|
||||
#else
|
||||
#define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
const nsCString& aPrefName)
|
||||
: WorkerCheckAPIExposureOnMainThreadRunnable(aWorkerPrivate)
|
||||
, mEnabled(false)
|
||||
, mPrefName(aPrefName)
|
||||
{ }
|
||||
|
||||
bool MainThreadRun() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mEnabled = Preferences::GetBool(mPrefName.get(), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsEnabled() const
|
||||
{
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
private:
|
||||
bool mEnabled;
|
||||
nsCString mPrefName;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Performance)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(Performance,
|
||||
DOMEventTargetHelper,
|
||||
mUserEntries,
|
||||
mResourceEntries);
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(Performance, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(Performance, DOMEventTargetHelper)
|
||||
|
||||
/* static */ already_AddRefed<Performance>
|
||||
Performance::CreateForMainThread(nsPIDOMWindowInner* aWindow,
|
||||
nsDOMNavigationTiming* aDOMTiming,
|
||||
nsITimedChannel* aChannel,
|
||||
Performance* aParentPerformance)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
RefPtr<Performance> performance =
|
||||
new PerformanceMainThread(aWindow, aDOMTiming, aChannel,
|
||||
aParentPerformance);
|
||||
return performance.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<Performance>
|
||||
Performance::CreateForWorker(workers::WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
RefPtr<Performance> performance = new PerformanceWorker(aWorkerPrivate);
|
||||
return performance.forget();
|
||||
}
|
||||
|
||||
Performance::Performance()
|
||||
: mResourceTimingBufferSize(kDefaultResourceTimingBufferSize)
|
||||
, mPendingNotificationObserversTask(false)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
}
|
||||
|
||||
Performance::Performance(nsPIDOMWindowInner* aWindow)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, mResourceTimingBufferSize(kDefaultResourceTimingBufferSize)
|
||||
, mPendingNotificationObserversTask(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
Performance::~Performance()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
Performance::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return PerformanceBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
Performance::GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval)
|
||||
{
|
||||
aRetval = mResourceEntries;
|
||||
aRetval.AppendElements(mUserEntries);
|
||||
aRetval.Sort(PerformanceEntryComparator());
|
||||
}
|
||||
|
||||
void
|
||||
Performance::GetEntriesByType(const nsAString& aEntryType,
|
||||
nsTArray<RefPtr<PerformanceEntry>>& aRetval)
|
||||
{
|
||||
if (aEntryType.EqualsLiteral("resource")) {
|
||||
aRetval = mResourceEntries;
|
||||
return;
|
||||
}
|
||||
|
||||
aRetval.Clear();
|
||||
|
||||
if (aEntryType.EqualsLiteral("mark") ||
|
||||
aEntryType.EqualsLiteral("measure")) {
|
||||
for (PerformanceEntry* entry : mUserEntries) {
|
||||
if (entry->GetEntryType().Equals(aEntryType)) {
|
||||
aRetval.AppendElement(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Performance::GetEntriesByName(const nsAString& aName,
|
||||
const Optional<nsAString>& aEntryType,
|
||||
nsTArray<RefPtr<PerformanceEntry>>& aRetval)
|
||||
{
|
||||
aRetval.Clear();
|
||||
|
||||
for (PerformanceEntry* entry : mResourceEntries) {
|
||||
if (entry->GetName().Equals(aName) &&
|
||||
(!aEntryType.WasPassed() ||
|
||||
entry->GetEntryType().Equals(aEntryType.Value()))) {
|
||||
aRetval.AppendElement(entry);
|
||||
}
|
||||
}
|
||||
|
||||
for (PerformanceEntry* entry : mUserEntries) {
|
||||
if (entry->GetName().Equals(aName) &&
|
||||
(!aEntryType.WasPassed() ||
|
||||
entry->GetEntryType().Equals(aEntryType.Value()))) {
|
||||
aRetval.AppendElement(entry);
|
||||
}
|
||||
}
|
||||
|
||||
aRetval.Sort(PerformanceEntryComparator());
|
||||
}
|
||||
|
||||
void
|
||||
Performance::ClearUserEntries(const Optional<nsAString>& aEntryName,
|
||||
const nsAString& aEntryType)
|
||||
{
|
||||
for (uint32_t i = 0; i < mUserEntries.Length();) {
|
||||
if ((!aEntryName.WasPassed() ||
|
||||
mUserEntries[i]->GetName().Equals(aEntryName.Value())) &&
|
||||
(aEntryType.IsEmpty() ||
|
||||
mUserEntries[i]->GetEntryType().Equals(aEntryType))) {
|
||||
mUserEntries.RemoveElementAt(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Performance::ClearResourceTimings()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mResourceEntries.Clear();
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
Performance::RoundTime(double aTime) const
|
||||
{
|
||||
// Round down to the nearest 20us, because if the timer is too accurate people
|
||||
// can do nasty timing attacks with it.
|
||||
const double maxResolutionMs = 0.020;
|
||||
return floor(aTime / maxResolutionMs) * maxResolutionMs;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Performance::Mark(const nsAString& aName, ErrorResult& aRv)
|
||||
{
|
||||
// Don't add the entry if the buffer is full. XXX should be removed by bug 1159003.
|
||||
if (mUserEntries.Length() >= mResourceTimingBufferSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsPerformanceTimingAttribute(aName)) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<PerformanceMark> performanceMark =
|
||||
new PerformanceMark(GetAsISupports(), aName, Now());
|
||||
InsertUserEntry(performanceMark);
|
||||
|
||||
if (profiler_is_active()) {
|
||||
PROFILER_MARKER(NS_ConvertUTF16toUTF8(aName).get());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Performance::ClearMarks(const Optional<nsAString>& aName)
|
||||
{
|
||||
ClearUserEntries(aName, NS_LITERAL_STRING("mark"));
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
Performance::ResolveTimestampFromName(const nsAString& aName,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AutoTArray<RefPtr<PerformanceEntry>, 1> arr;
|
||||
DOMHighResTimeStamp ts;
|
||||
Optional<nsAString> typeParam;
|
||||
nsAutoString str;
|
||||
str.AssignLiteral("mark");
|
||||
typeParam = &str;
|
||||
GetEntriesByName(aName, typeParam, arr);
|
||||
if (!arr.IsEmpty()) {
|
||||
return arr.LastElement()->StartTime();
|
||||
}
|
||||
|
||||
if (!IsPerformanceTimingAttribute(aName)) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ts = GetPerformanceTimingFromString(aName);
|
||||
if (!ts) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ts - CreationTime();
|
||||
}
|
||||
|
||||
void
|
||||
Performance::Measure(const nsAString& aName,
|
||||
const Optional<nsAString>& aStartMark,
|
||||
const Optional<nsAString>& aEndMark,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// Don't add the entry if the buffer is full. XXX should be removed by bug
|
||||
// 1159003.
|
||||
if (mUserEntries.Length() >= mResourceTimingBufferSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp startTime;
|
||||
DOMHighResTimeStamp endTime;
|
||||
|
||||
if (IsPerformanceTimingAttribute(aName)) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aStartMark.WasPassed()) {
|
||||
startTime = ResolveTimestampFromName(aStartMark.Value(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Navigation start is used in this case, but since DOMHighResTimeStamp is
|
||||
// in relation to navigation start, this will be zero if a name is not
|
||||
// passed.
|
||||
startTime = 0;
|
||||
}
|
||||
|
||||
if (aEndMark.WasPassed()) {
|
||||
endTime = ResolveTimestampFromName(aEndMark.Value(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
endTime = Now();
|
||||
}
|
||||
|
||||
RefPtr<PerformanceMeasure> performanceMeasure =
|
||||
new PerformanceMeasure(GetAsISupports(), aName, startTime, endTime);
|
||||
InsertUserEntry(performanceMeasure);
|
||||
}
|
||||
|
||||
void
|
||||
Performance::ClearMeasures(const Optional<nsAString>& aName)
|
||||
{
|
||||
ClearUserEntries(aName, NS_LITERAL_STRING("measure"));
|
||||
}
|
||||
|
||||
void
|
||||
Performance::LogEntry(PerformanceEntry* aEntry, const nsACString& aOwner) const
|
||||
{
|
||||
PERFLOG("Performance Entry: %s|%s|%s|%f|%f|%" PRIu64 "\n",
|
||||
aOwner.BeginReading(),
|
||||
NS_ConvertUTF16toUTF8(aEntry->GetEntryType()).get(),
|
||||
NS_ConvertUTF16toUTF8(aEntry->GetName()).get(),
|
||||
aEntry->StartTime(),
|
||||
aEntry->Duration(),
|
||||
static_cast<uint64_t>(PR_Now() / PR_USEC_PER_MSEC));
|
||||
}
|
||||
|
||||
void
|
||||
Performance::TimingNotification(PerformanceEntry* aEntry,
|
||||
const nsACString& aOwner, uint64_t aEpoch)
|
||||
{
|
||||
PerformanceEntryEventInit init;
|
||||
init.mBubbles = false;
|
||||
init.mCancelable = false;
|
||||
init.mName = aEntry->GetName();
|
||||
init.mEntryType = aEntry->GetEntryType();
|
||||
init.mStartTime = aEntry->StartTime();
|
||||
init.mDuration = aEntry->Duration();
|
||||
init.mEpoch = aEpoch;
|
||||
init.mOrigin = NS_ConvertUTF8toUTF16(aOwner.BeginReading());
|
||||
|
||||
RefPtr<PerformanceEntryEvent> perfEntryEvent =
|
||||
PerformanceEntryEvent::Constructor(this, NS_LITERAL_STRING("performanceentry"), init);
|
||||
|
||||
nsCOMPtr<EventTarget> et = do_QueryInterface(GetOwner());
|
||||
if (et) {
|
||||
bool dummy = false;
|
||||
et->DispatchEvent(perfEntryEvent, &dummy);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Performance::InsertUserEntry(PerformanceEntry* aEntry)
|
||||
{
|
||||
mUserEntries.InsertElementSorted(aEntry,
|
||||
PerformanceEntryComparator());
|
||||
|
||||
QueueEntry(aEntry);
|
||||
}
|
||||
|
||||
void
|
||||
Performance::SetResourceTimingBufferSize(uint64_t aMaxSize)
|
||||
{
|
||||
mResourceTimingBufferSize = aMaxSize;
|
||||
}
|
||||
|
||||
void
|
||||
Performance::InsertResourceEntry(PerformanceEntry* aEntry)
|
||||
{
|
||||
MOZ_ASSERT(aEntry);
|
||||
MOZ_ASSERT(mResourceEntries.Length() < mResourceTimingBufferSize);
|
||||
if (mResourceEntries.Length() >= mResourceTimingBufferSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
mResourceEntries.InsertElementSorted(aEntry,
|
||||
PerformanceEntryComparator());
|
||||
if (mResourceEntries.Length() == mResourceTimingBufferSize) {
|
||||
// call onresourcetimingbufferfull
|
||||
DispatchBufferFullEvent();
|
||||
}
|
||||
QueueEntry(aEntry);
|
||||
}
|
||||
|
||||
void
|
||||
Performance::AddObserver(PerformanceObserver* aObserver)
|
||||
{
|
||||
mObservers.AppendElementUnlessExists(aObserver);
|
||||
}
|
||||
|
||||
void
|
||||
Performance::RemoveObserver(PerformanceObserver* aObserver)
|
||||
{
|
||||
mObservers.RemoveElement(aObserver);
|
||||
}
|
||||
|
||||
void
|
||||
Performance::NotifyObservers()
|
||||
{
|
||||
mPendingNotificationObserversTask = false;
|
||||
NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers,
|
||||
PerformanceObserver,
|
||||
Notify, ());
|
||||
}
|
||||
|
||||
void
|
||||
Performance::CancelNotificationObservers()
|
||||
{
|
||||
mPendingNotificationObserversTask = false;
|
||||
}
|
||||
|
||||
class NotifyObserversTask final : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
explicit NotifyObserversTask(Performance* aPerformance)
|
||||
: mPerformance(aPerformance)
|
||||
{
|
||||
MOZ_ASSERT(mPerformance);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(mPerformance);
|
||||
mPerformance->NotifyObservers();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Cancel() override
|
||||
{
|
||||
mPerformance->CancelNotificationObservers();
|
||||
mPerformance = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~NotifyObserversTask()
|
||||
{
|
||||
}
|
||||
|
||||
RefPtr<Performance> mPerformance;
|
||||
};
|
||||
|
||||
void
|
||||
Performance::RunNotificationObserversTask()
|
||||
{
|
||||
mPendingNotificationObserversTask = true;
|
||||
nsCOMPtr<nsIRunnable> task = new NotifyObserversTask(this);
|
||||
nsresult rv = NS_DispatchToCurrentThread(task);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mPendingNotificationObserversTask = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Performance::QueueEntry(PerformanceEntry* aEntry)
|
||||
{
|
||||
if (mObservers.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers,
|
||||
PerformanceObserver,
|
||||
QueueEntry, (aEntry));
|
||||
|
||||
if (!mPendingNotificationObserversTask) {
|
||||
RunNotificationObserversTask();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Performance::IsEnabled(JSContext* aCx, JSObject* aGlobal)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return Preferences::GetBool("dom.enable_user_timing", false);
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
RefPtr<PrefEnabledRunnable> runnable =
|
||||
new PrefEnabledRunnable(workerPrivate,
|
||||
NS_LITERAL_CSTRING("dom.enable_user_timing"));
|
||||
return runnable->Dispatch() && runnable->IsEnabled();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Performance::IsObserverEnabled(JSContext* aCx, JSObject* aGlobal)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return Preferences::GetBool("dom.enable_performance_observer", false);
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
RefPtr<PrefEnabledRunnable> runnable =
|
||||
new PrefEnabledRunnable(workerPrivate,
|
||||
NS_LITERAL_CSTRING("dom.enable_performance_observer"));
|
||||
|
||||
return runnable->Dispatch() && runnable->IsEnabled();
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
162
dom/performance/Performance.h
Normal file
162
dom/performance/Performance.h
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/* -*- 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_Performance_h
|
||||
#define mozilla_dom_Performance_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
|
||||
class nsITimedChannel;
|
||||
class nsIHttpChannel;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class PerformanceEntry;
|
||||
class PerformanceNavigation;
|
||||
class PerformanceObserver;
|
||||
class PerformanceTiming;
|
||||
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
}
|
||||
|
||||
// Base class for main-thread and worker Performance API
|
||||
class Performance : public DOMEventTargetHelper
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Performance,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
static bool IsEnabled(JSContext* aCx, JSObject* aGlobal);
|
||||
|
||||
static bool IsObserverEnabled(JSContext* aCx, JSObject* aGlobal);
|
||||
|
||||
static already_AddRefed<Performance>
|
||||
CreateForMainThread(nsPIDOMWindowInner* aWindow,
|
||||
nsDOMNavigationTiming* aDOMTiming,
|
||||
nsITimedChannel* aChannel,
|
||||
Performance* aParentPerformance);
|
||||
|
||||
static already_AddRefed<Performance>
|
||||
CreateForWorker(workers::WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
JSObject* WrapObject(JSContext *cx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval);
|
||||
|
||||
void GetEntriesByType(const nsAString& aEntryType,
|
||||
nsTArray<RefPtr<PerformanceEntry>>& aRetval);
|
||||
|
||||
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;
|
||||
|
||||
void Mark(const nsAString& aName, ErrorResult& aRv);
|
||||
|
||||
void ClearMarks(const Optional<nsAString>& aName);
|
||||
|
||||
void Measure(const nsAString& aName,
|
||||
const Optional<nsAString>& aStartMark,
|
||||
const Optional<nsAString>& aEndMark,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void ClearMeasures(const Optional<nsAString>& aName);
|
||||
|
||||
void SetResourceTimingBufferSize(uint64_t aMaxSize);
|
||||
|
||||
void AddObserver(PerformanceObserver* aObserver);
|
||||
void RemoveObserver(PerformanceObserver* aObserver);
|
||||
void NotifyObservers();
|
||||
void CancelNotificationObservers();
|
||||
|
||||
virtual PerformanceTiming* Timing() = 0;
|
||||
|
||||
virtual PerformanceNavigation* Navigation() = 0;
|
||||
|
||||
IMPL_EVENT_HANDLER(resourcetimingbufferfull)
|
||||
|
||||
virtual void GetMozMemory(JSContext *aCx,
|
||||
JS::MutableHandle<JSObject*> aObj) = 0;
|
||||
|
||||
virtual nsDOMNavigationTiming* GetDOMTiming() const = 0;
|
||||
|
||||
virtual nsITimedChannel* GetChannel() const = 0;
|
||||
|
||||
virtual Performance* GetParentPerformance() const = 0;
|
||||
|
||||
protected:
|
||||
Performance();
|
||||
explicit Performance(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
virtual ~Performance();
|
||||
|
||||
virtual void InsertUserEntry(PerformanceEntry* aEntry);
|
||||
void InsertResourceEntry(PerformanceEntry* aEntry);
|
||||
|
||||
void ClearUserEntries(const Optional<nsAString>& aEntryName,
|
||||
const nsAString& aEntryType);
|
||||
|
||||
DOMHighResTimeStamp ResolveTimestampFromName(const nsAString& aName,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual nsISupports* GetAsISupports() = 0;
|
||||
|
||||
virtual void DispatchBufferFullEvent() = 0;
|
||||
|
||||
virtual TimeStamp CreationTimeStamp() const = 0;
|
||||
|
||||
virtual DOMHighResTimeStamp CreationTime() const = 0;
|
||||
|
||||
virtual bool IsPerformanceTimingAttribute(const nsAString& aName) = 0;
|
||||
|
||||
virtual DOMHighResTimeStamp
|
||||
GetPerformanceTimingFromString(const nsAString& aTimingName) = 0;
|
||||
|
||||
bool IsResourceEntryLimitReached() const
|
||||
{
|
||||
return mResourceEntries.Length() >= mResourceTimingBufferSize;
|
||||
}
|
||||
|
||||
void LogEntry(PerformanceEntry* aEntry, const nsACString& aOwner) const;
|
||||
void TimingNotification(PerformanceEntry* aEntry, const nsACString& aOwner,
|
||||
uint64_t epoch);
|
||||
|
||||
void RunNotificationObserversTask();
|
||||
void QueueEntry(PerformanceEntry* aEntry);
|
||||
|
||||
DOMHighResTimeStamp RoundTime(double aTime) const;
|
||||
|
||||
nsTObserverArray<PerformanceObserver*> mObservers;
|
||||
|
||||
private:
|
||||
nsTArray<RefPtr<PerformanceEntry>> mUserEntries;
|
||||
nsTArray<RefPtr<PerformanceEntry>> mResourceEntries;
|
||||
|
||||
uint64_t mResourceTimingBufferSize;
|
||||
static const uint64_t kDefaultResourceTimingBufferSize = 150;
|
||||
bool mPendingNotificationObserversTask;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_Performance_h
|
||||
43
dom/performance/PerformanceEntry.cpp
Normal file
43
dom/performance/PerformanceEntry.cpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- 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 "PerformanceEntry.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/dom/PerformanceEntryBinding.h"
|
||||
#include "nsIURI.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceEntry, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(PerformanceEntry)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(PerformanceEntry)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceEntry)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
PerformanceEntry::PerformanceEntry(nsISupports* aParent,
|
||||
const nsAString& aName,
|
||||
const nsAString& aEntryType)
|
||||
: mParent(aParent),
|
||||
mName(aName),
|
||||
mEntryType(aEntryType)
|
||||
{
|
||||
// mParent is null in workers.
|
||||
MOZ_ASSERT(mParent || !NS_IsMainThread());
|
||||
}
|
||||
|
||||
PerformanceEntry::~PerformanceEntry()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
PerformanceEntry::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return mozilla::dom::PerformanceEntryBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
96
dom/performance/PerformanceEntry.h
Normal file
96
dom/performance/PerformanceEntry.h
Normal file
|
|
@ -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/. */
|
||||
|
||||
#ifndef mozilla_dom_PerformanceEntry_h___
|
||||
#define mozilla_dom_PerformanceEntry_h___
|
||||
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsString.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
class nsISupports;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class PerformanceResourceTiming;
|
||||
|
||||
// http://www.w3.org/TR/performance-timeline/#performanceentry
|
||||
class PerformanceEntry : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
protected:
|
||||
virtual ~PerformanceEntry();
|
||||
|
||||
public:
|
||||
PerformanceEntry(nsISupports* aParent,
|
||||
const nsAString& aName,
|
||||
const nsAString& aEntryType);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PerformanceEntry)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsISupports* GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
void GetName(nsAString& aName) const
|
||||
{
|
||||
aName = mName;
|
||||
}
|
||||
|
||||
const nsAString& GetName() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
void SetName(const nsAString& aName)
|
||||
{
|
||||
mName = aName;
|
||||
}
|
||||
|
||||
void GetEntryType(nsAString& aEntryType) const
|
||||
{
|
||||
aEntryType = mEntryType;
|
||||
}
|
||||
|
||||
const nsAString& GetEntryType()
|
||||
{
|
||||
return mEntryType;
|
||||
}
|
||||
|
||||
void SetEntryType(const nsAString& aEntryType)
|
||||
{
|
||||
mEntryType = aEntryType;
|
||||
}
|
||||
|
||||
virtual DOMHighResTimeStamp StartTime() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual DOMHighResTimeStamp Duration() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual const PerformanceResourceTiming* ToResourceTiming() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
nsString mName;
|
||||
nsString mEntryType;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_PerformanceEntry_h___ */
|
||||
336
dom/performance/PerformanceMainThread.cpp
Normal file
336
dom/performance/PerformanceMainThread.cpp
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
/* -*- 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 "PerformanceMainThread.h"
|
||||
#include "PerformanceNavigation.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread,
|
||||
Performance)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming,
|
||||
mNavigation,
|
||||
mParentPerformance)
|
||||
tmp->mMozMemory = nullptr;
|
||||
mozilla::DropJSObjects(this);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMainThread,
|
||||
Performance)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTiming,
|
||||
mNavigation,
|
||||
mParentPerformance)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMainThread,
|
||||
Performance)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMozMemory)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(PerformanceMainThread, Performance)
|
||||
NS_IMPL_RELEASE_INHERITED(PerformanceMainThread, Performance)
|
||||
|
||||
// QueryInterface implementation for PerformanceMainThread
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceMainThread)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END_INHERITING(Performance)
|
||||
|
||||
PerformanceMainThread::PerformanceMainThread(nsPIDOMWindowInner* aWindow,
|
||||
nsDOMNavigationTiming* aDOMTiming,
|
||||
nsITimedChannel* aChannel,
|
||||
Performance* aParentPerformance)
|
||||
: Performance(aWindow)
|
||||
, mDOMTiming(aDOMTiming)
|
||||
, mChannel(aChannel)
|
||||
, mParentPerformance(aParentPerformance)
|
||||
{
|
||||
MOZ_ASSERT(aWindow, "Parent window object should be provided");
|
||||
}
|
||||
|
||||
PerformanceMainThread::~PerformanceMainThread()
|
||||
{
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceMainThread::GetMozMemory(JSContext *aCx,
|
||||
JS::MutableHandle<JSObject*> aObj)
|
||||
{
|
||||
if (!mMozMemory) {
|
||||
mMozMemory = js::gc::NewMemoryInfoObject(aCx);
|
||||
if (mMozMemory) {
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
}
|
||||
|
||||
aObj.set(mMozMemory);
|
||||
}
|
||||
|
||||
PerformanceTiming*
|
||||
PerformanceMainThread::Timing()
|
||||
{
|
||||
if (!mTiming) {
|
||||
// For navigation timing, the third argument (an nsIHtttpChannel) is null
|
||||
// since the cross-domain redirect were already checked. The last argument
|
||||
// (zero time) for performance.timing is the navigation start value.
|
||||
mTiming = new PerformanceTiming(this, mChannel, nullptr,
|
||||
mDOMTiming->GetNavigationStart());
|
||||
}
|
||||
|
||||
return mTiming;
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceMainThread::DispatchBufferFullEvent()
|
||||
{
|
||||
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
|
||||
// it bubbles, and it isn't cancelable
|
||||
event->InitEvent(NS_LITERAL_STRING("resourcetimingbufferfull"), true, false);
|
||||
event->SetTrusted(true);
|
||||
DispatchDOMEvent(nullptr, event, nullptr, nullptr);
|
||||
}
|
||||
|
||||
PerformanceNavigation*
|
||||
PerformanceMainThread::Navigation()
|
||||
{
|
||||
if (!mNavigation) {
|
||||
mNavigation = new PerformanceNavigation(this);
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
void
|
||||
PerformanceMainThread::AddEntry(nsIHttpChannel* channel,
|
||||
nsITimedChannel* timedChannel)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Check if resource timing is prefed off.
|
||||
if (!nsContentUtils::IsResourceTimingEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't add the entry if the buffer is full
|
||||
if (IsResourceEntryLimitReached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (channel && timedChannel) {
|
||||
nsAutoCString name;
|
||||
nsAutoString initiatorType;
|
||||
nsCOMPtr<nsIURI> originalURI;
|
||||
|
||||
timedChannel->GetInitiatorType(initiatorType);
|
||||
|
||||
// According to the spec, "The name attribute must return the resolved URL
|
||||
// of the requested resource. This attribute must not change even if the
|
||||
// fetch redirected to a different URL."
|
||||
channel->GetOriginalURI(getter_AddRefs(originalURI));
|
||||
originalURI->GetSpec(name);
|
||||
NS_ConvertUTF8toUTF16 entryName(name);
|
||||
|
||||
// The nsITimedChannel argument will be used to gather all the timings.
|
||||
// The nsIHttpChannel argument will be used to check if any cross-origin
|
||||
// redirects occurred.
|
||||
// The last argument is the "zero time" (offset). Since we don't want
|
||||
// any offset for the resource timing, this will be set to "0" - the
|
||||
// resource timing returns a relative timing (no offset).
|
||||
RefPtr<PerformanceTiming> performanceTiming =
|
||||
new PerformanceTiming(this, timedChannel, channel,
|
||||
0);
|
||||
|
||||
// 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);
|
||||
|
||||
// If the initiator type had no valid value, then set it to the default
|
||||
// ("other") value.
|
||||
if (initiatorType.IsEmpty()) {
|
||||
initiatorType = NS_LITERAL_STRING("other");
|
||||
}
|
||||
performanceEntry->SetInitiatorType(initiatorType);
|
||||
InsertResourceEntry(performanceEntry);
|
||||
}
|
||||
}
|
||||
|
||||
// To be removed once bug 1124165 lands
|
||||
bool
|
||||
PerformanceMainThread::IsPerformanceTimingAttribute(const nsAString& aName)
|
||||
{
|
||||
// Note that toJSON is added to this list due to bug 1047848
|
||||
static const char* attributes[] =
|
||||
{"navigationStart", "unloadEventStart", "unloadEventEnd", "redirectStart",
|
||||
"redirectEnd", "fetchStart", "domainLookupStart", "domainLookupEnd",
|
||||
"connectStart", "connectEnd", "requestStart", "responseStart",
|
||||
"responseEnd", "domLoading", "domInteractive",
|
||||
"domContentLoadedEventStart", "domContentLoadedEventEnd", "domComplete",
|
||||
"loadEventStart", "loadEventEnd", nullptr};
|
||||
|
||||
for (uint32_t i = 0; attributes[i]; ++i) {
|
||||
if (aName.EqualsASCII(attributes[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceMainThread::GetPerformanceTimingFromString(const nsAString& aProperty)
|
||||
{
|
||||
if (!IsPerformanceTimingAttribute(aProperty)) {
|
||||
return 0;
|
||||
}
|
||||
if (aProperty.EqualsLiteral("navigationStart")) {
|
||||
// DOMHighResTimeStamp is in relation to navigationStart, so this will be
|
||||
// zero.
|
||||
return GetDOMTiming()->GetNavigationStart();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("unloadEventStart")) {
|
||||
return GetDOMTiming()->GetUnloadEventStart();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("unloadEventEnd")) {
|
||||
return GetDOMTiming()->GetUnloadEventEnd();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("redirectStart")) {
|
||||
return Timing()->RedirectStart();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("redirectEnd")) {
|
||||
return Timing()->RedirectEnd();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("fetchStart")) {
|
||||
return Timing()->FetchStart();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("domainLookupStart")) {
|
||||
return Timing()->DomainLookupStart();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("domainLookupEnd")) {
|
||||
return Timing()->DomainLookupEnd();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("connectStart")) {
|
||||
return Timing()->ConnectStart();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("connectEnd")) {
|
||||
return Timing()->ConnectEnd();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("requestStart")) {
|
||||
return Timing()->RequestStart();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("responseStart")) {
|
||||
return Timing()->ResponseStart();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("responseEnd")) {
|
||||
return Timing()->ResponseEnd();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("domLoading")) {
|
||||
return GetDOMTiming()->GetDomLoading();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("domInteractive")) {
|
||||
return GetDOMTiming()->GetDomInteractive();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("domContentLoadedEventStart")) {
|
||||
return GetDOMTiming()->GetDomContentLoadedEventStart();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("domContentLoadedEventEnd")) {
|
||||
return GetDOMTiming()->GetDomContentLoadedEventEnd();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("domComplete")) {
|
||||
return GetDOMTiming()->GetDomComplete();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("loadEventStart")) {
|
||||
return GetDOMTiming()->GetLoadEventStart();
|
||||
}
|
||||
if (aProperty.EqualsLiteral("loadEventEnd")) {
|
||||
return GetDOMTiming()->GetLoadEventEnd();
|
||||
}
|
||||
MOZ_CRASH("IsPerformanceTimingAttribute and GetPerformanceTimingFromString are out of sync");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceMainThread::InsertUserEntry(PerformanceEntry* aEntry)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsAutoCString uri;
|
||||
uint64_t markCreationEpoch = 0;
|
||||
|
||||
if (nsContentUtils::IsUserTimingLoggingEnabled() ||
|
||||
nsContentUtils::SendPerformanceTimingNotifications()) {
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
|
||||
if (owner && owner->GetDocumentURI()) {
|
||||
rv = owner->GetDocumentURI()->GetHost(uri);
|
||||
}
|
||||
|
||||
if(NS_FAILED(rv)) {
|
||||
// If we have no URI, just put in "none".
|
||||
uri.AssignLiteral("none");
|
||||
}
|
||||
markCreationEpoch = static_cast<uint64_t>(PR_Now() / PR_USEC_PER_MSEC);
|
||||
|
||||
if (nsContentUtils::IsUserTimingLoggingEnabled()) {
|
||||
Performance::LogEntry(aEntry, uri);
|
||||
}
|
||||
}
|
||||
|
||||
if (nsContentUtils::SendPerformanceTimingNotifications()) {
|
||||
TimingNotification(aEntry, uri, markCreationEpoch);
|
||||
}
|
||||
|
||||
Performance::InsertUserEntry(aEntry);
|
||||
}
|
||||
|
||||
TimeStamp
|
||||
PerformanceMainThread::CreationTimeStamp() const
|
||||
{
|
||||
return GetDOMTiming()->GetNavigationStartTimeStamp();
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceMainThread::CreationTime() const
|
||||
{
|
||||
return GetDOMTiming()->GetNavigationStart();
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
87
dom/performance/PerformanceMainThread.h
Normal file
87
dom/performance/PerformanceMainThread.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* -*- 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_PerformanceMainThread_h
|
||||
#define mozilla_dom_PerformanceMainThread_h
|
||||
|
||||
#include "Performance.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PerformanceMainThread final : public Performance
|
||||
{
|
||||
public:
|
||||
PerformanceMainThread(nsPIDOMWindowInner* aWindow,
|
||||
nsDOMNavigationTiming* aDOMTiming,
|
||||
nsITimedChannel* aChannel,
|
||||
Performance* aParentPerformance);
|
||||
|
||||
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;
|
||||
|
||||
virtual void AddEntry(nsIHttpChannel* channel,
|
||||
nsITimedChannel* timedChannel) override;
|
||||
|
||||
TimeStamp CreationTimeStamp() const override;
|
||||
|
||||
DOMHighResTimeStamp CreationTime() const override;
|
||||
|
||||
virtual void GetMozMemory(JSContext *aCx,
|
||||
JS::MutableHandle<JSObject*> aObj) override;
|
||||
|
||||
virtual nsDOMNavigationTiming* GetDOMTiming() const override
|
||||
{
|
||||
return mDOMTiming;
|
||||
}
|
||||
|
||||
virtual nsITimedChannel* GetChannel() const override
|
||||
{
|
||||
return mChannel;
|
||||
}
|
||||
|
||||
virtual Performance* GetParentPerformance() const override
|
||||
{
|
||||
return mParentPerformance;
|
||||
}
|
||||
|
||||
protected:
|
||||
~PerformanceMainThread();
|
||||
|
||||
nsISupports* GetAsISupports() override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
void InsertUserEntry(PerformanceEntry* aEntry) override;
|
||||
|
||||
bool IsPerformanceTimingAttribute(const nsAString& aName) override;
|
||||
|
||||
DOMHighResTimeStamp
|
||||
GetPerformanceTimingFromString(const nsAString& aTimingName) override;
|
||||
|
||||
void DispatchBufferFullEvent() override;
|
||||
|
||||
RefPtr<nsDOMNavigationTiming> mDOMTiming;
|
||||
nsCOMPtr<nsITimedChannel> mChannel;
|
||||
RefPtr<PerformanceTiming> mTiming;
|
||||
RefPtr<PerformanceNavigation> mNavigation;
|
||||
RefPtr<Performance> mParentPerformance;
|
||||
JS::Heap<JSObject*> mMozMemory;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PerformanceMainThread_h
|
||||
31
dom/performance/PerformanceMark.cpp
Normal file
31
dom/performance/PerformanceMark.cpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- 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 "PerformanceMark.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/dom/PerformanceMarkBinding.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
PerformanceMark::PerformanceMark(nsISupports* aParent,
|
||||
const nsAString& aName,
|
||||
DOMHighResTimeStamp aStartTime)
|
||||
: PerformanceEntry(aParent, aName, NS_LITERAL_STRING("mark"))
|
||||
, mStartTime(aStartTime)
|
||||
{
|
||||
// mParent is null in workers.
|
||||
MOZ_ASSERT(mParent || !NS_IsMainThread());
|
||||
}
|
||||
|
||||
PerformanceMark::~PerformanceMark()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
PerformanceMark::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return PerformanceMarkBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
38
dom/performance/PerformanceMark.h
Normal file
38
dom/performance/PerformanceMark.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* -*- 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_performancemark_h___
|
||||
#define mozilla_dom_performancemark_h___
|
||||
|
||||
#include "mozilla/dom/PerformanceEntry.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// http://www.w3.org/TR/user-timing/#performancemark
|
||||
class PerformanceMark final : public PerformanceEntry
|
||||
{
|
||||
public:
|
||||
PerformanceMark(nsISupports* aParent,
|
||||
const nsAString& aName,
|
||||
DOMHighResTimeStamp aStartTime);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual DOMHighResTimeStamp StartTime() const override
|
||||
{
|
||||
return mStartTime;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~PerformanceMark();
|
||||
DOMHighResTimeStamp mStartTime;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_performancemark_h___ */
|
||||
33
dom/performance/PerformanceMeasure.cpp
Normal file
33
dom/performance/PerformanceMeasure.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/* -*- 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 "PerformanceMeasure.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/dom/PerformanceMeasureBinding.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
PerformanceMeasure::PerformanceMeasure(nsISupports* aParent,
|
||||
const nsAString& aName,
|
||||
DOMHighResTimeStamp aStartTime,
|
||||
DOMHighResTimeStamp aEndTime)
|
||||
: PerformanceEntry(aParent, aName, NS_LITERAL_STRING("measure")),
|
||||
mStartTime(aStartTime),
|
||||
mDuration(aEndTime - aStartTime)
|
||||
{
|
||||
// mParent is null in workers.
|
||||
MOZ_ASSERT(mParent || !NS_IsMainThread());
|
||||
}
|
||||
|
||||
PerformanceMeasure::~PerformanceMeasure()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
PerformanceMeasure::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return PerformanceMeasureBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
45
dom/performance/PerformanceMeasure.h
Normal file
45
dom/performance/PerformanceMeasure.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- 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_performancemeasure_h___
|
||||
#define mozilla_dom_performancemeasure_h___
|
||||
|
||||
#include "mozilla/dom/PerformanceEntry.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// http://www.w3.org/TR/user-timing/#performancemeasure
|
||||
class PerformanceMeasure final : public PerformanceEntry
|
||||
{
|
||||
public:
|
||||
PerformanceMeasure(nsISupports* aParent,
|
||||
const nsAString& aName,
|
||||
DOMHighResTimeStamp aStartTime,
|
||||
DOMHighResTimeStamp aEndTime);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual DOMHighResTimeStamp StartTime() const override
|
||||
{
|
||||
return mStartTime;
|
||||
}
|
||||
|
||||
virtual DOMHighResTimeStamp Duration() const override
|
||||
{
|
||||
return mDuration;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~PerformanceMeasure();
|
||||
DOMHighResTimeStamp mStartTime;
|
||||
DOMHighResTimeStamp mDuration;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_performancemeasure_h___ */
|
||||
43
dom/performance/PerformanceNavigation.cpp
Normal file
43
dom/performance/PerformanceNavigation.cpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- 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 "PerformanceNavigation.h"
|
||||
#include "PerformanceTiming.h"
|
||||
#include "mozilla/dom/PerformanceNavigationBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceNavigation, mPerformance)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PerformanceNavigation, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PerformanceNavigation, Release)
|
||||
|
||||
PerformanceNavigation::PerformanceNavigation(Performance* aPerformance)
|
||||
: mPerformance(aPerformance)
|
||||
{
|
||||
MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
|
||||
}
|
||||
|
||||
PerformanceNavigation::~PerformanceNavigation()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
PerformanceNavigation::WrapObject(JSContext *cx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return PerformanceNavigationBinding::Wrap(cx, this, aGivenProto);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
PerformanceNavigation::RedirectCount() const
|
||||
{
|
||||
return GetPerformanceTiming()->GetRedirectCount();
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
61
dom/performance/PerformanceNavigation.h
Normal file
61
dom/performance/PerformanceNavigation.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- 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_PerformanceNavigation_h
|
||||
#define mozilla_dom_PerformanceNavigation_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/Performance.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Script "performance.navigation" object
|
||||
class PerformanceNavigation final : public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
explicit PerformanceNavigation(Performance* aPerformance);
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PerformanceNavigation)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PerformanceNavigation)
|
||||
|
||||
nsDOMNavigationTiming* GetDOMTiming() const
|
||||
{
|
||||
return mPerformance->GetDOMTiming();
|
||||
}
|
||||
|
||||
PerformanceTiming* GetPerformanceTiming() const
|
||||
{
|
||||
return mPerformance->Timing();
|
||||
}
|
||||
|
||||
Performance* GetParentObject() const
|
||||
{
|
||||
return mPerformance;
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext *cx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
// PerformanceNavigation WebIDL methods
|
||||
uint16_t Type() const
|
||||
{
|
||||
return GetDOMTiming()->GetType();
|
||||
}
|
||||
|
||||
uint16_t RedirectCount() const;
|
||||
|
||||
private:
|
||||
~PerformanceNavigation();
|
||||
RefPtr<Performance> mPerformance;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PerformanceNavigation_h
|
||||
184
dom/performance/PerformanceObserver.cpp
Normal file
184
dom/performance/PerformanceObserver.cpp
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/* -*- 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 "PerformanceObserver.h"
|
||||
|
||||
#include "mozilla/dom/Performance.h"
|
||||
#include "mozilla/dom/PerformanceBinding.h"
|
||||
#include "mozilla/dom/PerformanceEntryBinding.h"
|
||||
#include "mozilla/dom/PerformanceObserverBinding.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include "nsString.h"
|
||||
#include "PerformanceEntry.h"
|
||||
#include "PerformanceObserverEntryList.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerScope.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::dom::workers;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PerformanceObserver)
|
||||
tmp->Disconnect();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PerformanceObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(PerformanceObserver)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(PerformanceObserver)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(PerformanceObserver)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceObserver)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
PerformanceObserver::PerformanceObserver(nsPIDOMWindowInner* aOwner,
|
||||
PerformanceObserverCallback& aCb)
|
||||
: mOwner(aOwner)
|
||||
, mCallback(&aCb)
|
||||
, mConnected(false)
|
||||
{
|
||||
MOZ_ASSERT(mOwner);
|
||||
mPerformance = aOwner->GetPerformance();
|
||||
}
|
||||
|
||||
PerformanceObserver::PerformanceObserver(WorkerPrivate* aWorkerPrivate,
|
||||
PerformanceObserverCallback& aCb)
|
||||
: mCallback(&aCb)
|
||||
, mConnected(false)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
mPerformance = aWorkerPrivate->GlobalScope()->GetPerformance();
|
||||
}
|
||||
|
||||
PerformanceObserver::~PerformanceObserver()
|
||||
{
|
||||
Disconnect();
|
||||
MOZ_ASSERT(!mConnected);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<PerformanceObserver>
|
||||
PerformanceObserver::Constructor(const GlobalObject& aGlobal,
|
||||
PerformanceObserverCallback& aCb,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> ownerWindow =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!ownerWindow) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(ownerWindow->IsInnerWindow());
|
||||
|
||||
RefPtr<PerformanceObserver> observer =
|
||||
new PerformanceObserver(ownerWindow, aCb);
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
JSContext* cx = aGlobal.Context();
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
RefPtr<PerformanceObserver> observer =
|
||||
new PerformanceObserver(workerPrivate, aCb);
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
PerformanceObserver::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return PerformanceObserverBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceObserver::Notify()
|
||||
{
|
||||
if (mQueuedEntries.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
RefPtr<PerformanceObserverEntryList> list =
|
||||
new PerformanceObserverEntryList(this, mQueuedEntries);
|
||||
|
||||
ErrorResult rv;
|
||||
mCallback->Call(this, *list, *this, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
}
|
||||
mQueuedEntries.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceObserver::QueueEntry(PerformanceEntry* aEntry)
|
||||
{
|
||||
MOZ_ASSERT(aEntry);
|
||||
|
||||
nsAutoString entryType;
|
||||
aEntry->GetEntryType(entryType);
|
||||
if (!mEntryTypes.Contains<nsString>(entryType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mQueuedEntries.AppendElement(aEntry);
|
||||
}
|
||||
|
||||
static const char16_t *const sValidTypeNames[4] = {
|
||||
u"mark",
|
||||
u"measure",
|
||||
u"resource",
|
||||
u"server"
|
||||
};
|
||||
|
||||
void
|
||||
PerformanceObserver::Observe(const PerformanceObserverInit& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aOptions.mEntryTypes.IsEmpty()) {
|
||||
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<nsString> validEntryTypes;
|
||||
|
||||
for (const char16_t* name : sValidTypeNames) {
|
||||
nsDependentString validTypeName(name);
|
||||
if (aOptions.mEntryTypes.Contains<nsString>(validTypeName) &&
|
||||
!validEntryTypes.Contains<nsString>(validTypeName)) {
|
||||
validEntryTypes.AppendElement(validTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
if (validEntryTypes.IsEmpty()) {
|
||||
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
mEntryTypes.SwapElements(validEntryTypes);
|
||||
|
||||
mPerformance->AddObserver(this);
|
||||
mConnected = true;
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceObserver::Disconnect()
|
||||
{
|
||||
if (mConnected) {
|
||||
MOZ_ASSERT(mPerformance);
|
||||
mPerformance->RemoveObserver(this);
|
||||
mConnected = false;
|
||||
}
|
||||
}
|
||||
79
dom/performance/PerformanceObserver.h
Normal file
79
dom/performance/PerformanceObserver.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/* -*- 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_PerformanceObserver_h__
|
||||
#define mozilla_dom_PerformanceObserver_h__
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsISupports.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
class Performance;
|
||||
class PerformanceEntry;
|
||||
class PerformanceObserverCallback;
|
||||
struct PerformanceObserverInit;
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
} // namespace workers
|
||||
|
||||
class PerformanceObserver final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PerformanceObserver)
|
||||
|
||||
static already_AddRefed<PerformanceObserver>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
PerformanceObserverCallback& aCb,
|
||||
ErrorResult& aRv);
|
||||
|
||||
PerformanceObserver(nsPIDOMWindowInner* aOwner,
|
||||
PerformanceObserverCallback& aCb);
|
||||
|
||||
PerformanceObserver(workers::WorkerPrivate* aWorkerPrivate,
|
||||
PerformanceObserverCallback& aCb);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
|
||||
void Observe(const PerformanceObserverInit& aOptions,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
void Disconnect();
|
||||
|
||||
void Notify();
|
||||
void QueueEntry(PerformanceEntry* aEntry);
|
||||
|
||||
private:
|
||||
~PerformanceObserver();
|
||||
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
RefPtr<PerformanceObserverCallback> mCallback;
|
||||
RefPtr<Performance> mPerformance;
|
||||
nsTArray<nsString> mEntryTypes;
|
||||
bool mConnected;
|
||||
nsTArray<RefPtr<PerformanceEntry>> mQueuedEntries;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
96
dom/performance/PerformanceObserverEntryList.cpp
Normal file
96
dom/performance/PerformanceObserverEntryList.cpp
Normal file
|
|
@ -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 "PerformanceObserverEntryList.h"
|
||||
|
||||
#include "mozilla/dom/Performance.h"
|
||||
#include "mozilla/dom/PerformanceObserverEntryListBinding.h"
|
||||
#include "nsString.h"
|
||||
#include "PerformanceResourceTiming.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceObserverEntryList,
|
||||
mOwner,
|
||||
mEntries)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(PerformanceObserverEntryList)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(PerformanceObserverEntryList)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceObserverEntryList)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
PerformanceObserverEntryList::~PerformanceObserverEntryList()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
PerformanceObserverEntryList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return PerformanceObserverEntryListBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceObserverEntryList::GetEntries(
|
||||
const PerformanceEntryFilterOptions& aFilter,
|
||||
nsTArray<RefPtr<PerformanceEntry>>& aRetval)
|
||||
{
|
||||
aRetval.Clear();
|
||||
for (const RefPtr<PerformanceEntry>& entry : mEntries) {
|
||||
if (aFilter.mInitiatorType.WasPassed()) {
|
||||
const PerformanceResourceTiming* resourceEntry =
|
||||
entry->ToResourceTiming();
|
||||
if (!resourceEntry) {
|
||||
continue;
|
||||
}
|
||||
nsAutoString initiatorType;
|
||||
resourceEntry->GetInitiatorType(initiatorType);
|
||||
if (!initiatorType.Equals(aFilter.mInitiatorType.Value())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (aFilter.mName.WasPassed() &&
|
||||
!entry->GetName().Equals(aFilter.mName.Value())) {
|
||||
continue;
|
||||
}
|
||||
if (aFilter.mEntryType.WasPassed() &&
|
||||
!entry->GetEntryType().Equals(aFilter.mEntryType.Value())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
aRetval.AppendElement(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceObserverEntryList::GetEntriesByType(
|
||||
const nsAString& aEntryType,
|
||||
nsTArray<RefPtr<PerformanceEntry>>& aRetval)
|
||||
{
|
||||
aRetval.Clear();
|
||||
for (const RefPtr<PerformanceEntry>& entry : mEntries) {
|
||||
if (entry->GetEntryType().Equals(aEntryType)) {
|
||||
aRetval.AppendElement(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceObserverEntryList::GetEntriesByName(
|
||||
const nsAString& aName,
|
||||
const Optional<nsAString>& aEntryType,
|
||||
nsTArray<RefPtr<PerformanceEntry>>& aRetval)
|
||||
{
|
||||
aRetval.Clear();
|
||||
for (const RefPtr<PerformanceEntry>& entry : mEntries) {
|
||||
if (entry->GetName().Equals(aName)) {
|
||||
aRetval.AppendElement(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
dom/performance/PerformanceObserverEntryList.h
Normal file
65
dom/performance/PerformanceObserverEntryList.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/* -*- 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_PerformanceObserverEntryList_h__
|
||||
#define mozilla_dom_PerformanceObserverEntryList_h__
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/dom/PerformanceEntryBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct PerformanceEntryFilterOptions;
|
||||
class PerformanceEntry;
|
||||
template<typename T> class Optional;
|
||||
|
||||
class PerformanceObserverEntryList final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
~PerformanceObserverEntryList();
|
||||
|
||||
public:
|
||||
PerformanceObserverEntryList(nsISupports* aOwner,
|
||||
nsTArray<RefPtr<PerformanceEntry>>&
|
||||
aEntries)
|
||||
: mOwner(aOwner)
|
||||
, mEntries(aEntries)
|
||||
{
|
||||
}
|
||||
|
||||
nsISupports* GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PerformanceObserverEntryList)
|
||||
|
||||
void GetEntries(const PerformanceEntryFilterOptions& aFilter,
|
||||
nsTArray<RefPtr<PerformanceEntry>>& aRetval);
|
||||
void GetEntriesByType(const nsAString& aEntryType,
|
||||
nsTArray<RefPtr<PerformanceEntry>>& aRetval);
|
||||
void GetEntriesByName(const nsAString& aName,
|
||||
const Optional<nsAString>& aEntryType,
|
||||
nsTArray<RefPtr<PerformanceEntry>>& aRetval);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
nsTArray<RefPtr<PerformanceEntry>> mEntries;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
#endif
|
||||
53
dom/performance/PerformanceResourceTiming.cpp
Normal file
53
dom/performance/PerformanceResourceTiming.cpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/* -*- 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 "PerformanceResourceTiming.h"
|
||||
#include "mozilla/dom/PerformanceResourceTimingBinding.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(PerformanceResourceTiming,
|
||||
PerformanceEntry,
|
||||
mTiming)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceResourceTiming,
|
||||
PerformanceEntry)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PerformanceResourceTiming)
|
||||
NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(PerformanceResourceTiming, PerformanceEntry)
|
||||
NS_IMPL_RELEASE_INHERITED(PerformanceResourceTiming, PerformanceEntry)
|
||||
|
||||
PerformanceResourceTiming::PerformanceResourceTiming(PerformanceTiming* aPerformanceTiming,
|
||||
Performance* aPerformance,
|
||||
const nsAString& aName)
|
||||
: PerformanceEntry(aPerformance, aName, NS_LITERAL_STRING("resource")),
|
||||
mTiming(aPerformanceTiming),
|
||||
mEncodedBodySize(0),
|
||||
mTransferSize(0),
|
||||
mDecodedBodySize(0)
|
||||
{
|
||||
MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
|
||||
}
|
||||
|
||||
PerformanceResourceTiming::~PerformanceResourceTiming()
|
||||
{
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceResourceTiming::StartTime() const
|
||||
{
|
||||
DOMHighResTimeStamp startTime = mTiming->RedirectStartHighRes();
|
||||
return startTime ? startTime : mTiming->FetchStartHighRes();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
PerformanceResourceTiming::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return PerformanceResourceTimingBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
185
dom/performance/PerformanceResourceTiming.h
Normal file
185
dom/performance/PerformanceResourceTiming.h
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/* -*- 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_PerformanceResourceTiming_h___
|
||||
#define mozilla_dom_PerformanceResourceTiming_h___
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsITimedChannel.h"
|
||||
#include "Performance.h"
|
||||
#include "PerformanceEntry.h"
|
||||
#include "PerformanceTiming.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// http://www.w3.org/TR/resource-timing/#performanceresourcetiming
|
||||
class PerformanceResourceTiming final : public PerformanceEntry
|
||||
{
|
||||
public:
|
||||
typedef mozilla::TimeStamp TimeStamp;
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
|
||||
PerformanceResourceTiming,
|
||||
PerformanceEntry)
|
||||
|
||||
PerformanceResourceTiming(PerformanceTiming* aPerformanceTiming,
|
||||
Performance* aPerformance,
|
||||
const nsAString& aName);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
|
||||
virtual DOMHighResTimeStamp StartTime() const override;
|
||||
|
||||
virtual DOMHighResTimeStamp Duration() const override
|
||||
{
|
||||
return ResponseEnd() - StartTime();
|
||||
}
|
||||
|
||||
void GetInitiatorType(nsAString& aInitiatorType) const
|
||||
{
|
||||
aInitiatorType = mInitiatorType;
|
||||
}
|
||||
|
||||
void SetInitiatorType(const nsAString& aInitiatorType)
|
||||
{
|
||||
mInitiatorType = aInitiatorType;
|
||||
}
|
||||
|
||||
void GetNextHopProtocol(nsAString& aNextHopProtocol) const
|
||||
{
|
||||
aNextHopProtocol = mNextHopProtocol;
|
||||
}
|
||||
|
||||
void SetNextHopProtocol(const nsAString& aNextHopProtocol)
|
||||
{
|
||||
mNextHopProtocol = aNextHopProtocol;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp FetchStart() const {
|
||||
return mTiming
|
||||
? mTiming->FetchStartHighRes()
|
||||
: 0;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp RedirectStart() const {
|
||||
// We have to check if all the redirect URIs had the same origin (since
|
||||
// there is no check in RedirectEndHighRes())
|
||||
return mTiming && mTiming->ShouldReportCrossOriginRedirect()
|
||||
? mTiming->RedirectStartHighRes()
|
||||
: 0;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp RedirectEnd() const {
|
||||
// We have to check if all the redirect URIs had the same origin (since
|
||||
// there is no check in RedirectEndHighRes())
|
||||
return mTiming && mTiming->ShouldReportCrossOriginRedirect()
|
||||
? mTiming->RedirectEndHighRes()
|
||||
: 0;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp DomainLookupStart() const {
|
||||
return mTiming && mTiming->TimingAllowed()
|
||||
? mTiming->DomainLookupStartHighRes()
|
||||
: 0;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp DomainLookupEnd() const {
|
||||
return mTiming && mTiming->TimingAllowed()
|
||||
? mTiming->DomainLookupEndHighRes()
|
||||
: 0;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp ConnectStart() const {
|
||||
return mTiming && mTiming->TimingAllowed()
|
||||
? mTiming->ConnectStartHighRes()
|
||||
: 0;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp ConnectEnd() const {
|
||||
return mTiming && mTiming->TimingAllowed()
|
||||
? mTiming->ConnectEndHighRes()
|
||||
: 0;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp RequestStart() const {
|
||||
return mTiming && mTiming->TimingAllowed()
|
||||
? mTiming->RequestStartHighRes()
|
||||
: 0;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp ResponseStart() const {
|
||||
return mTiming && mTiming->TimingAllowed()
|
||||
? mTiming->ResponseStartHighRes()
|
||||
: 0;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp ResponseEnd() const {
|
||||
return mTiming
|
||||
? mTiming->ResponseEndHighRes()
|
||||
: 0;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp SecureConnectionStart() const
|
||||
{
|
||||
// This measurement is not available for Navigation Timing either.
|
||||
// There is a different bug submitted for it.
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual const PerformanceResourceTiming* ToResourceTiming() const override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
uint64_t TransferSize() const
|
||||
{
|
||||
return mTiming && mTiming->TimingAllowed() ? mTransferSize : 0;
|
||||
}
|
||||
|
||||
uint64_t EncodedBodySize() const
|
||||
{
|
||||
return mTiming && mTiming->TimingAllowed() ? mEncodedBodySize : 0;
|
||||
}
|
||||
|
||||
uint64_t DecodedBodySize() const
|
||||
{
|
||||
return mTiming && mTiming->TimingAllowed() ? mDecodedBodySize : 0;
|
||||
}
|
||||
|
||||
void SetEncodedBodySize(uint64_t aEncodedBodySize)
|
||||
{
|
||||
mEncodedBodySize = aEncodedBodySize;
|
||||
}
|
||||
|
||||
void SetTransferSize(uint64_t aTransferSize)
|
||||
{
|
||||
mTransferSize = aTransferSize;
|
||||
}
|
||||
|
||||
void SetDecodedBodySize(uint64_t aDecodedBodySize)
|
||||
{
|
||||
mDecodedBodySize = aDecodedBodySize;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~PerformanceResourceTiming();
|
||||
|
||||
nsString mInitiatorType;
|
||||
nsString mNextHopProtocol;
|
||||
RefPtr<PerformanceTiming> mTiming;
|
||||
uint64_t mEncodedBodySize;
|
||||
uint64_t mTransferSize;
|
||||
uint64_t mDecodedBodySize;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_PerformanceResourceTiming_h___ */
|
||||
361
dom/performance/PerformanceTiming.cpp
Normal file
361
dom/performance/PerformanceTiming.cpp
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PerformanceTiming.h"
|
||||
#include "mozilla/dom/PerformanceTimingBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceTiming, mPerformance)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PerformanceTiming, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PerformanceTiming, Release)
|
||||
|
||||
PerformanceTiming::PerformanceTiming(Performance* aPerformance,
|
||||
nsITimedChannel* aChannel,
|
||||
nsIHttpChannel* aHttpChannel,
|
||||
DOMHighResTimeStamp aZeroTime)
|
||||
: mPerformance(aPerformance),
|
||||
mFetchStart(0.0),
|
||||
mZeroTime(aZeroTime),
|
||||
mRedirectCount(0),
|
||||
mTimingAllowed(true),
|
||||
mAllRedirectsSameOrigin(true),
|
||||
mInitialized(!!aChannel),
|
||||
mReportCrossOriginRedirect(true)
|
||||
{
|
||||
MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
|
||||
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
|
||||
mZeroTime = 0;
|
||||
}
|
||||
|
||||
// The aHttpChannel argument is null if this PerformanceTiming object is
|
||||
// being used for navigation timing (which is only relevant for documents).
|
||||
// It has a non-null value if this PerformanceTiming object is being used
|
||||
// for resource timing, which can include document loads, both toplevel and
|
||||
// in subframes, and resources linked from a document.
|
||||
if (aHttpChannel) {
|
||||
mTimingAllowed = CheckAllowedOrigin(aHttpChannel, aChannel);
|
||||
bool redirectsPassCheck = false;
|
||||
aChannel->GetAllRedirectsPassTimingAllowCheck(&redirectsPassCheck);
|
||||
mReportCrossOriginRedirect = mTimingAllowed && redirectsPassCheck;
|
||||
}
|
||||
|
||||
InitializeTimingInfo(aChannel);
|
||||
}
|
||||
|
||||
// Copy the timing info from the channel so we don't need to keep the channel
|
||||
// alive just to get the timestamps.
|
||||
void
|
||||
PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel)
|
||||
{
|
||||
if (aChannel) {
|
||||
aChannel->GetAsyncOpen(&mAsyncOpen);
|
||||
aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin);
|
||||
aChannel->GetRedirectCount(&mRedirectCount);
|
||||
aChannel->GetRedirectStart(&mRedirectStart);
|
||||
aChannel->GetRedirectEnd(&mRedirectEnd);
|
||||
aChannel->GetDomainLookupStart(&mDomainLookupStart);
|
||||
aChannel->GetDomainLookupEnd(&mDomainLookupEnd);
|
||||
aChannel->GetConnectStart(&mConnectStart);
|
||||
aChannel->GetConnectEnd(&mConnectEnd);
|
||||
aChannel->GetRequestStart(&mRequestStart);
|
||||
aChannel->GetResponseStart(&mResponseStart);
|
||||
aChannel->GetCacheReadStart(&mCacheReadStart);
|
||||
aChannel->GetResponseEnd(&mResponseEnd);
|
||||
aChannel->GetCacheReadEnd(&mCacheReadEnd);
|
||||
}
|
||||
}
|
||||
|
||||
PerformanceTiming::~PerformanceTiming()
|
||||
{
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceTiming::FetchStartHighRes()
|
||||
{
|
||||
if (!mFetchStart) {
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
MOZ_ASSERT(!mAsyncOpen.IsNull(), "The fetch start time stamp should always be "
|
||||
"valid if the performance timing is enabled");
|
||||
mFetchStart = (!mAsyncOpen.IsNull())
|
||||
? TimeStampToDOMHighRes(mAsyncOpen)
|
||||
: 0.0;
|
||||
}
|
||||
return mFetchStart;
|
||||
}
|
||||
|
||||
DOMTimeMilliSec
|
||||
PerformanceTiming::FetchStart()
|
||||
{
|
||||
return static_cast<int64_t>(FetchStartHighRes());
|
||||
}
|
||||
|
||||
bool
|
||||
PerformanceTiming::CheckAllowedOrigin(nsIHttpChannel* aResourceChannel,
|
||||
nsITimedChannel* aChannel)
|
||||
{
|
||||
if (!IsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the current document passes the ckeck.
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
aResourceChannel->GetLoadInfo(getter_AddRefs(loadInfo));
|
||||
if (!loadInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TYPE_DOCUMENT loads have no loadingPrincipal. And that's OK, because we
|
||||
// never actually need to have a performance timing entry for TYPE_DOCUMENT
|
||||
// loads.
|
||||
if (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = loadInfo->LoadingPrincipal();
|
||||
|
||||
// Check if the resource is either same origin as the page that started
|
||||
// the load, or if the response contains the proper Timing-Allow-Origin
|
||||
// header with the domain of the page that started the load.
|
||||
return aChannel->TimingAllowCheck(principal);
|
||||
}
|
||||
|
||||
bool
|
||||
PerformanceTiming::TimingAllowed() const
|
||||
{
|
||||
return mTimingAllowed;
|
||||
}
|
||||
|
||||
uint16_t
|
||||
PerformanceTiming::GetRedirectCount() const
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
|
||||
return 0;
|
||||
}
|
||||
if (!mAllRedirectsSameOrigin) {
|
||||
return 0;
|
||||
}
|
||||
return mRedirectCount;
|
||||
}
|
||||
|
||||
bool
|
||||
PerformanceTiming::ShouldReportCrossOriginRedirect() const
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the redirect count is 0, or if one of the cross-origin
|
||||
// redirects doesn't have the proper Timing-Allow-Origin header,
|
||||
// then RedirectStart and RedirectEnd will be set to zero
|
||||
return (mRedirectCount != 0) && mReportCrossOriginRedirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* RedirectStartHighRes() is used by both the navigation timing and the
|
||||
* resource timing. Since, navigation timing and resource timing check and
|
||||
* interpret cross-domain redirects in a different manner,
|
||||
* RedirectStartHighRes() will make no checks for cross-domain redirect.
|
||||
* It's up to the consumers of this method (PerformanceTiming::RedirectStart()
|
||||
* and PerformanceResourceTiming::RedirectStart() to make such verifications.
|
||||
*
|
||||
* @return a valid timing if the Performance Timing is enabled
|
||||
*/
|
||||
DOMHighResTimeStamp
|
||||
PerformanceTiming::RedirectStartHighRes()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
return TimeStampToDOMHighResOrFetchStart(mRedirectStart);
|
||||
}
|
||||
|
||||
DOMTimeMilliSec
|
||||
PerformanceTiming::RedirectStart()
|
||||
{
|
||||
if (!IsInitialized()) {
|
||||
return 0;
|
||||
}
|
||||
// We have to check if all the redirect URIs had the same origin (since there
|
||||
// is no check in RedirectStartHighRes())
|
||||
if (mAllRedirectsSameOrigin && mRedirectCount) {
|
||||
return static_cast<int64_t>(RedirectStartHighRes());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* RedirectEndHighRes() is used by both the navigation timing and the resource
|
||||
* timing. Since, navigation timing and resource timing check and interpret
|
||||
* cross-domain redirects in a different manner, RedirectEndHighRes() will make
|
||||
* no checks for cross-domain redirect. It's up to the consumers of this method
|
||||
* (PerformanceTiming::RedirectEnd() and
|
||||
* PerformanceResourceTiming::RedirectEnd() to make such verifications.
|
||||
*
|
||||
* @return a valid timing if the Performance Timing is enabled
|
||||
*/
|
||||
DOMHighResTimeStamp
|
||||
PerformanceTiming::RedirectEndHighRes()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
return TimeStampToDOMHighResOrFetchStart(mRedirectEnd);
|
||||
}
|
||||
|
||||
DOMTimeMilliSec
|
||||
PerformanceTiming::RedirectEnd()
|
||||
{
|
||||
if (!IsInitialized()) {
|
||||
return 0;
|
||||
}
|
||||
// We have to check if all the redirect URIs had the same origin (since there
|
||||
// is no check in RedirectEndHighRes())
|
||||
if (mAllRedirectsSameOrigin && mRedirectCount) {
|
||||
return static_cast<int64_t>(RedirectEndHighRes());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceTiming::DomainLookupStartHighRes()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
return TimeStampToDOMHighResOrFetchStart(mDomainLookupStart);
|
||||
}
|
||||
|
||||
DOMTimeMilliSec
|
||||
PerformanceTiming::DomainLookupStart()
|
||||
{
|
||||
return static_cast<int64_t>(DomainLookupStartHighRes());
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceTiming::DomainLookupEndHighRes()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
// Bug 1155008 - nsHttpTransaction is racy. Return DomainLookupStart when null
|
||||
return mDomainLookupEnd.IsNull() ? DomainLookupStartHighRes()
|
||||
: TimeStampToDOMHighRes(mDomainLookupEnd);
|
||||
}
|
||||
|
||||
DOMTimeMilliSec
|
||||
PerformanceTiming::DomainLookupEnd()
|
||||
{
|
||||
return static_cast<int64_t>(DomainLookupEndHighRes());
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceTiming::ConnectStartHighRes()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
return mConnectStart.IsNull() ? DomainLookupEndHighRes()
|
||||
: TimeStampToDOMHighRes(mConnectStart);
|
||||
}
|
||||
|
||||
DOMTimeMilliSec
|
||||
PerformanceTiming::ConnectStart()
|
||||
{
|
||||
return static_cast<int64_t>(ConnectStartHighRes());
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceTiming::ConnectEndHighRes()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
// Bug 1155008 - nsHttpTransaction is racy. Return ConnectStart when null
|
||||
return mConnectEnd.IsNull() ? ConnectStartHighRes()
|
||||
: TimeStampToDOMHighRes(mConnectEnd);
|
||||
}
|
||||
|
||||
DOMTimeMilliSec
|
||||
PerformanceTiming::ConnectEnd()
|
||||
{
|
||||
return static_cast<int64_t>(ConnectEndHighRes());
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceTiming::RequestStartHighRes()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
return TimeStampToDOMHighResOrFetchStart(mRequestStart);
|
||||
}
|
||||
|
||||
DOMTimeMilliSec
|
||||
PerformanceTiming::RequestStart()
|
||||
{
|
||||
return static_cast<int64_t>(RequestStartHighRes());
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceTiming::ResponseStartHighRes()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
if (mResponseStart.IsNull() ||
|
||||
(!mCacheReadStart.IsNull() && mCacheReadStart < mResponseStart)) {
|
||||
mResponseStart = mCacheReadStart;
|
||||
}
|
||||
return TimeStampToDOMHighResOrFetchStart(mResponseStart);
|
||||
}
|
||||
|
||||
DOMTimeMilliSec
|
||||
PerformanceTiming::ResponseStart()
|
||||
{
|
||||
return static_cast<int64_t>(ResponseStartHighRes());
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceTiming::ResponseEndHighRes()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
if (mResponseEnd.IsNull() ||
|
||||
(!mCacheReadEnd.IsNull() && mCacheReadEnd < mResponseEnd)) {
|
||||
mResponseEnd = mCacheReadEnd;
|
||||
}
|
||||
// Bug 1155008 - nsHttpTransaction is racy. Return ResponseStart when null
|
||||
return mResponseEnd.IsNull() ? ResponseStartHighRes()
|
||||
: TimeStampToDOMHighRes(mResponseEnd);
|
||||
}
|
||||
|
||||
DOMTimeMilliSec
|
||||
PerformanceTiming::ResponseEnd()
|
||||
{
|
||||
return static_cast<int64_t>(ResponseEndHighRes());
|
||||
}
|
||||
|
||||
bool
|
||||
PerformanceTiming::IsInitialized() const
|
||||
{
|
||||
return mInitialized;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
PerformanceTiming::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return PerformanceTimingBinding::Wrap(cx, this, aGivenProto);
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
278
dom/performance/PerformanceTiming.h
Normal file
278
dom/performance/PerformanceTiming.h
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
/* -*- 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_PerformanceTiming_h
|
||||
#define mozilla_dom_PerformanceTiming_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "Performance.h"
|
||||
|
||||
class nsIHttpChannel;
|
||||
class nsITimedChannel;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Script "performance.timing" object
|
||||
class PerformanceTiming final : public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param aPerformance
|
||||
* The performance object (the JS parent).
|
||||
* This will allow access to "window.performance.timing" attribute for
|
||||
* the navigation timing (can't be null).
|
||||
* @param aChannel
|
||||
* An nsITimedChannel used to gather all the networking timings by both
|
||||
* the navigation timing and the resource timing (can't be null).
|
||||
* @param aHttpChannel
|
||||
* An nsIHttpChannel (the resource's http channel).
|
||||
* This will be used by the resource timing cross-domain check
|
||||
* algorithm.
|
||||
* Argument is null for the navigation timing (navigation timing uses
|
||||
* another algorithm for the cross-domain redirects).
|
||||
* @param aZeroTime
|
||||
* The offset that will be added to the timestamp of each event. This
|
||||
* argument should be equal to performance.navigationStart for
|
||||
* navigation timing and "0" for the resource timing.
|
||||
*/
|
||||
PerformanceTiming(Performance* aPerformance,
|
||||
nsITimedChannel* aChannel,
|
||||
nsIHttpChannel* aHttpChannel,
|
||||
DOMHighResTimeStamp aZeroTime);
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PerformanceTiming)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PerformanceTiming)
|
||||
|
||||
nsDOMNavigationTiming* GetDOMTiming() const
|
||||
{
|
||||
return mPerformance->GetDOMTiming();
|
||||
}
|
||||
|
||||
Performance* GetParentObject() const
|
||||
{
|
||||
return mPerformance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param aStamp
|
||||
* The TimeStamp recorded for a specific event. This TimeStamp can
|
||||
* be null.
|
||||
* @return the duration of an event with a given TimeStamp, relative to the
|
||||
* navigationStart TimeStamp (the moment the user landed on the
|
||||
* page), if the given TimeStamp is valid. Otherwise, it will return
|
||||
* the FetchStart timing value.
|
||||
*/
|
||||
inline DOMHighResTimeStamp TimeStampToDOMHighResOrFetchStart(TimeStamp aStamp)
|
||||
{
|
||||
return (!aStamp.IsNull())
|
||||
? TimeStampToDOMHighRes(aStamp)
|
||||
: FetchStartHighRes();
|
||||
}
|
||||
|
||||
/**
|
||||
* The nsITimedChannel records an absolute timestamp for each event.
|
||||
* The nsDOMNavigationTiming will record the moment when the user landed on
|
||||
* the page. This is a window.performance unique timestamp, so it can be used
|
||||
* for all the events (navigation timing and resource timing events).
|
||||
*
|
||||
* The algorithm operates in 2 steps:
|
||||
* 1. The first step is to subtract the two timestamps: the argument (the
|
||||
* envet's timesramp) and the navigation start timestamp. This will result in
|
||||
* a relative timestamp of the event (relative to the navigation start -
|
||||
* window.performance.timing.navigationStart).
|
||||
* 2. The second step is to add any required offset (the mZeroTime). For now,
|
||||
* this offset value is either 0 (for the resource timing), or equal to
|
||||
* "performance.navigationStart" (for navigation timing).
|
||||
* For the resource timing, mZeroTime is set to 0, causing the result to be a
|
||||
* relative time.
|
||||
* For the navigation timing, mZeroTime is set to "performance.navigationStart"
|
||||
* causing the result be an absolute time.
|
||||
*
|
||||
* @param aStamp
|
||||
* The TimeStamp recorded for a specific event. This TimeStamp can't
|
||||
* be null.
|
||||
* @return number of milliseconds value as one of:
|
||||
* - relative to the navigation start time, time the user has landed on the
|
||||
* page
|
||||
* - an absolute wall clock time since the unix epoch
|
||||
*/
|
||||
inline DOMHighResTimeStamp TimeStampToDOMHighRes(TimeStamp aStamp) const
|
||||
{
|
||||
MOZ_ASSERT(!aStamp.IsNull());
|
||||
TimeDuration duration =
|
||||
aStamp - GetDOMTiming()->GetNavigationStartTimeStamp();
|
||||
return duration.ToMilliseconds() + mZeroTime;
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext *cx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
// PerformanceNavigation WebIDL methods
|
||||
DOMTimeMilliSec NavigationStart() const
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
return GetDOMTiming()->GetNavigationStart();
|
||||
}
|
||||
|
||||
DOMTimeMilliSec UnloadEventStart()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
return GetDOMTiming()->GetUnloadEventStart();
|
||||
}
|
||||
|
||||
DOMTimeMilliSec UnloadEventEnd()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
return GetDOMTiming()->GetUnloadEventEnd();
|
||||
}
|
||||
|
||||
uint16_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
|
||||
// with a value of * or matching the domain of the loading Principal
|
||||
bool CheckAllowedOrigin(nsIHttpChannel* aResourceChannel, nsITimedChannel* aChannel);
|
||||
|
||||
// Cached result of CheckAllowedOrigin. If false, security sensitive
|
||||
// attributes of the resourceTiming object will be set to 0
|
||||
bool TimingAllowed() const;
|
||||
|
||||
// If this is false the values of redirectStart/End will be 0
|
||||
// This is false if no redirects occured, or if any of the responses failed
|
||||
// the timing-allow-origin check in HttpBaseChannel::TimingAllowCheck
|
||||
bool ShouldReportCrossOriginRedirect() const;
|
||||
|
||||
// High resolution (used by resource timing)
|
||||
DOMHighResTimeStamp FetchStartHighRes();
|
||||
DOMHighResTimeStamp RedirectStartHighRes();
|
||||
DOMHighResTimeStamp RedirectEndHighRes();
|
||||
DOMHighResTimeStamp DomainLookupStartHighRes();
|
||||
DOMHighResTimeStamp DomainLookupEndHighRes();
|
||||
DOMHighResTimeStamp ConnectStartHighRes();
|
||||
DOMHighResTimeStamp ConnectEndHighRes();
|
||||
DOMHighResTimeStamp RequestStartHighRes();
|
||||
DOMHighResTimeStamp ResponseStartHighRes();
|
||||
DOMHighResTimeStamp ResponseEndHighRes();
|
||||
|
||||
// Low resolution (used by navigation timing)
|
||||
DOMTimeMilliSec FetchStart();
|
||||
DOMTimeMilliSec RedirectStart();
|
||||
DOMTimeMilliSec RedirectEnd();
|
||||
DOMTimeMilliSec DomainLookupStart();
|
||||
DOMTimeMilliSec DomainLookupEnd();
|
||||
DOMTimeMilliSec ConnectStart();
|
||||
DOMTimeMilliSec ConnectEnd();
|
||||
DOMTimeMilliSec RequestStart();
|
||||
DOMTimeMilliSec ResponseStart();
|
||||
DOMTimeMilliSec ResponseEnd();
|
||||
|
||||
DOMTimeMilliSec DomLoading()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
return GetDOMTiming()->GetDomLoading();
|
||||
}
|
||||
|
||||
DOMTimeMilliSec DomInteractive() const
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
return GetDOMTiming()->GetDomInteractive();
|
||||
}
|
||||
|
||||
DOMTimeMilliSec DomContentLoadedEventStart() const
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
return GetDOMTiming()->GetDomContentLoadedEventStart();
|
||||
}
|
||||
|
||||
DOMTimeMilliSec DomContentLoadedEventEnd() const
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
return GetDOMTiming()->GetDomContentLoadedEventEnd();
|
||||
}
|
||||
|
||||
DOMTimeMilliSec DomComplete() const
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
return GetDOMTiming()->GetDomComplete();
|
||||
}
|
||||
|
||||
DOMTimeMilliSec LoadEventStart() const
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
return GetDOMTiming()->GetLoadEventStart();
|
||||
}
|
||||
|
||||
DOMTimeMilliSec LoadEventEnd() const
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled()) {
|
||||
return 0;
|
||||
}
|
||||
return GetDOMTiming()->GetLoadEventEnd();
|
||||
}
|
||||
|
||||
private:
|
||||
~PerformanceTiming();
|
||||
|
||||
bool IsInitialized() const;
|
||||
void InitializeTimingInfo(nsITimedChannel* aChannel);
|
||||
|
||||
RefPtr<Performance> mPerformance;
|
||||
DOMHighResTimeStamp mFetchStart;
|
||||
|
||||
// This is an offset that will be added to each timing ([ms] resolution).
|
||||
// There are only 2 possible values: (1) logicaly equal to navigationStart
|
||||
// TimeStamp (results are absolute timstamps - wallclock); (2) "0" (results
|
||||
// are relative to the navigation start).
|
||||
DOMHighResTimeStamp mZeroTime;
|
||||
|
||||
TimeStamp mAsyncOpen;
|
||||
TimeStamp mRedirectStart;
|
||||
TimeStamp mRedirectEnd;
|
||||
TimeStamp mDomainLookupStart;
|
||||
TimeStamp mDomainLookupEnd;
|
||||
TimeStamp mConnectStart;
|
||||
TimeStamp mConnectEnd;
|
||||
TimeStamp mRequestStart;
|
||||
TimeStamp mResponseStart;
|
||||
TimeStamp mCacheReadStart;
|
||||
TimeStamp mResponseEnd;
|
||||
TimeStamp mCacheReadEnd;
|
||||
uint16_t mRedirectCount;
|
||||
bool mTimingAllowed;
|
||||
bool mAllRedirectsSameOrigin;
|
||||
bool mInitialized;
|
||||
|
||||
// If the resourceTiming object should have non-zero redirectStart and
|
||||
// redirectEnd attributes. It is false if there were no redirects, or if
|
||||
// any of the responses didn't pass the timing-allow-check
|
||||
bool mReportCrossOriginRedirect;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PerformanceTiming_h
|
||||
85
dom/performance/PerformanceWorker.cpp
Normal file
85
dom/performance/PerformanceWorker.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/* -*- 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 "PerformanceWorker.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
using namespace workers;
|
||||
|
||||
PerformanceWorker::PerformanceWorker(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (mWorkerPrivate->PerformanceLoggingEnabled()) {
|
||||
nsAutoCString uri;
|
||||
nsCOMPtr<nsIURI> scriptURI = mWorkerPrivate->GetResolvedScriptURI();
|
||||
if (!scriptURI || NS_FAILED(scriptURI->GetHost(uri))) {
|
||||
// If we have no URI, just put in "none".
|
||||
uri.AssignLiteral("none");
|
||||
}
|
||||
Performance::LogEntry(aEntry, uri);
|
||||
}
|
||||
Performance::InsertUserEntry(aEntry);
|
||||
}
|
||||
|
||||
TimeStamp
|
||||
PerformanceWorker::CreationTimeStamp() const
|
||||
{
|
||||
return mWorkerPrivate->NowBaseTimeStamp();
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceWorker::CreationTime() const
|
||||
{
|
||||
return mWorkerPrivate->NowBaseTime();
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
100
dom/performance/PerformanceWorker.h
Normal file
100
dom/performance/PerformanceWorker.h
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/* -*- 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_PerformanceWorker_h
|
||||
#define mozilla_dom_PerformanceWorker_h
|
||||
|
||||
#include "Performance.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
}
|
||||
|
||||
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.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual PerformanceNavigation* Navigation() override
|
||||
{
|
||||
MOZ_CRASH("This should not be called on workers.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void AddEntry(nsIHttpChannel* channel,
|
||||
nsITimedChannel* timedChannel) override
|
||||
{
|
||||
MOZ_CRASH("This should not be called on workers.");
|
||||
}
|
||||
|
||||
TimeStamp CreationTimeStamp() const override;
|
||||
|
||||
DOMHighResTimeStamp CreationTime() const override;
|
||||
|
||||
virtual void GetMozMemory(JSContext *aCx,
|
||||
JS::MutableHandle<JSObject*> aObj) override
|
||||
{
|
||||
MOZ_CRASH("This should not be called on workers.");
|
||||
}
|
||||
|
||||
virtual nsDOMNavigationTiming* GetDOMTiming() const override
|
||||
{
|
||||
MOZ_CRASH("This should not be called on workers.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual nsITimedChannel* GetChannel() const override
|
||||
{
|
||||
MOZ_CRASH("This should not be called on workers.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual Performance* GetParentPerformance() const override
|
||||
{
|
||||
MOZ_CRASH("This should not be called on workers.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
~PerformanceWorker();
|
||||
|
||||
nsISupports* GetAsISupports() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
private:
|
||||
workers::WorkerPrivate* mWorkerPrivate;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PerformanceWorker_h
|
||||
39
dom/performance/moz.build
Normal file
39
dom/performance/moz.build
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'Performance.h',
|
||||
'PerformanceEntry.h',
|
||||
'PerformanceMark.h',
|
||||
'PerformanceMeasure.h',
|
||||
'PerformanceNavigation.h',
|
||||
'PerformanceObserver.h',
|
||||
'PerformanceObserverEntryList.h',
|
||||
'PerformanceResourceTiming.h',
|
||||
'PerformanceTiming.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Performance.cpp',
|
||||
'PerformanceEntry.cpp',
|
||||
'PerformanceMainThread.cpp',
|
||||
'PerformanceMark.cpp',
|
||||
'PerformanceMeasure.cpp',
|
||||
'PerformanceNavigation.cpp',
|
||||
'PerformanceObserver.cpp',
|
||||
'PerformanceObserverEntryList.cpp',
|
||||
'PerformanceResourceTiming.cpp',
|
||||
'PerformanceTiming.cpp',
|
||||
'PerformanceWorker.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/workers',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += [ 'tests/mochitest.ini' ]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
17
dom/performance/tests/mochitest.ini
Normal file
17
dom/performance/tests/mochitest.ini
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
[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]
|
||||
[test_worker_user_timing.html]
|
||||
[test_worker_observer.html]
|
||||
[test_sharedWorker_performance_user_timing.html]
|
||||
[test_worker_performance_now.html]
|
||||
74
dom/performance/tests/performance_observer.html
Normal file
74
dom/performance/tests/performance_observer.html
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<!--
|
||||
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>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
var port;
|
||||
|
||||
function ok(a, msg) {
|
||||
dump("OK: " + !!a + " => " + a + " " + msg + "\n");
|
||||
port.postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
|
||||
}
|
||||
|
||||
function is(a, b, msg) {
|
||||
dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
|
||||
port.postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
|
||||
}
|
||||
|
||||
function isnot(a, b, msg) {
|
||||
dump("ISNOT: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
|
||||
port.postMessage({type: 'status', status: a != b, msg: a + " != " + b + ": " + msg });
|
||||
}
|
||||
|
||||
importScripts('test_performance_user_timing.js');
|
||||
|
||||
onconnect = function(evt) {
|
||||
port = evt.ports[0];
|
||||
|
||||
for (var i = 0; i < steps.length; ++i) {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
steps[i]();
|
||||
}
|
||||
|
||||
port.postMessage({type: 'finish'});
|
||||
}
|
||||
17
dom/performance/tests/test_performance_observer.html
Normal file
17
dom/performance/tests/test_performance_observer.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<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>
|
||||
<script>
|
||||
'use strict';
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.enable_performance_observer", true]]},
|
||||
function() {
|
||||
window.open("performance_observer.html");
|
||||
});
|
||||
</script>
|
||||
226
dom/performance/tests/test_performance_observer.js
Normal file
226
dom/performance/tests/test_performance_observer.js
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
setup({ explicit_done: true });
|
||||
|
||||
test(t => {
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
new PerformanceObserver();
|
||||
}, "PerformanceObserver constructor should throw TypeError if no argument is specified.");
|
||||
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
new PerformanceObserver({});
|
||||
}, "PerformanceObserver constructor should throw TypeError if the argument is not a function.");
|
||||
}, "Test that PerformanceObserver constructor throws exception");
|
||||
|
||||
test(t => {
|
||||
var observer = new PerformanceObserver(() => {
|
||||
});
|
||||
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
observer.observe();
|
||||
}, "observe() should throw TypeError exception if no option specified.");
|
||||
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
observer.observe({ unsupportedAttribute: "unsupported" });
|
||||
}, "obsrve() should throw TypeError exception if the option has no 'entryTypes' attribute.");
|
||||
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
observer.observe({ entryTypes: [] });
|
||||
}, "obsrve() should throw TypeError exception if 'entryTypes' attribute is an empty sequence.");
|
||||
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
observer.observe({ entryTypes: null });
|
||||
}, "obsrve() should throw TypeError exception if 'entryTypes' attribute is null.");
|
||||
|
||||
assert_throws({name: "TypeError"}, function() {
|
||||
observer.observe({ entryTypes: ["invalid"]});
|
||||
}, "obsrve() should throw TypeError exception if 'entryTypes' attribute value is invalid.");
|
||||
}, "Test that PerformanceObserver.observe throws exception");
|
||||
|
||||
function promiseObserve(test, options) {
|
||||
return new Promise(resolve => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observer = new PerformanceObserver(list => resolve(list));
|
||||
observer.observe(options);
|
||||
test.add_cleanup(() => observer.disconnect());
|
||||
});
|
||||
}
|
||||
|
||||
promise_test(t => {
|
||||
var promise = promiseObserve(t, {entryTypes: ['mark', 'measure']});
|
||||
|
||||
performance.mark("test-start");
|
||||
performance.mark("test-end");
|
||||
performance.measure("test-measure", "test-start", "test-end");
|
||||
|
||||
return promise.then(list => {
|
||||
assert_equals(list.getEntries().length, 3,
|
||||
"There should be three observed entries.");
|
||||
|
||||
var markEntries = list.getEntries().filter(entry => {
|
||||
return entry.entryType == "mark";
|
||||
});
|
||||
assert_array_equals(markEntries, performance.getEntriesByType("mark"),
|
||||
"Observed 'mark' entries should equal to entries obtained by getEntriesByType.");
|
||||
|
||||
var measureEntries = list.getEntries().filter(entry => {
|
||||
return entry.entryType == "measure";
|
||||
});
|
||||
assert_array_equals(measureEntries, performance.getEntriesByType("measure"),
|
||||
"Observed 'measure' entries should equal to entries obtained by getEntriesByType.");
|
||||
});
|
||||
}, "Test for user-timing with PerformanceObserver");
|
||||
|
||||
promise_test(t => {
|
||||
var promise = new Promise((resolve, reject) => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observer = new PerformanceObserver(list => reject(list));
|
||||
observer.observe({entryTypes: ['mark', 'measure']});
|
||||
observer.disconnect();
|
||||
t.step_timeout(resolve, 100);
|
||||
});
|
||||
|
||||
performance.mark("test-start");
|
||||
performance.mark("test-end");
|
||||
performance.measure("test-measure", "test-start", "test-end");
|
||||
|
||||
return promise.then(() => {
|
||||
assert_equals(performance.getEntriesByType("mark").length, 2);
|
||||
assert_equals(performance.getEntriesByType("measure").length, 1);
|
||||
}, list => {
|
||||
assert_unreached("Observer callback should never be called.");
|
||||
});
|
||||
|
||||
}, "Nothing should be notified after disconnecting observer");
|
||||
|
||||
promise_test(t => {
|
||||
var promise = promiseObserve(t, {entryTypes: ['mark']});
|
||||
|
||||
performance.mark("test");
|
||||
|
||||
return promise.then(list => {
|
||||
assert_array_equals(list.getEntries({"entryType": "mark"}),
|
||||
performance.getEntriesByType("mark"),
|
||||
"getEntries with entryType filter should return correct results.");
|
||||
|
||||
assert_array_equals(list.getEntries({"name": "test"}),
|
||||
performance.getEntriesByName("test"),
|
||||
"getEntries with name filter should return correct results.");
|
||||
|
||||
assert_array_equals(list.getEntries({"name": "test",
|
||||
"entryType": "mark"}),
|
||||
performance.getEntriesByName("test"),
|
||||
"getEntries with name and entryType filter should return correct results.");
|
||||
|
||||
assert_array_equals(list.getEntries({"name": "invalid"}),
|
||||
[],
|
||||
"getEntries with non-existent name filter should return an empty array.");
|
||||
|
||||
assert_array_equals(list.getEntries({"name": "test",
|
||||
"entryType": "measure"}),
|
||||
[],
|
||||
"getEntries with name filter and non-existent entryType should return an empty array.");
|
||||
|
||||
assert_array_equals(list.getEntries({"name": "invalid",
|
||||
"entryType": "mark"}),
|
||||
[],
|
||||
"getEntries with non-existent name and entryType filter should return an empty array.");
|
||||
|
||||
assert_array_equals(list.getEntries({initiatorType: "xmlhttprequest"}),
|
||||
[],
|
||||
"getEntries with initiatorType filter should return an empty array.");
|
||||
});
|
||||
}, "Test for PerformanceObserverEntryList.getEntries");
|
||||
|
||||
promise_test(t => {
|
||||
var promise = promiseObserve(t, {entryTypes: ['mark', 'measure']});
|
||||
|
||||
performance.mark("test");
|
||||
performance.measure("test-measure", "test", "test");
|
||||
|
||||
return promise.then(list => {
|
||||
assert_array_equals(list.getEntriesByType("mark"),
|
||||
performance.getEntriesByType("mark"));
|
||||
assert_array_equals(list.getEntriesByType("measure"),
|
||||
performance.getEntriesByType("measure"));
|
||||
});
|
||||
}, "Test for PerformanceObserverEntryList.getEntriesByType");
|
||||
|
||||
promise_test(t => {
|
||||
var promise = promiseObserve(t, {entryTypes: ['mark', 'measure']});
|
||||
|
||||
performance.mark("test");
|
||||
performance.measure("test-measure", "test", "test");
|
||||
|
||||
return promise.then(list => {
|
||||
assert_array_equals(list.getEntriesByName("test"),
|
||||
performance.getEntriesByName("test"));
|
||||
assert_array_equals(list.getEntriesByName("test-measure"),
|
||||
performance.getEntriesByName("test-measure"));
|
||||
});
|
||||
}, "Test for PerformanceObserverEntryList.getEntriesByName");
|
||||
|
||||
promise_test(t => {
|
||||
var promise = new Promise(resolve => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observer = new PerformanceObserver(list => resolve(list));
|
||||
observer.observe({entryTypes: ['mark', 'measure']});
|
||||
observer.observe({entryTypes: ['mark', 'measure']});
|
||||
t.add_cleanup(() => observer.disconnect());
|
||||
});
|
||||
|
||||
performance.mark("test-start");
|
||||
performance.mark("test-end");
|
||||
performance.measure("test-measure", "test-start", "test-end");
|
||||
|
||||
return promise.then(list => {
|
||||
assert_equals(list.getEntries().length, 3,
|
||||
"Observed user timing entries should have only three entries.");
|
||||
});
|
||||
}, "Test that invoking observe method twice affects nothing");
|
||||
|
||||
promise_test(t => {
|
||||
var promise = new Promise(resolve => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observer = new PerformanceObserver(list => resolve(list));
|
||||
observer.observe({entryTypes: ['mark', 'measure']});
|
||||
observer.observe({entryTypes: ['mark']});
|
||||
t.add_cleanup(() => observer.disconnect());
|
||||
});
|
||||
|
||||
performance.mark("test-start");
|
||||
performance.mark("test-end");
|
||||
performance.measure("test-measure", "test-start", "test-end");
|
||||
|
||||
return promise.then(list => {
|
||||
assert_equals(list.getEntries().length, 2,
|
||||
"Observed user timing entries should have only two entries.");
|
||||
});
|
||||
}, "Test that observing filter is replaced by a new filter");
|
||||
|
||||
promise_test(t => {
|
||||
var promise = new Promise(resolve => {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
var observer = new PerformanceObserver(list => resolve(list));
|
||||
observer.observe({entryTypes: ['mark']});
|
||||
observer.observe({entryTypes: ['measure']});
|
||||
t.add_cleanup(() => observer.disconnect());
|
||||
});
|
||||
|
||||
performance.mark("test-start");
|
||||
performance.mark("test-end");
|
||||
performance.measure("test-measure", "test-start", "test-end");
|
||||
|
||||
return promise.then(list => {
|
||||
assert_equals(list.getEntries().length, 1,
|
||||
"Observed user timing entries should have only 1 entries.");
|
||||
});
|
||||
}, "Test that observing filter is replaced by a new filter");
|
||||
45
dom/performance/tests/test_performance_user_timing.html
Normal file
45
dom/performance/tests/test_performance_user_timing.html
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=782751
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 782751</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>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=782751">Mozilla Bug 782751 - User Timing API</a>
|
||||
<div id="content">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var index = 0;
|
||||
|
||||
function next() {
|
||||
ok(true, "Begin!");
|
||||
var arr;
|
||||
for (var i = 0; i < steps.length; ++i) {
|
||||
try {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
performance.clearResourceTimings();
|
||||
is(performance.getEntriesByType("resource").length, 0, "clearing performance resource entries");
|
||||
is(performance.getEntriesByType("mark").length, 0, "clearing performance mark entries");
|
||||
is(performance.getEntriesByType("measure").length, 0, "clearing performance measure entries");
|
||||
steps[i]();
|
||||
} catch(ex) {
|
||||
ok(false, "Caught exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(next);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
272
dom/performance/tests/test_performance_user_timing.js
Normal file
272
dom/performance/tests/test_performance_user_timing.js
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
var steps = [
|
||||
// Test single mark addition
|
||||
function () {
|
||||
ok(true, "Running mark addition test");
|
||||
performance.mark("test");
|
||||
var marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 1, "Number of marks should be 1");
|
||||
var mark = marks[0];
|
||||
is(mark.name, "test", "mark name should be 'test'");
|
||||
is(mark.entryType, "mark", "mark type should be 'mark'");
|
||||
isnot(mark.startTime, 0, "mark start time should not be 0");
|
||||
is(mark.duration, 0, "mark duration should be 0");
|
||||
},
|
||||
// Test multiple mark addition
|
||||
function () {
|
||||
ok(true, "Running multiple mark with same name addition test");
|
||||
performance.mark("test");
|
||||
performance.mark("test");
|
||||
performance.mark("test");
|
||||
var marks_type = performance.getEntriesByType("mark");
|
||||
is(marks_type.length, 3, "Number of marks by type should be 3");
|
||||
var marks_name = performance.getEntriesByName("test");
|
||||
is(marks_name.length, 3, "Number of marks by name should be 3");
|
||||
var mark = marks_name[0];
|
||||
is(mark.name, "test", "mark name should be 'test'");
|
||||
is(mark.entryType, "mark", "mark type should be 'mark'");
|
||||
isnot(mark.startTime, 0, "mark start time should not be 0");
|
||||
is(mark.duration, 0, "mark duration should be 0");
|
||||
var times = [];
|
||||
// This also tests the chronological ordering specified as
|
||||
// required for getEntries in the performance timeline spec.
|
||||
marks_name.forEach(function(s) {
|
||||
times.forEach(function(time) {
|
||||
ok(s.startTime >= time.startTime,
|
||||
"Times should be equal or increasing between similarly named marks: " + s.startTime + " >= " + time.startTime);
|
||||
});
|
||||
times.push(s);
|
||||
});
|
||||
},
|
||||
// Test all marks removal
|
||||
function () {
|
||||
ok(true, "Running all mark removal test");
|
||||
performance.mark("test");
|
||||
performance.mark("test2");
|
||||
var marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 2, "number of marks before all removal");
|
||||
performance.clearMarks();
|
||||
marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 0, "number of marks after all removal");
|
||||
},
|
||||
// Test single mark removal
|
||||
function () {
|
||||
ok(true, "Running removal test (0 'test' marks with other marks)");
|
||||
performance.mark("test2");
|
||||
var marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 1, "number of marks before all removal");
|
||||
performance.clearMarks("test");
|
||||
marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 1, "number of marks after all removal");
|
||||
},
|
||||
// Test single mark removal
|
||||
function () {
|
||||
ok(true, "Running removal test (0 'test' marks with no other marks)");
|
||||
var marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 0, "number of marks before all removal");
|
||||
performance.clearMarks("test");
|
||||
marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 0, "number of marks after all removal");
|
||||
},
|
||||
function () {
|
||||
ok(true, "Running removal test (1 'test' mark with other marks)");
|
||||
performance.mark("test");
|
||||
performance.mark("test2");
|
||||
var marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 2, "number of marks before all removal");
|
||||
performance.clearMarks("test");
|
||||
marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 1, "number of marks after all removal");
|
||||
},
|
||||
function () {
|
||||
ok(true, "Running removal test (1 'test' mark with no other marks)");
|
||||
performance.mark("test");
|
||||
var marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 1, "number of marks before all removal");
|
||||
performance.clearMarks("test");
|
||||
marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 0, "number of marks after all removal");
|
||||
},
|
||||
function () {
|
||||
ok(true, "Running removal test (2 'test' marks with other marks)");
|
||||
performance.mark("test");
|
||||
performance.mark("test");
|
||||
performance.mark("test2");
|
||||
var marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 3, "number of marks before all removal");
|
||||
performance.clearMarks("test");
|
||||
marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 1, "number of marks after all removal");
|
||||
},
|
||||
function () {
|
||||
ok(true, "Running removal test (2 'test' marks with no other marks)");
|
||||
performance.mark("test");
|
||||
performance.mark("test");
|
||||
var marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 2, "number of marks before all removal");
|
||||
performance.clearMarks("test");
|
||||
marks = performance.getEntriesByType("mark");
|
||||
is(marks.length, 0, "number of marks after all removal");
|
||||
},
|
||||
// Test mark name being same as navigation timing parameter
|
||||
function () {
|
||||
ok(true, "Running mark name collision test");
|
||||
for (n in performance.timing) {
|
||||
try {
|
||||
if (n == "toJSON") {
|
||||
ok(true, "Skipping toJSON entry in collision test");
|
||||
continue;
|
||||
}
|
||||
performance.mark(n);
|
||||
ok(false, "Mark name collision test failed for name " + n + ", shouldn't make it here!");
|
||||
} catch (e) {
|
||||
ok(e instanceof DOMException, "DOM exception thrown for mark named " + n);
|
||||
is(e.code, e.SYNTAX_ERR, "DOM exception for name collision is syntax error");
|
||||
}
|
||||
};
|
||||
},
|
||||
// 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");
|
||||
},
|
||||
function () {
|
||||
ok(true, "Running measure addition with only start time test");
|
||||
performance.mark("test1");
|
||||
performance.measure("test", "test1", undefined);
|
||||
var measures = performance.getEntriesByName("test", "measure");
|
||||
var marks = performance.getEntriesByName("test1", "mark");
|
||||
var measure = measures[0];
|
||||
var mark = marks[0];
|
||||
is(measure.startTime, mark.startTime, "measure start time should be equal to the mark startTime");
|
||||
ok(measure.duration >= 0, "measure duration should not be negative");
|
||||
},
|
||||
function () {
|
||||
ok(true, "Running measure addition with only end time test");
|
||||
performance.mark("test1");
|
||||
performance.measure("test", undefined, "test1");
|
||||
var measures = performance.getEntriesByName("test", "measure");
|
||||
var marks = performance.getEntriesByName("test1", "mark");
|
||||
var measure = measures[0];
|
||||
var mark = marks[0];
|
||||
ok(measure.duration >= 0, "measure duration should not be negative");
|
||||
},
|
||||
// Test measure picking latest version of similarly named tags
|
||||
function () {
|
||||
ok(true, "Running multiple mark with same name addition test");
|
||||
performance.mark("test");
|
||||
performance.mark("test");
|
||||
performance.mark("test");
|
||||
performance.mark("test2");
|
||||
var marks_name = performance.getEntriesByName("test");
|
||||
is(marks_name.length, 3, "Number of marks by name should be 3");
|
||||
var marks_name2 = performance.getEntriesByName("test2");
|
||||
is(marks_name2.length, 1, "Number of marks by name should be 1");
|
||||
var test_mark = marks_name2[0];
|
||||
performance.measure("test", "test", "test2");
|
||||
var measures_type = performance.getEntriesByType("measure");
|
||||
var last_mark = marks_name[marks_name.length - 1];
|
||||
is(measures_type.length, 1, "Number of measures by type should be 1");
|
||||
var measure = measures_type[0];
|
||||
is(measure.startTime, last_mark.startTime, "Measure start time should be the start time of the latest 'test' mark");
|
||||
// Tolerance testing to avoid oranges, since we're doing double math across two different languages.
|
||||
ok(measure.duration - (test_mark.startTime - last_mark.startTime) < .00001,
|
||||
"Measure duration ( " + measure.duration + ") should be difference between two marks");
|
||||
},
|
||||
function() {
|
||||
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");
|
||||
},
|
||||
// Test all measure removal
|
||||
function () {
|
||||
ok(true, "Running all measure removal test");
|
||||
performance.measure("test");
|
||||
performance.measure("test2");
|
||||
var measures = performance.getEntriesByType("measure");
|
||||
is(measures.length, 2, "measure entries should be length 2");
|
||||
performance.clearMeasures();
|
||||
measures = performance.getEntriesByType("measure");
|
||||
is(measures.length, 0, "measure entries should be length 0");
|
||||
},
|
||||
// Test single measure removal
|
||||
function () {
|
||||
ok(true, "Running all measure removal test");
|
||||
performance.measure("test");
|
||||
performance.measure("test2");
|
||||
var measures = performance.getEntriesByType("measure");
|
||||
is(measures.length, 2, "measure entries should be length 2");
|
||||
performance.clearMeasures("test");
|
||||
measures = performance.getEntriesByType("measure");
|
||||
is(measures.length, 1, "measure entries should be length 1");
|
||||
},
|
||||
// Test measure with invalid start time mark name
|
||||
function () {
|
||||
ok(true, "Running measure invalid start test");
|
||||
try {
|
||||
performance.measure("test", "notamark");
|
||||
ok(false, "invalid measure start time exception not thrown!");
|
||||
} catch (e) {
|
||||
ok(e instanceof DOMException, "DOM exception thrown for invalid measure");
|
||||
is(e.code, e.SYNTAX_ERR, "DOM exception for invalid time is syntax error");
|
||||
}
|
||||
},
|
||||
// Test measure with invalid end time mark name
|
||||
function () {
|
||||
ok(true, "Running measure invalid end test");
|
||||
try {
|
||||
performance.measure("test", undefined, "notamark");
|
||||
ok(false, "invalid measure end time exception not thrown!");
|
||||
} catch (e) {
|
||||
ok(e instanceof DOMException, "DOM exception thrown for invalid measure");
|
||||
is(e.code, e.SYNTAX_ERR, "DOM exception for invalid time is syntax error");
|
||||
}
|
||||
},
|
||||
// Test measure name being same as navigation timing parameter
|
||||
function () {
|
||||
ok(true, "Running measure name collision test");
|
||||
for (n in performance.timing) {
|
||||
try {
|
||||
if (n == "toJSON") {
|
||||
ok(true, "Skipping toJSON entry in collision test");
|
||||
continue;
|
||||
}
|
||||
performance.measure(n);
|
||||
ok(false, "Measure name collision test failed for name " + n + ", shouldn't make it here!");
|
||||
} catch (e) {
|
||||
ok(e instanceof DOMException, "DOM exception thrown for measure named " + n);
|
||||
is(e.code, e.SYNTAX_ERR, "DOM exception for name collision is syntax error");
|
||||
}
|
||||
};
|
||||
},
|
||||
// Test measure mark being a reserved name
|
||||
function () {
|
||||
ok(true, "Create measures using all reserved names");
|
||||
for (n in performance.timing) {
|
||||
try {
|
||||
if (n == "toJSON") {
|
||||
ok(true, "Skipping toJSON entry in collision test");
|
||||
continue;
|
||||
}
|
||||
performance.measure("test", n);
|
||||
ok(true, "Measure created from reserved name as starting time: " + n);
|
||||
} catch (e) {
|
||||
ok(["redirectStart", "redirectEnd", "unloadEventStart", "unloadEventEnd", "loadEventEnd"].indexOf(n) >= 0,
|
||||
"Measure created from reserved name as starting time: " + n + " and threw expected error");
|
||||
}
|
||||
};
|
||||
},
|
||||
// TODO: Test measure picking latest version of similarly named tags
|
||||
];
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for worker performance timing API</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var sw = new SharedWorker('sharedworker_performance_user_timing.js');
|
||||
sw.port.onmessage = function(event) {
|
||||
if (event.data.type == 'finish') {
|
||||
SimpleTest.finish();
|
||||
} else if (event.data.type == 'status') {
|
||||
ok(event.data.status, event.data.msg);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
17
dom/performance/tests/test_worker_observer.html
Normal file
17
dom/performance/tests/test_worker_observer.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<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>
|
||||
<script>
|
||||
'use strict';
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.enable_performance_observer", true]]},
|
||||
function() {
|
||||
window.open("worker_performance_observer.html");
|
||||
});
|
||||
</script>
|
||||
32
dom/performance/tests/test_worker_performance_now.html
Normal file
32
dom/performance/tests/test_worker_performance_now.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Validate Interfaces Exposed to Workers</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
var worker = new Worker('test_worker_performance_now.js');
|
||||
worker.onmessage = function(event) {
|
||||
if (event.data.type == 'finish') {
|
||||
SimpleTest.finish();
|
||||
|
||||
} else if (event.data.type == 'status') {
|
||||
ok(event.data.status, event.data.msg);
|
||||
|
||||
} else if (event.data.type == 'getOSCPU') {
|
||||
worker.postMessage({
|
||||
type: 'returnOSCPU',
|
||||
result: navigator.oscpu
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
76
dom/performance/tests/test_worker_performance_now.js
Normal file
76
dom/performance/tests/test_worker_performance_now.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
function ok(a, msg) {
|
||||
dump("OK: " + !!a + " => " + a + ": " + msg + "\n");
|
||||
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
|
||||
}
|
||||
|
||||
function workerTestDone() {
|
||||
postMessage({ type: 'finish' });
|
||||
}
|
||||
|
||||
function workerTestGetOSCPU(cb) {
|
||||
addEventListener('message', function workerTestGetOSCPUCB(e) {
|
||||
if (e.data.type !== 'returnOSCPU') {
|
||||
return;
|
||||
}
|
||||
removeEventListener('message', workerTestGetOSCPUCB);
|
||||
cb(e.data.result);
|
||||
});
|
||||
postMessage({
|
||||
type: 'getOSCPU'
|
||||
});
|
||||
}
|
||||
|
||||
ok(self.performance, "Performance object should exist.");
|
||||
ok(typeof self.performance.now == 'function', "Performance object should have a 'now' method.");
|
||||
var n = self.performance.now(), d = Date.now();
|
||||
ok(n >= 0, "The value of now() should be equal to or greater than 0.");
|
||||
ok(self.performance.now() >= n, "The value of now() should monotonically increase.");
|
||||
|
||||
// The spec says performance.now() should have micro-second resolution, but allows 1ms if the platform doesn't support it.
|
||||
// Our implementation does provide micro-second resolution, except for windows XP combined with some HW properties
|
||||
// where we can't use QueryPerformanceCounters (see comments at mozilla-central/xpcom/ds/TimeStamp_windows.cpp).
|
||||
// This XP-low-res case results in about 15ms resolutions, and can be identified when perf.now() returns only integers.
|
||||
//
|
||||
// Since setTimeout might return too early/late, our goal is that perf.now() changed within 2ms
|
||||
// (or 25ms for XP-low-res), rather than specific number of setTimeout(N) invocations.
|
||||
// See bug 749894 (intermittent failures of this test)
|
||||
var platformPossiblyLowRes;
|
||||
workerTestGetOSCPU(function(oscpu) {
|
||||
platformPossiblyLowRes = oscpu.indexOf("Windows NT 5.1") == 0; // XP only
|
||||
setTimeout(checkAfterTimeout, 1);
|
||||
});
|
||||
var allInts = (n % 1) == 0; // Indicator of limited HW resolution.
|
||||
var checks = 0;
|
||||
|
||||
function checkAfterTimeout() {
|
||||
checks++;
|
||||
var d2 = Date.now();
|
||||
var n2 = self.performance.now();
|
||||
|
||||
allInts = allInts && (n2 % 1) == 0;
|
||||
var lowResCounter = platformPossiblyLowRes && allInts;
|
||||
|
||||
if ( n2 == n && checks < 50 && // 50 is just a failsafe. Our real goals are 2ms or 25ms.
|
||||
( (d2 - d) < 2 // The spec allows 1ms resolution. We allow up to measured 2ms to ellapse.
|
||||
||
|
||||
lowResCounter &&
|
||||
(d2 - d) < 25
|
||||
)
|
||||
) {
|
||||
setTimeout(checkAfterTimeout, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Loose spec: 1ms resolution, or 15ms resolution for the XP-low-res case.
|
||||
// We shouldn't test that dt is actually within 2/25ms since the iterations break if it isn't, and timeout could be late.
|
||||
ok(n2 > n, "Loose - the value of now() should increase within 2ms (or 25ms if low-res counter) (delta now(): " + (n2 - n) + " ms).");
|
||||
|
||||
// Strict spec: if it's not the XP-low-res case, while the spec allows 1ms resolution, it prefers microseconds, which we provide.
|
||||
// Since the fastest setTimeout return which I observed was ~500 microseconds, a microseconds counter should change in 1 iteretion.
|
||||
ok(n2 > n && (lowResCounter || checks == 1),
|
||||
"Strict - [if high-res counter] the value of now() should increase after one setTimeout (hi-res: " + (!lowResCounter) +
|
||||
", iters: " + checks +
|
||||
", dt: " + (d2 - d) +
|
||||
", now(): " + n2 + ").");
|
||||
workerTestDone();
|
||||
};
|
||||
27
dom/performance/tests/test_worker_user_timing.html
Normal file
27
dom/performance/tests/test_worker_user_timing.html
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for worker performance timing API</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var worker = new Worker('worker_performance_user_timing.js');
|
||||
worker.onmessage = function(event) {
|
||||
if (event.data.type == 'finish') {
|
||||
SimpleTest.finish();
|
||||
} else if (event.data.type == 'status') {
|
||||
ok(event.data.status, event.data.msg);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
32
dom/performance/tests/worker_performance_observer.html
Normal file
32
dom/performance/tests/worker_performance_observer.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!--
|
||||
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>
|
||||
4
dom/performance/tests/worker_performance_observer.js
Normal file
4
dom/performance/tests/worker_performance_observer.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
importScripts(['/resources/testharness.js']);
|
||||
importScripts(['test_performance_observer.js']);
|
||||
|
||||
done();
|
||||
24
dom/performance/tests/worker_performance_user_timing.js
Normal file
24
dom/performance/tests/worker_performance_user_timing.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
function ok(a, msg) {
|
||||
dump("OK: " + !!a + " => " + a + " " + msg + "\n");
|
||||
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
|
||||
}
|
||||
|
||||
function is(a, b, msg) {
|
||||
dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
|
||||
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
|
||||
}
|
||||
|
||||
function isnot(a, b, msg) {
|
||||
dump("ISNOT: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
|
||||
postMessage({type: 'status', status: a != b, msg: a + " != " + b + ": " + msg });
|
||||
}
|
||||
|
||||
importScripts(['test_performance_user_timing.js']);
|
||||
|
||||
for (var i = 0; i < steps.length; ++i) {
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
steps[i]();
|
||||
}
|
||||
|
||||
postMessage({type: 'finish'});
|
||||
Loading…
Add table
Add a link
Reference in a new issue