1// Copyright 2014 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 "extensions/shell/browser/shell_nacl_browser_delegate.h"
6
7#include <string>
8
9#include "base/base_paths.h"
10#include "base/command_line.h"
11#include "base/path_service.h"
12#include "base/strings/string_split.h"
13#include "base/strings/string_util.h"
14#include "content/public/browser/browser_context.h"
15#include "content/public/browser/browser_thread.h"
16#include "content/public/browser/render_frame_host.h"
17#include "content/public/browser/site_instance.h"
18#include "extensions/browser/extension_system.h"
19#include "extensions/browser/info_map.h"
20#include "extensions/browser/process_manager.h"
21#include "extensions/common/constants.h"
22#include "extensions/common/extension.h"
23#include "extensions/common/url_pattern.h"
24#include "extensions/shell/common/version.h"  // Generated file.
25#include "ppapi/c/private/ppb_nacl_private.h"
26#include "url/gurl.h"
27
28using content::BrowserContext;
29using content::BrowserThread;
30using content::BrowserPpapiHost;
31
32namespace extensions {
33namespace {
34
35// Handles an extension's NaCl process transitioning in or out of idle state by
36// relaying the state to the extension's process manager. See Chrome's
37// NaClBrowserDelegateImpl for another example.
38void OnKeepaliveOnUIThread(
39    const BrowserPpapiHost::OnKeepaliveInstanceData& instance_data,
40    const base::FilePath& profile_data_directory) {
41  DCHECK_CURRENTLY_ON(BrowserThread::UI);
42
43  // Only one instance will exist for NaCl embeds, even when more than one
44  // embed of the same plugin exists on the same page.
45  DCHECK(instance_data.size() == 1);
46  if (instance_data.size() < 1)
47    return;
48
49  ProcessManager::OnKeepaliveFromPlugin(instance_data[0].render_process_id,
50                                        instance_data[0].render_frame_id,
51                                        instance_data[0].document_url.host());
52}
53
54// Calls OnKeepaliveOnUIThread on UI thread.
55void OnKeepalive(const BrowserPpapiHost::OnKeepaliveInstanceData& instance_data,
56                 const base::FilePath& profile_data_directory) {
57  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
58  BrowserThread::PostTask(
59      BrowserThread::UI,
60      FROM_HERE,
61      base::Bind(
62          &OnKeepaliveOnUIThread, instance_data, profile_data_directory));
63}
64
65}  // namespace
66
67ShellNaClBrowserDelegate::ShellNaClBrowserDelegate(BrowserContext* context)
68    : browser_context_(context) {
69  DCHECK(browser_context_);
70}
71
72ShellNaClBrowserDelegate::~ShellNaClBrowserDelegate() {
73}
74
75void ShellNaClBrowserDelegate::ShowMissingArchInfobar(int render_process_id,
76                                                      int render_view_id) {
77  // app_shell does not have infobars.
78  LOG(ERROR) << "Missing architecture for pid " << render_process_id;
79}
80
81bool ShellNaClBrowserDelegate::DialogsAreSuppressed() {
82  return false;
83}
84
85bool ShellNaClBrowserDelegate::GetCacheDirectory(base::FilePath* cache_dir) {
86  // Just use the general cache directory, not a subdirectory like Chrome does.
87#if defined(OS_POSIX)
88  return PathService::Get(base::DIR_CACHE, cache_dir);
89#elif defined(OS_WIN)
90  // TODO(yoz): Find an appropriate persistent directory to use here.
91  return PathService::Get(base::DIR_TEMP, cache_dir);
92#endif
93}
94
95bool ShellNaClBrowserDelegate::GetPluginDirectory(base::FilePath* plugin_dir) {
96  // On Posix, plugins are in the module directory.
97  return PathService::Get(base::DIR_MODULE, plugin_dir);
98}
99
100bool ShellNaClBrowserDelegate::GetPnaclDirectory(base::FilePath* pnacl_dir) {
101  // On Posix, the pnacl directory is inside the plugin directory.
102  base::FilePath plugin_dir;
103  if (!GetPluginDirectory(&plugin_dir))
104    return false;
105  *pnacl_dir = plugin_dir.Append(FILE_PATH_LITERAL("pnacl"));
106  return true;
107}
108
109bool ShellNaClBrowserDelegate::GetUserDirectory(base::FilePath* user_dir) {
110  base::FilePath path = browser_context_->GetPath();
111  if (!path.empty()) {
112    *user_dir = path;
113    return true;
114  }
115  return false;
116}
117
118std::string ShellNaClBrowserDelegate::GetVersionString() const {
119  // A version change triggers an update of the NaCl validation caches.
120  // Example version: "39.0.2129.0 (290550)".
121  return PRODUCT_VERSION " (" LAST_CHANGE ")";
122}
123
124ppapi::host::HostFactory* ShellNaClBrowserDelegate::CreatePpapiHostFactory(
125    content::BrowserPpapiHost* ppapi_host) {
126  return NULL;
127}
128
129void ShellNaClBrowserDelegate::SetDebugPatterns(std::string debug_patterns) {
130  // No debugger support. Developers should use Chrome for debugging.
131}
132
133bool ShellNaClBrowserDelegate::URLMatchesDebugPatterns(
134    const GURL& manifest_url) {
135  // No debugger support. Developers should use Chrome for debugging.
136  return false;
137}
138
139// This function is security sensitive.  Be sure to check with a security
140// person before you modify it.
141// TODO(jamescook): Refactor this code into the extensions module so it can
142// be shared with Chrome's NaClBrowserDelegateImpl. http://crbug.com/403017
143bool ShellNaClBrowserDelegate::MapUrlToLocalFilePath(
144    const GURL& file_url,
145    bool use_blocking_api,
146    const base::FilePath& profile_directory,
147    base::FilePath* file_path) {
148  scoped_refptr<InfoMap> info_map =
149      ExtensionSystem::Get(browser_context_)->info_map();
150  // Check that the URL is recognized by the extension system.
151  const Extension* extension =
152      info_map->extensions().GetExtensionOrAppByURL(file_url);
153  if (!extension)
154    return false;
155
156  // This is a short-cut which avoids calling a blocking file operation
157  // (GetFilePath()), so that this can be called on the IO thread. It only
158  // handles a subset of the urls.
159  if (!use_blocking_api) {
160    if (file_url.SchemeIs(kExtensionScheme)) {
161      std::string path = file_url.path();
162      base::TrimString(path, "/", &path);  // Remove first slash
163      *file_path = extension->path().AppendASCII(path);
164      return true;
165    }
166    return false;
167  }
168
169  // Check that the URL references a resource in the extension.
170  // NOTE: app_shell does not support shared modules.
171  ExtensionResource resource = extension->GetResource(file_url.path());
172  if (resource.empty())
173    return false;
174
175  // GetFilePath is a blocking function call.
176  const base::FilePath resource_file_path = resource.GetFilePath();
177  if (resource_file_path.empty())
178    return false;
179
180  *file_path = resource_file_path;
181  return true;
182}
183
184content::BrowserPpapiHost::OnKeepaliveCallback
185ShellNaClBrowserDelegate::GetOnKeepaliveCallback() {
186  return base::Bind(&OnKeepalive);
187}
188
189bool ShellNaClBrowserDelegate::IsNonSfiModeAllowed(
190    const base::FilePath& profile_directory,
191    const GURL& manifest_url) {
192  return false;
193}
194
195}  // namespace extensions
196