| Index: chrome/browser/extensions/extension_service_unittest.cc
|
| diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
|
| index 368d048eaf0ef91fbfab97a3b0cec13bf134af36..100eb00fcf7c88667b21affb5acbafe148e9a47a 100644
|
| --- a/chrome/browser/extensions/extension_service_unittest.cc
|
| +++ b/chrome/browser/extensions/extension_service_unittest.cc
|
| @@ -21,6 +21,7 @@
|
| #include "base/memory/weak_ptr.h"
|
| #include "base/message_loop.h"
|
| #include "base/path_service.h"
|
| +#include "base/run_loop.h"
|
| #include "base/stl_util.h"
|
| #include "base/string16.h"
|
| #include "base/string_util.h"
|
| @@ -58,6 +59,7 @@
|
| #include "chrome/browser/prefs/pref_service_mock_builder.h"
|
| #include "chrome/browser/prefs/pref_service_syncable.h"
|
| #include "chrome/browser/prefs/scoped_user_pref_update.h"
|
| +#include "chrome/browser/profiles/profile_manager.h"
|
| #include "chrome/common/chrome_constants.h"
|
| #include "chrome/common/chrome_notification_types.h"
|
| #include "chrome/common/chrome_paths.h"
|
| @@ -68,12 +70,14 @@
|
| #include "chrome/common/extensions/extension.h"
|
| #include "chrome/common/extensions/extension_l10n_util.h"
|
| #include "chrome/common/extensions/extension_manifest_constants.h"
|
| +#include "chrome/common/extensions/extension_messages.h"
|
| #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
|
| #include "chrome/common/extensions/manifest_handlers/requirements_handler.h"
|
| #include "chrome/common/extensions/manifest_url_handler.h"
|
| #include "chrome/common/extensions/permissions/permission_set.h"
|
| #include "chrome/common/pref_names.h"
|
| #include "chrome/common/url_constants.h"
|
| +#include "chrome/test/base/testing_browser_process.h"
|
| #include "chrome/test/base/testing_profile.h"
|
| #include "components/user_prefs/pref_registry_syncable.h"
|
| #include "content/public/browser/dom_storage_context.h"
|
| @@ -85,7 +89,9 @@
|
| #include "content/public/browser/storage_partition.h"
|
| #include "content/public/common/content_constants.h"
|
| #include "content/public/common/gpu_info.h"
|
| +#include "content/public/test/mock_render_process_host.h"
|
| #include "content/public/test/test_browser_thread.h"
|
| +#include "content/public/test/test_notification_tracker.h"
|
| #include "extensions/common/constants.h"
|
| #include "extensions/common/extension_resource.h"
|
| #include "extensions/common/url_pattern.h"
|
| @@ -102,6 +108,7 @@
|
| #include "sync/protocol/app_specifics.pb.h"
|
| #include "sync/protocol/extension_specifics.pb.h"
|
| #include "sync/protocol/sync.pb.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
| #include "testing/platform_test.h"
|
| #include "webkit/database/database_tracker.h"
|
| @@ -113,6 +120,10 @@
|
| #include "chrome/browser/chromeos/extensions/install_limiter.h"
|
| #endif
|
|
|
| +#if defined(OS_WIN)
|
| +#include "ui/base/win/scoped_ole_initializer.h"
|
| +#endif
|
| +
|
| using content::BrowserContext;
|
| using content::BrowserThread;
|
| using content::DOMStorageContext;
|
| @@ -409,8 +420,13 @@ class MockProviderVisitor
|
| };
|
|
|
| // Our message loop may be used in tests which require it to be an IO loop.
|
| -ExtensionServiceTestBase::ExtensionServiceTestBase()
|
| - : loop_(MessageLoop::TYPE_IO),
|
| +ExtensionServiceTestBase::ExtensionServiceTestBase(
|
| + MessageLoop::Type main_loop_type)
|
| + : loop_(main_loop_type),
|
| + local_state_(TestingBrowserProcess::GetGlobal()),
|
| +#if defined(OS_WIN)
|
| + ole_initializer_(new ui::ScopedOleInitializer()),
|
| +#endif
|
| service_(NULL),
|
| management_policy_(NULL),
|
| expected_extensions_count_(0),
|
| @@ -419,9 +435,13 @@ ExtensionServiceTestBase::ExtensionServiceTestBase()
|
| webkit_thread_(BrowserThread::WEBKIT_DEPRECATED, &loop_),
|
| file_thread_(BrowserThread::FILE, &loop_),
|
| file_user_blocking_thread_(BrowserThread::FILE_USER_BLOCKING, &loop_),
|
| - io_thread_(BrowserThread::IO, &loop_),
|
| + io_thread_(main_loop_type == MessageLoop::TYPE_IO
|
| + ? new content::TestBrowserThread(BrowserThread::IO, &loop_)
|
| + : new content::TestBrowserThread(BrowserThread::IO)),
|
| override_sideload_wipeout_(
|
| FeatureSwitch::sideload_wipeout(), false) {
|
| + if (main_loop_type != MessageLoop::TYPE_IO)
|
| + io_thread_->StartIOThread();
|
| base::FilePath test_data_dir;
|
| if (!PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir)) {
|
| ADD_FAILURE();
|
| @@ -438,6 +458,8 @@ ExtensionServiceTestBase::~ExtensionServiceTestBase() {
|
| MessageLoop::current()->RunUntilIdle();
|
| profile_.reset(NULL);
|
| MessageLoop::current()->RunUntilIdle();
|
| +
|
| + TestingBrowserProcess::GetGlobal()->SetProfileManager(NULL);
|
| }
|
|
|
| void ExtensionServiceTestBase::InitializeExtensionService(
|
| @@ -507,6 +529,10 @@ void ExtensionServiceTestBase::InitializeEmptyExtensionService() {
|
| }
|
|
|
| void ExtensionServiceTestBase::InitializeExtensionProcessManager() {
|
| + // Needed in order to look up RenderProcessHosts.
|
| + TestingBrowserProcess::GetGlobal()->SetProfileManager(
|
| + new ProfileManagerWithoutInit(temp_dir_.path()));
|
| +
|
| static_cast<extensions::TestExtensionSystem*>(
|
| ExtensionSystem::Get(profile_.get()))->
|
| CreateExtensionProcessManager();
|
| @@ -556,6 +582,32 @@ void ExtensionServiceTestBase::SetUp() {
|
| (new extensions::RequirementsHandler)->Register();
|
| }
|
|
|
| +template<typename Result>
|
| +static Result PostTaskAndWaitForResult(
|
| + content::BrowserThread::ID thread,
|
| + const tracked_objects::Location& from_here,
|
| + const base::Callback<Result(void)>& task) {
|
| + struct ResultSaver {
|
| + static void UpdateResult(base::RunLoop* loop,
|
| + Result* saved,
|
| + Result from_task) {
|
| + using std::swap;
|
| + swap(*saved, from_task);
|
| + loop->Quit();
|
| + }
|
| + };
|
| +
|
| + Result result;
|
| + base::RunLoop loop;
|
| + content::BrowserThread::PostTaskAndReplyWithResult(
|
| + thread, from_here, task,
|
| + base::Bind(&ResultSaver::UpdateResult,
|
| + base::Unretained(&loop),
|
| + base::Unretained(&result)));
|
| + loop.Run();
|
| + return result;
|
| +}
|
| +
|
| class ExtensionServiceTest
|
| : public ExtensionServiceTestBase, public content::NotificationObserver {
|
| public:
|
| @@ -3607,10 +3659,70 @@ TEST_F(ExtensionServiceTest, ReloadExtensions) {
|
| EXPECT_EQ(0u, service_->disabled_extensions()->size());
|
| }
|
|
|
| +namespace {
|
| +std::vector<uint32> ExtensionMessageTypes(const IPC::TestSink& sink) {
|
| + std::vector<uint32> message_types;
|
| + for (size_t i = 0; i < sink.message_count(); ++i) {
|
| + uint32 type = sink.GetMessageAt(i)->type();
|
| + if (IPC_MESSAGE_ID_CLASS(type) == ExtensionMsgStart)
|
| + message_types.push_back(type);
|
| + }
|
| + return message_types;
|
| +}
|
| +
|
| +struct ProcessObserver
|
| + : public content::MockRenderProcessHostFactory::Observer {
|
| + explicit ProcessObserver(content::MockRenderProcessHostFactory* factory)
|
| + : Observer(factory) {}
|
| +
|
| + ~ProcessObserver() {
|
| + for (size_t i = 0; i < hosts.size(); ++i) {
|
| + if (destroyed_processes.count(process_ids[i]) == 0)
|
| + hosts[i]->sink().RemoveFilter(process_messages[i]);
|
| + delete process_messages[i];
|
| + }
|
| + }
|
| +
|
| + virtual void OnRenderProcessHostCreated(
|
| + content::MockRenderProcessHost* host) {
|
| + IPC::TestSink* sink = new IPC::TestSink;
|
| + hosts.push_back(host);
|
| + process_ids.push_back(host->GetID());
|
| + process_messages.push_back(sink);
|
| + host->sink().AddFilter(sink);
|
| + }
|
| + virtual void OnRenderProcessHostDestroyed(
|
| + content::MockRenderProcessHost* host) {
|
| + destroyed_processes.insert(host->GetID());
|
| + }
|
| +
|
| + std::vector<content::MockRenderProcessHost*> hosts;
|
| + std::vector<int> process_ids;
|
| + std::vector<IPC::TestSink*> process_messages;
|
| + std::set<int> destroyed_processes;
|
| +};
|
| +} // namespace
|
| +
|
| // Tests reloading an extension.
|
| TEST_F(ExtensionServiceTest, ReloadExtension) {
|
| + ProcessObserver observer(rvh_enabler_.rph_factory());
|
| +
|
| + content::TestNotificationTracker notifications;
|
| + notifications.ListenForAll(chrome::NOTIFICATION_EXTENSIONS_READY);
|
| + notifications.ListenForAll(chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED);
|
| + notifications.ListenForAll(chrome::NOTIFICATION_EXTENSION_LOADED);
|
| + notifications.ListenForAll(chrome::NOTIFICATION_EXTENSION_UNLOADED);
|
| + notifications.ListenForAll(content::NOTIFICATION_RENDERER_PROCESS_CLOSING);
|
| + notifications.ListenForAll(content::NOTIFICATION_RENDERER_PROCESS_CREATED);
|
| + notifications.ListenForAll(content::NOTIFICATION_RENDERER_PROCESS_TERMINATED);
|
| + notifications.ListenForAll(
|
| + content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW);
|
| +
|
| InitializeEmptyExtensionService();
|
| InitializeExtensionProcessManager();
|
| + service_->Init();
|
| + EXPECT_THAT(notifications.GetTypesAndReset(),
|
| + testing::ElementsAre(chrome::NOTIFICATION_EXTENSIONS_READY));
|
|
|
| // Simple extension that should install without error.
|
| const char* extension_id = "behllobkkfkfnphdnhnkndlbkcpglgmj";
|
| @@ -3622,23 +3734,81 @@ TEST_F(ExtensionServiceTest, ReloadExtension) {
|
| extensions::UnpackedInstaller::Create(service_)->Load(ext);
|
| loop_.RunUntilIdle();
|
|
|
| + EXPECT_THAT(notifications.GetTypesAndReset(),
|
| + testing::ElementsAre(chrome::NOTIFICATION_EXTENSION_LOADED));
|
| + ASSERT_EQ(1u, observer.process_messages.size());
|
| + EXPECT_THAT(ExtensionMessageTypes(*observer.process_messages[0]),
|
| + testing::ElementsAre(ExtensionMsg_Loaded::ID,
|
| + ExtensionMsg_ActivateExtension::ID,
|
| + ExtensionMsg_Loaded::ID,
|
| + ExtensionMsg_ActivateExtension::ID));
|
| + observer.process_messages[0]->ClearMessages();
|
| +
|
| EXPECT_EQ(1u, service_->extensions()->size());
|
| EXPECT_EQ(0u, service_->disabled_extensions()->size());
|
|
|
| service_->ReloadExtension(extension_id);
|
|
|
| + EXPECT_THAT(notifications.GetTypesAndReset(),
|
| + testing::ElementsAre(
|
| + chrome::NOTIFICATION_EXTENSION_UNLOADED,
|
| + content::NOTIFICATION_RENDERER_PROCESS_CLOSING,
|
| + chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
|
| + content::NOTIFICATION_RENDERER_PROCESS_TERMINATED));
|
| +
|
| + ASSERT_EQ(1u, observer.process_messages.size());
|
| + EXPECT_THAT(ExtensionMessageTypes(*observer.process_messages[0]),
|
| + testing::ElementsAre());
|
| + observer.process_messages[0]->ClearMessages();
|
| +
|
| // Extension should be disabled now, waiting to be reloaded.
|
| EXPECT_EQ(0u, service_->extensions()->size());
|
| EXPECT_EQ(1u, service_->disabled_extensions()->size());
|
| EXPECT_EQ(Extension::DISABLE_RELOAD,
|
| service_->extension_prefs()->GetDisableReasons(extension_id));
|
|
|
| - // Reloading again should not crash.
|
| + // Reloading again before iterating the MessageLoop should not crash and
|
| + // shouldn't cause an extra reload.
|
| service_->ReloadExtension(extension_id);
|
| + EXPECT_THAT(notifications.GetTypesAndReset(),
|
| + testing::ElementsAre());
|
| + ASSERT_EQ(1u, observer.process_messages.size());
|
| + EXPECT_THAT(ExtensionMessageTypes(*observer.process_messages[0]),
|
| + testing::ElementsAre());
|
| + observer.process_messages[0]->ClearMessages();
|
|
|
| // Finish reloading
|
| loop_.RunUntilIdle();
|
|
|
| + EXPECT_THAT(notifications.GetTypesAndReset(),
|
| + testing::ElementsAre(
|
| + chrome::NOTIFICATION_EXTENSION_LOADED,
|
| + chrome::NOTIFICATION_EXTENSION_UNLOADED,
|
| + content::NOTIFICATION_RENDERER_PROCESS_CLOSING,
|
| + chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
|
| + content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
|
| + chrome::NOTIFICATION_EXTENSION_LOADED));
|
| +
|
| + ASSERT_EQ(3u, observer.process_messages.size());
|
| + EXPECT_THAT(observer.destroyed_processes,
|
| + testing::ElementsAre(observer.process_ids[0],
|
| + observer.process_ids[1]));
|
| + EXPECT_THAT(ExtensionMessageTypes(*observer.process_messages[0]),
|
| + testing::ElementsAre());
|
| + observer.process_messages[0]->ClearMessages();
|
| + EXPECT_THAT(ExtensionMessageTypes(*observer.process_messages[1]),
|
| + testing::ElementsAre(ExtensionMsg_Loaded::ID,
|
| + ExtensionMsg_ActivateExtension::ID,
|
| + ExtensionMsg_Loaded::ID,
|
| + ExtensionMsg_ActivateExtension::ID));
|
| + observer.process_messages[1]->ClearMessages();
|
| + EXPECT_THAT(ExtensionMessageTypes(*observer.process_messages[2]),
|
| + testing::ElementsAre(ExtensionMsg_Loaded::ID,
|
| + ExtensionMsg_ActivateExtension::ID,
|
| + ExtensionMsg_Loaded::ID,
|
| + ExtensionMsg_ActivateExtension::ID));
|
| + observer.process_messages[2]->ClearMessages();
|
| +
|
| // Extension should be enabled again.
|
| EXPECT_EQ(1u, service_->extensions()->size());
|
| EXPECT_EQ(0u, service_->disabled_extensions()->size());
|
| @@ -3783,8 +3953,8 @@ TEST_F(ExtensionServiceTest, UnpackedRequirements) {
|
| class ExtensionCookieCallback {
|
| public:
|
| ExtensionCookieCallback()
|
| - : result_(false),
|
| - weak_factory_(MessageLoop::current()) {}
|
| + : result_(false),
|
| + weak_factory_(MessageLoop::current()) {}
|
|
|
| void SetCookieCallback(bool result) {
|
| MessageLoop::current()->PostTask(
|
| @@ -3797,11 +3967,18 @@ class ExtensionCookieCallback {
|
| FROM_HERE, base::Bind(&MessageLoop::Quit, weak_factory_.GetWeakPtr()));
|
| list_ = list;
|
| }
|
| +
|
| net::CookieList list_;
|
| bool result_;
|
| base::WeakPtrFactory<MessageLoop> weak_factory_;
|
| };
|
|
|
| +// Must run on IO thread to set up ThreadCheckers correctly.
|
| +static net::CookieMonster* GetCookieMonsterFromIOThread(
|
| + net::URLRequestContextGetter* getter) {
|
| + return getter->GetURLRequestContext()->cookie_store()->GetCookieMonster();
|
| +}
|
| +
|
| // Verifies extension state is removed upon uninstall.
|
| TEST_F(ExtensionServiceTest, ClearExtensionData) {
|
| InitializeEmptyExtensionService();
|
| @@ -3817,23 +3994,26 @@ TEST_F(ExtensionServiceTest, ClearExtensionData) {
|
| webkit_database::DatabaseUtil::GetOriginIdentifier(ext_url);
|
|
|
| // Set a cookie for the extension.
|
| - net::CookieMonster* cookie_monster =
|
| - profile_->GetRequestContextForExtensions()->GetURLRequestContext()->
|
| - cookie_store()->GetCookieMonster();
|
| + net::CookieMonster* cookie_monster = PostTaskAndWaitForResult(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&GetCookieMonsterFromIOThread,
|
| + make_scoped_refptr(
|
| + profile_->GetRequestContextForExtensions())));
|
| ASSERT_TRUE(cookie_monster);
|
| +
|
| net::CookieOptions options;
|
| cookie_monster->SetCookieWithOptionsAsync(
|
| - ext_url, "dummy=value", options,
|
| - base::Bind(&ExtensionCookieCallback::SetCookieCallback,
|
| - base::Unretained(&callback)));
|
| - loop_.RunUntilIdle();
|
| + ext_url, "dummy=value", options,
|
| + base::Bind(&ExtensionCookieCallback::SetCookieCallback,
|
| + base::Unretained(&callback)));
|
| + MessageLoop::current()->RunUntilIdle();
|
| EXPECT_TRUE(callback.result_);
|
|
|
| cookie_monster->GetAllCookiesForURLAsync(
|
| ext_url,
|
| base::Bind(&ExtensionCookieCallback::GetAllCookiesCallback,
|
| base::Unretained(&callback)));
|
| - loop_.RunUntilIdle();
|
| + MessageLoop::current()->RunUntilIdle();
|
| EXPECT_EQ(1U, callback.list_.size());
|
|
|
| // Open a database.
|
| @@ -3931,9 +4111,10 @@ TEST_F(ExtensionServiceTest, ClearAppData) {
|
| IsStorageUnlimited(origin2));
|
|
|
| // Set a cookie for the extension.
|
| - net::CookieMonster* cookie_monster =
|
| - profile_->GetRequestContext()->GetURLRequestContext()->
|
| - cookie_store()->GetCookieMonster();
|
| + net::CookieMonster* cookie_monster = PostTaskAndWaitForResult(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&GetCookieMonsterFromIOThread,
|
| + make_scoped_refptr(profile_->GetRequestContext())));
|
| ASSERT_TRUE(cookie_monster);
|
| net::CookieOptions options;
|
| cookie_monster->SetCookieWithOptionsAsync(
|
|
|