| Index: chrome/browser/extensions/crx_installer.cc
|
| diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..445d37cd2d16528e229024fbc034ecdf300d7357
|
| --- /dev/null
|
| +++ b/chrome/browser/extensions/crx_installer.cc
|
| @@ -0,0 +1,193 @@
|
| +// Copyright (c) 2009 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/extensions/crx_installer.h"
|
| +
|
| +#include "app/l10n_util.h"
|
| +#include "base/file_util.h"
|
| +#include "base/scoped_temp_dir.h"
|
| +#include "base/string_util.h"
|
| +#include "base/task.h"
|
| +#include "chrome/browser/browser_process.h"
|
| +#include "chrome/browser/extensions/extension_file_util.h"
|
| +#include "chrome/common/extensions/extension_error_reporter.h"
|
| +#include "grit/chromium_strings.h"
|
| +
|
| +#if defined(OS_WIN)
|
| +#include "app/win_util.h"
|
| +#elif defined(OS_MACOSX)
|
| +#include "base/scoped_cftyperef.h"
|
| +#include "base/sys_string_conversions.h"
|
| +#include <CoreFoundation/CFUserNotification.h>
|
| +#endif
|
| +
|
| +CrxInstaller::CrxInstaller(const FilePath& crx_path,
|
| + const FilePath& install_directory,
|
| + Extension::Location install_source,
|
| + const std::string& expected_id,
|
| + bool extensions_enabled,
|
| + bool is_from_gallery,
|
| + bool show_prompts,
|
| + bool delete_crx,
|
| + MessageLoop* file_loop,
|
| + ExtensionsService* frontend)
|
| + : crx_path_(crx_path),
|
| + install_directory_(install_directory),
|
| + install_source_(install_source),
|
| + expected_id_(expected_id),
|
| + extensions_enabled_(extensions_enabled),
|
| + is_from_gallery_(is_from_gallery),
|
| + show_prompts_(show_prompts),
|
| + file_loop_(file_loop),
|
| + ui_loop_(MessageLoop::current()) {
|
| +
|
| + // Note: this is a refptr so that we keep the frontend alive long enough to
|
| + // get our response.
|
| + frontend_ = frontend;
|
| + unpacker_ = new SandboxedExtensionUnpacker(
|
| + crx_path, g_browser_process->resource_dispatcher_host(), this);
|
| +
|
| + file_loop->PostTask(FROM_HERE, NewRunnableMethod(unpacker_,
|
| + &SandboxedExtensionUnpacker::Start));
|
| +}
|
| +
|
| +void CrxInstaller::OnUnpackFailure(const std::string& error_message) {
|
| + ReportFailureFromFileThread(error_message);
|
| +}
|
| +
|
| +void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir,
|
| + const FilePath& extension_dir,
|
| + Extension* extension) {
|
| + // Note: We take ownership of |extension| and |temp_dir|.
|
| + extension_.reset(extension);
|
| + temp_dir_ = temp_dir;
|
| +
|
| + // temp_dir_deleter is stack allocated instead of a member of CrxInstaller, so
|
| + // that delete always happens on the file thread.
|
| + ScopedTempDir temp_dir_deleter;
|
| + temp_dir_deleter.Set(temp_dir);
|
| +
|
| + // The unpack dir we don't have to delete explicity since it is a child of
|
| + // the temp dir.
|
| + unpacked_extension_root_ = extension_dir;
|
| + DCHECK(file_util::ContainsPath(temp_dir_, unpacked_extension_root_));
|
| +
|
| + // If we were supposed to delete the source file, we can do that now.
|
| + if (delete_crx_)
|
| + file_util::Delete(crx_path_, false); // non-recursive
|
| +
|
| + // Determine whether to allow installation. We always allow themes and
|
| + // external installs.
|
| + if (!extensions_enabled_ && !extension->IsTheme() &&
|
| + !Extension::IsExternalLocation(install_source_)) {
|
| + ReportFailureFromFileThread("Extensions are not enabled.");
|
| + return;
|
| + }
|
| +
|
| + // Make sure the expected id matches.
|
| + // TODO(aa): Also support expected version?
|
| + if (!expected_id_.empty() && expected_id_ != extension->id()) {
|
| + ReportFailureFromFileThread(
|
| + StringPrintf("ID in new extension manifest (%s) does not match "
|
| + "expected id (%s)",
|
| + extension->id().c_str(),
|
| + expected_id_.c_str()));
|
| + return;
|
| + }
|
| +
|
| + // Show the confirm UI if necessary.
|
| + // NOTE: We also special case themes to not have a dialog, because we show
|
| + // a special infobar UI for them instead.
|
| + if (show_prompts_ && !extension->IsTheme()) {
|
| + if (!ConfirmInstall())
|
| + return; // error reported by ConfirmInstall()
|
| + }
|
| +
|
| + CompleteInstall();
|
| +}
|
| +
|
| +bool CrxInstaller::ConfirmInstall() {
|
| +#if defined(OS_WIN)
|
| + if (win_util::MessageBox(GetForegroundWindow(),
|
| + L"Are you sure you want to install this extension?\n\n"
|
| + L"You should only install extensions from sources you trust.",
|
| + l10n_util::GetString(IDS_PRODUCT_NAME).c_str(),
|
| + MB_OKCANCEL) != IDOK) {
|
| + ReportFailureFromFileThread("User did not allow extension to be "
|
| + "installed.");
|
| + return false;
|
| + }
|
| +#elif defined(OS_MACOSX)
|
| + // Using CoreFoundation to do this dialog is unimaginably lame but will do
|
| + // until the UI is redone.
|
| + scoped_cftyperef<CFStringRef> product_name(
|
| + base::SysWideToCFStringRef(l10n_util::GetString(IDS_PRODUCT_NAME)));
|
| + CFOptionFlags response;
|
| + if (kCFUserNotificationAlternateResponse == CFUserNotificationDisplayAlert(
|
| + 0, kCFUserNotificationCautionAlertLevel, NULL, NULL, NULL,
|
| + product_name,
|
| + CFSTR("Are you sure you want to install this extension?\n\n"
|
| + "This is a temporary message and it will be removed when "
|
| + "extensions UI is finalized."),
|
| + NULL, CFSTR("Cancel"), NULL, &response);
|
| + ReportFailureFromFileThread("User did not allow extension to be "
|
| + "installed.");
|
| + return false;
|
| + }
|
| +#endif // OS_*
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void CrxInstaller::CompleteInstall() {
|
| + FilePath version_dir;
|
| + Extension::InstallType install_type = Extension::INSTALL_ERROR;
|
| + std::string error_msg;
|
| + if (!extension_file_util::InstallExtension(unpacked_extension_root_,
|
| + install_directory_,
|
| + extension_->id(),
|
| + extension_->VersionString(),
|
| + &version_dir,
|
| + &install_type, &error_msg)) {
|
| + ReportFailureFromFileThread(error_msg);
|
| + return;
|
| + }
|
| +
|
| + if (install_type == Extension::DOWNGRADE) {
|
| + ReportFailureFromFileThread("Attempted to downgrade extension.");
|
| + return;
|
| + }
|
| +
|
| + extension_->set_path(version_dir);
|
| + extension_->set_location(install_source_);
|
| +
|
| + if (install_type == Extension::REINSTALL) {
|
| + // We use this as a signal to switch themes.
|
| + ReportOverinstallFromFileThread();
|
| + return;
|
| + }
|
| +
|
| + ReportSuccessFromFileThread();
|
| +}
|
| +
|
| +void CrxInstaller::ReportFailureFromFileThread(const std::string& error) {
|
| + ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
|
| + &CrxInstaller::ReportFailureFromUIThread, error));
|
| +}
|
| +
|
| +void CrxInstaller::ReportFailureFromUIThread(const std::string& error) {
|
| + ExtensionErrorReporter::GetInstance()->ReportError(error, show_prompts_);
|
| +}
|
| +
|
| +void CrxInstaller::ReportOverinstallFromFileThread() {
|
| + ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_.get(),
|
| + &ExtensionsService::OnExtensionOverinstallAttempted, extension_->id()));
|
| +}
|
| +
|
| +void CrxInstaller::ReportSuccessFromFileThread() {
|
| + // Tell the frontend about the installation and hand off ownership of
|
| + // extension_ to it.
|
| + ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_.get(),
|
| + &ExtensionsService::OnExtensionInstalled, extension_.release()));
|
| +}
|
|
|