Fix shutdown hang due to queued extension installs

Upon signing in to a profile (or login to Chrome OS) sync starts pulling down
extensions and installing them. This creates long tasks on the file thread.
If one of these tasks completes during browser shutdown, it initiates another
installation and more file I/O. This leads to Chrome's shutdown watchdog being
triggered, which on Chrome OS leads to a SIGABRT after 10 seconds.

This is one of our top-crashes on Chrome OS and can be easily triggered by
signing into an account (triggering sync of a bunch of apps) and immediately
signing out.

Fix it by preventing extension installation and updates after the browser has
started shutting down. We make a best-effort attempt to clean up, and on next
run the extension garbage collector cleans things up and sync continues.

BUG=155994
TEST=added to ExtensionServiceTest, existing browser_tests and unit_tests


Review URL: https://chromiumcodereview.appspot.com/11233015

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@163407 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 38e5a80..199c57d 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -351,6 +351,7 @@
           new extensions::AppNotificationManager(profile)),
       event_routers_initialized_(false),
       update_once_all_providers_are_ready_(false),
+      browser_terminating_(false),
       app_sync_bundle_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
       extension_sync_bundle_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
       extension_warnings_(profile),
@@ -363,6 +364,8 @@
     extensions_enabled_ = false;
   }
 
+  registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
+                 content::NotificationService::AllBrowserContextsAndSources());
   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
                  content::NotificationService::AllBrowserContextsAndSources());
   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
@@ -605,6 +608,13 @@
                                        const GURL& download_url,
                                        CrxInstaller** out_crx_installer) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  if (browser_terminating_) {
+    LOG(WARNING) << "Skipping UpdateExtension due to browser shutdown";
+    // Leak the temp file at extension_path. We don't want to add to the disk
+    // I/O burden at shutdown, we can't rely on the I/O completing anyway, and
+    // the file is in the OS temp directory which should be cleaned up for us.
+    return false;
+  }
 
   const extensions::PendingExtensionInfo* pending_extension_info =
       pending_extension_manager()->GetById(id);
@@ -2493,6 +2503,12 @@
                                const content::NotificationSource& source,
                                const content::NotificationDetails& details) {
   switch (type) {
+    case chrome::NOTIFICATION_APP_TERMINATING:
+      // Shutdown has started. Don't start any more extension installs.
+      // (We cannot use ExtensionService::Shutdown() for this because it
+      // happens too late in browser teardown.)
+      browser_terminating_ = true;
+      break;
     case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED: {
       if (profile_ !=
           content::Source<Profile>(source).ptr()->GetOriginalProfile()) {