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

[ElementTiming] Fix background image loadTime

This change adds a map |background_image_timestamps_| of load timestamps of
background images tracked by StyleFetchedImage, which are the background images
with url. The map only tracks the load time, as |images_notified_| must still be
used to track the background image paints. Otherwise we'd only report an entry
per background image even when applied to multiple elements.

This change also makes computations a bit more efficient by only calling
base::TimeTicks::Now() on the first time we load an entry. It also fixes a
problem of calling Set() in between usages of an iterator, which is not allowed.
Before this change, the added test would crash due to this problem.

Bug: 879270, 986891
Change-Id: I86640f5587f69f94e13c429f3e55b3d5d6978cc0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1745497Reviewed-by: default avatarSteve Kobes <skobes@chromium.org>
Commit-Queue: Nicolás Peña Moreno <npm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#686176}
parent 2e9e0967
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "third_party/blink/renderer/core/style/border_edge.h" #include "third_party/blink/renderer/core/style/border_edge.h"
#include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/shadow_list.h" #include "third_party/blink/renderer/core/style/shadow_list.h"
#include "third_party/blink/renderer/core/style/style_fetched_image.h"
#include "third_party/blink/renderer/platform/geometry/layout_rect.h" #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h" #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
...@@ -563,7 +564,7 @@ inline bool PaintFastBottomLayer(Node* node, ...@@ -563,7 +564,7 @@ inline bool PaintFastBottomLayer(Node* node,
LocalDOMWindow* window = node->GetDocument().domWindow(); LocalDOMWindow* window = node->GetDocument().domWindow();
DCHECK(window); DCHECK(window);
ImageElementTiming::From(*window).NotifyBackgroundImagePainted( ImageElementTiming::From(*window).NotifyBackgroundImagePainted(
node, info.image, node, To<StyleFetchedImage>(info.image.Get()),
context.GetPaintController().CurrentPaintChunkProperties()); context.GetPaintController().CurrentPaintChunkProperties());
} }
return true; return true;
...@@ -695,7 +696,7 @@ void PaintFillLayerBackground(GraphicsContext& context, ...@@ -695,7 +696,7 @@ void PaintFillLayerBackground(GraphicsContext& context,
LocalDOMWindow* window = node->GetDocument().domWindow(); LocalDOMWindow* window = node->GetDocument().domWindow();
DCHECK(window); DCHECK(window);
ImageElementTiming::From(*window).NotifyBackgroundImagePainted( ImageElementTiming::From(*window).NotifyBackgroundImagePainted(
node, info.image, node, To<StyleFetchedImage>(info.image.Get()),
context.GetPaintController().CurrentPaintChunkProperties()); context.GetPaintController().CurrentPaintChunkProperties());
} }
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
#include "third_party/blink/renderer/core/loader/resource/image_resource_content.h" #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
#include "third_party/blink/renderer/core/page/chrome_client.h" #include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/paint/element_timing_utils.h" #include "third_party/blink/renderer/core/paint/element_timing_utils.h"
#include "third_party/blink/renderer/core/style/style_image.h" #include "third_party/blink/renderer/core/style/style_fetched_image.h"
#include "third_party/blink/renderer/core/timing/dom_window_performance.h" #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
#include "third_party/blink/renderer/core/timing/window_performance.h" #include "third_party/blink/renderer/core/timing/window_performance.h"
#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h" #include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
...@@ -71,8 +71,18 @@ void ImageElementTiming::NotifyImageFinished( ...@@ -71,8 +71,18 @@ void ImageElementTiming::NotifyImageFinished(
if (!internal::IsExplicitlyRegisteredForTiming(&layout_object)) if (!internal::IsExplicitlyRegisteredForTiming(&layout_object))
return; return;
images_notified_.Set(std::make_pair(&layout_object, cached_image), const auto& insertion_result = images_notified_.insert(
ImageInfo(base::TimeTicks::Now())); std::make_pair(&layout_object, cached_image), ImageInfo());
if (insertion_result.is_new_entry)
insertion_result.stored_value->value.load_time_ = base::TimeTicks::Now();
}
void ImageElementTiming::NotifyBackgroundImageFinished(
const StyleFetchedImage* style_image) {
const auto& insertion_result =
background_image_timestamps_.insert(style_image, base::TimeTicks());
if (insertion_result.is_new_entry)
insertion_result.stored_value->value = base::TimeTicks::Now();
} }
void ImageElementTiming::NotifyImagePainted( void ImageElementTiming::NotifyImagePainted(
...@@ -177,7 +187,7 @@ void ImageElementTiming::NotifyImagePaintedInternal( ...@@ -177,7 +187,7 @@ void ImageElementTiming::NotifyImagePaintedInternal(
void ImageElementTiming::NotifyBackgroundImagePainted( void ImageElementTiming::NotifyBackgroundImagePainted(
Node* node, Node* node,
const StyleImage* background_image, const StyleFetchedImage* background_image,
const PropertyTreeState& current_paint_chunk_properties) { const PropertyTreeState& current_paint_chunk_properties) {
DCHECK(node); DCHECK(node);
DCHECK(background_image); DCHECK(background_image);
...@@ -193,20 +203,18 @@ void ImageElementTiming::NotifyBackgroundImagePainted( ...@@ -193,20 +203,18 @@ void ImageElementTiming::NotifyBackgroundImagePainted(
if (!cached_image || !cached_image->IsLoaded()) if (!cached_image || !cached_image->IsLoaded())
return; return;
std::pair<const LayoutObject*, const ImageResourceContent*> pair = auto it = background_image_timestamps_.find(background_image);
std::make_pair(layout_object, cached_image); DCHECK(it != background_image_timestamps_.end());
auto it = images_notified_.find(pair);
// TODO(crbug.com/986891): ideally |images_notified_| would always be able to ImageInfo& info =
// find the pair here. However, for some background images that is currently images_notified_
// not possible. Therefore, in those cases we create the entry here with .insert(std::make_pair(layout_object, cached_image), ImageInfo())
// loadTime of 0. Once the bug is fixed, we should replace that with a DCHECK. .stored_value->value;
if (it == images_notified_.end()) if (!info.is_painted_) {
images_notified_.Set(pair, ImageInfo(base::TimeTicks())); info.is_painted_ = true;
if (!it->value.is_painted_ && cached_image) {
it->value.is_painted_ = true;
NotifyImagePaintedInternal(layout_object->GetNode(), *layout_object, NotifyImagePaintedInternal(layout_object->GetNode(), *layout_object,
*cached_image, current_paint_chunk_properties, *cached_image, current_paint_chunk_properties,
it->value.load_time_); it->value);
} }
} }
...@@ -233,6 +241,7 @@ void ImageElementTiming::NotifyImageRemoved(const LayoutObject* layout_object, ...@@ -233,6 +241,7 @@ void ImageElementTiming::NotifyImageRemoved(const LayoutObject* layout_object,
void ImageElementTiming::Trace(blink::Visitor* visitor) { void ImageElementTiming::Trace(blink::Visitor* visitor) {
visitor->Trace(element_timings_); visitor->Trace(element_timings_);
visitor->Trace(background_image_timestamps_);
Supplement<LocalDOMWindow>::Trace(visitor); Supplement<LocalDOMWindow>::Trace(visitor);
} }
......
...@@ -8,7 +8,9 @@ ...@@ -8,7 +8,9 @@
#include <utility> #include <utility>
#include "third_party/blink/public/web/web_widget_client.h" #include "third_party/blink/public/web/web_widget_client.h"
#include "third_party/blink/renderer/core/dom/element.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/platform/heap/heap_allocator.h"
#include "third_party/blink/renderer/platform/supplementable.h" #include "third_party/blink/renderer/platform/supplementable.h"
#include "third_party/blink/renderer/platform/wtf/functional.h" #include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h"
...@@ -19,7 +21,7 @@ namespace blink { ...@@ -19,7 +21,7 @@ namespace blink {
class ImageResourceContent; class ImageResourceContent;
class PropertyTreeState; class PropertyTreeState;
class StyleImage; class StyleFetchedImage;
// ImageElementTiming is responsible for tracking the paint timings for <img> // ImageElementTiming is responsible for tracking the paint timings for <img>
// elements for a given window. // elements for a given window.
...@@ -42,6 +44,8 @@ class CORE_EXPORT ImageElementTiming final ...@@ -42,6 +44,8 @@ class CORE_EXPORT ImageElementTiming final
void NotifyImageFinished(const LayoutObject&, const ImageResourceContent*); void NotifyImageFinished(const LayoutObject&, const ImageResourceContent*);
void NotifyBackgroundImageFinished(const StyleFetchedImage*);
// Called when the LayoutObject has been painted. This method might queue a // Called when the LayoutObject has been painted. This method might queue a
// swap promise to compute and report paint timestamps. // swap promise to compute and report paint timestamps.
void NotifyImagePainted( void NotifyImagePainted(
...@@ -51,7 +55,7 @@ class CORE_EXPORT ImageElementTiming final ...@@ -51,7 +55,7 @@ class CORE_EXPORT ImageElementTiming final
void NotifyBackgroundImagePainted( void NotifyBackgroundImagePainted(
Node*, Node*,
const StyleImage* background_image, const StyleFetchedImage* background_image,
const PropertyTreeState& current_paint_chunk_properties); const PropertyTreeState& current_paint_chunk_properties);
void NotifyImageRemoved(const LayoutObject*, void NotifyImageRemoved(const LayoutObject*,
...@@ -111,20 +115,24 @@ class CORE_EXPORT ImageElementTiming final ...@@ -111,20 +115,24 @@ class CORE_EXPORT ImageElementTiming final
// next swap promise callback. // next swap promise callback.
HeapVector<Member<ElementTimingInfo>> element_timings_; HeapVector<Member<ElementTimingInfo>> element_timings_;
struct ImageInfo { struct ImageInfo {
// HashMap values require default constructor so we set default value for ImageInfo() {}
// |load_time|.
ImageInfo(base::TimeTicks load_time = base::TimeTicks())
: load_time_(load_time), is_painted_(false) {}
base::TimeTicks load_time_; base::TimeTicks load_time_;
bool is_painted_; bool is_painted_ = false;
}; };
typedef std::pair<const LayoutObject*, const ImageResourceContent*> RecordId; typedef std::pair<const LayoutObject*, const ImageResourceContent*> RecordId;
// Hashmap of pairs of elements, LayoutObjects (for the elements) and // Hashmap of pairs of elements, LayoutObjects (for the elements) and
// ImageResourceContent (for the src) which correspond to either images or // ImageResourceContent (for the src) which correspond to either images or
// background images whose paint has been observed. // background images whose paint has been observed. For background images,
// only the |is_painted_| bit is used, as the timestamp needs to be tracked by
// |background_image_timestamps_|.
WTF::HashMap<RecordId, ImageInfo> images_notified_; WTF::HashMap<RecordId, ImageInfo> images_notified_;
// Hashmap of background images which contain information about the load time
// of the background image.
HeapHashMap<WeakMember<const StyleFetchedImage>, base::TimeTicks>
background_image_timestamps_;
DISALLOW_COPY_AND_ASSIGN(ImageElementTiming); DISALLOW_COPY_AND_ASSIGN(ImageElementTiming);
}; };
......
...@@ -30,11 +30,13 @@ ...@@ -30,11 +30,13 @@
#include "third_party/blink/renderer/core/frame/local_frame_client.h" #include "third_party/blink/renderer/core/frame/local_frame_client.h"
#include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/loader/resource/image_resource_content.h" #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
#include "third_party/blink/renderer/core/paint/image_element_timing.h"
#include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/svg/graphics/svg_image.h" #include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
#include "third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h" #include "third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h"
#include "third_party/blink/renderer/platform/geometry/layout_size.h" #include "third_party/blink/renderer/platform/geometry/layout_size.h"
#include "third_party/blink/renderer/platform/graphics/placeholder_image.h" #include "third_party/blink/renderer/platform/graphics/placeholder_image.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink { namespace blink {
...@@ -140,6 +142,12 @@ void StyleFetchedImage::ImageNotifyFinished(ImageResourceContent*) { ...@@ -140,6 +142,12 @@ void StyleFetchedImage::ImageNotifyFinished(ImageResourceContent*) {
image_->UpdateImageAnimationPolicy(); image_->UpdateImageAnimationPolicy();
} }
if (document_ && RuntimeEnabledFeatures::ElementTimingEnabled(document_)) {
if (LocalDOMWindow* window = document_->domWindow()) {
ImageElementTiming::From(*window).NotifyBackgroundImageFinished(this);
}
}
// Oilpan: do not prolong the Document's lifetime. // Oilpan: do not prolong the Document's lifetime.
document_.Clear(); document_.Clear();
} }
......
<!DOCTYPE HTML>
<meta charset=utf-8>
<title>Element Timing: observe element with background image in its first letter</title>
<body>
<style>
#target::first-letter {
background-image: url('/images/black-rectangle.png');
}
</style>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/element-timing-helpers.js"></script>
<script>
let beforeRender = performance.now();
async_test(function (t) {
if (!window.PerformanceElementTiming) {
assert_unreached("PerformanceElementTiming is not implemented");
}
const div = document.getElementById('target');
let textObserved = false;
let imageObserved = false;
const observer = new PerformanceObserver(
t.step_func(function(entryList) {
entryList.getEntries().forEach(entry => {
if (entry.name === 'text-paint') {
checkTextElement(entry, 'my_div', 'target', beforeRender, div);
textObserved = true;
}
else {
assert_equals(entry.name, 'image-paint');
const pathname = window.location.origin + '/images/black-rectangle.png';
checkElement(entry, pathname, 'my_div', 'target', beforeRender, div);
checkNaturalSize(entry, 100, 50);
imageObserved = true;
}
});
if (textObserved && imageObserved)
t.done();
})
);
observer.observe({entryTypes: ['element']});
}, 'Element with elementtiming attribute and background image in first-letter is observable.');
</script>
<div id='target' elementtiming='my_div'>A</div>
</body>
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