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