Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / extensions / api / developer_private / extension_info_generator.cc
blob316997ca81c6789edcee56bd05e1635d49b513e7
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/developer_private/extension_info_generator.h"
7 #include "base/base64.h"
8 #include "base/location.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "chrome/browser/extensions/api/commands/command_service.h"
13 #include "chrome/browser/extensions/api/developer_private/inspectable_views_finder.h"
14 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
15 #include "chrome/browser/extensions/error_console/error_console.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/extensions/extension_ui_util.h"
18 #include "chrome/browser/extensions/extension_util.h"
19 #include "chrome/browser/extensions/path_util.h"
20 #include "chrome/browser/extensions/shared_module_service.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
23 #include "chrome/common/extensions/command.h"
24 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
25 #include "chrome/common/pref_names.h"
26 #include "chrome/grit/generated_resources.h"
27 #include "content/public/browser/render_frame_host.h"
28 #include "extensions/browser/extension_error.h"
29 #include "extensions/browser/extension_prefs.h"
30 #include "extensions/browser/extension_registry.h"
31 #include "extensions/browser/extension_system.h"
32 #include "extensions/browser/image_loader.h"
33 #include "extensions/browser/warning_service.h"
34 #include "extensions/common/extension_set.h"
35 #include "extensions/common/feature_switch.h"
36 #include "extensions/common/install_warning.h"
37 #include "extensions/common/manifest.h"
38 #include "extensions/common/manifest_handlers/background_info.h"
39 #include "extensions/common/manifest_handlers/icons_handler.h"
40 #include "extensions/common/manifest_handlers/offline_enabled_info.h"
41 #include "extensions/common/manifest_handlers/options_page_info.h"
42 #include "extensions/common/manifest_url_handlers.h"
43 #include "extensions/common/permissions/permissions_data.h"
44 #include "extensions/grit/extensions_browser_resources.h"
45 #include "ui/base/l10n/l10n_util.h"
46 #include "ui/base/resource/resource_bundle.h"
47 #include "ui/gfx/codec/png_codec.h"
48 #include "ui/gfx/color_utils.h"
49 #include "ui/gfx/image/image.h"
50 #include "ui/gfx/skbitmap_operations.h"
52 namespace extensions {
54 namespace developer = api::developer_private;
56 namespace {
58 // Given a Manifest::Type, converts it into its developer_private
59 // counterpart.
60 developer::ExtensionType GetExtensionType(Manifest::Type manifest_type) {
61 developer::ExtensionType type = developer::EXTENSION_TYPE_EXTENSION;
62 switch (manifest_type) {
63 case Manifest::TYPE_EXTENSION:
64 type = developer::EXTENSION_TYPE_EXTENSION;
65 break;
66 case Manifest::TYPE_THEME:
67 type = developer::EXTENSION_TYPE_THEME;
68 break;
69 case Manifest::TYPE_HOSTED_APP:
70 type = developer::EXTENSION_TYPE_HOSTED_APP;
71 break;
72 case Manifest::TYPE_LEGACY_PACKAGED_APP:
73 type = developer::EXTENSION_TYPE_LEGACY_PACKAGED_APP;
74 break;
75 case Manifest::TYPE_PLATFORM_APP:
76 type = developer::EXTENSION_TYPE_PLATFORM_APP;
77 break;
78 case Manifest::TYPE_SHARED_MODULE:
79 type = developer::EXTENSION_TYPE_SHARED_MODULE;
80 break;
81 default:
82 NOTREACHED();
84 return type;
87 // Populates the common fields of an extension error.
88 template <typename ErrorType>
89 void PopulateErrorBase(const ExtensionError& error, ErrorType* out) {
90 CHECK(out);
91 out->type = error.type() == ExtensionError::MANIFEST_ERROR ?
92 developer::ERROR_TYPE_MANIFEST : developer::ERROR_TYPE_RUNTIME;
93 out->extension_id = error.extension_id();
94 out->from_incognito = error.from_incognito();
95 out->source = base::UTF16ToUTF8(error.source());
96 out->message = base::UTF16ToUTF8(error.message());
97 out->id = error.id();
100 // Given a ManifestError object, converts it into its developer_private
101 // counterpart.
102 linked_ptr<developer::ManifestError> ConstructManifestError(
103 const ManifestError& error) {
104 linked_ptr<developer::ManifestError> result(new developer::ManifestError());
105 PopulateErrorBase(error, result.get());
106 result->manifest_key = base::UTF16ToUTF8(error.manifest_key());
107 if (!error.manifest_specific().empty()) {
108 result->manifest_specific.reset(
109 new std::string(base::UTF16ToUTF8(error.manifest_specific())));
111 return result;
114 // Given a RuntimeError object, converts it into its developer_private
115 // counterpart.
116 linked_ptr<developer::RuntimeError> ConstructRuntimeError(
117 const RuntimeError& error) {
118 linked_ptr<developer::RuntimeError> result(new developer::RuntimeError());
119 PopulateErrorBase(error, result.get());
120 switch (error.level()) {
121 case logging::LOG_VERBOSE:
122 case logging::LOG_INFO:
123 result->severity = developer::ERROR_LEVEL_LOG;
124 break;
125 case logging::LOG_WARNING:
126 result->severity = developer::ERROR_LEVEL_WARN;
127 break;
128 case logging::LOG_FATAL:
129 case logging::LOG_ERROR:
130 result->severity = developer::ERROR_LEVEL_ERROR;
131 break;
132 default:
133 NOTREACHED();
135 result->occurrences = error.occurrences();
136 // NOTE(devlin): This is called "render_view_id" in the api for legacy
137 // reasons, but it's not a high priority to change.
138 result->render_view_id = error.render_frame_id();
139 result->render_process_id = error.render_process_id();
140 result->can_inspect =
141 content::RenderFrameHost::FromID(error.render_process_id(),
142 error.render_frame_id()) != nullptr;
143 for (const StackFrame& f : error.stack_trace()) {
144 linked_ptr<developer::StackFrame> frame(new developer::StackFrame());
145 frame->line_number = f.line_number;
146 frame->column_number = f.column_number;
147 frame->url = base::UTF16ToUTF8(f.source);
148 frame->function_name = base::UTF16ToUTF8(f.function);
149 result->stack_trace.push_back(frame);
151 return result;
154 // Constructs any commands for the extension with the given |id|, and adds them
155 // to the list of |commands|.
156 void ConstructCommands(CommandService* command_service,
157 const std::string& extension_id,
158 std::vector<linked_ptr<developer::Command>>* commands) {
159 auto construct_command = [](const Command& command,
160 bool active,
161 bool is_extension_action) {
162 developer::Command* command_value = new developer::Command();
163 command_value->description = is_extension_action ?
164 l10n_util::GetStringUTF8(IDS_EXTENSION_COMMANDS_GENERIC_ACTIVATE) :
165 base::UTF16ToUTF8(command.description());
166 command_value->keybinding =
167 base::UTF16ToUTF8(command.accelerator().GetShortcutText());
168 command_value->name = command.command_name();
169 command_value->is_active = active;
170 command_value->scope = command.global() ? developer::COMMAND_SCOPE_GLOBAL :
171 developer::COMMAND_SCOPE_CHROME;
172 command_value->is_extension_action = is_extension_action;
173 return command_value;
175 bool active = false;
176 Command browser_action;
177 if (command_service->GetBrowserActionCommand(extension_id,
178 CommandService::ALL,
179 &browser_action,
180 &active)) {
181 commands->push_back(
182 make_linked_ptr(construct_command(browser_action, active, true)));
185 Command page_action;
186 if (command_service->GetPageActionCommand(extension_id,
187 CommandService::ALL,
188 &page_action,
189 &active)) {
190 commands->push_back(
191 make_linked_ptr(construct_command(page_action, active, true)));
194 CommandMap named_commands;
195 if (command_service->GetNamedCommands(extension_id,
196 CommandService::ALL,
197 CommandService::ANY_SCOPE,
198 &named_commands)) {
199 for (auto& pair : named_commands) {
200 Command& command_to_use = pair.second;
201 // TODO(devlin): For some reason beyond my knowledge, FindCommandByName
202 // returns different data than GetNamedCommands, including the
203 // accelerators, but not the descriptions - and even then, only if the
204 // command is active.
205 // Unfortunately, some systems may be relying on the other data (which
206 // more closely matches manifest data).
207 // Until we can sort all this out, we merge the two command structures.
208 Command active_command = command_service->FindCommandByName(
209 extension_id, command_to_use.command_name());
210 command_to_use.set_accelerator(active_command.accelerator());
211 command_to_use.set_global(active_command.global());
212 bool active = command_to_use.accelerator().key_code() != ui::VKEY_UNKNOWN;
213 commands->push_back(
214 make_linked_ptr(construct_command(command_to_use, active, false)));
219 } // namespace
221 ExtensionInfoGenerator::ExtensionInfoGenerator(
222 content::BrowserContext* browser_context)
223 : browser_context_(browser_context),
224 command_service_(CommandService::Get(browser_context)),
225 extension_system_(ExtensionSystem::Get(browser_context)),
226 extension_prefs_(ExtensionPrefs::Get(browser_context)),
227 extension_action_api_(ExtensionActionAPI::Get(browser_context)),
228 warning_service_(WarningService::Get(browser_context)),
229 error_console_(ErrorConsole::Get(browser_context)),
230 image_loader_(ImageLoader::Get(browser_context)),
231 pending_image_loads_(0u),
232 weak_factory_(this) {
235 ExtensionInfoGenerator::~ExtensionInfoGenerator() {
238 void ExtensionInfoGenerator::CreateExtensionInfo(
239 const std::string& id,
240 const ExtensionInfosCallback& callback) {
241 DCHECK(callback_.is_null() && list_.empty()) <<
242 "Only a single generation can be running at a time!";
243 ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
245 developer::ExtensionState state = developer::EXTENSION_STATE_NONE;
246 const Extension* ext = nullptr;
247 if ((ext = registry->enabled_extensions().GetByID(id)) != nullptr)
248 state = developer::EXTENSION_STATE_ENABLED;
249 else if ((ext = registry->disabled_extensions().GetByID(id)) != nullptr)
250 state = developer::EXTENSION_STATE_DISABLED;
251 else if ((ext = registry->terminated_extensions().GetByID(id)) != nullptr)
252 state = developer::EXTENSION_STATE_TERMINATED;
254 if (ext && ui_util::ShouldDisplayInExtensionSettings(ext, browser_context_))
255 CreateExtensionInfoHelper(*ext, state);
257 if (pending_image_loads_ == 0) {
258 // Don't call the callback re-entrantly.
259 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
260 base::Bind(callback, list_));
261 list_.clear();
262 } else {
263 callback_ = callback;
267 void ExtensionInfoGenerator::CreateExtensionsInfo(
268 bool include_disabled,
269 bool include_terminated,
270 const ExtensionInfosCallback& callback) {
271 auto add_to_list = [this](const ExtensionSet& extensions,
272 developer::ExtensionState state) {
273 for (const scoped_refptr<const Extension>& extension : extensions) {
274 if (ui_util::ShouldDisplayInExtensionSettings(extension.get(),
275 browser_context_)) {
276 CreateExtensionInfoHelper(*extension, state);
281 ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
282 add_to_list(registry->enabled_extensions(),
283 developer::EXTENSION_STATE_ENABLED);
284 if (include_disabled) {
285 add_to_list(registry->disabled_extensions(),
286 developer::EXTENSION_STATE_DISABLED);
288 if (include_terminated) {
289 add_to_list(registry->terminated_extensions(),
290 developer::EXTENSION_STATE_TERMINATED);
293 if (pending_image_loads_ == 0) {
294 // Don't call the callback re-entrantly.
295 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
296 base::Bind(callback, list_));
297 list_.clear();
298 } else {
299 callback_ = callback;
303 void ExtensionInfoGenerator::CreateExtensionInfoHelper(
304 const Extension& extension,
305 developer::ExtensionState state) {
306 scoped_ptr<developer::ExtensionInfo> info(new developer::ExtensionInfo());
308 // Don't consider the button hidden with the redesign, because "hidden"
309 // buttons are now just hidden in the wrench menu.
310 info->action_button_hidden =
311 !extension_action_api_->GetBrowserActionVisibility(extension.id()) &&
312 !FeatureSwitch::extension_action_redesign()->IsEnabled();
314 // Blacklist text.
315 int blacklist_text = -1;
316 switch (extension_prefs_->GetExtensionBlacklistState(extension.id())) {
317 case BLACKLISTED_SECURITY_VULNERABILITY:
318 blacklist_text = IDS_OPTIONS_BLACKLISTED_SECURITY_VULNERABILITY;
319 break;
320 case BLACKLISTED_CWS_POLICY_VIOLATION:
321 blacklist_text = IDS_OPTIONS_BLACKLISTED_CWS_POLICY_VIOLATION;
322 break;
323 case BLACKLISTED_POTENTIALLY_UNWANTED:
324 blacklist_text = IDS_OPTIONS_BLACKLISTED_POTENTIALLY_UNWANTED;
325 break;
326 default:
327 break;
329 if (blacklist_text != -1) {
330 info->blacklist_text.reset(
331 new std::string(l10n_util::GetStringUTF8(blacklist_text)));
334 Profile* profile = Profile::FromBrowserContext(browser_context_);
336 // ControlledInfo.
337 bool is_policy_location = Manifest::IsPolicyLocation(extension.location());
338 if (is_policy_location || util::IsExtensionSupervised(&extension, profile)) {
339 info->controlled_info.reset(new developer::ControlledInfo());
340 if (is_policy_location) {
341 info->controlled_info->type = developer::CONTROLLER_TYPE_POLICY;
342 info->controlled_info->text =
343 l10n_util::GetStringUTF8(IDS_OPTIONS_INSTALL_LOCATION_ENTERPRISE);
344 } else if (profile->IsChild()) {
345 info->controlled_info->type = developer::CONTROLLER_TYPE_CHILD_CUSTODIAN;
346 info->controlled_info->text = l10n_util::GetStringUTF8(
347 IDS_EXTENSIONS_INSTALLED_BY_CHILD_CUSTODIAN);
348 } else {
349 info->controlled_info->type =
350 developer::CONTROLLER_TYPE_SUPERVISED_USER_CUSTODIAN;
351 info->controlled_info->text = l10n_util::GetStringUTF8(
352 IDS_EXTENSIONS_INSTALLED_BY_SUPERVISED_USER_CUSTODIAN);
356 bool is_enabled = state == developer::EXTENSION_STATE_ENABLED;
358 // Commands.
359 if (is_enabled)
360 ConstructCommands(command_service_, extension.id(), &info->commands);
362 // Dependent extensions.
363 if (extension.is_shared_module()) {
364 scoped_ptr<ExtensionSet> dependent_extensions =
365 extension_system_->extension_service()->
366 shared_module_service()->GetDependentExtensions(&extension);
367 for (const scoped_refptr<const Extension>& dependent :
368 *dependent_extensions)
369 info->dependent_extensions.push_back(dependent->id());
372 info->description = extension.description();
374 // Disable reasons.
375 int disable_reasons = extension_prefs_->GetDisableReasons(extension.id());
376 info->disable_reasons.suspicious_install =
377 (disable_reasons & Extension::DISABLE_NOT_VERIFIED) != 0;
378 info->disable_reasons.corrupt_install =
379 (disable_reasons & Extension::DISABLE_CORRUPTED) != 0;
380 info->disable_reasons.update_required =
381 (disable_reasons & Extension::DISABLE_UPDATE_REQUIRED_BY_POLICY) != 0;
383 // Error collection.
384 bool error_console_enabled =
385 error_console_->IsEnabledForChromeExtensionsPage();
386 info->error_collection.is_enabled = error_console_enabled;
387 info->error_collection.is_active =
388 error_console_enabled &&
389 error_console_->IsReportingEnabledForExtension(extension.id());
391 // File access.
392 info->file_access.is_enabled = extension.wants_file_access();
393 info->file_access.is_active =
394 util::AllowFileAccess(extension.id(), browser_context_);
396 // Home page.
397 info->home_page.url = ManifestURL::GetHomepageURL(&extension).spec();
398 info->home_page.specified = ManifestURL::SpecifiedHomepageURL(&extension);
400 info->id = extension.id();
402 // Incognito access.
403 info->incognito_access.is_enabled = extension.can_be_incognito_enabled();
404 info->incognito_access.is_active =
405 util::IsIncognitoEnabled(extension.id(), browser_context_);
407 // Install warnings, but only if unpacked, the error console isn't enabled
408 // (otherwise it shows these), and we're in developer mode (normal users don't
409 // need to see these).
410 if (!error_console_enabled &&
411 Manifest::IsUnpackedLocation(extension.location()) &&
412 profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode)) {
413 const std::vector<InstallWarning>& install_warnings =
414 extension.install_warnings();
415 for (const InstallWarning& warning : install_warnings)
416 info->install_warnings.push_back(warning.message);
419 // Launch url.
420 if (extension.is_app()) {
421 info->launch_url.reset(
422 new std::string(AppLaunchInfo::GetFullLaunchURL(&extension).spec()));
425 // Location.
426 if (extension.location() == Manifest::INTERNAL &&
427 ManifestURL::UpdatesFromGallery(&extension)) {
428 info->location = developer::LOCATION_FROM_STORE;
429 } else if (Manifest::IsUnpackedLocation(extension.location())) {
430 info->location = developer::LOCATION_UNPACKED;
431 } else if (Manifest::IsExternalLocation(extension.location()) &&
432 ManifestURL::UpdatesFromGallery(&extension)) {
433 info->location = developer::LOCATION_THIRD_PARTY;
434 } else {
435 info->location = developer::LOCATION_UNKNOWN;
438 // Location text.
439 int location_text = -1;
440 if (info->location == developer::LOCATION_UNKNOWN)
441 location_text = IDS_OPTIONS_INSTALL_LOCATION_UNKNOWN;
442 else if (extension.location() == Manifest::EXTERNAL_REGISTRY)
443 location_text = IDS_OPTIONS_INSTALL_LOCATION_3RD_PARTY;
444 else if (extension.is_shared_module())
445 location_text = IDS_OPTIONS_INSTALL_LOCATION_SHARED_MODULE;
446 if (location_text != -1) {
447 info->location_text.reset(
448 new std::string(l10n_util::GetStringUTF8(location_text)));
451 // Runtime/Manifest errors.
452 if (error_console_enabled) {
453 const ErrorList& errors =
454 error_console_->GetErrorsForExtension(extension.id());
455 for (const ExtensionError* error : errors) {
456 switch (error->type()) {
457 case ExtensionError::MANIFEST_ERROR:
458 info->manifest_errors.push_back(ConstructManifestError(
459 static_cast<const ManifestError&>(*error)));
460 break;
461 case ExtensionError::RUNTIME_ERROR:
462 info->runtime_errors.push_back(ConstructRuntimeError(
463 static_cast<const RuntimeError&>(*error)));
464 break;
465 case ExtensionError::INTERNAL_ERROR:
466 // TODO(wittman): Support InternalError in developer tools:
467 // https://crbug.com/503427.
468 break;
469 case ExtensionError::NUM_ERROR_TYPES:
470 NOTREACHED();
471 break;
476 ManagementPolicy* management_policy = extension_system_->management_policy();
477 info->must_remain_installed =
478 management_policy->MustRemainInstalled(&extension, nullptr);
480 info->name = extension.name();
481 info->offline_enabled = OfflineEnabledInfo::IsOfflineEnabled(&extension);
483 // Options page.
484 if (OptionsPageInfo::HasOptionsPage(&extension)) {
485 info->options_page.reset(new developer::OptionsPage());
486 info->options_page->open_in_tab =
487 OptionsPageInfo::ShouldOpenInTab(&extension);
488 info->options_page->url =
489 OptionsPageInfo::GetOptionsPage(&extension).spec();
492 // Path.
493 if (Manifest::IsUnpackedLocation(extension.location())) {
494 info->path.reset(new std::string(extension.path().AsUTF8Unsafe()));
495 info->prettified_path.reset(new std::string(
496 extensions::path_util::PrettifyPath(extension.path()).AsUTF8Unsafe()));
499 // Runs on all urls.
500 info->run_on_all_urls.is_enabled =
501 (FeatureSwitch::scripts_require_action()->IsEnabled() &&
502 PermissionsData::ScriptsMayRequireActionForExtension(
503 &extension,
504 extension.permissions_data()->active_permissions().get())) ||
505 extension.permissions_data()->HasWithheldImpliedAllHosts() ||
506 util::HasSetAllowedScriptingOnAllUrls(extension.id(), browser_context_);
507 info->run_on_all_urls.is_active =
508 util::AllowedScriptingOnAllUrls(extension.id(), browser_context_);
510 // Runtime warnings.
511 std::vector<std::string> warnings =
512 warning_service_->GetWarningMessagesForExtension(extension.id());
513 for (const std::string& warning : warnings)
514 info->runtime_warnings.push_back(warning);
516 info->state = state;
518 info->type = GetExtensionType(extension.manifest()->type());
520 info->update_url = ManifestURL::GetUpdateURL(&extension).spec();
522 info->user_may_modify =
523 management_policy->UserMayModifySettings(&extension, nullptr);
525 info->version = extension.GetVersionForDisplay();
527 if (state != developer::EXTENSION_STATE_TERMINATED) {
528 info->views = InspectableViewsFinder(profile).
529 GetViewsForExtension(extension, is_enabled);
532 // The icon.
533 ExtensionResource icon =
534 IconsInfo::GetIconResource(&extension,
535 extension_misc::EXTENSION_ICON_MEDIUM,
536 ExtensionIconSet::MATCH_BIGGER);
537 if (icon.empty()) {
538 info->icon_url = GetDefaultIconUrl(extension.is_app(), !is_enabled);
539 list_.push_back(make_linked_ptr(info.release()));
540 } else {
541 ++pending_image_loads_;
542 // Max size of 128x128 is a random guess at a nice balance between being
543 // overly eager to resize and sending across gigantic data urls. (The icon
544 // used by the url is 48x48).
545 gfx::Size max_size(128, 128);
546 image_loader_->LoadImageAsync(
547 &extension,
548 icon,
549 max_size,
550 base::Bind(&ExtensionInfoGenerator::OnImageLoaded,
551 weak_factory_.GetWeakPtr(),
552 base::Passed(info.Pass())));
556 const std::string& ExtensionInfoGenerator::GetDefaultIconUrl(
557 bool is_app,
558 bool is_greyscale) {
559 std::string* str;
560 if (is_app) {
561 str = is_greyscale ? &default_disabled_app_icon_url_ :
562 &default_app_icon_url_;
563 } else {
564 str = is_greyscale ? &default_disabled_extension_icon_url_ :
565 &default_extension_icon_url_;
568 if (str->empty()) {
569 *str = GetIconUrlFromImage(
570 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
571 is_app ? IDR_APP_DEFAULT_ICON : IDR_EXTENSION_DEFAULT_ICON),
572 is_greyscale);
575 return *str;
578 std::string ExtensionInfoGenerator::GetIconUrlFromImage(
579 const gfx::Image& image,
580 bool should_greyscale) {
581 scoped_refptr<base::RefCountedMemory> data;
582 if (should_greyscale) {
583 color_utils::HSL shift = {-1, 0, 0.6};
584 const SkBitmap* bitmap = image.ToSkBitmap();
585 DCHECK(bitmap);
586 SkBitmap grey = SkBitmapOperations::CreateHSLShiftedBitmap(*bitmap, shift);
587 scoped_refptr<base::RefCountedBytes> image_bytes(
588 new base::RefCountedBytes());
589 gfx::PNGCodec::EncodeBGRASkBitmap(grey, false, &image_bytes->data());
590 data = image_bytes;
591 } else {
592 data = image.As1xPNGBytes();
595 std::string base_64;
596 base::Base64Encode(std::string(data->front_as<char>(), data->size()),
597 &base_64);
598 const char kDataUrlPrefix[] = "data:image/png;base64,";
599 return GURL(kDataUrlPrefix + base_64).spec();
602 void ExtensionInfoGenerator::OnImageLoaded(
603 scoped_ptr<developer::ExtensionInfo> info,
604 const gfx::Image& icon) {
605 if (!icon.IsEmpty()) {
606 info->icon_url = GetIconUrlFromImage(
607 icon, info->state != developer::EXTENSION_STATE_ENABLED);
608 } else {
609 bool is_app =
610 info->type == developer::EXTENSION_TYPE_HOSTED_APP ||
611 info->type == developer::EXTENSION_TYPE_LEGACY_PACKAGED_APP ||
612 info->type == developer::EXTENSION_TYPE_PLATFORM_APP;
613 info->icon_url = GetDefaultIconUrl(
614 is_app, info->state != developer::EXTENSION_STATE_ENABLED);
617 list_.push_back(make_linked_ptr(info.release()));
619 --pending_image_loads_;
621 if (pending_image_loads_ == 0) { // All done!
622 // We assign to a temporary callback and list and reset the stored values so
623 // that at the end of the method, any stored refs are destroyed.
624 ExtensionInfoList list;
625 list.swap(list_);
626 ExtensionInfosCallback callback = callback_;
627 callback_.Reset();
628 callback.Run(list); // WARNING: |this| is possibly deleted after this line!
632 } // namespace extensions