Bug 1425458 - Resource timing entries Workers - part 3 - PerformanceStorageWorker, r=smaug

This commit is contained in:
Andrea Marchesini
2018-01-24 17:17:32 +01:00
parent 170e7db772
commit e78cbcb82c
11 changed files with 432 additions and 67 deletions

View File

@@ -438,6 +438,7 @@ Performance::InsertResourceEntry(PerformanceEntry* aEntry)
return;
}
// Don't add the entry if the buffer is full
if (mResourceEntries.Length() >= mResourceTimingBufferSize) {
return;
}

View File

@@ -100,11 +100,15 @@ public:
virtual nsITimedChannel* GetChannel() const = 0;
virtual TimeStamp CreationTimeStamp() const = 0;
void MemoryPressure();
size_t SizeOfUserEntries(mozilla::MallocSizeOf aMallocSizeOf) const;
size_t SizeOfResourceEntries(mozilla::MallocSizeOf aMallocSizeOf) const;
void InsertResourceEntry(PerformanceEntry* aEntry);
protected:
Performance();
explicit Performance(nsPIDOMWindowInner* aWindow);
@@ -112,7 +116,6 @@ protected:
virtual ~Performance();
virtual void InsertUserEntry(PerformanceEntry* aEntry);
void InsertResourceEntry(PerformanceEntry* aEntry);
void ClearUserEntries(const Optional<nsAString>& aEntryName,
const nsAString& aEntryType);
@@ -122,8 +125,6 @@ protected:
virtual void DispatchBufferFullEvent() = 0;
virtual TimeStamp CreationTimeStamp() const = 0;
virtual DOMHighResTimeStamp CreationTime() const = 0;
virtual bool IsPerformanceTimingAttribute(const nsAString& aName)
@@ -137,11 +138,6 @@ protected:
return 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);

View File

@@ -118,65 +118,25 @@ PerformanceMainThread::AddEntry(nsIHttpChannel* channel,
{
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;
nsAutoString entryName;
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);
bool reportTiming = true;
timedChannel->GetReportResourceTiming(&reportTiming);
if (!reportTiming) {
#ifdef DEBUG_jwatt
NS_WARNING(
nsPrintfCString("Not reporting CORS resource: %s", name.get()).get());
#endif
UniquePtr<PerformanceTimingData> performanceTimingData(
PerformanceTimingData::Create(timedChannel, channel, 0, initiatorType,
entryName));
if (!performanceTimingData) {
return;
}
// 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).
UniquePtr<PerformanceTimingData> performanceTimingData(
new PerformanceTimingData(timedChannel, channel, 0));
// The PerformanceResourceTiming object will use the PerformanceTiming
// The PerformanceResourceTiming object will use the PerformanceTimingData
// object to get all the required timings.
RefPtr<PerformanceResourceTiming> performanceEntry =
new PerformanceResourceTiming(Move(performanceTimingData), this,
entryName);
// 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

View File

@@ -15,6 +15,8 @@ class nsITimedChannel;
namespace mozilla {
namespace dom {
class PerformanceTimingData;
class PerformanceStorage
{
public:
@@ -23,7 +25,6 @@ public:
virtual void AddEntry(nsIHttpChannel* aChannel,
nsITimedChannel* aTimedChannel) = 0;
protected:
virtual ~PerformanceStorage() {}
};

View File

@@ -0,0 +1,271 @@
/* -*- 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 "PerformanceStorageWorker.h"
#include "WorkerPrivate.h"
#include "WorkerHolder.h"
namespace mozilla {
namespace dom {
using namespace workers;
class PerformanceProxyData
{
public:
PerformanceProxyData(UniquePtr<PerformanceTimingData>&& aData,
const nsAString& aInitiatorType,
const nsAString& aEntryName)
: mData(Move(aData))
, mInitiatorType(aInitiatorType)
, mEntryName(aEntryName)
{}
UniquePtr<PerformanceTimingData> mData;
nsString mInitiatorType;
nsString mEntryName;
};
namespace {
// This runnable calls InitializeOnWorker() on the worker thread. Here a
// workerHolder is used to monitor when the worker thread is starting the
// shutdown procedure.
// Here we use control runnable because this code must be executed also when in
// a sync event loop.
class PerformanceStorageInitializer final : public WorkerControlRunnable
{
RefPtr<PerformanceStorageWorker> mStorage;
public:
PerformanceStorageInitializer(WorkerPrivate* aWorkerPrivate,
PerformanceStorageWorker* aStorage)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
, mStorage(aStorage)
{}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
mStorage->InitializeOnWorker();
return true;
}
nsresult
Cancel() override
{
mStorage->ShutdownOnWorker();
return WorkerRunnable::Cancel();
}
bool
PreDispatch(WorkerPrivate* aWorkerPrivate) override
{
return true;
}
void
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
{}
};
// Here we use control runnable because this code must be executed also when in
// a sync event loop
class PerformanceEntryAdder final : public WorkerControlRunnable
{
public:
PerformanceEntryAdder(WorkerPrivate* aWorkerPrivate,
PerformanceStorageWorker* aStorage,
UniquePtr<PerformanceProxyData>&& aData)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
, mStorage(aStorage)
, mData(Move(aData))
{}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
mStorage->AddEntryOnWorker(Move(mData));
return true;
}
nsresult
Cancel() override
{
mStorage->ShutdownOnWorker();
return WorkerRunnable::Cancel();
}
bool
PreDispatch(WorkerPrivate* aWorkerPrivate) override
{
return true;
}
void
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
{}
private:
RefPtr<PerformanceStorageWorker> mStorage;
UniquePtr<PerformanceProxyData> mData;
};
class PerformanceStorageWorkerHolder final : public WorkerHolder
{
RefPtr<PerformanceStorageWorker> mStorage;
public:
explicit PerformanceStorageWorkerHolder(PerformanceStorageWorker* aStorage)
: WorkerHolder("PerformanceStorageWorkerHolder",
WorkerHolder::AllowIdleShutdownStart)
, mStorage(aStorage)
{}
bool
Notify(Status aStatus) override
{
if (mStorage) {
RefPtr<PerformanceStorageWorker> storage;
storage.swap(mStorage);
storage->ShutdownOnWorker();
}
return true;
}
};
} // anonymous
/* static */ already_AddRefed<PerformanceStorageWorker>
PerformanceStorageWorker::Create(WorkerPrivate* aWorkerPrivate)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<PerformanceStorageWorker> storage =
new PerformanceStorageWorker(aWorkerPrivate);
RefPtr<PerformanceStorageInitializer> r =
new PerformanceStorageInitializer(aWorkerPrivate, storage);
if (NS_WARN_IF(!r->Dispatch())) {
return nullptr;
}
return storage.forget();
}
PerformanceStorageWorker::PerformanceStorageWorker(WorkerPrivate* aWorkerPrivate)
: mMutex("PerformanceStorageWorker::mMutex")
, mWorkerPrivate(aWorkerPrivate)
, mState(eInitializing)
{
}
PerformanceStorageWorker::~PerformanceStorageWorker() = default;
void
PerformanceStorageWorker::AddEntry(nsIHttpChannel* aChannel,
nsITimedChannel* aTimedChannel)
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mMutex);
if (mState == eTerminated) {
return;
}
nsAutoString initiatorType;
nsAutoString entryName;
UniquePtr<PerformanceTimingData> performanceTimingData(
PerformanceTimingData::Create(aTimedChannel, aChannel, 0, initiatorType,
entryName));
if (!performanceTimingData) {
return;
}
UniquePtr<PerformanceProxyData> data(
new PerformanceProxyData(Move(performanceTimingData), initiatorType,
entryName));
RefPtr<PerformanceEntryAdder> r =
new PerformanceEntryAdder(mWorkerPrivate, this, Move(data));
Unused << NS_WARN_IF(!r->Dispatch());
}
void
PerformanceStorageWorker::InitializeOnWorker()
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mState == eInitializing);
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
mWorkerHolder.reset(new PerformanceStorageWorkerHolder(this));
if (!mWorkerHolder->HoldWorker(mWorkerPrivate, Canceling)) {
MutexAutoUnlock lock(mMutex);
ShutdownOnWorker();
return;
}
// We are ready to accept entries.
mState = eReady;
}
void
PerformanceStorageWorker::ShutdownOnWorker()
{
MutexAutoLock lock(mMutex);
if (mState == eTerminated) {
return;
}
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
mState = eTerminated;
mWorkerHolder = nullptr;
mWorkerPrivate = nullptr;
}
void
PerformanceStorageWorker::AddEntryOnWorker(UniquePtr<PerformanceProxyData>&& aData)
{
RefPtr<Performance> performance;
UniquePtr<PerformanceProxyData> data = Move(aData);
{
MutexAutoLock lock(mMutex);
if (mState == eTerminated) {
return;
}
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(mState == eReady);
WorkerGlobalScope* scope = mWorkerPrivate->GlobalScope();
performance = scope->GetPerformance();
}
if (NS_WARN_IF(!performance)) {
return;
}
RefPtr<PerformanceResourceTiming> performanceEntry =
new PerformanceResourceTiming(Move(data->mData), performance,
data->mEntryName);
performanceEntry->SetInitiatorType(data->mInitiatorType);
performance->InsertResourceEntry(performanceEntry);
}
} // namespace dom
} // namespace mozilla

View File

@@ -0,0 +1,64 @@
/* -*- 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_PerformanceStorageWorker_h
#define mozilla_dom_PerformanceStorageWorker_h
#include "PerformanceStorage.h"
namespace mozilla {
namespace dom {
namespace workers {
class WorkerHolder;
class WorkerPrivate;
}
class PerformanceProxyData;
class PerformanceStorageWorker final : public PerformanceStorage
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PerformanceStorageWorker, override)
static already_AddRefed<PerformanceStorageWorker>
Create(workers::WorkerPrivate* aWorkerPrivate);
void InitializeOnWorker();
void ShutdownOnWorker();
void AddEntry(nsIHttpChannel* aChannel,
nsITimedChannel* aTimedChannel) override;
void AddEntryOnWorker(UniquePtr<PerformanceProxyData>&& aData);
private:
explicit PerformanceStorageWorker(workers::WorkerPrivate* aWorkerPrivate);
~PerformanceStorageWorker();
Mutex mMutex;
// Protected by mutex.
// This raw pointer is nullified when the WorkerHolder communicates the
// shutting down of the worker thread.
workers::WorkerPrivate* mWorkerPrivate;
// Protected by mutex.
enum {
eInitializing,
eReady,
eTerminated,
} mState;
// Touched on worker-thread only.
UniquePtr<WorkerHolder> mWorkerHolder;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PerformanceStorageWorker_h

View File

@@ -20,6 +20,58 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceTiming, mPerformance)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PerformanceTiming, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PerformanceTiming, Release)
/* static */ PerformanceTimingData*
PerformanceTimingData::Create(nsITimedChannel* aTimedChannel,
nsIHttpChannel* aChannel,
DOMHighResTimeStamp aZeroTime,
nsAString& aInitiatorType,
nsAString& aEntryName)
{
MOZ_ASSERT(NS_IsMainThread());
// Check if resource timing is prefed off.
if (!nsContentUtils::IsResourceTimingEnabled()) {
return nullptr;
}
if (!aChannel || !aTimedChannel) {
return nullptr;
}
bool reportTiming = true;
aTimedChannel->GetReportResourceTiming(&reportTiming);
if (!reportTiming) {
return nullptr;
}
aTimedChannel->GetInitiatorType(aInitiatorType);
// If the initiator type had no valid value, then set it to the default
// ("other") value.
if (aInitiatorType.IsEmpty()) {
aInitiatorType = NS_LITERAL_STRING("other");
}
// 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."
nsCOMPtr<nsIURI> originalURI;
aChannel->GetOriginalURI(getter_AddRefs(originalURI));
nsAutoCString name;
originalURI->GetSpec(name);
aEntryName = NS_ConvertUTF8toUTF16(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).
return new PerformanceTimingData(aTimedChannel, aChannel, 0);
}
PerformanceTiming::PerformanceTiming(Performance* aPerformance,
nsITimedChannel* aChannel,
nsIHttpChannel* aHttpChannel,

View File

@@ -20,9 +20,21 @@ class nsITimedChannel;
namespace mozilla {
namespace dom {
class PerformanceTiming;
class PerformanceTimingData final
{
friend class PerformanceTiming;
public:
// This can return null.
static PerformanceTimingData*
Create(nsITimedChannel* aChannel,
nsIHttpChannel* aHttpChannel,
DOMHighResTimeStamp aZeroTime,
nsAString& aInitiatorType,
nsAString& aEntryName);
PerformanceTimingData(nsITimedChannel* aChannel,
nsIHttpChannel* aHttpChannel,
DOMHighResTimeStamp aZeroTime);
@@ -109,8 +121,7 @@ public:
MOZ_ASSERT(aPerformance);
MOZ_ASSERT(!aStamp.IsNull());
TimeDuration duration =
aStamp - aPerformance->GetDOMTiming()->GetNavigationStartTimeStamp();
TimeDuration duration = aStamp - aPerformance->CreationTimeStamp();
return duration.ToMilliseconds() + mZeroTime;
}

View File

@@ -19,6 +19,7 @@ EXPORTS.mozilla.dom += [
'PerformanceResourceTiming.h',
'PerformanceService.h',
'PerformanceStorage.h',
'PerformanceStorageWorker.h',
'PerformanceTiming.h',
]
@@ -34,6 +35,7 @@ UNIFIED_SOURCES += [
'PerformanceObserverEntryList.cpp',
'PerformanceResourceTiming.cpp',
'PerformanceService.cpp',
'PerformanceStorageWorker.cpp',
'PerformanceTiming.cpp',
'PerformanceWorker.cpp',
]

View File

@@ -64,6 +64,7 @@
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/nsCSPUtils.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/PerformanceStorageWorker.h"
#include "mozilla/dom/PMessagePort.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseDebugging.h"
@@ -7203,8 +7204,12 @@ PerformanceStorage*
WorkerPrivate::GetPerformanceStorage()
{
AssertIsOnMainThread();
// TODO
return nullptr;
if (!mPerformanceStorage) {
mPerformanceStorage = PerformanceStorageWorker::Create(this);
}
return mPerformanceStorage;
}
NS_IMPL_ISUPPORTS_INHERITED0(ExternalRunnableWrapper, WorkerRunnable)

View File

@@ -1068,6 +1068,8 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
// fired on the main thread if the worker script fails to load
nsCOMPtr<nsIRunnable> mLoadFailedRunnable;
RefPtr<PerformanceStorage> mPerformanceStorage;
JS::UniqueChars mDefaultLocale; // nulled during worker JSContext init
TimeStamp mKillTime;
uint32_t mErrorHandlerRecursionCount;