blob: 1ffaba600ee8ce1ff8b038b08a830fdc0f346567 [file] [log] [blame]
// Copyright 2020 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 "services/network/web_bundle_manager.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/unguessable_token.h"
#include "components/web_package/test_support/web_bundle_builder.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/system/data_pipe_utils.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/web_bundle_handle.mojom.h"
#include "services/network/test/test_url_loader_client.h"
#include "services/network/web_bundle_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
namespace {
const char kInitiatorUrl[] = "https://example.com/";
const char kBundleUrl[] = "https://example.com/bundle.wbn";
const char kResourceUrl[] = "https://example.com/a.txt";
const char kQuotaExceededErrorMessage[] =
"Memory quota exceeded. Currently, there is an upper limit on the total "
"size of subresource web bundles in a process. See "
"https://crbug.com/1154140 for more details.";
const int32_t process_id1 = 100;
const int32_t process_id2 = 200;
std::string CreateSmallBundleString() {
web_package::test::WebBundleBuilder builder(kResourceUrl,
"" /* manifest_url */);
builder.AddExchange(kResourceUrl,
{{":status", "200"}, {"content-type", "text/plain"}},
"body");
auto bundle = builder.CreateBundle();
return std::string(reinterpret_cast<const char*>(bundle.data()),
bundle.size());
}
class TestWebBundleHandle : public mojom::WebBundleHandle {
public:
explicit TestWebBundleHandle(
mojo::PendingReceiver<mojom::WebBundleHandle> receiver) {
web_bundle_handles_.Add(this, std::move(receiver));
}
const base::Optional<std::pair<mojom::WebBundleErrorType, std::string>>&
last_bundle_error() const {
return last_bundle_error_;
}
void RunUntilBundleError() {
if (last_bundle_error_.has_value())
return;
base::RunLoop run_loop;
quit_closure_for_bundle_error_ = run_loop.QuitClosure();
run_loop.Run();
}
// mojom::WebBundleHandle
void Clone(mojo::PendingReceiver<mojom::WebBundleHandle> receiver) override {
web_bundle_handles_.Add(this, std::move(receiver));
}
void OnWebBundleError(mojom::WebBundleErrorType type,
const std::string& message) override {
last_bundle_error_ = std::make_pair(type, message);
if (quit_closure_for_bundle_error_)
std::move(quit_closure_for_bundle_error_).Run();
}
private:
base::Optional<std::pair<mojom::WebBundleErrorType, std::string>>
last_bundle_error_;
base::OnceClosure quit_closure_for_bundle_error_;
mojo::ReceiverSet<network::mojom::WebBundleHandle> web_bundle_handles_;
};
std::tuple<base::WeakPtr<WebBundleURLLoaderFactory>,
std::unique_ptr<TestWebBundleHandle>>
CreateWebBundleLoaderFactory(WebBundleManager& manager, int32_t process_id) {
base::UnguessableToken token = base::UnguessableToken::Create();
mojo::PendingRemote<mojom::WebBundleHandle> remote_handle;
std::unique_ptr<TestWebBundleHandle> handle =
std::make_unique<TestWebBundleHandle>(
remote_handle.InitWithNewPipeAndPassReceiver());
ResourceRequest::WebBundleTokenParams create_params(token,
std::move(remote_handle));
base::WeakPtr<WebBundleURLLoaderFactory> factory =
manager.CreateWebBundleURLLoaderFactory(
GURL(kBundleUrl), create_params, process_id,
/*request_initiator_origin_lock=*/base::nullopt);
return std::forward_as_tuple(std::move(factory), std::move(handle));
}
mojo::ScopedDataPipeProducerHandle SetBundleStream(
WebBundleURLLoaderFactory& factory) {
mojo::ScopedDataPipeConsumerHandle consumer;
mojo::ScopedDataPipeProducerHandle producer;
CHECK_EQ(MOJO_RESULT_OK, CreateDataPipe(nullptr, &producer, &consumer));
factory.SetBundleStream(std::move(consumer));
return producer;
}
std::tuple<mojo::Remote<network::mojom::URLLoader>,
std::unique_ptr<network::TestURLLoaderClient>>
StartSubresourceLoad(WebBundleURLLoaderFactory& factory) {
mojo::Remote<network::mojom::URLLoader> loader;
auto client = std::make_unique<network::TestURLLoaderClient>();
network::ResourceRequest request;
request.url = GURL(kResourceUrl);
request.method = "GET";
request.request_initiator = url::Origin::Create(GURL(kInitiatorUrl));
factory.StartSubresourceRequest(loader.BindNewPipeAndPassReceiver(), request,
client->CreateRemote());
return std::forward_as_tuple(std::move(loader), std::move(client));
}
} // namespace
class WebBundleManagerTest : public testing::Test {
public:
WebBundleManagerTest() = default;
~WebBundleManagerTest() override = default;
protected:
void SetMaxMemoryPerProces(WebBundleManager& manager,
uint64_t max_memory_per_process) {
manager.set_max_memory_per_process_for_testing(max_memory_per_process);
}
base::WeakPtr<WebBundleURLLoaderFactory> GetWebBundleURLLoaderFactory(
WebBundleManager& manager,
const ResourceRequest::WebBundleTokenParams& params,
int32_t process_id) {
return manager.GetWebBundleURLLoaderFactory(params, process_id);
}
private:
base::test::TaskEnvironment task_environment_;
};
TEST_F(WebBundleManagerTest, NoFactoryExistsForDifferentProcessId) {
WebBundleManager manager;
base::UnguessableToken token = base::UnguessableToken::Create();
mojo::PendingRemote<network::mojom::WebBundleHandle> handle;
mojo::PendingReceiver<network::mojom::WebBundleHandle> receiver =
handle.InitWithNewPipeAndPassReceiver();
ResourceRequest::WebBundleTokenParams create_params(token, std::move(handle));
auto factory = manager.CreateWebBundleURLLoaderFactory(
GURL(kBundleUrl), create_params, process_id1,
/*request_initiator_origin_lock=*/base::nullopt);
ASSERT_TRUE(factory);
ResourceRequest::WebBundleTokenParams find_params(token,
mojom::kInvalidProcessId);
ASSERT_TRUE(GetWebBundleURLLoaderFactory(manager, find_params, process_id1));
ASSERT_FALSE(GetWebBundleURLLoaderFactory(manager, find_params, process_id2));
}
TEST_F(WebBundleManagerTest, UseProcesIdInTokenParamsForRequestsFromBrowser) {
WebBundleManager manager;
base::UnguessableToken token = base::UnguessableToken::Create();
mojo::PendingRemote<network::mojom::WebBundleHandle> handle;
mojo::PendingReceiver<network::mojom::WebBundleHandle> receiver =
handle.InitWithNewPipeAndPassReceiver();
ResourceRequest::WebBundleTokenParams create_params(token, std::move(handle));
auto factory = manager.CreateWebBundleURLLoaderFactory(
GURL(kBundleUrl), create_params, process_id1,
/*request_initiator_origin_lock=*/base::nullopt);
ASSERT_TRUE(factory);
ResourceRequest::WebBundleTokenParams find_params1(token, process_id1);
ASSERT_TRUE(GetWebBundleURLLoaderFactory(manager, find_params1,
mojom::kBrowserProcessId));
ASSERT_FALSE(
GetWebBundleURLLoaderFactory(manager, find_params1, process_id2));
ResourceRequest::WebBundleTokenParams find_params2(token, process_id2);
ASSERT_FALSE(GetWebBundleURLLoaderFactory(manager, find_params2,
mojom::kBrowserProcessId));
}
TEST_F(WebBundleManagerTest, RemoveFactoryWhenDisconnected) {
WebBundleManager manager;
base::UnguessableToken token = base::UnguessableToken::Create();
ResourceRequest::WebBundleTokenParams find_params(token,
mojom::kInvalidProcessId);
{
mojo::PendingRemote<network::mojom::WebBundleHandle> handle;
mojo::PendingReceiver<network::mojom::WebBundleHandle> receiver =
handle.InitWithNewPipeAndPassReceiver();
ResourceRequest::WebBundleTokenParams create_params(token,
std::move(handle));
auto factory = manager.CreateWebBundleURLLoaderFactory(
GURL(kBundleUrl), create_params, process_id1,
/*request_initiator_origin_lock=*/base::nullopt);
ASSERT_TRUE(factory);
ASSERT_TRUE(
GetWebBundleURLLoaderFactory(manager, find_params, process_id1));
// Getting out of scope to delete |receiver|.
}
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(GetWebBundleURLLoaderFactory(manager, find_params, process_id1))
<< "The manager should remove a factory when the handle is disconnected.";
}
TEST_F(WebBundleManagerTest,
SubresourceRequestArrivesEarlierThanBundleRequest) {
// Confirm that a subresource is correctly loaded, regardless of the arrival
// order of a webbundle request and a subresource request in the bundle.
//
// For example, given that we have the following main document:
//
// <link rel=webbundle href="https://example.com/bundle.wbn"
// resources="https://example.com/a.txt">
// <img src="https://example.com/a.txt"> # Please ignore that a.txt is weird
// for <img>.
//
// In this case, a network service should receive the following two resource
// requests:
//
// 1. A request for a bundle, "bundle.wbn"
// 2. A request for a subresource, "a.txt".
//
// Usually, the request 1 arrives earlier than the request 2,
// however, the arrival order is not guaranteed. The subresource should be
// loaded even if the request 2 arrives earlier.
//
// Since it would be non-trivial to reproduce this scenario in a reliable way,
// we simulate this scenario by calling WebBundleManager member functions
// manually here, as network::URLLoaderFactory does, and verify that the
// subresource request is correctly loaded.
//
// TODO(crbug.com/1158709): Find a better way to test this scenario.
WebBundleManager manager;
// Simulate that a subresource request arrives at first,
// calling WebBundleManager::StartSubresourceRequest.
base::UnguessableToken token = base::UnguessableToken::Create();
network::ResourceRequest request;
request.url = GURL(kResourceUrl);
request.method = "GET";
request.request_initiator = url::Origin::Create(GURL(kInitiatorUrl));
request.web_bundle_token_params = ResourceRequest::WebBundleTokenParams();
request.web_bundle_token_params->token = token;
mojo::Remote<network::mojom::URLLoader> loader;
auto client = std::make_unique<network::TestURLLoaderClient>();
manager.StartSubresourceRequest(loader.BindNewPipeAndPassReceiver(), request,
client->CreateRemote(), process_id1);
// Simulate that a webbundle request arrives, calling
// WebBundleManager::CreateWebBundleURLLoaderFactory.
ResourceRequest::WebBundleTokenParams token_params;
token_params.token = token;
token_params.handle = mojo::PendingRemote<network::mojom::WebBundleHandle>();
mojo::PendingReceiver<network::mojom::WebBundleHandle> receiver =
token_params.handle.InitWithNewPipeAndPassReceiver();
auto factory = manager.CreateWebBundleURLLoaderFactory(
GURL(kBundleUrl), token_params, process_id1,
/*request_initiator_origin_lock=*/base::nullopt);
// Then, simulate that the bundle is loaded from the network, calling
// SetBundleStream manually.
mojo::ScopedDataPipeConsumerHandle consumer;
mojo::ScopedDataPipeProducerHandle producer;
ASSERT_EQ(CreateDataPipe(nullptr, &producer, &consumer), MOJO_RESULT_OK);
factory->SetBundleStream(std::move(consumer));
mojo::BlockingCopyFromString(CreateSmallBundleString(), producer);
producer.reset();
client->RunUntilComplete();
// Confirm that a subresource is correctly loaded.
EXPECT_EQ(net::OK, client->completion_status().error_code);
EXPECT_EQ(client->response_head()->web_bundle_url, GURL(kBundleUrl));
std::string body;
EXPECT_TRUE(
mojo::BlockingCopyToString(client->response_body_release(), &body));
EXPECT_EQ("body", body);
}
TEST_F(WebBundleManagerTest, MemoryQuota_StartRequestAfterError) {
base::HistogramTester histogram_tester;
WebBundleManager manager;
std::string bundle = CreateSmallBundleString();
SetMaxMemoryPerProces(manager, bundle.size() - 1);
// Start loading the bundle which size is larger than the quota.
base::WeakPtr<WebBundleURLLoaderFactory> factory;
std::unique_ptr<TestWebBundleHandle> handle;
std::tie(factory, handle) =
CreateWebBundleLoaderFactory(manager, process_id1);
// Input the bundle to the factory.
auto producer = SetBundleStream(*factory);
mojo::BlockingCopyFromString(bundle, producer);
producer.reset();
// TestWebBundleHandle must receive the error.
handle->RunUntilBundleError();
ASSERT_TRUE(handle->last_bundle_error().has_value());
EXPECT_EQ(handle->last_bundle_error()->first,
mojom::WebBundleErrorType::kMemoryQuotaExceeded);
EXPECT_EQ(handle->last_bundle_error()->second, kQuotaExceededErrorMessage);
histogram_tester.ExpectUniqueSample(
"SubresourceWebBundles.LoadResult",
WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::
kMemoryQuotaExceeded,
1);
// Start the subresource request after triggering the quota error.
mojo::Remote<network::mojom::URLLoader> loader;
std::unique_ptr<network::TestURLLoaderClient> client;
std::tie(loader, client) = StartSubresourceLoad(*factory);
// The subresource request must fail.
client->RunUntilComplete();
EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
client->completion_status().error_code);
}
TEST_F(WebBundleManagerTest, MemoryQuota_StartRequestBeforeReceivingBundle) {
WebBundleManager manager;
std::string bundle = CreateSmallBundleString();
SetMaxMemoryPerProces(manager, bundle.size() - 1);
// Start loading the bundle which size is larger than the quota.
base::WeakPtr<WebBundleURLLoaderFactory> factory;
std::unique_ptr<TestWebBundleHandle> handle;
std::tie(factory, handle) =
CreateWebBundleLoaderFactory(manager, process_id1);
// Start the subresource request.
mojo::Remote<network::mojom::URLLoader> loader;
std::unique_ptr<network::TestURLLoaderClient> client;
std::tie(loader, client) = StartSubresourceLoad(*factory);
// Input the bundle to the factory after starting the subresource load.
auto producer = SetBundleStream(*factory);
mojo::BlockingCopyFromString(bundle, producer);
producer.reset();
// TestWebBundleHandle must receive the error.
handle->RunUntilBundleError();
ASSERT_TRUE(handle->last_bundle_error().has_value());
EXPECT_EQ(handle->last_bundle_error()->first,
mojom::WebBundleErrorType::kMemoryQuotaExceeded);
EXPECT_EQ(handle->last_bundle_error()->second, kQuotaExceededErrorMessage);
// The subresource request must fail.
client->RunUntilComplete();
EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
client->completion_status().error_code);
}
TEST_F(WebBundleManagerTest, MemoryQuota_QuotaErrorWhileReadingBody) {
WebBundleManager manager;
// Create a not small size bundle to trigger the quota error while reading the
// body of the subresource.
web_package::test::WebBundleBuilder builder(kResourceUrl,
"" /* manifest_url */);
builder.AddExchange(kResourceUrl,
{{":status", "200"}, {"content-type", "text/plain"}},
std::string(10000, 'X'));
std::vector<uint8_t> bundle = builder.CreateBundle();
std::string bundle_string =
std::string(reinterpret_cast<const char*>(bundle.data()), bundle.size());
// Set the max memory to trigger the quota error while reading the body of
// the subresource.
// Note: When WebBundleParser::MetadataParser parses the metadata, it reads
// "[fallback URL length] + kMaxSectionLengthsCBORSize(8192) +
// kMaxCBORItemHeaderSize(9) * 2" bytes after reading 10 bytes of
// kBundleMagicBytes and 5 bytes of kVersionB1MagicBytes and (1, 2, 3, 5 or 9)
// bytes of CBORHeader of fallback URL. If we set the quota smaller than
// this value, the quota error is triggered while parsing the metadata.
uint64_t required_bytes_for_parsing_metadata =
10 + // size of BundleMagicBytes
5 + // size of VersionB1MagicBytes
2 + // CBORHeader size for kResourceUrl string
sizeof(kResourceUrl) - 1 + // len(kResourceUrl)
8192 + // kMaxSectionLengthsCBORSize
9 * 2; // kMaxCBORItemHeaderSize * 2
SetMaxMemoryPerProces(manager, required_bytes_for_parsing_metadata);
ASSERT_GT(bundle_string.size(), required_bytes_for_parsing_metadata);
// Start loading the bundle.
base::WeakPtr<WebBundleURLLoaderFactory> factory;
std::unique_ptr<TestWebBundleHandle> handle;
std::tie(factory, handle) =
CreateWebBundleLoaderFactory(manager, process_id1);
// Start the subresource request.
mojo::Remote<network::mojom::URLLoader> loader;
std::unique_ptr<network::TestURLLoaderClient> client;
std::tie(loader, client) = StartSubresourceLoad(*factory);
// Input the first |required_bytes_for_parsing_metadata| bytes of the bundle
// to the factory.
auto producer = SetBundleStream(*factory);
mojo::BlockingCopyFromString(
bundle_string.substr(0, required_bytes_for_parsing_metadata), producer);
// The subresource request must be able to receive the response header.
client->RunUntilResponseReceived();
EXPECT_TRUE(client->has_received_response());
// Input the remaining bytes of the bundle to the factory.
mojo::BlockingCopyFromString(
bundle_string.substr(required_bytes_for_parsing_metadata), producer);
producer.reset();
// TestWebBundleHandle must receive the error.
handle->RunUntilBundleError();
ASSERT_TRUE(handle->last_bundle_error().has_value());
EXPECT_EQ(handle->last_bundle_error()->first,
mojom::WebBundleErrorType::kMemoryQuotaExceeded);
EXPECT_EQ(handle->last_bundle_error()->second, kQuotaExceededErrorMessage);
// The subresource request must receive the error.
client->RunUntilComplete();
EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
client->completion_status().error_code);
}
TEST_F(WebBundleManagerTest, MemoryQuota_QuotaErrorWhileParsingManifest) {
WebBundleManager manager;
std::string bundle = CreateSmallBundleString();
// Set the max memory to trigger the quota error while reading the manifest of
// the web bundle.
SetMaxMemoryPerProces(manager, 10);
// Start loading the bundle.
base::WeakPtr<WebBundleURLLoaderFactory> factory;
std::unique_ptr<TestWebBundleHandle> handle;
std::tie(factory, handle) =
CreateWebBundleLoaderFactory(manager, process_id1);
// Input the bundle to the factory byte by byte.
auto producer = SetBundleStream(*factory);
for (size_t i = 0; i < bundle.size(); ++i) {
mojo::BlockingCopyFromString(bundle.substr(i, 1), producer);
// Run the RunLoop to trigger OnDataAvailable() byte by byte.
base::RunLoop run_loop;
run_loop.RunUntilIdle();
}
producer.reset();
// TestWebBundleHandle must receive the error.
handle->RunUntilBundleError();
ASSERT_TRUE(handle->last_bundle_error().has_value());
EXPECT_EQ(handle->last_bundle_error()->first,
mojom::WebBundleErrorType::kMemoryQuotaExceeded);
EXPECT_EQ(handle->last_bundle_error()->second, kQuotaExceededErrorMessage);
// Start the subresource request.
mojo::Remote<network::mojom::URLLoader> loader;
std::unique_ptr<network::TestURLLoaderClient> client;
std::tie(loader, client) = StartSubresourceLoad(*factory);
// The subresource request must fail.
client->RunUntilComplete();
EXPECT_FALSE(client->has_received_response());
EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
client->completion_status().error_code);
}
TEST_F(WebBundleManagerTest, MemoryQuota_ProcessIsolation) {
base::HistogramTester histogram_tester;
WebBundleManager manager;
std::string bundle = CreateSmallBundleString();
// Set the max memory to trigger the quota error while loading the third
// web bundle.
SetMaxMemoryPerProces(manager, bundle.size() * 2.5);
// Start loading the first web bundle in the process 1.
base::WeakPtr<WebBundleURLLoaderFactory> factory1_1;
std::unique_ptr<TestWebBundleHandle> handle1_1;
std::tie(factory1_1, handle1_1) =
CreateWebBundleLoaderFactory(manager, process_id1);
auto producer1_1 = SetBundleStream(*factory1_1);
mojo::BlockingCopyFromString(bundle, producer1_1);
producer1_1.reset();
// Start loading the subresource from the first web bundle.
mojo::Remote<network::mojom::URLLoader> loader1_1;
std::unique_ptr<network::TestURLLoaderClient> client1_1;
std::tie(loader1_1, client1_1) = StartSubresourceLoad(*factory1_1);
// Confirm that the subresource is correctly loaded.
client1_1->RunUntilComplete();
EXPECT_EQ(net::OK, client1_1->completion_status().error_code);
EXPECT_EQ(client1_1->response_head()->web_bundle_url, GURL(kBundleUrl));
std::string body1_1;
EXPECT_TRUE(
mojo::BlockingCopyToString(client1_1->response_body_release(), &body1_1));
EXPECT_EQ("body", body1_1);
histogram_tester.ExpectUniqueSample("SubresourceWebBundles.ReceivedSize",
bundle.size(), 1);
histogram_tester.ExpectUniqueSample(
"SubresourceWebBundles.LoadResult",
WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::kSuccess, 1);
// Start loading the second web bundle in the process 1.
base::WeakPtr<WebBundleURLLoaderFactory> factory1_2;
std::unique_ptr<TestWebBundleHandle> handle1_2;
std::tie(factory1_2, handle1_2) =
CreateWebBundleLoaderFactory(manager, process_id1);
auto producer1_2 = SetBundleStream(*factory1_2);
mojo::BlockingCopyFromString(bundle, producer1_2);
producer1_2.reset();
// Start loading the subresource from the second web bundle.
mojo::Remote<network::mojom::URLLoader> loader1_2;
std::unique_ptr<network::TestURLLoaderClient> client1_2;
std::tie(loader1_2, client1_2) = StartSubresourceLoad(*factory1_2);
// Confirm that the subresource is correctly loaded.
client1_2->RunUntilComplete();
EXPECT_EQ(net::OK, client1_2->completion_status().error_code);
EXPECT_EQ(client1_2->response_head()->web_bundle_url, GURL(kBundleUrl));
std::string body1_2;
EXPECT_TRUE(
mojo::BlockingCopyToString(client1_2->response_body_release(), &body1_2));
EXPECT_EQ("body", body1_2);
histogram_tester.ExpectUniqueSample("SubresourceWebBundles.ReceivedSize",
bundle.size(), 2);
histogram_tester.ExpectUniqueSample(
"SubresourceWebBundles.LoadResult",
WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::kSuccess, 2);
// Start loading the third web bundle in the process 1.
base::WeakPtr<WebBundleURLLoaderFactory> factory1_3;
std::unique_ptr<TestWebBundleHandle> handle1_3;
std::tie(factory1_3, handle1_3) =
CreateWebBundleLoaderFactory(manager, process_id1);
auto producer1_3 = SetBundleStream(*factory1_3);
mojo::BlockingCopyFromString(bundle, producer1_3);
producer1_3.reset();
// TestWebBundleHandle must receive the error.
handle1_3->RunUntilBundleError();
ASSERT_TRUE(handle1_3->last_bundle_error().has_value());
EXPECT_EQ(handle1_3->last_bundle_error()->first,
mojom::WebBundleErrorType::kMemoryQuotaExceeded);
EXPECT_EQ(handle1_3->last_bundle_error()->second, kQuotaExceededErrorMessage);
// Start loading the subresource from the second web bundle.
mojo::Remote<network::mojom::URLLoader> loader1_3;
std::unique_ptr<network::TestURLLoaderClient> client1_3;
std::tie(loader1_3, client1_3) = StartSubresourceLoad(*factory1_3);
// The subresource request must fail.
client1_3->RunUntilComplete();
EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
client1_3->completion_status().error_code);
histogram_tester.ExpectBucketCount(
"SubresourceWebBundles.LoadResult",
WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::
kMemoryQuotaExceeded,
1);
// Start loading the third web bundle in the process 2.
base::WeakPtr<WebBundleURLLoaderFactory> factory2;
std::unique_ptr<TestWebBundleHandle> handle2;
std::tie(factory2, handle2) =
CreateWebBundleLoaderFactory(manager, process_id2);
auto producer2 = SetBundleStream(*factory2);
mojo::BlockingCopyFromString(bundle, producer2);
producer2.reset();
// Start loading the subresource from the third web bundle.
mojo::Remote<network::mojom::URLLoader> loader2;
std::unique_ptr<network::TestURLLoaderClient> client2;
std::tie(loader2, client2) = StartSubresourceLoad(*factory2);
// Confirm that the subresource is correctly loaded.
client2->RunUntilComplete();
EXPECT_EQ(net::OK, client2->completion_status().error_code);
EXPECT_EQ(client2->response_head()->web_bundle_url, GURL(kBundleUrl));
std::string body2;
EXPECT_TRUE(
mojo::BlockingCopyToString(client2->response_body_release(), &body2));
EXPECT_EQ("body", body2);
histogram_tester.ExpectUniqueSample("SubresourceWebBundles.ReceivedSize",
bundle.size(), 3);
histogram_tester.ExpectBucketCount(
"SubresourceWebBundles.LoadResult",
WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::kSuccess, 3);
// Reset handles and RunUntilIdle to trigger MaxMemoryUsagePerProcess
// histogram count.
handle1_1.reset();
handle1_2.reset();
handle1_3.reset();
handle2.reset();
base::RunLoop().RunUntilIdle();
histogram_tester.ExpectBucketCount(
"SubresourceWebBundles.MaxMemoryUsagePerProcess", bundle.size() * 2, 1);
histogram_tester.ExpectBucketCount(
"SubresourceWebBundles.MaxMemoryUsagePerProcess", bundle.size(), 1);
}
} // namespace network