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
;
58 // Given a Manifest::Type, converts it into its developer_private
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
;
66 case Manifest::TYPE_THEME
:
67 type
= developer::EXTENSION_TYPE_THEME
;
69 case Manifest::TYPE_HOSTED_APP
:
70 type
= developer::EXTENSION_TYPE_HOSTED_APP
;
72 case Manifest::TYPE_LEGACY_PACKAGED_APP
:
73 type
= developer::EXTENSION_TYPE_LEGACY_PACKAGED_APP
;
75 case Manifest::TYPE_PLATFORM_APP
:
76 type
= developer::EXTENSION_TYPE_PLATFORM_APP
;
78 case Manifest::TYPE_SHARED_MODULE
:
79 type
= developer::EXTENSION_TYPE_SHARED_MODULE
;
87 // Populates the common fields of an extension error.
88 template <typename ErrorType
>
89 void PopulateErrorBase(const ExtensionError
& error
, ErrorType
* 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());
100 // Given a ManifestError object, converts it into its developer_private
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())));
114 // Given a RuntimeError object, converts it into its developer_private
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
;
125 case logging::LOG_WARNING
:
126 result
->severity
= developer::ERROR_LEVEL_WARN
;
128 case logging::LOG_FATAL
:
129 case logging::LOG_ERROR
:
130 result
->severity
= developer::ERROR_LEVEL_ERROR
;
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
);
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
,
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
;
176 Command browser_action
;
177 if (command_service
->GetBrowserActionCommand(extension_id
,
182 make_linked_ptr(construct_command(browser_action
, active
, true)));
186 if (command_service
->GetPageActionCommand(extension_id
,
191 make_linked_ptr(construct_command(page_action
, active
, true)));
194 CommandMap named_commands
;
195 if (command_service
->GetNamedCommands(extension_id
,
197 CommandService::ANY_SCOPE
,
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
;
214 make_linked_ptr(construct_command(command_to_use
, active
, false)));
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_
));
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(),
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_
));
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();
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
;
320 case BLACKLISTED_CWS_POLICY_VIOLATION
:
321 blacklist_text
= IDS_OPTIONS_BLACKLISTED_CWS_POLICY_VIOLATION
;
323 case BLACKLISTED_POTENTIALLY_UNWANTED
:
324 blacklist_text
= IDS_OPTIONS_BLACKLISTED_POTENTIALLY_UNWANTED
;
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_
);
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
);
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
;
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();
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;
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());
392 info
->file_access
.is_enabled
= extension
.wants_file_access();
393 info
->file_access
.is_active
=
394 util::AllowFileAccess(extension
.id(), browser_context_
);
397 info
->home_page
.url
= ManifestURL::GetHomepageURL(&extension
).spec();
398 info
->home_page
.specified
= ManifestURL::SpecifiedHomepageURL(&extension
);
400 info
->id
= extension
.id();
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
);
420 if (extension
.is_app()) {
421 info
->launch_url
.reset(
422 new std::string(AppLaunchInfo::GetFullLaunchURL(&extension
).spec()));
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
;
435 info
->location
= developer::LOCATION_UNKNOWN
;
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
)));
461 case ExtensionError::RUNTIME_ERROR
:
462 info
->runtime_errors
.push_back(ConstructRuntimeError(
463 static_cast<const RuntimeError
&>(*error
)));
465 case ExtensionError::INTERNAL_ERROR
:
466 // TODO(wittman): Support InternalError in developer tools:
467 // https://crbug.com/503427.
469 case ExtensionError::NUM_ERROR_TYPES
:
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
);
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();
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()));
500 info
->run_on_all_urls
.is_enabled
=
501 (FeatureSwitch::scripts_require_action()->IsEnabled() &&
502 PermissionsData::ScriptsMayRequireActionForExtension(
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_
);
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
);
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
);
533 ExtensionResource icon
=
534 IconsInfo::GetIconResource(&extension
,
535 extension_misc::EXTENSION_ICON_MEDIUM
,
536 ExtensionIconSet::MATCH_BIGGER
);
538 info
->icon_url
= GetDefaultIconUrl(extension
.is_app(), !is_enabled
);
539 list_
.push_back(make_linked_ptr(info
.release()));
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(
550 base::Bind(&ExtensionInfoGenerator::OnImageLoaded
,
551 weak_factory_
.GetWeakPtr(),
552 base::Passed(info
.Pass())));
556 const std::string
& ExtensionInfoGenerator::GetDefaultIconUrl(
561 str
= is_greyscale
? &default_disabled_app_icon_url_
:
562 &default_app_icon_url_
;
564 str
= is_greyscale
? &default_disabled_extension_icon_url_
:
565 &default_extension_icon_url_
;
569 *str
= GetIconUrlFromImage(
570 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
571 is_app
? IDR_APP_DEFAULT_ICON
: IDR_EXTENSION_DEFAULT_ICON
),
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();
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());
592 data
= image
.As1xPNGBytes();
596 base::Base64Encode(std::string(data
->front_as
<char>(), data
->size()),
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
);
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
;
626 ExtensionInfosCallback callback
= callback_
;
628 callback
.Run(list
); // WARNING: |this| is possibly deleted after this line!
632 } // namespace extensions