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/files/file_util.h" 11#include "base/threading/sequenced_worker_pool.h" 12#include "chrome/browser/chromeos/extensions/install_limiter_factory.h" 13#include "content/public/browser/browser_thread.h" 14#include "content/public/browser/notification_details.h" 15#include "content/public/browser/notification_source.h" 16#include "extensions/browser/notification_types.h" 17 18using content::BrowserThread; 19 20namespace { 21 22int64 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 33namespace extensions { 34 35//////////////////////////////////////////////////////////////////////////////// 36// InstallLimiter::DeferredInstall 37 38InstallLimiter::DeferredInstall::DeferredInstall( 39 const scoped_refptr<CrxInstaller>& installer, 40 const base::FilePath& path) 41 : installer(installer), 42 path(path) { 43} 44 45InstallLimiter::DeferredInstall::~DeferredInstall() { 46} 47 48//////////////////////////////////////////////////////////////////////////////// 49// InstallLimiter 50 51InstallLimiter* InstallLimiter::Get(Profile* profile) { 52 return InstallLimiterFactory::GetForProfile(profile); 53} 54 55InstallLimiter::InstallLimiter() : disabled_for_test_(false) { 56} 57 58InstallLimiter::~InstallLimiter() { 59} 60 61void InstallLimiter::DisableForTest() { 62 disabled_for_test_ = true; 63} 64 65void 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 80void 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 108void 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 117void InstallLimiter::RunInstall(const scoped_refptr<CrxInstaller>& installer, 118 const base::FilePath& path) { 119 registrar_.Add(this, 120 extensions::NOTIFICATION_CRX_INSTALLER_DONE, 121 content::Source<CrxInstaller>(installer.get())); 122 123 installer->InstallCrx(path); 124 running_installers_.insert(installer); 125} 126 127void InstallLimiter::Observe(int type, 128 const content::NotificationSource& source, 129 const content::NotificationDetails& details) { 130 DCHECK_EQ(extensions::NOTIFICATION_CRX_INSTALLER_DONE, type); 131 132 registrar_.Remove(this, extensions::NOTIFICATION_CRX_INSTALLER_DONE, source); 133 134 const scoped_refptr<CrxInstaller> installer = 135 content::Source<extensions::CrxInstaller>(source).ptr(); 136 running_installers_.erase(installer); 137 CheckAndRunDeferrredInstalls(); 138} 139 140} // namespace extensions 141