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/files/file_util.h"
10 #include "base/location.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "build/build_config.h"
16 #include "content/child/npapi/plugin_host.h"
17 #include "content/child/npapi/plugin_lib.h"
18 #include "content/child/npapi/plugin_stream_url.h"
19 #include "content/child/npapi/plugin_string_stream.h"
20 #include "content/child/npapi/webplugin.h"
21 #include "content/child/npapi/webplugin_delegate.h"
22 #include "content/child/npapi/webplugin_resource_client.h"
23 #include "content/public/common/content_constants.h"
24 #include "content/public/common/content_switches.h"
25 #include "net/base/escape.h"
27 #if defined(OS_MACOSX)
28 #include <ApplicationServices/ApplicationServices.h>
33 PluginInstance::PluginInstance(PluginLib
* plugin
, const std::string
& mime_type
)
36 host_(PluginHost::Singleton()),
37 npp_functions_(plugin
->functions()),
42 mime_type_(mime_type
),
44 use_mozilla_user_agent_(false),
45 #if defined (OS_MACOSX)
46 #ifdef NP_NO_QUICKDRAW
47 drawing_model_(NPDrawingModelCoreGraphics
),
49 drawing_model_(NPDrawingModelQuickDraw
),
52 event_model_(NPEventModelCocoa
),
54 event_model_(NPEventModelCarbon
),
56 currently_handled_event_(NULL
),
58 task_runner_(base::ThreadTaskRunnerHandle::Get()),
59 load_manually_(false),
60 in_close_streams_(false),
63 next_range_request_id_(0),
64 handles_url_redirects_(false) {
69 if (mime_type_
== kFlashPluginSwfMimeType
)
72 memset(&zero_padding_
, 0, sizeof(zero_padding_
));
75 PluginInstance::~PluginInstance() {
84 plugin_
->CloseInstance();
87 PluginStreamUrl
* PluginInstance::CreateStream(unsigned long resource_id
,
89 const std::string
& mime_type
,
94 GetNotifyData(notify_id
, ¬ify
, ¬ify_data
);
95 PluginStreamUrl
* stream
= new PluginStreamUrl(
96 resource_id
, url
, this, notify
, notify_data
);
102 void PluginInstance::AddStream(PluginStream
* stream
) {
103 open_streams_
.push_back(make_scoped_refptr(stream
));
106 void PluginInstance::RemoveStream(PluginStream
* stream
) {
107 if (in_close_streams_
)
110 std::vector
<scoped_refptr
<PluginStream
> >::iterator stream_index
;
111 for (stream_index
= open_streams_
.begin();
112 stream_index
!= open_streams_
.end(); ++stream_index
) {
113 if (stream_index
->get() == stream
) {
114 open_streams_
.erase(stream_index
);
120 bool PluginInstance::IsValidStream(const NPStream
* stream
) {
121 std::vector
<scoped_refptr
<PluginStream
> >::iterator stream_index
;
122 for (stream_index
= open_streams_
.begin();
123 stream_index
!= open_streams_
.end(); ++stream_index
) {
124 if ((*stream_index
)->stream() == stream
)
131 void PluginInstance::CloseStreams() {
132 in_close_streams_
= true;
133 for (unsigned int index
= 0; index
< open_streams_
.size(); ++index
) {
134 // Close all streams on the way down.
135 open_streams_
[index
]->Close(NPRES_USER_BREAK
);
137 open_streams_
.clear();
138 in_close_streams_
= false;
141 WebPluginResourceClient
* PluginInstance::GetRangeRequest(
143 PendingRangeRequestMap::iterator iter
= pending_range_requests_
.find(id
);
144 if (iter
== pending_range_requests_
.end()) {
149 WebPluginResourceClient
* rv
= iter
->second
->AsResourceClient();
150 pending_range_requests_
.erase(iter
);
154 bool PluginInstance::Start(const GURL
& url
,
155 char** const param_names
,
156 char** const param_values
,
158 bool load_manually
) {
159 load_manually_
= load_manually
;
160 unsigned short mode
= load_manually_
? NP_FULL
: NP_EMBED
;
163 NPError err
= NPP_New(mode
, param_count
,
164 const_cast<char **>(param_names
), const_cast<char **>(param_values
));
166 if (err
== NPERR_NO_ERROR
) {
167 handles_url_redirects_
=
168 ((npp_functions_
->version
>= NPVERS_HAS_URL_REDIRECT_HANDLING
) &&
169 (npp_functions_
->urlredirectnotify
));
171 return err
== NPERR_NO_ERROR
;
174 NPObject
*PluginInstance::GetPluginScriptableObject() {
175 NPObject
*value
= NULL
;
176 NPError error
= NPP_GetValue(NPPVpluginScriptableNPObject
, &value
);
177 if (error
!= NPERR_NO_ERROR
|| value
== NULL
)
182 bool PluginInstance::GetFormValue(base::string16
* value
) {
183 // Plugins will allocate memory for the return value by using NPN_MemAlloc().
184 char *plugin_value
= NULL
;
185 NPError error
= NPP_GetValue(NPPVformValue
, &plugin_value
);
186 if (error
!= NPERR_NO_ERROR
|| !plugin_value
) {
189 // Assumes the result is UTF8 text, as Firefox does.
190 *value
= base::UTF8ToUTF16(plugin_value
);
191 host_
->host_functions()->memfree(plugin_value
);
195 // WebPluginLoadDelegate methods
196 void PluginInstance::DidFinishLoadWithReason(const GURL
& url
,
201 GetNotifyData(notify_id
, ¬ify
, ¬ify_data
);
207 NPP_URLNotify(url
.spec().c_str(), reason
, notify_data
);
210 unsigned PluginInstance::GetBackingTextureId() {
211 // By default the plugin instance is not backed by an OpenGL texture.
216 NPError
PluginInstance::NPP_New(unsigned short mode
,
220 DCHECK(npp_functions_
!= 0);
221 DCHECK(npp_functions_
->newp
!= 0);
224 if (npp_functions_
->newp
!= 0) {
225 return npp_functions_
->newp(
226 (NPMIMEType
)mime_type_
.c_str(), npp_
, mode
, argc
, argn
, argv
, NULL
);
228 return NPERR_INVALID_FUNCTABLE_ERROR
;
231 void PluginInstance::NPP_Destroy() {
232 DCHECK(npp_functions_
!= 0);
233 DCHECK(npp_functions_
->destroy
!= 0);
235 if (npp_functions_
->destroy
!= 0) {
236 NPSavedData
*savedData
= 0;
237 npp_functions_
->destroy(npp_
, &savedData
);
239 // TODO: Support savedData. Technically, these need to be
240 // saved on a per-URL basis, and then only passed
241 // to new instances of the plugin at the same URL.
242 // Sounds like a huge security risk. When we do support
243 // these, we should pass them back to the PluginLib
244 // to be stored there.
245 DCHECK(savedData
== 0);
248 for (unsigned int file_index
= 0; file_index
< files_created_
.size();
250 base::DeleteFile(files_created_
[file_index
], false);
253 // Ensure that no timer callbacks are invoked after NPP_Destroy.
257 NPError
PluginInstance::NPP_SetWindow(NPWindow
* window
) {
258 DCHECK(npp_functions_
!= 0);
259 DCHECK(npp_functions_
->setwindow
!= 0);
261 if (npp_functions_
->setwindow
!= 0) {
262 return npp_functions_
->setwindow(npp_
, window
);
264 return NPERR_INVALID_FUNCTABLE_ERROR
;
267 NPError
PluginInstance::NPP_NewStream(NPMIMEType type
,
270 unsigned short* stype
) {
271 DCHECK(npp_functions_
!= 0);
272 DCHECK(npp_functions_
->newstream
!= 0);
273 if (npp_functions_
->newstream
!= 0) {
274 return npp_functions_
->newstream(npp_
, type
, stream
, seekable
, stype
);
276 return NPERR_INVALID_FUNCTABLE_ERROR
;
279 NPError
PluginInstance::NPP_DestroyStream(NPStream
* stream
, NPReason reason
) {
280 DCHECK(npp_functions_
!= 0);
281 DCHECK(npp_functions_
->destroystream
!= 0);
283 if (stream
== NULL
|| !IsValidStream(stream
) || (stream
->ndata
== NULL
))
284 return NPERR_INVALID_INSTANCE_ERROR
;
286 if (npp_functions_
->destroystream
!= 0) {
287 NPError result
= npp_functions_
->destroystream(npp_
, stream
, reason
);
288 stream
->ndata
= NULL
;
291 return NPERR_INVALID_FUNCTABLE_ERROR
;
294 int PluginInstance::NPP_WriteReady(NPStream
* stream
) {
295 DCHECK(npp_functions_
!= 0);
296 DCHECK(npp_functions_
->writeready
!= 0);
297 if (npp_functions_
->writeready
!= 0) {
298 return npp_functions_
->writeready(npp_
, stream
);
303 int PluginInstance::NPP_Write(NPStream
* stream
,
307 DCHECK(npp_functions_
!= 0);
308 DCHECK(npp_functions_
->write
!= 0);
309 if (npp_functions_
->write
!= 0) {
310 return npp_functions_
->write(npp_
, stream
, offset
, len
, buffer
);
315 void PluginInstance::NPP_StreamAsFile(NPStream
* stream
, const char* fname
) {
316 DCHECK(npp_functions_
!= 0);
317 DCHECK(npp_functions_
->asfile
!= 0);
318 if (npp_functions_
->asfile
!= 0) {
319 npp_functions_
->asfile(npp_
, stream
, fname
);
322 // Creating a temporary FilePath instance on the stack as the explicit
323 // FilePath constructor with StringType as an argument causes a compiler
324 // error when invoked via vector push back.
325 base::FilePath file_name
= base::FilePath::FromUTF8Unsafe(fname
);
326 files_created_
.push_back(file_name
);
329 void PluginInstance::NPP_URLNotify(const char* url
,
332 DCHECK(npp_functions_
!= 0);
333 DCHECK(npp_functions_
->urlnotify
!= 0);
334 if (npp_functions_
->urlnotify
!= 0) {
335 npp_functions_
->urlnotify(npp_
, url
, reason
, notifyData
);
339 NPError
PluginInstance::NPP_GetValue(NPPVariable variable
, void* value
) {
340 DCHECK(npp_functions_
!= 0);
341 // getvalue is NULL for Shockwave
342 if (npp_functions_
->getvalue
!= 0) {
343 return npp_functions_
->getvalue(npp_
, variable
, value
);
345 return NPERR_INVALID_FUNCTABLE_ERROR
;
348 NPError
PluginInstance::NPP_SetValue(NPNVariable variable
, void* value
) {
349 DCHECK(npp_functions_
!= 0);
350 if (npp_functions_
->setvalue
!= 0) {
351 return npp_functions_
->setvalue(npp_
, variable
, value
);
353 return NPERR_INVALID_FUNCTABLE_ERROR
;
356 short PluginInstance::NPP_HandleEvent(void* event
) {
357 DCHECK(npp_functions_
!= 0);
358 DCHECK(npp_functions_
->event
!= 0);
359 if (npp_functions_
->event
!= 0) {
360 return npp_functions_
->event(npp_
, (void*)event
);
365 bool PluginInstance::NPP_Print(NPPrint
* platform_print
) {
366 DCHECK(npp_functions_
!= 0);
367 if (npp_functions_
->print
!= 0) {
368 npp_functions_
->print(npp_
, platform_print
);
374 void PluginInstance::NPP_URLRedirectNotify(const char* url
, int32_t status
,
376 DCHECK(npp_functions_
!= 0);
377 if (npp_functions_
->urlredirectnotify
!= 0) {
378 npp_functions_
->urlredirectnotify(npp_
, url
, status
, notify_data
);
382 void PluginInstance::SendJavaScriptStream(const GURL
& url
,
383 const std::string
& result
,
388 GetNotifyData(notify_id
, ¬ify
, ¬ify_data
);
391 PluginStringStream
*stream
=
392 new PluginStringStream(this, url
, notify
, notify_data
);
394 stream
->SendToPlugin(result
, "text/html");
396 // NOTE: Sending an empty stream here will crash MacroMedia
397 // Flash 9. Just send the URL Notify.
399 NPP_URLNotify(url
.spec().c_str(), NPRES_DONE
, notify_data
);
403 void PluginInstance::DidReceiveManualResponse(const GURL
& url
,
404 const std::string
& mime_type
,
405 const std::string
& headers
,
406 uint32 expected_length
,
407 uint32 last_modified
) {
408 DCHECK(load_manually_
);
410 plugin_data_stream_
=
411 CreateStream(static_cast<unsigned long>(-1), url
, mime_type
, 0);
412 plugin_data_stream_
->DidReceiveResponse(mime_type
, headers
, expected_length
,
413 last_modified
, true);
416 void PluginInstance::DidReceiveManualData(const char* buffer
, int length
) {
417 DCHECK(load_manually_
);
418 if (plugin_data_stream_
.get() != NULL
) {
419 plugin_data_stream_
->DidReceiveData(buffer
, length
, 0);
423 void PluginInstance::DidFinishManualLoading() {
424 DCHECK(load_manually_
);
425 if (plugin_data_stream_
.get() != NULL
) {
426 plugin_data_stream_
->DidFinishLoading(plugin_data_stream_
->ResourceId());
427 plugin_data_stream_
->Close(NPRES_DONE
);
428 plugin_data_stream_
= NULL
;
432 void PluginInstance::DidManualLoadFail() {
433 DCHECK(load_manually_
);
434 if (plugin_data_stream_
.get() != NULL
) {
435 plugin_data_stream_
->DidFail(plugin_data_stream_
->ResourceId());
436 plugin_data_stream_
= NULL
;
440 void PluginInstance::PluginThreadAsyncCall(void (*func
)(void*),
442 task_runner_
->PostTask(
443 FROM_HERE
, base::Bind(&PluginInstance::OnPluginThreadAsyncCall
, this,
447 void PluginInstance::OnPluginThreadAsyncCall(void (*func
)(void*),
449 // Do not invoke the callback if NPP_Destroy has already been invoked.
454 uint32
PluginInstance::ScheduleTimer(uint32 interval
,
456 void (*func
)(NPP id
, uint32 timer_id
)) {
457 // Use next timer id.
459 timer_id
= next_timer_id_
;
461 DCHECK(next_timer_id_
!= 0);
463 // Record timer interval and repeat.
465 info
.interval
= interval
;
466 info
.repeat
= repeat
? true : false;
467 timers_
[timer_id
] = info
;
469 // Schedule the callback.
470 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
472 base::Bind(&PluginInstance::OnTimerCall
, this, func
, npp_
, timer_id
),
473 base::TimeDelta::FromMilliseconds(interval
));
477 void PluginInstance::UnscheduleTimer(uint32 timer_id
) {
478 // Remove info about the timer.
479 TimerMap::iterator it
= timers_
.find(timer_id
);
480 if (it
!= timers_
.end())
484 #if !defined(OS_MACOSX)
485 NPError
PluginInstance::PopUpContextMenu(NPMenu
* menu
) {
487 return NPERR_GENERIC_ERROR
;
491 void PluginInstance::OnTimerCall(void (*func
)(NPP id
, uint32 timer_id
),
494 // Do not invoke callback if the timer has been unscheduled.
495 TimerMap::iterator it
= timers_
.find(timer_id
);
496 if (it
== timers_
.end())
499 // Get all information about the timer before invoking the callback. The
500 // callback might unschedule the timer.
501 TimerInfo info
= it
->second
;
505 // If the timer was unscheduled by the callback, just free up the timer id.
506 if (timers_
.find(timer_id
) == timers_
.end())
509 // Reschedule repeating timers after invoking the callback so callback is not
510 // re-entered if it pumps the message loop.
512 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
514 base::Bind(&PluginInstance::OnTimerCall
, this, func
, npp_
, timer_id
),
515 base::TimeDelta::FromMilliseconds(info
.interval
));
521 void PluginInstance::PushPopupsEnabledState(bool enabled
) {
522 popups_enabled_stack_
.push(enabled
);
525 void PluginInstance::PopPopupsEnabledState() {
526 popups_enabled_stack_
.pop();
529 void PluginInstance::RequestRead(NPStream
* stream
, NPByteRange
* range_list
) {
530 std::string range_info
= "bytes=";
533 range_info
+= base::IntToString(range_list
->offset
);
534 range_info
.push_back('-');
536 base::IntToString(range_list
->offset
+ range_list
->length
- 1);
537 range_list
= range_list
->next
;
539 range_info
.push_back(',');
542 if (plugin_data_stream_
.get()) {
543 if (plugin_data_stream_
->stream() == stream
) {
544 webplugin_
->CancelDocumentLoad();
545 plugin_data_stream_
= NULL
;
549 // The lifetime of a NPStream instance depends on the PluginStream instance
550 // which owns it. When a plugin invokes NPN_RequestRead on a seekable stream,
551 // we don't want to create a new stream when the corresponding response is
552 // received. We send over a cookie which represents the PluginStream
553 // instance which is sent back from the renderer when the response is
555 std::vector
<scoped_refptr
<PluginStream
> >::iterator stream_index
;
556 for (stream_index
= open_streams_
.begin();
557 stream_index
!= open_streams_
.end(); ++stream_index
) {
558 PluginStream
* plugin_stream
= stream_index
->get();
559 if (plugin_stream
->stream() == stream
) {
560 // A stream becomes seekable the first time NPN_RequestRead
562 plugin_stream
->set_seekable(true);
564 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
565 switches::kDisableDirectNPAPIRequests
)) {
566 pending_range_requests_
[++next_range_request_id_
] = plugin_stream
;
567 webplugin_
->InitiateHTTPRangeRequest(
568 stream
->url
, range_info
.c_str(), next_range_request_id_
);
571 PluginStreamUrl
* plugin_stream_url
=
572 static_cast<PluginStreamUrl
*>(plugin_stream
);
573 plugin_stream_url
->FetchRange(range_info
);
581 void PluginInstance::RequestURL(const char* url
,
590 notify_id
= ++next_notify_id_
;
591 pending_requests_
[notify_id
] = notify_data
;
594 webplugin_
->HandleURLRequest(
595 url
, method
, target
, buf
, len
, notify_id
, popups_allowed(),
596 notify
? handles_url_redirects_
: false);
599 bool PluginInstance::ConvertPoint(double source_x
, double source_y
,
600 NPCoordinateSpace source_space
,
601 double* dest_x
, double* dest_y
,
602 NPCoordinateSpace dest_space
) {
603 #if defined(OS_MACOSX)
604 CGRect main_display_bounds
= CGDisplayBounds(CGMainDisplayID());
606 double flipped_screen_x
= source_x
;
607 double flipped_screen_y
= source_y
;
608 switch(source_space
) {
609 case NPCoordinateSpacePlugin
:
610 flipped_screen_x
+= plugin_origin_
.x();
611 flipped_screen_y
+= plugin_origin_
.y();
613 case NPCoordinateSpaceWindow
:
614 flipped_screen_x
+= containing_window_frame_
.x();
615 flipped_screen_y
= containing_window_frame_
.height() - source_y
+
616 containing_window_frame_
.y();
618 case NPCoordinateSpaceFlippedWindow
:
619 flipped_screen_x
+= containing_window_frame_
.x();
620 flipped_screen_y
+= containing_window_frame_
.y();
622 case NPCoordinateSpaceScreen
:
623 flipped_screen_y
= main_display_bounds
.size
.height
- flipped_screen_y
;
625 case NPCoordinateSpaceFlippedScreen
:
632 double target_x
= flipped_screen_x
;
633 double target_y
= flipped_screen_y
;
635 case NPCoordinateSpacePlugin
:
636 target_x
-= plugin_origin_
.x();
637 target_y
-= plugin_origin_
.y();
639 case NPCoordinateSpaceWindow
:
640 target_x
-= containing_window_frame_
.x();
641 target_y
-= containing_window_frame_
.y();
642 target_y
= containing_window_frame_
.height() - target_y
;
644 case NPCoordinateSpaceFlippedWindow
:
645 target_x
-= containing_window_frame_
.x();
646 target_y
-= containing_window_frame_
.y();
648 case NPCoordinateSpaceScreen
:
649 target_y
= main_display_bounds
.size
.height
- flipped_screen_y
;
651 case NPCoordinateSpaceFlippedScreen
:
669 void PluginInstance::GetNotifyData(int notify_id
,
671 void** notify_data
) {
672 PendingRequestMap::iterator iter
= pending_requests_
.find(notify_id
);
673 if (iter
!= pending_requests_
.end()) {
675 *notify_data
= iter
->second
;
676 pending_requests_
.erase(iter
);
683 void PluginInstance::URLRedirectResponse(bool allow
, void* notify_data
) {
684 // The notify_data passed in allows us to identify the matching stream.
685 std::vector
<scoped_refptr
<PluginStream
> >::iterator stream_index
;
686 for (stream_index
= open_streams_
.begin();
687 stream_index
!= open_streams_
.end(); ++stream_index
) {
688 PluginStream
* plugin_stream
= stream_index
->get();
689 if (plugin_stream
->notify_data() == notify_data
) {
690 PluginStreamUrl
* plugin_stream_url
=
691 static_cast<PluginStreamUrl
*>(plugin_stream
);
692 plugin_stream_url
->URLRedirectResponse(allow
);
698 } // namespace content