blob: f835e44312e6c4f74e955f8f78eb359272dfc78c [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/chromeos/fileapi/external_file_url_loader_factory.h"
#include <stddef.h>
#include <memory>
#include "base/bind.h"
#include "chrome/browser/chromeos/drive/drive_integration_service.h"
#include "chrome/browser/chromeos/drive/file_system_util.h"
#include "chrome/browser/chromeos/file_system_provider/fake_extension_provider.h"
#include "chrome/browser/chromeos/file_system_provider/service.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/constants/chromeos_features.h"
#include "components/drive/chromeos/fake_file_system.h"
#include "components/drive/service/fake_drive_service.h"
#include "components/drive/service/test_util.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/common/child_process_host.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_service_manager_context.h"
#include "mojo/public/cpp/system/data_pipe_utils.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/redirect_info.h"
#include "services/network/test/test_url_loader_client.h"
#include "storage/browser/fileapi/external_mount_points.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace chromeos {
namespace {
constexpr char kExtensionId[] = "abc";
constexpr char kFileSystemId[] = "test-filesystem";
constexpr char kTestUrl[] =
"externalfile:abc:test-filesystem:test-user-hash/hello.txt";
constexpr char kExpectedFileContents[] =
"This is a testing file. Lorem ipsum dolor sit amet est.";
} // namespace
class ExternalFileURLLoaderFactoryTest : public testing::Test {
protected:
ExternalFileURLLoaderFactoryTest()
: thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
integration_service_factory_callback_(base::BindRepeating(
&ExternalFileURLLoaderFactoryTest::CreateDriveIntegrationService,
base::Unretained(this))),
fake_file_system_(nullptr) {}
~ExternalFileURLLoaderFactoryTest() override {}
void SetUp() override {
// Create a testing profile.
profile_manager_.reset(
new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
ASSERT_TRUE(profile_manager_->SetUp());
Profile* const profile =
profile_manager_->CreateTestingProfile("test-user");
user_manager_ = std::make_unique<chromeos::FakeChromeUserManager>();
user_manager_->AddUser(
AccountId::FromUserEmailGaiaId(profile->GetProfileUserName(), "12345"));
render_process_host_ =
std::make_unique<content::MockRenderProcessHost>(profile);
auto* service = chromeos::file_system_provider::Service::Get(profile);
service->RegisterProvider(
chromeos::file_system_provider::FakeExtensionProvider::Create(
kExtensionId));
const auto kProviderId =
chromeos::file_system_provider::ProviderId::CreateFromExtensionId(
kExtensionId);
service->MountFileSystem(kProviderId,
chromeos::file_system_provider::MountOptions(
kFileSystemId, "Test FileSystem"));
// Create the drive integration service for the profile.
integration_service_factory_scope_.reset(
new drive::DriveIntegrationServiceFactory::ScopedFactoryForTest(
&integration_service_factory_callback_));
drive::DriveIntegrationServiceFactory::GetForProfile(profile);
// Create the URLLoaderFactory.
url_loader_factory_ = std::make_unique<ExternalFileURLLoaderFactory>(
profile, render_process_host_id());
}
virtual int render_process_host_id() {
return content::ChildProcessHost::kInvalidUniqueID;
}
content::MockRenderProcessHost* render_process_host() {
return render_process_host_.get();
}
network::ResourceRequest CreateRequest(std::string url) {
network::ResourceRequest request;
request.method = "GET";
request.url = GURL(url);
return request;
}
network::mojom::URLLoaderPtr CreateURLLoaderAndStart(
network::TestURLLoaderClient* client,
const network::ResourceRequest& resource_request) {
network::mojom::URLLoaderPtr loader;
url_loader_factory_->CreateLoaderAndStart(
mojo::MakeRequest(&loader), 0 /* routing_id */, 0 /* request_id */,
network::mojom::kURLLoadOptionNone, resource_request,
client->CreateInterfacePtr(),
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
return loader;
}
private:
// Create the drive integration service for the |profile|
drive::DriveIntegrationService* CreateDriveIntegrationService(
Profile* profile) {
drive::FakeDriveService* const drive_service = new drive::FakeDriveService;
if (!drive::test_util::SetUpTestEntries(drive_service))
return NULL;
const std::string& drive_mount_name =
drive::util::GetDriveMountPointPath(profile).BaseName().AsUTF8Unsafe();
storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
drive_mount_name, storage::kFileSystemTypeDrive,
storage::FileSystemMountOption(),
drive::util::GetDriveMountPointPath(profile));
DCHECK(!fake_file_system_);
fake_file_system_ = new drive::test_util::FakeFileSystem(drive_service);
if (!drive_cache_dir_.CreateUniqueTempDir())
return NULL;
return new drive::DriveIntegrationService(
profile, nullptr, drive_service, drive_mount_name,
drive_cache_dir_.GetPath(), fake_file_system_);
}
content::TestBrowserThreadBundle thread_bundle_;
content::TestServiceManagerContext context_;
drive::DriveIntegrationServiceFactory::FactoryCallback
integration_service_factory_callback_;
std::unique_ptr<drive::DriveIntegrationServiceFactory::ScopedFactoryForTest>
integration_service_factory_scope_;
drive::test_util::FakeFileSystem* fake_file_system_;
std::unique_ptr<ExternalFileURLLoaderFactory> url_loader_factory_;
std::unique_ptr<TestingProfileManager> profile_manager_;
std::unique_ptr<chromeos::FakeChromeUserManager> user_manager_;
// Used to register the profile with the ChildProcessSecurityPolicyImpl.
std::unique_ptr<content::MockRenderProcessHost> render_process_host_;
base::ScopedTempDir drive_cache_dir_;
};
TEST_F(ExternalFileURLLoaderFactoryTest, NonGetMethod) {
network::TestURLLoaderClient client;
network::ResourceRequest request = CreateRequest(kTestUrl);
request.method = "POST";
network::mojom::URLLoaderPtr loader =
CreateURLLoaderAndStart(&client, request);
client.RunUntilComplete();
EXPECT_EQ(net::ERR_METHOD_NOT_SUPPORTED,
client.completion_status().error_code);
}
TEST_F(ExternalFileURLLoaderFactoryTest, RegularFile) {
network::TestURLLoaderClient client;
network::mojom::URLLoaderPtr loader =
CreateURLLoaderAndStart(&client, CreateRequest(kTestUrl));
client.RunUntilComplete();
ASSERT_EQ(net::OK, client.completion_status().error_code);
EXPECT_EQ("text/plain", client.response_head().mime_type);
std::string response_body;
ASSERT_TRUE(mojo::BlockingCopyToString(client.response_body_release(),
&response_body));
EXPECT_EQ(kExpectedFileContents, response_body);
}
TEST_F(ExternalFileURLLoaderFactoryTest, HostedDocument) {
// Hosted documents are never opened via externalfile: URLs with DriveFS.
if (base::FeatureList::IsEnabled(chromeos::features::kDriveFs)) {
return;
}
// Open a gdoc file.
network::TestURLLoaderClient client;
network::mojom::URLLoaderPtr loader = CreateURLLoaderAndStart(
&client,
CreateRequest("externalfile:drive-test-user-hash/root/Document 1 "
"excludeDir-test.gdoc"));
client.RunUntilRedirectReceived();
// Make sure that a hosted document triggers redirection.
EXPECT_TRUE(client.has_received_redirect());
EXPECT_TRUE(client.redirect_info().new_url.is_valid());
EXPECT_TRUE(client.redirect_info().new_url.SchemeIs("https"));
}
TEST_F(ExternalFileURLLoaderFactoryTest, RootDirectory) {
network::TestURLLoaderClient client;
network::mojom::URLLoaderPtr loader = CreateURLLoaderAndStart(
&client,
CreateRequest("externalfile:abc:test-filesystem:test-user-hash/"));
client.RunUntilComplete();
EXPECT_EQ(net::ERR_FAILED, client.completion_status().error_code);
}
TEST_F(ExternalFileURLLoaderFactoryTest, NonExistingFile) {
network::TestURLLoaderClient client;
network::mojom::URLLoaderPtr loader = CreateURLLoaderAndStart(
&client, CreateRequest("externalfile:abc:test-filesystem:test-user-hash/"
"non-existing-file.txt"));
client.RunUntilComplete();
EXPECT_EQ(net::ERR_FILE_NOT_FOUND, client.completion_status().error_code);
}
TEST_F(ExternalFileURLLoaderFactoryTest, WrongFormat) {
network::TestURLLoaderClient client;
network::mojom::URLLoaderPtr loader =
CreateURLLoaderAndStart(&client, CreateRequest("externalfile:"));
client.RunUntilComplete();
EXPECT_EQ(net::ERR_INVALID_URL, client.completion_status().error_code);
}
TEST_F(ExternalFileURLLoaderFactoryTest, RangeHeader) {
network::TestURLLoaderClient client;
network::ResourceRequest request = CreateRequest(kTestUrl);
request.headers.SetHeader(net::HttpRequestHeaders::kRange, "bytes=3-5");
network::mojom::URLLoaderPtr loader =
CreateURLLoaderAndStart(&client, request);
client.RunUntilComplete();
EXPECT_EQ(net::OK, client.completion_status().error_code);
std::string response_body;
ASSERT_TRUE(mojo::BlockingCopyToString(client.response_body_release(),
&response_body));
EXPECT_EQ(base::StringPiece(kExpectedFileContents).substr(3, 3),
response_body);
}
TEST_F(ExternalFileURLLoaderFactoryTest, WrongRangeHeader) {
network::TestURLLoaderClient client;
network::ResourceRequest request = CreateRequest(kTestUrl);
request.headers.SetHeader(net::HttpRequestHeaders::kRange, "Invalid range");
network::mojom::URLLoaderPtr loader =
CreateURLLoaderAndStart(&client, request);
client.RunUntilComplete();
EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE,
client.completion_status().error_code);
}
class SubresourceExternalFileURLLoaderFactoryTest
: public ExternalFileURLLoaderFactoryTest {
protected:
int render_process_host_id() override {
return render_process_host()->GetID();
}
};
TEST_F(SubresourceExternalFileURLLoaderFactoryTest, SubresourceAllowed) {
content::ChildProcessSecurityPolicy::GetInstance()->GrantRequestScheme(
render_process_host_id(), content::kExternalFileScheme);
network::TestURLLoaderClient client;
network::mojom::URLLoaderPtr loader =
CreateURLLoaderAndStart(&client, CreateRequest(kTestUrl));
client.RunUntilComplete();
ASSERT_EQ(net::OK, client.completion_status().error_code);
}
TEST_F(SubresourceExternalFileURLLoaderFactoryTest, SubresourceNotAllowed) {
network::TestURLLoaderClient client;
ASSERT_DEATH(CreateURLLoaderAndStart(&client, CreateRequest(kTestUrl)), "");
}
} // namespace chromeos