--- a/browser/locales/en-US/chrome/browser/devtools/markers.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/markers.properties
@@ -25,16 +25,17 @@ marker.label.parseXML=Parse XML
marker.label.domevent=DOM Event
marker.label.consoleTime=Console
marker.label.garbageCollection2=Garbage Collection
marker.label.garbageCollection.incremental=Incremental GC
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.*):
# These strings are displayed as JavaScript markers that have special
# reasons that can be translated.
marker.label.javascript.scriptElement=Script Tag
marker.label.javascript.promiseCallback=Promise Callback
marker.label.javascript.promiseInit=Promise Init
@@ -70,16 +71,21 @@ marker.field.DOMEventPhase=Phase:
# Non-incremental cause for a Garbage Collection marker
marker.field.nonIncrementalCause=Non-incremental Cause:
# For "Recalculate Style" markers
marker.field.restyleHint=Restyle Hint:
# General "reason" for a marker (JavaScript, Garbage Collection)
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>
marker.value.DOMEventTargetPhase=Target
marker.value.DOMEventCapturingPhase=Capture
marker.value.DOMEventBubblingPhase=Bubbling
# LOCALIZATION NOTE (marker.gcreason.label.*):
--- a/devtools/client/performance/modules/logic/marker-utils.js
+++ b/devtools/client/performance/modules/logic/marker-utils.js
@@ -434,16 +434,23 @@ const Formatters = {
}
},
CycleCollectionFields: function (marker) {
return {
[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}`)
+ };
+ }
};
/**
* Takes a marker and returns the definition for that marker type,
* falling back to the UNKNOWN definition if undefined.
*
* @param {Marker} marker
* @return {object}
--- a/devtools/client/performance/modules/markers.js
+++ b/devtools/client/performance/modules/markers.js
@@ -120,16 +120,22 @@ const TIMELINE_BLUEPRINT = {
fields: Formatters.CycleCollectionFields,
},
"nsCycleCollector::ForgetSkippable": {
group: 1,
colorName: "graphs-red",
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": {
group: 2,
colorName: "graphs-blue",
label: sublabelForProperty(L10N.getStr("marker.label.consoleTime"), "causeName"),
fields: [{
property: "causeName",
--- a/devtools/client/performance/test/browser.ini
+++ b/devtools/client/performance/test/browser.ini
@@ -1,16 +1,18 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
doc_allocs.html
doc_innerHTML.html
doc_markers.html
doc_simple-test.html
+ doc_worker.html
+ js_simpleWorker.js
head.js
[browser_aaa-run-first-leaktest.js]
[browser_perf-categories-js-calltree.js]
[browser_perf-clear-01.js]
[browser_perf-clear-02.js]
[browser_perf-columns-js-calltree.js]
[browser_perf-columns-memory-calltree.js]
@@ -140,8 +142,9 @@ skip-if = os == 'linux' || debug # bug 1
skip-if = true # Bug 1176370
[browser_timeline-filters-02.js]
[browser_timeline-waterfall-background.js]
[browser_timeline-waterfall-generic.js]
[browser_timeline-waterfall-rerender.js]
skip-if = true # Bug 1170105
[browser_timeline-waterfall-sidebar.js]
skip-if = true # Bug 1161817
+[browser_timeline-waterfall-workers.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance/test/browser_timeline-waterfall-workers.js
@@ -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;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance/test/doc_worker.html
@@ -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>
--- a/devtools/client/performance/test/head.js
+++ b/devtools/client/performance/test/head.js
@@ -24,16 +24,17 @@ var {
var mm = null;
const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js"
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";
const PROFILER_BUFFER_SIZE_PREF = "devtools.performance.profiler.buffer-size";
const PROFILER_SAMPLE_RATE_PREF = "devtools.performance.profiler.sample-frequency-khz";
const FRAMERATE_PREF = "devtools.performance.ui.enable-framerate";
const MEMORY_PREF = "devtools.performance.ui.enable-memory";
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance/test/js_simpleWorker.js
@@ -0,0 +1,4 @@
+self.addEventListener('message', function(e) {
+ self.postMessage(e.data);
+ self.close()
+}, false);
--- a/devtools/client/themes/performance.css
+++ b/devtools/client/themes/performance.css
@@ -564,16 +564,20 @@ menuitem.marker-color-graphs-bluegrey:be
menuitem.marker-color-graphs-purple:before,
.marker-color-graphs-purple {
background-color: var(--theme-graphs-purple);
}
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);
}
menuitem.marker-color-graphs-grey:before,
.marker-color-graphs-grey{
background-color: var(--theme-graphs-grey);
}
--- a/devtools/client/themes/variables.css
+++ b/devtools/client/themes/variables.css
@@ -43,16 +43,17 @@
--theme-highlight-pink: #b82ee5;
/* Colors used in Graphs, like performance tools. Similar colors to Chrome's timeline. */
--theme-graphs-green: #85d175;
--theme-graphs-blue: #83b7f6;
--theme-graphs-bluegrey: #0072ab;
--theme-graphs-purple: #b693eb;
--theme-graphs-yellow: #efc052;
+ --theme-graphs-orange: #d97e00;
--theme-graphs-red: #e57180;
--theme-graphs-grey: #cccccc;
}
:root.theme-dark {
--theme-body-background: #14171a;
--theme-sidebar-background: #181d20;
--theme-contrast-background: #b28025;
@@ -81,11 +82,12 @@
--theme-highlight-pink: #df80ff;
/* Colors used in Graphs, like performance tools. Mostly similar to some "highlight-*" colors. */
--theme-graphs-green: #70bf53;
--theme-graphs-blue: #46afe3;
--theme-graphs-bluegrey: #5e88b0;
--theme-graphs-purple: #df80ff;
--theme-graphs-yellow: #d99b28;
+ --theme-graphs-orange: #d96629;
--theme-graphs-red: #eb5368;
--theme-graphs-grey: #757873;
}
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -835,17 +835,17 @@ nsDocShell::nsDocShell()
getpid(),
AssertedCast<unsigned long long>(mHistoryID));
}
#endif
}
nsDocShell::~nsDocShell()
{
- MOZ_ASSERT(!IsObserved());
+ MOZ_ASSERT(!mObserved);
Destroy();
nsCOMPtr<nsISHistoryInternal> shPrivate(do_QueryInterface(mSessionHistory));
if (shPrivate) {
shPrivate->SetRootDocShell(nullptr);
}
@@ -2834,45 +2834,56 @@ nsDocShell::HistoryTransactionRemoved(in
return NS_OK;
}
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;
}
NS_IMETHODIMP
nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue)
{
- *aValue = IsObserved();
+ *aValue = !!mObserved;
return NS_OK;
}
nsresult
nsDocShell::PopProfileTimelineMarkers(
JSContext* aCx,
JS::MutableHandle<JS::Value> aOut)
{
nsTArray<dom::ProfileTimelineMarker> store;
SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
- if (IsObserved()) {
+ if (mObserved) {
mObserved->PopMarkers(aCx, store);
}
if (!ToJSValue(aCx, store, aOut)) {
JS_ClearPendingException(aCx);
return NS_ERROR_UNEXPECTED;
}
@@ -10645,25 +10656,25 @@ nsDocShell::DoURILoad(nsIURI* aURI,
// Currently only http and ftp channels support this.
props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"),
aReferrerURI);
}
nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(channel));
-
+
/* Get the cache Key from SH */
nsCOMPtr<nsISupports> cacheKey;
if (mLSHE) {
mLSHE->GetCacheKey(getter_AddRefs(cacheKey));
} 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.....
if (aPostData) {
// XXX it's a bit of a hack to rewind the postdata stream here but
// it has to be done in case the post data is being reused multiple
// times.
nsCOMPtr<nsISeekableStream> postDataSeekable =
@@ -10704,17 +10715,17 @@ nsDocShell::DoURILoad(nsIURI* aURI,
mLoadType == LOAD_RELOAD_NORMAL ||
mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
if (cacheChannel && cacheKey) {
cacheChannel->SetCacheKey(cacheKey);
}
}
}
}
-
+
if (httpChannel) {
if (aHeadersData) {
rv = AddHeadersToChannel(aHeadersData, httpChannel);
}
// Set the referrer explicitly
if (aReferrerURI && aSendReferrer) {
// Referrer is currenly only set for link clicks here.
httpChannel->SetReferrerWithPolicy(aReferrerURI, aReferrerPolicy);
@@ -13887,36 +13898,40 @@ nsDocShell::GetOpener()
}
void
nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
const char16_t* aFunctionName,
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);
+ }
}
}
void
nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
const nsString& aKeyword)
{
if (aProvider.IsEmpty()) {
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -270,33 +270,30 @@ public:
}
bool InFrameSwap();
mozilla::OriginAttributes GetOriginAttributes();
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
// be very fast, so instead of using a Map or having to search for some
// docshell-specific markers storage, a pointer to an `ObservedDocShell` is
// is stored on docshells directly.
friend void mozilla::TimelineConsumers::AddConsumer(nsDocShell*);
friend void mozilla::TimelineConsumers::RemoveConsumer(nsDocShell*);
friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
nsDocShell*, const char*, MarkerTracingType);
friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
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.
static void CopyFavicon(nsIURI* aOldURI,
nsIURI* aNewURI,
bool aInPrivateBrowsing);
static nsDocShell* Cast(nsIDocShell* aDocShell)
--- a/docshell/base/timeline/AbstractTimelineMarker.cpp
+++ b/docshell/base/timeline/AbstractTimelineMarker.cpp
@@ -30,16 +30,24 @@ AbstractTimelineMarker::AbstractTimeline
UniquePtr<AbstractTimelineMarker>
AbstractTimelineMarker::Clone()
{
MOZ_ASSERT(false, "Clone method not yet implemented on this marker type.");
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);
}
void
AbstractTimelineMarker::SetCurrentTime()
{
@@ -49,9 +57,15 @@ AbstractTimelineMarker::SetCurrentTime()
void
AbstractTimelineMarker::SetCustomTime(const TimeStamp& aTime)
{
bool isInconsistent = false;
mTime = (aTime - TimeStamp::ProcessCreation(isInconsistent)).ToMilliseconds();
}
+void
+AbstractTimelineMarker::SetCustomTime(DOMHighResTimeStamp aTime)
+{
+ mTime = aTime;
+}
+
} // namespace mozilla
--- a/docshell/base/timeline/AbstractTimelineMarker.h
+++ b/docshell/base/timeline/AbstractTimelineMarker.h
@@ -7,55 +7,58 @@
#ifndef mozilla_AbstractTimelineMarker_h_
#define mozilla_AbstractTimelineMarker_h_
#include "TimelineMarkerEnums.h" // for MarkerTracingType
#include "nsDOMNavigationTiming.h" // for DOMHighResTimeStamp
#include "mozilla/UniquePtr.h"
struct JSContext;
+class JSObject;
namespace mozilla {
class TimeStamp;
namespace dom {
struct ProfileTimelineMarker;
}
class AbstractTimelineMarker
{
private:
AbstractTimelineMarker() = delete;
AbstractTimelineMarker(const AbstractTimelineMarker& aOther) = delete;
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;
const char* GetName() const { return mName; }
DOMHighResTimeStamp GetTime() const { return mTime; }
MarkerTracingType GetTracingType() const { return mTracingType; }
private:
const char* mName;
DOMHighResTimeStamp mTime;
MarkerTracingType mTracingType;
+protected:
void SetCurrentTime();
void SetCustomTime(const TimeStamp& aTime);
+ void SetCustomTime(DOMHighResTimeStamp aTime);
};
} // namespace mozilla
#endif /* mozilla_AbstractTimelineMarker_h_ */
--- a/docshell/base/timeline/AutoGlobalTimelineMarker.cpp
+++ b/docshell/base/timeline/AutoGlobalTimelineMarker.cpp
@@ -1,37 +1,41 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include "mozilla/AutoGlobalTimelineMarker.h"
+#include "AutoGlobalTimelineMarker.h"
#include "TimelineConsumers.h"
#include "MainThreadUtils.h"
namespace mozilla {
AutoGlobalTimelineMarker::AutoGlobalTimelineMarker(const char* aName
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: mName(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
--- a/docshell/base/timeline/AutoGlobalTimelineMarker.h
+++ b/docshell/base/timeline/AutoGlobalTimelineMarker.h
@@ -3,19 +3,16 @@
/* 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_AutoGlobalTimelineMarker_h_
#define mozilla_AutoGlobalTimelineMarker_h_
#include "mozilla/GuardObjects.h"
-#include "mozilla/RefPtr.h"
-
-class nsDocShell;
namespace mozilla {
// # AutoGlobalTimelineMarker
//
// Similar to `AutoTimelineMarker`, but adds its traced marker to all docshells,
// not a single particular one. This is useful for operations that aren't
// associated with any one particular doc shell, or when it isn't clear which
--- a/docshell/base/timeline/AutoTimelineMarker.cpp
+++ b/docshell/base/timeline/AutoTimelineMarker.cpp
@@ -1,40 +1,51 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include "mozilla/AutoTimelineMarker.h"
+#include "AutoTimelineMarker.h"
#include "TimelineConsumers.h"
#include "MainThreadUtils.h"
-#include "nsDocShell.h"
namespace mozilla {
AutoTimelineMarker::AutoTimelineMarker(nsIDocShell* aDocShell, const char* aName
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: mName(aName)
, mDocShell(nullptr)
{
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
--- a/docshell/base/timeline/AutoTimelineMarker.h
+++ b/docshell/base/timeline/AutoTimelineMarker.h
@@ -6,17 +6,16 @@
#ifndef mozilla_AutoTimelineMarker_h_
#define mozilla_AutoTimelineMarker_h_
#include "mozilla/GuardObjects.h"
#include "mozilla/RefPtr.h"
class nsIDocShell;
-class nsDocShell;
namespace mozilla {
// # AutoTimelineMarker
//
// An RAII class to trace some task in the platform by adding a start and end
// timeline marker pair. These markers are then rendered in the devtools'
// performance tool's waterfall graph.
@@ -31,17 +30,17 @@ namespace mozilla {
class MOZ_RAII AutoTimelineMarker
{
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
// The name of the marker we are adding.
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
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
~AutoTimelineMarker();
AutoTimelineMarker(const AutoTimelineMarker& aOther) = delete;
void operator=(const AutoTimelineMarker& aOther) = delete;
--- a/docshell/base/timeline/LayerTimelineMarker.h
+++ b/docshell/base/timeline/LayerTimelineMarker.h
@@ -4,30 +4,28 @@
* 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_LayerTimelineMarker_h_
#define mozilla_LayerTimelineMarker_h_
#include "TimelineMarker.h"
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+#include "nsRegion.h"
namespace mozilla {
class LayerTimelineMarker : public TimelineMarker
{
public:
explicit LayerTimelineMarker(const nsIntRegion& aRegion)
: TimelineMarker("Layer", MarkerTracingType::HELPER_EVENT)
, mRegion(aRegion)
{}
- ~LayerTimelineMarker()
- {}
-
void AddLayerRectangles(dom::Sequence<dom::ProfileTimelineLayerRect>& aRectangles)
{
nsIntRegionRectIterator it(mRegion);
while (const nsIntRect* iterRect = it.Next()) {
dom::ProfileTimelineLayerRect rect;
rect.mX = iterRect->X();
rect.mY = iterRect->Y();
rect.mWidth = iterRect->Width();
new file mode 100644
--- /dev/null
+++ b/docshell/base/timeline/MarkersStorage.cpp
@@ -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
rename from docshell/base/timeline/OTMTMarkerReceiver.h
rename to docshell/base/timeline/MarkersStorage.h
--- a/docshell/base/timeline/OTMTMarkerReceiver.h
+++ b/docshell/base/timeline/MarkersStorage.h
@@ -1,40 +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_OTMTMarkerObserver_h_
-#define mozilla_OTMTMarkerObserver_h_
+#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;
-class OTMTMarkerReceiver
+namespace dom {
+struct ProfileTimelineMarker;
+}
+
+class MarkersStorage : public LinkedListElement<MarkersStorage>
{
private:
- OTMTMarkerReceiver() = delete;
- OTMTMarkerReceiver(const OTMTMarkerReceiver& aOther) = delete;
- void operator=(const OTMTMarkerReceiver& aOther) = delete;
+ MarkersStorage() = delete;
+ MarkersStorage(const MarkersStorage& aOther) = delete;
+ void operator=(const MarkersStorage& aOther) = delete;
public:
- explicit OTMTMarkerReceiver(const char* aMutexName)
- : mLock(aMutexName)
- {
- }
+ explicit MarkersStorage(const char* aMutexName);
+ virtual ~MarkersStorage();
- virtual ~OTMTMarkerReceiver() {}
- virtual void AddOTMTMarkerClone(UniquePtr<AbstractTimelineMarker>& aMarker) = 0;
+ 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() { return mLock; };
+ Mutex& GetLock();
private:
Mutex mLock;
};
} // namespace mozilla
-#endif /* mozilla_OTMTMarkerObserver_h_ */
+#endif /* mozilla_MarkersStorage_h_ */
--- a/docshell/base/timeline/ObservedDocShell.cpp
+++ b/docshell/base/timeline/ObservedDocShell.cpp
@@ -8,58 +8,71 @@
#include "AbstractTimelineMarker.h"
#include "LayerTimelineMarker.h"
#include "MainThreadUtils.h"
#include "mozilla/Move.h"
namespace mozilla {
-ObservedDocShell::ObservedDocShell(nsDocShell* aDocShell)
- : OTMTMarkerReceiver("ObservedDocShellMutex")
+ObservedDocShell::ObservedDocShell(nsIDocShell* aDocShell)
+ : MarkersStorage("ObservedDocShellMutex")
, mDocShell(aDocShell)
{
MOZ_ASSERT(NS_IsMainThread());
}
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
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.
if (startPayload->GetTracingType() == MarkerTracingType::TIMESTAMP) {
dom::ProfileTimelineMarker* marker = aStore.AppendElement();
marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
marker->mStart = startPayload->GetTime();
marker->mEnd = startPayload->GetTime();
@@ -88,22 +101,23 @@ ObservedDocShell::PopMarkers(JSContext*
// for the matching end. It doesn't hurt to apply this logic to
// all event types.
uint32_t markerDepth = 0;
// The assumption is that the devtools timeline flushes markers frequently
// 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;
}
if (!startPayload->Equals(*endPayload)) {
continue;
}
if (endPayload->GetTracingType() == MarkerTracingType::START) {
++markerDepth;
@@ -128,17 +142,17 @@ ObservedDocShell::PopMarkers(JSContext*
}
hasSeenEnd = true;
break;
}
}
// 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;
}
}
}
mTimelineMarkers.SwapElements(keptStartMarkers);
}
--- a/docshell/base/timeline/ObservedDocShell.h
+++ b/docshell/base/timeline/ObservedDocShell.h
@@ -2,46 +2,50 @@
/* 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_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;
namespace dom {
struct ProfileTimelineMarker;
}
// # ObservedDocShell
//
// 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
#endif /* mozilla_ObservedDocShell_h_ */
--- a/docshell/base/timeline/TimelineConsumers.cpp
+++ b/docshell/base/timeline/TimelineConsumers.cpp
@@ -1,134 +1,218 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include "mozilla/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);
+
+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()
+{
+ // 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());
-LinkedList<ObservedDocShell>&
-TimelineConsumers::GetOrCreateObservedDocShellsList()
-{
- if (!sObservedDocShells) {
- sObservedDocShells = new LinkedList<ObservedDocShell>();
+ // 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()
+{
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (!obs) {
+ return false;
+ }
+ if (NS_WARN_IF(NS_FAILED(
+ obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false)))) {
+ return false;
+ }
+ return true;
+}
+
+bool
+TimelineConsumers::RemoveObservers()
{
- if (!sLock) {
- sLock = new Mutex("TimelineConsumersMutex");
+ 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 *sLock;
+ 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());
+ 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());
+ 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
TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
const char* aName,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
- if (aDocShell->IsObserved()) {
+ if (HasConsumer(aDocShell)) {
aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTracingType)));
}
}
void
TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
- if (aDocShell->IsObserved()) {
+ if (HasConsumer(aDocShell)) {
aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTime, aTracingType)));
}
}
void
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,
MarkerTracingType aTracingType)
{
MOZ_ASSERT(NS_IsMainThread());
AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTracingType);
}
@@ -146,114 +230,67 @@ void
TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
UniquePtr<AbstractTimelineMarker>&& aMarker)
{
MOZ_ASSERT(NS_IsMainThread());
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));
+ }
}
}
void
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));
+ }
}
}
} // namespace mozilla
--- a/docshell/base/timeline/TimelineConsumers.h
+++ b/docshell/base/timeline/TimelineConsumers.h
@@ -1,112 +1,120 @@
-/* -*- 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_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
{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
private:
- // Counter for how many timelines are currently interested in markers.
- static unsigned long sActiveConsumers;
- static LinkedList<ObservedDocShell>* sObservedDocShells;
- static LinkedList<ObservedDocShell>& GetOrCreateObservedDocShellsList();
+ TimelineConsumers();
+ TimelineConsumers(const TimelineConsumers& aOther) = delete;
+ void operator=(const TimelineConsumers& aOther) = delete;
+ virtual ~TimelineConsumers() = default;
- // Lock used when adding off-the-main-thread markers.
- static Mutex* sLock;
+ 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);
+
+ // 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);
- // 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);
+private:
+ static StaticRefPtr<TimelineConsumers> sInstance;
+ static bool sInShutdown;
- // 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);
+ // 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
#endif /* mozilla_TimelineConsumers_h_ */
--- a/docshell/base/timeline/TimelineMarker.cpp
+++ b/docshell/base/timeline/TimelineMarker.cpp
@@ -20,24 +20,16 @@ TimelineMarker::TimelineMarker(const cha
const TimeStamp& aTime,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest)
: AbstractTimelineMarker(aName, aTime, aTracingType)
{
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)
{
// Nothing to do here for plain markers.
}
JSObject*
TimelineMarker::GetStack()
--- a/docshell/base/timeline/TimelineMarker.h
+++ b/docshell/base/timeline/TimelineMarker.h
@@ -3,35 +3,35 @@
/* 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_TimelineMarker_h_
#define mozilla_TimelineMarker_h_
#include "AbstractTimelineMarker.h"
+#include "js/RootingAPI.h"
namespace mozilla {
// Objects of this type can be added to the timeline if there is an interested
// consumer. The class can also be subclassed to let a given marker creator
// provide custom details.
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;
protected:
void CaptureStack();
private:
// While normally it is not a good idea to make a persistent root,
new file mode 100644
--- /dev/null
+++ b/docshell/base/timeline/WorkerTimelineMarker.h
@@ -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_ */
--- a/docshell/base/timeline/moz.build
+++ b/docshell/base/timeline/moz.build
@@ -7,29 +7,31 @@
EXPORTS.mozilla += [
'AbstractTimelineMarker.h',
'AutoGlobalTimelineMarker.h',
'AutoTimelineMarker.h',
'ConsoleTimelineMarker.h',
'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',
]
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
--- a/docshell/test/browser/browser_timelineMarkers-frame-02.js
+++ b/docshell/test/browser/browser_timelineMarkers-frame-02.js
@@ -100,16 +100,17 @@ var TESTS = [{
is(markers[0].name, "TimeStamp", "Got Timestamp marker");
is(markers[0].causeName, "rock", "Got Timestamp label value");
content.console.timeStamp("paper");
content.console.timeStamp("scissors");
content.console.timeStamp();
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");
is(markers[1].name, "TimeStamp", "Got Timestamp marker");
is(markers[1].causeName, "scissors", "Got Timestamp label value");
is(markers[2].name, "TimeStamp", "Got empty Timestamp marker when no argument given");
is(markers[2].causeName, void 0, "Got empty Timestamp label value");
is(markers[3].name, "TimeStamp", "Got empty Timestamp marker when argument is undefined");
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -1060,52 +1060,51 @@ Console::Method(JSContext* aCx, MethodNa
RefPtr<nsPerformance> performance = win->GetPerformance();
if (!performance) {
return;
}
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);
TimeDuration duration =
mozilla::TimeStamp::Now() - workerPrivate->CreationTimeStamp();
callData->mMonotonicTimer = duration.ToMilliseconds();
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -1109,45 +1109,45 @@ EventListenerManager::HandleEventInterna
aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent();
if (!aEvent->currentTarget) {
break;
}
}
// 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);
}
}
}
}
}
aEvent->currentTarget = nullptr;
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2970,25 +2970,30 @@ TabChild::DidComposite(uint64_t aTransac
manager->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
}
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
TabChild::ClearCachedResources()
{
MOZ_ASSERT(mPuppetWidget);
MOZ_ASSERT(mPuppetWidget->GetLayerManager());
MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() ==
--- a/dom/webidl/ProfileTimelineMarker.webidl
+++ b/dom/webidl/ProfileTimelineMarker.webidl
@@ -20,25 +20,40 @@ dictionary ProfileTimelineStackFrame {
dictionary ProfileTimelineLayerRect {
long x = 0;
long y = 0;
long width = 0;
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;
};
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -60,16 +60,18 @@
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/StructuredCloneHolder.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
#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"
#include "nsError.h"
#include "nsDOMJSUtils.h"
#include "nsHostObjectProtocolHandler.h"
#include "nsJSEnvironment.h"
#include "nsJSUtils.h"
@@ -633,17 +635,40 @@ public:
{
nsCOMPtr<nsPIDOMWindow> parent;
if (aIsMainThread) {
parent = do_QueryInterface(aTarget->GetParentObject());
}
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;
}
RefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr);
rv = event->InitMessageEvent(NS_LITERAL_STRING("message"),
false /* non-bubbling */,
@@ -2791,17 +2816,39 @@ WorkerPrivateParent<Derived>::PostMessag
}
transferable.setObject(*array);
}
RefPtr<MessageEventRunnable> runnable =
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;
}
runnable->SetMessageSource(Move(aClientInfo));
if (!runnable->Dispatch(aCx)) {
aRv.Throw(NS_ERROR_FAILURE);
@@ -5380,17 +5427,39 @@ WorkerPrivate::PostMessageToParentIntern
}
transferable.setObject(*array);
}
RefPtr<MessageEventRunnable> runnable =
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;
}
if (!runnable->Dispatch(aCx)) {
aRv = NS_ERROR_FAILURE;
}
}
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -5914,21 +5914,21 @@ FrameLayerBuilder::DrawPaintedLayer(Pain
gfxUtils::ClipToRegion(aContext, aRegionToDraw);
}
}
FlashPaint(aContext);
}
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)));
}
}
if (!aRegionToInvalidate.IsEmpty()) {
aLayer->AddInvalidRect(aRegionToInvalidate.GetBounds());
}
}
--- a/layout/base/RestyleTracker.cpp
+++ b/layout/base/RestyleTracker.cpp
@@ -213,22 +213,19 @@ RestyleTracker::DoProcessRestyles()
uri->GetSpec(docURL);
} else {
docURL = "N/A";
}
}
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
// restyling process.
RestyleManager::AnimationsWithDestroyedFrame
animationsWithDestroyedFrame(mRestyleManager);
// Create a ReframingStyleContexts struct on the stack and put it in our
@@ -336,35 +333,35 @@ RestyleTracker::DoProcessRestyles()
nsAutoPtr<RestyleData> data;
if (!GetRestyleData(element, data)) {
LOG_RESTYLE("skipping, already restyled");
continue;
}
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)
Maybe<GeckoProfilerTracingRAII> profilerRAII;
if (profiler_feature_active("restyle")) {
profilerRAII.emplace("Paint", "Styles", Move(data->mBacktrace));
}
#endif
ProcessOneRestyle(element, data->mRestyleHint, data->mChangeHint,
data->mRestyleHintData);
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)));
}
}
if (mHaveLaterSiblingRestyles) {
// Keep processing restyles for now
continue;
}
@@ -397,30 +394,30 @@ RestyleTracker::DoProcessRestyles()
#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(MOZILLA_XPCOMRT_API)
Maybe<GeckoProfilerTracingRAII> profilerRAII;
if (profiler_feature_active("restyle")) {
profilerRAII.emplace("Paint", "Styles", Move(currentRestyle->mBacktrace));
}
#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,
currentRestyle->mRestyleHint,
currentRestyle->mChangeHint,
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)));
}
}
}
}
}
// mPendingRestyles is now empty.
mHaveSelectors = false;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -8965,18 +8965,21 @@ PresShell::DoReflow(nsIFrame* target, bo
nsIURI *uri = mDocument->GetDocumentURI();
if (uri)
uri->GetSpec(docURL);
PROFILER_LABEL_PRINTF("PresShell", "DoReflow",
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) {
mReflowContinueTimer->Cancel();
mReflowContinueTimer = nullptr;
}
nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
@@ -9143,19 +9146,20 @@ PresShell::DoReflow(nsIFrame* target, bo
if (tp->current.numChars > 100) {
TimeDuration reflowTime = TimeStamp::Now() - timeStart;
LogTextPerfStats(tp, this, tp->current,
reflowTime.ToMilliseconds(), eLog_reflow, nullptr);
}
tp->Accumulate();
}
- if (docShell) {
- TimelineConsumers::AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::END);
- }
+ if (isTimelineRecording) {
+ timelines->AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::END);
+ }
+
return !interrupted;
}
#ifdef DEBUG
void
PresShell::DoVerifyReflow()
{
if (GetVerifyReflowEnable()) {
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1261,23 +1261,30 @@ HasPendingAnimations(nsIPresShell* aShel
/**
* Return a list of all the child docShells in a given root docShell that are
* visible and are recording markers for the profilingTimeline
*/
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;
}
nsCOMPtr<nsIDocShell> curItem;
bool hasMore = false;
while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
@@ -1677,39 +1684,48 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
}
for (nsIPresShell* shell : mPresShellsToInvalidateIfHidden) {
shell->InvalidatePresShellIfHidden();
}
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");
}
#endif
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()) {
nsContentUtils::XPConnect()->NotifyDidPaint();
nsJSContext::NotifyDidPaint();
}
}
--- a/view/nsView.cpp
+++ b/view/nsView.cpp
@@ -57,19 +57,19 @@ nsView::~nsView()
// just unhook it. Someone else will want to destroy this.
RemoveChild(child);
}
}
if (mViewManager)
{
DropMouseGrabbing();
-
+
nsView *rootView = mViewManager->GetRootView();
-
+
if (rootView)
{
// Root views can have parents!
if (mParent)
{
mViewManager->RemoveChild(this);
}
@@ -78,17 +78,17 @@ nsView::~nsView()
// Inform the view manager that the root view has gone away...
mViewManager->SetRootView(nullptr);
}
}
else if (mParent)
{
mParent->RemoveChild(this);
}
-
+
mViewManager = nullptr;
}
else if (mParent)
{
mParent->RemoveChild(this);
}
if (mPreviousWindow) {
@@ -121,17 +121,17 @@ NS_IMETHODIMP DestroyWidgetRunnable::Run
void nsView::DestroyWidget()
{
if (mWindow)
{
// 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);
}
else {
mWindow->SetWidgetListener(nullptr);
nsCOMPtr<nsIRunnable> widgetDestroyer =
new DestroyWidgetRunnable(mWindow);
@@ -584,17 +584,17 @@ nsresult nsView::CreateWidget(nsWidgetIn
}
// XXX: using aForceUseIWidgetParent=true to preserve previous
// semantics. It's not clear that it's actually needed.
mWindow = parentWidget->CreateChild(trect, aWidgetInitData, true);
if (!mWindow) {
return NS_ERROR_FAILURE;
}
-
+
InitializeWindow(aEnableDragDrop, aResetVisibility);
return NS_OK;
}
nsresult nsView::CreateWidgetForParent(nsIWidget* aParentWidget,
nsWidgetInitData *aWidgetInitData,
bool aEnableDragDrop,
@@ -667,17 +667,17 @@ nsView::InitializeWindow(bool aEnableDra
{
MOZ_ASSERT(mWindow, "Must have a window to initialize");
mWindow->SetWidgetListener(this);
if (aEnableDragDrop) {
mWindow->EnableDragDrop(true);
}
-
+
// propagate the z-index to the widget.
UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
//make sure visibility state is accurate
if (aResetVisibility) {
SetVisibility(GetVisibility());
}
@@ -720,17 +720,17 @@ nsresult nsView::AttachToTopLevelWidget(
mWidgetIsTopLevel = true;
// Refresh the view bounds
CalcWidgetBounds(mWindow->WindowType());
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!");
NS_PRECONDITION(mWindow, "null mWindow for DetachFromTopLevelWidget!");
mWindow->SetAttachedWidgetListener(nullptr);
nsIWidgetListener* listener = mWindow->GetPreviouslyAttachedWidgetListener();
@@ -742,26 +742,26 @@ nsresult nsView::DetachFromTopLevelWidge
// If the new view's frame is paint suppressed then the window
// will want to use us instead until that's done
mWindow->SetPreviouslyAttachedWidgetListener(this);
mPreviousWindow = mWindow;
mWindow = nullptr;
mWidgetIsTopLevel = false;
-
+
return NS_OK;
}
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));
}
}
void nsView::AssertNoWindow()
{
// XXX: it would be nice to make this a strong assert
@@ -1089,21 +1089,24 @@ nsView::DidCompositeWindow(const TimeSta
// If the two timestamps are identical, this was likely a fake composite
// event which wouldn't be terribly useful to display.
if (aCompositeStart == aCompositeEnd) {
return;
}
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);
+ }
}
}
void
nsView::RequestRepaint()
{
nsIPresShell* presShell = mViewManager->GetPresShell();
if (presShell) {