Commit 2a5c5826 authored by K. Moon's avatar K. Moon Committed by Commit Bot

Add basic chrome_pdf::BlinkUrlLoader state machine

Implements a state machine to bridge the gap between
chrome_pdf::UrlLoader's pull model and blink::WebAssociatedURLLoader's
push model.

This change only handles the basic state transitions; this is sufficient
to simulate an empty response. A future change will deal with handling
the data received by DidReceiveData().

Also fills in a basic blink::WebURLRequest, since BlinkUrlLoader calls
WebAssociatedLoader::LoadAsynchronously() now. Note that RequestContext
and RequestDestination must be set to pass certain security checks.

Bug: 1099022
Change-Id: I20840e44adda63816f8c7d6a531ffe106dd8bfdf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2411547
Commit-Queue: K. Moon <kmoon@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: default avatarDaniel Hosseinian <dhoss@chromium.org>
Cr-Commit-Position: refs/heads/master@{#807345}
parent 000405ee
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/check.h" #include "base/check_op.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/notreached.h" #include "base/notreached.h"
#include "pdf/ppapi_migration/callback.h" #include "pdf/ppapi_migration/callback.h"
...@@ -23,8 +23,14 @@ ...@@ -23,8 +23,14 @@
#include "ppapi/cpp/url_request_info.h" #include "ppapi/cpp/url_request_info.h"
#include "ppapi/cpp/url_response_info.h" #include "ppapi/cpp/url_response_info.h"
#include "ppapi/cpp/var.h" #include "ppapi/cpp/var.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/platform/web_url_response.h"
#include "third_party/blink/public/web/web_associated_url_loader.h" #include "third_party/blink/public/web/web_associated_url_loader.h"
#include "third_party/blink/public/web/web_associated_url_loader_options.h" #include "third_party/blink/public/web/web_associated_url_loader_options.h"
#include "url/gurl.h"
namespace chrome_pdf { namespace chrome_pdf {
...@@ -50,27 +56,42 @@ BlinkUrlLoader::BlinkUrlLoader(base::WeakPtr<Client> client) ...@@ -50,27 +56,42 @@ BlinkUrlLoader::BlinkUrlLoader(base::WeakPtr<Client> client)
BlinkUrlLoader::~BlinkUrlLoader() = default; BlinkUrlLoader::~BlinkUrlLoader() = default;
// Modeled on `content::PepperURLLoaderHost::OnHostMsgGrantUniversalAccess()`.
void BlinkUrlLoader::GrantUniversalAccess() { void BlinkUrlLoader::GrantUniversalAccess() {
DCHECK(!blink_loader_); DCHECK_EQ(state_, LoadingState::kWaitingToOpen);
grant_universal_access_ = true; grant_universal_access_ = true;
} }
// Modeled on `content::PepperURLLoaderHost::OnHostMsgOpen()`. // Modeled on `content::PepperURLLoaderHost::OnHostMsgOpen()`.
void BlinkUrlLoader::Open(const UrlRequest& request, ResultCallback callback) { void BlinkUrlLoader::Open(const UrlRequest& request, ResultCallback callback) {
DCHECK_EQ(state_, LoadingState::kWaitingToOpen);
DCHECK(callback);
state_ = LoadingState::kOpening;
open_callback_ = std::move(callback);
if (!client_) { if (!client_) {
std::move(callback).Run(PP_ERROR_FAILED); AbortLoad(PP_ERROR_FAILED);
return; return;
} }
// Modeled on `content::CreateWebURLRequest()`.
blink::WebURLRequest blink_request;
blink_request.SetUrl(GURL(request.url));
blink_request.SetHttpMethod(blink::WebString::FromASCII(request.method));
blink_request.SetRequestContext(blink::mojom::RequestContextType::PLUGIN);
blink_request.SetRequestDestination(
network::mojom::RequestDestination::kEmbed);
blink::WebAssociatedURLLoaderOptions options; blink::WebAssociatedURLLoaderOptions options;
options.grant_universal_access = grant_universal_access_; options.grant_universal_access = grant_universal_access_;
blink_loader_ = client_->CreateAssociatedURLLoader(options); blink_loader_ = client_->CreateAssociatedURLLoader(options);
if (!blink_loader_) { if (!blink_loader_) {
std::move(callback).Run(PP_ERROR_FAILED); AbortLoad(PP_ERROR_FAILED);
return; return;
} }
NOTIMPLEMENTED(); blink_loader_->LoadAsynchronously(blink_request, this);
} }
bool BlinkUrlLoader::GetDownloadProgress( bool BlinkUrlLoader::GetDownloadProgress(
...@@ -80,13 +101,26 @@ bool BlinkUrlLoader::GetDownloadProgress( ...@@ -80,13 +101,26 @@ bool BlinkUrlLoader::GetDownloadProgress(
return false; return false;
} }
// Modeled on `ppapi::proxy::URLLoaderResource::ReadResponseBody()`.
void BlinkUrlLoader::ReadResponseBody(base::span<char> buffer, void BlinkUrlLoader::ReadResponseBody(base::span<char> buffer,
ResultCallback callback) { ResultCallback callback) {
NOTIMPLEMENTED(); // Can be in `kLoadComplete` if still reading after loading finished.
DCHECK(state_ == LoadingState::kStreamingData ||
state_ == LoadingState::kLoadComplete)
<< static_cast<int>(state_);
DCHECK(!read_callback_);
DCHECK(callback);
read_callback_ = std::move(callback);
if (state_ == LoadingState::kLoadComplete)
RunReadCallback();
} }
// Modeled on `ppapi::proxy::URLLoadResource::Close()`.
void BlinkUrlLoader::Close() { void BlinkUrlLoader::Close() {
NOTIMPLEMENTED(); if (state_ != LoadingState::kLoadComplete)
AbortLoad(PP_ERROR_ABORTED);
} }
bool BlinkUrlLoader::WillFollowRedirect( bool BlinkUrlLoader::WillFollowRedirect(
...@@ -102,8 +136,14 @@ void BlinkUrlLoader::DidSendData(uint64_t bytes_sent, ...@@ -102,8 +136,14 @@ void BlinkUrlLoader::DidSendData(uint64_t bytes_sent,
NOTREACHED(); NOTREACHED();
} }
// Modeled on `content::PepperURLLoaderHost::DidReceiveResponse()`.
void BlinkUrlLoader::DidReceiveResponse(const blink::WebURLResponse& response) { void BlinkUrlLoader::DidReceiveResponse(const blink::WebURLResponse& response) {
NOTIMPLEMENTED(); DCHECK_EQ(state_, LoadingState::kOpening);
mutable_response().status_code = response.HttpStatusCode();
state_ = LoadingState::kStreamingData;
std::move(open_callback_).Run(PP_OK);
} }
void BlinkUrlLoader::DidDownloadData(uint64_t data_length) { void BlinkUrlLoader::DidDownloadData(uint64_t data_length) {
...@@ -121,14 +161,46 @@ void BlinkUrlLoader::DidReceiveCachedMetadata(const char* data, ...@@ -121,14 +161,46 @@ void BlinkUrlLoader::DidReceiveCachedMetadata(const char* data,
NOTREACHED(); NOTREACHED();
} }
// Modeled on `content::PepperURLLoaderHost::DidFinishLoading()`.
void BlinkUrlLoader::DidFinishLoading() { void BlinkUrlLoader::DidFinishLoading() {
NOTIMPLEMENTED(); DCHECK_EQ(state_, LoadingState::kStreamingData);
SetLoadComplete(PP_OK);
RunReadCallback();
} }
void BlinkUrlLoader::DidFail(const blink::WebURLError& error) { void BlinkUrlLoader::DidFail(const blink::WebURLError& error) {
NOTIMPLEMENTED(); NOTIMPLEMENTED();
} }
void BlinkUrlLoader::AbortLoad(int32_t result) {
DCHECK_LT(result, 0);
SetLoadComplete(result);
if (open_callback_) {
DCHECK(!read_callback_);
std::move(open_callback_).Run(complete_result_);
} else if (read_callback_) {
std::move(read_callback_).Run(complete_result_);
}
}
// TODO(crbug.com/1099022): Need to handle buffered data.
void BlinkUrlLoader::RunReadCallback() {
if (read_callback_)
std::move(read_callback_).Run(complete_result_);
}
void BlinkUrlLoader::SetLoadComplete(int32_t result) {
DCHECK_NE(state_, LoadingState::kLoadComplete);
DCHECK_LE(result, 0);
state_ = LoadingState::kLoadComplete;
complete_result_ = result;
blink_loader_.reset();
}
PepperUrlLoader::PepperUrlLoader(pp::InstanceHandle plugin_instance) PepperUrlLoader::PepperUrlLoader(pp::InstanceHandle plugin_instance)
: plugin_instance_(plugin_instance), pepper_loader_(plugin_instance) {} : plugin_instance_(plugin_instance), pepper_loader_(plugin_instance) {}
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "base/callback.h"
#include "base/containers/span.h" #include "base/containers/span.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/optional.h" #include "base/optional.h"
...@@ -152,10 +153,40 @@ class BlinkUrlLoader final : public UrlLoader, ...@@ -152,10 +153,40 @@ class BlinkUrlLoader final : public UrlLoader,
void DidFail(const blink::WebURLError& error) override; void DidFail(const blink::WebURLError& error) override;
private: private:
enum class LoadingState {
// Before calling `Open()`.
kWaitingToOpen,
// After calling `Open()`, but before `DidReceiveResponse()` or `DidFail()`.
kOpening,
// After `DidReceiveResponse()`, but before `DidFinishLoading()` or
// `DidFail()`. Zero or more calls allowed to `DidReceiveData()`.
kStreamingData,
// After `DidFinishLoading()` or `DidFail()`, or forced by `Close()`.
// Details about how the load completed are in `complete_result_`.
kLoadComplete,
};
// Aborts the load with `result`. Runs callback if pending.
void AbortLoad(int32_t result);
// Runs callback for `ReadResponseBody()` if pending.
void RunReadCallback();
void SetLoadComplete(int32_t result);
base::WeakPtr<Client> client_; base::WeakPtr<Client> client_;
bool grant_universal_access_ = false; bool grant_universal_access_ = false;
LoadingState state_ = LoadingState::kWaitingToOpen;
int32_t complete_result_ = 0;
std::unique_ptr<blink::WebAssociatedURLLoader> blink_loader_; std::unique_ptr<blink::WebAssociatedURLLoader> blink_loader_;
ResultCallback open_callback_;
ResultCallback read_callback_;
}; };
// A Pepper URL loader. // A Pepper URL loader.
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/containers/span.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/test/mock_callback.h" #include "base/test/mock_callback.h"
...@@ -16,10 +17,15 @@ ...@@ -16,10 +17,15 @@
#include "ppapi/c/pp_errors.h" #include "ppapi/c/pp_errors.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_request.h" #include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/platform/web_url_response.h"
#include "third_party/blink/public/web/web_associated_url_loader.h" #include "third_party/blink/public/web/web_associated_url_loader.h"
#include "third_party/blink/public/web/web_associated_url_loader_client.h" #include "third_party/blink/public/web/web_associated_url_loader_client.h"
#include "third_party/blink/public/web/web_associated_url_loader_options.h" #include "third_party/blink/public/web/web_associated_url_loader_options.h"
#include "url/gurl.h"
namespace chrome_pdf { namespace chrome_pdf {
namespace { namespace {
...@@ -69,6 +75,9 @@ class BlinkUrlLoaderTest : public testing::Test { ...@@ -69,6 +75,9 @@ class BlinkUrlLoaderTest : public testing::Test {
ON_CALL(mock_client_, CreateAssociatedURLLoader(_)) ON_CALL(mock_client_, CreateAssociatedURLLoader(_))
.WillByDefault( .WillByDefault(
Invoke(this, &BlinkUrlLoaderTest::FakeCreateAssociatedURLLoader)); Invoke(this, &BlinkUrlLoaderTest::FakeCreateAssociatedURLLoader));
ON_CALL(*mock_url_loader_, LoadAsynchronously(_, _))
.WillByDefault(
Invoke(this, &BlinkUrlLoaderTest::FakeLoadAsynchronously));
loader_ = std::make_unique<BlinkUrlLoader>(mock_client_.GetWeakPtr()); loader_ = std::make_unique<BlinkUrlLoader>(mock_client_.GetWeakPtr());
} }
...@@ -79,13 +88,20 @@ class BlinkUrlLoaderTest : public testing::Test { ...@@ -79,13 +88,20 @@ class BlinkUrlLoaderTest : public testing::Test {
return std::move(mock_url_loader_); return std::move(mock_url_loader_);
} }
void FakeLoadAsynchronously(const blink::WebURLRequest& request,
blink::WebAssociatedURLLoaderClient* client) {
saved_request_.CopyFrom(request);
EXPECT_EQ(loader_.get(), client);
}
NiceMock<MockBlinkUrlLoaderClient> mock_client_; NiceMock<MockBlinkUrlLoaderClient> mock_client_;
base::MockCallback<ResultCallback> mock_callback_; NiceMock<base::MockCallback<ResultCallback>> mock_callback_;
std::unique_ptr<BlinkUrlLoader> loader_; std::unique_ptr<BlinkUrlLoader> loader_;
std::unique_ptr<MockWebAssociatedURLLoader> mock_url_loader_ = std::unique_ptr<NiceMock<MockWebAssociatedURLLoader>> mock_url_loader_ =
std::make_unique<MockWebAssociatedURLLoader>(); std::make_unique<NiceMock<MockWebAssociatedURLLoader>>();
blink::WebAssociatedURLLoaderOptions saved_options_; blink::WebAssociatedURLLoaderOptions saved_options_;
blink::WebURLRequest saved_request_;
}; };
TEST_F(BlinkUrlLoaderTest, GrantUniversalAccess) { TEST_F(BlinkUrlLoaderTest, GrantUniversalAccess) {
...@@ -96,14 +112,26 @@ TEST_F(BlinkUrlLoaderTest, GrantUniversalAccess) { ...@@ -96,14 +112,26 @@ TEST_F(BlinkUrlLoaderTest, GrantUniversalAccess) {
TEST_F(BlinkUrlLoaderTest, Open) { TEST_F(BlinkUrlLoaderTest, Open) {
EXPECT_CALL(mock_client_, CreateAssociatedURLLoader(_)); EXPECT_CALL(mock_client_, CreateAssociatedURLLoader(_));
EXPECT_CALL(*mock_url_loader_, LoadAsynchronously(_, _));
EXPECT_CALL(mock_callback_, Run(_)).Times(0); EXPECT_CALL(mock_callback_, Run(_)).Times(0);
loader_->Open(UrlRequest(), mock_callback_.Get()); UrlRequest request;
request.url = "http://example.com/fake.pdf";
request.method = "FAKE";
loader_->Open(request, mock_callback_.Get());
EXPECT_FALSE(saved_options_.grant_universal_access); EXPECT_FALSE(saved_options_.grant_universal_access);
EXPECT_EQ(GURL("http://example.com/fake.pdf"), GURL(saved_request_.Url()));
EXPECT_EQ("FAKE", saved_request_.HttpMethod().Ascii());
EXPECT_EQ(blink::mojom::RequestContextType::PLUGIN,
saved_request_.GetRequestContext());
EXPECT_EQ(network::mojom::RequestDestination::kEmbed,
saved_request_.GetRequestDestination());
} }
TEST_F(BlinkUrlLoaderTest, OpenWithInvalidatedClient) { TEST_F(BlinkUrlLoaderTest, OpenWithInvalidatedClient) {
EXPECT_CALL(mock_client_, CreateAssociatedURLLoader(_)).Times(0); EXPECT_CALL(mock_client_, CreateAssociatedURLLoader(_)).Times(0);
EXPECT_CALL(*mock_url_loader_, LoadAsynchronously(_, _)).Times(0);
EXPECT_CALL(mock_callback_, Run(PP_ERROR_FAILED)); EXPECT_CALL(mock_callback_, Run(PP_ERROR_FAILED));
mock_client_.InvalidateWeakPtrs(); mock_client_.InvalidateWeakPtrs();
...@@ -113,10 +141,115 @@ TEST_F(BlinkUrlLoaderTest, OpenWithInvalidatedClient) { ...@@ -113,10 +141,115 @@ TEST_F(BlinkUrlLoaderTest, OpenWithInvalidatedClient) {
TEST_F(BlinkUrlLoaderTest, OpenWithFailingCreateAssociatedURLLoader) { TEST_F(BlinkUrlLoaderTest, OpenWithFailingCreateAssociatedURLLoader) {
EXPECT_CALL(mock_client_, CreateAssociatedURLLoader(_)) EXPECT_CALL(mock_client_, CreateAssociatedURLLoader(_))
.WillOnce(ReturnNull()); .WillOnce(ReturnNull());
EXPECT_CALL(*mock_url_loader_, LoadAsynchronously(_, _)).Times(0);
EXPECT_CALL(mock_callback_, Run(PP_ERROR_FAILED)); EXPECT_CALL(mock_callback_, Run(PP_ERROR_FAILED));
loader_->Open(UrlRequest(), mock_callback_.Get()); loader_->Open(UrlRequest(), mock_callback_.Get());
} }
TEST_F(BlinkUrlLoaderTest, DidReceiveResponse) {
loader_->Open(UrlRequest(), mock_callback_.Get());
EXPECT_CALL(mock_callback_, Run(PP_OK));
blink::WebURLResponse response;
response.SetHttpStatusCode(204);
loader_->DidReceiveResponse(response);
EXPECT_EQ(204, loader_->response().status_code);
}
TEST_F(BlinkUrlLoaderTest, ReadResponseBody) {
loader_->Open(UrlRequest(), mock_callback_.Get());
loader_->DidReceiveResponse(blink::WebURLResponse());
EXPECT_CALL(mock_callback_, Run(_)).Times(0);
char buffer[1];
loader_->ReadResponseBody(buffer, mock_callback_.Get());
}
TEST_F(BlinkUrlLoaderTest, ReadResponseBodyWithEmptyBuffer) {
loader_->Open(UrlRequest(), mock_callback_.Get());
loader_->DidReceiveResponse(blink::WebURLResponse());
EXPECT_CALL(mock_callback_, Run(_)).Times(0);
loader_->ReadResponseBody(base::span<char>(), mock_callback_.Get());
}
TEST_F(BlinkUrlLoaderTest, ReadResponseBodyWhileLoadComplete) {
loader_->Open(UrlRequest(), mock_callback_.Get());
loader_->DidReceiveResponse(blink::WebURLResponse());
loader_->DidFinishLoading();
EXPECT_CALL(mock_callback_, Run(0)); // Result represents read bytes.
char buffer[1];
loader_->ReadResponseBody(buffer, mock_callback_.Get());
}
TEST_F(BlinkUrlLoaderTest, DidFinishLoading) {
loader_->Open(UrlRequest(), mock_callback_.Get());
loader_->DidReceiveResponse(blink::WebURLResponse());
EXPECT_CALL(mock_callback_, Run(_)).Times(0);
loader_->DidFinishLoading();
}
TEST_F(BlinkUrlLoaderTest, DidFinishLoadingWithPendingCallback) {
char buffer[1];
loader_->Open(UrlRequest(), mock_callback_.Get());
loader_->DidReceiveResponse(blink::WebURLResponse());
loader_->ReadResponseBody(buffer, mock_callback_.Get());
EXPECT_CALL(mock_callback_, Run(0)); // Result represents read bytes.
loader_->DidFinishLoading();
}
TEST_F(BlinkUrlLoaderTest, CloseWhileWaitingToOpen) {
EXPECT_CALL(mock_callback_, Run(_)).Times(0);
loader_->Close();
}
TEST_F(BlinkUrlLoaderTest, CloseWhileOpening) {
loader_->Open(UrlRequest(), mock_callback_.Get());
EXPECT_CALL(mock_callback_, Run(PP_ERROR_ABORTED));
loader_->Close();
}
TEST_F(BlinkUrlLoaderTest, CloseWhileStreamingData) {
loader_->Open(UrlRequest(), mock_callback_.Get());
loader_->DidReceiveResponse(blink::WebURLResponse());
EXPECT_CALL(mock_callback_, Run(_)).Times(0);
loader_->Close();
}
TEST_F(BlinkUrlLoaderTest, CloseWhileStreamingDataWithPendingCallback) {
char buffer[1];
loader_->Open(UrlRequest(), mock_callback_.Get());
loader_->DidReceiveResponse(blink::WebURLResponse());
loader_->ReadResponseBody(buffer, mock_callback_.Get());
EXPECT_CALL(mock_callback_, Run(PP_ERROR_ABORTED));
loader_->Close();
}
TEST_F(BlinkUrlLoaderTest, CloseWhileLoadComplete) {
loader_->Open(UrlRequest(), mock_callback_.Get());
loader_->DidReceiveResponse(blink::WebURLResponse());
loader_->DidFinishLoading();
EXPECT_CALL(mock_callback_, Run(_)).Times(0);
loader_->Close();
}
TEST_F(BlinkUrlLoaderTest, CloseAgain) {
loader_->Open(UrlRequest(), mock_callback_.Get());
loader_->Close();
EXPECT_CALL(mock_callback_, Run(_)).Times(0);
loader_->Close();
}
} // namespace } // namespace
} // namespace chrome_pdf } // namespace chrome_pdf
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