Bug 1202657 - Add markers for workers' message passing and serialization/deserialization, r=smaug, jsantell, tromey, ejpbruel

This commit is contained in:
Victor Porof 2015-10-21 23:10:05 +02:00
parent 8fc84a1244
commit 306f80152f
41 changed files with 846 additions and 424 deletions

View File

@ -30,6 +30,7 @@ marker.label.garbageCollection.nonIncremental=Non-incremental GC
marker.label.cycleCollection=Cycle Collection
marker.label.cycleCollection.forgetSkippable=CC Graph Reduction
marker.label.timestamp=Timestamp
marker.label.worker=Worker
marker.label.unknown=Unknown
# LOCALIZATION NOTE (marker.label.javascript.*):
@ -75,6 +76,11 @@ marker.field.restyleHint=Restyle Hint:
marker.field.causeName=Cause:
# General "type" for a marker (Cycle Collection, Garbage Collection)
marker.field.type=Type:
# The type of operation performed by a Worker.
marker.worker.serializeDataOffMainThread=Serialize data in Worker
marker.worker.serializeDataOnMainThread=Serialize data on the main thread
marker.worker.deserializeDataOffMainThread=Deserialize data in Worker
marker.worker.deserializeDataOnMainThread=Deserialize data on the main thread
# Strings used in the waterfall sidebar as values.
marker.value.unknownFrame=<unknown location>

View File

@ -439,6 +439,13 @@ const Formatters = {
[L10N.getStr("marker.field.type")]: marker.name.replace(/nsCycleCollector::/g, "")
};
},
WorkerFields: function(marker) {
return {
[L10N.getStr("marker.field.type")]:
L10N.getStr(`marker.worker.${marker.workerOperation}`)
};
}
};
/**

View File

@ -125,6 +125,12 @@ const TIMELINE_BLUEPRINT = {
label: L10N.getStr("marker.label.cycleCollection.forgetSkippable"),
fields: Formatters.CycleCollectionFields,
},
"Worker": {
group: 1,
colorName: "graphs-orange",
label: L10N.getStr("marker.label.worker"),
fields: Formatters.WorkerFields
},
/* Group 2 - User Controlled */
"ConsoleTime": {

View File

@ -6,6 +6,8 @@ support-files =
doc_innerHTML.html
doc_markers.html
doc_simple-test.html
doc_worker.html
js_simpleWorker.js
head.js
[browser_aaa-run-first-leaktest.js]
@ -145,3 +147,4 @@ skip-if = true # Bug 1176370
skip-if = true # Bug 1170105
[browser_timeline-waterfall-sidebar.js]
skip-if = true # Bug 1161817
[browser_timeline-waterfall-workers.js]

View File

@ -0,0 +1,78 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the sidebar is properly updated with worker markers.
*/
function* spawnTest() {
let { panel } = yield initPerformance(WORKER_URL);
let { PerformanceController } = panel.panelWin;
loadFrameScripts();
yield startRecording(panel);
ok(true, "Recording has started.");
evalInDebuggee("performWork()");
yield waitUntil(() => {
// Wait until we get the worker markers.
let markers = PerformanceController.getCurrentRecording().getMarkers();
if (!markers.some(m => m.name == "Worker") ||
!markers.some(m => m.workerOperation == "serializeDataOffMainThread") ||
!markers.some(m => m.workerOperation == "serializeDataOnMainThread") ||
!markers.some(m => m.workerOperation == "deserializeDataOffMainThread") ||
!markers.some(m => m.workerOperation == "deserializeDataOnMainThread")) {
return false;
}
testWorkerMarker(markers.find(m => m.name == "Worker"));
return true;
});
yield stopRecording(panel);
ok(true, "Recording has ended.");
yield teardown(panel);
finish();
}
function testWorkerMarker(marker) {
ok(true, "Found a worker marker.");
ok("start" in marker,
"The start time is specified in the worker marker.");
ok("end" in marker,
"The end time is specified in the worker marker.");
ok("workerOperation" in marker,
"The worker operation is specified in the worker marker.");
}
/**
* Takes a string `script` and evaluates it directly in the content
* in potentially a different process.
*/
function evalInDebuggee (script) {
let { generateUUID } = Cc['@mozilla.org/uuid-generator;1'].getService(Ci.nsIUUIDGenerator);
let deferred = Promise.defer();
if (!mm) {
throw new Error("`loadFrameScripts()` must be called when using MessageManager.");
}
let id = generateUUID().toString();
mm.sendAsyncMessage("devtools:test:eval", { script: script, id: id });
mm.addMessageListener("devtools:test:eval:response", handler);
function handler ({ data }) {
if (id !== data.id) {
return;
}
mm.removeMessageListener("devtools:test:eval:response", handler);
deferred.resolve(data.value);
}
return deferred.promise;
}

View File

@ -0,0 +1,26 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Performance test page</title>
</head>
<body>
<script type="text/javascript">
function performWork() {
var worker = new Worker("js_simpleWorker.js");
worker.addEventListener("message", function(e) {
console.log(e.data);
console.timeStamp("Done");
}, false);
worker.postMessage("Hello World");
}
</script>
</body>
</html>

View File

@ -29,6 +29,7 @@ const EXAMPLE_URL = "http://example.com/browser/devtools/client/performance/test
const SIMPLE_URL = EXAMPLE_URL + "doc_simple-test.html";
const MARKERS_URL = EXAMPLE_URL + "doc_markers.html";
const ALLOCS_URL = EXAMPLE_URL + "doc_allocs.html";
const WORKER_URL = EXAMPLE_URL + "doc_worker.html";
const MEMORY_SAMPLE_PROB_PREF = "devtools.performance.memory.sample-probability";
const MEMORY_MAX_LOG_LEN_PREF = "devtools.performance.memory.max-log-length";

View File

@ -0,0 +1,4 @@
self.addEventListener('message', function(e) {
self.postMessage(e.data);
self.close()
}, false);

View File

@ -569,6 +569,10 @@ menuitem.marker-color-graphs-yellow:before,
.marker-color-graphs-yellow {
background-color: var(--theme-graphs-yellow);
}
menuitem.marker-color-graphs-orange:before,
.marker-color-graphs-orange {
background-color: var(--theme-graphs-orange);
}
menuitem.marker-color-graphs-red:before,
.marker-color-graphs-red {
background-color: var(--theme-graphs-red);

View File

@ -48,6 +48,7 @@
--theme-graphs-bluegrey: #0072ab;
--theme-graphs-purple: #b693eb;
--theme-graphs-yellow: #efc052;
--theme-graphs-orange: #d97e00;
--theme-graphs-red: #e57180;
--theme-graphs-grey: #cccccc;
}
@ -86,6 +87,7 @@
--theme-graphs-bluegrey: #5e88b0;
--theme-graphs-purple: #df80ff;
--theme-graphs-yellow: #d99b28;
--theme-graphs-orange: #d96629;
--theme-graphs-red: #eb5368;
--theme-graphs-grey: #757873;
}

View File

@ -840,7 +840,7 @@ nsDocShell::nsDocShell()
nsDocShell::~nsDocShell()
{
MOZ_ASSERT(!IsObserved());
MOZ_ASSERT(!mObserved);
Destroy();
@ -2839,14 +2839,25 @@ NS_IMETHODIMP
nsDocShell::SetRecordProfileTimelineMarkers(bool aValue)
{
bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers();
if (currentValue != aValue) {
if (aValue) {
TimelineConsumers::AddConsumer(this);
UseEntryScriptProfiling();
} else {
TimelineConsumers::RemoveConsumer(this);
UnuseEntryScriptProfiling();
}
if (currentValue == aValue) {
return NS_OK;
}
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (!timelines) {
return NS_OK;
}
if (aValue) {
MOZ_ASSERT(!timelines->HasConsumer(this));
timelines->AddConsumer(this);
MOZ_ASSERT(timelines->HasConsumer(this));
UseEntryScriptProfiling();
} else {
MOZ_ASSERT(timelines->HasConsumer(this));
timelines->RemoveConsumer(this);
MOZ_ASSERT(!timelines->HasConsumer(this));
UnuseEntryScriptProfiling();
}
return NS_OK;
@ -2855,7 +2866,7 @@ nsDocShell::SetRecordProfileTimelineMarkers(bool aValue)
NS_IMETHODIMP
nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue)
{
*aValue = IsObserved();
*aValue = !!mObserved;
return NS_OK;
}
@ -2867,7 +2878,7 @@ nsDocShell::PopProfileTimelineMarkers(
nsTArray<dom::ProfileTimelineMarker> store;
SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
if (IsObserved()) {
if (mObserved) {
mObserved->PopMarkers(aCx, store);
}
@ -10650,7 +10661,7 @@ nsDocShell::DoURILoad(nsIURI* aURI,
nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(channel));
/* Get the cache Key from SH */
nsCOMPtr<nsISupports> cacheKey;
if (mLSHE) {
@ -10658,7 +10669,7 @@ nsDocShell::DoURILoad(nsIURI* aURI,
} else if (mOSHE) { // for reload cases
mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
}
if (uploadChannel) {
// figure out if we need to set the post data stream on the channel...
// right now, this is only done for http channels.....
@ -10709,7 +10720,7 @@ nsDocShell::DoURILoad(nsIURI* aURI,
}
}
}
if (httpChannel) {
if (aHeadersData) {
rv = AddHeadersToChannel(aHeadersData, httpChannel);
@ -13892,26 +13903,30 @@ nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
const char16_t* aFilename,
const uint32_t aLineNumber)
{
bool timelineOn = nsIDocShell::GetRecordProfileTimelineMarkers();
// If first start, mark interval start.
if (timelineOn && mJSRunToCompletionDepth == 0) {
UniquePtr<TimelineMarker> marker = MakeUnique<JavascriptTimelineMarker>(
aReason, aFunctionName, aFilename, aLineNumber, MarkerTracingType::START);
TimelineConsumers::AddMarkerForDocShell(this, Move(marker));
if (mJSRunToCompletionDepth == 0) {
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (timelines && timelines->HasConsumer(this)) {
timelines->AddMarkerForDocShell(this, Move(
MakeUnique<JavascriptTimelineMarker>(
aReason, aFunctionName, aFilename, aLineNumber, MarkerTracingType::START)));
}
}
mJSRunToCompletionDepth++;
}
void
nsDocShell::NotifyJSRunToCompletionStop()
{
bool timelineOn = nsIDocShell::GetRecordProfileTimelineMarkers();
mJSRunToCompletionDepth--;
// If last stop, mark interval end.
mJSRunToCompletionDepth--;
if (timelineOn && mJSRunToCompletionDepth == 0) {
TimelineConsumers::AddMarkerForDocShell(this, "Javascript", MarkerTracingType::END);
if (mJSRunToCompletionDepth == 0) {
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (timelines && timelines->HasConsumer(this)) {
timelines->AddMarkerForDocShell(this, "Javascript", MarkerTracingType::END);
}
}
}

View File

@ -275,7 +275,6 @@ public:
private:
// An observed docshell wrapper is created when recording markers is enabled.
mozilla::UniquePtr<mozilla::ObservedDocShell> mObserved;
bool IsObserved() const { return !!mObserved; }
// It is necessary to allow adding a timeline marker wherever a docshell
// instance is available. This operation happens frequently and needs to
@ -290,8 +289,6 @@ private:
nsDocShell*, const char*, const TimeStamp&, MarkerTracingType);
friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
nsDocShell*, UniquePtr<AbstractTimelineMarker>&&);
friend void mozilla::TimelineConsumers::AddOTMTMarkerForDocShell(
nsDocShell*, UniquePtr<AbstractTimelineMarker>&);
public:
// Tell the favicon service that aNewURI has the same favicon as aOldURI.

View File

@ -35,6 +35,14 @@ AbstractTimelineMarker::Clone()
return nullptr;
}
bool
AbstractTimelineMarker::Equals(const AbstractTimelineMarker& aOther)
{
// Check whether two markers should be considered the same, for the purpose
// of pairing start and end markers. Normally this definition suffices.
return strcmp(mName, aOther.mName) == 0;
}
AbstractTimelineMarker::~AbstractTimelineMarker()
{
MOZ_COUNT_DTOR(AbstractTimelineMarker);
@ -54,4 +62,10 @@ AbstractTimelineMarker::SetCustomTime(const TimeStamp& aTime)
mTime = (aTime - TimeStamp::ProcessCreation(isInconsistent)).ToMilliseconds();
}
void
AbstractTimelineMarker::SetCustomTime(DOMHighResTimeStamp aTime)
{
mTime = aTime;
}
} // namespace mozilla

View File

@ -12,6 +12,7 @@
#include "mozilla/UniquePtr.h"
struct JSContext;
class JSObject;
namespace mozilla {
class TimeStamp;
@ -28,18 +29,18 @@ private:
void operator=(const AbstractTimelineMarker& aOther) = delete;
public:
AbstractTimelineMarker(const char* aName,
MarkerTracingType aTracingType);
explicit AbstractTimelineMarker(const char* aName,
MarkerTracingType aTracingType);
AbstractTimelineMarker(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
explicit AbstractTimelineMarker(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
virtual ~AbstractTimelineMarker();
virtual UniquePtr<AbstractTimelineMarker> Clone();
virtual bool Equals(const AbstractTimelineMarker& aOther);
virtual bool Equals(const AbstractTimelineMarker& aOther) = 0;
virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) = 0;
virtual JSObject* GetStack() = 0;
@ -52,8 +53,10 @@ private:
DOMHighResTimeStamp mTime;
MarkerTracingType mTracingType;
protected:
void SetCurrentTime();
void SetCustomTime(const TimeStamp& aTime);
void SetCustomTime(DOMHighResTimeStamp aTime);
};
} // namespace mozilla

View File

@ -4,7 +4,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/AutoGlobalTimelineMarker.h"
#include "AutoGlobalTimelineMarker.h"
#include "TimelineConsumers.h"
#include "MainThreadUtils.h"
@ -18,20 +18,24 @@ AutoGlobalTimelineMarker::AutoGlobalTimelineMarker(const char* aName
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
MOZ_ASSERT(NS_IsMainThread());
if (TimelineConsumers::IsEmpty()) {
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (!timelines || timelines->IsEmpty()) {
return;
}
TimelineConsumers::AddMarkerForAllObservedDocShells(mName, MarkerTracingType::START);
timelines->AddMarkerForAllObservedDocShells(mName, MarkerTracingType::START);
}
AutoGlobalTimelineMarker::~AutoGlobalTimelineMarker()
{
if (TimelineConsumers::IsEmpty()) {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (!timelines || timelines->IsEmpty()) {
return;
}
TimelineConsumers::AddMarkerForAllObservedDocShells(mName, MarkerTracingType::END);
timelines->AddMarkerForAllObservedDocShells(mName, MarkerTracingType::END);
}
} // namespace mozilla

View File

@ -8,9 +8,6 @@
#define mozilla_AutoGlobalTimelineMarker_h_
#include "mozilla/GuardObjects.h"
#include "mozilla/RefPtr.h"
class nsDocShell;
namespace mozilla {

View File

@ -4,11 +4,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/AutoTimelineMarker.h"
#include "AutoTimelineMarker.h"
#include "TimelineConsumers.h"
#include "MainThreadUtils.h"
#include "nsDocShell.h"
namespace mozilla {
@ -20,21 +19,33 @@ AutoTimelineMarker::AutoTimelineMarker(nsIDocShell* aDocShell, const char* aName
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
MOZ_ASSERT(NS_IsMainThread());
if (!aDocShell || TimelineConsumers::IsEmpty()) {
if (!aDocShell) {
return;
}
mDocShell = static_cast<nsDocShell*>(aDocShell);
TimelineConsumers::AddMarkerForDocShell(mDocShell, mName, MarkerTracingType::START);
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (!timelines || !timelines->HasConsumer(aDocShell)) {
return;
}
mDocShell = aDocShell;
timelines->AddMarkerForDocShell(mDocShell, mName, MarkerTracingType::START);
}
AutoTimelineMarker::~AutoTimelineMarker()
{
if (!mDocShell || TimelineConsumers::IsEmpty()) {
MOZ_ASSERT(NS_IsMainThread());
if (!mDocShell) {
return;
}
TimelineConsumers::AddMarkerForDocShell(mDocShell, mName, MarkerTracingType::END);
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (!timelines || !timelines->HasConsumer(mDocShell)) {
return;
}
timelines->AddMarkerForDocShell(mDocShell, mName, MarkerTracingType::END);
}
} // namespace mozilla

View File

@ -11,7 +11,6 @@
#include "mozilla/RefPtr.h"
class nsIDocShell;
class nsDocShell;
namespace mozilla {
@ -36,7 +35,7 @@ class MOZ_RAII AutoTimelineMarker
const char* mName;
// The docshell that is associated with this marker.
RefPtr<nsDocShell> mDocShell;
RefPtr<nsIDocShell> mDocShell;
public:
explicit AutoTimelineMarker(nsIDocShell* aDocShell, const char* aName

View File

@ -9,6 +9,7 @@
#include "TimelineMarker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
#include "nsRegion.h"
namespace mozilla {
@ -20,9 +21,6 @@ public:
, mRegion(aRegion)
{}
~LayerTimelineMarker()
{}
void AddLayerRectangles(dom::Sequence<dom::ProfileTimelineLayerRect>& aRectangles)
{
nsIntRegionRectIterator it(mRegion);

View File

@ -0,0 +1,29 @@
/* -*- 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 "MarkersStorage.h"
#include "MainThreadUtils.h"
namespace mozilla {
MarkersStorage::MarkersStorage(const char* aMutexName)
: mLock(aMutexName)
{
MOZ_ASSERT(NS_IsMainThread());
}
MarkersStorage::~MarkersStorage()
{
MOZ_ASSERT(NS_IsMainThread());
}
Mutex&
MarkersStorage::GetLock()
{
return mLock;
}
} // namespace mozilla

View File

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_MarkersStorage_h_
#define mozilla_MarkersStorage_h_
#include "TimelineMarkerEnums.h" // for MarkerReleaseRequest
#include "mozilla/Mutex.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/LinkedList.h"
#include "nsTArray.h"
namespace mozilla {
class AbstractTimelineMarker;
namespace dom {
struct ProfileTimelineMarker;
}
class MarkersStorage : public LinkedListElement<MarkersStorage>
{
private:
MarkersStorage() = delete;
MarkersStorage(const MarkersStorage& aOther) = delete;
void operator=(const MarkersStorage& aOther) = delete;
public:
explicit MarkersStorage(const char* aMutexName);
virtual ~MarkersStorage();
virtual void AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) = 0;
virtual void AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) = 0;
virtual void ClearMarkers() = 0;
virtual void PopMarkers(JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore) = 0;
protected:
Mutex& GetLock();
private:
Mutex mLock;
};
} // namespace mozilla
#endif /* mozilla_MarkersStorage_h_ */

View File

@ -1,40 +0,0 @@
/* -*- 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_OTMTMarkerObserver_h_
#define mozilla_OTMTMarkerObserver_h_
#include "mozilla/Mutex.h"
namespace mozilla {
class AbstractTimelineMarker;
class OTMTMarkerReceiver
{
private:
OTMTMarkerReceiver() = delete;
OTMTMarkerReceiver(const OTMTMarkerReceiver& aOther) = delete;
void operator=(const OTMTMarkerReceiver& aOther) = delete;
public:
explicit OTMTMarkerReceiver(const char* aMutexName)
: mLock(aMutexName)
{
}
virtual ~OTMTMarkerReceiver() {}
virtual void AddOTMTMarkerClone(UniquePtr<AbstractTimelineMarker>& aMarker) = 0;
protected:
Mutex& GetLock() { return mLock; };
private:
Mutex mLock;
};
} // namespace mozilla
#endif /* mozilla_OTMTMarkerObserver_h_ */

View File

@ -13,8 +13,8 @@
namespace mozilla {
ObservedDocShell::ObservedDocShell(nsDocShell* aDocShell)
: OTMTMarkerReceiver("ObservedDocShellMutex")
ObservedDocShell::ObservedDocShell(nsIDocShell* aDocShell)
: MarkersStorage("ObservedDocShellMutex")
, mDocShell(aDocShell)
{
MOZ_ASSERT(NS_IsMainThread());
@ -23,24 +23,31 @@ ObservedDocShell::ObservedDocShell(nsDocShell* aDocShell)
void
ObservedDocShell::AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker)
{
// Only allow main thread markers to go into this list. No need to lock
// here since `mTimelineMarkers` will only be accessed or modified on the
// main thread only.
MOZ_ASSERT(NS_IsMainThread());
mTimelineMarkers.AppendElement(Move(aMarker));
}
void
ObservedDocShell::AddOTMTMarkerClone(UniquePtr<AbstractTimelineMarker>& aMarker)
ObservedDocShell::AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker)
{
// Only allow off the main thread markers to go into this list. Since most
// of our markers come from the main thread, be a little more efficient and
// avoid dealing with multithreading scenarios until all the markers are
// actually cleared or popped in `ClearMarkers` or `PopMarkers`.
MOZ_ASSERT(!NS_IsMainThread());
MutexAutoLock lock(GetLock());
UniquePtr<AbstractTimelineMarker> cloned = aMarker->Clone();
mTimelineMarkers.AppendElement(Move(cloned));
MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
mOffTheMainThreadTimelineMarkers.AppendElement(Move(aMarker));
}
void
ObservedDocShell::ClearMarkers()
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
mTimelineMarkers.Clear();
mOffTheMainThreadTimelineMarkers.Clear();
}
void
@ -48,13 +55,19 @@ ObservedDocShell::PopMarkers(JSContext* aCx,
nsTArray<dom::ProfileTimelineMarker>& aStore)
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
// First, move all of our markers into a single array. We'll chose
// the `mTimelineMarkers` store because that's where we expect most of
// our markers to be.
mTimelineMarkers.AppendElements(Move(mOffTheMainThreadTimelineMarkers));
// If we see an unpaired START, we keep it around for the next call
// to ObservedDocShell::PopMarkers. We store the kept START objects here.
nsTArray<UniquePtr<AbstractTimelineMarker>> keptStartMarkers;
for (uint32_t i = 0; i < mTimelineMarkers.Length(); ++i) {
UniquePtr<AbstractTimelineMarker>& startPayload = mTimelineMarkers[i];
UniquePtr<AbstractTimelineMarker>& startPayload = mTimelineMarkers.ElementAt(i);
// If this is a TIMESTAMP marker, there's no corresponding END,
// as it's a single unit of time, not a duration.
@ -93,12 +106,13 @@ ObservedDocShell::PopMarkers(JSContext* aCx,
// enough for the amount of markers to always be small enough that the
// nested for loop isn't going to be a performance problem.
for (uint32_t j = i + 1; j < mTimelineMarkers.Length(); ++j) {
UniquePtr<AbstractTimelineMarker>& endPayload = mTimelineMarkers[j];
UniquePtr<AbstractTimelineMarker>& endPayload = mTimelineMarkers.ElementAt(j);
bool endIsLayerType = strcmp(endPayload->GetName(), "Layer") == 0;
// Look for "Layer" markers to stream out "Paint" markers.
if (startIsPaintType && endIsLayerType) {
LayerTimelineMarker* layerPayload = static_cast<LayerTimelineMarker*>(endPayload.get());
AbstractTimelineMarker* raw = endPayload.get();
LayerTimelineMarker* layerPayload = static_cast<LayerTimelineMarker*>(raw);
layerPayload->AddLayerRectangles(layerRectangles);
hasSeenLayerType = true;
}
@ -133,7 +147,7 @@ ObservedDocShell::PopMarkers(JSContext* aCx,
// If we did not see the corresponding END, keep the START.
if (!hasSeenEnd) {
keptStartMarkers.AppendElement(Move(mTimelineMarkers[i]));
keptStartMarkers.AppendElement(Move(mTimelineMarkers.ElementAt(i)));
mTimelineMarkers.RemoveElementAt(i);
--i;
}

View File

@ -7,11 +7,12 @@
#ifndef mozilla_ObservedDocShell_h_
#define mozilla_ObservedDocShell_h_
#include "OTMTMarkerReceiver.h"
#include "nsTArray.h"
#include "MarkersStorage.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"
class nsDocShell;
class nsIDocShell;
namespace mozilla {
class AbstractTimelineMarker;
@ -24,22 +25,25 @@ struct ProfileTimelineMarker;
//
// A wrapper around a docshell for which docshell-specific markers are
// allowed to exist. See TimelineConsumers for register/unregister logic.
class ObservedDocShell : public LinkedListElement<ObservedDocShell>,
public OTMTMarkerReceiver
class ObservedDocShell : public MarkersStorage
{
private:
RefPtr<nsDocShell> mDocShell;
RefPtr<nsIDocShell> mDocShell;
// Main thread only.
nsTArray<UniquePtr<AbstractTimelineMarker>> mTimelineMarkers;
// Off the main thread only.
nsTArray<UniquePtr<AbstractTimelineMarker>> mOffTheMainThreadTimelineMarkers;
public:
explicit ObservedDocShell(nsDocShell* aDocShell);
nsDocShell* operator*() const { return mDocShell.get(); }
explicit ObservedDocShell(nsIDocShell* aDocShell);
nsIDocShell* operator*() const { return mDocShell.get(); }
void AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker);
void AddOTMTMarkerClone(UniquePtr<AbstractTimelineMarker>& aMarker) override;
void ClearMarkers();
void PopMarkers(JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore);
void AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) override;
void AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) override;
void ClearMarkers() override;
void PopMarkers(JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore) override;
};
} // namespace mozilla

View File

@ -4,79 +4,174 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/TimelineConsumers.h"
#include "TimelineConsumers.h"
#include "mozilla/ClearOnShutdown.h"
#include "nsAppRunner.h" // for XRE_IsContentProcess, XRE_IsParentProcess
#include "nsDocShell.h"
namespace mozilla {
unsigned long TimelineConsumers::sActiveConsumers = 0;
LinkedList<ObservedDocShell>* TimelineConsumers::sObservedDocShells = nullptr;
Mutex* TimelineConsumers::sLock = nullptr;
NS_IMPL_ISUPPORTS(TimelineConsumers, nsIObserver);
LinkedList<ObservedDocShell>&
TimelineConsumers::GetOrCreateObservedDocShellsList()
StaticMutex TimelineConsumers::sMutex;
// Manually manage this singleton's lifetime and destroy it before shutdown.
// This avoids the leakchecker detecting false-positive memory leaks when
// using automatic memory management (i.e. statically instantiating this
// singleton inside the `Get` method), which would automatically destroy it on
// application shutdown, but too late for the leakchecker. Sigh...
StaticRefPtr<TimelineConsumers> TimelineConsumers::sInstance;
// This flag makes sure the singleton never gets instantiated while a shutdown
// is in progress. This can actually happen, and `ClearOnShutdown` doesn't work
// in these cases.
bool TimelineConsumers::sInShutdown = false;
already_AddRefed<TimelineConsumers>
TimelineConsumers::Get()
{
if (!sObservedDocShells) {
sObservedDocShells = new LinkedList<ObservedDocShell>();
// Using this class is not supported yet for other processes other than
// parent or content. To avoid accidental checks to methods like `IsEmpty`,
// which would probably always be true in those cases, assert here.
// Remember, there will be different singletons available to each process.
MOZ_ASSERT(XRE_IsContentProcess() || XRE_IsParentProcess());
// If we are shutting down, don't bother doing anything. Note: we can only
// know whether or not we're in shutdown if we're instantiated.
if (sInShutdown) {
return nullptr;
}
return *sObservedDocShells;
// Note: We don't simply check `sInstance` for null-ness here, since otherwise
// this can resurrect the TimelineConsumers pretty late during shutdown.
// We won't know if we're in shutdown or not though, because the singleton
// could have been destroyed or just never instantiated, so in the previous
// conditional `sInShutdown` would be false.
static bool firstTime = true;
if (firstTime) {
firstTime = false;
StaticMutexAutoLock lock(sMutex);
sInstance = new TimelineConsumers();
// Make sure the initialization actually suceeds, otherwise don't allow
// access by destroying the instance immediately.
if (sInstance->Init()) {
ClearOnShutdown(&sInstance);
} else {
NS_WARNING("TimelineConsumers could not be initialized.");
sInstance->RemoveObservers();
sInstance = nullptr;
}
}
RefPtr<TimelineConsumers> copy = sInstance.get();
return copy.forget();
}
Mutex&
TimelineConsumers::GetLock()
bool
TimelineConsumers::Init()
{
if (!sLock) {
sLock = new Mutex("TimelineConsumersMutex");
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (!obs) {
return false;
}
return *sLock;
if (NS_WARN_IF(NS_FAILED(
obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false)))) {
return false;
}
return true;
}
bool
TimelineConsumers::RemoveObservers()
{
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (!obs) {
return false;
}
if (NS_WARN_IF(NS_FAILED(
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)))) {
return false;
}
return true;
}
nsresult
TimelineConsumers::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
sInShutdown = true;
RemoveObservers();
return NS_OK;
}
MOZ_ASSERT(false, "TimelineConsumers got unexpected topic!");
return NS_ERROR_UNEXPECTED;
}
TimelineConsumers::TimelineConsumers()
: mActiveConsumers(0)
{
}
void
TimelineConsumers::AddConsumer(nsDocShell* aDocShell)
{
MOZ_ASSERT(NS_IsMainThread());
UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers` and `mMarkersStores`.
UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
MOZ_ASSERT(!observed);
sActiveConsumers++;
observed.reset(new ObservedDocShell(aDocShell));
GetOrCreateObservedDocShellsList().insertFront(observed.get());
mActiveConsumers++;
ObservedDocShell* obsDocShell = new ObservedDocShell(aDocShell);
MarkersStorage* storage = static_cast<MarkersStorage*>(obsDocShell);
observed.reset(obsDocShell);
mMarkersStores.insertFront(storage);
}
void
TimelineConsumers::RemoveConsumer(nsDocShell* aDocShell)
{
MOZ_ASSERT(NS_IsMainThread());
UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers` and `mMarkersStores`.
UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
MOZ_ASSERT(observed);
sActiveConsumers--;
mActiveConsumers--;
// Clear all markers from the `mTimelineMarkers` store.
observed.get()->ClearMarkers();
// Remove self from the `mMarkersStores` store.
observed.get()->remove();
// Prepare for becoming a consumer later.
observed.reset(nullptr);
}
bool
TimelineConsumers::HasConsumer(nsIDocShell* aDocShell)
{
MOZ_ASSERT(NS_IsMainThread());
if (!aDocShell) {
return false;
}
bool isTimelineRecording = false;
aDocShell->GetRecordProfileTimelineMarkers(&isTimelineRecording);
return isTimelineRecording;
}
bool
TimelineConsumers::IsEmpty()
{
MOZ_ASSERT(NS_IsMainThread());
return sActiveConsumers == 0;
}
bool
TimelineConsumers::GetKnownDocShells(Vector<RefPtr<nsDocShell>>& aStore)
{
MOZ_ASSERT(NS_IsMainThread());
const LinkedList<ObservedDocShell>& docShells = GetOrCreateObservedDocShellsList();
for (const ObservedDocShell* rds = docShells.getFirst();
rds != nullptr;
rds = rds->getNext()) {
if (!aStore.append(**rds)) {
return false;
}
}
return true;
StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers`.
return mActiveConsumers == 0;
}
void
@ -85,7 +180,7 @@ TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
if (aDocShell->IsObserved()) {
if (HasConsumer(aDocShell)) {
aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTracingType)));
}
}
@ -97,7 +192,7 @@ TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
if (aDocShell->IsObserved()) {
if (HasConsumer(aDocShell)) {
aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTime, aTracingType)));
}
}
@ -107,22 +202,11 @@ TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker)
{
MOZ_ASSERT(NS_IsMainThread());
if (aDocShell->IsObserved()) {
if (HasConsumer(aDocShell)) {
aDocShell->mObserved->AddMarker(Move(aMarker));
}
}
void
TimelineConsumers::AddOTMTMarkerForDocShell(nsDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>& aMarker)
{
MOZ_ASSERT(!NS_IsMainThread());
GetLock().AssertCurrentThreadOwns();
if (aDocShell->IsObserved()) {
aDocShell->mObserved->AddOTMTMarkerClone(aMarker);
}
}
void
TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
const char* aName,
@ -150,76 +234,23 @@ TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), Move(aMarker));
}
void
TimelineConsumers::AddOTMTMarkerForDocShell(nsIDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>& aMarker)
{
MOZ_ASSERT(!NS_IsMainThread());
GetLock().AssertCurrentThreadOwns();
AddOTMTMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aMarker);
}
void
TimelineConsumers::AddMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
const char* aName,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
for (Vector<RefPtr<nsDocShell>>::Range range = aDocShells.all();
!range.empty();
range.popFront()) {
AddMarkerForDocShell(range.front(), aName, aTracingType);
}
}
void
TimelineConsumers::AddMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
for (Vector<RefPtr<nsDocShell>>::Range range = aDocShells.all();
!range.empty();
range.popFront()) {
AddMarkerForDocShell(range.front(), aName, aTime, aTracingType);
}
}
void
TimelineConsumers::AddMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
UniquePtr<AbstractTimelineMarker>& aMarker)
{
MOZ_ASSERT(NS_IsMainThread());
for (Vector<RefPtr<nsDocShell>>::Range range = aDocShells.all();
!range.empty();
range.popFront()) {
UniquePtr<AbstractTimelineMarker> cloned = aMarker->Clone();
AddMarkerForDocShell(range.front(), Move(cloned));
}
}
void
TimelineConsumers::AddOTMTMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
UniquePtr<AbstractTimelineMarker>& aMarker)
{
MOZ_ASSERT(!NS_IsMainThread());
GetLock().AssertCurrentThreadOwns();
for (Vector<RefPtr<nsDocShell>>::Range range = aDocShells.all();
!range.empty();
range.popFront()) {
AddOTMTMarkerForDocShell(range.front(), aMarker);
}
}
void
TimelineConsumers::AddMarkerForAllObservedDocShells(const char* aName,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
Vector<RefPtr<nsDocShell>> docShells;
if (GetKnownDocShells(docShells)) {
AddMarkerForDocShellsList(docShells, aName, aTracingType);
bool isMainThread = NS_IsMainThread();
StaticMutexAutoLock lock(sMutex); // for `mMarkersStores`.
for (MarkersStorage* storage = mMarkersStores.getFirst();
storage != nullptr;
storage = storage->getNext()) {
UniquePtr<AbstractTimelineMarker> marker =
MakeUnique<TimelineMarker>(aName, aTracingType);
if (isMainThread) {
storage->AddMarker(Move(marker));
} else {
storage->AddOTMTMarker(Move(marker));
}
}
}
@ -228,31 +259,37 @@ TimelineConsumers::AddMarkerForAllObservedDocShells(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
Vector<RefPtr<nsDocShell>> docShells;
if (GetKnownDocShells(docShells)) {
AddMarkerForDocShellsList(docShells, aName, aTime, aTracingType);
bool isMainThread = NS_IsMainThread();
StaticMutexAutoLock lock(sMutex); // for `mMarkersStores`.
for (MarkersStorage* storage = mMarkersStores.getFirst();
storage != nullptr;
storage = storage->getNext()) {
UniquePtr<AbstractTimelineMarker> marker =
MakeUnique<TimelineMarker>(aName, aTime, aTracingType);
if (isMainThread) {
storage->AddMarker(Move(marker));
} else {
storage->AddOTMTMarker(Move(marker));
}
}
}
void
TimelineConsumers::AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker)
{
MOZ_ASSERT(NS_IsMainThread());
Vector<RefPtr<nsDocShell>> docShells;
if (GetKnownDocShells(docShells)) {
AddMarkerForDocShellsList(docShells, aMarker);
}
}
bool isMainThread = NS_IsMainThread();
StaticMutexAutoLock lock(sMutex); // for `mMarkersStores`.
void
TimelineConsumers::AddOTMTMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker)
{
MOZ_ASSERT(!NS_IsMainThread());
GetLock().AssertCurrentThreadOwns();
Vector<RefPtr<nsDocShell>> docShells;
if (GetKnownDocShells(docShells)) {
AddOTMTMarkerForDocShellsList(docShells, aMarker);
for (MarkersStorage* storage = mMarkersStores.getFirst();
storage != nullptr;
storage = storage->getNext()) {
UniquePtr<AbstractTimelineMarker> clone = aMarker->Clone();
if (isMainThread) {
storage->AddMarker(Move(clone));
} else {
storage->AddOTMTMarker(Move(clone));
}
}
}

View File

@ -1,4 +1,3 @@
/* -*- 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
@ -7,104 +6,113 @@
#ifndef mozilla_TimelineConsumers_h_
#define mozilla_TimelineConsumers_h_
#include "nsIObserver.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Vector.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Mutex.h"
#include "TimelineMarkerEnums.h"
#include "mozilla/StaticMutex.h"
#include "TimelineMarkerEnums.h" // for MarkerTracingType
class nsDocShell;
class nsIDocShell;
namespace mozilla {
class ObservedDocShell;
class TimeStamp;
class MarkersStorage;
class AbstractTimelineMarker;
class TimelineConsumers
class TimelineConsumers : public nsIObserver
{
private:
// Counter for how many timelines are currently interested in markers.
static unsigned long sActiveConsumers;
static LinkedList<ObservedDocShell>* sObservedDocShells;
static LinkedList<ObservedDocShell>& GetOrCreateObservedDocShellsList();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOBSERVER
// Lock used when adding off-the-main-thread markers.
static Mutex* sLock;
private:
TimelineConsumers();
TimelineConsumers(const TimelineConsumers& aOther) = delete;
void operator=(const TimelineConsumers& aOther) = delete;
virtual ~TimelineConsumers() = default;
bool Init();
bool RemoveObservers();
public:
static Mutex& GetLock();
static already_AddRefed<TimelineConsumers> Get();
static void AddConsumer(nsDocShell* aDocShell);
static void RemoveConsumer(nsDocShell* aDocShell);
static bool IsEmpty();
static bool GetKnownDocShells(Vector<RefPtr<nsDocShell>>& aStore);
// Methods for registering interested consumers (i.e. "devtools toolboxes").
// Each consumer should be directly focused on a particular docshell, but
// timeline markers don't necessarily have to be tied to that docshell.
// See the public `AddMarker*` methods below.
// Main thread only.
void AddConsumer(nsDocShell* aDocShell);
void RemoveConsumer(nsDocShell* aDocShell);
bool HasConsumer(nsIDocShell* aDocShell);
// Checks if there's any existing interested consumer.
// May be called from any thread.
bool IsEmpty();
// Methods for adding markers relevant for particular docshells, or generic
// (meaning that they either can't be tied to a particular docshell, or one
// wasn't accessible in the part of the codebase where they're instantiated).
// These will only add markers if at least one docshell is currently being
// observed by a timeline. Markers tied to a particular docshell won't be
// created unless that docshell is specifically being currently observed.
// See nsIDocShell::recordProfileTimelineMarkers
// These methods create a custom marker from a name and some metadata,
// These methods create a basic TimelineMarker from a name and some metadata,
// relevant for a specific docshell.
static void AddMarkerForDocShell(nsDocShell* aDocShell,
const char* aName,
MarkerTracingType aTracingType);
static void AddMarkerForDocShell(nsIDocShell* aDocShell,
const char* aName,
MarkerTracingType aTracingType);
// Main thread only.
void AddMarkerForDocShell(nsDocShell* aDocShell,
const char* aName,
MarkerTracingType aTracingType);
void AddMarkerForDocShell(nsIDocShell* aDocShell,
const char* aName,
MarkerTracingType aTracingType);
static void AddMarkerForDocShell(nsDocShell* aDocShell,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
static void AddMarkerForDocShell(nsIDocShell* aDocShell,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
void AddMarkerForDocShell(nsDocShell* aDocShell,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
void AddMarkerForDocShell(nsIDocShell* aDocShell,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
// These methods register and receive ownership of an already created marker,
// relevant for a specific docshell.
static void AddMarkerForDocShell(nsDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker);
static void AddMarkerForDocShell(nsIDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker);
// Main thread only.
void AddMarkerForDocShell(nsDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker);
void AddMarkerForDocShell(nsIDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker);
// These methods create or clone markers relevant for a list of docshells.
static void AddMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
const char* aName,
// These methods create a basic marker from a name and some metadata,
// which doesn't have to be relevant to a specific docshell.
// May be called from any thread.
void AddMarkerForAllObservedDocShells(const char* aName,
MarkerTracingType aTracingType);
static void AddMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
const char* aName,
void AddMarkerForAllObservedDocShells(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
static void AddMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
UniquePtr<AbstractTimelineMarker>& aMarker);
// These methods create or clone markers, none of which have to be tied to
// a particular docshell.
static void AddMarkerForAllObservedDocShells(const char* aName,
MarkerTracingType aTracingType);
static void AddMarkerForAllObservedDocShells(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
static void AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker);
// This method clones and registers an already instantiated marker,
// which doesn't have to be relevant to a specific docshell.
// May be called from any thread.
void AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker);
// Thread-safe versions of the above methods. Need to lock first using
// the mutex returned by `TimelineConsumers::GetLock()`.
static void AddOTMTMarkerForDocShell(nsDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>& aMarker);
static void AddOTMTMarkerForDocShell(nsIDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>& aMarker);
static void AddOTMTMarkerForDocShellsList(Vector<RefPtr<nsDocShell>>& aDocShells,
UniquePtr<AbstractTimelineMarker>& aMarker);
static void AddOTMTMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker);
private:
static StaticRefPtr<TimelineConsumers> sInstance;
static bool sInShutdown;
// Counter for how many timelines are currently interested in markers,
// and a list of the MarkersStorage interfaces representing them.
unsigned long mActiveConsumers;
LinkedList<MarkersStorage> mMarkersStores;
// Protects this class's data structures.
static StaticMutex sMutex;
};
} // namespace mozilla

View File

@ -25,14 +25,6 @@ TimelineMarker::TimelineMarker(const char* aName,
CaptureStackIfNecessary(aTracingType, aStackRequest);
}
bool
TimelineMarker::Equals(const AbstractTimelineMarker& aOther)
{
// Check whether two markers should be considered the same, for the purpose
// of pairing start and end markers. Normally this definition suffices.
return strcmp(GetName(), aOther.GetName()) == 0;
}
void
TimelineMarker::AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker)
{

View File

@ -8,6 +8,7 @@
#define mozilla_TimelineMarker_h_
#include "AbstractTimelineMarker.h"
#include "js/RootingAPI.h"
namespace mozilla {
@ -17,16 +18,15 @@ namespace mozilla {
class TimelineMarker : public AbstractTimelineMarker
{
public:
TimelineMarker(const char* aName,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
explicit TimelineMarker(const char* aName,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
TimelineMarker(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
explicit TimelineMarker(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
virtual bool Equals(const AbstractTimelineMarker& aOther) override;
virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override;
virtual JSObject* GetStack() override;

View File

@ -0,0 +1,44 @@
/* -*- 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_WorkerTimelineMarker_h_
#define mozilla_WorkerTimelineMarker_h_
#include "TimelineMarker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
namespace mozilla {
class WorkerTimelineMarker : public TimelineMarker
{
public:
explicit WorkerTimelineMarker(ProfileTimelineWorkerOperationType aOperationType,
MarkerTracingType aTracingType)
: TimelineMarker("Worker", aTracingType, MarkerStackRequest::NO_STACK)
, mOperationType(aOperationType)
{}
virtual UniquePtr<AbstractTimelineMarker> Clone() override
{
WorkerTimelineMarker* clone = new WorkerTimelineMarker(mOperationType, GetTracingType());
clone->SetCustomTime(GetTime());
return UniquePtr<AbstractTimelineMarker>(clone);
}
virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
{
if (GetTracingType() == MarkerTracingType::START) {
aMarker.mWorkerOperation.Construct(mOperationType);
}
}
private:
ProfileTimelineWorkerOperationType mOperationType;
};
} // namespace mozilla
#endif /* mozilla_WorkerTimelineMarker_h_ */

View File

@ -12,19 +12,21 @@ EXPORTS.mozilla += [
'EventTimelineMarker.h',
'JavascriptTimelineMarker.h',
'LayerTimelineMarker.h',
'MarkersStorage.h',
'ObservedDocShell.h',
'OTMTMarkerReceiver.h',
'RestyleTimelineMarker.h',
'TimelineConsumers.h',
'TimelineMarker.h',
'TimelineMarkerEnums.h',
'TimestampTimelineMarker.h',
'WorkerTimelineMarker.h',
]
UNIFIED_SOURCES += [
'AbstractTimelineMarker.cpp',
'AutoGlobalTimelineMarker.cpp',
'AutoTimelineMarker.cpp',
'MarkersStorage.cpp',
'ObservedDocShell.cpp',
'TimelineConsumers.cpp',
'TimelineMarker.cpp',

View File

@ -105,6 +105,7 @@ var TESTS = [{
content.console.timeStamp(undefined);
},
check: function (markers) {
markers = markers.filter(e => e.name != "Worker");
is(markers.length, 4, "Got 4 markers");
is(markers[0].name, "TimeStamp", "Got Timestamp marker");
is(markers[0].causeName, "paper", "Got Timestamp label value");

View File

@ -1065,42 +1065,41 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
callData->mMonotonicTimer = performance->Now();
// 'time' and 'timeEnd' are displayed in the devtools timeline if active.
bool isTimelineRecording = false;
nsDocShell* docShell = static_cast<nsDocShell*>(mWindow->GetDocShell());
if (docShell) {
docShell->GetRecordProfileTimelineMarkers(&isTimelineRecording);
}
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
// 'timeStamp' recordings do not need an argument; use empty string
// if no arguments passed in
// The 'timeStamp' recordings do not need an argument; use empty string
// if no arguments passed in.
if (isTimelineRecording && aMethodName == MethodTimeStamp) {
JS::Rooted<JS::Value> value(aCx, aData.Length() == 0 ?
JS_GetEmptyStringValue(aCx) : aData[0]);
JS::Rooted<JS::Value> value(aCx, aData.Length() == 0
? JS_GetEmptyStringValue(aCx)
: aData[0]);
JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
nsAutoJSString key;
if (jsString) {
key.init(aCx, jsString);
}
UniquePtr<TimelineMarker> marker = MakeUnique<TimestampTimelineMarker>(key);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<TimestampTimelineMarker>(key)));
}
// For `console.time(foo)` and `console.timeEnd(foo)`
// For `console.time(foo)` and `console.timeEnd(foo)`.
else if (isTimelineRecording && aData.Length() == 1) {
JS::Rooted<JS::Value> value(aCx, aData[0]);
JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
if (jsString) {
nsAutoJSString key;
if (key.init(aCx, jsString)) {
UniquePtr<TimelineMarker> marker = MakeUnique<ConsoleTimelineMarker>(
key, aMethodName == MethodTime ? MarkerTracingType::START
: MarkerTracingType::END);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<ConsoleTimelineMarker>(
key, aMethodName == MethodTime ? MarkerTracingType::START
: MarkerTracingType::END)));
}
}
}
} else {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);

View File

@ -1114,35 +1114,35 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
// Maybe add a marker to the docshell's timeline, but only
// bother with all the logic if some docshell is recording.
nsCOMPtr<nsIDocShell> docShell;
bool isTimelineRecording = false;
nsDocShell* docShell;
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool needsEndEventMarker = false;
if (mIsMainThreadELM &&
!TimelineConsumers::IsEmpty() &&
listener->mListenerType != Listener::eNativeListener) {
docShell = GetDocShellForTarget();
if (docShell) {
docShell->GetRecordProfileTimelineMarkers(&isTimelineRecording);
}
if (isTimelineRecording) {
nsDocShell* ds = static_cast<nsDocShell*>(docShell.get());
nsAutoString typeStr;
(*aDOMEvent)->GetType(typeStr);
uint16_t phase;
(*aDOMEvent)->GetEventPhase(&phase);
UniquePtr<TimelineMarker> marker = MakeUnique<EventTimelineMarker>(
typeStr, phase, MarkerTracingType::START);
TimelineConsumers::AddMarkerForDocShell(ds, Move(marker));
nsCOMPtr<nsIDocShell> docShellComPtr = GetDocShellForTarget();
if (docShellComPtr) {
docShell = static_cast<nsDocShell*>(docShellComPtr.get());
if (timelines && timelines->HasConsumer(docShell)) {
needsEndEventMarker = true;
nsAutoString typeStr;
(*aDOMEvent)->GetType(typeStr);
uint16_t phase;
(*aDOMEvent)->GetEventPhase(&phase);
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<EventTimelineMarker>(
typeStr, phase, MarkerTracingType::START)));
}
}
}
if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent,
aCurrentTarget))) {
if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, aCurrentTarget))) {
aEvent->mFlags.mExceptionHasBeenRisen = true;
}
if (isTimelineRecording) {
nsDocShell* ds = static_cast<nsDocShell*>(docShell.get());
TimelineConsumers::AddMarkerForDocShell(ds, "DOMEvent", MarkerTracingType::END);
if (needsEndEventMarker) {
timelines->AddMarkerForDocShell(
docShell, "DOMEvent", MarkerTracingType::END);
}
}
}

View File

@ -2975,15 +2975,20 @@ void
TabChild::DidRequestComposite(const TimeStamp& aCompositeReqStart,
const TimeStamp& aCompositeReqEnd)
{
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
if (!docShell) {
nsCOMPtr<nsIDocShell> docShellComPtr = do_GetInterface(WebNavigation());
if (!docShellComPtr) {
return;
}
TimelineConsumers::AddMarkerForDocShell(docShell.get(),
"CompositeForwardTransaction", aCompositeReqStart, MarkerTracingType::START);
TimelineConsumers::AddMarkerForDocShell(docShell.get(),
"CompositeForwardTransaction", aCompositeReqEnd, MarkerTracingType::END);
nsDocShell* docShell = static_cast<nsDocShell*>(docShellComPtr.get());
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (timelines && timelines->HasConsumer(docShell)) {
timelines->AddMarkerForDocShell(docShell,
"CompositeForwardTransaction", aCompositeReqStart, MarkerTracingType::START);
timelines->AddMarkerForDocShell(docShell,
"CompositeForwardTransaction", aCompositeReqEnd, MarkerTracingType::END);
}
}
void

View File

@ -25,20 +25,35 @@ dictionary ProfileTimelineLayerRect {
long height = 0;
};
enum ProfileTimelineWorkerOperationType {
"serializeDataOffMainThread",
"serializeDataOnMainThread",
"deserializeDataOffMainThread",
"deserializeDataOnMainThread",
};
dictionary ProfileTimelineMarker {
DOMString name = "";
DOMHighResTimeStamp start = 0;
DOMHighResTimeStamp end = 0;
object? stack = null;
/* For ConsoleTime, Timestamp and Javascript markers. */
DOMString causeName;
/* For ConsoleTime markers. */
object? endStack = null;
/* For DOMEvent markers. */
DOMString type;
unsigned short eventPhase;
/* For Paint markers. */
sequence<ProfileTimelineLayerRect> rectangles;
/* For Style markers. */
DOMString restyleHint;
/* For Worker markers. */
ProfileTimelineWorkerOperationType workerOperation;
};

View File

@ -65,6 +65,8 @@
#include "mozilla/dom/WorkerGlobalScopeBinding.h"
#include "mozilla/dom/indexedDB/IDBFactory.h"
#include "mozilla/Preferences.h"
#include "mozilla/TimelineConsumers.h"
#include "mozilla/WorkerTimelineMarker.h"
#include "nsAlgorithm.h"
#include "nsContentUtils.h"
#include "nsCycleCollector.h"
@ -638,7 +640,30 @@ public:
JS::Rooted<JS::Value> messageData(aCx);
ErrorResult rv;
UniquePtr<AbstractTimelineMarker> start;
UniquePtr<AbstractTimelineMarker> end;
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool isTimelineRecording = timelines && !timelines->IsEmpty();
if (isTimelineRecording) {
start = MakeUnique<WorkerTimelineMarker>(aIsMainThread
? ProfileTimelineWorkerOperationType::DeserializeDataOnMainThread
: ProfileTimelineWorkerOperationType::DeserializeDataOffMainThread,
MarkerTracingType::START);
}
Read(parent, aCx, &messageData, rv);
if (isTimelineRecording) {
end = MakeUnique<WorkerTimelineMarker>(aIsMainThread
? ProfileTimelineWorkerOperationType::DeserializeDataOnMainThread
: ProfileTimelineWorkerOperationType::DeserializeDataOffMainThread,
MarkerTracingType::END);
timelines->AddMarkerForAllObservedDocShells(start);
timelines->AddMarkerForAllObservedDocShells(end);
}
if (NS_WARN_IF(rv.Failed())) {
xpc::Throw(aCx, rv.StealNSResult());
return false;
@ -2796,7 +2821,29 @@ WorkerPrivateParent<Derived>::PostMessageInternal(
new MessageEventRunnable(ParentAsWorkerPrivate(),
WorkerRunnable::WorkerThreadModifyBusyCount);
UniquePtr<AbstractTimelineMarker> start;
UniquePtr<AbstractTimelineMarker> end;
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool isTimelineRecording = timelines && !timelines->IsEmpty();
if (isTimelineRecording) {
start = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
MarkerTracingType::START);
}
runnable->Write(aCx, aMessage, transferable, aRv);
if (isTimelineRecording) {
end = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
MarkerTracingType::END);
timelines->AddMarkerForAllObservedDocShells(start);
timelines->AddMarkerForAllObservedDocShells(end);
}
if (NS_WARN_IF(aRv.Failed())) {
return;
}
@ -5385,7 +5432,29 @@ WorkerPrivate::PostMessageToParentInternal(
new MessageEventRunnable(this,
WorkerRunnable::ParentThreadUnchangedBusyCount);
UniquePtr<AbstractTimelineMarker> start;
UniquePtr<AbstractTimelineMarker> end;
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool isTimelineRecording = timelines && !timelines->IsEmpty();
if (isTimelineRecording) {
start = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
MarkerTracingType::START);
}
runnable->Write(aCx, aMessage, transferable, aRv);
if (isTimelineRecording) {
end = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
MarkerTracingType::END);
timelines->AddMarkerForAllObservedDocShells(start);
timelines->AddMarkerForAllObservedDocShells(end);
}
if (NS_WARN_IF(aRv.Failed())) {
return;
}

View File

@ -5919,11 +5919,11 @@ FrameLayerBuilder::DrawPaintedLayer(PaintedLayer* aLayer,
if (presContext && presContext->GetDocShell() && isActiveLayerManager) {
nsDocShell* docShell = static_cast<nsDocShell*>(presContext->GetDocShell());
bool isRecording;
docShell->GetRecordProfileTimelineMarkers(&isRecording);
if (isRecording) {
UniquePtr<TimelineMarker> marker = MakeUnique<LayerTimelineMarker>(aRegionToDraw);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (timelines && timelines->HasConsumer(docShell)) {
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<LayerTimelineMarker>(aRegionToDraw)));
}
}

View File

@ -218,12 +218,9 @@ RestyleTracker::DoProcessRestyles()
PROFILER_LABEL_PRINTF("RestyleTracker", "ProcessRestyles",
js::ProfileEntry::Category::CSS, "(%s)", docURL.get());
bool isTimelineRecording = false;
nsDocShell* docShell =
static_cast<nsDocShell*>(mRestyleManager->PresContext()->GetDocShell());
if (docShell) {
docShell->GetRecordProfileTimelineMarkers(&isTimelineRecording);
}
nsDocShell* docShell = static_cast<nsDocShell*>(mRestyleManager->PresContext()->GetDocShell());
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
// Create a AnimationsWithDestroyedFrame during restyling process to
// stop animations on elements that have no frame at the end of the
@ -341,9 +338,9 @@ RestyleTracker::DoProcessRestyles()
}
if (isTimelineRecording) {
UniquePtr<TimelineMarker> marker = MakeUnique<RestyleTimelineMarker>(
data->mRestyleHint, MarkerTracingType::START);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<RestyleTimelineMarker>(
data->mRestyleHint, MarkerTracingType::START)));
}
#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(MOZILLA_XPCOMRT_API)
@ -357,9 +354,9 @@ RestyleTracker::DoProcessRestyles()
AddRestyleRootsIfAwaitingRestyle(data->mDescendants);
if (isTimelineRecording) {
UniquePtr<TimelineMarker> marker = MakeUnique<RestyleTimelineMarker>(
data->mRestyleHint, MarkerTracingType::END);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<RestyleTimelineMarker>(
data->mRestyleHint, MarkerTracingType::END)));
}
}
@ -402,9 +399,9 @@ RestyleTracker::DoProcessRestyles()
}
#endif
if (isTimelineRecording) {
UniquePtr<TimelineMarker> marker = MakeUnique<RestyleTimelineMarker>(
currentRestyle->mRestyleHint, MarkerTracingType::START);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<RestyleTimelineMarker>(
currentRestyle->mRestyleHint, MarkerTracingType::START)));
}
ProcessOneRestyle(currentRestyle->mElement,
@ -413,9 +410,9 @@ RestyleTracker::DoProcessRestyles()
currentRestyle->mRestyleHintData);
if (isTimelineRecording) {
UniquePtr<TimelineMarker> marker = MakeUnique<RestyleTimelineMarker>(
currentRestyle->mRestyleHint, MarkerTracingType::END);
TimelineConsumers::AddMarkerForDocShell(docShell, Move(marker));
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<RestyleTimelineMarker>(
currentRestyle->mRestyleHint, MarkerTracingType::END)));
}
}
}

View File

@ -8970,8 +8970,11 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible)
js::ProfileEntry::Category::GRAPHICS, "(%s)", docURL.get());
nsDocShell* docShell = static_cast<nsDocShell*>(GetPresContext()->GetDocShell());
if (docShell) {
TimelineConsumers::AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::START);
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
if (isTimelineRecording) {
timelines->AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::START);
}
if (mReflowContinueTimer) {
@ -9148,9 +9151,10 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible)
tp->Accumulate();
}
if (docShell) {
TimelineConsumers::AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::END);
if (isTimelineRecording) {
timelines->AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::END);
}
return !interrupted;
}

View File

@ -1266,13 +1266,20 @@ HasPendingAnimations(nsIPresShell* aShell)
static void GetProfileTimelineSubDocShells(nsDocShell* aRootDocShell,
nsTArray<nsDocShell*>& aShells)
{
if (!aRootDocShell || TimelineConsumers::IsEmpty()) {
if (!aRootDocShell) {
return;
}
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (!timelines || timelines->IsEmpty()) {
return;
}
nsCOMPtr<nsISimpleEnumerator> enumerator;
nsresult rv = aRootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll,
nsIDocShell::ENUMERATE_BACKWARDS, getter_AddRefs(enumerator));
nsresult rv = aRootDocShell->GetDocShellEnumerator(
nsIDocShellTreeItem::typeAll,
nsIDocShell::ENUMERATE_BACKWARDS,
getter_AddRefs(enumerator));
if (NS_FAILED(rv)) {
return;
@ -1682,13 +1689,18 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
mPresShellsToInvalidateIfHidden.Clear();
if (mViewManagerFlushIsPending) {
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
nsTArray<nsDocShell*> profilingDocShells;
GetProfileTimelineSubDocShells(GetDocShell(mPresContext), profilingDocShells);
for (nsDocShell* docShell : profilingDocShells) {
// For the sake of the profile timeline's simplicity, this is flagged as
// paint even if it includes creating display lists
TimelineConsumers::AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::START);
MOZ_ASSERT(timelines);
MOZ_ASSERT(timelines->HasConsumer(docShell));
timelines->AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::START);
}
#ifdef MOZ_DUMP_PAINTING
if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
printf_stderr("Starting ProcessPendingUpdates\n");
@ -1698,13 +1710,17 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
mViewManagerFlushIsPending = false;
RefPtr<nsViewManager> vm = mPresContext->GetPresShell()->GetViewManager();
vm->ProcessPendingUpdates();
#ifdef MOZ_DUMP_PAINTING
if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
printf_stderr("Ending ProcessPendingUpdates\n");
}
#endif
for (nsDocShell* docShell : profilingDocShells) {
TimelineConsumers::AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::END);
MOZ_ASSERT(timelines);
MOZ_ASSERT(timelines->HasConsumer(docShell));
timelines->AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::END);
}
if (nsContentUtils::XPConnect()) {

View File

@ -62,9 +62,9 @@ nsView::~nsView()
if (mViewManager)
{
DropMouseGrabbing();
nsView *rootView = mViewManager->GetRootView();
if (rootView)
{
// Root views can have parents!
@ -83,7 +83,7 @@ nsView::~nsView()
{
mParent->RemoveChild(this);
}
mViewManager = nullptr;
}
else if (mParent)
@ -126,7 +126,7 @@ void nsView::DestroyWidget()
// If we are not attached to a base window, we're going to tear down our
// widget here. However, if we're attached to somebody elses widget, we
// want to leave the widget alone: don't reset the client data or call
// Destroy. Just clear our event view ptr and free our reference to it.
// Destroy. Just clear our event view ptr and free our reference to it.
if (mWidgetIsTopLevel) {
mWindow->SetAttachedWidgetListener(nullptr);
}
@ -589,7 +589,7 @@ nsresult nsView::CreateWidget(nsWidgetInitData *aWidgetInitData,
if (!mWindow) {
return NS_ERROR_FAILURE;
}
InitializeWindow(aEnableDragDrop, aResetVisibility);
return NS_OK;
@ -672,7 +672,7 @@ nsView::InitializeWindow(bool aEnableDragDrop, bool aResetVisibility)
if (aEnableDragDrop) {
mWindow->EnableDragDrop(true);
}
// propagate the z-index to the widget.
UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
@ -725,7 +725,7 @@ nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget)
return NS_OK;
}
// Detach this view from an attached widget.
// Detach this view from an attached widget.
nsresult nsView::DetachFromTopLevelWidget()
{
NS_PRECONDITION(mWidgetIsTopLevel, "Not attached currently!");
@ -747,7 +747,7 @@ nsresult nsView::DetachFromTopLevelWidget()
mWindow = nullptr;
mWidgetIsTopLevel = false;
return NS_OK;
}
@ -756,7 +756,7 @@ void nsView::SetZIndex(bool aAuto, int32_t aZIndex)
bool oldIsAuto = GetZIndexIsAuto();
mVFlags = (mVFlags & ~NS_VIEW_FLAG_AUTO_ZINDEX) | (aAuto ? NS_VIEW_FLAG_AUTO_ZINDEX : 0);
mZIndex = aZIndex;
if (HasWidget() || !oldIsAuto || !aAuto) {
UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
}
@ -1094,11 +1094,14 @@ nsView::DidCompositeWindow(const TimeStamp& aCompositeStart,
}
nsIDocShell* docShell = context->GetDocShell();
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
TimelineConsumers::AddMarkerForDocShell(docShell,
"Composite", aCompositeStart, MarkerTracingType::START);
TimelineConsumers::AddMarkerForDocShell(docShell,
"Composite", aCompositeEnd, MarkerTracingType::END);
if (timelines && timelines->HasConsumer(docShell)) {
timelines->AddMarkerForDocShell(docShell,
"Composite", aCompositeStart, MarkerTracingType::START);
timelines->AddMarkerForDocShell(docShell,
"Composite", aCompositeEnd, MarkerTracingType::END);
}
}
}