Commit b7559455 authored by Oleg Davydov's avatar Oleg Davydov Committed by Commit Bot

[Extensions] Make g_test_content_verify_job_observer thread-safe

ContentVerifyJob has ability to inject test observer, but before this CL
the observer is accessed from different threads, leading to flaky
crashes in tests. See https://crrev.com/c/2032998.

Now this observer (ContentVerifyJob::TestObserver and pointer to it from
g_test_content_verify_gob_observer in content_verify_job.cc) supports
thread-safe refcounted pointing, therefore is not destroyed too early.
Also it's wrappend into lazy initialized since we need access to it be
no-op outside of the tests.

Bug: 796395, 958794
Change-Id: I7479216f23083d4fd41dae08cb2e7acb62da0abd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2033159Reviewed-by: default avatarSergey Poromov <poromov@chromium.org>
Reviewed-by: default avatarIstiaque Ahmed <lazyboy@chromium.org>
Commit-Queue: Oleg Davydov <burunduk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#739387}
parent caf53052
...@@ -573,7 +573,7 @@ TEST_F(ExtensionProtocolsTest, VerificationSeenForFileAccessErrors) { ...@@ -573,7 +573,7 @@ TEST_F(ExtensionProtocolsTest, VerificationSeenForFileAccessErrors) {
// chmod -r 1024.js. // chmod -r 1024.js.
{ {
TestContentVerifySingleJobObserver observer(extension->id(), kRelativePath); TestContentVerifySingleJobObserver observer(extension_id, kRelativePath);
base::FilePath file_path = unzipped_path.AppendASCII(kJs); base::FilePath file_path = unzipped_path.AppendASCII(kJs);
ASSERT_TRUE(base::MakeFileUnreadable(file_path)); ASSERT_TRUE(base::MakeFileUnreadable(file_path));
EXPECT_EQ(net::ERR_ACCESS_DENIED, DoRequestOrLoad(extension, kJs).result()); EXPECT_EQ(net::ERR_ACCESS_DENIED, DoRequestOrLoad(extension, kJs).result());
...@@ -637,7 +637,7 @@ TEST_F(ExtensionProtocolsTest, VerificationSeenForZeroByteFile) { ...@@ -637,7 +637,7 @@ TEST_F(ExtensionProtocolsTest, VerificationSeenForZeroByteFile) {
// current behavior of ContentVerifyJob. // current behavior of ContentVerifyJob.
// TODO(lazyboy): The behavior is probably incorrect. // TODO(lazyboy): The behavior is probably incorrect.
{ {
TestContentVerifySingleJobObserver observer(extension->id(), kRelativePath); TestContentVerifySingleJobObserver observer(extension_id, kRelativePath);
base::FilePath file_path = unzipped_path.AppendASCII(kEmptyJs); base::FilePath file_path = unzipped_path.AppendASCII(kEmptyJs);
ASSERT_TRUE(base::MakeFileUnreadable(file_path)); ASSERT_TRUE(base::MakeFileUnreadable(file_path));
EXPECT_EQ(net::ERR_ACCESS_DENIED, EXPECT_EQ(net::ERR_ACCESS_DENIED,
......
...@@ -22,18 +22,46 @@ namespace extensions { ...@@ -22,18 +22,46 @@ namespace extensions {
TestContentVerifySingleJobObserver::TestContentVerifySingleJobObserver( TestContentVerifySingleJobObserver::TestContentVerifySingleJobObserver(
const ExtensionId& extension_id, const ExtensionId& extension_id,
const base::FilePath& relative_path) const base::FilePath& relative_path)
: extension_id_(extension_id), relative_path_(relative_path) { : client_(
ContentVerifyJob::SetObserverForTests(this); base::MakeRefCounted<ObserverClient>(extension_id, relative_path)) {
ContentVerifyJob::SetObserverForTests(client_);
} }
TestContentVerifySingleJobObserver::~TestContentVerifySingleJobObserver() { TestContentVerifySingleJobObserver::~TestContentVerifySingleJobObserver() {
ContentVerifyJob::SetObserverForTests(nullptr); ContentVerifyJob::SetObserverForTests(nullptr);
} }
void TestContentVerifySingleJobObserver::JobFinished( ContentVerifyJob::FailureReason
TestContentVerifySingleJobObserver::WaitForJobFinished() {
return client_->WaitForJobFinished();
}
void TestContentVerifySingleJobObserver::WaitForOnHashesReady() {
client_->WaitForOnHashesReady();
}
TestContentVerifySingleJobObserver::ObserverClient::ObserverClient(
const ExtensionId& extension_id,
const base::FilePath& relative_path)
: extension_id_(extension_id), relative_path_(relative_path) {
EXPECT_TRUE(
content::BrowserThread::GetCurrentThreadIdentifier(&creation_thread_));
}
TestContentVerifySingleJobObserver::ObserverClient::~ObserverClient() = default;
void TestContentVerifySingleJobObserver::ObserverClient::JobFinished(
const ExtensionId& extension_id, const ExtensionId& extension_id,
const base::FilePath& relative_path, const base::FilePath& relative_path,
ContentVerifyJob::FailureReason reason) { ContentVerifyJob::FailureReason reason) {
if (!content::BrowserThread::CurrentlyOn(creation_thread_)) {
base::PostTask(
FROM_HERE, {creation_thread_},
base::BindOnce(
&TestContentVerifySingleJobObserver::ObserverClient::JobFinished,
this, extension_id, relative_path, reason));
return;
}
if (extension_id != extension_id_ || relative_path != relative_path_) if (extension_id != extension_id_ || relative_path != relative_path_)
return; return;
EXPECT_FALSE(failure_reason_.has_value()); EXPECT_FALSE(failure_reason_.has_value());
...@@ -41,10 +69,18 @@ void TestContentVerifySingleJobObserver::JobFinished( ...@@ -41,10 +69,18 @@ void TestContentVerifySingleJobObserver::JobFinished(
job_finished_run_loop_.Quit(); job_finished_run_loop_.Quit();
} }
void TestContentVerifySingleJobObserver::OnHashesReady( void TestContentVerifySingleJobObserver::ObserverClient::OnHashesReady(
const ExtensionId& extension_id, const ExtensionId& extension_id,
const base::FilePath& relative_path, const base::FilePath& relative_path,
bool success) { bool success) {
if (!content::BrowserThread::CurrentlyOn(creation_thread_)) {
base::PostTask(
FROM_HERE, {creation_thread_},
base::BindOnce(
&TestContentVerifySingleJobObserver::ObserverClient::OnHashesReady,
this, extension_id, relative_path, success));
return;
}
if (extension_id != extension_id_ || relative_path != relative_path_) if (extension_id != extension_id_ || relative_path != relative_path_)
return; return;
EXPECT_FALSE(seen_on_hashes_ready_); EXPECT_FALSE(seen_on_hashes_ready_);
...@@ -53,60 +89,57 @@ void TestContentVerifySingleJobObserver::OnHashesReady( ...@@ -53,60 +89,57 @@ void TestContentVerifySingleJobObserver::OnHashesReady(
} }
ContentVerifyJob::FailureReason ContentVerifyJob::FailureReason
TestContentVerifySingleJobObserver::WaitForJobFinished() { TestContentVerifySingleJobObserver::ObserverClient::WaitForJobFinished() {
// Run() returns immediately if Quit() has already been called. // Run() returns immediately if Quit() has already been called.
job_finished_run_loop_.Run(); job_finished_run_loop_.Run();
EXPECT_TRUE(failure_reason_.has_value()); EXPECT_TRUE(failure_reason_.has_value());
return failure_reason_.value_or(ContentVerifyJob::FAILURE_REASON_MAX); return failure_reason_.value_or(ContentVerifyJob::FAILURE_REASON_MAX);
} }
void TestContentVerifySingleJobObserver::WaitForOnHashesReady() { void TestContentVerifySingleJobObserver::ObserverClient::
WaitForOnHashesReady() {
// Run() returns immediately if Quit() has already been called. // Run() returns immediately if Quit() has already been called.
on_hashes_ready_run_loop_.Run(); on_hashes_ready_run_loop_.Run();
} }
// TestContentVerifyJobObserver ------------------------------------------------ // TestContentVerifyJobObserver ------------------------------------------------
TestContentVerifyJobObserver::TestContentVerifyJobObserver()
: client_(base::MakeRefCounted<ObserverClient>()) {
ContentVerifyJob::SetObserverForTests(client_);
}
TestContentVerifyJobObserver::~TestContentVerifyJobObserver() {
ContentVerifyJob::SetObserverForTests(nullptr);
}
void TestContentVerifyJobObserver::ExpectJobResult( void TestContentVerifyJobObserver::ExpectJobResult(
const ExtensionId& extension_id, const ExtensionId& extension_id,
const base::FilePath& relative_path, const base::FilePath& relative_path,
Result expected_result) { Result expected_result) {
expectations_.push_back( client_->ExpectJobResult(extension_id, relative_path, expected_result);
ExpectedResult(extension_id, relative_path, expected_result));
} }
TestContentVerifyJobObserver::TestContentVerifyJobObserver() { bool TestContentVerifyJobObserver::WaitForExpectedJobs() {
EXPECT_TRUE( return client_->WaitForExpectedJobs();
content::BrowserThread::GetCurrentThreadIdentifier(&creation_thread_));
ContentVerifyJob::SetObserverForTests(this);
}
TestContentVerifyJobObserver::~TestContentVerifyJobObserver() {
ContentVerifyJob::SetObserverForTests(nullptr);
} }
bool TestContentVerifyJobObserver::WaitForExpectedJobs() { TestContentVerifyJobObserver::ObserverClient::ObserverClient() {
EXPECT_TRUE(content::BrowserThread::CurrentlyOn(creation_thread_)); EXPECT_TRUE(
if (!expectations_.empty()) { content::BrowserThread::GetCurrentThreadIdentifier(&creation_thread_));
base::RunLoop run_loop;
job_quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
return expectations_.empty();
} }
void TestContentVerifyJobObserver::JobStarted( TestContentVerifyJobObserver::ObserverClient::~ObserverClient() = default;
const ExtensionId& extension_id,
const base::FilePath& relative_path) {}
void TestContentVerifyJobObserver::JobFinished( void TestContentVerifyJobObserver::ObserverClient::JobFinished(
const ExtensionId& extension_id, const ExtensionId& extension_id,
const base::FilePath& relative_path, const base::FilePath& relative_path,
ContentVerifyJob::FailureReason failure_reason) { ContentVerifyJob::FailureReason failure_reason) {
if (!content::BrowserThread::CurrentlyOn(creation_thread_)) { if (!content::BrowserThread::CurrentlyOn(creation_thread_)) {
base::PostTask(FROM_HERE, {creation_thread_}, base::PostTask(
base::BindOnce(&TestContentVerifyJobObserver::JobFinished, FROM_HERE, {creation_thread_},
base::Unretained(this), extension_id, base::BindOnce(
relative_path, failure_reason)); &TestContentVerifyJobObserver::ObserverClient::JobFinished, this,
extension_id, relative_path, failure_reason));
return; return;
} }
Result result = failure_reason == ContentVerifyJob::NONE ? Result::SUCCESS Result result = failure_reason == ContentVerifyJob::NONE ? Result::SUCCESS
...@@ -130,6 +163,24 @@ void TestContentVerifyJobObserver::JobFinished( ...@@ -130,6 +163,24 @@ void TestContentVerifyJobObserver::JobFinished(
} }
} }
void TestContentVerifyJobObserver::ObserverClient::ExpectJobResult(
const ExtensionId& extension_id,
const base::FilePath& relative_path,
Result expected_result) {
expectations_.push_back(
ExpectedResult(extension_id, relative_path, expected_result));
}
bool TestContentVerifyJobObserver::ObserverClient::WaitForExpectedJobs() {
EXPECT_TRUE(content::BrowserThread::CurrentlyOn(creation_thread_));
if (!expectations_.empty()) {
base::RunLoop run_loop;
job_quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
return expectations_.empty();
}
// MockContentVerifierDelegate ------------------------------------------------ // MockContentVerifierDelegate ------------------------------------------------
MockContentVerifierDelegate::MockContentVerifierDelegate() = default; MockContentVerifierDelegate::MockContentVerifierDelegate() = default;
MockContentVerifierDelegate::~MockContentVerifierDelegate() = default; MockContentVerifierDelegate::~MockContentVerifierDelegate() = default;
......
...@@ -24,22 +24,12 @@ class Extension; ...@@ -24,22 +24,12 @@ class Extension;
// Test class to observe *a particular* extension resource's ContentVerifyJob // Test class to observe *a particular* extension resource's ContentVerifyJob
// lifetime. Provides a way to wait for a job to finish and return // lifetime. Provides a way to wait for a job to finish and return
// the job's result. // the job's result.
class TestContentVerifySingleJobObserver : ContentVerifyJob::TestObserver { class TestContentVerifySingleJobObserver {
public: public:
TestContentVerifySingleJobObserver(const ExtensionId& extension_id, TestContentVerifySingleJobObserver(const ExtensionId& extension_id,
const base::FilePath& relative_path); const base::FilePath& relative_path);
~TestContentVerifySingleJobObserver(); ~TestContentVerifySingleJobObserver();
// ContentVerifyJob::TestObserver:
void JobStarted(const ExtensionId& extension_id,
const base::FilePath& relative_path) override {}
void JobFinished(const ExtensionId& extension_id,
const base::FilePath& relative_path,
ContentVerifyJob::FailureReason reason) override;
void OnHashesReady(const ExtensionId& extension_id,
const base::FilePath& relative_path,
bool success) override;
// Waits for a ContentVerifyJob to finish and returns job's status. // Waits for a ContentVerifyJob to finish and returns job's status.
ContentVerifyJob::FailureReason WaitForJobFinished() WARN_UNUSED_RESULT; ContentVerifyJob::FailureReason WaitForJobFinished() WARN_UNUSED_RESULT;
...@@ -47,22 +37,55 @@ class TestContentVerifySingleJobObserver : ContentVerifyJob::TestObserver { ...@@ -47,22 +37,55 @@ class TestContentVerifySingleJobObserver : ContentVerifyJob::TestObserver {
void WaitForOnHashesReady(); void WaitForOnHashesReady();
private: private:
base::RunLoop job_finished_run_loop_; class ObserverClient : public ContentVerifyJob::TestObserver {
base::RunLoop on_hashes_ready_run_loop_; public:
ObserverClient(const ExtensionId& extension_id,
const base::FilePath& relative_path);
// ContentVerifyJob::TestObserver:
void JobStarted(const ExtensionId& extension_id,
const base::FilePath& relative_path) override {}
void JobFinished(const ExtensionId& extension_id,
const base::FilePath& relative_path,
ContentVerifyJob::FailureReason reason) override;
void OnHashesReady(const ExtensionId& extension_id,
const base::FilePath& relative_path,
bool success) override;
// Passed methods from ContentVerifySingleJobObserver:
ContentVerifyJob::FailureReason WaitForJobFinished() WARN_UNUSED_RESULT;
void WaitForOnHashesReady();
private:
~ObserverClient() override;
ObserverClient(const ObserverClient&) = delete;
ObserverClient& operator=(const ObserverClient&) = delete;
content::BrowserThread::ID creation_thread_;
ExtensionId extension_id_; base::RunLoop job_finished_run_loop_;
base::FilePath relative_path_; base::RunLoop on_hashes_ready_run_loop_;
base::Optional<ContentVerifyJob::FailureReason> failure_reason_;
bool seen_on_hashes_ready_ = false;
DISALLOW_COPY_AND_ASSIGN(TestContentVerifySingleJobObserver); ExtensionId extension_id_;
base::FilePath relative_path_;
base::Optional<ContentVerifyJob::FailureReason> failure_reason_;
bool seen_on_hashes_ready_ = false;
};
TestContentVerifySingleJobObserver(
const TestContentVerifySingleJobObserver&) = delete;
TestContentVerifySingleJobObserver& operator=(
const TestContentVerifySingleJobObserver&) = delete;
scoped_refptr<ObserverClient> client_;
}; };
// Test class to observe expected set of ContentVerifyJobs. // Test class to observe expected set of ContentVerifyJobs.
class TestContentVerifyJobObserver : public ContentVerifyJob::TestObserver { class TestContentVerifyJobObserver {
public: public:
TestContentVerifyJobObserver(); TestContentVerifyJobObserver();
virtual ~TestContentVerifyJobObserver(); ~TestContentVerifyJobObserver();
enum class Result { SUCCESS, FAILURE }; enum class Result { SUCCESS, FAILURE };
...@@ -75,34 +98,56 @@ class TestContentVerifyJobObserver : public ContentVerifyJob::TestObserver { ...@@ -75,34 +98,56 @@ class TestContentVerifyJobObserver : public ContentVerifyJob::TestObserver {
// finish, or false if there was an error or timeout. // finish, or false if there was an error or timeout.
bool WaitForExpectedJobs(); bool WaitForExpectedJobs();
// ContentVerifyJob::TestObserver interface
void JobStarted(const ExtensionId& extension_id,
const base::FilePath& relative_path) override;
void JobFinished(const ExtensionId& extension_id,
const base::FilePath& relative_path,
ContentVerifyJob::FailureReason failure_reason) override;
void OnHashesReady(const ExtensionId& extension_id,
const base::FilePath& relative_path,
bool success) override {}
private: private:
struct ExpectedResult { class ObserverClient : public ContentVerifyJob::TestObserver {
public: public:
ExtensionId extension_id; ObserverClient();
base::FilePath path;
Result result; // ContentVerifyJob::TestObserver:
void JobStarted(const ExtensionId& extension_id,
ExpectedResult(const ExtensionId& extension_id, const base::FilePath& relative_path) override {}
const base::FilePath& path, void JobFinished(const ExtensionId& extension_id,
Result result) const base::FilePath& relative_path,
: extension_id(extension_id), path(path), result(result) {} ContentVerifyJob::FailureReason failure_reason) override;
void OnHashesReady(const ExtensionId& extension_id,
const base::FilePath& relative_path,
bool success) override {}
// Passed methods from TestContentVerifyJobObserver:
void ExpectJobResult(const ExtensionId& extension_id,
const base::FilePath& relative_path,
Result expected_result);
bool WaitForExpectedJobs();
private:
struct ExpectedResult {
public:
ExtensionId extension_id;
base::FilePath path;
Result result;
ExpectedResult(const ExtensionId& extension_id,
const base::FilePath& path,
Result result)
: extension_id(extension_id), path(path), result(result) {}
};
~ObserverClient() override;
ObserverClient(const ObserverClient&) = delete;
ObserverClient& operator=(const ObserverClient&) = delete;
std::list<ExpectedResult> expectations_;
content::BrowserThread::ID creation_thread_;
// Accessed on |creation_thread_|.
base::OnceClosure job_quit_closure_;
}; };
std::list<ExpectedResult> expectations_;
content::BrowserThread::ID creation_thread_;
// Accessed on |creation_thread_|.
base::OnceClosure job_quit_closure_;
DISALLOW_COPY_AND_ASSIGN(TestContentVerifyJobObserver); TestContentVerifyJobObserver(const TestContentVerifyJobObserver&) = delete;
TestContentVerifyJobObserver& operator=(const TestContentVerifyJobObserver&) =
delete;
scoped_refptr<ObserverClient> client_;
}; };
// An extensions/ implementation of ContentVerifierDelegate for using in tests. // An extensions/ implementation of ContentVerifierDelegate for using in tests.
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <algorithm> #include <algorithm>
#include "base/bind.h" #include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
...@@ -23,7 +24,15 @@ namespace extensions { ...@@ -23,7 +24,15 @@ namespace extensions {
namespace { namespace {
bool g_ignore_verification_for_tests = false; bool g_ignore_verification_for_tests = false;
ContentVerifyJob::TestObserver* g_content_verify_job_test_observer = NULL;
base::LazyInstance<scoped_refptr<ContentVerifyJob::TestObserver>>::Leaky
g_content_verify_job_test_observer = LAZY_INSTANCE_INITIALIZER;
scoped_refptr<ContentVerifyJob::TestObserver> GetTestObserver() {
if (!g_content_verify_job_test_observer.IsCreated())
return nullptr;
return g_content_verify_job_test_observer.Get();
}
class ScopedElapsedTimer { class ScopedElapsedTimer {
public: public:
...@@ -86,9 +95,9 @@ void ContentVerifyJob::DidGetContentHashOnIO( ...@@ -86,9 +95,9 @@ void ContentVerifyJob::DidGetContentHashOnIO(
scoped_refptr<const ContentHash> content_hash) { scoped_refptr<const ContentHash> content_hash) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO); DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
if (g_content_verify_job_test_observer) scoped_refptr<TestObserver> test_observer = GetTestObserver();
g_content_verify_job_test_observer->JobStarted(extension_id_, if (test_observer)
relative_path_); test_observer->JobStarted(extension_id_, relative_path_);
// Build |hash_reader_|. // Build |hash_reader_|.
base::PostTaskAndReplyWithResult( base::PostTaskAndReplyWithResult(
FROM_HERE, FROM_HERE,
...@@ -119,10 +128,9 @@ void ContentVerifyJob::Done() { ...@@ -119,10 +128,9 @@ void ContentVerifyJob::Done() {
const bool can_proceed = has_ignorable_read_error_ || FinishBlock(); const bool can_proceed = has_ignorable_read_error_ || FinishBlock();
if (can_proceed) { if (can_proceed) {
if (g_content_verify_job_test_observer) { scoped_refptr<TestObserver> test_observer = GetTestObserver();
g_content_verify_job_test_observer->JobFinished(extension_id_, if (test_observer)
relative_path_, NONE); test_observer->JobFinished(extension_id_, relative_path_, NONE);
}
} else { } else {
DispatchFailureCallback(HASH_MISMATCH); DispatchFailureCallback(HASH_MISMATCH);
} }
...@@ -214,10 +222,9 @@ void ContentVerifyJob::OnHashesReady( ...@@ -214,10 +222,9 @@ void ContentVerifyJob::OnHashesReady(
if (g_ignore_verification_for_tests) if (g_ignore_verification_for_tests)
return; return;
if (g_content_verify_job_test_observer) { scoped_refptr<TestObserver> test_observer = GetTestObserver();
g_content_verify_job_test_observer->OnHashesReady(extension_id_, if (test_observer)
relative_path_, success); test_observer->OnHashesReady(extension_id_, relative_path_, success);
}
if (!success) { if (!success) {
if (!hash_reader_->has_content_hashes()) { if (!hash_reader_->has_content_hashes()) {
DispatchFailureCallback(MISSING_ALL_HASHES); DispatchFailureCallback(MISSING_ALL_HASHES);
...@@ -226,10 +233,9 @@ void ContentVerifyJob::OnHashesReady( ...@@ -226,10 +233,9 @@ void ContentVerifyJob::OnHashesReady(
if (hash_reader_->file_missing_from_verified_contents()) { if (hash_reader_->file_missing_from_verified_contents()) {
// Ignore verification of non-existent resources. // Ignore verification of non-existent resources.
if (g_content_verify_job_test_observer) { scoped_refptr<TestObserver> test_observer = GetTestObserver();
g_content_verify_job_test_observer->JobFinished(extension_id_, if (test_observer)
relative_path_, NONE); test_observer->JobFinished(extension_id_, relative_path_, NONE);
}
return; return;
} }
DispatchFailureCallback(NO_HASHES_FOR_FILE); DispatchFailureCallback(NO_HASHES_FOR_FILE);
...@@ -250,9 +256,10 @@ void ContentVerifyJob::OnHashesReady( ...@@ -250,9 +256,10 @@ void ContentVerifyJob::OnHashesReady(
ScopedElapsedTimer timer(&time_spent_); ScopedElapsedTimer timer(&time_spent_);
if (!has_ignorable_read_error_ && !FinishBlock()) { if (!has_ignorable_read_error_ && !FinishBlock()) {
DispatchFailureCallback(HASH_MISMATCH); DispatchFailureCallback(HASH_MISMATCH);
} else if (g_content_verify_job_test_observer) { } else {
g_content_verify_job_test_observer->JobFinished(extension_id_, scoped_refptr<TestObserver> test_observer = GetTestObserver();
relative_path_, NONE); if (test_observer)
test_observer->JobFinished(extension_id_, relative_path_, NONE);
} }
} }
} }
...@@ -264,11 +271,13 @@ void ContentVerifyJob::SetIgnoreVerificationForTests(bool value) { ...@@ -264,11 +271,13 @@ void ContentVerifyJob::SetIgnoreVerificationForTests(bool value) {
} }
// static // static
void ContentVerifyJob::SetObserverForTests(TestObserver* observer) { void ContentVerifyJob::SetObserverForTests(
DCHECK(observer == nullptr || g_content_verify_job_test_observer == nullptr) scoped_refptr<TestObserver> observer) {
DCHECK(observer == nullptr ||
g_content_verify_job_test_observer.Get() == nullptr)
<< "SetObserverForTests does not support interleaving. Observers should " << "SetObserverForTests does not support interleaving. Observers should "
<< "be set and then cleared one at a time."; << "be set and then cleared one at a time.";
g_content_verify_job_test_observer = observer; g_content_verify_job_test_observer.Get() = std::move(observer);
} }
void ContentVerifyJob::DispatchFailureCallback(FailureReason reason) { void ContentVerifyJob::DispatchFailureCallback(FailureReason reason) {
...@@ -279,10 +288,9 @@ void ContentVerifyJob::DispatchFailureCallback(FailureReason reason) { ...@@ -279,10 +288,9 @@ void ContentVerifyJob::DispatchFailureCallback(FailureReason reason) {
<< relative_path_.MaybeAsASCII() << " reason:" << reason; << relative_path_.MaybeAsASCII() << " reason:" << reason;
std::move(failure_callback_).Run(reason); std::move(failure_callback_).Run(reason);
} }
if (g_content_verify_job_test_observer) { scoped_refptr<TestObserver> test_observer = GetTestObserver();
g_content_verify_job_test_observer->JobFinished(extension_id_, if (test_observer)
relative_path_, reason); test_observer->JobFinished(extension_id_, relative_path_, reason);
}
} }
} // namespace extensions } // namespace extensions
...@@ -80,7 +80,7 @@ class ContentVerifyJob : public base::RefCountedThreadSafe<ContentVerifyJob> { ...@@ -80,7 +80,7 @@ class ContentVerifyJob : public base::RefCountedThreadSafe<ContentVerifyJob> {
// is not so appropriate. // is not so appropriate.
void Done(); void Done();
class TestObserver { class TestObserver : public base::RefCountedThreadSafe<TestObserver> {
public: public:
virtual void JobStarted(const ExtensionId& extension_id, virtual void JobStarted(const ExtensionId& extension_id,
const base::FilePath& relative_path) = 0; const base::FilePath& relative_path) = 0;
...@@ -92,12 +92,16 @@ class ContentVerifyJob : public base::RefCountedThreadSafe<ContentVerifyJob> { ...@@ -92,12 +92,16 @@ class ContentVerifyJob : public base::RefCountedThreadSafe<ContentVerifyJob> {
virtual void OnHashesReady(const ExtensionId& extension_id, virtual void OnHashesReady(const ExtensionId& extension_id,
const base::FilePath& relative_path, const base::FilePath& relative_path,
bool success) = 0; bool success) = 0;
protected:
virtual ~TestObserver() = default;
friend class base::RefCountedThreadSafe<TestObserver>;
}; };
static void SetIgnoreVerificationForTests(bool value); static void SetIgnoreVerificationForTests(bool value);
// Note: having interleaved observer is not supported. // Note: having interleaved observer is not supported.
static void SetObserverForTests(TestObserver* observer); static void SetObserverForTests(scoped_refptr<TestObserver> observer);
private: private:
virtual ~ContentVerifyJob(); virtual ~ContentVerifyJob();
......
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