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 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/memory/weak_ptr.h"
#include "base/notreached.h"
#include "pdf/ppapi_migration/callback.h"
......@@ -23,8 +23,14 @@
#include "ppapi/cpp/url_request_info.h"
#include "ppapi/cpp/url_response_info.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_options.h"
#include "url/gurl.h"
namespace chrome_pdf {
......@@ -50,27 +56,42 @@ BlinkUrlLoader::BlinkUrlLoader(base::WeakPtr<Client> client)
BlinkUrlLoader::~BlinkUrlLoader() = default;
// Modeled on `content::PepperURLLoaderHost::OnHostMsgGrantUniversalAccess()`.
void BlinkUrlLoader::GrantUniversalAccess() {
DCHECK(!blink_loader_);
DCHECK_EQ(state_, LoadingState::kWaitingToOpen);
grant_universal_access_ = true;
}
// Modeled on `content::PepperURLLoaderHost::OnHostMsgOpen()`.
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_) {
std::move(callback).Run(PP_ERROR_FAILED);
AbortLoad(PP_ERROR_FAILED);
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;
options.grant_universal_access = grant_universal_access_;
blink_loader_ = client_->CreateAssociatedURLLoader(options);
if (!blink_loader_) {
std::move(callback).Run(PP_ERROR_FAILED);
AbortLoad(PP_ERROR_FAILED);
return;
}
NOTIMPLEMENTED();
blink_loader_->LoadAsynchronously(blink_request, this);
}
bool BlinkUrlLoader::GetDownloadProgress(
......@@ -80,13 +101,26 @@ bool BlinkUrlLoader::GetDownloadProgress(
return false;
}
// Modeled on `ppapi::proxy::URLLoaderResource::ReadResponseBody()`.
void BlinkUrlLoader::ReadResponseBody(base::span<char> buffer,
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() {
NOTIMPLEMENTED();
if (state_ != LoadingState::kLoadComplete)
AbortLoad(PP_ERROR_ABORTED);
}
bool BlinkUrlLoader::WillFollowRedirect(
......@@ -102,8 +136,14 @@ void BlinkUrlLoader::DidSendData(uint64_t bytes_sent,
NOTREACHED();
}
// Modeled on `content::PepperURLLoaderHost::DidReceiveResponse()`.
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) {
......@@ -121,14 +161,46 @@ void BlinkUrlLoader::DidReceiveCachedMetadata(const char* data,
NOTREACHED();
}
// Modeled on `content::PepperURLLoaderHost::DidFinishLoading()`.
void BlinkUrlLoader::DidFinishLoading() {
NOTIMPLEMENTED();
DCHECK_EQ(state_, LoadingState::kStreamingData);
SetLoadComplete(PP_OK);
RunReadCallback();
}
void BlinkUrlLoader::DidFail(const blink::WebURLError& error) {
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)
: plugin_instance_(plugin_instance), pepper_loader_(plugin_instance) {}
......
......@@ -10,6 +10,7 @@
#include <memory>
#include <string>
#include "base/callback.h"
#include "base/containers/span.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
......@@ -152,10 +153,40 @@ class BlinkUrlLoader final : public UrlLoader,
void DidFail(const blink::WebURLError& error) override;
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_;
bool grant_universal_access_ = false;
LoadingState state_ = LoadingState::kWaitingToOpen;
int32_t complete_result_ = 0;
std::unique_ptr<blink::WebAssociatedURLLoader> blink_loader_;
ResultCallback open_callback_;
ResultCallback read_callback_;
};
// A Pepper URL loader.
......
......@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/containers/span.h"
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/test/mock_callback.h"
......@@ -16,10 +17,15 @@
#include "ppapi/c/pp_errors.h"
#include "testing/gmock/include/gmock/gmock.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_response.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_options.h"
#include "url/gurl.h"
namespace chrome_pdf {
namespace {
......@@ -69,6 +75,9 @@ class BlinkUrlLoaderTest : public testing::Test {
ON_CALL(mock_client_, CreateAssociatedURLLoader(_))
.WillByDefault(
Invoke(this, &BlinkUrlLoaderTest::FakeCreateAssociatedURLLoader));
ON_CALL(*mock_url_loader_, LoadAsynchronously(_, _))
.WillByDefault(
Invoke(this, &BlinkUrlLoaderTest::FakeLoadAsynchronously));
loader_ = std::make_unique<BlinkUrlLoader>(mock_client_.GetWeakPtr());
}
......@@ -79,13 +88,20 @@ class BlinkUrlLoaderTest : public testing::Test {
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_;
base::MockCallback<ResultCallback> mock_callback_;
NiceMock<base::MockCallback<ResultCallback>> mock_callback_;
std::unique_ptr<BlinkUrlLoader> loader_;
std::unique_ptr<MockWebAssociatedURLLoader> mock_url_loader_ =
std::make_unique<MockWebAssociatedURLLoader>();
std::unique_ptr<NiceMock<MockWebAssociatedURLLoader>> mock_url_loader_ =
std::make_unique<NiceMock<MockWebAssociatedURLLoader>>();
blink::WebAssociatedURLLoaderOptions saved_options_;
blink::WebURLRequest saved_request_;
};
TEST_F(BlinkUrlLoaderTest, GrantUniversalAccess) {
......@@ -96,14 +112,26 @@ TEST_F(BlinkUrlLoaderTest, GrantUniversalAccess) {
TEST_F(BlinkUrlLoaderTest, Open) {
EXPECT_CALL(mock_client_, CreateAssociatedURLLoader(_));
EXPECT_CALL(*mock_url_loader_, LoadAsynchronously(_, _));
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_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) {
EXPECT_CALL(mock_client_, CreateAssociatedURLLoader(_)).Times(0);
EXPECT_CALL(*mock_url_loader_, LoadAsynchronously(_, _)).Times(0);
EXPECT_CALL(mock_callback_, Run(PP_ERROR_FAILED));
mock_client_.InvalidateWeakPtrs();
......@@ -113,10 +141,115 @@ TEST_F(BlinkUrlLoaderTest, OpenWithInvalidatedClient) {
TEST_F(BlinkUrlLoaderTest, OpenWithFailingCreateAssociatedURLLoader) {
EXPECT_CALL(mock_client_, CreateAssociatedURLLoader(_))
.WillOnce(ReturnNull());
EXPECT_CALL(*mock_url_loader_, LoadAsynchronously(_, _)).Times(0);
EXPECT_CALL(mock_callback_, Run(PP_ERROR_FAILED));
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 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