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/chrome_plugin_host.h" | |
6 | |
7 #include <set> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "base/basictypes.h" | |
12 #include "base/command_line.h" | |
13 #include "base/file_path.h" | |
14 #include "base/file_util.h" | |
15 #include "base/message_loop.h" | |
16 #include "base/metrics/histogram.h" | |
17 #include "base/path_service.h" | |
18 #include "base/perftimer.h" | |
19 #include "base/singleton.h" | |
20 #include "base/string_util.h" | |
21 #include "base/utf_string_conversions.h" | |
22 #include "chrome/browser/browser_list.h" | |
23 #include "chrome/browser/chrome_plugin_browsing_context.h" | |
24 #include "chrome/browser/gears_integration.h" | |
25 #include "chrome/browser/profiles/profile.h" | |
26 #include "chrome/browser/ui/webui/html_dialog_ui.h" | |
27 #include "chrome/common/chrome_constants.h" | |
28 #include "chrome/common/chrome_counters.h" | |
29 #include "chrome/common/chrome_paths.h" | |
30 #include "chrome/common/chrome_plugin_lib.h" | |
31 #include "chrome/common/chrome_plugin_util.h" | |
32 #include "chrome/common/gears_api.h" | |
33 #include "chrome/common/net/url_request_context_getter.h" | |
34 #include "chrome/common/net/url_request_intercept_job.h" | |
35 #include "chrome/common/plugin_messages.h" | |
36 #include "chrome/common/render_messages.h" | |
37 #include "content/browser/browser_thread.h" | |
38 #include "content/browser/plugin_process_host.h" | |
39 #include "content/browser/plugin_service.h" | |
40 #include "content/browser/renderer_host/render_process_host.h" | |
41 #include "content/common/notification_service.h" | |
42 #include "net/base/cookie_monster.h" | |
43 #include "net/base/io_buffer.h" | |
44 #include "net/base/net_errors.h" | |
45 #include "net/http/http_request_headers.h" | |
46 #include "net/url_request/url_request.h" | |
47 #include "net/url_request/url_request_context.h" | |
48 #include "net/url_request/url_request_error_job.h" | |
49 #include "third_party/skia/include/core/SkBitmap.h" | |
50 | |
51 | |
52 using base::TimeDelta; | |
53 | |
54 // This class manages the interception of network requests. It queries the | |
55 // plugin on every request, and creates an intercept job if the plugin can | |
56 // intercept the request. | |
57 // NOTE: All methods must be called on the IO thread. | |
58 class PluginRequestInterceptor | |
59 : public PluginHelper, public net::URLRequest::Interceptor { | |
60 public: | |
61 static net::URLRequestJob* UninterceptedProtocolHandler( | |
62 net::URLRequest* request, const std::string& scheme) { | |
63 // This will get called if a plugin failed to intercept a request for a | |
64 // protocol it has registered. In that case, we return NULL and the request | |
65 // will result in an error. | |
66 return new net::URLRequestErrorJob(request, net::ERR_FILE_NOT_FOUND); | |
67 } | |
68 | |
69 explicit PluginRequestInterceptor(ChromePluginLib* plugin) | |
70 : PluginHelper(plugin) { | |
71 net::URLRequest::RegisterRequestInterceptor(this); | |
72 } | |
73 | |
74 virtual ~PluginRequestInterceptor() { | |
75 net::URLRequest::UnregisterRequestInterceptor(this); | |
76 | |
77 // Unregister our protocols. | |
78 for (HandledProtocolList::iterator it = registered_protocols_.begin(); | |
79 it != registered_protocols_.end(); ++it) { | |
80 net::URLRequest::ProtocolFactory* factory = | |
81 net::URLRequest::RegisterProtocolFactory(*it, NULL); | |
82 DCHECK(factory == UninterceptedProtocolHandler); | |
83 } | |
84 } | |
85 | |
86 void RegisterProtocol(const std::string& scheme) { | |
87 DCHECK(CalledOnValidThread()); | |
88 | |
89 std::string lower_scheme = StringToLowerASCII(scheme); | |
90 handled_protocols_.insert(lower_scheme); | |
91 | |
92 // Only add a protocol factory if the net::URLRequest doesn't already handle | |
93 // it. If we fail to intercept, the request will be treated as an error. | |
94 if (!net::URLRequest::IsHandledProtocol(lower_scheme)) { | |
95 registered_protocols_.insert(lower_scheme); | |
96 net::URLRequest::RegisterProtocolFactory(lower_scheme, | |
97 &UninterceptedProtocolHandler); | |
98 } | |
99 } | |
100 | |
101 // net::URLRequest::Interceptor | |
102 virtual net::URLRequestJob* MaybeIntercept(net::URLRequest* request) { | |
103 // TODO(darin): This DCHECK fails in the unit tests because our interceptor | |
104 // is being persisted across unit tests. As a result, each time we get | |
105 // poked on a different thread, but never from more than one thread at a | |
106 // time. We need a way to have the URLRequestJobManager get reset between | |
107 // unit tests. | |
108 // DCHECK(CalledOnValidThread()); | |
109 | |
110 if (!IsHandledProtocol(request->url().scheme())) | |
111 return NULL; | |
112 | |
113 CPBrowsingContext context = | |
114 CPBrowsingContextManager::GetInstance()->Lookup(request->context()); | |
115 scoped_ptr<ScopableCPRequest> cprequest( | |
116 new ScopableCPRequest(request->url().spec().c_str(), | |
117 request->method().c_str(), | |
118 context)); | |
119 | |
120 PerfTimer timer; | |
121 if (!plugin_->functions().should_intercept_request(cprequest.get())) { | |
122 LogInterceptMissTime(timer.Elapsed()); | |
123 return NULL; | |
124 } | |
125 LogInterceptHitTime(timer.Elapsed()); | |
126 return new URLRequestInterceptJob(request, plugin_, cprequest.release()); | |
127 } | |
128 | |
129 private: | |
130 bool IsHandledProtocol(const std::string& scheme) { | |
131 return handled_protocols_.find(scheme) != handled_protocols_.end(); | |
132 } | |
133 | |
134 void LogInterceptHitTime(const TimeDelta& time) { | |
135 UMA_HISTOGRAM_TIMES("Gears.InterceptHit", time); | |
136 } | |
137 | |
138 void LogInterceptMissTime(const TimeDelta& time) { | |
139 UMA_HISTOGRAM_TIMES("Gears.InterceptMiss", time); | |
140 } | |
141 | |
142 typedef std::set<std::string> HandledProtocolList; | |
143 HandledProtocolList handled_protocols_; | |
144 HandledProtocolList registered_protocols_; | |
145 }; | |
146 | |
147 // This class manages a network request made by the plugin, also acting as | |
148 // the net::URLRequest delegate. | |
149 // NOTE: All methods must be called on the IO thread. | |
150 class PluginRequestHandler : public PluginHelper, | |
151 public net::URLRequest::Delegate { | |
152 public: | |
153 static PluginRequestHandler* FromCPRequest(CPRequest* request) { | |
154 return ScopableCPRequest::GetData<PluginRequestHandler*>(request); | |
155 } | |
156 | |
157 PluginRequestHandler(ChromePluginLib* plugin, ScopableCPRequest* cprequest) | |
158 : PluginHelper(plugin), cprequest_(cprequest), user_buffer_(NULL) { | |
159 cprequest_->data = this; // see FromCPRequest(). | |
160 | |
161 net::URLRequestContext* context = CPBrowsingContextManager::GetInstance()-> | |
162 ToURLRequestContext(cprequest_->context); | |
163 // TODO(mpcomplete): remove fallback case when Gears support is prevalent. | |
164 if (!context) | |
165 context = Profile::GetDefaultRequestContext()->GetURLRequestContext(); | |
166 | |
167 GURL gurl(cprequest_->url); | |
168 request_.reset(new net::URLRequest(gurl, this)); | |
169 request_->set_context(context); | |
170 request_->set_method(cprequest_->method); | |
171 request_->set_load_flags(PluginResponseUtils::CPLoadFlagsToNetFlags(0)); | |
172 } | |
173 | |
174 net::URLRequest* request() { return request_.get(); } | |
175 | |
176 // Wraper of net::URLRequest::Read() | |
177 bool Read(char* dest, int dest_size, int *bytes_read) { | |
178 CHECK(!my_buffer_.get()); | |
179 // We'll use our own buffer until the read actually completes. | |
180 user_buffer_ = dest; | |
181 my_buffer_ = new net::IOBuffer(dest_size); | |
182 | |
183 if (request_->Read(my_buffer_, dest_size, bytes_read)) { | |
184 memcpy(dest, my_buffer_->data(), *bytes_read); | |
185 my_buffer_ = NULL; | |
186 return true; | |
187 } | |
188 | |
189 if (!request_->status().is_io_pending()) | |
190 my_buffer_ = NULL; | |
191 | |
192 return false; | |
193 } | |
194 | |
195 // net::URLRequest::Delegate | |
196 virtual void OnReceivedRedirect(net::URLRequest* request, const GURL& new_url, | |
197 bool* defer_redirect) { | |
198 plugin_->functions().response_funcs->received_redirect( | |
199 cprequest_.get(), new_url.spec().c_str()); | |
200 } | |
201 | |
202 virtual void OnResponseStarted(net::URLRequest* request) { | |
203 // TODO(mpcomplete): better error codes | |
204 CPError result = | |
205 request_->status().is_success() ? CPERR_SUCCESS : CPERR_FAILURE; | |
206 plugin_->functions().response_funcs->start_completed( | |
207 cprequest_.get(), result); | |
208 } | |
209 | |
210 virtual void OnReadCompleted(net::URLRequest* request, int bytes_read) { | |
211 CHECK(my_buffer_.get()); | |
212 CHECK(user_buffer_); | |
213 if (bytes_read > 0) { | |
214 memcpy(user_buffer_, my_buffer_->data(), bytes_read); | |
215 } else if (bytes_read < 0) { | |
216 // TODO(mpcomplete): better error codes | |
217 bytes_read = CPERR_FAILURE; | |
218 } | |
219 my_buffer_ = NULL; | |
220 plugin_->functions().response_funcs->read_completed( | |
221 cprequest_.get(), bytes_read); | |
222 } | |
223 | |
224 private: | |
225 scoped_ptr<ScopableCPRequest> cprequest_; | |
226 scoped_ptr<net::URLRequest> request_; | |
227 scoped_refptr<net::IOBuffer> my_buffer_; | |
228 char* user_buffer_; | |
229 }; | |
230 | |
231 // This class manages plugins that want to handle UI commands. Right now, we | |
232 // only allow 1 plugin to do this, so there's only ever 1 instance of this | |
233 // class at once. | |
234 // NOTE: All methods must be called on the IO thread. | |
235 class PluginCommandHandler : public PluginHelper { | |
236 public: | |
237 static void HandleCommand(int command, | |
238 CPCommandInterface* data, | |
239 int32 context_as_int32) { | |
240 CPBrowsingContext context = | |
241 static_cast<CPBrowsingContext>(context_as_int32); | |
242 // Ensure plugins are loaded before we try to talk to it. This will be a | |
243 // noop if plugins are loaded already. | |
244 ChromePluginLib::LoadChromePlugins(GetCPBrowserFuncsForBrowser()); | |
245 | |
246 DCHECK(ChromePluginLib::IsPluginThread()); | |
247 CPError rv = CPERR_INVALID_VERSION; | |
248 if (instance_ && instance_->plugin_->functions().handle_command) { | |
249 rv = instance_->plugin_->functions().handle_command( | |
250 context, command, data ? data->GetData() : NULL); | |
251 } | |
252 if (data) | |
253 data->OnCommandInvoked(rv); | |
254 } | |
255 | |
256 static void RegisterPlugin(ChromePluginLib* plugin) { | |
257 DCHECK(ChromePluginLib::IsPluginThread()); | |
258 // TODO(mpcomplete): We only expect to have Gears register a command handler | |
259 // at the moment. We should either add support for other plugins to do | |
260 // this, or verify that the plugin is Gears. | |
261 DCHECK(!instance_) << | |
262 "We only support a single plugin handling UI commands."; | |
263 if (instance_) | |
264 return; | |
265 // Deleted in response to a notification in PluginHelper. | |
266 new PluginCommandHandler(plugin); | |
267 } | |
268 | |
269 private: | |
270 explicit PluginCommandHandler(ChromePluginLib* plugin) | |
271 : PluginHelper(plugin) { | |
272 DCHECK(instance_ == NULL); | |
273 instance_ = this; | |
274 } | |
275 | |
276 virtual ~PluginCommandHandler() { | |
277 instance_ = NULL; | |
278 } | |
279 | |
280 static PluginCommandHandler* instance_; | |
281 }; | |
282 | |
283 PluginCommandHandler* PluginCommandHandler::instance_ = NULL; | |
284 | |
285 // This class acts as a helper to display the HTML dialog. It is created | |
286 // on demand on the plugin thread, and proxies calls to and from the UI thread | |
287 // to display the UI. | |
288 class ModelessHtmlDialogDelegate : public HtmlDialogUIDelegate { | |
289 public: | |
290 ModelessHtmlDialogDelegate(const GURL& url, | |
291 int width, int height, | |
292 const std::string& json_arguments, | |
293 void* plugin_context, | |
294 ChromePluginLib* plugin, | |
295 MessageLoop* main_message_loop, | |
296 gfx::NativeWindow parent_wnd) | |
297 : main_message_loop_(main_message_loop), | |
298 io_message_loop_(MessageLoop::current()), | |
299 plugin_(plugin), | |
300 plugin_context_(plugin_context), | |
301 parent_wnd_(parent_wnd) { | |
302 DCHECK(ChromePluginLib::IsPluginThread()); | |
303 params_.url = url; | |
304 params_.height = height; | |
305 params_.width = width; | |
306 params_.json_input = json_arguments; | |
307 | |
308 main_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( | |
309 this, &ModelessHtmlDialogDelegate::Show)); | |
310 } | |
311 virtual ~ModelessHtmlDialogDelegate() { | |
312 DCHECK(ChromePluginLib::IsPluginThread()); | |
313 } | |
314 | |
315 // The following public methods are called from the UI thread. | |
316 | |
317 // HtmlDialogUIDelegate implementation: | |
318 virtual bool IsDialogModal() const { return false; } | |
319 virtual std::wstring GetDialogTitle() const { return L"Gears"; } | |
320 virtual GURL GetDialogContentURL() const { return params_.url; } | |
321 virtual void GetWebUIMessageHandlers( | |
322 std::vector<WebUIMessageHandler*>* handlers) const {} | |
323 virtual void GetDialogSize(gfx::Size* size) const { | |
324 size->set_width(params_.width); | |
325 size->set_height(params_.height); | |
326 } | |
327 virtual std::string GetDialogArgs() const { return params_.json_input; } | |
328 virtual void OnDialogClosed(const std::string& json_retval) { | |
329 io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( | |
330 this, &ModelessHtmlDialogDelegate::ReportResults, json_retval)); | |
331 } | |
332 virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { } | |
333 virtual bool ShouldShowDialogTitle() const { return true; } | |
334 | |
335 private: | |
336 // Actually shows the dialog on the UI thread. | |
337 void Show() { | |
338 DCHECK(MessageLoop::current() == main_message_loop_); | |
339 Browser* browser = BrowserList::GetLastActive(); | |
340 browser->BrowserShowHtmlDialog(this, parent_wnd_); | |
341 } | |
342 | |
343 // Gives the JSON result string back to the plugin. | |
344 void ReportResults(const std::string& json_retval) { | |
345 DCHECK(MessageLoop::current() == io_message_loop_); | |
346 // The plugin may have unloaded before it was time to report the results. | |
347 if (plugin_->is_loaded()) | |
348 plugin_->functions().html_dialog_closed(plugin_context_, | |
349 json_retval.c_str()); | |
350 delete this; | |
351 } | |
352 | |
353 // The parameters needed to display a modal HTML dialog. | |
354 HtmlDialogUI::HtmlDialogParams params_; | |
355 | |
356 // Message loops for sending messages between UI and IO threads. | |
357 MessageLoop* main_message_loop_; | |
358 MessageLoop* io_message_loop_; | |
359 | |
360 // The plugin object that requested the dialog. This can only be accessed on | |
361 // the IO thread. | |
362 scoped_refptr<ChromePluginLib> plugin_; | |
363 | |
364 // The plugin's context argument to CPB_ShowHtmlDialog. | |
365 void* plugin_context_; | |
366 | |
367 // The window this dialog box should be parented to, or NULL for the last | |
368 // active browser window. | |
369 gfx::NativeWindow parent_wnd_; | |
370 | |
371 DISALLOW_COPY_AND_ASSIGN(ModelessHtmlDialogDelegate); | |
372 }; | |
373 | |
374 // Allows InvokeLater without adding refcounting. The object is only deleted | |
375 // when its last InvokeLater is run anyway. | |
376 DISABLE_RUNNABLE_METHOD_REFCOUNT(ModelessHtmlDialogDelegate); | |
377 | |
378 namespace { | |
379 | |
380 // | |
381 // Generic functions | |
382 // | |
383 | |
384 void STDCALL CPB_SetKeepProcessAlive(CPID id, CPBool keep_alive) { | |
385 // This is a no-op in the main browser process | |
386 } | |
387 | |
388 CPError STDCALL CPB_GetCookies(CPID id, CPBrowsingContext bcontext, | |
389 const char* url, char** cookies) { | |
390 CHECK(ChromePluginLib::IsPluginThread()); | |
391 net::URLRequestContext* context = CPBrowsingContextManager::GetInstance()-> | |
392 ToURLRequestContext(bcontext); | |
393 // TODO(mpcomplete): remove fallback case when Gears support is prevalent. | |
394 if (!context) { | |
395 context = Profile::GetDefaultRequestContext()->GetURLRequestContext(); | |
396 if (!context) | |
397 return CPERR_FAILURE; | |
398 } | |
399 std::string cookies_str = context->cookie_store()->GetCookies(GURL(url)); | |
400 *cookies = CPB_StringDup(CPB_Alloc, cookies_str); | |
401 return CPERR_SUCCESS; | |
402 } | |
403 | |
404 CPError STDCALL CPB_ShowHtmlDialogModal( | |
405 CPID id, CPBrowsingContext context, const char* url, int width, int height, | |
406 const char* json_arguments, char** json_retval) { | |
407 // Should not be called in browser process. | |
408 return CPERR_FAILURE; | |
409 } | |
410 | |
411 CPError STDCALL CPB_ShowHtmlDialog( | |
412 CPID id, CPBrowsingContext context, const char* url, int width, int height, | |
413 const char* json_arguments, void* plugin_context) { | |
414 CHECK(ChromePluginLib::IsPluginThread()); | |
415 ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); | |
416 CHECK(plugin); | |
417 | |
418 #if defined(OS_WIN) | |
419 HWND parent_hwnd = reinterpret_cast<HWND>(static_cast<uintptr_t>(context)); | |
420 PluginService* service = PluginService::GetInstance(); | |
421 if (!service) | |
422 return CPERR_FAILURE; | |
423 MessageLoop* main_message_loop = service->main_message_loop(); | |
424 ModelessHtmlDialogDelegate* delegate = | |
425 new ModelessHtmlDialogDelegate(GURL(url), width, height, json_arguments, | |
426 plugin_context, plugin, main_message_loop, | |
427 parent_hwnd); | |
428 #else | |
429 // TODO(port): port ModelessHtmlDialogDelegate | |
430 NOTIMPLEMENTED(); | |
431 #endif | |
432 | |
433 return CPERR_SUCCESS; | |
434 } | |
435 | |
436 CPError STDCALL CPB_GetCommandLineArguments( | |
437 CPID id, CPBrowsingContext context, const char* url, char** arguments) { | |
438 CHECK(ChromePluginLib::IsPluginThread()); | |
439 std::string arguments_str; | |
440 CPError rv = CPB_GetCommandLineArgumentsCommon(url, &arguments_str); | |
441 if (rv == CPERR_SUCCESS) | |
442 *arguments = CPB_StringDup(CPB_Alloc, arguments_str); | |
443 return rv; | |
444 } | |
445 | |
446 CPBrowsingContext STDCALL CPB_GetBrowsingContextFromNPP(struct _NPP* npp) { | |
447 CHECK(ChromePluginLib::IsPluginThread()); | |
448 NOTREACHED() << "NPP does not exist in the browser process."; | |
449 return 0; | |
450 } | |
451 | |
452 int STDCALL CPB_GetBrowsingContextInfo( | |
453 CPID id, CPBrowsingContext context, CPBrowsingContextInfoType type, | |
454 void* buf, uint32 buf_size) { | |
455 CHECK(ChromePluginLib::IsPluginThread()); | |
456 switch (type) { | |
457 case CPBROWSINGCONTEXT_DATA_DIR_PTR: { | |
458 if (buf_size < sizeof(char*)) | |
459 return sizeof(char*); | |
460 | |
461 // TODO(mpcomplete): http://b/1143021 - When we support multiple profiles, | |
462 // fetch the data dir from the context. | |
463 PluginService* service = PluginService::GetInstance(); | |
464 if (!service) | |
465 return CPERR_FAILURE; | |
466 FilePath path = service->GetChromePluginDataDir(); | |
467 // This is wrong -- we can't in general stuff a path through a std::string. | |
468 // But this code is Gears-specific, so Windows-only anyway for now. | |
469 std::string retval; | |
470 #if defined(OS_WIN) | |
471 retval = WideToUTF8(path.Append(chrome::kChromePluginDataDirname).value()); | |
472 #else | |
473 NOTREACHED(); | |
474 #endif | |
475 *static_cast<char**>(buf) = CPB_StringDup(CPB_Alloc, retval); | |
476 return CPERR_SUCCESS; | |
477 } | |
478 case CPBROWSINGCONTEXT_UI_LOCALE_PTR: { | |
479 if (buf_size < sizeof(char*)) | |
480 return sizeof(char*); | |
481 | |
482 PluginService* service = PluginService::GetInstance(); | |
483 if (!service) | |
484 return CPERR_FAILURE; | |
485 const std::string& retval = service->GetUILocale(); | |
486 *static_cast<char**>(buf) = CPB_StringDup(CPB_Alloc, retval); | |
487 return CPERR_SUCCESS; | |
488 } | |
489 } | |
490 | |
491 return CPERR_FAILURE; | |
492 } | |
493 | |
494 CPError STDCALL CPB_AddUICommand(CPID id, int command) { | |
495 CHECK(ChromePluginLib::IsPluginThread()); | |
496 ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); | |
497 CHECK(plugin); | |
498 | |
499 PluginCommandHandler::RegisterPlugin(plugin); | |
500 return CPERR_SUCCESS; | |
501 } | |
502 | |
503 static void NotifyGearsShortcutsChanged() { | |
504 DCHECK(MessageLoop::current() == | |
505 PluginService::GetInstance()->main_message_loop()); | |
506 | |
507 // TODO(michaeln): source should be the original profile, fix this | |
508 // when gears provides the correct browser context, and when we | |
509 // can relate that to an actual profile. | |
510 NotificationService::current()->Notify( | |
511 NotificationType::WEB_APP_INSTALL_CHANGED, | |
512 Source<Profile>(NULL), | |
513 NotificationService::NoDetails()); | |
514 } | |
515 | |
516 CPError STDCALL CPB_HandleCommand( | |
517 CPID id, CPBrowsingContext context, int command, void *data) { | |
518 CHECK(ChromePluginLib::IsPluginThread()); | |
519 ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); | |
520 CHECK(plugin); | |
521 | |
522 if (command == GEARSBROWSERCOMMAND_CREATE_SHORTCUT_DONE) { | |
523 GearsCreateShortcutResult* result = | |
524 static_cast<GearsCreateShortcutResult*>(data); | |
525 CHECK(result); | |
526 | |
527 GearsCreateShortcutData* shortcut_data = | |
528 static_cast<GearsCreateShortcutData*>(result->shortcut); | |
529 shortcut_data->command_interface->OnCommandResponse(result->result); | |
530 } else if (command == GEARSBROWSERCOMMAND_NOTIFY_SHORTCUTS_CHANGED) { | |
531 PluginService* service = PluginService::GetInstance(); | |
532 if (!service) | |
533 return CPERR_FAILURE; | |
534 service->main_message_loop()->PostTask(FROM_HERE, | |
535 NewRunnableFunction(NotifyGearsShortcutsChanged)); | |
536 return CPERR_SUCCESS; | |
537 } | |
538 return CPERR_FAILURE; | |
539 } | |
540 | |
541 CPError STDCALL CPB_GetDragData( | |
542 CPID id, CPBrowsingContext context, struct NPObject* event, bool add_data, | |
543 int32* identity, int32* event_id, char** drag_type, char** drag_data) { | |
544 *identity = *event_id = 0; | |
545 NOTREACHED() << "Should not be called in the browser process."; | |
546 return CPERR_FAILURE; | |
547 } | |
548 | |
549 CPError STDCALL CPB_SetDropEffect( | |
550 CPID id, CPBrowsingContext context, struct NPObject* event, int effect) { | |
551 NOTREACHED() << "Should not be called in the browser process."; | |
552 return CPERR_FAILURE; | |
553 } | |
554 | |
555 CPError STDCALL CPB_AllowFileDrop( | |
556 CPID id, CPBrowsingContext context, const char* file_drag_data) { | |
557 NOTREACHED() << "Should not be called in the browser process."; | |
558 return CPERR_FAILURE; | |
559 } | |
560 | |
561 // | |
562 // Functions related to network interception | |
563 // | |
564 | |
565 void STDCALL CPB_EnableRequestIntercept(CPID id, const char** schemes, | |
566 uint32 num_schemes) { | |
567 CHECK(ChromePluginLib::IsPluginThread()); | |
568 ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); | |
569 CHECK(plugin); | |
570 | |
571 if (schemes && num_schemes > 0) { | |
572 PluginRequestInterceptor* interceptor = | |
573 new PluginRequestInterceptor(plugin); | |
574 for (uint32 i = 0; i < num_schemes; ++i) | |
575 interceptor->RegisterProtocol(schemes[i]); | |
576 } else { | |
577 PluginRequestInterceptor::DestroyAllHelpersForPlugin(plugin); | |
578 } | |
579 } | |
580 | |
581 void STDCALL CPRR_ReceivedRedirect(CPRequest* request, const char* new_url) { | |
582 } | |
583 | |
584 void STDCALL CPRR_StartCompleted(CPRequest* request, CPError result) { | |
585 CHECK(ChromePluginLib::IsPluginThread()); | |
586 URLRequestInterceptJob* job = URLRequestInterceptJob::FromCPRequest(request); | |
587 CHECK(job); | |
588 job->OnStartCompleted(result); | |
589 } | |
590 | |
591 void STDCALL CPRR_ReadCompleted(CPRequest* request, int bytes_read) { | |
592 CHECK(ChromePluginLib::IsPluginThread()); | |
593 URLRequestInterceptJob* job = URLRequestInterceptJob::FromCPRequest(request); | |
594 CHECK(job); | |
595 job->OnReadCompleted(bytes_read); | |
596 } | |
597 | |
598 void STDCALL CPRR_UploadProgress(CPRequest* request, uint64 pos, uint64 size) { | |
599 // Does not apply, plugins do not yet intercept uploads | |
600 } | |
601 | |
602 // | |
603 // Functions related to serving network requests to the plugin | |
604 // | |
605 | |
606 CPError STDCALL CPB_CreateRequest(CPID id, CPBrowsingContext context, | |
607 const char* method, const char* url, | |
608 CPRequest** request) { | |
609 CHECK(ChromePluginLib::IsPluginThread()); | |
610 ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); | |
611 CHECK(plugin); | |
612 | |
613 ScopableCPRequest* cprequest = new ScopableCPRequest(url, method, context); | |
614 PluginRequestHandler* handler = new PluginRequestHandler(plugin, cprequest); | |
615 CHECK(handler); | |
616 | |
617 *request = cprequest; | |
618 return CPERR_SUCCESS; | |
619 } | |
620 | |
621 CPError STDCALL CPR_StartRequest(CPRequest* request) { | |
622 CHECK(ChromePluginLib::IsPluginThread()); | |
623 PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); | |
624 CHECK(handler); | |
625 handler->request()->Start(); | |
626 return CPERR_IO_PENDING; | |
627 } | |
628 | |
629 void STDCALL CPR_EndRequest(CPRequest* request, CPError reason) { | |
630 CHECK(ChromePluginLib::IsPluginThread()); | |
631 PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); | |
632 delete handler; | |
633 } | |
634 | |
635 void STDCALL CPR_SetExtraRequestHeaders(CPRequest* request, | |
636 const char* headers) { | |
637 CHECK(ChromePluginLib::IsPluginThread()); | |
638 PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); | |
639 CHECK(handler); | |
640 net::HttpRequestHeaders http_headers; | |
641 http_headers.AddHeadersFromString(headers); | |
642 handler->request()->SetExtraRequestHeaders(http_headers); | |
643 } | |
644 | |
645 void STDCALL CPR_SetRequestLoadFlags(CPRequest* request, uint32 flags) { | |
646 CHECK(ChromePluginLib::IsPluginThread()); | |
647 PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); | |
648 CHECK(handler); | |
649 uint32 net_flags = PluginResponseUtils::CPLoadFlagsToNetFlags(flags); | |
650 handler->request()->set_load_flags(net_flags); | |
651 } | |
652 | |
653 void STDCALL CPR_AppendDataToUpload(CPRequest* request, const char* bytes, | |
654 int bytes_len) { | |
655 CHECK(ChromePluginLib::IsPluginThread()); | |
656 PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); | |
657 CHECK(handler); | |
658 handler->request()->AppendBytesToUpload(bytes, bytes_len); | |
659 } | |
660 | |
661 CPError STDCALL CPR_AppendFileToUpload(CPRequest* request, const char* filepath, | |
662 uint64 offset, uint64 length) { | |
663 CHECK(ChromePluginLib::IsPluginThread()); | |
664 PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); | |
665 CHECK(handler); | |
666 | |
667 if (!length) length = kuint64max; | |
668 FilePath path(FilePath::FromWStringHack(UTF8ToWide(filepath))); | |
669 handler->request()->AppendFileRangeToUpload(path, offset, length, | |
670 base::Time()); | |
671 return CPERR_SUCCESS; | |
672 } | |
673 | |
674 int STDCALL CPR_GetResponseInfo(CPRequest* request, CPResponseInfoType type, | |
675 void* buf, uint32 buf_size) { | |
676 CHECK(ChromePluginLib::IsPluginThread()); | |
677 PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); | |
678 CHECK(handler); | |
679 return PluginResponseUtils::GetResponseInfo( | |
680 handler->request()->response_headers(), type, buf, buf_size); | |
681 } | |
682 | |
683 int STDCALL CPR_Read(CPRequest* request, void* buf, uint32 buf_size) { | |
684 CHECK(ChromePluginLib::IsPluginThread()); | |
685 PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); | |
686 CHECK(handler); | |
687 | |
688 int bytes_read; | |
689 if (handler->Read(static_cast<char*>(buf), buf_size, &bytes_read)) | |
690 return bytes_read; // 0 == CPERR_SUCESS | |
691 | |
692 if (handler->request()->status().is_io_pending()) | |
693 return CPERR_IO_PENDING; | |
694 | |
695 return CPERR_FAILURE; | |
696 } | |
697 | |
698 CPBool STDCALL CPB_IsPluginProcessRunning(CPID id) { | |
699 CHECK(ChromePluginLib::IsPluginThread()); | |
700 ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); | |
701 CHECK(plugin); | |
702 PluginService* service = PluginService::GetInstance(); | |
703 if (!service) | |
704 return false; | |
705 PluginProcessHost *host = service->FindNpapiPluginProcess(plugin->filename()); | |
706 return host ? true : false; | |
707 } | |
708 | |
709 CPProcessType STDCALL CPB_GetProcessType(CPID id) { | |
710 CHECK(ChromePluginLib::IsPluginThread()); | |
711 return CP_PROCESS_BROWSER; | |
712 } | |
713 | |
714 CPError STDCALL CPB_SendMessage(CPID id, const void *data, uint32 data_len) { | |
715 CHECK(ChromePluginLib::IsPluginThread()); | |
716 ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); | |
717 CHECK(plugin); | |
718 | |
719 PluginService* service = PluginService::GetInstance(); | |
720 if (!service) | |
721 return CPERR_FAILURE; | |
722 PluginProcessHost *host = | |
723 service->FindOrStartNpapiPluginProcess(plugin->filename()); | |
724 if (!host) | |
725 return CPERR_FAILURE; | |
726 | |
727 const unsigned char* data_ptr = static_cast<const unsigned char*>(data); | |
728 std::vector<uint8> v(data_ptr, data_ptr + data_len); | |
729 #if defined(OS_WIN) | |
730 if (!host->Send(new PluginProcessMsg_PluginMessage(v))) | |
731 return CPERR_FAILURE; | |
732 #else | |
733 // TODO(port): Implement PluginProcessMsg. | |
734 NOTIMPLEMENTED(); | |
735 #endif | |
736 | |
737 return CPERR_SUCCESS; | |
738 } | |
739 | |
740 CPError STDCALL CPB_SendSyncMessage(CPID id, const void *data, uint32 data_len, | |
741 void **retval, uint32 *retval_len) { | |
742 NOTREACHED() << "Sync messages should not be sent from the browser process."; | |
743 | |
744 return CPERR_FAILURE; | |
745 } | |
746 | |
747 CPError STDCALL CPB_PluginThreadAsyncCall(CPID id, | |
748 void (*func)(void *), | |
749 void *user_data) { | |
750 bool posted = BrowserThread::PostTask( | |
751 BrowserThread::IO, FROM_HERE, | |
752 NewRunnableFunction(func, user_data)); | |
753 return posted ? CPERR_SUCCESS : CPERR_FAILURE; | |
754 } | |
755 | |
756 CPError STDCALL CPB_OpenFileDialog(CPID id, | |
757 CPBrowsingContext context, | |
758 bool multiple_files, | |
759 const char *title, | |
760 const char *filter, | |
761 void *user_data) { | |
762 NOTREACHED() << | |
763 "Open file dialog should only be called from the renderer process."; | |
764 | |
765 return CPERR_FAILURE; | |
766 } | |
767 | |
768 } // namespace | |
769 | |
770 CPBrowserFuncs* GetCPBrowserFuncsForBrowser() { | |
771 static CPBrowserFuncs browser_funcs; | |
772 static CPRequestFuncs request_funcs; | |
773 static CPResponseFuncs response_funcs; | |
774 static bool initialized = false; | |
775 if (!initialized) { | |
776 initialized = true; | |
777 | |
778 browser_funcs.size = sizeof(browser_funcs); | |
779 browser_funcs.version = CP_VERSION; | |
780 browser_funcs.enable_request_intercept = CPB_EnableRequestIntercept; | |
781 browser_funcs.create_request = CPB_CreateRequest; | |
782 browser_funcs.get_cookies = CPB_GetCookies; | |
783 browser_funcs.alloc = CPB_Alloc; | |
784 browser_funcs.free = CPB_Free; | |
785 browser_funcs.set_keep_process_alive = CPB_SetKeepProcessAlive; | |
786 browser_funcs.show_html_dialog_modal = CPB_ShowHtmlDialogModal; | |
787 browser_funcs.show_html_dialog = CPB_ShowHtmlDialog; | |
788 browser_funcs.is_plugin_process_running = CPB_IsPluginProcessRunning; | |
789 browser_funcs.get_process_type = CPB_GetProcessType; | |
790 browser_funcs.send_message = CPB_SendMessage; | |
791 browser_funcs.get_browsing_context_from_npp = CPB_GetBrowsingContextFromNPP; | |
792 browser_funcs.get_browsing_context_info = CPB_GetBrowsingContextInfo; | |
793 browser_funcs.get_command_line_arguments = CPB_GetCommandLineArguments; | |
794 browser_funcs.add_ui_command = CPB_AddUICommand; | |
795 browser_funcs.handle_command = CPB_HandleCommand; | |
796 | |
797 browser_funcs.request_funcs = &request_funcs; | |
798 browser_funcs.response_funcs = &response_funcs; | |
799 browser_funcs.send_sync_message = CPB_SendSyncMessage; | |
800 browser_funcs.plugin_thread_async_call = CPB_PluginThreadAsyncCall; | |
801 browser_funcs.open_file_dialog = CPB_OpenFileDialog; | |
802 browser_funcs.get_drag_data = CPB_GetDragData; | |
803 browser_funcs.set_drop_effect = CPB_SetDropEffect; | |
804 browser_funcs.allow_file_drop = CPB_AllowFileDrop; | |
805 | |
806 request_funcs.size = sizeof(request_funcs); | |
807 request_funcs.start_request = CPR_StartRequest; | |
808 request_funcs.end_request = CPR_EndRequest; | |
809 request_funcs.set_extra_request_headers = CPR_SetExtraRequestHeaders; | |
810 request_funcs.set_request_load_flags = CPR_SetRequestLoadFlags; | |
811 request_funcs.append_data_to_upload = CPR_AppendDataToUpload; | |
812 request_funcs.get_response_info = CPR_GetResponseInfo; | |
813 request_funcs.read = CPR_Read; | |
814 request_funcs.append_file_to_upload = CPR_AppendFileToUpload; | |
815 | |
816 response_funcs.size = sizeof(response_funcs); | |
817 response_funcs.received_redirect = CPRR_ReceivedRedirect; | |
818 response_funcs.start_completed = CPRR_StartCompleted; | |
819 response_funcs.read_completed = CPRR_ReadCompleted; | |
820 response_funcs.upload_progress = CPRR_UploadProgress; | |
821 } | |
822 | |
823 return &browser_funcs; | |
824 } | |
825 | |
826 void CPCommandInterface::OnCommandInvoked(CPError retval) { | |
827 delete this; | |
828 } | |
829 | |
830 void CPCommandInterface::OnCommandResponse(CPError retval) {} | |
831 | |
832 void CPHandleCommand(int command, CPCommandInterface* data, | |
833 CPBrowsingContext context) { | |
834 // Sadly if we try and pass context through, we seem to break cl's little | |
835 // brain trying to compile the Tuple3 ctor. This cast works. | |
836 int32 context_as_int32 = static_cast<int32>(context); | |
837 // Plugins can only be accessed on the IO thread. | |
838 BrowserThread::PostTask( | |
839 BrowserThread::IO, FROM_HERE, | |
840 NewRunnableFunction(PluginCommandHandler::HandleCommand, | |
841 command, data, context_as_int32)); | |
842 } | |
OLD | NEW |