OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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. | |
4 | |
5 #include "chrome/browser/extensions/extension_management_api.h" | |
6 | |
7 #include <map> | |
8 #include <string> | |
9 | |
10 #include "base/basictypes.h" | |
11 #include "base/bind.h" | |
12 #include "base/json/json_writer.h" | |
13 #include "base/metrics/histogram.h" | |
14 #include "base/string_number_conversions.h" | |
15 #include "base/string_util.h" | |
16 #include "chrome/browser/extensions/event_names.h" | |
17 #include "chrome/browser/extensions/event_router.h" | |
18 #include "chrome/browser/extensions/extension_management_api_constants.h" | |
19 #include "chrome/browser/extensions/extension_service.h" | |
20 #include "chrome/browser/extensions/extension_system.h" | |
21 #include "chrome/browser/extensions/extension_uninstall_dialog.h" | |
22 #include "chrome/browser/extensions/management_policy.h" | |
23 #include "chrome/browser/profiles/profile.h" | |
24 #include "chrome/browser/ui/extensions/application_launch.h" | |
25 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h" | |
26 #include "chrome/common/chrome_notification_types.h" | |
27 #include "chrome/common/chrome_utility_messages.h" | |
28 #include "chrome/common/extensions/extension.h" | |
29 #include "chrome/common/extensions/extension_constants.h" | |
30 #include "chrome/common/extensions/extension_error_utils.h" | |
31 #include "chrome/common/extensions/extension_icon_set.h" | |
32 #include "chrome/common/extensions/permissions/permission_set.h" | |
33 #include "chrome/common/extensions/url_pattern.h" | |
34 #include "content/public/browser/notification_details.h" | |
35 #include "content/public/browser/notification_source.h" | |
36 #include "content/public/browser/utility_process_host.h" | |
37 #include "content/public/browser/utility_process_host_client.h" | |
38 | |
39 #if !defined(OS_ANDROID) | |
40 #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h" | |
41 #endif | |
42 | |
43 using base::IntToString; | |
44 using content::BrowserThread; | |
45 using content::UtilityProcessHost; | |
46 using content::UtilityProcessHostClient; | |
47 using extensions::Extension; | |
48 using extensions::PermissionMessages; | |
49 | |
50 namespace events = extensions::event_names; | |
51 namespace keys = extension_management_api_constants; | |
52 | |
53 namespace { | |
54 | |
55 enum AutoConfirmForTest { | |
56 DO_NOT_SKIP = 0, | |
57 PROCEED, | |
58 ABORT | |
59 }; | |
60 | |
61 AutoConfirmForTest auto_confirm_for_test = DO_NOT_SKIP; | |
62 | |
63 } // namespace | |
64 | |
65 ExtensionService* ExtensionManagementFunction::service() { | |
66 return profile()->GetExtensionService(); | |
67 } | |
68 | |
69 ExtensionService* AsyncExtensionManagementFunction::service() { | |
70 return profile()->GetExtensionService(); | |
71 } | |
72 | |
73 static DictionaryValue* CreateExtensionInfo(const Extension& extension, | |
74 ExtensionService* service) { | |
75 DictionaryValue* info = new DictionaryValue(); | |
76 bool enabled = service->IsExtensionEnabled(extension.id()); | |
77 extension.GetBasicInfo(enabled, info); | |
78 | |
79 const extensions::ManagementPolicy* policy = extensions::ExtensionSystem::Get( | |
80 service->profile())->management_policy(); | |
81 info->SetBoolean(keys::kMayDisableKey, | |
82 policy->UserMayModifySettings(&extension, NULL)); | |
83 | |
84 info->SetBoolean(keys::kIsAppKey, extension.is_app()); | |
85 | |
86 if (!enabled) { | |
87 extensions::ExtensionPrefs* prefs = service->extension_prefs(); | |
88 bool permissions_escalated = | |
89 prefs->DidExtensionEscalatePermissions(extension.id()); | |
90 const char* reason = permissions_escalated ? | |
91 keys::kDisabledReasonPermissionsIncrease : keys::kDisabledReasonUnknown; | |
92 info->SetString(keys::kDisabledReasonKey, reason); | |
93 } | |
94 | |
95 if (!extension.update_url().is_empty()) | |
96 info->SetString(keys::kUpdateUrlKey, | |
97 extension.update_url().possibly_invalid_spec()); | |
98 if (extension.is_app()) | |
99 info->SetString(keys::kAppLaunchUrlKey, | |
100 extension.GetFullLaunchURL().possibly_invalid_spec()); | |
101 | |
102 const ExtensionIconSet::IconMap& icons = extension.icons().map(); | |
103 if (!icons.empty()) { | |
104 ListValue* icon_list = new ListValue(); | |
105 std::map<ExtensionIconSet::Icons, std::string>::const_iterator icon_iter; | |
106 for (icon_iter = icons.begin(); icon_iter != icons.end(); ++icon_iter) { | |
107 DictionaryValue* icon_info = new DictionaryValue(); | |
108 ExtensionIconSet::Icons size = icon_iter->first; | |
109 GURL url = ExtensionIconSource::GetIconURL( | |
110 &extension, size, ExtensionIconSet::MATCH_EXACTLY, false, NULL); | |
111 icon_info->SetInteger(keys::kSizeKey, icon_iter->first); | |
112 icon_info->SetString(keys::kUrlKey, url.spec()); | |
113 icon_list->Append(icon_info); | |
114 } | |
115 info->Set(keys::kIconsKey, icon_list); | |
116 } | |
117 | |
118 const std::set<std::string> perms = | |
119 extension.GetActivePermissions()->GetAPIsAsStrings(); | |
120 ListValue* permission_list = new ListValue(); | |
121 if (!perms.empty()) { | |
122 std::set<std::string>::const_iterator perms_iter; | |
123 for (perms_iter = perms.begin(); perms_iter != perms.end(); ++perms_iter) { | |
124 StringValue* permission_name = new StringValue(*perms_iter); | |
125 permission_list->Append(permission_name); | |
126 } | |
127 } | |
128 info->Set(keys::kPermissionsKey, permission_list); | |
129 | |
130 ListValue* host_permission_list = new ListValue(); | |
131 if (!extension.is_hosted_app()) { | |
132 // Skip host permissions for hosted apps. | |
133 const URLPatternSet host_perms = | |
134 extension.GetActivePermissions()->explicit_hosts(); | |
135 if (!host_perms.is_empty()) { | |
136 URLPatternSet::const_iterator host_perms_iter; | |
137 for (host_perms_iter = host_perms.begin(); | |
138 host_perms_iter != host_perms.end(); | |
139 ++host_perms_iter) { | |
140 StringValue* name = new StringValue(host_perms_iter->GetAsString()); | |
141 host_permission_list->Append(name); | |
142 } | |
143 } | |
144 } | |
145 info->Set(keys::kHostPermissionsKey, host_permission_list); | |
146 | |
147 return info; | |
148 } | |
149 | |
150 static void AddExtensionInfo(ListValue* list, | |
151 const ExtensionSet& extensions, | |
152 ExtensionService* service) { | |
153 for (ExtensionSet::const_iterator i = extensions.begin(); | |
154 i != extensions.end(); ++i) { | |
155 const Extension& extension = **i; | |
156 | |
157 if (extension.location() == Extension::COMPONENT) | |
158 continue; // Skip built-in extensions. | |
159 | |
160 list->Append(CreateExtensionInfo(extension, service)); | |
161 } | |
162 } | |
163 | |
164 bool GetAllExtensionsFunction::RunImpl() { | |
165 ListValue* result = new ListValue(); | |
166 SetResult(result); | |
167 | |
168 AddExtensionInfo(result, *service()->extensions(), service()); | |
169 AddExtensionInfo(result, *service()->disabled_extensions(), service()); | |
170 | |
171 return true; | |
172 } | |
173 | |
174 bool GetExtensionByIdFunction::RunImpl() { | |
175 std::string extension_id; | |
176 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id)); | |
177 const Extension* extension = service()->GetExtensionById(extension_id, true); | |
178 if (!extension) { | |
179 error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoExtensionError, | |
180 extension_id); | |
181 return false; | |
182 } | |
183 DictionaryValue* result = CreateExtensionInfo(*extension, service()); | |
184 SetResult(result); | |
185 | |
186 return true; | |
187 } | |
188 | |
189 bool GetPermissionWarningsByIdFunction::RunImpl() { | |
190 std::string ext_id; | |
191 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &ext_id)); | |
192 | |
193 const Extension* extension = service()->GetExtensionById(ext_id, true); | |
194 if (!extension) { | |
195 error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoExtensionError, | |
196 ext_id); | |
197 return false; | |
198 } | |
199 | |
200 PermissionMessages warnings = extension->GetPermissionMessages(); | |
201 ListValue* result = new ListValue(); | |
202 for (PermissionMessages::const_iterator i = warnings.begin(); | |
203 i < warnings.end(); ++i) | |
204 result->Append(Value::CreateStringValue(i->message())); | |
205 SetResult(result); | |
206 return true; | |
207 } | |
208 | |
209 namespace { | |
210 | |
211 // This class helps GetPermissionWarningsByManifestFunction manage | |
212 // sending manifest JSON strings to the utility process for parsing. | |
213 class SafeManifestJSONParser : public UtilityProcessHostClient { | |
214 public: | |
215 SafeManifestJSONParser(GetPermissionWarningsByManifestFunction* client, | |
216 const std::string& manifest) | |
217 : client_(client), | |
218 manifest_(manifest) {} | |
219 | |
220 void Start() { | |
221 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
222 BrowserThread::PostTask( | |
223 BrowserThread::IO, | |
224 FROM_HERE, | |
225 base::Bind(&SafeManifestJSONParser::StartWorkOnIOThread, this)); | |
226 } | |
227 | |
228 void StartWorkOnIOThread() { | |
229 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
230 UtilityProcessHost* host = | |
231 UtilityProcessHost::Create(this, BrowserThread::IO); | |
232 host->EnableZygote(); | |
233 host->Send(new ChromeUtilityMsg_ParseJSON(manifest_)); | |
234 } | |
235 | |
236 virtual bool OnMessageReceived(const IPC::Message& message) { | |
237 bool handled = true; | |
238 IPC_BEGIN_MESSAGE_MAP(SafeManifestJSONParser, message) | |
239 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Succeeded, | |
240 OnJSONParseSucceeded) | |
241 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Failed, | |
242 OnJSONParseFailed) | |
243 IPC_MESSAGE_UNHANDLED(handled = false) | |
244 IPC_END_MESSAGE_MAP() | |
245 return handled; | |
246 } | |
247 | |
248 void OnJSONParseSucceeded(const ListValue& wrapper) { | |
249 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
250 Value* value = NULL; | |
251 CHECK(wrapper.Get(0, &value)); | |
252 if (value->IsType(Value::TYPE_DICTIONARY)) | |
253 parsed_manifest_.reset(static_cast<DictionaryValue*>(value)->DeepCopy()); | |
254 else | |
255 error_ = keys::kManifestParseError; | |
256 | |
257 BrowserThread::PostTask( | |
258 BrowserThread::UI, | |
259 FROM_HERE, | |
260 base::Bind(&SafeManifestJSONParser::ReportResultFromUIThread, this)); | |
261 } | |
262 | |
263 void OnJSONParseFailed(const std::string& error) { | |
264 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
265 error_ = error; | |
266 BrowserThread::PostTask( | |
267 BrowserThread::UI, | |
268 FROM_HERE, | |
269 base::Bind(&SafeManifestJSONParser::ReportResultFromUIThread, this)); | |
270 } | |
271 | |
272 void ReportResultFromUIThread() { | |
273 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
274 if (error_.empty() && parsed_manifest_.get()) | |
275 client_->OnParseSuccess(parsed_manifest_.release()); | |
276 else | |
277 client_->OnParseFailure(error_); | |
278 } | |
279 | |
280 private: | |
281 ~SafeManifestJSONParser() {} | |
282 | |
283 // The client who we'll report results back to. | |
284 GetPermissionWarningsByManifestFunction* client_; | |
285 | |
286 // Data to parse. | |
287 std::string manifest_; | |
288 | |
289 // Results of parsing. | |
290 scoped_ptr<DictionaryValue> parsed_manifest_; | |
291 | |
292 std::string error_; | |
293 }; | |
294 | |
295 } // namespace | |
296 | |
297 bool GetPermissionWarningsByManifestFunction::RunImpl() { | |
298 std::string manifest_str; | |
299 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &manifest_str)); | |
300 | |
301 scoped_refptr<SafeManifestJSONParser> parser = | |
302 new SafeManifestJSONParser(this, manifest_str); | |
303 parser->Start(); | |
304 | |
305 // Matched with a Release() in OnParseSuccess/Failure(). | |
306 AddRef(); | |
307 | |
308 // Response is sent async in OnParseSuccess/Failure(). | |
309 return true; | |
310 } | |
311 | |
312 void GetPermissionWarningsByManifestFunction::OnParseSuccess( | |
313 DictionaryValue* parsed_manifest) { | |
314 CHECK(parsed_manifest); | |
315 | |
316 scoped_refptr<Extension> extension = Extension::Create( | |
317 FilePath(), Extension::INVALID, *parsed_manifest, Extension::NO_FLAGS, | |
318 &error_); | |
319 if (!extension.get()) { | |
320 OnParseFailure(keys::kExtensionCreateError); | |
321 return; | |
322 } | |
323 | |
324 PermissionMessages warnings = extension->GetPermissionMessages(); | |
325 ListValue* result = new ListValue(); | |
326 for (PermissionMessages::const_iterator i = warnings.begin(); | |
327 i < warnings.end(); ++i) | |
328 result->Append(Value::CreateStringValue(i->message())); | |
329 SetResult(result); | |
330 SendResponse(true); | |
331 | |
332 // Matched with AddRef() in RunImpl(). | |
333 Release(); | |
334 } | |
335 | |
336 void GetPermissionWarningsByManifestFunction::OnParseFailure( | |
337 const std::string& error) { | |
338 error_ = error; | |
339 SendResponse(false); | |
340 | |
341 // Matched with AddRef() in RunImpl(). | |
342 Release(); | |
343 } | |
344 | |
345 bool LaunchAppFunction::RunImpl() { | |
346 std::string extension_id; | |
347 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id)); | |
348 const Extension* extension = service()->GetExtensionById(extension_id, true); | |
349 if (!extension) { | |
350 error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoExtensionError, | |
351 extension_id); | |
352 return false; | |
353 } | |
354 if (!extension->is_app()) { | |
355 error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNotAnAppError, | |
356 extension_id); | |
357 return false; | |
358 } | |
359 | |
360 // Look at prefs to find the right launch container. | |
361 // |default_pref_value| is set to LAUNCH_REGULAR so that if | |
362 // the user has not set a preference, we open the app in a tab. | |
363 extension_misc::LaunchContainer launch_container = | |
364 service()->extension_prefs()->GetLaunchContainer( | |
365 extension, extensions::ExtensionPrefs::LAUNCH_DEFAULT); | |
366 application_launch::OpenApplication(application_launch::LaunchParams( | |
367 profile(), extension, launch_container, NEW_FOREGROUND_TAB)); | |
368 #if !defined(OS_ANDROID) | |
369 AppLauncherHandler::RecordAppLaunchType( | |
370 extension_misc::APP_LAUNCH_EXTENSION_API); | |
371 #endif | |
372 | |
373 return true; | |
374 } | |
375 | |
376 SetEnabledFunction::SetEnabledFunction() { | |
377 } | |
378 | |
379 SetEnabledFunction::~SetEnabledFunction() { | |
380 } | |
381 | |
382 bool SetEnabledFunction::RunImpl() { | |
383 bool enable; | |
384 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id_)); | |
385 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &enable)); | |
386 | |
387 const Extension* extension = service()->GetExtensionById(extension_id_, true); | |
388 if (!extension) { | |
389 error_ = ExtensionErrorUtils::FormatErrorMessage( | |
390 keys::kNoExtensionError, extension_id_); | |
391 return false; | |
392 } | |
393 | |
394 const extensions::ManagementPolicy* policy = extensions::ExtensionSystem::Get( | |
395 profile())->management_policy(); | |
396 if (!policy->UserMayModifySettings(extension, NULL)) { | |
397 error_ = ExtensionErrorUtils::FormatErrorMessage( | |
398 keys::kUserCantModifyError, extension_id_); | |
399 return false; | |
400 } | |
401 | |
402 bool currently_enabled = service()->IsExtensionEnabled(extension_id_); | |
403 | |
404 if (!currently_enabled && enable) { | |
405 extensions::ExtensionPrefs* prefs = service()->extension_prefs(); | |
406 if (prefs->DidExtensionEscalatePermissions(extension_id_)) { | |
407 if (!user_gesture()) { | |
408 error_ = keys::kGestureNeededForEscalationError; | |
409 return false; | |
410 } | |
411 AddRef(); // Matched in InstallUIProceed/InstallUIAbort | |
412 install_prompt_.reset( | |
413 chrome::CreateExtensionInstallPromptWithBrowser(GetCurrentBrowser())); | |
414 install_prompt_->ConfirmReEnable(this, extension); | |
415 return true; | |
416 } | |
417 service()->EnableExtension(extension_id_); | |
418 } else if (currently_enabled && !enable) { | |
419 service()->DisableExtension(extension_id_, Extension::DISABLE_USER_ACTION); | |
420 } | |
421 | |
422 BrowserThread::PostTask( | |
423 BrowserThread::UI, | |
424 FROM_HERE, | |
425 base::Bind(&SetEnabledFunction::SendResponse, this, true)); | |
426 | |
427 return true; | |
428 } | |
429 | |
430 void SetEnabledFunction::InstallUIProceed() { | |
431 service()->EnableExtension(extension_id_); | |
432 SendResponse(true); | |
433 Release(); | |
434 } | |
435 | |
436 void SetEnabledFunction::InstallUIAbort(bool user_initiated) { | |
437 error_ = keys::kUserDidNotReEnableError; | |
438 SendResponse(false); | |
439 Release(); | |
440 } | |
441 | |
442 UninstallFunction::UninstallFunction() { | |
443 } | |
444 | |
445 UninstallFunction::~UninstallFunction() { | |
446 } | |
447 | |
448 bool UninstallFunction::RunImpl() { | |
449 bool show_confirm_dialog = false; | |
450 | |
451 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id_)); | |
452 | |
453 if (HasOptionalArgument(1)) { | |
454 DictionaryValue* options = NULL; | |
455 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options)); | |
456 | |
457 if (options->HasKey(keys::kShowConfirmDialogKey)) { | |
458 EXTENSION_FUNCTION_VALIDATE(options->GetBoolean( | |
459 keys::kShowConfirmDialogKey, &show_confirm_dialog)); | |
460 } | |
461 } | |
462 | |
463 const Extension* extension = service()->GetExtensionById(extension_id_, true); | |
464 if (!extension) { | |
465 error_ = ExtensionErrorUtils::FormatErrorMessage( | |
466 keys::kNoExtensionError, extension_id_); | |
467 return false; | |
468 } | |
469 | |
470 if (!extensions::ExtensionSystem::Get( | |
471 profile())->management_policy()->UserMayModifySettings(extension, NULL)) { | |
472 error_ = ExtensionErrorUtils::FormatErrorMessage( | |
473 keys::kUserCantModifyError, extension_id_); | |
474 return false; | |
475 } | |
476 | |
477 if (auto_confirm_for_test == DO_NOT_SKIP) { | |
478 if (show_confirm_dialog) { | |
479 AddRef(); // Balanced in ExtensionUninstallAccepted/Canceled | |
480 extension_uninstall_dialog_.reset(ExtensionUninstallDialog::Create( | |
481 GetCurrentBrowser(), this)); | |
482 extension_uninstall_dialog_->ConfirmUninstall(extension); | |
483 } else { | |
484 Finish(true); | |
485 } | |
486 } else { | |
487 Finish(auto_confirm_for_test == PROCEED); | |
488 } | |
489 | |
490 return true; | |
491 } | |
492 | |
493 // static | |
494 void UninstallFunction::SetAutoConfirmForTest(bool should_proceed) { | |
495 auto_confirm_for_test = should_proceed ? PROCEED : ABORT; | |
496 } | |
497 | |
498 void UninstallFunction::Finish(bool should_uninstall) { | |
499 if (should_uninstall) { | |
500 bool success = service()->UninstallExtension( | |
501 extension_id_, | |
502 false, /* external uninstall */ | |
503 NULL); | |
504 | |
505 // TODO set error_ if !success | |
506 SendResponse(success); | |
507 } else { | |
508 error_ = ExtensionErrorUtils::FormatErrorMessage( | |
509 keys::kUninstallCanceledError, extension_id_); | |
510 SendResponse(false); | |
511 } | |
512 | |
513 } | |
514 | |
515 void UninstallFunction::ExtensionUninstallAccepted() { | |
516 Finish(true); | |
517 Release(); | |
518 } | |
519 | |
520 void UninstallFunction::ExtensionUninstallCanceled() { | |
521 Finish(false); | |
522 Release(); | |
523 } | |
524 | |
525 ExtensionManagementEventRouter::ExtensionManagementEventRouter(Profile* profile) | |
526 : profile_(profile) {} | |
527 | |
528 ExtensionManagementEventRouter::~ExtensionManagementEventRouter() {} | |
529 | |
530 void ExtensionManagementEventRouter::Init() { | |
531 int types[] = { | |
532 chrome::NOTIFICATION_EXTENSION_INSTALLED, | |
533 chrome::NOTIFICATION_EXTENSION_UNINSTALLED, | |
534 chrome::NOTIFICATION_EXTENSION_LOADED, | |
535 chrome::NOTIFICATION_EXTENSION_UNLOADED | |
536 }; | |
537 | |
538 CHECK(registrar_.IsEmpty()); | |
539 for (size_t i = 0; i < arraysize(types); i++) { | |
540 registrar_.Add(this, | |
541 types[i], | |
542 content::Source<Profile>(profile_)); | |
543 } | |
544 } | |
545 | |
546 void ExtensionManagementEventRouter::Observe( | |
547 int type, | |
548 const content::NotificationSource& source, | |
549 const content::NotificationDetails& details) { | |
550 const char* event_name = NULL; | |
551 Profile* profile = content::Source<Profile>(source).ptr(); | |
552 CHECK(profile); | |
553 CHECK(profile_->IsSameProfile(profile)); | |
554 | |
555 switch (type) { | |
556 case chrome::NOTIFICATION_EXTENSION_INSTALLED: | |
557 event_name = events::kOnExtensionInstalled; | |
558 break; | |
559 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: | |
560 event_name = events::kOnExtensionUninstalled; | |
561 break; | |
562 case chrome::NOTIFICATION_EXTENSION_LOADED: | |
563 event_name = events::kOnExtensionEnabled; | |
564 break; | |
565 case chrome::NOTIFICATION_EXTENSION_UNLOADED: | |
566 event_name = events::kOnExtensionDisabled; | |
567 break; | |
568 default: | |
569 NOTREACHED(); | |
570 return; | |
571 } | |
572 | |
573 ListValue args; | |
574 if (event_name == events::kOnExtensionUninstalled) { | |
575 args.Append(Value::CreateStringValue( | |
576 content::Details<const extensions::Extension>(details).ptr()->id())); | |
577 } else { | |
578 const Extension* extension = NULL; | |
579 if (event_name == events::kOnExtensionDisabled) { | |
580 extension = content::Details<extensions::UnloadedExtensionInfo>( | |
581 details)->extension; | |
582 } else { | |
583 extension = content::Details<const Extension>(details).ptr(); | |
584 } | |
585 CHECK(extension); | |
586 ExtensionService* service = profile->GetExtensionService(); | |
587 args.Append(CreateExtensionInfo(*extension, service)); | |
588 } | |
589 | |
590 std::string args_json; | |
591 base::JSONWriter::Write(&args, &args_json); | |
592 | |
593 profile->GetExtensionEventRouter()->DispatchEventToRenderers( | |
594 event_name, args_json, NULL, GURL(), extensions::EventFilteringInfo()); | |
595 } | |
OLD | NEW |