Files
basilisk55/dom/performance/PerformanceMainThread.cpp
T
roytam1 6b744a90ee ported from UXP:
- Issue #2282 - Align the Performance Observer navigation and resource with the spec. This now passes the conformance tests. https://bugzilla.mozilla.org/show_bug.cgi?id=1425458 Resource timing entries Workers - part 2 - PerformanceTimingData. https://bugzilla.mozilla.org/show_bug.cgi?id=1462605 PerformanceNavigationTiming.name must be the value of the address of the current document. https://bugzilla.mozilla.org/show_bug.cgi?id=1462883 Update PerformanceTimingData::mReportCrossOriginRedirect in SetPropertiesFromHttpChannel. https://bugzilla.mozilla.org/show_bug.cgi?id=1462879 PerformanceNavigationTiming must be notified correctly - part 1 - notify. (373fb9ae)
- Issue #2282 - Part 2 - Re-add some timer clamping that got clobber erroneously. (bb656856)
2023-10-21 12:54:28 +08:00

401 lines
11 KiB
C++

/* -*- 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 "PerformanceMainThread.h"
#include "PerformanceNavigation.h"
#include "nsICacheInfoChannel.h"
namespace mozilla {
namespace dom {
namespace {
void
GetURLSpecFromChannel(nsITimedChannel* aChannel, nsAString& aSpec)
{
aSpec.AssignLiteral("document");
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aChannel);
if (!channel) {
return;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = channel->GetURI(getter_AddRefs(uri));
if (NS_WARN_IF(NS_FAILED(rv)) || !uri) {
return;
}
nsAutoCString spec;
rv = uri->GetSpec(spec);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
aSpec = NS_ConvertUTF8toUTF16(spec);
}
} // anonymous
NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread,
Performance)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming,
mNavigation,
mDocEntry)
#ifdef MOZ_DEVTOOLS_SERVER
tmp->mMozMemory = nullptr;
mozilla::DropJSObjects(this);
#endif
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMainThread,
Performance)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTiming,
mNavigation,
mDocEntry)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMainThread,
Performance)
#ifdef MOZ_DEVTOOLS_SERVER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMozMemory)
#endif
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_ADDREF_INHERITED(PerformanceMainThread, Performance)
NS_IMPL_RELEASE_INHERITED(PerformanceMainThread, Performance)
// QueryInterface implementation for PerformanceMainThread
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceMainThread)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END_INHERITING(Performance)
PerformanceMainThread::PerformanceMainThread(nsPIDOMWindowInner* aWindow,
nsDOMNavigationTiming* aDOMTiming,
nsITimedChannel* aChannel)
: Performance(aWindow)
, mDOMTiming(aDOMTiming)
, mChannel(aChannel)
{
MOZ_ASSERT(aWindow, "Parent window object should be provided");
CreateNavigationTimingEntry();
}
PerformanceMainThread::~PerformanceMainThread()
{
#ifdef MOZ_DEVTOOLS_SERVER
mozilla::DropJSObjects(this);
#endif
}
#ifdef MOZ_DEVTOOLS_SERVER
void
PerformanceMainThread::GetMozMemory(JSContext *aCx,
JS::MutableHandle<JSObject*> aObj)
{
if (!mMozMemory) {
mMozMemory = js::gc::NewMemoryInfoObject(aCx);
if (mMozMemory) {
mozilla::HoldJSObjects(this);
}
}
aObj.set(mMozMemory);
}
#endif
PerformanceTiming*
PerformanceMainThread::Timing()
{
if (!mTiming) {
// For navigation timing, the third argument (an nsIHttpChannel) is null
// since the cross-domain redirect were already checked. The last argument
// (zero time) for performance.timing is the navigation start value.
mTiming = new PerformanceTiming(this, mChannel, nullptr,
mDOMTiming->GetNavigationStart());
}
return mTiming;
}
void
PerformanceMainThread::DispatchBufferFullEvent()
{
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
// it bubbles, and it isn't cancelable
event->InitEvent(NS_LITERAL_STRING("resourcetimingbufferfull"), true, false);
event->SetTrusted(true);
DispatchDOMEvent(nullptr, event, nullptr, nullptr);
}
PerformanceNavigation*
PerformanceMainThread::Navigation()
{
if (!mNavigation) {
mNavigation = new PerformanceNavigation(this);
}
return mNavigation;
}
/**
* An entry should be added only after the resource is loaded.
* This method is not thread safe and can only be called on the main thread.
*/
void
PerformanceMainThread::AddEntry(nsIHttpChannel* channel,
nsITimedChannel* timedChannel)
{
MOZ_ASSERT(NS_IsMainThread());
// Check if resource timing is prefed off.
if (!nsContentUtils::IsResourceTimingEnabled()) {
return;
}
// Don't add the entry if the buffer is full
if (IsResourceEntryLimitReached()) {
return;
}
if (channel && timedChannel) {
nsAutoCString name;
nsAutoString initiatorType;
nsCOMPtr<nsIURI> originalURI;
timedChannel->GetInitiatorType(initiatorType);
// According to the spec, "The name attribute must return the resolved URL
// of the requested resource. This attribute must not change even if the
// fetch redirected to a different URL."
channel->GetOriginalURI(getter_AddRefs(originalURI));
originalURI->GetSpec(name);
NS_ConvertUTF8toUTF16 entryName(name);
// The nsITimedChannel argument will be used to gather all the timings.
// The nsIHttpChannel argument will be used to check if any cross-origin
// redirects occurred.
// The last argument is the "zero time" (offset). Since we don't want
// any offset for the resource timing, this will be set to "0" - the
// resource timing returns a relative timing (no offset).
UniquePtr<PerformanceTimingData> performanceTimingData(
new PerformanceTimingData(timedChannel, channel, 0));
// The PerformanceResourceTiming object will use the PerformanceTiming
// object to get all the required timings.
RefPtr<PerformanceResourceTiming> performanceEntry =
new PerformanceResourceTiming(std::move(performanceTimingData), this, entryName);
// If the initiator type had no valid value, then set it to the default
// ("other") value.
if (initiatorType.IsEmpty()) {
initiatorType = NS_LITERAL_STRING("other");
}
performanceEntry->SetInitiatorType(initiatorType);
InsertResourceEntry(performanceEntry);
}
}
DOMHighResTimeStamp
PerformanceMainThread::GetPerformanceTimingFromString(const nsAString& aProperty)
{
if (!IsPerformanceTimingAttribute(aProperty)) {
return 0;
}
if (aProperty.EqualsLiteral("navigationStart")) {
// DOMHighResTimeStamp is in relation to navigationStart, so this will be
// zero.
return GetDOMTiming()->GetNavigationStart();
}
if (aProperty.EqualsLiteral("unloadEventStart")) {
return GetDOMTiming()->GetUnloadEventStart();
}
if (aProperty.EqualsLiteral("unloadEventEnd")) {
return GetDOMTiming()->GetUnloadEventEnd();
}
if (aProperty.EqualsLiteral("redirectStart")) {
return Timing()->RedirectStart();
}
if (aProperty.EqualsLiteral("redirectEnd")) {
return Timing()->RedirectEnd();
}
if (aProperty.EqualsLiteral("fetchStart")) {
return Timing()->FetchStart();
}
if (aProperty.EqualsLiteral("domainLookupStart")) {
return Timing()->DomainLookupStart();
}
if (aProperty.EqualsLiteral("domainLookupEnd")) {
return Timing()->DomainLookupEnd();
}
if (aProperty.EqualsLiteral("connectStart")) {
return Timing()->ConnectStart();
}
if (aProperty.EqualsLiteral("secureConnectionStart")) {
return Timing()->SecureConnectionStart();
}
if (aProperty.EqualsLiteral("connectEnd")) {
return Timing()->ConnectEnd();
}
if (aProperty.EqualsLiteral("requestStart")) {
return Timing()->RequestStart();
}
if (aProperty.EqualsLiteral("responseStart")) {
return Timing()->ResponseStart();
}
if (aProperty.EqualsLiteral("responseEnd")) {
return Timing()->ResponseEnd();
}
if (aProperty.EqualsLiteral("domLoading")) {
return GetDOMTiming()->GetDomLoading();
}
if (aProperty.EqualsLiteral("domInteractive")) {
return GetDOMTiming()->GetDomInteractive();
}
if (aProperty.EqualsLiteral("domContentLoadedEventStart")) {
return GetDOMTiming()->GetDomContentLoadedEventStart();
}
if (aProperty.EqualsLiteral("domContentLoadedEventEnd")) {
return GetDOMTiming()->GetDomContentLoadedEventEnd();
}
if (aProperty.EqualsLiteral("domComplete")) {
return GetDOMTiming()->GetDomComplete();
}
if (aProperty.EqualsLiteral("loadEventStart")) {
return GetDOMTiming()->GetLoadEventStart();
}
if (aProperty.EqualsLiteral("loadEventEnd")) {
return GetDOMTiming()->GetLoadEventEnd();
}
MOZ_CRASH("IsPerformanceTimingAttribute and GetPerformanceTimingFromString are out of sync");
return 0;
}
void
PerformanceMainThread::InsertUserEntry(PerformanceEntry* aEntry)
{
MOZ_ASSERT(NS_IsMainThread());
nsAutoCString uri;
uint64_t markCreationEpoch = 0;
if (nsContentUtils::IsUserTimingLoggingEnabled() ||
nsContentUtils::SendPerformanceTimingNotifications()) {
nsresult rv = NS_ERROR_FAILURE;
nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
if (owner && owner->GetDocumentURI()) {
rv = owner->GetDocumentURI()->GetHost(uri);
}
if(NS_FAILED(rv)) {
// If we have no URI, just put in "none".
uri.AssignLiteral("none");
}
markCreationEpoch = static_cast<uint64_t>(PR_Now() / PR_USEC_PER_MSEC);
if (nsContentUtils::IsUserTimingLoggingEnabled()) {
Performance::LogEntry(aEntry, uri);
}
}
if (nsContentUtils::SendPerformanceTimingNotifications()) {
TimingNotification(aEntry, uri, markCreationEpoch);
}
Performance::InsertUserEntry(aEntry);
}
TimeStamp
PerformanceMainThread::CreationTimeStamp() const
{
return GetDOMTiming()->GetNavigationStartTimeStamp();
}
DOMHighResTimeStamp
PerformanceMainThread::CreationTime() const
{
return GetDOMTiming()->GetNavigationStart();
}
void
PerformanceMainThread::CreateNavigationTimingEntry()
{
MOZ_ASSERT(!mDocEntry, "mDocEntry should be null.");
nsAutoString name;
GetURLSpecFromChannel(mChannel, name);
UniquePtr<PerformanceTimingData> timing(
new PerformanceTimingData(mChannel, nullptr, 0));
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
if (httpChannel) {
timing->SetPropertiesFromHttpChannel(httpChannel, mChannel);
}
mDocEntry = new PerformanceNavigationTiming(std::move(timing), this, name);
}
void
PerformanceMainThread::QueueNavigationTimingEntry()
{
if (mDocEntry) {
QueueEntry(mDocEntry);
}
}
void
PerformanceMainThread::GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval)
{
aRetval = mResourceEntries;
aRetval.AppendElements(mUserEntries);
if (mDocEntry) {
aRetval.AppendElement(mDocEntry);
}
aRetval.Sort(PerformanceEntryComparator());
}
void
PerformanceMainThread::GetEntriesByType(const nsAString& aEntryType,
nsTArray<RefPtr<PerformanceEntry>>& aRetval)
{
if (aEntryType.EqualsLiteral("navigation")) {
aRetval.Clear();
if (mDocEntry) {
aRetval.AppendElement(mDocEntry);
}
return;
}
Performance::GetEntriesByType(aEntryType, aRetval);
}
void
PerformanceMainThread::GetEntriesByName(const nsAString& aName,
const Optional<nsAString>& aEntryType,
nsTArray<RefPtr<PerformanceEntry>>& aRetval)
{
if (aName.EqualsLiteral("document")) {
aRetval.Clear();
if (mDocEntry) {
aRetval.AppendElement(mDocEntry);
}
return;
}
Performance::GetEntriesByName(aName, aEntryType, aRetval);
}
} // dom namespace
} // mozilla namespace