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.
5 #include "chrome/browser/extensions/api/management/management_api.h"
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/json/json_writer.h"
14 #include "base/lazy_instance.h"
15 #include "base/logging.h"
16 #include "base/memory/linked_ptr.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/metrics/histogram.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/extensions/api/management/management_api_constants.h"
24 #include "chrome/browser/extensions/event_router.h"
25 #include "chrome/browser/extensions/extension_service.h"
26 #include "chrome/browser/extensions/extension_system.h"
27 #include "chrome/browser/extensions/extension_uninstall_dialog.h"
28 #include "chrome/browser/extensions/management_policy.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/ui/extensions/application_launch.h"
31 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
32 #include "chrome/common/chrome_utility_messages.h"
33 #include "chrome/common/extensions/api/management.h"
34 #include "chrome/common/extensions/extension.h"
35 #include "chrome/common/extensions/extension_constants.h"
36 #include "chrome/common/extensions/extension_icon_set.h"
37 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
38 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
39 #include "chrome/common/extensions/manifest_handlers/offline_enabled_info.h"
40 #include "chrome/common/extensions/manifest_url_handler.h"
41 #include "chrome/common/extensions/permissions/permissions_data.h"
42 #include "content/public/browser/notification_details.h"
43 #include "content/public/browser/notification_source.h"
44 #include "content/public/browser/utility_process_host.h"
45 #include "content/public/browser/utility_process_host_client.h"
46 #include "extensions/common/error_utils.h"
47 #include "extensions/common/permissions/permission_set.h"
48 #include "extensions/common/url_pattern.h"
50 #if !defined(OS_ANDROID)
51 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
54 using base::IntToString;
55 using content::BrowserThread;
56 using content::UtilityProcessHost;
57 using content::UtilityProcessHostClient;
59 namespace keys = extension_management_api_constants;
61 namespace extensions {
63 namespace management = api::management;
67 typedef std::vector<linked_ptr<management::ExtensionInfo> > ExtensionInfoList;
68 typedef std::vector<linked_ptr<management::IconInfo> > IconInfoList;
70 enum AutoConfirmForTest {
76 AutoConfirmForTest auto_confirm_for_test = DO_NOT_SKIP;
78 std::vector<std::string> CreateWarningsList(const Extension* extension) {
79 std::vector<std::string> warnings_list;
80 PermissionMessages warnings =
81 PermissionsData::GetPermissionMessages(extension);
82 for (PermissionMessages::const_iterator iter = warnings.begin();
83 iter != warnings.end(); ++iter) {
84 warnings_list.push_back(UTF16ToUTF8(iter->message()));
90 scoped_ptr<management::ExtensionInfo> CreateExtensionInfo(
91 const Extension& extension,
92 ExtensionSystem* system) {
93 scoped_ptr<management::ExtensionInfo> info(new management::ExtensionInfo());
94 ExtensionService* service = system->extension_service();
96 info->id = extension.id();
97 info->name = extension.name();
98 info->short_name = extension.short_name();
99 info->enabled = service->IsExtensionEnabled(info->id);
100 info->offline_enabled = OfflineEnabledInfo::IsOfflineEnabled(&extension);
101 info->version = extension.VersionString();
102 info->description = extension.description();
103 info->options_url = ManifestURL::GetOptionsPage(&extension).spec();
104 info->homepage_url.reset(new std::string(
105 ManifestURL::GetHomepageURL(&extension).spec()));
106 info->may_disable = system->management_policy()->
107 UserMayModifySettings(&extension, NULL);
108 info->is_app = extension.is_app();
110 if (extension.is_legacy_packaged_app())
111 info->type = management::ExtensionInfo::TYPE_LEGACY_PACKAGED_APP;
112 else if (extension.is_hosted_app())
113 info->type = management::ExtensionInfo::TYPE_HOSTED_APP;
115 info->type = management::ExtensionInfo::TYPE_PACKAGED_APP;
116 } else if (extension.is_theme()) {
117 info->type = management::ExtensionInfo::TYPE_THEME;
119 info->type = management::ExtensionInfo::TYPE_EXTENSION;
123 info->disabled_reason = management::ExtensionInfo::DISABLED_REASON_NONE;
125 ExtensionPrefs* prefs = service->extension_prefs();
126 if (prefs->DidExtensionEscalatePermissions(extension.id())) {
127 info->disabled_reason =
128 management::ExtensionInfo::DISABLED_REASON_PERMISSIONS_INCREASE;
130 info->disabled_reason =
131 management::ExtensionInfo::DISABLED_REASON_UNKNOWN;
135 if (!ManifestURL::GetUpdateURL(&extension).is_empty()) {
136 info->update_url.reset(new std::string(
137 ManifestURL::GetUpdateURL(&extension).spec()));
140 if (extension.is_app()) {
141 info->app_launch_url.reset(new std::string(
142 AppLaunchInfo::GetFullLaunchURL(&extension).spec()));
145 const ExtensionIconSet::IconMap& icons =
146 IconsInfo::GetIcons(&extension).map();
147 if (!icons.empty()) {
148 info->icons.reset(new IconInfoList());
149 ExtensionIconSet::IconMap::const_iterator icon_iter;
150 for (icon_iter = icons.begin(); icon_iter != icons.end(); ++icon_iter) {
151 management::IconInfo* icon_info = new management::IconInfo();
152 icon_info->size = icon_iter->first;
153 GURL url = ExtensionIconSource::GetIconURL(
154 &extension, icon_info->size, ExtensionIconSet::MATCH_EXACTLY, false,
156 icon_info->url = url.spec();
157 info->icons->push_back(make_linked_ptr<management::IconInfo>(icon_info));
161 const std::set<std::string> perms =
162 extension.GetActivePermissions()->GetAPIsAsStrings();
163 if (!perms.empty()) {
164 std::set<std::string>::const_iterator perms_iter;
165 for (perms_iter = perms.begin(); perms_iter != perms.end(); ++perms_iter)
166 info->permissions.push_back(*perms_iter);
169 if (!extension.is_hosted_app()) {
170 // Skip host permissions for hosted apps.
171 const URLPatternSet host_perms =
172 extension.GetActivePermissions()->explicit_hosts();
173 if (!host_perms.is_empty()) {
174 for (URLPatternSet::const_iterator iter = host_perms.begin();
175 iter != host_perms.end(); ++iter) {
176 info->host_permissions.push_back(iter->GetAsString());
181 switch (extension.location()) {
182 case Manifest::INTERNAL:
183 info->install_type = management::ExtensionInfo::INSTALL_TYPE_NORMAL;
185 case Manifest::UNPACKED:
186 case Manifest::COMMAND_LINE:
187 info->install_type = management::ExtensionInfo::INSTALL_TYPE_DEVELOPMENT;
189 case Manifest::EXTERNAL_PREF:
190 case Manifest::EXTERNAL_REGISTRY:
191 case Manifest::EXTERNAL_PREF_DOWNLOAD:
192 info->install_type = management::ExtensionInfo::INSTALL_TYPE_SIDELOAD;
194 case Manifest::EXTERNAL_POLICY:
195 case Manifest::EXTERNAL_POLICY_DOWNLOAD:
196 info->install_type = management::ExtensionInfo::INSTALL_TYPE_ADMIN;
198 case Manifest::NUM_LOCATIONS:
200 case Manifest::INVALID_LOCATION:
201 case Manifest::COMPONENT:
202 case Manifest::EXTERNAL_COMPONENT:
203 info->install_type = management::ExtensionInfo::INSTALL_TYPE_OTHER;
210 void AddExtensionInfo(const ExtensionSet& extensions,
211 ExtensionSystem* system,
212 ExtensionInfoList* extension_list) {
213 for (ExtensionSet::const_iterator iter = extensions.begin();
214 iter != extensions.end(); ++iter) {
215 const Extension& extension = *iter->get();
217 if (extension.ShouldNotBeVisible())
218 continue; // Skip built-in extensions/apps.
220 extension_list->push_back(make_linked_ptr<management::ExtensionInfo>(
221 CreateExtensionInfo(extension, system).release()));
227 ExtensionService* ManagementFunction::service() {
228 return GetProfile()->GetExtensionService();
231 ExtensionService* AsyncManagementFunction::service() {
232 return GetProfile()->GetExtensionService();
235 bool ManagementGetAllFunction::RunImpl() {
236 ExtensionInfoList extensions;
237 ExtensionSystem* system = ExtensionSystem::Get(GetProfile());
239 AddExtensionInfo(*service()->extensions(), system, &extensions);
240 AddExtensionInfo(*service()->disabled_extensions(), system, &extensions);
241 AddExtensionInfo(*service()->terminated_extensions(), system, &extensions);
243 results_ = management::GetAll::Results::Create(extensions);
247 bool ManagementGetFunction::RunImpl() {
248 scoped_ptr<management::Get::Params> params(
249 management::Get::Params::Create(*args_));
250 EXTENSION_FUNCTION_VALIDATE(params.get());
252 const Extension* extension = service()->GetExtensionById(params->id, true);
254 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError,
259 scoped_ptr<management::ExtensionInfo> info =
260 CreateExtensionInfo(*extension, ExtensionSystem::Get(GetProfile()));
261 results_ = management::Get::Results::Create(*info);
266 bool ManagementGetPermissionWarningsByIdFunction::RunImpl() {
267 scoped_ptr<management::GetPermissionWarningsById::Params> params(
268 management::GetPermissionWarningsById::Params::Create(*args_));
269 EXTENSION_FUNCTION_VALIDATE(params.get());
271 const Extension* extension = service()->GetExtensionById(params->id, true);
273 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError,
278 std::vector<std::string> warnings = CreateWarningsList(extension);
279 results_ = management::GetPermissionWarningsById::Results::Create(warnings);
285 // This class helps ManagementGetPermissionWarningsByManifestFunction manage
286 // sending manifest JSON strings to the utility process for parsing.
287 class SafeManifestJSONParser : public UtilityProcessHostClient {
289 SafeManifestJSONParser(
290 ManagementGetPermissionWarningsByManifestFunction* client,
291 const std::string& manifest)
293 manifest_(manifest) {}
296 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
297 BrowserThread::PostTask(
300 base::Bind(&SafeManifestJSONParser::StartWorkOnIOThread, this));
303 void StartWorkOnIOThread() {
304 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
305 UtilityProcessHost* host = UtilityProcessHost::Create(
306 this, base::MessageLoopProxy::current().get());
307 host->EnableZygote();
308 host->Send(new ChromeUtilityMsg_ParseJSON(manifest_));
311 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
313 IPC_BEGIN_MESSAGE_MAP(SafeManifestJSONParser, message)
314 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Succeeded,
315 OnJSONParseSucceeded)
316 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Failed,
318 IPC_MESSAGE_UNHANDLED(handled = false)
319 IPC_END_MESSAGE_MAP()
323 void OnJSONParseSucceeded(const base::ListValue& wrapper) {
324 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
325 const Value* value = NULL;
326 CHECK(wrapper.Get(0, &value));
327 if (value->IsType(Value::TYPE_DICTIONARY))
328 parsed_manifest_.reset(
329 static_cast<const base::DictionaryValue*>(value)->DeepCopy());
331 error_ = keys::kManifestParseError;
333 BrowserThread::PostTask(
336 base::Bind(&SafeManifestJSONParser::ReportResultFromUIThread, this));
339 void OnJSONParseFailed(const std::string& error) {
340 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
342 BrowserThread::PostTask(
345 base::Bind(&SafeManifestJSONParser::ReportResultFromUIThread, this));
348 void ReportResultFromUIThread() {
349 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
350 if (error_.empty() && parsed_manifest_.get())
351 client_->OnParseSuccess(parsed_manifest_.Pass());
353 client_->OnParseFailure(error_);
357 virtual ~SafeManifestJSONParser() {}
359 // The client who we'll report results back to.
360 ManagementGetPermissionWarningsByManifestFunction* client_;
363 std::string manifest_;
365 // Results of parsing.
366 scoped_ptr<base::DictionaryValue> parsed_manifest_;
373 bool ManagementGetPermissionWarningsByManifestFunction::RunImpl() {
374 scoped_ptr<management::GetPermissionWarningsByManifest::Params> params(
375 management::GetPermissionWarningsByManifest::Params::Create(*args_));
376 EXTENSION_FUNCTION_VALIDATE(params.get());
378 scoped_refptr<SafeManifestJSONParser> parser =
379 new SafeManifestJSONParser(this, params->manifest_str);
382 // Matched with a Release() in OnParseSuccess/Failure().
385 // Response is sent async in OnParseSuccess/Failure().
389 void ManagementGetPermissionWarningsByManifestFunction::OnParseSuccess(
390 scoped_ptr<base::DictionaryValue> parsed_manifest) {
391 CHECK(parsed_manifest.get());
393 scoped_refptr<Extension> extension = Extension::Create(
394 base::FilePath(), Manifest::INVALID_LOCATION, *parsed_manifest,
395 Extension::NO_FLAGS, &error_);
396 if (!extension.get()) {
397 OnParseFailure(keys::kExtensionCreateError);
401 std::vector<std::string> warnings = CreateWarningsList(extension.get());
403 management::GetPermissionWarningsByManifest::Results::Create(warnings);
406 // Matched with AddRef() in RunImpl().
410 void ManagementGetPermissionWarningsByManifestFunction::OnParseFailure(
411 const std::string& error) {
415 // Matched with AddRef() in RunImpl().
419 bool ManagementLaunchAppFunction::RunImpl() {
420 scoped_ptr<management::LaunchApp::Params> params(
421 management::LaunchApp::Params::Create(*args_));
422 EXTENSION_FUNCTION_VALIDATE(params.get());
423 const Extension* extension = service()->GetExtensionById(params->id, true);
425 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError,
429 if (!extension->is_app()) {
430 error_ = ErrorUtils::FormatErrorMessage(keys::kNotAnAppError,
435 // Look at prefs to find the right launch container.
436 // |default_pref_value| is set to LAUNCH_DEFAULT so that if
437 // the user has not set a preference, we open the app in a tab.
438 extension_misc::LaunchContainer launch_container =
439 service()->extension_prefs()->GetLaunchContainer(
440 extension, ExtensionPrefs::LAUNCH_DEFAULT);
441 OpenApplication(AppLaunchParams(
442 GetProfile(), extension, launch_container, NEW_FOREGROUND_TAB));
443 #if !defined(OS_ANDROID)
444 CoreAppLauncherHandler::RecordAppLaunchType(
445 extension_misc::APP_LAUNCH_EXTENSION_API,
446 extension->GetType());
452 ManagementSetEnabledFunction::ManagementSetEnabledFunction() {
455 ManagementSetEnabledFunction::~ManagementSetEnabledFunction() {
458 bool ManagementSetEnabledFunction::RunImpl() {
459 scoped_ptr<management::SetEnabled::Params> params(
460 management::SetEnabled::Params::Create(*args_));
461 EXTENSION_FUNCTION_VALIDATE(params.get());
463 extension_id_ = params->id;
465 const Extension* extension = service()->GetInstalledExtension(extension_id_);
466 if (!extension || extension->ShouldNotBeVisible()) {
467 error_ = ErrorUtils::FormatErrorMessage(
468 keys::kNoExtensionError, extension_id_);
472 const ManagementPolicy* policy =
473 ExtensionSystem::Get(GetProfile())->management_policy();
474 if (!policy->UserMayModifySettings(extension, NULL) ||
475 (!params->enabled && policy->MustRemainEnabled(extension, NULL)) ||
476 (params->enabled && policy->MustRemainDisabled(extension, NULL, NULL))) {
477 error_ = ErrorUtils::FormatErrorMessage(
478 keys::kUserCantModifyError, extension_id_);
482 bool currently_enabled = service()->IsExtensionEnabled(extension_id_);
484 if (!currently_enabled && params->enabled) {
485 ExtensionPrefs* prefs = service()->extension_prefs();
486 if (prefs->DidExtensionEscalatePermissions(extension_id_)) {
487 if (!user_gesture()) {
488 error_ = keys::kGestureNeededForEscalationError;
491 AddRef(); // Matched in InstallUIProceed/InstallUIAbort
492 install_prompt_.reset(
493 new ExtensionInstallPrompt(GetAssociatedWebContents()));
494 install_prompt_->ConfirmReEnable(this, extension);
497 service()->EnableExtension(extension_id_);
498 } else if (currently_enabled && !params->enabled) {
499 service()->DisableExtension(extension_id_, Extension::DISABLE_USER_ACTION);
502 BrowserThread::PostTask(
505 base::Bind(&ManagementSetEnabledFunction::SendResponse, this, true));
510 void ManagementSetEnabledFunction::InstallUIProceed() {
511 service()->EnableExtension(extension_id_);
516 void ManagementSetEnabledFunction::InstallUIAbort(bool user_initiated) {
517 error_ = keys::kUserDidNotReEnableError;
522 ManagementUninstallFunctionBase::ManagementUninstallFunctionBase() {
525 ManagementUninstallFunctionBase::~ManagementUninstallFunctionBase() {
528 bool ManagementUninstallFunctionBase::Uninstall(
529 const std::string& extension_id,
530 bool show_confirm_dialog) {
531 extension_id_ = extension_id;
532 const Extension* extension = service()->GetExtensionById(extension_id_, true);
533 if (!extension || extension->ShouldNotBeVisible()) {
534 error_ = ErrorUtils::FormatErrorMessage(
535 keys::kNoExtensionError, extension_id_);
539 if (!ExtensionSystem::Get(GetProfile())
540 ->management_policy()
541 ->UserMayModifySettings(extension, NULL)) {
542 error_ = ErrorUtils::FormatErrorMessage(
543 keys::kUserCantModifyError, extension_id_);
547 if (auto_confirm_for_test == DO_NOT_SKIP) {
548 if (show_confirm_dialog) {
549 AddRef(); // Balanced in ExtensionUninstallAccepted/Canceled
550 extension_uninstall_dialog_.reset(ExtensionUninstallDialog::Create(
551 GetProfile(), GetCurrentBrowser(), this));
552 extension_uninstall_dialog_->ConfirmUninstall(extension);
557 Finish(auto_confirm_for_test == PROCEED);
564 void ManagementUninstallFunctionBase::SetAutoConfirmForTest(
565 bool should_proceed) {
566 auto_confirm_for_test = should_proceed ? PROCEED : ABORT;
569 void ManagementUninstallFunctionBase::Finish(bool should_uninstall) {
570 if (should_uninstall) {
571 bool success = service()->UninstallExtension(
573 false, /* external uninstall */
576 // TODO set error_ if !success
577 SendResponse(success);
579 error_ = ErrorUtils::FormatErrorMessage(
580 keys::kUninstallCanceledError, extension_id_);
586 void ManagementUninstallFunctionBase::ExtensionUninstallAccepted() {
591 void ManagementUninstallFunctionBase::ExtensionUninstallCanceled() {
596 ManagementUninstallFunction::ManagementUninstallFunction() {
599 ManagementUninstallFunction::~ManagementUninstallFunction() {
602 bool ManagementUninstallFunction::RunImpl() {
603 scoped_ptr<management::Uninstall::Params> params(
604 management::Uninstall::Params::Create(*args_));
605 EXTENSION_FUNCTION_VALIDATE(params.get());
607 bool show_confirm_dialog = false;
608 if (params->options.get() && params->options->show_confirm_dialog.get())
609 show_confirm_dialog = *params->options->show_confirm_dialog;
611 return Uninstall(params->id, show_confirm_dialog);
614 ManagementUninstallSelfFunction::ManagementUninstallSelfFunction() {
617 ManagementUninstallSelfFunction::~ManagementUninstallSelfFunction() {
620 bool ManagementUninstallSelfFunction::RunImpl() {
621 scoped_ptr<management::UninstallSelf::Params> params(
622 management::UninstallSelf::Params::Create(*args_));
623 EXTENSION_FUNCTION_VALIDATE(params.get());
625 bool show_confirm_dialog = false;
626 if (params->options.get() && params->options->show_confirm_dialog.get())
627 show_confirm_dialog = *params->options->show_confirm_dialog;
628 return Uninstall(extension_->id(), show_confirm_dialog);
631 ManagementEventRouter::ManagementEventRouter(Profile* profile)
632 : profile_(profile) {
634 chrome::NOTIFICATION_EXTENSION_INSTALLED,
635 chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
636 chrome::NOTIFICATION_EXTENSION_LOADED,
637 chrome::NOTIFICATION_EXTENSION_UNLOADED
640 CHECK(registrar_.IsEmpty());
641 for (size_t i = 0; i < arraysize(types); i++) {
644 content::Source<Profile>(profile_));
648 ManagementEventRouter::~ManagementEventRouter() {}
650 void ManagementEventRouter::Observe(
652 const content::NotificationSource& source,
653 const content::NotificationDetails& details) {
654 const char* event_name = NULL;
655 const Extension* extension = NULL;
656 Profile* profile = content::Source<Profile>(source).ptr();
658 CHECK(profile_->IsSameProfile(profile));
661 case chrome::NOTIFICATION_EXTENSION_INSTALLED:
662 event_name = management::OnInstalled::kEventName;
664 content::Details<const InstalledExtensionInfo>(details)->extension;
666 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED:
667 event_name = management::OnUninstalled::kEventName;
668 extension = content::Details<const Extension>(details).ptr();
670 case chrome::NOTIFICATION_EXTENSION_LOADED:
671 event_name = management::OnEnabled::kEventName;
672 extension = content::Details<const Extension>(details).ptr();
674 case chrome::NOTIFICATION_EXTENSION_UNLOADED:
675 event_name = management::OnDisabled::kEventName;
677 content::Details<const UnloadedExtensionInfo>(details)->extension;
686 if (extension->ShouldNotBeVisible())
687 return; // Don't dispatch events for built-in extensions.
689 scoped_ptr<base::ListValue> args(new base::ListValue());
690 if (event_name == management::OnUninstalled::kEventName) {
691 args->Append(new base::StringValue(extension->id()));
693 scoped_ptr<management::ExtensionInfo> info = CreateExtensionInfo(
694 *extension, ExtensionSystem::Get(profile));
695 args->Append(info->ToValue().release());
698 scoped_ptr<Event> event(new Event(event_name, args.Pass()));
699 ExtensionSystem::Get(profile)->event_router()->BroadcastEvent(event.Pass());
702 ManagementAPI::ManagementAPI(Profile* profile)
703 : profile_(profile) {
704 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
705 this, management::OnInstalled::kEventName);
706 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
707 this, management::OnUninstalled::kEventName);
708 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
709 this, management::OnEnabled::kEventName);
710 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
711 this, management::OnDisabled::kEventName);
714 ManagementAPI::~ManagementAPI() {
717 void ManagementAPI::Shutdown() {
718 ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
721 static base::LazyInstance<ProfileKeyedAPIFactory<ManagementAPI> >
722 g_factory = LAZY_INSTANCE_INITIALIZER;
725 ProfileKeyedAPIFactory<ManagementAPI>* ManagementAPI::GetFactoryInstance() {
726 return &g_factory.Get();
729 void ManagementAPI::OnListenerAdded(const EventListenerInfo& details) {
730 management_event_router_.reset(new ManagementEventRouter(profile_));
731 ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
734 } // namespace extensions