pepper_flash_component_installer.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/component_updater/flash_component_installer.h" 6 7#include <string.h> 8 9#include <vector> 10 11#include "base/base_paths.h" 12#include "base/bind.h" 13#include "base/command_line.h" 14#include "base/compiler_specific.h" 15#include "base/file_util.h" 16#include "base/files/file_enumerator.h" 17#include "base/files/file_path.h" 18#include "base/logging.h" 19#include "base/path_service.h" 20#include "base/strings/string_split.h" 21#include "base/strings/string_util.h" 22#include "base/strings/stringprintf.h" 23#include "base/strings/utf_string_conversions.h" 24#include "base/values.h" 25#include "base/version.h" 26#include "build/build_config.h" 27#include "chrome/browser/component_updater/component_updater_service.h" 28#include "chrome/browser/component_updater/ppapi_utils.h" 29#include "chrome/browser/plugins/plugin_prefs.h" 30#include "chrome/common/chrome_constants.h" 31#include "chrome/common/chrome_paths.h" 32#include "chrome/common/chrome_switches.h" 33#include "chrome/common/pepper_flash.h" 34#include "content/public/browser/browser_thread.h" 35#include "content/public/browser/plugin_service.h" 36#include "content/public/common/content_constants.h" 37#include "content/public/common/pepper_plugin_info.h" 38#include "ppapi/c/private/ppb_pdf.h" 39#include "ppapi/shared_impl/ppapi_permissions.h" 40 41#include "flapper_version.h" // In SHARED_INTERMEDIATE_DIR. 42 43using content::BrowserThread; 44using content::PluginService; 45 46namespace component_updater { 47 48namespace { 49 50// File name of the Pepper Flash component manifest on different platforms. 51const char kPepperFlashManifestName[] = "Flapper"; 52 53#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX) 54// CRX hash. The extension id is: mimojjlkmoijpicakmndhoigimigcmbb. 55const uint8 kSha2Hash[] = {0xc8, 0xce, 0x99, 0xba, 0xce, 0x89, 0xf8, 0x20, 56 0xac, 0xd3, 0x7e, 0x86, 0x8c, 0x86, 0x2c, 0x11, 57 0xb9, 0x40, 0xc5, 0x55, 0xaf, 0x08, 0x63, 0x70, 58 0x54, 0xf9, 0x56, 0xd3, 0xe7, 0x88, 0xba, 0x8c}; 59 60// If we don't have a Pepper Flash component, this is the version we claim. 61const char kNullVersion[] = "0.0.0.0"; 62 63#endif // defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX) 64 65// Name of the Pepper Flash OS in the component manifest. 66const char kPepperFlashOperatingSystem[] = 67#if defined(OS_MACOSX) 68 "mac"; 69#elif defined(OS_WIN) 70 "win"; 71#else // OS_LINUX, etc. TODO(viettrungluu): Separate out Chrome OS and Android? 72 "linux"; 73#endif 74 75// Name of the Pepper Flash architecture in the component manifest. 76const char kPepperFlashArch[] = 77#if defined(ARCH_CPU_X86) 78 "ia32"; 79#elif defined(ARCH_CPU_X86_64) 80 "x64"; 81#else // TODO(viettrungluu): Support an ARM check? 82 "???"; 83#endif 84 85// The base directory on Windows looks like: 86// <profile>\AppData\Local\Google\Chrome\User Data\PepperFlash\. 87base::FilePath GetPepperFlashBaseDirectory() { 88 base::FilePath result; 89 PathService::Get(chrome::DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN, &result); 90 return result; 91} 92 93#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX) 94// Pepper Flash plugins have the version encoded in the path itself 95// so we need to enumerate the directories to find the full path. 96// On success, |latest_dir| returns something like: 97// <profile>\AppData\Local\Google\Chrome\User Data\PepperFlash\10.3.44.555\. 98// |latest_version| returns the corresponding version number. |older_dirs| 99// returns directories of all older versions. 100bool GetPepperFlashDirectory(base::FilePath* latest_dir, 101 Version* latest_version, 102 std::vector<base::FilePath>* older_dirs) { 103 base::FilePath base_dir = GetPepperFlashBaseDirectory(); 104 bool found = false; 105 base::FileEnumerator 106 file_enumerator(base_dir, false, base::FileEnumerator::DIRECTORIES); 107 for (base::FilePath path = file_enumerator.Next(); !path.value().empty(); 108 path = file_enumerator.Next()) { 109 Version version(path.BaseName().MaybeAsASCII()); 110 if (!version.IsValid()) 111 continue; 112 if (found) { 113 if (version.CompareTo(*latest_version) > 0) { 114 older_dirs->push_back(*latest_dir); 115 *latest_dir = path; 116 *latest_version = version; 117 } else { 118 older_dirs->push_back(path); 119 } 120 } else { 121 *latest_dir = path; 122 *latest_version = version; 123 found = true; 124 } 125 } 126 return found; 127} 128#endif 129 130// Returns true if the Pepper |interface_name| is implemented by this browser. 131// It does not check if the interface is proxied. 132bool SupportsPepperInterface(const char* interface_name) { 133 if (IsSupportedPepperInterface(interface_name)) 134 return true; 135 // The PDF interface is invisible to SupportsInterface() on the browser 136 // process because it is provided using PpapiInterfaceFactoryManager. We need 137 // to check for that as well. 138 // TODO(cpu): make this more sane. 139 return (strcmp(interface_name, PPB_PDF_INTERFACE) == 0); 140} 141 142bool MakePepperFlashPluginInfo(const base::FilePath& flash_path, 143 const Version& flash_version, 144 bool out_of_process, 145 content::PepperPluginInfo* plugin_info) { 146 if (!flash_version.IsValid()) 147 return false; 148 const std::vector<uint16> ver_nums = flash_version.components(); 149 if (ver_nums.size() < 3) 150 return false; 151 152 plugin_info->is_internal = false; 153 plugin_info->is_out_of_process = out_of_process; 154 plugin_info->path = flash_path; 155 plugin_info->name = content::kFlashPluginName; 156 plugin_info->permissions = kPepperFlashPermissions; 157 158 // The description is like "Shockwave Flash 10.2 r154". 159 plugin_info->description = base::StringPrintf("%s %d.%d r%d", 160 content::kFlashPluginName, ver_nums[0], ver_nums[1], ver_nums[2]); 161 162 plugin_info->version = flash_version.GetString(); 163 164 content::WebPluginMimeType swf_mime_type(content::kFlashPluginSwfMimeType, 165 content::kFlashPluginSwfExtension, 166 content::kFlashPluginName); 167 plugin_info->mime_types.push_back(swf_mime_type); 168 content::WebPluginMimeType spl_mime_type(content::kFlashPluginSplMimeType, 169 content::kFlashPluginSplExtension, 170 content::kFlashPluginName); 171 plugin_info->mime_types.push_back(spl_mime_type); 172 return true; 173} 174 175bool IsPepperFlash(const content::WebPluginInfo& plugin) { 176 // We try to recognize Pepper Flash by the following criteria: 177 // * It is a Pepper plug-in. 178 // * It has the special Flash permissions. 179 return plugin.is_pepper_plugin() && 180 (plugin.pepper_permissions & ppapi::PERMISSION_FLASH); 181} 182 183void RegisterPepperFlashWithChrome(const base::FilePath& path, 184 const Version& version) { 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 186 content::PepperPluginInfo plugin_info; 187 if (!MakePepperFlashPluginInfo(path, version, true, &plugin_info)) 188 return; 189 190 std::vector<content::WebPluginInfo> plugins; 191 PluginService::GetInstance()->GetInternalPlugins(&plugins); 192 for (std::vector<content::WebPluginInfo>::const_iterator it = plugins.begin(); 193 it != plugins.end(); ++it) { 194 if (!IsPepperFlash(*it)) 195 continue; 196 197 // Do it only if the version we're trying to register is newer. 198 Version registered_version(base::UTF16ToUTF8(it->version)); 199 if (registered_version.IsValid() && 200 version.CompareTo(registered_version) <= 0) { 201 return; 202 } 203 204 // If the version is newer, remove the old one first. 205 PluginService::GetInstance()->UnregisterInternalPlugin(it->path); 206 break; 207 } 208 209 PluginService::GetInstance()->RegisterInternalPlugin( 210 plugin_info.ToWebPluginInfo(), true); 211 PluginService::GetInstance()->RefreshPlugins(); 212} 213 214// Returns true if this browser implements one of the interfaces given in 215// |interface_string|, which is a '|'-separated string of interface names. 216bool CheckPepperFlashInterfaceString(const std::string& interface_string) { 217 std::vector<std::string> interface_names; 218 base::SplitString(interface_string, '|', &interface_names); 219 for (size_t i = 0; i < interface_names.size(); i++) { 220 if (SupportsPepperInterface(interface_names[i].c_str())) 221 return true; 222 } 223 return false; 224} 225 226// Returns true if this browser implements all the interfaces that Flash 227// specifies in its component installer manifest. 228bool CheckPepperFlashInterfaces(const base::DictionaryValue& manifest) { 229 const base::ListValue* interface_list = NULL; 230 231 // We don't *require* an interface list, apparently. 232 if (!manifest.GetList("x-ppapi-required-interfaces", &interface_list)) 233 return true; 234 235 for (size_t i = 0; i < interface_list->GetSize(); i++) { 236 std::string interface_string; 237 if (!interface_list->GetString(i, &interface_string)) 238 return false; 239 if (!CheckPepperFlashInterfaceString(interface_string)) 240 return false; 241 } 242 243 return true; 244} 245 246} // namespace 247 248class PepperFlashComponentInstaller : public ComponentInstaller { 249 public: 250 explicit PepperFlashComponentInstaller(const Version& version); 251 252 virtual ~PepperFlashComponentInstaller() {} 253 254 virtual void OnUpdateError(int error) OVERRIDE; 255 256 virtual bool Install(const base::DictionaryValue& manifest, 257 const base::FilePath& unpack_path) OVERRIDE; 258 259 virtual bool GetInstalledFile(const std::string& file, 260 base::FilePath* installed_file) OVERRIDE; 261 262 private: 263 Version current_version_; 264}; 265 266PepperFlashComponentInstaller::PepperFlashComponentInstaller( 267 const Version& version) : current_version_(version) { 268 DCHECK(version.IsValid()); 269} 270 271void PepperFlashComponentInstaller::OnUpdateError(int error) { 272 NOTREACHED() << "Pepper Flash update error: " << error; 273} 274 275bool PepperFlashComponentInstaller::Install( 276 const base::DictionaryValue& manifest, 277 const base::FilePath& unpack_path) { 278 Version version; 279 if (!CheckPepperFlashManifest(manifest, &version)) 280 return false; 281 if (current_version_.CompareTo(version) > 0) 282 return false; 283 if (!base::PathExists(unpack_path.Append( 284 chrome::kPepperFlashPluginFilename))) 285 return false; 286 // Passed the basic tests. Time to install it. 287 base::FilePath path = 288 GetPepperFlashBaseDirectory().AppendASCII(version.GetString()); 289 if (base::PathExists(path)) 290 return false; 291 if (!base::Move(unpack_path, path)) 292 return false; 293 // Installation is done. Now tell the rest of chrome. Both the path service 294 // and to the plugin service. 295 current_version_ = version; 296 PathService::Override(chrome::DIR_PEPPER_FLASH_PLUGIN, path); 297 path = path.Append(chrome::kPepperFlashPluginFilename); 298 BrowserThread::PostTask( 299 BrowserThread::UI, FROM_HERE, 300 base::Bind(&RegisterPepperFlashWithChrome, path, version)); 301 return true; 302} 303 304bool PepperFlashComponentInstaller::GetInstalledFile( 305 const std::string& file, base::FilePath* installed_file) { 306 return false; 307} 308 309bool CheckPepperFlashManifest(const base::DictionaryValue& manifest, 310 Version* version_out) { 311 std::string name; 312 manifest.GetStringASCII("name", &name); 313 // TODO(viettrungluu): Support WinFlapper for now, while we change the format 314 // of the manifest. (Should be safe to remove checks for "WinFlapper" in, say, 315 // Nov. 2011.) crbug.com/98458 316 if (name != kPepperFlashManifestName && name != "WinFlapper") 317 return false; 318 319 std::string proposed_version; 320 manifest.GetStringASCII("version", &proposed_version); 321 Version version(proposed_version.c_str()); 322 if (!version.IsValid()) 323 return false; 324 325 if (!CheckPepperFlashInterfaces(manifest)) 326 return false; 327 328 // TODO(viettrungluu): See above TODO. 329 if (name == "WinFlapper") { 330 *version_out = version; 331 return true; 332 } 333 334 std::string os; 335 manifest.GetStringASCII("x-ppapi-os", &os); 336 if (os != kPepperFlashOperatingSystem) 337 return false; 338 339 std::string arch; 340 manifest.GetStringASCII("x-ppapi-arch", &arch); 341 if (arch != kPepperFlashArch) 342 return false; 343 344 *version_out = version; 345 return true; 346} 347 348namespace { 349 350#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX) 351void FinishPepperFlashUpdateRegistration(ComponentUpdateService* cus, 352 const Version& version) { 353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 354 CrxComponent pepflash; 355 pepflash.name = "pepper_flash"; 356 pepflash.installer = new PepperFlashComponentInstaller(version); 357 pepflash.version = version; 358 pepflash.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]); 359 if (cus->RegisterComponent(pepflash) != ComponentUpdateService::kOk) { 360 NOTREACHED() << "Pepper Flash component registration failed."; 361 } 362} 363 364void StartPepperFlashUpdateRegistration(ComponentUpdateService* cus) { 365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 366 base::FilePath path = GetPepperFlashBaseDirectory(); 367 if (!base::PathExists(path)) { 368 if (!base::CreateDirectory(path)) { 369 NOTREACHED() << "Could not create Pepper Flash directory."; 370 return; 371 } 372 } 373 374 Version version(kNullVersion); 375 std::vector<base::FilePath> older_dirs; 376 if (GetPepperFlashDirectory(&path, &version, &older_dirs)) { 377 path = path.Append(chrome::kPepperFlashPluginFilename); 378 if (base::PathExists(path)) { 379 BrowserThread::PostTask( 380 BrowserThread::UI, FROM_HERE, 381 base::Bind(&RegisterPepperFlashWithChrome, path, version)); 382 } else { 383 version = Version(kNullVersion); 384 } 385 } 386 387 BrowserThread::PostTask( 388 BrowserThread::UI, FROM_HERE, 389 base::Bind(&FinishPepperFlashUpdateRegistration, cus, version)); 390 391 // Remove older versions of Pepper Flash. 392 for (std::vector<base::FilePath>::iterator iter = older_dirs.begin(); 393 iter != older_dirs.end(); ++iter) { 394 base::DeleteFile(*iter, true); 395 } 396} 397#endif // defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX) 398 399} // namespace 400 401void RegisterPepperFlashComponent(ComponentUpdateService* cus) { 402#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX) 403 // Component updated flash supersedes bundled flash therefore if that one 404 // is disabled then this one should never install. 405 CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 406 if (cmd_line->HasSwitch(switches::kDisableBundledPpapiFlash)) 407 return; 408 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 409 base::Bind(&StartPepperFlashUpdateRegistration, cus)); 410#endif 411} 412 413} // namespace component_updater 414 415