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
;
59 // Given a Manifest::Type, converts it into its developer_private
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
;
67 case Manifest::TYPE_THEME
:
68 type
= developer::EXTENSION_TYPE_THEME
;
70 case Manifest::TYPE_HOSTED_APP
:
71 type
= developer::EXTENSION_TYPE_HOSTED_APP
;
73 case Manifest::TYPE_LEGACY_PACKAGED_APP
:
74 type
= developer::EXTENSION_TYPE_LEGACY_PACKAGED_APP
;
76 case Manifest::TYPE_PLATFORM_APP
:
77 type
= developer::EXTENSION_TYPE_PLATFORM_APP
;
79 case Manifest::TYPE_SHARED_MODULE
:
80 type
= developer::EXTENSION_TYPE_SHARED_MODULE
;
88 // Populates the common fields of an extension error.
89 template <typename ErrorType
>
90 void PopulateErrorBase(const ExtensionError
& error
, ErrorType
* 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());
101 // Given a ManifestError object, converts it into its developer_private
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())));
115 // Given a RuntimeError object, converts it into its developer_private
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
;
126 case logging::LOG_WARNING
:
127 result
->severity
= developer::ERROR_LEVEL_WARN
;
129 case logging::LOG_FATAL
:
130 case logging::LOG_ERROR
:
131 result
->severity
= developer::ERROR_LEVEL_ERROR
;
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
);
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
,
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
;
177 Command browser_action
;
178 if (command_service
->GetBrowserActionCommand(extension_id
,
183 make_linked_ptr(construct_command(browser_action
, active
, true)));
187 if (command_service
->GetPageActionCommand(extension_id
,
192 make_linked_ptr(construct_command(page_action
, active
, true)));
195 CommandMap named_commands
;
196 if (command_service
->GetNamedCommands(extension_id
,
198 CommandService::ANY_SCOPE
,
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
;
215 make_linked_ptr(construct_command(command_to_use
, active
, false)));
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_
));
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(),
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_
));
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();
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
;
321 case BLACKLISTED_CWS_POLICY_VIOLATION
:
322 blacklist_text
= IDS_OPTIONS_BLACKLISTED_CWS_POLICY_VIOLATION
;
324 case BLACKLISTED_POTENTIALLY_UNWANTED
:
325 blacklist_text
= IDS_OPTIONS_BLACKLISTED_POTENTIALLY_UNWANTED
;
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_
);
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
);
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
;
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();
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;
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());
393 info
->file_access
.is_enabled
= extension
.wants_file_access();
394 info
->file_access
.is_active
=
395 util::AllowFileAccess(extension
.id(), browser_context_
);
398 info
->home_page
.url
= ManifestURL::GetHomepageURL(&extension
).spec();
399 info
->home_page
.specified
= ManifestURL::SpecifiedHomepageURL(&extension
);
401 info
->id
= extension
.id();
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
);
421 if (extension
.is_app()) {
422 info
->launch_url
.reset(
423 new std::string(AppLaunchInfo::GetFullLaunchURL(&extension
).spec()));
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
;
436 info
->location
= developer::LOCATION_UNKNOWN
;
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
)));
462 case ExtensionError::RUNTIME_ERROR
:
463 info
->runtime_errors
.push_back(ConstructRuntimeError(
464 static_cast<const RuntimeError
&>(*error
)));
466 case ExtensionError::INTERNAL_ERROR
:
467 // TODO(wittman): Support InternalError in developer tools:
468 // https://crbug.com/503427.
470 case ExtensionError::NUM_ERROR_TYPES
:
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
);
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();
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()));
501 info
->run_on_all_urls
.is_enabled
=
502 (FeatureSwitch::scripts_require_action()->IsEnabled() &&
503 PermissionsData::ScriptsMayRequireActionForExtension(
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_
);
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
);
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
);
534 ExtensionResource icon
=
535 IconsInfo::GetIconResource(&extension
,
536 extension_misc::EXTENSION_ICON_MEDIUM
,
537 ExtensionIconSet::MATCH_BIGGER
);
539 info
->icon_url
= GetDefaultIconUrl(extension
.is_app(), !is_enabled
);
540 list_
.push_back(make_linked_ptr(info
.release()));
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(
551 base::Bind(&ExtensionInfoGenerator::OnImageLoaded
,
552 weak_factory_
.GetWeakPtr(),
553 base::Passed(info
.Pass())));
557 const std::string
& ExtensionInfoGenerator::GetDefaultIconUrl(
562 str
= is_greyscale
? &default_disabled_app_icon_url_
:
563 &default_app_icon_url_
;
565 str
= is_greyscale
? &default_disabled_extension_icon_url_
:
566 &default_extension_icon_url_
;
570 *str
= GetIconUrlFromImage(
571 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
572 is_app
? IDR_APP_DEFAULT_ICON
: IDR_EXTENSION_DEFAULT_ICON
),
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();
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());
593 data
= image
.As1xPNGBytes();
597 base::Base64Encode(std::string(data
->front_as
<char>(), data
->size()),
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
);
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
;
627 ExtensionInfosCallback callback
= callback_
;
629 callback
.Run(list
); // WARNING: |this| is possibly deleted after this line!
633 } // namespace extensions