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