diff options
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r-- | chrome/browser/extensions/extension.cc | 117 | ||||
-rw-r--r-- | chrome/browser/extensions/extension.h | 32 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.cc | 53 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.h | 11 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service_unittest.cc | 77 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_ui.cc | 2 |
6 files changed, 251 insertions, 41 deletions
diff --git a/chrome/browser/extensions/extension.cc b/chrome/browser/extensions/extension.cc index 0f9c24e..e1533db6 100644 --- a/chrome/browser/extensions/extension.cc +++ b/chrome/browser/extensions/extension.cc @@ -30,6 +30,9 @@ const wchar_t* Extension::kPluginsDirKey = L"plugins_dir"; const wchar_t* Extension::kBackgroundKey = L"background_page"; const wchar_t* Extension::kRunAtKey = L"run_at"; const wchar_t* Extension::kThemeKey = L"theme"; +const wchar_t* Extension::kThemeImagesKey = L"images"; +const wchar_t* Extension::kThemeColorsKey = L"colors"; +const wchar_t* Extension::kThemeTintsKey = L"tints"; const wchar_t* Extension::kToolstripsKey = L"toolstrips"; const wchar_t* Extension::kTooltipKey = L"tooltip"; const wchar_t* Extension::kTypeKey = L"type"; @@ -110,6 +113,16 @@ const char* Extension::kMissingFileError = "At least one js or css file is required for 'content_scripts[*]'."; const char* Extension::kMissingPageActionIcon = "Unable to find 'page_actions[*].icon'"; +const char* Extension::kInvalidThemeError = + "Invalid value for 'theme'."; +const char* Extension::kInvalidThemeImagesError = + "Invalid value for theme images - images must be strings."; +const char* Extension::kInvalidThemeImagesMissingError = + "Am image specified in the theme is missing."; +const char* Extension::kInvalidThemeColorsError = + "Invalid value for theme colors - colors must be integers"; +const char* Extension::kInvalidThemeTintsError = + "Invalid value for theme images - tints must be decimal numbers."; const size_t Extension::kIdSize = 20; // SHA1 (160 bits) == 20 bytes @@ -124,7 +137,10 @@ Extension::Extension(const Extension& rhs) page_actions_(rhs.page_actions_), plugins_dir_(rhs.plugins_dir_), zip_hash_(rhs.zip_hash_), - theme_paths_(rhs.theme_paths_) { + theme_images_(rhs.theme_images_), + theme_colors_(rhs.theme_colors_), + theme_tints_(rhs.theme_tints_), + is_theme_(rhs.is_theme_) { } Extension::~Extension() { @@ -149,14 +165,6 @@ GURL Extension::GetResourceURL(const GURL& extension_url, return ret_val; } -FilePath Extension::GetThemeResourcePath(const int resource_id) { - std::wstring id = IntToWString(resource_id); - std::string path = theme_paths_[id]; - if (path.size()) - return path_.AppendASCII(path.c_str()); - return FilePath(); -} - const PageAction* Extension::GetPageAction(std::string id) const { PageActionMap::const_iterator it = page_actions_.find(id); if (it == page_actions_.end()) @@ -500,6 +508,81 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_id, } } + // Initialize themes. If a theme is included, no other items may be processed + // (we currently don't want people bundling themes and extension stuff + // together). + // + // TODO(glen): Error if other items *are* included. + is_theme_ = false; + if (source.HasKey(kThemeKey)) { + DictionaryValue* theme_value; + if (!source.GetDictionary(kThemeKey, &theme_value)) { + *error = kInvalidThemeError; + return false; + } + is_theme_ = true; + + theme_images_ = new DictionaryValue; + DictionaryValue* images_value; + if (theme_value->GetDictionary(kThemeImagesKey, &images_value)) { + // Validate that the images are all strings + DictionaryValue::key_iterator iter = images_value->begin_keys(); + while (iter != images_value->end_keys()) { + std::string val; + if (!images_value->GetString(*iter, &val)) { + *error = kInvalidThemeImagesError; + return false; + } + ++iter; + } + theme_images_ = static_cast<DictionaryValue*>(images_value->DeepCopy()); + } + + theme_colors_ = new DictionaryValue; + DictionaryValue* colors_value; + if (theme_value->GetDictionary(kThemeColorsKey, &colors_value)) { + // Validate that the colors are all three-item lists + DictionaryValue::key_iterator iter = colors_value->begin_keys(); + while (iter != colors_value->end_keys()) { + std::string val; + int color = 0; + ListValue* color_list; + if (!colors_value->GetList(*iter, &color_list) || + color_list->GetSize() != 3 || + !color_list->GetInteger(0, &color) || + !color_list->GetInteger(1, &color) || + !color_list->GetInteger(2, &color)) { + *error = kInvalidThemeColorsError; + return false; + } + ++iter; + } + theme_colors_ = static_cast<DictionaryValue*>(colors_value->DeepCopy()); + } + + theme_tints_ = new DictionaryValue; + DictionaryValue* tints_value; + if (theme_value->GetDictionary(kThemeTintsKey, &tints_value)) { + // Validate that the tints are all reals. + DictionaryValue::key_iterator iter = tints_value->begin_keys(); + while (iter != tints_value->end_keys()) { + ListValue* tint_list; + double hue = 0; + if (!tints_value->GetList(*iter, &tint_list) && + tint_list->GetSize() != 3 || + !tint_list->GetReal(0, &hue) || + !tint_list->GetReal(1, &hue) || + !tint_list->GetReal(2, &hue)) { + *error = kInvalidThemeTintsError; + return false; + } + ++iter; + } + theme_tints_ = static_cast<DictionaryValue*>(tints_value->DeepCopy()); + } + return true; + } + // Initialize plugins dir (optional). if (source.HasKey(kPluginsDirKey)) { std::string plugins_dir; @@ -538,22 +621,6 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_id, } } - if (source.HasKey(kThemeKey)) { - DictionaryValue* dict_value; - if (source.GetDictionary(kThemeKey, &dict_value)) { - DictionaryValue::key_iterator iter = dict_value->begin_keys(); - while (iter != dict_value->end_keys()) { - std::string val; - if (dict_value->GetString(*iter, &val)) { - std::wstring id = *iter; - theme_paths_[id] = val; - } - ++iter; - } - ResourceBundle::GetSharedInstance().SetThemeExtension(*this); - } - } - // Initialize content scripts (optional). if (source.HasKey(kContentScriptsKey)) { ListValue* list_value; diff --git a/chrome/browser/extensions/extension.h b/chrome/browser/extensions/extension.h index c612203..4e19c9a 100644 --- a/chrome/browser/extensions/extension.h +++ b/chrome/browser/extensions/extension.h @@ -18,6 +18,7 @@ #include "chrome/common/extensions/url_pattern.h" #include "chrome/common/page_action.h" #include "googleurl/src/gurl.h" +#include "SkColor.h" // Represents a Chromium extension. class Extension { @@ -45,6 +46,9 @@ class Extension { static const wchar_t* kBackgroundKey; static const wchar_t* kRunAtKey; static const wchar_t* kThemeKey; + static const wchar_t* kThemeImagesKey; + static const wchar_t* kThemeColorsKey; + static const wchar_t* kThemeTintsKey; static const wchar_t* kToolstripsKey; static const wchar_t* kTooltipKey; static const wchar_t* kTypeKey; @@ -87,6 +91,11 @@ class Extension { static const char* kInvalidPermissionError; static const char* kInvalidPermissionSchemeError; static const char* kInvalidZipHashError; + static const char* kInvalidThemeError; + static const char* kInvalidThemeImagesMissingError; + static const char* kInvalidThemeImagesError; + static const char* kInvalidThemeColorsError; + static const char* kInvalidThemeTintsError; static const char* kMissingFileError; static const char* kMissingPageActionIcon; @@ -115,19 +124,17 @@ class Extension { return GetResourcePath(path(), relative_path); } + DictionaryValue* GetThemeImages() { return theme_images_; } + DictionaryValue* GetThemeColors() { return theme_colors_; } + DictionaryValue* GetThemeTints() { return theme_tints_; } + bool IsTheme() { return is_theme_; } + // Initialize the extension from a parsed manifest. // If |require_id| is true, will return an error if the "id" key is missing // from the value. bool InitFromValue(const DictionaryValue& value, bool require_id, std::string* error); - // Returns an absolute path to a resource inside of an extension if the - // extension has a theme defined with the given |resource_id|. Otherwise - // the path will be empty. Note that this method is not static as it is - // only intended to be called on an extension which has registered itself - // as providing a theme. - FilePath GetThemeResourcePath(const int resource_id); - // Retrieves a page action by |id|. const PageAction* GetPageAction(std::string id) const; @@ -207,7 +214,16 @@ class Extension { std::string zip_hash_; // A map of resource id's to relative file paths. - std::map<const std::wstring, std::string> theme_paths_; + DictionaryValue* theme_images_; + + // A map of color names to colors. + DictionaryValue* theme_colors_; + + // A map of color names to colors. + DictionaryValue* theme_tints_; + + // Whether the extension is a theme - if it is, certain things are disabled. + bool is_theme_; // The sites this extension has permission to talk to (using XHR, etc). std::vector<URLPattern> permissions_; diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index da5db0f1..2a152e1 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -183,11 +183,30 @@ void ExtensionsService::OnExtensionInstalled(Extension* extension, NotificationType::EXTENSION_INSTALLED, NotificationService::AllSources(), Details<Extension>(extension)); + + // If the extension is a theme, tell the profile (and therefore ThemeProvider) + // to apply it. + if (extension->IsTheme()) { + NotificationService::current()->Notify( + NotificationType::THEME_INSTALLED, + NotificationService::AllSources(), + Details<Extension>(extension)); + } +} + +void ExtensionsService::OnExtensionVersionReinstalled(const std::string& id) { + Extension* extension = GetExtensionByID(id); + if (extension && extension->IsTheme()) { + NotificationService::current()->Notify( + NotificationType::THEME_INSTALLED, + NotificationService::AllSources(), + Details<Extension>(extension)); + } } Extension* ExtensionsService::GetExtensionByID(std::string id) { for (ExtensionList::const_iterator iter = extensions_.begin(); - iter != extensions_.end(); ++iter) { + iter != extensions_.end(); ++iter) { if ((*iter)->id() == id) return *iter; } @@ -329,7 +348,24 @@ Extension* ExtensionsServiceBackend::LoadExtension( return NULL; } - // Validate that claimed resources actually exist. + // Validate that claimed image resources actually exist. + DictionaryValue* images_value = extension->GetThemeImages(); + if (images_value) { + DictionaryValue::key_iterator iter = images_value->begin_keys(); + while (iter != images_value->end_keys()) { + std::string val; + images_value->GetString(*iter, &val); + const FilePath& path = extension->path().AppendASCII(val); + if (!file_util::PathExists(path)) { + ReportExtensionLoadError(extension_path, + StringPrintf("Could not load '%s' for theme.", + WideToUTF8(path.ToWStringHack()).c_str())); + return NULL; + } + ++iter; + } + } + // Validate that claimed script resources actually exist. for (size_t i = 0; i < extension->content_scripts().size(); ++i) { const UserScript& script = extension->content_scripts()[i]; @@ -532,8 +568,9 @@ bool ExtensionsServiceBackend::CheckCurrentVersion( // has actually loaded successfully. FilePath version_dir = dest_dir.AppendASCII(current_version_str); if (file_util::PathExists(version_dir)) { - ReportExtensionInstallError(dest_dir, - "Existing version is already up to date."); + std::string id = WideToASCII(dest_dir.BaseName().ToWStringHack()); + StringToLowerASCII(&id); + ReportExtensionVersionReinstalled(id); return false; } } @@ -727,6 +764,14 @@ void ExtensionsServiceBackend::ReportExtensionInstallError( ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_); } +void ExtensionsServiceBackend::ReportExtensionVersionReinstalled( + const std::string& id) { + frontend_->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod( + frontend_, + &ExtensionsServiceFrontendInterface::OnExtensionVersionReinstalled, + id)); +} + void ExtensionsServiceBackend::ReportExtensionInstalled( const FilePath& path, bool update) { // After it's installed, load it right away with the same settings. diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h index 9a83c99..31b8f90e 100644 --- a/chrome/browser/extensions/extensions_service.h +++ b/chrome/browser/extensions/extensions_service.h @@ -51,8 +51,15 @@ class ExtensionsServiceFrontendInterface // installed extension rather than a new installation. virtual void OnExtensionInstalled(Extension* extension, bool is_update) = 0; + // Called when an existing extension is installed by the user. We may wish to + // notify the user about the prior existence of the extension, or take some + // action using the fact that the user chose to reinstall the extension as a + // signal (for example, setting the default theme to the extension). + virtual void OnExtensionVersionReinstalled(const std::string& id) = 0; + // Lookup an extension by |id|. virtual Extension* GetExtensionByID(std::string id) = 0; + }; @@ -77,6 +84,7 @@ class ExtensionsService : public ExtensionsServiceFrontendInterface { virtual void OnExtensionsLoaded(ExtensionList* extensions); virtual void OnExtensionInstalled(Extension* extension, bool is_update); virtual Extension* GetExtensionByID(std::string id); + virtual void OnExtensionVersionReinstalled(const std::string& id); // The name of the file that the current active version number is stored in. static const char* kCurrentVersionFileName; @@ -176,6 +184,9 @@ class ExtensionsServiceBackend void ReportExtensionInstallError(const FilePath& extension_path, const std::string& error); + // Notify the frontend that the extension had already been installed. + void ReportExtensionVersionReinstalled(const std::string& id); + // Notify the frontend that extensions were installed. // |is_update| is true if this was an update to an existing extension. void ReportExtensionInstalled(const FilePath& path, bool is_update); diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc index e6c27df..dc9be78 100644 --- a/chrome/browser/extensions/extensions_service_unittest.cc +++ b/chrome/browser/extensions/extensions_service_unittest.cc @@ -66,10 +66,19 @@ class ExtensionsServiceTestFrontend return &extensions_; } + void ClearInstalledReinstalled() { + installed_ = NULL; + reinstalled_id_ = std::string(); + } + Extension* installed() { return installed_; } + std::string reinstalled_id() { + return reinstalled_id_; + } + // ExtensionsServiceFrontendInterface virtual MessageLoop* GetMessageLoop() { return &message_loop_; @@ -94,6 +103,10 @@ class ExtensionsServiceTestFrontend installed_ = extension; } + virtual void OnExtensionVersionReinstalled(const std::string& id) { + reinstalled_id_ = id; + } + virtual Extension* GetExtensionByID(std::string id) { return NULL; } @@ -122,11 +135,11 @@ class ExtensionsServiceTestFrontend ExtensionErrorReporter::GetInstance()->ClearErrors(); } - private: MessageLoop message_loop_; ExtensionList extensions_; Extension* installed_; + std::string reinstalled_id_; }; // make the test a PlatformTest to setup autorelease pools properly on mac @@ -277,9 +290,6 @@ TEST_F(ExtensionsServiceTest, InstallExtension) { frontend->TestInstallExtension(path, backend, true); // TODO(erikkay): verify the contents of the installed extension. - // Installing the same extension twice should fail. - frontend->TestInstallExtension(path, backend, false); - // 0-length extension file. path = extensions_path.AppendASCII("not_an_extension.crx"); frontend->TestInstallExtension(path, backend, false); @@ -300,6 +310,65 @@ TEST_F(ExtensionsServiceTest, InstallExtension) { // TODO(erikkay): add tests for upgrade cases. } +TEST_F(ExtensionsServiceTest, ReinstallExtension) { + // In this test, we install two extensions, verify that they both install + // correctly, then install the first extension again and verify that it was + // not installed, and that VersionReinstalled was called instead. + FilePath extensions_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path)); + extensions_path = extensions_path.AppendASCII("extensions"); + + FilePath install_dir; + file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("ext_test"), + &install_dir); + scoped_refptr<ExtensionsServiceBackend> backend( + new ExtensionsServiceBackend(install_dir)); + scoped_refptr<ExtensionsServiceTestFrontend> frontend( + new ExtensionsServiceTestFrontend); + + FilePath path = extensions_path.AppendASCII("good.crx"); + FilePath path2 = extensions_path.AppendASCII("theme.crx"); + + // Verify that our extensions are valid. + ASSERT_TRUE(file_util::PathExists(path)); + ASSERT_TRUE(file_util::PathExists(path2)); + + frontend->ClearInstalledReinstalled(); + // Install an extension. + backend->InstallExtension(path, + scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get())); + frontend->GetMessageLoop()->RunAllPending(); + std::vector<std::string> errors = GetErrors(); + + // Verify that it was installed. + EXPECT_TRUE(frontend->installed()) << path.value(); + EXPECT_EQ(0u, errors.size()) << path.value(); + + // Install our second extension. + frontend->ClearInstalledReinstalled(); + backend->InstallExtension(path2, + scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get())); + frontend->GetMessageLoop()->RunAllPending(); + errors = GetErrors(); + + // Verify that it was installed without reinstall getting called. + EXPECT_TRUE(frontend->installed()) << path2.value(); + EXPECT_TRUE(frontend->reinstalled_id().empty()); + EXPECT_EQ(0u, errors.size()) << path.value(); + + // Install the first extension again. + frontend->ClearInstalledReinstalled(); + backend->InstallExtension(path, + scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get())); + frontend->GetMessageLoop()->RunAllPending(); + errors = GetErrors(); + + // Verify that reinstall was called and installed was not. + EXPECT_FALSE(frontend->installed()) << path.value(); + EXPECT_FALSE(frontend->reinstalled_id().empty()) << path.value(); + EXPECT_EQ(0u, errors.size()) << path.value(); +} + TEST_F(ExtensionsServiceTest, LoadExtension) { FilePath extensions_path; ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path)); diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc index d74b802..d05e39a 100644 --- a/chrome/browser/extensions/extensions_ui.cc +++ b/chrome/browser/extensions/extensions_ui.cc @@ -9,12 +9,14 @@ #include "base/string_util.h" #include "base/thread.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/extension.h" #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/extensions/extension_error_reporter.h" #include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/render_widget_host.h" #include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/common/extensions/user_script.h" #include "chrome/common/extensions/url_pattern.h" #include "chrome/common/jstemplate_builder.h" #include "chrome/common/url_constants.h" |