0

Moved JsonPrefStore to use SequencedWorkerPool instead of FILE thread. The pool also ensures that the same file requests are written in order received and that they block on shutdown.

BUG=153367
TEST=existing unit/browser tests

Review URL: https://codereview.chromium.org/11027070

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@166603 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
zelidrag@chromium.org
2012-11-08 04:40:59 +00:00
parent 667be6ec36
commit 0de615a09d
45 changed files with 425 additions and 230 deletions

@@ -13,7 +13,7 @@
#include "base/file_path.h" #include "base/file_path.h"
#include "base/file_util.h" #include "base/file_util.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/message_loop_proxy.h" #include "base/task_runner.h"
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
#include "base/string_number_conversions.h" #include "base/string_number_conversions.h"
#include "base/threading/thread.h" #include "base/threading/thread.h"
@@ -90,14 +90,14 @@ void WriteToDiskTask(const FilePath& path, const std::string& data) {
} // namespace } // namespace
ImportantFileWriter::ImportantFileWriter( ImportantFileWriter::ImportantFileWriter(
const FilePath& path, MessageLoopProxy* file_message_loop_proxy) const FilePath& path, base::SequencedTaskRunner* task_runner)
: path_(path), : path_(path),
file_message_loop_proxy_(file_message_loop_proxy), task_runner_(task_runner),
serializer_(NULL), serializer_(NULL),
commit_interval_(TimeDelta::FromMilliseconds( commit_interval_(TimeDelta::FromMilliseconds(
kDefaultCommitIntervalMs)) { kDefaultCommitIntervalMs)) {
DCHECK(CalledOnValidThread()); DCHECK(CalledOnValidThread());
DCHECK(file_message_loop_proxy_.get()); DCHECK(task_runner_.get());
} }
ImportantFileWriter::~ImportantFileWriter() { ImportantFileWriter::~ImportantFileWriter() {
@@ -122,8 +122,8 @@ void ImportantFileWriter::WriteNow(const std::string& data) {
if (HasPendingWrite()) if (HasPendingWrite())
timer_.Stop(); timer_.Stop();
if (!file_message_loop_proxy_->PostTask( if (!task_runner_->PostTask(FROM_HERE,
FROM_HERE, MakeCriticalClosure(Bind(&WriteToDiskTask, path_, data)))) { MakeCriticalClosure(Bind(&WriteToDiskTask, path_, data)))) {
// Posting the task to background message loop is not expected // Posting the task to background message loop is not expected
// to fail, but if it does, avoid losing data and just hit the disk // to fail, but if it does, avoid losing data and just hit the disk
// on the current thread. // on the current thread.

@@ -17,7 +17,7 @@
namespace base { namespace base {
class MessageLoopProxy; class SequencedTaskRunner;
class Thread; class Thread;
// Helper to ensure that a file won't be corrupted by the write (for example on // Helper to ensure that a file won't be corrupted by the write (for example on
@@ -53,11 +53,11 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe {
// Initialize the writer. // Initialize the writer.
// |path| is the name of file to write. // |path| is the name of file to write.
// |file_message_loop_proxy| is the MessageLoopProxy for a thread on which // |task_runner| is the SequencedTaskRunner instance where on which we will
// file I/O can be done. // execute file I/O operations.
// All non-const methods, ctor and dtor must be called on the same thread. // All non-const methods, ctor and dtor must be called on the same thread.
ImportantFileWriter(const FilePath& path, ImportantFileWriter(const FilePath& path,
MessageLoopProxy* file_message_loop_proxy); base::SequencedTaskRunner* task_runner);
// You have to ensure that there are no pending writes at the moment // You have to ensure that there are no pending writes at the moment
// of destruction. // of destruction.
@@ -96,8 +96,8 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe {
// Path being written to. // Path being written to.
const FilePath path_; const FilePath path_;
// MessageLoopProxy for the thread on which file I/O can be done. // TaskRunner for the thread on which file I/O can be done.
scoped_refptr<MessageLoopProxy> file_message_loop_proxy_; const scoped_refptr<base::SequencedTaskRunner> task_runner_;
// Timer used to schedule commit after ScheduleWrite. // Timer used to schedule commit after ScheduleWrite.
OneShotTimer<ImportantFileWriter> timer_; OneShotTimer<ImportantFileWriter> timer_;

@@ -13,6 +13,8 @@
#include "base/json/json_string_value_serializer.h" #include "base/json/json_string_value_serializer.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/message_loop_proxy.h" #include "base/message_loop_proxy.h"
#include "base/sequenced_task_runner.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/values.h" #include "base/values.h"
namespace { namespace {
@@ -26,25 +28,25 @@ class FileThreadDeserializer
: public base::RefCountedThreadSafe<FileThreadDeserializer> { : public base::RefCountedThreadSafe<FileThreadDeserializer> {
public: public:
FileThreadDeserializer(JsonPrefStore* delegate, FileThreadDeserializer(JsonPrefStore* delegate,
base::MessageLoopProxy* file_loop_proxy) base::SequencedTaskRunner* sequenced_task_runner)
: no_dir_(false), : no_dir_(false),
error_(PersistentPrefStore::PREF_READ_ERROR_NONE), error_(PersistentPrefStore::PREF_READ_ERROR_NONE),
delegate_(delegate), delegate_(delegate),
file_loop_proxy_(file_loop_proxy), sequenced_task_runner_(sequenced_task_runner),
origin_loop_proxy_(base::MessageLoopProxy::current()) { origin_loop_proxy_(base::MessageLoopProxy::current()) {
} }
void Start(const FilePath& path) { void Start(const FilePath& path) {
DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
file_loop_proxy_->PostTask( sequenced_task_runner_->PostTask(
FROM_HERE, FROM_HERE,
base::Bind(&FileThreadDeserializer::ReadFileAndReport, base::Bind(&FileThreadDeserializer::ReadFileAndReport,
this, path)); this, path));
} }
// Deserializes JSON on the file thread. // Deserializes JSON on the sequenced task runner.
void ReadFileAndReport(const FilePath& path) { void ReadFileAndReport(const FilePath& path) {
DCHECK(file_loop_proxy_->BelongsToCurrentThread()); DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread());
value_.reset(DoReading(path, &error_, &no_dir_)); value_.reset(DoReading(path, &error_, &no_dir_));
@@ -84,9 +86,9 @@ class FileThreadDeserializer
bool no_dir_; bool no_dir_;
PersistentPrefStore::PrefReadError error_; PersistentPrefStore::PrefReadError error_;
scoped_ptr<Value> value_; scoped_ptr<Value> value_;
scoped_refptr<JsonPrefStore> delegate_; const scoped_refptr<JsonPrefStore> delegate_;
scoped_refptr<base::MessageLoopProxy> file_loop_proxy_; const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_; const scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_;
}; };
// static // static
@@ -98,7 +100,8 @@ void FileThreadDeserializer::HandleErrors(
PersistentPrefStore::PrefReadError* error) { PersistentPrefStore::PrefReadError* error) {
*error = PersistentPrefStore::PREF_READ_ERROR_NONE; *error = PersistentPrefStore::PREF_READ_ERROR_NONE;
if (!value) { if (!value) {
DVLOG(1) << "Error while loading JSON file: " << error_msg; DVLOG(1) << "Error while loading JSON file: " << error_msg
<< ", file: " << path.value();
switch (error_code) { switch (error_code) {
case JSONFileValueSerializer::JSON_ACCESS_DENIED: case JSONFileValueSerializer::JSON_ACCESS_DENIED:
*error = PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED; *error = PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED;
@@ -137,13 +140,23 @@ void FileThreadDeserializer::HandleErrors(
} // namespace } // namespace
scoped_refptr<base::SequencedTaskRunner> JsonPrefStore::GetTaskRunnerForFile(
const FilePath& filename,
base::SequencedWorkerPool* worker_pool) {
std::string token("json_pref_store-");
token.append(filename.AsUTF8Unsafe());
return worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
worker_pool->GetNamedSequenceToken(token),
base::SequencedWorkerPool::BLOCK_SHUTDOWN);
}
JsonPrefStore::JsonPrefStore(const FilePath& filename, JsonPrefStore::JsonPrefStore(const FilePath& filename,
base::MessageLoopProxy* file_message_loop_proxy) base::SequencedTaskRunner* sequenced_task_runner)
: path_(filename), : path_(filename),
file_message_loop_proxy_(file_message_loop_proxy), sequenced_task_runner_(sequenced_task_runner),
prefs_(new DictionaryValue()), prefs_(new DictionaryValue()),
read_only_(false), read_only_(false),
writer_(filename, file_message_loop_proxy), writer_(filename, sequenced_task_runner),
error_delegate_(NULL), error_delegate_(NULL),
initialized_(false), initialized_(false),
read_error_(PREF_READ_ERROR_OTHER) { read_error_(PREF_READ_ERROR_OTHER) {
@@ -245,7 +258,7 @@ void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate *error_delegate) {
// Start async reading of the preferences file. It will delete itself // Start async reading of the preferences file. It will delete itself
// in the end. // in the end.
scoped_refptr<FileThreadDeserializer> deserializer( scoped_refptr<FileThreadDeserializer> deserializer(
new FileThreadDeserializer(this, file_message_loop_proxy_.get())); new FileThreadDeserializer(this, sequenced_task_runner_.get()));
deserializer->Start(path_); deserializer->Start(path_);
} }

@@ -20,7 +20,8 @@
namespace base { namespace base {
class DictionaryValue; class DictionaryValue;
class MessageLoopProxy; class SequencedWorkerPool;
class SequencedTaskRunner;
class Value; class Value;
} }
@@ -31,10 +32,16 @@ class BASE_PREFS_EXPORT JsonPrefStore
: public PersistentPrefStore, : public PersistentPrefStore,
public base::ImportantFileWriter::DataSerializer { public base::ImportantFileWriter::DataSerializer {
public: public:
// |file_message_loop_proxy| is the MessageLoopProxy for a thread on which // Returns instance of SequencedTaskRunner which guarantees that file
// file I/O can be done. // operations on the same file will be executed in sequenced order.
static scoped_refptr<base::SequencedTaskRunner> GetTaskRunnerForFile(
const FilePath& pref_filename,
base::SequencedWorkerPool* worker_pool);
// |sequenced_task_runner| is must be a shutdown-blocking task runner, ideally
// created by GetTaskRunnerForFile() method above.
JsonPrefStore(const FilePath& pref_filename, JsonPrefStore(const FilePath& pref_filename,
base::MessageLoopProxy* file_message_loop_proxy); base::SequencedTaskRunner* sequenced_task_runner);
// PrefStore overrides: // PrefStore overrides:
virtual ReadResult GetValue(const std::string& key, virtual ReadResult GetValue(const std::string& key,
@@ -72,7 +79,7 @@ class BASE_PREFS_EXPORT JsonPrefStore
virtual bool SerializeData(std::string* output) OVERRIDE; virtual bool SerializeData(std::string* output) OVERRIDE;
FilePath path_; FilePath path_;
scoped_refptr<base::MessageLoopProxy> file_message_loop_proxy_; const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
scoped_ptr<base::DictionaryValue> prefs_; scoped_ptr<base::DictionaryValue> prefs_;

@@ -5,13 +5,12 @@
#include "base/file_util.h" #include "base/file_util.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/message_loop_proxy.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/prefs/json_pref_store.h" #include "base/prefs/json_pref_store.h"
#include "base/scoped_temp_dir.h" #include "base/scoped_temp_dir.h"
#include "base/string_number_conversions.h" #include "base/string_number_conversions.h"
#include "base/string_util.h" #include "base/string_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/thread.h" #include "base/threading/thread.h"
#include "base/utf_string_conversions.h" #include "base/utf_string_conversions.h"
#include "base/values.h" #include "base/values.h"
@@ -37,9 +36,7 @@ class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
class JsonPrefStoreTest : public testing::Test { class JsonPrefStoreTest : public testing::Test {
protected: protected:
virtual void SetUp() { virtual void SetUp() OVERRIDE {
message_loop_proxy_ = base::MessageLoopProxy::current();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_dir_)); ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_dir_));
@@ -53,7 +50,6 @@ class JsonPrefStoreTest : public testing::Test {
FilePath data_dir_; FilePath data_dir_;
// A message loop that we can use as the file thread message loop. // A message loop that we can use as the file thread message loop.
MessageLoop message_loop_; MessageLoop message_loop_;
scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
}; };
// Test fallback behavior for a nonexistent file. // Test fallback behavior for a nonexistent file.
@@ -61,7 +57,8 @@ TEST_F(JsonPrefStoreTest, NonExistentFile) {
FilePath bogus_input_file = data_dir_.AppendASCII("read.txt"); FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
ASSERT_FALSE(file_util::PathExists(bogus_input_file)); ASSERT_FALSE(file_util::PathExists(bogus_input_file));
scoped_refptr<JsonPrefStore> pref_store = scoped_refptr<JsonPrefStore> pref_store =
new JsonPrefStore(bogus_input_file, message_loop_proxy_.get()); new JsonPrefStore(
bogus_input_file, message_loop_.message_loop_proxy());
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
pref_store->ReadPrefs()); pref_store->ReadPrefs());
EXPECT_FALSE(pref_store->ReadOnly()); EXPECT_FALSE(pref_store->ReadOnly());
@@ -73,7 +70,8 @@ TEST_F(JsonPrefStoreTest, InvalidFile) {
FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json"); FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json");
ASSERT_TRUE(file_util::CopyFile(invalid_file_original, invalid_file)); ASSERT_TRUE(file_util::CopyFile(invalid_file_original, invalid_file));
scoped_refptr<JsonPrefStore> pref_store = scoped_refptr<JsonPrefStore> pref_store =
new JsonPrefStore(invalid_file, message_loop_proxy_.get()); new JsonPrefStore(
invalid_file, message_loop_.message_loop_proxy());
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE, EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
pref_store->ReadPrefs()); pref_store->ReadPrefs());
EXPECT_FALSE(pref_store->ReadOnly()); EXPECT_FALSE(pref_store->ReadOnly());
@@ -88,7 +86,7 @@ TEST_F(JsonPrefStoreTest, InvalidFile) {
// This function is used to avoid code duplication while testing synchronous and // This function is used to avoid code duplication while testing synchronous and
// asynchronous version of the JsonPrefStore loading. // asynchronous version of the JsonPrefStore loading.
void RunBasicJsonPrefStoreTest(JsonPrefStore *pref_store, void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
const FilePath& output_file, const FilePath& output_file,
const FilePath& golden_output_file) { const FilePath& golden_output_file) {
const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs"; const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs";
@@ -166,7 +164,8 @@ TEST_F(JsonPrefStoreTest, Basic) {
FilePath input_file = temp_dir_.path().AppendASCII("write.json"); FilePath input_file = temp_dir_.path().AppendASCII("write.json");
ASSERT_TRUE(file_util::PathExists(input_file)); ASSERT_TRUE(file_util::PathExists(input_file));
scoped_refptr<JsonPrefStore> pref_store = scoped_refptr<JsonPrefStore> pref_store =
new JsonPrefStore(input_file, message_loop_proxy_.get()); new JsonPrefStore(
input_file, message_loop_.message_loop_proxy());
ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
ASSERT_FALSE(pref_store->ReadOnly()); ASSERT_FALSE(pref_store->ReadOnly());
@@ -193,21 +192,24 @@ TEST_F(JsonPrefStoreTest, BasicAsync) {
FilePath input_file = temp_dir_.path().AppendASCII("write.json"); FilePath input_file = temp_dir_.path().AppendASCII("write.json");
ASSERT_TRUE(file_util::PathExists(input_file)); ASSERT_TRUE(file_util::PathExists(input_file));
scoped_refptr<JsonPrefStore> pref_store = scoped_refptr<JsonPrefStore> pref_store =
new JsonPrefStore(input_file, message_loop_proxy_.get()); new JsonPrefStore(
input_file, message_loop_.message_loop_proxy());
MockPrefStoreObserver mock_observer; {
pref_store->AddObserver(&mock_observer); MockPrefStoreObserver mock_observer;
pref_store->AddObserver(&mock_observer);
MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate; MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
pref_store->ReadPrefsAsync(mock_error_delegate); pref_store->ReadPrefsAsync(mock_error_delegate);
EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
EXPECT_CALL(*mock_error_delegate, EXPECT_CALL(*mock_error_delegate,
OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
message_loop_.RunUntilIdle(); message_loop_.RunUntilIdle();
pref_store->RemoveObserver(&mock_observer); pref_store->RemoveObserver(&mock_observer);
ASSERT_FALSE(pref_store->ReadOnly()); ASSERT_FALSE(pref_store->ReadOnly());
}
// The JSON file looks like this: // The JSON file looks like this:
// { // {
@@ -229,7 +231,8 @@ TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
FilePath bogus_input_file = data_dir_.AppendASCII("read.txt"); FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
ASSERT_FALSE(file_util::PathExists(bogus_input_file)); ASSERT_FALSE(file_util::PathExists(bogus_input_file));
scoped_refptr<JsonPrefStore> pref_store = scoped_refptr<JsonPrefStore> pref_store =
new JsonPrefStore(bogus_input_file, message_loop_proxy_.get()); new JsonPrefStore(
bogus_input_file, message_loop_.message_loop_proxy());
MockPrefStoreObserver mock_observer; MockPrefStoreObserver mock_observer;
pref_store->AddObserver(&mock_observer); pref_store->AddObserver(&mock_observer);
@@ -255,7 +258,8 @@ TEST_F(JsonPrefStoreTest, NeedsEmptyValue) {
// Test that the persistent value can be loaded. // Test that the persistent value can be loaded.
ASSERT_TRUE(file_util::PathExists(pref_file)); ASSERT_TRUE(file_util::PathExists(pref_file));
scoped_refptr<JsonPrefStore> pref_store = scoped_refptr<JsonPrefStore> pref_store =
new JsonPrefStore(pref_file, message_loop_proxy_.get()); new JsonPrefStore(
pref_file, message_loop_.message_loop_proxy());
ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
ASSERT_FALSE(pref_store->ReadOnly()); ASSERT_FALSE(pref_store->ReadOnly());

@@ -19,6 +19,7 @@
#include "base/path_service.h" #include "base/path_service.h"
#include "base/process.h" #include "base/process.h"
#include "base/process_util.h" #include "base/process_util.h"
#include "base/sequenced_task_runner.h"
#include "base/stringprintf.h" #include "base/stringprintf.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "base/time.h" #include "base/time.h"
@@ -1288,7 +1289,9 @@ void TestingAutomationProvider::GetBookmarksAsJSON(
return; return;
} }
scoped_refptr<BookmarkStorage> storage( scoped_refptr<BookmarkStorage> storage(
new BookmarkStorage(browser->profile(), bookmark_model)); new BookmarkStorage(browser->profile(),
bookmark_model,
browser->profile()->GetIOTaskRunner()));
if (!storage->SerializeData(&bookmarks_as_json)) { if (!storage->SerializeData(&bookmarks_as_json)) {
reply.SendError("Failed to serialize bookmarks"); reply.SendError("Failed to serialize bookmarks");
return; return;

@@ -11,6 +11,7 @@
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/json/json_string_value_serializer.h" #include "base/json/json_string_value_serializer.h"
#include "base/memory/scoped_vector.h" #include "base/memory/scoped_vector.h"
#include "base/sequenced_task_runner.h"
#include "base/string_util.h" #include "base/string_util.h"
#include "base/values.h" #include "base/values.h"
#include "build/build_config.h" #include "build/build_config.h"
@@ -247,7 +248,7 @@ void BookmarkModel::Load() {
content::Source<Profile>(profile_)); content::Source<Profile>(profile_));
// Load the bookmarks. BookmarkStorage notifies us when done. // Load the bookmarks. BookmarkStorage notifies us when done.
store_ = new BookmarkStorage(profile_, this); store_ = new BookmarkStorage(profile_, this, profile_->GetIOTaskRunner());
store_->LoadBookmarks(CreateLoadDetails()); store_->LoadBookmarks(CreateLoadDetails());
} }

@@ -107,15 +107,17 @@ BookmarkLoadDetails::~BookmarkLoadDetails() {
// BookmarkStorage ------------------------------------------------------------- // BookmarkStorage -------------------------------------------------------------
BookmarkStorage::BookmarkStorage(content::BrowserContext* context, BookmarkStorage::BookmarkStorage(
BookmarkModel* model) content::BrowserContext* context,
BookmarkModel* model,
base::SequencedTaskRunner* sequenced_task_runner)
: model_(model), : model_(model),
writer_(context->GetPath().Append(chrome::kBookmarksFileName), writer_(context->GetPath().Append(chrome::kBookmarksFileName),
BrowserThread::GetMessageLoopProxyForThread( sequenced_task_runner) {
BrowserThread::FILE)) { sequenced_task_runner_ = sequenced_task_runner;
writer_.set_commit_interval(base::TimeDelta::FromMilliseconds(kSaveDelayMS)); writer_.set_commit_interval(base::TimeDelta::FromMilliseconds(kSaveDelayMS));
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, sequenced_task_runner_->PostTask(FROM_HERE,
base::Bind(&BackupCallback, writer_.path())); base::Bind(&BackupCallback, writer_.path()));
} }
BookmarkStorage::~BookmarkStorage() { BookmarkStorage::~BookmarkStorage() {
@@ -127,8 +129,10 @@ void BookmarkStorage::LoadBookmarks(BookmarkLoadDetails* details) {
DCHECK(!details_.get()); DCHECK(!details_.get());
DCHECK(details); DCHECK(details);
details_.reset(details); details_.reset(details);
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( sequenced_task_runner_->PostTask(
&LoadCallback, writer_.path(), make_scoped_refptr(this), details_.get())); FROM_HERE,
base::Bind(&LoadCallback, writer_.path(), make_scoped_refptr(this),
details_.get()));
} }
void BookmarkStorage::ScheduleSave() { void BookmarkStorage::ScheduleSave() {

@@ -13,6 +13,10 @@
class BookmarkModel; class BookmarkModel;
class BookmarkPermanentNode; class BookmarkPermanentNode;
namespace base {
class SequencedTaskRunner;
}
namespace content { namespace content {
class BrowserContext; class BrowserContext;
} }
@@ -102,7 +106,9 @@ class BookmarkStorage : public base::ImportantFileWriter::DataSerializer,
public base::RefCountedThreadSafe<BookmarkStorage> { public base::RefCountedThreadSafe<BookmarkStorage> {
public: public:
// Creates a BookmarkStorage for the specified model // Creates a BookmarkStorage for the specified model
BookmarkStorage(content::BrowserContext* context, BookmarkModel* model); BookmarkStorage(content::BrowserContext* context,
BookmarkModel* model,
base::SequencedTaskRunner* sequenced_task_runner);
// Loads the bookmarks into the model, notifying the model when done. This // Loads the bookmarks into the model, notifying the model when done. This
// takes ownership of |details|. See BookmarkLoadDetails for details. // takes ownership of |details|. See BookmarkLoadDetails for details.
@@ -139,6 +145,9 @@ class BookmarkStorage : public base::ImportantFileWriter::DataSerializer,
// See class description of BookmarkLoadDetails for details on this. // See class description of BookmarkLoadDetails for details on this.
scoped_ptr<BookmarkLoadDetails> details_; scoped_ptr<BookmarkLoadDetails> details_;
// Sequenced task runner where file I/O operations will be performed at.
scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
DISALLOW_COPY_AND_ASSIGN(BookmarkStorage); DISALLOW_COPY_AND_ASSIGN(BookmarkStorage);
}; };

@@ -133,7 +133,9 @@ using content::ChildProcessSecurityPolicy;
using content::PluginService; using content::PluginService;
using content::ResourceDispatcherHost; using content::ResourceDispatcherHost;
BrowserProcessImpl::BrowserProcessImpl(const CommandLine& command_line) BrowserProcessImpl::BrowserProcessImpl(
base::SequencedTaskRunner* local_state_task_runner,
const CommandLine& command_line)
: created_metrics_service_(false), : created_metrics_service_(false),
created_watchdog_thread_(false), created_watchdog_thread_(false),
created_browser_policy_connector_(false), created_browser_policy_connector_(false),
@@ -147,7 +149,8 @@ BrowserProcessImpl::BrowserProcessImpl(const CommandLine& command_line)
checked_for_new_frames_(false), checked_for_new_frames_(false),
using_new_frames_(false), using_new_frames_(false),
render_widget_snapshot_taker_(new RenderWidgetSnapshotTaker), render_widget_snapshot_taker_(new RenderWidgetSnapshotTaker),
download_status_updater_(new DownloadStatusUpdater) { download_status_updater_(new DownloadStatusUpdater),
local_state_task_runner_(local_state_task_runner) {
g_browser_process = this; g_browser_process = this;
#if defined(ENABLE_PRINTING) #if defined(ENABLE_PRINTING)
@@ -709,10 +712,10 @@ void BrowserProcessImpl::CreateLocalState() {
created_local_state_ = true; created_local_state_ = true;
FilePath local_state_path; FilePath local_state_path;
PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path); CHECK(PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path));
local_state_.reset( local_state_.reset(
PrefService::CreatePrefService(local_state_path, policy_service(), NULL, PrefService::CreatePrefService(local_state_path, local_state_task_runner_,
false)); policy_service(), NULL, false));
// Initialize the prefs of the local state. // Initialize the prefs of the local state.
chrome::RegisterLocalState(local_state_.get()); chrome::RegisterLocalState(local_state_.get());

@@ -31,6 +31,10 @@ class RemoteDebuggingServer;
class PluginsResourceService; class PluginsResourceService;
#endif #endif
namespace base {
class SequencedTaskRunner;
}
namespace policy { namespace policy {
class BrowserPolicyConnector; class BrowserPolicyConnector;
class PolicyService; class PolicyService;
@@ -41,7 +45,9 @@ class BrowserProcessImpl : public BrowserProcess,
public base::NonThreadSafe, public base::NonThreadSafe,
public PrefObserver { public PrefObserver {
public: public:
explicit BrowserProcessImpl(const CommandLine& command_line); // |local_state_task_runner| must be a shutdown-blocking task runner.
BrowserProcessImpl(base::SequencedTaskRunner* local_state_task_runner,
const CommandLine& command_line);
virtual ~BrowserProcessImpl(); virtual ~BrowserProcessImpl();
// Called before the browser threads are created. // Called before the browser threads are created.
@@ -226,6 +232,9 @@ class BrowserProcessImpl : public BrowserProcess,
scoped_refptr<DownloadRequestLimiter> download_request_limiter_; scoped_refptr<DownloadRequestLimiter> download_request_limiter_;
// Sequenced task runner for local state related I/O tasks.
const scoped_refptr<base::SequencedTaskRunner> local_state_task_runner_;
// Ensures that the observers of plugin/print disable/enable state // Ensures that the observers of plugin/print disable/enable state
// notifications are properly added and removed. // notifications are properly added and removed.
PrefChangeRegistrar pref_change_registrar_; PrefChangeRegistrar pref_change_registrar_;

@@ -277,8 +277,11 @@ void InitializeNetworkOptions(const CommandLine& parsed_command_line,
} }
// Returns the new local state object, guaranteed non-NULL. // Returns the new local state object, guaranteed non-NULL.
PrefService* InitializeLocalState(const CommandLine& parsed_command_line, // |local_state_task_runner| must be a shutdown-blocking task runner.
bool is_first_run) { PrefService* InitializeLocalState(
base::SequencedTaskRunner* local_state_task_runner,
const CommandLine& parsed_command_line,
bool is_first_run) {
FilePath local_state_path; FilePath local_state_path;
PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path); PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path);
bool local_state_file_exists = file_util::PathExists(local_state_path); bool local_state_file_exists = file_util::PathExists(local_state_path);
@@ -332,7 +335,7 @@ PrefService* InitializeLocalState(const CommandLine& parsed_command_line,
FilePath parent_profile = FilePath parent_profile =
parsed_command_line.GetSwitchValuePath(switches::kParentProfile); parsed_command_line.GetSwitchValuePath(switches::kParentProfile);
scoped_ptr<PrefService> parent_local_state( scoped_ptr<PrefService> parent_local_state(
PrefService::CreatePrefService(parent_profile, PrefService::CreatePrefService(parent_profile, local_state_task_runner,
g_browser_process->policy_service(), g_browser_process->policy_service(),
NULL, false)); NULL, false));
parent_local_state->RegisterStringPref(prefs::kApplicationLocale, parent_local_state->RegisterStringPref(prefs::kApplicationLocale,
@@ -723,7 +726,14 @@ int ChromeBrowserMainParts::PreCreateThreadsImpl() {
parsed_command_line().HasSwitch(switches::kFirstRun)) && parsed_command_line().HasSwitch(switches::kFirstRun)) &&
!HasImportSwitch(parsed_command_line()); !HasImportSwitch(parsed_command_line());
#endif #endif
browser_process_.reset(new BrowserProcessImpl(parsed_command_line()));
FilePath local_state_path;
CHECK(PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path));
scoped_refptr<base::SequencedTaskRunner> local_state_task_runner =
JsonPrefStore::GetTaskRunnerForFile(local_state_path,
BrowserThread::GetBlockingPool());
browser_process_.reset(new BrowserProcessImpl(local_state_task_runner,
parsed_command_line()));
if (parsed_command_line().HasSwitch(switches::kEnableProfiling)) { if (parsed_command_line().HasSwitch(switches::kEnableProfiling)) {
// User wants to override default tracking status. // User wants to override default tracking status.
@@ -745,7 +755,9 @@ int ChromeBrowserMainParts::PreCreateThreadsImpl() {
switches::kProfilingOutputFile)); switches::kProfilingOutputFile));
} }
local_state_ = InitializeLocalState(parsed_command_line(), is_first_run_); local_state_ = InitializeLocalState(local_state_task_runner,
parsed_command_line(),
is_first_run_);
// These members must be initialized before returning from this function. // These members must be initialized before returning from this function.
master_prefs_.reset(new first_run::MasterPrefs); master_prefs_.reset(new first_run::MasterPrefs);

@@ -220,14 +220,14 @@ class LoginUtilsTest : public testing::Test,
connector_ = browser_process_->browser_policy_connector(); connector_ = browser_process_->browser_policy_connector();
connector_->Init(); connector_->Init();
RunAllPending(); RunUntilIdle();
} }
virtual void TearDown() OVERRIDE { virtual void TearDown() OVERRIDE {
cryptohome::AsyncMethodCaller::Shutdown(); cryptohome::AsyncMethodCaller::Shutdown();
mock_async_method_caller_ = NULL; mock_async_method_caller_ = NULL;
RunAllPending(); RunUntilIdle();
{ {
// chrome_browser_net::Predictor usually skips its shutdown routines on // chrome_browser_net::Predictor usually skips its shutdown routines on
// unit_tests, but does the full thing when // unit_tests, but does the full thing when
@@ -242,7 +242,7 @@ class LoginUtilsTest : public testing::Test,
loop_.PostTask(FROM_HERE, loop_.PostTask(FROM_HERE,
base::Bind(&LoginUtilsTest::TearDownOnIO, base::Bind(&LoginUtilsTest::TearDownOnIO,
base::Unretained(this))); base::Unretained(this)));
RunAllPending(); RunUntilIdle();
io_thread_.DeprecatedSetMessageLoop(NULL); io_thread_.DeprecatedSetMessageLoop(NULL);
} }
@@ -251,7 +251,7 @@ class LoginUtilsTest : public testing::Test,
browser_process_->SetProfileManager(NULL); browser_process_->SetProfileManager(NULL);
connector_ = NULL; connector_ = NULL;
browser_process_->SetBrowserPolicyConnector(NULL); browser_process_->SetBrowserPolicyConnector(NULL);
RunAllPending(); RunUntilIdle();
} }
void TearDownOnIO() { void TearDownOnIO() {
@@ -267,10 +267,10 @@ class LoginUtilsTest : public testing::Test,
} }
} }
void RunAllPending() { void RunUntilIdle() {
loop_.RunAllPending(); loop_.RunUntilIdle();
BrowserThread::GetBlockingPool()->FlushForTesting(); BrowserThread::GetBlockingPool()->FlushForTesting();
loop_.RunAllPending(); loop_.RunUntilIdle();
} }
virtual void OnProfilePrepared(Profile* profile) OVERRIDE { virtual void OnProfilePrepared(Profile* profile) OVERRIDE {
@@ -299,7 +299,7 @@ class LoginUtilsTest : public testing::Test,
device_data_store->set_device_id(kDeviceId); device_data_store->set_device_id(kDeviceId);
EXPECT_EQ(policy::EnterpriseInstallAttributes::LOCK_SUCCESS, EXPECT_EQ(policy::EnterpriseInstallAttributes::LOCK_SUCCESS,
connector_->LockDevice(username)); connector_->LockDevice(username));
RunAllPending(); RunUntilIdle();
} }
void PrepareProfile(const std::string& username) { void PrepareProfile(const std::string& username) {
@@ -325,7 +325,7 @@ class LoginUtilsTest : public testing::Test,
kPendingRequests, kUsingOAuth, kPendingRequests, kUsingOAuth,
kHasCookies, this); kHasCookies, this);
device_settings_test_helper.Flush(); device_settings_test_helper.Flush();
RunAllPending(); RunUntilIdle();
} }
net::TestURLFetcher* PrepareOAuthFetcher(const std::string& expected_url) { net::TestURLFetcher* PrepareOAuthFetcher(const std::string& expected_url) {
@@ -477,7 +477,7 @@ TEST_F(LoginUtilsTest, OAuth1TokenFetchFailureUnblocksRefreshPolicies) {
bool refresh_policies_completed = false; bool refresh_policies_completed = false;
browser_process_->policy_service()->RefreshPolicies( browser_process_->policy_service()->RefreshPolicies(
base::Bind(SetFlag, &refresh_policies_completed)); base::Bind(SetFlag, &refresh_policies_completed));
RunAllPending(); RunUntilIdle();
ASSERT_FALSE(refresh_policies_completed); ASSERT_FALSE(refresh_policies_completed);
// 4. Now make the fetcher fail. RefreshPolicies() should unblock. // 4. Now make the fetcher fail. RefreshPolicies() should unblock.
@@ -495,7 +495,7 @@ TEST_F(LoginUtilsTest, OAuth1TokenFetchFailureUnblocksRefreshPolicies) {
for (int i = 0; i < 6; ++i) { for (int i = 0; i < 6; ++i) {
ASSERT_FALSE(refresh_policies_completed); ASSERT_FALSE(refresh_policies_completed);
delegate->OnURLFetchComplete(&mock_fetcher); delegate->OnURLFetchComplete(&mock_fetcher);
RunAllPending(); RunUntilIdle();
} }
EXPECT_TRUE(refresh_policies_completed); EXPECT_TRUE(refresh_policies_completed);
} }
@@ -555,14 +555,14 @@ TEST_P(LoginUtilsBlockingLoginTest, EnterpriseLoginBlocksForEnterpriseUser) {
// The cloud policy subsystem is now ready to fetch the dmtoken and the user // The cloud policy subsystem is now ready to fetch the dmtoken and the user
// policy. // policy.
RunAllPending(); RunUntilIdle();
if (steps < 4) break; if (steps < 4) break;
fetcher = PrepareDMRegisterFetcher(); fetcher = PrepareDMRegisterFetcher();
ASSERT_TRUE(fetcher); ASSERT_TRUE(fetcher);
fetcher->delegate()->OnURLFetchComplete(fetcher); fetcher->delegate()->OnURLFetchComplete(fetcher);
// The policy fetch job has now been scheduled, run it: // The policy fetch job has now been scheduled, run it:
RunAllPending(); RunUntilIdle();
if (steps < 5) break; if (steps < 5) break;
// Verify that there is no profile prepared just before the policy fetch. // Verify that there is no profile prepared just before the policy fetch.

@@ -53,10 +53,11 @@ static void AddPattern(URLPatternSet* extent, const std::string& pattern) {
ExtensionPrefsTest::ExtensionPrefsTest() ExtensionPrefsTest::ExtensionPrefsTest()
: ui_thread_(BrowserThread::UI, &message_loop_), : ui_thread_(BrowserThread::UI, &message_loop_),
file_thread_(BrowserThread::FILE, &message_loop_) { prefs_(message_loop_.message_loop_proxy()) {
} }
ExtensionPrefsTest::~ExtensionPrefsTest() {} ExtensionPrefsTest::~ExtensionPrefsTest() {
}
void ExtensionPrefsTest::RegisterPreferences() {} void ExtensionPrefsTest::RegisterPreferences() {}
@@ -72,6 +73,8 @@ void ExtensionPrefsTest::TearDown() {
prefs_.RecreateExtensionPrefs(); prefs_.RecreateExtensionPrefs();
RegisterPreferences(); RegisterPreferences();
Verify(); Verify();
prefs_.pref_service()->CommitPendingWrite();
message_loop_.RunUntilIdle();
} }
// Tests the LastPingDay/SetLastPingDay functions. // Tests the LastPingDay/SetLastPingDay functions.

@@ -44,7 +44,6 @@ class ExtensionPrefsTest : public testing::Test {
MessageLoop message_loop_; MessageLoop message_loop_;
content::TestBrowserThread ui_thread_; content::TestBrowserThread ui_thread_;
content::TestBrowserThread file_thread_;
TestExtensionPrefs prefs_; TestExtensionPrefs prefs_;

@@ -422,7 +422,8 @@ void ExtensionServiceTestBase::InitializeExtensionService(
TestingProfile::Builder profile_builder; TestingProfile::Builder profile_builder;
// Create a PrefService that only contains user defined preference values. // Create a PrefService that only contains user defined preference values.
scoped_ptr<PrefService> prefs( scoped_ptr<PrefService> prefs(
PrefServiceMockBuilder().WithUserFilePrefs(pref_file).Create()); PrefServiceMockBuilder().WithUserFilePrefs(
pref_file, loop_.message_loop_proxy()).Create());
Profile::RegisterUserPrefs(prefs.get()); Profile::RegisterUserPrefs(prefs.get());
chrome::RegisterUserPrefs(prefs.get()); chrome::RegisterUserPrefs(prefs.get());
profile_builder.SetPrefService(prefs.Pass()); profile_builder.SetPrefService(prefs.Pass());

@@ -15,6 +15,7 @@
#include "chrome/browser/extensions/event_router.h" #include "chrome/browser/extensions/event_router.h"
#include "chrome/browser/extensions/extension_system_factory.h" #include "chrome/browser/extensions/extension_system_factory.h"
#include "chrome/browser/extensions/menu_manager.h" #include "chrome/browser/extensions/menu_manager.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/extensions/test_extension_prefs.h" #include "chrome/browser/extensions/test_extension_prefs.h"
#include "chrome/browser/extensions/test_extension_system.h" #include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_notification_types.h"
@@ -44,9 +45,15 @@ class MenuManagerTest : public testing::Test {
MenuManagerTest() : ui_thread_(BrowserThread::UI, &message_loop_), MenuManagerTest() : ui_thread_(BrowserThread::UI, &message_loop_),
file_thread_(BrowserThread::FILE, &message_loop_), file_thread_(BrowserThread::FILE, &message_loop_),
manager_(&profile_), manager_(&profile_),
prefs_(message_loop_.message_loop_proxy()),
next_id_(1) { next_id_(1) {
} }
virtual void TearDown() OVERRIDE {
prefs_.pref_service()->CommitPendingWrite();
message_loop_.RunUntilIdle();
}
// Returns a test item. // Returns a test item.
MenuItem* CreateTestItem(Extension* extension, bool incognito = false) { MenuItem* CreateTestItem(Extension* extension, bool incognito = false) {
MenuItem::Type type = MenuItem::NORMAL; MenuItem::Type type = MenuItem::NORMAL;

@@ -11,6 +11,8 @@
#include "base/message_loop.h" #include "base/message_loop.h"
#include "base/message_loop_proxy.h" #include "base/message_loop_proxy.h"
#include "base/prefs/json_pref_store.h" #include "base/prefs/json_pref_store.h"
#include "base/run_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/extensions/extension_pref_store.h" #include "chrome/browser/extensions/extension_pref_store.h"
@@ -31,6 +33,8 @@ namespace extensions {
namespace { namespace {
void DoNothing() {}
// Mock ExtensionPrefs class with artificial clock to guarantee that no two // Mock ExtensionPrefs class with artificial clock to guarantee that no two
// extensions get the same installation time stamp and we can reliably // extensions get the same installation time stamp and we can reliably
// assert the installation order in the tests below. // assert the installation order in the tests below.
@@ -54,9 +58,10 @@ class MockExtensionPrefs : public ExtensionPrefs {
} // namespace } // namespace
TestExtensionPrefs::TestExtensionPrefs() TestExtensionPrefs::TestExtensionPrefs(
: pref_service_(NULL), base::SequencedTaskRunner* task_runner) : pref_service_(NULL),
extensions_disabled_(false) { task_runner_(task_runner),
extensions_disabled_(false) {
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
preferences_file_ = temp_dir_.path().AppendASCII("Preferences"); preferences_file_ = temp_dir_.path().AppendASCII("Preferences");
extensions_dir_ = temp_dir_.path().AppendASCII("Extensions"); extensions_dir_ = temp_dir_.path().AppendASCII("Extensions");
@@ -65,36 +70,29 @@ TestExtensionPrefs::TestExtensionPrefs()
RecreateExtensionPrefs(); RecreateExtensionPrefs();
} }
TestExtensionPrefs::~TestExtensionPrefs() {} TestExtensionPrefs::~TestExtensionPrefs() {
}
void TestExtensionPrefs::RecreateExtensionPrefs() { void TestExtensionPrefs::RecreateExtensionPrefs() {
// We persist and reload the PrefService's PrefStores because this process // We persist and reload the PrefService's PrefStores because this process
// deletes all empty dictionaries. The ExtensionPrefs implementation // deletes all empty dictionaries. The ExtensionPrefs implementation
// needs to be able to handle this situation. // needs to be able to handle this situation.
if (pref_service_.get()) { if (pref_service_.get()) {
// The PrefService writes its persistent file on the file thread, so we // Commit a pending write (which posts a task to task_runner_) and wait for
// need to wait for any pending I/O to complete before creating a new // it to finish.
// PrefService. pref_service_->CommitPendingWrite();
base::WaitableEvent io_finished(false, false); base::RunLoop run_loop;
pref_service_-> CommitPendingWrite(); ASSERT_TRUE(
EXPECT_TRUE(BrowserThread::PostTask( task_runner_->PostTaskAndReply(
BrowserThread::FILE, FROM_HERE,
FROM_HERE, base::Bind(&DoNothing),
base::Bind(&base::WaitableEvent::Signal, run_loop.QuitClosure()));
base::Unretained(&io_finished)))); run_loop.Run();
// If the FILE thread is in fact the current thread (possible in testing
// scenarios), we have to ensure the task has a chance to run. If the FILE
// thread is a different thread, the test must ensure that thread is running
// (otherwise the Wait below will hang).
MessageLoop::current()->RunAllPending();
io_finished.Wait();
} }
extension_pref_value_map_.reset(new ExtensionPrefValueMap); extension_pref_value_map_.reset(new ExtensionPrefValueMap);
PrefServiceMockBuilder builder; PrefServiceMockBuilder builder;
builder.WithUserFilePrefs(preferences_file_); builder.WithUserFilePrefs(preferences_file_, task_runner_);
builder.WithExtensionPrefs( builder.WithExtensionPrefs(
new ExtensionPrefStore(extension_pref_value_map_.get(), false)); new ExtensionPrefStore(extension_pref_value_map_.get(), false));
pref_service_.reset(builder.Create()); pref_service_.reset(builder.Create());

@@ -16,6 +16,7 @@ class PrefService;
namespace base { namespace base {
class DictionaryValue; class DictionaryValue;
class SequencedTaskRunner;
} }
namespace extensions { namespace extensions {
@@ -25,7 +26,7 @@ class ExtensionPrefs;
// in tests. // in tests.
class TestExtensionPrefs { class TestExtensionPrefs {
public: public:
TestExtensionPrefs(); explicit TestExtensionPrefs(base::SequencedTaskRunner* task_runner);
virtual ~TestExtensionPrefs(); virtual ~TestExtensionPrefs();
ExtensionPrefs* prefs() { return prefs_.get(); } ExtensionPrefs* prefs() { return prefs_.get(); }
@@ -77,6 +78,7 @@ class TestExtensionPrefs {
scoped_ptr<PrefService> pref_service_; scoped_ptr<PrefService> pref_service_;
scoped_ptr<ExtensionPrefs> prefs_; scoped_ptr<ExtensionPrefs> prefs_;
scoped_ptr<ExtensionPrefValueMap> extension_pref_value_map_; scoped_ptr<ExtensionPrefValueMap> extension_pref_value_map_;
const scoped_refptr<base::SequencedTaskRunner> task_runner_;
private: private:
bool extensions_disabled_; bool extensions_disabled_;

@@ -162,10 +162,12 @@ class NotificationsObserver : public content::NotificationObserver {
// Base class for further specialized test classes. // Base class for further specialized test classes.
class MockService : public TestExtensionService { class MockService : public TestExtensionService {
public: public:
MockService() explicit MockService(TestExtensionPrefs* prefs)
: pending_extension_manager_(ALLOW_THIS_IN_INITIALIZER_LIST(*this)) { : prefs_(prefs),
pending_extension_manager_(ALLOW_THIS_IN_INITIALIZER_LIST(*this)) {
profile_.CreateRequestContext(); profile_.CreateRequestContext();
} }
virtual ~MockService() {} virtual ~MockService() {}
virtual PendingExtensionManager* pending_extension_manager() OVERRIDE { virtual PendingExtensionManager* pending_extension_manager() OVERRIDE {
@@ -180,9 +182,9 @@ class MockService : public TestExtensionService {
return profile_.GetRequestContext(); return profile_.GetRequestContext();
} }
ExtensionPrefs* extension_prefs() { return prefs_.prefs(); } ExtensionPrefs* extension_prefs() { return prefs_->prefs(); }
PrefService* pref_service() { return prefs_.pref_service(); } PrefService* pref_service() { return prefs_->pref_service(); }
// Creates test extensions and inserts them into list. The name and // Creates test extensions and inserts them into list. The name and
// version are all based on their index. If |update_url| is non-null, it // version are all based on their index. If |update_url| is non-null, it
@@ -201,15 +203,15 @@ class MockService : public TestExtensionService {
if (update_url) if (update_url)
manifest.SetString(extension_manifest_keys::kUpdateURL, *update_url); manifest.SetString(extension_manifest_keys::kUpdateURL, *update_url);
scoped_refptr<Extension> e = scoped_refptr<Extension> e =
prefs_.AddExtensionWithManifest(manifest, location); prefs_->AddExtensionWithManifest(manifest, location);
ASSERT_TRUE(e != NULL); ASSERT_TRUE(e != NULL);
list->push_back(e); list->push_back(e);
} }
} }
protected: protected:
TestExtensionPrefs* const prefs_;
PendingExtensionManager pending_extension_manager_; PendingExtensionManager pending_extension_manager_;
TestExtensionPrefs prefs_;
TestingProfile profile_; TestingProfile profile_;
private: private:
@@ -260,7 +262,9 @@ void SetupPendingExtensionManagerForTest(
class ServiceForManifestTests : public MockService { class ServiceForManifestTests : public MockService {
public: public:
ServiceForManifestTests() {} explicit ServiceForManifestTests(TestExtensionPrefs* prefs)
: MockService(prefs) {
}
virtual ~ServiceForManifestTests() {} virtual ~ServiceForManifestTests() {}
@@ -305,8 +309,8 @@ class ServiceForManifestTests : public MockService {
class ServiceForDownloadTests : public MockService { class ServiceForDownloadTests : public MockService {
public: public:
ServiceForDownloadTests() explicit ServiceForDownloadTests(TestExtensionPrefs* prefs)
: MockService() { : MockService(prefs) {
} }
// Add a fake crx installer to be returned by a call to UpdateExtension() // Add a fake crx installer to be returned by a call to UpdateExtension()
@@ -369,8 +373,8 @@ class ServiceForDownloadTests : public MockService {
class ServiceForBlacklistTests : public MockService { class ServiceForBlacklistTests : public MockService {
public: public:
ServiceForBlacklistTests() explicit ServiceForBlacklistTests(TestExtensionPrefs* prefs)
: MockService(), : MockService(prefs),
processed_blacklist_(false) { processed_blacklist_(false) {
} }
virtual void UpdateExtensionBlacklist( virtual void UpdateExtensionBlacklist(
@@ -417,17 +421,27 @@ class ExtensionUpdaterTest : public testing::Test {
ExtensionUpdaterTest() ExtensionUpdaterTest()
: ui_thread_(BrowserThread::UI, &loop_), : ui_thread_(BrowserThread::UI, &loop_),
file_thread_(BrowserThread::FILE, &loop_), file_thread_(BrowserThread::FILE, &loop_),
io_thread_(BrowserThread::IO, &loop_) {} io_thread_(BrowserThread::IO, &loop_) {
}
virtual ~ExtensionUpdaterTest() {
}
virtual void SetUp() OVERRIDE {
prefs_.reset(new TestExtensionPrefs(loop_.message_loop_proxy()));
}
virtual void TearDown() OVERRIDE { virtual void TearDown() OVERRIDE {
// Some tests create URLRequestContextGetters, whose destruction must run // Some tests create URLRequestContextGetters, whose destruction must run
// on the IO thread. Make sure the IO loop spins before shutdown so that // on the IO thread. Make sure the IO loop spins before shutdown so that
// those objects are released. // those objects are released.
loop_.RunAllPending(); RunUntilIdle();
prefs_.reset();
} }
void RunAllPending() { void RunUntilIdle() {
loop_.RunAllPending(); prefs_->pref_service()->CommitPendingWrite();
loop_.RunUntilIdle();
} }
void SimulateTimerFired(ExtensionUpdater* updater) { void SimulateTimerFired(ExtensionUpdater* updater) {
@@ -466,7 +480,7 @@ class ExtensionUpdaterTest : public testing::Test {
void TestExtensionUpdateCheckRequests(bool pending) { void TestExtensionUpdateCheckRequests(bool pending) {
// Create an extension with an update_url. // Create an extension with an update_url.
ServiceForManifestTests service; ServiceForManifestTests service(prefs_.get());
std::string update_url("http://foo.com/bar"); std::string update_url("http://foo.com/bar");
ExtensionList extensions; ExtensionList extensions;
PendingExtensionManager* pending_extension_manager = PendingExtensionManager* pending_extension_manager =
@@ -526,7 +540,7 @@ class ExtensionUpdaterTest : public testing::Test {
void TestBlacklistUpdateCheckRequests() { void TestBlacklistUpdateCheckRequests() {
// Setup and start the updater. // Setup and start the updater.
ServiceForManifestTests service; ServiceForManifestTests service(prefs_.get());
net::TestURLFetcherFactory factory; net::TestURLFetcherFactory factory;
ExtensionUpdater updater( ExtensionUpdater updater(
@@ -608,7 +622,7 @@ class ExtensionUpdaterTest : public testing::Test {
void TestUpdateUrlDataFromGallery(const std::string& gallery_url) { void TestUpdateUrlDataFromGallery(const std::string& gallery_url) {
net::TestURLFetcherFactory factory; net::TestURLFetcherFactory factory;
MockService service; MockService service(prefs_.get());
MockExtensionDownloaderDelegate delegate; MockExtensionDownloaderDelegate delegate;
ExtensionDownloader downloader(&delegate, service.request_context()); ExtensionDownloader downloader(&delegate, service.request_context());
ExtensionList extensions; ExtensionList extensions;
@@ -689,7 +703,7 @@ class ExtensionUpdaterTest : public testing::Test {
void TestDetermineUpdatesPending() { void TestDetermineUpdatesPending() {
// Create a set of test extensions // Create a set of test extensions
ServiceForManifestTests service; ServiceForManifestTests service(prefs_.get());
PendingExtensionManager* pending_extension_manager = PendingExtensionManager* pending_extension_manager =
service.pending_extension_manager(); service.pending_extension_manager();
SetupPendingExtensionManagerForTest(3, GURL(), pending_extension_manager); SetupPendingExtensionManagerForTest(3, GURL(), pending_extension_manager);
@@ -731,7 +745,7 @@ class ExtensionUpdaterTest : public testing::Test {
net::TestURLFetcherFactory factory; net::TestURLFetcherFactory factory;
net::TestURLFetcher* fetcher = NULL; net::TestURLFetcher* fetcher = NULL;
NotificationsObserver observer; NotificationsObserver observer;
MockService service; MockService service(prefs_.get());
MockExtensionDownloaderDelegate delegate; MockExtensionDownloaderDelegate delegate;
ExtensionDownloader downloader(&delegate, service.request_context()); ExtensionDownloader downloader(&delegate, service.request_context());
@@ -753,7 +767,7 @@ class ExtensionUpdaterTest : public testing::Test {
downloader.StartUpdateCheck(fetch2); downloader.StartUpdateCheck(fetch2);
downloader.StartUpdateCheck(fetch3); downloader.StartUpdateCheck(fetch3);
downloader.StartUpdateCheck(fetch4); downloader.StartUpdateCheck(fetch4);
RunAllPending(); RunUntilIdle();
// The first fetch will fail. // The first fetch will fail.
fetcher = factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId); fetcher = factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
@@ -765,7 +779,7 @@ class ExtensionUpdaterTest : public testing::Test {
fetcher->set_status(net::URLRequestStatus()); fetcher->set_status(net::URLRequestStatus());
fetcher->set_response_code(400); fetcher->set_response_code(400);
fetcher->delegate()->OnURLFetchComplete(fetcher); fetcher->delegate()->OnURLFetchComplete(fetcher);
RunAllPending(); RunUntilIdle();
Mock::VerifyAndClearExpectations(&delegate); Mock::VerifyAndClearExpectations(&delegate);
// The second fetch gets invalid data. // The second fetch gets invalid data.
@@ -780,7 +794,7 @@ class ExtensionUpdaterTest : public testing::Test {
fetcher->set_response_code(200); fetcher->set_response_code(200);
fetcher->SetResponseString(kInvalidXml); fetcher->SetResponseString(kInvalidXml);
fetcher->delegate()->OnURLFetchComplete(fetcher); fetcher->delegate()->OnURLFetchComplete(fetcher);
RunAllPending(); RunUntilIdle();
Mock::VerifyAndClearExpectations(&delegate); Mock::VerifyAndClearExpectations(&delegate);
// The third fetcher doesn't have an update available. // The third fetcher doesn't have an update available.
@@ -807,7 +821,7 @@ class ExtensionUpdaterTest : public testing::Test {
fetcher->set_response_code(200); fetcher->set_response_code(200);
fetcher->SetResponseString(kNoUpdate); fetcher->SetResponseString(kNoUpdate);
fetcher->delegate()->OnURLFetchComplete(fetcher); fetcher->delegate()->OnURLFetchComplete(fetcher);
RunAllPending(); RunUntilIdle();
Mock::VerifyAndClearExpectations(&delegate); Mock::VerifyAndClearExpectations(&delegate);
// The last fetcher has an update. // The last fetcher has an update.
@@ -832,7 +846,7 @@ class ExtensionUpdaterTest : public testing::Test {
fetcher->set_response_code(200); fetcher->set_response_code(200);
fetcher->SetResponseString(kUpdateAvailable); fetcher->SetResponseString(kUpdateAvailable);
fetcher->delegate()->OnURLFetchComplete(fetcher); fetcher->delegate()->OnURLFetchComplete(fetcher);
RunAllPending(); RunUntilIdle();
Mock::VerifyAndClearExpectations(&delegate); Mock::VerifyAndClearExpectations(&delegate);
// Verify that the downloader decided to update this extension. // Verify that the downloader decided to update this extension.
@@ -843,7 +857,8 @@ class ExtensionUpdaterTest : public testing::Test {
void TestSingleExtensionDownloading(bool pending) { void TestSingleExtensionDownloading(bool pending) {
net::TestURLFetcherFactory factory; net::TestURLFetcherFactory factory;
net::TestURLFetcher* fetcher = NULL; net::TestURLFetcher* fetcher = NULL;
scoped_ptr<ServiceForDownloadTests> service(new ServiceForDownloadTests); scoped_ptr<ServiceForDownloadTests> service(
new ServiceForDownloadTests(prefs_.get()));
ExtensionUpdater updater(service.get(), service->extension_prefs(), ExtensionUpdater updater(service.get(), service->extension_prefs(),
service->pref_service(), service->pref_service(),
service->profile(), service->profile(),
@@ -885,7 +900,7 @@ class ExtensionUpdaterTest : public testing::Test {
fetcher->SetResponseFilePath(extension_file_path); fetcher->SetResponseFilePath(extension_file_path);
fetcher->delegate()->OnURLFetchComplete(fetcher); fetcher->delegate()->OnURLFetchComplete(fetcher);
RunAllPending(); RunUntilIdle();
// Expect that ExtensionUpdater asked the mock extensions service to install // Expect that ExtensionUpdater asked the mock extensions service to install
// a file with the test data for the right id. // a file with the test data for the right id.
@@ -899,7 +914,7 @@ class ExtensionUpdaterTest : public testing::Test {
void TestBlacklistDownloading() { void TestBlacklistDownloading() {
net::TestURLFetcherFactory factory; net::TestURLFetcherFactory factory;
net::TestURLFetcher* fetcher = NULL; net::TestURLFetcher* fetcher = NULL;
ServiceForBlacklistTests service; ServiceForBlacklistTests service(prefs_.get());
ExtensionUpdater updater( ExtensionUpdater updater(
&service, service.extension_prefs(), service.pref_service(), &service, service.extension_prefs(), service.pref_service(),
service.profile(), kUpdateFrequencySecs); service.profile(), kUpdateFrequencySecs);
@@ -930,7 +945,7 @@ class ExtensionUpdaterTest : public testing::Test {
fetcher->SetResponseString(extension_data); fetcher->SetResponseString(extension_data);
fetcher->delegate()->OnURLFetchComplete(fetcher); fetcher->delegate()->OnURLFetchComplete(fetcher);
RunAllPending(); RunUntilIdle();
// The updater should have called extension service to process the // The updater should have called extension service to process the
// blacklist. // blacklist.
@@ -947,7 +962,7 @@ class ExtensionUpdaterTest : public testing::Test {
void TestMultipleExtensionDownloading(bool updates_start_running) { void TestMultipleExtensionDownloading(bool updates_start_running) {
net::TestURLFetcherFactory factory; net::TestURLFetcherFactory factory;
net::TestURLFetcher* fetcher = NULL; net::TestURLFetcher* fetcher = NULL;
ServiceForDownloadTests service; ServiceForDownloadTests service(prefs_.get());
ExtensionUpdater updater( ExtensionUpdater updater(
&service, service.extension_prefs(), service.pref_service(), &service, service.extension_prefs(), service.pref_service(),
service.profile(), kUpdateFrequencySecs); service.profile(), kUpdateFrequencySecs);
@@ -1017,14 +1032,14 @@ class ExtensionUpdaterTest : public testing::Test {
fetcher->SetResponseFilePath(extension_file_path); fetcher->SetResponseFilePath(extension_file_path);
fetcher->delegate()->OnURLFetchComplete(fetcher); fetcher->delegate()->OnURLFetchComplete(fetcher);
RunAllPending(); RunUntilIdle();
// Expect that the service was asked to do an install with the right data. // Expect that the service was asked to do an install with the right data.
FilePath tmpfile_path = service.install_path(); FilePath tmpfile_path = service.install_path();
EXPECT_FALSE(tmpfile_path.empty()); EXPECT_FALSE(tmpfile_path.empty());
EXPECT_EQ(id1, service.extension_id()); EXPECT_EQ(id1, service.extension_id());
EXPECT_EQ(url1, service.download_url()); EXPECT_EQ(url1, service.download_url());
RunAllPending(); RunUntilIdle();
// Make sure the second fetch finished and asked the service to do an // Make sure the second fetch finished and asked the service to do an
// update. // update.
@@ -1038,7 +1053,7 @@ class ExtensionUpdaterTest : public testing::Test {
fetcher->set_response_code(200); fetcher->set_response_code(200);
fetcher->SetResponseFilePath(extension_file_path2); fetcher->SetResponseFilePath(extension_file_path2);
fetcher->delegate()->OnURLFetchComplete(fetcher); fetcher->delegate()->OnURLFetchComplete(fetcher);
RunAllPending(); RunUntilIdle();
if (updates_start_running) { if (updates_start_running) {
EXPECT_TRUE(updater.crx_install_is_running_); EXPECT_TRUE(updater.crx_install_is_running_);
@@ -1116,7 +1131,8 @@ class ExtensionUpdaterTest : public testing::Test {
// Set up 2 mock extensions, one with a google.com update url and one // Set up 2 mock extensions, one with a google.com update url and one
// without. // without.
ServiceForManifestTests service; prefs_.reset(new TestExtensionPrefs(loop_.message_loop_proxy()));
ServiceForManifestTests service(prefs_.get());
ExtensionList tmp; ExtensionList tmp;
GURL url1("http://clients2.google.com/service/update2/crx"); GURL url1("http://clients2.google.com/service/update2/crx");
GURL url2("http://www.somewebsite.com"); GURL url2("http://www.somewebsite.com");
@@ -1228,6 +1244,8 @@ class ExtensionUpdaterTest : public testing::Test {
// queries. // queries.
EXPECT_TRUE(url1_query.find(brand_string) == std::string::npos); EXPECT_TRUE(url1_query.find(brand_string) == std::string::npos);
#endif #endif
RunUntilIdle();
} }
// This makes sure that the extension updater properly stores the results // This makes sure that the extension updater properly stores the results
@@ -1235,7 +1253,7 @@ class ExtensionUpdaterTest : public testing::Test {
// the first time we fetched the extension, or 2) We sent a ping value of // the first time we fetched the extension, or 2) We sent a ping value of
// >= 1 day for the extension. // >= 1 day for the extension.
void TestHandleManifestResults() { void TestHandleManifestResults() {
ServiceForManifestTests service; ServiceForManifestTests service(prefs_.get());
GURL update_url("http://www.google.com/manifest"); GURL update_url("http://www.google.com/manifest");
ExtensionList tmp; ExtensionList tmp;
service.CreateTestExtensions(1, 1, &tmp, &update_url.spec(), service.CreateTestExtensions(1, 1, &tmp, &update_url.spec(),
@@ -1265,6 +1283,9 @@ class ExtensionUpdaterTest : public testing::Test {
EXPECT_LT(seconds_diff - results.daystart_elapsed_seconds, 5); EXPECT_LT(seconds_diff - results.daystart_elapsed_seconds, 5);
} }
protected:
scoped_ptr<TestExtensionPrefs> prefs_;
private: private:
MessageLoop loop_; MessageLoop loop_;
content::TestBrowserThread ui_thread_; content::TestBrowserThread ui_thread_;
@@ -1345,7 +1366,7 @@ TEST_F(ExtensionUpdaterTest, TestHandleManifestResults) {
TEST_F(ExtensionUpdaterTest, TestNonAutoUpdateableLocations) { TEST_F(ExtensionUpdaterTest, TestNonAutoUpdateableLocations) {
net::TestURLFetcherFactory factory; net::TestURLFetcherFactory factory;
ServiceForManifestTests service; ServiceForManifestTests service(prefs_.get());
ExtensionUpdater updater(&service, service.extension_prefs(), ExtensionUpdater updater(&service, service.extension_prefs(),
service.pref_service(), service.profile(), service.pref_service(), service.profile(),
kUpdateFrequencySecs); kUpdateFrequencySecs);
@@ -1376,7 +1397,7 @@ TEST_F(ExtensionUpdaterTest, TestNonAutoUpdateableLocations) {
TEST_F(ExtensionUpdaterTest, TestUpdatingDisabledExtensions) { TEST_F(ExtensionUpdaterTest, TestUpdatingDisabledExtensions) {
net::TestURLFetcherFactory factory; net::TestURLFetcherFactory factory;
ServiceForManifestTests service; ServiceForManifestTests service(prefs_.get());
ExtensionUpdater updater(&service, service.extension_prefs(), ExtensionUpdater updater(&service, service.extension_prefs(),
service.pref_service(), service.profile(), service.pref_service(), service.profile(),
kUpdateFrequencySecs); kUpdateFrequencySecs);
@@ -1414,7 +1435,7 @@ TEST_F(ExtensionUpdaterTest, TestUpdatingDisabledExtensions) {
TEST_F(ExtensionUpdaterTest, TestManifestFetchesBuilderAddExtension) { TEST_F(ExtensionUpdaterTest, TestManifestFetchesBuilderAddExtension) {
net::TestURLFetcherFactory factory; net::TestURLFetcherFactory factory;
MockService service; MockService service(prefs_.get());
MockExtensionDownloaderDelegate delegate; MockExtensionDownloaderDelegate delegate;
scoped_ptr<ExtensionDownloader> downloader( scoped_ptr<ExtensionDownloader> downloader(
new ExtensionDownloader(&delegate, service.request_context())); new ExtensionDownloader(&delegate, service.request_context()));
@@ -1466,7 +1487,7 @@ TEST_F(ExtensionUpdaterTest, TestManifestFetchesBuilderAddExtension) {
TEST_F(ExtensionUpdaterTest, TestStartUpdateCheckMemory) { TEST_F(ExtensionUpdaterTest, TestStartUpdateCheckMemory) {
net::TestURLFetcherFactory factory; net::TestURLFetcherFactory factory;
MockService service; MockService service(prefs_.get());
MockExtensionDownloaderDelegate delegate; MockExtensionDownloaderDelegate delegate;
ExtensionDownloader downloader(&delegate, service.request_context()); ExtensionDownloader downloader(&delegate, service.request_context());
@@ -1480,7 +1501,7 @@ TEST_F(ExtensionUpdaterTest, TestStartUpdateCheckMemory) {
} }
TEST_F(ExtensionUpdaterTest, TestCheckSoon) { TEST_F(ExtensionUpdaterTest, TestCheckSoon) {
ServiceForManifestTests service; ServiceForManifestTests service(prefs_.get());
net::TestURLFetcherFactory factory; net::TestURLFetcherFactory factory;
ExtensionUpdater updater( ExtensionUpdater updater(
&service, service.extension_prefs(), service.pref_service(), &service, service.extension_prefs(), service.pref_service(),
@@ -1492,7 +1513,7 @@ TEST_F(ExtensionUpdaterTest, TestCheckSoon) {
EXPECT_TRUE(updater.WillCheckSoon()); EXPECT_TRUE(updater.WillCheckSoon());
updater.CheckSoon(); updater.CheckSoon();
EXPECT_TRUE(updater.WillCheckSoon()); EXPECT_TRUE(updater.WillCheckSoon());
RunAllPending(); RunUntilIdle();
EXPECT_FALSE(updater.WillCheckSoon()); EXPECT_FALSE(updater.WillCheckSoon());
updater.CheckSoon(); updater.CheckSoon();
EXPECT_TRUE(updater.WillCheckSoon()); EXPECT_TRUE(updater.WillCheckSoon());

@@ -1723,27 +1723,11 @@ bool MetricsService::UmaMetricsProperlyShutdown() {
return clean_shutdown_status_ == CLEANLY_SHUTDOWN; return clean_shutdown_status_ == CLEANLY_SHUTDOWN;
} }
// For use in hack in LogCleanShutdown.
static void Signal(base::WaitableEvent* event) {
event->Signal();
}
void MetricsService::LogCleanShutdown() { void MetricsService::LogCleanShutdown() {
// Redundant hack to write pref ASAP. // Redundant hack to write pref ASAP.
PrefService* pref = g_browser_process->local_state(); PrefService* pref = g_browser_process->local_state();
pref->SetBoolean(prefs::kStabilityExitedCleanly, true); pref->SetBoolean(prefs::kStabilityExitedCleanly, true);
pref->CommitPendingWrite(); pref->CommitPendingWrite();
// Hack: TBD: Remove this wait.
// We are so concerned that the pref gets written, we are now willing to stall
// the UI thread until we get assurance that a pref-writing task has
// completed.
base::WaitableEvent done_writing(false, false);
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(Signal, &done_writing));
// http://crbug.com/124954
base::ThreadRestrictions::ScopedAllowWait allow_wait;
done_writing.TimedWait(base::TimeDelta::FromHours(1));
// Redundant setting to assure that we always reset this value at shutdown // Redundant setting to assure that we always reset this value at shutdown
// (and that we don't use some alternate path, and not call LogCleanShutdown). // (and that we don't use some alternate path, and not call LogCleanShutdown).
clean_shutdown_status_ = CLEANLY_SHUTDOWN; clean_shutdown_status_ = CLEANLY_SHUTDOWN;

@@ -5,6 +5,7 @@
#include "chrome/browser/policy/managed_mode_policy_provider.h" #include "chrome/browser/policy/managed_mode_policy_provider.h"
#include "base/prefs/json_pref_store.h" #include "base/prefs/json_pref_store.h"
#include "base/threading/sequenced_worker_pool.h"
#include "chrome/browser/policy/policy_bundle.h" #include "chrome/browser/policy/policy_bundle.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_constants.h"
@@ -18,12 +19,11 @@ namespace policy {
const char ManagedModePolicyProvider::kPolicies[] = "policies"; const char ManagedModePolicyProvider::kPolicies[] = "policies";
// static // static
ManagedModePolicyProvider* ManagedModePolicyProvider::Create(Profile* profile) { ManagedModePolicyProvider* ManagedModePolicyProvider::Create(
JsonPrefStore* pref_store = Profile* profile,
new JsonPrefStore(profile->GetPath().Append( base::SequencedTaskRunner* sequenced_task_runner) {
chrome::kManagedModePolicyFilename), FilePath path = profile->GetPath().Append(chrome::kManagedModePolicyFilename);
BrowserThread::GetMessageLoopProxyForThread( JsonPrefStore* pref_store = new JsonPrefStore(path, sequenced_task_runner);
BrowserThread::FILE));
return new ManagedModePolicyProvider(pref_store); return new ManagedModePolicyProvider(pref_store);
} }

@@ -11,6 +11,10 @@
class Profile; class Profile;
namespace base {
class SequencedTaskRunner;
}
namespace policy { namespace policy {
// This class sets policies that are defined by a local user via "Managed Mode". // This class sets policies that are defined by a local user via "Managed Mode".
@@ -25,8 +29,12 @@ class ManagedModePolicyProvider
static const char kPolicies[]; static const char kPolicies[];
// Creates a new ManagedModePolicyProvider that caches its policies in a JSON // Creates a new ManagedModePolicyProvider that caches its policies in a JSON
// file inside the profile folder. // file inside the profile folder. |sequenced_task_runner| ensures that all
static ManagedModePolicyProvider* Create(Profile* profile); // file I/O operations are executed in the order that does not collide
// with Profile's file operations.
static ManagedModePolicyProvider* Create(
Profile* profile,
base::SequencedTaskRunner* sequenced_task_runner);
// Use this constructor to inject a different PrefStore (e.g. for testing). // Use this constructor to inject a different PrefStore (e.g. for testing).
explicit ManagedModePolicyProvider(PersistentPrefStore* store); explicit ManagedModePolicyProvider(PersistentPrefStore* store);

@@ -123,6 +123,7 @@ PrefServiceBase* PrefServiceBase::FromBrowserContext(BrowserContext* context) {
// static // static
PrefService* PrefService::CreatePrefService( PrefService* PrefService::CreatePrefService(
const FilePath& pref_filename, const FilePath& pref_filename,
base::SequencedTaskRunner* pref_io_task_runner,
policy::PolicyService* policy_service, policy::PolicyService* policy_service,
PrefStore* extension_prefs, PrefStore* extension_prefs,
bool async) { bool async) {
@@ -156,8 +157,7 @@ PrefService* PrefService::CreatePrefService(
CommandLinePrefStore* command_line = CommandLinePrefStore* command_line =
new CommandLinePrefStore(CommandLine::ForCurrentProcess()); new CommandLinePrefStore(CommandLine::ForCurrentProcess());
JsonPrefStore* user = new JsonPrefStore( JsonPrefStore* user = new JsonPrefStore(
pref_filename, pref_filename, pref_io_task_runner);
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
DefaultPrefStore* default_pref_store = new DefaultPrefStore(); DefaultPrefStore* default_pref_store = new DefaultPrefStore();
PrefNotifierImpl* pref_notifier = new PrefNotifierImpl(); PrefNotifierImpl* pref_notifier = new PrefNotifierImpl();

@@ -33,6 +33,10 @@ class PrefServiceObserver;
class PrefStore; class PrefStore;
class PrefValueStore; class PrefValueStore;
namespace base {
class SequencedTaskRunner;
}
namespace syncer { namespace syncer {
class SyncableService; class SyncableService;
} }
@@ -110,10 +114,12 @@ class PrefService : public PrefServiceBase, public base::NonThreadSafe {
// the created PrefService or NULL if creation has failed. Note, it is // the created PrefService or NULL if creation has failed. Note, it is
// guaranteed that in asynchronous version initialization happens after this // guaranteed that in asynchronous version initialization happens after this
// function returned. // function returned.
static PrefService* CreatePrefService(const FilePath& pref_filename, static PrefService* CreatePrefService(
policy::PolicyService* policy_service, const FilePath& pref_filename,
PrefStore* extension_pref_store, base::SequencedTaskRunner* pref_io_task_runner,
bool async); policy::PolicyService* policy_service,
PrefStore* extension_pref_store,
bool async);
// Creates an incognito copy of the pref service that shares most pref stores // Creates an incognito copy of the pref service that shares most pref stores
// but uses a fresh non-persistent overlay for the user pref store and an // but uses a fresh non-persistent overlay for the user pref store and an

@@ -77,16 +77,17 @@ PrefServiceMockBuilder::WithCommandLine(CommandLine* command_line) {
PrefServiceMockBuilder& PrefServiceMockBuilder&
PrefServiceMockBuilder::WithUserFilePrefs(const FilePath& prefs_file) { PrefServiceMockBuilder::WithUserFilePrefs(const FilePath& prefs_file) {
return WithUserFilePrefs(prefs_file, return WithUserFilePrefs(
BrowserThread::GetMessageLoopProxyForThread( prefs_file,
BrowserThread::FILE)); JsonPrefStore::GetTaskRunnerForFile(prefs_file,
BrowserThread::GetBlockingPool()));
} }
PrefServiceMockBuilder& PrefServiceMockBuilder&
PrefServiceMockBuilder::WithUserFilePrefs( PrefServiceMockBuilder::WithUserFilePrefs(
const FilePath& prefs_file, const FilePath& prefs_file,
base::MessageLoopProxy* message_loop_proxy) { base::SequencedTaskRunner* task_runner) {
user_prefs_ = new JsonPrefStore(prefs_file, message_loop_proxy); user_prefs_ = new JsonPrefStore(prefs_file, task_runner);
return *this; return *this;
} }

@@ -15,7 +15,7 @@ class FilePath;
class PrefService; class PrefService;
namespace base { namespace base {
class MessageLoopProxy; class SequencedTaskRunner;
} }
namespace policy { namespace policy {
@@ -48,10 +48,11 @@ class PrefServiceMockBuilder {
PrefServiceMockBuilder& WithCommandLine(CommandLine* command_line); PrefServiceMockBuilder& WithCommandLine(CommandLine* command_line);
// Specifies to use an actual file-backed user pref store. // Specifies to use an actual file-backed user pref store.
// TODO(zelidrag): Remove the first overloaded method below.
PrefServiceMockBuilder& WithUserFilePrefs(const FilePath& prefs_file); PrefServiceMockBuilder& WithUserFilePrefs(const FilePath& prefs_file);
PrefServiceMockBuilder& WithUserFilePrefs( PrefServiceMockBuilder& WithUserFilePrefs(
const FilePath& prefs_file, const FilePath& prefs_file,
base::MessageLoopProxy* message_loop_proxy); base::SequencedTaskRunner* task_runner);
// Creates the PrefService, invalidating the entire builder configuration. // Creates the PrefService, invalidating the entire builder configuration.
PrefService* Create(); PrefService* Create();

@@ -315,7 +315,7 @@ TEST_F(PrefServiceUserFilePrefsTest, PreserveEmptyValue) {
pref_file)); pref_file));
PrefServiceMockBuilder builder; PrefServiceMockBuilder builder;
builder.WithUserFilePrefs(pref_file, base::MessageLoopProxy::current()); builder.WithUserFilePrefs(pref_file, message_loop_.message_loop_proxy());
scoped_ptr<PrefService> prefs(builder.Create()); scoped_ptr<PrefService> prefs(builder.Create());
// Register testing prefs. // Register testing prefs.
@@ -344,7 +344,7 @@ TEST_F(PrefServiceUserFilePrefsTest, PreserveEmptyValue) {
// Write to file. // Write to file.
prefs->CommitPendingWrite(); prefs->CommitPendingWrite();
MessageLoop::current()->RunAllPending(); message_loop_.RunUntilIdle();
// Compare to expected output. // Compare to expected output.
FilePath golden_output_file = FilePath golden_output_file =

@@ -190,6 +190,11 @@ FilePath OffTheRecordProfileImpl::GetPath() {
return profile_->GetPath(); return profile_->GetPath();
} }
scoped_refptr<base::SequencedTaskRunner>
OffTheRecordProfileImpl::GetIOTaskRunner() {
return profile_->GetIOTaskRunner();
}
bool OffTheRecordProfileImpl::IsOffTheRecord() const { bool OffTheRecordProfileImpl::IsOffTheRecord() const {
return true; return true;
} }

@@ -87,6 +87,7 @@ class OffTheRecordProfileImpl : public Profile,
// content::BrowserContext implementation: // content::BrowserContext implementation:
virtual FilePath GetPath() OVERRIDE; virtual FilePath GetPath() OVERRIDE;
virtual scoped_refptr<base::SequencedTaskRunner> GetIOTaskRunner() OVERRIDE;
virtual bool IsOffTheRecord() const OVERRIDE; virtual bool IsOffTheRecord() const OVERRIDE;
virtual content::DownloadManagerDelegate* virtual content::DownloadManagerDelegate*
GetDownloadManagerDelegate() OVERRIDE; GetDownloadManagerDelegate() OVERRIDE;

@@ -35,6 +35,7 @@ class TabContentsProvider;
} }
namespace base { namespace base {
class SequencedTaskRunner;
class Time; class Time;
} }
@@ -140,6 +141,10 @@ class Profile : public content::BrowserContext {
// time. // time.
static void RegisterUserPrefs(PrefService* prefs); static void RegisterUserPrefs(PrefService* prefs);
// Gets task runner for I/O operations associated with |profile|.
static scoped_refptr<base::SequencedTaskRunner> GetTaskRunnerForProfile(
Profile* profile);
// Create a new profile given a path. If |create_mode| is // Create a new profile given a path. If |create_mode| is
// CREATE_MODE_ASYNCHRONOUS then the profile is initialized asynchronously. // CREATE_MODE_ASYNCHRONOUS then the profile is initialized asynchronously.
static Profile* CreateProfile(const FilePath& path, static Profile* CreateProfile(const FilePath& path,
@@ -157,6 +162,10 @@ class Profile : public content::BrowserContext {
// Typesafe upcast. // Typesafe upcast.
virtual TestingProfile* AsTestingProfile(); virtual TestingProfile* AsTestingProfile();
// Returns sequenced task runner where browser context dependent I/O
// operations should be performed.
virtual scoped_refptr<base::SequencedTaskRunner> GetIOTaskRunner() = 0;
// Returns the name associated with this profile. This name is displayed in // Returns the name associated with this profile. This name is displayed in
// the browser frame. // the browser frame.
virtual std::string GetProfileName() = 0; virtual std::string GetProfileName() = 0;

@@ -13,10 +13,13 @@
#include "base/file_util.h" #include "base/file_util.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/prefs/json_pref_store.h"
#include "base/string_number_conversions.h" #include "base/string_number_conversions.h"
#include "base/string_tokenizer.h" #include "base/string_tokenizer.h"
#include "base/string_util.h" #include "base/string_util.h"
#include "base/stringprintf.h" #include "base/stringprintf.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/utf_string_conversions.h" #include "base/utf_string_conversions.h"
#include "base/version.h" #include "base/version.h"
#include "chrome/browser/autocomplete/autocomplete_classifier.h" #include "chrome/browser/autocomplete/autocomplete_classifier.h"
@@ -149,6 +152,38 @@ static const char kReadmeText[] =
const char* const kPrefExitTypeCrashed = "Crashed"; const char* const kPrefExitTypeCrashed = "Crashed";
const char* const kPrefExitTypeSessionEnded = "SessionEnded"; const char* const kPrefExitTypeSessionEnded = "SessionEnded";
// Helper method needed because PostTask cannot currently take a Callback
// function with non-void return type.
void CreateDirectoryAndSignal(const FilePath& path,
base::WaitableEvent* done_creating) {
DVLOG(1) << "Creating directory " << path.value();
file_util::CreateDirectory(path);
done_creating->Signal();
}
// Task that blocks the FILE thread until CreateDirectoryAndSignal() finishes on
// blocking I/O pool.
void BlockFileThreadOnDirectoryCreate(base::WaitableEvent* done_creating) {
done_creating->Wait();
}
// Initiates creation of profile directory on |sequenced_task_runner| and
// ensures that FILE thread is blocked until that operation finishes.
void CreateProfileDirectory(base::SequencedTaskRunner* sequenced_task_runner,
const FilePath& path) {
base::WaitableEvent* done_creating = new base::WaitableEvent(false, false);
sequenced_task_runner->PostTask(FROM_HERE,
base::Bind(&CreateDirectoryAndSignal,
path,
done_creating));
// Block the FILE thread until directory is created on I/O pool to make sure
// that we don't attempt any operation until that part completes.
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&BlockFileThreadOnDirectoryCreate,
base::Owned(done_creating)));
}
FilePath GetCachePath(const FilePath& base) { FilePath GetCachePath(const FilePath& base) {
return base.Append(chrome::kCacheDirname); return base.Append(chrome::kCacheDirname);
} }
@@ -199,12 +234,15 @@ std::string ExitTypeToSessionTypePrefValue(Profile::ExitType type) {
Profile* Profile::CreateProfile(const FilePath& path, Profile* Profile::CreateProfile(const FilePath& path,
Delegate* delegate, Delegate* delegate,
CreateMode create_mode) { CreateMode create_mode) {
// Get sequenced task runner for making sure that file operations of
// this profile (defined by |path|) are executed in expected order
// (what was previously assured by the FILE thread).
scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner =
JsonPrefStore::GetTaskRunnerForFile(path,
BrowserThread::GetBlockingPool());
if (create_mode == CREATE_MODE_ASYNCHRONOUS) { if (create_mode == CREATE_MODE_ASYNCHRONOUS) {
DCHECK(delegate); DCHECK(delegate);
// This is safe while all file operations are done on the FILE thread. CreateProfileDirectory(sequenced_task_runner, path);
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(base::IgnoreResult(&file_util::CreateDirectory), path));
} else if (create_mode == CREATE_MODE_SYNCHRONOUS) { } else if (create_mode == CREATE_MODE_SYNCHRONOUS) {
if (!file_util::PathExists(path)) { if (!file_util::PathExists(path)) {
// TODO(tc): http://b/1094718 Bad things happen if we can't write to the // TODO(tc): http://b/1094718 Bad things happen if we can't write to the
@@ -217,7 +255,7 @@ Profile* Profile::CreateProfile(const FilePath& path,
NOTREACHED(); NOTREACHED();
} }
return new ProfileImpl(path, delegate, create_mode); return new ProfileImpl(path, delegate, create_mode, sequenced_task_runner);
} }
// static // static
@@ -270,9 +308,11 @@ void ProfileImpl::RegisterUserPrefs(PrefService* prefs) {
PrefService::SYNCABLE_PREF); PrefService::SYNCABLE_PREF);
} }
ProfileImpl::ProfileImpl(const FilePath& path, ProfileImpl::ProfileImpl(
Delegate* delegate, const FilePath& path,
CreateMode create_mode) Delegate* delegate,
CreateMode create_mode,
base::SequencedTaskRunner* sequenced_task_runner)
: path_(path), : path_(path),
ALLOW_THIS_IN_INITIALIZER_LIST(io_data_(this)), ALLOW_THIS_IN_INITIALIZER_LIST(io_data_(this)),
host_content_settings_map_(NULL), host_content_settings_map_(NULL),
@@ -307,7 +347,7 @@ ProfileImpl::ProfileImpl(const FilePath& path,
if (cloud_policy_manager_) if (cloud_policy_manager_)
cloud_policy_manager_->Init(); cloud_policy_manager_->Init();
managed_mode_policy_provider_.reset( managed_mode_policy_provider_.reset(
policy::ManagedModePolicyProvider::Create(this)); policy::ManagedModePolicyProvider::Create(this, sequenced_task_runner));
managed_mode_policy_provider_->Init(); managed_mode_policy_provider_->Init();
policy_service_ = connector->CreatePolicyService(this); policy_service_ = connector->CreatePolicyService(this);
#else #else
@@ -317,6 +357,7 @@ ProfileImpl::ProfileImpl(const FilePath& path,
if (create_mode == CREATE_MODE_ASYNCHRONOUS) { if (create_mode == CREATE_MODE_ASYNCHRONOUS) {
prefs_.reset(PrefService::CreatePrefService( prefs_.reset(PrefService::CreatePrefService(
GetPrefFilePath(), GetPrefFilePath(),
sequenced_task_runner,
policy_service_.get(), policy_service_.get(),
new ExtensionPrefStore( new ExtensionPrefStore(
ExtensionPrefValueMapFactory::GetForProfile(this), false), ExtensionPrefValueMapFactory::GetForProfile(this), false),
@@ -331,6 +372,7 @@ ProfileImpl::ProfileImpl(const FilePath& path,
// Load prefs synchronously. // Load prefs synchronously.
prefs_.reset(PrefService::CreatePrefService( prefs_.reset(PrefService::CreatePrefService(
GetPrefFilePath(), GetPrefFilePath(),
sequenced_task_runner,
policy_service_.get(), policy_service_.get(),
new ExtensionPrefStore( new ExtensionPrefStore(
ExtensionPrefValueMapFactory::GetForProfile(this), false), ExtensionPrefValueMapFactory::GetForProfile(this), false),
@@ -354,10 +396,10 @@ void ProfileImpl::DoFinalInit(bool is_new_profile) {
// to PathService. // to PathService.
chrome::GetUserCacheDirectory(path_, &base_cache_path_); chrome::GetUserCacheDirectory(path_, &base_cache_path_);
// Always create the cache directory asynchronously. // Always create the cache directory asynchronously.
BrowserThread::PostTask( scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner =
BrowserThread::FILE, FROM_HERE, JsonPrefStore::GetTaskRunnerForFile(base_cache_path_,
base::Bind(base::IgnoreResult(&file_util::CreateDirectory), BrowserThread::GetBlockingPool());
base_cache_path_)); CreateProfileDirectory(sequenced_task_runner, base_cache_path_);
// Now that the profile is hooked up to receive pref change notifications to // Now that the profile is hooked up to receive pref change notifications to
// kGoogleServicesUsername, initialize components that depend on it to reflect // kGoogleServicesUsername, initialize components that depend on it to reflect
@@ -562,6 +604,11 @@ FilePath ProfileImpl::GetPath() {
return path_; return path_;
} }
scoped_refptr<base::SequencedTaskRunner> ProfileImpl::GetIOTaskRunner() {
return JsonPrefStore::GetTaskRunnerForFile(
GetPath(), BrowserThread::GetBlockingPool());
}
bool ProfileImpl::IsOffTheRecord() const { bool ProfileImpl::IsOffTheRecord() const {
return false; return false;
} }

@@ -34,6 +34,10 @@ class Preferences;
} }
#endif #endif
namespace base {
class SequencedTaskRunner;
}
namespace content { namespace content {
class SpeechRecognitionPreferences; class SpeechRecognitionPreferences;
} }
@@ -79,6 +83,7 @@ class ProfileImpl : public Profile,
virtual quota::SpecialStoragePolicy* GetSpecialStoragePolicy() OVERRIDE; virtual quota::SpecialStoragePolicy* GetSpecialStoragePolicy() OVERRIDE;
// Profile implementation: // Profile implementation:
virtual scoped_refptr<base::SequencedTaskRunner> GetIOTaskRunner() OVERRIDE;
virtual std::string GetProfileName() OVERRIDE; virtual std::string GetProfileName() OVERRIDE;
virtual bool IsOffTheRecord() const OVERRIDE; virtual bool IsOffTheRecord() const OVERRIDE;
virtual Profile* GetOffTheRecordProfile() OVERRIDE; virtual Profile* GetOffTheRecordProfile() OVERRIDE;
@@ -149,7 +154,8 @@ class ProfileImpl : public Profile,
ProfileImpl(const FilePath& path, ProfileImpl(const FilePath& path,
Delegate* delegate, Delegate* delegate,
CreateMode create_mode); CreateMode create_mode,
base::SequencedTaskRunner* sequenced_task_runner);
// Does final initialization. Should be called after prefs were loaded. // Does final initialization. Should be called after prefs were loaded.
void DoFinalInit(bool is_new_profile); void DoFinalInit(bool is_new_profile);

@@ -691,8 +691,9 @@ void CredentialCacheService::LookForCachedCredentialsInAlternateProfile() {
// Attempt to read cached credentials from the alternate profile. If no file // Attempt to read cached credentials from the alternate profile. If no file
// exists, ReadPrefsAsync() will cause PREF_READ_ERROR_NO_FILE to be returned // exists, ReadPrefsAsync() will cause PREF_READ_ERROR_NO_FILE to be returned
// after initialization is complete. // after initialization is complete.
FilePath path = GetCredentialPathInAlternateProfile();
alternate_store_ = new JsonPrefStore( alternate_store_ = new JsonPrefStore(
GetCredentialPathInAlternateProfile(), path,
content::BrowserThread::GetMessageLoopProxyForThread( content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::FILE)); content::BrowserThread::FILE));
alternate_store_observer_ = new AlternateStoreObserver(this, alternate_store_observer_ = new AlternateStoreObserver(this,

@@ -9,6 +9,7 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/utf_string_conversions.h" #include "base/utf_string_conversions.h"
#include "chrome/app/chrome_command_ids.h" #include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/bookmarks/bookmark_model.h"
@@ -76,6 +77,7 @@ class BookmarkContextMenuTest : public testing::Test {
ui::Clipboard::DestroyClipboardForCurrentThread(); ui::Clipboard::DestroyClipboardForCurrentThread();
BrowserThread::GetBlockingPool()->FlushForTesting();
// Flush the message loop to make application verifiers happy. // Flush the message loop to make application verifiers happy.
message_loop_.RunAllPending(); message_loop_.RunAllPending();
} }

@@ -44,7 +44,7 @@ const char kServiceStateContent[] =
class ConnectorSettingsTest : public testing::Test { class ConnectorSettingsTest : public testing::Test {
protected: protected:
virtual void SetUp() { virtual void SetUp() OVERRIDE {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
message_loop_proxy_ = base::MessageLoopProxy::current(); message_loop_proxy_ = base::MessageLoopProxy::current();
} }
@@ -58,7 +58,7 @@ class ConnectorSettingsTest : public testing::Test {
file_util::WriteFile(file_name, content.c_str(), content.size()); file_util::WriteFile(file_name, content.c_str(), content.size());
} }
ServiceProcessPrefs* prefs = ServiceProcessPrefs* prefs =
new ServiceProcessPrefs(file_name, message_loop_proxy_.get()); new ServiceProcessPrefs(file_name, message_loop_proxy_);
prefs->ReadPrefs(); prefs->ReadPrefs();
return prefs; return prefs;
} }

@@ -13,6 +13,7 @@
#include "base/i18n/rtl.h" #include "base/i18n/rtl.h"
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/prefs/json_pref_store.h"
#include "base/string16.h" #include "base/string16.h"
#include "base/utf_string_conversions.h" #include "base/utf_string_conversions.h"
#include "base/values.h" #include "base/values.h"
@@ -152,6 +153,7 @@ bool ServiceProcess::Initialize(MessageLoopForUI* message_loop,
Teardown(); Teardown();
return false; return false;
} }
blocking_pool_ = new base::SequencedWorkerPool(3, "ServiceBlocking");
request_context_getter_ = new ServiceURLRequestContextGetter(); request_context_getter_ = new ServiceURLRequestContextGetter();
@@ -159,7 +161,9 @@ bool ServiceProcess::Initialize(MessageLoopForUI* message_loop,
PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
FilePath pref_path = user_data_dir.Append(chrome::kServiceStateFileName); FilePath pref_path = user_data_dir.Append(chrome::kServiceStateFileName);
service_prefs_.reset( service_prefs_.reset(
new ServiceProcessPrefs(pref_path, file_thread_->message_loop_proxy())); new ServiceProcessPrefs(
pref_path,
JsonPrefStore::GetTaskRunnerForFile(pref_path, blocking_pool_)));
service_prefs_->ReadPrefs(); service_prefs_->ReadPrefs();
// Check if a locale override has been specified on the command-line. // Check if a locale override has been specified on the command-line.
@@ -218,6 +222,12 @@ bool ServiceProcess::Teardown() {
shutdown_event_.Signal(); shutdown_event_.Signal();
io_thread_.reset(); io_thread_.reset();
file_thread_.reset(); file_thread_.reset();
if (blocking_pool_.get()) {
blocking_pool_->Shutdown();
blocking_pool_ = NULL;
}
// The NetworkChangeNotifier must be destroyed after all other threads that // The NetworkChangeNotifier must be destroyed after all other threads that
// might use it have been shut down. // might use it have been shut down.
network_change_notifier_.reset(); network_change_notifier_.reset();

@@ -10,6 +10,7 @@
#include "base/gtest_prod_util.h" #include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/thread.h" #include "base/threading/thread.h"
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include "chrome/service/cloud_print/cloud_print_proxy.h" #include "chrome/service/cloud_print/cloud_print_proxy.h"
@@ -123,6 +124,7 @@ class ServiceProcess : public CloudPrintProxy::Client {
scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_; scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
scoped_ptr<base::Thread> io_thread_; scoped_ptr<base::Thread> io_thread_;
scoped_ptr<base::Thread> file_thread_; scoped_ptr<base::Thread> file_thread_;
scoped_refptr<base::SequencedWorkerPool> blocking_pool_;
scoped_ptr<CloudPrintProxy> cloud_print_proxy_; scoped_ptr<CloudPrintProxy> cloud_print_proxy_;
scoped_ptr<ServiceProcessPrefs> service_prefs_; scoped_ptr<ServiceProcessPrefs> service_prefs_;
scoped_ptr<ServiceIPCServer> ipc_server_; scoped_ptr<ServiceIPCServer> ipc_server_;

@@ -4,12 +4,13 @@
#include "chrome/service/service_process_prefs.h" #include "chrome/service/service_process_prefs.h"
#include "base/message_loop_proxy.h"
#include "base/values.h" #include "base/values.h"
ServiceProcessPrefs::ServiceProcessPrefs( ServiceProcessPrefs::ServiceProcessPrefs(
const FilePath& pref_filename, const FilePath& pref_filename,
base::MessageLoopProxy* file_message_loop_proxy) base::SequencedTaskRunner* task_runner)
: prefs_(new JsonPrefStore(pref_filename, file_message_loop_proxy)) { : prefs_(new JsonPrefStore(pref_filename, task_runner)) {
} }
ServiceProcessPrefs::~ServiceProcessPrefs() {} ServiceProcessPrefs::~ServiceProcessPrefs() {}

@@ -12,16 +12,16 @@
namespace base { namespace base {
class DictionaryValue; class DictionaryValue;
class ListValue; class ListValue;
class SequencedTaskRunner;
} }
// Manages persistent preferences for the service process. This is basically a // Manages persistent preferences for the service process. This is basically a
// thin wrapper around JsonPrefStore for more comfortable use. // thin wrapper around JsonPrefStore for more comfortable use.
class ServiceProcessPrefs { class ServiceProcessPrefs {
public: public:
// |file_message_loop_proxy| is the MessageLoopProxy for a thread on which // |sequenced_task_runner| must be a shutdown-blocking task runner.
// file I/O can be done.
ServiceProcessPrefs(const FilePath& pref_filename, ServiceProcessPrefs(const FilePath& pref_filename,
base::MessageLoopProxy* file_message_loop_proxy); base::SequencedTaskRunner* task_runner);
~ServiceProcessPrefs(); ~ServiceProcessPrefs();
// Read preferences from the backing file. // Read preferences from the backing file.

@@ -5,29 +5,30 @@
#include <string> #include <string>
#include "base/message_loop.h" #include "base/message_loop.h"
#include "base/message_loop_proxy.h"
#include "base/scoped_temp_dir.h" #include "base/scoped_temp_dir.h"
#include "base/sequenced_task_runner.h"
#include "chrome/service/service_process_prefs.h" #include "chrome/service/service_process_prefs.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
class ServiceProcessPrefsTest : public testing::Test { class ServiceProcessPrefsTest : public testing::Test {
protected: protected:
virtual void SetUp() { virtual void SetUp() OVERRIDE {
message_loop_proxy_ = base::MessageLoopProxy::current();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
prefs_.reset(new ServiceProcessPrefs( prefs_.reset(new ServiceProcessPrefs(
temp_dir_.path().AppendASCII("service_process_prefs.txt"), temp_dir_.path().AppendASCII("service_process_prefs.txt"),
message_loop_proxy_.get())); message_loop_.message_loop_proxy()));
}
virtual void TearDown() OVERRIDE {
prefs_.reset();
} }
// The path to temporary directory used to contain the test operations. // The path to temporary directory used to contain the test operations.
ScopedTempDir temp_dir_; ScopedTempDir temp_dir_;
// A message loop that we can use as the file thread message loop. // A message loop that we can use as the file thread message loop.
MessageLoop message_loop_; MessageLoop message_loop_;
scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
scoped_ptr<ServiceProcessPrefs> prefs_; scoped_ptr<ServiceProcessPrefs> prefs_;
}; };
@@ -36,7 +37,7 @@ TEST_F(ServiceProcessPrefsTest, RetrievePrefs) {
prefs_->SetBoolean("testb", true); prefs_->SetBoolean("testb", true);
prefs_->SetString("tests", "testvalue"); prefs_->SetString("tests", "testvalue");
prefs_->WritePrefs(); prefs_->WritePrefs();
MessageLoop::current()->RunAllPending(); message_loop_.RunUntilIdle();
prefs_->SetBoolean("testb", false); // overwrite prefs_->SetBoolean("testb", false); // overwrite
prefs_->SetString("tests", ""); // overwrite prefs_->SetString("tests", ""); // overwrite
prefs_->ReadPrefs(); prefs_->ReadPrefs();

@@ -478,6 +478,10 @@ FilePath TestingProfile::GetPath() {
return profile_path_; return profile_path_;
} }
scoped_refptr<base::SequencedTaskRunner> TestingProfile::GetIOTaskRunner() {
return MessageLoop::current()->message_loop_proxy();
}
TestingPrefService* TestingProfile::GetTestingPrefService() { TestingPrefService* TestingProfile::GetTestingPrefService() {
if (!prefs_.get()) if (!prefs_.get())
CreateTestingPrefService(); CreateTestingPrefService();

@@ -178,6 +178,7 @@ class TestingProfile : public Profile {
// content::BrowserContext // content::BrowserContext
virtual FilePath GetPath() OVERRIDE; virtual FilePath GetPath() OVERRIDE;
virtual scoped_refptr<base::SequencedTaskRunner> GetIOTaskRunner() OVERRIDE;
virtual bool IsOffTheRecord() const OVERRIDE; virtual bool IsOffTheRecord() const OVERRIDE;
virtual content::DownloadManagerDelegate* virtual content::DownloadManagerDelegate*
GetDownloadManagerDelegate() OVERRIDE; GetDownloadManagerDelegate() OVERRIDE;

@@ -739,7 +739,8 @@ class PageLoadTest : public UITest {
// returned PrefService object. // returned PrefService object.
PrefService* GetLocalState() { PrefService* GetLocalState() {
FilePath path = user_data_dir().Append(chrome::kLocalStateFilename); FilePath path = user_data_dir().Append(chrome::kLocalStateFilename);
return PrefServiceMockBuilder().WithUserFilePrefs(path).Create(); return PrefServiceMockBuilder().WithUserFilePrefs(
path, MessageLoop::current()->message_loop_proxy()).Create();
} }
void GetStabilityMetrics(NavigationMetrics* metrics) { void GetStabilityMetrics(NavigationMetrics* metrics) {

@@ -18,6 +18,7 @@
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/prefs/json_pref_store.h"
#include "base/scoped_temp_dir.h" #include "base/scoped_temp_dir.h"
#include "base/string_piece.h" #include "base/string_piece.h"
#include "base/string_util.h" #include "base/string_util.h"
@@ -353,8 +354,9 @@ void FilterDisabledTests() {
// Same as BrowserProcessImpl, but uses custom profile manager. // Same as BrowserProcessImpl, but uses custom profile manager.
class FakeBrowserProcessImpl : public BrowserProcessImpl { class FakeBrowserProcessImpl : public BrowserProcessImpl {
public: public:
explicit FakeBrowserProcessImpl(const CommandLine& command_line) FakeBrowserProcessImpl(base::SequencedTaskRunner* local_state_task_runner,
: BrowserProcessImpl(command_line) { const CommandLine& command_line)
: BrowserProcessImpl(local_state_task_runner, command_line) {
profiles_dir_.CreateUniqueTempDir(); profiles_dir_.CreateUniqueTempDir();
} }
@@ -495,7 +497,13 @@ void FakeExternalTab::Initialize() {
cmd->AppendSwitch(switches::kDisableWebResources); cmd->AppendSwitch(switches::kDisableWebResources);
cmd->AppendSwitch(switches::kSingleProcess); cmd->AppendSwitch(switches::kSingleProcess);
browser_process_.reset(new FakeBrowserProcessImpl(*cmd)); FilePath local_state_path;
CHECK(PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path));
scoped_refptr<base::SequencedTaskRunner> local_state_task_runner =
JsonPrefStore::GetTaskRunnerForFile(local_state_path,
BrowserThread::GetBlockingPool());
browser_process_.reset(new FakeBrowserProcessImpl(local_state_task_runner,
*cmd));
// BrowserProcessImpl's constructor should set g_browser_process. // BrowserProcessImpl's constructor should set g_browser_process.
DCHECK(g_browser_process); DCHECK(g_browser_process);
g_browser_process->SetApplicationLocale("en-US"); g_browser_process->SetApplicationLocale("en-US");