import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View 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

View 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

View 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);
}

View 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___ */

View 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

View 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

View 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);
}

View 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___ */

View 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);
}

View 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___ */

View 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

View 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

View 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;
}
}

View 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

View 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);
}
}
}

View 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

View 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);
}

View 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___ */

View 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

View 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

View 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

View 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
View 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'

View 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]

View 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>

View file

@ -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'});
}

View 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>

View 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");

View 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>

View 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
];

View 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 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>

View 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>

View 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>

View 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();
};

View 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>

View 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>

View file

@ -0,0 +1,4 @@
importScripts(['/resources/testharness.js']);
importScripts(['test_performance_observer.js']);
done();

View 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'});