1// Copyright 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/nacl_host/nacl_browser_delegate_impl.h"
6
7#include "base/path_service.h"
8#include "base/strings/string_split.h"
9#include "chrome/browser/browser_process.h"
10#include "chrome/browser/component_updater/pnacl/pnacl_component_installer.h"
11#include "chrome/browser/extensions/extension_service.h"
12#include "chrome/browser/nacl_host/nacl_infobar_delegate.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/profiles/profile_manager.h"
15#include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h"
16#include "chrome/common/chrome_paths.h"
17#include "chrome/common/chrome_paths_internal.h"
18#include "chrome/common/chrome_version_info.h"
19#include "chrome/common/logging_chrome.h"
20#include "chrome/common/pepper_permission_util.h"
21#include "content/public/browser/browser_thread.h"
22#include "extensions/browser/extension_system.h"
23#include "extensions/browser/info_map.h"
24#include "extensions/browser/process_manager.h"
25#include "extensions/common/constants.h"
26#include "extensions/common/extension.h"
27#include "extensions/common/url_pattern.h"
28#include "ppapi/c/private/ppb_nacl_private.h"
29#include "url/gurl.h"
30
31namespace {
32
33// These are temporarily needed for testing non-sfi mode on ChromeOS without
34// passing command-line arguments to Chrome.
35const char* const kAllowedNonSfiOrigins[] = {
36    "6EAED1924DB611B6EEF2A664BD077BE7EAD33B8F",  // see http://crbug.com/355141
37    "4EB74897CB187C7633357C2FE832E0AD6A44883A"   // see http://crbug.com/355141
38};
39
40// Handles an extension's NaCl process transitioning in or out of idle state by
41// relaying the state to the extension's process manager.
42//
43// A NaCl instance, when active (making PPAPI calls or receiving callbacks),
44// sends keepalive IPCs to the browser process BrowserPpapiHost at a throttled
45// rate. The content::BrowserPpapiHost passes context information up to the
46// chrome level NaClProcessHost where we use the instance's context to find the
47// associated extension process manager.
48//
49// There is a 1:many relationship for extension:nacl-embeds, but only a
50// 1:1 relationship for NaClProcessHost:PP_Instance. The content layer doesn't
51// rely on this knowledge because it routes messages for ppapi non-nacl
52// instances as well, though they won't have callbacks set. Here the 1:1
53// assumption is made and DCHECKed.
54void OnKeepaliveOnUIThread(
55    const content::BrowserPpapiHost::OnKeepaliveInstanceData& instance_data,
56    const base::FilePath& profile_data_directory) {
57  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
58
59  // Only one instance will exist for NaCl embeds, even when more than one
60  // embed of the same plugin exists on the same page.
61  DCHECK(instance_data.size() == 1);
62  if (instance_data.size() < 1)
63    return;
64
65#if defined(ENABLE_EXTENSIONS)
66  extensions::ProcessManager::OnKeepaliveFromPlugin(
67      instance_data[0].render_process_id,
68      instance_data[0].render_frame_id,
69      instance_data[0].document_url.host());
70#endif
71}
72
73// Calls OnKeepaliveOnUIThread on UI thread.
74void OnKeepalive(
75    const content::BrowserPpapiHost::OnKeepaliveInstanceData& instance_data,
76    const base::FilePath& profile_data_directory) {
77  DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
78  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
79                                   base::Bind(&OnKeepaliveOnUIThread,
80                                              instance_data,
81                                              profile_data_directory));
82}
83
84}  // namespace
85
86NaClBrowserDelegateImpl::NaClBrowserDelegateImpl(
87    ProfileManager* profile_manager)
88    : profile_manager_(profile_manager), inverse_debug_patterns_(false) {
89  DCHECK(profile_manager_);
90  for (size_t i = 0; i < arraysize(kAllowedNonSfiOrigins); ++i) {
91    allowed_nonsfi_origins_.insert(kAllowedNonSfiOrigins[i]);
92  }
93}
94
95NaClBrowserDelegateImpl::~NaClBrowserDelegateImpl() {
96}
97
98void NaClBrowserDelegateImpl::ShowMissingArchInfobar(int render_process_id,
99                                                     int render_view_id) {
100  content::BrowserThread::PostTask(
101      content::BrowserThread::UI, FROM_HERE,
102      base::Bind(&NaClInfoBarDelegate::Create, render_process_id,
103                 render_view_id));
104}
105
106bool NaClBrowserDelegateImpl::DialogsAreSuppressed() {
107  return logging::DialogsAreSuppressed();
108}
109
110bool NaClBrowserDelegateImpl::GetCacheDirectory(base::FilePath* cache_dir) {
111  base::FilePath user_data_dir;
112  if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
113    return false;
114  chrome::GetUserCacheDirectory(user_data_dir, cache_dir);
115  return true;
116}
117
118bool NaClBrowserDelegateImpl::GetPluginDirectory(base::FilePath* plugin_dir) {
119  return PathService::Get(chrome::DIR_INTERNAL_PLUGINS, plugin_dir);
120}
121
122bool NaClBrowserDelegateImpl::GetPnaclDirectory(base::FilePath* pnacl_dir) {
123  return PathService::Get(chrome::DIR_PNACL_COMPONENT, pnacl_dir);
124}
125
126bool NaClBrowserDelegateImpl::GetUserDirectory(base::FilePath* user_dir) {
127  return PathService::Get(chrome::DIR_USER_DATA, user_dir);
128}
129
130std::string NaClBrowserDelegateImpl::GetVersionString() const {
131  return chrome::VersionInfo().CreateVersionString();
132}
133
134ppapi::host::HostFactory* NaClBrowserDelegateImpl::CreatePpapiHostFactory(
135    content::BrowserPpapiHost* ppapi_host) {
136  return new chrome::ChromeBrowserPepperHostFactory(ppapi_host);
137}
138
139void NaClBrowserDelegateImpl::SetDebugPatterns(std::string debug_patterns) {
140  if (!debug_patterns.empty() && debug_patterns[0] == '!') {
141    inverse_debug_patterns_ = true;
142    debug_patterns.erase(0, 1);
143  }
144  if (debug_patterns.empty()) {
145    return;
146  }
147  std::vector<std::string> patterns;
148  base::SplitString(debug_patterns, ',', &patterns);
149  for (std::vector<std::string>::iterator iter = patterns.begin();
150       iter != patterns.end(); ++iter) {
151    // Allow chrome:// schema, which is used to filter out the internal
152    // PNaCl translator. Also allow chrome-extension:// schema (which
153    // can have NaCl modules). The default is to disallow these schema
154    // since they can be dangerous in the context of chrome extension
155    // permissions, but they are okay here, for NaCl GDB avoidance.
156    URLPattern pattern(URLPattern::SCHEME_ALL);
157    if (pattern.Parse(*iter) == URLPattern::PARSE_SUCCESS) {
158      // If URL pattern has scheme equal to *, Parse method resets valid
159      // schemes mask to http and https only, so we need to reset it after
160      // Parse to re-include chrome-extension and chrome schema.
161      pattern.SetValidSchemes(URLPattern::SCHEME_ALL);
162      debug_patterns_.push_back(pattern);
163    }
164  }
165}
166
167bool NaClBrowserDelegateImpl::URLMatchesDebugPatterns(
168    const GURL& manifest_url) {
169  // Empty patterns are forbidden so we ignore them.
170  if (debug_patterns_.empty()) {
171    return true;
172  }
173  bool matches = false;
174  for (std::vector<URLPattern>::iterator iter = debug_patterns_.begin();
175       iter != debug_patterns_.end(); ++iter) {
176    if (iter->MatchesURL(manifest_url)) {
177      matches = true;
178      break;
179    }
180  }
181  if (inverse_debug_patterns_) {
182    return !matches;
183  } else {
184    return matches;
185  }
186}
187
188// This function is security sensitive.  Be sure to check with a security
189// person before you modify it.
190bool NaClBrowserDelegateImpl::MapUrlToLocalFilePath(
191    const GURL& file_url,
192    bool use_blocking_api,
193    const base::FilePath& profile_directory,
194    base::FilePath* file_path) {
195#if defined(ENABLE_EXTENSIONS)
196  scoped_refptr<extensions::InfoMap> extension_info_map =
197      GetExtensionInfoMap(profile_directory);
198  return extension_info_map->MapUrlToLocalFilePath(
199      file_url, use_blocking_api, file_path);
200#else
201  return false;
202#endif
203}
204
205content::BrowserPpapiHost::OnKeepaliveCallback
206NaClBrowserDelegateImpl::GetOnKeepaliveCallback() {
207  return base::Bind(&OnKeepalive);
208}
209
210bool NaClBrowserDelegateImpl::IsNonSfiModeAllowed(
211    const base::FilePath& profile_directory,
212    const GURL& manifest_url) {
213#if defined(ENABLE_EXTENSIONS)
214  const extensions::ExtensionSet* extension_set =
215      &GetExtensionInfoMap(profile_directory)->extensions();
216  return chrome::IsExtensionOrSharedModuleWhitelisted(
217      manifest_url, extension_set, allowed_nonsfi_origins_);
218#else
219  return false;
220#endif
221}
222
223#if defined(ENABLE_EXTENSIONS)
224scoped_refptr<extensions::InfoMap> NaClBrowserDelegateImpl::GetExtensionInfoMap(
225    const base::FilePath& profile_directory) {
226  // Get the profile associated with the renderer.
227  Profile* profile = profile_manager_->GetProfileByPath(profile_directory);
228  DCHECK(profile);
229  scoped_refptr<extensions::InfoMap> extension_info_map =
230      extensions::ExtensionSystem::Get(profile)->info_map();
231  DCHECK(extension_info_map.get());
232  return extension_info_map;
233}
234#endif
235