0

ContentCapture: Get the visual rect from cc

Currently to get the visual rect from layout tree requires document
being paint clean, it became not a good way for ContentCapture task
to get the visual rect now.

This patch get the visual rect from cc while capturing the content.
It should have better performance than from layout tree.

Bug: 1126615,1122472


Change-Id: Id10fa1ad84f64058f30c3523b78d2aff822e62a1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2388344
Reviewed-by: Philip Rogers <pdr@chromium.org>
Reviewed-by: Xianzhu Wang <wangxianzhu@chromium.org>
Commit-Queue: Tao Bai <michaelbai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805934}
This commit is contained in:
Michael Bai
2020-09-10 22:06:48 +00:00
committed by Commit Bot
parent 4f1ee370d1
commit c57702478b
24 changed files with 278 additions and 104 deletions

@ -11,6 +11,7 @@
#include <algorithm>
#include <cmath>
#include <map>
#include <utility>
#include <vector>
#include "base/check_op.h"
@ -70,7 +71,9 @@ class RTree {
// Given a query rect, returns elements that intersect the rect. Elements are
// returned in the order they appeared in the initial container.
void Search(const gfx::Rect& query, std::vector<T>* results) const;
void Search(const gfx::Rect& query,
std::vector<T>* results,
std::vector<gfx::Rect>* rects = nullptr) const;
// Given a query rect, returns non-owning pointers to elements that intersect
// the rect. Elements are returned in the order they appeared in the initial
@ -123,7 +126,8 @@ class RTree {
void SearchRecursive(Node<T>* root,
const gfx::Rect& query,
std::vector<T>* results) const;
std::vector<T>* results,
std::vector<gfx::Rect>* rects = nullptr) const;
void SearchRefsRecursive(Node<T>* root,
const gfx::Rect& query,
std::vector<const T*>* results) const;
@ -132,7 +136,8 @@ class RTree {
// and SearchRefsRecursive for when !has_valid_bounds().
void SearchRecursiveFallback(Node<T>* root,
const gfx::Rect& query,
std::vector<T>* results) const;
std::vector<T>* results,
std::vector<gfx::Rect>* rects = nullptr) const;
void SearchRefsRecursiveFallback(Node<T>* root,
const gfx::Rect& query,
std::vector<const T*>* results) const;
@ -307,14 +312,16 @@ auto RTree<T>::BuildRecursive(std::vector<Branch<T>>* branches, int level)
}
template <typename T>
void RTree<T>::Search(const gfx::Rect& query, std::vector<T>* results) const {
void RTree<T>::Search(const gfx::Rect& query,
std::vector<T>* results,
std::vector<gfx::Rect>* rects) const {
results->clear();
if (num_data_elements_ == 0)
return;
if (!has_valid_bounds_) {
SearchRecursiveFallback(root_.subtree, query, results);
SearchRecursiveFallback(root_.subtree, query, results, rects);
} else if (query.Intersects(root_.bounds)) {
SearchRecursive(root_.subtree, query, results);
SearchRecursive(root_.subtree, query, results, rects);
}
}
@ -334,13 +341,17 @@ void RTree<T>::SearchRefs(const gfx::Rect& query,
template <typename T>
void RTree<T>::SearchRecursive(Node<T>* node,
const gfx::Rect& query,
std::vector<T>* results) const {
std::vector<T>* results,
std::vector<gfx::Rect>* rects) const {
for (uint16_t i = 0; i < node->num_children; ++i) {
if (query.Intersects(node->children[i].bounds)) {
if (node->level == 0)
if (node->level == 0) {
results->push_back(node->children[i].payload);
else
SearchRecursive(node->children[i].subtree, query, results);
if (rects)
rects->push_back(node->children[i].bounds);
} else {
SearchRecursive(node->children[i].subtree, query, results, rects);
}
}
}
}
@ -364,13 +375,17 @@ void RTree<T>::SearchRefsRecursive(Node<T>* node,
template <typename T>
void RTree<T>::SearchRecursiveFallback(Node<T>* node,
const gfx::Rect& query,
std::vector<T>* results) const {
std::vector<T>* results,
std::vector<gfx::Rect>* rects) const {
for (uint16_t i = 0; i < node->num_children; ++i) {
if (node->level == 0) {
if (query.Intersects(node->children[i].bounds))
if (query.Intersects(node->children[i].bounds)) {
results->push_back(node->children[i].payload);
if (rects)
rects->push_back(node->children[i].bounds);
}
} else {
SearchRecursive(node->children[i].subtree, query, results);
SearchRecursive(node->children[i].subtree, query, results, rects);
}
}
}

@ -5,6 +5,7 @@
#include "cc/base/rtree.h"
#include <stddef.h>
#include <utility>
#include "testing/gtest/include/gtest/gtest.h"
@ -26,6 +27,18 @@ void SearchAndVerifyRefs(const RTree<T>& rtree,
EXPECT_EQ(*ref_results[i], (*results)[i]);
}
}
template <typename T>
void SearchAndVerifyBounds(const RTree<T>& rtree,
const gfx::Rect& query,
std::vector<T>* results,
std::vector<gfx::Rect>* rects) {
rtree.Search(query, results, rects);
ASSERT_EQ(results->size(), rects->size());
for (auto& rect : *rects) {
EXPECT_TRUE(rect.Intersects(query));
}
}
} // namespace
TEST(RTreeTest, ReserveNodesDoesntDcheck) {
@ -220,6 +233,14 @@ TEST(RTreeTest, Payload) {
ASSERT_EQ(1u, results.size());
EXPECT_FLOAT_EQ(10.f, results[0]);
// Search with bounds
std::vector<gfx::Rect> rects;
SearchAndVerifyBounds(rtree, gfx::Rect(0, 0, 1, 1), &results, &rects);
ASSERT_EQ(1u, results.size());
ASSERT_EQ(results.size(), rects.size());
EXPECT_FLOAT_EQ(10.f, results[0]);
EXPECT_EQ(gfx::Rect(0, 0, 10, 10), rects[0]);
SearchAndVerifyRefs(rtree, gfx::Rect(5, 5, 10, 10), &results);
ASSERT_EQ(4u, results.size());
// Items returned should be in the order they were inserted.

@ -242,7 +242,7 @@ bool Layer::IsPropertyChangeAllowed() const {
}
void Layer::CaptureContent(const gfx::Rect& rect,
std::vector<NodeId>* content) {}
std::vector<NodeInfo>* content) {}
sk_sp<SkPicture> Layer::GetPicture() const {
return nullptr;

@ -570,9 +570,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> {
void ShowScrollbars() { needs_show_scrollbars_ = true; }
// Captures text content within the given |rect| and returns the associated
// NodeId in |content|.
// NodeInfo in |content|.
virtual void CaptureContent(const gfx::Rect& rect,
std::vector<NodeId>* content);
std::vector<NodeInfo>* content);
// For tracing. Gets a recorded rasterization of this layer's contents that
// can be displayed inside representations of this layer. May return null, in

@ -230,7 +230,7 @@ void PictureLayer::RunMicroBenchmark(MicroBenchmark* benchmark) {
}
void PictureLayer::CaptureContent(const gfx::Rect& rect,
std::vector<NodeId>* content) {
std::vector<NodeInfo>* content) {
if (!DrawsContent())
return;
@ -253,6 +253,22 @@ void PictureLayer::CaptureContent(const gfx::Rect& rect,
return;
display_item_list->CaptureContent(transformed, content);
if (auto* outer_viewport_layer = layer_tree_host()->LayerByElementId(
layer_tree_host()->OuterViewportScrollElementId())) {
if (transform_tree_index() == outer_viewport_layer->transform_tree_index())
return;
gfx::Transform inverse_outer_screen_space_transform;
if (!outer_viewport_layer->ScreenSpaceTransform().GetInverse(
&inverse_outer_screen_space_transform)) {
return;
}
gfx::Transform combined_transform{ScreenSpaceTransform(),
inverse_outer_screen_space_transform};
for (auto& i : *content) {
i.visual_rect = MathUtil::ProjectEnclosingClippedRect(combined_transform,
i.visual_rect);
}
}
}
void PictureLayer::DropRecordingSourceContentIfInvalid() {

@ -47,7 +47,7 @@ class CC_EXPORT PictureLayer : public Layer {
bool Update() override;
void RunMicroBenchmark(MicroBenchmark* benchmark) override;
void CaptureContent(const gfx::Rect& rect,
std::vector<NodeId>* content) override;
std::vector<NodeInfo>* content) override;
ContentLayerClient* client() { return picture_layer_inputs_.client; }

@ -6,6 +6,7 @@
#include <stddef.h>
#include <map>
#include <string>
#include "base/trace_event/trace_event.h"
@ -17,6 +18,7 @@
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/skia_util.h"
namespace cc {
@ -32,28 +34,37 @@ bool GetCanvasClipBounds(SkCanvas* canvas, gfx::Rect* clip_bounds) {
}
template <typename Function>
void IterateTextContent(const PaintOpBuffer* buffer, const Function& yield) {
for (auto* op : PaintOpBuffer::Iterator(buffer)) {
void IterateTextContent(const PaintOpBuffer& buffer,
const Function& yield,
const gfx::Rect& rect) {
if (!buffer.has_draw_text_ops())
return;
for (auto* op : PaintOpBuffer::Iterator(&buffer)) {
if (op->GetType() == PaintOpType::DrawTextBlob) {
yield(static_cast<DrawTextBlobOp*>(op));
yield(static_cast<DrawTextBlobOp*>(op), rect);
} else if (op->GetType() == PaintOpType::DrawRecord) {
IterateTextContent(static_cast<DrawRecordOp*>(op)->record.get(), yield);
IterateTextContent(*static_cast<DrawRecordOp*>(op)->record.get(), yield,
rect);
}
}
}
template <typename Function>
void IterateTextContentByOffsets(const PaintOpBuffer* buffer,
void IterateTextContentByOffsets(const PaintOpBuffer& buffer,
const std::vector<size_t>& offsets,
const std::vector<gfx::Rect>& rects,
const Function& yield) {
if (!buffer)
return;
for (auto* op : PaintOpBuffer::OffsetIterator(buffer, &offsets)) {
DCHECK(buffer.has_draw_text_ops());
DCHECK_EQ(rects.size(), offsets.size());
size_t index = 0;
for (auto* op : PaintOpBuffer::OffsetIterator(&buffer, &offsets)) {
if (op->GetType() == PaintOpType::DrawTextBlob) {
yield(static_cast<DrawTextBlobOp*>(op));
yield(static_cast<DrawTextBlobOp*>(op), rects[index]);
} else if (op->GetType() == PaintOpType::DrawRecord) {
IterateTextContent(static_cast<DrawRecordOp*>(op)->record.get(), yield);
IterateTextContent(*static_cast<DrawRecordOp*>(op)->record.get(), yield,
rects[index]);
}
++index;
}
}
@ -90,20 +101,35 @@ void DisplayItemList::Raster(SkCanvas* canvas,
}
void DisplayItemList::CaptureContent(const gfx::Rect& rect,
std::vector<NodeId>* content) const {
std::vector<NodeInfo>* content) const {
if (!paint_op_buffer_.has_draw_text_ops())
return;
std::vector<size_t> offsets;
rtree_.Search(rect, &offsets);
std::vector<gfx::Rect> rects;
rtree_.Search(rect, &offsets, &rects);
IterateTextContentByOffsets(
&paint_op_buffer_, offsets,
[content](const DrawTextBlobOp* op) { content->push_back(op->node_id); });
paint_op_buffer_, offsets, rects,
[content](const DrawTextBlobOp* op, const gfx::Rect& rect) {
// Only union the rect if the current is the same as the last one.
if (!content->empty() && content->back().node_id == op->node_id)
content->back().visual_rect.Union(rect);
else
content->emplace_back(op->node_id, rect);
});
}
double DisplayItemList::AreaOfDrawText(const gfx::Rect& rect) const {
std::vector<size_t> offsets;
rtree_.Search(rect, &offsets);
double area = 0;
if (!paint_op_buffer_.has_draw_text_ops())
return area;
std::vector<size_t> offsets;
std::vector<gfx::Rect> rects;
rtree_.Search(rect, &offsets, &rects);
IterateTextContentByOffsets(
&paint_op_buffer_, offsets, [&area](const DrawTextBlobOp* op) {
paint_op_buffer_, offsets, rects,
[&area](const DrawTextBlobOp* op, const gfx::Rect& rect) {
// TODO(wangxianzhu) : crbug.com/1126582 use the visual_rect from
// callback.
// This is not fully accurate, e.g. when there is transform operations,
// but is good for statistics purpose.
SkRect bounds = op->blob->bounds();

@ -65,7 +65,7 @@ class CC_PAINT_EXPORT DisplayItemList
// Captures |DrawTextBlobOp|s intersecting |rect| and returns the associated
// |NodeId|s in |content|.
void CaptureContent(const gfx::Rect& rect,
std::vector<NodeId>* content) const;
std::vector<NodeInfo>* content) const;
// Returns the approximate total area covered by |DrawTextBlobOp|s
// intersecting |rect|, used for statistics purpose.

@ -5,6 +5,9 @@
#ifndef CC_PAINT_NODE_ID_H_
#define CC_PAINT_NODE_ID_H_
#include "cc/paint/paint_export.h"
#include "ui/gfx/geometry/rect.h"
namespace cc {
// The NodeId is used to associate the DOM node with PaintOp, its peer in
// blink is DOMNodeId.
@ -17,6 +20,14 @@ using NodeId = int;
static const NodeId kInvalidNodeId = 0;
struct CC_PAINT_EXPORT NodeInfo {
NodeInfo(NodeId node_id, const gfx::Rect& visual_rect)
: node_id(node_id), visual_rect(visual_rect) {}
NodeId node_id;
gfx::Rect visual_rect;
};
} // namespace cc
#endif // CC_PAINT_NODE_ID_H_

@ -774,15 +774,17 @@ std::string LayerTreeHost::LayersAsString() const {
return layers;
}
bool LayerTreeHost::CaptureContent(std::vector<NodeId>* content) {
bool LayerTreeHost::CaptureContent(std::vector<NodeInfo>* content) {
if (viewport_visible_rect_.IsEmpty())
return false;
gfx::Rect rect = gfx::Rect(viewport_visible_rect_.width(),
viewport_visible_rect_.height());
for (auto* layer : *this)
for (auto* layer : *this) {
// Normally, the node won't be drawn in multiple layers, even it is, such as
// text strokes, the visual rect don't have too much different.
layer->CaptureContent(rect, content);
}
return true;
}

@ -706,8 +706,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
std::string LayersAsString() const;
// Captures the on-screen text content, if success, fills the associated
// NodeId in |content| and return true, otherwise return false.
bool CaptureContent(std::vector<NodeId>* content);
// NodeInfo in |content| and return true, otherwise return false.
bool CaptureContent(std::vector<NodeInfo>* content);
std::unique_ptr<BeginMainFrameMetrics> begin_main_frame_metrics() {
return std::move(begin_main_frame_metrics_);

@ -81,7 +81,7 @@ class LayerTreeHostCaptureContentTest : public LayerTreeTest {
for (auto& c : captured_content_) {
for (auto it = expected_result->begin(); it != expected_result->end();
++it) {
if (it->node_id() == c) {
if (it->node_id() == c.node_id) {
expected_result->erase(it);
break;
}
@ -107,7 +107,7 @@ class LayerTreeHostCaptureContentTest : public LayerTreeTest {
}
scoped_refptr<FakePictureLayer> root_picture_layer_;
std::vector<NodeId> captured_content_;
std::vector<NodeInfo> captured_content_;
const gfx::Size device_bounds_;
base::WeakPtrFactory<LayerTreeHostCaptureContentTest> weak_factory_{this};
};

@ -16,7 +16,7 @@ const base::Feature kContentCapture{"ContentCapture",
base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kContentCaptureTriggeringForExperiment{
"ContentCaptureTriggeringForExperiment", base::FEATURE_DISABLED_BY_DEFAULT};
"ContentCaptureTriggeringForExperiment", base::FEATURE_ENABLED_BY_DEFAULT};
#else
const base::Feature kContentCapture{"ContentCapture",
base::FEATURE_DISABLED_BY_DEFAULT};

@ -13,7 +13,7 @@
namespace blink {
class Node;
class ContentHolder;
// The class to represent the captured content.
class BLINK_EXPORT WebContentHolder {
@ -27,11 +27,11 @@ class BLINK_EXPORT WebContentHolder {
uint64_t GetId() const;
#if INSIDE_BLINK
WebContentHolder(Node& node);
explicit WebContentHolder(ContentHolder& node_info);
#endif
private:
WebPrivatePtr<Node> private_;
WebPrivatePtr<ContentHolder> private_;
};
} // namespace blink

@ -12,6 +12,8 @@ blink_core_sources("content_capture") {
"content_capture_task.h",
"content_capture_task_histogram_reporter.cc",
"content_capture_task_histogram_reporter.h",
"content_holder.cc",
"content_holder.h",
"sent_nodes.cc",
"sent_nodes.h",
"task_session.cc",

@ -74,7 +74,7 @@ void ContentCaptureTask::Shutdown() {
local_frame_root_ = nullptr;
}
bool ContentCaptureTask::CaptureContent(Vector<cc::NodeId>& data) {
bool ContentCaptureTask::CaptureContent(Vector<cc::NodeInfo>& data) {
if (captured_content_for_testing_) {
data = captured_content_for_testing_.value();
return true;
@ -84,7 +84,7 @@ bool ContentCaptureTask::CaptureContent(Vector<cc::NodeId>& data) {
if (const auto* root_frame_view = local_frame_root_->View()) {
if (const auto* cc_layer = root_frame_view->RootCcLayer()) {
if (auto* layer_tree_host = cc_layer->layer_tree_host()) {
std::vector<cc::NodeId> content;
std::vector<cc::NodeInfo> content;
if (layer_tree_host->CaptureContent(&content)) {
for (auto c : content)
data.push_back(std::move(c));
@ -99,7 +99,7 @@ bool ContentCaptureTask::CaptureContent(Vector<cc::NodeId>& data) {
bool ContentCaptureTask::CaptureContent() {
DCHECK(task_session_);
Vector<cc::NodeId> buffer;
Vector<cc::NodeInfo> buffer;
if (histogram_reporter_)
histogram_reporter_->OnCaptureContentStarted();
bool result = CaptureContent(buffer);
@ -124,14 +124,14 @@ void ContentCaptureTask::SendContent(
// Only send changed content after the new content was sent.
bool sending_changed_content = !doc_session.HasUnsentCapturedContent();
while (content_batch.size() < kBatchSize) {
Node* node;
ContentHolder* holder;
if (sending_changed_content)
node = doc_session.GetNextChangedNode();
holder = doc_session.GetNextChangedNode();
else
node = doc_session.GetNextUnsentNode();
if (!node)
holder = doc_session.GetNextUnsentNode();
if (!holder)
break;
content_batch.emplace_back(WebContentHolder(*node));
content_batch.emplace_back(WebContentHolder(*holder));
}
if (!content_batch.empty()) {
if (sending_changed_content) {

@ -88,7 +88,7 @@ class CORE_EXPORT ContentCaptureTask
}
void SetCapturedContentForTesting(
const Vector<cc::NodeId>& captured_content) {
const Vector<cc::NodeInfo>& captured_content) {
captured_content_for_testing_ = captured_content;
}
@ -133,7 +133,7 @@ class CORE_EXPORT ContentCaptureTask
base::TimeDelta GetAndAdjustDelay(ScheduleReason reason);
void ScheduleInternal(ScheduleReason reason);
bool CaptureContent(Vector<cc::NodeId>& data);
bool CaptureContent(Vector<cc::NodeInfo>& data);
// Indicates if there is content change since last run.
bool has_content_change_ = false;
@ -147,7 +147,7 @@ class CORE_EXPORT ContentCaptureTask
scoped_refptr<ContentCaptureTaskHistogramReporter> histogram_reporter_;
base::Optional<TaskState> task_stop_for_testing_;
base::Optional<Vector<cc::NodeId>> captured_content_for_testing_;
base::Optional<Vector<cc::NodeInfo>> captured_content_for_testing_;
};
} // namespace blink

@ -26,6 +26,12 @@
namespace blink {
namespace {
gfx::Rect GetRect(LayoutObject* layout_object) {
return gfx::Rect(EnclosingIntRect(layout_object->VisualRectInDocument()));
}
} // namespace
class WebContentCaptureClientTestHelper : public WebContentCaptureClient {
public:
~WebContentCaptureClientTestHelper() override = default;
@ -212,7 +218,8 @@ class ContentCaptureTest : public PageTestBase,
UpdateAllLifecyclePhasesForTest();
GetContentCaptureManager()->ScheduleTaskIfNeeded(*node);
created_node_id_ = DOMNodeIds::IdForNode(node);
Vector<DOMNodeId> captured_content{created_node_id_};
Vector<cc::NodeInfo> captured_content{
cc::NodeInfo(created_node_id_, GetRect(node->GetLayoutObject()))};
content_capture_manager_->GetContentCaptureTask()
->SetCapturedContentForTesting(captured_content);
}
@ -269,7 +276,7 @@ class ContentCaptureTest : public PageTestBase,
return node_ids_.size() - GetExpectedFirstResultSize();
}
const Vector<DOMNodeId>& NodeIds() const { return node_ids_; }
const Vector<cc::NodeInfo>& NodeIds() const { return node_ids_; }
const Vector<Persistent<Node>> Nodes() const { return nodes_; }
private:
@ -288,12 +295,13 @@ class ContentCaptureTest : public PageTestBase,
CHECK(layout_object->IsText());
nodes_.push_back(node);
GetContentCaptureManager()->ScheduleTaskIfNeeded(*node);
node_ids_.push_back(DOMNodeIds::IdForNode(node));
node_ids_.push_back(
cc::NodeInfo(DOMNodeIds::IdForNode(node), GetRect(layout_object)));
}
}
Vector<Persistent<Node>> nodes_;
Vector<DOMNodeId> node_ids_;
Vector<cc::NodeInfo> node_ids_;
std::unique_ptr<WebContentCaptureClientTestHelper> content_capture_client_;
Persistent<ContentCaptureManagerTestHelper> content_capture_manager_;
Persistent<ContentCaptureLocalFrameClientHelper> local_frame_client_;
@ -636,7 +644,7 @@ class ContentCaptureSimTest : public SimTest {
} else if (type == ContentType::kChildFrame) {
SetCapturedContent(child_frame_content_);
} else if (type == ContentType::kAll) {
Vector<DOMNodeId> holders(main_frame_content_);
Vector<cc::NodeInfo> holders(main_frame_content_);
holders.AppendRange(child_frame_content_.begin(),
child_frame_content_.end());
SetCapturedContent(holders);
@ -765,18 +773,20 @@ class ContentCaptureSimTest : public SimTest {
EXPECT_EQ(2u, child_frame_content_.size());
}
void InitNodeHolders(Vector<DOMNodeId>& buffer,
void InitNodeHolders(Vector<cc::NodeInfo>& buffer,
const Vector<std::string>& ids,
const Document& document) {
for (auto id : ids) {
LayoutText* layout_text = ToLayoutText(
document.getElementById(id.c_str())->firstChild()->GetLayoutObject());
auto* layout_object =
document.getElementById(id.c_str())->firstChild()->GetLayoutObject();
LayoutText* layout_text = ToLayoutText(layout_object);
EXPECT_TRUE(layout_text->HasNodeId());
buffer.push_back(layout_text->EnsureNodeId());
buffer.push_back(
cc::NodeInfo(layout_text->EnsureNodeId(), GetRect(layout_object)));
}
}
void AddNodeToDocument(Document& doc, Vector<DOMNodeId>& buffer) {
void AddNodeToDocument(Document& doc, Vector<cc::NodeInfo>& buffer) {
Node* node = doc.createTextNode("New Text");
auto* element = MakeGarbageCollected<Element>(html_names::kPTag, &doc);
element->appendChild(node);
@ -785,7 +795,8 @@ class ContentCaptureSimTest : public SimTest {
Compositor().BeginFrame();
LayoutText* layout_text = ToLayoutText(node->GetLayoutObject());
EXPECT_TRUE(layout_text->HasNodeId());
buffer.push_front(layout_text->EnsureNodeId());
buffer.push_back(cc::NodeInfo(layout_text->EnsureNodeId(),
GetRect(node->GetLayoutObject())));
}
void InsertNodeContent(Document& doc,
@ -807,7 +818,7 @@ class ContentCaptureSimTest : public SimTest {
Compositor().BeginFrame();
}
void SetCapturedContent(const Vector<DOMNodeId>& captured_content) {
void SetCapturedContent(const Vector<cc::NodeInfo>& captured_content) {
GetDocument()
.GetFrame()
->LocalFrameRoot()
@ -818,8 +829,8 @@ class ContentCaptureSimTest : public SimTest {
Vector<std::string> main_frame_expected_text_;
Vector<std::string> child_frame_expected_text_;
Vector<DOMNodeId> main_frame_content_;
Vector<DOMNodeId> child_frame_content_;
Vector<cc::NodeInfo> main_frame_content_;
Vector<cc::NodeInfo> child_frame_content_;
WebContentCaptureClientTestHelper client_;
WebContentCaptureClientTestHelper child_client_;
Persistent<Document> child_document_;

@ -0,0 +1,20 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/content_capture/content_holder.h"
namespace blink {
ContentHolder::ContentHolder() = default;
ContentHolder::ContentHolder(Node* node, const gfx::Rect& rect)
: node_(node), rect_(rect) {}
ContentHolder::~ContentHolder() = default;
void ContentHolder::Trace(Visitor* visitor) const {
visitor->Trace(node_);
}
} // namespace blink

@ -0,0 +1,36 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CONTENT_CAPTURE_CONTENT_HOLDER_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_CONTENT_CAPTURE_CONTENT_HOLDER_H_
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "ui/gfx/geometry/rect.h"
namespace blink {
class Node;
class CORE_EXPORT ContentHolder : public GarbageCollected<ContentHolder> {
public:
ContentHolder();
ContentHolder(Node* node, const gfx::Rect& rect);
virtual ~ContentHolder();
Node* node() const { return node_; }
const gfx::Rect& rect() const { return rect_; }
void Trace(Visitor*) const;
private:
WeakMember<Node> node_;
gfx::Rect rect_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_CONTENT_CAPTURE_CONTENT_HOLDER_H_

@ -22,40 +22,46 @@ TaskSession::DocumentSession::~DocumentSession() {
callback_.value().Run(total_sent_nodes_);
}
void TaskSession::DocumentSession::AddCapturedNode(Node& node) {
captured_content_.insert(WeakMember<Node>(&node));
void TaskSession::DocumentSession::AddCapturedNode(Node& node,
const gfx::Rect& rect) {
// Replace the previous rect if any.
captured_content_.Set(WeakMember<Node>(&node), rect);
}
void TaskSession::DocumentSession::AddDetachedNode(int64_t id) {
detached_nodes_.emplace_back(id);
}
void TaskSession::DocumentSession::AddChangedNode(Node& node) {
changed_content_.insert(WeakMember<Node>(&node));
void TaskSession::DocumentSession::AddChangedNode(Node& node,
const gfx::Rect& rect) {
// Replace the previous rect if any.
changed_content_.Set(WeakMember<Node>(&node), rect);
}
WebVector<int64_t> TaskSession::DocumentSession::MoveDetachedNodes() {
return std::move(detached_nodes_);
}
Node* TaskSession::DocumentSession::GetNextUnsentNode() {
ContentHolder* TaskSession::DocumentSession::GetNextUnsentNode() {
while (!captured_content_.IsEmpty()) {
Node* node = captured_content_.TakeAny().Get();
auto node = captured_content_.begin()->key;
const gfx::Rect rect = captured_content_.Take(node);
if (node && node->GetLayoutObject() && !sent_nodes_->HasSent(*node)) {
sent_nodes_->OnSent(*node);
total_sent_nodes_++;
return node;
return MakeGarbageCollected<ContentHolder>(node, rect);
}
}
return nullptr;
}
Node* TaskSession::DocumentSession::GetNextChangedNode() {
ContentHolder* TaskSession::DocumentSession::GetNextChangedNode() {
while (!changed_content_.IsEmpty()) {
Node* node = changed_content_.TakeAny().Get();
if (node && node->GetLayoutObject()) {
auto node = changed_content_.begin()->key;
const gfx::Rect rect = changed_content_.Take(node);
if (node.Get() && node->GetLayoutObject()) {
total_sent_nodes_++;
return node;
return MakeGarbageCollected<ContentHolder>(node, rect);
}
}
return nullptr;
@ -87,7 +93,7 @@ TaskSession::DocumentSession* TaskSession::GetNextUnsentDocumentSession() {
}
void TaskSession::SetCapturedContent(
const Vector<cc::NodeId>& captured_content) {
const Vector<cc::NodeInfo>& captured_content) {
DCHECK(!HasUnsentData());
DCHECK(!captured_content.IsEmpty());
GroupCapturedContentByDocument(captured_content);
@ -95,20 +101,26 @@ void TaskSession::SetCapturedContent(
}
void TaskSession::GroupCapturedContentByDocument(
const Vector<cc::NodeId>& captured_content) {
for (const cc::NodeId& node_id : captured_content) {
if (Node* node = DOMNodeIds::NodeForId(node_id)) {
const Vector<cc::NodeInfo>& captured_content) {
// In rare cases, the same node could have multiple entries in the
// |captured_content|, but the visual_rect are almost same, we just let the
// later replace the previous.
for (const auto& i : captured_content) {
if (Node* node = DOMNodeIds::NodeForId(i.node_id)) {
if (changed_nodes_.Take(node)) {
// The changed node might not be sent.
if (sent_nodes_->HasSent(*node)) {
EnsureDocumentSession(node->GetDocument()).AddChangedNode(*node);
EnsureDocumentSession(node->GetDocument())
.AddChangedNode(*node, i.visual_rect);
} else {
EnsureDocumentSession(node->GetDocument()).AddCapturedNode(*node);
EnsureDocumentSession(node->GetDocument())
.AddCapturedNode(*node, i.visual_rect);
}
continue;
}
if (!sent_nodes_->HasSent(*node)) {
EnsureDocumentSession(node->GetDocument()).AddCapturedNode(*node);
EnsureDocumentSession(node->GetDocument())
.AddCapturedNode(*node, i.visual_rect);
}
}
}

@ -11,10 +11,12 @@
#include "base/memory/scoped_refptr.h"
#include "cc/paint/node_id.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/renderer/core/content_capture/content_holder.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
@ -52,9 +54,9 @@ class TaskSession final : public GarbageCollected<TaskSession> {
SentNodes& sent_nodes,
SentNodeCountCallback& call_back);
~DocumentSession();
void AddCapturedNode(Node& node);
void AddCapturedNode(Node& node, const gfx::Rect& rect);
void AddDetachedNode(int64_t id);
void AddChangedNode(Node& node);
void AddChangedNode(Node& node, const gfx::Rect& rect);
bool HasUnsentData() const {
return HasUnsentCapturedContent() || HasUnsentChangedContent() ||
HasUnsentDetachedNodes();
@ -70,9 +72,9 @@ class TaskSession final : public GarbageCollected<TaskSession> {
void SetFirstDataHasSent() { first_data_has_sent_ = true; }
// Removes the unsent node from |captured_content_|, and returns it.
Node* GetNextUnsentNode();
ContentHolder* GetNextUnsentNode();
Node* GetNextChangedNode();
ContentHolder* GetNextChangedNode();
// Resets the |captured_content_| and the |detached_nodes_|, shall only be
// used if those data doesn't need to be sent, e.g. there is no
@ -83,14 +85,14 @@ class TaskSession final : public GarbageCollected<TaskSession> {
private:
// The captured content that belongs to this document.
HeapHashSet<WeakMember<Node>> captured_content_;
HeapHashMap<WeakMember<Node>, gfx::Rect> captured_content_;
// The list of content id of node that has been detached from the
// LayoutTree.
WebVector<int64_t> detached_nodes_;
WeakMember<const Document> document_;
Member<SentNodes> sent_nodes_;
// The list of changed nodes that needs to be sent.
HeapHashSet<WeakMember<Node>> changed_content_;
HeapHashMap<WeakMember<Node>, gfx::Rect> changed_content_;
bool first_data_has_sent_ = false;
// This is for the metrics to record the total node that has been sent.
@ -107,7 +109,7 @@ class TaskSession final : public GarbageCollected<TaskSession> {
// This can only be invoked when all data has been sent (i.e. HasUnsentData()
// returns False).
void SetCapturedContent(const Vector<cc::NodeId>& captured_content);
void SetCapturedContent(const Vector<cc::NodeInfo>& captured_content);
void OnNodeDetached(const Node& node);
@ -126,7 +128,7 @@ class TaskSession final : public GarbageCollected<TaskSession> {
private:
void GroupCapturedContentByDocument(
const Vector<cc::NodeId>& captured_content);
const Vector<cc::NodeInfo>& captured_content);
DocumentSession& EnsureDocumentSession(const Document& doc);
DocumentSession* GetDocumentSession(const Document& document) const;

@ -4,6 +4,7 @@
#include "third_party/blink/public/web/web_content_holder.h"
#include "third_party/blink/renderer/core/content_capture/content_holder.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
@ -23,19 +24,17 @@ WebContentHolder::~WebContentHolder() {
}
WebString WebContentHolder::GetValue() const {
return private_->nodeValue();
return private_->node()->nodeValue();
}
WebRect WebContentHolder::GetBoundingBox() const {
if (auto* layout_obj = private_->GetLayoutObject())
return EnclosingIntRect(layout_obj->VisualRectInDocument());
return IntRect();
return WebRect(private_->rect());
}
uint64_t WebContentHolder::GetId() const {
return reinterpret_cast<uint64_t>(private_.Get());
return reinterpret_cast<uint64_t>(private_->node());
}
WebContentHolder::WebContentHolder(Node& node) : private_(&node) {}
WebContentHolder::WebContentHolder(ContentHolder& holder) : private_(&holder) {}
} // namespace blink

@ -257,6 +257,7 @@ _CONFIG = [
'cc::PaintShader',
'cc::PaintWorkletInput',
'cc::NodeId',
'cc::NodeInfo',
# Chromium geometry types.
'gfx::Point',