Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(778)

Unified Diff: content/browser/loader/navigation_resource_handler.cc

Issue 2797443005: PlzNavigate data pipe
Patch Set: cleanup Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/loader/navigation_resource_handler.cc
diff --git a/content/browser/loader/navigation_resource_handler.cc b/content/browser/loader/navigation_resource_handler.cc
index 5e6dd692da0c6a3b875e82c340a71b8d5f3e941a..49a9682a6c14350608c4184ba5a1d27c61636b46 100644
--- a/content/browser/loader/navigation_resource_handler.cc
+++ b/content/browser/loader/navigation_resource_handler.cc
@@ -21,11 +21,72 @@
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/stream_handle.h"
#include "content/public/common/resource_response.h"
+#include "net/base/io_buffer.h"
+#include "net/base/mime_sniffer.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_request.h"
namespace content {
+namespace {
+
+// TODO(kinuko): Most of the code below is dup'ed from MojoAsyncResourceHandler,
+// we should unify the implementation.
+
+// For MimeSniffingResourceHandler.
+constexpr size_t kMinAllocationSize = 2 * net::kMaxBytesToSniff;
+
+constexpr size_t kMaxChunkSize = 32 * 1024;
+
+constexpr size_t kDefaultAllocationSize = 512 * 1024;
+
+} // namespace
+
+// This class is for sharing the ownership of a ScopedDataPipeProducerHandle
+// between WriterIOBuffer and other class.
+class NavigationResourceHandler::SharedWriter final
+ : public base::RefCountedThreadSafe<SharedWriter> {
+ public:
+ explicit SharedWriter(mojo::ScopedDataPipeProducerHandle writer)
+ : writer_(std::move(writer)) {}
+ mojo::DataPipeProducerHandle writer() { return writer_.get(); }
+
+ private:
+ friend class base::RefCountedThreadSafe<SharedWriter>;
+ ~SharedWriter() {}
+
+ const mojo::ScopedDataPipeProducerHandle writer_;
+
+ DISALLOW_COPY_AND_ASSIGN(SharedWriter);
+};
+
+// This class is a IOBuffer subclass for data gotten from a
+// ScopedDataPipeProducerHandle.
+class NavigationResourceHandler::WriterIOBuffer final
+ : public net::IOBufferWithSize {
+ public:
+ // |data| and |size| should be gotten from |writer| via BeginWriteDataRaw.
+ // They will be accesible via IOBuffer methods. As |writer| is stored in this
+ // instance, |data| will be kept valid as long as the following conditions
+ // hold:
+ // 1. |data| is not invalidated via EndWriteDataRaw.
+ // 2. |this| instance is alive.
+ WriterIOBuffer(scoped_refptr<SharedWriter> writer, void* data, size_t size)
+ : net::IOBufferWithSize(static_cast<char*>(data), size),
+ writer_(std::move(writer)) {}
+
+ private:
+ ~WriterIOBuffer() override {
+ // Avoid deleting |data_| in the IOBuffer destructor.
+ data_ = nullptr;
+ }
+
+ // This member is for keeping the writer alive.
+ scoped_refptr<SharedWriter> writer_;
+
+ DISALLOW_COPY_AND_ASSIGN(WriterIOBuffer);
+};
+
void NavigationResourceHandler::GetSSLStatusForRequest(
const GURL& url,
const net::SSLInfo& ssl_info,
@@ -41,9 +102,9 @@ NavigationResourceHandler::NavigationResourceHandler(
ResourceDispatcherHostDelegate* resource_dispatcher_host_delegate)
: ResourceHandler(request),
core_(core),
- resource_dispatcher_host_delegate_(resource_dispatcher_host_delegate) {
+ resource_dispatcher_host_delegate_(resource_dispatcher_host_delegate),
+ handle_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL) {
core_->set_resource_handler(this);
- writer_.set_immediate_mode(true);
}
NavigationResourceHandler::~NavigationResourceHandler() {
@@ -95,13 +156,8 @@ void NavigationResourceHandler::OnResponseStarted(
ResourceRequestInfoImpl* info = GetRequestInfo();
- StreamContext* stream_context =
- GetStreamContextForResourceContext(info->GetContext());
- writer_.InitializeStream(
- stream_context->registry(), request()->url().GetOrigin(),
- base::Bind(&NavigationResourceHandler::OutOfBandCancel,
- base::Unretained(this), net::ERR_ABORTED,
- true /* tell_renderer */));
+ mojo::ScopedDataPipeConsumerHandle data_consumer_handle;
+ InitializeDataPipe(&data_consumer_handle);
NetLogObserver::PopulateResponseInfo(request(), response);
response->head.encoded_data_length = request()->raw_header_size();
@@ -123,7 +179,7 @@ void NavigationResourceHandler::OnResponseStarted(
info->GetChildID(), &ssl_status);
}
- core_->NotifyResponseStarted(response, writer_.stream()->CreateHandle(),
+ core_->NotifyResponseStarted(response, std::move(data_consumer_handle),
ssl_status, std::move(cloned_data),
info->GetGlobalRequestID(), info->IsDownload(),
info->is_stream());
@@ -159,7 +215,52 @@ void NavigationResourceHandler::OnWillRead(
int* buf_size,
std::unique_ptr<ResourceController> controller) {
DCHECK(!has_controller());
- writer_.OnWillRead(buf, buf_size, -1);
+
+ DCHECK(shared_writer_);
+ DCHECK(!buffer_);
+ DCHECK_EQ(0u, buffer_offset_);
+
+ bool first_call = first_read_;
+
+ if (first_read_) {
+ first_read_ = false;
+ handle_watcher_.Watch(shared_writer_->writer(), MOJO_HANDLE_SIGNAL_WRITABLE,
+ base::Bind(&NavigationResourceHandler::OnWritable,
+ base::Unretained(this)));
+ handle_watcher_.ArmOrNotify();
+ }
+
+ bool defer = false;
+ if (!AllocateWriterIOBuffer(&buffer_, &defer)) {
+ controller->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
+ return;
+ }
+
+ if (defer) {
+ parent_buffer_ = buf;
+ parent_buffer_size_ = buf_size;
+ HoldController(std::move(controller));
+ request()->LogBlockedBy("MojoStreamWriter");
+ did_defer_on_will_read_ = true;
+ return;
+ }
+
+ // The first call to OnWillRead must return a buffer of at least
+ // kMinAllocationSize. If the Mojo buffer is too small, need to allocate an
+ // intermediary buffer.
+ if (first_call && static_cast<size_t>(buffer_->size()) < kMinAllocationSize) {
+ // The allocated buffer is too small, so need to create an intermediary one.
+ if (EndWrite(0) != MOJO_RESULT_OK) {
+ controller->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
+ return;
+ }
+ DCHECK(!is_using_io_buffer_not_from_writer_);
+ is_using_io_buffer_not_from_writer_ = true;
+ buffer_ = new net::IOBufferWithSize(kMinAllocationSize);
+ }
+
+ *buf = buffer_;
+ *buf_size = buffer_->size();
controller->Resume();
}
@@ -167,17 +268,51 @@ void NavigationResourceHandler::OnReadCompleted(
int bytes_read,
std::unique_ptr<ResourceController> controller) {
DCHECK(!has_controller());
- writer_.OnReadCompleted(bytes_read,
- base::Bind(&ResourceController::Resume,
- base::Passed(std::move(controller))));
+ DCHECK_GE(bytes_read, 0);
+ DCHECK(buffer_);
+
+ if (bytes_read == 0) {
+ // Note that |buffer_| is not cleared here, which will cause a DCHECK on
+ // subsequent OnWillRead calls.
+ controller->Resume();
+ return;
+ }
+
+ if (is_using_io_buffer_not_from_writer_) {
+ // Couldn't allocate a large enough buffer on the data pipe in OnWillRead.
+ DCHECK_EQ(0u, buffer_bytes_read_);
+ buffer_bytes_read_ = bytes_read;
+ bool defer = false;
+ if (!CopyReadDataToDataPipe(&defer)) {
+ controller->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
+ return;
+ }
+ if (defer) {
+ request()->LogBlockedBy("NavigationResourceHandler");
+ did_defer_on_writing_ = true;
+ HoldController(std::move(controller));
+ return;
+ }
+ controller->Resume();
+ return;
+ }
+
+ if (EndWrite(bytes_read) != MOJO_RESULT_OK) {
+ controller->Cancel();
+ return;
+ }
+
+ buffer_ = nullptr;
+ controller->Resume();
}
void NavigationResourceHandler::OnResponseCompleted(
const net::URLRequestStatus& status,
std::unique_ptr<ResourceController> controller) {
- // If the request has already committed, close the stream and leave it as-is.
- if (writer_.stream()) {
- writer_.Finalize(status.error());
+ if (shared_writer_) {
+ shared_writer_ = nullptr;
+ buffer_ = nullptr;
+ handle_watcher_.Cancel();
controller->Resume();
return;
}
@@ -201,4 +336,122 @@ void NavigationResourceHandler::DetachFromCore() {
core_ = nullptr;
}
+void NavigationResourceHandler::InitializeDataPipe(
+ mojo::ScopedDataPipeConsumerHandle* consumer_handle) {
+ DCHECK(!shared_writer_);
+
+ MojoCreateDataPipeOptions options;
+ options.struct_size = sizeof(MojoCreateDataPipeOptions);
+ options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+ options.element_num_bytes = 1;
+ options.capacity_num_bytes = kDefaultAllocationSize;
+ mojo::DataPipe data_pipe(options);
+
+ DCHECK(data_pipe.producer_handle.is_valid());
+ DCHECK(data_pipe.consumer_handle.is_valid());
+
+ *consumer_handle = std::move(data_pipe.consumer_handle);
+ shared_writer_ = new SharedWriter(std::move(data_pipe.producer_handle));
+}
+
+MojoResult NavigationResourceHandler::BeginWrite(void** data,
+ uint32_t* available) {
+ MojoResult result = mojo::BeginWriteDataRaw(
+ shared_writer_->writer(), data, available, MOJO_WRITE_DATA_FLAG_NONE);
+ if (result == MOJO_RESULT_OK)
+ *available = std::min(*available, static_cast<uint32_t>(kMaxChunkSize));
+ else if (result == MOJO_RESULT_SHOULD_WAIT)
+ handle_watcher_.ArmOrNotify();
+ return result;
+}
+
+MojoResult NavigationResourceHandler::EndWrite(uint32_t written) {
+ MojoResult result = mojo::EndWriteDataRaw(shared_writer_->writer(), written);
+ if (result == MOJO_RESULT_OK)
+ handle_watcher_.ArmOrNotify();
+ return result;
+}
+
+bool NavigationResourceHandler::CopyReadDataToDataPipe(bool* defer) {
+ while (buffer_bytes_read_ > 0) {
+ scoped_refptr<net::IOBufferWithSize> dest;
+ if (!AllocateWriterIOBuffer(&dest, defer))
+ return false;
+ if (*defer)
+ return true;
+
+ size_t copied_size =
+ std::min(buffer_bytes_read_, static_cast<size_t>(dest->size()));
+ memcpy(dest->data(), buffer_->data() + buffer_offset_, copied_size);
+ buffer_offset_ += copied_size;
+ buffer_bytes_read_ -= copied_size;
+ if (EndWrite(copied_size) != MOJO_RESULT_OK)
+ return false;
+ }
+
+ // All bytes are copied.
+ buffer_ = nullptr;
+ buffer_offset_ = 0;
+ is_using_io_buffer_not_from_writer_ = false;
+ return true;
+}
+
+bool NavigationResourceHandler::AllocateWriterIOBuffer(
+ scoped_refptr<net::IOBufferWithSize>* buf,
+ bool* defer) {
+ void* data = nullptr;
+ uint32_t available = 0;
+ MojoResult result = BeginWrite(&data, &available);
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ *defer = true;
+ return true;
+ }
+ if (result != MOJO_RESULT_OK)
+ return false;
+ DCHECK_GT(available, 0u);
+ *buf = new WriterIOBuffer(shared_writer_, data, available);
+ return true;
+}
+
+void NavigationResourceHandler::OnWritable(MojoResult result) {
+ if (did_defer_on_will_read_) {
+ DCHECK(has_controller());
+ DCHECK(!did_defer_on_writing_);
+ DCHECK(!did_defer_on_redirect_);
+
+ did_defer_on_will_read_ = false;
+
+ scoped_refptr<net::IOBuffer>* parent_buffer = parent_buffer_;
+ parent_buffer_ = nullptr;
+ int* parent_buffer_size = parent_buffer_size_;
+ parent_buffer_size_ = nullptr;
+
+ request()->LogUnblocked();
+ OnWillRead(parent_buffer, parent_buffer_size, ReleaseController());
+ return;
+ }
+
+ if (!did_defer_on_writing_)
+ return;
+ DCHECK(has_controller());
+ DCHECK(!did_defer_on_redirect_);
+ did_defer_on_writing_ = false;
+
+ DCHECK(is_using_io_buffer_not_from_writer_);
+ // |buffer_| is set to a net::IOBufferWithSize. Write the buffer contents
+ // to the data pipe.
+ DCHECK_GT(buffer_bytes_read_, 0u);
+ if (!CopyReadDataToDataPipe(&did_defer_on_writing_)) {
+ CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
+ return;
+ }
+
+ if (did_defer_on_writing_) {
+ // Continue waiting.
+ return;
+ }
+ request()->LogUnblocked();
+ Resume();
+}
+
} // namespace content
« no previous file with comments | « content/browser/loader/navigation_resource_handler.h ('k') | content/browser/loader/navigation_url_loader_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698