Commit 4411acbd authored by Tao Bai's avatar Tao Bai Committed by Commit Bot

Send captured content per document

- Moves the session related code to TaskSession.
- TaskSession categorizes the captured content and the detached
  nodes into DocumentSession.
- ContentCaptureTask uses the DocumentSession to send the data
  though the LocalFrame's WebContentCaptureClient.

Bug: 924681
Change-Id: I8cd5c96c1ab183e7a567a70fef813881c8b2ab4a
Reviewed-on: https://chromium-review.googlesource.com/c/1493400Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Commit-Queue: Tao Bai <michaelbai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#636680}
parent 6c773a66
...@@ -12,5 +12,9 @@ blink_core_sources("content_capture") { ...@@ -12,5 +12,9 @@ blink_core_sources("content_capture") {
"content_capture_task.h", "content_capture_task.h",
"content_holder.cc", "content_holder.cc",
"content_holder.h", "content_holder.h",
"sent_nodes.cc",
"sent_nodes.h",
"task_session.cc",
"task_session.h",
] ]
} }
...@@ -5,13 +5,20 @@ ...@@ -5,13 +5,20 @@
#include "third_party/blink/renderer/core/content_capture/content_capture_manager.h" #include "third_party/blink/renderer/core/content_capture/content_capture_manager.h"
#include "third_party/blink/renderer/core/content_capture/content_holder.h" #include "third_party/blink/renderer/core/content_capture/content_holder.h"
#include "third_party/blink/renderer/core/content_capture/sent_nodes.h"
#include "third_party/blink/renderer/core/dom/dom_node_ids.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/layout/layout_text.h" #include "third_party/blink/renderer/core/layout/layout_text.h"
namespace blink { namespace blink {
ContentCaptureManager::ContentCaptureManager(Document& document, ContentCaptureManager::ContentCaptureManager(LocalFrame& local_frame_root,
NodeHolder::Type type) NodeHolder::Type type)
: document_(&document), node_holder_type_(type) {} : local_frame_root_(&local_frame_root), node_holder_type_(type) {
DCHECK(local_frame_root.IsLocalRoot());
sent_nodes_ = MakeGarbageCollected<SentNodes>();
task_session_ = MakeGarbageCollected<TaskSession>(*sent_nodes_);
}
ContentCaptureManager::~ContentCaptureManager() = default; ContentCaptureManager::~ContentCaptureManager() = default;
...@@ -37,23 +44,12 @@ void ContentCaptureManager::ScheduleTask( ...@@ -37,23 +44,12 @@ void ContentCaptureManager::ScheduleTask(
scoped_refptr<ContentCaptureTask> scoped_refptr<ContentCaptureTask>
ContentCaptureManager::CreateContentCaptureTask() { ContentCaptureManager::CreateContentCaptureTask() {
return base::MakeRefCounted<ContentCaptureTask>(*document_, *this); return base::MakeRefCounted<ContentCaptureTask>(*local_frame_root_,
*task_session_);
} }
void ContentCaptureManager::NotifyNodeDetached(const NodeHolder& node_holder) { void ContentCaptureManager::NotifyNodeDetached(const NodeHolder& node_holder) {
if (node_holder.type == NodeHolder::Type::kID) { task_session_->OnNodeDetached(node_holder);
Node* node = DOMNodeIds::NodeForId(node_holder.id);
if (node && HasSent(*node))
content_capture_idle_task_->OnNodeDetached(*node);
} else if (node_holder.type == NodeHolder::Type::kTextHolder) {
ContentHolder* content_holder =
static_cast<ContentHolder*>(node_holder.text_holder.get());
if (!content_holder || !content_holder->IsValid() ||
!content_holder->HasSent()) {
return;
}
content_capture_idle_task_->OnNodeDetached(*(content_holder->GetNode()));
}
} }
void ContentCaptureManager::OnLayoutTextWillBeDestroyed( void ContentCaptureManager::OnLayoutTextWillBeDestroyed(
...@@ -73,16 +69,9 @@ void ContentCaptureManager::OnScrollPositionChanged() { ...@@ -73,16 +69,9 @@ void ContentCaptureManager::OnScrollPositionChanged() {
ScheduleTask(ContentCaptureTask::ScheduleReason::kScrolling); ScheduleTask(ContentCaptureTask::ScheduleReason::kScrolling);
} }
bool ContentCaptureManager::HasSent(const Node& node) {
return sent_nodes_.Contains(&node);
}
void ContentCaptureManager::OnSent(const Node& node) {
sent_nodes_.insert(WeakMember<const Node>(&node));
}
void ContentCaptureManager::Trace(Visitor* visitor) { void ContentCaptureManager::Trace(Visitor* visitor) {
visitor->Trace(document_); visitor->Trace(local_frame_root_);
visitor->Trace(task_session_);
visitor->Trace(sent_nodes_); visitor->Trace(sent_nodes_);
} }
......
...@@ -9,21 +9,20 @@ ...@@ -9,21 +9,20 @@
#include "third_party/blink/renderer/core/content_capture/content_capture_task.h" #include "third_party/blink/renderer/core/content_capture/content_capture_task.h"
#include "third_party/blink/renderer/core/content_capture/content_holder.h" #include "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/core_export.h"
#include "third_party/blink/renderer/core/dom/dom_node_ids.h"
namespace blink { namespace blink {
class Document; class LocalFrame;
class Node; class Node;
class SentNodes;
// This class is used to create the NodeHolder, and start the ContentCaptureTask // This class is used to create the NodeHolder, and start the ContentCaptureTask
// when necessary. The ContentCaptureManager is owned by Document. // when necessary. The ContentCaptureManager is owned by main frame.
class CORE_EXPORT ContentCaptureManager class CORE_EXPORT ContentCaptureManager
: public GarbageCollectedFinalized<ContentCaptureManager>, : public GarbageCollectedFinalized<ContentCaptureManager> {
public ContentCaptureTask::Delegate {
public: public:
ContentCaptureManager(Document& document, NodeHolder::Type type); ContentCaptureManager(LocalFrame& local_frame_root, NodeHolder::Type type);
~ContentCaptureManager() override; virtual ~ContentCaptureManager();
// Creates and returns NodeHolder for the given |node|, and schedules // Creates and returns NodeHolder for the given |node|, and schedules
// ContentCaptureTask if it isn't already scheduled. // ContentCaptureTask if it isn't already scheduled.
...@@ -37,31 +36,26 @@ class CORE_EXPORT ContentCaptureManager ...@@ -37,31 +36,26 @@ class CORE_EXPORT ContentCaptureManager
// Invokes when scroll position was changed. // Invokes when scroll position was changed.
void OnScrollPositionChanged(); void OnScrollPositionChanged();
// Invokes when the document shutdown. // Invokes when the local_frame_root shutdown.
void Shutdown(); void Shutdown();
// ContentCaptureTask::Delegate, these methods have to be in this class
// because the node stores in HeapHashSet.
bool HasSent(const Node& node) override;
void OnSent(const Node& node) override;
void EnableContentCaptureTask() { should_capture_content_ = true; }
virtual void Trace(blink::Visitor*); virtual void Trace(blink::Visitor*);
ContentCaptureTask* GetContentCaptureTaskForTesting() const {
return content_capture_idle_task_.get();
}
protected: protected:
virtual scoped_refptr<ContentCaptureTask> CreateContentCaptureTask(); virtual scoped_refptr<ContentCaptureTask> CreateContentCaptureTask();
TaskSession& GetTaskSessionForTesting() const { return *task_session_; }
private: private:
void NotifyNodeDetached(const NodeHolder& node_holder); void NotifyNodeDetached(const NodeHolder& node_holder);
void ScheduleTask(ContentCaptureTask::ScheduleReason reason); void ScheduleTask(ContentCaptureTask::ScheduleReason reason);
// Indicates if the ContentCaptureTask should be started.
bool should_capture_content_ = false;
scoped_refptr<ContentCaptureTask> content_capture_idle_task_; scoped_refptr<ContentCaptureTask> content_capture_idle_task_;
Member<Document> document_; Member<LocalFrame> local_frame_root_;
// Indicates the NodeHolder::Type should be used. // Indicates the NodeHolder::Type should be used.
NodeHolder::Type node_holder_type_; NodeHolder::Type node_holder_type_;
...@@ -69,9 +63,10 @@ class CORE_EXPORT ContentCaptureManager ...@@ -69,9 +63,10 @@ class CORE_EXPORT ContentCaptureManager
// Indicates if the first NodeHolder is created. // Indicates if the first NodeHolder is created.
bool first_node_holder_created_ = false; bool first_node_holder_created_ = false;
// The list of nodes that have been sent, only used when Member<TaskSession> task_session_;
// |node_identification_method| is kNodeID.
HeapHashSet<WeakMember<const Node>> sent_nodes_; // A set of weak reference of the node that has been sent.
Member<SentNodes> sent_nodes_;
}; };
} // namespace blink } // namespace blink
......
...@@ -19,119 +19,116 @@ ...@@ -19,119 +19,116 @@
namespace blink { namespace blink {
ContentCaptureTask::ContentCaptureTask(Document& document, Delegate& delegate) ContentCaptureTask::ContentCaptureTask(LocalFrame& local_frame_root,
: document_(&document), delegate_(&delegate) {} TaskSession& task_session)
: local_frame_root_(&local_frame_root), task_session_(&task_session) {}
ContentCaptureTask::~ContentCaptureTask() {} ContentCaptureTask::~ContentCaptureTask() {}
void ContentCaptureTask::Shutdown() { void ContentCaptureTask::Shutdown() {
DCHECK(document_); DCHECK(local_frame_root_);
document_ = nullptr; local_frame_root_ = nullptr;
delegate_ = nullptr;
}
void ContentCaptureTask::OnNodeDetached(const Node& node) {
if (!session_) {
session_ = std::make_unique<Session>();
}
// TODO(michaelbai): might limit the size of detached_nodes.
session_->detached_nodes.push_back(reinterpret_cast<int64_t>(&node));
} }
bool ContentCaptureTask::CaptureContent(std::vector<cc::NodeHolder>& data) { bool ContentCaptureTask::CaptureContent(std::vector<cc::NodeHolder>& data) {
if (captured_content_for_testing_) {
data = captured_content_for_testing_.value();
return true;
}
// Because this is called from a different task, the frame may be in any // Because this is called from a different task, the frame may be in any
// lifecycle step so we need to early-out in many cases. // lifecycle step so we need to early-out in many cases.
// TODO(michaelbai): runs task in main frame, and sends the captured content if (const auto* root_frame_view = local_frame_root_->View()) {
// for each document separately. if (const auto* cc_layer = root_frame_view->RootCcLayer()) {
if (const auto* frame = document_->GetFrame()) { if (auto* layer_tree_host = cc_layer->layer_tree_host())
if (const auto* root_frame_view = frame->LocalFrameRoot().View()) { return layer_tree_host->CaptureContent(&data);
if (const auto* cc_layer = root_frame_view->RootCcLayer()) {
if (auto* layer_tree_host = cc_layer->layer_tree_host())
return layer_tree_host->CaptureContent(&data);
}
} }
} }
return false; return false;
} }
bool ContentCaptureTask::CaptureContent() { bool ContentCaptureTask::CaptureContent() {
DCHECK(session_); DCHECK(task_session_);
bool success = CaptureContent(session_->captured_content); std::vector<cc::NodeHolder> buffer;
session_->unsent = session_->captured_content.begin(); bool result = CaptureContent(buffer);
return success; if (!buffer.empty())
task_session_->SetCapturedContent(buffer);
return result;
} }
void ContentCaptureTask::SendContent() { void ContentCaptureTask::SendContent(
DCHECK(session_); TaskSession::DocumentSession& doc_session) {
auto* document = doc_session.GetDocument();
DCHECK(document);
std::vector<scoped_refptr<WebContentHolder>> content_batch; std::vector<scoped_refptr<WebContentHolder>> content_batch;
content_batch.reserve(kBatchSize); content_batch.reserve(kBatchSize);
for (; session_->unsent != session_->captured_content.end() && while (content_batch.size() < kBatchSize) {
content_batch.size() < kBatchSize; scoped_refptr<ContentHolder> content_holder =
++session_->unsent) { doc_session.GetNextUnsentContentHolder();
scoped_refptr<ContentHolder> content_holder; if (!content_holder)
if (session_->unsent->type == cc::NodeHolder::Type::kID) { break;
Node* node = DOMNodeIds::NodeForId(session_->unsent->id); content_batch.push_back(
if (node && node->GetLayoutObject() && !delegate_->HasSent(*node)) { base::MakeRefCounted<WebContentHolder>(content_holder));
content_holder = base::MakeRefCounted<ContentHolder>(*node);
delegate_->OnSent(*node);
content_batch.push_back(
base::MakeRefCounted<WebContentHolder>(content_holder));
}
} else if (session_->unsent->type == cc::NodeHolder::Type::kTextHolder &&
session_->unsent->text_holder) {
content_holder = scoped_refptr<ContentHolder>(
static_cast<ContentHolder*>(session_->unsent->text_holder.get()));
if (content_holder && content_holder->IsValid() &&
!content_holder->HasSent()) {
content_holder->SetHasSent();
content_batch.push_back(
base::MakeRefCounted<WebContentHolder>(content_holder));
}
}
} }
if (!content_batch.empty()) { if (!content_batch.empty()) {
GetWebContentCaptureClient()->DidCaptureContent(content_batch, DCHECK(GetWebContentCaptureClient(*document));
!has_first_data_sent_); GetWebContentCaptureClient(*document)->DidCaptureContent(
has_first_data_sent_ = true; content_batch, !doc_session.FirstDataHasSent());
doc_session.SetFirstDataHasSent();
} }
if (session_->unsent == session_->captured_content.end())
session_->captured_content.clear();
} }
WebContentCaptureClient* ContentCaptureTask::GetWebContentCaptureClient() { WebContentCaptureClient* ContentCaptureTask::GetWebContentCaptureClient(
// TODO(michaelbai): Enable this after integrate with document. const Document& document) {
// return document_->GetFrame()->Client()->GetContentCaptureClient(); if (auto* frame = document.GetFrame())
return frame->Client()->GetWebContentCaptureClient();
return nullptr; return nullptr;
} }
bool ContentCaptureTask::ProcessSession() { bool ContentCaptureTask::ProcessSession() {
DCHECK(session_); DCHECK(task_session_);
while (!session_->captured_content.empty()) { while (auto* document_session =
SendContent(); task_session_->GetNextUnsentDocumentSession()) {
if (!ProcessDocumentSession(*document_session))
return false;
if (ShouldPause())
return !task_session_->HasUnsentData();
}
return true;
}
bool ContentCaptureTask::ProcessDocumentSession(
TaskSession::DocumentSession& doc_session) {
// If no client, we don't need to send it at all.
auto* content_capture_client =
GetWebContentCaptureClient(*doc_session.GetDocument());
if (!content_capture_client) {
doc_session.Reset();
return true;
}
while (doc_session.HasUnsentCapturedContent()) {
SendContent(doc_session);
if (ShouldPause()) { if (ShouldPause()) {
return session_->captured_content.empty() && return !doc_session.HasUnsentData();
session_->detached_nodes.empty();
} }
} }
// Sent the detached nodes. // Sent the detached nodes.
if (!session_->detached_nodes.empty()) { if (doc_session.HasUnsentDetachedNodes())
GetWebContentCaptureClient()->DidRemoveContent(session_->detached_nodes); content_capture_client->DidRemoveContent(doc_session.MoveDetachedNodes());
session_->detached_nodes.clear(); DCHECK(!doc_session.HasUnsentData());
}
session_.reset();
return true; return true;
} }
bool ContentCaptureTask::RunInternal() { bool ContentCaptureTask::RunInternal() {
base::AutoReset<TaskState> state(&task_state_, TaskState::kProcessRetryTask); base::AutoReset<TaskState> state(&task_state_, TaskState::kProcessRetryTask);
// Already shutdown. // Already shutdown.
if (!document_ || !GetWebContentCaptureClient()) if (!local_frame_root_)
return true; return true;
do { do {
switch (task_state_) { switch (task_state_) {
case TaskState::kProcessRetryTask: case TaskState::kProcessRetryTask:
if (session_) { if (task_session_->HasUnsentData()) {
if (!ProcessSession()) if (!ProcessSession())
return false; return false;
} }
...@@ -140,13 +137,12 @@ bool ContentCaptureTask::RunInternal() { ...@@ -140,13 +137,12 @@ bool ContentCaptureTask::RunInternal() {
case TaskState::kCaptureContent: case TaskState::kCaptureContent:
if (!has_content_change_) if (!has_content_change_)
return true; return true;
session_ = std::make_unique<Session>();
if (!CaptureContent()) { if (!CaptureContent()) {
// Don't schedule task again in this case. // Don't schedule task again in this case.
return true; return true;
} }
has_content_change_ = false; has_content_change_ = false;
if (session_->captured_content.empty()) if (!task_session_->HasUnsentData())
return true; return true;
task_state_ = TaskState::kProcessCurrentSession; task_state_ = TaskState::kProcessCurrentSession;
...@@ -164,16 +160,13 @@ bool ContentCaptureTask::RunInternal() { ...@@ -164,16 +160,13 @@ bool ContentCaptureTask::RunInternal() {
void ContentCaptureTask::Run(TimerBase*) { void ContentCaptureTask::Run(TimerBase*) {
TRACE_EVENT0("blink", "CaptureContentTask::Run"); TRACE_EVENT0("blink", "CaptureContentTask::Run");
is_scheduled_ = false; is_scheduled_ = false;
bool success = RunInternal(); if (!RunInternal()) {
if (success) {
session_.reset();
} else {
ScheduleInternal(ScheduleReason::kRetryTask); ScheduleInternal(ScheduleReason::kRetryTask);
} }
} }
void ContentCaptureTask::ScheduleInternal(ScheduleReason reason) { void ContentCaptureTask::ScheduleInternal(ScheduleReason reason) {
DCHECK(document_); DCHECK(local_frame_root_);
if (is_scheduled_) if (is_scheduled_)
return; return;
...@@ -191,7 +184,7 @@ void ContentCaptureTask::ScheduleInternal(ScheduleReason reason) { ...@@ -191,7 +184,7 @@ void ContentCaptureTask::ScheduleInternal(ScheduleReason reason) {
if (!delay_task_) { if (!delay_task_) {
scoped_refptr<base::SingleThreadTaskRunner> task_runner = scoped_refptr<base::SingleThreadTaskRunner> task_runner =
document_->GetTaskRunner(TaskType::kInternalContentCapture); local_frame_root_->GetTaskRunner(TaskType::kInternalContentCapture);
delay_task_ = std::make_unique<TaskRunnerTimer<ContentCaptureTask>>( delay_task_ = std::make_unique<TaskRunnerTimer<ContentCaptureTask>>(
task_runner, this, &ContentCaptureTask::Run); task_runner, this, &ContentCaptureTask::Run);
} }
...@@ -202,12 +195,15 @@ void ContentCaptureTask::ScheduleInternal(ScheduleReason reason) { ...@@ -202,12 +195,15 @@ void ContentCaptureTask::ScheduleInternal(ScheduleReason reason) {
} }
void ContentCaptureTask::Schedule(ScheduleReason reason) { void ContentCaptureTask::Schedule(ScheduleReason reason) {
DCHECK(document_); DCHECK(local_frame_root_);
has_content_change_ = true; has_content_change_ = true;
ScheduleInternal(reason); ScheduleInternal(reason);
} }
bool ContentCaptureTask::ShouldPause() { bool ContentCaptureTask::ShouldPause() {
if (task_stop_for_testing_) {
return task_state_ == task_stop_for_testing_.value();
}
return ThreadScheduler::Current()->ShouldYieldForHighPriorityWork(); return ThreadScheduler::Current()->ShouldYieldForHighPriorityWork();
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <vector> #include <vector>
#include "cc/paint/node_holder.h" #include "cc/paint/node_holder.h"
#include "third_party/blink/renderer/core/content_capture/task_session.h"
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/timer.h" #include "third_party/blink/renderer/platform/timer.h"
#include "third_party/blink/renderer/platform/wtf/ref_counted.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
...@@ -17,7 +18,7 @@ namespace blink { ...@@ -17,7 +18,7 @@ namespace blink {
class WebContentCaptureClient; class WebContentCaptureClient;
class Document; class Document;
class Node; class LocalFrame;
// This class is used to capture the on-screen content and send them out // This class is used to capture the on-screen content and send them out
// through WebContentCaptureClient. // through WebContentCaptureClient.
...@@ -25,18 +26,6 @@ class CORE_EXPORT ContentCaptureTask : public RefCounted<ContentCaptureTask> { ...@@ -25,18 +26,6 @@ class CORE_EXPORT ContentCaptureTask : public RefCounted<ContentCaptureTask> {
USING_FAST_MALLOC(ContentCaptureTask); USING_FAST_MALLOC(ContentCaptureTask);
public: public:
// This class is used for DOMNodeIds.
class Delegate {
public:
// Return if the give |node| has been sent out.
virtual bool HasSent(const Node& node) = 0;
// Notify the |node| has been sent.
virtual void OnSent(const Node& node) = 0;
protected:
virtual ~Delegate() = default;
};
enum class ScheduleReason { enum class ScheduleReason {
kFirstContentChange, kFirstContentChange,
kContentChange, kContentChange,
...@@ -51,16 +40,13 @@ class CORE_EXPORT ContentCaptureTask : public RefCounted<ContentCaptureTask> { ...@@ -51,16 +40,13 @@ class CORE_EXPORT ContentCaptureTask : public RefCounted<ContentCaptureTask> {
kStop, kStop,
}; };
ContentCaptureTask(Document& document, Delegate& delegate); ContentCaptureTask(LocalFrame& local_frame_root, TaskSession& task_session);
virtual ~ContentCaptureTask(); virtual ~ContentCaptureTask();
// Schedule the task if it hasn't been done. // Schedule the task if it hasn't been done.
void Schedule(ScheduleReason reason); void Schedule(ScheduleReason reason);
void Shutdown(); void Shutdown();
// Invoked when the |node| is detached from LayoutTree.
void OnNodeDetached(const Node& node);
// Make those const public for testing purpose. // Make those const public for testing purpose.
static constexpr size_t kBatchSize = 5; static constexpr size_t kBatchSize = 5;
...@@ -71,24 +57,24 @@ class CORE_EXPORT ContentCaptureTask : public RefCounted<ContentCaptureTask> { ...@@ -71,24 +57,24 @@ class CORE_EXPORT ContentCaptureTask : public RefCounted<ContentCaptureTask> {
TaskState GetTaskStateForTesting() const { return task_state_; } TaskState GetTaskStateForTesting() const { return task_state_; }
void RunTaskForTestingUntil(TaskState stop_state) {
task_stop_for_testing_ = stop_state;
Run(nullptr);
}
void SetCapturedContentForTesting(
const std::vector<cc::NodeHolder>& captured_content) {
captured_content_for_testing_ = captured_content;
}
protected: protected:
// All protected data and methods are for testing purpose. // All protected data and methods are for testing purpose.
// Return true if the task should pause. // Return true if the task should pause.
// TODO(michaelbai): Uses RunTaskForTestingUntil().
virtual bool ShouldPause(); virtual bool ShouldPause();
virtual bool CaptureContent(std::vector<cc::NodeHolder>& data); virtual WebContentCaptureClient* GetWebContentCaptureClient(const Document&);
virtual WebContentCaptureClient* GetWebContentCaptureClient();
private: private:
struct Session {
// The list of the captured content.
std::vector<cc::NodeHolder> captured_content;
// The first NodeHolder in |captured_content| hasn't been sent.
std::vector<cc::NodeHolder>::iterator unsent = captured_content.end();
// The list of content id of node that has been detached from LayoutTree
// since the last running.
std::vector<int64_t> detached_nodes;
};
// Callback method of delay_task_, runs the content capture task and // Callback method of delay_task_, runs the content capture task and
// reschedule it if it necessary. // reschedule it if it necessary.
void Run(TimerBase*); void Run(TimerBase*);
...@@ -102,23 +88,27 @@ class CORE_EXPORT ContentCaptureTask : public RefCounted<ContentCaptureTask> { ...@@ -102,23 +88,27 @@ class CORE_EXPORT ContentCaptureTask : public RefCounted<ContentCaptureTask> {
// Runs the sub task to process the captured content and the detached nodes. // Runs the sub task to process the captured content and the detached nodes.
bool ProcessSession(); bool ProcessSession();
// Processes |doc_session|, return True if |doc_session| has been processed,
// otherwise, the process was interrupted because the task has to pause.
bool ProcessDocumentSession(TaskSession::DocumentSession& doc_session);
// Sends the captured content in batch. // Sends the captured content in batch.
void SendContent(); void SendContent(TaskSession::DocumentSession& doc_session);
void ScheduleInternal(ScheduleReason reason); void ScheduleInternal(ScheduleReason reason);
bool CaptureContent(std::vector<cc::NodeHolder>& data);
std::unique_ptr<Session> session_;
bool is_scheduled_ = false; bool is_scheduled_ = false;
// Indicates if there is content change since last run. // Indicates if there is content change since last run.
bool has_content_change_ = false; bool has_content_change_ = false;
// Indicates if first data has been sent out. UntracedMember<LocalFrame> local_frame_root_;
bool has_first_data_sent_ = false; UntracedMember<TaskSession> task_session_;
UntracedMember<Document> document_;
Delegate* delegate_;
std::unique_ptr<TaskRunnerTimer<ContentCaptureTask>> delay_task_; std::unique_ptr<TaskRunnerTimer<ContentCaptureTask>> delay_task_;
TaskState task_state_ = TaskState::kStop; TaskState task_state_ = TaskState::kStop;
base::Optional<TaskState> task_stop_for_testing_;
base::Optional<std::vector<cc::NodeHolder>> captured_content_for_testing_;
}; };
} // namespace blink } // namespace blink
......
...@@ -58,31 +58,16 @@ class WebContentCaptureClientTestHelper : public WebContentCaptureClient { ...@@ -58,31 +58,16 @@ class WebContentCaptureClientTestHelper : public WebContentCaptureClient {
class ContentCaptureTaskTestHelper : public ContentCaptureTask { class ContentCaptureTaskTestHelper : public ContentCaptureTask {
public: public:
ContentCaptureTaskTestHelper(Document& document, ContentCaptureTaskTestHelper(LocalFrame& local_frame_root,
Delegate& delegate, TaskSession& task_session,
WebContentCaptureClient& content_capture_client) WebContentCaptureClient& content_capture_client)
: ContentCaptureTask(document, delegate), : ContentCaptureTask(local_frame_root, task_session),
content_capture_client_(&content_capture_client) {} content_capture_client_(&content_capture_client) {}
void SetCapturedContent(const std::vector<cc::NodeHolder> captured_content) {
captured_content_ = captured_content;
}
void SetTaskStopState(TaskState state) { task_stop_state_ = state; } void SetTaskStopState(TaskState state) { task_stop_state_ = state; }
void ResetIsCaptureContentCalled() { is_capture_content_called_ = false; }
bool IsCaptureContentCalled() { return is_capture_content_called_; }
protected: protected:
bool CaptureContent(std::vector<cc::NodeHolder>& data) override { WebContentCaptureClient* GetWebContentCaptureClient(
is_capture_content_called_ = true; const Document& document) override {
for (auto cc : captured_content_)
data.push_back(cc);
return true;
}
WebContentCaptureClient* GetWebContentCaptureClient() override {
return content_capture_client_; return content_capture_client_;
} }
...@@ -91,21 +76,19 @@ class ContentCaptureTaskTestHelper : public ContentCaptureTask { ...@@ -91,21 +76,19 @@ class ContentCaptureTaskTestHelper : public ContentCaptureTask {
} }
private: private:
std::vector<cc::NodeHolder> captured_content_;
WebContentCaptureClient* content_capture_client_; WebContentCaptureClient* content_capture_client_;
TaskState task_stop_state_ = TaskState::kStop; TaskState task_stop_state_ = TaskState::kStop;
bool is_capture_content_called_ = false;
}; };
class ContentCaptureManagerTestHelper : public ContentCaptureManager { class ContentCaptureManagerTestHelper : public ContentCaptureManager {
public: public:
ContentCaptureManagerTestHelper( ContentCaptureManagerTestHelper(
Document& document, LocalFrame& local_frame_root,
WebContentCaptureClientTestHelper& content_capture_client) WebContentCaptureClientTestHelper& content_capture_client)
: ContentCaptureManager(document, : ContentCaptureManager(local_frame_root,
content_capture_client.GetNodeHolderType()) { content_capture_client.GetNodeHolderType()) {
content_capture_task_ = base::MakeRefCounted<ContentCaptureTaskTestHelper>( content_capture_task_ = base::MakeRefCounted<ContentCaptureTaskTestHelper>(
document, *this, content_capture_client); local_frame_root, GetTaskSessionForTesting(), content_capture_client);
} }
scoped_refptr<ContentCaptureTaskTestHelper> GetContentCaptureTask() { scoped_refptr<ContentCaptureTaskTestHelper> GetContentCaptureTask() {
...@@ -140,18 +123,18 @@ class ContentCaptureTest ...@@ -140,18 +123,18 @@ class ContentCaptureTest
"<p id='p7'>7</p>" "<p id='p7'>7</p>"
"<p id='p8'>8</p>"); "<p id='p8'>8</p>");
platform()->SetAutoAdvanceNowToPendingTasks(false); platform()->SetAutoAdvanceNowToPendingTasks(false);
// TODO(michaelbai): ContentCaptureManager should be get from Document. // TODO(michaelbai): ContentCaptureManager should be get from LocalFrame.
content_capture_client_ = content_capture_client_ =
std::make_unique<WebContentCaptureClientTestHelper>(GetParam()); std::make_unique<WebContentCaptureClientTestHelper>(GetParam());
content_capture_manager_ = content_capture_manager_ =
MakeGarbageCollected<ContentCaptureManagerTestHelper>( MakeGarbageCollected<ContentCaptureManagerTestHelper>(
GetDocument(), *content_capture_client_); GetFrame(), *content_capture_client_);
InitNodeHolders(); InitNodeHolders();
// Setup captured content to ContentCaptureTask, it isn't necessary once // Setup captured content to ContentCaptureTask, it isn't necessary once
// ContentCaptureManager is created by Document. // ContentCaptureManager is created by LocalFrame.
content_capture_manager_->GetContentCaptureTask()->SetCapturedContent( content_capture_manager_->GetContentCaptureTask()
node_holders_); ->SetCapturedContentForTesting(node_holders_);
} }
ContentCaptureManagerTestHelper* GetContentCaptureManager() const { ContentCaptureManagerTestHelper* GetContentCaptureManager() const {
...@@ -195,7 +178,6 @@ class ContentCaptureTest ...@@ -195,7 +178,6 @@ class ContentCaptureTest
private: private:
void ResetResult() { void ResetResult() {
GetContentCaptureTask()->ResetIsCaptureContentCalled();
GetWebContentCaptureClient()->ResetResults(); GetWebContentCaptureClient()->ResetResults();
} }
...@@ -242,7 +224,6 @@ TEST_P(ContentCaptureTest, PauseAndResume) { ...@@ -242,7 +224,6 @@ TEST_P(ContentCaptureTest, PauseAndResume) {
EXPECT_FALSE(GetWebContentCaptureClient()->FirstData()); EXPECT_FALSE(GetWebContentCaptureClient()->FirstData());
EXPECT_TRUE(GetWebContentCaptureClient()->Data().empty()); EXPECT_TRUE(GetWebContentCaptureClient()->Data().empty());
EXPECT_TRUE(GetWebContentCaptureClient()->RemovedData().empty()); EXPECT_TRUE(GetWebContentCaptureClient()->RemovedData().empty());
EXPECT_FALSE(GetContentCaptureTask()->IsCaptureContentCalled());
// The task stops before sends the captured content out. // The task stops before sends the captured content out.
GetContentCaptureTask()->SetTaskStopState( GetContentCaptureTask()->SetTaskStopState(
...@@ -251,7 +232,6 @@ TEST_P(ContentCaptureTest, PauseAndResume) { ...@@ -251,7 +232,6 @@ TEST_P(ContentCaptureTest, PauseAndResume) {
EXPECT_FALSE(GetWebContentCaptureClient()->FirstData()); EXPECT_FALSE(GetWebContentCaptureClient()->FirstData());
EXPECT_TRUE(GetWebContentCaptureClient()->Data().empty()); EXPECT_TRUE(GetWebContentCaptureClient()->Data().empty());
EXPECT_TRUE(GetWebContentCaptureClient()->RemovedData().empty()); EXPECT_TRUE(GetWebContentCaptureClient()->RemovedData().empty());
EXPECT_TRUE(GetContentCaptureTask()->IsCaptureContentCalled());
// The task should be stop at kProcessRetryTask because the captured content // The task should be stop at kProcessRetryTask because the captured content
// needs to be sent with 2 batch. // needs to be sent with 2 batch.
...@@ -274,7 +254,6 @@ TEST_P(ContentCaptureTest, PauseAndResume) { ...@@ -274,7 +254,6 @@ TEST_P(ContentCaptureTest, PauseAndResume) {
EXPECT_TRUE(GetWebContentCaptureClient()->RemovedData().empty()); EXPECT_TRUE(GetWebContentCaptureClient()->RemovedData().empty());
EXPECT_EQ(GetExpectedSecondResultSize(), EXPECT_EQ(GetExpectedSecondResultSize(),
GetWebContentCaptureClient()->Data().size()); GetWebContentCaptureClient()->Data().size());
EXPECT_FALSE(GetContentCaptureTask()->IsCaptureContentCalled());
} }
TEST_P(ContentCaptureTest, NodeOnlySendOnce) { TEST_P(ContentCaptureTest, NodeOnlySendOnce) {
...@@ -283,11 +262,9 @@ TEST_P(ContentCaptureTest, NodeOnlySendOnce) { ...@@ -283,11 +262,9 @@ TEST_P(ContentCaptureTest, NodeOnlySendOnce) {
EXPECT_FALSE(GetWebContentCaptureClient()->Data().empty()); EXPECT_FALSE(GetWebContentCaptureClient()->Data().empty());
EXPECT_EQ(GetExpectedSecondResultSize(), EXPECT_EQ(GetExpectedSecondResultSize(),
GetWebContentCaptureClient()->Data().size()); GetWebContentCaptureClient()->Data().size());
EXPECT_TRUE(GetContentCaptureTask()->IsCaptureContentCalled());
GetContentCaptureManager()->OnScrollPositionChanged(); GetContentCaptureManager()->OnScrollPositionChanged();
RunContentCaptureTask(); RunContentCaptureTask();
EXPECT_TRUE(GetContentCaptureTask()->IsCaptureContentCalled());
EXPECT_TRUE(GetWebContentCaptureClient()->Data().empty()); EXPECT_TRUE(GetWebContentCaptureClient()->Data().empty());
EXPECT_TRUE(GetWebContentCaptureClient()->RemovedData().empty()); EXPECT_TRUE(GetWebContentCaptureClient()->RemovedData().empty());
} }
...@@ -298,7 +275,6 @@ TEST_P(ContentCaptureTest, RemoveNodeBeforeSendingOut) { ...@@ -298,7 +275,6 @@ TEST_P(ContentCaptureTest, RemoveNodeBeforeSendingOut) {
ContentCaptureTask::TaskState::kProcessCurrentSession); ContentCaptureTask::TaskState::kProcessCurrentSession);
RunContentCaptureTask(); RunContentCaptureTask();
EXPECT_TRUE(GetWebContentCaptureClient()->Data().empty()); EXPECT_TRUE(GetWebContentCaptureClient()->Data().empty());
EXPECT_TRUE(GetContentCaptureTask()->IsCaptureContentCalled());
// Remove the node and sent the captured content out. // Remove the node and sent the captured content out.
RemoveNode(NodeHolders().at(0), Nodes().at(0)); RemoveNode(NodeHolders().at(0), Nodes().at(0));
...@@ -319,13 +295,43 @@ TEST_P(ContentCaptureTest, RemoveNodeBeforeSendingOut) { ...@@ -319,13 +295,43 @@ TEST_P(ContentCaptureTest, RemoveNodeBeforeSendingOut) {
EXPECT_EQ(0u, GetWebContentCaptureClient()->RemovedData().size()); EXPECT_EQ(0u, GetWebContentCaptureClient()->RemovedData().size());
} }
TEST_P(ContentCaptureTest, RemoveNodeInBetweenSendingOut) {
// Capture the content, but didn't send them.
GetContentCaptureTask()->SetTaskStopState(
ContentCaptureTask::TaskState::kProcessCurrentSession);
RunContentCaptureTask();
EXPECT_TRUE(GetWebContentCaptureClient()->Data().empty());
// Sends first batch.
GetContentCaptureTask()->SetTaskStopState(
ContentCaptureTask::TaskState::kProcessRetryTask);
RunContentCaptureTask();
EXPECT_EQ(GetExpectedFirstResultSize(),
GetWebContentCaptureClient()->Data().size());
EXPECT_EQ(0u, GetWebContentCaptureClient()->RemovedData().size());
// This depends on the DocumentSession returning the unsent nodes reversely.
// Remove the first node and sent the captured content out.
RemoveNode(NodeHolders().at(0), Nodes().at(0));
GetContentCaptureTask()->SetTaskStopState(
ContentCaptureTask::TaskState::kProcessRetryTask);
RunContentCaptureTask();
// Total 7 content returned instead of 8.
EXPECT_EQ(GetExpectedSecondResultSize() - 1,
GetWebContentCaptureClient()->Data().size());
EXPECT_EQ(0u, GetWebContentCaptureClient()->RemovedData().size());
RunContentCaptureTask();
// No removed node because it hasn't been sent out.
EXPECT_EQ(0u, GetWebContentCaptureClient()->Data().size());
EXPECT_EQ(0u, GetWebContentCaptureClient()->RemovedData().size());
}
TEST_P(ContentCaptureTest, RemoveNodeAfterSendingOut) { TEST_P(ContentCaptureTest, RemoveNodeAfterSendingOut) {
// Captures the content, but didn't send them. // Captures the content, but didn't send them.
GetContentCaptureTask()->SetTaskStopState( GetContentCaptureTask()->SetTaskStopState(
ContentCaptureTask::TaskState::kProcessCurrentSession); ContentCaptureTask::TaskState::kProcessCurrentSession);
RunContentCaptureTask(); RunContentCaptureTask();
EXPECT_TRUE(GetWebContentCaptureClient()->Data().empty()); EXPECT_TRUE(GetWebContentCaptureClient()->Data().empty());
EXPECT_TRUE(GetContentCaptureTask()->IsCaptureContentCalled());
// Sends first batch. // Sends first batch.
GetContentCaptureTask()->SetTaskStopState( GetContentCaptureTask()->SetTaskStopState(
......
// Copyright 2019 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/sent_nodes.h"
#include "third_party/blink/renderer/core/dom/node.h"
namespace blink {
bool SentNodes::HasSent(const Node& node) {
return sent_nodes_.Contains(&node);
}
void SentNodes::OnSent(const Node& node) {
sent_nodes_.insert(WeakMember<const Node>(&node));
}
void SentNodes::Trace(blink::Visitor* visitor) {
visitor->Trace(sent_nodes_);
}
} // namespace blink
// Copyright 2019 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_SENT_NODES_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_CONTENT_CAPTURE_SENT_NODES_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"
namespace blink {
class Node;
// The class manages a list of nodes that have been sent, is only used when
// kNodeID is used, see WebContentCaptureClient::GetNodeType().
class SentNodes : public GarbageCollectedFinalized<SentNodes> {
public:
bool HasSent(const Node& node);
void OnSent(const Node& node);
void Trace(blink::Visitor*);
private:
HeapHashSet<WeakMember<const Node>> sent_nodes_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_CONTENT_CAPTURE_SENT_NODES_H_
// Copyright 2019 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/task_session.h"
#include <utility>
#include "third_party/blink/renderer/core/content_capture/content_holder.h"
#include "third_party/blink/renderer/core/content_capture/sent_nodes.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/dom_node_ids.h"
namespace blink {
TaskSession::DocumentSession::DocumentSession(const Document& document,
SentNodes& sent_nodes)
: document_(&document), sent_nodes_(&sent_nodes) {}
TaskSession::DocumentSession::~DocumentSession() = default;
void TaskSession::DocumentSession::AddNodeHolder(cc::NodeHolder node_holder) {
captured_content_.push_back(node_holder);
}
void TaskSession::DocumentSession::AddDetachedNode(int64_t id) {
detached_nodes_.push_back(id);
}
std::vector<int64_t> TaskSession::DocumentSession::MoveDetachedNodes() {
return std::move(detached_nodes_);
}
scoped_refptr<blink::ContentHolder>
TaskSession::DocumentSession::GetNextUnsentContentHolder() {
scoped_refptr<ContentHolder> content_holder;
while (!captured_content_.empty() && !content_holder) {
auto node_holder = captured_content_.back();
if (node_holder.type == cc::NodeHolder::Type::kID) {
Node* node = DOMNodeIds::NodeForId(node_holder.id);
if (node && node->GetLayoutObject() && !sent_nodes_->HasSent(*node)) {
sent_nodes_->OnSent(*node);
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->HasSent()) {
content_holder->SetHasSent();
} else {
content_holder.reset();
}
}
captured_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() {
captured_content_.clear();
detached_nodes_.clear();
}
TaskSession::TaskSession(SentNodes& sent_nodes) : sent_nodes_(sent_nodes) {}
TaskSession::DocumentSession* TaskSession::GetNextUnsentDocumentSession() {
for (auto& doc : to_document_session_.Values()) {
if (!doc->HasUnsentData())
continue;
return doc;
}
has_unsent_data_ = false;
return nullptr;
}
void TaskSession::SetCapturedContent(
const std::vector<cc::NodeHolder>& captured_content) {
DCHECK(!HasUnsentData());
DCHECK(!captured_content.empty());
GroupCapturedContentByDocument(captured_content);
has_unsent_data_ = true;
}
void TaskSession::GroupCapturedContentByDocument(
const std::vector<cc::NodeHolder>& captured_content) {
for (const cc::NodeHolder& node_holder : captured_content) {
if (const Node* node = GetNodeIf(false /* sent */, node_holder)) {
EnsureDocumentSession(node->GetDocument()).AddNodeHolder(node_holder);
}
}
}
void TaskSession::OnNodeDetached(const cc::NodeHolder& node_holder) {
if (const Node* node = GetNodeIf(true /* sent */, node_holder)) {
EnsureDocumentSession(node->GetDocument())
.AddDetachedNode(reinterpret_cast<int64_t>(&node));
has_unsent_data_ = true;
}
}
const Node* TaskSession::GetNodeIf(bool sent,
const cc::NodeHolder& node_holder) const {
Node* node = nullptr;
if (node_holder.type == cc::NodeHolder::Type::kID) {
node = DOMNodeIds::NodeForId(node_holder.id);
if (node && (sent_nodes_->HasSent(*node) == sent))
return node;
} else if (node_holder.type == cc::NodeHolder::Type::kTextHolder) {
ContentHolder* content_holder =
static_cast<ContentHolder*>(node_holder.text_holder.get());
if (content_holder && content_holder->IsValid() &&
(content_holder->HasSent() == sent)) {
return content_holder->GetNode();
}
}
return nullptr;
}
TaskSession::DocumentSession& TaskSession::EnsureDocumentSession(
const Document& doc) {
DocumentSession* doc_session = GetDocumentSession(doc);
if (!doc_session) {
doc_session = MakeGarbageCollected<DocumentSession>(doc, *sent_nodes_);
to_document_session_.insert(&doc, doc_session);
}
return *doc_session;
}
TaskSession::DocumentSession* TaskSession::GetDocumentSession(
const Document& document) const {
auto it = to_document_session_.find(&document);
if (it == to_document_session_.end())
return nullptr;
return it->value;
}
void TaskSession::Trace(blink::Visitor* visitor) {
visitor->Trace(sent_nodes_);
visitor->Trace(to_document_session_);
}
} // namespace blink
// Copyright 2019 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_TASK_SESSION_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_CONTENT_CAPTURE_TASK_SESSION_H_
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "cc/paint/node_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"
namespace blink {
class ContentHolder;
class Document;
class SentNodes;
// This class wraps the captured content and the detached nodes that need to be
// sent out by the ContentCaptureTask, it has a Document to DocumentSession
// mapping, and all data is grouped by document. There are two sources of data:
//
// One is the captured content which is set by the ContentCaptureTask through
// SetCapturedContent() only if the task session is empty, i.e all data must be
// sent before capturing the on-screen content, the captured content is then
// grouped into DocumentSession.
//
// Another is the detached nodes which are set by the ContentCaptureManager,
// they are saved to the DocumentSession directly.
//
// ContentCaptureTask gets the data per document by using
// GetUnsentDocumentSession() and GetNextUnsentContentHolder(), and must send
// all data out before capturing on-screen content again.
class TaskSession : public GarbageCollectedFinalized<TaskSession> {
public:
// This class manages the captured content and the detached nodes per
// document, the data is moved to the ContentCaptureTask while required. This
// class has an instance per document, will be released while the associated
// document is GC-ed, see TaskSession::to_document_session_.
class DocumentSession : public GarbageCollectedFinalized<DocumentSession> {
public:
DocumentSession(const Document& document, SentNodes& sent_nodes);
~DocumentSession();
void AddNodeHolder(cc::NodeHolder node_holder);
void AddDetachedNode(int64_t id);
bool HasUnsentData() const {
return HasUnsentCapturedContent() || HasUnsentDetachedNodes();
}
bool HasUnsentCapturedContent() const { return !captured_content_.empty(); }
bool HasUnsentDetachedNodes() const { return !detached_nodes_.empty(); }
std::vector<int64_t> MoveDetachedNodes();
const Document* GetDocument() const { return document_; }
bool FirstDataHasSent() const { return first_data_has_sent_; }
void SetFirstDataHasSent() { first_data_has_sent_ = true; }
// Removes the unsent node from |captured_content_|, and returns it as
// ContentHolder.
scoped_refptr<ContentHolder> GetNextUnsentContentHolder();
// 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.
void Reset();
void Trace(blink::Visitor*);
private:
// The captured content that belongs to this document.
std::vector<cc::NodeHolder> captured_content_;
// The list of content id of node that has been detached from the
// LayoutTree.
std::vector<int64_t> detached_nodes_;
WeakMember<const Document> document_;
Member<SentNodes> sent_nodes_;
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;
};
TaskSession(SentNodes& sent_nodes);
// Returns the DocumentSession that hasn't been sent.
DocumentSession* GetNextUnsentDocumentSession();
// This can only be invoked when all data has been sent (i.e. HasUnsentData()
// returns False).
void SetCapturedContent(const std::vector<cc::NodeHolder>& captured_content);
void OnNodeDetached(const cc::NodeHolder& node_holder);
bool HasUnsentData() const { return has_unsent_data_; }
void Trace(blink::Visitor*);
private:
void GroupCapturedContentByDocument(
const std::vector<cc::NodeHolder>& captured_content);
DocumentSession& EnsureDocumentSession(const Document& doc);
DocumentSession* GetDocumentSession(const Document& document) const;
const Node* GetNodeIf(bool sent, const cc::NodeHolder& node_holder) const;
Member<SentNodes> sent_nodes_;
// This owns the DocumentSession which is released along with Document.
HeapHashMap<WeakMember<const Document>, Member<DocumentSession>>
to_document_session_;
// Because the captured content and the detached node are in the
// DocumentSession, this is used to avoid to iterate all document sessions
// to find out if there is any of them.
bool has_unsent_data_ = false;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_CONTENT_CAPTURE_TASK_SESSION_H_
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