Commit 6222cc8d authored by Nicolás Peña Moreno's avatar Nicolás Peña Moreno Committed by Commit Bot

[ElementTiming] Text support part 2

This CL allows dispatching text Element Timing entries. The
TextElementTiming attribute in TextRecordsManager cannot be set on the
constructor of TextPaintTimingDetector because it is constructed too
early, upon construction of LocalFrameView. The intersectionRect is not
supported yet because TextRecord does not include that information.

Bug: 942033
Change-Id: I65001c25d5ba874456a7bdea4cc26df8d059ee63
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1639107
Commit-Queue: Nicolás Peña Moreno <npm@chromium.org>
Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#666397}
parent 4689439b
...@@ -32,6 +32,11 @@ constexpr const unsigned kInlineImageMaxChars = 100u; ...@@ -32,6 +32,11 @@ constexpr const unsigned kInlineImageMaxChars = 100u;
// static // static
const char ImageElementTiming::kSupplementName[] = "ImageElementTiming"; const char ImageElementTiming::kSupplementName[] = "ImageElementTiming";
AtomicString ImagePaintString() {
DEFINE_STATIC_LOCAL(const AtomicString, kImagePaint, ("image-paint"));
return kImagePaint;
}
// static // static
ImageElementTiming& ImageElementTiming::From(LocalDOMWindow& window) { ImageElementTiming& ImageElementTiming::From(LocalDOMWindow& window) {
ImageElementTiming* timing = ImageElementTiming* timing =
...@@ -109,7 +114,7 @@ void ImageElementTiming::NotifyImagePaintedInternal( ...@@ -109,7 +114,7 @@ void ImageElementTiming::NotifyImagePaintedInternal(
performance->ShouldBufferEntries())) { performance->ShouldBufferEntries())) {
// Create an entry with a |startTime| of 0. // Create an entry with a |startTime| of 0.
performance->AddElementTiming( performance->AddElementTiming(
url.GetString(), intersection_rect, TimeTicks(), ImagePaintString(), url.GetString(), intersection_rect, TimeTicks(),
cached_image.LoadResponseEnd(), attr, cached_image.LoadResponseEnd(), attr,
cached_image.IntrinsicSize(kDoNotRespectImageOrientation), id, cached_image.IntrinsicSize(kDoNotRespectImageOrientation), id,
element); element);
...@@ -200,8 +205,8 @@ void ImageElementTiming::ReportImagePaintSwapTime(WebWidgetClient::SwapResult, ...@@ -200,8 +205,8 @@ void ImageElementTiming::ReportImagePaintSwapTime(WebWidgetClient::SwapResult,
performance->ShouldBufferEntries())) { performance->ShouldBufferEntries())) {
for (const auto& element_timing : element_timings_) { for (const auto& element_timing : element_timings_) {
performance->AddElementTiming( performance->AddElementTiming(
element_timing->url, element_timing->rect, timestamp, ImagePaintString(), element_timing->url, element_timing->rect,
element_timing->response_end, element_timing->identifier, timestamp, element_timing->response_end, element_timing->identifier,
element_timing->intrinsic_size, element_timing->id, element_timing->intrinsic_size, element_timing->id,
element_timing->element); element_timing->element);
} }
......
...@@ -5,7 +5,9 @@ ...@@ -5,7 +5,9 @@
#include "third_party/blink/renderer/core/paint/text_element_timing.h" #include "third_party/blink/renderer/core/paint/text_element_timing.h"
#include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/paint/text_paint_timing_detector.h" #include "third_party/blink/renderer/core/paint/text_paint_timing_detector.h"
#include "third_party/blink/renderer/core/timing/dom_window_performance.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink { namespace blink {
...@@ -25,18 +27,46 @@ TextElementTiming& TextElementTiming::From(LocalDOMWindow& window) { ...@@ -25,18 +27,46 @@ TextElementTiming& TextElementTiming::From(LocalDOMWindow& window) {
} }
TextElementTiming::TextElementTiming(LocalDOMWindow& window) TextElementTiming::TextElementTiming(LocalDOMWindow& window)
: Supplement<LocalDOMWindow>(window) { : Supplement<LocalDOMWindow>(window),
performance_(DOMWindowPerformance::performance(window)) {
DCHECK(RuntimeEnabledFeatures::ElementTimingEnabled( DCHECK(RuntimeEnabledFeatures::ElementTimingEnabled(
GetSupplementable()->document())); GetSupplementable()->document()));
} }
void TextElementTiming::OnTextNodesPainted( void TextElementTiming::OnTextNodesPainted(
const Deque<base::WeakPtr<TextRecord>>& text_nodes_painted) { const Deque<base::WeakPtr<TextRecord>>& text_nodes_painted) {
// TODO(npm): implement entry creation. DCHECK(performance_);
// If the entries cannot be exposed via PerformanceObserver nor added to the
// buffer, bail out.
if (!performance_->HasObserverFor(PerformanceEntry::kElement) &&
performance_->IsElementTimingBufferFull()) {
return;
}
for (const auto record : text_nodes_painted) {
Node* node = DOMNodeIds::NodeForId(record->node_id);
if (!node || node->IsInShadowTree())
continue;
// Text aggregators should be Elements!
DCHECK(node->IsElementNode());
Element* element = ToElement(node);
const AtomicString& attr =
element->FastGetAttribute(html_names::kElementtimingAttr);
if (attr.IsEmpty())
continue;
const AtomicString& id = element->GetIdAttribute();
DEFINE_STATIC_LOCAL(const AtomicString, kTextPaint, ("text-paint"));
// TODO(npm): Add the rect once these are stored in TextRecord.
performance_->AddElementTiming(kTextPaint, g_empty_string, FloatRect(),
record->paint_time, TimeTicks(), attr,
IntSize(), id, element);
}
} }
void TextElementTiming::Trace(blink::Visitor* visitor) { void TextElementTiming::Trace(blink::Visitor* visitor) {
Supplement<LocalDOMWindow>::Trace(visitor); Supplement<LocalDOMWindow>::Trace(visitor);
visitor->Trace(performance_);
} }
} // namespace blink } // namespace blink
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/timing/window_performance.h"
#include "third_party/blink/renderer/platform/supplementable.h" #include "third_party/blink/renderer/platform/supplementable.h"
#include "third_party/blink/renderer/platform/wtf/deque.h" #include "third_party/blink/renderer/platform/wtf/deque.h"
...@@ -34,6 +35,8 @@ class CORE_EXPORT TextElementTiming final ...@@ -34,6 +35,8 @@ class CORE_EXPORT TextElementTiming final
void Trace(blink::Visitor* visitor) override; void Trace(blink::Visitor* visitor) override;
Member<WindowPerformance> performance_;
DISALLOW_COPY_AND_ASSIGN(TextElementTiming); DISALLOW_COPY_AND_ASSIGN(TextElementTiming);
}; };
......
...@@ -44,14 +44,7 @@ TextPaintTimingDetector::TextPaintTimingDetector(LocalFrameView* frame_view) ...@@ -44,14 +44,7 @@ TextPaintTimingDetector::TextPaintTimingDetector(LocalFrameView* frame_view)
timer_(frame_view->GetFrame().GetTaskRunner(TaskType::kInternalDefault), timer_(frame_view->GetFrame().GetTaskRunner(TaskType::kInternalDefault),
this, this,
&TextPaintTimingDetector::TimerFired), &TextPaintTimingDetector::TimerFired),
frame_view_(frame_view) { frame_view_(frame_view) {}
Document* document = frame_view_->GetFrame().GetDocument();
if (document && RuntimeEnabledFeatures::ElementTimingEnabled(document)) {
LocalDOMWindow* window = document->domWindow();
if (window)
records_manager_.SetTextElementTiming(&TextElementTiming::From(*window));
}
}
void TextPaintTimingDetector::PopulateTraceValue( void TextPaintTimingDetector::PopulateTraceValue(
TracedValue& value, TracedValue& value,
...@@ -160,6 +153,16 @@ void TextPaintTimingDetector::RegisterNotifySwapTime( ...@@ -160,6 +153,16 @@ void TextPaintTimingDetector::RegisterNotifySwapTime(
void TextPaintTimingDetector::ReportSwapTime(WebWidgetClient::SwapResult result, void TextPaintTimingDetector::ReportSwapTime(WebWidgetClient::SwapResult result,
base::TimeTicks timestamp) { base::TimeTicks timestamp) {
if (!records_manager_.HasTextElementTiming()) {
Document* document = frame_view_->GetFrame().GetDocument();
if (document && RuntimeEnabledFeatures::ElementTimingEnabled(document)) {
LocalDOMWindow* window = document->domWindow();
if (window) {
records_manager_.SetTextElementTiming(
&TextElementTiming::From(*window));
}
}
}
records_manager_.AssignPaintTimeToQueuedNodes(timestamp); records_manager_.AssignPaintTimeToQueuedNodes(timestamp);
awaiting_swap_promise_ = false; awaiting_swap_promise_ = false;
} }
......
...@@ -79,6 +79,7 @@ class TextRecordsManager { ...@@ -79,6 +79,7 @@ class TextRecordsManager {
void StopRecordingLargestTextPaint(); void StopRecordingLargestTextPaint();
bool IsRecordingLargestTextPaint() const { return is_recording_ltp_; } bool IsRecordingLargestTextPaint() const { return is_recording_ltp_; }
bool HasTextElementTiming() const { return !!text_element_timing_; }
void SetTextElementTiming(TextElementTiming* text_element_timing) { void SetTextElementTiming(TextElementTiming* text_element_timing) {
text_element_timing_ = text_element_timing; text_element_timing_ = text_element_timing;
} }
...@@ -105,7 +106,7 @@ class TextRecordsManager { ...@@ -105,7 +106,7 @@ class TextRecordsManager {
TextRecordSet size_ordered_set_; TextRecordSet size_ordered_set_;
Deque<base::WeakPtr<TextRecord>> texts_queued_for_paint_time_; Deque<base::WeakPtr<TextRecord>> texts_queued_for_paint_time_;
TextRecord* cached_largest_paint_candidate_; TextRecord* cached_largest_paint_candidate_;
WeakMember<TextElementTiming> text_element_timing_; Member<TextElementTiming> text_element_timing_;
DISALLOW_COPY_AND_ASSIGN(TextRecordsManager); DISALLOW_COPY_AND_ASSIGN(TextRecordsManager);
}; };
......
...@@ -11,6 +11,7 @@ namespace blink { ...@@ -11,6 +11,7 @@ namespace blink {
// static // static
PerformanceElementTiming* PerformanceElementTiming::Create( PerformanceElementTiming* PerformanceElementTiming::Create(
const AtomicString& name,
const String& url, const String& url,
const FloatRect& intersection_rect, const FloatRect& intersection_rect,
DOMHighResTimeStamp start_time, DOMHighResTimeStamp start_time,
...@@ -26,11 +27,12 @@ PerformanceElementTiming* PerformanceElementTiming::Create( ...@@ -26,11 +27,12 @@ PerformanceElementTiming* PerformanceElementTiming::Create(
DCHECK_GE(naturalHeight, 0); DCHECK_GE(naturalHeight, 0);
DCHECK(element); DCHECK(element);
return MakeGarbageCollected<PerformanceElementTiming>( return MakeGarbageCollected<PerformanceElementTiming>(
url, intersection_rect, start_time, response_end, identifier, name, url, intersection_rect, start_time, response_end, identifier,
naturalWidth, naturalHeight, id, element); naturalWidth, naturalHeight, id, element);
} }
PerformanceElementTiming::PerformanceElementTiming( PerformanceElementTiming::PerformanceElementTiming(
const AtomicString& name,
const String& url, const String& url,
const FloatRect& intersection_rect, const FloatRect& intersection_rect,
DOMHighResTimeStamp start_time, DOMHighResTimeStamp start_time,
...@@ -40,7 +42,7 @@ PerformanceElementTiming::PerformanceElementTiming( ...@@ -40,7 +42,7 @@ PerformanceElementTiming::PerformanceElementTiming(
int naturalHeight, int naturalHeight,
const AtomicString& id, const AtomicString& id,
Element* element) Element* element)
: PerformanceEntry("image-paint", start_time, start_time), : PerformanceEntry(name, start_time, start_time),
element_(element), element_(element),
intersection_rect_(DOMRectReadOnly::FromFloatRect(intersection_rect)), intersection_rect_(DOMRectReadOnly::FromFloatRect(intersection_rect)),
response_end_(response_end), response_end_(response_end),
......
...@@ -21,7 +21,8 @@ class CORE_EXPORT PerformanceElementTiming final : public PerformanceEntry { ...@@ -21,7 +21,8 @@ class CORE_EXPORT PerformanceElementTiming final : public PerformanceEntry {
DEFINE_WRAPPERTYPEINFO(); DEFINE_WRAPPERTYPEINFO();
public: public:
static PerformanceElementTiming* Create(const String& url, static PerformanceElementTiming* Create(const AtomicString& name,
const String& url,
const FloatRect& intersection_rect, const FloatRect& intersection_rect,
DOMHighResTimeStamp start_time, DOMHighResTimeStamp start_time,
DOMHighResTimeStamp response_end, DOMHighResTimeStamp response_end,
...@@ -30,7 +31,8 @@ class CORE_EXPORT PerformanceElementTiming final : public PerformanceEntry { ...@@ -30,7 +31,8 @@ class CORE_EXPORT PerformanceElementTiming final : public PerformanceEntry {
int naturalHeight, int naturalHeight,
const AtomicString& id, const AtomicString& id,
Element*); Element*);
PerformanceElementTiming(const String& url, PerformanceElementTiming(const AtomicString& name,
const String& url,
const FloatRect& intersection_rect, const FloatRect& intersection_rect,
DOMHighResTimeStamp start_time, DOMHighResTimeStamp start_time,
DOMHighResTimeStamp response_end, DOMHighResTimeStamp response_end,
......
...@@ -392,7 +392,8 @@ void WindowPerformance::ReportEventTimings(WebWidgetClient::SwapResult result, ...@@ -392,7 +392,8 @@ void WindowPerformance::ReportEventTimings(WebWidgetClient::SwapResult result,
event_timings_.clear(); event_timings_.clear();
} }
void WindowPerformance::AddElementTiming(const String& url, void WindowPerformance::AddElementTiming(const AtomicString& name,
const String& url,
const FloatRect& rect, const FloatRect& rect,
TimeTicks start_time, TimeTicks start_time,
TimeTicks response_end, TimeTicks response_end,
...@@ -402,7 +403,7 @@ void WindowPerformance::AddElementTiming(const String& url, ...@@ -402,7 +403,7 @@ void WindowPerformance::AddElementTiming(const String& url,
Element* element) { Element* element) {
DCHECK(RuntimeEnabledFeatures::ElementTimingEnabled(GetExecutionContext())); DCHECK(RuntimeEnabledFeatures::ElementTimingEnabled(GetExecutionContext()));
PerformanceElementTiming* entry = PerformanceElementTiming::Create( PerformanceElementTiming* entry = PerformanceElementTiming::Create(
url, rect, MonotonicTimeToDOMHighResTimeStamp(start_time), name, url, rect, MonotonicTimeToDOMHighResTimeStamp(start_time),
MonotonicTimeToDOMHighResTimeStamp(response_end), identifier, MonotonicTimeToDOMHighResTimeStamp(response_end), identifier,
intrinsic_size.Width(), intrinsic_size.Height(), id, element); intrinsic_size.Width(), intrinsic_size.Height(), id, element);
if (HasObserverFor(PerformanceEntry::kElement)) { if (HasObserverFor(PerformanceEntry::kElement)) {
......
...@@ -76,7 +76,8 @@ class CORE_EXPORT WindowPerformance final : public Performance, ...@@ -76,7 +76,8 @@ class CORE_EXPORT WindowPerformance final : public Performance,
TimeTicks processing_end, TimeTicks processing_end,
bool cancelable); bool cancelable);
void AddElementTiming(const String& url, void AddElementTiming(const AtomicString& name,
const String& url,
const FloatRect& rect, const FloatRect& rect,
TimeTicks start_time, TimeTicks start_time,
TimeTicks response_end, TimeTicks response_end,
......
...@@ -29,7 +29,8 @@ body { ...@@ -29,7 +29,8 @@ body {
let beforeRender = performance.now(); let beforeRender = performance.now();
let numObservedElements = 0; let numObservedElements = 0;
let observedDiv1 = false; let observedDiv1 = false;
let observedDiv2 = false; let observedDiv2Img = false;
let observedDiv2Txt = false;
const index = window.location.href.lastIndexOf('/'); const index = window.location.href.lastIndexOf('/');
const pathname = window.location.href.substring(0, index) + const pathname = window.location.href.substring(0, index) +
'/resources/square100.png'; '/resources/square100.png';
...@@ -46,19 +47,29 @@ body { ...@@ -46,19 +47,29 @@ body {
checkNaturalSize(entry, 100, 100); checkNaturalSize(entry, 100, 100);
} }
else if (entry.id == 'div2') { else if (entry.id == 'div2') {
observedDiv2 = true; // Check image entry.
checkElement(entry, pathname, 'et2', 'div2', beforeRender, if (entry.name !== 'text-paint') {
observedDiv2Img = true;
checkElement(entry, pathname, 'et2', 'div2', beforeRender,
document.getElementById('div2'));
// Div is below div1, on the left.
checkRect(entry, [0, 200, 100, 200]);
checkNaturalSize(entry, 100, 100);
}
// Check the text entry.
else {
observedDiv2Txt = true;
checkTextElement(entry, 'et2', 'div2', beforeRender,
document.getElementById('div2')); document.getElementById('div2'));
// Div is below div1, on the left. }
checkRect(entry, [0, 200, 100, 200]);
checkNaturalSize(entry, 100, 100);
} }
else { else {
assert_unreached("Should not observe other elements!"); assert_unreached("Should not observe other elements!");
} }
if (numObservedElements === 2) { if (numObservedElements === 3) {
assert_true(observedDiv1); assert_true(observedDiv1);
assert_true(observedDiv2); assert_true(observedDiv2Img);
assert_true(observedDiv2Txt);
t.done(); t.done();
} }
}); });
......
<!DOCTYPE HTML>
<meta charset=utf-8>
<title>Element Timing: observe text</title>
<body>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/element-timing-helpers.js"></script>
<script>
async_test((t) => {
if (!window.PerformanceElementTiming) {
assert_unreached("PerformanceElementTiming is not implemented");
}
let paragraph;
let beforeRender;
const observer = new PerformanceObserver(
t.step_func_done((entryList) => {
assert_equals(entryList.getEntries().length, 1);
checkTextElement(entryList.getEntries()[0], 'my_text', 'text_id', beforeRender, paragraph);
})
);
observer.observe({entryTypes: ['element']});
// We add the iframe during onload to be sure that the observer is registered
// in time for it to observe the element timing.
window.onload = () => {
paragraph = document.createElement('p');
paragraph.innerHTML = 'This is text I care about';
paragraph.setAttribute('elementtiming', 'my_text');
paragraph.setAttribute('id', 'text_id');
document.body.appendChild(paragraph);
beforeRender = performance.now();
};
}, 'Paragraph with elementtiming attribute is observed.');
</script>
</body>
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
function checkElementInternal(entry, expectedUrl, expectedIdentifier, expectedID, beforeRender, function checkElementInternal(entry, expectedUrl, expectedIdentifier, expectedID, beforeRender,
expectedElement) { expectedElement) {
assert_equals(entry.entryType, 'element'); assert_equals(entry.entryType, 'element');
assert_equals(entry.name, 'image-paint');
assert_equals(entry.url, expectedUrl); assert_equals(entry.url, expectedUrl);
assert_equals(entry.identifier, expectedIdentifier); assert_equals(entry.identifier, expectedIdentifier);
assert_equals(entry.duration, 0); assert_equals(entry.duration, 0);
...@@ -19,6 +18,7 @@ function checkElement(entry, expectedUrl, expectedIdentifier, expectedID, before ...@@ -19,6 +18,7 @@ function checkElement(entry, expectedUrl, expectedIdentifier, expectedID, before
expectedElement) { expectedElement) {
checkElementInternal(entry, expectedUrl, expectedIdentifier, expectedID, beforeRender, checkElementInternal(entry, expectedUrl, expectedIdentifier, expectedID, beforeRender,
expectedElement); expectedElement);
assert_equals(entry.name, 'image-paint');
const rt_entries = performance.getEntriesByName(expectedUrl, 'resource'); const rt_entries = performance.getEntriesByName(expectedUrl, 'resource');
assert_equals(rt_entries.length, 1); assert_equals(rt_entries.length, 1);
assert_equals(rt_entries[0].responseEnd, entry.responseEnd); assert_equals(rt_entries[0].responseEnd, entry.responseEnd);
...@@ -28,6 +28,7 @@ function checkElementWithoutResourceTiming(entry, expectedUrl, expectedIdentifie ...@@ -28,6 +28,7 @@ function checkElementWithoutResourceTiming(entry, expectedUrl, expectedIdentifie
expectedID, beforeRender, expectedElement) { expectedID, beforeRender, expectedElement) {
checkElementInternal(entry, expectedUrl, expectedIdentifier, expectedID, beforeRender, checkElementInternal(entry, expectedUrl, expectedIdentifier, expectedID, beforeRender,
expectedElement); expectedElement);
assert_equals(entry.name, 'image-paint');
// No associated resource from ResourceTiming, so the responseEnd should be 0. // No associated resource from ResourceTiming, so the responseEnd should be 0.
assert_equals(entry.responseEnd, 0); assert_equals(entry.responseEnd, 0);
} }
...@@ -49,3 +50,11 @@ function checkNaturalSize(entry, width, height) { ...@@ -49,3 +50,11 @@ function checkNaturalSize(entry, width, height) {
assert_equals(entry.naturalWidth, width); assert_equals(entry.naturalWidth, width);
assert_equals(entry.naturalHeight, height); assert_equals(entry.naturalHeight, height);
} }
function checkTextElement(entry, expectedIdentifier, expectedID, beforeRender,
expectedElement) {
checkElementInternal(entry, '', expectedIdentifier, expectedID, beforeRender,
expectedElement);
assert_equals(entry.name, 'text-paint');
assert_equals(entry.responseEnd, 0);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment