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