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 "chrome/common/extensions/manifest.h" 22 #include "content/public/browser/browser_thread.h" 23 #include "extensions/common/id_util.h" 24 #include "sync/api/string_ordinal.h" 25 26 using content::BrowserThread; 27 using extensions::Extension; 28 29 namespace { 30 31 const char kUnpackedExtensionsBlacklistedError[] = 32 "Loading of unpacked extensions is disabled by the administrator."; 33 34 // Manages an ExtensionInstallPrompt for a particular extension. 35 class 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 54 SimpleExtensionLoadPrompt::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 64 SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() { 65 } 66 67 void SimpleExtensionLoadPrompt::ShowPrompt() { 68 install_ui_->ConfirmInstall( 69 this, 70 extension_.get(), 71 ExtensionInstallPrompt::GetDefaultShowDialogCallback()); 72 } 73 74 void SimpleExtensionLoadPrompt::InstallUIProceed() { 75 callback_.Run(); 76 delete this; 77 } 78 79 void SimpleExtensionLoadPrompt::InstallUIAbort(bool user_initiated) { 80 delete this; 81 } 82 83 } // namespace 84 85 namespace extensions { 86 87 // static 88 scoped_refptr<UnpackedInstaller> UnpackedInstaller::Create( 89 ExtensionService* extension_service) { 90 return scoped_refptr<UnpackedInstaller>( 91 new UnpackedInstaller(extension_service)); 92 } 93 94 UnpackedInstaller::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 102 UnpackedInstaller::~UnpackedInstaller() { 103 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || 104 BrowserThread::CurrentlyOn(BrowserThread::FILE)); 105 } 106 107 void 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 116 bool 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 149 void 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 169 void UnpackedInstaller::CallCheckRequirements() { 170 installer_.CheckRequirements( 171 base::Bind(&UnpackedInstaller::OnRequirementsChecked, this)); 172 } 173 174 void 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 186 int 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 203 bool 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 212 void UnpackedInstaller::GetAbsolutePath() { 213 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 214 215 extension_path_ = base::MakeAbsoluteFilePath(extension_path_); 216 217 BrowserThread::PostTask( 218 BrowserThread::UI, FROM_HERE, 219 base::Bind(&UnpackedInstaller::CheckExtensionFileAccess, this)); 220 } 221 222 void UnpackedInstaller::CheckExtensionFileAccess() { 223 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 224 if (!service_weak_.get()) 225 return; 226 227 if (!IsLoadingUnpackedAllowed()) { 228 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError); 229 return; 230 } 231 232 BrowserThread::PostTask( 233 BrowserThread::FILE, 234 FROM_HERE, 235 base::Bind(&UnpackedInstaller::LoadWithFileAccess, this, GetFlags())); 236 } 237 238 void UnpackedInstaller::LoadWithFileAccess(int flags) { 239 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 240 241 std::string error; 242 installer_.set_extension(extension_file_util::LoadExtension( 243 extension_path_, Manifest::UNPACKED, flags, &error).get()); 244 245 if (!installer_.extension().get()) { 246 BrowserThread::PostTask( 247 BrowserThread::UI, 248 FROM_HERE, 249 base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error)); 250 return; 251 } 252 253 BrowserThread::PostTask( 254 BrowserThread::UI, 255 FROM_HERE, 256 base::Bind(&UnpackedInstaller::ShowInstallPrompt, this)); 257 } 258 259 void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) { 260 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 261 if (!service_weak_.get()) 262 return; 263 service_weak_->ReportExtensionLoadError(extension_path_, error, true); 264 } 265 266 void UnpackedInstaller::ConfirmInstall() { 267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 268 string16 error = installer_.CheckManagementPolicy(); 269 if (!error.empty()) { 270 ReportExtensionLoadError(UTF16ToUTF8(error)); 271 return; 272 } 273 274 PermissionsUpdater perms_updater(service_weak_->profile()); 275 perms_updater.GrantActivePermissions(installer_.extension().get()); 276 277 service_weak_->OnExtensionInstalled( 278 installer_.extension().get(), 279 syncer::StringOrdinal(), 280 false /* no requirement errors */, 281 Blacklist::NOT_BLACKLISTED, 282 false /* don't wait for idle */); 283 } 284 285 } // namespace extensions 286