OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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/plugin_process_host.h" | |
6 | |
7 #if defined(OS_WIN) | |
8 #include <windows.h> | |
9 #elif defined(OS_POSIX) | |
10 #include <utility> // for pair<> | |
11 #endif | |
12 | |
13 #include <vector> | |
14 | |
15 #include "app/app_switches.h" | |
16 #include "base/command_line.h" | |
17 #include "base/file_path.h" | |
18 #include "base/file_util.h" | |
19 #include "base/logging.h" | |
20 #include "base/path_service.h" | |
21 #include "base/string_util.h" | |
22 #include "base/utf_string_conversions.h" | |
23 #include "chrome/browser/browser_thread.h" | |
24 #include "chrome/browser/child_process_security_policy.h" | |
25 #include "chrome/browser/chrome_plugin_browsing_context.h" | |
26 #include "chrome/browser/net/url_request_tracking.h" | |
27 #include "chrome/browser/plugin_download_helper.h" | |
28 #include "chrome/browser/plugin_service.h" | |
29 #include "chrome/browser/profiles/profile.h" | |
30 #include "chrome/browser/renderer_host/resource_dispatcher_host.h" | |
31 #include "chrome/browser/renderer_host/resource_message_filter.h" | |
32 #include "chrome/common/chrome_paths.h" | |
33 #include "chrome/common/chrome_plugin_lib.h" | |
34 #include "chrome/common/chrome_switches.h" | |
35 #include "chrome/common/logging_chrome.h" | |
36 #include "chrome/common/net/url_request_context_getter.h" | |
37 #include "chrome/common/plugin_messages.h" | |
38 #include "chrome/common/render_messages.h" | |
39 #include "chrome/common/render_messages_params.h" | |
40 #include "ipc/ipc_switches.h" | |
41 #include "net/base/cookie_store.h" | |
42 #include "net/base/io_buffer.h" | |
43 #include "net/url_request/url_request.h" | |
44 #include "net/url_request/url_request_context.h" | |
45 #include "ui/base/ui_base_switches.h" | |
46 #include "ui/gfx/native_widget_types.h" | |
47 | |
48 #if defined(USE_X11) | |
49 #include "ui/gfx/gtk_native_view_id_manager.h" | |
50 #endif | |
51 | |
52 #if defined(OS_MACOSX) | |
53 #include "base/mac/mac_util.h" | |
54 #include "chrome/common/plugin_carbon_interpose_constants_mac.h" | |
55 #include "ui/gfx/rect.h" | |
56 #endif | |
57 | |
58 static const char kDefaultPluginFinderURL[] = | |
59 "https://dl-ssl.google.com/edgedl/chrome/plugins/plugins2.xml"; | |
60 | |
61 namespace { | |
62 | |
63 // Helper class that we pass to ResourceMessageFilter so that it can find the | |
64 // right net::URLRequestContext for a request. | |
65 class PluginURLRequestContextOverride | |
66 : public ResourceMessageFilter::URLRequestContextOverride { | |
67 public: | |
68 PluginURLRequestContextOverride() { | |
69 } | |
70 | |
71 virtual net::URLRequestContext* GetRequestContext( | |
72 const ViewHostMsg_Resource_Request& resource_request) { | |
73 return CPBrowsingContextManager::GetInstance()->ToURLRequestContext( | |
74 resource_request.request_context); | |
75 } | |
76 | |
77 private: | |
78 virtual ~PluginURLRequestContextOverride() {} | |
79 }; | |
80 | |
81 } // namespace | |
82 | |
83 #if defined(OS_WIN) | |
84 void PluginProcessHost::OnPluginWindowDestroyed(HWND window, HWND parent) { | |
85 // The window is destroyed at this point, we just care about its parent, which | |
86 // is the intermediate window we created. | |
87 std::set<HWND>::iterator window_index = | |
88 plugin_parent_windows_set_.find(parent); | |
89 if (window_index == plugin_parent_windows_set_.end()) | |
90 return; | |
91 | |
92 plugin_parent_windows_set_.erase(window_index); | |
93 PostMessage(parent, WM_CLOSE, 0, 0); | |
94 } | |
95 | |
96 void PluginProcessHost::OnDownloadUrl(const std::string& url, | |
97 int source_pid, | |
98 gfx::NativeWindow caller_window) { | |
99 PluginDownloadUrlHelper* download_url_helper = | |
100 new PluginDownloadUrlHelper(url, source_pid, caller_window, NULL); | |
101 download_url_helper->InitiateDownload( | |
102 Profile::GetDefaultRequestContext()->GetURLRequestContext()); | |
103 } | |
104 | |
105 void PluginProcessHost::AddWindow(HWND window) { | |
106 plugin_parent_windows_set_.insert(window); | |
107 } | |
108 | |
109 #endif // defined(OS_WIN) | |
110 | |
111 #if defined(TOOLKIT_USES_GTK) | |
112 void PluginProcessHost::OnMapNativeViewId(gfx::NativeViewId id, | |
113 gfx::PluginWindowHandle* output) { | |
114 *output = 0; | |
115 GtkNativeViewManager::GetInstance()->GetXIDForId(output, id); | |
116 } | |
117 #endif // defined(TOOLKIT_USES_GTK) | |
118 | |
119 PluginProcessHost::PluginProcessHost() | |
120 : BrowserChildProcessHost( | |
121 PLUGIN_PROCESS, | |
122 PluginService::GetInstance()->resource_dispatcher_host(), | |
123 new PluginURLRequestContextOverride()), | |
124 ALLOW_THIS_IN_INITIALIZER_LIST(resolve_proxy_msg_helper_(this, NULL)) | |
125 #if defined(OS_MACOSX) | |
126 , plugin_cursor_visible_(true) | |
127 #endif | |
128 { | |
129 } | |
130 | |
131 PluginProcessHost::~PluginProcessHost() { | |
132 #if defined(OS_WIN) | |
133 // We erase HWNDs from the plugin_parent_windows_set_ when we receive a | |
134 // notification that the window is being destroyed. If we don't receive this | |
135 // notification and the PluginProcessHost instance is being destroyed, it | |
136 // means that the plugin process crashed. We paint a sad face in this case in | |
137 // the renderer process. To ensure that the sad face shows up, and we don't | |
138 // leak HWNDs, we should destroy existing plugin parent windows. | |
139 std::set<HWND>::iterator window_index; | |
140 for (window_index = plugin_parent_windows_set_.begin(); | |
141 window_index != plugin_parent_windows_set_.end(); | |
142 window_index++) { | |
143 PostMessage(*window_index, WM_CLOSE, 0, 0); | |
144 } | |
145 #elif defined(OS_MACOSX) | |
146 // If the plugin process crashed but had fullscreen windows open at the time, | |
147 // make sure that the menu bar is visible. | |
148 std::set<uint32>::iterator window_index; | |
149 for (window_index = plugin_fullscreen_windows_set_.begin(); | |
150 window_index != plugin_fullscreen_windows_set_.end(); | |
151 window_index++) { | |
152 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
153 base::mac::ReleaseFullScreen(base::mac::kFullScreenModeHideAll); | |
154 } else { | |
155 BrowserThread::PostTask( | |
156 BrowserThread::UI, FROM_HERE, | |
157 NewRunnableFunction(base::mac::ReleaseFullScreen, | |
158 base::mac::kFullScreenModeHideAll)); | |
159 } | |
160 } | |
161 // If the plugin hid the cursor, reset that. | |
162 if (!plugin_cursor_visible_) { | |
163 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
164 base::mac::SetCursorVisibility(true); | |
165 } else { | |
166 BrowserThread::PostTask( | |
167 BrowserThread::UI, FROM_HERE, | |
168 NewRunnableFunction(base::mac::SetCursorVisibility, | |
169 true)); | |
170 } | |
171 } | |
172 #endif | |
173 // Cancel all pending and sent requests. | |
174 CancelRequests(); | |
175 } | |
176 | |
177 bool PluginProcessHost::Init(const webkit::npapi::WebPluginInfo& info, | |
178 const std::string& locale) { | |
179 info_ = info; | |
180 set_name(UTF16ToWideHack(info_.name)); | |
181 set_version(UTF16ToWideHack(info_.version)); | |
182 | |
183 if (!CreateChannel()) | |
184 return false; | |
185 | |
186 // Build command line for plugin. When we have a plugin launcher, we can't | |
187 // allow "self" on linux and we need the real file path. | |
188 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); | |
189 CommandLine::StringType plugin_launcher = | |
190 browser_command_line.GetSwitchValueNative(switches::kPluginLauncher); | |
191 FilePath exe_path = GetChildPath(plugin_launcher.empty()); | |
192 if (exe_path.empty()) | |
193 return false; | |
194 | |
195 CommandLine* cmd_line = new CommandLine(exe_path); | |
196 // Put the process type and plugin path first so they're easier to see | |
197 // in process listings using native process management tools. | |
198 cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kPluginProcess); | |
199 cmd_line->AppendSwitchPath(switches::kPluginPath, info.path); | |
200 | |
201 if (logging::DialogsAreSuppressed()) | |
202 cmd_line->AppendSwitch(switches::kNoErrorDialogs); | |
203 | |
204 // Propagate the following switches to the plugin command line (along with | |
205 // any associated values) if present in the browser command line | |
206 static const char* const kSwitchNames[] = { | |
207 switches::kPluginStartupDialog, | |
208 switches::kNoSandbox, | |
209 switches::kSafePlugins, | |
210 switches::kTestSandbox, | |
211 switches::kUserAgent, | |
212 switches::kDisableBreakpad, | |
213 switches::kFullMemoryCrashReport, | |
214 switches::kEnableLogging, | |
215 switches::kDisableLogging, | |
216 switches::kLoggingLevel, | |
217 switches::kLogPluginMessages, | |
218 switches::kUserDataDir, | |
219 switches::kEnableDCHECK, | |
220 switches::kSilentDumpOnDCHECK, | |
221 switches::kMemoryProfiling, | |
222 switches::kUseLowFragHeapCrt, | |
223 switches::kEnableStatsTable, | |
224 switches::kEnableGPUPlugin, | |
225 switches::kUseGL, | |
226 #if defined(OS_CHROMEOS) | |
227 switches::kLoginProfile, | |
228 #endif | |
229 }; | |
230 | |
231 cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames, | |
232 arraysize(kSwitchNames)); | |
233 | |
234 // If specified, prepend a launcher program to the command line. | |
235 if (!plugin_launcher.empty()) | |
236 cmd_line->PrependWrapper(plugin_launcher); | |
237 | |
238 if (!locale.empty()) { | |
239 // Pass on the locale so the null plugin will use the right language in the | |
240 // prompt to install the desired plugin. | |
241 cmd_line->AppendSwitchASCII(switches::kLang, locale); | |
242 } | |
243 | |
244 // Gears requires the data dir to be available on startup. | |
245 FilePath data_dir = | |
246 PluginService::GetInstance()->GetChromePluginDataDir(); | |
247 DCHECK(!data_dir.empty()); | |
248 cmd_line->AppendSwitchPath(switches::kPluginDataDir, data_dir); | |
249 | |
250 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id()); | |
251 | |
252 SetCrashReporterCommandLine(cmd_line); | |
253 | |
254 #if defined(OS_POSIX) | |
255 base::environment_vector env; | |
256 #if defined(OS_MACOSX) && !defined(__LP64__) | |
257 // Add our interposing library for Carbon. This is stripped back out in | |
258 // plugin_main.cc, so changes here should be reflected there. | |
259 std::string interpose_list(plugin_interpose_strings::kInterposeLibraryPath); | |
260 const char* existing_list = | |
261 getenv(plugin_interpose_strings::kDYLDInsertLibrariesKey); | |
262 if (existing_list) { | |
263 interpose_list.insert(0, ":"); | |
264 interpose_list.insert(0, existing_list); | |
265 } | |
266 env.push_back(std::pair<std::string, std::string>( | |
267 plugin_interpose_strings::kDYLDInsertLibrariesKey, | |
268 interpose_list)); | |
269 #endif | |
270 #endif | |
271 | |
272 Launch( | |
273 #if defined(OS_WIN) | |
274 FilePath(), | |
275 #elif defined(OS_POSIX) | |
276 false, | |
277 env, | |
278 #endif | |
279 cmd_line); | |
280 | |
281 return true; | |
282 } | |
283 | |
284 void PluginProcessHost::ForceShutdown() { | |
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
286 Send(new PluginProcessMsg_NotifyRenderersOfPendingShutdown()); | |
287 BrowserChildProcessHost::ForceShutdown(); | |
288 } | |
289 | |
290 void PluginProcessHost::OnProcessLaunched() { | |
291 FilePath gears_path; | |
292 if (PathService::Get(chrome::FILE_GEARS_PLUGIN, &gears_path)) { | |
293 FilePath::StringType gears_path_lc = StringToLowerASCII(gears_path.value()); | |
294 FilePath::StringType plugin_path_lc = | |
295 StringToLowerASCII(info_.path.value()); | |
296 if (plugin_path_lc == gears_path_lc) { | |
297 // Give Gears plugins "background" priority. See http://b/1280317. | |
298 SetProcessBackgrounded(); | |
299 } | |
300 } | |
301 } | |
302 | |
303 bool PluginProcessHost::OnMessageReceived(const IPC::Message& msg) { | |
304 bool handled = true; | |
305 IPC_BEGIN_MESSAGE_MAP(PluginProcessHost, msg) | |
306 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ChannelCreated, OnChannelCreated) | |
307 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_GetPluginFinderUrl, | |
308 OnGetPluginFinderUrl) | |
309 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginMessage, OnPluginMessage) | |
310 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_GetCookies, OnGetCookies) | |
311 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_AccessFiles, OnAccessFiles) | |
312 IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginProcessHostMsg_ResolveProxy, | |
313 OnResolveProxy) | |
314 #if defined(OS_WIN) | |
315 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginWindowDestroyed, | |
316 OnPluginWindowDestroyed) | |
317 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_DownloadUrl, OnDownloadUrl) | |
318 #endif | |
319 #if defined(TOOLKIT_USES_GTK) | |
320 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_MapNativeViewId, | |
321 OnMapNativeViewId) | |
322 #endif | |
323 #if defined(OS_MACOSX) | |
324 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginSelectWindow, | |
325 OnPluginSelectWindow) | |
326 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginShowWindow, | |
327 OnPluginShowWindow) | |
328 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginHideWindow, | |
329 OnPluginHideWindow) | |
330 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginSetCursorVisibility, | |
331 OnPluginSetCursorVisibility) | |
332 #endif | |
333 IPC_MESSAGE_UNHANDLED(handled = false) | |
334 IPC_END_MESSAGE_MAP() | |
335 | |
336 DCHECK(handled); | |
337 return handled; | |
338 } | |
339 | |
340 void PluginProcessHost::OnChannelConnected(int32 peer_pid) { | |
341 for (size_t i = 0; i < pending_requests_.size(); ++i) { | |
342 RequestPluginChannel(pending_requests_[i]); | |
343 } | |
344 | |
345 pending_requests_.clear(); | |
346 } | |
347 | |
348 void PluginProcessHost::OnChannelError() { | |
349 CancelRequests(); | |
350 } | |
351 | |
352 bool PluginProcessHost::CanShutdown() { | |
353 return sent_requests_.empty(); | |
354 } | |
355 | |
356 void PluginProcessHost::CancelRequests() { | |
357 for (size_t i = 0; i < pending_requests_.size(); ++i) | |
358 pending_requests_[i]->OnError(); | |
359 pending_requests_.clear(); | |
360 | |
361 while (!sent_requests_.empty()) { | |
362 sent_requests_.front()->OnError(); | |
363 sent_requests_.pop(); | |
364 } | |
365 } | |
366 | |
367 void PluginProcessHost::OpenChannelToPlugin(Client* client) { | |
368 InstanceCreated(); | |
369 client->SetPluginInfo(info_); | |
370 if (opening_channel()) { | |
371 // The channel is already in the process of being opened. Put | |
372 // this "open channel" request into a queue of requests that will | |
373 // be run once the channel is open. | |
374 pending_requests_.push_back(client); | |
375 return; | |
376 } | |
377 | |
378 // We already have an open channel, send a request right away to plugin. | |
379 RequestPluginChannel(client); | |
380 } | |
381 | |
382 void PluginProcessHost::OnGetCookies(uint32 request_context, | |
383 const GURL& url, | |
384 std::string* cookies) { | |
385 net::URLRequestContext* context = CPBrowsingContextManager::GetInstance()-> | |
386 ToURLRequestContext(request_context); | |
387 // TODO(mpcomplete): remove fallback case when Gears support is prevalent. | |
388 if (!context) | |
389 context = Profile::GetDefaultRequestContext()->GetURLRequestContext(); | |
390 | |
391 // Note: We don't have a first_party_for_cookies check because plugins bypass | |
392 // third-party cookie blocking. | |
393 if (context && context->cookie_store()) { | |
394 *cookies = context->cookie_store()->GetCookies(url); | |
395 } else { | |
396 DLOG(ERROR) << "Could not serve plugin cookies request."; | |
397 cookies->clear(); | |
398 } | |
399 } | |
400 | |
401 void PluginProcessHost::OnAccessFiles(int renderer_id, | |
402 const std::vector<std::string>& files, | |
403 bool* allowed) { | |
404 ChildProcessSecurityPolicy* policy = | |
405 ChildProcessSecurityPolicy::GetInstance(); | |
406 | |
407 for (size_t i = 0; i < files.size(); ++i) { | |
408 const FilePath path = FilePath::FromWStringHack(UTF8ToWide(files[i])); | |
409 if (!policy->CanReadFile(renderer_id, path)) { | |
410 VLOG(1) << "Denied unauthorized request for file " << files[i]; | |
411 *allowed = false; | |
412 return; | |
413 } | |
414 } | |
415 | |
416 *allowed = true; | |
417 } | |
418 | |
419 void PluginProcessHost::OnResolveProxy(const GURL& url, | |
420 IPC::Message* reply_msg) { | |
421 resolve_proxy_msg_helper_.Start(url, reply_msg); | |
422 } | |
423 | |
424 void PluginProcessHost::OnResolveProxyCompleted(IPC::Message* reply_msg, | |
425 int result, | |
426 const std::string& proxy_list) { | |
427 PluginProcessHostMsg_ResolveProxy::WriteReplyParams( | |
428 reply_msg, result, proxy_list); | |
429 Send(reply_msg); | |
430 } | |
431 | |
432 void PluginProcessHost::RequestPluginChannel(Client* client) { | |
433 // We can't send any sync messages from the browser because it might lead to | |
434 // a hang. However this async messages must be answered right away by the | |
435 // plugin process (i.e. unblocks a Send() call like a sync message) otherwise | |
436 // a deadlock can occur if the plugin creation request from the renderer is | |
437 // a result of a sync message by the plugin process. | |
438 PluginProcessMsg_CreateChannel* msg = | |
439 new PluginProcessMsg_CreateChannel(client->ID(), | |
440 client->OffTheRecord()); | |
441 msg->set_unblock(true); | |
442 if (Send(msg)) { | |
443 sent_requests_.push(client); | |
444 } else { | |
445 client->OnError(); | |
446 } | |
447 } | |
448 | |
449 void PluginProcessHost::OnChannelCreated( | |
450 const IPC::ChannelHandle& channel_handle) { | |
451 Client* client = sent_requests_.front(); | |
452 | |
453 client->OnChannelOpened(channel_handle); | |
454 sent_requests_.pop(); | |
455 } | |
456 | |
457 void PluginProcessHost::OnGetPluginFinderUrl(std::string* plugin_finder_url) { | |
458 if (!plugin_finder_url) { | |
459 NOTREACHED(); | |
460 return; | |
461 } | |
462 | |
463 // TODO(iyengar) Add the plumbing to retrieve the default | |
464 // plugin finder URL. | |
465 *plugin_finder_url = kDefaultPluginFinderURL; | |
466 } | |
467 | |
468 void PluginProcessHost::OnPluginMessage( | |
469 const std::vector<uint8>& data) { | |
470 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
471 | |
472 ChromePluginLib *chrome_plugin = ChromePluginLib::Find(info_.path); | |
473 if (chrome_plugin) { | |
474 void *data_ptr = const_cast<void*>(reinterpret_cast<const void*>(&data[0])); | |
475 uint32 data_len = static_cast<uint32>(data.size()); | |
476 chrome_plugin->functions().on_message(data_ptr, data_len); | |
477 } | |
478 } | |
OLD | NEW |