| Index: chrome/browser/component_updater/pnacl/pnacl_component_installer.cc
|
| diff --git a/chrome/browser/component_updater/pnacl/pnacl_component_installer.cc b/chrome/browser/component_updater/pnacl/pnacl_component_installer.cc
|
| index d6ab1a0b44046a8311c17b4642b30245f554da7f..32211f81a80d55aa03a88c4f60e4c106afb8d834 100644
|
| --- a/chrome/browser/component_updater/pnacl/pnacl_component_installer.cc
|
| +++ b/chrome/browser/component_updater/pnacl/pnacl_component_installer.cc
|
| @@ -4,8 +4,11 @@
|
|
|
| #include "chrome/browser/component_updater/pnacl/pnacl_component_installer.h"
|
|
|
| +#include <list>
|
| +
|
| #include "base/base_paths.h"
|
| #include "base/bind.h"
|
| +#include "base/callback.h"
|
| #include "base/compiler_specific.h"
|
| #include "base/file_path.h"
|
| #include "base/file_util.h"
|
| @@ -25,10 +28,9 @@ using content::BrowserThread;
|
|
|
| namespace {
|
|
|
| -// If PNaCl isn't installed yet, but a user is running chrome with
|
| -// --enable-pnacl, this is the amount of time to wait before starting
|
| -// a background install.
|
| -const int kInitialDelaySeconds = 10;
|
| +// Time in seconds to wait for CheckUpdatesForPnacl to complete
|
| +// before considering the update failed.
|
| +int kPnaclInstallerTimeout = 45;
|
|
|
| // One of the Pnacl component files, for checking that expected files exist.
|
| // TODO(jvoung): perhaps replace this with a list of the expected files in the
|
| @@ -72,7 +74,7 @@ std::string SanitizeForPath(const std::string& input) {
|
| }
|
|
|
| // Set the component's hash to the arch-specific PNaCl package.
|
| -void SetPnaclHash(CrxComponent* component) {
|
| +void SetPnaclHash(std::vector<uint8>* pk_hash) {
|
| #if defined(ARCH_CPU_X86_FAMILY)
|
| // Define both x86_32 and x86_64, and choose below.
|
| static const uint8 x86_sha256_hash[][32] = {
|
| @@ -87,23 +89,23 @@ void SetPnaclHash(CrxComponent* component) {
|
| };
|
|
|
| #if defined(ARCH_CPU_X86_64)
|
| - component->pk_hash.assign(
|
| + pk_hash->assign(
|
| x86_sha256_hash[1],
|
| &x86_sha256_hash[1][sizeof(x86_sha256_hash[1])]);
|
| #elif defined(OS_WIN)
|
| bool x86_64 = (base::win::OSInfo::GetInstance()->wow64_status() ==
|
| base::win::OSInfo::WOW64_ENABLED);
|
| if (x86_64) {
|
| - component->pk_hash.assign(
|
| + pk_hash->assign(
|
| x86_sha256_hash[1],
|
| &x86_sha256_hash[1][sizeof(x86_sha256_hash[1])]);
|
| } else {
|
| - component->pk_hash.assign(
|
| + pk_hash->assign(
|
| x86_sha256_hash[0],
|
| &x86_sha256_hash[0][sizeof(x86_sha256_hash[0])]);
|
| }
|
| #else
|
| - component->pk_hash.assign(
|
| + pk_hash->assign(
|
| x86_sha256_hash[0],
|
| &x86_sha256_hash[0][sizeof(x86_sha256_hash[0])]);
|
| #endif
|
| @@ -114,8 +116,8 @@ void SetPnaclHash(CrxComponent* component) {
|
| 0x62, 0xde, 0x5a, 0x14, 0x14, 0x99, 0xd4, 0xd9, 0x01, 0x85, 0xc6,
|
| 0x9a, 0xd2, 0x51, 0x90, 0xa4, 0xb4, 0x94, 0xbd, 0xb8, 0x8b, 0xe8};
|
|
|
| - component->pk_hash.assign(arm_sha256_hash,
|
| - &arm_sha256_hash[sizeof(arm_sha256_hash)]);
|
| + pk_hash->assign(arm_sha256_hash,
|
| + &arm_sha256_hash[sizeof(arm_sha256_hash)]);
|
| #elif defined(ARCH_CPU_MIPSEL)
|
| // This is a dummy CRX hash for MIPS, so that it will at least compile.
|
| static const uint8 mips32_sha256_hash[] = {
|
| @@ -123,8 +125,8 @@ void SetPnaclHash(CrxComponent* component) {
|
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
|
| - component->pk_hash.assign(mips32_sha256_hash,
|
| - &mips32_sha256_hash[sizeof(mips32_sha256_hash)]);
|
| + pk_hash->assign(mips32_sha256_hash,
|
| + &mips32_sha256_hash[sizeof(mips32_sha256_hash)]);
|
| #else
|
| #error "Add support for your architecture to Pnacl Component Installer."
|
| #endif
|
| @@ -239,12 +241,28 @@ class PnaclComponentInstaller : public ComponentInstaller {
|
| virtual bool Install(base::DictionaryValue* manifest,
|
| const base::FilePath& unpack_path) OVERRIDE;
|
|
|
| + typedef base::Callback<void(bool)> InstallCallback;
|
| + void AddInstallCallback(const InstallCallback& cb);
|
| +
|
| private:
|
| + // Cancel a particular callback after a timeout.
|
| + void CancelCallback(int callback_num);
|
| +
|
| + void NotifyInstallError();
|
| +
|
| + void NotifyInstallSuccess();
|
| +
|
| Version current_version_;
|
| +
|
| + // Counter for issue identifiers to each callback.
|
| + int callback_nums_;
|
| +
|
| + // List of callbacks to issue when an install completes successfully.
|
| + std::list<std::pair<InstallCallback, int> > install_callbacks_;
|
| };
|
|
|
| PnaclComponentInstaller::PnaclComponentInstaller(
|
| - const Version& version) : current_version_(version) {
|
| + const Version& version) : current_version_(version), callback_nums_(0) {
|
| DCHECK(version.IsValid());
|
| }
|
|
|
| @@ -263,14 +281,32 @@ bool PathContainsPnacl(const base::FilePath& base_path) {
|
| return file_util::PathExists(base_path.AppendASCII(expected_filename));
|
| }
|
|
|
| +CrxComponent GetPnaclComponentWithVersion(const Version& current_version) {
|
| + // Note: the source is the default of BANDAID, even though the
|
| + // crxes are hosted from CWS.
|
| + LOG(WARNING) << "GetPnaclComponentWithVersion!!\n";
|
| + CrxComponent pnacl;
|
| + pnacl.name = "pnacl";
|
| + // We need a singleton installer... or singleton component,
|
| + // so that the correct installer's Install() hook gets called.
|
| + // Or the component updater service needs to be able to look up
|
| + // the component by id instead.
|
| + pnacl.installer = new PnaclComponentInstaller(current_version);
|
| + pnacl.version = current_version;
|
| + SetPnaclHash(&pnacl.pk_hash);
|
| + return pnacl;
|
| +}
|
| +
|
| } // namespace
|
|
|
| bool PnaclComponentInstaller::Install(base::DictionaryValue* manifest,
|
| const base::FilePath& unpack_path) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| scoped_ptr<base::DictionaryValue> pnacl_manifest(
|
| ReadPnaclManifest(unpack_path));
|
| if (pnacl_manifest == NULL) {
|
| LOG(WARNING) << "Failed to read pnacl manifest.";
|
| + NotifyInstallError();
|
| return false;
|
| }
|
|
|
| @@ -279,15 +315,19 @@ bool PnaclComponentInstaller::Install(base::DictionaryValue* manifest,
|
| pnacl_manifest.get(),
|
| &version)) {
|
| LOG(WARNING) << "CheckPnaclComponentManifest failed, not installing.";
|
| + NotifyInstallError();
|
| return false;
|
| }
|
|
|
| // Don't install if the current version is actually newer.
|
| - if (current_version_.CompareTo(version) > 0)
|
| + if (current_version_.CompareTo(version) > 0) {
|
| + NotifyInstallError();
|
| return false;
|
| + }
|
|
|
| if (!PathContainsPnacl(unpack_path)) {
|
| LOG(WARNING) << "PathContainsPnacl check failed, not installing.";
|
| + NotifyInstallError();
|
| return false;
|
| }
|
|
|
| @@ -296,58 +336,88 @@ bool PnaclComponentInstaller::Install(base::DictionaryValue* manifest,
|
| GetPnaclBaseDirectory().AppendASCII(version.GetString());
|
| if (file_util::PathExists(path)) {
|
| LOG(WARNING) << "Target path already exists, not installing.";
|
| + NotifyInstallError();
|
| return false;
|
| }
|
| if (!file_util::Move(unpack_path, path)) {
|
| LOG(WARNING) << "Move failed, not installing.";
|
| + NotifyInstallError();
|
| return false;
|
| }
|
|
|
| - // Installation is done. Now tell the rest of chrome (just the path service
|
| - // for now). TODO(jvoung): we need notifications if someone surfed to a
|
| - // Pnacl webpage and Pnacl was just installed at this time. They should
|
| - // then be able to reload the page and retry (or something).
|
| - // See: http://code.google.com/p/chromium/issues/detail?id=107438
|
| + // Installation is done. Now tell the rest of chrome.
|
| + // - The path service.
|
| + // - Callbacks that requested an update.
|
| current_version_ = version;
|
| -
|
| + NotifyInstallSuccess();
|
| PathService::Override(chrome::DIR_PNACL_COMPONENT, path);
|
| return true;
|
| }
|
|
|
| -namespace {
|
| +void PnaclComponentInstaller::AddInstallCallback(
|
| + const InstallCallback& cb) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| + int num = ++callback_nums_;
|
| + install_callbacks_.push_back(std::make_pair(cb, num));
|
| +
|
| + // Set a timeout. If the install doesn't complete within a minute,
|
| + // assume that the update failed and cancel the callback.
|
| + // Do this on the same thread that would have checked the callbacks.
|
| + BrowserThread::PostDelayedTask(
|
| + BrowserThread::FILE, FROM_HERE,
|
| + base::Bind(&PnaclComponentInstaller::CancelCallback,
|
| + // Why unretained? The installer should have
|
| + // the same lifetime as the component updater service,
|
| + // which lives until process shutdown.
|
| + base::Unretained(this),
|
| + num),
|
| + base::TimeDelta::FromSeconds(kPnaclInstallerTimeout));
|
| +}
|
| +
|
| +void PnaclComponentInstaller::CancelCallback(int num) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| + for (std::list<std::pair<InstallCallback, int> >::iterator
|
| + i = install_callbacks_.begin(),
|
| + e = install_callbacks_.end(); i != e; ++i) {
|
| + if (i->second == num) {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::UI, FROM_HERE,
|
| + base::Bind(i->first, false));
|
| + install_callbacks_.erase(i);
|
| + return;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void PnaclComponentInstaller::NotifyInstallError() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| + while (!install_callbacks_.empty()) {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::UI, FROM_HERE,
|
| + base::Bind(install_callbacks_.front().first, false));
|
| + install_callbacks_.pop_front();
|
| + }
|
| +}
|
|
|
| -void DoCheckForUpdate(ComponentUpdateService* cus,
|
| - const CrxComponent& pnacl) {
|
| - if (cus->CheckForUpdateSoon(pnacl) != ComponentUpdateService::kOk) {
|
| - LOG(WARNING) << "Pnacl check for update failed.";
|
| +void PnaclComponentInstaller::NotifyInstallSuccess() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| + while (!install_callbacks_.empty()) {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::UI, FROM_HERE,
|
| + base::Bind(install_callbacks_.front().first, true));
|
| + install_callbacks_.pop_front();
|
| }
|
| }
|
|
|
| +namespace {
|
| +
|
| // Finally, do the registration with the right version number.
|
| void FinishPnaclUpdateRegistration(ComponentUpdateService* cus,
|
| - const Version& current_version) {
|
| + const CrxComponent& pnacl) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - // Note: the source is the default of BANDAID, even though the
|
| - // crxes are hosted from CWS.
|
| - CrxComponent pnacl;
|
| - pnacl.name = "pnacl";
|
| - pnacl.installer = new PnaclComponentInstaller(current_version);
|
| - pnacl.version = current_version;
|
| - SetPnaclHash(&pnacl);
|
| if (cus->RegisterComponent(pnacl) != ComponentUpdateService::kOk) {
|
| NOTREACHED() << "Pnacl component registration failed.";
|
| }
|
| -
|
| - // If PNaCl is not yet installed but it is requested by --enable-pnacl,
|
| - // we want it to be available "soon", so kick off an update check
|
| - // earlier than usual.
|
| - Version null_version(kNullVersion);
|
| - if (current_version.Equals(null_version)) {
|
| - BrowserThread::PostDelayedTask(
|
| - BrowserThread::UI, FROM_HERE,
|
| - base::Bind(DoCheckForUpdate, cus, pnacl),
|
| - base::TimeDelta::FromSeconds(kInitialDelaySeconds));
|
| - }
|
| }
|
|
|
| // Check if there is an existing version on disk first to know when
|
| @@ -374,7 +444,9 @@ void StartPnaclUpdateRegistration(ComponentUpdateService* cus) {
|
|
|
| BrowserThread::PostTask(
|
| BrowserThread::UI, FROM_HERE,
|
| - base::Bind(&FinishPnaclUpdateRegistration, cus, version));
|
| + base::Bind(&FinishPnaclUpdateRegistration,
|
| + cus,
|
| + GetPnaclComponentWithVersion(version)));
|
|
|
| // Remove older versions of PNaCl.
|
| for (std::vector<base::FilePath>::iterator iter = older_dirs.begin();
|
| @@ -390,3 +462,30 @@ void RegisterPnaclComponent(ComponentUpdateService* cus) {
|
| BrowserThread::FILE, FROM_HERE,
|
| base::Bind(&StartPnaclUpdateRegistration, cus));
|
| }
|
| +
|
| +void CheckUpdatesForPnacl(ComponentUpdateService* cus,
|
| + const base::Callback<void(bool)>& installed) {
|
| + std::vector<uint8> pk_hash;
|
| + SetPnaclHash(&pk_hash);
|
| + CrxComponent pnacl;
|
| + if (!cus->FindRegisteredComponent(pk_hash, &pnacl)) {
|
| + installed.Run(false);
|
| + return;
|
| + }
|
| + ComponentUpdateService::Status status = cus->CheckForUpdateSoon(pnacl);
|
| + if (status != ComponentUpdateService::kOk) {
|
| + installed.Run(false);
|
| + return;
|
| + }
|
| + PnaclComponentInstaller* installer =
|
| + static_cast<PnaclComponentInstaller*>(pnacl.installer);
|
| + BrowserThread::PostTask(
|
| + BrowserThread::FILE, FROM_HERE,
|
| + base::Bind(
|
| + &PnaclComponentInstaller::AddInstallCallback,
|
| + // Why unretained? The installer should have
|
| + // the same lifetime as the component updater service,
|
| + // which lives until process shutdown.
|
| + base::Unretained(installer),
|
| + installed));
|
| +}
|
|
|