diff options
Diffstat (limited to '')
-rw-r--r-- | docshell/base/timeline/ObservedDocShell.cpp | 171 |
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 |