| Index: chrome/browser/extensions/extension_protocols_unittest.cc
|
| diff --git a/chrome/browser/extensions/extension_protocols_unittest.cc b/chrome/browser/extensions/extension_protocols_unittest.cc
|
| index e3da5f8a4a3d0642b7ef4b413e386b2063fc8c56..c762e8d5599b815cced82bf59933402c5a3a1010 100644
|
| --- a/chrome/browser/extensions/extension_protocols_unittest.cc
|
| +++ b/chrome/browser/extensions/extension_protocols_unittest.cc
|
| @@ -7,23 +7,31 @@
|
| #include <memory>
|
| #include <string>
|
|
|
| +#include "base/command_line.h"
|
| #include "base/files/file_util.h"
|
| #include "base/macros.h"
|
| -#include "base/message_loop/message_loop.h"
|
| #include "base/run_loop.h"
|
| #include "base/strings/string_number_conversions.h"
|
| #include "base/strings/string_util.h"
|
| +#include "base/test/test_file_util.h"
|
| #include "base/values.h"
|
| +#include "chrome/browser/extensions/chrome_content_verifier_delegate.h"
|
| #include "chrome/common/chrome_paths.h"
|
| +#include "chrome/common/chrome_switches.h"
|
| +#include "chrome/test/base/testing_profile.h"
|
| +#include "components/crx_file/id_util.h"
|
| #include "content/public/browser/resource_request_info.h"
|
| #include "content/public/common/browser_side_navigation_policy.h"
|
| #include "content/public/common/previews_state.h"
|
| #include "content/public/test/mock_resource_context.h"
|
| #include "content/public/test/test_browser_thread_bundle.h"
|
| +#include "content/public/test/test_utils.h"
|
| +#include "extensions/browser/content_verifier.h"
|
| #include "extensions/browser/extension_protocols.h"
|
| #include "extensions/browser/info_map.h"
|
| #include "extensions/common/constants.h"
|
| #include "extensions/common/extension.h"
|
| +#include "extensions/common/extension_builder.h"
|
| #include "extensions/common/file_util.h"
|
| #include "net/base/request_priority.h"
|
| #include "net/url_request/url_request.h"
|
| @@ -43,6 +51,16 @@ base::FilePath GetTestPath(const std::string& name) {
|
| return path.AppendASCII("extensions").AppendASCII(name);
|
| }
|
|
|
| +// Helper function that creates a file at |relative_path| within |directory|
|
| +// and fills it with |content|.
|
| +bool AddFileToDirectory(const base::FilePath& directory,
|
| + const base::FilePath& relative_path,
|
| + const std::string& content) {
|
| + base::FilePath full_path = directory.Append(relative_path);
|
| + int result = base::WriteFile(full_path, content.data(), content.size());
|
| + return static_cast<size_t>(result) == content.size();
|
| +}
|
| +
|
| scoped_refptr<Extension> CreateTestExtension(const std::string& name,
|
| bool incognito_split_mode) {
|
| base::DictionaryValue manifest;
|
| @@ -98,30 +116,96 @@ scoped_refptr<Extension> CreateTestResponseHeaderExtension() {
|
| return extension;
|
| }
|
|
|
| +// A ContentVerifyJob::TestDelegate that observes DoneReading().
|
| +class JobDelegate : public ContentVerifyJob::TestDelegate {
|
| + public:
|
| + explicit JobDelegate(const std::string& expected_contents)
|
| + : expected_contents_(expected_contents), run_loop_(new base::RunLoop()) {
|
| + ContentVerifyJob::SetDelegateForTests(this);
|
| + }
|
| + ~JobDelegate() override { ContentVerifyJob::SetDelegateForTests(nullptr); }
|
| +
|
| + ContentVerifyJob::FailureReason BytesRead(const ExtensionId& id,
|
| + int count,
|
| + const char* data) override {
|
| + read_contents_.append(data, count);
|
| + return ContentVerifyJob::NONE;
|
| + }
|
| +
|
| + ContentVerifyJob::FailureReason DoneReading(const ExtensionId& id) override {
|
| + seen_done_reading_extension_ids_.insert(id);
|
| + if (waiting_for_extension_id_ == id)
|
| + run_loop_->Quit();
|
| +
|
| + if (!base::StartsWith(expected_contents_, read_contents_,
|
| + base::CompareCase::SENSITIVE)) {
|
| + ADD_FAILURE() << "Unexpected read, expected: " << expected_contents_
|
| + << ", but found: " << read_contents_;
|
| + }
|
| + return ContentVerifyJob::NONE;
|
| + }
|
| +
|
| + void WaitForDoneReading(const ExtensionId& id) {
|
| + ASSERT_FALSE(waiting_for_extension_id_);
|
| + if (seen_done_reading_extension_ids_.count(id))
|
| + return;
|
| + waiting_for_extension_id_ = id;
|
| + run_loop_->Run();
|
| + }
|
| +
|
| + void Reset() {
|
| + read_contents_.clear();
|
| + waiting_for_extension_id_.reset();
|
| + seen_done_reading_extension_ids_.clear();
|
| + run_loop_ = base::MakeUnique<base::RunLoop>();
|
| + }
|
| +
|
| + private:
|
| + std::string expected_contents_;
|
| + std::string read_contents_;
|
| + std::set<ExtensionId> seen_done_reading_extension_ids_;
|
| + base::Optional<ExtensionId> waiting_for_extension_id_;
|
| + std::unique_ptr<base::RunLoop> run_loop_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(JobDelegate);
|
| +};
|
| +
|
| } // namespace
|
|
|
| // This test lives in src/chrome instead of src/extensions because it tests
|
| // functionality delegated back to Chrome via ChromeExtensionsBrowserClient.
|
| // See chrome/browser/extensions/chrome_url_request_util.cc.
|
| -class ExtensionProtocolTest : public testing::Test {
|
| +class ExtensionProtocolsTest : public testing::Test {
|
| public:
|
| - ExtensionProtocolTest()
|
| + ExtensionProtocolsTest()
|
| : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
|
| old_factory_(NULL),
|
| resource_context_(&test_url_request_context_) {}
|
|
|
| void SetUp() override {
|
| testing::Test::SetUp();
|
| + testing_profile_ = TestingProfile::Builder().Build();
|
| extension_info_map_ = new InfoMap();
|
| net::URLRequestContext* request_context =
|
| resource_context_.GetRequestContext();
|
| old_factory_ = request_context->job_factory();
|
| +
|
| + // Set up content verification.
|
| + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
| + command_line->AppendSwitchASCII(
|
| + switches::kExtensionContentVerification,
|
| + switches::kExtensionContentVerificationEnforce);
|
| + content_verifier_ = new ContentVerifier(
|
| + testing_profile_.get(),
|
| + new ChromeContentVerifierDelegate(testing_profile_.get()));
|
| + extension_info_map_->SetContentVerifier(content_verifier_.get());
|
| }
|
|
|
| void TearDown() override {
|
| net::URLRequestContext* request_context =
|
| resource_context_.GetRequestContext();
|
| request_context->set_job_factory(old_factory_);
|
| + content_verifier_->Shutdown();
|
| }
|
|
|
| void SetProtocolHandler(bool is_incognito) {
|
| @@ -175,13 +259,15 @@ class ExtensionProtocolTest : public testing::Test {
|
| net::TestDelegate test_delegate_;
|
| net::TestURLRequestContext test_url_request_context_;
|
| content::MockResourceContext resource_context_;
|
| + scoped_refptr<ContentVerifier> content_verifier_;
|
| + std::unique_ptr<TestingProfile> testing_profile_;
|
| };
|
|
|
| // Tests that making a chrome-extension request in an incognito context is
|
| // only allowed under the right circumstances (if the extension is allowed
|
| // in incognito, and it's either a non-main-frame request or a split-mode
|
| // extension).
|
| -TEST_F(ExtensionProtocolTest, IncognitoRequest) {
|
| +TEST_F(ExtensionProtocolsTest, IncognitoRequest) {
|
| // Register an incognito extension protocol handler.
|
| SetProtocolHandler(true);
|
|
|
| @@ -264,7 +350,7 @@ void CheckForContentLengthHeader(net::URLRequest* request) {
|
|
|
| // Tests getting a resource for a component extension works correctly, both when
|
| // the extension is enabled and when it is disabled.
|
| -TEST_F(ExtensionProtocolTest, ComponentResourceRequest) {
|
| +TEST_F(ExtensionProtocolsTest, ComponentResourceRequest) {
|
| // Register a non-incognito extension protocol handler.
|
| SetProtocolHandler(false);
|
|
|
| @@ -301,7 +387,7 @@ TEST_F(ExtensionProtocolTest, ComponentResourceRequest) {
|
|
|
| // Tests that a URL request for resource from an extension returns a few
|
| // expected response headers.
|
| -TEST_F(ExtensionProtocolTest, ResourceRequestResponseHeaders) {
|
| +TEST_F(ExtensionProtocolsTest, ResourceRequestResponseHeaders) {
|
| // Register a non-incognito extension protocol handler.
|
| SetProtocolHandler(false);
|
|
|
| @@ -339,7 +425,7 @@ TEST_F(ExtensionProtocolTest, ResourceRequestResponseHeaders) {
|
|
|
| // Tests that a URL request for main frame or subframe from an extension
|
| // succeeds, but subresources fail. See http://crbug.com/312269.
|
| -TEST_F(ExtensionProtocolTest, AllowFrameRequests) {
|
| +TEST_F(ExtensionProtocolsTest, AllowFrameRequests) {
|
| // Register a non-incognito extension protocol handler.
|
| SetProtocolHandler(false);
|
|
|
| @@ -386,8 +472,7 @@ TEST_F(ExtensionProtocolTest, AllowFrameRequests) {
|
| }
|
| }
|
|
|
| -
|
| -TEST_F(ExtensionProtocolTest, MetadataFolder) {
|
| +TEST_F(ExtensionProtocolsTest, MetadataFolder) {
|
| SetProtocolHandler(false);
|
|
|
| base::FilePath extension_dir = GetTestPath("metadata_folder");
|
| @@ -414,4 +499,58 @@ TEST_F(ExtensionProtocolTest, MetadataFolder) {
|
| DoRequest(*extension, relative_path.AsUTF8Unsafe()));
|
| }
|
|
|
| +// Tests that unreadable files and deleted files correctly go through
|
| +// ContentVerifyJob.
|
| +TEST_F(ExtensionProtocolsTest, VerificationSeenForFileAccessErrors) {
|
| + const char kFooJsContents[] = "hello world.";
|
| + JobDelegate test_job_delegate(kFooJsContents);
|
| + SetProtocolHandler(false);
|
| +
|
| + const std::string kFooJs("foo.js");
|
| + // Create a temporary directory that a fake extension will live in and fill
|
| + // it with some test files.
|
| + base::ScopedTempDir temp_dir;
|
| + ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
|
| + base::FilePath foo_js(FILE_PATH_LITERAL("foo.js"));
|
| + ASSERT_TRUE(AddFileToDirectory(temp_dir.GetPath(), foo_js, kFooJsContents))
|
| + << "Failed to write " << temp_dir.GetPath().value() << "/"
|
| + << foo_js.value();
|
| +
|
| + ExtensionBuilder builder;
|
| + builder
|
| + .SetManifest(DictionaryBuilder()
|
| + .Set("name", "Foo")
|
| + .Set("version", "1.0")
|
| + .Set("manifest_version", 2)
|
| + .Set("update_url",
|
| + "https://clients2.google.com/service/update2/crx")
|
| + .Build())
|
| + .SetID(crx_file::id_util::GenerateId("whatever"))
|
| + .SetPath(temp_dir.GetPath())
|
| + .SetLocation(Manifest::INTERNAL);
|
| + scoped_refptr<Extension> extension(builder.Build());
|
| +
|
| + ASSERT_TRUE(extension.get());
|
| + content_verifier_->OnExtensionLoaded(testing_profile_.get(), extension.get());
|
| + // Wait for PostTask to ContentVerifierIOData::AddData() to finish.
|
| + content::RunAllPendingInMessageLoop();
|
| +
|
| + // Valid and readable foo.js.
|
| + EXPECT_EQ(net::OK, DoRequest(*extension, kFooJs));
|
| + test_job_delegate.WaitForDoneReading(extension->id());
|
| +
|
| + // chmod -r foo.js.
|
| + base::FilePath foo_path = temp_dir.GetPath().AppendASCII(kFooJs);
|
| + ASSERT_TRUE(base::MakeFileUnreadable(foo_path));
|
| + test_job_delegate.Reset();
|
| + EXPECT_EQ(net::ERR_ACCESS_DENIED, DoRequest(*extension, kFooJs));
|
| + test_job_delegate.WaitForDoneReading(extension->id());
|
| +
|
| + // Delete foo.js.
|
| + ASSERT_TRUE(base::DieFileDie(foo_path, false));
|
| + test_job_delegate.Reset();
|
| + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, DoRequest(*extension, kFooJs));
|
| + test_job_delegate.WaitForDoneReading(extension->id());
|
| +}
|
| +
|
| } // namespace extensions
|
|
|