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