summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/app/generated_resources.grd2
-rw-r--r--chrome/browser/extensions/extensions_ui.cc154
-rw-r--r--chrome/browser/extensions/extensions_ui.h53
-rw-r--r--chrome/browser/resources/extensions_ui.html13
-rw-r--r--chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json1
-rw-r--r--chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json1
-rw-r--r--chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json1
7 files changed, 194 insertions, 31 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 5bf6760..f8ce541 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2008,7 +2008,7 @@ each locale. -->
</message>
<message name="IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION"
desc="Message shown to the user to validate the download of an extension file.">
- Extensions and themes can harm your computer. Are you sure you want to continue?
+ Extensions can harm your computer. Are you sure you want to continue?
</message>
<message name="IDS_SAVE_DOWNLOAD"
desc="Text for the button used to validate the downloading of a dangerous download.">
diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc
index 758f70d..67304f7 100644
--- a/chrome/browser/extensions/extensions_ui.cc
+++ b/chrome/browser/extensions/extensions_ui.cc
@@ -4,8 +4,12 @@
#include "chrome/browser/extensions/extensions_ui.h"
+#include "app/gfx/codec/png_codec.h"
+#include "app/gfx/color_utils.h"
+#include "app/gfx/skbitmap_operations.h"
#include "app/l10n_util.h"
#include "app/resource_bundle.h"
+#include "base/file_util.h"
#include "base/string_util.h"
#include "base/thread.h"
#include "chrome/browser/browser.h"
@@ -31,11 +35,12 @@
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "chrome/common/url_constants.h"
-#include "net/base/net_util.h"
-
#include "grit/browser_resources.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
+#include "net/base/base64.h"
+#include "net/base/net_util.h"
+#include "webkit/glue/image_decoder.h"
////////////////////////////////////////////////////////////////////////////////
//
@@ -115,6 +120,97 @@ void ExtensionsUIHTMLSource::StartDataRequest(const std::string& path,
SendResponse(request_id, html_bytes);
}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ExtensionsDOMHandler::IconLoader
+//
+////////////////////////////////////////////////////////////////////////////////
+
+ExtensionsDOMHandler::IconLoader::IconLoader(ExtensionsDOMHandler* handler)
+ : handler_(handler) {
+}
+
+void ExtensionsDOMHandler::IconLoader::LoadIcons(
+ std::vector<ExtensionResource>* icons, DictionaryValue* json) {
+ ChromeThread::PostTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(this,
+ &IconLoader::LoadIconsOnFileThread, icons, json));
+}
+
+void ExtensionsDOMHandler::IconLoader::Cancel() {
+ handler_ = NULL;
+}
+
+void ExtensionsDOMHandler::IconLoader::LoadIconsOnFileThread(
+ std::vector<ExtensionResource>* icons, DictionaryValue* json) {
+ scoped_ptr<std::vector<ExtensionResource> > icons_deleter(icons);
+ scoped_ptr<DictionaryValue> json_deleter(json);
+
+ ListValue* extensions = NULL;
+ CHECK(json->GetList(L"extensions", &extensions));
+
+ for (size_t i = 0; i < icons->size(); ++i) {
+ DictionaryValue* extension = NULL;
+ CHECK(extensions->GetDictionary(static_cast<int>(i), &extension));
+
+ // Read the file.
+ std::string file_contents;
+ if (icons->at(i).relative_path().empty() ||
+ !file_util::ReadFileToString(icons->at(i).GetFilePath(),
+ &file_contents)) {
+ // If there's no icon, default to the puzzle icon. This is safe to do from
+ // the file thread.
+ file_contents = ResourceBundle::GetSharedInstance().GetDataResource(
+ IDR_INFOBAR_PLUGIN_INSTALL);
+ }
+
+ // If the extension is disabled, we desaturate the icon to add to the
+ // disabledness effect.
+ bool enabled = false;
+ CHECK(extension->GetBoolean(L"enabled", &enabled));
+ if (!enabled) {
+ const unsigned char* data =
+ reinterpret_cast<const unsigned char*>(file_contents.data());
+ webkit_glue::ImageDecoder decoder;
+ scoped_ptr<SkBitmap> decoded(new SkBitmap());
+ *decoded = decoder.Decode(data, file_contents.length());
+
+ // Desaturate the icon and lighten it a bit.
+ color_utils::HSL shift = {-1, 0, 0.6};
+ *decoded = SkBitmapOperations::CreateHSLShiftedBitmap(*decoded, shift);
+
+ std::vector<unsigned char> output;
+ gfx::PNGCodec::EncodeBGRASkBitmap(*decoded, false, &output);
+
+ // Lame, but we must make a copy of this now, because base64 doesn't take
+ // the same input type.
+ file_contents.assign(reinterpret_cast<char*>(&output.front()),
+ output.size());
+ }
+
+ // Create a data URL (all icons are converted to PNGs during unpacking).
+ std::string base64_encoded;
+ net::Base64Encode(file_contents, &base64_encoded);
+ GURL icon_url("data:image/png;base64," + base64_encoded);
+
+ extension->SetString(L"icon", icon_url.spec());
+ }
+
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &IconLoader::ReportResultOnUIThread,
+ json_deleter.release()));
+}
+
+void ExtensionsDOMHandler::IconLoader::ReportResultOnUIThread(
+ DictionaryValue* json) {
+ if (handler_)
+ handler_->OnIconsLoaded(json);
+}
+
+
///////////////////////////////////////////////////////////////////////////////
//
// ExtensionsDOMHandler
@@ -151,10 +247,17 @@ void ExtensionsDOMHandler::RegisterMessages() {
}
void ExtensionsDOMHandler::HandleRequestExtensionsData(const Value* value) {
- DictionaryValue results;
+ DictionaryValue* results = new DictionaryValue();
// Add the extensions to the results structure.
ListValue *extensions_list = new ListValue();
+
+ // Stores the icon resource for each of the extensions in extensions_list. We
+ // build up a list of them here, then load them on the file thread in
+ // ::LoadIcons().
+ std::vector<ExtensionResource>* extension_icons =
+ new std::vector<ExtensionResource>();
+
const ExtensionList* extensions = extensions_service_->extensions();
for (ExtensionList::const_iterator extension = extensions->begin();
extension != extensions->end(); ++extension) {
@@ -163,6 +266,7 @@ void ExtensionsDOMHandler::HandleRequestExtensionsData(const Value* value) {
if (!(*extension)->IsTheme()) {
extensions_list->Append(CreateExtensionDetailValue(
*extension, GetActivePagesForExtension((*extension)->id()), true));
+ extension_icons->push_back(PickExtensionIcon(*extension));
}
}
extensions = extensions_service_->disabled_extensions();
@@ -171,15 +275,25 @@ void ExtensionsDOMHandler::HandleRequestExtensionsData(const Value* value) {
if (!(*extension)->IsTheme()) {
extensions_list->Append(CreateExtensionDetailValue(
*extension, GetActivePagesForExtension((*extension)->id()), false));
+ extension_icons->push_back(PickExtensionIcon(*extension));
}
}
- results.Set(L"extensions", extensions_list);
+ results->Set(L"extensions", extensions_list);
bool developer_mode = dom_ui_->GetProfile()->GetPrefs()
->GetBoolean(prefs::kExtensionsUIDeveloperMode);
- results.SetBoolean(L"developerMode", developer_mode);
+ results->SetBoolean(L"developerMode", developer_mode);
+
+ if (icon_loader_.get())
+ icon_loader_->Cancel();
+
+ icon_loader_ = new IconLoader(this);
+ icon_loader_->LoadIcons(extension_icons, results);
+}
- dom_ui_->CallJavascriptFunction(L"returnExtensionsData", results);
+void ExtensionsDOMHandler::OnIconsLoaded(DictionaryValue* json) {
+ dom_ui_->CallJavascriptFunction(L"returnExtensionsData", *json);
+ delete json;
// Register for notifications that we need to reload the page.
registrar_.RemoveAll();
@@ -193,6 +307,20 @@ void ExtensionsDOMHandler::HandleRequestExtensionsData(const Value* value) {
NotificationService::AllSources());
}
+ExtensionResource ExtensionsDOMHandler::PickExtensionIcon(
+ Extension* extension) {
+ // Try to fetch the medium sized icon, then (if missing) go for the large one.
+ const std::map<int, std::string>& icons = extension->icons();
+ std::map<int, std::string>::const_iterator iter =
+ icons.find(Extension::EXTENSION_ICON_MEDIUM);
+ if (iter == icons.end())
+ iter = icons.find(Extension::EXTENSION_ICON_LARGE);
+ if (iter != icons.end())
+ return extension->GetResource(iter->second);
+ else
+ return ExtensionResource();
+}
+
void ExtensionsDOMHandler::HandleToggleDeveloperMode(const Value* value) {
bool developer_mode = dom_ui_->GetProfile()->GetPrefs()
->GetBoolean(prefs::kExtensionsUIDeveloperMode);
@@ -481,17 +609,6 @@ DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue(
if (!extension->options_url().is_empty())
extension_data->SetString(L"options_url", extension->options_url().spec());
- // Try to fetch the medium sized icon, then (if missing) go for the large one.
- const std::map<int, std::string>& icons = extension->icons();
- std::map<int, std::string>::const_iterator iter =
- icons.find(Extension::EXTENSION_ICON_MEDIUM);
- if (iter == icons.end())
- iter = icons.find(Extension::EXTENSION_ICON_LARGE);
- if (iter != icons.end())
- extension_data->SetString(L"icon", iter->second);
- else
- extension_data->SetString(L"icon", "");
-
// Add list of content_script detail DictionaryValues
ListValue *content_script_list = new ListValue();
UserScriptList content_scripts = extension->content_scripts();
@@ -550,6 +667,9 @@ std::vector<ExtensionPage> ExtensionsDOMHandler::GetActivePagesForExtension(
ExtensionsDOMHandler::~ExtensionsDOMHandler() {
if (pack_job_.get())
pack_job_->ClearClient();
+
+ if (icon_loader_.get())
+ icon_loader_->Cancel();
}
// ExtensionsDOMHandler, public: -----------------------------------------------
diff --git a/chrome/browser/extensions/extensions_ui.h b/chrome/browser/extensions/extensions_ui.h
index 37e73b6..6023255 100644
--- a/chrome/browser/extensions/extensions_ui.h
+++ b/chrome/browser/extensions/extensions_ui.h
@@ -12,6 +12,7 @@
#include "chrome/browser/dom_ui/dom_ui.h"
#include "chrome/browser/extensions/pack_extension_job.h"
#include "chrome/browser/shell_dialogs.h"
+#include "chrome/common/extensions/extension_resource.h"
#include "chrome/common/notification_observer.h"
#include "chrome/common/notification_registrar.h"
#include "googleurl/src/gurl.h"
@@ -57,6 +58,41 @@ class ExtensionsDOMHandler
public PackExtensionJob::Client,
public SelectFileDialog::Listener {
public:
+
+ // Helper class that loads the icons for the extensions in the management UI.
+ // We do this with native code instead of just using chrome-extension:// URLs
+ // for two reasons:
+ //
+ // 1. We need to support the disabled extensions, too, and using URLs won't
+ // work for them.
+ // 2. We want to desaturate the icons of the disabled extensions to make them
+ // look disabled.
+ class IconLoader : public base::RefCountedThreadSafe<IconLoader> {
+ public:
+ explicit IconLoader(ExtensionsDOMHandler* handler);
+
+ // Load |icons|. Will call handler->OnIconsLoaded when complete. IconLoader
+ // takes ownership of both arguments.
+ void LoadIcons(std::vector<ExtensionResource>* icons,
+ DictionaryValue* json);
+
+ // Cancel the load. IconLoader won't try to call back to the handler after
+ // this.
+ void Cancel();
+
+ private:
+ // Load the icons and call ReportResultOnUIThread when done. This method
+ // takes ownership of both arguments.
+ void LoadIconsOnFileThread(std::vector<ExtensionResource>* icons,
+ DictionaryValue* json);
+
+ // Report back to the handler. This method takes ownership of |json|.
+ void ReportResultOnUIThread(DictionaryValue* json);
+
+ // The handler we will report back to.
+ ExtensionsDOMHandler* handler_;
+ };
+
explicit ExtensionsDOMHandler(ExtensionsService* extension_service);
virtual ~ExtensionsDOMHandler();
@@ -135,6 +171,20 @@ class ExtensionsDOMHandler
std::vector<ExtensionPage> GetActivePagesForExtension(
const std::string& extension_id);
+ // 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);
+
+ // Loads the extension resources into the json data, then calls OnIconsLoaded.
+ // Takes ownership of |icons|.
+ // Called on the file thread.
+ void LoadExtensionIcons(std::vector<ExtensionResource>* icons,
+ DictionaryValue* json_data);
+
+ // Takes ownership of |json_data| and tells HTML about it.
+ // Called on the UI thread.
+ void OnIconsLoaded(DictionaryValue* json_data);
+
// Our model.
scoped_refptr<ExtensionsService> extensions_service_;
@@ -144,6 +194,9 @@ class ExtensionsDOMHandler
// Used to package the extension.
scoped_refptr<PackExtensionJob> pack_job_;
+ // Used to load icons asynchronously on the file thread.
+ scoped_refptr<IconLoader> icon_loader_;
+
// We monitor changes to the extension system so that we can reload when
// necessary.
NotificationRegistrar registrar_;
diff --git a/chrome/browser/resources/extensions_ui.html b/chrome/browser/resources/extensions_ui.html
index ac7e4f0..8873aa0 100644
--- a/chrome/browser/resources/extensions_ui.html
+++ b/chrome/browser/resources/extensions_ui.html
@@ -653,16 +653,9 @@ function autoUpdate() {
<table width="100%" cellpadding="2" cellspacing="0">
<tr jsvalues=".className:enabled ? 'extension_enabled' : 'extension_disabled'">
<td width="62" height="50" align="center" valign="top">
- <span jsdisplay="icon"
- ><img
- jsvalues=".src:'chrome-extension://' + id + '/' + icon"
- width="48" height="48" />
- </span>
- <span jsdisplay="icon == ''"
- ><img
- width="48" height="48"
- src="../../app/theme/infobar_plugin.png" /></span>
- </td>
+ <span jsdisplay="icon"><img jsvalues=".src:icon" width="48"
+ height="48" />
+ </td>
<td valign="top">
<div>
<span class="extension-name"
diff --git a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json
index f364e57..e038286 100644
--- a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json
+++ b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension1.json
@@ -5,7 +5,6 @@
"enabled": true,
"description": "The first extension that I made.",
"permissions": ["http://*.google.com/*", "https://*.google.com/*"],
- "icon": "icon_128.png",
"order": 2,
"content_scripts": [
{
diff --git a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json
index 18bc9d4..e3c8b9d 100644
--- a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json
+++ b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension2.json
@@ -5,7 +5,6 @@
"enabled": true,
"description": "",
"permissions": [],
- "icon": "",
"order": 2,
"content_scripts": [],
"views": [
diff --git a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json
index 3b2847a..fa011fb 100644
--- a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json
+++ b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json
@@ -5,7 +5,6 @@
"enabled": true,
"description": "",
"permissions": [],
- "icon": "",
"order": 2,
"content_scripts": [
{