Commit 89852235 authored by Dmitry Gozman's avatar Dmitry Gozman Committed by Commit Bot

DocumentLoader: simplify CommitData

- Merge data_buffer_ and committed_data_buffer_.
- Simplify blocked parser logic.

Reentrancy protection now accounts for any possible code path
leading to appending bytes to the parser: CommitData changes protection
flag in_commit_data_, while its only caller ProcessBufferData checks
the flag.

Bug: none
Change-Id: I1b019bf43ab73b0ebc3855169fd8ef075c911e9e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1830068Reviewed-by: default avatarNate Chapin <japhet@chromium.org>
Commit-Queue: Dmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#701379}
parent aaa67b1e
...@@ -127,8 +127,7 @@ DocumentLoader::DocumentLoader( ...@@ -127,8 +127,7 @@ DocumentLoader::DocumentLoader(
std::move(params_->service_worker_network_provider)), std::move(params_->service_worker_network_provider)),
was_blocked_after_csp_(false), was_blocked_after_csp_(false),
state_(kNotStarted), state_(kNotStarted),
committed_data_buffer_(nullptr), in_commit_data_(false),
in_data_received_(false),
data_buffer_(SharedBuffer::Create()), data_buffer_(SharedBuffer::Create()),
devtools_navigation_token_(params_->devtools_navigation_token), devtools_navigation_token_(params_->devtools_navigation_token),
had_sticky_activation_(params_->is_user_activated), had_sticky_activation_(params_->is_user_activated),
...@@ -575,7 +574,24 @@ void DocumentLoader::BodyDataReceived(base::span<const char> data) { ...@@ -575,7 +574,24 @@ void DocumentLoader::BodyDataReceived(base::span<const char> data) {
probe::DidReceiveData(probe::ToCoreProbeSink(GetFrame()), probe::DidReceiveData(probe::ToCoreProbeSink(GetFrame()),
main_resource_identifier_, this, data.data(), main_resource_identifier_, this, data.data(),
data.size()); data.size());
HandleData(data.data(), data.size());
TRACE_EVENT1("loading", "DocumentLoader::HandleData", "length", data.size());
DCHECK(data.data());
DCHECK(data.size());
DCHECK(!frame_->GetPage()->Paused());
time_of_last_data_received_ = clock_->NowTicks();
if (listing_ftp_directory_ || loading_mhtml_archive_) {
// 1) Ftp directory listings accumulate data buffer and transform it later
// to the actual document content.
// 2) Mhtml archives accumulate data buffer and parse it as mhtml later
// to retrieve the actual document content.
data_buffer_->Append(data.data(), data.size());
return;
}
ProcessDataBuffer(data.data(), data.size());
} }
void DocumentLoader::BodyLoadingFinished( void DocumentLoader::BodyLoadingFinished(
...@@ -659,10 +675,9 @@ void DocumentLoader::FinishedLoading(base::TimeTicks finish_time) { ...@@ -659,10 +675,9 @@ void DocumentLoader::FinishedLoading(base::TimeTicks finish_time) {
MainThreadDebugger::Instance()->IsPaused()); MainThreadDebugger::Instance()->IsPaused());
if (listing_ftp_directory_) { if (listing_ftp_directory_) {
scoped_refptr<SharedBuffer> buffer = GenerateFtpDirectoryListingHtml( data_buffer_ = GenerateFtpDirectoryListingHtml(
response_.CurrentRequestUrl(), data_buffer_.get()); response_.CurrentRequestUrl(), data_buffer_.get());
for (const auto& span : *buffer) ProcessDataBuffer();
CommitData(span.data(), span.size());
} }
if (loading_mhtml_archive_ && state_ < kCommitted) { if (loading_mhtml_archive_ && state_ < kCommitted) {
...@@ -689,7 +704,7 @@ void DocumentLoader::FinishedLoading(base::TimeTicks finish_time) { ...@@ -689,7 +704,7 @@ void DocumentLoader::FinishedLoading(base::TimeTicks finish_time) {
if (parser_) { if (parser_) {
if (parser_blocked_count_) { if (parser_blocked_count_) {
finished_loading_ = true; finish_loading_when_parser_resumed_ = true;
} else { } else {
parser_->Finish(); parser_->Finish();
parser_.Clear(); parser_.Clear();
...@@ -884,7 +899,6 @@ void DocumentLoader::FinishNavigationCommit(const AtomicString& mime_type, ...@@ -884,7 +899,6 @@ void DocumentLoader::FinishNavigationCommit(const AtomicString& mime_type,
void DocumentLoader::CommitData(const char* bytes, size_t length) { void DocumentLoader::CommitData(const char* bytes, size_t length) {
TRACE_EVENT1("loading", "DocumentLoader::CommitData", "length", length); TRACE_EVENT1("loading", "DocumentLoader::CommitData", "length", length);
DCHECK_GE(state_, kCommitted);
// This can happen if document.close() is called by an event handler while // This can happen if document.close() is called by an event handler while
// there's still pending incoming data. // there's still pending incoming data.
...@@ -894,16 +908,10 @@ void DocumentLoader::CommitData(const char* bytes, size_t length) { ...@@ -894,16 +908,10 @@ void DocumentLoader::CommitData(const char* bytes, size_t length) {
if (!frame_ || !frame_->GetDocument()->Parsing()) if (!frame_ || !frame_->GetDocument()->Parsing())
return; return;
base::AutoReset<bool> reentrancy_protector(&in_commit_data_, true);
if (length) if (length)
data_received_ = true; data_received_ = true;
parser_->AppendBytes(bytes, length);
if (parser_blocked_count_) {
if (!committed_data_buffer_)
committed_data_buffer_ = SharedBuffer::Create();
committed_data_buffer_->Append(bytes, length);
} else {
parser_->AppendBytes(bytes, length);
}
} }
mojom::CommitResult DocumentLoader::CommitSameDocumentNavigation( mojom::CommitResult DocumentLoader::CommitSameDocumentNavigation(
...@@ -1012,35 +1020,23 @@ void DocumentLoader::CommitSameDocumentNavigationInternal( ...@@ -1012,35 +1020,23 @@ void DocumentLoader::CommitSameDocumentNavigationInternal(
history_item); history_item);
} }
void DocumentLoader::HandleData(const char* data, size_t length) { void DocumentLoader::ProcessDataBuffer(const char* bytes, size_t length) {
TRACE_EVENT1("loading", "DocumentLoader::HandleData", "length", length); DCHECK_GE(state_, kCommitted);
DCHECK(data); if (parser_blocked_count_ || in_commit_data_) {
DCHECK(length); // 1) If parser is blocked, we buffer data and process it upon resume.
DCHECK(!frame_->GetPage()->Paused()); // 2) If this function is reentered, we defer processing of the additional
time_of_last_data_received_ = clock_->NowTicks(); // data to the top-level invocation. Reentrant calls can occur because
// of web platform (mis-)features that require running a nested run loop:
if (listing_ftp_directory_ || loading_mhtml_archive_) { // - alert(), confirm(), prompt()
data_buffer_->Append(data, length); // - Detach of plugin elements.
return; // - Synchronous XMLHTTPRequest
} if (bytes)
data_buffer_->Append(bytes, length);
if (in_data_received_) {
// If this function is reentered, defer processing of the additional data to
// the top-level invocation. Reentrant calls can occur because of web
// platform (mis-)features that require running a nested run loop:
// - alert(), confirm(), prompt()
// - Detach of plugin elements.
// - Synchronous XMLHTTPRequest
data_buffer_->Append(data, length);
return; return;
} }
base::AutoReset<bool> reentrancy_protector(&in_data_received_, true); if (bytes)
CommitData(data, length); CommitData(bytes, length);
ProcessDataBuffer();
}
void DocumentLoader::ProcessDataBuffer() {
// Process data received in reentrant invocations. Note that the invocations // Process data received in reentrant invocations. Note that the invocations
// of CommitData() may queue more data in reentrant invocations, so iterate // of CommitData() may queue more data in reentrant invocations, so iterate
// until it's empty. // until it's empty.
...@@ -1283,9 +1279,8 @@ void DocumentLoader::StartLoadingResponse() { ...@@ -1283,9 +1279,8 @@ void DocumentLoader::StartLoadingResponse() {
// Finish load of MHTML archives and empty documents. // Finish load of MHTML archives and empty documents.
if (main_resource) { if (main_resource) {
scoped_refptr<SharedBuffer> data(main_resource->Data()); data_buffer_ = main_resource->Data();
for (const auto& span : *data) ProcessDataBuffer();
CommitData(span.data(), span.size());
} }
if (loading_mhtml_archive_ || loading_url_as_empty_document_) { if (loading_mhtml_archive_ || loading_url_as_empty_document_) {
...@@ -1688,22 +1683,10 @@ void DocumentLoader::ResumeParser() { ...@@ -1688,22 +1683,10 @@ void DocumentLoader::ResumeParser() {
if (parser_blocked_count_ != 0) if (parser_blocked_count_ != 0)
return; return;
if (committed_data_buffer_ && !committed_data_buffer_->IsEmpty()) { ProcessDataBuffer();
// Don't recursively process data.
base::AutoReset<bool> reentrancy_protector(&in_data_received_, true);
// Append data to the parser that may have been received while the parser
// was blocked.
for (const auto& span : *committed_data_buffer_)
parser_->AppendBytes(span.data(), span.size());
committed_data_buffer_->Clear();
// DataReceived may be called in a nested message loop.
ProcessDataBuffer();
}
if (finished_loading_) { if (finish_loading_when_parser_resumed_) {
finished_loading_ = false; finish_loading_when_parser_resumed_ = false;
parser_->Finish(); parser_->Finish();
parser_.Clear(); parser_.Clear();
} }
......
...@@ -356,8 +356,6 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected<DocumentLoader>, ...@@ -356,8 +356,6 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected<DocumentLoader>,
FrameLoader& GetFrameLoader() const; FrameLoader& GetFrameLoader() const;
LocalFrameClient& GetLocalFrameClient() const; LocalFrameClient& GetLocalFrameClient() const;
void CommitData(const char* bytes, size_t length);
ContentSecurityPolicy* CreateCSP( ContentSecurityPolicy* CreateCSP(
const ResourceResponse&, const ResourceResponse&,
const base::Optional<WebOriginPolicy>& origin_policy); const base::Optional<WebOriginPolicy>& origin_policy);
...@@ -368,15 +366,15 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected<DocumentLoader>, ...@@ -368,15 +366,15 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected<DocumentLoader>,
void FinalizeMHTMLArchiveLoad(); void FinalizeMHTMLArchiveLoad();
void HandleRedirect(const KURL& current_request_url); void HandleRedirect(const KURL& current_request_url);
void HandleResponse(); void HandleResponse();
void HandleData(const char* data, size_t length);
void InitializeEmptyResponse(); void InitializeEmptyResponse();
bool ShouldReportTimingInfoToParent(); bool ShouldReportTimingInfoToParent();
void CommitData(const char* bytes, size_t length);
// Processes the data stored in the data_buffer_, used to avoid appending data // Processes the data stored in the data_buffer_, used to avoid appending data
// to the parser in a nested message loop. // to the parser in a nested message loop.
void ProcessDataBuffer(); void ProcessDataBuffer(const char* bytes = nullptr, size_t length = 0);
// Sends an intervention report if the page is being served as a preview. // Sends an intervention report if the page is being served as a preview.
void ReportPreviewsIntervention() const; void ReportPreviewsIntervention() const;
...@@ -477,11 +475,10 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected<DocumentLoader>, ...@@ -477,11 +475,10 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected<DocumentLoader>,
// Used to block the parser. // Used to block the parser.
int parser_blocked_count_ = 0; int parser_blocked_count_ = 0;
bool finished_loading_ = false; bool finish_loading_when_parser_resumed_ = false;
scoped_refptr<SharedBuffer> committed_data_buffer_;
// Used to protect against reentrancy into dataReceived(). // Used to protect against reentrancy into CommitData().
bool in_data_received_; bool in_commit_data_;
scoped_refptr<SharedBuffer> data_buffer_; scoped_refptr<SharedBuffer> data_buffer_;
base::UnguessableToken devtools_navigation_token_; base::UnguessableToken devtools_navigation_token_;
......
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