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