Commit 52d8ec7a authored by Tao Bai's avatar Tao Bai Committed by Commit Bot

ContentCapture: Support content change

The some changes of element will not cause remove/add LayoutText,
instead, LayoutText is changed.

This patch supports the content change in this case, DidUpdateContent()
is added and invoked after new content is sent.

Bug: 952973
Change-Id: Ia530e7e71570801f1f494bd584526cc7dc3ccba8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1598310
Commit-Queue: Tao Bai <michaelbai@chromium.org>
Reviewed-by: default avatarTobias Sargeant <tobiasjs@chromium.org>
Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#659145}
parent 2611e35d
......@@ -69,6 +69,9 @@ void ContentCaptureSender::DidCaptureContent(
GetContentCaptureReceiver()->DidCaptureContent(frame_data, first_data);
}
void ContentCaptureSender::DidUpdateContent(
const std::vector<scoped_refptr<blink::WebContentHolder>>& data) {}
void ContentCaptureSender::DidRemoveContent(const std::vector<int64_t>& data) {
GetContentCaptureReceiver()->DidRemoveContent(data);
}
......
......@@ -42,9 +42,11 @@ class ContentCaptureSender : public content::RenderFrameObserver,
void DidCaptureContent(
const std::vector<scoped_refptr<blink::WebContentHolder>>& data,
bool first_data) override;
void DidUpdateContent(
const std::vector<scoped_refptr<blink::WebContentHolder>>& data) override;
void DidRemoveContent(const std::vector<int64_t>& data) override;
// mojom::ContentCaptureSender
// mojom::ContentCaptureSender:
void StartCapture() override;
void StopCapture() override;
......
......@@ -31,6 +31,10 @@ class WebContentCaptureClient {
const std::vector<scoped_refptr<WebContentHolder>>& content,
bool first_data) = 0;
// Invoked when a list of |content| is updated.
virtual void DidUpdateContent(
const std::vector<scoped_refptr<WebContentHolder>>& content) = 0;
// Invoked when the previously captured content is removed, |content_ids| is a
// list of removed content id.
virtual void DidRemoveContent(const std::vector<int64_t>& content_ids) = 0;
......
......@@ -69,6 +69,11 @@ void ContentCaptureManager::OnScrollPositionChanged() {
ScheduleTask(ContentCaptureTask::ScheduleReason::kScrolling);
}
void ContentCaptureManager::OnNodeTextChanged(const NodeHolder& node_holder) {
task_session_->OnNodeChanged(node_holder);
ScheduleTask(ContentCaptureTask::ScheduleReason::kContentChange);
}
void ContentCaptureManager::Trace(Visitor* visitor) {
visitor->Trace(local_frame_root_);
visitor->Trace(task_session_);
......
......@@ -36,6 +36,9 @@ class CORE_EXPORT ContentCaptureManager
// Invokes when scroll position was changed.
void OnScrollPositionChanged();
// Invokes when text node content was changed.
void OnNodeTextChanged(const NodeHolder& node_holder);
// Invokes when the local_frame_root shutdown.
void Shutdown();
......
......@@ -77,23 +77,33 @@ void ContentCaptureTask::SendContent(
TaskSession::DocumentSession& doc_session) {
auto* document = doc_session.GetDocument();
DCHECK(document);
auto* client = GetWebContentCaptureClient(*document);
DCHECK(client);
if (histogram_reporter_)
histogram_reporter_->OnSendContentStarted();
std::vector<scoped_refptr<WebContentHolder>> content_batch;
content_batch.reserve(kBatchSize);
// Only send changed content after the new content was sent.
bool sending_changed_content = !doc_session.HasUnsentCapturedContent();
while (content_batch.size() < kBatchSize) {
scoped_refptr<ContentHolder> content_holder =
doc_session.GetNextUnsentContentHolder();
scoped_refptr<ContentHolder> content_holder;
if (sending_changed_content)
content_holder = doc_session.GetNextChangedContentHolder();
else
content_holder = doc_session.GetNextUnsentContentHolder();
if (!content_holder)
break;
content_batch.push_back(
base::MakeRefCounted<WebContentHolder>(content_holder));
}
if (!content_batch.empty()) {
DCHECK(GetWebContentCaptureClient(*document));
GetWebContentCaptureClient(*document)->DidCaptureContent(
content_batch, !doc_session.FirstDataHasSent());
doc_session.SetFirstDataHasSent();
if (sending_changed_content) {
client->DidUpdateContent(content_batch);
} else {
client->DidCaptureContent(content_batch, !doc_session.FirstDataHasSent());
doc_session.SetFirstDataHasSent();
}
}
if (histogram_reporter_)
histogram_reporter_->OnSendContentEnded(content_batch.size());
......@@ -128,7 +138,8 @@ bool ContentCaptureTask::ProcessDocumentSession(
return true;
}
while (doc_session.HasUnsentCapturedContent()) {
while (doc_session.HasUnsentCapturedContent() ||
doc_session.HasUnsentChangedContent()) {
SendContent(doc_session);
if (ShouldPause()) {
return !doc_session.HasUnsentData();
......
......@@ -31,6 +31,11 @@ void TaskSession::DocumentSession::AddDetachedNode(int64_t id) {
detached_nodes_.push_back(id);
}
void TaskSession::DocumentSession::AddChangedNodeHolder(
cc::NodeHolder node_holder) {
changed_content_.push_back(node_holder);
}
std::vector<int64_t> TaskSession::DocumentSession::MoveDetachedNodes() {
return std::move(detached_nodes_);
}
......@@ -64,12 +69,36 @@ TaskSession::DocumentSession::GetNextUnsentContentHolder() {
return content_holder;
}
scoped_refptr<blink::ContentHolder>
TaskSession::DocumentSession::GetNextChangedContentHolder() {
scoped_refptr<ContentHolder> content_holder;
while (!changed_content_.empty() && !content_holder) {
auto node_holder = changed_content_.back();
if (node_holder.type == cc::NodeHolder::Type::kID) {
Node* node = DOMNodeIds::NodeForId(node_holder.id);
if (node && node->GetLayoutObject())
content_holder = base::MakeRefCounted<ContentHolder>(*node);
} else if (node_holder.type == cc::NodeHolder::Type::kTextHolder &&
node_holder.text_holder) {
content_holder = scoped_refptr<ContentHolder>(
static_cast<ContentHolder*>(node_holder.text_holder.get()));
if (content_holder && !content_holder->IsValid())
content_holder.reset();
}
changed_content_.pop_back();
}
if (content_holder)
total_sent_nodes_++;
return content_holder;
}
void TaskSession::DocumentSession::Trace(blink::Visitor* visitor) {
visitor->Trace(sent_nodes_);
visitor->Trace(document_);
}
void TaskSession::DocumentSession::Reset() {
changed_content_.clear();
captured_content_.clear();
detached_nodes_.clear();
}
......@@ -97,6 +126,19 @@ void TaskSession::SetCapturedContent(
void TaskSession::GroupCapturedContentByDocument(
const std::vector<cc::NodeHolder>& captured_content) {
for (const cc::NodeHolder& node_holder : captured_content) {
if (const Node* node = GetNode(node_holder)) {
node = changed_nodes_.Take(node);
if (node) {
// The changed node might not be sent.
if (GetNodeIf(true, node_holder)) {
EnsureDocumentSession(node->GetDocument())
.AddChangedNodeHolder(node_holder);
} else {
EnsureDocumentSession(node->GetDocument()).AddNodeHolder(node_holder);
}
continue;
}
}
if (const Node* node = GetNodeIf(false /* sent */, node_holder)) {
EnsureDocumentSession(node->GetDocument()).AddNodeHolder(node_holder);
}
......@@ -111,6 +153,12 @@ void TaskSession::OnNodeDetached(const cc::NodeHolder& node_holder) {
}
}
void TaskSession::OnNodeChanged(const cc::NodeHolder& node_holder) {
if (const Node* node = GetNode(node_holder)) {
changed_nodes_.insert(WeakMember<const Node>(node));
}
}
const Node* TaskSession::GetNodeIf(bool sent,
const cc::NodeHolder& node_holder) const {
Node* node = nullptr;
......@@ -129,6 +177,19 @@ const Node* TaskSession::GetNodeIf(bool sent,
return nullptr;
}
const Node* TaskSession::GetNode(const cc::NodeHolder& node_holder) const {
if (node_holder.type == cc::NodeHolder::Type::kID)
return DOMNodeIds::NodeForId(node_holder.id);
if (node_holder.type == cc::NodeHolder::Type::kTextHolder) {
ContentHolder* content_holder =
static_cast<ContentHolder*>(node_holder.text_holder.get());
if (content_holder)
return content_holder->GetNode();
}
return nullptr;
}
TaskSession::DocumentSession& TaskSession::EnsureDocumentSession(
const Document& doc) {
DocumentSession* doc_session = GetDocumentSession(doc);
......@@ -150,6 +211,7 @@ TaskSession::DocumentSession* TaskSession::GetDocumentSession(
void TaskSession::Trace(blink::Visitor* visitor) {
visitor->Trace(sent_nodes_);
visitor->Trace(changed_nodes_);
visitor->Trace(to_document_session_);
}
......
......@@ -54,10 +54,13 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> {
~DocumentSession();
void AddNodeHolder(cc::NodeHolder node_holder);
void AddDetachedNode(int64_t id);
void AddChangedNodeHolder(cc::NodeHolder node);
bool HasUnsentData() const {
return HasUnsentCapturedContent() || HasUnsentDetachedNodes();
return HasUnsentCapturedContent() || HasUnsentChangedContent() ||
HasUnsentDetachedNodes();
}
bool HasUnsentCapturedContent() const { return !captured_content_.empty(); }
bool HasUnsentChangedContent() const { return !changed_content_.empty(); }
bool HasUnsentDetachedNodes() const { return !detached_nodes_.empty(); }
std::vector<int64_t> MoveDetachedNodes();
const Document* GetDocument() const { return document_; }
......@@ -68,6 +71,8 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> {
// ContentHolder.
scoped_refptr<ContentHolder> GetNextUnsentContentHolder();
scoped_refptr<ContentHolder> GetNextChangedContentHolder();
// 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
// WebContentCaptureClient for this document.
......@@ -83,6 +88,9 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> {
std::vector<int64_t> detached_nodes_;
WeakMember<const Document> document_;
Member<SentNodes> sent_nodes_;
// The list of changed nodes that needs to be sent.
std::vector<cc::NodeHolder> changed_content_;
bool first_data_has_sent_ = false;
// This is for the metrics to record the total node that has been sent.
size_t total_sent_nodes_ = 0;
......@@ -102,6 +110,8 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> {
void OnNodeDetached(const cc::NodeHolder& node_holder);
void OnNodeChanged(const cc::NodeHolder& node_holder);
bool HasUnsentData() const { return has_unsent_data_; }
void SetSentNodeCountCallback(
......@@ -119,9 +129,13 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> {
DocumentSession& EnsureDocumentSession(const Document& doc);
DocumentSession* GetDocumentSession(const Document& document) const;
const Node* GetNodeIf(bool sent, const cc::NodeHolder& node_holder) const;
const Node* GetNode(const cc::NodeHolder& node_holder) const;
Member<SentNodes> sent_nodes_;
// The list of node whose value has changed.
HeapHashSet<WeakMember<const Node>> changed_nodes_;
// This owns the DocumentSession which is released along with Document.
HeapHashMap<WeakMember<const Document>, Member<DocumentSession>>
to_document_session_;
......
......@@ -1873,6 +1873,11 @@ void LayoutText::SetText(scoped_refptr<StringImpl> text,
if (text_autosizer)
text_autosizer->Record(this);
if (HasNodeHolder()) {
if (auto* content_capture_manager = GetContentCaptureManager())
content_capture_manager->OnNodeTextChanged(node_holder_);
}
valid_ng_items_ = false;
SetNeedsCollectInlines();
}
......
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