| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/chromeos/extensions/install_limiter.h" |
| |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/file_util.h" |
| #include "base/threading/sequenced_worker_pool.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/chromeos/extensions/install_limiter_factory.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/notification_details.h" |
| #include "content/public/browser/notification_source.h" |
| |
| using content::BrowserThread; |
| |
| namespace { |
| |
| int64 GetFileSizeOnBlockingPool(const base::FilePath& file) { |
| DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| |
| // Get file size. In case of error, sets 0 as file size to let the installer |
| // run and fail. |
| int64 size; |
| return file_util::GetFileSize(file, &size) ? size : 0; |
| } |
| |
| } // namespace |
| |
| namespace extensions { |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // InstallLimiter::DeferredInstall |
| |
| InstallLimiter::DeferredInstall::DeferredInstall( |
| const scoped_refptr<CrxInstaller>& installer, |
| const base::FilePath& path) |
| : installer(installer), |
| path(path) { |
| } |
| |
| InstallLimiter::DeferredInstall::~DeferredInstall() { |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // InstallLimiter |
| |
| InstallLimiter* InstallLimiter::Get(Profile* profile) { |
| return InstallLimiterFactory::GetForProfile(profile); |
| } |
| |
| InstallLimiter::InstallLimiter() : disabled_for_test_(false) { |
| } |
| |
| InstallLimiter::~InstallLimiter() { |
| } |
| |
| void InstallLimiter::DisableForTest() { |
| disabled_for_test_ = true; |
| } |
| |
| void InstallLimiter::Add(const scoped_refptr<CrxInstaller>& installer, |
| const base::FilePath& path) { |
| // No deferred installs when disabled for test. |
| if (disabled_for_test_) { |
| installer->InstallCrx(path); |
| return; |
| } |
| |
| base::PostTaskAndReplyWithResult( |
| BrowserThread::GetBlockingPool(), |
| FROM_HERE, |
| base::Bind(&GetFileSizeOnBlockingPool, path), |
| base::Bind(&InstallLimiter::AddWithSize, AsWeakPtr(), installer, path)); |
| } |
| |
| void InstallLimiter::AddWithSize( |
| const scoped_refptr<CrxInstaller>& installer, |
| const base::FilePath& path, |
| int64 size) { |
| const int64 kBigAppSizeThreshold = 1048576; // 1MB |
| |
| if (size <= kBigAppSizeThreshold) { |
| RunInstall(installer, path); |
| |
| // Stop wait timer and let install notification drive deferred installs. |
| wait_timer_.Stop(); |
| return; |
| } |
| |
| deferred_installs_.push(DeferredInstall(installer, path)); |
| |
| // When there are no running installs, wait a bit before running deferred |
| // installs to allow small app install to take precedence, especially when a |
| // big app is the first one in the list. |
| if (running_installers_.empty() && !wait_timer_.IsRunning()) { |
| const int kMaxWaitTimeInMs = 5000; // 5 seconds. |
| wait_timer_.Start( |
| FROM_HERE, |
| base::TimeDelta::FromMilliseconds(kMaxWaitTimeInMs), |
| this, &InstallLimiter::CheckAndRunDeferrredInstalls); |
| } |
| } |
| |
| void InstallLimiter::CheckAndRunDeferrredInstalls() { |
| if (deferred_installs_.empty() || !running_installers_.empty()) |
| return; |
| |
| const DeferredInstall& deferred = deferred_installs_.front(); |
| RunInstall(deferred.installer, deferred.path); |
| deferred_installs_.pop(); |
| } |
| |
| void InstallLimiter::RunInstall(const scoped_refptr<CrxInstaller>& installer, |
| const base::FilePath& path) { |
| registrar_.Add(this, |
| chrome::NOTIFICATION_CRX_INSTALLER_DONE, |
| content::Source<CrxInstaller>(installer.get())); |
| |
| installer->InstallCrx(path); |
| running_installers_.insert(installer); |
| } |
| |
| void InstallLimiter::Observe(int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| DCHECK_EQ(chrome::NOTIFICATION_CRX_INSTALLER_DONE, type); |
| |
| registrar_.Remove(this, |
| chrome::NOTIFICATION_CRX_INSTALLER_DONE, |
| source); |
| |
| const scoped_refptr<CrxInstaller> installer = |
| content::Source<extensions::CrxInstaller>(source).ptr(); |
| running_installers_.erase(installer); |
| CheckAndRunDeferrredInstalls(); |
| } |
| |
| } // namespace extensions |