summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r--chrome/browser/extensions/extension.cc117
-rw-r--r--chrome/browser/extensions/extension.h32
-rw-r--r--chrome/browser/extensions/extensions_service.cc53
-rw-r--r--chrome/browser/extensions/extensions_service.h11
-rw-r--r--chrome/browser/extensions/extensions_service_unittest.cc77
-rw-r--r--chrome/browser/extensions/extensions_ui.cc2
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"