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.
5 #include "content/child/npapi/plugin_instance.h"
8 #include "base/command_line.h"
9 #include "base/file_util.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "build/build_config.h"
14 #include "content/child/npapi/plugin_host.h"
15 #include "content/child/npapi/plugin_lib.h"
16 #include "content/child/npapi/plugin_stream_url.h"
17 #include "content/child/npapi/plugin_string_stream.h"
18 #include "content/child/npapi/webplugin.h"
19 #include "content/child/npapi/webplugin_delegate.h"
20 #include "content/child/npapi/webplugin_resource_client.h"
21 #include "content/public/common/content_constants.h"
22 #include "content/public/common/content_switches.h"
23 #include "net/base/escape.h"
25 #if defined(OS_MACOSX)
26 #include <ApplicationServices/ApplicationServices.h>
31 PluginInstance::PluginInstance(PluginLib
* plugin
, const std::string
& mime_type
)
34 host_(PluginHost::Singleton()),
35 npp_functions_(plugin
->functions()),
40 mime_type_(mime_type
),
42 use_mozilla_user_agent_(false),
43 #if defined (OS_MACOSX)
44 #ifdef NP_NO_QUICKDRAW
45 drawing_model_(NPDrawingModelCoreGraphics
),
47 drawing_model_(NPDrawingModelQuickDraw
),
50 event_model_(NPEventModelCocoa
),
52 event_model_(NPEventModelCarbon
),
54 currently_handled_event_(NULL
),
56 message_loop_(base::MessageLoop::current()),
57 load_manually_(false),
58 in_close_streams_(false),
61 next_range_request_id_(0),
62 handles_url_redirects_(false) {
67 if (mime_type_
== kFlashPluginSwfMimeType
)
70 memset(&zero_padding_
, 0, sizeof(zero_padding_
));
71 DCHECK(message_loop_
);
74 PluginInstance::~PluginInstance() {
83 plugin_
->CloseInstance();
86 PluginStreamUrl
* PluginInstance::CreateStream(unsigned long resource_id
,
88 const std::string
& mime_type
,
93 GetNotifyData(notify_id
, ¬ify
, ¬ify_data
);
94 PluginStreamUrl
* stream
= new PluginStreamUrl(
95 resource_id
, url
, this, notify
, notify_data
);
101 void PluginInstance::AddStream(PluginStream
* stream
) {
102 open_streams_
.push_back(make_scoped_refptr(stream
));
105 void PluginInstance::RemoveStream(PluginStream
* stream
) {
106 if (in_close_streams_
)
109 std::vector
<scoped_refptr
<PluginStream
> >::iterator stream_index
;
110 for (stream_index
= open_streams_
.begin();
111 stream_index
!= open_streams_
.end(); ++stream_index
) {
112 if (stream_index
->get() == stream
) {
113 open_streams_
.erase(stream_index
);
119 bool PluginInstance::IsValidStream(const NPStream
* stream
) {
120 std::vector
<scoped_refptr
<PluginStream
> >::iterator stream_index
;
121 for (stream_index
= open_streams_
.begin();
122 stream_index
!= open_streams_
.end(); ++stream_index
) {
123 if ((*stream_index
)->stream() == stream
)
130 void PluginInstance::CloseStreams() {
131 in_close_streams_
= true;
132 for (unsigned int index
= 0; index
< open_streams_
.size(); ++index
) {
133 // Close all streams on the way down.
134 open_streams_
[index
]->Close(NPRES_USER_BREAK
);
136 open_streams_
.clear();
137 in_close_streams_
= false;
140 WebPluginResourceClient
* PluginInstance::GetRangeRequest(
142 PendingRangeRequestMap::iterator iter
= pending_range_requests_
.find(id
);
143 if (iter
== pending_range_requests_
.end()) {
148 WebPluginResourceClient
* rv
= iter
->second
->AsResourceClient();
149 pending_range_requests_
.erase(iter
);
153 bool PluginInstance::Start(const GURL
& url
,
154 char** const param_names
,
155 char** const param_values
,
157 bool load_manually
) {
158 load_manually_
= load_manually
;
159 unsigned short mode
= load_manually_
? NP_FULL
: NP_EMBED
;
162 NPError err
= NPP_New(mode
, param_count
,
163 const_cast<char **>(param_names
), const_cast<char **>(param_values
));
165 if (err
== NPERR_NO_ERROR
) {
166 handles_url_redirects_
=
167 ((npp_functions_
->version
>= NPVERS_HAS_URL_REDIRECT_HANDLING
) &&
168 (npp_functions_
->urlredirectnotify
));
170 return err
== NPERR_NO_ERROR
;
173 NPObject
*PluginInstance::GetPluginScriptableObject() {
174 NPObject
*value
= NULL
;
175 NPError error
= NPP_GetValue(NPPVpluginScriptableNPObject
, &value
);
176 if (error
!= NPERR_NO_ERROR
|| value
== NULL
)
181 bool PluginInstance::GetFormValue(base::string16
* value
) {
182 // Plugins will allocate memory for the return value by using NPN_MemAlloc().
183 char *plugin_value
= NULL
;
184 NPError error
= NPP_GetValue(NPPVformValue
, &plugin_value
);
185 if (error
!= NPERR_NO_ERROR
|| !plugin_value
) {
188 // Assumes the result is UTF8 text, as Firefox does.
189 *value
= base::UTF8ToUTF16(plugin_value
);
190 host_
->host_functions()->memfree(plugin_value
);
194 // WebPluginLoadDelegate methods
195 void PluginInstance::DidFinishLoadWithReason(const GURL
& url
,
200 GetNotifyData(notify_id
, ¬ify
, ¬ify_data
);
206 NPP_URLNotify(url
.spec().c_str(), reason
, notify_data
);
209 unsigned PluginInstance::GetBackingTextureId() {
210 // By default the plugin instance is not backed by an OpenGL texture.
215 NPError
PluginInstance::NPP_New(unsigned short mode
,
219 DCHECK(npp_functions_
!= 0);
220 DCHECK(npp_functions_
->newp
!= 0);
223 if (npp_functions_
->newp
!= 0) {
224 return npp_functions_
->newp(
225 (NPMIMEType
)mime_type_
.c_str(), npp_
, mode
, argc
, argn
, argv
, NULL
);
227 return NPERR_INVALID_FUNCTABLE_ERROR
;
230 void PluginInstance::NPP_Destroy() {
231 DCHECK(npp_functions_
!= 0);
232 DCHECK(npp_functions_
->destroy
!= 0);
234 if (npp_functions_
->destroy
!= 0) {
235 NPSavedData
*savedData
= 0;
236 npp_functions_
->destroy(npp_
, &savedData
);
238 // TODO: Support savedData. Technically, these need to be
239 // saved on a per-URL basis, and then only passed
240 // to new instances of the plugin at the same URL.
241 // Sounds like a huge security risk. When we do support
242 // these, we should pass them back to the PluginLib
243 // to be stored there.
244 DCHECK(savedData
== 0);
247 for (unsigned int file_index
= 0; file_index
< files_created_
.size();
249 base::DeleteFile(files_created_
[file_index
], false);
252 // Ensure that no timer callbacks are invoked after NPP_Destroy.
256 NPError
PluginInstance::NPP_SetWindow(NPWindow
* window
) {
257 DCHECK(npp_functions_
!= 0);
258 DCHECK(npp_functions_
->setwindow
!= 0);
260 if (npp_functions_
->setwindow
!= 0) {
261 return npp_functions_
->setwindow(npp_
, window
);
263 return NPERR_INVALID_FUNCTABLE_ERROR
;
266 NPError
PluginInstance::NPP_NewStream(NPMIMEType type
,
269 unsigned short* stype
) {
270 DCHECK(npp_functions_
!= 0);
271 DCHECK(npp_functions_
->newstream
!= 0);
272 if (npp_functions_
->newstream
!= 0) {
273 return npp_functions_
->newstream(npp_
, type
, stream
, seekable
, stype
);
275 return NPERR_INVALID_FUNCTABLE_ERROR
;
278 NPError
PluginInstance::NPP_DestroyStream(NPStream
* stream
, NPReason reason
) {
279 DCHECK(npp_functions_
!= 0);
280 DCHECK(npp_functions_
->destroystream
!= 0);
282 if (stream
== NULL
|| !IsValidStream(stream
) || (stream
->ndata
== NULL
))
283 return NPERR_INVALID_INSTANCE_ERROR
;
285 if (npp_functions_
->destroystream
!= 0) {
286 NPError result
= npp_functions_
->destroystream(npp_
, stream
, reason
);
287 stream
->ndata
= NULL
;
290 return NPERR_INVALID_FUNCTABLE_ERROR
;
293 int PluginInstance::NPP_WriteReady(NPStream
* stream
) {
294 DCHECK(npp_functions_
!= 0);
295 DCHECK(npp_functions_
->writeready
!= 0);
296 if (npp_functions_
->writeready
!= 0) {
297 return npp_functions_
->writeready(npp_
, stream
);
302 int PluginInstance::NPP_Write(NPStream
* stream
,
306 DCHECK(npp_functions_
!= 0);
307 DCHECK(npp_functions_
->write
!= 0);
308 if (npp_functions_
->write
!= 0) {
309 return npp_functions_
->write(npp_
, stream
, offset
, len
, buffer
);
314 void PluginInstance::NPP_StreamAsFile(NPStream
* stream
, const char* fname
) {
315 DCHECK(npp_functions_
!= 0);
316 DCHECK(npp_functions_
->asfile
!= 0);
317 if (npp_functions_
->asfile
!= 0) {
318 npp_functions_
->asfile(npp_
, stream
, fname
);
321 // Creating a temporary FilePath instance on the stack as the explicit
322 // FilePath constructor with StringType as an argument causes a compiler
323 // error when invoked via vector push back.
324 base::FilePath file_name
= base::FilePath::FromUTF8Unsafe(fname
);
325 files_created_
.push_back(file_name
);
328 void PluginInstance::NPP_URLNotify(const char* url
,
331 DCHECK(npp_functions_
!= 0);
332 DCHECK(npp_functions_
->urlnotify
!= 0);
333 if (npp_functions_
->urlnotify
!= 0) {
334 npp_functions_
->urlnotify(npp_
, url
, reason
, notifyData
);
338 NPError
PluginInstance::NPP_GetValue(NPPVariable variable
, void* value
) {
339 DCHECK(npp_functions_
!= 0);
340 // getvalue is NULL for Shockwave
341 if (npp_functions_
->getvalue
!= 0) {
342 return npp_functions_
->getvalue(npp_
, variable
, value
);
344 return NPERR_INVALID_FUNCTABLE_ERROR
;
347 NPError
PluginInstance::NPP_SetValue(NPNVariable variable
, void* value
) {
348 DCHECK(npp_functions_
!= 0);
349 if (npp_functions_
->setvalue
!= 0) {
350 return npp_functions_
->setvalue(npp_
, variable
, value
);
352 return NPERR_INVALID_FUNCTABLE_ERROR
;
355 short PluginInstance::NPP_HandleEvent(void* event
) {
356 DCHECK(npp_functions_
!= 0);
357 DCHECK(npp_functions_
->event
!= 0);
358 if (npp_functions_
->event
!= 0) {
359 return npp_functions_
->event(npp_
, (void*)event
);
364 bool PluginInstance::NPP_Print(NPPrint
* platform_print
) {
365 DCHECK(npp_functions_
!= 0);
366 if (npp_functions_
->print
!= 0) {
367 npp_functions_
->print(npp_
, platform_print
);
373 void PluginInstance::NPP_URLRedirectNotify(const char* url
, int32_t status
,
375 DCHECK(npp_functions_
!= 0);
376 if (npp_functions_
->urlredirectnotify
!= 0) {
377 npp_functions_
->urlredirectnotify(npp_
, url
, status
, notify_data
);
381 void PluginInstance::SendJavaScriptStream(const GURL
& url
,
382 const std::string
& result
,
387 GetNotifyData(notify_id
, ¬ify
, ¬ify_data
);
390 PluginStringStream
*stream
=
391 new PluginStringStream(this, url
, notify
, notify_data
);
393 stream
->SendToPlugin(result
, "text/html");
395 // NOTE: Sending an empty stream here will crash MacroMedia
396 // Flash 9. Just send the URL Notify.
398 NPP_URLNotify(url
.spec().c_str(), NPRES_DONE
, notify_data
);
402 void PluginInstance::DidReceiveManualResponse(const GURL
& url
,
403 const std::string
& mime_type
,
404 const std::string
& headers
,
405 uint32 expected_length
,
406 uint32 last_modified
) {
407 DCHECK(load_manually_
);
409 plugin_data_stream_
=
410 CreateStream(static_cast<unsigned long>(-1), url
, mime_type
, 0);
411 plugin_data_stream_
->DidReceiveResponse(mime_type
, headers
, expected_length
,
412 last_modified
, true);
415 void PluginInstance::DidReceiveManualData(const char* buffer
, int length
) {
416 DCHECK(load_manually_
);
417 if (plugin_data_stream_
.get() != NULL
) {
418 plugin_data_stream_
->DidReceiveData(buffer
, length
, 0);
422 void PluginInstance::DidFinishManualLoading() {
423 DCHECK(load_manually_
);
424 if (plugin_data_stream_
.get() != NULL
) {
425 plugin_data_stream_
->DidFinishLoading(plugin_data_stream_
->ResourceId());
426 plugin_data_stream_
->Close(NPRES_DONE
);
427 plugin_data_stream_
= NULL
;
431 void PluginInstance::DidManualLoadFail() {
432 DCHECK(load_manually_
);
433 if (plugin_data_stream_
.get() != NULL
) {
434 plugin_data_stream_
->DidFail(plugin_data_stream_
->ResourceId());
435 plugin_data_stream_
= NULL
;
439 void PluginInstance::PluginThreadAsyncCall(void (*func
)(void*),
441 message_loop_
->PostTask(
442 FROM_HERE
, base::Bind(&PluginInstance::OnPluginThreadAsyncCall
, this,
446 void PluginInstance::OnPluginThreadAsyncCall(void (*func
)(void*),
448 // Do not invoke the callback if NPP_Destroy has already been invoked.
453 uint32
PluginInstance::ScheduleTimer(uint32 interval
,
455 void (*func
)(NPP id
, uint32 timer_id
)) {
456 // Use next timer id.
458 timer_id
= next_timer_id_
;
460 DCHECK(next_timer_id_
!= 0);
462 // Record timer interval and repeat.
464 info
.interval
= interval
;
465 info
.repeat
= repeat
? true : false;
466 timers_
[timer_id
] = info
;
468 // Schedule the callback.
469 base::MessageLoop::current()->PostDelayedTask(
471 base::Bind(&PluginInstance::OnTimerCall
, this, func
, npp_
, timer_id
),
472 base::TimeDelta::FromMilliseconds(interval
));
476 void PluginInstance::UnscheduleTimer(uint32 timer_id
) {
477 // Remove info about the timer.
478 TimerMap::iterator it
= timers_
.find(timer_id
);
479 if (it
!= timers_
.end())
483 #if !defined(OS_MACOSX)
484 NPError
PluginInstance::PopUpContextMenu(NPMenu
* menu
) {
486 return NPERR_GENERIC_ERROR
;
490 void PluginInstance::OnTimerCall(void (*func
)(NPP id
, uint32 timer_id
),
493 // Do not invoke callback if the timer has been unscheduled.
494 TimerMap::iterator it
= timers_
.find(timer_id
);
495 if (it
== timers_
.end())
498 // Get all information about the timer before invoking the callback. The
499 // callback might unschedule the timer.
500 TimerInfo info
= it
->second
;
504 // If the timer was unscheduled by the callback, just free up the timer id.
505 if (timers_
.find(timer_id
) == timers_
.end())
508 // Reschedule repeating timers after invoking the callback so callback is not
509 // re-entered if it pumps the message loop.
511 base::MessageLoop::current()->PostDelayedTask(
513 base::Bind(&PluginInstance::OnTimerCall
, this, func
, npp_
, timer_id
),
514 base::TimeDelta::FromMilliseconds(info
.interval
));
520 void PluginInstance::PushPopupsEnabledState(bool enabled
) {
521 popups_enabled_stack_
.push(enabled
);
524 void PluginInstance::PopPopupsEnabledState() {
525 popups_enabled_stack_
.pop();
528 void PluginInstance::RequestRead(NPStream
* stream
, NPByteRange
* range_list
) {
529 std::string range_info
= "bytes=";
532 range_info
+= base::IntToString(range_list
->offset
);
533 range_info
.push_back('-');
535 base::IntToString(range_list
->offset
+ range_list
->length
- 1);
536 range_list
= range_list
->next
;
538 range_info
.push_back(',');
541 if (plugin_data_stream_
.get()) {
542 if (plugin_data_stream_
->stream() == stream
) {
543 webplugin_
->CancelDocumentLoad();
544 plugin_data_stream_
= NULL
;
548 // The lifetime of a NPStream instance depends on the PluginStream instance
549 // which owns it. When a plugin invokes NPN_RequestRead on a seekable stream,
550 // we don't want to create a new stream when the corresponding response is
551 // received. We send over a cookie which represents the PluginStream
552 // instance which is sent back from the renderer when the response is
554 std::vector
<scoped_refptr
<PluginStream
> >::iterator stream_index
;
555 for (stream_index
= open_streams_
.begin();
556 stream_index
!= open_streams_
.end(); ++stream_index
) {
557 PluginStream
* plugin_stream
= stream_index
->get();
558 if (plugin_stream
->stream() == stream
) {
559 // A stream becomes seekable the first time NPN_RequestRead
561 plugin_stream
->set_seekable(true);
563 if (CommandLine::ForCurrentProcess()->HasSwitch(
564 switches::kDisableDirectNPAPIRequests
)) {
565 pending_range_requests_
[++next_range_request_id_
] = plugin_stream
;
566 webplugin_
->InitiateHTTPRangeRequest(
567 stream
->url
, range_info
.c_str(), next_range_request_id_
);
570 PluginStreamUrl
* plugin_stream_url
=
571 static_cast<PluginStreamUrl
*>(plugin_stream
);
572 plugin_stream_url
->FetchRange(range_info
);
580 void PluginInstance::RequestURL(const char* url
,
589 notify_id
= ++next_notify_id_
;
590 pending_requests_
[notify_id
] = notify_data
;
593 webplugin_
->HandleURLRequest(
594 url
, method
, target
, buf
, len
, notify_id
, popups_allowed(),
595 notify
? handles_url_redirects_
: false);
598 bool PluginInstance::ConvertPoint(double source_x
, double source_y
,
599 NPCoordinateSpace source_space
,
600 double* dest_x
, double* dest_y
,
601 NPCoordinateSpace dest_space
) {
602 #if defined(OS_MACOSX)
603 CGRect main_display_bounds
= CGDisplayBounds(CGMainDisplayID());
605 double flipped_screen_x
= source_x
;
606 double flipped_screen_y
= source_y
;
607 switch(source_space
) {
608 case NPCoordinateSpacePlugin
:
609 flipped_screen_x
+= plugin_origin_
.x();
610 flipped_screen_y
+= plugin_origin_
.y();
612 case NPCoordinateSpaceWindow
:
613 flipped_screen_x
+= containing_window_frame_
.x();
614 flipped_screen_y
= containing_window_frame_
.height() - source_y
+
615 containing_window_frame_
.y();
617 case NPCoordinateSpaceFlippedWindow
:
618 flipped_screen_x
+= containing_window_frame_
.x();
619 flipped_screen_y
+= containing_window_frame_
.y();
621 case NPCoordinateSpaceScreen
:
622 flipped_screen_y
= main_display_bounds
.size
.height
- flipped_screen_y
;
624 case NPCoordinateSpaceFlippedScreen
:
631 double target_x
= flipped_screen_x
;
632 double target_y
= flipped_screen_y
;
634 case NPCoordinateSpacePlugin
:
635 target_x
-= plugin_origin_
.x();
636 target_y
-= plugin_origin_
.y();
638 case NPCoordinateSpaceWindow
:
639 target_x
-= containing_window_frame_
.x();
640 target_y
-= containing_window_frame_
.y();
641 target_y
= containing_window_frame_
.height() - target_y
;
643 case NPCoordinateSpaceFlippedWindow
:
644 target_x
-= containing_window_frame_
.x();
645 target_y
-= containing_window_frame_
.y();
647 case NPCoordinateSpaceScreen
:
648 target_y
= main_display_bounds
.size
.height
- flipped_screen_y
;
650 case NPCoordinateSpaceFlippedScreen
:
668 void PluginInstance::GetNotifyData(int notify_id
,
670 void** notify_data
) {
671 PendingRequestMap::iterator iter
= pending_requests_
.find(notify_id
);
672 if (iter
!= pending_requests_
.end()) {
674 *notify_data
= iter
->second
;
675 pending_requests_
.erase(iter
);
682 void PluginInstance::URLRedirectResponse(bool allow
, void* notify_data
) {
683 // The notify_data passed in allows us to identify the matching stream.
684 std::vector
<scoped_refptr
<PluginStream
> >::iterator stream_index
;
685 for (stream_index
= open_streams_
.begin();
686 stream_index
!= open_streams_
.end(); ++stream_index
) {
687 PluginStream
* plugin_stream
= stream_index
->get();
688 if (plugin_stream
->notify_data() == notify_data
) {
689 PluginStreamUrl
* plugin_stream_url
=
690 static_cast<PluginStreamUrl
*>(plugin_stream
);
691 plugin_stream_url
->URLRedirectResponse(allow
);
697 } // namespace content