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/extensions/unpacked_installer.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/files/file_util.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/threading/thread_restrictions.h"
     12 #include "chrome/browser/extensions/extension_error_reporter.h"
     13 #include "chrome/browser/extensions/extension_install_prompt.h"
     14 #include "chrome/browser/extensions/extension_install_ui.h"
     15 #include "chrome/browser/extensions/extension_management.h"
     16 #include "chrome/browser/extensions/extension_service.h"
     17 #include "chrome/browser/extensions/permissions_updater.h"
     18 #include "chrome/browser/profiles/profile.h"
     19 #include "chrome/common/extensions/api/plugins/plugins_handler.h"
     20 #include "components/crx_file/id_util.h"
     21 #include "content/public/browser/browser_thread.h"
     22 #include "extensions/browser/extension_prefs.h"
     23 #include "extensions/browser/extension_registry.h"
     24 #include "extensions/browser/install_flag.h"
     25 #include "extensions/common/extension.h"
     26 #include "extensions/common/extension_l10n_util.h"
     27 #include "extensions/common/file_util.h"
     28 #include "extensions/common/manifest.h"
     29 #include "sync/api/string_ordinal.h"
     30 
     31 using content::BrowserThread;
     32 using extensions::Extension;
     33 
     34 namespace {
     35 
     36 const char kUnpackedExtensionsBlacklistedError[] =
     37     "Loading of unpacked extensions is disabled by the administrator.";
     38 
     39 // Manages an ExtensionInstallPrompt for a particular extension.
     40 class SimpleExtensionLoadPrompt : public ExtensionInstallPrompt::Delegate {
     41  public:
     42   SimpleExtensionLoadPrompt(const Extension* extension,
     43                             Profile* profile,
     44                             const base::Closure& callback);
     45   virtual ~SimpleExtensionLoadPrompt();
     46 
     47   void ShowPrompt();
     48 
     49   // ExtensionInstallUI::Delegate
     50   virtual void InstallUIProceed() OVERRIDE;
     51   virtual void InstallUIAbort(bool user_initiated) OVERRIDE;
     52 
     53  private:
     54   scoped_ptr<ExtensionInstallPrompt> install_ui_;
     55   scoped_refptr<const Extension> extension_;
     56   base::Closure callback_;
     57 };
     58 
     59 SimpleExtensionLoadPrompt::SimpleExtensionLoadPrompt(
     60     const Extension* extension,
     61     Profile* profile,
     62     const base::Closure& callback)
     63     : install_ui_(ExtensionInstallUI::CreateInstallPromptWithProfile(
     64           profile)),
     65       extension_(extension),
     66       callback_(callback) {
     67 }
     68 
     69 SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() {
     70 }
     71 
     72 void SimpleExtensionLoadPrompt::ShowPrompt() {
     73   switch (ExtensionInstallPrompt::g_auto_confirm_for_tests) {
     74     case ExtensionInstallPrompt::NONE:
     75       install_ui_->ConfirmInstall(
     76           this,
     77           extension_.get(),
     78           ExtensionInstallPrompt::GetDefaultShowDialogCallback());
     79       break;
     80     case ExtensionInstallPrompt::ACCEPT:
     81       InstallUIProceed();
     82       break;
     83     case ExtensionInstallPrompt::CANCEL:
     84       InstallUIAbort(false);
     85   }
     86 }
     87 
     88 void SimpleExtensionLoadPrompt::InstallUIProceed() {
     89   callback_.Run();
     90   delete this;
     91 }
     92 
     93 void SimpleExtensionLoadPrompt::InstallUIAbort(bool user_initiated) {
     94   delete this;
     95 }
     96 
     97 }  // namespace
     98 
     99 namespace extensions {
    100 
    101 // static
    102 scoped_refptr<UnpackedInstaller> UnpackedInstaller::Create(
    103     ExtensionService* extension_service) {
    104   DCHECK(extension_service);
    105   return scoped_refptr<UnpackedInstaller>(
    106       new UnpackedInstaller(extension_service));
    107 }
    108 
    109 UnpackedInstaller::UnpackedInstaller(ExtensionService* extension_service)
    110     : service_weak_(extension_service->AsWeakPtr()),
    111       prompt_for_plugins_(true),
    112       require_modern_manifest_version_(true),
    113       be_noisy_on_failure_(true),
    114       install_checker_(extension_service->profile()) {
    115   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    116 }
    117 
    118 UnpackedInstaller::~UnpackedInstaller() {
    119   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
    120         BrowserThread::CurrentlyOn(BrowserThread::FILE));
    121 }
    122 
    123 void UnpackedInstaller::Load(const base::FilePath& path_in) {
    124   DCHECK(extension_path_.empty());
    125   extension_path_ = path_in;
    126   BrowserThread::PostTask(
    127       BrowserThread::FILE,
    128       FROM_HERE,
    129       base::Bind(&UnpackedInstaller::GetAbsolutePath, this));
    130 }
    131 
    132 bool UnpackedInstaller::LoadFromCommandLine(const base::FilePath& path_in,
    133                                             std::string* extension_id) {
    134   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    135   DCHECK(extension_path_.empty());
    136 
    137   if (!service_weak_.get())
    138     return false;
    139   // Load extensions from the command line synchronously to avoid a race
    140   // between extension loading and loading an URL from the command line.
    141   base::ThreadRestrictions::ScopedAllowIO allow_io;
    142 
    143   extension_path_ = base::MakeAbsoluteFilePath(path_in);
    144 
    145   if (!IsLoadingUnpackedAllowed()) {
    146     ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
    147     return false;
    148   }
    149 
    150   std::string error;
    151   install_checker_.set_extension(
    152       file_util::LoadExtension(
    153           extension_path_, Manifest::COMMAND_LINE, GetFlags(), &error).get());
    154 
    155   if (!extension() ||
    156       !extension_l10n_util::ValidateExtensionLocales(
    157           extension_path_, extension()->manifest()->value(), &error)) {
    158     ReportExtensionLoadError(error);
    159     return false;
    160   }
    161 
    162   PermissionsUpdater(
    163       service_weak_->profile(), PermissionsUpdater::INIT_FLAG_TRANSIENT)
    164       .InitializePermissions(extension());
    165   ShowInstallPrompt();
    166 
    167   *extension_id = extension()->id();
    168   return true;
    169 }
    170 
    171 void UnpackedInstaller::ShowInstallPrompt() {
    172   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    173   if (!service_weak_.get())
    174     return;
    175 
    176   const ExtensionSet& disabled_extensions =
    177       ExtensionRegistry::Get(service_weak_->profile())->disabled_extensions();
    178   if (service_weak_->show_extensions_prompts() && prompt_for_plugins_ &&
    179       PluginInfo::HasPlugins(extension()) &&
    180       !disabled_extensions.Contains(extension()->id())) {
    181     SimpleExtensionLoadPrompt* prompt = new SimpleExtensionLoadPrompt(
    182         extension(),
    183         install_checker_.profile(),
    184         base::Bind(&UnpackedInstaller::StartInstallChecks, this));
    185     prompt->ShowPrompt();
    186     return;
    187   }
    188   StartInstallChecks();
    189 }
    190 
    191 void UnpackedInstaller::StartInstallChecks() {
    192   install_checker_.Start(
    193       ExtensionInstallChecker::CHECK_REQUIREMENTS |
    194           ExtensionInstallChecker::CHECK_MANAGEMENT_POLICY,
    195       true /* fail fast */,
    196       base::Bind(&UnpackedInstaller::OnInstallChecksComplete, this));
    197 }
    198 
    199 void UnpackedInstaller::OnInstallChecksComplete(int failed_checks) {
    200   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    201 
    202   if (!install_checker_.policy_error().empty()) {
    203     ReportExtensionLoadError(install_checker_.policy_error());
    204     return;
    205   }
    206 
    207   if (!install_checker_.requirement_errors().empty()) {
    208     ReportExtensionLoadError(
    209         JoinString(install_checker_.requirement_errors(), ' '));
    210     return;
    211   }
    212 
    213   InstallExtension();
    214 }
    215 
    216 int UnpackedInstaller::GetFlags() {
    217   std::string id = crx_file::id_util::GenerateIdForPath(extension_path_);
    218   bool allow_file_access =
    219       Manifest::ShouldAlwaysAllowFileAccess(Manifest::UNPACKED);
    220   ExtensionPrefs* prefs = ExtensionPrefs::Get(service_weak_->profile());
    221   if (prefs->HasAllowFileAccessSetting(id))
    222     allow_file_access = prefs->AllowFileAccess(id);
    223 
    224   int result = Extension::FOLLOW_SYMLINKS_ANYWHERE;
    225   if (allow_file_access)
    226     result |= Extension::ALLOW_FILE_ACCESS;
    227   if (require_modern_manifest_version_)
    228     result |= Extension::REQUIRE_MODERN_MANIFEST_VERSION;
    229 
    230   return result;
    231 }
    232 
    233 bool UnpackedInstaller::IsLoadingUnpackedAllowed() const {
    234   if (!service_weak_.get())
    235     return true;
    236   // If there is a "*" in the extension blacklist, then no extensions should be
    237   // allowed at all (except explicitly whitelisted extensions).
    238   return !ExtensionManagementFactory::GetForBrowserContext(
    239               service_weak_->profile())->BlacklistedByDefault();
    240 }
    241 
    242 void UnpackedInstaller::GetAbsolutePath() {
    243   CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    244 
    245   extension_path_ = base::MakeAbsoluteFilePath(extension_path_);
    246 
    247   std::string error;
    248   if (!file_util::CheckForIllegalFilenames(extension_path_, &error)) {
    249     BrowserThread::PostTask(
    250         BrowserThread::UI,
    251         FROM_HERE,
    252         base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error));
    253     return;
    254   }
    255   BrowserThread::PostTask(
    256       BrowserThread::UI, FROM_HERE,
    257       base::Bind(&UnpackedInstaller::CheckExtensionFileAccess, this));
    258 }
    259 
    260 void UnpackedInstaller::CheckExtensionFileAccess() {
    261   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    262   if (!service_weak_.get())
    263     return;
    264 
    265   if (!IsLoadingUnpackedAllowed()) {
    266     ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
    267     return;
    268   }
    269 
    270   BrowserThread::PostTask(
    271       BrowserThread::FILE,
    272       FROM_HERE,
    273       base::Bind(&UnpackedInstaller::LoadWithFileAccess, this, GetFlags()));
    274 }
    275 
    276 void UnpackedInstaller::LoadWithFileAccess(int flags) {
    277   CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    278 
    279   std::string error;
    280   install_checker_.set_extension(
    281       file_util::LoadExtension(
    282           extension_path_, Manifest::UNPACKED, flags, &error).get());
    283 
    284   if (!extension() ||
    285       !extension_l10n_util::ValidateExtensionLocales(
    286           extension_path_, extension()->manifest()->value(), &error)) {
    287     BrowserThread::PostTask(
    288         BrowserThread::UI,
    289         FROM_HERE,
    290         base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error));
    291     return;
    292   }
    293 
    294   BrowserThread::PostTask(
    295       BrowserThread::UI,
    296       FROM_HERE,
    297       base::Bind(&UnpackedInstaller::ShowInstallPrompt, this));
    298 }
    299 
    300 void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) {
    301   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    302 
    303   if (service_weak_.get()) {
    304     ExtensionErrorReporter::GetInstance()->ReportLoadError(
    305         extension_path_,
    306         error,
    307         service_weak_->profile(),
    308         be_noisy_on_failure_);
    309   }
    310 }
    311 
    312 void UnpackedInstaller::InstallExtension() {
    313   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    314 
    315   PermissionsUpdater perms_updater(service_weak_->profile());
    316   perms_updater.InitializePermissions(extension());
    317   perms_updater.GrantActivePermissions(extension());
    318 
    319   service_weak_->OnExtensionInstalled(
    320       extension(), syncer::StringOrdinal(), kInstallFlagInstallImmediately);
    321 }
    322 
    323 }  // namespace extensions
    324