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_
= CreateStream(-1, url
, mime_type
, 0);
410 plugin_data_stream_
->DidReceiveResponse(mime_type
, headers
, expected_length
,
411 last_modified
, true);
414 void PluginInstance::DidReceiveManualData(const char* buffer
, int length
) {
415 DCHECK(load_manually_
);
416 if (plugin_data_stream_
.get() != NULL
) {
417 plugin_data_stream_
->DidReceiveData(buffer
, length
, 0);
421 void PluginInstance::DidFinishManualLoading() {
422 DCHECK(load_manually_
);
423 if (plugin_data_stream_
.get() != NULL
) {
424 plugin_data_stream_
->DidFinishLoading(plugin_data_stream_
->ResourceId());
425 plugin_data_stream_
->Close(NPRES_DONE
);
426 plugin_data_stream_
= NULL
;
430 void PluginInstance::DidManualLoadFail() {
431 DCHECK(load_manually_
);
432 if (plugin_data_stream_
.get() != NULL
) {
433 plugin_data_stream_
->DidFail(plugin_data_stream_
->ResourceId());
434 plugin_data_stream_
= NULL
;
438 void PluginInstance::PluginThreadAsyncCall(void (*func
)(void*),
440 message_loop_
->PostTask(
441 FROM_HERE
, base::Bind(&PluginInstance::OnPluginThreadAsyncCall
, this,
445 void PluginInstance::OnPluginThreadAsyncCall(void (*func
)(void*),
447 // Do not invoke the callback if NPP_Destroy has already been invoked.
452 uint32
PluginInstance::ScheduleTimer(uint32 interval
,
454 void (*func
)(NPP id
, uint32 timer_id
)) {
455 // Use next timer id.
457 timer_id
= next_timer_id_
;
459 DCHECK(next_timer_id_
!= 0);
461 // Record timer interval and repeat.
463 info
.interval
= interval
;
464 info
.repeat
= repeat
? true : false;
465 timers_
[timer_id
] = info
;
467 // Schedule the callback.
468 base::MessageLoop::current()->PostDelayedTask(
470 base::Bind(&PluginInstance::OnTimerCall
, this, func
, npp_
, timer_id
),
471 base::TimeDelta::FromMilliseconds(interval
));
475 void PluginInstance::UnscheduleTimer(uint32 timer_id
) {
476 // Remove info about the timer.
477 TimerMap::iterator it
= timers_
.find(timer_id
);
478 if (it
!= timers_
.end())
482 #if !defined(OS_MACOSX)
483 NPError
PluginInstance::PopUpContextMenu(NPMenu
* menu
) {
485 return NPERR_GENERIC_ERROR
;
489 void PluginInstance::OnTimerCall(void (*func
)(NPP id
, uint32 timer_id
),
492 // Do not invoke callback if the timer has been unscheduled.
493 TimerMap::iterator it
= timers_
.find(timer_id
);
494 if (it
== timers_
.end())
497 // Get all information about the timer before invoking the callback. The
498 // callback might unschedule the timer.
499 TimerInfo info
= it
->second
;
503 // If the timer was unscheduled by the callback, just free up the timer id.
504 if (timers_
.find(timer_id
) == timers_
.end())
507 // Reschedule repeating timers after invoking the callback so callback is not
508 // re-entered if it pumps the message loop.
510 base::MessageLoop::current()->PostDelayedTask(
512 base::Bind(&PluginInstance::OnTimerCall
, this, func
, npp_
, timer_id
),
513 base::TimeDelta::FromMilliseconds(info
.interval
));
519 void PluginInstance::PushPopupsEnabledState(bool enabled
) {
520 popups_enabled_stack_
.push(enabled
);
523 void PluginInstance::PopPopupsEnabledState() {
524 popups_enabled_stack_
.pop();
527 void PluginInstance::RequestRead(NPStream
* stream
, NPByteRange
* range_list
) {
528 std::string range_info
= "bytes=";
531 range_info
+= base::IntToString(range_list
->offset
);
532 range_info
.push_back('-');
534 base::IntToString(range_list
->offset
+ range_list
->length
- 1);
535 range_list
= range_list
->next
;
537 range_info
.push_back(',');
540 if (plugin_data_stream_
.get()) {
541 if (plugin_data_stream_
->stream() == stream
) {
542 webplugin_
->CancelDocumentLoad();
543 plugin_data_stream_
= NULL
;
547 // The lifetime of a NPStream instance depends on the PluginStream instance
548 // which owns it. When a plugin invokes NPN_RequestRead on a seekable stream,
549 // we don't want to create a new stream when the corresponding response is
550 // received. We send over a cookie which represents the PluginStream
551 // instance which is sent back from the renderer when the response is
553 std::vector
<scoped_refptr
<PluginStream
> >::iterator stream_index
;
554 for (stream_index
= open_streams_
.begin();
555 stream_index
!= open_streams_
.end(); ++stream_index
) {
556 PluginStream
* plugin_stream
= stream_index
->get();
557 if (plugin_stream
->stream() == stream
) {
558 // A stream becomes seekable the first time NPN_RequestRead
560 plugin_stream
->set_seekable(true);
562 if (CommandLine::ForCurrentProcess()->HasSwitch(
563 switches::kDisableDirectNPAPIRequests
)) {
564 pending_range_requests_
[++next_range_request_id_
] = plugin_stream
;
565 webplugin_
->InitiateHTTPRangeRequest(
566 stream
->url
, range_info
.c_str(), next_range_request_id_
);
569 PluginStreamUrl
* plugin_stream_url
=
570 static_cast<PluginStreamUrl
*>(plugin_stream
);
571 plugin_stream_url
->FetchRange(range_info
);
579 void PluginInstance::RequestURL(const char* url
,
588 notify_id
= ++next_notify_id_
;
589 pending_requests_
[notify_id
] = notify_data
;
592 webplugin_
->HandleURLRequest(
593 url
, method
, target
, buf
, len
, notify_id
, popups_allowed(),
594 notify
? handles_url_redirects_
: false);
597 bool PluginInstance::ConvertPoint(double source_x
, double source_y
,
598 NPCoordinateSpace source_space
,
599 double* dest_x
, double* dest_y
,
600 NPCoordinateSpace dest_space
) {
601 #if defined(OS_MACOSX)
602 CGRect main_display_bounds
= CGDisplayBounds(CGMainDisplayID());
604 double flipped_screen_x
= source_x
;
605 double flipped_screen_y
= source_y
;
606 switch(source_space
) {
607 case NPCoordinateSpacePlugin
:
608 flipped_screen_x
+= plugin_origin_
.x();
609 flipped_screen_y
+= plugin_origin_
.y();
611 case NPCoordinateSpaceWindow
:
612 flipped_screen_x
+= containing_window_frame_
.x();
613 flipped_screen_y
= containing_window_frame_
.height() - source_y
+
614 containing_window_frame_
.y();
616 case NPCoordinateSpaceFlippedWindow
:
617 flipped_screen_x
+= containing_window_frame_
.x();
618 flipped_screen_y
+= containing_window_frame_
.y();
620 case NPCoordinateSpaceScreen
:
621 flipped_screen_y
= main_display_bounds
.size
.height
- flipped_screen_y
;
623 case NPCoordinateSpaceFlippedScreen
:
630 double target_x
= flipped_screen_x
;
631 double target_y
= flipped_screen_y
;
633 case NPCoordinateSpacePlugin
:
634 target_x
-= plugin_origin_
.x();
635 target_y
-= plugin_origin_
.y();
637 case NPCoordinateSpaceWindow
:
638 target_x
-= containing_window_frame_
.x();
639 target_y
-= containing_window_frame_
.y();
640 target_y
= containing_window_frame_
.height() - target_y
;
642 case NPCoordinateSpaceFlippedWindow
:
643 target_x
-= containing_window_frame_
.x();
644 target_y
-= containing_window_frame_
.y();
646 case NPCoordinateSpaceScreen
:
647 target_y
= main_display_bounds
.size
.height
- flipped_screen_y
;
649 case NPCoordinateSpaceFlippedScreen
:
667 void PluginInstance::GetNotifyData(int notify_id
,
669 void** notify_data
) {
670 PendingRequestMap::iterator iter
= pending_requests_
.find(notify_id
);
671 if (iter
!= pending_requests_
.end()) {
673 *notify_data
= iter
->second
;
674 pending_requests_
.erase(iter
);
681 void PluginInstance::URLRedirectResponse(bool allow
, void* notify_data
) {
682 // The notify_data passed in allows us to identify the matching stream.
683 std::vector
<scoped_refptr
<PluginStream
> >::iterator stream_index
;
684 for (stream_index
= open_streams_
.begin();
685 stream_index
!= open_streams_
.end(); ++stream_index
) {
686 PluginStream
* plugin_stream
= stream_index
->get();
687 if (plugin_stream
->notify_data() == notify_data
) {
688 PluginStreamUrl
* plugin_stream_url
=
689 static_cast<PluginStreamUrl
*>(plugin_stream
);
690 plugin_stream_url
->URLRedirectResponse(allow
);
696 } // namespace content