Bug 1518999 - Implement PerformancePaintTiming for FirstContentfulPaint r=smaug,mstange

Spec: https://w3c.github.io/paint-timing/#sec-PerformancePaintTiming
We only support FirstContentfulPaint at the moment.

Differential Revision: https://phabricator.services.mozilla.com/D66463
This commit is contained in:
Sean Feng 2020-10-23 19:25:36 +00:00
Родитель 6bfb9413b5
Коммит 981acf390f
23 изменённых файлов: 373 добавлений и 10 удалений

Просмотреть файл

@ -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,

Просмотреть файл

@ -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<PerformanceTimingData> 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)) {

Просмотреть файл

@ -38,6 +38,7 @@ class PerformanceMainThread final : public Performance,
void AddRawEntry(UniquePtr<PerformanceTimingData>,
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<nsITimedChannel> mChannel;
RefPtr<PerformanceTiming> mTiming;
RefPtr<PerformanceNavigation> mNavigation;
RefPtr<PerformancePaintTiming> mFCPTiming;
JS::Heap<JSObject*> mMozMemory;
const bool mCrossOriginIsolated;

Просмотреть файл

@ -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(

Просмотреть файл

@ -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<JSObject*> 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);
}

Просмотреть файл

@ -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<JSObject*> aGivenProto) override;
DOMHighResTimeStamp StartTime() const override;
size_t SizeOfIncludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const override;
private:
~PerformancePaintTiming();
RefPtr<Performance> mPerformance;
const TimeStamp mStartTime;
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_PerformanacePaintTiming_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;

Просмотреть файл

@ -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',

Двоичные данные
dom/performance/tests/logo.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 21 KiB

Просмотреть файл

@ -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]

Просмотреть файл

@ -0,0 +1,40 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1518999
-->
<head>
<title>Test for Bug 1518999 (Observer API) </title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1518999">Mozilla
Bug 1518999 - Paint Timing API For Observers</a>
<p id="display"></p>
<div id="content" style="display: none">
<pre id="test">
<script class="testbody" type="text/javascript">
let tab;
function runTest() {
tab = window.open("test_performance_paint_observer_helper.html");
}
function done() {
tab.close();
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(runTest);
</script>
</pre>
</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,35 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE html>
<html>
<body>
</body>
<script>
var promise = new Promise(resolve => {
var observer = new PerformanceObserver(list => resolve(list));
observer.observe({entryTypes: ["paint"]});
});
promise.then(list => {
var perfEntries = list.getEntries();
opener.is(list.getEntries().length, 1);
opener.isDeeply(list.getEntries(),
performance.getEntriesByType("paint"),
"Observed 'paint' entries should equal to entries obtained by getEntriesByType.");
opener.isDeeply(list.getEntries({name: "paint"}),
performance.getEntriesByName("paint"),
"getEntries with name filter should return correct results.");
opener.isDeeply(list.getEntries({entryType: "paint"}),
performance.getEntriesByType("paint"),
"getEntries with entryType filter should return correct results.");
opener.done();
});
const img = document.createElement("IMG");
img.src = "http://example.org/tests/dom/performance/tests/logo.png";
document.body.appendChild(img);
</script>
</html>

Просмотреть файл

@ -0,0 +1,38 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1518999
-->
<head>
<title>Test for Bug 1518999</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1518999">Mozilla
Bug 1518999 - Paint Timing API</a>
<p id="display"></p>
<div id="content" style="display: none">
<pre id="test">
<script class="testbody" type="text/javascript">
let tab;
function runTest() {
tab = window.open("test_performance_paint_timing_helper.html");
}
function done() {
tab.close();
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(runTest);
</script>
</pre>
</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,65 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1518999
-->
<head>
<title>Test for Bug 1518999</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<div id="main"></div>
<div id="image"></div>
<div id="test">
<script class="testbody" type="text/javascript">
async function runTest() {
const paintEntries = performance.getEntriesByType('paint');
opener.is(paintEntries.length, 0, "No paint entries yet");
const img = document.createElement("img");
img.src = "http://example.org/tests/dom/performance/tests/logo.png";
img.onload = function() {
function getAndTestEntries(runCount) {
function testEntries(entries) {
opener.is(entries.length, 1, "FCP Only returns");
opener.is(entries[0].entryType, "paint", "entryType is paint");
opener.is(entries[0].name, "first-contentful-paint",
"Returned entry should be first-contentful-paint" );
const fcpEntriesGotByName =
performance.getEntriesByName('first-contentful-paint');
opener.is(fcpEntriesGotByName.length, 1, "entries length should match");
opener.is(entries[0], fcpEntriesGotByName[0], "should be the same entry");
opener.done();
}
const entries = performance.getEntriesByType('paint');
if (entries.length < 1) {
if (runCount < 4) {
opener.SimpleTest.requestFlakyTimeout("FCP is being registered asynchronously, so wait a bit of time");
setTimeout(function() {
getAndTestEntries(runCount + 1);
}, 20);
} else {
opener.ok(false, "Unable to find paint entries within a reasonable amount of time");
opener.done();
}
} else {
testEntries(entries);
}
}
getAndTestEntries(1);
}
document.body.appendChild(img);
}
window.onload = function() {
runTest();
}
</script>
</div>
</div>
</body>
</html>

Просмотреть файл

@ -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 },

Просмотреть файл

@ -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
{
};

Просмотреть файл

@ -753,6 +753,7 @@ WEBIDL_FILES = [
'PerformanceNavigationTiming.webidl',
'PerformanceObserver.webidl',
'PerformanceObserverEntryList.webidl',
'PerformancePaintTiming.webidl',
'PerformanceResourceTiming.webidl',
'PerformanceServerTiming.webidl',
'PerformanceTiming.webidl',

Просмотреть файл

@ -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),

Просмотреть файл

@ -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;

Просмотреть файл

@ -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;
}
}

Просмотреть файл

@ -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<nsVideoFrame*>(Frame());
HTMLVideoElement* video = HTMLVideoElement::FromNode(f->GetContent());
return video->VideoWidth() > 0;
}
};
void nsVideoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,

Просмотреть файл

@ -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;
}
}

Просмотреть файл

@ -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;