Chromium Code Reviews| Index: chrome/browser/extensions/extensions_ui.cc |
| diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc |
| index e4ef3cf063682fece61a80a4b9505445d50ad502..0e3dc3286cf0d28f83cd2bb09d72eab191351936 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/base64.h" |
| #include "net/base/net_util.h" |
| - |
|
Finnur
2009/11/05 03:55:08
nit: Did you delete this line on purpose?
I think
|
| #include "grit/browser_resources.h" |
| #include "grit/generated_resources.h" |
| #include "grit/theme_resources.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 |
| +// |
| +/////////////////////////////////////////////////////////////////////////////// |
|
Finnur
2009/11/05 03:55:08
nit: Add one more / to complete 80 columns. :)
|
| + |
| +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)); |
|
Finnur
2009/11/05 03:55:08
nit: Why CHECK here instead of DCHECK?
|
| + 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); |
| - dom_ui_->CallJavascriptFunction(L"returnExtensionsData", results); |
| + if (icon_loader_.get()) |
| + icon_loader_->Cancel(); |
| + |
| + icon_loader_ = new IconLoader(this); |
| + icon_loader_->LoadIcons(extension_icons, 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: ----------------------------------------------- |