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( ...@@ -69,6 +69,9 @@ void ContentCaptureSender::DidCaptureContent(
GetContentCaptureReceiver()->DidCaptureContent(frame_data, first_data); 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) { void ContentCaptureSender::DidRemoveContent(const std::vector<int64_t>& data) {
GetContentCaptureReceiver()->DidRemoveContent(data); GetContentCaptureReceiver()->DidRemoveContent(data);
} }
......
...@@ -42,9 +42,11 @@ class ContentCaptureSender : public content::RenderFrameObserver, ...@@ -42,9 +42,11 @@ class ContentCaptureSender : public content::RenderFrameObserver,
void DidCaptureContent( void DidCaptureContent(
const std::vector<scoped_refptr<blink::WebContentHolder>>& data, const std::vector<scoped_refptr<blink::WebContentHolder>>& data,
bool first_data) override; bool first_data) override;
void DidUpdateContent(
const std::vector<scoped_refptr<blink::WebContentHolder>>& data) override;
void DidRemoveContent(const std::vector<int64_t>& data) override; void DidRemoveContent(const std::vector<int64_t>& data) override;
// mojom::ContentCaptureSender // mojom::ContentCaptureSender:
void StartCapture() override; void StartCapture() override;
void StopCapture() override; void StopCapture() override;
......
...@@ -31,6 +31,10 @@ class WebContentCaptureClient { ...@@ -31,6 +31,10 @@ class WebContentCaptureClient {
const std::vector<scoped_refptr<WebContentHolder>>& content, const std::vector<scoped_refptr<WebContentHolder>>& content,
bool first_data) = 0; 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 // Invoked when the previously captured content is removed, |content_ids| is a
// list of removed content id. // list of removed content id.
virtual void DidRemoveContent(const std::vector<int64_t>& content_ids) = 0; virtual void DidRemoveContent(const std::vector<int64_t>& content_ids) = 0;
......
...@@ -69,6 +69,11 @@ void ContentCaptureManager::OnScrollPositionChanged() { ...@@ -69,6 +69,11 @@ void ContentCaptureManager::OnScrollPositionChanged() {
ScheduleTask(ContentCaptureTask::ScheduleReason::kScrolling); 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) { void ContentCaptureManager::Trace(Visitor* visitor) {
visitor->Trace(local_frame_root_); visitor->Trace(local_frame_root_);
visitor->Trace(task_session_); visitor->Trace(task_session_);
......
...@@ -36,6 +36,9 @@ class CORE_EXPORT ContentCaptureManager ...@@ -36,6 +36,9 @@ class CORE_EXPORT ContentCaptureManager
// Invokes when scroll position was changed. // Invokes when scroll position was changed.
void OnScrollPositionChanged(); void OnScrollPositionChanged();
// Invokes when text node content was changed.
void OnNodeTextChanged(const NodeHolder& node_holder);
// Invokes when the local_frame_root shutdown. // Invokes when the local_frame_root shutdown.
void Shutdown(); void Shutdown();
......
...@@ -77,23 +77,33 @@ void ContentCaptureTask::SendContent( ...@@ -77,23 +77,33 @@ void ContentCaptureTask::SendContent(
TaskSession::DocumentSession& doc_session) { TaskSession::DocumentSession& doc_session) {
auto* document = doc_session.GetDocument(); auto* document = doc_session.GetDocument();
DCHECK(document); DCHECK(document);
auto* client = GetWebContentCaptureClient(*document);
DCHECK(client);
if (histogram_reporter_) if (histogram_reporter_)
histogram_reporter_->OnSendContentStarted(); histogram_reporter_->OnSendContentStarted();
std::vector<scoped_refptr<WebContentHolder>> content_batch; std::vector<scoped_refptr<WebContentHolder>> content_batch;
content_batch.reserve(kBatchSize); 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) { while (content_batch.size() < kBatchSize) {
scoped_refptr<ContentHolder> content_holder = scoped_refptr<ContentHolder> content_holder;
doc_session.GetNextUnsentContentHolder(); if (sending_changed_content)
content_holder = doc_session.GetNextChangedContentHolder();
else
content_holder = doc_session.GetNextUnsentContentHolder();
if (!content_holder) if (!content_holder)
break; break;
content_batch.push_back( content_batch.push_back(
base::MakeRefCounted<WebContentHolder>(content_holder)); base::MakeRefCounted<WebContentHolder>(content_holder));
} }
if (!content_batch.empty()) { if (!content_batch.empty()) {
DCHECK(GetWebContentCaptureClient(*document)); if (sending_changed_content) {
GetWebContentCaptureClient(*document)->DidCaptureContent( client->DidUpdateContent(content_batch);
content_batch, !doc_session.FirstDataHasSent()); } else {
doc_session.SetFirstDataHasSent(); client->DidCaptureContent(content_batch, !doc_session.FirstDataHasSent());
doc_session.SetFirstDataHasSent();
}
} }
if (histogram_reporter_) if (histogram_reporter_)
histogram_reporter_->OnSendContentEnded(content_batch.size()); histogram_reporter_->OnSendContentEnded(content_batch.size());
...@@ -128,7 +138,8 @@ bool ContentCaptureTask::ProcessDocumentSession( ...@@ -128,7 +138,8 @@ bool ContentCaptureTask::ProcessDocumentSession(
return true; return true;
} }
while (doc_session.HasUnsentCapturedContent()) { while (doc_session.HasUnsentCapturedContent() ||
doc_session.HasUnsentChangedContent()) {
SendContent(doc_session); SendContent(doc_session);
if (ShouldPause()) { if (ShouldPause()) {
return !doc_session.HasUnsentData(); return !doc_session.HasUnsentData();
......
...@@ -55,6 +55,13 @@ class WebContentCaptureClientTestHelper : public WebContentCaptureClient { ...@@ -55,6 +55,13 @@ class WebContentCaptureClientTestHelper : public WebContentCaptureClient {
all_text_.push_back(d->GetValue().Utf8()); all_text_.push_back(d->GetValue().Utf8());
} }
void DidUpdateContent(
const std::vector<scoped_refptr<WebContentHolder>>& data) override {
updated_data_ = data;
for (auto d : data)
updated_text_.push_back(d->GetValue().Utf8());
}
void DidRemoveContent(const std::vector<int64_t>& data) override { void DidRemoveContent(const std::vector<int64_t>& data) override {
removed_data_ = data; removed_data_ = data;
} }
...@@ -65,22 +72,31 @@ class WebContentCaptureClientTestHelper : public WebContentCaptureClient { ...@@ -65,22 +72,31 @@ class WebContentCaptureClientTestHelper : public WebContentCaptureClient {
return data_; return data_;
} }
const std::vector<scoped_refptr<WebContentHolder>>& UpdatedData() const {
return updated_data_;
}
const std::vector<std::string>& AllText() const { return all_text_; } const std::vector<std::string>& AllText() const { return all_text_; }
const std::vector<std::string>& UpdatedText() const { return updated_text_; }
const std::vector<int64_t>& RemovedData() const { return removed_data_; } const std::vector<int64_t>& RemovedData() const { return removed_data_; }
void ResetResults() { void ResetResults() {
first_data_ = false; first_data_ = false;
data_.clear(); data_.clear();
updated_data_.clear();
removed_data_.clear(); removed_data_.clear();
} }
private: private:
bool first_data_ = false; bool first_data_ = false;
std::vector<scoped_refptr<WebContentHolder>> data_; std::vector<scoped_refptr<WebContentHolder>> data_;
std::vector<scoped_refptr<WebContentHolder>> updated_data_;
std::vector<int64_t> removed_data_; std::vector<int64_t> removed_data_;
NodeHolder::Type node_holder_type_; NodeHolder::Type node_holder_type_;
std::vector<std::string> all_text_; std::vector<std::string> all_text_;
std::vector<std::string> updated_text_;
}; };
class ContentCaptureTaskTestHelper : public ContentCaptureTask { class ContentCaptureTaskTestHelper : public ContentCaptureTask {
...@@ -525,6 +541,8 @@ class ContentCaptureSimTest ...@@ -525,6 +541,8 @@ class ContentCaptureSimTest
: public SimTest, : public SimTest,
public ::testing::WithParamInterface<NodeHolder::Type> { public ::testing::WithParamInterface<NodeHolder::Type> {
public: public:
static const char* kEditableContent;
ContentCaptureSimTest() : client_(GetParam()), child_client_(GetParam()) {} ContentCaptureSimTest() : client_(GetParam()), child_client_(GetParam()) {}
void SetUp() override { void SetUp() override {
SimTest::SetUp(); SimTest::SetUp();
...@@ -570,6 +588,15 @@ class ContentCaptureSimTest ...@@ -570,6 +588,15 @@ class ContentCaptureSimTest
child_frame_expected_text_.push_back("New Text"); child_frame_expected_text_.push_back("New Text");
} }
void InsertMainFrameEditableContent(const std::string& content,
unsigned offset) {
InsertNodeContent(GetDocument(), "editable_id", content, offset);
}
void DeleteMainFrameEditableContent(unsigned offset, unsigned length) {
DeleteNodeContent(GetDocument(), "editable_id", offset, length);
}
const std::vector<std::string>& MainFrameExpectedText() const { const std::vector<std::string>& MainFrameExpectedText() const {
return main_frame_expected_text_; return main_frame_expected_text_;
} }
...@@ -578,6 +605,12 @@ class ContentCaptureSimTest ...@@ -578,6 +605,12 @@ class ContentCaptureSimTest
return child_frame_expected_text_; return child_frame_expected_text_;
} }
void ReplaceMainFrameExpectedText(const std::string& old_text,
const std::string& new_text) {
std::replace(main_frame_expected_text_.begin(),
main_frame_expected_text_.end(), old_text, new_text);
}
private: private:
void SetupPage() { void SetupPage() {
SimRequest main_resource("https://example.com/test.html", "text/html"); SimRequest main_resource("https://example.com/test.html", "text/html");
...@@ -595,6 +628,7 @@ class ContentCaptureSimTest ...@@ -595,6 +628,7 @@ class ContentCaptureSimTest
<p id='p5'>Hello World5</p> <p id='p5'>Hello World5</p>
<p id='p6'>Hello World6</p> <p id='p6'>Hello World6</p>
<p id='p7'>Hello World7</p> <p id='p7'>Hello World7</p>
<div id='editable_id'>editable</div>
<svg> <svg>
<text id="s8">Hello World8</text> <text id="s8">Hello World8</text>
</svg> </svg>
...@@ -620,13 +654,14 @@ class ContentCaptureSimTest ...@@ -620,13 +654,14 @@ class ContentCaptureSimTest
} }
void InitMainFrameNodeHolders() { void InitMainFrameNodeHolders() {
std::vector<std::string> ids = {"p1", "p2", "p3", "p4", std::vector<std::string> ids = {"p1", "p2", "p3", "p4", "p5",
"p5", "p6", "p7", "s8"}; "p6", "p7", "s8", "editable_id"};
main_frame_expected_text_ = {"Hello World1", "Hello World2", "Hello World3", main_frame_expected_text_ = {
"Hello World4", "Hello World5", "Hello World6", "Hello World1", "Hello World2", "Hello World3",
"Hello World7", "Hello World8"}; "Hello World4", "Hello World5", "Hello World6",
"Hello World7", "Hello World8", kEditableContent};
InitNodeHolders(main_frame_content_, ids, GetDocument()); InitNodeHolders(main_frame_content_, ids, GetDocument());
EXPECT_EQ(8u, main_frame_content_.size()); EXPECT_EQ(9u, main_frame_content_.size());
} }
void InitChildFrameNodeHolders(const Document& doc) { void InitChildFrameNodeHolders(const Document& doc) {
...@@ -659,6 +694,25 @@ class ContentCaptureSimTest ...@@ -659,6 +694,25 @@ class ContentCaptureSimTest
buffer.insert(buffer.begin(), layout_text->EnsureNodeHolder()); buffer.insert(buffer.begin(), layout_text->EnsureNodeHolder());
} }
void InsertNodeContent(Document& doc,
const std::string& id,
const std::string& content,
unsigned offset) {
To<Text>(doc.getElementById(id.c_str())->firstChild())
->insertData(offset, String(content.c_str()),
IGNORE_EXCEPTION_FOR_TESTING);
Compositor().BeginFrame();
}
void DeleteNodeContent(Document& doc,
const std::string& id,
unsigned offset,
unsigned length) {
To<Text>(doc.getElementById(id.c_str())->firstChild())
->deleteData(offset, length, IGNORE_EXCEPTION_FOR_TESTING);
Compositor().BeginFrame();
}
void SetCapturedContent(const std::vector<NodeHolder>& captured_content) { void SetCapturedContent(const std::vector<NodeHolder>& captured_content) {
GetDocument() GetDocument()
.GetFrame() .GetFrame()
...@@ -677,6 +731,8 @@ class ContentCaptureSimTest ...@@ -677,6 +731,8 @@ class ContentCaptureSimTest
Persistent<Document> child_document_; Persistent<Document> child_document_;
}; };
const char* ContentCaptureSimTest::kEditableContent = "editable";
INSTANTIATE_TEST_SUITE_P(, INSTANTIATE_TEST_SUITE_P(,
ContentCaptureSimTest, ContentCaptureSimTest,
testing::Values(NodeHolder::Type::kID, testing::Values(NodeHolder::Type::kID,
...@@ -685,7 +741,7 @@ INSTANTIATE_TEST_SUITE_P(, ...@@ -685,7 +741,7 @@ INSTANTIATE_TEST_SUITE_P(,
TEST_P(ContentCaptureSimTest, MultiFrame) { TEST_P(ContentCaptureSimTest, MultiFrame) {
SetCapturedContent(ContentType::kAll); SetCapturedContent(ContentType::kAll);
RunContentCaptureTaskUntil(ContentCaptureTask::TaskState::kStop); RunContentCaptureTaskUntil(ContentCaptureTask::TaskState::kStop);
EXPECT_EQ(3u, Client().Data().size()); EXPECT_EQ(4u, Client().Data().size());
EXPECT_EQ(2u, ChildClient().Data().size()); EXPECT_EQ(2u, ChildClient().Data().size());
EXPECT_THAT(Client().AllText(), EXPECT_THAT(Client().AllText(),
testing::UnorderedElementsAreArray(MainFrameExpectedText())); testing::UnorderedElementsAreArray(MainFrameExpectedText()));
...@@ -710,7 +766,7 @@ TEST_P(ContentCaptureSimTest, AddNodeToMultiFrame) { ...@@ -710,7 +766,7 @@ TEST_P(ContentCaptureSimTest, AddNodeToMultiFrame) {
// Sends the reset of data // Sends the reset of data
RunContentCaptureTaskUntil(ContentCaptureTask::TaskState::kProcessRetryTask); RunContentCaptureTaskUntil(ContentCaptureTask::TaskState::kProcessRetryTask);
EXPECT_EQ(3u, Client().Data().size()); EXPECT_EQ(4u, Client().Data().size());
EXPECT_FALSE(Client().FirstData()); EXPECT_FALSE(Client().FirstData());
EXPECT_TRUE(ChildClient().Data().empty()); EXPECT_TRUE(ChildClient().Data().empty());
EXPECT_THAT(Client().AllText(), EXPECT_THAT(Client().AllText(),
...@@ -735,4 +791,104 @@ TEST_P(ContentCaptureSimTest, AddNodeToMultiFrame) { ...@@ -735,4 +791,104 @@ TEST_P(ContentCaptureSimTest, AddNodeToMultiFrame) {
EXPECT_TRUE(ChildClient().FirstData()); EXPECT_TRUE(ChildClient().FirstData());
} }
TEST_P(ContentCaptureSimTest, ChangeNode) {
SetCapturedContent(ContentType::kMainFrame);
RunContentCaptureTaskUntil(ContentCaptureTask::TaskState::kStop);
EXPECT_EQ(4u, Client().Data().size());
EXPECT_FALSE(Client().FirstData());
EXPECT_TRUE(ChildClient().Data().empty());
EXPECT_THAT(Client().AllText(),
testing::UnorderedElementsAreArray(MainFrameExpectedText()));
std::vector<std::string> expected_text_update;
std::string insert_text = "content ";
// Changed content to 'content editable'.
InsertMainFrameEditableContent(insert_text, 0);
SetCapturedContent(ContentType::kMainFrame);
RunContentCaptureTaskUntil(ContentCaptureTask::TaskState::kStop);
EXPECT_EQ(1u, Client().UpdatedData().size());
EXPECT_FALSE(Client().FirstData());
EXPECT_TRUE(ChildClient().Data().empty());
expected_text_update.push_back(insert_text + kEditableContent);
EXPECT_THAT(Client().UpdatedText(),
testing::UnorderedElementsAreArray(expected_text_update));
// Changing content multiple times before capturing.
std::string insert_text1 = "i";
// Changed content to 'content ieditable'.
InsertMainFrameEditableContent(insert_text1, insert_text.size());
std::string insert_text2 = "s ";
// Changed content to 'content is editable'.
InsertMainFrameEditableContent(insert_text2,
insert_text.size() + insert_text1.size());
SetCapturedContent(ContentType::kMainFrame);
RunContentCaptureTaskUntil(ContentCaptureTask::TaskState::kStop);
EXPECT_EQ(1u, Client().UpdatedData().size());
EXPECT_FALSE(Client().FirstData());
EXPECT_TRUE(ChildClient().Data().empty());
expected_text_update.push_back(insert_text + insert_text1 + insert_text2 +
kEditableContent);
EXPECT_THAT(Client().UpdatedText(),
testing::UnorderedElementsAreArray(expected_text_update));
}
TEST_P(ContentCaptureSimTest, ChangeNodeBeforeCapture) {
// Changed content to 'content editable' before capture.
std::string insert_text = "content ";
InsertMainFrameEditableContent(insert_text, 0);
// Changing content multiple times before capturing.
std::string insert_text1 = "i";
// Changed content to 'content ieditable'.
InsertMainFrameEditableContent(insert_text1, insert_text.size());
std::string insert_text2 = "s ";
// Changed content to 'content is editable'.
InsertMainFrameEditableContent(insert_text2,
insert_text.size() + insert_text1.size());
// The changed content shall be captured as new content.
ReplaceMainFrameExpectedText(
kEditableContent,
insert_text + insert_text1 + insert_text2 + kEditableContent);
SetCapturedContent(ContentType::kMainFrame);
RunContentCaptureTaskUntil(ContentCaptureTask::TaskState::kStop);
EXPECT_EQ(4u, Client().Data().size());
EXPECT_FALSE(Client().FirstData());
EXPECT_TRUE(ChildClient().Data().empty());
EXPECT_TRUE(ChildClient().UpdatedData().empty());
EXPECT_THAT(Client().AllText(),
testing::UnorderedElementsAreArray(MainFrameExpectedText()));
}
TEST_P(ContentCaptureSimTest, DeleteNodeContent) {
SetCapturedContent(ContentType::kMainFrame);
RunContentCaptureTaskUntil(ContentCaptureTask::TaskState::kStop);
EXPECT_EQ(4u, Client().Data().size());
EXPECT_FALSE(Client().FirstData());
EXPECT_TRUE(ChildClient().Data().empty());
EXPECT_THAT(Client().AllText(),
testing::UnorderedElementsAreArray(MainFrameExpectedText()));
// Deleted 4 char, changed content to 'edit'.
DeleteMainFrameEditableContent(4, 4);
SetCapturedContent(ContentType::kMainFrame);
RunContentCaptureTaskUntil(ContentCaptureTask::TaskState::kStop);
EXPECT_EQ(1u, Client().UpdatedData().size());
EXPECT_FALSE(Client().FirstData());
EXPECT_TRUE(ChildClient().Data().empty());
std::vector<std::string> expected_text_update;
expected_text_update.push_back("edit");
EXPECT_THAT(Client().UpdatedText(),
testing::UnorderedElementsAreArray(expected_text_update));
// Emptied content, the node shall be removed.
DeleteMainFrameEditableContent(0, 4);
SetCapturedContent(ContentType::kMainFrame);
RunContentCaptureTaskUntil(ContentCaptureTask::TaskState::kStop);
EXPECT_TRUE(Client().UpdatedData().empty());
EXPECT_FALSE(Client().FirstData());
EXPECT_TRUE(ChildClient().Data().empty());
EXPECT_EQ(1u, Client().RemovedData().size());
}
} // namespace blink } // namespace blink
...@@ -31,6 +31,11 @@ void TaskSession::DocumentSession::AddDetachedNode(int64_t id) { ...@@ -31,6 +31,11 @@ void TaskSession::DocumentSession::AddDetachedNode(int64_t id) {
detached_nodes_.push_back(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() { std::vector<int64_t> TaskSession::DocumentSession::MoveDetachedNodes() {
return std::move(detached_nodes_); return std::move(detached_nodes_);
} }
...@@ -64,12 +69,36 @@ TaskSession::DocumentSession::GetNextUnsentContentHolder() { ...@@ -64,12 +69,36 @@ TaskSession::DocumentSession::GetNextUnsentContentHolder() {
return content_holder; 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) { void TaskSession::DocumentSession::Trace(blink::Visitor* visitor) {
visitor->Trace(sent_nodes_); visitor->Trace(sent_nodes_);
visitor->Trace(document_); visitor->Trace(document_);
} }
void TaskSession::DocumentSession::Reset() { void TaskSession::DocumentSession::Reset() {
changed_content_.clear();
captured_content_.clear(); captured_content_.clear();
detached_nodes_.clear(); detached_nodes_.clear();
} }
...@@ -97,6 +126,19 @@ void TaskSession::SetCapturedContent( ...@@ -97,6 +126,19 @@ void TaskSession::SetCapturedContent(
void TaskSession::GroupCapturedContentByDocument( void TaskSession::GroupCapturedContentByDocument(
const std::vector<cc::NodeHolder>& captured_content) { const std::vector<cc::NodeHolder>& captured_content) {
for (const cc::NodeHolder& node_holder : 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)) { if (const Node* node = GetNodeIf(false /* sent */, node_holder)) {
EnsureDocumentSession(node->GetDocument()).AddNodeHolder(node_holder); EnsureDocumentSession(node->GetDocument()).AddNodeHolder(node_holder);
} }
...@@ -111,6 +153,12 @@ void TaskSession::OnNodeDetached(const cc::NodeHolder& 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 Node* TaskSession::GetNodeIf(bool sent,
const cc::NodeHolder& node_holder) const { const cc::NodeHolder& node_holder) const {
Node* node = nullptr; Node* node = nullptr;
...@@ -129,6 +177,19 @@ const Node* TaskSession::GetNodeIf(bool sent, ...@@ -129,6 +177,19 @@ const Node* TaskSession::GetNodeIf(bool sent,
return nullptr; 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( TaskSession::DocumentSession& TaskSession::EnsureDocumentSession(
const Document& doc) { const Document& doc) {
DocumentSession* doc_session = GetDocumentSession(doc); DocumentSession* doc_session = GetDocumentSession(doc);
...@@ -150,6 +211,7 @@ TaskSession::DocumentSession* TaskSession::GetDocumentSession( ...@@ -150,6 +211,7 @@ TaskSession::DocumentSession* TaskSession::GetDocumentSession(
void TaskSession::Trace(blink::Visitor* visitor) { void TaskSession::Trace(blink::Visitor* visitor) {
visitor->Trace(sent_nodes_); visitor->Trace(sent_nodes_);
visitor->Trace(changed_nodes_);
visitor->Trace(to_document_session_); visitor->Trace(to_document_session_);
} }
......
...@@ -54,10 +54,13 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> { ...@@ -54,10 +54,13 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> {
~DocumentSession(); ~DocumentSession();
void AddNodeHolder(cc::NodeHolder node_holder); void AddNodeHolder(cc::NodeHolder node_holder);
void AddDetachedNode(int64_t id); void AddDetachedNode(int64_t id);
void AddChangedNodeHolder(cc::NodeHolder node);
bool HasUnsentData() const { bool HasUnsentData() const {
return HasUnsentCapturedContent() || HasUnsentDetachedNodes(); return HasUnsentCapturedContent() || HasUnsentChangedContent() ||
HasUnsentDetachedNodes();
} }
bool HasUnsentCapturedContent() const { return !captured_content_.empty(); } bool HasUnsentCapturedContent() const { return !captured_content_.empty(); }
bool HasUnsentChangedContent() const { return !changed_content_.empty(); }
bool HasUnsentDetachedNodes() const { return !detached_nodes_.empty(); } bool HasUnsentDetachedNodes() const { return !detached_nodes_.empty(); }
std::vector<int64_t> MoveDetachedNodes(); std::vector<int64_t> MoveDetachedNodes();
const Document* GetDocument() const { return document_; } const Document* GetDocument() const { return document_; }
...@@ -68,6 +71,8 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> { ...@@ -68,6 +71,8 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> {
// ContentHolder. // ContentHolder.
scoped_refptr<ContentHolder> GetNextUnsentContentHolder(); scoped_refptr<ContentHolder> GetNextUnsentContentHolder();
scoped_refptr<ContentHolder> GetNextChangedContentHolder();
// Resets the |captured_content_| and the |detached_nodes_|, shall only be // 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 // used if those data doesn't need to be sent, e.g. there is no
// WebContentCaptureClient for this document. // WebContentCaptureClient for this document.
...@@ -83,6 +88,9 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> { ...@@ -83,6 +88,9 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> {
std::vector<int64_t> detached_nodes_; std::vector<int64_t> detached_nodes_;
WeakMember<const Document> document_; WeakMember<const Document> document_;
Member<SentNodes> sent_nodes_; 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; bool first_data_has_sent_ = false;
// This is for the metrics to record the total node that has been sent. // This is for the metrics to record the total node that has been sent.
size_t total_sent_nodes_ = 0; size_t total_sent_nodes_ = 0;
...@@ -102,6 +110,8 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> { ...@@ -102,6 +110,8 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> {
void OnNodeDetached(const cc::NodeHolder& node_holder); void OnNodeDetached(const cc::NodeHolder& node_holder);
void OnNodeChanged(const cc::NodeHolder& node_holder);
bool HasUnsentData() const { return has_unsent_data_; } bool HasUnsentData() const { return has_unsent_data_; }
void SetSentNodeCountCallback( void SetSentNodeCountCallback(
...@@ -119,9 +129,13 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> { ...@@ -119,9 +129,13 @@ class TaskSession : public GarbageCollectedFinalized<TaskSession> {
DocumentSession& EnsureDocumentSession(const Document& doc); DocumentSession& EnsureDocumentSession(const Document& doc);
DocumentSession* GetDocumentSession(const Document& document) const; DocumentSession* GetDocumentSession(const Document& document) const;
const Node* GetNodeIf(bool sent, const cc::NodeHolder& node_holder) 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_; 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. // This owns the DocumentSession which is released along with Document.
HeapHashMap<WeakMember<const Document>, Member<DocumentSession>> HeapHashMap<WeakMember<const Document>, Member<DocumentSession>>
to_document_session_; to_document_session_;
......
...@@ -1873,6 +1873,11 @@ void LayoutText::SetText(scoped_refptr<StringImpl> text, ...@@ -1873,6 +1873,11 @@ void LayoutText::SetText(scoped_refptr<StringImpl> text,
if (text_autosizer) if (text_autosizer)
text_autosizer->Record(this); text_autosizer->Record(this);
if (HasNodeHolder()) {
if (auto* content_capture_manager = GetContentCaptureManager())
content_capture_manager->OnNodeTextChanged(node_holder_);
}
valid_ng_items_ = false; valid_ng_items_ = false;
SetNeedsCollectInlines(); 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