summaryrefslogtreecommitdiffstats
path: root/docshell/base/timeline/ObservedDocShell.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'docshell/base/timeline/ObservedDocShell.cpp')
-rw-r--r--docshell/base/timeline/ObservedDocShell.cpp171
1 files changed, 171 insertions, 0 deletions
diff --git a/docshell/base/timeline/ObservedDocShell.cpp b/docshell/base/timeline/ObservedDocShell.cpp
new file mode 100644
index 0000000000..e4a2b319f5
--- /dev/null
+++ b/docshell/base/timeline/ObservedDocShell.cpp
@@ -0,0 +1,171 @@
+/* -*- 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 "ObservedDocShell.h"
+
+#include <utility>
+
+#include "AbstractTimelineMarker.h"
+#include "LayerTimelineMarker.h"
+#include "MainThreadUtils.h"
+#include "mozilla/AutoRestore.h"
+#include "nsIDocShell.h"
+
+namespace mozilla {
+
+ObservedDocShell::ObservedDocShell(nsIDocShell* aDocShell)
+ : mDocShell(aDocShell), mLock("ObservedDocShellMutex") {
+ 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());
+ // Don't accept any markers generated by the process of popping
+ // markers.
+ if (!mPopping) {
+ mTimelineMarkers.AppendElement(std::move(aMarker));
+ }
+}
+
+void 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(mLock); // for `mOffTheMainThreadTimelineMarkers`.
+ mOffTheMainThreadTimelineMarkers.AppendElement(std::move(aMarker));
+}
+
+void ObservedDocShell::ClearMarkers() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mLock); // for `mOffTheMainThreadTimelineMarkers`.
+ mTimelineMarkers.Clear();
+ mOffTheMainThreadTimelineMarkers.Clear();
+}
+
+void ObservedDocShell::PopMarkers(
+ JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_RELEASE_ASSERT(!mPopping);
+ AutoRestore<bool> resetPopping(mPopping);
+ mPopping = true;
+
+ {
+ MutexAutoLock lock(mLock); // 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, and we can access it without holding the lock.
+ mTimelineMarkers.AppendElements(
+ std::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.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();
+ marker->mStack = startPayload->GetStack();
+ startPayload->AddDetails(aCx, *marker);
+ continue;
+ }
+
+ // Whenever a START marker is found, look for the corresponding END
+ // and build a {name,start,end} JS object.
+ if (startPayload->GetTracingType() == MarkerTracingType::START) {
+ bool hasSeenEnd = false;
+
+ // "Paint" markers are different because painting is handled at root
+ // docshell level. The information that a paint was done is stored at
+ // sub-docshell level, but we can only be sure that a paint did actually
+ // happen in if a "Layer" marker was recorded too.
+ bool startIsPaintType = strcmp(startPayload->GetName(), "Paint") == 0;
+ bool hasSeenLayerType = false;
+
+ // If we are processing a "Paint" marker, we append information from
+ // all the embedded "Layer" markers to this array.
+ dom::Sequence<dom::ProfileTimelineLayerRect> layerRectangles;
+
+ // DOM events can be nested, so we must take care when searching
+ // 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.ElementAt(j);
+ bool endIsLayerType = strcmp(endPayload->GetName(), "Layer") == 0;
+
+ // Look for "Layer" markers to stream out "Paint" markers.
+ if (startIsPaintType && endIsLayerType) {
+ 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;
+ continue;
+ }
+ if (endPayload->GetTracingType() == MarkerTracingType::END) {
+ if (markerDepth > 0) {
+ --markerDepth;
+ continue;
+ }
+ if (!startIsPaintType || (startIsPaintType && hasSeenLayerType)) {
+ dom::ProfileTimelineMarker* marker = aStore.AppendElement();
+ marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
+ marker->mStart = startPayload->GetTime();
+ marker->mEnd = endPayload->GetTime();
+ marker->mStack = startPayload->GetStack();
+ if (hasSeenLayerType) {
+ marker->mRectangles.Construct(layerRectangles);
+ }
+ startPayload->AddDetails(aCx, *marker);
+ endPayload->AddDetails(aCx, *marker);
+ }
+ hasSeenEnd = true;
+ break;
+ }
+ }
+
+ // If we did not see the corresponding END, keep the START.
+ if (!hasSeenEnd) {
+ keptStartMarkers.AppendElement(
+ std::move(mTimelineMarkers.ElementAt(i)));
+ mTimelineMarkers.RemoveElementAt(i);
+ --i;
+ }
+ }
+ }
+
+ mTimelineMarkers = std::move(keptStartMarkers);
+}
+
+} // namespace mozilla