From a9f39a313b7ecc11d98727d869e15094481f3a65 Mon Sep 17 00:00:00 2001
From: "asargent@chromium.org"
 <asargent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Thu, 23 Dec 2010 22:14:27 +0000
Subject: [PATCH] Change extension unload notification to indicate updates.

When an extension in being unloaded, some listeners want to know if it's
because the extension is being updated to a newer version, or disabled. This
changes the details sent to include a reason.

Also this removes the EXTENSION_UNLOADED_DISABLED notification, since only a
small number of places actually cared about the disctinction between
EXTENSION_UNLOADED and EXTENSION_UNLOADED_DISABLED, and puts that information
into the details as well.

BUG=65510
TEST=Should be covered by existing unit & browser tests.


Review URL: http://codereview.chromium.org/5968009

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@70104 0039d316-1c4b-4281-b951-d872f2087c98
---
 .../automation_extension_tracker.cc           | 18 ++++------
 .../automation_provider_observers.cc          |  5 +--
 .../background_application_list_model.cc      | 10 ++----
 .../background_application_list_model.h       |  4 +--
 chrome/browser/background_contents_service.cc |  3 +-
 chrome/browser/background_mode_manager.cc     |  3 +-
 chrome/browser/background_page_tracker.cc     |  2 +-
 .../extensions/extension_browsertest.cc       |  2 +-
 .../extension_disabled_infobar_delegate.cc    | 17 ++++++---
 chrome/browser/extensions/extension_host.cc   |  2 +-
 .../extensions/extension_infobar_delegate.cc  |  4 ++-
 .../extensions/extension_management_api.cc    |  7 +++-
 .../extensions/extension_menu_manager.cc      |  3 +-
 .../extension_menu_manager_unittest.cc        |  3 +-
 .../extensions/extension_process_manager.cc   |  3 +-
 .../browser/extensions/extension_service.cc   | 36 +++++++++++--------
 chrome/browser/extensions/extension_service.h |  6 ++--
 .../extensions/extension_service_unittest.cc  |  3 +-
 .../extensions/extension_toolbar_model.cc     | 12 ++++---
 chrome/browser/extensions/extensions_ui.cc    |  3 --
 .../extensions/image_loading_tracker.cc       |  8 ++---
 .../image_loading_tracker_unittest.cc         |  4 ++-
 .../extensions/user_script_listener.cc        |  2 +-
 .../user_script_listener_unittest.cc          |  3 +-
 .../browser/extensions/user_script_master.cc  |  3 +-
 .../gtk/extension_installed_bubble_gtk.cc     |  3 +-
 .../desktop_notification_service.cc           |  3 +-
 chrome/browser/plugin_service.cc              |  3 +-
 .../sync/glue/extension_change_processor.cc   | 13 +++----
 .../sync/glue/theme_change_processor.cc       |  7 +++-
 chrome/browser/tab_contents/tab_contents.cc   |  3 --
 chrome/browser/tabs/tab_strip_model.cc        |  3 +-
 chrome/browser/ui/browser.cc                  |  8 ++---
 .../extensions/extension_installed_bubble.cc  |  3 +-
 chrome/common/extensions/extension.cc         |  8 +++++
 chrome/common/extensions/extension.h          | 18 ++++++++++
 chrome/common/notification_type.h             |  7 ++--
 37 files changed, 146 insertions(+), 99 deletions(-)

diff --git a/chrome/browser/automation/automation_extension_tracker.cc b/chrome/browser/automation/automation_extension_tracker.cc
index 18a480d9b9642..88ed0e916c9f4 100644
--- a/chrome/browser/automation/automation_extension_tracker.cc
+++ b/chrome/browser/automation/automation_extension_tracker.cc
@@ -13,8 +13,6 @@ AutomationExtensionTracker::AutomationExtensionTracker(
     : AutomationResourceTracker<const Extension*>(automation) {
   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
                  NotificationService::AllSources());
-  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
-                 NotificationService::AllSources());
 }
 
 AutomationExtensionTracker::~AutomationExtensionTracker() {
@@ -27,20 +25,18 @@ void AutomationExtensionTracker::RemoveObserver(const Extension* resource) {}
 void AutomationExtensionTracker::Observe(NotificationType type,
                                          const NotificationSource& source,
                                          const NotificationDetails& details) {
-  if (type != NotificationType::EXTENSION_UNLOADED &&
-      type != NotificationType::EXTENSION_UNLOADED_DISABLED)
+  if (type != NotificationType::EXTENSION_UNLOADED) {
+    NOTREACHED();
     return;
-
-  const Extension* extension = Details<const Extension>(details).ptr();
+  }
+  UnloadedExtensionInfo* info = Details<UnloadedExtensionInfo>(details).ptr();
+  const Extension* extension = info->extension;
   Profile* profile = Source<Profile>(source).ptr();
   if (profile) {
     ExtensionService* service = profile->GetExtensionService();
-    if (service) {
+    if (service && info->reason == UnloadedExtensionInfo::UNINSTALL) {
       // Remove this extension only if it is uninstalled, not just disabled.
-      // If it is being uninstalled, the extension will not be in the regular
-      // or disabled list.
-      if (!service->GetExtensionById(extension->id(), true))
-        CloseResource(extension);
+      CloseResource(extension);
     }
   }
 }
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc
index dc0e9392833e1..480a8ecd53f35 100644
--- a/chrome/browser/automation/automation_provider_observers.cc
+++ b/chrome/browser/automation/automation_provider_observers.cc
@@ -550,8 +550,6 @@ ExtensionUnloadNotificationObserver::ExtensionUnloadNotificationObserver()
     : did_receive_unload_notification_(false) {
   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
                  NotificationService::AllSources());
-  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
-                 NotificationService::AllSources());
 }
 
 ExtensionUnloadNotificationObserver::~ExtensionUnloadNotificationObserver() {
@@ -560,8 +558,7 @@ ExtensionUnloadNotificationObserver::~ExtensionUnloadNotificationObserver() {
 void ExtensionUnloadNotificationObserver::Observe(
     NotificationType type, const NotificationSource& source,
     const NotificationDetails& details) {
-  if (type.value == NotificationType::EXTENSION_UNLOADED ||
-      type.value == NotificationType::EXTENSION_UNLOADED_DISABLED) {
+  if (type.value == NotificationType::EXTENSION_UNLOADED) {
     did_receive_unload_notification_ = true;
   } else {
     NOTREACHED();
diff --git a/chrome/browser/background_application_list_model.cc b/chrome/browser/background_application_list_model.cc
index eac257761222c..0392d091e2884 100644
--- a/chrome/browser/background_application_list_model.cc
+++ b/chrome/browser/background_application_list_model.cc
@@ -152,9 +152,6 @@ BackgroundApplicationListModel::BackgroundApplicationListModel(Profile* profile)
   registrar_.Add(this,
                  NotificationType::EXTENSION_UNLOADED,
                  Source<Profile>(profile));
-  registrar_.Add(this,
-                 NotificationType::EXTENSION_UNLOADED_DISABLED,
-                 Source<Profile>(profile));
   registrar_.Add(this,
                  NotificationType::EXTENSIONS_READY,
                  Source<Profile>(profile));
@@ -261,9 +258,7 @@ void BackgroundApplicationListModel::Observe(
       OnExtensionLoaded(Details<Extension>(details).ptr());
       break;
     case NotificationType::EXTENSION_UNLOADED:
-      // Handle extension unload uniformly, falling through to next case.
-    case NotificationType::EXTENSION_UNLOADED_DISABLED:
-      OnExtensionUnloaded(Details<Extension>(details).ptr());
+      OnExtensionUnloaded(Details<UnloadedExtensionInfo>(details)->extension);
       break;
     default:
       NOTREACHED() << "Received unexpected notification";
@@ -283,7 +278,8 @@ void BackgroundApplicationListModel::OnExtensionLoaded(Extension* extension) {
   Update();
 }
 
-void BackgroundApplicationListModel::OnExtensionUnloaded(Extension* extension) {
+void BackgroundApplicationListModel::OnExtensionUnloaded(
+    const Extension* extension) {
   if (!IsBackgroundApp(*extension))
     return;
   Update();
diff --git a/chrome/browser/background_application_list_model.h b/chrome/browser/background_application_list_model.h
index 8f42164a04479..29bfe6552be6d 100644
--- a/chrome/browser/background_application_list_model.h
+++ b/chrome/browser/background_application_list_model.h
@@ -119,8 +119,8 @@ class BackgroundApplicationListModel : public NotificationObserver {
   // Invoked by Observe for EXTENSION_LOADED notifications.
   void OnExtensionLoaded(Extension* extension);
 
-  // Invoked by Observe for EXTENSION_UNLOADED* notifications.
-  void OnExtensionUnloaded(Extension* extension);
+  // Invoked by Observe for EXTENSION_UNLOADED notifications.
+  void OnExtensionUnloaded(const Extension* extension);
 
   // Refresh the list of background applications and generates ApplicationAdded
   // and ApplicationRemoved events.
diff --git a/chrome/browser/background_contents_service.cc b/chrome/browser/background_contents_service.cc
index 92368004397c7..df6db030e7d88 100644
--- a/chrome/browser/background_contents_service.cc
+++ b/chrome/browser/background_contents_service.cc
@@ -115,7 +115,8 @@ void BackgroundContentsService::Observe(NotificationType type,
       break;
     case NotificationType::EXTENSION_UNLOADED:
       ShutdownAssociatedBackgroundContents(
-          ASCIIToUTF16(Details<const Extension>(details)->id()));
+          ASCIIToUTF16(
+              Details<UnloadedExtensionInfo>(details)->extension->id()));
       break;
     default:
       NOTREACHED();
diff --git a/chrome/browser/background_mode_manager.cc b/chrome/browser/background_mode_manager.cc
index c3ffdef1392d2..f51ffa7284d17 100644
--- a/chrome/browser/background_mode_manager.cc
+++ b/chrome/browser/background_mode_manager.cc
@@ -129,7 +129,7 @@ void BackgroundModeManager::Observe(NotificationType type,
       break;
     case NotificationType::EXTENSION_UNLOADED:
       if (BackgroundApplicationListModel::IsBackgroundApp(
-              *Details<Extension>(details).ptr())) {
+              *Details<UnloadedExtensionInfo>(details)->extension)) {
         OnBackgroundAppUnloaded();
       }
       break;
@@ -377,4 +377,3 @@ bool BackgroundModeManager::IsBackgroundModeEnabled(
 
   return background_mode_enabled;
 }
-
diff --git a/chrome/browser/background_page_tracker.cc b/chrome/browser/background_page_tracker.cc
index faf12f6cee67f..7bc4c4f27f3cb 100644
--- a/chrome/browser/background_page_tracker.cc
+++ b/chrome/browser/background_page_tracker.cc
@@ -165,7 +165,7 @@ void BackgroundPageTracker::Observe(NotificationType type,
       break;
     }
     case NotificationType::EXTENSION_UNLOADED: {
-      std::string id = Details<const Extension>(details)->id();
+      std::string id = Details<UnloadedExtensionInfo>(details)->extension->id();
       OnExtensionUnloaded(id);
       break;
     }
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index 6f29447014552..817884805c762 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -183,7 +183,7 @@ void ExtensionBrowserTest::ReloadExtension(const std::string& extension_id) {
 
 void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) {
   ExtensionService* service = browser()->profile()->GetExtensionService();
-  service->UnloadExtension(extension_id);
+  service->UnloadExtension(extension_id, UnloadedExtensionInfo::DISABLE);
 }
 
 void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) {
diff --git a/chrome/browser/extensions/extension_disabled_infobar_delegate.cc b/chrome/browser/extensions/extension_disabled_infobar_delegate.cc
index 740611765fef5..28e07092ce66f 100644
--- a/chrome/browser/extensions/extension_disabled_infobar_delegate.cc
+++ b/chrome/browser/extensions/extension_disabled_infobar_delegate.cc
@@ -70,7 +70,7 @@ class ExtensionDisabledInfobarDelegate
     // The user might re-enable the extension in other ways, so watch for that.
     registrar_.Add(this, NotificationType::EXTENSION_LOADED,
                    Source<Profile>(service->profile()));
-    registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
+    registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
                    Source<Profile>(service->profile()));
   }
   virtual ~ExtensionDisabledInfobarDelegate() {
@@ -106,17 +106,24 @@ class ExtensionDisabledInfobarDelegate
                        const NotificationDetails& details) {
     // TODO(mpcomplete): RemoveInfoBar doesn't seem to always result in us
     // getting deleted.
+    const Extension* extension = NULL;
     switch (type.value) {
       case NotificationType::EXTENSION_LOADED:
-      case NotificationType::EXTENSION_UNLOADED_DISABLED: {
-        const Extension* extension = Details<const Extension>(details).ptr();
-        if (extension == extension_)
-          tab_contents_->RemoveInfoBar(this);
+        extension = Details<const Extension>(details).ptr();
+        break;
+      case NotificationType::EXTENSION_UNLOADED: {
+        UnloadedExtensionInfo* info =
+            Details<UnloadedExtensionInfo>(details).ptr();
+        if (info->reason == UnloadedExtensionInfo::DISABLE)
+          extension = info->extension;
         break;
       }
       default:
         NOTREACHED();
+        return;
     }
+    if (extension == extension_)
+      tab_contents_->RemoveInfoBar(this);
   }
 
  private:
diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc
index df34a973a5aa2..1d60390e8a214 100644
--- a/chrome/browser/extensions/extension_host.cc
+++ b/chrome/browser/extensions/extension_host.cc
@@ -265,7 +265,7 @@ void ExtensionHost::Observe(NotificationType type,
       // sent. NULL it out so that dirty pointer issues don't arise in cases
       // when multiple ExtensionHost objects pointing to the same Extension are
       // present.
-      if (extension_ == Details<const Extension>(details).ptr())
+      if (extension_ == Details<UnloadedExtensionInfo>(details)->extension)
         extension_ = NULL;
       break;
     default:
diff --git a/chrome/browser/extensions/extension_infobar_delegate.cc b/chrome/browser/extensions/extension_infobar_delegate.cc
index 3956563c03190..140a82f2654b0 100644
--- a/chrome/browser/extensions/extension_infobar_delegate.cc
+++ b/chrome/browser/extensions/extension_infobar_delegate.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/tab_contents/tab_contents.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/common/extensions/extension.h"
 #include "chrome/common/notification_details.h"
 #include "chrome/common/notification_source.h"
 #include "chrome/common/notification_type.h"
@@ -82,7 +83,8 @@ void ExtensionInfoBarDelegate::Observe(NotificationType type,
       break;
     }
     case NotificationType::EXTENSION_UNLOADED: {
-      const Extension* extension = Details<const Extension>(details).ptr();
+      const Extension* extension =
+          Details<UnloadedExtensionInfo>(details)->extension;
       if (extension_ == extension)
         tab_contents_->RemoveInfoBar(this);
       break;
diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc
index 216648c391a90..50117c47e9175 100644
--- a/chrome/browser/extensions/extension_management_api.cc
+++ b/chrome/browser/extensions/extension_management_api.cc
@@ -267,7 +267,12 @@ void ExtensionManagementEventRouter::Observe(
         Details<UninstalledExtensionInfo>(details).ptr()->extension_id;
     args.Append(Value::CreateStringValue(extension_id));
   } else {
-    const Extension* extension = Details<const Extension>(details).ptr();
+    const Extension* extension = NULL;
+    if (event_name == events::kOnExtensionDisabled) {
+      extension = Details<UnloadedExtensionInfo>(details)->extension;
+    } else {
+      extension = Details<const Extension>(details).ptr();
+    }
     CHECK(extension);
     ExtensionService* service = profile->GetExtensionService();
     bool enabled = service->GetExtensionById(extension->id(), false) != NULL;
diff --git a/chrome/browser/extensions/extension_menu_manager.cc b/chrome/browser/extensions/extension_menu_manager.cc
index 5e2cabd4edbac..0d3c891ac2e71 100644
--- a/chrome/browser/extensions/extension_menu_manager.cc
+++ b/chrome/browser/extensions/extension_menu_manager.cc
@@ -453,7 +453,8 @@ void ExtensionMenuManager::Observe(NotificationType type,
     NOTREACHED();
     return;
   }
-  const Extension* extension = Details<const Extension>(details).ptr();
+  const Extension* extension =
+      Details<UnloadedExtensionInfo>(details)->extension;
   if (ContainsKey(context_items_, extension->id())) {
     RemoveAllContextItems(extension->id());
   }
diff --git a/chrome/browser/extensions/extension_menu_manager_unittest.cc b/chrome/browser/extensions/extension_menu_manager_unittest.cc
index ff225424b7c33..ca844ccd30ace 100644
--- a/chrome/browser/extensions/extension_menu_manager_unittest.cc
+++ b/chrome/browser/extensions/extension_menu_manager_unittest.cc
@@ -322,9 +322,10 @@ TEST_F(ExtensionMenuManagerTest, ExtensionUnloadRemovesMenuItems) {
 
   // Notify that the extension was unloaded, and make sure the right item is
   // gone.
+  UnloadedExtensionInfo details(extension1, UnloadedExtensionInfo::DISABLE);
   notifier->Notify(NotificationType::EXTENSION_UNLOADED,
                    Source<Profile>(NULL),
-                   Details<const Extension>(extension1));
+                   Details<UnloadedExtensionInfo>(&details));
   ASSERT_EQ(NULL, manager_.MenuItems(extension1->id()));
   ASSERT_EQ(1u, manager_.MenuItems(extension2->id())->size());
   ASSERT_TRUE(manager_.GetItemById(id1) == NULL);
diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc
index 6eb248807861e..03e868d96135f 100644
--- a/chrome/browser/extensions/extension_process_manager.cc
+++ b/chrome/browser/extensions/extension_process_manager.cc
@@ -296,7 +296,8 @@ void ExtensionProcessManager::Observe(NotificationType type,
     }
 
     case NotificationType::EXTENSION_UNLOADED: {
-      const Extension* extension = Details<const Extension>(details).ptr();
+      const Extension* extension =
+          Details<UnloadedExtensionInfo>(details)->extension;
       for (ExtensionHostSet::iterator iter = background_hosts_.begin();
            iter != background_hosts_.end(); ++iter) {
         ExtensionHost* host = *iter;
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index d36464896dbdc..bb24e1e51bfda 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -893,7 +893,7 @@ void ExtensionService::UninstallExtension(const std::string& extension_id,
 
   // Unload before doing more cleanup to ensure that nothing is hanging on to
   // any of these resources.
-  UnloadExtension(extension_id);
+  UnloadExtension(extension_id, UnloadedExtensionInfo::UNINSTALL);
 
   extension_prefs_->OnExtensionUninstalled(extension_id_copy, location,
                                            external_uninstall);
@@ -971,7 +971,7 @@ void ExtensionService::DisableExtension(const std::string& extension_id) {
   ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
       extension->GetChromeURLOverrides());
 
-  NotifyExtensionUnloaded(extension);
+  NotifyExtensionUnloaded(extension, UnloadedExtensionInfo::DISABLE);
   UpdateActiveExtensionsInCrashReporter();
 }
 
@@ -1238,11 +1238,13 @@ void ExtensionService::NotifyExtensionLoaded(const Extension* extension) {
       Details<const Extension>(extension));
 }
 
-void ExtensionService::NotifyExtensionUnloaded(const Extension* extension) {
+void ExtensionService::NotifyExtensionUnloaded(
+    const Extension* extension, UnloadedExtensionInfo::Reason reason) {
+  UnloadedExtensionInfo details(extension, reason);
   NotificationService::current()->Notify(
       NotificationType::EXTENSION_UNLOADED,
       Source<Profile>(profile_),
-      Details<const Extension>(extension));
+      Details<UnloadedExtensionInfo>(&details));
 
   if (profile_) {
     profile_->UnregisterExtensionWithRequestContexts(extension);
@@ -1372,7 +1374,7 @@ void ExtensionService::UpdateExtensionBlacklist(
   // UnloadExtension will change the extensions_ list. So, we should
   // call it outside the iterator loop.
   for (unsigned int i = 0; i < to_be_removed.size(); ++i) {
-    UnloadExtension(to_be_removed[i]);
+    UnloadExtension(to_be_removed[i], UnloadedExtensionInfo::DISABLE);
   }
 }
 
@@ -1399,7 +1401,7 @@ void ExtensionService::CheckAdminBlacklist() {
   // UnloadExtension will change the extensions_ list. So, we should
   // call it outside the iterator loop.
   for (unsigned int i = 0; i < to_be_removed.size(); ++i)
-    UnloadExtension(to_be_removed[i]);
+    UnloadExtension(to_be_removed[i], UnloadedExtensionInfo::DISABLE);
 }
 
 bool ExtensionService::IsIncognitoEnabled(const Extension* extension) {
@@ -1421,7 +1423,7 @@ void ExtensionService::SetIsIncognitoEnabled(const Extension* extension,
   bool is_enabled = std::find(extensions_.begin(), extensions_.end(),
                               extension) != extensions_.end();
   if (is_enabled) {
-    NotifyExtensionUnloaded(extension);
+    NotifyExtensionUnloaded(extension, UnloadedExtensionInfo::DISABLE);
     NotifyExtensionLoaded(extension);
   }
 }
@@ -1480,7 +1482,9 @@ void ExtensionService::UpdateExternalPolicyExtensionProvider() {
               new RefCountedList(list_copy))));
 }
 
-void ExtensionService::UnloadExtension(const std::string& extension_id) {
+void ExtensionService::UnloadExtension(
+    const std::string& extension_id,
+    UnloadedExtensionInfo::Reason reason) {
   // Make sure the extension gets deleted after we return from this function.
   scoped_refptr<const Extension> extension(
       GetExtensionByIdInternal(extension_id, true, true));
@@ -1507,11 +1511,13 @@ void ExtensionService::UnloadExtension(const std::string& extension_id) {
                                            disabled_extensions_.end(),
                                            extension.get());
   if (iter != disabled_extensions_.end()) {
+    UnloadedExtensionInfo details(extension, reason);
+    details.already_disabled = true;
     disabled_extensions_.erase(iter);
     NotificationService::current()->Notify(
-        NotificationType::EXTENSION_UNLOADED_DISABLED,
+        NotificationType::EXTENSION_UNLOADED,
         Source<Profile>(profile_),
-        Details<const Extension>(extension.get()));
+        Details<UnloadedExtensionInfo>(&details));
     return;
   }
 
@@ -1520,7 +1526,7 @@ void ExtensionService::UnloadExtension(const std::string& extension_id) {
   // Remove the extension from our list.
   extensions_.erase(iter);
 
-  NotifyExtensionUnloaded(extension.get());
+  NotifyExtensionUnloaded(extension.get(), reason);
   UpdateActiveExtensionsInCrashReporter();
 }
 
@@ -1706,7 +1712,7 @@ void ExtensionService::DisableIfPrivilegeIncrease(const Extension* extension) {
 
     // To upgrade an extension in place, unload the old one and
     // then load the new one.
-    UnloadExtension(old->id());
+    UnloadExtension(old->id(), UnloadedExtensionInfo::UPDATE);
     old = NULL;
   }
 
@@ -1996,8 +2002,10 @@ void ExtensionService::Observe(NotificationType type,
       // We do it in a PostTask so that other handlers of this notification will
       // still have access to the Extension and ExtensionHost.
       MessageLoop::current()->PostTask(FROM_HERE,
-          NewRunnableMethod(this, &ExtensionService::UnloadExtension,
-                            host->extension()->id()));
+          NewRunnableMethod(this,
+                            &ExtensionService::UnloadExtension,
+                            host->extension()->id(),
+                            UnloadedExtensionInfo::DISABLE));
       break;
     }
 
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 2ae45efdc00f5..9e4206c5ec52c 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -302,7 +302,8 @@ class ExtensionService
   void UpdateExternalPolicyExtensionProvider();
 
   // Unload the specified extension.
-  void UnloadExtension(const std::string& extension_id);
+  void UnloadExtension(const std::string& extension_id,
+                       UnloadedExtensionInfo::Reason reason);
 
   // Unload all extensions. This is currently only called on shutdown, and
   // does not send notifications.
@@ -480,7 +481,8 @@ class ExtensionService
   void NotifyExtensionLoaded(const Extension* extension);
 
   // Handles sending notification that |extension| was unloaded.
-  void NotifyExtensionUnloaded(const Extension* extension);
+  void NotifyExtensionUnloaded(const Extension* extension,
+                               UnloadedExtensionInfo::Reason reason);
 
   // Helper that updates the active extension list used for crash reporting.
   void UpdateActiveExtensionsInCrashReporter();
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 5d47e8800b8a3..7f052b68590f6 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -448,7 +448,8 @@ class ExtensionServiceTest
       }
 
       case NotificationType::EXTENSION_UNLOADED: {
-        const Extension* e = Details<const Extension>(details).ptr();
+        const Extension* e =
+            Details<UnloadedExtensionInfo>(details)->extension;
         unloaded_id_ = e->id();
         ExtensionList::iterator i =
             std::find(loaded_.begin(), loaded_.end(), e);
diff --git a/chrome/browser/extensions/extension_toolbar_model.cc b/chrome/browser/extensions/extension_toolbar_model.cc
index 8a62b5b1a4ebc..3979cbffb0509 100644
--- a/chrome/browser/extensions/extension_toolbar_model.cc
+++ b/chrome/browser/extensions/extension_toolbar_model.cc
@@ -22,8 +22,6 @@ ExtensionToolbarModel::ExtensionToolbarModel(ExtensionService* service)
                  Source<Profile>(service_->profile()));
   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
                  Source<Profile>(service_->profile()));
-  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
-                 Source<Profile>(service_->profile()));
   registrar_.Add(this, NotificationType::EXTENSIONS_READY,
                  Source<Profile>(service_->profile()));
   registrar_.Add(this,
@@ -96,7 +94,12 @@ void ExtensionToolbarModel::Observe(NotificationType type,
   if (!service_->is_ready())
     return;
 
-  const Extension* extension = Details<const Extension>(details).ptr();
+  const Extension* extension = NULL;
+  if (type == NotificationType::EXTENSION_UNLOADED) {
+    extension = Details<UnloadedExtensionInfo>(details)->extension;
+  } else {
+    extension = Details<const Extension>(details).ptr();
+  }
   if (type == NotificationType::EXTENSION_LOADED) {
     // We don't want to add the same extension twice. It may have already been
     // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
@@ -107,8 +110,7 @@ void ExtensionToolbarModel::Observe(NotificationType type,
     }
     if (service_->GetBrowserActionVisibility(extension))
       AddExtension(extension);
-  } else if (type == NotificationType::EXTENSION_UNLOADED ||
-             type == NotificationType::EXTENSION_UNLOADED_DISABLED) {
+  } else if (type == NotificationType::EXTENSION_UNLOADED) {
     RemoveExtension(extension);
   } else if (type ==
              NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED) {
diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc
index 24d7161ea7bb2..5d3a05d2187d8 100644
--- a/chrome/browser/extensions/extensions_ui.cc
+++ b/chrome/browser/extensions/extensions_ui.cc
@@ -391,8 +391,6 @@ void ExtensionsDOMHandler::OnIconsLoaded(DictionaryValue* json) {
       NotificationService::AllSources());
   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
       NotificationService::AllSources());
-  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
-      NotificationService::AllSources());
   registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
       NotificationService::AllSources());
   registrar_.Add(this, NotificationType::EXTENSION_FUNCTION_DISPATCHER_CREATED,
@@ -711,7 +709,6 @@ void ExtensionsDOMHandler::Observe(NotificationType type,
     case NotificationType::EXTENSION_LOADED:
     case NotificationType::EXTENSION_PROCESS_CREATED:
     case NotificationType::EXTENSION_UNLOADED:
-    case NotificationType::EXTENSION_UNLOADED_DISABLED:
     case NotificationType::EXTENSION_UPDATE_DISABLED:
     case NotificationType::EXTENSION_FUNCTION_DISPATCHER_CREATED:
     case NotificationType::EXTENSION_FUNCTION_DISPATCHER_DESTROYED:
diff --git a/chrome/browser/extensions/image_loading_tracker.cc b/chrome/browser/extensions/image_loading_tracker.cc
index 496c3ad03b488..873dcaaed5c4c 100644
--- a/chrome/browser/extensions/image_loading_tracker.cc
+++ b/chrome/browser/extensions/image_loading_tracker.cc
@@ -123,8 +123,6 @@ ImageLoadingTracker::ImageLoadingTracker(Observer* observer)
       next_id_(0) {
   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
                  NotificationService::AllSources());
-  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
-                 NotificationService::AllSources());
 }
 
 ImageLoadingTracker::~ImageLoadingTracker() {
@@ -184,10 +182,10 @@ void ImageLoadingTracker::OnImageLoaded(
 void ImageLoadingTracker::Observe(NotificationType type,
                                   const NotificationSource& source,
                                   const NotificationDetails& details) {
-  DCHECK(type == NotificationType::EXTENSION_UNLOADED ||
-         type == NotificationType::EXTENSION_UNLOADED_DISABLED);
+  DCHECK(type == NotificationType::EXTENSION_UNLOADED);
 
-  const Extension* extension = Details<const Extension>(details).ptr();
+  const Extension* extension =
+      Details<UnloadedExtensionInfo>(details)->extension;
 
   // Remove all entries in the load_map_ referencing the extension. This ensures
   // we don't attempt to cache the image when the load completes.
diff --git a/chrome/browser/extensions/image_loading_tracker_unittest.cc b/chrome/browser/extensions/image_loading_tracker_unittest.cc
index 57b5268b224b8..4c9734f7b5b34 100644
--- a/chrome/browser/extensions/image_loading_tracker_unittest.cc
+++ b/chrome/browser/extensions/image_loading_tracker_unittest.cc
@@ -160,10 +160,12 @@ TEST_F(ImageLoadingTrackerTest, DeleteExtensionWhileWaitingForCache) {
   EXPECT_EQ(0, image_loaded_count());
 
   // Send out notification the extension was uninstalled.
+  UnloadedExtensionInfo details(extension.get(),
+                                UnloadedExtensionInfo::UNINSTALL);
   NotificationService::current()->Notify(
       NotificationType::EXTENSION_UNLOADED,
       NotificationService::AllSources(),
-      Details<const Extension>(extension.get()));
+      Details<UnloadedExtensionInfo>(&details));
 
   // Chuck the extension, that way if anyone tries to access it we should crash
   // or get valgrind errors.
diff --git a/chrome/browser/extensions/user_script_listener.cc b/chrome/browser/extensions/user_script_listener.cc
index 0123290cf9d31..43965f0060b1a 100644
--- a/chrome/browser/extensions/user_script_listener.cc
+++ b/chrome/browser/extensions/user_script_listener.cc
@@ -136,7 +136,7 @@ void UserScriptListener::Observe(NotificationType type,
 
     case NotificationType::EXTENSION_UNLOADED: {
       const Extension* unloaded_extension =
-          Details<const Extension>(details).ptr();
+          Details<UnloadedExtensionInfo>(details)->extension;
       if (unloaded_extension->content_scripts().empty())
         return;  // no patterns to delete for this extension.
 
diff --git a/chrome/browser/extensions/user_script_listener_unittest.cc b/chrome/browser/extensions/user_script_listener_unittest.cc
index c1a673474473c..cd40fd5740d3c 100644
--- a/chrome/browser/extensions/user_script_listener_unittest.cc
+++ b/chrome/browser/extensions/user_script_listener_unittest.cc
@@ -164,7 +164,8 @@ class UserScriptListenerTest
 
   void UnloadTestExtension() {
     ASSERT_FALSE(service_->extensions()->empty());
-    service_->UnloadExtension(service_->extensions()->at(0)->id());
+    service_->UnloadExtension(service_->extensions()->at(0)->id(),
+                              UnloadedExtensionInfo::DISABLE);
   }
 
   scoped_refptr<UserScriptListener> listener_;
diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc
index 3f448b9141891..2af102e9f0907 100644
--- a/chrome/browser/extensions/user_script_master.cc
+++ b/chrome/browser/extensions/user_script_master.cc
@@ -356,7 +356,8 @@ void UserScriptMaster::Observe(NotificationType type,
     }
     case NotificationType::EXTENSION_UNLOADED: {
       // Remove any content scripts.
-      const Extension* extension = Details<const Extension>(details).ptr();
+      const Extension* extension =
+          Details<UnloadedExtensionInfo>(details)->extension;
       UserScriptList new_lone_scripts;
       for (UserScriptList::iterator iter = lone_scripts_.begin();
            iter != lone_scripts_.end(); ++iter) {
diff --git a/chrome/browser/gtk/extension_installed_bubble_gtk.cc b/chrome/browser/gtk/extension_installed_bubble_gtk.cc
index 881551bbc4b10..9ca5854cec02e 100644
--- a/chrome/browser/gtk/extension_installed_bubble_gtk.cc
+++ b/chrome/browser/gtk/extension_installed_bubble_gtk.cc
@@ -95,7 +95,8 @@ void ExtensionInstalledBubbleGtk::Observe(NotificationType type,
           &ExtensionInstalledBubbleGtk::ShowInternal));
     }
   } else if (type == NotificationType::EXTENSION_UNLOADED) {
-    const Extension* extension = Details<const Extension>(details).ptr();
+    const Extension* extension =
+        Details<UnloadedExtensionInfo>(details)->extension;
     if (extension == extension_)
       extension_ = NULL;
   } else {
diff --git a/chrome/browser/notifications/desktop_notification_service.cc b/chrome/browser/notifications/desktop_notification_service.cc
index 2badc861dda7b..afc01c7325578 100644
--- a/chrome/browser/notifications/desktop_notification_service.cc
+++ b/chrome/browser/notifications/desktop_notification_service.cc
@@ -317,7 +317,8 @@ void DesktopNotificationService::Observe(NotificationType type,
   } else if (NotificationType::EXTENSION_UNLOADED == type) {
     // Remove all notifications currently shown or queued by the extension
     // which was unloaded.
-    Extension* extension = Details<Extension>(details).ptr();
+    const Extension* extension =
+        Details<UnloadedExtensionInfo>(details)->extension;
     if (extension)
       ui_manager_->CancelAllBySourceOrigin(extension->url());
   } else if (NotificationType::PROFILE_DESTROYED == type) {
diff --git a/chrome/browser/plugin_service.cc b/chrome/browser/plugin_service.cc
index 50677f75848a0..ee7cd8115827a 100644
--- a/chrome/browser/plugin_service.cc
+++ b/chrome/browser/plugin_service.cc
@@ -353,7 +353,8 @@ void PluginService::Observe(NotificationType type,
     }
 
     case NotificationType::EXTENSION_UNLOADED: {
-      const Extension* extension = Details<const Extension>(details).ptr();
+      const Extension* extension =
+          Details<UnloadedExtensionInfo>(details)->extension;
       bool plugins_changed = false;
       for (size_t i = 0; i < extension->plugins().size(); ++i) {
         const Extension::PluginInfo& plugin = extension->plugins()[i];
diff --git a/chrome/browser/sync/glue/extension_change_processor.cc b/chrome/browser/sync/glue/extension_change_processor.cc
index fc28a62c0c249..8997b20224221 100644
--- a/chrome/browser/sync/glue/extension_change_processor.cc
+++ b/chrome/browser/sync/glue/extension_change_processor.cc
@@ -49,8 +49,7 @@ void ExtensionChangeProcessor::Observe(NotificationType type,
       (type != NotificationType::EXTENSION_UNINSTALLED) &&
       (type != NotificationType::EXTENSION_LOADED) &&
       (type != NotificationType::EXTENSION_UPDATE_DISABLED) &&
-      (type != NotificationType::EXTENSION_UNLOADED) &&
-      (type != NotificationType::EXTENSION_UNLOADED_DISABLED)) {
+      (type != NotificationType::EXTENSION_UNLOADED)) {
     LOG(DFATAL) << "Received unexpected notification of type "
                 << type.value;
     return;
@@ -69,7 +68,12 @@ void ExtensionChangeProcessor::Observe(NotificationType type,
       RemoveServerData(traits_, id, profile_->GetProfileSyncService());
     }
   } else {
-    const Extension* extension = Details<const Extension>(details).ptr();
+    const Extension* extension = NULL;
+    if (type == NotificationType::EXTENSION_UNLOADED) {
+      extension = Details<UnloadedExtensionInfo>(details)->extension;
+    } else {
+      extension = Details<const Extension>(details).ptr();
+    }
     CHECK(extension);
     VLOG(1) << "Updating server data for extension " << extension->id()
             << " (notification type = " << type.value << ")";
@@ -177,9 +181,6 @@ void ExtensionChangeProcessor::StartObserving() {
   notification_registrar_.Add(
       this, NotificationType::EXTENSION_UNLOADED,
       Source<Profile>(profile_));
-  notification_registrar_.Add(
-      this, NotificationType::EXTENSION_UNLOADED_DISABLED,
-      Source<Profile>(profile_));
 }
 
 void ExtensionChangeProcessor::StopObserving() {
diff --git a/chrome/browser/sync/glue/theme_change_processor.cc b/chrome/browser/sync/glue/theme_change_processor.cc
index 0807732f96d96..58f25c2d861bc 100644
--- a/chrome/browser/sync/glue/theme_change_processor.cc
+++ b/chrome/browser/sync/glue/theme_change_processor.cc
@@ -39,7 +39,12 @@ void ThemeChangeProcessor::Observe(NotificationType type,
                                    const NotificationDetails& details) {
   DCHECK(running());
   DCHECK(profile_);
-  const Extension* extension = Details<const Extension>(details).ptr();
+  const Extension* extension = NULL;
+  if (type == NotificationType::EXTENSION_UNLOADED) {
+    extension = Details<UnloadedExtensionInfo>(details)->extension;
+  } else {
+    extension = Details<const Extension>(details).ptr();
+  }
   std::string current_or_future_theme_id =
       profile_->GetThemeProvider()->GetThemeID();
   const Extension* current_theme = profile_->GetTheme();
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
index bf96e491fa763..3dba1a856dbbe 100644
--- a/chrome/browser/tab_contents/tab_contents.cc
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -422,8 +422,6 @@ TabContents::TabContents(Profile* profile,
                  NotificationService::AllSources());
   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
                  NotificationService::AllSources());
-  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
-                 NotificationService::AllSources());
 
   // Listen for Google URL changes
   registrar_.Add(this, NotificationType::GOOGLE_URL_UPDATED,
@@ -3254,7 +3252,6 @@ void TabContents::Observe(NotificationType type,
       break;
 
     case NotificationType::EXTENSION_UNLOADED:
-    case NotificationType::EXTENSION_UNLOADED_DISABLED:
       break;
 
     case NotificationType::GOOGLE_URL_UPDATED:
diff --git a/chrome/browser/tabs/tab_strip_model.cc b/chrome/browser/tabs/tab_strip_model.cc
index c79b10e242f76..7d939215fd6f4 100644
--- a/chrome/browser/tabs/tab_strip_model.cc
+++ b/chrome/browser/tabs/tab_strip_model.cc
@@ -778,7 +778,8 @@ void TabStripModel::Observe(NotificationType type,
     }
 
     case NotificationType::EXTENSION_UNLOADED: {
-      const Extension* extension = Details<const Extension>(details).ptr();
+      const Extension* extension =
+          Details<UnloadedExtensionInfo>(details)->extension;
       // Iterate backwards as we may remove items while iterating.
       for (int i = count() - 1; i >= 0; i--) {
         TabContentsWrapper* contents = GetTabContentsAt(i);
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 44c28b6d95def..9d0353c7f104c 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -213,8 +213,6 @@ Browser::Browser(Type type, Profile* profile)
                  NotificationService::AllSources());
   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
                  NotificationService::AllSources());
-  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
-                 NotificationService::AllSources());
   registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED,
                  NotificationService::AllSources());
   registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
@@ -3212,12 +3210,12 @@ void Browser::Observe(NotificationType type,
       break;
     }
 
-    case NotificationType::EXTENSION_UNLOADED:
-    case NotificationType::EXTENSION_UNLOADED_DISABLED: {
+    case NotificationType::EXTENSION_UNLOADED: {
       window()->GetLocationBar()->UpdatePageActions();
 
       // Close any tabs from the unloaded extension.
-      const Extension* extension = Details<const Extension>(details).ptr();
+      const Extension* extension =
+          Details<UnloadedExtensionInfo>(details)->extension;
       TabStripModel* model = tab_handler_->GetTabStripModel();
       for (int i = model->count() - 1; i >= 0; --i) {
         TabContents* tc = model->GetTabContentsAt(i)->tab_contents();
diff --git a/chrome/browser/ui/views/extensions/extension_installed_bubble.cc b/chrome/browser/ui/views/extensions/extension_installed_bubble.cc
index bbac7846c5d60..1b549d39dd9da 100644
--- a/chrome/browser/ui/views/extensions/extension_installed_bubble.cc
+++ b/chrome/browser/ui/views/extensions/extension_installed_bubble.cc
@@ -268,7 +268,8 @@ void ExtensionInstalledBubble::Observe(NotificationType type,
           &ExtensionInstalledBubble::ShowInternal));
     }
   } else if (type == NotificationType::EXTENSION_UNLOADED) {
-    const Extension* extension = Details<const Extension>(details).ptr();
+    const Extension* extension =
+        Details<UnloadedExtensionInfo>(details)->extension;
     if (extension == extension_)
       extension_ = NULL;
   } else {
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 7bd2c27c4da90..bdbb33a356d17 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -2310,3 +2310,11 @@ UninstalledExtensionInfo::UninstalledExtensionInfo(
       update_url(extension.update_url()) {}
 
 UninstalledExtensionInfo::~UninstalledExtensionInfo() {}
+
+
+UnloadedExtensionInfo::UnloadedExtensionInfo(
+    const Extension* extension,
+    Reason reason)
+  : reason(reason),
+    already_disabled(false),
+    extension(extension) {}
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index a4cf829a55f35..4c82fd68d321d 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -744,4 +744,22 @@ struct UninstalledExtensionInfo {
   GURL update_url;
 };
 
+struct UnloadedExtensionInfo {
+  enum Reason {
+    DISABLE,    // The extension is being disabled.
+    UPDATE,     // The extension is being updated to a newer version.
+    UNINSTALL,  // The extension is being uninstalled.
+  };
+
+  Reason reason;
+
+  // Was the extension already disabled?
+  bool already_disabled;
+
+  // The extension being unloaded - this should always be non-NULL.
+  const Extension* extension;
+
+  UnloadedExtensionInfo(const Extension* extension, Reason reason);
+};
+
 #endif  // CHROME_COMMON_EXTENSIONS_EXTENSION_H_
diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h
index edbc3711a85cb..e7484aae84575 100644
--- a/chrome/common/notification_type.h
+++ b/chrome/common/notification_type.h
@@ -854,16 +854,13 @@ class NotificationType {
     EXTENSION_UNINSTALLED,
 
     // Sent when an extension is unloaded. This happens when an extension is
-    // uninstalled or disabled. The details are an Extension, and the source is
-    // a Profile.
+    // uninstalled or disabled. The details are an UnloadedExtensionInfo, and
+    // the source is a Profile.
     //
     // Note that when this notification is sent, ExtensionService has already
     // removed the extension from its internal state.
     EXTENSION_UNLOADED,
 
-    // Same as above, but for a disabled extension.
-    EXTENSION_UNLOADED_DISABLED,
-
     // Sent when an extension has updated its user scripts. The details are an
     // Extension, and the source is a Profile.
     EXTENSION_USER_SCRIPTS_UPDATED,
-- 
GitLab