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