diff options
Diffstat (limited to 'chrome/browser/extensions')
140 files changed, 3137 insertions, 1189 deletions
diff --git a/chrome/browser/extensions/alert_apitest.cc b/chrome/browser/extensions/alert_apitest.cc index 23e8fe8..8e70fdb 100644 --- a/chrome/browser/extensions/alert_apitest.cc +++ b/chrome/browser/extensions/alert_apitest.cc @@ -14,7 +14,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, AlertBasic) { ASSERT_TRUE(RunExtensionTest("alert")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ExtensionHost* host = browser()->profile()->GetExtensionProcessManager()-> GetBackgroundHostForExtension(extension); ASSERT_TRUE(host); diff --git a/chrome/browser/extensions/app_background_page_apitest.cc b/chrome/browser/extensions/app_background_page_apitest.cc index d1e78d1..80d2218 100644 --- a/chrome/browser/extensions/app_background_page_apitest.cc +++ b/chrome/browser/extensions/app_background_page_apitest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/string_util.h" #include "chrome/browser/browser.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/common/chrome_switches.h" @@ -14,14 +15,53 @@ class AppBackgroundPageApiTest : public ExtensionApiTest { ExtensionApiTest::SetUpCommandLine(command_line); command_line->AppendSwitch(switches::kDisablePopupBlocking); } + + bool CreateApp(const std::string& app_manifest, + FilePath* app_dir) { + if (!app_dir_.CreateUniqueTempDir()) { + LOG(ERROR) << "Unable to create a temporary directory."; + return false; + } + FilePath manifest_path = app_dir_.path().AppendASCII("manifest.json"); + int bytes_written = file_util::WriteFile(manifest_path, + app_manifest.data(), + app_manifest.size()); + if (bytes_written != static_cast<int>(app_manifest.size())) { + LOG(ERROR) << "Unable to write complete manifest to file. Return code=" + << bytes_written; + return false; + } + *app_dir = app_dir_.path(); + return true; + } + + private: + ScopedTempDir app_dir_; }; IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, Basic) { host_resolver()->AddRule("a.com", "127.0.0.1"); ASSERT_TRUE(StartTestServer()); - LoadExtension(test_data_dir_.AppendASCII( - "app_background_page/app_has_permission")); + std::string app_manifest = StringPrintf( + "{" + " \"name\": \"App\"," + " \"version\": \"0.1\"," + " \"app\": {" + " \"urls\": [" + " \"http://a.com/\"" + " ]," + " \"launch\": {" + " \"web_url\": \"http://a.com:%d/\"" + " }" + " }," + " \"permissions\": [\"background\"]" + "}", + test_server()->host_port_pair().port()); + + FilePath app_dir; + ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); + ASSERT_TRUE(LoadExtension(app_dir)); ASSERT_TRUE(RunExtensionTest("app_background_page/basic")) << message_; } @@ -29,8 +69,24 @@ IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, LacksPermission) { host_resolver()->AddRule("a.com", "127.0.0.1"); ASSERT_TRUE(StartTestServer()); - LoadExtension(test_data_dir_.AppendASCII( - "app_background_page/app_lacks_permission")); + std::string app_manifest = StringPrintf( + "{" + " \"name\": \"App\"," + " \"version\": \"0.1\"," + " \"app\": {" + " \"urls\": [" + " \"http://a.com/\"" + " ]," + " \"launch\": {" + " \"web_url\": \"http://a.com:%d/\"" + " }" + " }" + "}", + test_server()->host_port_pair().port()); + + FilePath app_dir; + ASSERT_TRUE(CreateApp(app_manifest, &app_dir)); + ASSERT_TRUE(LoadExtension(app_dir)); ASSERT_TRUE(RunExtensionTest("app_background_page/lacks_permission")) << message_; } diff --git a/chrome/browser/extensions/autoupdate_interceptor.cc b/chrome/browser/extensions/autoupdate_interceptor.cc index 2acd0a0..ef0091f 100644 --- a/chrome/browser/extensions/autoupdate_interceptor.cc +++ b/chrome/browser/extensions/autoupdate_interceptor.cc @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/autoupdate_interceptor.h" #include "base/file_util.h" +#include "base/thread_restrictions.h" #include "chrome/browser/browser_thread.h" #include "net/url_request/url_request_test_job.h" #include "testing/gtest/include/gtest/gtest.h" @@ -40,6 +41,10 @@ URLRequestJob* AutoUpdateInterceptor::MaybeIntercept(URLRequest* request) { return NULL; } + // It's ok to do a blocking disk access on this thread; this class + // is just used for tests. + base::ThreadRestrictions::ScopedAllowIO allow_io; + // Search for this request's url, ignoring any query parameters. GURL url = request->url(); if (url.has_query()) { @@ -61,6 +66,9 @@ URLRequestJob* AutoUpdateInterceptor::MaybeIntercept(URLRequest* request) { void AutoUpdateInterceptor::SetResponse(const std::string url, const FilePath& path) { EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // It's ok to do a blocking disk access on this thread; this class + // is just used for tests. + base::ThreadRestrictions::ScopedAllowIO allow_io; GURL gurl(url); EXPECT_EQ("http", gurl.scheme()); EXPECT_EQ("localhost", gurl.host()); diff --git a/chrome/browser/extensions/browser_action_apitest.cc b/chrome/browser/extensions/browser_action_apitest.cc index 8b09783..5e76310 100644 --- a/chrome/browser/extensions/browser_action_apitest.cc +++ b/chrome/browser/extensions/browser_action_apitest.cc @@ -47,7 +47,7 @@ class BrowserActionApiTest : public ExtensionApiTest { IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, Basic) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(RunExtensionTest("browser_action/basics")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // Test that there is a browser action in the toolbar. @@ -87,7 +87,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, Basic) { IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, DynamicBrowserAction) { ASSERT_TRUE(RunExtensionTest("browser_action/no_icon")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // Test that there is a browser action in the toolbar and that it has no icon. @@ -117,7 +117,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, DynamicBrowserAction) { IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, TabSpecificBrowserActionState) { ASSERT_TRUE(RunExtensionTest("browser_action/tab_specific_state")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // Test that there is a browser action in the toolbar and that it has an icon. @@ -147,7 +147,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionPopup) { ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII( "browser_action/popup"))); BrowserActionTestUtil actions_bar = GetBrowserActionsBar(); - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // The extension's popup's size grows by |growFactor| each click. @@ -180,7 +180,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionPopup) { // a popup. IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionAddPopup) { ASSERT_TRUE(RunExtensionTest("browser_action/add_popup")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents()); @@ -235,7 +235,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionAddPopup) { IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionRemovePopup) { // Load the extension, which has a browser action with a default popup. ASSERT_TRUE(RunExtensionTest("browser_action/remove_popup")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents()); @@ -269,7 +269,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoBasic) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(RunExtensionTest("browser_action/basics")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // Test that there is a browser action in the toolbar. diff --git a/chrome/browser/extensions/content_script_all_frames_apitest.cc b/chrome/browser/extensions/content_script_all_frames_apitest.cc index fac6362..b465c42 100644 --- a/chrome/browser/extensions/content_script_all_frames_apitest.cc +++ b/chrome/browser/extensions/content_script_all_frames_apitest.cc @@ -5,11 +5,11 @@ #include "chrome/browser/extensions/extension_apitest.h" IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptAllFrames) { - ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(StartTestServer()); ASSERT_TRUE(RunExtensionTest("content_scripts/all_frames")) << message_; } IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptExtensionIframe) { - ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(StartTestServer()); ASSERT_TRUE(RunExtensionTest("content_scripts/extension_iframe")) << message_; } diff --git a/chrome/browser/extensions/content_script_extension_process_apitest.cc b/chrome/browser/extensions/content_script_extension_process_apitest.cc index 46729d2..64574a0 100644 --- a/chrome/browser/extensions/content_script_extension_process_apitest.cc +++ b/chrome/browser/extensions/content_script_extension_process_apitest.cc @@ -10,6 +10,7 @@ #include "chrome/test/ui_test_utils.h" IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptExtensionProcess) { - ASSERT_TRUE(test_server()->Start()); - ASSERT_TRUE(RunExtensionTest("content_scripts/extension_process")) << message_; + ASSERT_TRUE(StartTestServer()); + ASSERT_TRUE( + RunExtensionTest("content_scripts/extension_process")) << message_; } diff --git a/chrome/browser/extensions/convert_user_script.cc b/chrome/browser/extensions/convert_user_script.cc index d50bec1..89caec4 100644 --- a/chrome/browser/extensions/convert_user_script.cc +++ b/chrome/browser/extensions/convert_user_script.cc @@ -24,9 +24,9 @@ namespace keys = extension_manifest_keys; -Extension* ConvertUserScriptToExtension(const FilePath& user_script_path, - const GURL& original_url, - std::string* error) { +scoped_refptr<Extension> ConvertUserScriptToExtension( + const FilePath& user_script_path, const GURL& original_url, + std::string* error) { std::string content; if (!file_util::ReadFileToString(user_script_path, &content)) { *error = "Could not read source file: " + @@ -138,12 +138,13 @@ Extension* ConvertUserScriptToExtension(const FilePath& user_script_path, return NULL; } - scoped_ptr<Extension> extension(new Extension(temp_dir.path())); - if (!extension->InitFromValue(*root, false, error)) { + scoped_refptr<Extension> extension = Extension::Create( + temp_dir.path(), Extension::INTERNAL, *root, false, error); + if (!extension) { NOTREACHED() << "Could not init extension " << *error; return NULL; } temp_dir.Take(); // The caller takes ownership of the directory. - return extension.release(); + return extension; } diff --git a/chrome/browser/extensions/convert_user_script.h b/chrome/browser/extensions/convert_user_script.h index d392c4d..d779de0 100644 --- a/chrome/browser/extensions/convert_user_script.h +++ b/chrome/browser/extensions/convert_user_script.h @@ -8,6 +8,8 @@ #include <string> +#include "base/ref_counted.h" + class Extension; class FilePath; class GURL; @@ -17,8 +19,7 @@ class GURL; // should take ownership on success, or NULL and |error| on failure. // // NOTE: This function does file IO and should not be called on the UI thread. -Extension* ConvertUserScriptToExtension(const FilePath& user_script, - const GURL& original_url, - std::string* error); +scoped_refptr<Extension> ConvertUserScriptToExtension( + const FilePath& user_script, const GURL& original_url, std::string* error); #endif // CHROME_BROWSER_EXTENSIONS_CONVERT_USER_SCRIPT_H_ diff --git a/chrome/browser/extensions/convert_user_script_unittest.cc b/chrome/browser/extensions/convert_user_script_unittest.cc index 299fee7..a0e1b8b 100644 --- a/chrome/browser/extensions/convert_user_script_unittest.cc +++ b/chrome/browser/extensions/convert_user_script_unittest.cc @@ -21,7 +21,7 @@ TEST(ExtensionFromUserScript, Basic) { .AppendASCII("user_script_basic.user.js"); std::string error; - scoped_ptr<Extension> extension(ConvertUserScriptToExtension( + scoped_refptr<Extension> extension(ConvertUserScriptToExtension( test_file, GURL("http://www.google.com/foo"), &error)); ASSERT_TRUE(extension.get()); @@ -61,7 +61,7 @@ TEST(ExtensionFromUserScript, NoMetdata) { .AppendASCII("user_script_no_metadata.user.js"); std::string error; - scoped_ptr<Extension> extension(ConvertUserScriptToExtension( + scoped_refptr<Extension> extension(ConvertUserScriptToExtension( test_file, GURL("http://www.google.com/foo/bar.user.js?monkey"), &error)); ASSERT_TRUE(extension.get()); diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index 6f34b3c..b2c60e3 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -4,6 +4,8 @@ #include "chrome/browser/extensions/crx_installer.h" +#include <list> + #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/file_util.h" @@ -12,6 +14,7 @@ #include "base/singleton.h" #include "base/stringprintf.h" #include "base/task.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "base/version.h" #include "chrome/browser/browser_process.h" @@ -45,7 +48,7 @@ struct WhitelistedInstallData { std::list<std::string> ids; }; -} +} // namespace // static void CrxInstaller::SetWhitelistedInstallId(const std::string& id) { @@ -55,6 +58,7 @@ void CrxInstaller::SetWhitelistedInstallId(const std::string& id) { // static bool CrxInstaller::ClearWhitelistedInstallId(const std::string& id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); std::list<std::string>& ids = Singleton<WhitelistedInstallData>::get()->ids; std::list<std::string>::iterator iter = ids.begin(); for (; iter != ids.end(); ++iter) { @@ -112,7 +116,12 @@ void CrxInstaller::InstallCrx(const FilePath& source_file) { source_file_ = source_file; FilePath user_data_temp_dir; - CHECK(PathService::Get(chrome::DIR_USER_DATA_TEMP, &user_data_temp_dir)); + { + // We shouldn't be doing disk IO on the UI thread. + // http://code.google.com/p/chromium/issues/detail?id=60634 + base::ThreadRestrictions::ScopedAllowIO allow_io; + CHECK(PathService::Get(chrome::DIR_USER_DATA_TEMP, &user_data_temp_dir)); + } scoped_refptr<SandboxedExtensionUnpacker> unpacker( new SandboxedExtensionUnpacker( @@ -141,8 +150,8 @@ void CrxInstaller::InstallUserScript(const FilePath& source_file, void CrxInstaller::ConvertUserScriptOnFileThread() { std::string error; - Extension* extension = ConvertUserScriptToExtension(source_file_, - original_url_, &error); + scoped_refptr<Extension> extension = + ConvertUserScriptToExtension(source_file_, original_url_, &error); if (!extension) { ReportFailureFromFileThread(error); return; @@ -151,7 +160,8 @@ void CrxInstaller::ConvertUserScriptOnFileThread() { OnUnpackSuccess(extension->path(), extension->path(), extension); } -bool CrxInstaller::AllowInstall(Extension* extension, std::string* error) { +bool CrxInstaller::AllowInstall(const Extension* extension, + std::string* error) { DCHECK(error); // We always allow themes and external installs. @@ -233,11 +243,11 @@ void CrxInstaller::OnUnpackFailure(const std::string& error_message) { void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, const FilePath& extension_dir, - Extension* extension) { + const Extension* extension) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); // Note: We take ownership of |extension| and |temp_dir|. - extension_.reset(extension); + extension_ = extension; temp_dir_ = temp_dir; // We don't have to delete the unpack dir explicity since it is a child of @@ -263,8 +273,8 @@ void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, void CrxInstaller::ConfirmInstall() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (frontend_->extension_prefs()->IsExtensionBlacklisted(extension_->id())) { - LOG(INFO) << "This extension: " << extension_->id() - << " is blacklisted. Install failed."; + VLOG(1) << "This extension: " << extension_->id() + << " is blacklisted. Install failed."; ReportFailureFromUIThread("This extension is blacklisted."); return; } @@ -276,7 +286,7 @@ void CrxInstaller::ConfirmInstall() { } GURL overlapping_url; - Extension* overlapping_extension = + const Extension* overlapping_extension = frontend_->GetExtensionByOverlappingWebExtent(extension_->web_extent()); if (overlapping_extension) { ReportFailureFromUIThread(l10n_util::GetStringFUTF8( @@ -288,9 +298,11 @@ void CrxInstaller::ConfirmInstall() { current_version_ = frontend_->extension_prefs()->GetVersionString(extension_->id()); + bool whitelisted = ClearWhitelistedInstallId(extension_->id()) && + extension_->plugins().empty(); + if (client_ && - (!allow_silent_install_ || - !ClearWhitelistedInstallId(extension_->id()))) { + (!allow_silent_install_ || !whitelisted)) { AddRef(); // Balanced in Proceed() and Abort(). client_->ConfirmInstall(this, extension_.get()); } else { @@ -351,8 +363,8 @@ void CrxInstaller::CompleteInstall() { // TODO(aa): All paths to resources inside extensions should be created // lazily and based on the Extension's root path at that moment. std::string error; - extension_.reset(extension_file_util::LoadExtension( - version_dir, install_source_, true, &error)); + extension_ = extension_file_util::LoadExtension( + version_dir, install_source_, true, &error); DCHECK(error.empty()); ReportSuccessFromFileThread(); @@ -400,8 +412,8 @@ void CrxInstaller::ReportSuccessFromUIThread() { // Tell the frontend about the installation and hand off ownership of // extension_ to it. - frontend_->OnExtensionInstalled(extension_.release(), - allow_privilege_increase_); + frontend_->OnExtensionInstalled(extension_, allow_privilege_increase_); + extension_ = NULL; // We're done. We don't post any more tasks to ourselves so we are deleted // soon. diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h index b1212a2..349b225 100644 --- a/chrome/browser/extensions/crx_installer.h +++ b/chrome/browser/extensions/crx_installer.h @@ -49,11 +49,12 @@ class CrxInstaller // ExtensionFunction to a resulting download in the download manager, it's // currently necessary. This is the |id| of an extension to be installed // *by the web store only* which should not get the permissions install - // prompt. + // prompt. This should only be called on the UI thread. // crbug.com/54916 static void SetWhitelistedInstallId(const std::string& id); - // Returns whether |id| was found and removed (was whitelisted). + // Returns whether |id| was found and removed (was whitelisted). This should + // only be called on the UI thread. static bool ClearWhitelistedInstallId(const std::string& id); // Constructor. Extensions will be unpacked to |install_directory|. @@ -120,13 +121,13 @@ class CrxInstaller // Called after OnUnpackSuccess as a last check to see whether the install // should complete. - bool AllowInstall(Extension* extension, std::string* error); + bool AllowInstall(const Extension* extension, std::string* error); // SandboxedExtensionUnpackerClient virtual void OnUnpackFailure(const std::string& error_message); virtual void OnUnpackSuccess(const FilePath& temp_dir, const FilePath& extension_dir, - Extension* extension); + const Extension* extension); // Runs on the UI thread. Confirms with the user (via ExtensionInstallUI) that // it is OK to install this extension. @@ -187,7 +188,7 @@ class CrxInstaller // The extension we're installing. We own this and either pass it off to // ExtensionsService on success, or delete it on failure. - scoped_ptr<Extension> extension_; + scoped_refptr<const Extension> extension_; // If non-empty, contains the current version of the extension we're // installing (for upgrades). diff --git a/chrome/browser/extensions/crx_installer_browsertest.cc b/chrome/browser/extensions/crx_installer_browsertest.cc new file mode 100644 index 0000000..535efc9 --- /dev/null +++ b/chrome/browser/extensions/crx_installer_browsertest.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2010 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/browser/browser.h" +#include "chrome/browser/extensions/crx_installer.h" +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/extensions/extension_install_ui.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/profile.h" +#include "chrome/test/ui_test_utils.h" + +namespace { + +class MockInstallUI : public ExtensionInstallUI { + public: + explicit MockInstallUI(Profile* profile) : + ExtensionInstallUI(profile), confirmation_requested_(false) {} + + // Did the Delegate request confirmation? + bool confirmation_requested() { return confirmation_requested_; } + + // Overriding some of the ExtensionInstallUI API. + void ConfirmInstall(Delegate* delegate, const Extension* extension) { + confirmation_requested_ = true; + delegate->InstallUIProceed(); + } + void OnInstallSuccess(const Extension* extension) { + MessageLoopForUI::current()->Quit(); + } + void OnInstallFailure(const std::string& error) { + ADD_FAILURE() << "insall failed"; + MessageLoopForUI::current()->Quit(); + } + + private: + bool confirmation_requested_; +}; + +} // namespace + +class ExtensionCrxInstallerTest : public ExtensionBrowserTest { + public: + // Installs a crx from |crx_relpath| (a path relative to the extension test + // data dir) with expected id |id|. Returns whether a confirmation prompt + // happened or not. + bool DidWhitelistInstallPrompt(const std::string& crx_relpath, + const std::string& id) { + ExtensionsService* service = browser()->profile()->GetExtensionsService(); + MockInstallUI* mock_install_ui = new MockInstallUI(browser()->profile()); + + scoped_refptr<CrxInstaller> installer( + new CrxInstaller(service->install_directory(), + service, + mock_install_ui /* ownership transferred */)); + + installer->set_allow_silent_install(true); + CrxInstaller::SetWhitelistedInstallId(id); + + FilePath crx_path = test_data_dir_.AppendASCII(crx_relpath); + installer->InstallCrx(crx_path); + ui_test_utils::RunMessageLoop(); + + return mock_install_ui->confirmation_requested(); + } +}; + +IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, Whitelisting) { + // A regular extension should give no prompt. + EXPECT_FALSE(DidWhitelistInstallPrompt("good.crx", + "ldnnhddmnhbkjipkidpdiheffobcpfmf")); +#if !defined(OS_CHROMEOS) + // An extension with NPAPI should give a prompt. + EXPECT_TRUE(DidWhitelistInstallPrompt("uitest/plugins.crx", + "hdgllgikmikobbofgnabhfimcfoopgnd")); +#endif // !defined(OS_CHROMEOS +} diff --git a/chrome/browser/extensions/default_apps.cc b/chrome/browser/extensions/default_apps.cc index ad39a34..1dff210 100644 --- a/chrome/browser/extensions/default_apps.cc +++ b/chrome/browser/extensions/default_apps.cc @@ -19,10 +19,12 @@ void DefaultApps::RegisterUserPrefs(PrefService* prefs) { DefaultApps::DefaultApps(PrefService* prefs) : prefs_(prefs) { +#if !defined(OS_CHROMEOS) // gmail, calendar, docs ids_.insert("pjkljhegncpnkpknbcohdijeoejaedia"); ids_.insert("ejjicmeblgpmajnghnpcppodonldlgfn"); ids_.insert("apdfllckaahabafndbhieahigkjlhalf"); +#endif // OS_CHROMEOS } DefaultApps::~DefaultApps() {} diff --git a/chrome/browser/extensions/default_apps_unittest.cc b/chrome/browser/extensions/default_apps_unittest.cc index 8037236..24a9d70 100644 --- a/chrome/browser/extensions/default_apps_unittest.cc +++ b/chrome/browser/extensions/default_apps_unittest.cc @@ -7,6 +7,8 @@ #include "chrome/test/testing_pref_service.h" #include "testing/gtest/include/gtest/gtest.h" +// TODO(dpolukhin): On Chrome OS all apps are installed via external extensions +#if !defined(OS_CHROMEOS) TEST(DefaultApps, Basics) { TestingPrefService pref_service; DefaultApps::RegisterUserPrefs(&pref_service); @@ -59,6 +61,7 @@ TEST(DefaultApps, Basics) { EXPECT_FALSE(default_apps.ShouldShowPromo(default_app_ids)); EXPECT_EQ(DefaultApps::kAppsPromoCounterMax, default_apps.GetPromoCounter()); } +#endif // OS_CHROMEOS TEST(DefaultApps, HidePromo) { TestingPrefService pref_service; @@ -97,6 +100,8 @@ TEST(DefaultApps, InstallingAnAppHidesPromo) { EXPECT_EQ(DefaultApps::kAppsPromoCounterMax, default_apps.GetPromoCounter()); } +// TODO(dpolukhin): On Chrome OS all apps are installed via external extensions +#if !defined(OS_CHROMEOS) TEST(DefaultApps, ManualAppInstalledWhileInstallingDefaultApps) { // It is possible to have apps manually installed while the default apps are // being installed. The network or server might be down, causing the default @@ -131,3 +136,4 @@ TEST(DefaultApps, ManualAppInstalledWhileInstallingDefaultApps) { EXPECT_FALSE(default_apps.ShouldShowPromo(installed_ids)); EXPECT_EQ(DefaultApps::kAppsPromoCounterMax, default_apps.GetPromoCounter()); } +#endif // OS_CHROMEOS diff --git a/chrome/browser/extensions/execute_code_in_tab_function.cc b/chrome/browser/extensions/execute_code_in_tab_function.cc index b7bf999..8021e6c 100644 --- a/chrome/browser/extensions/execute_code_in_tab_function.cc +++ b/chrome/browser/extensions/execute_code_in_tab_function.cc @@ -78,7 +78,7 @@ bool ExecuteCodeInTabFunction::RunImpl() { // NOTE: This can give the wrong answer due to race conditions, but it is OK, // we check again in the renderer. - Extension* extension = GetExtension(); + const Extension* extension = GetExtension(); const std::vector<URLPattern> host_permissions = extension->host_permissions(); if (!Extension::CanExecuteScriptOnPage( @@ -158,7 +158,7 @@ bool ExecuteCodeInTabFunction::Execute(const std::string& code_string) { return false; } - Extension* extension = GetExtension(); + const Extension* extension = GetExtension(); if (!extension) { SendResponse(false); return false; diff --git a/chrome/browser/extensions/execute_script_apitest.cc b/chrome/browser/extensions/execute_script_apitest.cc index a450fe0..9edd0b6 100644 --- a/chrome/browser/extensions/execute_script_apitest.cc +++ b/chrome/browser/extensions/execute_script_apitest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 20109 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc index d136b50..07668b3 100644 --- a/chrome/browser/extensions/extension_apitest.cc +++ b/chrome/browser/extensions/extension_apitest.cc @@ -13,10 +13,6 @@ #include "chrome/common/notification_registrar.h" #include "chrome/test/ui_test_utils.h" -#if defined(OS_CHROMEOS) -#include "chrome/browser/chromeos/views/domui_menu_widget.h" -#endif - namespace { const char kTestServerPort[] = "testServer.port"; @@ -68,7 +64,7 @@ void ExtensionApiTest::ResultCatcher::Observe( switch (type.value) { case NotificationType::EXTENSION_TEST_PASSED: - LOG(INFO) << "Got EXTENSION_TEST_PASSED notification."; + VLOG(1) << "Got EXTENSION_TEST_PASSED notification."; results_.push_back(true); messages_.push_back(""); if (waiting_) @@ -76,7 +72,7 @@ void ExtensionApiTest::ResultCatcher::Observe( break; case NotificationType::EXTENSION_TEST_FAILED: - LOG(INFO) << "Got EXTENSION_TEST_FAILED notification."; + VLOG(1) << "Got EXTENSION_TEST_FAILED notification."; results_.push_back(false); messages_.push_back(*(Details<std::string>(details).ptr())); if (waiting_) @@ -122,13 +118,6 @@ bool ExtensionApiTest::RunPageTest(const std::string& page_url) { bool ExtensionApiTest::RunExtensionTestImpl(const char* extension_name, const std::string& page_url, bool enable_incognito) { -#if defined(OS_CHROMEOS) - // ChromeOS uses DOMUI for menu and creates a stand-by renderer to - // improve 1st time response. This can confuse this test as the test - // uses AllSources to listen to notifications, thus disabling the warmup - // process for ExtensionAPITests. - chromeos::DOMUIMenuWidget::DisableWarmUp(); -#endif ResultCatcher catcher; DCHECK(!std::string(extension_name).empty() || !page_url.empty()) << "extension_name and page_url cannot both be empty"; @@ -155,7 +144,7 @@ bool ExtensionApiTest::RunExtensionTestImpl(const char* extension_name, "Relative page_url given with no extension_name"; ExtensionsService* service = browser()->profile()->GetExtensionsService(); - Extension* extension = + const Extension* extension = service->GetExtensionById(last_loaded_extension_id_, false); if (!extension) return false; @@ -176,7 +165,7 @@ bool ExtensionApiTest::RunExtensionTestImpl(const char* extension_name, } // Test that exactly one extension loaded. -Extension* ExtensionApiTest::GetSingleLoadedExtension() { +const Extension* ExtensionApiTest::GetSingleLoadedExtension() { ExtensionsService* service = browser()->profile()->GetExtensionsService(); int found_extension_index = -1; @@ -196,7 +185,7 @@ Extension* ExtensionApiTest::GetSingleLoadedExtension() { found_extension_index = static_cast<int>(i); } - Extension* extension = service->extensions()->at(found_extension_index); + const Extension* extension = service->extensions()->at(found_extension_index); if (!extension) { message_ = "extension pointer is NULL."; return NULL; diff --git a/chrome/browser/extensions/extension_apitest.h b/chrome/browser/extensions/extension_apitest.h index b0a0e01..b2079a6 100644 --- a/chrome/browser/extensions/extension_apitest.h +++ b/chrome/browser/extensions/extension_apitest.h @@ -94,7 +94,7 @@ class ExtensionApiTest : public ExtensionBrowserTest { // Test that exactly one extension loaded. If so, return a pointer to // the extension. If not, return NULL and set message_. - Extension* GetSingleLoadedExtension(); + const Extension* GetSingleLoadedExtension(); // All extensions tested by ExtensionApiTest are in the "api_test" dir. virtual void SetUpCommandLine(CommandLine* command_line); diff --git a/chrome/browser/extensions/extension_browser_actions_api.cc b/chrome/browser/extensions/extension_browser_actions_api.cc index 1ce4dfe..1986d28 100644 --- a/chrome/browser/extensions/extension_browser_actions_api.cc +++ b/chrome/browser/extensions/extension_browser_actions_api.cc @@ -26,7 +26,7 @@ bool BrowserActionFunction::RunImpl() { if (details_->HasKey("tabId")) EXTENSION_FUNCTION_VALIDATE(details_->GetInteger("tabId", &tab_id_)); - Extension* extension = GetExtension(); + const Extension* extension = GetExtension(); browser_action_ = extension->browser_action(); if (!browser_action_) { error_ = kNoBrowserActionError; diff --git a/chrome/browser/extensions/extension_browser_event_router.cc b/chrome/browser/extensions/extension_browser_event_router.cc index ce58dc7..dcf9b9d 100644 --- a/chrome/browser/extensions/extension_browser_event_router.cc +++ b/chrome/browser/extensions/extension_browser_event_router.cc @@ -270,7 +270,7 @@ void ExtensionBrowserEventRouter::TabInsertedAt(TabContents* contents, bool foreground) { // If tab is new, send created event. int tab_id = ExtensionTabUtil::GetTabId(contents); - if (tab_entries_.find(tab_id) == tab_entries_.end()) { + if (!GetTabEntry(contents)) { tab_entries_[tab_id] = TabEntry(); TabCreatedAt(contents, index, foreground); @@ -295,14 +295,13 @@ void ExtensionBrowserEventRouter::TabInsertedAt(TabContents* contents, void ExtensionBrowserEventRouter::TabDetachedAt(TabContents* contents, int index) { - int tab_id = ExtensionTabUtil::GetTabId(contents); - if (tab_entries_.find(tab_id) == tab_entries_.end()) { + if (!GetTabEntry(contents)) { // The tab was removed. Don't send detach event. return; } ListValue args; - args.Append(Value::CreateIntegerValue(tab_id)); + args.Append(Value::CreateIntegerValue(ExtensionTabUtil::GetTabId(contents))); DictionaryValue* object_args = new DictionaryValue(); object_args->Set(tab_keys::kOldWindowIdKey, Value::CreateIntegerValue( @@ -384,36 +383,51 @@ void ExtensionBrowserEventRouter::TabMoved(TabContents* contents, void ExtensionBrowserEventRouter::TabUpdated(TabContents* contents, bool did_navigate) { - int tab_id = ExtensionTabUtil::GetTabId(contents); - std::map<int, TabEntry>::iterator i = tab_entries_.find(tab_id); - DCHECK(tab_entries_.end() != i); - TabEntry& entry = i->second; - + TabEntry* entry = GetTabEntry(contents); DictionaryValue* changed_properties = NULL; + + DCHECK(entry); + if (did_navigate) - changed_properties = entry.DidNavigate(contents); + changed_properties = entry->DidNavigate(contents); else - changed_properties = entry.UpdateLoadState(contents); + changed_properties = entry->UpdateLoadState(contents); + + if (changed_properties) + DispatchTabUpdatedEvent(contents, changed_properties); +} - if (changed_properties) { - // The state of the tab (as seen from the extension point of view) has - // changed. Send a notification to the extension. - ListValue args; +void ExtensionBrowserEventRouter::DispatchTabUpdatedEvent( + TabContents* contents, DictionaryValue* changed_properties) { + DCHECK(changed_properties); + DCHECK(contents); - // First arg: The id of the tab that changed. - args.Append(Value::CreateIntegerValue(tab_id)); + // The state of the tab (as seen from the extension point of view) has + // changed. Send a notification to the extension. + ListValue args; - // Second arg: An object containing the changes to the tab state. - args.Append(changed_properties); + // First arg: The id of the tab that changed. + args.Append(Value::CreateIntegerValue(ExtensionTabUtil::GetTabId(contents))); - // Third arg: An object containing the state of the tab. - args.Append(ExtensionTabUtil::CreateTabValue(contents)); + // Second arg: An object containing the changes to the tab state. + args.Append(changed_properties); - std::string json_args; - base::JSONWriter::Write(&args, false, &json_args); + // Third arg: An object containing the state of the tab. + args.Append(ExtensionTabUtil::CreateTabValue(contents)); - DispatchEvent(contents->profile(), events::kOnTabUpdated, json_args); - } + std::string json_args; + base::JSONWriter::Write(&args, false, &json_args); + + DispatchEvent(contents->profile(), events::kOnTabUpdated, json_args); +} + +ExtensionBrowserEventRouter::TabEntry* ExtensionBrowserEventRouter::GetTabEntry( + const TabContents* contents) { + int tab_id = ExtensionTabUtil::GetTabId(contents); + std::map<int, TabEntry>::iterator i = tab_entries_.find(tab_id); + if (tab_entries_.end() == i) + return NULL; + return &i->second; } void ExtensionBrowserEventRouter::Observe(NotificationType type, @@ -455,6 +469,19 @@ void ExtensionBrowserEventRouter::TabReplacedAt(TabContents* old_contents, RegisterForTabNotifications(new_contents); } +void ExtensionBrowserEventRouter::TabPinnedStateChanged(TabContents* contents, + int index) { + TabStripModel* tab_strip = NULL; + int tab_index; + + if (ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index)) { + DictionaryValue* changed_properties = new DictionaryValue(); + changed_properties->SetBoolean(tab_keys::kPinnedKey, + tab_strip->IsTabPinned(tab_index)); + DispatchTabUpdatedEvent(contents, changed_properties); + } +} + void ExtensionBrowserEventRouter::TabStripEmpty() {} void ExtensionBrowserEventRouter::DispatchOldPageActionEvent( diff --git a/chrome/browser/extensions/extension_browser_event_router.h b/chrome/browser/extensions/extension_browser_event_router.h index bfd2205..a84749d 100644 --- a/chrome/browser/extensions/extension_browser_event_router.h +++ b/chrome/browser/extensions/extension_browser_event_router.h @@ -74,6 +74,7 @@ class ExtensionBrowserEventRouter : public TabStripModelObserver, virtual void TabReplacedAt(TabContents* old_contents, TabContents* new_contents, int index); + virtual void TabPinnedStateChanged(TabContents* contents, int index); virtual void TabStripEmpty(); // Page Action execute event. @@ -100,6 +101,11 @@ class ExtensionBrowserEventRouter : public TabStripModelObserver, // and Observe/NAV_ENTRY_COMMITTED. void TabUpdated(TabContents* contents, bool did_navigate); + // Packages |changed_properties| as a tab updated event for the tab |contents| + // and dispatches the event to the extension. + void DispatchTabUpdatedEvent(TabContents* contents, + DictionaryValue* changed_properties); + // Called to dispatch a deprecated style page action click event that was // registered like: // chrome.pageActions["name"].addListener(function(actionId, info){}) @@ -164,6 +170,10 @@ class ExtensionBrowserEventRouter : public TabStripModelObserver, GURL url_; }; + // Gets the TabEntry for the given |contents|. Returns TabEntry* if + // found, NULL if not. + TabEntry* GetTabEntry(const TabContents* contents); + std::map<int, TabEntry> tab_entries_; // The currently focused window. We keep this so as to avoid sending multiple diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index aa5b46a..af8a6de 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc @@ -58,7 +58,6 @@ void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) { bool ExtensionBrowserTest::LoadExtensionImpl(const FilePath& path, bool incognito_enabled) { ExtensionsService* service = browser()->profile()->GetExtensionsService(); - size_t num_before = service->extensions()->size(); { NotificationRegistrar registrar; registrar.Add(this, NotificationType::EXTENSION_LOADED, @@ -66,15 +65,26 @@ bool ExtensionBrowserTest::LoadExtensionImpl(const FilePath& path, service->LoadExtension(path); ui_test_utils::RunMessageLoop(); } - size_t num_after = service->extensions()->size(); - if (num_after != (num_before + 1)) + + // Find the extension by iterating backwards since it is likely last. + FilePath extension_path = path; + file_util::AbsolutePath(&extension_path); + const Extension* extension = NULL; + for (ExtensionList::const_reverse_iterator iter = + service->extensions()->rbegin(); + iter != service->extensions()->rend(); ++iter) { + if ((*iter)->path() == extension_path) { + extension = *iter; + break; + } + } + if (!extension) return false; if (incognito_enabled) { // Enable the incognito bit in the extension prefs. The call to // OnExtensionInstalled ensures the other extension prefs are set up with // the defaults. - Extension* extension = service->extensions()->at(num_after - 1); service->extension_prefs()->OnExtensionInstalled( extension, Extension::ENABLED, false); service->SetIsIncognitoEnabled(extension, true); @@ -97,14 +107,15 @@ class MockAbortExtensionInstallUI : public ExtensionInstallUI { MockAbortExtensionInstallUI() : ExtensionInstallUI(NULL) {} // Simulate a user abort on an extension installation. - virtual void ConfirmInstall(Delegate* delegate, Extension* extension) { + virtual void ConfirmInstall(Delegate* delegate, const Extension* extension) { delegate->InstallUIAbort(); MessageLoopForUI::current()->Quit(); } - virtual void ConfirmUninstall(Delegate* delegate, Extension* extension) {} + virtual void ConfirmUninstall(Delegate* delegate, + const Extension* extension) {} - virtual void OnInstallSuccess(Extension* extension) {} + virtual void OnInstallSuccess(const Extension* extension) {} virtual void OnInstallFailure(const std::string& error) {} }; @@ -142,21 +153,19 @@ bool ExtensionBrowserTest::InstallOrUpdateExtension(const std::string& id, size_t num_after = service->extensions()->size(); if (num_after != (num_before + expected_change)) { - LOG(INFO) << "Num extensions before: " - << base::IntToString(num_before) << " " - << "num after: " << base::IntToString(num_after) << " " - << "Installed extensions follow:"; + VLOG(1) << "Num extensions before: " << base::IntToString(num_before) + << " num after: " << base::IntToString(num_after) + << " Installed extensions follow:"; for (size_t i = 0; i < service->extensions()->size(); ++i) - LOG(INFO) << " " << service->extensions()->at(i)->id(); + VLOG(1) << " " << (*service->extensions())[i]->id(); - LOG(INFO) << "Errors follow:"; + VLOG(1) << "Errors follow:"; const std::vector<std::string>* errors = ExtensionErrorReporter::GetInstance()->GetErrors(); for (std::vector<std::string>::const_iterator iter = errors->begin(); - iter != errors->end(); ++iter) { - LOG(INFO) << *iter; - } + iter != errors->end(); ++iter) + VLOG(1) << *iter; return false; } @@ -280,48 +289,47 @@ void ExtensionBrowserTest::Observe(NotificationType type, const NotificationDetails& details) { switch (type.value) { case NotificationType::EXTENSION_LOADED: - last_loaded_extension_id_ = Details<Extension>(details).ptr()->id(); - LOG(INFO) << "Got EXTENSION_LOADED notification."; + last_loaded_extension_id_ = Details<const Extension>(details).ptr()->id(); + VLOG(1) << "Got EXTENSION_LOADED notification."; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_UPDATE_DISABLED: - LOG(INFO) << "Got EXTENSION_UPDATE_DISABLED notification."; + VLOG(1) << "Got EXTENSION_UPDATE_DISABLED notification."; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: - LOG(INFO) << "Got EXTENSION_HOST_DID_STOP_LOADING notification."; + VLOG(1) << "Got EXTENSION_HOST_DID_STOP_LOADING notification."; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_INSTALLED: - LOG(INFO) << "Got EXTENSION_INSTALLED notification."; + VLOG(1) << "Got EXTENSION_INSTALLED notification."; ++extension_installs_observed_; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_INSTALL_ERROR: - LOG(INFO) << "Got EXTENSION_INSTALL_ERROR notification."; + VLOG(1) << "Got EXTENSION_INSTALL_ERROR notification."; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_PROCESS_CREATED: - LOG(INFO) << "Got EXTENSION_PROCESS_CREATED notification."; + VLOG(1) << "Got EXTENSION_PROCESS_CREATED notification."; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_PROCESS_TERMINATED: - LOG(INFO) << "Got EXTENSION_PROCESS_TERMINATED notification."; + VLOG(1) << "Got EXTENSION_PROCESS_TERMINATED notification."; MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED: { LocationBarTesting* location_bar = browser()->window()->GetLocationBar()->GetLocationBarForTesting(); - LOG(INFO) << "Got EXTENSION_PAGE_ACTION_COUNT_CHANGED " - << "notification. Number of page actions: " - << location_bar->PageActionCount() << ""; + VLOG(1) << "Got EXTENSION_PAGE_ACTION_COUNT_CHANGED notification. Number " + "of page actions: " << location_bar->PageActionCount(); if (location_bar->PageActionCount() == target_page_action_count_) { target_page_action_count_ = -1; @@ -333,9 +341,9 @@ void ExtensionBrowserTest::Observe(NotificationType type, case NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED: { LocationBarTesting* location_bar = browser()->window()->GetLocationBar()->GetLocationBarForTesting(); - LOG(INFO) << "Got EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED " - << "notification. Number of visible page actions: " - << location_bar->PageActionVisibleCount(); + VLOG(1) << "Got EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED notification. " + "Number of visible page actions: " + << location_bar->PageActionVisibleCount(); if (location_bar->PageActionVisibleCount() == target_visible_page_action_count_) { target_visible_page_action_count_ = -1; diff --git a/chrome/browser/extensions/extension_browsertests_misc.cc b/chrome/browser/extensions/extension_browsertests_misc.cc index 8338a52..3b1780d 100644 --- a/chrome/browser/extensions/extension_browsertests_misc.cc +++ b/chrome/browser/extensions/extension_browsertests_misc.cc @@ -260,13 +260,13 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, PageActionRefreshCrash) { ASSERT_TRUE(LoadExtension(base_path.AppendASCII("ExtA"))); ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); ASSERT_EQ(size_before + 1, service->extensions()->size()); - Extension* extensionA = service->extensions()->at(size_before); + const Extension* extensionA = service->extensions()->at(size_before); // Load extension B. ASSERT_TRUE(LoadExtension(base_path.AppendASCII("ExtB"))); ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(2)); ASSERT_EQ(size_before + 2, service->extensions()->size()); - Extension* extensionB = service->extensions()->at(size_before + 1); + const Extension* extensionB = service->extensions()->at(size_before + 1); ReloadExtension(extensionA->id()); // ExtensionA has changed, so refetch it. @@ -306,7 +306,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TitleLocalizationBrowserAction) { ASSERT_TRUE(LoadExtension(extension_path)); ASSERT_EQ(size_before + 1, service->extensions()->size()); - Extension* extension = service->extensions()->at(size_before); + const Extension* extension = service->extensions()->at(size_before); EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur: l10n browser action").c_str(), extension->description().c_str()); @@ -335,7 +335,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TitleLocalizationPageAction) { ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); ASSERT_EQ(size_before + 1, service->extensions()->size()); - Extension* extension = service->extensions()->at(size_before); + const Extension* extension = service->extensions()->at(size_before); EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur: l10n page action").c_str(), extension->description().c_str()); @@ -422,7 +422,7 @@ void NavigateToFeedAndValidate(net::TestServer* server, } ExtensionsService* service = browser->profile()->GetExtensionsService(); - Extension* extension = service->extensions()->back(); + const Extension* extension = service->extensions()->back(); std::string id = extension->id(); // Navigate to the subscribe page directly. @@ -701,7 +701,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WindowOpenNoPrivileges) { #define MAYBE_PluginLoadUnload PluginLoadUnload #elif defined(OS_LINUX) // http://crbug.com/47598 -#define MAYBE_PluginLoadUnload FLAKY_PluginLoadUnload +#define MAYBE_PluginLoadUnload DISABLED_PluginLoadUnload #else // TODO(mpcomplete): http://crbug.com/29900 need cross platform plugin support. #define MAYBE_PluginLoadUnload DISABLED_PluginLoadUnload @@ -778,7 +778,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, DISABLED_OptionsPage) { ExtensionsService* service = browser()->profile()->GetExtensionsService(); const ExtensionList* extensions = service->extensions(); ASSERT_EQ(1u, extensions->size()); - Extension* extension = extensions->at(0); + const Extension* extension = extensions->at(0); // Go to the chrome://extensions page and click the Options button. ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIExtensionsURL)); diff --git a/chrome/browser/extensions/extension_context_menu_api.cc b/chrome/browser/extensions/extension_context_menu_api.cc index 92e9ad4..a004b97 100644 --- a/chrome/browser/extensions/extension_context_menu_api.cc +++ b/chrome/browser/extensions/extension_context_menu_api.cc @@ -138,7 +138,7 @@ bool ExtensionContextMenuFunction::ParseURLPatterns( return false; URLPattern pattern(ExtensionMenuManager::kAllowedSchemes); - if (!pattern.Parse(tmp)) { + if (URLPattern::PARSE_SUCCESS != pattern.Parse(tmp)) { error_ = ExtensionErrorUtils::FormatErrorMessage(kInvalidURLPatternError, tmp); return false; @@ -174,22 +174,21 @@ bool ExtensionContextMenuFunction::SetURLPatterns( return true; } - bool ExtensionContextMenuFunction::GetParent( const DictionaryValue& properties, const ExtensionMenuManager& manager, ExtensionMenuItem** result) { if (!properties.HasKey(kParentIdKey)) return true; - ExtensionMenuItem::Id parent_id(extension_id(), 0); + ExtensionMenuItem::Id parent_id(profile(), extension_id(), 0); if (properties.HasKey(kParentIdKey) && - !properties.GetInteger(kParentIdKey, &parent_id.second)) + !properties.GetInteger(kParentIdKey, &parent_id.uid)) return false; ExtensionMenuItem* parent = manager.GetItemById(parent_id); if (!parent) { error_ = "Cannot find menu item with id " + - base::IntToString(parent_id.second); + base::IntToString(parent_id.uid); return false; } if (parent->type() != ExtensionMenuItem::NORMAL) { @@ -205,9 +204,9 @@ bool CreateContextMenuFunction::RunImpl() { EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &properties)); EXTENSION_FUNCTION_VALIDATE(properties != NULL); - ExtensionMenuItem::Id id(extension_id(), 0); + ExtensionMenuItem::Id id(profile(), extension_id(), 0); EXTENSION_FUNCTION_VALIDATE(properties->GetInteger(kGeneratedIdKey, - &id.second)); + &id.uid)); std::string title; if (properties->HasKey(kTitleKey) && !properties->GetString(kTitleKey, &title)) @@ -241,13 +240,13 @@ bool CreateContextMenuFunction::RunImpl() { bool success = true; if (properties->HasKey(kParentIdKey)) { - ExtensionMenuItem::Id parent_id(extension_id(), 0); + ExtensionMenuItem::Id parent_id(profile(), extension_id(), 0); EXTENSION_FUNCTION_VALIDATE(properties->GetInteger(kParentIdKey, - &parent_id.second)); + &parent_id.uid)); ExtensionMenuItem* parent = menu_manager->GetItemById(parent_id); if (!parent) { error_ = ExtensionErrorUtils::FormatErrorMessage( - kCannotFindItemError, base::IntToString(parent_id.second)); + kCannotFindItemError, base::IntToString(parent_id.uid)); return false; } if (parent->type() != ExtensionMenuItem::NORMAL) { @@ -266,15 +265,15 @@ bool CreateContextMenuFunction::RunImpl() { } bool UpdateContextMenuFunction::RunImpl() { - ExtensionMenuItem::Id item_id(extension_id(), 0); - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &item_id.second)); + ExtensionMenuItem::Id item_id(profile(), extension_id(), 0); + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &item_id.uid)); ExtensionsService* service = profile()->GetExtensionsService(); ExtensionMenuManager* manager = service->menu_manager(); ExtensionMenuItem* item = manager->GetItemById(item_id); if (!item || item->extension_id() != extension_id()) { error_ = ExtensionErrorUtils::FormatErrorMessage( - kCannotFindItemError, base::IntToString(item_id.second)); + kCannotFindItemError, base::IntToString(item_id.uid)); return false; } @@ -333,8 +332,8 @@ bool UpdateContextMenuFunction::RunImpl() { } bool RemoveContextMenuFunction::RunImpl() { - ExtensionMenuItem::Id id(extension_id(), 0); - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &id.second)); + ExtensionMenuItem::Id id(profile(), extension_id(), 0); + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &id.uid)); ExtensionsService* service = profile()->GetExtensionsService(); ExtensionMenuManager* manager = service->menu_manager(); @@ -342,7 +341,7 @@ bool RemoveContextMenuFunction::RunImpl() { // Ensure one extension can't remove another's menu items. if (!item || item->extension_id() != extension_id()) { error_ = ExtensionErrorUtils::FormatErrorMessage( - kCannotFindItemError, base::IntToString(id.second)); + kCannotFindItemError, base::IntToString(id.uid)); return false; } diff --git a/chrome/browser/extensions/extension_context_menu_browsertest.cc b/chrome/browser/extensions/extension_context_menu_browsertest.cc index 62221e1..da83124 100644 --- a/chrome/browser/extensions/extension_context_menu_browsertest.cc +++ b/chrome/browser/extensions/extension_context_menu_browsertest.cc @@ -3,8 +3,9 @@ // found in the LICENSE file. #include "app/menus/menu_model.h" -#include "chrome/app/chrome_dll_resource.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_test_message_listener.h" #include "chrome/browser/extensions/extensions_service.h" @@ -122,9 +123,16 @@ class ExtensionContextMenuBrowserTest : public ExtensionBrowserTest { return LoadExtension(extension_dir); } - TestRenderViewContextMenu* CreateMenu(const GURL& page_url, + bool LoadContextMenuExtensionIncognito(std::string subdirectory) { + FilePath extension_dir = + test_data_dir_.AppendASCII("context_menus").AppendASCII(subdirectory); + return LoadExtensionIncognito(extension_dir); + } + + TestRenderViewContextMenu* CreateMenu(Browser* browser, + const GURL& page_url, const GURL& link_url) { - TabContents* tab_contents = browser()->GetSelectedTabContents(); + TabContents* tab_contents = browser->GetSelectedTabContents(); WebContextMenuData data; ContextMenuParams params(data); params.page_url = page_url; @@ -142,7 +150,7 @@ class ExtensionContextMenuBrowserTest : public ExtensionBrowserTest { // Returns a pointer to the currently loaded extension with |name|, or null // if not found. - Extension* GetExtensionNamed(std::string name) { + const Extension* GetExtensionNamed(std::string name) { const ExtensionList* extensions = browser()->profile()->GetExtensionsService()->extensions(); ExtensionList::const_iterator i; @@ -173,7 +181,8 @@ class ExtensionContextMenuBrowserTest : public ExtensionBrowserTest { bool MenuHasItemWithLabel(const GURL& page_url, const GURL& link_url, const std::string& label) { - scoped_ptr<TestRenderViewContextMenu> menu(CreateMenu(page_url, link_url)); + scoped_ptr<TestRenderViewContextMenu> menu( + CreateMenu(browser(), page_url, link_url)); return menu->HasExtensionItemWithLabel(label); } }; @@ -190,7 +199,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Simple) { GURL page_url("http://www.google.com"); // Create and build our test context menu. - scoped_ptr<TestRenderViewContextMenu> menu(CreateMenu(page_url, GURL())); + scoped_ptr<TestRenderViewContextMenu> menu( + CreateMenu(browser(), page_url, GURL())); // Look for the extension item in the menu, and execute it. int command_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; @@ -249,7 +259,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, LongTitle) { // Create a context menu, then find the item's label. It should be properly // truncated. GURL url("http://foo.com/"); - scoped_ptr<TestRenderViewContextMenu> menu(CreateMenu(url, GURL())); + scoped_ptr<TestRenderViewContextMenu> menu( + CreateMenu(browser(), url, GURL())); string16 label; ASSERT_TRUE(menu->GetItemLabel(item->id(), &label)); @@ -301,7 +312,7 @@ static void VerifyMenuForSeparatorsTest(const MenuModel& menu) { IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Separators) { // Load the extension. ASSERT_TRUE(LoadContextMenuExtension("separators")); - Extension* extension = GetExtensionNamed("Separators Test"); + const Extension* extension = GetExtensionNamed("Separators Test"); ASSERT_TRUE(extension != NULL); // Navigate to test1.html inside the extension, which should create a bunch @@ -313,7 +324,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Separators) { listener1.WaitUntilSatisfied(); GURL url("http://www.google.com/"); - scoped_ptr<TestRenderViewContextMenu> menu(CreateMenu(url, GURL())); + scoped_ptr<TestRenderViewContextMenu> menu( + CreateMenu(browser(), url, GURL())); // The top-level item should be an "automagic parent" with the extension's // name. @@ -336,7 +348,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Separators) { ui_test_utils::NavigateToURL(browser(), GURL(extension->GetResourceURL("test2.html"))); listener2.WaitUntilSatisfied(); - menu.reset(CreateMenu(url, GURL())); + menu.reset(CreateMenu(browser(), url, GURL())); ASSERT_TRUE(menu->GetMenuModelAndItemIndex( IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST, &model, &index)); EXPECT_EQ(UTF8ToUTF16("parent"), model->GetLabelAt(index)); @@ -368,3 +380,50 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, TargetURLs) { non_google_url, std::string("item1"))); } + +// Tests adding a simple context menu item. +IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, IncognitoSplit) { + ExtensionTestMessageListener created("created item regular", false); + ExtensionTestMessageListener created_incognito("created item incognito", + false); + + ExtensionTestMessageListener onclick("onclick fired regular", false); + ExtensionTestMessageListener onclick_incognito("onclick fired incognito", + false); + + // Open an incognito window. + ui_test_utils::OpenURLOffTheRecord(browser()->profile(), GURL("about:blank")); + + ASSERT_TRUE(LoadContextMenuExtensionIncognito("incognito")); + + // Wait for the extension's processes to tell us they've created an item. + ASSERT_TRUE(created.WaitUntilSatisfied()); + ASSERT_TRUE(created_incognito.WaitUntilSatisfied()); + + GURL page_url("http://www.google.com"); + + // Create and build our test context menu. + Browser* browser_incognito = BrowserList::FindBrowserWithType( + browser()->profile()->GetOffTheRecordProfile(), + Browser::TYPE_NORMAL, false); + ASSERT_TRUE(browser_incognito); + scoped_ptr<TestRenderViewContextMenu> menu( + CreateMenu(browser(), page_url, GURL())); + scoped_ptr<TestRenderViewContextMenu> menu_incognito( + CreateMenu(browser_incognito, page_url, GURL())); + + // Look for the extension item in the menu, and execute it. + int command_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; + ASSERT_TRUE(menu->IsCommandIdEnabled(command_id)); + menu->ExecuteCommand(command_id); + + // Wait for the extension's script to tell us its onclick fired. Ensure + // that the incognito version doesn't fire until we explicitly click the + // incognito menu item. + ASSERT_TRUE(onclick.WaitUntilSatisfied()); + EXPECT_FALSE(onclick_incognito.was_satisfied()); + + ASSERT_TRUE(menu_incognito->IsCommandIdEnabled(command_id)); + menu_incognito->ExecuteCommand(command_id); + ASSERT_TRUE(onclick_incognito.WaitUntilSatisfied()); +} diff --git a/chrome/browser/extensions/extension_context_menu_model.cc b/chrome/browser/extensions/extension_context_menu_model.cc index 16ef321..404241e 100644 --- a/chrome/browser/extensions/extension_context_menu_model.cc +++ b/chrome/browser/extensions/extension_context_menu_model.cc @@ -28,11 +28,11 @@ enum MenuEntries { }; ExtensionContextMenuModel::ExtensionContextMenuModel( - Extension* extension, + const Extension* extension, Browser* browser, PopupDelegate* delegate) : ALLOW_THIS_IN_INITIALIZER_LIST(SimpleMenuModel(this)), - extension_(extension), + extension_id_(extension->id()), browser_(browser), profile_(browser->profile()), delegate_(delegate) { @@ -53,7 +53,13 @@ ExtensionContextMenuModel::~ExtensionContextMenuModel() { } void ExtensionContextMenuModel::InitCommonCommands() { - AddItem(NAME, UTF8ToUTF16(extension_->name())); + const Extension* extension = GetExtension(); + + // The extension pointer should only be null if the extension was uninstalled, + // and since the menu just opened, it should still be installed. + DCHECK(extension); + + AddItem(NAME, UTF8ToUTF16(extension->name())); AddSeparator(); AddItemWithStringId(CONFIGURE, IDS_EXTENSIONS_OPTIONS); AddItemWithStringId(DISABLE, IDS_EXTENSIONS_DISABLE); @@ -67,14 +73,16 @@ bool ExtensionContextMenuModel::IsCommandIdChecked(int command_id) const { } bool ExtensionContextMenuModel::IsCommandIdEnabled(int command_id) const { + const Extension* extension = this->GetExtension(); + if (!extension) + return false; + if (command_id == CONFIGURE) { - return extension_->options_url().spec().length() > 0; + return extension->options_url().spec().length() > 0; } else if (command_id == NAME) { - // The NAME links to the gallery page, which only makes sense if Google is - // hosting the extension. For other 3rd party extensions we don't have a - // homepage url, so we just disable this menu item on those cases, at least - // for now. - return extension_->GalleryUrl().is_valid(); + // The NAME links to the Homepage URL. If the extension doesn't have a + // homepage, we just disable this menu item. + return extension->GetHomepageURL().is_valid(); } else if (command_id == INSPECT_POPUP) { TabContents* contents = browser_->GetSelectedTabContents(); if (!contents) @@ -91,26 +99,30 @@ bool ExtensionContextMenuModel::GetAcceleratorForCommandId( } void ExtensionContextMenuModel::ExecuteCommand(int command_id) { + const Extension* extension = GetExtension(); + if (!extension) + return; + switch (command_id) { case NAME: { - browser_->OpenURL(extension_->GalleryUrl(), GURL(), + browser_->OpenURL(extension->GetHomepageURL(), GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); break; } case CONFIGURE: - DCHECK(!extension_->options_url().is_empty()); - profile_->GetExtensionProcessManager()->OpenOptionsPage(extension_, + DCHECK(!extension->options_url().is_empty()); + profile_->GetExtensionProcessManager()->OpenOptionsPage(extension, browser_); break; case DISABLE: { ExtensionsService* extension_service = profile_->GetExtensionsService(); - extension_service->DisableExtension(extension_->id()); + extension_service->DisableExtension(extension_id_); break; } case UNINSTALL: { AddRef(); // Balanced in InstallUIProceed and InstallUIAbort. install_ui_.reset(new ExtensionInstallUI(profile_)); - install_ui_->ConfirmUninstall(this, extension_); + install_ui_->ConfirmUninstall(this, extension); break; } case MANAGE: { @@ -129,8 +141,8 @@ void ExtensionContextMenuModel::ExecuteCommand(int command_id) { } void ExtensionContextMenuModel::InstallUIProceed() { - std::string id = extension_->id(); - profile_->GetExtensionsService()->UninstallExtension(id, false); + if (GetExtension()) + profile_->GetExtensionsService()->UninstallExtension(extension_id_, false); Release(); } @@ -138,3 +150,8 @@ void ExtensionContextMenuModel::InstallUIProceed() { void ExtensionContextMenuModel::InstallUIAbort() { Release(); } + +const Extension* ExtensionContextMenuModel::GetExtension() const { + ExtensionsService* extension_service = profile_->GetExtensionsService(); + return extension_service->GetExtensionById(extension_id_, false); +} diff --git a/chrome/browser/extensions/extension_context_menu_model.h b/chrome/browser/extensions/extension_context_menu_model.h index 35af19d..89cf3f9 100644 --- a/chrome/browser/extensions/extension_context_menu_model.h +++ b/chrome/browser/extensions/extension_context_menu_model.h @@ -37,7 +37,7 @@ class ExtensionContextMenuModel // prefs::kExtensionsUIDeveloperMode is enabled then a menu item // will be shown for "Inspect Popup" which, when selected, will cause // ShowPopupForDevToolsWindow() to be called on |delegate|. - ExtensionContextMenuModel(Extension* extension, + ExtensionContextMenuModel(const Extension* extension, Browser* browser, PopupDelegate* delegate); virtual ~ExtensionContextMenuModel(); @@ -56,8 +56,12 @@ class ExtensionContextMenuModel private: void InitCommonCommands(); - // The extension we are displaying the menu for. - Extension* extension_; + // Gets the extension we are displaying the menu for. Returns NULL if the + // extension has been uninstalled and no longer exists. + const Extension* GetExtension() const; + + // A copy of the extension's id. + std::string extension_id_; // The extension action we are displaying the menu for (or NULL). ExtensionAction* extension_action_; diff --git a/chrome/browser/extensions/extension_cookies_api.cc b/chrome/browser/extensions/extension_cookies_api.cc index b97c74d..e66c4b3 100644 --- a/chrome/browser/extensions/extension_cookies_api.cc +++ b/chrome/browser/extensions/extension_cookies_api.cc @@ -409,7 +409,7 @@ bool RemoveCookieFunction::RunImpl() { // should happen after this. bool rv = BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - new RemoveCookieTask(url, name, store_context)); + new RemoveCookieTask(url, name, make_scoped_refptr(store_context))); DCHECK(rv); return true; diff --git a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc index 32b67af..3ad8813 100644 --- a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc +++ b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc @@ -51,7 +51,8 @@ class ExtensionCrashRecoveryTest : public ExtensionBrowserTest { void CrashExtension(size_t index) { ASSERT_LT(index, GetExtensionsService()->extensions()->size()); - Extension* extension = GetExtensionsService()->extensions()->at(index); + const Extension* extension = + GetExtensionsService()->extensions()->at(index); ASSERT_TRUE(extension); std::string extension_id(extension->id()); ExtensionHost* extension_host = @@ -69,7 +70,8 @@ class ExtensionCrashRecoveryTest : public ExtensionBrowserTest { void CheckExtensionConsistency(size_t index) { ASSERT_LT(index, GetExtensionsService()->extensions()->size()); - Extension* extension = GetExtensionsService()->extensions()->at(index); + const Extension* extension = + GetExtensionsService()->extensions()->at(index); ASSERT_TRUE(extension); ExtensionHost* extension_host = GetExtensionProcessManager()->GetBackgroundHostForExtension(extension); @@ -85,7 +87,7 @@ class ExtensionCrashRecoveryTest : public ExtensionBrowserTest { const size_t size_before = GetExtensionsService()->extensions()->size(); ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("common").AppendASCII("background_page"))); - Extension* extension = GetExtensionsService()->extensions()->back(); + const Extension* extension = GetExtensionsService()->extensions()->back(); ASSERT_TRUE(extension); first_extension_id_ = extension->id(); CheckExtensionConsistency(size_before); @@ -95,7 +97,8 @@ class ExtensionCrashRecoveryTest : public ExtensionBrowserTest { int offset = GetExtensionsService()->extensions()->size(); ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("install").AppendASCII("install"))); - Extension* extension = GetExtensionsService()->extensions()->at(offset); + const Extension* extension = + GetExtensionsService()->extensions()->at(offset); ASSERT_TRUE(extension); second_extension_id_ = extension->id(); CheckExtensionConsistency(offset); diff --git a/chrome/browser/extensions/extension_creator.cc b/chrome/browser/extensions/extension_creator.cc index 86c78fe..8bd68a2 100644 --- a/chrome/browser/extensions/extension_creator.cc +++ b/chrome/browser/extensions/extension_creator.cc @@ -56,7 +56,7 @@ bool ExtensionCreator::InitializeInput( // Load the extension once. We don't really need it, but this does a lot of // useful validation of the structure. - scoped_ptr<Extension> extension( + scoped_refptr<Extension> extension( extension_file_util::LoadExtension(extension_dir, Extension::INTERNAL, false, // key not required diff --git a/chrome/browser/extensions/extension_disabled_infobar_delegate.cc b/chrome/browser/extensions/extension_disabled_infobar_delegate.cc index 6e51c47..af38e4f 100644 --- a/chrome/browser/extensions/extension_disabled_infobar_delegate.cc +++ b/chrome/browser/extensions/extension_disabled_infobar_delegate.cc @@ -26,7 +26,7 @@ class ExtensionDisabledDialogDelegate public: ExtensionDisabledDialogDelegate(Profile* profile, ExtensionsService* service, - Extension* extension) + const Extension* extension) : service_(service), extension_(extension) { AddRef(); // Balanced in Proceed or Abort. @@ -55,7 +55,7 @@ class ExtensionDisabledDialogDelegate scoped_ptr<ExtensionInstallUI> install_ui_; ExtensionsService* service_; - Extension* extension_; + const Extension* extension_; }; class ExtensionDisabledInfobarDelegate @@ -64,7 +64,7 @@ class ExtensionDisabledInfobarDelegate public: ExtensionDisabledInfobarDelegate(TabContents* tab_contents, ExtensionsService* service, - Extension* extension) + const Extension* extension) : ConfirmInfoBarDelegate(tab_contents), tab_contents_(tab_contents), service_(service), @@ -110,7 +110,7 @@ class ExtensionDisabledInfobarDelegate switch (type.value) { case NotificationType::EXTENSION_LOADED: case NotificationType::EXTENSION_UNLOADED_DISABLED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (extension == extension_) tab_contents_->RemoveInfoBar(this); break; @@ -124,11 +124,11 @@ class ExtensionDisabledInfobarDelegate NotificationRegistrar registrar_; TabContents* tab_contents_; ExtensionsService* service_; - Extension* extension_; + const Extension* extension_; }; void ShowExtensionDisabledUI(ExtensionsService* service, Profile* profile, - Extension* extension) { + const Extension* extension) { Browser* browser = BrowserList::GetLastActiveWithProfile(profile); if (!browser) return; @@ -142,7 +142,7 @@ void ShowExtensionDisabledUI(ExtensionsService* service, Profile* profile, } void ShowExtensionDisabledDialog(ExtensionsService* service, Profile* profile, - Extension* extension) { + const Extension* extension) { // This object manages its own lifetime. new ExtensionDisabledDialogDelegate(profile, service, extension); } diff --git a/chrome/browser/extensions/extension_disabled_infobar_delegate.h b/chrome/browser/extensions/extension_disabled_infobar_delegate.h index 16d3b6f..430e652 100644 --- a/chrome/browser/extensions/extension_disabled_infobar_delegate.h +++ b/chrome/browser/extensions/extension_disabled_infobar_delegate.h @@ -13,10 +13,10 @@ class Profile; // Shows UI to inform the user that an extension was disabled after upgrading // to higher permissions. void ShowExtensionDisabledUI(ExtensionsService* service, Profile* profile, - Extension* extension); + const Extension* extension); // Shows the extension install dialog. void ShowExtensionDisabledDialog(ExtensionsService* service, Profile* profile, - Extension* extension); + const Extension* extension); #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_DISABLED_INFOBAR_DELEGATE_H_ diff --git a/chrome/browser/extensions/extension_dom_ui.cc b/chrome/browser/extensions/extension_dom_ui.cc index 08f3b45..602ad6a 100644 --- a/chrome/browser/extensions/extension_dom_ui.cc +++ b/chrome/browser/extensions/extension_dom_ui.cc @@ -112,7 +112,7 @@ class ExtensionDOMUIImageLoadingTracker : public ImageLoadingTracker::Observer { ImageLoadingTracker tracker_; scoped_refptr<FaviconService::GetFaviconRequest> request_; - Extension* extension_; + const Extension* extension_; DISALLOW_COPY_AND_ASSIGN(ExtensionDOMUIImageLoadingTracker); }; @@ -122,10 +122,11 @@ class ExtensionDOMUIImageLoadingTracker : public ImageLoadingTracker::Observer { const char ExtensionDOMUI::kExtensionURLOverrides[] = "extensions.chrome_url_overrides"; -ExtensionDOMUI::ExtensionDOMUI(TabContents* tab_contents, GURL url) - : DOMUI(tab_contents) { +ExtensionDOMUI::ExtensionDOMUI(TabContents* tab_contents, const GURL& url) + : DOMUI(tab_contents), + url_(url) { ExtensionsService* service = tab_contents->profile()->GetExtensionsService(); - Extension* extension = service->GetExtensionByURL(url); + const Extension* extension = service->GetExtensionByURL(url); if (!extension) extension = service->GetExtensionByWebExtent(url); DCHECK(extension); @@ -150,12 +151,10 @@ ExtensionDOMUI::~ExtensionDOMUI() {} void ExtensionDOMUI::ResetExtensionFunctionDispatcher( RenderViewHost* render_view_host) { - // Use the NavigationController to get the URL rather than the TabContents - // since this is the real underlying URL (see HandleChromeURLOverride). - NavigationController& controller = tab_contents()->controller(); - const GURL& url = controller.GetActiveEntry()->url(); + // TODO(jcivelli): http://crbug.com/60608 we should get the URL out of the + // active entry of the navigation controller. extension_function_dispatcher_.reset( - ExtensionFunctionDispatcher::Create(render_view_host, this, url)); + ExtensionFunctionDispatcher::Create(render_view_host, this, url_)); DCHECK(extension_function_dispatcher_.get()); } @@ -195,10 +194,6 @@ TabContents* ExtensionDOMUI::associated_tab_contents() const { return tab_contents(); } -Profile* ExtensionDOMUI::GetProfile() { - return DOMUI::GetProfile(); -} - ExtensionBookmarkManagerEventRouter* ExtensionDOMUI::extension_bookmark_manager_event_router() { return extension_bookmark_manager_event_router_.get(); @@ -272,7 +267,7 @@ bool ExtensionDOMUI::HandleChromeURLOverride(GURL* url, Profile* profile) { } // Verify that the extension that's being referred to actually exists. - Extension* extension = service->GetExtensionByURL(extension_url); + const Extension* extension = service->GetExtensionByURL(extension_url); if (!extension) { // This can currently happen if you use --load-extension one run, and // then don't use it the next. It could also happen if an extension diff --git a/chrome/browser/extensions/extension_dom_ui.h b/chrome/browser/extensions/extension_dom_ui.h index 22a106c..ea59b41 100644 --- a/chrome/browser/extensions/extension_dom_ui.h +++ b/chrome/browser/extensions/extension_dom_ui.h @@ -33,7 +33,7 @@ class ExtensionDOMUI public: static const char kExtensionURLOverrides[]; - explicit ExtensionDOMUI(TabContents* tab_contents, GURL url); + explicit ExtensionDOMUI(TabContents* tab_contents, const GURL& url); virtual ~ExtensionDOMUI(); @@ -51,7 +51,6 @@ class ExtensionDOMUI virtual gfx::NativeView GetNativeViewOfHost(); virtual gfx::NativeWindow GetCustomFrameNativeWindow(); virtual TabContents* associated_tab_contents() const; - virtual Profile* GetProfile(); virtual ExtensionBookmarkManagerEventRouter* extension_bookmark_manager_event_router(); @@ -99,6 +98,9 @@ class ExtensionDOMUI // the other extension APIs? scoped_ptr<ExtensionBookmarkManagerEventRouter> extension_bookmark_manager_event_router_; + + // The URL this DOMUI was created for. + GURL url_; }; #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_DOM_UI_H_ diff --git a/chrome/browser/extensions/extension_error_reporter.h b/chrome/browser/extensions/extension_error_reporter.h index 2866211..8753c39 100644 --- a/chrome/browser/extensions/extension_error_reporter.h +++ b/chrome/browser/extensions/extension_error_reporter.h @@ -28,7 +28,7 @@ class ExtensionErrorReporter { // Get the singleton instance. static ExtensionErrorReporter* GetInstance(); - // Report an error. Errors always go to LOG(INFO). Optionally, they can also + // Report an error. Errors always go to VLOG(1). Optionally, they can also // cause a noisy alert box. This method can be called from any thread. void ReportError(const std::string& message, bool be_noisy); diff --git a/chrome/browser/extensions/extension_event_router.cc b/chrome/browser/extensions/extension_event_router.cc index 9d22da7..4fc48e3 100644 --- a/chrome/browser/extensions/extension_event_router.cc +++ b/chrome/browser/extensions/extension_event_router.cc @@ -33,17 +33,6 @@ static void DispatchEvent(RenderProcessHost* renderer, extension_id, kDispatchEvent, args, event_url)); } -static bool CanCrossIncognito(Profile* profile, - const std::string& extension_id) { - // We allow the extension to see events and data from another profile iff it - // uses "spanning" behavior and it has incognito access. "split" mode - // extensions only see events for a matching profile. - Extension* extension = - profile->GetExtensionsService()->GetExtensionById(extension_id, false); - return (profile->GetExtensionsService()->IsIncognitoEnabled(extension) && - !extension->incognito_split_mode()); -} - } // namespace struct ExtensionEventRouter::EventListener { @@ -63,6 +52,24 @@ struct ExtensionEventRouter::EventListener { } }; +// static +bool ExtensionEventRouter::CanCrossIncognito(Profile* profile, + const std::string& extension_id) { + const Extension* extension = + profile->GetExtensionsService()->GetExtensionById(extension_id, false); + return CanCrossIncognito(profile, extension); +} + +// static +bool ExtensionEventRouter::CanCrossIncognito(Profile* profile, + const Extension* extension) { + // We allow the extension to see events and data from another profile iff it + // uses "spanning" behavior and it has incognito access. "split" mode + // extensions only see events for a matching profile. + return (profile->GetExtensionsService()->IsIncognitoEnabled(extension) && + !extension->incognito_split_mode()); +} + ExtensionEventRouter::ExtensionEventRouter(Profile* profile) : profile_(profile), extension_devtools_manager_(profile->GetExtensionDevToolsManager()) { @@ -161,6 +168,7 @@ void ExtensionEventRouter::DispatchEventImpl( return; std::set<EventListener>& listeners = it->second; + ExtensionsService* service = profile_->GetExtensionsService(); // Send the event only to renderers that are listening for it. for (std::set<EventListener>::iterator listener = listeners.begin(); @@ -178,7 +186,9 @@ void ExtensionEventRouter::DispatchEventImpl( // incognito tab event sent to a normal process, or vice versa). bool cross_incognito = restrict_to_profile && listener->process->profile() != restrict_to_profile; - if (cross_incognito && !CanCrossIncognito(profile_, listener->extension_id)) + const Extension* extension = service->GetExtensionById( + listener->extension_id, false); + if (cross_incognito && !service->CanCrossIncognito(extension)) continue; DispatchEvent(listener->process, listener->extension_id, diff --git a/chrome/browser/extensions/extension_event_router.h b/chrome/browser/extensions/extension_event_router.h index 5e1ca97..c9f1f21 100644 --- a/chrome/browser/extensions/extension_event_router.h +++ b/chrome/browser/extensions/extension_event_router.h @@ -15,12 +15,19 @@ #include "chrome/common/notification_registrar.h" class GURL; +class Extension; class ExtensionDevToolsManager; class Profile; class RenderProcessHost; class ExtensionEventRouter : public NotificationObserver { public: + // Returns true if the given extension can see events and data from another + // sub-profile (incognito to original profile, or vice versa). + static bool CanCrossIncognito(Profile* profile, + const std::string& extension_id); + static bool CanCrossIncognito(Profile* profile, const Extension* extension); + explicit ExtensionEventRouter(Profile* profile); ~ExtensionEventRouter(); diff --git a/chrome/browser/extensions/extension_function.cc b/chrome/browser/extensions/extension_function.cc index f285e9d..88a6c9e 100644 --- a/chrome/browser/extensions/extension_function.cc +++ b/chrome/browser/extensions/extension_function.cc @@ -21,7 +21,7 @@ ExtensionFunction::ExtensionFunction() ExtensionFunction::~ExtensionFunction() { } -Extension* ExtensionFunction::GetExtension() { +const Extension* ExtensionFunction::GetExtension() { ExtensionsService* service = profile_->GetExtensionsService(); DCHECK(service); return service->GetExtensionById(extension_id_, false); diff --git a/chrome/browser/extensions/extension_function.h b/chrome/browser/extensions/extension_function.h index b48d0a9..57a8665 100644 --- a/chrome/browser/extensions/extension_function.h +++ b/chrome/browser/extensions/extension_function.h @@ -107,7 +107,7 @@ class ExtensionFunction : public base::RefCountedThreadSafe<ExtensionFunction> { // Gets the extension that called this function. This can return NULL for // async functions, for example if the extension is unloaded while the // function is running. - Extension* GetExtension(); + const Extension* GetExtension(); // Gets the "current" browser, if any. // diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index e3ca590..beb96e8 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -274,6 +274,7 @@ void FactoryRegistry::ResetFunctions() { // Management. RegisterFunction<GetAllExtensionsFunction>(); + RegisterFunction<GetExtensionByIdFunction>(); RegisterFunction<LaunchAppFunction>(); RegisterFunction<SetEnabledFunction>(); RegisterFunction<UninstallFunction>(); @@ -341,7 +342,7 @@ ExtensionFunctionDispatcher* ExtensionFunctionDispatcher::Create( if (!service->ExtensionBindingsAllowed(url)) return NULL; - Extension* extension = service->GetExtensionByURL(url); + const Extension* extension = service->GetExtensionByURL(url); if (!extension) extension = service->GetExtensionByWebExtent(url); @@ -355,7 +356,7 @@ ExtensionFunctionDispatcher* ExtensionFunctionDispatcher::Create( ExtensionFunctionDispatcher::ExtensionFunctionDispatcher( RenderViewHost* render_view_host, Delegate* delegate, - Extension* extension, + const Extension* extension, const GURL& url) : profile_(render_view_host->process()->profile()), render_view_host_(render_view_host), @@ -448,10 +449,9 @@ void ExtensionFunctionDispatcher::HandleRequest( function->set_user_gesture(params.user_gesture); ExtensionsService* service = profile()->GetExtensionsService(); DCHECK(service); - Extension* extension = service->GetExtensionById(extension_id(), false); + const Extension* extension = service->GetExtensionById(extension_id(), false); DCHECK(extension); - function->set_include_incognito(service->IsIncognitoEnabled(extension) && - !extension->incognito_split_mode()); + function->set_include_incognito(service->CanCrossIncognito(extension)); if (!service->ExtensionBindingsAllowed(function->source_url()) || !extension->HasApiPermission(function->name())) { diff --git a/chrome/browser/extensions/extension_function_dispatcher.h b/chrome/browser/extensions/extension_function_dispatcher.h index 99328f7..9969b1f 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.h +++ b/chrome/browser/extensions/extension_function_dispatcher.h @@ -125,7 +125,7 @@ class ExtensionFunctionDispatcher { private: ExtensionFunctionDispatcher(RenderViewHost* render_view_host, Delegate* delegate, - Extension* extension, + const Extension* extension, const GURL& url); // We need to keep a pointer to the profile because we use it in the dtor diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc index b35d58c..09d30b7 100644 --- a/chrome/browser/extensions/extension_host.cc +++ b/chrome/browser/extensions/extension_host.cc @@ -34,6 +34,7 @@ #include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/browser/renderer_host/site_instance.h" #include "chrome/browser/renderer_preferences_util.h" +#include "chrome/browser/tab_contents/popup_menu_helper_mac.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/browser/themes/browser_theme_provider.h" @@ -121,8 +122,10 @@ class ExtensionHost::ProcessCreationQueue { //////////////// // ExtensionHost -ExtensionHost::ExtensionHost(Extension* extension, SiteInstance* site_instance, - const GURL& url, ViewType::Type host_type) +ExtensionHost::ExtensionHost(const Extension* extension, + SiteInstance* site_instance, + const GURL& url, + ViewType::Type host_type) : extension_(extension), profile_(site_instance->browsing_instance()->profile()), did_stop_loading_(false), @@ -235,7 +238,8 @@ void ExtensionHost::NavigateToURL(const GURL& url) { url_ = url; - if (!is_background_page() && !extension_->GetBackgroundPageReady()) { + if (!is_background_page() && + !profile_->GetExtensionsService()->IsBackgroundPageReady(extension_)) { // Make sure the background page loads before any others. registrar_.Add(this, NotificationType::EXTENSION_BACKGROUND_PAGE_READY, Source<Extension>(extension_)); @@ -250,7 +254,8 @@ void ExtensionHost::Observe(NotificationType type, const NotificationDetails& details) { switch (type.value) { case NotificationType::EXTENSION_BACKGROUND_PAGE_READY: - DCHECK(extension_->GetBackgroundPageReady()); + DCHECK(profile_->GetExtensionsService()-> + IsBackgroundPageReady(extension_)); NavigateToURL(url_); break; case NotificationType::RENDERER_PROCESS_CREATED: @@ -264,7 +269,7 @@ void ExtensionHost::Observe(NotificationType type, // sent. NULL it out so that dirty pointer issues don't arise in cases // when multiple ExtensionHost objects pointing to the same Extension are // present. - if (extension_ == Details<Extension>(details).ptr()) + if (extension_ == Details<const Extension>(details).ptr()) extension_ = NULL; break; default: @@ -394,7 +399,7 @@ void ExtensionHost::DocumentAvailableInMainFrame(RenderViewHost* rvh) { document_element_available_ = true; if (is_background_page()) { - extension_->SetBackgroundPageReady(); + profile_->GetExtensionsService()->SetBackgroundPageReady(extension_); } else { switch (extension_host_type_) { case ViewType::EXTENSION_INFOBAR: @@ -564,33 +569,43 @@ void ExtensionHost::ShowCreatedWindow(int route_id, contents, initial_pos); browser->window()->Show(); - } else { - Browser* browser = BrowserList::FindBrowserWithType( + return; + } + + // If the tab contents isn't a popup, it's a normal tab. We need to find a + // home for it. This is typically a Browser, but it can also be some other + // TabContentsDelegate in the case of ChromeFrame. + + // First, if the creating extension view was associated with a tab contents, + // use that tab content's delegate. We must be careful here that the + // associated tab contents has the same profile as the new tab contents. In + // the case of extensions in 'spanning' incognito mode, they can mismatch. + // We don't want to end up putting a normal tab into an incognito window, or + // vice versa. + TabContents* associated_contents = associated_tab_contents(); + if (associated_contents && + associated_contents->profile() == contents->profile()) { + associated_contents->AddNewContents(contents, disposition, initial_pos, + user_gesture); + return; + } + + // If there's no associated tab contents, or it doesn't have a matching + // profile, try finding an open window. Again, we must make sure to find a + // window with the correct profile. + Browser* browser = BrowserList::FindBrowserWithType( contents->profile(), Browser::TYPE_NORMAL, false); // Match incognito exactly. - if (!browser) { - // If no browser is associated with the created TabContents, then the - // created TabContents may be an intermediate struct used during topmost - // url navigation from within an experimental extension popup view. - // - // If the ExtensionHost has an associated TabContents, then register the - // new contents with this contents. This will allow top-level link - // navigation within the new contents to function just as navigation - // within the current host. - TabContents* associated_contents = associated_tab_contents(); - if (associated_contents) { - associated_contents->AddNewContents(contents, disposition, initial_pos, - user_gesture); - } else { - browser = Browser::Create(contents->profile()); - browser->window()->Show(); - } - } - if (browser) - browser->AddTabContents(contents, disposition, initial_pos, user_gesture); + // If there's no Browser open with the right profile, create a new one. + if (!browser) { + browser = Browser::Create(contents->profile()); + browser->window()->Show(); } + + if (browser) + browser->AddTabContents(contents, disposition, initial_pos, user_gesture); } void ExtensionHost::ShowCreatedWidget(int route_id, @@ -621,6 +636,22 @@ void ExtensionHost::ShowContextMenu(const ContextMenuParams& params) { // TODO(erikkay) Show a default context menu. } +void ExtensionHost::ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned) { +#if defined(OS_MACOSX) + PopupMenuHelper popup_menu_helper(render_view_host()); + popup_menu_helper.ShowPopupMenu(bounds, item_height, item_font_size, + selected_item, items, right_aligned); +#else + // Only on Mac are select popup menus external. + NOTREACHED(); +#endif +} + void ExtensionHost::StartDragging(const WebDropData& drop_data, WebDragOperationsMask operation_mask, const SkBitmap& image, diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h index 49b8191..bb96c5d 100644 --- a/chrome/browser/extensions/extension_host.h +++ b/chrome/browser/extensions/extension_host.h @@ -6,8 +6,9 @@ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_HOST_H_ #pragma once -#include <string> #include <list> +#include <string> +#include <vector> #include "base/perftimer.h" #include "base/scoped_ptr.h" @@ -50,7 +51,7 @@ class ExtensionHost : public RenderViewHostDelegate, typedef std::list<ExtensionHost*> HostPointerList; static HostPointerList* recently_deleted(); - ExtensionHost(Extension* extension, SiteInstance* site_instance, + ExtensionHost(const Extension* extension, SiteInstance* site_instance, const GURL& url, ViewType::Type host_type); ~ExtensionHost(); @@ -72,7 +73,7 @@ class ExtensionHost : public RenderViewHostDelegate, // instantiate Browser objects. void CreateView(Browser* browser); - Extension* extension() { return extension_; } + const Extension* extension() { return extension_; } RenderViewHost* render_view_host() const { return render_view_host_; } RenderProcessHost* render_process_host() const; SiteInstance* site_instance() const; @@ -155,6 +156,12 @@ class ExtensionHost : public RenderViewHostDelegate, const gfx::Rect& initial_pos); virtual void ShowCreatedFullscreenWidget(int route_id); virtual void ShowContextMenu(const ContextMenuParams& params); + virtual void ShowPopupMenu(const gfx::Rect& bounds, + int item_height, + double item_font_size, + int selected_item, + const std::vector<WebMenuItem>& items, + bool right_aligned); virtual void StartDragging(const WebDropData& drop_data, WebKit::WebDragOperationsMask allowed_operations, const SkBitmap& image, @@ -231,7 +238,7 @@ class ExtensionHost : public RenderViewHostDelegate, bool is_background_page() const { return !view(); } // The extension that we're hosting in this view. - Extension* extension_; + const Extension* extension_; // The profile that this host is tied to. Profile* profile_; diff --git a/chrome/browser/extensions/extension_host_mac.h b/chrome/browser/extensions/extension_host_mac.h index 65aceaa..da46f16 100644 --- a/chrome/browser/extensions/extension_host_mac.h +++ b/chrome/browser/extensions/extension_host_mac.h @@ -12,7 +12,7 @@ class RenderWidgetHostView; class ExtensionHostMac : public ExtensionHost { public: - ExtensionHostMac(Extension* extension, SiteInstance* site_instance, + ExtensionHostMac(const Extension* extension, SiteInstance* site_instance, const GURL& url, ViewType::Type host_type) : ExtensionHost(extension, site_instance, url, host_type) {} virtual ~ExtensionHostMac(); diff --git a/chrome/browser/extensions/extension_host_mac.mm b/chrome/browser/extensions/extension_host_mac.mm index 3239ebd..d6de067 100644 --- a/chrome/browser/extensions/extension_host_mac.mm +++ b/chrome/browser/extensions/extension_host_mac.mm @@ -31,9 +31,6 @@ RenderWidgetHostView* ExtensionHostMac::CreateNewWidgetInternal( static_cast<RenderWidgetHostViewMac*>(widget_view); [widget_view_mac->native_view() retain]; - // |widget_view_mac| needs to know how to position itself in our view. - widget_view_mac->set_parent_view(view()->native_view()); - return widget_view; } diff --git a/chrome/browser/extensions/extension_icon_manager.cc b/chrome/browser/extensions/extension_icon_manager.cc index 53c8923..887dc3f 100644 --- a/chrome/browser/extensions/extension_icon_manager.cc +++ b/chrome/browser/extensions/extension_icon_manager.cc @@ -45,7 +45,7 @@ ExtensionIconManager::ExtensionIconManager() ExtensionIconManager::~ExtensionIconManager() { } -void ExtensionIconManager::LoadIcon(Extension* extension) { +void ExtensionIconManager::LoadIcon(const Extension* extension) { ExtensionResource icon_resource = extension->GetIconResource( Extension::EXTENSION_ICON_BITTY, ExtensionIconSet::MATCH_BIGGER); if (!icon_resource.extension_root().empty()) { diff --git a/chrome/browser/extensions/extension_icon_manager.h b/chrome/browser/extensions/extension_icon_manager.h index 876f5e0..cc0deb9 100644 --- a/chrome/browser/extensions/extension_icon_manager.h +++ b/chrome/browser/extensions/extension_icon_manager.h @@ -23,7 +23,7 @@ class ExtensionIconManager : public ImageLoadingTracker::Observer { virtual ~ExtensionIconManager(); // Start loading the icon for the given extension. - void LoadIcon(Extension* extension); + void LoadIcon(const Extension* extension); // This returns a bitmap of width/height kFavIconSize, loaded either from an // entry specified in the extension's 'icon' section of the manifest, or a diff --git a/chrome/browser/extensions/extension_icon_manager_unittest.cc b/chrome/browser/extensions/extension_icon_manager_unittest.cc index 51c76ea..37f3453 100644 --- a/chrome/browser/extensions/extension_icon_manager_unittest.cc +++ b/chrome/browser/extensions/extension_icon_manager_unittest.cc @@ -109,26 +109,26 @@ TEST_F(ExtensionIconManagerTest, LoadRemoveLoad) { static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL))); ASSERT_TRUE(manifest.get() != NULL); - Extension extension(manifest_path.DirName()); - ASSERT_TRUE(extension.InitFromValue(*manifest.get(), - false /* require_key */, - NULL /* errors */)); + scoped_refptr<Extension> extension(Extension::Create( + manifest_path.DirName(), Extension::INVALID, *manifest.get(), + false, NULL)); + ASSERT_TRUE(extension.get()); TestIconManager icon_manager(this); // Load the icon and grab the bitmap. - icon_manager.LoadIcon(&extension); + icon_manager.LoadIcon(extension.get()); WaitForImageLoad(); - SkBitmap first_icon = icon_manager.GetIcon(extension.id()); + SkBitmap first_icon = icon_manager.GetIcon(extension->id()); EXPECT_FALSE(gfx::BitmapsAreEqual(first_icon, default_icon)); // Remove the icon from the manager. - icon_manager.RemoveIcon(extension.id()); + icon_manager.RemoveIcon(extension->id()); // Now re-load the icon - we should get the same result bitmap (and not the // default icon). - icon_manager.LoadIcon(&extension); + icon_manager.LoadIcon(extension.get()); WaitForImageLoad(); - SkBitmap second_icon = icon_manager.GetIcon(extension.id()); + SkBitmap second_icon = icon_manager.GetIcon(extension->id()); EXPECT_FALSE(gfx::BitmapsAreEqual(second_icon, default_icon)); EXPECT_TRUE(gfx::BitmapsAreEqual(first_icon, second_icon)); diff --git a/chrome/browser/extensions/extension_info_map.cc b/chrome/browser/extensions/extension_info_map.cc index f9f90aa..313e573 100644 --- a/chrome/browser/extensions/extension_info_map.cc +++ b/chrome/browser/extensions/extension_info_map.cc @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/extension_info_map.h" #include "chrome/browser/browser_thread.h" +#include "chrome/common/extensions/extension.h" #include "chrome/common/url_constants.h" namespace { @@ -21,13 +22,13 @@ ExtensionInfoMap::ExtensionInfoMap() { ExtensionInfoMap::~ExtensionInfoMap() { } -void ExtensionInfoMap::AddExtension(const Extension::StaticData* data) { +void ExtensionInfoMap::AddExtension(const Extension* extension) { CheckOnValidThread(); - extension_info_[data->id] = data; + extension_info_[extension->id()] = extension; // Our map has already added a reference. Balance the reference given at the // call-site. - data->Release(); + extension->Release(); } void ExtensionInfoMap::RemoveExtension(const std::string& id) { @@ -48,7 +49,7 @@ void ExtensionInfoMap::RemoveExtension(const std::string& id) { std::string ExtensionInfoMap::GetNameForExtension(const std::string& id) const { Map::const_iterator iter = extension_info_.find(id); if (iter != extension_info_.end()) - return iter->second->name; + return iter->second->name(); else return std::string(); } @@ -56,21 +57,22 @@ std::string ExtensionInfoMap::GetNameForExtension(const std::string& id) const { FilePath ExtensionInfoMap::GetPathForExtension(const std::string& id) const { Map::const_iterator iter = extension_info_.find(id); if (iter != extension_info_.end()) - return iter->second->path; + return iter->second->path(); else return FilePath(); } bool ExtensionInfoMap::ExtensionHasWebExtent(const std::string& id) const { Map::const_iterator iter = extension_info_.find(id); - return iter != extension_info_.end() && !iter->second->extent.is_empty(); + return iter != extension_info_.end() && + !iter->second->web_extent().is_empty(); } bool ExtensionInfoMap::ExtensionCanLoadInIncognito( const std::string& id) const { Map::const_iterator iter = extension_info_.find(id); // Only split-mode extensions can load in incognito profiles. - return iter != extension_info_.end() && iter->second->incognito_split_mode; + return iter != extension_info_.end() && iter->second->incognito_split_mode(); } std::string ExtensionInfoMap::GetDefaultLocaleForExtension( @@ -78,7 +80,7 @@ std::string ExtensionInfoMap::GetDefaultLocaleForExtension( Map::const_iterator iter = extension_info_.find(id); std::string result; if (iter != extension_info_.end()) - result = iter->second->default_locale; + result = iter->second->default_locale(); return result; } @@ -88,7 +90,7 @@ ExtensionExtent ExtensionInfoMap::GetEffectiveHostPermissionsForExtension( Map::const_iterator iter = extension_info_.find(id); ExtensionExtent result; if (iter != extension_info_.end()) - result = iter->second->effective_host_permissions; + result = iter->second->GetEffectiveHostPermissions(); return result; } @@ -106,15 +108,14 @@ bool ExtensionInfoMap::CheckURLAccessToExtensionPermission( // disallowed, so only one will match. info = extension_info_.begin(); while (info != extension_info_.end() && - !info->second->extent.ContainsURL(url)) + !info->second->web_extent().ContainsURL(url)) ++info; } if (info == extension_info_.end()) return false; - const std::set<std::string>& api_permissions = info->second->api_permissions; - return api_permissions.count(permission_name) != 0; + return info->second->api_permissions().count(permission_name) != 0; } bool ExtensionInfoMap::URLIsForExtensionIcon(const GURL& url) const { @@ -127,5 +128,5 @@ bool ExtensionInfoMap::URLIsForExtensionIcon(const GURL& url) const { std::string path = url.path(); DCHECK(path.length() > 0 && path[0] == '/'); path = path.substr(1); - return iter->second->icons.ContainsPath(path); + return iter->second->icons().ContainsPath(path); } diff --git a/chrome/browser/extensions/extension_info_map.h b/chrome/browser/extensions/extension_info_map.h index fb03162..7d872ba 100644 --- a/chrome/browser/extensions/extension_info_map.h +++ b/chrome/browser/extensions/extension_info_map.h @@ -12,9 +12,11 @@ #include "base/basictypes.h" #include "base/file_path.h" #include "base/ref_counted.h" -#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_extent.h" #include "googleurl/src/gurl.h" +class Extension; + // Contains extension data that needs to be accessed on the IO thread. It can // be created/destroyed on any thread, but all other methods must be called on // the IO thread. @@ -27,7 +29,7 @@ class ExtensionInfoMap : public base::RefCountedThreadSafe<ExtensionInfoMap> { ~ExtensionInfoMap(); // Callback for when new extensions are loaded. - void AddExtension(const Extension::StaticData* data); + void AddExtension(const Extension* extension); // Callback for when an extension is unloaded. void RemoveExtension(const std::string& id); @@ -63,8 +65,7 @@ class ExtensionInfoMap : public base::RefCountedThreadSafe<ExtensionInfoMap> { private: // Map of extension info by extension id. - typedef std::map<std::string, scoped_refptr<const Extension::StaticData> > - Map; + typedef std::map<std::string, scoped_refptr<const Extension> > Map; Map extension_info_; }; diff --git a/chrome/browser/extensions/extension_info_map_unittest.cc b/chrome/browser/extensions/extension_info_map_unittest.cc index 559febb..d4892d1 100644 --- a/chrome/browser/extensions/extension_info_map_unittest.cc +++ b/chrome/browser/extensions/extension_info_map_unittest.cc @@ -7,6 +7,7 @@ #include "chrome/browser/browser_thread.h" #include "chrome/browser/extensions/extension_info_map.h" #include "chrome/common/chrome_paths.h" +#include "chrome/common/extensions/extension.h" #include "chrome/common/json_value_serializer.h" #include "testing/gtest/include/gtest/gtest.h" @@ -28,27 +29,27 @@ class ExtensionInfoMapTest : public testing::Test { }; // Returns a barebones test Extension object with the given name. -static Extension* CreateExtension(const std::string& name) { +static scoped_refptr<Extension> CreateExtension(const std::string& name) { #if defined(OS_WIN) FilePath path(FILE_PATH_LITERAL("c:\\foo")); #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - scoped_ptr<Extension> extension(new Extension(path.AppendASCII(name))); - DictionaryValue manifest; manifest.SetString(keys::kVersion, "1.0.0.0"); manifest.SetString(keys::kName, name); std::string error; - EXPECT_TRUE(extension->InitFromValue(manifest, false, &error)) << error; + scoped_refptr<Extension> extension = Extension::Create( + path.AppendASCII(name), Extension::INVALID, manifest, false, &error); + EXPECT_TRUE(extension) << error; - return extension.release(); + return extension; } -static Extension* LoadManifest(const std::string& dir, - const std::string& test_file) { +static scoped_refptr<Extension> LoadManifest(const std::string& dir, + const std::string& test_file) { FilePath path; PathService::Get(chrome::DIR_TEST_DATA, &path); path = path.AppendASCII("extensions") @@ -61,61 +62,61 @@ static Extension* LoadManifest(const std::string& dir, return NULL; std::string error; - scoped_ptr<Extension> extension(new Extension(path)); - EXPECT_TRUE(extension->InitFromValue( - *static_cast<DictionaryValue*>(result.get()), false, &error)) << error; + scoped_refptr<Extension> extension = Extension::Create( + path, Extension::INVALID, *static_cast<DictionaryValue*>(result.get()), + false, &error); + EXPECT_TRUE(extension) << error; - return extension.release(); + return extension; } // Test that the ExtensionInfoMap handles refcounting properly. TEST_F(ExtensionInfoMapTest, RefCounting) { scoped_refptr<ExtensionInfoMap> info_map(new ExtensionInfoMap()); - // New extensions should have a single reference holding onto their static - // data. - scoped_ptr<Extension> extension1(CreateExtension("extension1")); - scoped_ptr<Extension> extension2(CreateExtension("extension2")); - scoped_ptr<Extension> extension3(CreateExtension("extension3")); - EXPECT_TRUE(extension1->static_data()->HasOneRef()); - EXPECT_TRUE(extension2->static_data()->HasOneRef()); - EXPECT_TRUE(extension3->static_data()->HasOneRef()); + // New extensions should have a single reference holding onto them. + scoped_refptr<Extension> extension1(CreateExtension("extension1")); + scoped_refptr<Extension> extension2(CreateExtension("extension2")); + scoped_refptr<Extension> extension3(CreateExtension("extension3")); + EXPECT_TRUE(extension1->HasOneRef()); + EXPECT_TRUE(extension2->HasOneRef()); + EXPECT_TRUE(extension3->HasOneRef()); // Add a ref to each extension and give it to the info map. The info map // expects the caller to add a ref for it, but then assumes ownership of that // reference. - extension1->static_data()->AddRef(); - info_map->AddExtension(extension1->static_data()); - extension2->static_data()->AddRef(); - info_map->AddExtension(extension2->static_data()); - extension3->static_data()->AddRef(); - info_map->AddExtension(extension3->static_data()); - - // Delete extension1, and the info map should have the only ref. - const Extension::StaticData* data1 = extension1->static_data(); - extension1.reset(); - EXPECT_TRUE(data1->HasOneRef()); + extension1->AddRef(); + info_map->AddExtension(extension1); + extension2->AddRef(); + info_map->AddExtension(extension2); + extension3->AddRef(); + info_map->AddExtension(extension3); + + // Release extension1, and the info map should have the only ref. + const Extension* weak_extension1 = extension1; + extension1 = NULL; + EXPECT_TRUE(weak_extension1->HasOneRef()); // Remove extension2, and the extension2 object should have the only ref. info_map->RemoveExtension(extension2->id()); - EXPECT_TRUE(extension2->static_data()->HasOneRef()); + EXPECT_TRUE(extension2->HasOneRef()); // Delete the info map, and the extension3 object should have the only ref. info_map = NULL; - EXPECT_TRUE(extension3->static_data()->HasOneRef()); + EXPECT_TRUE(extension3->HasOneRef()); } // Tests that we can query a few extension properties from the ExtensionInfoMap. TEST_F(ExtensionInfoMapTest, Properties) { scoped_refptr<ExtensionInfoMap> info_map(new ExtensionInfoMap()); - scoped_ptr<Extension> extension1(CreateExtension("extension1")); - scoped_ptr<Extension> extension2(CreateExtension("extension2")); + scoped_refptr<Extension> extension1(CreateExtension("extension1")); + scoped_refptr<Extension> extension2(CreateExtension("extension2")); - extension1->static_data()->AddRef(); - info_map->AddExtension(extension1->static_data()); - extension2->static_data()->AddRef(); - info_map->AddExtension(extension2->static_data()); + extension1->AddRef(); + info_map->AddExtension(extension1); + extension2->AddRef(); + info_map->AddExtension(extension2); EXPECT_EQ(extension1->name(), info_map->GetNameForExtension(extension1->id())); @@ -132,19 +133,19 @@ TEST_F(ExtensionInfoMapTest, Properties) { TEST_F(ExtensionInfoMapTest, CheckPermissions) { scoped_refptr<ExtensionInfoMap> info_map(new ExtensionInfoMap()); - scoped_ptr<Extension> app(LoadManifest("manifest_tests", + scoped_refptr<Extension> app(LoadManifest("manifest_tests", "valid_app.json")); - scoped_ptr<Extension> extension(LoadManifest("manifest_tests", + scoped_refptr<Extension> extension(LoadManifest("manifest_tests", "tabs_extension.json")); GURL app_url("http://www.google.com/mail/foo.html"); ASSERT_TRUE(app->is_app()); ASSERT_TRUE(app->web_extent().ContainsURL(app_url)); - app->static_data()->AddRef(); - info_map->AddExtension(app->static_data()); - extension->static_data()->AddRef(); - info_map->AddExtension(extension->static_data()); + app->AddRef(); + info_map->AddExtension(app); + extension->AddRef(); + info_map->AddExtension(extension); // The app should have the notifications permission, either from a // chrome-extension URL or from its web extent. diff --git a/chrome/browser/extensions/extension_infobar_apitest.cc b/chrome/browser/extensions/extension_infobar_apitest.cc index a7afb17..5932185 100644 --- a/chrome/browser/extensions/extension_infobar_apitest.cc +++ b/chrome/browser/extensions/extension_infobar_apitest.cc @@ -6,10 +6,11 @@ #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/common/chrome_switches.h" -#if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX) +#if defined(TOOLKIT_VIEWS) #define MAYBE_Infobars Infobars #else // Need to finish port to Linux. See http://crbug.com/39916 for details. +// Temporarily marked as DISABLED on OSX too. See http://crbug.com/60990 for details. #define MAYBE_Infobars DISABLED_Infobars #endif diff --git a/chrome/browser/extensions/extension_infobar_delegate.cc b/chrome/browser/extensions/extension_infobar_delegate.cc index b3b8a39..0aea7ce 100644 --- a/chrome/browser/extensions/extension_infobar_delegate.cc +++ b/chrome/browser/extensions/extension_infobar_delegate.cc @@ -15,7 +15,7 @@ ExtensionInfoBarDelegate::ExtensionInfoBarDelegate(Browser* browser, TabContents* tab_contents, - Extension* extension, + const Extension* extension, const GURL& url) : InfoBarDelegate(tab_contents), observer_(NULL), @@ -78,7 +78,7 @@ void ExtensionInfoBarDelegate::Observe(NotificationType type, break; } case NotificationType::EXTENSION_UNLOADED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (extension_ == extension) tab_contents_->RemoveInfoBar(this); break; diff --git a/chrome/browser/extensions/extension_infobar_delegate.h b/chrome/browser/extensions/extension_infobar_delegate.h index dae7aa9..949b655 100644 --- a/chrome/browser/extensions/extension_infobar_delegate.h +++ b/chrome/browser/extensions/extension_infobar_delegate.h @@ -28,10 +28,10 @@ class ExtensionInfoBarDelegate : public InfoBarDelegate, }; ExtensionInfoBarDelegate(Browser* browser, TabContents* contents, - Extension* extension, const GURL& url); + const Extension* extension, const GURL& url); ~ExtensionInfoBarDelegate(); - Extension* extension() { return extension_; } + const Extension* extension() { return extension_; } ExtensionHost* extension_host() { return extension_host_.get(); } void set_observer(DelegateObserver* observer) { observer_ = observer; } @@ -60,7 +60,7 @@ class ExtensionInfoBarDelegate : public InfoBarDelegate, // The observer monitoring when the delegate dies. DelegateObserver* observer_; - Extension* extension_; + const Extension* extension_; TabContents* tab_contents_; diff --git a/chrome/browser/extensions/extension_infobar_module.cc b/chrome/browser/extensions/extension_infobar_module.cc index 590bfc1..6662bea 100644 --- a/chrome/browser/extensions/extension_infobar_module.cc +++ b/chrome/browser/extensions/extension_infobar_module.cc @@ -32,7 +32,7 @@ bool ShowInfoBarFunction::RunImpl() { std::string html_path; EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kHtmlPath, &html_path)); - Extension* extension = GetExtension(); + const Extension* extension = GetExtension(); GURL url = extension->GetResourceURL(extension->url(), html_path); Browser* browser = NULL; diff --git a/chrome/browser/extensions/extension_install_ui.cc b/chrome/browser/extensions/extension_install_ui.cc index b1b2205..3c5f819 100644 --- a/chrome/browser/extensions/extension_install_ui.cc +++ b/chrome/browser/extensions/extension_install_ui.cc @@ -82,7 +82,7 @@ ExtensionInstallUI::~ExtensionInstallUI() { } void ExtensionInstallUI::ConfirmInstall(Delegate* delegate, - Extension* extension) { + const Extension* extension) { DCHECK(ui_loop_ == MessageLoop::current()); extension_ = extension; delegate_ = delegate; @@ -92,7 +92,7 @@ void ExtensionInstallUI::ConfirmInstall(Delegate* delegate, // to allow the user to revert if they don't like it. if (extension->is_theme()) { // Remember the current theme in case the user pressed undo. - Extension* previous_theme = profile_->GetTheme(); + const Extension* previous_theme = profile_->GetTheme(); if (previous_theme) previous_theme_id_ = previous_theme->id(); @@ -113,7 +113,7 @@ void ExtensionInstallUI::ConfirmInstall(Delegate* delegate, } void ExtensionInstallUI::ConfirmUninstall(Delegate* delegate, - Extension* extension) { + const Extension* extension) { DCHECK(ui_loop_ == MessageLoop::current()); extension_ = extension; delegate_ = delegate; @@ -121,7 +121,7 @@ void ExtensionInstallUI::ConfirmUninstall(Delegate* delegate, ShowConfirmation(UNINSTALL_PROMPT); } -void ExtensionInstallUI::OnInstallSuccess(Extension* extension) { +void ExtensionInstallUI::OnInstallSuccess(const Extension* extension) { if (extension->is_theme()) { ShowThemeInfoBar(previous_theme_id_, previous_use_system_theme_, extension, profile_); @@ -226,7 +226,7 @@ void ExtensionInstallUI::OnImageLoaded( void ExtensionInstallUI::ShowThemeInfoBar( const std::string& previous_theme_id, bool previous_use_system_theme, - Extension* new_theme, Profile* profile) { + const Extension* new_theme, Profile* profile) { if (!new_theme->is_theme()) return; @@ -283,7 +283,7 @@ void ExtensionInstallUI::ShowConfirmation(PromptType prompt_type) { #if defined(OS_MACOSX) void ExtensionInstallUI::ShowGenericExtensionInstalledInfoBar( - Extension* new_extension) { + const Extension* new_extension) { Browser* browser = BrowserList::GetLastActiveWithProfile(profile_); if (!browser) return; @@ -304,7 +304,7 @@ void ExtensionInstallUI::ShowGenericExtensionInstalledInfoBar( #endif InfoBarDelegate* ExtensionInstallUI::GetNewThemeInstalledInfoBarDelegate( - TabContents* tab_contents, Extension* new_theme, + TabContents* tab_contents, const Extension* new_theme, const std::string& previous_theme_id, bool previous_use_system_theme) { #if defined(TOOLKIT_GTK) return new GtkThemeInstalledInfoBarDelegate(tab_contents, new_theme, diff --git a/chrome/browser/extensions/extension_install_ui.h b/chrome/browser/extensions/extension_install_ui.h index 7ab2c02..6cc8876 100644 --- a/chrome/browser/extensions/extension_install_ui.h +++ b/chrome/browser/extensions/extension_install_ui.h @@ -58,17 +58,17 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { // // We *MUST* eventually call either Proceed() or Abort() // on |delegate|. - virtual void ConfirmInstall(Delegate* delegate, Extension* extension); + virtual void ConfirmInstall(Delegate* delegate, const Extension* extension); // This is called by the extensions management page to verify whether the // uninstallation should proceed. This is declared virtual for testing. // // We *MUST* eventually call either Proceed() or Abort() // on |delegate|. - virtual void ConfirmUninstall(Delegate* delegate, Extension* extension); + virtual void ConfirmUninstall(Delegate* delegate, const Extension* extension); // Installation was successful. This is declared virtual for testing. - virtual void OnInstallSuccess(Extension* extension); + virtual void OnInstallSuccess(const Extension* extension); // Installation failed. This is declared virtual for testing. virtual void OnInstallFailure(const std::string& error); @@ -85,7 +85,7 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { // GetNewThemeInstalledInfoBarDelegate()). static void ShowThemeInfoBar( const std::string& previous_theme_id, bool previous_use_system_theme, - Extension* new_theme, Profile* profile); + const Extension* new_theme, Profile* profile); private: // Starts the process of showing a confirmation UI, which is split into two. @@ -96,25 +96,25 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { #if defined(OS_MACOSX) // When an extension is installed on Mac with neither browser action nor // page action icons, show an infobar instead of a popup bubble. - void ShowGenericExtensionInstalledInfoBar(Extension* new_extension); + void ShowGenericExtensionInstalledInfoBar(const Extension* new_extension); #endif // Returns the delegate to control the browser's info bar. This is // within its own function due to its platform-specific nature. static InfoBarDelegate* GetNewThemeInstalledInfoBarDelegate( - TabContents* tab_contents, Extension* new_theme, + TabContents* tab_contents, const Extension* new_theme, const std::string& previous_theme_id, bool previous_use_system_theme); // Implements the showing of the install/uninstall dialog prompt. // NOTE: The implementations of this function is platform-specific. static void ShowExtensionInstallUIPromptImpl( - Profile* profile, Delegate* delegate, Extension* extension, + Profile* profile, Delegate* delegate, const Extension* extension, SkBitmap* icon, PromptType type); // Implements the showing of the new install dialog. The implementations of // this function are platform-specific. static void ShowExtensionInstallUIPrompt2Impl( - Profile* profile, Delegate* delegate, Extension* extension, + Profile* profile, Delegate* delegate, const Extension* extension, SkBitmap* icon, const std::vector<string16>& permissions); Profile* profile_; @@ -125,7 +125,7 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { bool previous_use_system_theme_; SkBitmap icon_; // The extensions installation icon. - Extension* extension_; // The extension we are showing the UI for. + const Extension* extension_; // The extension we are showing the UI for. Delegate* delegate_; // The delegate we will call Proceed/Abort on after // confirmation UI. PromptType prompt_type_; // The type of prompt we are going to show. diff --git a/chrome/browser/extensions/extension_install_ui_browsertest.cc b/chrome/browser/extensions/extension_install_ui_browsertest.cc index 14a6a5b..9a5a9f2 100644 --- a/chrome/browser/extensions/extension_install_ui_browsertest.cc +++ b/chrome/browser/extensions/extension_install_ui_browsertest.cc @@ -39,7 +39,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionInstallUIBrowserTest, // Install theme once and undo to verify we go back to default theme. FilePath theme_path = test_data_dir_.AppendASCII("theme.crx"); ASSERT_TRUE(InstallExtensionWithUI(theme_path, 1)); - Extension* theme = browser()->profile()->GetTheme(); + const Extension* theme = browser()->profile()->GetTheme(); ASSERT_TRUE(theme); ASSERT_EQ(theme_crx, theme->id()); VerifyThemeInfoBarAndUndoInstall(); diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc index 50777db..44fd63f 100644 --- a/chrome/browser/extensions/extension_management_api.cc +++ b/chrome/browser/extensions/extension_management_api.cc @@ -104,10 +104,28 @@ bool GetAllExtensionsFunction::RunImpl() { return true; } +bool GetExtensionByIdFunction::RunImpl() { + std::string extension_id; + EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id)); + const Extension* extension = service()->GetExtensionById(extension_id, true); + if (!extension) { + error_ = ExtensionErrorUtils::FormatErrorMessage(kNoExtensionError, + extension_id); + return false; + } + bool enabled = service()->extension_prefs()-> + GetExtensionState(extension_id) == Extension::ENABLED; + + DictionaryValue* result = CreateExtensionInfo(*extension, enabled); + result_.reset(result); + + return true; +} + bool LaunchAppFunction::RunImpl() { std::string extension_id; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id)); - Extension* extension = service()->GetExtensionById(extension_id, true); + const Extension* extension = service()->GetExtensionById(extension_id, true); if (!extension) { error_ = ExtensionErrorUtils::FormatErrorMessage(kNoExtensionError, extension_id); @@ -219,7 +237,7 @@ void ExtensionManagementEventRouter::Observe( Details<UninstalledExtensionInfo>(details).ptr()->extension_id; args.Append(Value::CreateStringValue(extension_id)); } else { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); CHECK(extension); ExtensionsService* service = profile->GetExtensionsService(); bool enabled = service->GetExtensionById(extension->id(), false) != NULL; diff --git a/chrome/browser/extensions/extension_management_api.h b/chrome/browser/extensions/extension_management_api.h index 3176aef..864741f 100644 --- a/chrome/browser/extensions/extension_management_api.h +++ b/chrome/browser/extensions/extension_management_api.h @@ -24,6 +24,12 @@ class GetAllExtensionsFunction : public ExtensionManagementFunction { DECLARE_EXTENSION_FUNCTION_NAME("management.getAll"); }; +class GetExtensionByIdFunction : public ExtensionManagementFunction { + ~GetExtensionByIdFunction() {} + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("management.get"); +}; + class LaunchAppFunction : public ExtensionManagementFunction { ~LaunchAppFunction() {} virtual bool RunImpl(); diff --git a/chrome/browser/extensions/extension_management_browsertest.cc b/chrome/browser/extensions/extension_management_browsertest.cc index e6b1f8f..7931e91 100644 --- a/chrome/browser/extensions/extension_management_browsertest.cc +++ b/chrome/browser/extensions/extension_management_browsertest.cc @@ -25,7 +25,7 @@ class ExtensionManagementTest : public ExtensionBrowserTest { // in the extension's manifest. We use the version as reported by the // background page to test how overinstalling crx files with the same // manifest version works. - bool IsExtensionAtVersion(Extension* extension, + bool IsExtensionAtVersion(const Extension* extension, const std::string& expected_version) { // Test that the extension's version from the manifest and reported by the // background page is correct. This is to ensure that the processes are in @@ -171,7 +171,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, DisableEnable) { .AppendASCII("1.0"))); ASSERT_EQ(size_before + 1, service->extensions()->size()); EXPECT_EQ(0u, service->disabled_extensions()->size()); - Extension* extension = service->extensions()->at(size_before); + const Extension* extension = service->extensions()->at(size_before); EXPECT_TRUE(manager->GetBackgroundHostForExtension(extension)); ASSERT_TRUE(service->HasInstalledExtensions()); @@ -274,7 +274,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, ExternalUrlUpdate) { // is race-prone, because instantating the ExtensionService starts a read // of external_extensions.json before this test function starts. service->AddPendingExtensionFromExternalUpdateUrl( - kExtensionId, GURL("http://localhost/autoupdate/manifest")); + kExtensionId, GURL("http://localhost/autoupdate/manifest"), + Extension::EXTERNAL_PREF_DOWNLOAD); // Run autoupdate and make sure version 2 of the extension was installed. service->updater()->CheckNow(); diff --git a/chrome/browser/extensions/extension_menu_manager.cc b/chrome/browser/extensions/extension_menu_manager.cc index 307c339..eae74f6 100644 --- a/chrome/browser/extensions/extension_menu_manager.cc +++ b/chrome/browser/extensions/extension_menu_manager.cc @@ -125,7 +125,7 @@ const ExtensionMenuItem::List* ExtensionMenuManager::MenuItems( return NULL; } -bool ExtensionMenuManager::AddContextItem(Extension* extension, +bool ExtensionMenuManager::AddContextItem(const Extension* extension, ExtensionMenuItem* item) { const std::string& extension_id = item->extension_id(); // The item must have a non-empty extension id, and not have already been @@ -276,8 +276,10 @@ bool ExtensionMenuManager::RemoveContextMenuItem( items_by_id_.erase(*removed_iter); } - if (list.empty()) + if (list.empty()) { + context_items_.erase(extension_id); icon_manager_.RemoveIcon(extension_id); + } return result; } @@ -387,9 +389,9 @@ void ExtensionMenuManager::ExecuteCommand( ListValue args; DictionaryValue* properties = new DictionaryValue(); - properties->SetInteger("menuItemId", item->id().second); + properties->SetInteger("menuItemId", item->id().uid); if (item->parent_id()) - properties->SetInteger("parentMenuItemId", item->parent_id()->second); + properties->SetInteger("parentMenuItemId", item->parent_id()->uid); switch (params.media_type) { case WebKit::WebContextMenuData::MediaTypeImage: @@ -453,7 +455,7 @@ void ExtensionMenuManager::Observe(NotificationType type, NOTREACHED(); return; } - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (ContainsKey(context_items_, extension->id())) { RemoveAllContextItems(extension->id()); } @@ -469,3 +471,36 @@ bool ExtensionMenuManager::HasAllowedScheme(const GURL& url) { URLPattern pattern(kAllowedSchemes); return pattern.SetScheme(url.scheme()); } + +ExtensionMenuItem::Id::Id() + : profile(NULL), uid(0) { +} + +ExtensionMenuItem::Id::Id(Profile* profile, std::string extension_id, int uid) + : profile(profile), extension_id(extension_id), uid(uid) { +} + +ExtensionMenuItem::Id::~Id() { +} + +bool ExtensionMenuItem::Id::operator==(const Id& other) const { + return (profile == other.profile && + extension_id == other.extension_id && + uid == other.uid); +} + +bool ExtensionMenuItem::Id::operator!=(const Id& other) const { + return !(*this == other); +} + +bool ExtensionMenuItem::Id::operator<(const Id& other) const { + if (profile < other.profile) + return true; + if (profile == other.profile) { + if (extension_id < other.extension_id) + return true; + if (extension_id == other.extension_id) + return uid < other.uid; + } + return false; +} diff --git a/chrome/browser/extensions/extension_menu_manager.h b/chrome/browser/extensions/extension_menu_manager.h index a58c1df..c8224d1 100644 --- a/chrome/browser/extensions/extension_menu_manager.h +++ b/chrome/browser/extensions/extension_menu_manager.h @@ -33,9 +33,20 @@ class ExtensionMenuItem { // A list of ExtensionMenuItem's. typedef std::vector<ExtensionMenuItem*> List; - // An Id is a pair of |extension id|, |int| where the |int| is unique per - // extension. - typedef std::pair<std::string, int> Id; + // An Id uniquely identifies a context menu item registered by an extension. + struct Id { + Id(); + Id(Profile* profile, std::string extension_id, int uid); + ~Id(); + + bool operator==(const Id& other) const; + bool operator!=(const Id& other) const; + bool operator<(const Id& other) const; + + Profile* profile; + std::string extension_id; + int uid; + }; // For context menus, these are the contexts where an item can appear. enum Context { @@ -93,7 +104,7 @@ class ExtensionMenuItem { virtual ~ExtensionMenuItem(); // Simple accessor methods. - const std::string& extension_id() const { return id_.first; } + const std::string& extension_id() const { return id_.extension_id; } const std::string& title() const { return title_; } const List& children() { return children_; } const Id& id() const { return id_; } @@ -198,7 +209,7 @@ class ExtensionMenuManager : public NotificationObserver { // Adds a top-level menu item for an extension, requiring the |extension| // pointer so it can load the icon for the extension. Takes ownership of // |item|. Returns a boolean indicating success or failure. - bool AddContextItem(Extension* extension, ExtensionMenuItem* item); + bool AddContextItem(const Extension* extension, ExtensionMenuItem* item); // Add an item as a child of another item which has been previously added, and // takes ownership of |item|. Returns a boolean indicating success or failure. @@ -244,6 +255,7 @@ class ExtensionMenuManager : public NotificationObserver { private: FRIEND_TEST_ALL_PREFIXES(ExtensionMenuManagerTest, DeleteParent); + FRIEND_TEST_ALL_PREFIXES(ExtensionMenuManagerTest, RemoveOneByOne); // This is a helper function which takes care of de-selecting any other radio // items in the same group (i.e. that are adjacent in the list). diff --git a/chrome/browser/extensions/extension_menu_manager_unittest.cc b/chrome/browser/extensions/extension_menu_manager_unittest.cc index 0b653ba..cbd9a52 100644 --- a/chrome/browser/extensions/extension_menu_manager_unittest.cc +++ b/chrome/browser/extensions/extension_menu_manager_unittest.cc @@ -37,21 +37,21 @@ class ExtensionMenuManagerTest : public testing::Test { ExtensionMenuItem* CreateTestItem(Extension* extension) { ExtensionMenuItem::Type type = ExtensionMenuItem::NORMAL; ExtensionMenuItem::ContextList contexts(ExtensionMenuItem::ALL); - ExtensionMenuItem::Id id(extension->id(), next_id_++); + ExtensionMenuItem::Id id(NULL, extension->id(), next_id_++); return new ExtensionMenuItem(id, "test", false, type, contexts); } // Creates and returns a test Extension. The caller does *not* own the return // value. Extension* AddExtension(std::string name) { - Extension* extension = prefs_.AddExtension(name); + scoped_refptr<Extension> extension = prefs_.AddExtension(name); extensions_.push_back(extension); return extension; } protected: ExtensionMenuManager manager_; - ScopedVector<Extension> extensions_; + ExtensionList extensions_; TestExtensionPrefs prefs_; int next_id_; @@ -94,7 +94,7 @@ TEST_F(ExtensionMenuManagerTest, AddGetRemoveItems) { ASSERT_EQ(2u, manager_.MenuItems(extension_id)->size()); // Make sure removing a non-existent item returns false. - ExtensionMenuItem::Id id(extension->id(), id3.second + 50); + ExtensionMenuItem::Id id(NULL, extension->id(), id3.uid + 50); ASSERT_FALSE(manager_.RemoveContextMenuItem(id)); } @@ -211,7 +211,7 @@ TEST_F(ExtensionMenuManagerTest, DeleteParent) { // Now remove item1 and make sure item2 and item3 are gone as well. ASSERT_TRUE(manager_.RemoveContextMenuItem(item1_id)); - ASSERT_EQ(0u, manager_.MenuItems(extension->id())->size()); + ASSERT_EQ(NULL, manager_.MenuItems(extension->id())); ASSERT_EQ(0u, manager_.items_by_id_.size()); ASSERT_EQ(NULL, manager_.GetItemById(item1_id)); ASSERT_EQ(NULL, manager_.GetItemById(item2_id)); @@ -323,7 +323,7 @@ TEST_F(ExtensionMenuManagerTest, ExtensionUnloadRemovesMenuItems) { // gone. notifier->Notify(NotificationType::EXTENSION_UNLOADED, Source<Profile>(NULL), - Details<Extension>(extension1)); + Details<const Extension>(extension1)); ASSERT_EQ(NULL, manager_.MenuItems(extension1->id())); ASSERT_EQ(1u, manager_.MenuItems(extension2->id())->size()); ASSERT_TRUE(manager_.GetItemById(id1) == NULL); @@ -389,6 +389,23 @@ TEST_F(ExtensionMenuManagerTest, RemoveAll) { EXPECT_EQ(NULL, manager_.MenuItems(extension1->id())); } +// Tests that removing all items one-by-one doesn't leave an entry around. +TEST_F(ExtensionMenuManagerTest, RemoveOneByOne) { + // Add 2 test items. + Extension* extension1 = AddExtension("1111"); + ExtensionMenuItem* item1 = CreateTestItem(extension1); + ExtensionMenuItem* item2 = CreateTestItem(extension1); + ASSERT_TRUE(manager_.AddContextItem(extension1, item1)); + ASSERT_TRUE(manager_.AddContextItem(extension1, item2)); + + ASSERT_FALSE(manager_.context_items_.empty()); + + manager_.RemoveContextMenuItem(item1->id()); + manager_.RemoveContextMenuItem(item2->id()); + + ASSERT_TRUE(manager_.context_items_.empty()); +} + TEST_F(ExtensionMenuManagerTest, ExecuteCommand) { MessageLoopForUI message_loop; BrowserThread ui_thread(BrowserThread::UI, &message_loop); @@ -444,7 +461,7 @@ TEST_F(ExtensionMenuManagerTest, ExecuteCommand) { int tmp_id = 0; ASSERT_TRUE(info->GetInteger("menuItemId", &tmp_id)); - ASSERT_EQ(id.second, tmp_id); + ASSERT_EQ(id.uid, tmp_id); std::string tmp; ASSERT_TRUE(info->GetString("mediaType", &tmp)); diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc index a3a13c1..01faa1d 100644 --- a/chrome/browser/extensions/extension_message_service.cc +++ b/chrome/browser/extensions/extension_message_service.cc @@ -74,9 +74,11 @@ static void DispatchOnConnect(const ExtensionMessageService::MessagePort& port, } static void DispatchOnDisconnect( - const ExtensionMessageService::MessagePort& port, int source_port_id) { + const ExtensionMessageService::MessagePort& port, int source_port_id, + bool connection_error) { ListValue args; args.Set(0, Value::CreateIntegerValue(source_port_id)); + args.Set(1, Value::CreateBooleanValue(connection_error)); port.sender->Send(new ViewMsg_ExtensionMessageInvoke(port.routing_id, "", ExtensionMessageService::kDispatchOnDisconnect, args, GURL())); } @@ -183,7 +185,7 @@ void ExtensionMessageService::OpenChannelToTab( // The tab isn't loaded yet. Don't attempt to connect. Treat this as a // disconnect. DispatchOnDisconnect(MessagePort(source, MSG_ROUTING_CONTROL), - GET_OPPOSITE_PORT_ID(receiver_port_id)); + GET_OPPOSITE_PORT_ID(receiver_port_id), true); return; } @@ -209,14 +211,13 @@ bool ExtensionMessageService::OpenChannelImpl( const std::string& source_extension_id, const std::string& target_extension_id, const std::string& channel_name) { - // TODO(mpcomplete): notify source if receiver doesn't exist if (!source) return false; // Closed while in flight. if (!receiver.sender) { // Treat it as a disconnect. DispatchOnDisconnect(MessagePort(source, MSG_ROUTING_CONTROL), - GET_OPPOSITE_PORT_ID(receiver_port_id)); + GET_OPPOSITE_PORT_ID(receiver_port_id), true); return false; } @@ -303,7 +304,7 @@ void ExtensionMessageService::CloseChannelImpl( channel_iter->second->receiver : channel_iter->second->opener; if (notify_other_port) - DispatchOnDisconnect(port, GET_OPPOSITE_PORT_ID(closing_port_id)); + DispatchOnDisconnect(port, GET_OPPOSITE_PORT_ID(closing_port_id), false); delete channel_iter->second; channels_.erase(channel_iter); } diff --git a/chrome/browser/extensions/extension_metrics_apitest.cc b/chrome/browser/extensions/extension_metrics_apitest.cc index ee4761c..c9464d2 100644 --- a/chrome/browser/extensions/extension_metrics_apitest.cc +++ b/chrome/browser/extensions/extension_metrics_apitest.cc @@ -146,7 +146,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Metrics) { UserActionObserver observer; ASSERT_TRUE(RunExtensionTest("metrics")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension); observer.ValidateUserActions(extension, diff --git a/chrome/browser/extensions/extension_omnibox_api.cc b/chrome/browser/extensions/extension_omnibox_api.cc index 3e4887d..93b44bd 100644 --- a/chrome/browser/extensions/extension_omnibox_api.cc +++ b/chrome/browser/extensions/extension_omnibox_api.cc @@ -160,7 +160,7 @@ ExtensionOmniboxSuggestion::ExtensionOmniboxSuggestion() {} ExtensionOmniboxSuggestion::~ExtensionOmniboxSuggestion() {} -ExtensionOmniboxSuggestions::ExtensionOmniboxSuggestions() {} +ExtensionOmniboxSuggestions::ExtensionOmniboxSuggestions() : request_id(0) {} ExtensionOmniboxSuggestions::~ExtensionOmniboxSuggestions() {} diff --git a/chrome/browser/extensions/extension_omnibox_api.h b/chrome/browser/extensions/extension_omnibox_api.h index 538ce85..9d0dc99 100644 --- a/chrome/browser/extensions/extension_omnibox_api.h +++ b/chrome/browser/extensions/extension_omnibox_api.h @@ -7,7 +7,7 @@ #pragma once #include "base/string16.h" -#include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/extensions/extension_function.h" // Event router class for events related to the omnibox API. diff --git a/chrome/browser/extensions/extension_omnibox_apitest.cc b/chrome/browser/extensions/extension_omnibox_apitest.cc index f48d9a1..9f9c6a0 100644 --- a/chrome/browser/extensions/extension_omnibox_apitest.cc +++ b/chrome/browser/extensions/extension_omnibox_apitest.cc @@ -8,6 +8,7 @@ #include "chrome/browser/autocomplete/autocomplete.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" #include "chrome/browser/autocomplete/autocomplete_edit_view.h" +#include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_window.h" diff --git a/chrome/browser/extensions/extension_popup_apitest.cc b/chrome/browser/extensions/extension_popup_apitest.cc index 1bc4aed..d4afd66 100644 --- a/chrome/browser/extensions/extension_popup_apitest.cc +++ b/chrome/browser/extensions/extension_popup_apitest.cc @@ -6,8 +6,7 @@ #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/common/chrome_switches.h" -// Times out. See http://crbug.com/46601. -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_Popup) { +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Popup) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); diff --git a/chrome/browser/extensions/extension_pref_store.cc b/chrome/browser/extensions/extension_pref_store.cc index 773e94e..27719ed 100644 --- a/chrome/browser/extensions/extension_pref_store.cc +++ b/chrome/browser/extensions/extension_pref_store.cc @@ -25,7 +25,7 @@ ExtensionPrefStore::~ExtensionPrefStore() { notification_registrar_.RemoveAll(); } -void ExtensionPrefStore::InstallExtensionPref(Extension* extension, +void ExtensionPrefStore::InstallExtensionPref(const Extension* extension, const char* new_pref_path, Value* new_pref_value) { ExtensionStack::iterator i; @@ -62,7 +62,7 @@ void ExtensionPrefStore::InstallExtensionPref(Extension* extension, UpdateOnePref(new_pref_path); } -void ExtensionPrefStore::UninstallExtension(Extension* extension) { +void ExtensionPrefStore::UninstallExtension(const Extension* extension) { // Remove this extension from the stack. for (ExtensionStack::iterator i = extension_stack_.begin(); i != extension_stack_.end(); ++i) { @@ -178,7 +178,7 @@ void ExtensionPrefStore::Observe(NotificationType type, } case NotificationType::EXTENSION_UNLOADED: { Profile* extension_profile = Source<Profile>(source).ptr(); - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); // The ExtensionPrefStore for the local state watches all profiles. if (profile_ == NULL || profile_ == extension_profile) UninstallExtension(extension); @@ -190,7 +190,7 @@ void ExtensionPrefStore::Observe(NotificationType type, } } -ExtensionPrefStore::ExtensionPrefs::ExtensionPrefs(Extension* extension, +ExtensionPrefStore::ExtensionPrefs::ExtensionPrefs(const Extension* extension, PrefValueMap* values) : extension(extension), pref_values(values) {} ExtensionPrefStore::ExtensionPrefs::~ExtensionPrefs() { diff --git a/chrome/browser/extensions/extension_pref_store.h b/chrome/browser/extensions/extension_pref_store.h index 926f29e..f176758 100644 --- a/chrome/browser/extensions/extension_pref_store.h +++ b/chrome/browser/extensions/extension_pref_store.h @@ -33,32 +33,35 @@ class Value; class ExtensionPrefStore : public PrefStore, public NotificationObserver { public: + // Maps preference paths to their values. + typedef std::map<const char*, Value*> PrefValueMap; + + // The type passed as Details for an EXTENSION_PREF_CHANGED notification. + // The nested pairs are <extension, <pref_path, pref_value> >. This is here, + // rather than in (say) notification_type.h, to keep the dependency on + // std::pair out of the many places that include notification_type.h. + typedef std::pair<const Extension*, std::pair<const char*, Value*> > + ExtensionPrefDetails; + ExtensionPrefStore(Profile* profile, PrefNotifier::PrefStoreType type); virtual ~ExtensionPrefStore(); // Begins tracking the preference and value an extension wishes to set. This // must be called each time an extension API tries to set a preference. // The ExtensionPrefStore will take ownership of the |pref_value|. - virtual void InstallExtensionPref(Extension* extension, + virtual void InstallExtensionPref(const Extension* extension, const char* pref_path, Value* pref_value); // Removes an extension and all its preference settings from this PrefStore. // This must be called when an extension is uninstalled or disabled. - virtual void UninstallExtension(Extension* extension); + virtual void UninstallExtension(const Extension* extension); // PrefStore methods: - virtual DictionaryValue* prefs() { return prefs_.get(); } + virtual DictionaryValue* prefs() const { return prefs_.get(); } virtual PrefReadError ReadPrefs() { return PREF_READ_ERROR_NONE; } - // The type passed as Details for an EXTENSION_PREF_CHANGED notification. - // The nested pairs are <extension, <pref_path, pref_value> >. This is here, - // rather than in (say) notification_type.h, to keep the dependency on - // std::pair out of the many places that include notification_type.h. - typedef std::pair<Extension*, std::pair<const char*, Value*> > - ExtensionPrefDetails; - protected: // Returns a vector of the extension IDs in the extension_stack_. // This should only be accessed by subclasses for unit-testing. @@ -70,8 +73,18 @@ class ExtensionPrefStore : public PrefStore, virtual PrefService* GetPrefService(); private: - // Maps preference paths to their values. - typedef std::map<const char*, Value*> PrefValueMap; + // Associates an extension with the prefs it sets. Owns the pref values. + struct ExtensionPrefs { + ExtensionPrefs(const Extension* extension, PrefValueMap* values); + ~ExtensionPrefs(); + + const Extension* extension; + PrefValueMap* pref_values; + }; + + // A pseudo-stack of extensions and their preferences. Extensions are always + // added to the head, but may be removed from the middle. + typedef std::list<ExtensionPrefs*> ExtensionStack; // Applies the highest-priority extension's setting for the given preference // path to the |prefs_| store, or clears the setting there if no extensions @@ -93,18 +106,6 @@ class ExtensionPrefStore : public PrefStore, // extension is controlling, for quick read access. Owns the stored values. scoped_ptr<DictionaryValue> prefs_; - // Associates an extension with the prefs it sets. Owns the pref values. - struct ExtensionPrefs { - ExtensionPrefs(Extension* extension, PrefValueMap* values); - ~ExtensionPrefs(); - - Extension* extension; - PrefValueMap* pref_values; - }; - - // A pseudo-stack of extensions and their preferences. Extensions are always - // added to the head, but may be removed from the middle. - typedef std::list<ExtensionPrefs*> ExtensionStack; ExtensionStack extension_stack_; NotificationRegistrar notification_registrar_; diff --git a/chrome/browser/extensions/extension_pref_store_unittest.cc b/chrome/browser/extensions/extension_pref_store_unittest.cc index aebe5e6..90bed62 100644 --- a/chrome/browser/extensions/extension_pref_store_unittest.cc +++ b/chrome/browser/extensions/extension_pref_store_unittest.cc @@ -17,6 +17,8 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +namespace keys = extension_manifest_keys; + namespace { class TestExtensionPrefStore : public ExtensionPrefStore { @@ -32,20 +34,25 @@ class TestExtensionPrefStore : public ExtensionPrefStore { ADD_FAILURE() << "Failed to create temp dir"; return; } - DictionaryValue empty_dict; + DictionaryValue simple_dict; std::string error; - ext1_scoped_.reset(new Extension(temp_dir_.path().AppendASCII("ext1"))); - ext2_scoped_.reset(new Extension(temp_dir_.path().AppendASCII("ext2"))); - ext3_scoped_.reset(new Extension(temp_dir_.path().AppendASCII("ext3"))); + simple_dict.SetString(keys::kVersion, "1.0.0.0"); + simple_dict.SetString(keys::kName, "unused"); + + ext1_scoped_ = Extension::Create( + temp_dir_.path().AppendASCII("ext1"), Extension::INVALID, + simple_dict, false, &error); + ext2_scoped_ = Extension::Create( + temp_dir_.path().AppendASCII("ext2"), Extension::INVALID, + simple_dict, false, &error); + ext3_scoped_ = Extension::Create( + temp_dir_.path().AppendASCII("ext3"), Extension::INVALID, + simple_dict, false, &error); ext1 = ext1_scoped_.get(); ext2 = ext2_scoped_.get(); ext3 = ext3_scoped_.get(); - - EXPECT_FALSE(ext1->InitFromValue(empty_dict, false, &error)); - EXPECT_FALSE(ext2->InitFromValue(empty_dict, false, &error)); - EXPECT_FALSE(ext3->InitFromValue(empty_dict, false, &error)); } typedef std::vector<std::string> ExtensionIDs; @@ -70,9 +77,9 @@ class TestExtensionPrefStore : public ExtensionPrefStore { private: ScopedTempDir temp_dir_; - scoped_ptr<Extension> ext1_scoped_; - scoped_ptr<Extension> ext2_scoped_; - scoped_ptr<Extension> ext3_scoped_; + scoped_refptr<Extension> ext1_scoped_; + scoped_refptr<Extension> ext2_scoped_; + scoped_refptr<Extension> ext3_scoped_; // Weak reference. PrefService* pref_service_; diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc index 5ebae6a..bf80550 100644 --- a/chrome/browser/extensions/extension_prefs.cc +++ b/chrome/browser/extensions/extension_prefs.cc @@ -320,7 +320,7 @@ bool ExtensionPrefs::DidExtensionEscalatePermissions( } void ExtensionPrefs::SetDidExtensionEscalatePermissions( - Extension* extension, bool did_escalate) { + const Extension* extension, bool did_escalate) { UpdateExtensionPref(extension->id(), kExtensionDidEscalatePermissions, Value::CreateBooleanValue(did_escalate)); prefs_->ScheduleSavePersistentPrefs(); @@ -518,7 +518,7 @@ void ExtensionPrefs::SetToolbarOrder( } void ExtensionPrefs::OnExtensionInstalled( - Extension* extension, Extension::State initial_state, + const Extension* extension, Extension::State initial_state, bool initial_incognito_enabled) { const std::string& id = extension->id(); UpdateExtensionPref(id, kPrefState, @@ -575,7 +575,7 @@ Extension::State ExtensionPrefs::GetExtensionState( return static_cast<Extension::State>(state); } -void ExtensionPrefs::SetExtensionState(Extension* extension, +void ExtensionPrefs::SetExtensionState(const Extension* extension, Extension::State state) { UpdateExtensionPref(extension->id(), kPrefState, Value::CreateIntegerValue(state)); @@ -596,7 +596,7 @@ std::string ExtensionPrefs::GetVersionString(const std::string& extension_id) { return version; } -void ExtensionPrefs::UpdateManifest(Extension* extension) { +void ExtensionPrefs::UpdateManifest(const Extension* extension) { if (extension->location() != Extension::LOAD) { UpdateExtensionPref(extension->id(), kPrefManifest, extension->manifest_value()->DeepCopy()); @@ -903,5 +903,6 @@ void ExtensionPrefs::RegisterUserPrefs(PrefService* prefs) { prefs->RegisterDictionaryPref(kExtensionsBlacklistUpdate); prefs->RegisterListPref(prefs::kExtensionInstallAllowList); prefs->RegisterListPref(prefs::kExtensionInstallDenyList); + prefs->RegisterListPref(prefs::kExtensionInstallForceList); prefs->RegisterStringPref(kWebStoreLogin, std::string() /* default_value */); } diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h index 97547ea..eb16329 100644 --- a/chrome/browser/extensions/extension_prefs.h +++ b/chrome/browser/extensions/extension_prefs.h @@ -63,7 +63,7 @@ class ExtensionPrefs { void SetToolbarOrder(const std::vector<std::string>& extension_ids); // Called when an extension is installed, so that prefs get created. - void OnExtensionInstalled(Extension* extension, + void OnExtensionInstalled(const Extension* extension, Extension::State initial_state, bool initial_incognito_enabled); @@ -76,14 +76,14 @@ class ExtensionPrefs { Extension::State GetExtensionState(const std::string& extension_id); // Called to change the extension's state when it is enabled/disabled. - void SetExtensionState(Extension* extension, Extension::State); + void SetExtensionState(const Extension* extension, Extension::State); // Did the extension ask to escalate its permission during an upgrade? bool DidExtensionEscalatePermissions(const std::string& id); // If |did_escalate| is true, the preferences for |extension| will be set to // require the install warning when the user tries to enable. - void SetDidExtensionEscalatePermissions(Extension* extension, + void SetDidExtensionEscalatePermissions(const Extension* extension, bool did_escalate); // Returns the version string for the currently installed extension, or @@ -92,7 +92,7 @@ class ExtensionPrefs { // Re-writes the extension manifest into the prefs. // Called to change the extension's manifest when it's re-localized. - void UpdateManifest(Extension* extension); + void UpdateManifest(const Extension* extension); // Returns extension path based on extension ID, or empty FilePath on error. FilePath GetExtensionPath(const std::string& extension_id); diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc index 8275a99..c8d1967 100644 --- a/chrome/browser/extensions/extension_prefs_unittest.cc +++ b/chrome/browser/extensions/extension_prefs_unittest.cc @@ -114,7 +114,7 @@ TEST_F(ExtensionPrefsToolbarOrder, ToolbarOrder) {} class ExtensionPrefsExtensionState : public ExtensionPrefsTest { public: virtual void Initialize() { - extension.reset(prefs_.AddExtension("test")); + extension = prefs_.AddExtension("test"); prefs()->SetExtensionState(extension.get(), Extension::DISABLED); } @@ -123,7 +123,7 @@ class ExtensionPrefsExtensionState : public ExtensionPrefsTest { } private: - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; }; TEST_F(ExtensionPrefsExtensionState, ExtensionState) {} @@ -131,7 +131,7 @@ TEST_F(ExtensionPrefsExtensionState, ExtensionState) {} class ExtensionPrefsEscalatePermissions : public ExtensionPrefsTest { public: virtual void Initialize() { - extension.reset(prefs_.AddExtension("test")); + extension = prefs_.AddExtension("test"); prefs()->SetDidExtensionEscalatePermissions(extension.get(), true); } @@ -140,7 +140,7 @@ class ExtensionPrefsEscalatePermissions : public ExtensionPrefsTest { } private: - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; }; TEST_F(ExtensionPrefsEscalatePermissions, EscalatePermissions) {} @@ -149,7 +149,7 @@ TEST_F(ExtensionPrefsEscalatePermissions, EscalatePermissions) {} class ExtensionPrefsVersionString : public ExtensionPrefsTest { public: virtual void Initialize() { - extension.reset(prefs_.AddExtension("test")); + extension = prefs_.AddExtension("test"); EXPECT_EQ("0.1", prefs()->GetVersionString(extension->id())); prefs()->OnExtensionUninstalled(extension->id(), Extension::INTERNAL, false); @@ -160,7 +160,7 @@ class ExtensionPrefsVersionString : public ExtensionPrefsTest { } private: - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; }; TEST_F(ExtensionPrefsVersionString, VersionString) {} @@ -173,11 +173,11 @@ class ExtensionPrefsBlacklist : public ExtensionPrefsTest { // Install 5 extensions. for (int i = 0; i < 5; i++) { std::string name = "test" + base::IntToString(i); - extensions_.push_back(linked_ptr<Extension>(prefs_.AddExtension(name))); + extensions_.push_back(prefs_.AddExtension(name)); } EXPECT_EQ(NULL, prefs()->GetInstalledExtensionInfo(not_installed_id_)); - std::vector<linked_ptr<Extension> >::const_iterator iter; + ExtensionList::const_iterator iter; for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) { EXPECT_FALSE(prefs()->IsExtensionBlacklisted((*iter)->id())); } @@ -194,7 +194,7 @@ class ExtensionPrefsBlacklist : public ExtensionPrefsTest { EXPECT_TRUE(prefs()->IsExtensionBlacklisted(not_installed_id_)); // Make sure the other id's are not blacklisted. - std::vector<linked_ptr<Extension> >::const_iterator iter; + ExtensionList::const_iterator iter; for (iter = extensions_.begin() + 1; iter != extensions_.end(); ++iter) { EXPECT_FALSE(prefs()->IsExtensionBlacklisted((*iter)->id())); } @@ -212,7 +212,7 @@ class ExtensionPrefsBlacklist : public ExtensionPrefsTest { } private: - std::vector<linked_ptr<Extension> > extensions_; + ExtensionList extensions_; // An id we'll make up that doesn't match any installed extension id. std::string not_installed_id_; @@ -315,7 +315,7 @@ TEST_F(ExtensionPrefsIdleInstallInfo, IdleInstallInfo) {} class ExtensionPrefsOnExtensionInstalled : public ExtensionPrefsTest { public: virtual void Initialize() { - extension_.reset(prefs_.AddExtension("on_extension_installed")); + extension_ = prefs_.AddExtension("on_extension_installed"); EXPECT_EQ(Extension::ENABLED, prefs()->GetExtensionState(extension_->id())); EXPECT_FALSE(prefs()->IsIncognitoEnabled(extension_->id())); @@ -330,7 +330,7 @@ class ExtensionPrefsOnExtensionInstalled : public ExtensionPrefsTest { } private: - scoped_ptr<Extension> extension_; + scoped_refptr<Extension> extension_; }; TEST_F(ExtensionPrefsOnExtensionInstalled, ExtensionPrefsOnExtensionInstalled) {} @@ -341,7 +341,7 @@ public: // No extensions yet. EXPECT_EQ(0, prefs()->GetNextAppLaunchIndex()); - extension_.reset(prefs_.AddExtension("on_extension_installed")); + extension_ = prefs_.AddExtension("on_extension_installed"); EXPECT_EQ(Extension::ENABLED, prefs()->GetExtensionState(extension_->id())); prefs()->OnExtensionInstalled(extension_.get(), @@ -364,6 +364,6 @@ public: } private: - scoped_ptr<Extension> extension_; + scoped_refptr<Extension> extension_; }; TEST_F(ExtensionPrefsAppLaunchIndex, ExtensionPrefsAppLaunchIndex) {} diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc index 5a28617..c4e0799 100644 --- a/chrome/browser/extensions/extension_process_manager.cc +++ b/chrome/browser/extensions/extension_process_manager.cc @@ -32,11 +32,12 @@ class IncognitoExtensionProcessManager : public ExtensionProcessManager { public: explicit IncognitoExtensionProcessManager(Profile* profile); virtual ~IncognitoExtensionProcessManager() {} - virtual ExtensionHost* CreateView(Extension* extension, + virtual ExtensionHost* CreateView(const Extension* extension, const GURL& url, Browser* browser, ViewType::Type view_type); - virtual void CreateBackgroundHost(Extension* extension, const GURL& url); + virtual void CreateBackgroundHost(const Extension* extension, + const GURL& url); virtual SiteInstance* GetSiteInstanceForURL(const GURL& url); virtual RenderProcessHost* GetExtensionProcess(const GURL& url); @@ -48,7 +49,7 @@ class IncognitoExtensionProcessManager : public ExtensionProcessManager { // Returns the extension for an URL, which can either be a chrome-extension // URL or a web app URL. - Extension* GetExtensionOrAppByURL(const GURL& url); + const Extension* GetExtensionOrAppByURL(const GURL& url); // Returns true if the extension is allowed to run in incognito mode. bool IsIncognitoEnabled(const Extension* extension); @@ -57,7 +58,7 @@ class IncognitoExtensionProcessManager : public ExtensionProcessManager { }; static void CreateBackgroundHost( - ExtensionProcessManager* manager, Extension* extension) { + ExtensionProcessManager* manager, const Extension* extension) { // Start the process for the master page, if it exists. if (extension->background_url().is_valid()) manager->CreateBackgroundHost(extension, extension->background_url()); @@ -105,12 +106,12 @@ ExtensionProcessManager::ExtensionProcessManager(Profile* profile) } ExtensionProcessManager::~ExtensionProcessManager() { - LOG_IF(INFO, g_log_bug53991) << "~ExtensionProcessManager: " << this; + VLOG_IF(1, g_log_bug53991) << "~ExtensionProcessManager: " << this; CloseBackgroundHosts(); DCHECK(background_hosts_.empty()); } -ExtensionHost* ExtensionProcessManager::CreateView(Extension* extension, +ExtensionHost* ExtensionProcessManager::CreateView(const Extension* extension, const GURL& url, Browser* browser, ViewType::Type view_type) { @@ -137,14 +138,14 @@ ExtensionHost* ExtensionProcessManager::CreateView(const GURL& url, ExtensionsService* service = browsing_instance_->profile()->GetExtensionsService(); if (service) { - Extension* extension = service->GetExtensionByURL(url); + const Extension* extension = service->GetExtensionByURL(url); if (extension) return CreateView(extension, url, browser, view_type); } return NULL; } -ExtensionHost* ExtensionProcessManager::CreatePopup(Extension* extension, +ExtensionHost* ExtensionProcessManager::CreatePopup(const Extension* extension, const GURL& url, Browser* browser) { return CreateView(extension, url, browser, ViewType::EXTENSION_POPUP); @@ -155,9 +156,8 @@ ExtensionHost* ExtensionProcessManager::CreatePopup(const GURL& url, return CreateView(url, browser, ViewType::EXTENSION_POPUP); } -ExtensionHost* ExtensionProcessManager::CreateInfobar(Extension* extension, - const GURL& url, - Browser* browser) { +ExtensionHost* ExtensionProcessManager::CreateInfobar( + const Extension* extension, const GURL& url, Browser* browser) { return CreateView(extension, url, browser, ViewType::EXTENSION_INFOBAR); } @@ -167,7 +167,7 @@ ExtensionHost* ExtensionProcessManager::CreateInfobar(const GURL& url, } void ExtensionProcessManager::CreateBackgroundHost( - Extension* extension, const GURL& url) { + const Extension* extension, const GURL& url) { // Don't create multiple background hosts for an extension. if (GetBackgroundHostForExtension(extension)) return; @@ -185,7 +185,7 @@ void ExtensionProcessManager::CreateBackgroundHost( OnExtensionHostCreated(host, true); } -void ExtensionProcessManager::OpenOptionsPage(Extension* extension, +void ExtensionProcessManager::OpenOptionsPage(const Extension* extension, Browser* browser) { DCHECK(!extension->options_url().is_empty()); @@ -203,7 +203,7 @@ void ExtensionProcessManager::OpenOptionsPage(Extension* extension, } ExtensionHost* ExtensionProcessManager::GetBackgroundHostForExtension( - Extension* extension) { + const Extension* extension) { for (ExtensionHostSet::iterator iter = background_hosts_.begin(); iter != background_hosts_.end(); ++iter) { ExtensionHost* host = *iter; @@ -230,7 +230,7 @@ void ExtensionProcessManager::RegisterExtensionProcess( browsing_instance_->profile()->GetExtensionsService(); std::vector<std::string> page_action_ids; - Extension* extension = + const Extension* extension = extension_service->GetExtensionById(extension_id, false); if (extension->page_action()) page_action_ids.push_back(extension->page_action()->id()); @@ -254,8 +254,8 @@ RenderProcessHost* ExtensionProcessManager::GetExtensionProcess( const GURL& url) { if (!browsing_instance_->HasSiteInstance(url)) return NULL; - scoped_refptr<SiteInstance> site = - browsing_instance_->GetSiteInstanceForURL(url); + scoped_refptr<SiteInstance> site( + browsing_instance_->GetSiteInstanceForURL(url)); if (site->HasProcess()) return site->GetProcess(); return NULL; @@ -288,14 +288,14 @@ void ExtensionProcessManager::Observe(NotificationType type, ExtensionsService* service = Source<Profile>(source).ptr()->GetExtensionsService(); if (service->is_ready()) { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); ::CreateBackgroundHost(this, extension); } break; } case NotificationType::EXTENSION_UNLOADED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); for (ExtensionHostSet::iterator iter = background_hosts_.begin(); iter != background_hosts_.end(); ++iter) { ExtensionHost* host = *iter; @@ -350,7 +350,7 @@ void ExtensionProcessManager::OnExtensionHostCreated(ExtensionHost* host, } void ExtensionProcessManager::CloseBackgroundHosts() { - LOG_IF(INFO, g_log_bug53991) << "CloseBackgroundHosts: " << this; + VLOG_IF(1, g_log_bug53991) << "CloseBackgroundHosts: " << this; for (ExtensionHostSet::iterator iter = background_hosts_.begin(); iter != background_hosts_.end(); ) { ExtensionHostSet::iterator current = iter++; @@ -374,7 +374,7 @@ IncognitoExtensionProcessManager::IncognitoExtensionProcessManager( } ExtensionHost* IncognitoExtensionProcessManager::CreateView( - Extension* extension, + const Extension* extension, const GURL& url, Browser* browser, ViewType::Type view_type) { @@ -394,7 +394,7 @@ ExtensionHost* IncognitoExtensionProcessManager::CreateView( } void IncognitoExtensionProcessManager::CreateBackgroundHost( - Extension* extension, const GURL& url) { + const Extension* extension, const GURL& url) { if (extension->incognito_split_mode()) { if (IsIncognitoEnabled(extension)) ExtensionProcessManager::CreateBackgroundHost(extension, url); @@ -406,7 +406,7 @@ void IncognitoExtensionProcessManager::CreateBackgroundHost( SiteInstance* IncognitoExtensionProcessManager::GetSiteInstanceForURL( const GURL& url) { - Extension* extension = GetExtensionOrAppByURL(url); + const Extension* extension = GetExtensionOrAppByURL(url); if (!extension || extension->incognito_split_mode()) { return ExtensionProcessManager::GetSiteInstanceForURL(url); } else { @@ -416,7 +416,7 @@ SiteInstance* IncognitoExtensionProcessManager::GetSiteInstanceForURL( RenderProcessHost* IncognitoExtensionProcessManager::GetExtensionProcess( const GURL& url) { - Extension* extension = GetExtensionOrAppByURL(url); + const Extension* extension = GetExtensionOrAppByURL(url); if (!extension || extension->incognito_split_mode()) { return ExtensionProcessManager::GetExtensionProcess(url); } else { @@ -424,7 +424,7 @@ RenderProcessHost* IncognitoExtensionProcessManager::GetExtensionProcess( } } -Extension* IncognitoExtensionProcessManager::GetExtensionOrAppByURL( +const Extension* IncognitoExtensionProcessManager::GetExtensionOrAppByURL( const GURL& url) { ExtensionsService* service = browsing_instance_->profile()->GetExtensionsService(); diff --git a/chrome/browser/extensions/extension_process_manager.h b/chrome/browser/extensions/extension_process_manager.h index d284275..2d812ef 100644 --- a/chrome/browser/extensions/extension_process_manager.h +++ b/chrome/browser/extensions/extension_process_manager.h @@ -35,33 +35,34 @@ class ExtensionProcessManager : public NotificationObserver { // Creates a new ExtensionHost with its associated view, grouping it in the // appropriate SiteInstance (and therefore process) based on the URL and // profile. - virtual ExtensionHost* CreateView(Extension* extension, + virtual ExtensionHost* CreateView(const Extension* extension, const GURL& url, Browser* browser, ViewType::Type view_type); ExtensionHost* CreateView(const GURL& url, Browser* browser, ViewType::Type view_type); - ExtensionHost* CreatePopup(Extension* extension, + ExtensionHost* CreatePopup(const Extension* extension, const GURL& url, Browser* browser); ExtensionHost* CreatePopup(const GURL& url, Browser* browser); - ExtensionHost* CreateInfobar(Extension* extension, + ExtensionHost* CreateInfobar(const Extension* extension, const GURL& url, Browser* browser); ExtensionHost* CreateInfobar(const GURL& url, Browser* browser); // Open the extension's options page. - void OpenOptionsPage(Extension* extension, Browser* browser); + void OpenOptionsPage(const Extension* extension, Browser* browser); // Creates a new UI-less extension instance. Like CreateView, but not // displayed anywhere. - virtual void CreateBackgroundHost(Extension* extension, const GURL& url); + virtual void CreateBackgroundHost(const Extension* extension, + const GURL& url); // Gets the ExtensionHost for the background page for an extension, or NULL if // the extension isn't running or doesn't have a background page. - ExtensionHost* GetBackgroundHostForExtension(Extension* extension); + ExtensionHost* GetBackgroundHostForExtension(const Extension* extension); // Returns the SiteInstance that the given URL belongs to. virtual SiteInstance* GetSiteInstanceForURL(const GURL& url); diff --git a/chrome/browser/extensions/extension_protocols.cc b/chrome/browser/extensions/extension_protocols.cc index ce56589..90d198c 100644 --- a/chrome/browser/extensions/extension_protocols.cc +++ b/chrome/browser/extensions/extension_protocols.cc @@ -84,7 +84,8 @@ bool AllowExtensionResourceLoad(URLRequest* request, // chrome:// URLs are always allowed to load chrome-extension:// resources. // The app launcher in the NTP uses this feature, as does dev tools. - if (origin_url.SchemeIs(chrome::kChromeUIScheme)) + if (origin_url.SchemeIs(chrome::kChromeDevToolsScheme) || + origin_url.SchemeIs(chrome::kChromeUIScheme)) return true; // Disallow loading of packaged resources for hosted apps. We don't allow diff --git a/chrome/browser/extensions/extension_proxy_apitest.cc b/chrome/browser/extensions/extension_proxy_apitest.cc index 9c127ad..a22baf8 100644 --- a/chrome/browser/extensions/extension_proxy_apitest.cc +++ b/chrome/browser/extensions/extension_proxy_apitest.cc @@ -16,7 +16,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ProxyAutoSettings) { switches::kEnableExperimentalExtensionApis); ASSERT_TRUE(RunExtensionTest("proxy/auto")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension); PrefService* pref_service = browser()->profile()->GetPrefs(); @@ -46,7 +46,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ProxyManualSingle) { switches::kEnableExperimentalExtensionApis); ASSERT_TRUE(RunExtensionTest("proxy/single")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension); PrefService* pref_service = browser()->profile()->GetPrefs(); @@ -81,7 +81,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ProxyManualIndividual) { switches::kEnableExperimentalExtensionApis); ASSERT_TRUE(RunExtensionTest("proxy/individual")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension); PrefService* pref_service = browser()->profile()->GetPrefs(); diff --git a/chrome/browser/extensions/extension_rlz_module.cc b/chrome/browser/extensions/extension_rlz_module.cc index e31fc69..4f76bcc 100644 --- a/chrome/browser/extensions/extension_rlz_module.cc +++ b/chrome/browser/extensions/extension_rlz_module.cc @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/extension_rlz_module.h" #include "base/scoped_ptr.h" +#include "base/thread_restrictions.h" #include "base/values.h" #include "chrome/common/extensions/extension.h" #include "rlz/win/lib/lib_values.h" @@ -70,6 +71,11 @@ bool GetEventFromName(const std::string& event_name, } // namespace bool RlzRecordProductEventFunction::RunImpl() { + // This can be slow if registry access goes to disk. Should preferably + // perform registry operations on the File thread. + // http://code.google.com/p/chromium/issues/detail?id=62098 + base::ThreadRestrictions::ScopedAllowIO allow_io; + std::string product_name; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &product_name)); rlz_lib::Product product; @@ -90,6 +96,11 @@ bool RlzRecordProductEventFunction::RunImpl() { } bool RlzGetAccessPointRlzFunction::RunImpl() { + // This can be slow if registry access goes to disk. Should preferably + // perform registry operations on the File thread. + // http://code.google.com/p/chromium/issues/detail?id=62098 + base::ThreadRestrictions::ScopedAllowIO allow_io; + std::string ap_name; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &ap_name)); rlz_lib::AccessPoint access_point; @@ -103,6 +114,11 @@ bool RlzGetAccessPointRlzFunction::RunImpl() { } bool RlzSendFinancialPingFunction::RunImpl() { + // This can be slow if registry access goes to disk. Should preferably + // perform registry operations on the File thread. + // http://code.google.com/p/chromium/issues/detail?id=62098 + base::ThreadRestrictions::ScopedAllowIO allow_io; + std::string product_name; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &product_name)); rlz_lib::Product product; @@ -159,6 +175,11 @@ bool RlzSendFinancialPingFunction::RunImpl() { } bool RlzClearProductStateFunction::RunImpl() { + // This can be slow if registry access goes to disk. Should preferably + // perform registry operations on the File thread. + // http://code.google.com/p/chromium/issues/detail?id=62098 + base::ThreadRestrictions::ScopedAllowIO allow_io; + std::string product_name; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &product_name)); rlz_lib::Product product; diff --git a/chrome/browser/extensions/extension_sidebar_api.cc b/chrome/browser/extensions/extension_sidebar_api.cc index 8b6913e..7dbb6fd 100644 --- a/chrome/browser/extensions/extension_sidebar_api.cc +++ b/chrome/browser/extensions/extension_sidebar_api.cc @@ -51,7 +51,7 @@ const char kShownState[] = "shown"; } static GURL ResolvePossiblyRelativeURL(const std::string& url_string, - Extension* extension) { + const Extension* extension) { GURL url = GURL(url_string); if (!url.is_valid()) url = extension->GetResourceURL(url_string); @@ -59,7 +59,7 @@ static GURL ResolvePossiblyRelativeURL(const std::string& url_string, return url; } -static bool CanUseHost(Extension* extension, +static bool CanUseHost(const Extension* extension, const GURL& url, std::string* error) { if (extension->HasHostPermission(url)) diff --git a/chrome/browser/extensions/extension_tabs_apitest.cc b/chrome/browser/extensions/extension_tabs_apitest.cc index df108f3..b9aba66 100644 --- a/chrome/browser/extensions/extension_tabs_apitest.cc +++ b/chrome/browser/extensions/extension_tabs_apitest.cc @@ -9,21 +9,20 @@ #include "chrome/browser/profile.h" #include "chrome/common/pref_names.h" -#if defined(OS_WIN) -// This test times out on win. -// http://crbug.com/58269 -#define MAYBE_Tabs FAILS_Tabs -#else -#define MAYBE_Tabs Tabs -#endif - // Possible race in ChromeURLDataManager. http://crbug.com/59198 #if defined(OS_MACOSX) || defined(OS_LINUX) -#define MAYBE_TabOnRemoved FLAKY_TabOnRemoved +#define MAYBE_TabOnRemoved DISABLED_TabOnRemoved #else #define MAYBE_TabOnRemoved TabOnRemoved #endif +// Crashes on linux views. http://crbug.com/61592 +#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) +#define MAYBE_Tabs DISABLED_Tabs +#else +#define MAYBE_Tabs Tabs +#endif + IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tabs) { ASSERT_TRUE(test_server()->Start()); @@ -33,7 +32,28 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tabs) { browser()->profile()->GetPrefs()->SetBoolean( prefs::kHomePageIsNewTabPage, true); - ASSERT_TRUE(RunExtensionTest("tabs/basics")) << message_; + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "crud.html")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabPinned) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "pinned.html")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabMove) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "move.html")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabEvents) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "events.html")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabRelativeURLs) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "relative_urls.html")) + << message_; } IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabGetCurrent) { diff --git a/chrome/browser/extensions/extension_tabs_module.cc b/chrome/browser/extensions/extension_tabs_module.cc index 8da37de..8157f52 100644 --- a/chrome/browser/extensions/extension_tabs_module.cc +++ b/chrome/browser/extensions/extension_tabs_module.cc @@ -11,6 +11,7 @@ #include "base/utf_string_conversions.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_navigator.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/extensions/extension_function_dispatcher.h" #include "chrome/browser/extensions/extension_host.h" @@ -65,7 +66,7 @@ static bool GetTabById(int tab_id, Profile* profile, // but because the api shipped with urls resolved relative to their extension // base, we decided it wasn't worth breaking existing extensions to fix. static GURL ResolvePossiblyRelativeURL(std::string url_string, - Extension* extension); + const Extension* extension); // Return the type name for a browser window type. static std::string GetWindowTypeText(Browser::Type type); @@ -89,14 +90,10 @@ int ExtensionTabUtil::GetWindowIdOfTab(const TabContents* tab_contents) { DictionaryValue* ExtensionTabUtil::CreateTabValue( const TabContents* contents) { // Find the tab strip and index of this guy. - for (BrowserList::const_iterator it = BrowserList::begin(); - it != BrowserList::end(); ++it) { - TabStripModel* tab_strip = (*it)->tabstrip_model(); - int tab_index = tab_strip->GetIndexOfTabContents(contents); - if (tab_index != -1) { - return ExtensionTabUtil::CreateTabValue(contents, tab_strip, tab_index); - } - } + TabStripModel* tab_strip = NULL; + int tab_index; + if (ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index)) + return ExtensionTabUtil::CreateTabValue(contents, tab_strip, tab_index); // Couldn't find it. This can happen if the tab is being dragged. return ExtensionTabUtil::CreateTabValue(contents, NULL, -1); @@ -124,6 +121,8 @@ DictionaryValue* ExtensionTabUtil::CreateTabValue( result->SetString(keys::kStatusKey, GetTabStatusText(contents->is_loading())); result->SetBoolean(keys::kSelectedKey, tab_strip && tab_index == tab_strip->selected_index()); + result->SetBoolean(keys::kPinnedKey, + tab_strip && tab_strip->IsTabPinned(tab_index)); result->SetString(keys::kTitleKey, contents->GetTitle()); result->SetBoolean(keys::kIncognitoKey, contents->profile()->IsOffTheRecord()); @@ -165,6 +164,27 @@ DictionaryValue* ExtensionTabUtil::CreateWindowValue(const Browser* browser, return result; } +bool ExtensionTabUtil::GetTabStripModel(const TabContents* tab_contents, + TabStripModel** tab_strip_model, + int* tab_index) { + DCHECK(tab_contents); + DCHECK(tab_strip_model); + DCHECK(tab_index); + + for (BrowserList::const_iterator it = BrowserList::begin(); + it != BrowserList::end(); ++it) { + TabStripModel* tab_strip = (*it)->tabstrip_model(); + int index = tab_strip->GetIndexOfTabContents(tab_contents); + if (index != -1) { + *tab_strip_model = tab_strip; + *tab_index = index; + return true; + } + } + + return false; +} + bool ExtensionTabUtil::GetDefaultTab(Browser* browser, TabContents** contents, int* tab_id) { DCHECK(browser); @@ -406,11 +426,8 @@ bool CreateWindowFunction::RunImpl() { } Browser* new_window = Browser::CreateForType(window_type, window_profile); - for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) { - Browser::AddTabWithURLParams addTabParams = - Browser::AddTabWithURLParams(*i, PageTransition::LINK); - new_window->AddTabWithURL(&addTabParams); - } + for (std::vector<GURL>::iterator i = urls.begin(); i != urls.end(); ++i) + new_window->AddSelectedTabWithURL(*i, PageTransition::LINK); if (urls.size() == 0) new_window->NewTab(); new_window->SelectNumberedTab(0); @@ -600,9 +617,16 @@ bool CreateTabFunction::RunImpl() { EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kSelectedKey, &selected)); - // We can't load extension URLs into incognito windows. Special case to - // fall back to a normal window. + // Default to not pinning the tab. Setting the 'pinned' property to true + // will override this default. + bool pinned = false; + if (args->HasKey(keys::kPinnedKey)) + EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kPinnedKey, &pinned)); + + // We can't load extension URLs into incognito windows unless the extension + // uses split mode. Special case to fall back to a normal window. if (url.SchemeIs(chrome::kExtensionScheme) && + !GetExtension()->incognito_split_mode() && browser->profile()->IsOffTheRecord()) { Profile* profile = browser->profile()->GetOriginalProfile(); browser = BrowserList::FindBrowserWithType(profile, @@ -626,20 +650,25 @@ bool CreateTabFunction::RunImpl() { int add_types = selected ? TabStripModel::ADD_SELECTED : TabStripModel::ADD_NONE; add_types |= TabStripModel::ADD_FORCE_INDEX; - Browser::AddTabWithURLParams params(url, PageTransition::LINK); - params.index = index; - params.add_types = add_types; - TabContents* contents = browser->AddTabWithURL(¶ms); - index = browser->tabstrip_model()->GetIndexOfTabContents(contents); + if (pinned) + add_types |= TabStripModel::ADD_PINNED; + browser::NavigateParams params(browser, url, PageTransition::LINK); + params.disposition = selected ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; + params.tabstrip_index = index; + params.tabstrip_add_types = add_types; + browser::Navigate(¶ms); if (selected) - contents->view()->SetInitialFocus(); + params.target_contents->view()->SetInitialFocus(); // Return data about the newly created tab. - if (has_callback()) - result_.reset(ExtensionTabUtil::CreateTabValue(contents, - browser->tabstrip_model(), - index)); + if (has_callback()) { + result_.reset(ExtensionTabUtil::CreateTabValue( + params.target_contents, + params.browser->tabstrip_model(), + params.browser->tabstrip_model()->GetIndexOfTabContents( + params.target_contents))); + } return true; } @@ -705,7 +734,7 @@ bool UpdateTabFunction::RunImpl() { // JavaScript URLs can do the same kinds of things as cross-origin XHR, so // we need to check host permissions before allowing them. if (url.SchemeIs(chrome::kJavaScriptScheme)) { - Extension* extension = GetExtension(); + const Extension* extension = GetExtension(); const std::vector<URLPattern> host_permissions = extension->host_permissions(); if (!Extension::CanExecuteScriptOnPage( @@ -722,12 +751,6 @@ bool UpdateTabFunction::RunImpl() { // controller->GetURL()? } - if (tab_strip->IsTabPinned(tab_index)) { - // Don't allow changing the url of pinned tabs. - error_ = keys::kCannotUpdatePinnedTab; - return false; - } - controller.LoadURL(url, GURL(), PageTransition::LINK); // The URL of a tab contents never actually changes to a JavaScript URL, so @@ -752,6 +775,16 @@ bool UpdateTabFunction::RunImpl() { } } + bool pinned = false; + if (update_props->HasKey(keys::kPinnedKey)) { + EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(keys::kPinnedKey, + &pinned)); + tab_strip->SetTabPinned(tab_index, pinned); + + // Update the tab index because it may move when being pinned. + tab_index = tab_strip->GetIndexOfTabContents(contents); + } + if (has_callback()) result_.reset(ExtensionTabUtil::CreateTabValue(contents, tab_strip, tab_index)); @@ -942,7 +975,7 @@ bool CaptureVisibleTabFunction::CaptureSnapshotFromBackingStore( &temp_canvas)) { return false; } - LOG(INFO) << "captureVisibleTab() Got image from backing store."; + VLOG(1) << "captureVisibleTab() got image from backing store."; SendResultFromBitmap( temp_canvas.getTopPlatformDevice().accessBitmap(false)); @@ -964,7 +997,7 @@ void CaptureVisibleTabFunction::Observe(NotificationType type, error_ = keys::kInternalVisibleTabCaptureError; SendResponse(false); } else { - LOG(INFO) << "captureVisibleTab() Got image from renderer."; + VLOG(1) << "captureVisibleTab() got image from renderer."; SendResultFromBitmap(*screen_capture); } @@ -1149,7 +1182,7 @@ static std::string GetWindowTypeText(Browser::Type type) { } static GURL ResolvePossiblyRelativeURL(std::string url_string, - Extension* extension) { + const Extension* extension) { GURL url = GURL(url_string); if (!url.is_valid()) url = extension->GetResourceURL(url_string); diff --git a/chrome/browser/extensions/extension_tabs_module.h b/chrome/browser/extensions/extension_tabs_module.h index cee8a0d..9bf36cb 100644 --- a/chrome/browser/extensions/extension_tabs_module.h +++ b/chrome/browser/extensions/extension_tabs_module.h @@ -34,7 +34,10 @@ class ExtensionTabUtil { int tab_index); static DictionaryValue* CreateWindowValue(const Browser* browser, bool populate_tabs); - + // Gets the |tab_strip_model| and |tab_index| for the given |tab_contents|. + static bool GetTabStripModel(const TabContents* tab_contents, + TabStripModel** tab_strip_model, + int* tab_index); static bool GetDefaultTab(Browser* browser, TabContents** contents, int* tab_id); // Any out parameter (|browser|, |tab_strip|, |contents|, & |tab_index|) may diff --git a/chrome/browser/extensions/extension_tabs_module_constants.cc b/chrome/browser/extensions/extension_tabs_module_constants.cc index 9dc19b8..6405783 100644 --- a/chrome/browser/extensions/extension_tabs_module_constants.cc +++ b/chrome/browser/extensions/extension_tabs_module_constants.cc @@ -22,6 +22,7 @@ const char kNewPositionKey[] = "newPosition"; const char kNewWindowIdKey[] = "newWindowId"; const char kOldPositionKey[] = "oldPosition"; const char kOldWindowIdKey[] = "oldWindowId"; +const char kPinnedKey[] = "pinned"; const char kPopulateKey[] = "populate"; const char kQualityKey[] = "quality"; const char kSelectedKey[] = "selected"; @@ -68,7 +69,6 @@ const char kNoCodeOrFileToExecuteError[] = "No source code or file specified."; const char kMoreThanOneValuesError[] = "Code and file should not be specified " "at the same time in the second argument."; const char kLoadFileError[] = "Failed to load file: \"*\". "; -const char kCannotUpdatePinnedTab[] = "Cannot update pinned tabs"; const char kCannotDetermineLanguageOfUnloadedTab[] = "Cannot determine language: tab not loaded"; diff --git a/chrome/browser/extensions/extension_tabs_module_constants.h b/chrome/browser/extensions/extension_tabs_module_constants.h index 7ae35b7..89c1cd3 100644 --- a/chrome/browser/extensions/extension_tabs_module_constants.h +++ b/chrome/browser/extensions/extension_tabs_module_constants.h @@ -26,6 +26,7 @@ extern const char kNewPositionKey[]; extern const char kNewWindowIdKey[]; extern const char kOldPositionKey[]; extern const char kOldWindowIdKey[]; +extern const char kPinnedKey[]; extern const char kPopulateKey[]; extern const char kQualityKey[]; extern const char kSelectedKey[]; @@ -69,7 +70,6 @@ extern const char kSupportedInWindowsOnlyError[]; extern const char kNoCodeOrFileToExecuteError[]; extern const char kMoreThanOneValuesError[]; extern const char kLoadFileError[]; -extern const char kCannotUpdatePinnedTab[]; extern const char kCannotDetermineLanguageOfUnloadedTab[]; }; // namespace extension_tabs_module_constants diff --git a/chrome/browser/extensions/extension_test_message_listener.cc b/chrome/browser/extensions/extension_test_message_listener.cc index 423bf3b..8ba0683 100644 --- a/chrome/browser/extensions/extension_test_message_listener.cc +++ b/chrome/browser/extensions/extension_test_message_listener.cc @@ -43,8 +43,8 @@ void ExtensionTestMessageListener::Observe( const NotificationSource& source, const NotificationDetails& details) { const std::string& content = *Details<std::string>(details).ptr(); - function_ = Source<ExtensionTestSendMessageFunction>(source).ptr(); if (!satisfied_ && content == expected_message_) { + function_ = Source<ExtensionTestSendMessageFunction>(source).ptr(); satisfied_ = true; registrar_.RemoveAll(); // Stop listening for more messages. if (!will_reply_) { diff --git a/chrome/browser/extensions/extension_test_message_listener.h b/chrome/browser/extensions/extension_test_message_listener.h index bead446..86c4cf2 100644 --- a/chrome/browser/extensions/extension_test_message_listener.h +++ b/chrome/browser/extensions/extension_test_message_listener.h @@ -63,6 +63,8 @@ class ExtensionTestMessageListener : public NotificationObserver { const NotificationSource& source, const NotificationDetails& details); + bool was_satisfied() const { return satisfied_; } + private: NotificationRegistrar registrar_; diff --git a/chrome/browser/extensions/extension_toolbar_model.cc b/chrome/browser/extensions/extension_toolbar_model.cc index 086b8c4..9b36825 100644 --- a/chrome/browser/extensions/extension_toolbar_model.cc +++ b/chrome/browser/extensions/extension_toolbar_model.cc @@ -45,7 +45,7 @@ void ExtensionToolbarModel::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } -void ExtensionToolbarModel::MoveBrowserAction(Extension* extension, +void ExtensionToolbarModel::MoveBrowserAction(const Extension* extension, int index) { ExtensionList::iterator pos = std::find(begin(), end(), extension); if (pos == end()) { @@ -58,7 +58,7 @@ void ExtensionToolbarModel::MoveBrowserAction(Extension* extension, bool inserted = false; for (ExtensionList::iterator iter = begin(); iter != end(); ++iter, ++i) { if (i == index) { - toolitems_.insert(iter, extension); + toolitems_.insert(iter, make_scoped_refptr(extension)); inserted = true; break; } @@ -68,7 +68,7 @@ void ExtensionToolbarModel::MoveBrowserAction(Extension* extension, DCHECK_EQ(index, static_cast<int>(toolitems_.size())); index = toolitems_.size(); - toolitems_.push_back(extension); + toolitems_.push_back(make_scoped_refptr(extension)); } FOR_EACH_OBSERVER(Observer, observers_, BrowserActionMoved(extension, index)); @@ -93,7 +93,7 @@ void ExtensionToolbarModel::Observe(NotificationType type, if (!service_->is_ready()) return; - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (type == NotificationType::EXTENSION_LOADED) { AddExtension(extension); } else if (type == NotificationType::EXTENSION_UNLOADED || @@ -104,18 +104,19 @@ void ExtensionToolbarModel::Observe(NotificationType type, } } -void ExtensionToolbarModel::AddExtension(Extension* extension) { +void ExtensionToolbarModel::AddExtension(const Extension* extension) { // We only care about extensions with browser actions. if (!extension->browser_action()) return; if (extension->id() == last_extension_removed_ && last_extension_removed_index_ < toolitems_.size()) { - toolitems_.insert(begin() + last_extension_removed_index_, extension); + toolitems_.insert(begin() + last_extension_removed_index_, + make_scoped_refptr(extension)); FOR_EACH_OBSERVER(Observer, observers_, BrowserActionAdded(extension, last_extension_removed_index_)); } else { - toolitems_.push_back(extension); + toolitems_.push_back(make_scoped_refptr(extension)); FOR_EACH_OBSERVER(Observer, observers_, BrowserActionAdded(extension, toolitems_.size() - 1)); } @@ -126,7 +127,7 @@ void ExtensionToolbarModel::AddExtension(Extension* extension) { UpdatePrefs(); } -void ExtensionToolbarModel::RemoveExtension(Extension* extension) { +void ExtensionToolbarModel::RemoveExtension(const Extension* extension) { ExtensionList::iterator pos = std::find(begin(), end(), extension); if (pos == end()) { return; @@ -162,7 +163,7 @@ void ExtensionToolbarModel::InitializeExtensionList() { // Create the lists. for (size_t i = 0; i < service_->extensions()->size(); ++i) { - Extension* extension = service_->extensions()->at(i); + const Extension* extension = service_->extensions()->at(i); if (!extension->browser_action()) continue; @@ -172,7 +173,7 @@ void ExtensionToolbarModel::InitializeExtensionList() { int index = std::distance(pref_order.begin(), pos); sorted[index] = extension; } else { - unsorted.push_back(extension); + unsorted.push_back(make_scoped_refptr(extension)); } } @@ -208,7 +209,7 @@ void ExtensionToolbarModel::UpdatePrefs() { service_->extension_prefs()->SetToolbarOrder(ids); } -Extension* ExtensionToolbarModel::GetExtensionByIndex(int index) const { +const Extension* ExtensionToolbarModel::GetExtensionByIndex(int index) const { return toolitems_.at(index); } diff --git a/chrome/browser/extensions/extension_toolbar_model.h b/chrome/browser/extensions/extension_toolbar_model.h index a38fc8c..37c3a08 100644 --- a/chrome/browser/extensions/extension_toolbar_model.h +++ b/chrome/browser/extensions/extension_toolbar_model.h @@ -30,13 +30,13 @@ class ExtensionToolbarModel : public NotificationObserver { public: // An extension with a browser action button has been added, and should go // in the toolbar at |index|. - virtual void BrowserActionAdded(Extension* extension, int index) {} + virtual void BrowserActionAdded(const Extension* extension, int index) {} // The browser action button for |extension| should no longer show. - virtual void BrowserActionRemoved(Extension* extension) {} + virtual void BrowserActionRemoved(const Extension* extension) {} // The browser action button for |extension| has been moved to |index|. - virtual void BrowserActionMoved(Extension* extension, int index) {} + virtual void BrowserActionMoved(const Extension* extension, int index) {} // Called when the model has finished loading. virtual void ModelLoaded() {} @@ -48,7 +48,7 @@ class ExtensionToolbarModel : public NotificationObserver { // Functions called by the view. void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); - void MoveBrowserAction(Extension* extension, int index); + void MoveBrowserAction(const Extension* extension, int index); // If count == size(), this will set the visible icon count to -1, meaning // "show all actions". void SetVisibleIconCount(int count); @@ -69,7 +69,7 @@ class ExtensionToolbarModel : public NotificationObserver { return toolitems_.end(); } - Extension* GetExtensionByIndex(int index) const; + const Extension* GetExtensionByIndex(int index) const; // Utility functions for converting between an index into the list of // incognito-enabled browser actions, and the list of all browser actions. @@ -93,8 +93,8 @@ class ExtensionToolbarModel : public NotificationObserver { // Our observers. ObserverList<Observer> observers_; - void AddExtension(Extension* extension); - void RemoveExtension(Extension* extension); + void AddExtension(const Extension* extension); + void RemoveExtension(const Extension* extension); // Our ExtensionsService, guaranteed to outlive us. ExtensionsService* service_; diff --git a/chrome/browser/extensions/extension_toolbar_model_browsertest.cc b/chrome/browser/extensions/extension_toolbar_model_browsertest.cc index 031672e..45c936e 100644 --- a/chrome/browser/extensions/extension_toolbar_model_browsertest.cc +++ b/chrome/browser/extensions/extension_toolbar_model_browsertest.cc @@ -37,19 +37,19 @@ class ExtensionToolbarModelTest : public ExtensionBrowserTest, model_->RemoveObserver(this); } - virtual void BrowserActionAdded(Extension* extension, int index) { + virtual void BrowserActionAdded(const Extension* extension, int index) { inserted_count_++; } - virtual void BrowserActionRemoved(Extension* extension) { + virtual void BrowserActionRemoved(const Extension* extension) { removed_count_++; } - virtual void BrowserActionMoved(Extension* extension, int index) { + virtual void BrowserActionMoved(const Extension* extension, int index) { moved_count_++; } - Extension* ExtensionAt(int index) { + const Extension* ExtensionAt(int index) { for (ExtensionList::iterator i = model_->begin(); i < model_->end(); ++i) { if (index-- == 0) return *i; @@ -87,7 +87,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, Basic) { // We should now find our extension in the model. EXPECT_EQ(1, inserted_count_); EXPECT_EQ(1u, model_->size()); - Extension* extension = ExtensionAt(0); + const Extension* extension = ExtensionAt(0); ASSERT_TRUE(NULL != extension); EXPECT_STREQ("A browser action with no icon that makes the page red", extension->name().c_str()); @@ -96,7 +96,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, Basic) { model_->MoveBrowserAction(extension, 0); EXPECT_EQ(1, moved_count_); EXPECT_EQ(1u, model_->size()); - Extension* extension2 = ExtensionAt(0); + const Extension* extension2 = ExtensionAt(0); EXPECT_EQ(extension, extension2); UnloadExtension(extension->id()); @@ -118,7 +118,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, ReorderAndReinsert) { // First extension loaded. EXPECT_EQ(1, inserted_count_); EXPECT_EQ(1u, model_->size()); - Extension* extensionA = ExtensionAt(0); + const Extension* extensionA = ExtensionAt(0); ASSERT_TRUE(NULL != extensionA); EXPECT_STREQ("A browser action with no icon that makes the page red", extensionA->name().c_str()); @@ -132,7 +132,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, ReorderAndReinsert) { // Second extension loaded. EXPECT_EQ(2, inserted_count_); EXPECT_EQ(2u, model_->size()); - Extension* extensionB = ExtensionAt(1); + const Extension* extensionB = ExtensionAt(1); ASSERT_TRUE(NULL != extensionB); EXPECT_STREQ("Popup tester", extensionB->name().c_str()); @@ -145,7 +145,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, ReorderAndReinsert) { // Third extension loaded. EXPECT_EQ(3, inserted_count_); EXPECT_EQ(3u, model_->size()); - Extension* extensionC = ExtensionAt(2); + const Extension* extensionC = ExtensionAt(2); ASSERT_TRUE(NULL != extensionC); EXPECT_STREQ("A page action which removes a popup.", extensionC->name().c_str()); diff --git a/chrome/browser/extensions/extension_tts_api.cc b/chrome/browser/extensions/extension_tts_api.cc index b022ec0..1654b07 100644 --- a/chrome/browser/extensions/extension_tts_api.cc +++ b/chrome/browser/extensions/extension_tts_api.cc @@ -7,68 +7,206 @@ #include <string> #include "base/float_util.h" +#include "base/message_loop.h" #include "base/values.h" namespace util = extension_tts_api_util; namespace { - const char kCrosLibraryNotLoadedError[] = - "Cros shared library not loaded."; +const char kCrosLibraryNotLoadedError[] = + "Cros shared library not loaded."; +const int kSpeechCheckDelayIntervalMs = 100; }; +// static +ExtensionTtsController* ExtensionTtsController::GetInstance() { + return Singleton<ExtensionTtsController>::get(); +} + +ExtensionTtsController::ExtensionTtsController() + : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), + current_utterance_(NULL), + platform_impl_(NULL) { +} + +void ExtensionTtsController::SpeakOrEnqueue( + Utterance* utterance, bool can_enqueue) { + if (IsSpeaking() && can_enqueue) { + utterance_queue_.push(utterance); + } else { + Stop(); + SpeakNow(utterance); + } +} + +void ExtensionTtsController::SpeakNow(Utterance* utterance) { + GetPlatformImpl()->clear_error(); + bool success = GetPlatformImpl()->Speak( + utterance->text, + utterance->language, + utterance->gender, + utterance->rate, + utterance->pitch, + utterance->volume); + if (!success) { + utterance->error = GetPlatformImpl()->error(); + utterance->failure_task->Run(); + delete utterance->success_task; + delete utterance; + return; + } + current_utterance_ = utterance; + + // Post a task to check if this utterance has completed after a delay. + MessageLoop::current()->PostDelayedTask( + FROM_HERE, method_factory_.NewRunnableMethod( + &ExtensionTtsController::CheckSpeechStatus), + kSpeechCheckDelayIntervalMs); +} + +void ExtensionTtsController::Stop() { + GetPlatformImpl()->clear_error(); + GetPlatformImpl()->StopSpeaking(); + + FinishCurrentUtterance(); + ClearUtteranceQueue(); +} + +bool ExtensionTtsController::IsSpeaking() const { + return current_utterance_ != NULL; +} + +void ExtensionTtsController::FinishCurrentUtterance() { + if (current_utterance_) { + current_utterance_->success_task->Run(); + delete current_utterance_->failure_task; + delete current_utterance_; + current_utterance_ = NULL; + } +} + +void ExtensionTtsController::ClearUtteranceQueue() { + while (!utterance_queue_.empty()) { + Utterance* utterance = utterance_queue_.front(); + utterance_queue_.pop(); + utterance->success_task->Run(); + delete utterance->failure_task; + delete utterance; + } +} + +void ExtensionTtsController::CheckSpeechStatus() { + if (!current_utterance_) + return; + + if (GetPlatformImpl()->IsSpeaking() == false) { + FinishCurrentUtterance(); + + // Start speaking the next utterance in the queue. Keep trying in case + // one fails but there are still more in the queue to try. + while (!utterance_queue_.empty() && !current_utterance_) { + Utterance* utterance = utterance_queue_.front(); + utterance_queue_.pop(); + SpeakNow(utterance); + } + } + + // If we're still speaking something (either the prevoius utterance or + // a new utterance), keep calling this method after another delay. + if (current_utterance_) { + MessageLoop::current()->PostDelayedTask( + FROM_HERE, method_factory_.NewRunnableMethod( + &ExtensionTtsController::CheckSpeechStatus), + kSpeechCheckDelayIntervalMs); + } +} + +void ExtensionTtsController::SetPlatformImpl( + ExtensionTtsPlatformImpl* platform_impl) { + platform_impl_ = platform_impl; +} + +ExtensionTtsPlatformImpl* ExtensionTtsController::GetPlatformImpl() { + if (!platform_impl_) + platform_impl_ = ExtensionTtsPlatformImpl::GetInstance(); + return platform_impl_; +} + +// +// Extension API functions +// + bool ExtensionTtsSpeakFunction::RunImpl() { - std::string utterance; - std::string language; - std::string gender; - double rate = -1.0; - double pitch = -1.0; - double volume = -1.0; + utterance_ = new ExtensionTtsController::Utterance(); + bool can_enqueue = false; DictionaryValue* speak_options = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &utterance)); + EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &utterance_->text)); if (args_->GetDictionary(1, &speak_options)) { if (speak_options->HasKey(util::kLanguageNameKey)) { - speak_options->GetString(util::kLanguageNameKey, &language); + speak_options->GetString(util::kLanguageNameKey, &utterance_->language); } if (speak_options->HasKey(util::kGenderKey)) { - speak_options->GetString(util::kGenderKey, &gender); + speak_options->GetString(util::kGenderKey, &utterance_->gender); + } + + if (speak_options->HasKey(util::kEnqueueKey)) { + speak_options->GetBoolean(util::kEnqueueKey, &can_enqueue); } - if (util::ReadNumberByKey(speak_options, util::kRateKey, &rate)) { - if (!base::IsFinite(rate) || rate < 0.0 || rate > 1.0) { - rate = -1.0; + if (util::ReadNumberByKey( + speak_options, util::kRateKey, &utterance_->rate)) { + if (!base::IsFinite(utterance_->rate) || + utterance_->rate < 0.0 || + utterance_->rate > 1.0) { + utterance_->rate = -1.0; } } - if (util::ReadNumberByKey(speak_options, util::kPitchKey, &pitch)) { - if (!base::IsFinite(pitch) || pitch < 0.0 || pitch > 1.0) { - pitch = -1.0; + if (util::ReadNumberByKey( + speak_options, util::kPitchKey, &utterance_->pitch)) { + if (!base::IsFinite(utterance_->pitch) || + utterance_->pitch < 0.0 || + utterance_->pitch > 1.0) { + utterance_->pitch = -1.0; } } - if (util::ReadNumberByKey(speak_options, util::kVolumeKey, &volume)) { - if (!base::IsFinite(volume) || volume < 0.0 || volume > 1.0) { - volume = -1.0; + if (util::ReadNumberByKey( + speak_options, util::kVolumeKey, &utterance_->volume)) { + if (!base::IsFinite(utterance_->volume) || + utterance_->volume < 0.0 || + utterance_->volume > 1.0) { + utterance_->volume = -1.0; } } } - ExtensionTtsPlatformImpl* impl = ExtensionTtsPlatformImpl::GetInstance(); - impl->clear_error(); - return impl->Speak(utterance, language, gender, rate, pitch, volume); + AddRef(); // Balanced in SpeechFinished(). + utterance_->success_task = NewRunnableMethod( + this, &ExtensionTtsSpeakFunction::SpeechFinished, true); + utterance_->failure_task = NewRunnableMethod( + this, &ExtensionTtsSpeakFunction::SpeechFinished, false); + ExtensionTtsController::GetInstance()->SpeakOrEnqueue( + utterance_, can_enqueue); + return true; +} + +void ExtensionTtsSpeakFunction::SpeechFinished(bool success) { + error_ = utterance_->error; + SendResponse(success); + Release(); // Balanced in Speak(). } bool ExtensionTtsStopSpeakingFunction::RunImpl() { - ExtensionTtsPlatformImpl* impl = ExtensionTtsPlatformImpl::GetInstance(); - impl->clear_error(); - return impl->StopSpeaking(); + ExtensionTtsController::GetInstance()->Stop(); + return true; } bool ExtensionTtsIsSpeakingFunction::RunImpl() { - ExtensionTtsPlatformImpl* impl = ExtensionTtsPlatformImpl::GetInstance(); - impl->clear_error(); - result_.reset(Value::CreateBooleanValue(impl->IsSpeaking())); + result_.reset(Value::CreateBooleanValue( + ExtensionTtsController::GetInstance()->IsSpeaking())); return true; } diff --git a/chrome/browser/extensions/extension_tts_api.h b/chrome/browser/extensions/extension_tts_api.h index 73fc887..0eedcc2 100644 --- a/chrome/browser/extensions/extension_tts_api.h +++ b/chrome/browser/extensions/extension_tts_api.h @@ -5,6 +5,10 @@ #ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_TTS_API_H_ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_TTS_API_H_ +#include <queue> + +#include "base/singleton.h" +#include "base/task.h" #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/extensions/extension_tts_api_util.h" @@ -17,6 +21,11 @@ class ExtensionTtsPlatformImpl { // and return true on success. Utterance will always be nonempty. // If the user does not specify the other values, language and gender // will be empty strings, and rate, pitch, and volume will be -1.0. + // + // The ExtensionTtsController will only try to speak one utterance at + // a time. If it wants to intterupt speech, it will always call Stop + // before speaking again, otherwise it will wait until IsSpeaking + // returns false before calling Speak again. virtual bool Speak( const std::string& utterance, const std::string& language, @@ -44,23 +53,108 @@ class ExtensionTtsPlatformImpl { DISALLOW_COPY_AND_ASSIGN(ExtensionTtsPlatformImpl); }; +// Singleton class that manages text-to-speech. +class ExtensionTtsController { + public: + // Get the single instance of this class. + static ExtensionTtsController* GetInstance(); + + struct Utterance { + Utterance() + : rate(-1.0), + pitch(-1.0), + volume(-1.0), + success_task(NULL), + failure_task(NULL) { + } + + std::string text; + std::string language; + std::string gender; + double rate; + double pitch; + double volume; + + Task* success_task; + Task* failure_task; + + std::string error; + }; + + // Returns true if we're currently speaking an utterance. + bool IsSpeaking() const; + + // Speak the given utterance. If |can_enqueue| is true and another + // utterance is in progress, adds it to the end of the queue. Otherwise, + // interrupts any current utterance and speaks this one immediately. + void SpeakOrEnqueue(Utterance* utterance, bool can_enqueue); + + // Stop all utterances and flush the queue. + void Stop(); + + // For unit testing. + void SetPlatformImpl(ExtensionTtsPlatformImpl* platform_impl); + + private: + ExtensionTtsController(); + virtual ~ExtensionTtsController() {} + + // Get the platform TTS implementation (or injected mock). + ExtensionTtsPlatformImpl* GetPlatformImpl(); + + // Start speaking the given utterance. Will either take ownership of + // |utterance| or delete it if there's an error. + void SpeakNow(Utterance* utterance); + + // Called periodically when speech is ongoing. Checks to see if the + // underlying platform speech system has finished the current utterance, + // and if so finishes it and pops the next utterance off the queue. + void CheckSpeechStatus(); + + // Clear the utterance queue. + void ClearUtteranceQueue(); + + // Finalize and delete the current utterance. + void FinishCurrentUtterance(); + + ScopedRunnableMethodFactory<ExtensionTtsController> method_factory_; + friend struct DefaultSingletonTraits<ExtensionTtsController>; + + // The current utterance being spoken. + Utterance* current_utterance_; + + // A queue of utterances to speak after the current one finishes. + std::queue<Utterance*> utterance_queue_; + + // A pointer to the platform implementation of text-to-speech, for + // dependency injection. + ExtensionTtsPlatformImpl* platform_impl_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionTtsController); +}; + // // Extension API function definitions // -class ExtensionTtsSpeakFunction : public SyncExtensionFunction { +class ExtensionTtsSpeakFunction : public AsyncExtensionFunction { + private: ~ExtensionTtsSpeakFunction() {} virtual bool RunImpl(); + void SpeechFinished(bool success); + ExtensionTtsController::Utterance* utterance_; DECLARE_EXTENSION_FUNCTION_NAME("experimental.tts.speak") }; class ExtensionTtsStopSpeakingFunction : public SyncExtensionFunction { + private: ~ExtensionTtsStopSpeakingFunction() {} virtual bool RunImpl(); DECLARE_EXTENSION_FUNCTION_NAME("experimental.tts.stop") }; class ExtensionTtsIsSpeakingFunction : public SyncExtensionFunction { + private: ~ExtensionTtsIsSpeakingFunction() {} virtual bool RunImpl(); DECLARE_EXTENSION_FUNCTION_NAME("experimental.tts.isSpeaking") diff --git a/chrome/browser/extensions/extension_tts_api_linux.cc b/chrome/browser/extensions/extension_tts_api_linux.cc index 8ee228b..7b89799 100644 --- a/chrome/browser/extensions/extension_tts_api_linux.cc +++ b/chrome/browser/extensions/extension_tts_api_linux.cc @@ -38,7 +38,7 @@ class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { // Get the single instance of this class. static ExtensionTtsPlatformImplLinux* GetInstance() { - return ExtensionTtsPlatformImplLinux::GetInstance(); + return Singleton<ExtensionTtsPlatformImplLinux>::get(); } private: diff --git a/chrome/browser/extensions/extension_tts_api_util.h b/chrome/browser/extensions/extension_tts_api_util.h index 4b41871..cf0d702 100644 --- a/chrome/browser/extensions/extension_tts_api_util.h +++ b/chrome/browser/extensions/extension_tts_api_util.h @@ -17,6 +17,7 @@ const char kGenderKey[] = "gender"; const char kRateKey[] = "rate"; const char kPitchKey[] = "pitch"; const char kVolumeKey[] = "volume"; +const char kEnqueueKey[] = "enqueue"; const char kEqualStr[] = "="; const char kDelimiter[] = ";"; diff --git a/chrome/browser/extensions/extension_tts_apitest.cc b/chrome/browser/extensions/extension_tts_apitest.cc index dcdfb9d..6f37404 100644 --- a/chrome/browser/extensions/extension_tts_apitest.cc +++ b/chrome/browser/extensions/extension_tts_apitest.cc @@ -2,19 +2,164 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/command_line.h" -#include "chrome/browser/chromeos/cros/cros_mock.h" #include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_tts_api.h" #include "chrome/common/chrome_switches.h" #include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Needed for CreateFunctor. +#define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#include "testing/gmock_mutant.h" -// This extension API is currently only supported on Chrome OS. #if defined(OS_CHROMEOS) -#define MAYBE_Tts Tts -#else -#define MAYBE_Tts DISABLED_Tts +#include "chrome/browser/chromeos/cros/cros_mock.h" #endif -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tts) { +using ::testing::CreateFunctor; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::InvokeWithoutArgs; +using ::testing::Return; +using ::testing::StrictMock; +using ::testing::_; + +class MockExtensionTtsPlatformImpl : public ExtensionTtsPlatformImpl { + public: + MOCK_METHOD6(Speak, + bool(const std::string& utterance, + const std::string& language, + const std::string& gender, + double rate, + double pitch, + double volume)); + MOCK_METHOD0(StopSpeaking, bool(void)); + MOCK_METHOD0(IsSpeaking, bool(void)); + + void SetErrorToEpicFail() { + set_error("epic fail"); + } +}; + +class TtsApiTest : public ExtensionApiTest { + public: + virtual void SetUpCommandLine(CommandLine* command_line) { + ExtensionApiTest::SetUpCommandLine(command_line); + command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis); + } + + virtual void SetUpInProcessBrowserTestFixture() { + ExtensionApiTest::SetUpInProcessBrowserTestFixture(); + ExtensionTtsController::GetInstance()->SetPlatformImpl( + &mock_platform_impl_); + } + + protected: + StrictMock<MockExtensionTtsPlatformImpl> mock_platform_impl_; +}; + +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakFinishesImmediately) { + InSequence s; + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(false)); + ASSERT_TRUE(RunExtensionTest("tts/speak_once")) << message_; +} + +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakKeepsSpeakingTwice) { + InSequence s; + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + ASSERT_TRUE(RunExtensionTest("tts/speak_once")) << message_; +} + +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakInterrupt) { + InSequence s; + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak("text 1", _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak("text 2", _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + ASSERT_TRUE(RunExtensionTest("tts/interrupt")) << message_; +} + +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakQueueInterrupt) { + // In this test, two utterances are queued, and then a third + // interrupts. Speak() never gets called on the second utterance. + InSequence s; + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak("text 1", _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak("text 3", _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + ASSERT_TRUE(RunExtensionTest("tts/queue_interrupt")) << message_; +} + +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakEnqueue) { + InSequence s; + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak("text 1", _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + EXPECT_CALL(mock_platform_impl_, Speak("text 2", _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + ASSERT_TRUE(RunExtensionTest("tts/enqueue")) << message_; +} + +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakError) { + InSequence s; + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(false)); + EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _)) + .WillOnce(DoAll( + InvokeWithoutArgs( + CreateFunctor(&mock_platform_impl_, + &MockExtensionTtsPlatformImpl::SetErrorToEpicFail)), + Return(false))); + EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .WillOnce(Return(false)); + ASSERT_TRUE(RunExtensionTest("tts/speak_error")) << message_; +} + +#if defined(OS_CHROMEOS) +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TtsChromeOs) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); @@ -24,3 +169,4 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tts) { ASSERT_TRUE(RunExtensionTest("tts/chromeos")) << message_; } +#endif diff --git a/chrome/browser/extensions/extension_ui_unittest.cc b/chrome/browser/extensions/extension_ui_unittest.cc index ba429ba..1bda60a 100644 --- a/chrome/browser/extensions/extension_ui_unittest.cc +++ b/chrome/browser/extensions/extension_ui_unittest.cc @@ -33,7 +33,6 @@ namespace { #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - Extension extension(path); std::string error; FilePath manifest_path = extension_path.Append( @@ -41,7 +40,10 @@ namespace { scoped_ptr<DictionaryValue> extension_data(DeserializeJSONTestData( manifest_path, &error)); EXPECT_EQ("", error); - EXPECT_TRUE(extension.InitFromValue(*extension_data, true, &error)); + + scoped_refptr<Extension> extension(Extension::Create( + path, Extension::INVALID, *extension_data, true, &error)); + EXPECT_TRUE(extension.get()); EXPECT_EQ("", error); scoped_ptr<DictionaryValue> expected_output_data(DeserializeJSONTestData( @@ -50,7 +52,7 @@ namespace { // Produce test output. scoped_ptr<DictionaryValue> actual_output_data( - ExtensionsDOMHandler::CreateExtensionDetailValue(NULL, &extension, + ExtensionsDOMHandler::CreateExtensionDetailValue(NULL, extension.get(), pages, true)); // Compare the outputs. diff --git a/chrome/browser/extensions/extension_uitest.cc b/chrome/browser/extensions/extension_uitest.cc index 2076949..f074c9b 100644 --- a/chrome/browser/extensions/extension_uitest.cc +++ b/chrome/browser/extensions/extension_uitest.cc @@ -121,6 +121,7 @@ class ExtensionTestSimpleApiCall : public ExtensionUITest { DISALLOW_COPY_AND_ASSIGN(ExtensionTestSimpleApiCall); }; +// Flaky: http://crbug.com/44599 TEST_F(ExtensionTestSimpleApiCall, FLAKY_RunTest) { namespace keys = extension_automation_constants; diff --git a/chrome/browser/extensions/extension_updater.cc b/chrome/browser/extensions/extension_updater.cc index f4e9e94..b2b3972 100644 --- a/chrome/browser/extensions/extension_updater.cc +++ b/chrome/browser/extensions/extension_updater.cc @@ -68,7 +68,7 @@ static const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days // request. We want to stay under 2K because of proxies, etc. static const int kExtensionsManifestMaxURLSize = 2000; -ManifestFetchData::ManifestFetchData(GURL update_url) +ManifestFetchData::ManifestFetchData(const GURL& update_url) : base_url_(update_url), full_url_(update_url) { } @@ -232,9 +232,7 @@ void ManifestFetchesBuilder::AddExtensionData( PendingExtensionInfo::ExpectedCrxType crx_type, GURL update_url) { - // Only internal and external extensions can be autoupdated. - if (location != Extension::INTERNAL && - !Extension::IsExternalLocation(location)) { + if (!Extension::IsAutoUpdateableLocation(location)) { return; } @@ -520,13 +518,13 @@ void ExtensionUpdater::OnManifestFetchComplete(const GURL& url, // We want to try parsing the manifest, and if it indicates updates are // available, we want to fire off requests to fetch those updates. if (status.status() == URLRequestStatus::SUCCESS && response_code == 200) { - scoped_refptr<SafeManifestParser> safe_parser = - new SafeManifestParser(data, current_manifest_fetch_.release(), this); + scoped_refptr<SafeManifestParser> safe_parser( + new SafeManifestParser(data, current_manifest_fetch_.release(), this)); safe_parser->Start(); } else { // TODO(asargent) Do exponential backoff here. (http://crbug.com/12546). - LOG(INFO) << "Failed to fetch manifest '" << url.possibly_invalid_spec() << - "' response code:" << response_code; + VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec() + << "' response code:" << response_code; } manifest_fetcher_.reset(); current_manifest_fetch_.reset(); @@ -620,8 +618,8 @@ void ExtensionUpdater::OnCRXFetchComplete(const GURL& url, // TODO(asargent) do things like exponential backoff, handling // 503 Service Unavailable / Retry-After headers, etc. here. // (http://crbug.com/12546). - LOG(INFO) << "Failed to fetch extension '" << - url.possibly_invalid_spec() << "' response code:" << response_code; + VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec() + << "' response code:" << response_code; } extension_fetcher_.reset(); current_extension_fetch_ = ExtensionFetch(); @@ -736,7 +734,7 @@ bool ExtensionUpdater::GetExistingVersion(const std::string& id, *version = prefs_->GetString(kExtensionBlacklistUpdateVersion); return true; } - Extension* extension = service_->GetExtensionById(id, false); + const Extension* extension = service_->GetExtensionById(id, false); if (!extension) { return false; } @@ -835,7 +833,8 @@ void ExtensionUpdater::StartUpdateCheck(ManifestFetchData* fetch_data) { URLFetcher::GET, this)); manifest_fetcher_->set_request_context(Profile::GetDefaultRequestContext()); manifest_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES | - net::LOAD_DO_NOT_SAVE_COOKIES); + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DISABLE_CACHE); manifest_fetcher_->Start(); } } @@ -862,7 +861,8 @@ void ExtensionUpdater::FetchUpdatedExtension(const std::string& id, extension_fetcher_->set_request_context( Profile::GetDefaultRequestContext()); extension_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES | - net::LOAD_DO_NOT_SAVE_COOKIES); + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DISABLE_CACHE); extension_fetcher_->Start(); current_extension_fetch_ = ExtensionFetch(id, url, hash, version); } diff --git a/chrome/browser/extensions/extension_updater.h b/chrome/browser/extensions/extension_updater.h index 4540dd0..79fa166 100644 --- a/chrome/browser/extensions/extension_updater.h +++ b/chrome/browser/extensions/extension_updater.h @@ -36,7 +36,7 @@ class ManifestFetchData { public: static const int kNeverPinged = -1; - explicit ManifestFetchData(GURL update_url); + explicit ManifestFetchData(const GURL& update_url); ~ManifestFetchData(); // Returns true if this extension information was successfully added. If the @@ -112,7 +112,6 @@ class ManifestFetchesBuilder { const Version& version, PendingExtensionInfo::ExpectedCrxType crx_type, GURL update_url); - ExtensionUpdateService* service_; // List of data on fetches we're going to do. We limit the number of diff --git a/chrome/browser/extensions/extension_updater_unittest.cc b/chrome/browser/extensions/extension_updater_unittest.cc index 7989e7a..f099b86 100644 --- a/chrome/browser/extensions/extension_updater_unittest.cc +++ b/chrome/browser/extensions/extension_updater_unittest.cc @@ -33,7 +33,9 @@ using base::Time; using base::TimeDelta; static int expected_load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; + net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DISABLE_CACHE; // Base class for further specialized test classes. class MockService : public ExtensionUpdateService { @@ -57,7 +59,7 @@ class MockService : public ExtensionUpdateService { FAIL(); } - virtual Extension* GetExtensionById(const std::string& id, bool) { + virtual const Extension* GetExtensionById(const std::string& id, bool) { ADD_FAILURE(); return NULL; } @@ -94,7 +96,8 @@ class MockService : public ExtensionUpdateService { base::StringPrintf("Extension %d", i)); if (update_url) manifest.SetString(extension_manifest_keys::kUpdateURL, *update_url); - Extension* e = prefs_.AddExtensionWithManifest(manifest, location); + scoped_refptr<Extension> e = + prefs_.AddExtensionWithManifest(manifest, location); ASSERT_TRUE(e != NULL); list->push_back(e); } @@ -141,7 +144,7 @@ class ServiceForManifestTests : public MockService { virtual ~ServiceForManifestTests() {} - virtual Extension* GetExtensionById(const std::string& id, bool) { + virtual const Extension* GetExtensionById(const std::string& id, bool) { for (ExtensionList::iterator iter = extensions_.begin(); iter != extensions_.end(); ++iter) { if ((*iter)->id() == id) { @@ -193,7 +196,7 @@ class ServiceForDownloadTests : public MockService { return pending_extensions_; } - virtual Extension* GetExtensionById(const std::string& id, bool) { + virtual const Extension* GetExtensionById(const std::string& id, bool) { last_inquired_extension_id_ = id; return NULL; } @@ -307,8 +310,8 @@ class ExtensionUpdaterTest : public testing::Test { TestURLFetcherFactory factory; URLFetcher::set_factory(&factory); - scoped_refptr<ExtensionUpdater> updater = - new ExtensionUpdater(&service, service.pref_service(), 60*60*24); + scoped_refptr<ExtensionUpdater> updater( + new ExtensionUpdater(&service, service.pref_service(), 60*60*24)); updater->Start(); // Tell the update that it's time to do update checks. @@ -343,10 +346,6 @@ class ExtensionUpdaterTest : public testing::Test { EXPECT_EQ(extensions[0]->VersionString(), params["v"]); } EXPECT_EQ("", params["uc"]); - - if (!pending) { - STLDeleteElements(&extensions); - } } static void TestBlacklistUpdateCheckRequests() { @@ -359,8 +358,8 @@ class ExtensionUpdaterTest : public testing::Test { TestURLFetcherFactory factory; URLFetcher::set_factory(&factory); - scoped_refptr<ExtensionUpdater> updater = - new ExtensionUpdater(&service, service.pref_service(), 60*60*24); + scoped_refptr<ExtensionUpdater> updater( + new ExtensionUpdater(&service, service.pref_service(), 60*60*24)); updater->Start(); // Tell the updater that it's time to do update checks. @@ -411,9 +410,9 @@ class ExtensionUpdaterTest : public testing::Test { service.set_extensions(tmp); MessageLoop message_loop; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); // Check passing an empty list of parse results to DetermineUpdates ManifestFetchData fetch_data(GURL("http://localhost/foo")); @@ -438,7 +437,6 @@ class ExtensionUpdaterTest : public testing::Test { updateable = updater->DetermineUpdates(fetch_data, updates); EXPECT_EQ(1u, updateable.size()); EXPECT_EQ(0, updateable[0]); - STLDeleteElements(&tmp); } static void TestDetermineUpdatesPending() { @@ -449,9 +447,9 @@ class ExtensionUpdaterTest : public testing::Test { service.set_pending_extensions(pending_extensions); MessageLoop message_loop; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); ManifestFetchData fetch_data(GURL("http://localhost/foo")); UpdateManifest::Results updates; @@ -483,9 +481,9 @@ class ExtensionUpdaterTest : public testing::Test { TestURLFetcher* fetcher = NULL; URLFetcher::set_factory(&factory); ServiceForDownloadTests service; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); GURL url1("http://localhost/manifest1"); GURL url2("http://localhost/manifest2"); @@ -547,9 +545,9 @@ class ExtensionUpdaterTest : public testing::Test { TestURLFetcher* fetcher = NULL; URLFetcher::set_factory(&factory); ServiceForDownloadTests service; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); GURL test_url("http://localhost/extension.crx"); @@ -610,9 +608,9 @@ class ExtensionUpdaterTest : public testing::Test { TestURLFetcher* fetcher = NULL; URLFetcher::set_factory(&factory); ServiceForBlacklistTests service; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); service.pref_service()-> RegisterStringPref(prefs::kExtensionBlacklistUpdateVersion, "0"); GURL test_url("http://localhost/extension.crx"); @@ -657,9 +655,9 @@ class ExtensionUpdaterTest : public testing::Test { TestURLFetcher* fetcher = NULL; URLFetcher::set_factory(&factory); ServiceForDownloadTests service; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); GURL url1("http://localhost/extension1.crx"); GURL url2("http://localhost/extension2.crx"); @@ -744,9 +742,9 @@ class ExtensionUpdaterTest : public testing::Test { } MessageLoop message_loop; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); updater->set_blacklist_checks_enabled(false); // Make the updater do manifest fetching, and note the urls it tries to @@ -788,8 +786,6 @@ class ExtensionUpdaterTest : public testing::Test { size_t pos = url1_query.find(search_string); EXPECT_TRUE(pos != std::string::npos); } - - STLDeleteElements(&tmp); } // This makes sure that the extension updater properly stores the results @@ -798,9 +794,9 @@ class ExtensionUpdaterTest : public testing::Test { // >= 1 day for the extension. static void TestHandleManifestResults() { ServiceForManifestTests service; - scoped_refptr<ExtensionUpdater> updater = + scoped_refptr<ExtensionUpdater> updater( new ExtensionUpdater(&service, service.pref_service(), - kUpdateFrequencySecs); + kUpdateFrequencySecs)); GURL update_url("http://www.google.com/manifest"); ExtensionList tmp; @@ -809,7 +805,7 @@ class ExtensionUpdaterTest : public testing::Test { service.set_extensions(tmp); ManifestFetchData fetch_data(update_url); - Extension* extension = tmp[0]; + const Extension* extension = tmp[0]; fetch_data.AddExtension(extension->id(), extension->VersionString(), ManifestFetchData::kNeverPinged); UpdateManifest::Results results; @@ -821,8 +817,6 @@ class ExtensionUpdaterTest : public testing::Test { EXPECT_FALSE(last_ping_day.is_null()); int64 seconds_diff = (Time::Now() - last_ping_day).InSeconds(); EXPECT_LT(seconds_diff - results.daystart_elapsed_seconds, 5); - - STLDeleteElements(&tmp); } }; @@ -894,7 +888,6 @@ TEST(ExtensionUpdaterTest, TestManifestFetchesBuilderAddExtension) { ASSERT_FALSE(extensions.empty()); builder.AddExtension(*extensions[0]); EXPECT_TRUE(builder.GetFetches().empty()); - STLDeleteElements(&extensions); } // Extensions with invalid update URLs should be rejected. diff --git a/chrome/browser/extensions/extension_webnavigation_api.cc b/chrome/browser/extensions/extension_webnavigation_api.cc index 159fdb7..9a3e4ab 100644 --- a/chrome/browser/extensions/extension_webnavigation_api.cc +++ b/chrome/browser/extensions/extension_webnavigation_api.cc @@ -17,6 +17,7 @@ #include "chrome/browser/tab_contents/provisional_load_details.h" #include "chrome/common/notification_type.h" #include "chrome/common/notification_service.h" +#include "chrome/common/url_constants.h" #include "net/base/net_errors.h" namespace keys = extension_webnavigation_api_constants; @@ -36,6 +37,71 @@ double MilliSecondsFromTime(const base::Time& time) { } // namespace +FrameNavigationState::FrameNavigationState() { +} + +FrameNavigationState::~FrameNavigationState() { +} + +bool FrameNavigationState::CanSendEvents(long long frame_id) const { + FrameIdToStateMap::const_iterator frame_state = + frame_state_map_.find(frame_id); + return frame_state != frame_state_map_.end() && + !frame_state->second.error_occurred; +} + +void FrameNavigationState::TrackFrame(long long frame_id, + const GURL& url, + bool is_main_frame, + const TabContents* tab_contents) { + if (is_main_frame) + RemoveTabContentsState(tab_contents); + tab_contents_map_.insert( + TabContentsToFrameIdMap::value_type(tab_contents, frame_id)); + FrameState& frame_state = frame_state_map_[frame_id]; + frame_state.error_occurred = (url.spec() == chrome::kUnreachableWebDataURL); + frame_state.url = url; + frame_state.is_main_frame = is_main_frame; +} + +GURL FrameNavigationState::GetUrl(long long frame_id) const { + FrameIdToStateMap::const_iterator frame_state = + frame_state_map_.find(frame_id); + if (frame_state == frame_state_map_.end()) { + NOTREACHED(); + return GURL(); + } + return frame_state->second.url; +} + +bool FrameNavigationState::IsMainFrame(long long frame_id) const { + FrameIdToStateMap::const_iterator frame_state = + frame_state_map_.find(frame_id); + if (frame_state == frame_state_map_.end()) { + NOTREACHED(); + return false; + } + return frame_state->second.is_main_frame; +} + +void FrameNavigationState::ErrorOccurredInFrame(long long frame_id) { + DCHECK(frame_state_map_.find(frame_id) != frame_state_map_.end()); + frame_state_map_[frame_id].error_occurred = true; +} + +void FrameNavigationState::RemoveTabContentsState( + const TabContents* tab_contents) { + typedef TabContentsToFrameIdMap::iterator FrameIdIterator; + std::pair<FrameIdIterator, FrameIdIterator> frame_ids = + tab_contents_map_.equal_range(tab_contents); + for (FrameIdIterator frame_id = frame_ids.first; frame_id != frame_ids.second; + ++frame_id) { + frame_state_map_.erase(frame_id->second); + } + tab_contents_map_.erase(tab_contents); +} + + // static ExtensionWebNavigationEventRouter* ExtensionWebNavigationEventRouter::GetInstance() { @@ -51,8 +117,17 @@ void ExtensionWebNavigationEventRouter::Init() { NotificationType::FRAME_PROVISIONAL_LOAD_COMMITTED, NotificationService::AllSources()); registrar_.Add(this, + NotificationType::FRAME_DOM_CONTENT_LOADED, + NotificationService::AllSources()); + registrar_.Add(this, + NotificationType::FRAME_DID_FINISH_LOAD, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR, NotificationService::AllSources()); + registrar_.Add(this, + NotificationType::TAB_CONTENTS_DESTROYED, + NotificationService::AllSources()); } } @@ -71,12 +146,27 @@ void ExtensionWebNavigationEventRouter::Observe( Source<NavigationController>(source).ptr(), Details<ProvisionalLoadDetails>(details).ptr()); break; + case NotificationType::FRAME_DOM_CONTENT_LOADED: + FrameDomContentLoaded( + Source<NavigationController>(source).ptr(), + *Details<long long>(details).ptr()); + break; + case NotificationType::FRAME_DID_FINISH_LOAD: + FrameDidFinishLoad( + Source<NavigationController>(source).ptr(), + *Details<long long>(details).ptr()); + break; case NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR: FailProvisionalLoadWithError( Source<NavigationController>(source).ptr(), Details<ProvisionalLoadDetails>(details).ptr()); break; + case NotificationType::TAB_CONTENTS_DESTROYED: + navigation_state_.RemoveTabContentsState( + Source<TabContents>(source).ptr()); + break; + default: NOTREACHED(); } @@ -84,6 +174,12 @@ void ExtensionWebNavigationEventRouter::Observe( void ExtensionWebNavigationEventRouter::FrameProvisionalLoadStart( NavigationController* controller, ProvisionalLoadDetails* details) { + navigation_state_.TrackFrame(details->frame_id(), + details->url(), + details->main_frame(), + controller->tab_contents()); + if (!navigation_state_.CanSendEvents(details->frame_id())) + return; ListValue args; DictionaryValue* dict = new DictionaryValue(); dict->SetInteger(keys::kTabIdKey, @@ -103,6 +199,8 @@ void ExtensionWebNavigationEventRouter::FrameProvisionalLoadStart( void ExtensionWebNavigationEventRouter::FrameProvisionalLoadCommitted( NavigationController* controller, ProvisionalLoadDetails* details) { + if (!navigation_state_.CanSendEvents(details->frame_id())) + return; ListValue args; DictionaryValue* dict = new DictionaryValue(); dict->SetInteger(keys::kTabIdKey, @@ -124,9 +222,49 @@ void ExtensionWebNavigationEventRouter::FrameProvisionalLoadCommitted( DispatchEvent(controller->profile(), keys::kOnCommitted, json_args); } +void ExtensionWebNavigationEventRouter::FrameDomContentLoaded( + NavigationController* controller, long long frame_id) { + if (!navigation_state_.CanSendEvents(frame_id)) + return; + ListValue args; + DictionaryValue* dict = new DictionaryValue(); + dict->SetInteger(keys::kTabIdKey, + ExtensionTabUtil::GetTabId(controller->tab_contents())); + dict->SetString(keys::kUrlKey, navigation_state_.GetUrl(frame_id).spec()); + dict->SetInteger(keys::kFrameIdKey, navigation_state_.IsMainFrame(frame_id) ? + 0 : static_cast<int>(frame_id)); + dict->SetReal(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now())); + args.Append(dict); + + std::string json_args; + base::JSONWriter::Write(&args, false, &json_args); + DispatchEvent(controller->profile(), keys::kOnDOMContentLoaded, json_args); +} + +void ExtensionWebNavigationEventRouter::FrameDidFinishLoad( + NavigationController* controller, long long frame_id) { + if (!navigation_state_.CanSendEvents(frame_id)) + return; + ListValue args; + DictionaryValue* dict = new DictionaryValue(); + dict->SetInteger(keys::kTabIdKey, + ExtensionTabUtil::GetTabId(controller->tab_contents())); + dict->SetString(keys::kUrlKey, navigation_state_.GetUrl(frame_id).spec()); + dict->SetInteger(keys::kFrameIdKey, navigation_state_.IsMainFrame(frame_id) ? + 0 : static_cast<int>(frame_id)); + dict->SetReal(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now())); + args.Append(dict); + + std::string json_args; + base::JSONWriter::Write(&args, false, &json_args); + DispatchEvent(controller->profile(), keys::kOnCompleted, json_args); +} + void ExtensionWebNavigationEventRouter::FailProvisionalLoadWithError( NavigationController* controller, ProvisionalLoadDetails* details) { + if (!navigation_state_.CanSendEvents(details->frame_id())) + return; ListValue args; DictionaryValue* dict = new DictionaryValue(); dict->SetInteger(keys::kTabIdKey, @@ -141,6 +279,7 @@ void ExtensionWebNavigationEventRouter::FailProvisionalLoadWithError( std::string json_args; base::JSONWriter::Write(&args, false, &json_args); + navigation_state_.ErrorOccurredInFrame(details->frame_id()); DispatchEvent(controller->profile(), keys::kOnErrorOccurred, json_args); } diff --git a/chrome/browser/extensions/extension_webnavigation_api.h b/chrome/browser/extensions/extension_webnavigation_api.h index 21cfb16..b595aca 100644 --- a/chrome/browser/extensions/extension_webnavigation_api.h +++ b/chrome/browser/extensions/extension_webnavigation_api.h @@ -22,6 +22,53 @@ class NavigationController; class ProvisionalLoadDetails; class TabContents; +// Tracks which frames are in an error state, and no navigation events should +// be sent for. +class FrameNavigationState { + public: + FrameNavigationState(); + ~FrameNavigationState(); + + // True if navigation events for the given frame can be sent. + bool CanSendEvents(long long frame_id) const; + + // Starts to track a frame given by its |frame_id| showing the URL |url| in + // a |tab_contents|. + void TrackFrame(long long frame_id, + const GURL& url, + bool is_main_frame, + const TabContents* tab_contents); + + // Returns the URL corresponding to a tracked frame given by its |frame_id|. + GURL GetUrl(long long frame_id) const; + + // True if the frame given by its |frame_id| is the main frame of its tab. + bool IsMainFrame(long long frame_id) const; + + // Marks a frame as in an error state. + void ErrorOccurredInFrame(long long frame_id); + + // Removes state associated with this tab contents and all of its frames. + void RemoveTabContentsState(const TabContents* tab_contents); + + private: + typedef std::multimap<const TabContents*, long long> TabContentsToFrameIdMap; + struct FrameState { + bool error_occurred; // True if an error has occurred in this frame. + bool is_main_frame; // True if this is a main frame. + GURL url; // URL of this frame. + }; + typedef std::map<long long, FrameState> FrameIdToStateMap; + + // Tracks which frames belong to a given tab contents object. + TabContentsToFrameIdMap tab_contents_map_; + + // Tracks the state of known frames. + FrameIdToStateMap frame_state_map_; + + DISALLOW_COPY_AND_ASSIGN(FrameNavigationState); +}; + // Observes navigation notifications and routes them as events to the extension // system. class ExtensionWebNavigationEventRouter : public NotificationObserver { @@ -54,6 +101,15 @@ class ExtensionWebNavigationEventRouter : public NotificationObserver { void FrameProvisionalLoadCommitted(NavigationController* controller, ProvisionalLoadDetails* details); + // Handler for the FRAME_DOM_CONTENT_LOADED event. The method takes the frame + // ID and constructs a suitable JSON formatted extension event from it. + void FrameDomContentLoaded(NavigationController* controller, + long long frame_id); + + // Handler for the FRAME_DID_FINISH_LOAD event. The method takes the frame + // ID and constructs a suitable JSON formatted extension event from it. + void FrameDidFinishLoad(NavigationController* controller, long long frame_id); + // Handler for the FAIL_PROVISIONAL_LOAD_WITH_ERROR event. The method takes // the details of such an event and constructs a suitable JSON formatted // extension event from it. @@ -65,6 +121,9 @@ class ExtensionWebNavigationEventRouter : public NotificationObserver { const char* event_name, const std::string& json_args); + // Tracks the state of the frames we are sending events for. + FrameNavigationState navigation_state_; + // Used for tracking registrations to navigation notifications. NotificationRegistrar registrar_; diff --git a/chrome/browser/extensions/extension_webnavigation_apitest.cc b/chrome/browser/extensions/extension_webnavigation_apitest.cc index 5dd3f3e..b27887b 100644 --- a/chrome/browser/extensions/extension_webnavigation_apitest.cc +++ b/chrome/browser/extensions/extension_webnavigation_apitest.cc @@ -13,10 +13,16 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WebNavigation) { ASSERT_TRUE(RunExtensionTest("webnavigation/api")) << message_; } -// This test is flaky: http://crbug.com/60229 -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FLAKY_WebNavigationEvents) { +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WebNavigationEvents1) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); - ASSERT_TRUE(RunExtensionTest("webnavigation/navigation")) << message_; + ASSERT_TRUE(RunExtensionTest("webnavigation/navigation1")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WebNavigationEvents2) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + + ASSERT_TRUE(RunExtensionTest("webnavigation/navigation2")) << message_; } diff --git a/chrome/browser/extensions/extension_webnavigation_unittest.cc b/chrome/browser/extensions/extension_webnavigation_unittest.cc new file mode 100644 index 0000000..5ac378e --- /dev/null +++ b/chrome/browser/extensions/extension_webnavigation_unittest.cc @@ -0,0 +1,105 @@ +// Copyright (c) 2010 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. + +// Tests common functionality used by the Chrome Extensions webNavigation API +// implementation. + +#include "testing/gtest/include/gtest/gtest.h" + +#include "base/values.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/extensions/extension_webnavigation_api.h" +#include "chrome/browser/renderer_host/test/test_render_view_host.h" +#include "chrome/browser/tab_contents/test_tab_contents.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/testing_profile.h" + +class FrameNavigationStateTest : public RenderViewHostTestHarness { +}; + +// Test that a frame is correctly tracked, and removed once the tab contents +// goes away. +TEST_F(FrameNavigationStateTest, TrackFrame) { + FrameNavigationState navigation_state; + const long long frame_id1 = 23; + const long long frame_id2 = 42; + const GURL url1("http://www.google.com/"); + const GURL url2("http://mail.google.com/"); + + // Create a main frame. + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id1)); + navigation_state.TrackFrame(frame_id1, url1, true, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id1)); + + // Add a sub frame. + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id2)); + navigation_state.TrackFrame(frame_id2, url2, false, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id2)); + + // Check frame state. + EXPECT_TRUE(navigation_state.IsMainFrame(frame_id1)); + EXPECT_EQ(url1, navigation_state.GetUrl(frame_id1)); + EXPECT_FALSE(navigation_state.IsMainFrame(frame_id2)); + EXPECT_EQ(url2, navigation_state.GetUrl(frame_id2)); + + + // Removing the tab contents should also remove all state of its frames. + navigation_state.RemoveTabContentsState(contents()); + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id1)); + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id2)); +} + +// Test that no events can be sent for a frame after an error occurred, but +// before a new navigation happened in this frame. +TEST_F(FrameNavigationStateTest, ErrorState) { + FrameNavigationState navigation_state; + const long long frame_id = 42; + const GURL url("http://www.google.com/"); + + navigation_state.TrackFrame(frame_id, url, true, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id)); + + // After an error occurred, no further events should be sent. + navigation_state.ErrorOccurredInFrame(frame_id); + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id)); + + // Navigations to the "unreachable web data" URL should be ignored. + navigation_state.TrackFrame( + frame_id, GURL(chrome::kUnreachableWebDataURL), true, contents()); + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id)); + + // However, when the frame navigates again, it should send events again. + navigation_state.TrackFrame(frame_id, url, true, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id)); +} + +// Tests that for a sub frame, no events are send after an error occurred, but +// before a new navigation happened in this frame. +TEST_F(FrameNavigationStateTest, ErrorStateFrame) { + FrameNavigationState navigation_state; + const long long frame_id1 = 23; + const long long frame_id2 = 42; + const GURL url("http://www.google.com/"); + + navigation_state.TrackFrame(frame_id1, url, true, contents()); + navigation_state.TrackFrame(frame_id2, url, false, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id1)); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id2)); + + // After an error occurred, no further events should be sent. + navigation_state.ErrorOccurredInFrame(frame_id2); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id1)); + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id2)); + + // Navigations to the "unreachable web data" URL should be ignored. + navigation_state.TrackFrame( + frame_id2, GURL(chrome::kUnreachableWebDataURL), false, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id1)); + EXPECT_FALSE(navigation_state.CanSendEvents(frame_id2)); + + // However, when the frame navigates again, it should send events again. + navigation_state.TrackFrame(frame_id2, url, false, contents()); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id1)); + EXPECT_TRUE(navigation_state.CanSendEvents(frame_id2)); +} diff --git a/chrome/browser/extensions/extension_webstore_private_api.cc b/chrome/browser/extensions/extension_webstore_private_api.cc index bf965a3..092d395 100644 --- a/chrome/browser/extensions/extension_webstore_private_api.cc +++ b/chrome/browser/extensions/extension_webstore_private_api.cc @@ -7,25 +7,33 @@ #include <string> #include <vector> +#include "app/l10n_util.h" #include "base/string_util.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/profile_manager.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/net/gaia/gaia_constants.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" #include "net/base/escape.h" namespace { const char* install_base_url = extension_urls::kGalleryUpdateHttpsUrl; -const char kAlreadyLoggedInError[] = "User already logged in"; const char kLoginKey[] = "login"; const char kTokenKey[] = "token"; ProfileSyncService* test_sync_service = NULL; +BrowserSignin* test_signin = NULL; // Returns either the test sync service, or the real one from |profile|. ProfileSyncService* GetSyncService(Profile* profile) { @@ -35,13 +43,46 @@ ProfileSyncService* GetSyncService(Profile* profile) { return profile->GetProfileSyncService(); } +BrowserSignin* GetBrowserSignin(Profile* profile) { + if (test_signin) + return test_signin; + else + return profile->GetBrowserSignin(); +} + bool IsWebStoreURL(Profile* profile, const GURL& url) { ExtensionsService* service = profile->GetExtensionsService(); - Extension* store = service->GetWebStoreApp(); + const Extension* store = service->GetWebStoreApp(); DCHECK(store); return (service->GetExtensionByWebExtent(url) == store); } +// Helper to create a dictionary with login and token properties set from +// the appropriate values in the passed-in |profile|. +DictionaryValue* CreateLoginResult(Profile* profile) { + DictionaryValue* dictionary = new DictionaryValue(); + std::string username = GetBrowserSignin(profile)->GetSignedInUsername(); + dictionary->SetString(kLoginKey, username); + if (!username.empty()) { + TokenService* token_service = profile->GetTokenService(); + if (token_service->HasTokenForService(GaiaConstants::kGaiaService)) { + dictionary->SetString(kTokenKey, + token_service->GetTokenForService( + GaiaConstants::kGaiaService)); + } + } + return dictionary; +} + +// If |profile| is not off the record, returns it. Otherwise returns the real +// (not off the record) default profile. +Profile* GetDefaultProfile(Profile* profile) { + if (!profile->IsOffTheRecord()) + return profile; + else + return g_browser_process->profile_manager()->GetDefaultProfile(); +} + } // namespace // static @@ -51,6 +92,11 @@ void WebstorePrivateApi::SetTestingProfileSyncService( } // static +void WebstorePrivateApi::SetTestingBrowserSignin(BrowserSignin* signin) { + test_signin = signin; +} + +// static void InstallFunction::SetTestingInstallBaseUrl( const char* testing_install_base_url) { install_base_url = testing_install_base_url; @@ -92,13 +138,7 @@ bool InstallFunction::RunImpl() { bool GetBrowserLoginFunction::RunImpl() { if (!IsWebStoreURL(profile_, source_url())) return false; - string16 username = GetSyncService(profile_)->GetAuthenticatedUsername(); - DictionaryValue* dictionary = new DictionaryValue(); - - dictionary->SetString(kLoginKey, username); - // TODO(asargent) - send the browser login token here too if available. - - result_.reset(dictionary); + result_.reset(CreateLoginResult(GetDefaultProfile(profile_))); return true; } @@ -127,50 +167,118 @@ bool SetStoreLoginFunction::RunImpl() { return true; } -PromptBrowserLoginFunction::~PromptBrowserLoginFunction() {} +PromptBrowserLoginFunction::PromptBrowserLoginFunction() + : waiting_for_token_(false) {} + +PromptBrowserLoginFunction::~PromptBrowserLoginFunction() { +} bool PromptBrowserLoginFunction::RunImpl() { if (!IsWebStoreURL(profile_, source_url())) return false; std::string preferred_email; - ProfileSyncService* sync_service = GetSyncService(profile_); if (args_->GetSize() > 0) { EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &preferred_email)); - if (!sync_service->GetAuthenticatedUsername().empty()) { - error_ = kAlreadyLoggedInError; - return false; - } } + Profile* profile = GetDefaultProfile(profile_); + + // Login can currently only be invoked tab-modal. Since this is + // coming from the webstore, we should always have a tab, but check + // just in case. + TabContents* tab = dispatcher()->delegate()->associated_tab_contents(); + if (!tab) + return false; + // We return the result asynchronously, so we addref to keep ourself alive. - // Matched with a Release in OnStateChanged(). + // Matched with a Release in OnLoginSuccess() and OnLoginFailure(). AddRef(); - sync_service->AddObserver(this); - // TODO(mirandac/estade) - make use of |preferred_email| to pre-populate the - // browser login dialog if it was set to non-empty above. - sync_service->ShowLoginDialog(NULL); - - // The response will be sent asynchronously in OnStateChanged(). + // Start listening for notifications about the token. + TokenService* token_service = profile->GetTokenService(); + registrar_.Add(this, + NotificationType::TOKEN_AVAILABLE, + Source<TokenService>(token_service)); + registrar_.Add(this, + NotificationType::TOKEN_REQUEST_FAILED, + Source<TokenService>(token_service)); + + GetBrowserSignin(profile)->RequestSignin(tab, + ASCIIToUTF16(preferred_email), + GetLoginMessage(), + this); + + // The response will be sent asynchronously in OnLoginSuccess/OnLoginFailure. return true; } -void PromptBrowserLoginFunction::OnStateChanged() { - ProfileSyncService* sync_service = GetSyncService(profile_); - // If the setup is finished, we'll report back what happened. - if (!sync_service->SetupInProgress()) { - sync_service->RemoveObserver(this); - DictionaryValue* dictionary = new DictionaryValue(); +string16 PromptBrowserLoginFunction::GetLoginMessage() { + using l10n_util::GetStringUTF16; + using l10n_util::GetStringFUTF16; + + // TODO(johnnyg): This would be cleaner as an HTML template. + // http://crbug.com/60216 + string16 message; + message = ASCIIToUTF16("<p>") + + GetStringUTF16(IDS_WEB_STORE_LOGIN_INTRODUCTION_1) + + ASCIIToUTF16("</p>"); + message = message + ASCIIToUTF16("<p>") + + GetStringFUTF16(IDS_WEB_STORE_LOGIN_INTRODUCTION_2, + GetStringUTF16(IDS_PRODUCT_NAME)) + + ASCIIToUTF16("</p>"); + return message; +} - // TODO(asargent) - send the browser login token here too if available. - string16 username = sync_service->GetAuthenticatedUsername(); - dictionary->SetString(kLoginKey, username); +void PromptBrowserLoginFunction::OnLoginSuccess() { + // Ensure that apps are synced. + // - If the user has already setup sync, we add Apps to the current types. + // - If not, we create a new set which is just Apps. + ProfileSyncService* service = GetSyncService(GetDefaultProfile(profile_)); + syncable::ModelTypeSet types; + if (service->HasSyncSetupCompleted()) + service->GetPreferredDataTypes(&types); + types.insert(syncable::APPS); + service->ChangePreferredDataTypes(types); + service->SetSyncSetupCompleted(); + + // We'll finish up in Observe() when the token is ready. + waiting_for_token_ = true; +} + +void PromptBrowserLoginFunction::OnLoginFailure( + const GoogleServiceAuthError& error) { + SendResponse(false); + // Matches the AddRef in RunImpl(). + Release(); +} - result_.reset(dictionary); - SendResponse(true); +void PromptBrowserLoginFunction::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + // Make sure this notification is for the service we are interested in. + std::string service; + if (type == NotificationType::TOKEN_AVAILABLE) { + TokenService::TokenAvailableDetails* available = + Details<TokenService::TokenAvailableDetails>(details).ptr(); + service = available->service(); + } else if (type == NotificationType::TOKEN_REQUEST_FAILED) { + TokenService::TokenRequestFailedDetails* failed = + Details<TokenService::TokenRequestFailedDetails>(details).ptr(); + service = failed->service(); + } else { + NOTREACHED(); + } - // Matches the AddRef in RunImpl(). - Release(); + if (service != GaiaConstants::kGaiaService) { + return; } + + DCHECK(waiting_for_token_); + + result_.reset(CreateLoginResult(GetDefaultProfile(profile_))); + SendResponse(true); + + // Matches the AddRef in RunImpl(). + Release(); } diff --git a/chrome/browser/extensions/extension_webstore_private_api.h b/chrome/browser/extensions/extension_webstore_private_api.h index a47ba2a..39d883c 100644 --- a/chrome/browser/extensions/extension_webstore_private_api.h +++ b/chrome/browser/extensions/extension_webstore_private_api.h @@ -6,8 +6,11 @@ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_WEBSTORE_PRIVATE_API_H_ #pragma once +#include "chrome/browser/browser_signin.h" #include "chrome/browser/extensions/extension_function.h" -#include "chrome/browser/sync/profile_sync_service_observer.h" +#include "chrome/common/net/gaia/google_service_auth_error.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" class ProfileSyncService; @@ -16,6 +19,10 @@ class WebstorePrivateApi { // Allows you to set the ProfileSyncService the function will use for // testing purposes. static void SetTestingProfileSyncService(ProfileSyncService* service); + + // Allows you to set the BrowserSignin the function will use for + // testing purposes. + static void SetTestingBrowserSignin(BrowserSignin* signin); }; class InstallFunction : public SyncExtensionFunction { @@ -44,15 +51,33 @@ class SetStoreLoginFunction : public SyncExtensionFunction { }; class PromptBrowserLoginFunction : public AsyncExtensionFunction, - public ProfileSyncServiceObserver { + public NotificationObserver, + public BrowserSignin::SigninDelegate { public: - // Implements ProfileSyncServiceObserver interface. - virtual void OnStateChanged(); + PromptBrowserLoginFunction(); + // Implements BrowserSignin::SigninDelegate interface. + virtual void OnLoginSuccess(); + virtual void OnLoginFailure(const GoogleServiceAuthError& error); + + // Implements the NotificationObserver interface. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); protected: virtual ~PromptBrowserLoginFunction(); virtual bool RunImpl(); + private: + // Creates the message for signing in. + virtual string16 GetLoginMessage(); + + // Are we waiting for a token available notification? + bool waiting_for_token_; + + // Used for listening for TokenService notifications. + NotificationRegistrar registrar_; + DECLARE_EXTENSION_FUNCTION_NAME("webstorePrivate.promptBrowserLogin"); }; diff --git a/chrome/browser/extensions/extension_webstore_private_browsertest.cc b/chrome/browser/extensions/extension_webstore_private_browsertest.cc index db9a027..01b8e45 100644 --- a/chrome/browser/extensions/extension_webstore_private_browsertest.cc +++ b/chrome/browser/extensions/extension_webstore_private_browsertest.cc @@ -4,11 +4,17 @@ #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_signin.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_test_message_listener.h" #include "chrome/browser/extensions/extension_webstore_private_api.h" +#include "chrome/browser/net/gaia/token_service.h" +#include "chrome/browser/profile_manager.h" #include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/url_constants.h" #include "chrome/test/ui_test_utils.h" #include "googleurl/src/gurl.h" @@ -26,43 +32,75 @@ const char kTestUrlHostname[] = "www.example.com"; // A fake version of ProfileSyncService used for testing. class FakeProfileSyncService : public ProfileSyncService { public: - // The |username_after_login| parameter determines what this fake - // ProfileSyncService will set the username to when ShowLoginDialog is called. - FakeProfileSyncService(const std::string& initial_username, - const std::string& username_after_login) : - username_(ASCIIToUTF16(initial_username)), - username_after_login_(username_after_login), - observer_(NULL) {} - - virtual ~FakeProfileSyncService() { - EXPECT_TRUE(observer_ == NULL); + FakeProfileSyncService() + : setup_(false) { } + virtual ~FakeProfileSyncService() {} // Overrides of virtual methods in ProfileSyncService. - virtual string16 GetAuthenticatedUsername() const { - return username_; + virtual bool HasSyncSetupCompleted() const { + return setup_; } - virtual void ShowLoginDialog(gfx::NativeWindow) { - EXPECT_TRUE(observer_ != NULL); - username_ = ASCIIToUTF16(username_after_login_); - observer_->OnStateChanged(); + virtual void ChangePreferredDataTypes(const syncable::ModelTypeSet& types) { + types_ = types; } - virtual bool SetupInProgress() const { - return false; + virtual void GetPreferredDataTypes(syncable::ModelTypeSet* types) const { + *types = types_; } - virtual void AddObserver(ProfileSyncServiceObserver* observer) { - EXPECT_TRUE(observer_ == NULL); - observer_ = observer; + virtual void SetSyncSetupCompleted() { + setup_ = true; } - virtual void RemoveObserver(ProfileSyncServiceObserver* observer) { - EXPECT_TRUE(observer == observer_); - observer_ = NULL; + + private: + bool setup_; + syncable::ModelTypeSet types_; +}; + +class FakeBrowserSignin : public BrowserSignin { + public: + // The |username_after_login| parameter determines what this fake + // BrowserSignin will set the username to when ShowLoginDialog is called. + FakeBrowserSignin(bool should_succeed, + const std::string& initial_username, + const std::string& username_after_login) + : BrowserSignin(NULL), + should_succeed_(should_succeed), + username_(initial_username), + username_after_login_(username_after_login) { + } + virtual ~FakeBrowserSignin() {} + + virtual std::string GetSignedInUsername() const { + return username_; + } + + virtual void RequestSignin(TabContents* tab_contents, + const string16& preferred_email, + const string16& message, + SigninDelegate* delegate) { + if (should_succeed_) { + // Simulate valid login. + username_ = username_after_login_; + delegate->OnLoginSuccess(); + + // Fake a token available notification. + Profile* profile = tab_contents->profile(); + if (profile->IsOffTheRecord()) { + profile = g_browser_process->profile_manager()->GetDefaultProfile(); + } + TokenService* token_service = profile->GetTokenService(); + token_service->IssueAuthTokenForTest(GaiaConstants::kGaiaService, + "new_token"); + } else { + delegate->OnLoginFailure(GoogleServiceAuthError( + GoogleServiceAuthError::REQUEST_CANCELED)); + } } private: - string16 username_; + bool should_succeed_; + std::string username_; std::string username_after_login_; - ProfileSyncServiceObserver* observer_; }; class ExtensionWebstorePrivateBrowserTest : public ExtensionBrowserTest { @@ -90,18 +128,54 @@ class ExtensionWebstorePrivateBrowserTest : public ExtensionBrowserTest { return base_url.ReplaceComponents(replacements); } - void RunLoginTest(const std::string& relative_path, - const std::string& initial_login, - const std::string& login_result) { - FakeProfileSyncService sync_service(initial_login, login_result); + void RunLoginTestImpl(bool incognito, + const std::string& relative_path, + const std::string& initial_login, + bool login_succeeds, + const std::string& login_result) { + // Clear the token service so previous tests don't affect things. + TokenService* token_service = browser()->profile()->GetTokenService(); + token_service->ResetCredentialsInMemory(); + if (!initial_login.empty()) { + // Initialize the token service with an existing token. + token_service->IssueAuthTokenForTest(GaiaConstants::kGaiaService, + "existing_token"); + } + + FakeProfileSyncService sync_service; + FakeBrowserSignin signin(login_succeeds, initial_login, login_result); WebstorePrivateApi::SetTestingProfileSyncService(&sync_service); + WebstorePrivateApi::SetTestingBrowserSignin(&signin); + ExtensionTestMessageListener listener("success", false); GURL url = GetUrl(relative_path); - ui_test_utils::NavigateToURL(browser(), url); + if (incognito) { + ui_test_utils::OpenURLOffTheRecord(browser()->profile(), url); + } else { + ui_test_utils::NavigateToURL(browser(), url); + } EXPECT_TRUE(listener.WaitUntilSatisfied()); + + WebstorePrivateApi::SetTestingBrowserSignin(NULL); WebstorePrivateApi::SetTestingProfileSyncService(NULL); } + void RunLoginTest(const std::string& relative_path, + const std::string& initial_login, + bool login_succeeds, + const std::string& login_result) { + RunLoginTestImpl(true, + relative_path, + initial_login, + login_succeeds, + login_result); + RunLoginTestImpl(false, + relative_path, + initial_login, + login_succeeds, + login_result); + } + protected: std::string test_url_base_; }; @@ -110,10 +184,13 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateBrowserTest, BrowserLogin) { host_resolver()->AddRule(kTestUrlHostname, "127.0.0.1"); ASSERT_TRUE(test_server()->Start()); - RunLoginTest("browser_login/expect_nonempty.html", "foo@bar.com", ""); - RunLoginTest("browser_login/prompt_no_preferred.html", "", ""); - RunLoginTest("browser_login/prompt_preferred.html", "", "foo@bar.com"); - RunLoginTest("browser_login/prompt_already_logged_in_error.html", - "foo@bar.com", - "foo@bar.com"); + RunLoginTest("browser_login/expect_nonempty.html", + "foo@bar.com", false, ""); + + RunLoginTest("browser_login/prompt_no_preferred.html", "", true, ""); + + RunLoginTest("browser_login/prompt_preferred.html", "", true, "foo@bar.com"); + + RunLoginTest("browser_login/prompt_login_fails.html", + "", false, "foo@bar.com"); } diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index 33fade0..4227333 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -39,6 +39,7 @@ #include "chrome/browser/extensions/extension_updater.h" #include "chrome/browser/extensions/extension_webnavigation_api.h" #include "chrome/browser/extensions/external_extension_provider.h" +#include "chrome/browser/extensions/external_policy_extension_provider.h" #include "chrome/browser/extensions/external_pref_extension_provider.h" #include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/prefs/pref_service.h" @@ -94,7 +95,7 @@ bool ShouldReloadExtensionManifest(const ExtensionInfo& info) { return extension_l10n_util::ShouldRelocalizeManifest(info); } -void GetExplicitOriginsInExtent(Extension* extension, +void GetExplicitOriginsInExtent(const Extension* extension, std::vector<GURL>* origins) { typedef std::vector<URLPattern> PatternList; std::set<GURL> set; @@ -150,6 +151,15 @@ PendingExtensionInfo::PendingExtensionInfo() enable_incognito_on_install(false), install_source(Extension::INVALID) {} + +ExtensionsService::ExtensionRuntimeData::ExtensionRuntimeData() + : background_page_ready(false), + being_upgraded(false) { +} + +ExtensionsService::ExtensionRuntimeData::~ExtensionRuntimeData() { +} + // ExtensionsService. const char* ExtensionsService::kInstallDirectoryName = "Extensions"; @@ -164,7 +174,8 @@ class ExtensionsServiceBackend // |install_directory| is a path where to look for extensions to load. // |load_external_extensions| indicates whether or not backend should load // external extensions listed in JSON file and Windows registry. - ExtensionsServiceBackend(const FilePath& install_directory, + ExtensionsServiceBackend(PrefService* prefs, + const FilePath& install_directory, bool load_external_extensions); // Loads a single extension from |path| where |path| is the top directory of @@ -179,22 +190,20 @@ class ExtensionsServiceBackend // Check externally updated extensions for updates and install if necessary. // Errors are reported through ExtensionErrorReporter. Succcess is not // reported. - void CheckForExternalUpdates(std::set<std::string> ids_to_ignore, + void CheckForExternalUpdates(const std::set<std::string>& ids_to_ignore, scoped_refptr<ExtensionsService> frontend); // For the extension in |version_path| with |id|, check to see if it's an // externally managed extension. If so, tell the frontend to uninstall it. void CheckExternalUninstall(scoped_refptr<ExtensionsService> frontend, - const std::string& id, - Extension::Location location); + const std::string& id); // Clear all ExternalExtensionProviders. void ClearProvidersForTesting(); - // Sets an ExternalExtensionProvider for the service to use during testing. - // |location| specifies what type of provider should be added. - void SetProviderForTesting(Extension::Location location, - ExternalExtensionProvider* test_provider); + // Adds an ExternalExtensionProvider for the service to use during testing. + // Takes ownership of |test_provider|. + void AddProviderForTesting(ExternalExtensionProvider* test_provider); // ExternalExtensionProvider::Visitor implementation. virtual void OnExternalExtensionFileFound(const std::string& id, @@ -203,7 +212,8 @@ class ExtensionsServiceBackend Extension::Location location); virtual void OnExternalExtensionUpdateUrlFound(const std::string& id, - const GURL& update_url); + const GURL& update_url, + Extension::Location location); // Reloads the given extensions from their manifests on disk (instead of what // we have cached in the prefs). @@ -226,23 +236,13 @@ class ExtensionsServiceBackend // Note: We take ownership of |extension|. void OnExtensionUnpacked(const FilePath& crx_path, const FilePath& unpacked_path, - Extension* extension, + const Extension* extension, const std::string expected_id); // Notify the frontend that there was an error loading an extension. void ReportExtensionLoadError(const FilePath& extension_path, const std::string& error); - // Lookup an external extension by |id| by going through all registered - // external extension providers until we find a provider that contains an - // extension that matches. If |version| is not NULL, the extension version - // will be returned (caller is responsible for deleting that pointer). - // |location| can also be null, if not needed. Returns true if extension is - // found, false otherwise. - bool LookupExternalExtension(const std::string& id, - Version** version, - Extension::Location* location); - // This is a naked pointer which is set by each entry point. // The entry point is responsible for ensuring lifetime. ExtensionsService* frontend_; @@ -253,12 +253,12 @@ class ExtensionsServiceBackend // Whether errors result in noisy alerts. bool alert_on_error_; - // A map from external extension type to the external extension provider - // for that type. Because a single provider may handle more than one - // external extension type, more than one key may map to the same object. - typedef std::map<Extension::Location, - linked_ptr<ExternalExtensionProvider> > ProviderMap; - ProviderMap external_extension_providers_; + // A collection of external extension providers. Each provider reads + // a source of external extension information. Examples include the + // windows registry and external_extensions.json. + typedef std::vector<linked_ptr<ExternalExtensionProvider> > + ProviderCollection; + ProviderCollection external_extension_providers_; // Set to true by OnExternalExtensionUpdateUrlFound() when an external // extension URL is found. Used in CheckForExternalUpdates() to see @@ -269,6 +269,7 @@ class ExtensionsServiceBackend }; ExtensionsServiceBackend::ExtensionsServiceBackend( + PrefService* prefs, const FilePath& install_directory, bool load_external_extensions) : frontend_(NULL), @@ -281,18 +282,20 @@ ExtensionsServiceBackend::ExtensionsServiceBackend( // TODO(aa): This ends up doing blocking IO on the UI thread because it reads // pref data in the ctor and that is called on the UI thread. Would be better // to re-read data each time we list external extensions, anyway. - external_extension_providers_[Extension::EXTERNAL_PREF] = + external_extension_providers_.push_back( linked_ptr<ExternalExtensionProvider>( - new ExternalPrefExtensionProvider()); - // EXTERNAL_PREF_DOWNLOAD and EXTERNAL_PREF extensions are handled by the - // same object. - external_extension_providers_[Extension::EXTERNAL_PREF_DOWNLOAD] = - external_extension_providers_[Extension::EXTERNAL_PREF]; + new ExternalPrefExtensionProvider())); #if defined(OS_WIN) - external_extension_providers_[Extension::EXTERNAL_REGISTRY] = + external_extension_providers_.push_back( linked_ptr<ExternalExtensionProvider>( - new ExternalRegistryExtensionProvider()); + new ExternalRegistryExtensionProvider())); #endif + ExternalPolicyExtensionProvider* policy_extension_provider = + new ExternalPolicyExtensionProvider(); + policy_extension_provider->SetPreferences(prefs); + external_extension_providers_.push_back( + linked_ptr<ExternalExtensionProvider>(policy_extension_provider)); + } ExtensionsServiceBackend::~ExtensionsServiceBackend() { @@ -300,6 +303,8 @@ ExtensionsServiceBackend::~ExtensionsServiceBackend() { void ExtensionsServiceBackend::LoadSingleExtension( const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + frontend_ = frontend; // Explicit UI loads are always noisy. @@ -309,11 +314,11 @@ void ExtensionsServiceBackend::LoadSingleExtension( file_util::AbsolutePath(&extension_path); std::string error; - Extension* extension = extension_file_util::LoadExtension( + scoped_refptr<const Extension> extension(extension_file_util::LoadExtension( extension_path, Extension::LOAD, false, // Don't require id - &error); + &error)); if (!extension) { ReportExtensionLoadError(extension_path, error); @@ -330,6 +335,7 @@ void ExtensionsServiceBackend::LoadSingleExtension( void ExtensionsServiceBackend::ReportExtensionLoadError( const FilePath& extension_path, const std::string &error) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod( @@ -338,22 +344,6 @@ void ExtensionsServiceBackend::ReportExtensionLoadError( error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_)); } -bool ExtensionsServiceBackend::LookupExternalExtension( - const std::string& id, Version** version, Extension::Location* location) { - scoped_ptr<Version> extension_version; - for (ProviderMap::const_iterator i = external_extension_providers_.begin(); - i != external_extension_providers_.end(); ++i) { - const ExternalExtensionProvider* provider = i->second.get(); - extension_version.reset(provider->RegisteredVersion(id, location)); - if (extension_version.get()) { - if (version) - *version = extension_version.release(); - return true; - } - } - return false; -} - // Some extensions will autoupdate themselves externally from Chrome. These // are typically part of some larger client application package. To support // these, the extension will register its location in the the preferences file @@ -361,8 +351,10 @@ bool ExtensionsServiceBackend::LookupExternalExtension( // check that location for a .crx file, which it will then install locally if // a new version is available. void ExtensionsServiceBackend::CheckForExternalUpdates( - std::set<std::string> ids_to_ignore, + const std::set<std::string>& ids_to_ignore, scoped_refptr<ExtensionsService> frontend) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + // Note that this installation is intentionally silent (since it didn't // go through the front-end). Extensions that are registered in this // way are effectively considered 'pre-bundled', and so implicitly @@ -374,10 +366,10 @@ void ExtensionsServiceBackend::CheckForExternalUpdates( // Ask each external extension provider to give us a call back for each // extension they know about. See OnExternalExtension(File|UpdateUrl)Found. - - for (ProviderMap::const_iterator i = external_extension_providers_.begin(); + ProviderCollection::const_iterator i; + for (i = external_extension_providers_.begin(); i != external_extension_providers_.end(); ++i) { - ExternalExtensionProvider* provider = i->second.get(); + ExternalExtensionProvider* provider = i->get(); provider->VisitRegisteredExtension(this, ids_to_ignore); } @@ -390,21 +382,17 @@ void ExtensionsServiceBackend::CheckForExternalUpdates( } void ExtensionsServiceBackend::CheckExternalUninstall( - scoped_refptr<ExtensionsService> frontend, const std::string& id, - Extension::Location location) { + scoped_refptr<ExtensionsService> frontend, const std::string& id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + // Check if the providers know about this extension. - ProviderMap::const_iterator i = external_extension_providers_.find(location); - if (i == external_extension_providers_.end()) { - NOTREACHED() << "CheckExternalUninstall called for non-external extension " - << location; - return; + ProviderCollection::const_iterator i; + for (i = external_extension_providers_.begin(); + i != external_extension_providers_.end(); ++i) { + if (i->get()->HasExtension(id)) + return; // Yup, known extension, don't uninstall. } - scoped_ptr<Version> version; - version.reset(i->second->RegisteredVersion(id, NULL)); - if (version.get()) - return; // Yup, known extension, don't uninstall. - // This is an external extension that we don't have registered. Uninstall. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -416,17 +404,18 @@ void ExtensionsServiceBackend::ClearProvidersForTesting() { external_extension_providers_.clear(); } -void ExtensionsServiceBackend::SetProviderForTesting( - Extension::Location location, +void ExtensionsServiceBackend::AddProviderForTesting( ExternalExtensionProvider* test_provider) { DCHECK(test_provider); - external_extension_providers_[location] = - linked_ptr<ExternalExtensionProvider>(test_provider); + external_extension_providers_.push_back( + linked_ptr<ExternalExtensionProvider>(test_provider)); } void ExtensionsServiceBackend::OnExternalExtensionFileFound( const std::string& id, const Version* version, const FilePath& path, Extension::Location location) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(version); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -437,13 +426,21 @@ void ExtensionsServiceBackend::OnExternalExtensionFileFound( void ExtensionsServiceBackend::OnExternalExtensionUpdateUrlFound( const std::string& id, - const GURL& update_url) { + const GURL& update_url, + Extension::Location location) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + if (frontend_->GetExtensionById(id, true)) { // Already installed. Do not change the update URL that the extension set. return; } - frontend_->AddPendingExtensionFromExternalUpdateUrl(id, update_url); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod( + frontend_, + &ExtensionsService::AddPendingExtensionFromExternalUpdateUrl, + id, update_url, location)); external_extension_added_ |= true; } @@ -451,6 +448,8 @@ void ExtensionsServiceBackend::ReloadExtensionManifests( ExtensionPrefs::ExtensionsInfo* extensions_to_reload, base::TimeTicks start_time, scoped_refptr<ExtensionsService> frontend) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + frontend_ = frontend; for (size_t i = 0; i < extensions_to_reload->size(); ++i) { @@ -460,7 +459,7 @@ void ExtensionsServiceBackend::ReloadExtensionManifests( // We need to reload original manifest in order to localize properly. std::string error; - scoped_ptr<Extension> extension(extension_file_util::LoadExtension( + scoped_refptr<const Extension> extension(extension_file_util::LoadExtension( info->extension_path, info->extension_location, false, &error)); if (extension.get()) @@ -490,9 +489,9 @@ bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url, return true; } - Extension* download_extension = GetExtensionByWebExtent(download_url); - Extension* referrer_extension = GetExtensionByWebExtent(referrer_url); - Extension* webstore_app = GetWebStoreApp(); + const Extension* download_extension = GetExtensionByWebExtent(download_url); + const Extension* referrer_extension = GetExtensionByWebExtent(referrer_url); + const Extension* webstore_app = GetWebStoreApp(); bool referrer_valid = (referrer_extension == webstore_app); bool download_valid = (download_extension == webstore_app); @@ -547,7 +546,7 @@ bool ExtensionsService::UninstallExtensionHelper( extensions_service->UninstallExtension(extension_id, false); } else { LOG(WARNING) << "Attempted uninstallation of non-existent extension with " - << "extension with id: " << extension_id; + << "id: " << extension_id; return false; } @@ -568,6 +567,8 @@ ExtensionsService::ExtensionsService(Profile* profile, ALLOW_THIS_IN_INITIALIZER_LIST(toolbar_model_(this)), default_apps_(profile->GetPrefs()), event_routers_initialized_(false) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Figure out if extension installation should be enabled. if (command_line->HasSwitch(switches::kDisableExtensions)) { extensions_enabled_ = false; @@ -594,7 +595,8 @@ ExtensionsService::ExtensionsService(Profile* profile, update_frequency); } - backend_ = new ExtensionsServiceBackend(install_directory_, + backend_ = new ExtensionsServiceBackend(profile->GetPrefs(), + install_directory_, extensions_enabled_); // Use monochrome icons for Omnibox icons. @@ -629,6 +631,8 @@ void ExtensionsService::InitEventRouters() { } void ExtensionsService::Init() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(!ready_); DCHECK_EQ(extensions_.size(), 0u); @@ -667,6 +671,8 @@ namespace { void ExtensionsService::UpdateExtension(const std::string& id, const FilePath& extension_path, const GURL& download_url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + PendingExtensionMap::const_iterator it = pending_extensions_.find(id); bool is_pending_extension = (it != pending_extensions_.end()); @@ -718,7 +724,8 @@ void ExtensionsService::AddPendingExtensionFromSync( } void ExtensionsService::AddPendingExtensionFromExternalUpdateUrl( - const std::string& id, const GURL& update_url) { + const std::string& id, const GURL& update_url, + Extension::Location location) { // Add the extension to this list of extensions to update. const PendingExtensionInfo::ExpectedCrxType kExpectedCrxType = PendingExtensionInfo::UNKNOWN; @@ -736,7 +743,7 @@ void ExtensionsService::AddPendingExtensionFromExternalUpdateUrl( AddPendingExtensionInternal(id, update_url, kExpectedCrxType, kIsFromSync, kInstallSilently, kEnableOnInstall, kEnableIncognitoOnInstall, - Extension::EXTERNAL_PREF_DOWNLOAD); + location); } void ExtensionsService::AddPendingExtensionFromDefaultAppList( @@ -766,6 +773,27 @@ void ExtensionsService::AddPendingExtensionInternal( bool is_from_sync, bool install_silently, bool enable_on_install, bool enable_incognito_on_install, Extension::Location install_source) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // If a non-sync update is pending, a sync request should not + // overwrite it. This is important for external extensions. + // If an external extension download is pending, and the user has + // the extension in their sync profile, the install should set the + // type to be external. An external extension should not be + // rejected if it fails the safty checks for a syncable extension. + // TODO(skerner): Work out other potential overlapping conditions. + // (crbug/61000) + PendingExtensionMap::iterator it = pending_extensions_.find(id); + if (it != pending_extensions_.end()) { + VLOG(1) << "Extension id " << id + << " was entered for update more than once." + << " old is_from_sync = " << it->second.is_from_sync + << " new is_from_sync = " << is_from_sync; + if (!it->second.is_from_sync && is_from_sync) + return; + } + + pending_extensions_[id] = PendingExtensionInfo(update_url, expected_crx_type, is_from_sync, install_silently, enable_on_install, @@ -773,8 +801,9 @@ void ExtensionsService::AddPendingExtensionInternal( } void ExtensionsService::ReloadExtension(const std::string& extension_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); FilePath path; - Extension* current_extension = GetExtensionById(extension_id, false); + const Extension* current_extension = GetExtensionById(extension_id, false); // Disable the extension if it's loaded. It might not be loaded if it crashed. if (current_extension) { @@ -816,7 +845,10 @@ void ExtensionsService::ReloadExtension(const std::string& extension_id) { void ExtensionsService::UninstallExtension(const std::string& extension_id, bool external_uninstall) { - Extension* extension = GetExtensionByIdInternal(extension_id, true, true); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + const Extension* extension = + GetExtensionByIdInternal(extension_id, true, true); // Callers should not send us nonexistent extensions. DCHECK(extension); @@ -870,7 +902,10 @@ void ExtensionsService::ClearExtensionData(const GURL& extension_url) { } void ExtensionsService::EnableExtension(const std::string& extension_id) { - Extension* extension = GetExtensionByIdInternal(extension_id, false, true); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + const Extension* extension = + GetExtensionByIdInternal(extension_id, false, true); if (!extension) { return; } @@ -878,7 +913,7 @@ void ExtensionsService::EnableExtension(const std::string& extension_id) { extension_prefs_->SetExtensionState(extension, Extension::ENABLED); // Move it over to the enabled list. - extensions_.push_back(extension); + extensions_.push_back(make_scoped_refptr(extension)); ExtensionList::iterator iter = std::find(disabled_extensions_.begin(), disabled_extensions_.end(), extension); @@ -892,7 +927,10 @@ void ExtensionsService::EnableExtension(const std::string& extension_id) { } void ExtensionsService::DisableExtension(const std::string& extension_id) { - Extension* extension = GetExtensionByIdInternal(extension_id, true, false); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + const Extension* extension = + GetExtensionByIdInternal(extension_id, true, false); // The extension may have been disabled already. if (!extension) return; @@ -900,7 +938,7 @@ void ExtensionsService::DisableExtension(const std::string& extension_id) { extension_prefs_->SetExtensionState(extension, Extension::DISABLED); // Move it over to the disabled list. - disabled_extensions_.push_back(extension); + disabled_extensions_.push_back(make_scoped_refptr(extension)); ExtensionList::iterator iter = std::find(extensions_.begin(), extensions_.end(), extension); @@ -933,20 +971,19 @@ void ExtensionsService::LoadComponentExtensions() { continue; } - scoped_ptr<Extension> extension(new Extension(it->root_directory)); - extension->set_location(Extension::COMPONENT); - std::string error; - if (!extension->InitFromValue( - *static_cast<DictionaryValue*>(manifest.get()), - true, // require key - &error)) { + scoped_refptr<const Extension> extension(Extension::Create( + it->root_directory, + Extension::COMPONENT, + *static_cast<DictionaryValue*>(manifest.get()), + true, // require key + &error)); + if (!extension.get()) { NOTREACHED() << error; return; } - OnExtensionLoaded(extension.release(), false); // Don't allow privilege - // increase. + OnExtensionLoaded(extension, false); // Don't allow privilege increase. } } @@ -1075,15 +1112,14 @@ void ExtensionsService::ContinueLoadAllExtensions( void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info, bool write_to_prefs) { std::string error; - Extension* extension = NULL; + scoped_refptr<const Extension> extension(NULL); if (!extension_prefs_->IsExtensionAllowedByPolicy(info.extension_id)) { error = errors::kDisabledByPolicy; } else if (info.extension_manifest.get()) { - scoped_ptr<Extension> tmp(new Extension(info.extension_path)); bool require_key = info.extension_location != Extension::LOAD; - tmp->set_location(info.extension_location); - if (tmp->InitFromValue(*info.extension_manifest, require_key, &error)) - extension = tmp.release(); + extension = Extension::Create( + info.extension_path, info.extension_location, *info.extension_manifest, + require_key, &error); } else { error = errors::kManifestUnreadable; } @@ -1108,12 +1144,11 @@ void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info, backend_.get(), &ExtensionsServiceBackend::CheckExternalUninstall, scoped_refptr<ExtensionsService>(this), - info.extension_id, - info.extension_location)); + info.extension_id)); } } -void ExtensionsService::NotifyExtensionLoaded(Extension* extension) { +void ExtensionsService::NotifyExtensionLoaded(const Extension* extension) { // The ChromeURLRequestContexts need to be first to know that the extension // was loaded, otherwise a race can arise where a renderer that is created // for the extension may try to load an extension URL with an extension id @@ -1136,14 +1171,14 @@ void ExtensionsService::NotifyExtensionLoaded(Extension* extension) { NotificationService::current()->Notify( NotificationType::EXTENSION_LOADED, Source<Profile>(profile_), - Details<Extension>(extension)); + Details<const Extension>(extension)); } -void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) { +void ExtensionsService::NotifyExtensionUnloaded(const Extension* extension) { NotificationService::current()->Notify( NotificationType::EXTENSION_UNLOADED, Source<Profile>(profile_), - Details<Extension>(extension)); + Details<const Extension>(extension)); if (profile_) { profile_->UnregisterExtensionWithRequestContexts(extension); @@ -1159,7 +1194,7 @@ void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) { } } -void ExtensionsService::GrantProtectedStorage(Extension* extension) { +void ExtensionsService::GrantProtectedStorage(const Extension* extension) { DCHECK(extension->is_app()) << "Only Apps are allowed protected storage."; std::vector<GURL> origins; GetExplicitOriginsInExtent(extension, &origins); @@ -1167,7 +1202,7 @@ void ExtensionsService::GrantProtectedStorage(Extension* extension) { ++protected_storage_map_[origins[i]]; } -void ExtensionsService::RevokeProtectedStorage(Extension* extension) { +void ExtensionsService::RevokeProtectedStorage(const Extension* extension) { DCHECK(extension->is_app()) << "Attempting to revoke protected storage from " << " a non-app extension."; std::vector<GURL> origins; @@ -1180,7 +1215,7 @@ void ExtensionsService::RevokeProtectedStorage(Extension* extension) { } } -void ExtensionsService::GrantUnlimitedStorage(Extension* extension) { +void ExtensionsService::GrantUnlimitedStorage(const Extension* extension) { DCHECK(extension->HasApiPermission(Extension::kUnlimitedStoragePermission)); std::vector<GURL> origins; GetExplicitOriginsInExtent(extension, &origins); @@ -1215,7 +1250,7 @@ void ExtensionsService::GrantUnlimitedStorage(Extension* extension) { } } -void ExtensionsService::RevokeUnlimitedStorage(Extension* extension) { +void ExtensionsService::RevokeUnlimitedStorage(const Extension* extension) { DCHECK(extension->HasApiPermission(Extension::kUnlimitedStoragePermission)); std::vector<GURL> origins; GetExplicitOriginsInExtent(extension, &origins); @@ -1264,7 +1299,7 @@ void ExtensionsService::UpdateExtensionBlacklist( // Loop current extensions, unload installed extensions. for (ExtensionList::const_iterator iter = extensions_.begin(); iter != extensions_.end(); ++iter) { - Extension* extension = (*iter); + const Extension* extension = (*iter); if (blacklist_set.find(extension->id()) != blacklist_set.end()) { to_be_removed.push_back(extension->id()); } @@ -1288,7 +1323,7 @@ void ExtensionsService::CheckAdminBlacklist() { // Loop through extensions list, unload installed extensions. for (ExtensionList::const_iterator iter = extensions_.begin(); iter != extensions_.end(); ++iter) { - Extension* extension = (*iter); + const Extension* extension = (*iter); if (!extension_prefs_->IsExtensionAllowedByPolicy(extension->id())) to_be_removed.push_back(extension->id()); } @@ -1309,7 +1344,7 @@ bool ExtensionsService::IsIncognitoEnabled(const Extension* extension) { return extension_prefs_->IsIncognitoEnabled(extension->id()); } -void ExtensionsService::SetIsIncognitoEnabled(Extension* extension, +void ExtensionsService::SetIsIncognitoEnabled(const Extension* extension, bool enabled) { extension_prefs_->SetIsIncognitoEnabled(extension->id(), enabled); @@ -1323,18 +1358,26 @@ void ExtensionsService::SetIsIncognitoEnabled(Extension* extension, } } +bool ExtensionsService::CanCrossIncognito(const Extension* extension) { + // We allow the extension to see events and data from another profile iff it + // uses "spanning" behavior and it has incognito access. "split" mode + // extensions only see events for a matching profile. + return IsIncognitoEnabled(extension) && !extension->incognito_split_mode(); +} + bool ExtensionsService::AllowFileAccess(const Extension* extension) { return (CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableExtensionsFileAccessCheck) || extension_prefs_->AllowFileAccess(extension->id())); } -void ExtensionsService::SetAllowFileAccess(Extension* extension, bool allow) { +void ExtensionsService::SetAllowFileAccess(const Extension* extension, + bool allow) { extension_prefs_->SetAllowFileAccess(extension->id(), allow); NotificationService::current()->Notify( NotificationType::EXTENSION_USER_SCRIPTS_UPDATED, Source<Profile>(profile_), - Details<Extension>(extension)); + Details<const Extension>(extension)); } void ExtensionsService::CheckForExternalUpdates() { @@ -1352,7 +1395,7 @@ void ExtensionsService::CheckForExternalUpdates() { void ExtensionsService::UnloadExtension(const std::string& extension_id) { // Make sure the extension gets deleted after we return from this function. - scoped_ptr<Extension> extension( + scoped_refptr<const Extension> extension( GetExtensionByIdInternal(extension_id, true, true)); // Callers should not send us nonexistent extensions. @@ -1365,6 +1408,9 @@ void ExtensionsService::UnloadExtension(const std::string& extension_id) { // Clean up if the extension is meant to be enabled after a reload. disabled_extension_paths_.erase(extension->id()); + // Clean up runtime data. + extension_runtime_data_.erase(extension_id); + ExtensionDOMUI::UnregisterChromeURLOverrides(profile_, extension->GetChromeURLOverrides()); @@ -1376,7 +1422,7 @@ void ExtensionsService::UnloadExtension(const std::string& extension_id) { NotificationService::current()->Notify( NotificationType::EXTENSION_UNLOADED_DISABLED, Source<Profile>(profile_), - Details<Extension>(extension.get())); + Details<const Extension>(extension.get())); return; } @@ -1390,12 +1436,9 @@ void ExtensionsService::UnloadExtension(const std::string& extension_id) { } void ExtensionsService::UnloadAllExtensions() { - STLDeleteContainerPointers(extensions_.begin(), extensions_.end()); extensions_.clear(); - - STLDeleteContainerPointers(disabled_extensions_.begin(), - disabled_extensions_.end()); disabled_extensions_.clear(); + extension_runtime_data_.clear(); // TODO(erikkay) should there be a notification for this? We can't use // EXTENSION_UNLOADED since that implies that the extension has been disabled @@ -1436,10 +1479,10 @@ void ExtensionsService::OnLoadedInstalledExtensions() { NotificationService::NoDetails()); } -void ExtensionsService::OnExtensionLoaded(Extension* extension, +void ExtensionsService::OnExtensionLoaded(const Extension* extension, bool allow_privilege_increase) { // Ensure extension is deleted unless we transfer ownership. - scoped_ptr<Extension> scoped_extension(extension); + scoped_refptr<const Extension> scoped_extension(extension); // The extension is now loaded, remove its data from unloaded extension map. unloaded_extension_paths_.erase(extension->id()); @@ -1455,7 +1498,8 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, extension->location() == Extension::LOAD || extension->location() == Extension::COMPONENT || Extension::IsExternalLocation(extension->location())) { - Extension* old = GetExtensionByIdInternal(extension->id(), true, true); + const Extension* old = GetExtensionByIdInternal(extension->id(), + true, true); if (old) { // CrxInstaller should have guaranteed that we aren't downgrading. CHECK(extension->version()->CompareTo(*(old->version())) >= 0); @@ -1467,8 +1511,8 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, // Extensions get upgraded if silent upgrades are allowed, otherwise // they get disabled. if (allow_silent_upgrade) { - old->set_being_upgraded(true); - extension->set_being_upgraded(true); + SetBeingUpgraded(old, true); + SetBeingUpgraded(extension, true); } // To upgrade an extension in place, unload the old one and @@ -1486,7 +1530,7 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, switch (extension_prefs_->GetExtensionState(extension->id())) { case Extension::ENABLED: - extensions_.push_back(scoped_extension.release()); + extensions_.push_back(scoped_extension); NotifyExtensionLoaded(extension); @@ -1494,11 +1538,11 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, extension->GetChromeURLOverrides()); break; case Extension::DISABLED: - disabled_extensions_.push_back(scoped_extension.release()); + disabled_extensions_.push_back(scoped_extension); NotificationService::current()->Notify( NotificationType::EXTENSION_UPDATE_DISABLED, Source<Profile>(profile_), - Details<Extension>(extension)); + Details<const Extension>(extension)); break; default: NOTREACHED(); @@ -1506,7 +1550,7 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, } } - extension->set_being_upgraded(false); + SetBeingUpgraded(extension, false); UpdateActiveExtensionsInCrashReporter(); @@ -1524,17 +1568,20 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, void ExtensionsService::UpdateActiveExtensionsInCrashReporter() { std::set<std::string> extension_ids; for (size_t i = 0; i < extensions_.size(); ++i) { - if (!extensions_[i]->is_theme()) + if (!extensions_[i]->is_theme() && + extensions_[i]->location() != Extension::COMPONENT) extension_ids.insert(extensions_[i]->id()); } child_process_logging::SetActiveExtensions(extension_ids); } -void ExtensionsService::OnExtensionInstalled(Extension* extension, +void ExtensionsService::OnExtensionInstalled(const Extension* extension, bool allow_privilege_increase) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Ensure extension is deleted unless we transfer ownership. - scoped_ptr<Extension> scoped_extension(extension); + scoped_refptr<const Extension> scoped_extension(extension); Extension::State initial_state = Extension::DISABLED; bool initial_enable_incognito = false; PendingExtensionMap::iterator it = @@ -1656,12 +1703,12 @@ void ExtensionsService::OnExtensionInstalled(Extension* extension, NotificationService::current()->Notify( NotificationType::THEME_INSTALLED, Source<Profile>(profile_), - Details<Extension>(extension)); + Details<const Extension>(extension)); } else { NotificationService::current()->Notify( NotificationType::EXTENSION_INSTALLED, Source<Profile>(profile_), - Details<Extension>(extension)); + Details<const Extension>(extension)); } if (extension->is_app()) { @@ -1671,12 +1718,11 @@ void ExtensionsService::OnExtensionInstalled(Extension* extension, } // Transfer ownership of |extension| to OnExtensionLoaded. - OnExtensionLoaded(scoped_extension.release(), allow_privilege_increase); + OnExtensionLoaded(scoped_extension, allow_privilege_increase); } -Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id, - bool include_enabled, - bool include_disabled) { +const Extension* ExtensionsService::GetExtensionByIdInternal( + const std::string& id, bool include_enabled, bool include_disabled) { std::string lowercase_id = StringToLowerASCII(id); if (include_enabled) { for (ExtensionList::const_iterator iter = extensions_.begin(); @@ -1695,16 +1741,16 @@ Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id, return NULL; } -Extension* ExtensionsService::GetWebStoreApp() { +const Extension* ExtensionsService::GetWebStoreApp() { return GetExtensionById(extension_misc::kWebStoreAppId, false); } -Extension* ExtensionsService::GetExtensionByURL(const GURL& url) { +const Extension* ExtensionsService::GetExtensionByURL(const GURL& url) { return url.scheme() != chrome::kExtensionScheme ? NULL : GetExtensionById(url.host(), false); } -Extension* ExtensionsService::GetExtensionByWebExtent(const GURL& url) { +const Extension* ExtensionsService::GetExtensionByWebExtent(const GURL& url) { for (size_t i = 0; i < extensions_.size(); ++i) { if (extensions_[i]->web_extent().ContainsURL(url)) return extensions_[i]; @@ -1718,11 +1764,11 @@ bool ExtensionsService::ExtensionBindingsAllowed(const GURL& url) { return true; // Allow bindings for all component, hosted apps. - Extension* extension = GetExtensionByWebExtent(url); + const Extension* extension = GetExtensionByWebExtent(url); return (extension && extension->location() == Extension::COMPONENT); } -Extension* ExtensionsService::GetExtensionByOverlappingWebExtent( +const Extension* ExtensionsService::GetExtensionByOverlappingWebExtent( const ExtensionExtent& extent) { for (size_t i = 0; i < extensions_.size(); ++i) { if (extensions_[i]->web_extent().OverlapsWith(extent)) @@ -1749,13 +1795,13 @@ void ExtensionsService::ClearProvidersForTesting() { backend_.get(), &ExtensionsServiceBackend::ClearProvidersForTesting)); } -void ExtensionsService::SetProviderForTesting( - Extension::Location location, ExternalExtensionProvider* test_provider) { +void ExtensionsService::AddProviderForTesting( + ExternalExtensionProvider* test_provider) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod( - backend_.get(), &ExtensionsServiceBackend::SetProviderForTesting, - location, test_provider)); + backend_.get(), &ExtensionsServiceBackend::AddProviderForTesting, + test_provider)); } void ExtensionsService::OnExternalExtensionFileFound( @@ -1763,10 +1809,12 @@ void ExtensionsService::OnExternalExtensionFileFound( const std::string& version, const FilePath& path, Extension::Location location) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Before even bothering to unpack, check and see if we already have this // version. This is important because these extensions are going to get // installed on every startup. - Extension* existing = GetExtensionById(id, true); + const Extension* existing = GetExtensionById(id, true); scoped_ptr<Version> other(Version::GetVersionFromString(version)); if (existing) { switch (existing->version()->CompareTo(*other)) { @@ -1883,3 +1931,26 @@ ExtensionIdSet ExtensionsService::GetAppIds() const { return result; } + +bool ExtensionsService::IsBackgroundPageReady(const Extension* extension) { + return (extension->background_url().is_empty() || + extension_runtime_data_[extension->id()].background_page_ready); +} + +void ExtensionsService::SetBackgroundPageReady(const Extension* extension) { + DCHECK(!extension->background_url().is_empty()); + extension_runtime_data_[extension->id()].background_page_ready = true; + NotificationService::current()->Notify( + NotificationType::EXTENSION_BACKGROUND_PAGE_READY, + Source<const Extension>(extension), + NotificationService::NoDetails()); +} + +bool ExtensionsService::IsBeingUpgraded(const Extension* extension) { + return extension_runtime_data_[extension->id()].being_upgraded; +} + +void ExtensionsService::SetBeingUpgraded(const Extension* extension, + bool value) { + extension_runtime_data_[extension->id()].being_upgraded = value; +} diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h index bf398de..d3fd80b 100644 --- a/chrome/browser/extensions/extensions_service.h +++ b/chrome/browser/extensions/extensions_service.h @@ -89,8 +89,8 @@ class ExtensionUpdateService { virtual const PendingExtensionMap& pending_extensions() const = 0; virtual void UpdateExtension(const std::string& id, const FilePath& path, const GURL& download_url) = 0; - virtual Extension* GetExtensionById(const std::string& id, - bool include_disabled) = 0; + virtual const Extension* GetExtensionById(const std::string& id, + bool include_disabled) = 0; virtual void UpdateExtensionBlacklist( const std::vector<std::string>& blacklist) = 0; virtual void CheckAdminBlacklist() = 0; @@ -179,11 +179,26 @@ class ExtensionsService // Whether this extension can run in an incognito window. bool IsIncognitoEnabled(const Extension* extension); - void SetIsIncognitoEnabled(Extension* extension, bool enabled); + void SetIsIncognitoEnabled(const Extension* extension, bool enabled); + + // Returns true if the given extension can see events and data from another + // sub-profile (incognito to original profile, or vice versa). + bool CanCrossIncognito(const Extension* extension); // Whether this extension can inject scripts into pages with file URLs. bool AllowFileAccess(const Extension* extension); - void SetAllowFileAccess(Extension* extension, bool allow); + void SetAllowFileAccess(const Extension* extension, bool allow); + + // Whether the background page, if any, is ready. We don't load other + // components until then. If there is no background page, we consider it to + // be ready. + bool IsBackgroundPageReady(const Extension* extension); + void SetBackgroundPageReady(const Extension* extension); + + // Getter and setter for the flag that specifies whether the extension is + // being upgraded. + bool IsBeingUpgraded(const Extension* extension); + void SetBeingUpgraded(const Extension* extension, bool value); // Initialize and start all installed extensions. void Init(); @@ -192,7 +207,8 @@ class ExtensionsService void InitEventRouters(); // Look up an extension by ID. - Extension* GetExtensionById(const std::string& id, bool include_disabled) { + const Extension* GetExtensionById(const std::string& id, + bool include_disabled) { return GetExtensionByIdInternal(id, true, include_disabled); } @@ -229,7 +245,8 @@ class ExtensionsService // Given an extension id and an update URL, schedule the extension // to be fetched, installed, and activated. void AddPendingExtensionFromExternalUpdateUrl(const std::string& id, - const GURL& update_url); + const GURL& update_url, + Extension::Location location); // Like the above. Always installed silently, and defaults update url // from extension id. @@ -285,18 +302,19 @@ class ExtensionsService void GarbageCollectExtensions(); // The App that represents the web store. - Extension* GetWebStoreApp(); + const Extension* GetWebStoreApp(); // Lookup an extension by |url|. - Extension* GetExtensionByURL(const GURL& url); + const Extension* GetExtensionByURL(const GURL& url); // If there is an extension for the specified url it is returned. Otherwise // returns the extension whose web extent contains |url|. - Extension* GetExtensionByWebExtent(const GURL& url); + const Extension* GetExtensionByWebExtent(const GURL& url); // Returns an extension that contains any URL that overlaps with the given // extent, if one exists. - Extension* GetExtensionByOverlappingWebExtent(const ExtensionExtent& extent); + const Extension* GetExtensionByOverlappingWebExtent( + const ExtensionExtent& extent); // Returns true if |url| should get extension api bindings and be permitted // to make api calls. Note that this is independent of what extension @@ -314,19 +332,18 @@ class ExtensionsService void ClearProvidersForTesting(); // Sets an ExternalExtensionProvider for the service to use during testing. - // |location| specifies what type of provider should be added. - void SetProviderForTesting(Extension::Location location, - ExternalExtensionProvider* test_provider); + // Takes ownership of |test_provider|. + void AddProviderForTesting(ExternalExtensionProvider* test_provider); // Called when the initial extensions load has completed. virtual void OnLoadedInstalledExtensions(); // Called when an extension has been loaded. - void OnExtensionLoaded(Extension* extension, + void OnExtensionLoaded(const Extension* extension, bool allow_privilege_increase); // Called by the backend when an extension has been installed. - void OnExtensionInstalled(Extension* extension, + void OnExtensionInstalled(const Extension* extension, bool allow_privilege_increase); // Called by the backend when an external extension is found. @@ -403,18 +420,33 @@ class ExtensionsService ExtensionIdSet GetAppIds() const; private: - virtual ~ExtensionsService(); friend class BrowserThread; friend class DeleteTask<ExtensionsService>; + // Contains Extension data that can change during the life of the process, + // but does not persist across restarts. + struct ExtensionRuntimeData { + // True if the background page is ready. + bool background_page_ready; + + // True while the extension is being upgraded. + bool being_upgraded; + + ExtensionRuntimeData(); + ~ExtensionRuntimeData(); + }; + typedef std::map<std::string, ExtensionRuntimeData> ExtensionRuntimeDataMap; + + virtual ~ExtensionsService(); + // Clear all persistent data that may have been stored by the extension. void ClearExtensionData(const GURL& extension_url); // Look up an extension by ID, optionally including either or both of enabled // and disabled extensions. - Extension* GetExtensionByIdInternal(const std::string& id, - bool include_enabled, - bool include_disabled); + const Extension* GetExtensionByIdInternal(const std::string& id, + bool include_enabled, + bool include_disabled); // Like AddPendingExtension() but assumes an extension with the same // id is not already installed. @@ -426,10 +458,10 @@ class ExtensionsService Extension::Location install_source); // Handles sending notification that |extension| was loaded. - void NotifyExtensionLoaded(Extension* extension); + void NotifyExtensionLoaded(const Extension* extension); // Handles sending notification that |extension| was unloaded. - void NotifyExtensionUnloaded(Extension* extension); + void NotifyExtensionUnloaded(const Extension* extension); // Helper that updates the active extension list used for crash reporting. void UpdateActiveExtensionsInCrashReporter(); @@ -438,10 +470,10 @@ class ExtensionsService void LoadInstalledExtension(const ExtensionInfo& info, bool write_to_prefs); // Helper methods to configure the storage services accordingly. - void GrantProtectedStorage(Extension* extension); - void RevokeProtectedStorage(Extension* extension); - void GrantUnlimitedStorage(Extension* extension); - void RevokeUnlimitedStorage(Extension* extension); + void GrantProtectedStorage(const Extension* extension); + void RevokeProtectedStorage(const Extension* extension); + void GrantUnlimitedStorage(const Extension* extension); + void RevokeUnlimitedStorage(const Extension* extension); // The profile this ExtensionsService is part of. Profile* profile_; @@ -458,6 +490,9 @@ class ExtensionsService // The set of pending extensions. PendingExtensionMap pending_extensions_; + // The map of extension IDs to their runtime data. + ExtensionRuntimeDataMap extension_runtime_data_; + // The full path to the directory where extensions are installed. FilePath install_directory_; diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc index df29995..fa8f341 100644 --- a/chrome/browser/extensions/extensions_service_unittest.cc +++ b/chrome/browser/extensions/extensions_service_unittest.cc @@ -103,7 +103,7 @@ static std::vector<std::string> GetErrors() { class MockExtensionProvider : public ExternalExtensionProvider { public: explicit MockExtensionProvider(Extension::Location location) - : location_(location) {} + : location_(location), visit_count_(0) {} virtual ~MockExtensionProvider() {} void UpdateOrAddExtension(const std::string& id, @@ -119,6 +119,7 @@ class MockExtensionProvider : public ExternalExtensionProvider { // ExternalExtensionProvider implementation: virtual void VisitRegisteredExtension( Visitor* visitor, const std::set<std::string>& ids_to_ignore) const { + visit_count_++; for (DataMap::const_iterator i = extension_map_.begin(); i != extension_map_.end(); ++i) { if (ids_to_ignore.find(i->first) != ids_to_ignore.end()) @@ -131,15 +132,28 @@ class MockExtensionProvider : public ExternalExtensionProvider { } } - virtual Version* RegisteredVersion(const std::string& id, - Extension::Location* location) const { + virtual bool HasExtension(const std::string& id) const { + return extension_map_.find(id) != extension_map_.end(); + } + + virtual bool GetExtensionDetails(const std::string& id, + Extension::Location* location, + scoped_ptr<Version>* version) const { DataMap::const_iterator it = extension_map_.find(id); if (it == extension_map_.end()) - return NULL; + return false; + + if (version) + version->reset(Version::GetVersionFromString(it->second.first)); if (location) *location = location_; - return Version::GetVersionFromString(it->second.first); + + return true; + } + int visit_count() const { return visit_count_; } + void set_visit_count(int visit_count) { + visit_count_ = visit_count; } private: @@ -147,6 +161,12 @@ class MockExtensionProvider : public ExternalExtensionProvider { DataMap extension_map_; Extension::Location location_; + // visit_count_ tracks the number of calls to VisitRegisteredExtension(). + // Mutable because it must be incremented on each call to + // VisitRegisteredExtension(), which must be a const method to inherit + // from the class being mocked. + mutable int visit_count_; + DISALLOW_COPY_AND_ASSIGN(MockExtensionProvider); }; @@ -197,10 +217,18 @@ class MockProviderVisitor : public ExternalExtensionProvider::Visitor { << "Got back ID (" << id.c_str() << ") we weren't expecting"; if (pref) { + EXPECT_TRUE(provider_->HasExtension(id)); + // Ask provider if the extension we got back is registered. Extension::Location location = Extension::INVALID; - scoped_ptr<Version> v1(provider_->RegisteredVersion(id, NULL)); - scoped_ptr<Version> v2(provider_->RegisteredVersion(id, &location)); + scoped_ptr<Version> v1; + FilePath crx_path; + + EXPECT_TRUE(provider_->GetExtensionDetails(id, NULL, &v1)); + EXPECT_STREQ(version->GetString().c_str(), v1->GetString().c_str()); + + scoped_ptr<Version> v2; + EXPECT_TRUE(provider_->GetExtensionDetails(id, &location, &v2)); EXPECT_STREQ(version->GetString().c_str(), v1->GetString().c_str()); EXPECT_STREQ(version->GetString().c_str(), v2->GetString().c_str()); EXPECT_EQ(Extension::EXTERNAL_PREF, location); @@ -210,8 +238,9 @@ class MockProviderVisitor : public ExternalExtensionProvider::Visitor { } } - virtual void OnExternalExtensionUpdateUrlFound(const std::string& id, - const GURL& update_url) { + virtual void OnExternalExtensionUpdateUrlFound( + const std::string& id, const GURL& update_url, + Extension::Location location) { ++ids_found_; DictionaryValue* pref; // This tests is to make sure that the provider only notifies us of the @@ -219,8 +248,18 @@ class MockProviderVisitor : public ExternalExtensionProvider::Visitor { // dictionary then something is wrong. EXPECT_TRUE(prefs_->GetDictionary(id, &pref)) << L"Got back ID (" << id.c_str() << ") we weren't expecting"; + EXPECT_EQ(Extension::EXTERNAL_PREF_DOWNLOAD, location); if (pref) { + EXPECT_TRUE(provider_->HasExtension(id)); + + // External extensions with update URLs do not have versions. + scoped_ptr<Version> v1; + Extension::Location location1 = Extension::INVALID; + EXPECT_TRUE(provider_->GetExtensionDetails(id, &location1, &v1)); + EXPECT_FALSE(v1.get()); + EXPECT_EQ(Extension::EXTERNAL_PREF_DOWNLOAD, location1); + // Remove it so we won't count it again. prefs_->Remove(id, NULL); } @@ -303,12 +342,6 @@ void ExtensionsServiceTestBase::InitializeExtensionsService( profile_.reset(profile); - // TODO(scherkus): Remove this when we no longer need to have Talk - // component extension state as a preference http://crbug.com/56429 - DictionaryValue* dict = - profile->GetPrefs()->GetMutableDictionary("extensions.settings"); - dict->Remove("ggnioahjipcehijkhpdjekioddnjoben", NULL); - service_ = profile->CreateExtensionsService( CommandLine::ForCurrentProcess(), extensions_install_dir); @@ -386,8 +419,8 @@ class ExtensionsServiceTest const NotificationDetails& details) { switch (type.value) { case NotificationType::EXTENSION_LOADED: { - Extension* extension = Details<Extension>(details).ptr(); - loaded_.push_back(extension); + const Extension* extension = Details<const Extension>(details).ptr(); + loaded_.push_back(make_scoped_refptr(extension)); // The tests rely on the errors being in a certain order, which can vary // depending on how filesystem iteration works. std::stable_sort(loaded_.begin(), loaded_.end(), ExtensionsOrder()); @@ -395,7 +428,7 @@ class ExtensionsServiceTest } case NotificationType::EXTENSION_UNLOADED: { - Extension* e = Details<Extension>(details).ptr(); + const Extension* e = Details<const Extension>(details).ptr(); unloaded_id_ = e->id(); ExtensionList::iterator i = std::find(loaded_.begin(), loaded_.end(), e); @@ -408,7 +441,7 @@ class ExtensionsServiceTest } case NotificationType::EXTENSION_INSTALLED: case NotificationType::THEME_INSTALLED: - installed_ = Details<Extension>(details).ptr(); + installed_ = Details<const Extension>(details).ptr(); break; default: @@ -416,9 +449,8 @@ class ExtensionsServiceTest } } - void SetMockExternalProvider(Extension::Location location, - ExternalExtensionProvider* provider) { - service_->SetProviderForTesting(location, provider); + void AddMockExternalProvider(ExternalExtensionProvider* provider) { + service_->AddProviderForTesting(provider); } protected: @@ -644,7 +676,7 @@ class ExtensionsServiceTest protected: ExtensionList loaded_; std::string unloaded_id_; - Extension* installed_; + const Extension* installed_; private: NotificationRegistrar registrar_; @@ -742,7 +774,7 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectorySuccess) { ValidateIntegerPref(good2, "state", Extension::ENABLED); ValidateIntegerPref(good2, "location", Extension::INTERNAL); - Extension* extension = loaded_[0]; + const Extension* extension = loaded_[0]; const UserScriptList& scripts = extension->content_scripts(); ASSERT_EQ(2u, scripts.size()); EXPECT_EQ(3u, scripts[0].url_patterns().size()); @@ -1223,9 +1255,8 @@ TEST_F(ExtensionsServiceTest, InstallApps) { ValidatePrefKeyCount(++pref_count); // A third app whose extent overlaps the first. Should fail. - // TODO(aa): bring this back when overlap is fixed. http://crbug.com/47445. - // PackAndInstallExtension(extensions_path.AppendASCII("app3"), false); - // ValidatePrefKeyCount(pref_count); + PackAndInstallExtension(extensions_path.AppendASCII("app3"), false); + ValidatePrefKeyCount(pref_count); } TEST_F(ExtensionsServiceTest, InstallAppsWithUnlimtedStorage) { @@ -1243,7 +1274,7 @@ TEST_F(ExtensionsServiceTest, InstallAppsWithUnlimtedStorage) { PackAndInstallExtension(extensions_path.AppendASCII("app1"), true); ValidatePrefKeyCount(++pref_count); ASSERT_EQ(1u, service_->extensions()->size()); - Extension* extension = service_->extensions()->at(0); + const Extension* extension = service_->extensions()->at(0); const std::string id1 = extension->id(); EXPECT_TRUE(extension->HasApiPermission( Extension::kUnlimitedStoragePermission)); @@ -1301,7 +1332,7 @@ TEST_F(ExtensionsServiceTest, InstallAppsAndCheckStorageProtection) { PackAndInstallExtension(extensions_path.AppendASCII("app1"), true); ValidatePrefKeyCount(++pref_count); ASSERT_EQ(1u, service_->extensions()->size()); - Extension* extension = service_->extensions()->at(0); + const Extension* extension = service_->extensions()->at(0); EXPECT_TRUE(extension->is_app()); const std::string id1 = extension->id(); EXPECT_FALSE(service_->protected_storage_map_.empty()); @@ -1430,7 +1461,7 @@ TEST_F(ExtensionsServiceTest, UpdateExtension) { FilePath path = extensions_path.AppendASCII("good.crx"); InstallExtension(path, true); - Extension* good = service_->extensions()->at(0); + const Extension* good = service_->extensions()->at(0); ASSERT_EQ("1.0.0.0", good->VersionString()); ASSERT_EQ(good_crx, good->id()); @@ -1465,7 +1496,7 @@ TEST_F(ExtensionsServiceTest, UpdateWillNotDowngrade) { FilePath path = extensions_path.AppendASCII("good2.crx"); InstallExtension(path, true); - Extension* good = service_->extensions()->at(0); + const Extension* good = service_->extensions()->at(0); ASSERT_EQ("1.0.0.1", good->VersionString()); ASSERT_EQ(good_crx, good->id()); @@ -1485,7 +1516,7 @@ TEST_F(ExtensionsServiceTest, UpdateToSameVersionIsNoop) { FilePath path = extensions_path.AppendASCII("good.crx"); InstallExtension(path, true); - Extension* good = service_->extensions()->at(0); + const Extension* good = service_->extensions()->at(0); ASSERT_EQ(good_crx, good->id()); UpdateExtension(good_crx, path, FAILED_SILENTLY); } @@ -1500,7 +1531,7 @@ TEST_F(ExtensionsServiceTest, UpdateExtensionPreservesState) { FilePath path = extensions_path.AppendASCII("good.crx"); InstallExtension(path, true); - Extension* good = service_->extensions()->at(0); + const Extension* good = service_->extensions()->at(0); ASSERT_EQ("1.0.0.0", good->VersionString()); ASSERT_EQ(good_crx, good->id()); @@ -1512,7 +1543,7 @@ TEST_F(ExtensionsServiceTest, UpdateExtensionPreservesState) { path = extensions_path.AppendASCII("good2.crx"); UpdateExtension(good_crx, path, INSTALLED); ASSERT_EQ(1u, service_->disabled_extensions()->size()); - Extension* good2 = service_->disabled_extensions()->at(0); + const Extension* good2 = service_->disabled_extensions()->at(0); ASSERT_EQ("1.0.0.1", good2->version()->GetString()); EXPECT_TRUE(service_->IsIncognitoEnabled(good2)); } @@ -1570,7 +1601,7 @@ TEST_F(ExtensionsServiceTest, UpdatePendingExtension) { EXPECT_FALSE(ContainsKey(service_->pending_extensions(), kGoodId)); - Extension* extension = service_->GetExtensionById(kGoodId, true); + const Extension* extension = service_->GetExtensionById(kGoodId, true); ASSERT_TRUE(extension); bool enabled = service_->GetExtensionById(kGoodId, false); @@ -1597,7 +1628,7 @@ TEST_F(ExtensionsServiceTest, UpdatePendingTheme) { EXPECT_FALSE(ContainsKey(service_->pending_extensions(), theme_crx)); - Extension* extension = service_->GetExtensionById(theme_crx, true); + const Extension* extension = service_->GetExtensionById(theme_crx, true); ASSERT_TRUE(extension); EXPECT_EQ(Extension::ENABLED, @@ -1610,7 +1641,8 @@ TEST_F(ExtensionsServiceTest, UpdatePendingTheme) { // or not. TEST_F(ExtensionsServiceTest, UpdatePendingExternalCrx) { InitializeEmptyExtensionsService(); - service_->AddPendingExtensionFromExternalUpdateUrl(theme_crx, GURL()); + service_->AddPendingExtensionFromExternalUpdateUrl( + theme_crx, GURL(), Extension::EXTERNAL_PREF_DOWNLOAD); EXPECT_TRUE(ContainsKey(service_->pending_extensions(), theme_crx)); @@ -1622,7 +1654,7 @@ TEST_F(ExtensionsServiceTest, UpdatePendingExternalCrx) { EXPECT_FALSE(ContainsKey(service_->pending_extensions(), theme_crx)); - Extension* extension = service_->GetExtensionById(theme_crx, true); + const Extension* extension = service_->GetExtensionById(theme_crx, true); ASSERT_TRUE(extension); EXPECT_EQ(Extension::ENABLED, @@ -1630,6 +1662,45 @@ TEST_F(ExtensionsServiceTest, UpdatePendingExternalCrx) { EXPECT_FALSE(service_->IsIncognitoEnabled(extension)); } +// Test updating a pending CRX as if the source is an external extension +// with an update URL. The external update should overwrite a sync update, +// but a sync update should not overwrite a non-sync update. +TEST_F(ExtensionsServiceTest, UpdatePendingExternalCrxWinsOverSync) { + InitializeEmptyExtensionsService(); + + // Add a crx to be installed from the update mechanism. + service_->AddPendingExtensionFromSync( + kGoodId, GURL(kGoodUpdateURL), kCrxTypeExtension, + kGoodInstallSilently, kGoodInitialState, + kGoodInitialIncognitoEnabled); + + // Check that there is a pending crx, with is_from_sync set to true. + PendingExtensionMap::const_iterator it; + it = service_->pending_extensions().find(kGoodId); + ASSERT_TRUE(it != service_->pending_extensions().end()); + EXPECT_TRUE(it->second.is_from_sync); + + // Add a crx to be updated, with the same ID, from a non-sync source. + service_->AddPendingExtensionFromExternalUpdateUrl( + kGoodId, GURL(kGoodUpdateURL), Extension::EXTERNAL_PREF_DOWNLOAD); + + // Check that there is a pending crx, with is_from_sync set to false. + it = service_->pending_extensions().find(kGoodId); + ASSERT_TRUE(it != service_->pending_extensions().end()); + EXPECT_FALSE(it->second.is_from_sync); + + // Add a crx to be installed from the update mechanism. + service_->AddPendingExtensionFromSync( + kGoodId, GURL(kGoodUpdateURL), kCrxTypeExtension, + kGoodInstallSilently, kGoodInitialState, + kGoodInitialIncognitoEnabled); + + // Check that the external, non-sync update was not overridden. + it = service_->pending_extensions().find(kGoodId); + ASSERT_TRUE(it != service_->pending_extensions().end()); + EXPECT_FALSE(it->second.is_from_sync); +} + // Updating a theme should fail if the updater is explicitly told that // the CRX is not a theme. TEST_F(ExtensionsServiceTest, UpdatePendingCrxThemeMismatch) { @@ -1649,7 +1720,7 @@ TEST_F(ExtensionsServiceTest, UpdatePendingCrxThemeMismatch) { EXPECT_FALSE(ContainsKey(service_->pending_extensions(), theme_crx)); - Extension* extension = service_->GetExtensionById(theme_crx, true); + const Extension* extension = service_->GetExtensionById(theme_crx, true); ASSERT_FALSE(extension); } @@ -1706,7 +1777,7 @@ TEST_F(ExtensionsServiceTest, UpdatePendingExtensionAlreadyInstalled) { FilePath path = extensions_path.AppendASCII("good.crx"); InstallExtension(path, true); ASSERT_EQ(1u, service_->extensions()->size()); - Extension* good = service_->extensions()->at(0); + const Extension* good = service_->extensions()->at(0); EXPECT_FALSE(good->is_theme()); @@ -1761,7 +1832,7 @@ TEST_F(ExtensionsServiceTest, UnloadBlacklistedExtension) { FilePath path = extensions_path.AppendASCII("good.crx"); InstallExtension(path, true); - Extension* good = service_->extensions()->at(0); + const Extension* good = service_->extensions()->at(0); EXPECT_EQ(good_crx, good->id()); UpdateExtension(good_crx, path, FAILED_SILENTLY); @@ -2094,7 +2165,7 @@ TEST_F(ExtensionsServiceTest, ClearExtensionData) { path = path.AppendASCII("extensions"); path = path.AppendASCII("good.crx"); InstallExtension(path, true); - Extension* extension = service_->GetExtensionById(good_crx, false); + const Extension* extension = service_->GetExtensionById(good_crx, false); ASSERT_TRUE(extension); GURL ext_url(extension->url()); string16 origin_id = @@ -2227,6 +2298,8 @@ void ExtensionsServiceTest::TestExternalProvider( loop_.RunAllPending(); ASSERT_EQ(0u, loaded_.size()); + provider->set_visit_count(0); + // Register a test extension externally using the mock registry provider. FilePath source_path; ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &source_path)); @@ -2342,6 +2415,8 @@ void ExtensionsServiceTest::TestExternalProvider( loop_.RunAllPending(); ASSERT_EQ(0u, loaded_.size()); ValidatePrefKeyCount(1); + + EXPECT_EQ(5, provider->visit_count()); } // Tests the external installation feature @@ -2354,7 +2429,7 @@ TEST_F(ExtensionsServiceTest, ExternalInstallRegistry) { // Now add providers. Extension system takes ownership of the objects. MockExtensionProvider* reg_provider = new MockExtensionProvider(Extension::EXTERNAL_REGISTRY); - SetMockExternalProvider(Extension::EXTERNAL_REGISTRY, reg_provider); + AddMockExternalProvider(reg_provider); TestExternalProvider(reg_provider, Extension::EXTERNAL_REGISTRY); } #endif @@ -2367,7 +2442,8 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPref) { // Now add providers. Extension system takes ownership of the objects. MockExtensionProvider* pref_provider = new MockExtensionProvider(Extension::EXTERNAL_PREF); - SetMockExternalProvider(Extension::EXTERNAL_PREF, pref_provider); + + AddMockExternalProvider(pref_provider); TestExternalProvider(pref_provider, Extension::EXTERNAL_PREF); } @@ -2376,10 +2452,16 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPrefUpdateUrl) { InitializeEmptyExtensionsService(); set_extensions_enabled(false); - // Now add providers. Extension system takes ownership of the objects. + // TODO(skerner): The mock provider is not a good model of a provider + // that works with update URLs, because it adds file and version info. + // Extend the mock to work with update URLs. This test checks the + // behavior that is common to all external extension visitors. The + // browser test ExtensionManagementTest.ExternalUrlUpdate tests that + // what the visitor does results in an extension being downloaded and + // installed. MockExtensionProvider* pref_provider = new MockExtensionProvider(Extension::EXTERNAL_PREF_DOWNLOAD); - SetMockExternalProvider(Extension::EXTERNAL_PREF_DOWNLOAD, pref_provider); + AddMockExternalProvider(pref_provider); TestExternalProvider(pref_provider, Extension::EXTERNAL_PREF_DOWNLOAD); } diff --git a/chrome/browser/extensions/extensions_startup.cc b/chrome/browser/extensions/extensions_startup.cc index 1286179..9faeac6 100644 --- a/chrome/browser/extensions/extensions_startup.cc +++ b/chrome/browser/extensions/extensions_startup.cc @@ -83,8 +83,8 @@ bool HandlePackExtension(const CommandLine& cmd_line) { // Launch a job to perform the packing on the file thread. PackExtensionLogger pack_client; - scoped_refptr<PackExtensionJob> pack_job = - new PackExtensionJob(&pack_client, src_dir, private_key_path); + scoped_refptr<PackExtensionJob> pack_job( + new PackExtensionJob(&pack_client, src_dir, private_key_path)); pack_job->Start(); // The job will post a notification task to the current thread's message diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc index 586ec6f..d3f6a6c 100644 --- a/chrome/browser/extensions/extensions_ui.cc +++ b/chrome/browser/extensions/extensions_ui.cc @@ -58,8 +58,7 @@ namespace { -bool ShouldShowExtension(Extension* extension) { - +bool ShouldShowExtension(const Extension* extension) { // Don't show themes since this page's UI isn't really useful for themes. if (extension->is_theme()) return false; @@ -407,7 +406,7 @@ void ExtensionsDOMHandler::OnIconsLoaded(DictionaryValue* json) { } ExtensionResource ExtensionsDOMHandler::PickExtensionIcon( - Extension* extension) { + const Extension* extension) { return extension->GetIconResource(Extension::EXTENSION_ICON_MEDIUM, ExtensionIconSet::MATCH_BIGGER); } @@ -459,7 +458,7 @@ void ExtensionsDOMHandler::HandleEnableMessage(const ListValue* args) { if (enable_str == "true") { ExtensionPrefs* prefs = extensions_service_->extension_prefs(); if (prefs->DidExtensionEscalatePermissions(extension_id)) { - Extension* extension = + const Extension* extension = extensions_service_->GetExtensionById(extension_id, true); ShowExtensionDisabledDialog(extensions_service_, dom_ui_->GetProfile(), extension); @@ -476,8 +475,8 @@ void ExtensionsDOMHandler::HandleEnableIncognitoMessage(const ListValue* args) { std::string extension_id, enable_str; CHECK(args->GetString(0, &extension_id)); CHECK(args->GetString(1, &enable_str)); - Extension* extension = extensions_service_->GetExtensionById(extension_id, - true); + const Extension* extension = + extensions_service_->GetExtensionById(extension_id, true); DCHECK(extension); // Flipping the incognito bit will generate unload/load notifications for the @@ -501,15 +500,15 @@ void ExtensionsDOMHandler::HandleAllowFileAccessMessage(const ListValue* args) { std::string extension_id, allow_str; CHECK(args->GetString(0, &extension_id)); CHECK(args->GetString(1, &allow_str)); - Extension* extension = extensions_service_->GetExtensionById(extension_id, - true); + const Extension* extension = + extensions_service_->GetExtensionById(extension_id, true); DCHECK(extension); extensions_service_->SetAllowFileAccess(extension, allow_str == "true"); } void ExtensionsDOMHandler::HandleUninstallMessage(const ListValue* args) { - Extension* extension = GetExtension(args); + const Extension* extension = GetExtension(args); if (!extension) return; @@ -527,7 +526,7 @@ void ExtensionsDOMHandler::InstallUIProceed() { // The extension can be uninstalled in another window while the UI was // showing. Do nothing in that case. - Extension* extension = + const Extension* extension = extensions_service_->GetExtensionById(extension_id_prompting_, true); if (!extension) return; @@ -542,7 +541,7 @@ void ExtensionsDOMHandler::InstallUIAbort() { } void ExtensionsDOMHandler::HandleOptionsMessage(const ListValue* args) { - Extension* extension = GetExtension(args); + const Extension* extension = GetExtension(args); if (!extension || extension->options_url().is_empty()) return; dom_ui_->GetProfile()->GetExtensionProcessManager()->OpenOptionsPage( @@ -703,7 +702,7 @@ void ExtensionsDOMHandler::Observe(NotificationType type, } } -Extension* ExtensionsDOMHandler::GetExtension(const ListValue* args) { +const Extension* ExtensionsDOMHandler::GetExtension(const ListValue* args) { std::string extension_id = WideToASCII(ExtractStringValue(args)); CHECK(!extension_id.empty()); return extensions_service_->GetExtensionById(extension_id, true); @@ -840,13 +839,13 @@ DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue( extension_data->Set("views", views); extension_data->SetBoolean("hasPopupAction", extension->browser_action() || extension->page_action()); - extension_data->SetString("galleryUrl", extension->GalleryUrl().spec()); + extension_data->SetString("homepageUrl", extension->GetHomepageURL().spec()); return extension_data; } std::vector<ExtensionPage> ExtensionsDOMHandler::GetActivePagesForExtension( - Extension* extension) { + const Extension* extension) { std::vector<ExtensionPage> result; // Get the extension process's active views. @@ -872,7 +871,7 @@ std::vector<ExtensionPage> ExtensionsDOMHandler::GetActivePagesForExtension( void ExtensionsDOMHandler::GetActivePagesForExtensionProcess( RenderProcessHost* process, - Extension* extension, + const Extension* extension, std::vector<ExtensionPage> *result) { if (!process) return; diff --git a/chrome/browser/extensions/extensions_ui.h b/chrome/browser/extensions/extensions_ui.h index 5660eaa..c8c6143 100644 --- a/chrome/browser/extensions/extensions_ui.h +++ b/chrome/browser/extensions/extensions_ui.h @@ -178,7 +178,7 @@ class ExtensionsDOMHandler void HandleSelectFilePathMessage(const ListValue* args); // Utility for callbacks that get an extension ID as the sole argument. - Extension* GetExtension(const ListValue* args); + const Extension* GetExtension(const ListValue* args); // Forces a UI update if appropriate after a notification is received. void MaybeUpdateAfterNotification(); @@ -198,15 +198,16 @@ class ExtensionsDOMHandler const NotificationDetails& details); // Helper that lists the current active html pages for an extension. - std::vector<ExtensionPage> GetActivePagesForExtension(Extension* extension); + std::vector<ExtensionPage> GetActivePagesForExtension( + const Extension* extension); void GetActivePagesForExtensionProcess( RenderProcessHost* process, - Extension* extension, + const Extension* extension, std::vector<ExtensionPage> *result); // Returns the best icon to display in the UI for an extension, or an empty // ExtensionResource if no good icon exists. - ExtensionResource PickExtensionIcon(Extension* extension); + ExtensionResource PickExtensionIcon(const Extension* extension); // Loads the extension resources into the json data, then calls OnIconsLoaded. // Takes ownership of |icons|. diff --git a/chrome/browser/extensions/external_extension_provider.h b/chrome/browser/extensions/external_extension_provider.h index eddea62..1d040bd 100644 --- a/chrome/browser/extensions/external_extension_provider.h +++ b/chrome/browser/extensions/external_extension_provider.h @@ -32,7 +32,8 @@ class ExternalExtensionProvider { virtual void OnExternalExtensionUpdateUrlFound( const std::string& id, - const GURL& update_url) = 0; + const GURL& update_url, + Extension::Location location) = 0; protected: virtual ~Visitor() {} @@ -46,11 +47,16 @@ class ExternalExtensionProvider { virtual void VisitRegisteredExtension( Visitor* visitor, const std::set<std::string>& ids_to_ignore) const = 0; - // Gets the version of extension with |id| and its |location|. |location| can - // be NULL. The caller is responsible for cleaning up the Version object - // returned. This function returns NULL if the extension is not found. - virtual Version* RegisteredVersion(const std::string& id, - Extension::Location* location) const = 0; + // Test if this provider has an extension with id |id| registered. + virtual bool HasExtension(const std::string& id) const = 0; + + // Gets details of an extension by its id. Output params will be set only + // if they are not NULL. If an output parameter is not specified by the + // provider type, it will not be changed. + // This function is no longer used outside unit tests. + virtual bool GetExtensionDetails(const std::string& id, + Extension::Location* location, + scoped_ptr<Version>* version) const = 0; }; #endif // CHROME_BROWSER_EXTENSIONS_EXTERNAL_EXTENSION_PROVIDER_H_ diff --git a/chrome/browser/extensions/external_policy_extension_provider.cc b/chrome/browser/extensions/external_policy_extension_provider.cc new file mode 100644 index 0000000..0eabccb --- /dev/null +++ b/chrome/browser/extensions/external_policy_extension_provider.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2010 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/browser/extensions/external_policy_extension_provider.h" + +#include "base/values.h" +#include "chrome/common/pref_names.h" +#include "chrome/browser/extensions/stateful_external_extension_provider.h" +#include "chrome/browser/prefs/pref_service.h" + +namespace { + +// Check an extension ID and an URL to be syntactically correct. +bool CheckExtension(std::string id, std::string update_url) { + GURL url(update_url); + if (!url.is_valid()) { + LOG(WARNING) << "Policy specifies invalid update URL for external " + << "extension: " << update_url; + return false; + } + if (!Extension::IdIsValid(id)) { + LOG(WARNING) << "Policy specifies invalid ID for external " + << "extension: " << id; + return false; + } + return true; +} + +} + +ExternalPolicyExtensionProvider::ExternalPolicyExtensionProvider() + : StatefulExternalExtensionProvider(Extension::INVALID, + Extension::EXTERNAL_POLICY_DOWNLOAD) { +} + +ExternalPolicyExtensionProvider::~ExternalPolicyExtensionProvider() { +} + +void ExternalPolicyExtensionProvider::SetPreferences( + PrefService* prefs) { + SetPreferences(prefs->GetList(prefs::kExtensionInstallForceList)); +} + +void ExternalPolicyExtensionProvider::SetPreferences( + const ListValue* forcelist) { + DictionaryValue* result = new DictionaryValue(); + if (forcelist != NULL) { + std::string extension_desc; + for (ListValue::const_iterator it = forcelist->begin(); + it != forcelist->end(); ++it) { + if (!(*it)->GetAsString(&extension_desc)) { + LOG(WARNING) << "Failed to read forcelist string."; + } else { + // Each string item of the list has the following form: + // extension_id_code;extension_update_url + // The update URL might also contain semicolons. + size_t pos = extension_desc.find(';'); + std::string id = extension_desc.substr(0, pos); + std::string update_url = extension_desc.substr(pos+1); + if (CheckExtension(id, update_url)) { + result->SetString(id + ".external_update_url", update_url); + } + } + } + } + prefs_.reset(result); +} diff --git a/chrome/browser/extensions/external_policy_extension_provider.h b/chrome/browser/extensions/external_policy_extension_provider.h new file mode 100644 index 0000000..8f34a34 --- /dev/null +++ b/chrome/browser/extensions/external_policy_extension_provider.h @@ -0,0 +1,35 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTERNAL_POLICY_EXTENSION_PROVIDER_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTERNAL_POLICY_EXTENSION_PROVIDER_H_ +#pragma once + +#include "chrome/browser/extensions/stateful_external_extension_provider.h" + +class ListValue; +class MockExternalPolicyExtensionProviderVisitor; +class PrefService; + +// A specialization of the ExternalExtensionProvider that uses +// prefs::kExtensionInstallForceList to look up which external extensions are +// registered. +class ExternalPolicyExtensionProvider + : public StatefulExternalExtensionProvider { + public: + explicit ExternalPolicyExtensionProvider(); + virtual ~ExternalPolicyExtensionProvider(); + + // Set the internal list of extensions based on + // prefs::kExtensionInstallForceList. + void SetPreferences(PrefService* prefs); + + private: + friend class MockExternalPolicyExtensionProviderVisitor; + + // Set the internal list of extensions based on |forcelist|. + void SetPreferences(const ListValue* forcelist); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTERNAL_POLICY_EXTENSION_PROVIDER_H_ diff --git a/chrome/browser/extensions/external_policy_extension_provider_unittest.cc b/chrome/browser/extensions/external_policy_extension_provider_unittest.cc new file mode 100644 index 0000000..4fb5117 --- /dev/null +++ b/chrome/browser/extensions/external_policy_extension_provider_unittest.cc @@ -0,0 +1,113 @@ +// Copyright (c) 2010 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 <string> + +#include "base/logging.h" +#include "base/values.h" +#include "base/version.h" +#include "chrome/browser/extensions/external_policy_extension_provider.h" +#include "chrome/common/extensions/extension.h" +#include "testing/gtest/include/gtest/gtest.h" + +class ExternalPolicyExtensionProviderTest : public testing::Test { +}; + +class MockExternalPolicyExtensionProviderVisitor + : public ExternalExtensionProvider::Visitor { + public: + MockExternalPolicyExtensionProviderVisitor() { + } + + // Initialize a provider with |policy_forcelist|, and check that it parses + // exactly those extensions, that are specified in |policy_validlist|. + void Visit(ListValue* policy_forcelist, + ListValue* policy_validlist, + const std::set<std::string>& ignore_list) { + provider_.reset(new ExternalPolicyExtensionProvider()); + // Give the list extensions to the provider. + provider_->SetPreferences(policy_forcelist); + + // Extensions will be removed from this list as they visited, + // so it should be emptied by the end. + remaining_extensions = policy_validlist; + provider_->VisitRegisteredExtension(this, ignore_list); + EXPECT_EQ(0u, remaining_extensions->GetSize()); + } + + virtual void OnExternalExtensionFileFound(const std::string& id, + const Version* version, + const FilePath& path, + Extension::Location unused) { + ADD_FAILURE() << "There should be no external extensions from files."; + } + + virtual void OnExternalExtensionUpdateUrlFound( + const std::string& id, const GURL& update_url, + Extension::Location location) { + // Extension has the correct location. + EXPECT_EQ(Extension::EXTERNAL_POLICY_DOWNLOAD, location); + + // Provider returns the correct location when asked. + Extension::Location location1; + scoped_ptr<Version> version1; + provider_->GetExtensionDetails(id, &location1, &version1); + EXPECT_EQ(Extension::EXTERNAL_POLICY_DOWNLOAD, location1); + EXPECT_FALSE(version1.get()); + + // Remove the extension from our list. + StringValue ext_str(id + ";" + update_url.spec()); + EXPECT_NE(-1, remaining_extensions->Remove(ext_str)); + } + + private: + ListValue* remaining_extensions; + + scoped_ptr<ExternalPolicyExtensionProvider> provider_; + + DISALLOW_COPY_AND_ASSIGN(MockExternalPolicyExtensionProviderVisitor); +}; + +TEST_F(ExternalPolicyExtensionProviderTest, PolicyIsParsed) { + ListValue forced_extensions; + forced_extensions.Append(Value::CreateStringValue( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;http://www.example.com/crx?a=5;b=6")); + forced_extensions.Append(Value::CreateStringValue( + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;" + "https://clients2.google.com/service/update2/crx")); + + MockExternalPolicyExtensionProviderVisitor mv; + std::set<std::string> empty; + mv.Visit(&forced_extensions, &forced_extensions, empty); +} + +TEST_F(ExternalPolicyExtensionProviderTest, InvalidPolicyIsNotParsed) { + ListValue forced_extensions, valid_extensions; + StringValue valid( + "cccccccccccccccccccccccccccccccc;http://www.example.com/crx"); + valid_extensions.Append(valid.DeepCopy()); + forced_extensions.Append(valid.DeepCopy()); + // Add invalid strings: + forced_extensions.Append(Value::CreateStringValue("")); + forced_extensions.Append(Value::CreateStringValue(";")); + forced_extensions.Append(Value::CreateStringValue(";;")); + forced_extensions.Append(Value::CreateStringValue( + ";http://www.example.com/crx")); + forced_extensions.Append(Value::CreateStringValue( + ";aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + forced_extensions.Append(Value::CreateStringValue( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;")); + forced_extensions.Append(Value::CreateStringValue( + "http://www.example.com/crx;aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + forced_extensions.Append(Value::CreateStringValue( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;http#//www.example.com/crx")); + forced_extensions.Append(Value::CreateStringValue( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + forced_extensions.Append(Value::CreateStringValue( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahttp#//www.example.com/crx")); + + MockExternalPolicyExtensionProviderVisitor mv; + std::set<std::string> empty; + mv.Visit(&forced_extensions, &valid_extensions, empty); +} diff --git a/chrome/browser/extensions/external_pref_extension_provider.cc b/chrome/browser/extensions/external_pref_extension_provider.cc index 3eaafc9..7580388 100644 --- a/chrome/browser/extensions/external_pref_extension_provider.cc +++ b/chrome/browser/extensions/external_pref_extension_provider.cc @@ -7,20 +7,14 @@ #include "app/app_paths.h" #include "base/file_path.h" #include "base/file_util.h" +#include "base/logging.h" #include "base/path_service.h" -#include "base/string_util.h" -#include "base/utf_string_conversions.h" -#include "base/version.h" +#include "chrome/browser/extensions/stateful_external_extension_provider.h" #include "chrome/common/json_value_serializer.h" -// Constants for keeping track of extension preferences. -const char kLocation[] = "location"; -const char kState[] = "state"; -const char kExternalCrx[] = "external_crx"; -const char kExternalVersion[] = "external_version"; -const char kExternalUpdateUrl[] = "external_update_url"; - -ExternalPrefExtensionProvider::ExternalPrefExtensionProvider() { +ExternalPrefExtensionProvider::ExternalPrefExtensionProvider() + : StatefulExternalExtensionProvider(Extension::EXTERNAL_PREF, + Extension::EXTERNAL_PREF_DOWNLOAD) { FilePath json_file; PathService::Get(app::DIR_EXTERNAL_EXTENSIONS, &json_file); json_file = json_file.Append(FILE_PATH_LITERAL("external_extensions.json")); @@ -42,100 +36,6 @@ void ExternalPrefExtensionProvider::SetPreferencesForTesting( SetPreferences(&serializer); } -void ExternalPrefExtensionProvider::VisitRegisteredExtension( - Visitor* visitor, const std::set<std::string>& ids_to_ignore) const { - for (DictionaryValue::key_iterator i = prefs_->begin_keys(); - i != prefs_->end_keys(); ++i) { - const std::string& extension_id = *i; - if (ids_to_ignore.find(extension_id) != ids_to_ignore.end()) - continue; - - DictionaryValue* extension; - if (!prefs_->GetDictionaryWithoutPathExpansion(extension_id, &extension)) - continue; - - FilePath::StringType external_crx; - std::string external_version; - std::string external_update_url; - - bool has_external_crx = extension->GetString(kExternalCrx, &external_crx); - bool has_external_version = extension->GetString(kExternalVersion, - &external_version); - bool has_external_update_url = extension->GetString(kExternalUpdateUrl, - &external_update_url); - if (has_external_crx != has_external_version) { - LOG(WARNING) << "Malformed extension dictionary for extension: " - << extension_id.c_str() << ". " << kExternalCrx - << " and " << kExternalVersion << " must be used together."; - continue; - } - - if (has_external_crx == has_external_update_url) { - LOG(WARNING) << "Malformed extension dictionary for extension: " - << extension_id.c_str() << ". Exactly one of the " - << "followng keys should be used: " << kExternalCrx - << ", " << kExternalUpdateUrl << "."; - continue; - } - - if (has_external_crx) { - if (external_crx.find(FilePath::kParentDirectory) != - base::StringPiece::npos) { - LOG(WARNING) << "Path traversal not allowed in path: " - << external_crx.c_str(); - continue; - } - - // If the path is relative, make it absolute. - FilePath path(external_crx); - if (!path.IsAbsolute()) { - // Try path as relative path from external extension dir. - FilePath base_path; - PathService::Get(app::DIR_EXTERNAL_EXTENSIONS, &base_path); - path = base_path.Append(external_crx); - } - - scoped_ptr<Version> version; - version.reset(Version::GetVersionFromString(external_version)); - if (!version.get()) { - LOG(ERROR) << "Malformed extension dictionary for extension: " - << extension_id.c_str() << ". Invalid version string \"" - << external_version << "\"."; - continue; - } - visitor->OnExternalExtensionFileFound(extension_id, version.get(), path, - Extension::EXTERNAL_PREF); - continue; - } - - DCHECK(has_external_update_url); // Checking of keys above ensures this. - GURL update_url(external_update_url); - if (!update_url.is_valid()) { - LOG(WARNING) << "Malformed extension dictionary for extension: " - << extension_id.c_str() << ". " << kExternalUpdateUrl - << " must be a valid URL. Saw \"" << external_update_url - << "\"."; - continue; - } - visitor->OnExternalExtensionUpdateUrlFound(extension_id, update_url); - } -} - -Version* ExternalPrefExtensionProvider::RegisteredVersion( - const std::string& id, Extension::Location* location) const { - DictionaryValue* extension = NULL; - if (!prefs_->GetDictionary(id, &extension)) - return NULL; - - std::string external_version; - if (!extension->GetString(kExternalVersion, &external_version)) - return NULL; - - if (location) - *location = Extension::EXTERNAL_PREF; - return Version::GetVersionFromString(external_version); -} - void ExternalPrefExtensionProvider::SetPreferences( ValueSerializer* serializer) { std::string error_msg; diff --git a/chrome/browser/extensions/external_pref_extension_provider.h b/chrome/browser/extensions/external_pref_extension_provider.h index aef6b17..8b9eb0b 100644 --- a/chrome/browser/extensions/external_pref_extension_provider.h +++ b/chrome/browser/extensions/external_pref_extension_provider.h @@ -6,18 +6,11 @@ #define CHROME_BROWSER_EXTENSIONS_EXTERNAL_PREF_EXTENSION_PROVIDER_H_ #pragma once -#include <set> -#include <string> - -#include "chrome/browser/extensions/external_extension_provider.h" - -class DictionaryValue; -class ValueSerializer; -class Version; +#include "chrome/browser/extensions/stateful_external_extension_provider.h" // A specialization of the ExternalExtensionProvider that uses a json file to // look up which external extensions are registered. -class ExternalPrefExtensionProvider : public ExternalExtensionProvider { +class ExternalPrefExtensionProvider : public StatefulExternalExtensionProvider { public: explicit ExternalPrefExtensionProvider(); virtual ~ExternalPrefExtensionProvider(); @@ -26,15 +19,6 @@ class ExternalPrefExtensionProvider : public ExternalExtensionProvider { // but instead parse a json file specified by the test. void SetPreferencesForTesting(const std::string& json_data_for_testing); - // ExternalExtensionProvider implementation: - virtual void VisitRegisteredExtension( - Visitor* visitor, const std::set<std::string>& ids_to_ignore) const; - - virtual Version* RegisteredVersion(const std::string& id, - Extension::Location* location) const; - protected: - scoped_ptr<DictionaryValue> prefs_; - private: void SetPreferences(ValueSerializer* serializer); }; diff --git a/chrome/browser/extensions/external_registry_extension_provider_win.cc b/chrome/browser/extensions/external_registry_extension_provider_win.cc index 02ae850..00bea09 100644 --- a/chrome/browser/extensions/external_registry_extension_provider_win.cc +++ b/chrome/browser/extensions/external_registry_extension_provider_win.cc @@ -10,6 +10,8 @@ #include "base/version.h" #include "base/win/registry.h" +namespace { + // The Registry hive where to look for external extensions. const HKEY kRegRoot = HKEY_LOCAL_MACHINE; @@ -22,6 +24,16 @@ const wchar_t kRegistryExtensionPath[] = L"path"; // Registry value of that key that defines the current version of the .crx file. const wchar_t kRegistryExtensionVersion[] = L"version"; +bool OpenKeyById(const std::string& id, base::win::RegKey *key) { + std::wstring key_path = ASCIIToWide(kRegistryExtensions); + key_path.append(L"\\"); + key_path.append(ASCIIToWide(id)); + + return key->Open(kRegRoot, key_path.c_str(), KEY_READ); +} + +} // namespace + ExternalRegistryExtensionProvider::ExternalRegistryExtensionProvider() { } @@ -54,6 +66,7 @@ void ExternalRegistryExtensionProvider::VisitRegisteredExtension( if (!version.get()) { LOG(ERROR) << "Invalid version value " << extension_version << " for key " << key_path; + ++iterator; continue; } @@ -75,22 +88,29 @@ void ExternalRegistryExtensionProvider::VisitRegisteredExtension( } } -Version* ExternalRegistryExtensionProvider::RegisteredVersion( - const std::string& id, - Extension::Location* location) const { + +bool ExternalRegistryExtensionProvider::HasExtension( + const std::string& id) const { base::win::RegKey key; - std::wstring key_path = ASCIIToWide(kRegistryExtensions); - key_path.append(L"\\"); - key_path.append(ASCIIToWide(id)); + return OpenKeyById(id, &key); +} - if (!key.Open(kRegRoot, key_path.c_str(), KEY_READ)) - return NULL; +bool ExternalRegistryExtensionProvider::GetExtensionDetails( + const std::string& id, + Extension::Location* location, + scoped_ptr<Version>* version) const { + base::win::RegKey key; + if (!OpenKeyById(id, &key)) + return false; std::wstring extension_version; if (!key.ReadValue(kRegistryExtensionVersion, &extension_version)) - return NULL; + return false; + + if (version) + version->reset(Version::GetVersionFromString(extension_version)); if (location) *location = Extension::EXTERNAL_REGISTRY; - return Version::GetVersionFromString(extension_version); + return true; } diff --git a/chrome/browser/extensions/external_registry_extension_provider_win.h b/chrome/browser/extensions/external_registry_extension_provider_win.h index b5b44bb..bb60106 100644 --- a/chrome/browser/extensions/external_registry_extension_provider_win.h +++ b/chrome/browser/extensions/external_registry_extension_provider_win.h @@ -24,8 +24,11 @@ class ExternalRegistryExtensionProvider : public ExternalExtensionProvider { virtual void VisitRegisteredExtension( Visitor* visitor, const std::set<std::string>& ids_to_ignore) const; - virtual Version* RegisteredVersion(const std::string& id, - Extension::Location* location) const; + virtual bool HasExtension(const std::string& id) const; + + virtual bool GetExtensionDetails(const std::string& id, + Extension::Location* location, + scoped_ptr<Version>* version) const; }; #endif // CHROME_BROWSER_EXTENSIONS_EXTERNAL_REGISTRY_EXTENSION_PROVIDER_WIN_H_ diff --git a/chrome/browser/extensions/image_loading_tracker.cc b/chrome/browser/extensions/image_loading_tracker.cc index f4f9bfd..496c3ad 100644 --- a/chrome/browser/extensions/image_loading_tracker.cc +++ b/chrome/browser/extensions/image_loading_tracker.cc @@ -134,7 +134,7 @@ ImageLoadingTracker::~ImageLoadingTracker() { loader_->StopTracking(); } -void ImageLoadingTracker::LoadImage(Extension* extension, +void ImageLoadingTracker::LoadImage(const Extension* extension, const ExtensionResource& resource, const gfx::Size& max_size, CacheParam cache) { @@ -187,7 +187,7 @@ void ImageLoadingTracker::Observe(NotificationType type, DCHECK(type == NotificationType::EXTENSION_UNLOADED || type == NotificationType::EXTENSION_UNLOADED_DISABLED); - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); // Remove all entries in the load_map_ referencing the extension. This ensures // we don't attempt to cache the image when the load completes. diff --git a/chrome/browser/extensions/image_loading_tracker.h b/chrome/browser/extensions/image_loading_tracker.h index bac2f04..0ce3703 100644 --- a/chrome/browser/extensions/image_loading_tracker.h +++ b/chrome/browser/extensions/image_loading_tracker.h @@ -64,13 +64,13 @@ class ImageLoadingTracker : public NotificationObserver { // |max_size| it will be resized to those dimensions. IMPORTANT NOTE: this // function may call back your observer synchronously (ie before it returns) // if the image was found in the cache. - void LoadImage(Extension* extension, + void LoadImage(const Extension* extension, const ExtensionResource& resource, const gfx::Size& max_size, CacheParam cache); private: - typedef std::map<int, Extension*> LoadMap; + typedef std::map<int, const Extension*> LoadMap; class ImageLoader; diff --git a/chrome/browser/extensions/image_loading_tracker_unittest.cc b/chrome/browser/extensions/image_loading_tracker_unittest.cc index f796a36..57b5268 100644 --- a/chrome/browser/extensions/image_loading_tracker_unittest.cc +++ b/chrome/browser/extensions/image_loading_tracker_unittest.cc @@ -51,7 +51,7 @@ class ImageLoadingTrackerTest : public testing::Test, return result; } - Extension* CreateExtension() { + scoped_refptr<Extension> CreateExtension() { // Create and load an extension. FilePath test_file; if (!PathService::Get(chrome::DIR_TEST_DATA, &test_file)) { @@ -74,11 +74,8 @@ class ImageLoadingTrackerTest : public testing::Test, if (!valid_value.get()) return NULL; - scoped_ptr<Extension> extension(new Extension(test_file)); - if (!extension->InitFromValue(*valid_value, false, &error)) - return NULL; - - return extension.release(); + return Extension::Create( + test_file, Extension::INVALID, *valid_value, false, &error); } SkBitmap image_; @@ -99,7 +96,7 @@ class ImageLoadingTrackerTest : public testing::Test, // Tests asking ImageLoadingTracker to cache pushes the result to the Extension. TEST_F(ImageLoadingTrackerTest, Cache) { - scoped_ptr<Extension> extension(CreateExtension()); + scoped_refptr<Extension> extension(CreateExtension()); ASSERT_TRUE(extension.get() != NULL); ExtensionResource image_resource = @@ -146,7 +143,7 @@ TEST_F(ImageLoadingTrackerTest, Cache) { // Tests deleting an extension while waiting for the image to load doesn't cause // problems. TEST_F(ImageLoadingTrackerTest, DeleteExtensionWhileWaitingForCache) { - scoped_ptr<Extension> extension(CreateExtension()); + scoped_refptr<Extension> extension(CreateExtension()); ASSERT_TRUE(extension.get() != NULL); ExtensionResource image_resource = @@ -166,11 +163,11 @@ TEST_F(ImageLoadingTrackerTest, DeleteExtensionWhileWaitingForCache) { NotificationService::current()->Notify( NotificationType::EXTENSION_UNLOADED, NotificationService::AllSources(), - Details<Extension>(extension.get())); + Details<const Extension>(extension.get())); // Chuck the extension, that way if anyone tries to access it we should crash // or get valgrind errors. - extension.reset(); + extension = NULL; WaitForImageLoad(); diff --git a/chrome/browser/extensions/page_action_apitest.cc b/chrome/browser/extensions/page_action_apitest.cc index c03c030..95e029e 100644 --- a/chrome/browser/extensions/page_action_apitest.cc +++ b/chrome/browser/extensions/page_action_apitest.cc @@ -18,7 +18,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageAction) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(RunExtensionTest("page_action/basics")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; { // Tell the extension to update the page action state. @@ -62,7 +62,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageAction) { IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageActionAddPopup) { // Load the extension, which has no default popup. ASSERT_TRUE(RunExtensionTest("page_action/add_popup")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents()); @@ -107,7 +107,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageActionAddPopup) { IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageActionRemovePopup) { // Load the extension, which has a page action with a default popup. ASSERT_TRUE(RunExtensionTest("page_action/remove_popup")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents()); @@ -136,7 +136,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageActionRemovePopup) { // break. IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OldPageActions) { ASSERT_TRUE(RunExtensionTest("page_action/old_api")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // Have the extension enable the page action. @@ -161,7 +161,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OldPageActions) { // Tests popups in page actions. IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ShowPageActionPopup) { ASSERT_TRUE(RunExtensionTest("page_action/popup")) << message_; - Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker.cc b/chrome/browser/extensions/sandboxed_extension_unpacker.cc index fc8aea8..c1702b1 100644 --- a/chrome/browser/extensions/sandboxed_extension_unpacker.cc +++ b/chrome/browser/extensions/sandboxed_extension_unpacker.cc @@ -9,6 +9,7 @@ #include "base/base64.h" #include "base/crypto/signature_verifier.h" #include "base/file_util.h" +#include "base/file_util_proxy.h" #include "base/message_loop.h" #include "base/scoped_handle.h" #include "base/task.h" @@ -116,7 +117,13 @@ void SandboxedExtensionUnpacker::Start() { } } -SandboxedExtensionUnpacker::~SandboxedExtensionUnpacker() {} +SandboxedExtensionUnpacker::~SandboxedExtensionUnpacker() { + base::FileUtilProxy::Delete( + BrowserThread::GetMessageLoopProxyForThread(thread_identifier_), + temp_dir_.Take(), + true, + NULL); +} void SandboxedExtensionUnpacker::StartProcessOnIOThread( const FilePath& temp_crx_path) { @@ -140,18 +147,20 @@ void SandboxedExtensionUnpacker::OnUnpackExtensionSucceeded( // extension was unpacked to. We use this until the extension is finally // installed. For example, the install UI shows images from inside the // extension. - extension_.reset(new Extension(extension_root_)); // Localize manifest now, so confirm UI gets correct extension name. std::string error; - if (!extension_l10n_util::LocalizeExtension(extension_.get(), + if (!extension_l10n_util::LocalizeExtension(extension_root_, final_manifest.get(), &error)) { ReportFailure(error); return; } - if (!extension_->InitFromValue(*final_manifest, true, &error)) { + extension_ = Extension::Create( + extension_root_, Extension::INTERNAL, *final_manifest, true, &error); + + if (!extension_.get()) { ReportFailure(std::string("Manifest is invalid: ") + error); return; } @@ -270,8 +279,8 @@ void SandboxedExtensionUnpacker::ReportFailure(const std::string& error) { void SandboxedExtensionUnpacker::ReportSuccess() { // Client takes ownership of temporary directory and extension. - client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_, - extension_.release()); + client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_, extension_); + extension_ = NULL; } DictionaryValue* SandboxedExtensionUnpacker::RewriteManifestFile( diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker.h b/chrome/browser/extensions/sandboxed_extension_unpacker.h index 8df1414..8438482 100644 --- a/chrome/browser/extensions/sandboxed_extension_unpacker.h +++ b/chrome/browser/extensions/sandboxed_extension_unpacker.h @@ -29,7 +29,7 @@ class SandboxedExtensionUnpackerClient // for deleting this memory. virtual void OnUnpackSuccess(const FilePath& temp_dir, const FilePath& extension_root, - Extension* extension) = 0; + const Extension* extension) = 0; virtual void OnUnpackFailure(const std::string& error) = 0; protected: @@ -160,7 +160,7 @@ class SandboxedExtensionUnpacker : public UtilityProcessHost::Client { FilePath extension_root_; // Represents the extension we're unpacking. - scoped_ptr<Extension> extension_; + scoped_refptr<Extension> extension_; // Whether we've received a response from the utility process yet. bool got_response_; diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker_unittest.cc b/chrome/browser/extensions/sandboxed_extension_unpacker_unittest.cc index d461ff4..1842e00 100644 --- a/chrome/browser/extensions/sandboxed_extension_unpacker_unittest.cc +++ b/chrome/browser/extensions/sandboxed_extension_unpacker_unittest.cc @@ -25,8 +25,7 @@ using testing::Invoke; void OnUnpackSuccess(const FilePath& temp_dir, const FilePath& extension_root, - Extension* extension) { - delete extension; + const Extension* extension) { // Don't delete temp_dir here, we need to do some post op checking. } @@ -38,7 +37,7 @@ class MockSandboxedExtensionUnpackerClient MOCK_METHOD3(OnUnpackSuccess, void(const FilePath& temp_dir, const FilePath& extension_root, - Extension* extension)); + const Extension* extension)); MOCK_METHOD1(OnUnpackFailure, void(const std::string& error)); @@ -52,12 +51,17 @@ class MockSandboxedExtensionUnpackerClient class SandboxedExtensionUnpackerTest : public testing::Test { public: virtual void SetUp() { + file_thread_.reset(new BrowserThread(BrowserThread::FILE, &loop_)); // It will delete itself. client_ = new MockSandboxedExtensionUnpackerClient; client_->DelegateToFake(); } virtual void TearDown() { + // Need to destruct SandboxedExtensionUnpacker before the message loop since + // it posts a task to it. + sandboxed_unpacker_ = NULL; + loop_.RunAllPending(); // Clean up finally. ASSERT_TRUE(file_util::Delete(install_dir_, true)) << install_dir_.value(); @@ -94,6 +98,9 @@ class SandboxedExtensionUnpackerTest : public testing::Test { sandboxed_unpacker_ = new SandboxedExtensionUnpacker(crx_path, temp_dir_, NULL, client_); + // Hack since SandboxedExtensionUnpacker gets its background thread id from + // the Start call, but we don't call it here. + sandboxed_unpacker_->thread_identifier_ = BrowserThread::FILE; PrepareUnpackerEnv(); } @@ -145,6 +152,8 @@ class SandboxedExtensionUnpackerTest : public testing::Test { MockSandboxedExtensionUnpackerClient* client_; scoped_ptr<ExtensionUnpacker> unpacker_; scoped_refptr<SandboxedExtensionUnpacker> sandboxed_unpacker_; + MessageLoop loop_; + scoped_ptr<BrowserThread> file_thread_; }; TEST_F(SandboxedExtensionUnpackerTest, NoCatalogsSuccess) { diff --git a/chrome/browser/extensions/stateful_external_extension_provider.cc b/chrome/browser/extensions/stateful_external_extension_provider.cc new file mode 100644 index 0000000..63899da --- /dev/null +++ b/chrome/browser/extensions/stateful_external_extension_provider.cc @@ -0,0 +1,159 @@ +// Copyright (c) 2010 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/browser/extensions/stateful_external_extension_provider.h" + +#include "app/app_paths.h" +#include "base/file_path.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/values.h" +#include "base/version.h" + +namespace { + +// Constants for keeping track of extension preferences. +const char kLocation[] = "location"; +const char kState[] = "state"; +const char kExternalCrx[] = "external_crx"; +const char kExternalVersion[] = "external_version"; +const char kExternalUpdateUrl[] = "external_update_url"; + +} + +StatefulExternalExtensionProvider::StatefulExternalExtensionProvider( + Extension::Location crx_location, + Extension::Location download_location) + : crx_location_(crx_location), + download_location_(download_location) { +} + +StatefulExternalExtensionProvider::~StatefulExternalExtensionProvider() { +} + +void StatefulExternalExtensionProvider::VisitRegisteredExtension( + Visitor* visitor, const std::set<std::string>& ids_to_ignore) const { + for (DictionaryValue::key_iterator i = prefs_->begin_keys(); + i != prefs_->end_keys(); ++i) { + const std::string& extension_id = *i; + if (ids_to_ignore.find(extension_id) != ids_to_ignore.end()) + continue; + + DictionaryValue* extension; + if (!prefs_->GetDictionaryWithoutPathExpansion(extension_id, &extension)) + continue; + + FilePath::StringType external_crx; + std::string external_version; + std::string external_update_url; + + bool has_external_crx = extension->GetString(kExternalCrx, &external_crx); + bool has_external_version = extension->GetString(kExternalVersion, + &external_version); + bool has_external_update_url = extension->GetString(kExternalUpdateUrl, + &external_update_url); + if (has_external_crx != has_external_version) { + LOG(WARNING) << "Malformed extension dictionary for extension: " + << extension_id.c_str() << ". " << kExternalCrx + << " and " << kExternalVersion << " must be used together."; + continue; + } + + if (has_external_crx == has_external_update_url) { + LOG(WARNING) << "Malformed extension dictionary for extension: " + << extension_id.c_str() << ". Exactly one of the " + << "followng keys should be used: " << kExternalCrx + << ", " << kExternalUpdateUrl << "."; + continue; + } + + if (has_external_crx) { + if (crx_location_ == Extension::INVALID) { + LOG(WARNING) << "This provider does not support installing external " + << "extensions from crx files."; + continue; + } + if (external_crx.find(FilePath::kParentDirectory) != + base::StringPiece::npos) { + LOG(WARNING) << "Path traversal not allowed in path: " + << external_crx.c_str(); + continue; + } + + // If the path is relative, make it absolute. + FilePath path(external_crx); + if (!path.IsAbsolute()) { + // Try path as relative path from external extension dir. + FilePath base_path; + PathService::Get(app::DIR_EXTERNAL_EXTENSIONS, &base_path); + path = base_path.Append(external_crx); + } + + scoped_ptr<Version> version; + version.reset(Version::GetVersionFromString(external_version)); + if (!version.get()) { + LOG(WARNING) << "Malformed extension dictionary for extension: " + << extension_id.c_str() << ". Invalid version string \"" + << external_version << "\"."; + continue; + } + visitor->OnExternalExtensionFileFound(extension_id, version.get(), path, + crx_location_); + } else { // if (has_external_update_url) + DCHECK(has_external_update_url); // Checking of keys above ensures this. + if (download_location_ == Extension::INVALID) { + LOG(WARNING) << "This provider does not support installing external " + << "extensions from update URLs."; + continue; + } + GURL update_url(external_update_url); + if (!update_url.is_valid()) { + LOG(WARNING) << "Malformed extension dictionary for extension: " + << extension_id.c_str() << ". " << kExternalUpdateUrl + << " must be a valid URL. Saw \"" << external_update_url + << "\"."; + continue; + } + visitor->OnExternalExtensionUpdateUrlFound( + extension_id, update_url, download_location_); + } + } +} + +bool StatefulExternalExtensionProvider::HasExtension( + const std::string& id) const { + return prefs_->HasKey(id); +} + +bool StatefulExternalExtensionProvider::GetExtensionDetails( + const std::string& id, Extension::Location* location, + scoped_ptr<Version>* version) const { + DictionaryValue* extension = NULL; + if (!prefs_->GetDictionary(id, &extension)) + return false; + + Extension::Location loc = Extension::INVALID; + if (extension->HasKey(kExternalUpdateUrl)) { + loc = download_location_; + + } else if (extension->HasKey(kExternalCrx)) { + loc = crx_location_; + + std::string external_version; + if (!extension->GetString(kExternalVersion, &external_version)) + return false; + + if (version) + version->reset(Version::GetVersionFromString(external_version)); + + } else { + NOTREACHED(); // Chrome should not allow prefs to get into this state. + return false; + } + + if (location) + *location = loc; + + return true; +} diff --git a/chrome/browser/extensions/stateful_external_extension_provider.h b/chrome/browser/extensions/stateful_external_extension_provider.h new file mode 100644 index 0000000..506873d --- /dev/null +++ b/chrome/browser/extensions/stateful_external_extension_provider.h @@ -0,0 +1,57 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_STATEFUL_EXTERNAL_EXTENSION_PROVIDER_H_ +#define CHROME_BROWSER_EXTENSIONS_STATEFUL_EXTERNAL_EXTENSION_PROVIDER_H_ +#pragma once + +#include <set> +#include <string> + +#include "chrome/browser/extensions/external_extension_provider.h" + +class DictionaryValue; +class ValueSerializer; +class Version; + +// A specialization of the ExternalExtensionProvider that stores the registered +// external extensions in a dictionary. This dictionary (|prefs_|) will be used +// by HasExtension() and GetExtensionDetails() to return information about the +// stored external extensions. It is the responsibility of specialized +// subclasses to initialize this internal dictionary. +// This provider can provide external extensions from two sources: crx files +// and udpate URLs. The locations that the provider will report for these +// are specified at the constructor. +class StatefulExternalExtensionProvider : public ExternalExtensionProvider { + public: + // Initialize the location for external extensions originating from crx + // files: |crx_location|, and originating from update URLs: + // |download_location|. If either of the origins is not supported by this + // provider, then it should be initialized as Extensions::INVALID. + StatefulExternalExtensionProvider( + Extension::Location crx_location, + Extension::Location download_location); + virtual ~StatefulExternalExtensionProvider(); + + // ExternalExtensionProvider implementation: + virtual void VisitRegisteredExtension( + Visitor* visitor, const std::set<std::string>& ids_to_ignore) const; + + virtual bool HasExtension(const std::string& id) const; + + virtual bool GetExtensionDetails(const std::string& id, + Extension::Location* location, + scoped_ptr<Version>* version) const; + protected: + // Location for external extensions that are provided by this provider from + // local crx files. + const Extension::Location crx_location_; + // Location for external extensions that are provided by this provider from + // update URLs. + const Extension::Location download_location_; + // Dictionary of the external extensions that are provided by this provider. + scoped_ptr<DictionaryValue> prefs_; +}; + +#endif // CHROME_BROWSER_EXTENSIONS_STATEFUL_EXTERNAL_EXTENSION_PROVIDER_H_ diff --git a/chrome/browser/extensions/test_extension_prefs.cc b/chrome/browser/extensions/test_extension_prefs.cc index a32f847..2cbb2d1 100644 --- a/chrome/browser/extensions/test_extension_prefs.cc +++ b/chrome/browser/extensions/test_extension_prefs.cc @@ -45,22 +45,25 @@ void TestExtensionPrefs::RecreateExtensionPrefs() { prefs_.reset(new ExtensionPrefs(pref_service_.get(), temp_dir_.path())); } -Extension* TestExtensionPrefs::AddExtension(std::string name) { +scoped_refptr<Extension> TestExtensionPrefs::AddExtension(std::string name) { DictionaryValue dictionary; dictionary.SetString(extension_manifest_keys::kName, name); dictionary.SetString(extension_manifest_keys::kVersion, "0.1"); return AddExtensionWithManifest(dictionary, Extension::INTERNAL); } -Extension* TestExtensionPrefs::AddExtensionWithManifest( +scoped_refptr<Extension> TestExtensionPrefs::AddExtensionWithManifest( const DictionaryValue& manifest, Extension::Location location) { std::string name; EXPECT_TRUE(manifest.GetString(extension_manifest_keys::kName, &name)); FilePath path = extensions_dir_.AppendASCII(name); - Extension* extension = new Extension(path); std::string errors; - extension->set_location(location); - EXPECT_TRUE(extension->InitFromValue(manifest, false, &errors)); + scoped_refptr<Extension> extension = Extension::Create( + path, location, manifest, false, &errors); + EXPECT_TRUE(extension); + if (!extension) + return NULL; + EXPECT_TRUE(Extension::IdIsValid(extension->id())); const bool kInitialIncognitoEnabled = false; prefs_->OnExtensionInstalled(extension, Extension::ENABLED, @@ -69,6 +72,6 @@ Extension* TestExtensionPrefs::AddExtensionWithManifest( } std::string TestExtensionPrefs::AddExtensionAndReturnId(std::string name) { - scoped_ptr<Extension> extension(AddExtension(name)); + scoped_refptr<Extension> extension(AddExtension(name)); return extension->id(); } diff --git a/chrome/browser/extensions/test_extension_prefs.h b/chrome/browser/extensions/test_extension_prefs.h index 1523bee..cb723b2 100644 --- a/chrome/browser/extensions/test_extension_prefs.h +++ b/chrome/browser/extensions/test_extension_prefs.h @@ -34,11 +34,11 @@ class TestExtensionPrefs { // Creates a new Extension with the given name in our temp dir, adds it to // our ExtensionPrefs, and returns it. - Extension* AddExtension(std::string name); + scoped_refptr<Extension> AddExtension(std::string name); // Similar to AddExtension, but takes a dictionary with manifest values. - Extension* AddExtensionWithManifest(const DictionaryValue& manifest, - Extension::Location location); + scoped_refptr<Extension> AddExtensionWithManifest( + const DictionaryValue& manifest, Extension::Location location); // Similar to AddExtension, this adds a new test Extension. This is useful for // cases when you don't need the Extension object, but just the id it was diff --git a/chrome/browser/extensions/theme_installed_infobar_delegate.cc b/chrome/browser/extensions/theme_installed_infobar_delegate.cc index 6d00c22..ebfbe98 100644 --- a/chrome/browser/extensions/theme_installed_infobar_delegate.cc +++ b/chrome/browser/extensions/theme_installed_infobar_delegate.cc @@ -81,7 +81,7 @@ bool ThemeInstalledInfoBarDelegate::Cancel() { if (!previous_theme_id_.empty()) { ExtensionsService* service = profile_->GetExtensionsService(); if (service) { - Extension* previous_theme = + const Extension* previous_theme = service->GetExtensionById(previous_theme_id_, true); if (previous_theme) { profile_->SetTheme(previous_theme); @@ -102,7 +102,7 @@ void ThemeInstalledInfoBarDelegate::Observe( case NotificationType::BROWSER_THEME_CHANGED: { // If the new theme is different from what this info bar is associated // with, close this info bar since it is no longer relevant. - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (!extension || theme_id_ != extension->id()) tab_contents_->RemoveInfoBar(this); break; @@ -113,6 +113,6 @@ void ThemeInstalledInfoBarDelegate::Observe( } } -bool ThemeInstalledInfoBarDelegate::MatchesTheme(Extension* theme) { +bool ThemeInstalledInfoBarDelegate::MatchesTheme(const Extension* theme) { return (theme && theme->id() == theme_id_); } diff --git a/chrome/browser/extensions/theme_installed_infobar_delegate.h b/chrome/browser/extensions/theme_installed_infobar_delegate.h index 2c54fa5..b1ed99e 100644 --- a/chrome/browser/extensions/theme_installed_infobar_delegate.h +++ b/chrome/browser/extensions/theme_installed_infobar_delegate.h @@ -33,7 +33,7 @@ class ThemeInstalledInfoBarDelegate : public ConfirmInfoBarDelegate, // Returns true if the given theme is the same as the one associated with this // info bar. - bool MatchesTheme(Extension* theme); + bool MatchesTheme(const Extension* theme); // NotificationObserver implementation. virtual void Observe(NotificationType type, diff --git a/chrome/browser/extensions/user_script_listener.cc b/chrome/browser/extensions/user_script_listener.cc index 4e84045..c553b1f 100644 --- a/chrome/browser/extensions/user_script_listener.cc +++ b/chrome/browser/extensions/user_script_listener.cc @@ -98,7 +98,7 @@ void UserScriptListener::ReplaceURLPatterns(const URLPatterns& patterns) { url_patterns_ = patterns; } -void UserScriptListener::CollectURLPatterns(Extension* extension, +void UserScriptListener::CollectURLPatterns(const Extension* extension, URLPatterns* patterns) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -118,12 +118,13 @@ void UserScriptListener::Observe(NotificationType type, switch (type.value) { case NotificationType::EXTENSION_LOADED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); if (extension->content_scripts().empty()) return; // no new patterns from this extension. URLPatterns new_patterns; - CollectURLPatterns(Details<Extension>(details).ptr(), &new_patterns); + CollectURLPatterns(Details<const Extension>(details).ptr(), + &new_patterns); if (!new_patterns.empty()) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, @@ -134,7 +135,8 @@ void UserScriptListener::Observe(NotificationType type, } case NotificationType::EXTENSION_UNLOADED: { - Extension* unloaded_extension = Details<Extension>(details).ptr(); + const Extension* unloaded_extension = + Details<const Extension>(details).ptr(); if (unloaded_extension->content_scripts().empty()) return; // no patterns to delete for this extension. diff --git a/chrome/browser/extensions/user_script_listener.h b/chrome/browser/extensions/user_script_listener.h index 05de21a..3ac83ed 100644 --- a/chrome/browser/extensions/user_script_listener.h +++ b/chrome/browser/extensions/user_script_listener.h @@ -82,7 +82,7 @@ class UserScriptListener // Helper to collect the extension's user script URL patterns in a list and // return it. - void CollectURLPatterns(Extension* extension, URLPatterns* patterns); + void CollectURLPatterns(const Extension* extension, URLPatterns* patterns); // NotificationObserver virtual void Observe(NotificationType type, diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc index 560fda3..1712bb6 100644 --- a/chrome/browser/extensions/user_script_master.cc +++ b/chrome/browser/extensions/user_script_master.cc @@ -108,7 +108,7 @@ bool UserScriptMaster::ScriptReloader::ParseMetadataHeader( script->set_description(value); } else if (GetDeclarationValue(line, kMatchDeclaration, &value)) { URLPattern pattern(UserScript::kValidUserScriptSchemes); - if (!pattern.Parse(value)) + if (URLPattern::PARSE_SUCCESS != pattern.Parse(value)) return false; script->add_url_pattern(pattern); } else if (GetDeclarationValue(line, kRunAtDeclaration, &value)) { @@ -253,14 +253,7 @@ static base::SharedMemory* Serialize(const UserScriptList& scripts) { // Create the shared memory object. scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); - if (!shared_memory->Create(std::string(), // anonymous - false, // read-only - false, // open existing - pickle.size())) - return NULL; - - // Map into our process. - if (!shared_memory->Map(pickle.size())) + if (!shared_memory->CreateAndMapAnonymous(pickle.size())) return NULL; // Copy the pickle to shared memory. @@ -346,7 +339,7 @@ void UserScriptMaster::Observe(NotificationType type, break; case NotificationType::EXTENSION_LOADED: { // Add any content scripts inside the extension. - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); bool incognito_enabled = profile_->GetExtensionsService()-> IsIncognitoEnabled(extension); bool allow_file_access = profile_->GetExtensionsService()-> @@ -364,7 +357,7 @@ void UserScriptMaster::Observe(NotificationType type, } case NotificationType::EXTENSION_UNLOADED: { // Remove any content scripts. - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); UserScriptList new_lone_scripts; for (UserScriptList::iterator iter = lone_scripts_.begin(); iter != lone_scripts_.end(); ++iter) { @@ -380,7 +373,7 @@ void UserScriptMaster::Observe(NotificationType type, break; } case NotificationType::EXTENSION_USER_SCRIPTS_UPDATED: { - Extension* extension = Details<Extension>(details).ptr(); + const Extension* extension = Details<const Extension>(details).ptr(); UserScriptList new_lone_scripts; bool incognito_enabled = profile_->GetExtensionsService()-> IsIncognitoEnabled(extension); diff --git a/chrome/browser/extensions/window_open_apitest.cc b/chrome/browser/extensions/window_open_apitest.cc index 27c1b96..5de474b 100644 --- a/chrome/browser/extensions/window_open_apitest.cc +++ b/chrome/browser/extensions/window_open_apitest.cc @@ -11,13 +11,7 @@ #include "chrome/test/ui_test_utils.h" #include "net/base/mock_host_resolver.h" -// crbug.com/60156 -#if defined(OS_MACOSX) -// On mac, this test basically never succeeds. -#define FLAKY_WindowOpen DISABLED_WindowOpen -#endif - -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FLAKY_WindowOpen) { +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowOpen) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); |