diff --git a/dom/performance/Performance.h b/dom/performance/Performance.h index 26c99c8cf2c2..87f497079daf 100644 --- a/dom/performance/Performance.h +++ b/dom/performance/Performance.h @@ -22,6 +22,7 @@ namespace dom { class PerformanceEntry; class PerformanceNavigation; +class PerformancePaintTiming; class PerformanceObserver; class PerformanceService; class PerformanceStorage; @@ -85,6 +86,8 @@ class Performance : public DOMEventTargetHelper { virtual PerformanceNavigation* Navigation() = 0; + virtual void SetFCPTimingEntry(PerformancePaintTiming* aEntry) = 0; + IMPL_EVENT_HANDLER(resourcetimingbufferfull) virtual void GetMozMemory(JSContext* aCx, diff --git a/dom/performance/PerformanceMainThread.cpp b/dom/performance/PerformanceMainThread.cpp index ec9a337e5f3b..60427ed1cfa9 100644 --- a/dom/performance/PerformanceMainThread.cpp +++ b/dom/performance/PerformanceMainThread.cpp @@ -6,6 +6,7 @@ #include "PerformanceMainThread.h" #include "PerformanceNavigation.h" +#include "PerformancePaintTiming.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_privacy.h" @@ -43,14 +44,14 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread, Performance) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming, mNavigation, mDocEntry) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming, mNavigation, mDocEntry, mFCPTiming) tmp->mMozMemory = nullptr; mozilla::DropJSObjects(this); 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(mTiming, mNavigation, mDocEntry, mFCPTiming) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMainThread, @@ -161,6 +162,14 @@ void PerformanceMainThread::AddRawEntry(UniquePtr aData, InsertResourceEntry(entry); } +void PerformanceMainThread::SetFCPTimingEntry(PerformancePaintTiming* aEntry) { + MOZ_ASSERT(aEntry); + if (!mFCPTiming) { + mFCPTiming = aEntry; + QueueEntry(aEntry); + } +} + // To be removed once bug 1124165 lands bool PerformanceMainThread::IsPerformanceTimingAttribute( const nsAString& aName) { @@ -376,6 +385,9 @@ void PerformanceMainThread::GetEntries( aRetval.AppendElement(mDocEntry); } + if (mFCPTiming) { + aRetval.AppendElement(mFCPTiming); + } aRetval.Sort(PerformanceEntryComparator()); } @@ -396,6 +408,13 @@ void PerformanceMainThread::GetEntriesByType( return; } + if (aEntryType.EqualsLiteral("paint")) { + if (mFCPTiming) { + aRetval.AppendElement(mFCPTiming); + return; + } + } + Performance::GetEntriesByType(aEntryType, aRetval); } @@ -410,6 +429,13 @@ void PerformanceMainThread::GetEntriesByName( Performance::GetEntriesByName(aName, aEntryType, aRetval); + if (mFCPTiming && mFCPTiming->GetName().Equals(aName) && + (!aEntryType.WasPassed() || + mFCPTiming->GetEntryType().Equals(aEntryType.Value()))) { + aRetval.AppendElement(mFCPTiming); + return; + } + // The navigation entry is the first one. If it exists and the name matches, // let put it in front. if (mDocEntry && mDocEntry->GetName().Equals(aName)) { diff --git a/dom/performance/PerformanceMainThread.h b/dom/performance/PerformanceMainThread.h index 4199d2c64681..708597c393b8 100644 --- a/dom/performance/PerformanceMainThread.h +++ b/dom/performance/PerformanceMainThread.h @@ -38,6 +38,7 @@ class PerformanceMainThread final : public Performance, void AddRawEntry(UniquePtr, const nsAString& aInitiatorType, const nsAString& aEntryName); + virtual void SetFCPTimingEntry(PerformancePaintTiming* aEntry) override; TimeStamp CreationTimeStamp() const override; @@ -90,6 +91,7 @@ class PerformanceMainThread final : public Performance, nsCOMPtr mChannel; RefPtr mTiming; RefPtr mNavigation; + RefPtr mFCPTiming; JS::Heap mMozMemory; const bool mCrossOriginIsolated; diff --git a/dom/performance/PerformanceObserver.cpp b/dom/performance/PerformanceObserver.cpp index 6c521d039466..3d8de3b82ab0 100644 --- a/dom/performance/PerformanceObserver.cpp +++ b/dom/performance/PerformanceObserver.cpp @@ -132,11 +132,8 @@ void PerformanceObserver::QueueEntry(PerformanceEntry* aEntry) { * Keep this list in alphabetical order. * https://w3c.github.io/performance-timeline/#supportedentrytypes-attribute */ -static const char16_t* const sValidTypeNames[4] = { - u"mark", - u"measure", - u"navigation", - u"resource", +static const char16_t* const sValidTypeNames[5] = { + u"mark", u"measure", u"navigation", u"paint", u"resource", }; void PerformanceObserver::ReportUnsupportedTypesErrorToConsole( diff --git a/dom/performance/PerformancePaintTiming.cpp b/dom/performance/PerformancePaintTiming.cpp new file mode 100644 index 000000000000..c2062f060c1c --- /dev/null +++ b/dom/performance/PerformancePaintTiming.cpp @@ -0,0 +1,47 @@ +/* -*- 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 "PerformancePaintTiming.h" +#include "MainThreadUtils.h" +#include "mozilla/dom/PerformanceMeasureBinding.h" + +using namespace mozilla::dom; + +NS_IMPL_CYCLE_COLLECTION_INHERITED(PerformancePaintTiming, PerformanceEntry, + mPerformance) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformancePaintTiming) +NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry) + +NS_IMPL_ADDREF_INHERITED(PerformancePaintTiming, PerformanceEntry) +NS_IMPL_RELEASE_INHERITED(PerformancePaintTiming, PerformanceEntry) + +PerformancePaintTiming::PerformancePaintTiming(Performance* aPerformance, + const nsAString& aName, + const TimeStamp& aStartTime) + : PerformanceEntry(aPerformance->GetParentObject(), aName, u"paint"_ns), + mPerformance(aPerformance), + mStartTime(aStartTime) {} + +PerformancePaintTiming::~PerformancePaintTiming() = default; + +JSObject* PerformancePaintTiming::WrapObject( + JSContext* aCx, JS::Handle aGivenProto) { + return PerformancePaintTiming_Binding::Wrap(aCx, this, aGivenProto); +} + +DOMHighResTimeStamp PerformancePaintTiming::StartTime() const { + DOMHighResTimeStamp rawValue = + mPerformance->GetDOMTiming()->TimeStampToDOMHighRes(mStartTime); + return nsRFPService::ReduceTimePrecisionAsMSecs( + rawValue, mPerformance->GetRandomTimelineSeed(), + mPerformance->IsSystemPrincipal(), mPerformance->CrossOriginIsolated()); +} + +size_t PerformancePaintTiming::SizeOfIncludingThis( + mozilla::MallocSizeOf aMallocSizeOf) const { + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); +} diff --git a/dom/performance/PerformancePaintTiming.h b/dom/performance/PerformancePaintTiming.h new file mode 100644 index 000000000000..544af14cd37a --- /dev/null +++ b/dom/performance/PerformancePaintTiming.h @@ -0,0 +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_dom_PerformancePaintTiming_h___ +#define mozilla_dom_PerformancePaintTiming_h___ + +#include "mozilla/dom/PerformanceEntry.h" +#include "mozilla/dom/PerformancePaintTimingBinding.h" + +namespace mozilla { +namespace dom { + +// https://w3c.github.io/paint-timing/#sec-PerformancePaintTiming +// Unlike timeToContentfulPaint, this timing is generated during +// displaylist building, when a frame is contentful, we collect +// the timestamp. Whereas, timeToContentfulPaint uses a compositor-side +// timestamp. +class PerformancePaintTiming final : public PerformanceEntry { + public: + PerformancePaintTiming(Performance* aPerformance, const nsAString& aName, + const TimeStamp& aStartTime); + + NS_DECL_ISUPPORTS_INHERITED + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PerformancePaintTiming, + PerformanceEntry) + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + DOMHighResTimeStamp StartTime() const override; + + size_t SizeOfIncludingThis( + mozilla::MallocSizeOf aMallocSizeOf) const override; + + private: + ~PerformancePaintTiming(); + RefPtr mPerformance; + const TimeStamp mStartTime; +}; + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_PerformanacePaintTiming_h___ */ diff --git a/dom/performance/PerformanceWorker.h b/dom/performance/PerformanceWorker.h index dc2ac18469a2..99f7fdaa25e4 100644 --- a/dom/performance/PerformanceWorker.h +++ b/dom/performance/PerformanceWorker.h @@ -33,6 +33,10 @@ class PerformanceWorker final : public Performance { return nullptr; } + virtual void SetFCPTimingEntry(PerformancePaintTiming* aEntry) override { + MOZ_CRASH("This should not be called on workers."); + } + TimeStamp CreationTimeStamp() const override; DOMHighResTimeStamp CreationTime() const override; diff --git a/dom/performance/moz.build b/dom/performance/moz.build index df7f7dc72fc2..f5019df3f9b2 100644 --- a/dom/performance/moz.build +++ b/dom/performance/moz.build @@ -17,6 +17,7 @@ EXPORTS.mozilla.dom += [ 'PerformanceNavigationTiming.h', 'PerformanceObserver.h', 'PerformanceObserverEntryList.h', + 'PerformancePaintTiming.h', 'PerformanceResourceTiming.h', 'PerformanceServerTiming.h', 'PerformanceService.h', @@ -35,6 +36,7 @@ UNIFIED_SOURCES += [ 'PerformanceNavigationTiming.cpp', 'PerformanceObserver.cpp', 'PerformanceObserverEntryList.cpp', + 'PerformancePaintTiming.cpp', 'PerformanceResourceTiming.cpp', 'PerformanceServerTiming.cpp', 'PerformanceService.cpp', diff --git a/dom/performance/tests/logo.png b/dom/performance/tests/logo.png new file mode 100644 index 000000000000..a05926bcd714 Binary files /dev/null and b/dom/performance/tests/logo.png differ diff --git a/dom/performance/tests/mochitest.ini b/dom/performance/tests/mochitest.ini index 72c6a3268bcc..a43158f77159 100644 --- a/dom/performance/tests/mochitest.ini +++ b/dom/performance/tests/mochitest.ini @@ -14,6 +14,14 @@ support-files = [test_performance_observer.html] [test_performance_user_timing.html] [test_performance_navigation_timing.html] +[test_performance_paint_timing.html] +support-files = + test_performance_paint_timing_helper.html + logo.png +[test_performance_paint_observer.html] +support-files = + test_performance_paint_observer_helper.html + logo.png [test_worker_user_timing.html] [test_worker_observer.html] [test_sharedWorker_performance_user_timing.html] diff --git a/dom/performance/tests/test_performance_paint_observer.html b/dom/performance/tests/test_performance_paint_observer.html new file mode 100644 index 000000000000..2ded1db7975c --- /dev/null +++ b/dom/performance/tests/test_performance_paint_observer.html @@ -0,0 +1,40 @@ + + + + + + Test for Bug 1518999 (Observer API) + + + + + + Mozilla + Bug 1518999 - Paint Timing API For Observers +

+ + + diff --git a/dom/performance/tests/test_performance_paint_observer_helper.html b/dom/performance/tests/test_performance_paint_observer_helper.html new file mode 100644 index 000000000000..ae27c9480dc9 --- /dev/null +++ b/dom/performance/tests/test_performance_paint_observer_helper.html @@ -0,0 +1,35 @@ + + + + + + + diff --git a/dom/performance/tests/test_performance_paint_timing.html b/dom/performance/tests/test_performance_paint_timing.html new file mode 100644 index 000000000000..f8784ecf260f --- /dev/null +++ b/dom/performance/tests/test_performance_paint_timing.html @@ -0,0 +1,38 @@ + + + + + + Test for Bug 1518999 + + + + + + Mozilla + Bug 1518999 - Paint Timing API +

+ + + diff --git a/dom/performance/tests/test_performance_paint_timing_helper.html b/dom/performance/tests/test_performance_paint_timing_helper.html new file mode 100644 index 000000000000..c05b38cac043 --- /dev/null +++ b/dom/performance/tests/test_performance_paint_timing_helper.html @@ -0,0 +1,65 @@ + + + + + + Test for Bug 1518999 + + + +
+
+
+ +
+ + + diff --git a/dom/tests/mochitest/general/test_interfaces.js b/dom/tests/mochitest/general/test_interfaces.js index 2d09a426590f..98d23cf56544 100644 --- a/dom/tests/mochitest/general/test_interfaces.js +++ b/dom/tests/mochitest/general/test_interfaces.js @@ -857,6 +857,8 @@ var interfaceNamesInGlobalScope = [ // IMPORTANT: Do not change this list without review from a DOM peer! { name: "PerformanceObserverEntryList", insecureContext: true }, // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "PerformancePaintTiming", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! { name: "PerformanceResourceTiming", insecureContext: true }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "PerformanceServerTiming", insecureContext: false }, diff --git a/dom/webidl/PerformancePaintTiming.webidl b/dom/webidl/PerformancePaintTiming.webidl new file mode 100644 index 000000000000..6551bdfd8d8d --- /dev/null +++ b/dom/webidl/PerformancePaintTiming.webidl @@ -0,0 +1,16 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + * + * The origin of this IDL file is + * https://w3c.github.io/paint-timing/#sec-PerformancePaintTiming + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +[Exposed=(Window)] +interface PerformancePaintTiming : PerformanceEntry +{ +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 8b2b2741d286..2434fdf07863 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -753,6 +753,7 @@ WEBIDL_FILES = [ 'PerformanceNavigationTiming.webidl', 'PerformanceObserver.webidl', 'PerformanceObserverEntryList.webidl', + 'PerformancePaintTiming.webidl', 'PerformanceResourceTiming.webidl', 'PerformanceServerTiming.webidl', 'PerformanceTiming.webidl', diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index bd7192600426..a85e9ea6007b 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -90,6 +90,7 @@ #include "mozilla/Telemetry.h" #include "mozilla/dom/Performance.h" #include "mozilla/dom/PerformanceTiming.h" +#include "mozilla/dom/PerformancePaintTiming.h" #include "mozilla/layers/APZThreadUtils.h" #include "MobileViewportManager.h" #include "mozilla/dom/ImageTracker.h" @@ -190,6 +191,7 @@ nsPresContext::nsPresContext(dom::Document* aDocument, nsPresContextType aType) mInflationDisabledForShrinkWrap(false), mInteractionTimeEnabled(true), mHasPendingInterrupt(false), + mHasEverBuiltInvisibleText(false), mPendingInterruptFromTest(false), mInterruptsEnabled(false), mSendAfterPaintToContent(false), diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index 9c2cdea5ea16..3fc23cf47865 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -1024,6 +1024,9 @@ class nsPresContext : public nsISupports, public mozilla::SupportsWeakPtr { void NotifyPaintStatusReset(); void NotifyDOMContentFlushed(); + bool HasEverBuiltInvisibleText() const { return mHasEverBuiltInvisibleText; } + void SetBuiltInvisibleText() { mHasEverBuiltInvisibleText = true; } + bool UsesExChUnits() const { return mUsesExChUnits; } void SetUsesExChUnits(bool aValue) { mUsesExChUnits = aValue; } @@ -1266,6 +1269,7 @@ class nsPresContext : public nsISupports, public mozilla::SupportsWeakPtr { protected: unsigned mInteractionTimeEnabled : 1; unsigned mHasPendingInterrupt : 1; + unsigned mHasEverBuiltInvisibleText : 1; unsigned mPendingInterruptFromTest : 1; unsigned mInterruptsEnabled : 1; unsigned mSendAfterPaintToContent : 1; diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index a26fee7ba269..bc5e0cf65016 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -4906,6 +4906,9 @@ void nsTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, TextDecorations textDecs; GetTextDecorations(PresContext(), eResolvedColors, textDecs); if (!textDecs.HasDecorationLines()) { + if (auto* currentPresContext = aBuilder->CurrentPresContext()) { + currentPresContext->SetBuiltInvisibleText(); + } return; } } diff --git a/layout/generic/nsVideoFrame.cpp b/layout/generic/nsVideoFrame.cpp index 4b0d49a80cc6..9bbd06c8ca05 100644 --- a/layout/generic/nsVideoFrame.cpp +++ b/layout/generic/nsVideoFrame.cpp @@ -508,6 +508,13 @@ class nsDisplayVideo : public nsPaintedDisplayItem { return elem->IsPotentiallyPlaying() ? LayerState::LAYER_ACTIVE_FORCE : LayerState::LAYER_INACTIVE; } + + // Only report FirstContentfulPaint when the video is set + bool IsContentful() const override { + nsVideoFrame* f = static_cast(Frame()); + HTMLVideoElement* video = HTMLVideoElement::FromNode(f->GetContent()); + return video->VideoWidth() > 0; + } }; void nsVideoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index ffe828a37805..fd88a0841189 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -1107,7 +1107,8 @@ static bool DisplayListIsNonBlank(nsDisplayList* aList) { // non-white canvas or SVG. This excludes any content of iframes, but // includes text with pending webfonts. This is the first time users // could start consuming page content." -static bool DisplayListIsContentful(nsDisplayList* aList) { +static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder, + nsDisplayList* aList) { for (nsDisplayItem* i : *aList) { DisplayItemType type = i->GetType(); nsDisplayList* children = i->GetChildren(); @@ -1119,10 +1120,14 @@ static bool DisplayListIsContentful(nsDisplayList* aList) { // actually tracking all modifications) default: if (i->IsContentful()) { - return true; + bool dummy; + nsRect bound = i->GetBounds(aBuilder, &dummy); + if (!bound.IsEmpty()) { + return true; + } } if (children) { - if (DisplayListIsContentful(children)) { + if (DisplayListIsContentful(aBuilder, children)) { return true; } } diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 0b9065623c5f..aed7f2457377 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -4651,6 +4651,14 @@ class nsDisplayBackgroundImage : public nsDisplayImageContainer { nsDisplayImageContainer::RemoveFrame(aFrame); } + // Match https://w3c.github.io/paint-timing/#contentful-image + bool IsContentful() const override { + const auto& styleImage = + mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mImage; + + return styleImage.IsSizeAvailable() && styleImage.IsUrl(); + } + protected: typedef class mozilla::layers::ImageContainer ImageContainer; typedef class mozilla::layers::ImageLayer ImageLayer;