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)); |
+} |