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 000000000..b394f37b3
--- /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 "AbstractTimelineMarker.h"
+#include "LayerTimelineMarker.h"
+#include "MainThreadUtils.h"
+#include "mozilla/Move.h"
+#include "mozilla/AutoRestore.h"
+
+namespace mozilla {
+
+ObservedDocShell::ObservedDocShell(nsIDocShell* aDocShell)
+ : MarkersStorage("ObservedDocShellMutex")
+ , mDocShell(aDocShell)
+ , mPopping(false)
+{
+ 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(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(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`.
+
+ MOZ_RELEASE_ASSERT(!mPopping);
+ AutoRestore<bool> resetPopping(mPopping);
+ mPopping = true;
+
+ // 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.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(Move(mTimelineMarkers.ElementAt(i)));
+ mTimelineMarkers.RemoveElementAt(i);
+ --i;
+ }
+ }
+ }
+
+ mTimelineMarkers.SwapElements(keptStartMarkers);
+}
+
+} // namespace mozilla