New notification sent when extension uninstall is not allowed.

In the event that an extension uninstall is skipped (not allowed)
because that extension is not user-manageable, a new notification is
sent.  The automation hook UninstallExtensionById is revised to handle
this case, and a new PyAuto test is written to exercise this new
functionality (the test attempts to uninstall the WebStore and verifies that
this extension cannot be uninstalled).

BUG=76598
TEST=None.

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80814 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 361e165..9887b4c 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -258,7 +258,7 @@
   }
 
   // This is an external extension that we don't have registered.  Uninstall.
-  UninstallExtension(id, true);
+  UninstallExtension(id, true, NULL);
 }
 
 void ExtensionService::ClearProvidersForTesting() {
@@ -362,6 +362,9 @@
 }
 
 // static
+// This function is used to implement the command-line switch
+// --uninstall-extension.  The LOG statements within this function are used to
+// inform the user if the uninstall cannot be done.
 bool ExtensionService::UninstallExtensionHelper(
     ExtensionService* extensions_service,
     const std::string& extension_id) {
@@ -371,19 +374,21 @@
   if (!extension)
     extension = extensions_service->GetTerminatedExtension(extension_id);
 
-  // We can't call UninstallExtension with an invalid extension ID. We should
-  // not allow an uninstall of a policy-controlled extension.
+  // We can't call UninstallExtension with an invalid extension ID.
   if (!extension) {
     LOG(WARNING) << "Attempted uninstallation of non-existent extension with "
                  << "id: " << extension_id;
     return false;
-  } else if (!Extension::UserMayDisable(extension->location())) {
-    LOG(WARNING) << "Attempted uninstallation of an extension that is non-"
-                 << "usermanagable with id: " << extension_id;
-    return false;
   }
 
-  extensions_service->UninstallExtension(extension_id, false);
+  // The following call to UninstallExtension will not allow an uninstall of a
+  // policy-controlled extension.
+  std::string error;
+  if (!extensions_service->UninstallExtension(extension_id, false, &error)) {
+    LOG(WARNING) << "Cannot uninstall extension with id " << extension_id
+                 << ": " << error;
+    return false;
+  }
 
   return true;
 }
@@ -607,8 +612,9 @@
   }
 }
 
-void ExtensionService::UninstallExtension(const std::string& extension_id,
-                                          bool external_uninstall) {
+bool ExtensionService::UninstallExtension(const std::string& extension_id,
+                                          bool external_uninstall,
+                                          std::string* error) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   const Extension* extension =
@@ -627,8 +633,16 @@
   // Policy change which triggers an uninstall will always set
   // |external_uninstall| to true so this is the only way to uninstall
   // managed extensions.
-  if (!Extension::UserMayDisable(location) && !external_uninstall)
-    return;
+  if (!Extension::UserMayDisable(location) && !external_uninstall) {
+    NotificationService::current()->Notify(
+        NotificationType::EXTENSION_UNINSTALL_NOT_ALLOWED,
+        Source<Profile>(profile_),
+        Details<const Extension>(extension));
+    if (error != NULL) {
+      *error = errors::kCannotUninstallManagedExtension;
+    }
+    return false;
+  }
 
   UninstalledExtensionInfo uninstalled_extension_info(*extension);
 
@@ -667,6 +681,8 @@
       NotificationType::EXTENSION_UNINSTALLED,
       Source<Profile>(profile_),
       Details<UninstalledExtensionInfo>(&uninstalled_extension_info));
+
+  return true;
 }
 
 void ExtensionService::ClearExtensionData(const GURL& extension_url) {