Introduce ProfileManagerObserver.

Currently there are two methods, OnProfileAdded (to replace
NOTIFICATION_PROFILE_ADDED and NOTIFICATION_PROFILE_CREATED) and
OnProfileMarkedForPermanentDeletion (to replace
NOTIFICATION_PROFILE_DESTRUCTION_STARTED).

Use the new interface in NoteTakingHelper and ExtensionService.

Another option would be to have OnProfileMarkedForPermanentDeletion
be a part of a ProfileObserver interface, but it seems best to
minimize the number of observer interfaces.

Bug: 268984
Change-Id: I96ba72e558ee3da30729207adcd8e22e0755293a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1776682
Commit-Queue: Evan Stade <[email protected]>
Reviewed-by: Scott Violet <[email protected]>
Cr-Commit-Position: refs/heads/master@{#693858}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index e0d99c9..0bc6da64 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1472,6 +1472,7 @@
     "profiles/profile_key.h",
     "profiles/profile_manager.cc",
     "profiles/profile_manager.h",
+    "profiles/profile_manager_observer.h",
     "profiles/profile_metrics.cc",
     "profiles/profile_metrics.h",
     "profiles/profile_shortcut_manager_win.cc",
diff --git a/chrome/browser/chrome_notification_types.h b/chrome/browser/chrome_notification_types.h
index d6f87fb..fa06a890 100644
--- a/chrome/browser/chrome_notification_types.h
+++ b/chrome/browser/chrome_notification_types.h
@@ -119,29 +119,18 @@
   // The details are none and the source is the new profile.
   NOTIFICATION_PROFILE_CREATED,
 
+  // Use ProfileManagerObserver::OnProfileAdded instead of this notification.
   // Sent after a Profile has been added to ProfileManager.
   // The details are none and the source is the new profile.
   NOTIFICATION_PROFILE_ADDED,
 
   // Use KeyedServiceShutdownNotifier instead this notification type (you did
   // read the comment at the top of the file, didn't you?).
-  // Sent early in the process of destroying a Profile, at the time a user
-  // initiates the deletion of a profile versus the much later time when the
-  // profile object is actually destroyed (use NOTIFICATION_PROFILE_DESTROYED).
-  // The details are none and the source is a Profile*.
-  NOTIFICATION_PROFILE_DESTRUCTION_STARTED,
-
-  // Use KeyedServiceShutdownNotifier instead this notification type (you did
-  // read the comment at the top of the file, didn't you?).
   // Sent before a Profile is destroyed. This notification is sent both for
   // normal and OTR profiles.
   // The details are none and the source is a Profile*.
   NOTIFICATION_PROFILE_DESTROYED,
 
-  // Sent after the URLRequestContextGetter for a Profile has been initialized.
-  // The details are none and the source is a Profile*.
-  NOTIFICATION_PROFILE_URL_REQUEST_CONTEXT_GETTER_INITIALIZED,
-
   // Printing ----------------------------------------------------------------
 
   // Notification from PrintJob that an event occurred. It can be that a page
diff --git a/chrome/browser/chromeos/note_taking_helper.cc b/chrome/browser/chromeos/note_taking_helper.cc
index 797801c..8a9b52a6 100644
--- a/chrome/browser/chromeos/note_taking_helper.cc
+++ b/chrome/browser/chromeos/note_taking_helper.cc
@@ -18,7 +18,6 @@
 #include "base/stl_util.h"
 #include "base/strings/string_split.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/chromeos/lock_screen_apps/state_controller.h"
@@ -34,7 +33,6 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_service.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/api/app_runtime.h"
 #include "extensions/common/extension.h"
@@ -329,6 +327,24 @@
     observer.OnAvailableNoteTakingAppsUpdated();
 }
 
+void NoteTakingHelper::OnProfileAdded(Profile* profile) {
+  auto* registry = extensions::ExtensionRegistry::Get(profile);
+  DCHECK(!extension_registry_observer_.IsObserving(registry));
+  extension_registry_observer_.Add(registry);
+
+  // TODO(derat): Remove this once OnArcPlayStoreEnabledChanged() is always
+  // called after an ARC-enabled user logs in: http://b/36655474
+  if (!play_store_enabled_ && arc::IsArcPlayStoreEnabledForProfile(profile)) {
+    play_store_enabled_ = true;
+    for (Observer& observer : observers_)
+      observer.OnAvailableNoteTakingAppsUpdated();
+  }
+
+  auto* bridge = arc::ArcIntentHelperBridge::GetForBrowserContext(profile);
+  if (bridge)
+    bridge->AddObserver(this);
+}
+
 void NoteTakingHelper::SetProfileWithEnabledLockScreenApps(Profile* profile) {
   DCHECK(!profile_with_enabled_lock_screen_apps_);
   profile_with_enabled_lock_screen_apps_ = profile;
@@ -361,8 +377,7 @@
                                      kExtensionIds + base::size(kExtensionIds));
 
   // Track profiles so we can observe their extension registries.
-  registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
-                 content::NotificationService::AllBrowserContextsAndSources());
+  g_browser_process->profile_manager()->AddObserver(this);
   play_store_enabled_ = false;
   for (Profile* profile :
        g_browser_process->profile_manager()->GetLoadedProfiles()) {
@@ -399,6 +414,8 @@
 NoteTakingHelper::~NoteTakingHelper() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+  g_browser_process->profile_manager()->RemoveObserver(this);
+
   // ArcSessionManagerTest shuts down ARC before NoteTakingHelper.
   if (arc::ArcSessionManager::Get())
     arc::ArcSessionManager::Get()->RemoveObserver(this);
@@ -538,30 +555,6 @@
   NOTREACHED();
 }
 
-void NoteTakingHelper::Observe(int type,
-                               const content::NotificationSource& source,
-                               const content::NotificationDetails& details) {
-  DCHECK_EQ(type, chrome::NOTIFICATION_PROFILE_ADDED);
-  Profile* profile = content::Source<Profile>(source).ptr();
-  DCHECK(profile);
-
-  auto* registry = extensions::ExtensionRegistry::Get(profile);
-  DCHECK(!extension_registry_observer_.IsObserving(registry));
-  extension_registry_observer_.Add(registry);
-
-  // TODO(derat): Remove this once OnArcPlayStoreEnabledChanged() is always
-  // called after an ARC-enabled user logs in: http://b/36655474
-  if (!play_store_enabled_ && arc::IsArcPlayStoreEnabledForProfile(profile)) {
-    play_store_enabled_ = true;
-    for (Observer& observer : observers_)
-      observer.OnAvailableNoteTakingAppsUpdated();
-  }
-
-  auto* bridge = arc::ArcIntentHelperBridge::GetForBrowserContext(profile);
-  if (bridge)
-    bridge->AddObserver(this);
-}
-
 void NoteTakingHelper::OnExtensionLoaded(
     content::BrowserContext* browser_context,
     const extensions::Extension* extension) {
diff --git a/chrome/browser/chromeos/note_taking_helper.h b/chrome/browser/chromeos/note_taking_helper.h
index 8af050e..dd7868b 100644
--- a/chrome/browser/chromeos/note_taking_helper.h
+++ b/chrome/browser/chromeos/note_taking_helper.h
@@ -15,13 +15,10 @@
 #include "base/observer_list.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
+#include "chrome/browser/profiles/profile_manager_observer.h"
 #include "components/arc/intent_helper/arc_intent_helper_observer.h"
 #include "components/arc/mojom/intent_helper.mojom.h"
 #include "components/prefs/pref_change_registrar.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "content/public/browser/notification_source.h"
 #include "extensions/browser/extension_registry_observer.h"
 #include "extensions/common/extension.h"
 
@@ -92,8 +89,8 @@
 // Singleton class used to launch a note-taking app.
 class NoteTakingHelper : public arc::ArcIntentHelperObserver,
                          public arc::ArcSessionManager::Observer,
-                         public content::NotificationObserver,
-                         public extensions::ExtensionRegistryObserver {
+                         public extensions::ExtensionRegistryObserver,
+                         public ProfileManagerObserver {
  public:
   // Interface for observing changes to the list of available apps.
   class Observer {
@@ -202,6 +199,9 @@
   // arc::ArcSessionManager::Observer:
   void OnArcPlayStoreEnabledChanged(bool enabled) override;
 
+  // ProfileManagerObserver:
+  void OnProfileAdded(Profile* profile) override;
+
   // Sets the profile which supports note taking apps on the lock screen.
   void SetProfileWithEnabledLockScreenApps(Profile* profile);
 
@@ -246,11 +246,6 @@
                                  const std::string& app_id,
                                  const base::FilePath& path);
 
-  // content::NotificationObserver:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
-
   // extensions::ExtensionRegistryObserver:
   void OnExtensionLoaded(content::BrowserContext* browser_context,
                          const extensions::Extension* extension) override;
@@ -322,8 +317,6 @@
 
   base::ObserverList<Observer>::Unchecked observers_;
 
-  content::NotificationRegistrar registrar_;
-
   std::unique_ptr<NoteTakingControllerClient> note_taking_controller_client_;
 
   base::WeakPtrFactory<NoteTakingHelper> weak_ptr_factory_{this};
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index d2fd26b6..3ea9b0cd 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -32,6 +32,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/api/content_settings/content_settings_custom_extension_provider.h"
 #include "chrome/browser/extensions/api/content_settings/content_settings_service.h"
@@ -322,9 +323,9 @@
                  content::NotificationService::AllBrowserContextsAndSources());
   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
                  content::NotificationService::AllBrowserContextsAndSources());
-  registrar_.Add(this,
-                 chrome::NOTIFICATION_PROFILE_DESTRUCTION_STARTED,
-                 content::Source<Profile>(profile_));
+  // The ProfileManager may be null in unit tests.
+  if (g_browser_process->profile_manager())
+    profile_manager_observer_.Add(g_browser_process->profile_manager());
 
   UpgradeDetector::GetInstance()->AddObserver(this);
 
@@ -1842,10 +1843,6 @@
                                     system_->info_map(), process->GetID()));
       break;
     }
-    case chrome::NOTIFICATION_PROFILE_DESTRUCTION_STARTED: {
-      OnProfileDestructionStarted();
-      break;
-    }
 
     default:
       NOTREACHED() << "Unexpected notification type.";
@@ -1999,6 +1996,15 @@
   return !extension || CanBlockExtension(extension);
 }
 
+void ExtensionService::OnProfileMarkedForPermanentDeletion(Profile* profile) {
+  if (profile != profile_)
+    return;
+
+  ExtensionIdSet ids_to_unload = registry_->enabled_extensions().GetIDs();
+  for (auto it = ids_to_unload.begin(); it != ids_to_unload.end(); ++it)
+    UnloadExtension(*it, UnloadedExtensionReason::PROFILE_SHUTDOWN);
+}
+
 void ExtensionService::ManageBlacklist(
     const Blacklist::BlacklistStateMap& state_map) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -2166,13 +2172,6 @@
   // been disabled or uninstalled.
 }
 
-void ExtensionService::OnProfileDestructionStarted() {
-  ExtensionIdSet ids_to_unload = registry_->enabled_extensions().GetIDs();
-  for (auto it = ids_to_unload.begin(); it != ids_to_unload.end(); ++it) {
-    UnloadExtension(*it, UnloadedExtensionReason::PROFILE_SHUTDOWN);
-  }
-}
-
 void ExtensionService::OnInstalledExtensionsLoaded() {
   if (updater_)
     updater_->Start();
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 8564a67..4153a22 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -18,12 +18,15 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
+#include "base/scoped_observer.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/extensions/blacklist.h"
 #include "chrome/browser/extensions/extension_management.h"
 #include "chrome/browser/extensions/forced_extensions/installation_tracker.h"
 #include "chrome/browser/extensions/install_gate.h"
 #include "chrome/browser/extensions/pending_extension_manager.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_manager_observer.h"
 #include "chrome/browser/upgrade_detector/upgrade_observer.h"
 #include "components/sync/model/string_ordinal.h"
 #include "content/public/browser/notification_observer.h"
@@ -159,7 +162,8 @@
                          public Blacklist::Observer,
                          public ExtensionManagement::Observer,
                          public UpgradeObserver,
-                         public ExtensionRegistrar::Delegate {
+                         public ExtensionRegistrar::Delegate,
+                         public ProfileManagerObserver {
  public:
   // Constructor stores pointers to |profile| and |extension_prefs| but
   // ownership remains at caller.
@@ -448,6 +452,9 @@
   bool CanDisableExtension(const Extension* extension) override;
   bool ShouldBlockExtension(const Extension* extension) override;
 
+  // ProfileManagerObserver implementation.
+  void OnProfileMarkedForPermanentDeletion(Profile* profile) override;
+
   // For the extension in |version_path| with |id|, check to see if it's an
   // externally managed extension.  If so, uninstall it.
   void CheckExternalUninstall(const std::string& id);
@@ -666,6 +673,9 @@
   // Tracker of enterprise policy forced installation.
   InstallationTracker forced_extensions_tracker_;
 
+  ScopedObserver<ProfileManager, ProfileManagerObserver>
+      profile_manager_observer_{this};
+
   using InstallGateRegistry =
       std::map<ExtensionPrefs::DelayReason, InstallGate*>;
   InstallGateRegistry install_delayer_registry_;
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 80e3abd..8026104 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -7262,9 +7262,7 @@
   EXPECT_EQ(0u, registry()->terminated_extensions().size());
   EXPECT_EQ(0u, registry()->blacklisted_extensions().size());
 
-  service()->Observe(chrome::NOTIFICATION_PROFILE_DESTRUCTION_STARTED,
-                     content::Source<Profile>(profile()),
-                     content::NotificationService::NoDetails());
+  service()->OnProfileMarkedForPermanentDeletion(profile());
   EXPECT_EQ(UnloadedExtensionReason::PROFILE_SHUTDOWN, unloaded_reason_);
   EXPECT_EQ(0u, registry()->enabled_extensions().size());
   EXPECT_EQ(0u, registry()->disabled_extensions().size());
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 8b9e5e2..5fb5b84 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -53,6 +53,7 @@
 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
 #include "chrome/browser/profiles/profile_destroyer.h"
 #include "chrome/browser/profiles/profile_info_cache.h"
+#include "chrome/browser/profiles/profile_manager_observer.h"
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/signin/account_reconcilor_factory.h"
@@ -484,6 +485,14 @@
   return profile;
 }
 
+void ProfileManager::AddObserver(ProfileManagerObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void ProfileManager::RemoveObserver(ProfileManagerObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
 Profile* ProfileManager::GetProfile(const base::FilePath& profile_dir) {
   TRACE_EVENT0("browser", "ProfileManager::GetProfile");
 
@@ -1171,6 +1180,9 @@
   // GetProfileByPath().
   profile_info->created = true;
 
+  for (auto& observer : observers_)
+    observer.OnProfileAdded(profile);
+
   content::NotificationService::current()->Notify(
       chrome::NOTIFICATION_PROFILE_ADDED,
       content::Source<Profile>(profile),
@@ -1471,12 +1483,10 @@
   // TODO(sail): Due to bug 88586 we don't delete the profile instance. Once we
   // start deleting the profile instance we need to close background apps too.
   if (profile) {
-    // TODO: Migrate additional code in this block to observe this notification
-    // instead of being implemented here.
-    content::NotificationService::current()->Notify(
-        chrome::NOTIFICATION_PROFILE_DESTRUCTION_STARTED,
-        content::Source<Profile>(profile),
-        content::NotificationService::NoDetails());
+    // TODO(estade): Migrate additional code in this block to observe
+    // ProfileManager instead of handling shutdown here.
+    for (auto& observer : observers_)
+      observer.OnProfileMarkedForPermanentDeletion(profile);
 
     // Disable sync for doomed profile.
     if (ProfileSyncServiceFactory::HasSyncService(profile)) {
diff --git a/chrome/browser/profiles/profile_manager.h b/chrome/browser/profiles/profile_manager.h
index bcfb7d7..87ecf1f 100644
--- a/chrome/browser/profiles/profile_manager.h
+++ b/chrome/browser/profiles/profile_manager.h
@@ -18,6 +18,7 @@
 #include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
+#include "base/observer_list.h"
 #include "base/threading/thread_checker.h"
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
@@ -30,6 +31,7 @@
 
 class ProfileAttributesStorage;
 class ProfileInfoCache;
+class ProfileManagerObserver;
 
 class ProfileManager : public content::NotificationObserver,
                        public Profile::Delegate {
@@ -86,6 +88,9 @@
   // platforms, this returns the default profile.
   static Profile* CreateInitialProfile();
 
+  void AddObserver(ProfileManagerObserver* observer);
+  void RemoveObserver(ProfileManagerObserver* observer);
+
   // Returns a profile for a specific profile directory within the user data
   // dir. This will return an existing profile it had already been created,
   // otherwise it will create and manage it.
@@ -445,6 +450,8 @@
   // Controls whether to initialize some services. Only disabled for testing.
   bool do_final_services_init_ = true;
 
+  base::ObserverList<ProfileManagerObserver> observers_;
+
   // TODO(chrome/browser/profiles/OWNERS): Usage of this in profile_manager.cc
   // should likely be turned into DCHECK_CURRENTLY_ON(BrowserThread::UI) for
   // consistency with surrounding code in the same file but that wasn't trivial
diff --git a/chrome/browser/profiles/profile_manager_observer.h b/chrome/browser/profiles/profile_manager_observer.h
new file mode 100644
index 0000000..5f15d1d
--- /dev/null
+++ b/chrome/browser/profiles/profile_manager_observer.h
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PROFILES_PROFILE_MANAGER_OBSERVER_H_
+#define CHROME_BROWSER_PROFILES_PROFILE_MANAGER_OBSERVER_H_
+
+#include "base/observer_list_types.h"
+
+class Profile;
+
+class ProfileManagerObserver : public base::CheckedObserver {
+ public:
+  // Called when a profile is added to the manager. The profile is fully created
+  // and registered with the ProfileManager.
+  virtual void OnProfileAdded(Profile* profile) {}
+
+  // Called when the user deletes a profile and all associated data should be
+  // erased. Note that the Profile object will not be destroyed until Chrome
+  // shuts down. See https://crbug.com/88586
+  virtual void OnProfileMarkedForPermanentDeletion(Profile* profile) {}
+};
+
+#endif  // CHROME_BROWSER_PROFILES_PROFILE_MANAGER_OBSERVER_H_