1 // Copyright (c) 2013 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. 4 5 #include "chrome/browser/chromeos/extensions/install_limiter.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/file_util.h" 11 #include "base/threading/sequenced_worker_pool.h" 12 #include "chrome/browser/chrome_notification_types.h" 13 #include "chrome/browser/chromeos/extensions/install_limiter_factory.h" 14 #include "content/public/browser/browser_thread.h" 15 #include "content/public/browser/notification_details.h" 16 #include "content/public/browser/notification_source.h" 17 18 using content::BrowserThread; 19 20 namespace { 21 22 int64 GetFileSizeOnBlockingPool(const base::FilePath& file) { 23 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 24 25 // Get file size. In case of error, sets 0 as file size to let the installer 26 // run and fail. 27 int64 size; 28 return base::GetFileSize(file, &size) ? size : 0; 29 } 30 31 } // namespace 32 33 namespace extensions { 34 35 //////////////////////////////////////////////////////////////////////////////// 36 // InstallLimiter::DeferredInstall 37 38 InstallLimiter::DeferredInstall::DeferredInstall( 39 const scoped_refptr<CrxInstaller>& installer, 40 const base::FilePath& path) 41 : installer(installer), 42 path(path) { 43 } 44 45 InstallLimiter::DeferredInstall::~DeferredInstall() { 46 } 47 48 //////////////////////////////////////////////////////////////////////////////// 49 // InstallLimiter 50 51 InstallLimiter* InstallLimiter::Get(Profile* profile) { 52 return InstallLimiterFactory::GetForProfile(profile); 53 } 54 55 InstallLimiter::InstallLimiter() : disabled_for_test_(false) { 56 } 57 58 InstallLimiter::~InstallLimiter() { 59 } 60 61 void InstallLimiter::DisableForTest() { 62 disabled_for_test_ = true; 63 } 64 65 void InstallLimiter::Add(const scoped_refptr<CrxInstaller>& installer, 66 const base::FilePath& path) { 67 // No deferred installs when disabled for test. 68 if (disabled_for_test_) { 69 installer->InstallCrx(path); 70 return; 71 } 72 73 base::PostTaskAndReplyWithResult( 74 BrowserThread::GetBlockingPool(), 75 FROM_HERE, 76 base::Bind(&GetFileSizeOnBlockingPool, path), 77 base::Bind(&InstallLimiter::AddWithSize, AsWeakPtr(), installer, path)); 78 } 79 80 void InstallLimiter::AddWithSize( 81 const scoped_refptr<CrxInstaller>& installer, 82 const base::FilePath& path, 83 int64 size) { 84 const int64 kBigAppSizeThreshold = 1048576; // 1MB 85 86 if (size <= kBigAppSizeThreshold) { 87 RunInstall(installer, path); 88 89 // Stop wait timer and let install notification drive deferred installs. 90 wait_timer_.Stop(); 91 return; 92 } 93 94 deferred_installs_.push(DeferredInstall(installer, path)); 95 96 // When there are no running installs, wait a bit before running deferred 97 // installs to allow small app install to take precedence, especially when a 98 // big app is the first one in the list. 99 if (running_installers_.empty() && !wait_timer_.IsRunning()) { 100 const int kMaxWaitTimeInMs = 5000; // 5 seconds. 101 wait_timer_.Start( 102 FROM_HERE, 103 base::TimeDelta::FromMilliseconds(kMaxWaitTimeInMs), 104 this, &InstallLimiter::CheckAndRunDeferrredInstalls); 105 } 106 } 107 108 void InstallLimiter::CheckAndRunDeferrredInstalls() { 109 if (deferred_installs_.empty() || !running_installers_.empty()) 110 return; 111 112 const DeferredInstall& deferred = deferred_installs_.front(); 113 RunInstall(deferred.installer, deferred.path); 114 deferred_installs_.pop(); 115 } 116 117 void InstallLimiter::RunInstall(const scoped_refptr<CrxInstaller>& installer, 118 const base::FilePath& path) { 119 registrar_.Add(this, 120 chrome::NOTIFICATION_CRX_INSTALLER_DONE, 121 content::Source<CrxInstaller>(installer.get())); 122 123 installer->InstallCrx(path); 124 running_installers_.insert(installer); 125 } 126 127 void InstallLimiter::Observe(int type, 128 const content::NotificationSource& source, 129 const content::NotificationDetails& details) { 130 DCHECK_EQ(chrome::NOTIFICATION_CRX_INSTALLER_DONE, type); 131 132 registrar_.Remove(this, 133 chrome::NOTIFICATION_CRX_INSTALLER_DONE, 134 source); 135 136 const scoped_refptr<CrxInstaller> installer = 137 content::Source<extensions::CrxInstaller>(source).ptr(); 138 running_installers_.erase(installer); 139 CheckAndRunDeferrredInstalls(); 140 } 141 142 } // namespace extensions 143