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