1// Copyright (c) 2012 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/extensions/api/messaging/native_process_launcher.h" 6 7#include "base/basictypes.h" 8#include "base/bind.h" 9#include "base/callback.h" 10#include "base/command_line.h" 11#include "base/logging.h" 12#include "base/memory/ref_counted.h" 13#include "base/path_service.h" 14#include "base/strings/string_number_conversions.h" 15#include "base/strings/string_split.h" 16#include "base/threading/sequenced_worker_pool.h" 17#include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h" 18#include "chrome/common/chrome_paths.h" 19#include "chrome/common/chrome_switches.h" 20#include "content/public/browser/browser_thread.h" 21#include "url/gurl.h" 22 23namespace extensions { 24 25namespace { 26 27// Name of the command line switch used to pass handle of the native view to 28// the native messaging host. 29const char kParentWindowSwitchName[] = "parent-window"; 30 31base::FilePath GetHostManifestPathFromCommandLine( 32 const std::string& native_host_name) { 33 const std::string& value = 34 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 35 switches::kNativeMessagingHosts); 36 if (value.empty()) 37 return base::FilePath(); 38 39 std::vector<std::string> hosts; 40 base::SplitString(value, ',', &hosts); 41 for (size_t i = 0; i < hosts.size(); ++i) { 42 std::vector<std::string> key_and_value; 43 base::SplitString(hosts[i], '=', &key_and_value); 44 if (key_and_value.size() != 2) 45 continue; 46 if (key_and_value[0] == native_host_name) 47 return base::FilePath::FromUTF8Unsafe(key_and_value[1]); 48 } 49 50 return base::FilePath(); 51} 52 53 54// Default implementation on NativeProcessLauncher interface. 55class NativeProcessLauncherImpl : public NativeProcessLauncher { 56 public: 57 explicit NativeProcessLauncherImpl(gfx::NativeView native_view); 58 virtual ~NativeProcessLauncherImpl(); 59 60 virtual void Launch(const GURL& origin, 61 const std::string& native_host_name, 62 LaunchedCallback callback) const OVERRIDE; 63 64 private: 65 class Core : public base::RefCountedThreadSafe<Core> { 66 public: 67 explicit Core(gfx::NativeView native_view); 68 void Launch(const GURL& origin, 69 const std::string& native_host_name, 70 LaunchedCallback callback); 71 void Detach(); 72 73 private: 74 friend class base::RefCountedThreadSafe<Core>; 75 virtual ~Core(); 76 77 void DoLaunchOnThreadPool(const GURL& origin, 78 const std::string& native_host_name, 79 LaunchedCallback callback); 80 void PostErrorResult(const LaunchedCallback& callback, LaunchResult error); 81 void PostResult(const LaunchedCallback& callback, 82 base::PlatformFile read_file, 83 base::PlatformFile write_file); 84 void CallCallbackOnIOThread(LaunchedCallback callback, 85 LaunchResult result, 86 base::PlatformFile read_file, 87 base::PlatformFile write_file); 88 89 bool detached_; 90 91 // Handle of the native view corrsponding to the extension. 92 gfx::NativeView native_view_; 93 94 DISALLOW_COPY_AND_ASSIGN(Core); 95 }; 96 97 scoped_refptr<Core> core_; 98 99 DISALLOW_COPY_AND_ASSIGN(NativeProcessLauncherImpl); 100}; 101 102NativeProcessLauncherImpl::Core::Core(gfx::NativeView native_view) 103 : detached_(false), 104 native_view_(native_view) { 105} 106 107NativeProcessLauncherImpl::Core::~Core() { 108 DCHECK(detached_); 109} 110 111void NativeProcessLauncherImpl::Core::Detach() { 112 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 113 detached_ = true; 114} 115 116void NativeProcessLauncherImpl::Core::Launch( 117 const GURL& origin, 118 const std::string& native_host_name, 119 LaunchedCallback callback) { 120 content::BrowserThread::PostBlockingPoolTask( 121 FROM_HERE, base::Bind(&Core::DoLaunchOnThreadPool, this, 122 origin, native_host_name, callback)); 123} 124 125void NativeProcessLauncherImpl::Core::DoLaunchOnThreadPool( 126 const GURL& origin, 127 const std::string& native_host_name, 128 LaunchedCallback callback) { 129 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 130 131 if (!NativeMessagingHostManifest::IsValidName(native_host_name)) { 132 PostErrorResult(callback, RESULT_INVALID_NAME); 133 return; 134 } 135 136 std::string error_message; 137 scoped_ptr<NativeMessagingHostManifest> manifest; 138 139 // First check if the manifest location is specified in the command line. 140 base::FilePath manifest_path = 141 GetHostManifestPathFromCommandLine(native_host_name); 142 if (manifest_path.empty()) 143 manifest_path = FindManifest(native_host_name, &error_message); 144 145 if (manifest_path.empty()) { 146 LOG(ERROR) << "Can't find manifest for native messaging host " 147 << native_host_name; 148 PostErrorResult(callback, RESULT_NOT_FOUND); 149 return; 150 } 151 152 manifest = NativeMessagingHostManifest::Load(manifest_path, &error_message); 153 154 if (!manifest) { 155 LOG(ERROR) << "Failed to load manifest for native messaging host " 156 << native_host_name << ": " << error_message; 157 PostErrorResult(callback, RESULT_NOT_FOUND); 158 return; 159 } 160 161 if (manifest->name() != native_host_name) { 162 LOG(ERROR) << "Failed to load manifest for native messaging host " 163 << native_host_name 164 << ": Invalid name specified in the manifest."; 165 PostErrorResult(callback, RESULT_NOT_FOUND); 166 return; 167 } 168 169 if (!manifest->allowed_origins().MatchesSecurityOrigin(origin)) { 170 // Not an allowed origin. 171 PostErrorResult(callback, RESULT_FORBIDDEN); 172 return; 173 } 174 175 base::FilePath host_path = manifest->path(); 176 if (!host_path.IsAbsolute()) { 177 // On Windows host path is allowed to be relative to the location of the 178 // manifest file. On all other platforms the path must be absolute. 179#if defined(OS_WIN) 180 host_path = manifest_path.DirName().Append(host_path); 181#else // defined(OS_WIN) 182 LOG(ERROR) << "Native messaging host path must be absolute for " 183 << native_host_name; 184 PostErrorResult(callback, RESULT_NOT_FOUND); 185 return; 186#endif // !defined(OS_WIN) 187 } 188 189 CommandLine command_line(host_path); 190 command_line.AppendArg(origin.spec()); 191 192 // Pass handle of the native view window to the native messaging host. This 193 // way the host will be able to create properly focused UI windows. 194#if defined(OS_WIN) 195 int64 window_handle = reinterpret_cast<intptr_t>(native_view_); 196 command_line.AppendSwitchASCII(kParentWindowSwitchName, 197 base::Int64ToString(window_handle)); 198#endif // !defined(OS_WIN) 199 200 base::PlatformFile read_file; 201 base::PlatformFile write_file; 202 if (NativeProcessLauncher::LaunchNativeProcess( 203 command_line, &read_file, &write_file)) { 204 PostResult(callback, read_file, write_file); 205 } else { 206 PostErrorResult(callback, RESULT_FAILED_TO_START); 207 } 208} 209 210void NativeProcessLauncherImpl::Core::CallCallbackOnIOThread( 211 LaunchedCallback callback, 212 LaunchResult result, 213 base::PlatformFile read_file, 214 base::PlatformFile write_file) { 215 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 216 if (detached_) { 217 if (read_file != base::kInvalidPlatformFileValue) 218 base::ClosePlatformFile(read_file); 219 if (write_file != base::kInvalidPlatformFileValue) 220 base::ClosePlatformFile(write_file); 221 return; 222 } 223 224 callback.Run(result, read_file, write_file); 225} 226 227void NativeProcessLauncherImpl::Core::PostErrorResult( 228 const LaunchedCallback& callback, 229 LaunchResult error) { 230 content::BrowserThread::PostTask( 231 content::BrowserThread::IO, FROM_HERE, 232 base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread, 233 this, callback, error, 234 base::kInvalidPlatformFileValue, 235 base::kInvalidPlatformFileValue)); 236} 237 238void NativeProcessLauncherImpl::Core::PostResult( 239 const LaunchedCallback& callback, 240 base::PlatformFile read_file, 241 base::PlatformFile write_file) { 242 content::BrowserThread::PostTask( 243 content::BrowserThread::IO, FROM_HERE, 244 base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread, 245 this, callback, RESULT_SUCCESS, read_file, write_file)); 246} 247 248NativeProcessLauncherImpl::NativeProcessLauncherImpl( 249 gfx::NativeView native_view) 250 : core_(new Core(native_view)) { 251} 252 253NativeProcessLauncherImpl::~NativeProcessLauncherImpl() { 254 core_->Detach(); 255} 256 257void NativeProcessLauncherImpl::Launch(const GURL& origin, 258 const std::string& native_host_name, 259 LaunchedCallback callback) const { 260 core_->Launch(origin, native_host_name, callback); 261} 262 263} // namespace 264 265// static 266scoped_ptr<NativeProcessLauncher> NativeProcessLauncher::CreateDefault( 267 gfx::NativeView native_view) { 268 return scoped_ptr<NativeProcessLauncher>( 269 new NativeProcessLauncherImpl(native_view)); 270} 271 272} // namespace extensions 273