// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/test/base/testing_profile.h"

#include "build/build_config.h"

#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/path_service.h"
#include "base/prefs/testing_pref_store.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/autocomplete/autocomplete_classifier.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/bookmarks/chrome_bookmark_client.h"
#include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/content_settings/host_content_settings_map.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_special_storage_policy.h"
#include "chrome/browser/extensions/extension_system_factory.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/favicon/favicon_service.h"
#include "chrome/browser/favicon/favicon_service_factory.h"
#include "chrome/browser/history/chrome_history_client.h"
#include "chrome/browser/history/chrome_history_client_factory.h"
#include "chrome/browser/history/history_backend.h"
#include "chrome/browser/history/history_db_task.h"
#include "chrome/browser/history/history_service.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/history/top_sites.h"
#include "chrome/browser/history/web_history_service_factory.h"
#include "chrome/browser/net/pref_proxy_config_tracker.h"
#include "chrome/browser/net/proxy_service_factory.h"
#include "chrome/browser/notifications/desktop_notification_service.h"
#include "chrome/browser/notifications/desktop_notification_service_factory.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/policy/profile_policy_connector_factory.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/prefs/pref_service_syncable.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/storage_partition_descriptor.h"
#include "chrome/browser/search_engines/template_url_fetcher_factory.h"
#include "chrome/browser/webdata/web_data_service.h"
#include "chrome/browser/webdata/web_data_service_factory.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/history_index_restore_observer.h"
#include "chrome/test/base/testing_pref_service_syncable.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/common/bookmark_constants.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/policy/core/common/policy_service.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/cookie_store_factory.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/test/mock_resource_context.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/constants.h"
#include "net/cookies/cookie_monster.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"

#if defined(ENABLE_CONFIGURATION_POLICY)
#include "chrome/browser/policy/schema_registry_service.h"
#include "chrome/browser/policy/schema_registry_service_factory.h"
#include "components/policy/core/common/configuration_policy_provider.h"
#include "components/policy/core/common/policy_service_impl.h"
#include "components/policy/core/common/schema.h"
#else
#include "components/policy/core/common/policy_service_stub.h"
#endif  // defined(ENABLE_CONFIGURATION_POLICY)

#if defined(ENABLE_EXTENSIONS)
#include "chrome/browser/guest_view/guest_view_manager.h"
#endif

#if defined(OS_ANDROID)
#include "chrome/browser/signin/android_profile_oauth2_token_service.h"
#endif

#if defined(ENABLE_MANAGED_USERS)
#include "chrome/browser/supervised_user/supervised_user_settings_service.h"
#include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
#endif

using base::Time;
using content::BrowserThread;
using content::DownloadManagerDelegate;
using testing::NiceMock;
using testing::Return;

namespace {

// Task used to make sure history has finished processing a request. Intended
// for use with BlockUntilHistoryProcessesPendingRequests.

class QuittingHistoryDBTask : public history::HistoryDBTask {
 public:
  QuittingHistoryDBTask() {}

  virtual bool RunOnDBThread(history::HistoryBackend* backend,
                             history::HistoryDatabase* db) OVERRIDE {
    return true;
  }

  virtual void DoneRunOnMainThread() OVERRIDE {
    base::MessageLoop::current()->Quit();
  }

 private:
  virtual ~QuittingHistoryDBTask() {}

  DISALLOW_COPY_AND_ASSIGN(QuittingHistoryDBTask);
};

class TestExtensionURLRequestContext : public net::URLRequestContext {
 public:
  TestExtensionURLRequestContext() {
    net::CookieMonster* cookie_monster =
        content::CreateCookieStore(content::CookieStoreConfig())->
            GetCookieMonster();
    const char* schemes[] = {extensions::kExtensionScheme};
    cookie_monster->SetCookieableSchemes(schemes, 1);
    set_cookie_store(cookie_monster);
  }

  virtual ~TestExtensionURLRequestContext() {}
};

class TestExtensionURLRequestContextGetter
    : public net::URLRequestContextGetter {
 public:
  virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE {
    if (!context_.get())
      context_.reset(new TestExtensionURLRequestContext());
    return context_.get();
  }
  virtual scoped_refptr<base::SingleThreadTaskRunner>
      GetNetworkTaskRunner() const OVERRIDE {
    return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
  }

 protected:
  virtual ~TestExtensionURLRequestContextGetter() {}

 private:
  scoped_ptr<net::URLRequestContext> context_;
};

#if defined(ENABLE_NOTIFICATIONS)
KeyedService* CreateTestDesktopNotificationService(
    content::BrowserContext* profile) {
  return new DesktopNotificationService(static_cast<Profile*>(profile), NULL);
}
#endif

}  // namespace

// static
#if defined(OS_CHROMEOS)
// Must be kept in sync with
// ChromeBrowserMainPartsChromeos::PreEarlyInitialization.
const char TestingProfile::kTestUserProfileDir[] = "test-user";
#else
const char TestingProfile::kTestUserProfileDir[] = "Default";
#endif

TestingProfile::TestingProfile()
    : start_time_(Time::Now()),
      testing_prefs_(NULL),
      incognito_(false),
      force_incognito_(false),
      original_profile_(NULL),
      guest_session_(false),
      last_session_exited_cleanly_(true),
      browser_context_dependency_manager_(
          BrowserContextDependencyManager::GetInstance()),
      resource_context_(NULL),
      delegate_(NULL) {
  CreateTempProfileDir();
  profile_path_ = temp_dir_.path();

  Init();
  FinishInit();
}

TestingProfile::TestingProfile(const base::FilePath& path)
    : start_time_(Time::Now()),
      testing_prefs_(NULL),
      incognito_(false),
      force_incognito_(false),
      original_profile_(NULL),
      guest_session_(false),
      last_session_exited_cleanly_(true),
      profile_path_(path),
      browser_context_dependency_manager_(
          BrowserContextDependencyManager::GetInstance()),
      resource_context_(NULL),
      delegate_(NULL) {
  Init();
  FinishInit();
}

TestingProfile::TestingProfile(const base::FilePath& path,
                               Delegate* delegate)
    : start_time_(Time::Now()),
      testing_prefs_(NULL),
      incognito_(false),
      force_incognito_(false),
      original_profile_(NULL),
      guest_session_(false),
      last_session_exited_cleanly_(true),
      profile_path_(path),
      browser_context_dependency_manager_(
          BrowserContextDependencyManager::GetInstance()),
      resource_context_(NULL),
      delegate_(delegate) {
  Init();
  if (delegate_) {
    base::MessageLoop::current()->PostTask(
        FROM_HERE,
        base::Bind(&TestingProfile::FinishInit, base::Unretained(this)));
  } else {
    FinishInit();
  }
}

TestingProfile::TestingProfile(
    const base::FilePath& path,
    Delegate* delegate,
    scoped_refptr<ExtensionSpecialStoragePolicy> extension_policy,
    scoped_ptr<PrefServiceSyncable> prefs,
    bool incognito,
    bool guest_session,
    const std::string& supervised_user_id,
    scoped_ptr<policy::PolicyService> policy_service,
    const TestingFactories& factories)
    : start_time_(Time::Now()),
      prefs_(prefs.release()),
      testing_prefs_(NULL),
      incognito_(incognito),
      force_incognito_(false),
      original_profile_(NULL),
      guest_session_(guest_session),
      supervised_user_id_(supervised_user_id),
      last_session_exited_cleanly_(true),
      extension_special_storage_policy_(extension_policy),
      profile_path_(path),
      browser_context_dependency_manager_(
          BrowserContextDependencyManager::GetInstance()),
      resource_context_(NULL),
      delegate_(delegate),
      policy_service_(policy_service.release()) {
  // If no profile path was supplied, create one.
  if (profile_path_.empty()) {
    CreateTempProfileDir();
    profile_path_ = temp_dir_.path();
  }

  // Set any testing factories prior to initializing the services.
  for (TestingFactories::const_iterator it = factories.begin();
       it != factories.end(); ++it) {
    it->first->SetTestingFactory(this, it->second);
  }

  Init();
  // If caller supplied a delegate, delay the FinishInit invocation until other
  // tasks have run.
  // TODO(atwilson): See if this is still required once we convert the current
  // users of the constructor that takes a Delegate* param.
  if (delegate_) {
    base::MessageLoop::current()->PostTask(
        FROM_HERE,
        base::Bind(&TestingProfile::FinishInit, base::Unretained(this)));
  } else {
    FinishInit();
  }
}

void TestingProfile::CreateTempProfileDir() {
  if (!temp_dir_.CreateUniqueTempDir()) {
    LOG(ERROR) << "Failed to create unique temporary directory.";

    // Fallback logic in case we fail to create unique temporary directory.
    base::FilePath system_tmp_dir;
    bool success = PathService::Get(base::DIR_TEMP, &system_tmp_dir);

    // We're severly screwed if we can't get the system temporary
    // directory. Die now to avoid writing to the filesystem root
    // or other bad places.
    CHECK(success);

    base::FilePath fallback_dir(
        system_tmp_dir.AppendASCII("TestingProfilePath"));
    base::DeleteFile(fallback_dir, true);
    base::CreateDirectory(fallback_dir);
    if (!temp_dir_.Set(fallback_dir)) {
      // That shouldn't happen, but if it does, try to recover.
      LOG(ERROR) << "Failed to use a fallback temporary directory.";

      // We're screwed if this fails, see CHECK above.
      CHECK(temp_dir_.Set(system_tmp_dir));
    }
  }
}

void TestingProfile::Init() {
  // If threads have been initialized, we should be on the UI thread.
  DCHECK(!content::BrowserThread::IsThreadInitialized(
             content::BrowserThread::UI) ||
         content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

#if defined(OS_ANDROID)
  // Make sure token service knows its running in tests.
  AndroidProfileOAuth2TokenService::set_is_testing_profile();
#endif

  // Normally this would happen during browser startup, but for tests
  // we need to trigger creation of Profile-related services.
  ChromeBrowserMainExtraPartsProfiles::
      EnsureBrowserContextKeyedServiceFactoriesBuilt();

  if (prefs_.get())
    user_prefs::UserPrefs::Set(this, prefs_.get());
  else
    CreateTestingPrefService();

  if (!base::PathExists(profile_path_))
    base::CreateDirectory(profile_path_);

  // TODO(joaodasilva): remove this once this PKS isn't created in ProfileImpl
  // anymore, after converting the PrefService to a PKS. Until then it must
  // be associated with a TestingProfile too.
  if (!IsOffTheRecord())
    CreateProfilePolicyConnector();

  extensions::ExtensionSystemFactory::GetInstance()->SetTestingFactory(
      this, extensions::TestExtensionSystem::Build);

  // If no original profile was specified for this profile: register preferences
  // even if this is an incognito profile - this allows tests to create a
  // standalone incognito profile while still having prefs registered.
  if (!IsOffTheRecord() || !original_profile_) {
    user_prefs::PrefRegistrySyncable* pref_registry =
        static_cast<user_prefs::PrefRegistrySyncable*>(
            prefs_->DeprecatedGetPrefRegistry());
    browser_context_dependency_manager_->
        RegisterProfilePrefsForServices(this, pref_registry);
  }

  browser_context_dependency_manager_->CreateBrowserContextServicesForTest(
      this);

#if defined(ENABLE_NOTIFICATIONS)
  // Install profile keyed service factory hooks for dummy/test services
  DesktopNotificationServiceFactory::GetInstance()->SetTestingFactory(
      this, CreateTestDesktopNotificationService);
#endif

#if defined(ENABLE_MANAGED_USERS)
  SupervisedUserSettingsService* settings_service =
      SupervisedUserSettingsServiceFactory::GetForProfile(this);
  TestingPrefStore* store = new TestingPrefStore();
  settings_service->Init(store);
  store->SetInitializationCompleted();
#endif

  profile_name_ = "testing_profile";
}

void TestingProfile::FinishInit() {
  DCHECK(content::NotificationService::current());
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_PROFILE_CREATED,
      content::Source<Profile>(static_cast<Profile*>(this)),
      content::NotificationService::NoDetails());

  ProfileManager* profile_manager = g_browser_process->profile_manager();
  if (profile_manager)
    profile_manager->InitProfileUserPrefs(this);

  if (delegate_)
    delegate_->OnProfileCreated(this, true, false);
}

TestingProfile::~TestingProfile() {
  // Revert to non-incognito mode before shutdown.
  force_incognito_ = false;

  // Any objects holding live URLFetchers should be deleted before teardown.
  TemplateURLFetcherFactory::ShutdownForProfile(this);

  MaybeSendDestroyedNotification();

  browser_context_dependency_manager_->DestroyBrowserContextServices(this);

  if (host_content_settings_map_.get())
    host_content_settings_map_->ShutdownOnUIThread();

  DestroyTopSites();

  if (pref_proxy_config_tracker_.get())
    pref_proxy_config_tracker_->DetachFromPrefService();
  // Failing a post == leaks == heapcheck failure. Make that an immediate test
  // failure.
  if (resource_context_) {
    CHECK(BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE,
                                    resource_context_));
    resource_context_ = NULL;
    content::RunAllPendingInMessageLoop(BrowserThread::IO);
  }
}

static KeyedService* BuildFaviconService(content::BrowserContext* profile) {
  return new FaviconService(static_cast<Profile*>(profile));
}

void TestingProfile::CreateFaviconService() {
  // It is up to the caller to create the history service if one is needed.
  FaviconServiceFactory::GetInstance()->SetTestingFactory(
      this, BuildFaviconService);
}

static KeyedService* BuildHistoryService(content::BrowserContext* context) {
  Profile* profile = static_cast<Profile*>(context);
  return new HistoryService(ChromeHistoryClientFactory::GetForProfile(profile),
                            profile);
}

bool TestingProfile::CreateHistoryService(bool delete_file, bool no_db) {
  DestroyHistoryService();
  if (delete_file) {
    base::FilePath path = GetPath();
    path = path.Append(chrome::kHistoryFilename);
    if (!base::DeleteFile(path, false) || base::PathExists(path))
      return false;
  }
  // This will create and init the history service.
  HistoryService* history_service = static_cast<HistoryService*>(
      HistoryServiceFactory::GetInstance()->SetTestingFactoryAndUse(
          this, BuildHistoryService));
  if (!history_service->Init(this->GetPath(), no_db)) {
    HistoryServiceFactory::GetInstance()->SetTestingFactoryAndUse(this, NULL);
  }
  // Disable WebHistoryService by default, since it makes network requests.
  WebHistoryServiceFactory::GetInstance()->SetTestingFactory(this, NULL);
  return true;
}

void TestingProfile::DestroyHistoryService() {
  HistoryService* history_service =
      HistoryServiceFactory::GetForProfileWithoutCreating(this);
  if (!history_service)
    return;

  history_service->ClearCachedDataForContextID(0);
  history_service->SetOnBackendDestroyTask(base::MessageLoop::QuitClosure());
  history_service->Cleanup();
  HistoryServiceFactory::ShutdownForProfile(this);

  // Wait for the backend class to terminate before deleting the files and
  // moving to the next test. Note: if this never terminates, somebody is
  // probably leaking a reference to the history backend, so it never calls
  // our destroy task.
  base::MessageLoop::current()->Run();

  // Make sure we don't have any event pending that could disrupt the next
  // test.
  base::MessageLoop::current()->PostTask(FROM_HERE,
                                         base::MessageLoop::QuitClosure());
  base::MessageLoop::current()->Run();
}

void TestingProfile::CreateTopSites() {
  DestroyTopSites();
  top_sites_ = history::TopSites::Create(
      this, GetPath().Append(chrome::kTopSitesFilename));
}

void TestingProfile::DestroyTopSites() {
  if (top_sites_.get()) {
    top_sites_->Shutdown();
    top_sites_ = NULL;
    // TopSitesImpl::Shutdown schedules some tasks (from TopSitesBackend) that
    // need to be run to properly shutdown. Run all pending tasks now. This is
    // normally handled by browser_process shutdown.
    if (base::MessageLoop::current())
      base::MessageLoop::current()->RunUntilIdle();
  }
}

static KeyedService* BuildBookmarkModel(content::BrowserContext* context) {
  Profile* profile = static_cast<Profile*>(context);
  ChromeBookmarkClient* bookmark_client =
      ChromeBookmarkClientFactory::GetForProfile(profile);
  BookmarkModel* bookmark_model = new BookmarkModel(bookmark_client, false);
  bookmark_client->Init(bookmark_model);
  bookmark_model->Load(profile->GetPrefs(),
                       profile->GetPrefs()->GetString(prefs::kAcceptLanguages),
                       profile->GetPath(),
                       profile->GetIOTaskRunner(),
                       content::BrowserThread::GetMessageLoopProxyForThread(
                           content::BrowserThread::UI));
  return bookmark_model;
}

static KeyedService* BuildChromeBookmarkClient(
    content::BrowserContext* context) {
  return new ChromeBookmarkClient(static_cast<Profile*>(context));
}

static KeyedService* BuildChromeHistoryClient(
    content::BrowserContext* context) {
  Profile* profile = static_cast<Profile*>(context);
  return new ChromeHistoryClient(BookmarkModelFactory::GetForProfile(profile));
}

void TestingProfile::CreateBookmarkModel(bool delete_file) {
  if (delete_file) {
    base::FilePath path = GetPath().Append(bookmarks::kBookmarksFileName);
    base::DeleteFile(path, false);
  }
  ChromeHistoryClientFactory::GetInstance()->SetTestingFactory(
      this, BuildChromeHistoryClient);
  ChromeBookmarkClientFactory::GetInstance()->SetTestingFactory(
      this, BuildChromeBookmarkClient);
  // This creates the BookmarkModel.
  ignore_result(BookmarkModelFactory::GetInstance()->SetTestingFactoryAndUse(
      this, BuildBookmarkModel));
}

static KeyedService* BuildWebDataService(content::BrowserContext* profile) {
  return new WebDataServiceWrapper(static_cast<Profile*>(profile));
}

void TestingProfile::CreateWebDataService() {
  WebDataServiceFactory::GetInstance()->SetTestingFactory(
      this, BuildWebDataService);
}

void TestingProfile::BlockUntilHistoryIndexIsRefreshed() {
  // Only get the history service if it actually exists since the caller of the
  // test should explicitly call CreateHistoryService to build it.
  HistoryService* history_service =
      HistoryServiceFactory::GetForProfileWithoutCreating(this);
  DCHECK(history_service);
  history::InMemoryURLIndex* index = history_service->InMemoryIndex();
  if (!index || index->restored())
    return;
  base::RunLoop run_loop;
  HistoryIndexRestoreObserver observer(
      content::GetQuitTaskForRunLoop(&run_loop));
  index->set_restore_cache_observer(&observer);
  run_loop.Run();
  index->set_restore_cache_observer(NULL);
  DCHECK(index->restored());
}

// TODO(phajdan.jr): Doesn't this hang if Top Sites are already loaded?
void TestingProfile::BlockUntilTopSitesLoaded() {
  content::WindowedNotificationObserver top_sites_loaded_observer(
      chrome::NOTIFICATION_TOP_SITES_LOADED,
      content::NotificationService::AllSources());
  top_sites_loaded_observer.Wait();
}

void TestingProfile::SetGuestSession(bool guest) {
  guest_session_ = guest;
}

base::FilePath TestingProfile::GetPath() const {
  return profile_path_;
}

scoped_refptr<base::SequencedTaskRunner> TestingProfile::GetIOTaskRunner() {
  return base::MessageLoop::current()->message_loop_proxy();
}

TestingPrefServiceSyncable* TestingProfile::GetTestingPrefService() {
  if (!prefs_.get())
    CreateTestingPrefService();
  DCHECK(testing_prefs_);
  return testing_prefs_;
}

TestingProfile* TestingProfile::AsTestingProfile() {
  return this;
}

std::string TestingProfile::GetProfileName() {
  return profile_name_;
}

Profile::ProfileType TestingProfile::GetProfileType() const {
  if (guest_session_)
    return GUEST_PROFILE;
  if (force_incognito_ || incognito_)
    return INCOGNITO_PROFILE;
  return REGULAR_PROFILE;
}

bool TestingProfile::IsOffTheRecord() const {
  return force_incognito_ || incognito_;
}

void TestingProfile::SetOffTheRecordProfile(scoped_ptr<Profile> profile) {
  DCHECK(!IsOffTheRecord());
  incognito_profile_ = profile.Pass();
}

void TestingProfile::SetOriginalProfile(Profile* profile) {
  DCHECK(IsOffTheRecord());
  original_profile_ = profile;
}

Profile* TestingProfile::GetOffTheRecordProfile() {
  if (IsOffTheRecord())
    return this;
  if (!incognito_profile_) {
    TestingProfile::Builder builder;
    builder.SetIncognito();
    scoped_ptr<TestingProfile> incognito_test_profile(builder.Build());
    incognito_test_profile->SetOriginalProfile(this);
    SetOffTheRecordProfile(incognito_test_profile.PassAs<Profile>());
  }
  return incognito_profile_.get();
}

bool TestingProfile::HasOffTheRecordProfile() {
  return incognito_profile_.get() != NULL;
}

Profile* TestingProfile::GetOriginalProfile() {
  if (original_profile_)
    return original_profile_;
  return this;
}

bool TestingProfile::IsSupervised() {
  return !supervised_user_id_.empty();
}

ExtensionService* TestingProfile::GetExtensionService() {
  return extensions::ExtensionSystem::Get(this)->extension_service();
}

void TestingProfile::SetExtensionSpecialStoragePolicy(
    ExtensionSpecialStoragePolicy* extension_special_storage_policy) {
  extension_special_storage_policy_ = extension_special_storage_policy;
}

ExtensionSpecialStoragePolicy*
TestingProfile::GetExtensionSpecialStoragePolicy() {
  if (!extension_special_storage_policy_.get())
    extension_special_storage_policy_ = new ExtensionSpecialStoragePolicy(NULL);
  return extension_special_storage_policy_.get();
}

net::CookieMonster* TestingProfile::GetCookieMonster() {
  if (!GetRequestContext())
    return NULL;
  return GetRequestContext()->GetURLRequestContext()->cookie_store()->
      GetCookieMonster();
}

void TestingProfile::CreateTestingPrefService() {
  DCHECK(!prefs_.get());
  testing_prefs_ = new TestingPrefServiceSyncable();
  prefs_.reset(testing_prefs_);
  user_prefs::UserPrefs::Set(this, prefs_.get());
  chrome::RegisterUserProfilePrefs(testing_prefs_->registry());
}

void TestingProfile::CreateProfilePolicyConnector() {
#if defined(ENABLE_CONFIGURATION_POLICY)
  schema_registry_service_ =
      policy::SchemaRegistryServiceFactory::CreateForContext(
          this, policy::Schema(), NULL);
  CHECK_EQ(schema_registry_service_.get(),
           policy::SchemaRegistryServiceFactory::GetForContext(this));
#endif  // defined(ENABLE_CONFIGURATION_POLICY)

if (!policy_service_) {
#if defined(ENABLE_CONFIGURATION_POLICY)
    std::vector<policy::ConfigurationPolicyProvider*> providers;
    policy_service_.reset(new policy::PolicyServiceImpl(providers));
#else
    policy_service_.reset(new policy::PolicyServiceStub());
#endif
  }
  profile_policy_connector_.reset(new policy::ProfilePolicyConnector());
  profile_policy_connector_->InitForTesting(policy_service_.Pass());
  policy::ProfilePolicyConnectorFactory::GetInstance()->SetServiceForTesting(
      this, profile_policy_connector_.get());
  CHECK_EQ(profile_policy_connector_.get(),
           policy::ProfilePolicyConnectorFactory::GetForProfile(this));
}

PrefService* TestingProfile::GetPrefs() {
  if (!prefs_.get()) {
    CreateTestingPrefService();
  }
  return prefs_.get();
}

history::TopSites* TestingProfile::GetTopSites() {
  return top_sites_.get();
}

history::TopSites* TestingProfile::GetTopSitesWithoutCreating() {
  return top_sites_.get();
}

DownloadManagerDelegate* TestingProfile::GetDownloadManagerDelegate() {
  return NULL;
}

net::URLRequestContextGetter* TestingProfile::GetRequestContext() {
  return GetDefaultStoragePartition(this)->GetURLRequestContext();
}

net::URLRequestContextGetter* TestingProfile::CreateRequestContext(
    content::ProtocolHandlerMap* protocol_handlers,
    content::URLRequestInterceptorScopedVector request_interceptors) {
  return new net::TestURLRequestContextGetter(
            BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO));
}

net::URLRequestContextGetter* TestingProfile::GetRequestContextForRenderProcess(
    int renderer_child_id) {
  content::RenderProcessHost* rph = content::RenderProcessHost::FromID(
      renderer_child_id);
  return rph->GetStoragePartition()->GetURLRequestContext();
}

net::URLRequestContextGetter* TestingProfile::GetMediaRequestContext() {
  return NULL;
}

net::URLRequestContextGetter*
TestingProfile::GetMediaRequestContextForRenderProcess(
    int renderer_child_id) {
  return NULL;
}

net::URLRequestContextGetter*
TestingProfile::GetMediaRequestContextForStoragePartition(
    const base::FilePath& partition_path,
    bool in_memory) {
  return NULL;
}

net::URLRequestContextGetter* TestingProfile::GetRequestContextForExtensions() {
  if (!extensions_request_context_.get())
    extensions_request_context_ = new TestExtensionURLRequestContextGetter();
  return extensions_request_context_.get();
}

net::SSLConfigService* TestingProfile::GetSSLConfigService() {
  if (!GetRequestContext())
    return NULL;
  return GetRequestContext()->GetURLRequestContext()->ssl_config_service();
}

net::URLRequestContextGetter*
TestingProfile::CreateRequestContextForStoragePartition(
    const base::FilePath& partition_path,
    bool in_memory,
    content::ProtocolHandlerMap* protocol_handlers,
    content::URLRequestInterceptorScopedVector request_interceptors) {
  // We don't test storage partitions here yet, so returning the same dummy
  // context is sufficient for now.
  return GetRequestContext();
}

content::ResourceContext* TestingProfile::GetResourceContext() {
  if (!resource_context_)
    resource_context_ = new content::MockResourceContext();
  return resource_context_;
}

HostContentSettingsMap* TestingProfile::GetHostContentSettingsMap() {
  if (!host_content_settings_map_.get()) {
    host_content_settings_map_ = new HostContentSettingsMap(GetPrefs(), false);
#if defined(ENABLE_EXTENSIONS)
    ExtensionService* extension_service = GetExtensionService();
    if (extension_service)
      host_content_settings_map_->RegisterExtensionService(extension_service);
#endif
  }
  return host_content_settings_map_.get();
}

content::BrowserPluginGuestManager* TestingProfile::GetGuestManager() {
#if defined(ENABLE_EXTENSIONS)
  return GuestViewManager::FromBrowserContext(this);
#else
  return NULL;
#endif
}

content::PushMessagingService* TestingProfile::GetPushMessagingService() {
  return NULL;
}

bool TestingProfile::IsSameProfile(Profile *p) {
  return this == p;
}

base::Time TestingProfile::GetStartTime() const {
  return start_time_;
}

base::FilePath TestingProfile::last_selected_directory() {
  return last_selected_directory_;
}

void TestingProfile::set_last_selected_directory(const base::FilePath& path) {
  last_selected_directory_ = path;
}

PrefProxyConfigTracker* TestingProfile::GetProxyConfigTracker() {
  if (!pref_proxy_config_tracker_.get()) {
    // TestingProfile is used in unit tests, where local state is not available.
    pref_proxy_config_tracker_.reset(
        ProxyServiceFactory::CreatePrefProxyConfigTrackerOfProfile(GetPrefs(),
                                                                   NULL));
  }
  return pref_proxy_config_tracker_.get();
}

void TestingProfile::BlockUntilHistoryProcessesPendingRequests() {
  HistoryService* history_service =
      HistoryServiceFactory::GetForProfile(this, Profile::EXPLICIT_ACCESS);
  DCHECK(history_service);
  DCHECK(base::MessageLoop::current());

  CancelableRequestConsumer consumer;
  history_service->ScheduleDBTask(new QuittingHistoryDBTask(), &consumer);
  base::MessageLoop::current()->Run();
}

chrome_browser_net::Predictor* TestingProfile::GetNetworkPredictor() {
  return NULL;
}

DevToolsNetworkController* TestingProfile::GetDevToolsNetworkController() {
  return NULL;
}

void TestingProfile::ClearNetworkingHistorySince(
    base::Time time,
    const base::Closure& completion) {
  if (!completion.is_null()) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, completion);
  }
}

void TestingProfile::ClearDomainReliabilityMonitor(
    domain_reliability::DomainReliabilityClearMode mode,
    const base::Closure& completion) {
  if (!completion.is_null()) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, completion);
  }
}

GURL TestingProfile::GetHomePage() {
  return GURL(chrome::kChromeUINewTabURL);
}

PrefService* TestingProfile::GetOffTheRecordPrefs() {
  return NULL;
}

quota::SpecialStoragePolicy* TestingProfile::GetSpecialStoragePolicy() {
  return GetExtensionSpecialStoragePolicy();
}

bool TestingProfile::WasCreatedByVersionOrLater(const std::string& version) {
  return true;
}

bool TestingProfile::IsGuestSession() const {
  return guest_session_;
}

Profile::ExitType TestingProfile::GetLastSessionExitType() {
  return last_session_exited_cleanly_ ? EXIT_NORMAL : EXIT_CRASHED;
}

TestingProfile::Builder::Builder()
    : build_called_(false),
      delegate_(NULL),
      incognito_(false),
      guest_session_(false) {
}

TestingProfile::Builder::~Builder() {
}

void TestingProfile::Builder::SetPath(const base::FilePath& path) {
  path_ = path;
}

void TestingProfile::Builder::SetDelegate(Delegate* delegate) {
  delegate_ = delegate;
}

void TestingProfile::Builder::SetExtensionSpecialStoragePolicy(
    scoped_refptr<ExtensionSpecialStoragePolicy> policy) {
  extension_policy_ = policy;
}

void TestingProfile::Builder::SetPrefService(
    scoped_ptr<PrefServiceSyncable> prefs) {
  pref_service_ = prefs.Pass();
}

void TestingProfile::Builder::SetIncognito() {
  incognito_ = true;
}

void TestingProfile::Builder::SetGuestSession() {
  guest_session_ = true;
}

void TestingProfile::Builder::SetSupervisedUserId(
    const std::string& supervised_user_id) {
  supervised_user_id_ = supervised_user_id;
}

void TestingProfile::Builder::SetPolicyService(
    scoped_ptr<policy::PolicyService> policy_service) {
  policy_service_ = policy_service.Pass();
}

void TestingProfile::Builder::AddTestingFactory(
    BrowserContextKeyedServiceFactory* service_factory,
    BrowserContextKeyedServiceFactory::TestingFactoryFunction callback) {
  testing_factories_.push_back(std::make_pair(service_factory, callback));
}

scoped_ptr<TestingProfile> TestingProfile::Builder::Build() {
  DCHECK(!build_called_);
  build_called_ = true;

  return scoped_ptr<TestingProfile>(new TestingProfile(
      path_,
      delegate_,
      extension_policy_,
      pref_service_.Pass(),
      incognito_,
      guest_session_,
      supervised_user_id_,
      policy_service_.Pass(),
      testing_factories_));
}