Home | History | Annotate | Download | only in extensions
      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 
     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                  extensions::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(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