blob: 22fe6d011d394b20f59b34b2873ff6695f37aa40 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/child/npapi/plugin_host.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "content/child/npapi/plugin_instance.h"
#include "content/child/npapi/plugin_lib.h"
#include "content/child/npapi/plugin_stream_url.h"
#include "content/child/npapi/webplugin_delegate.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/webplugininfo.h"
#include "net/base/net_util.h"
#include "third_party/WebKit/public/web/WebBindings.h"
#include "third_party/WebKit/public/web/WebKit.h"
#include "third_party/npapi/bindings/npruntime.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface.h"
#include "webkit/common/user_agent/user_agent.h"
#if defined(OS_MACOSX)
#include "base/mac/mac_util.h"
#endif
using blink::WebBindings;
// Declarations for stub implementations of deprecated functions, which are no
// longer listed in npapi.h.
extern "C" {
void* NPN_GetJavaEnv();
void* NPN_GetJavaPeer(NPP);
}
namespace content {
// Finds a PluginInstance from an NPP.
// The caller must take a reference if needed.
static PluginInstance* FindInstance(NPP id) {
if (id == NULL) {
return NULL;
}
return reinterpret_cast<PluginInstance*>(id->ndata);
}
#if defined(OS_MACOSX)
// Returns true if Core Animation plugins are supported. This requires that the
// OS supports shared accelerated surfaces via IOSurface. This is true on Snow
// Leopard and higher.
static bool SupportsCoreAnimationPlugins() {
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableCoreAnimationPlugins))
return false;
// We also need to be running with desktop GL and not the software
// OSMesa renderer in order to share accelerated surfaces between
// processes.
gfx::GLImplementation implementation = gfx::GetGLImplementation();
if (implementation == gfx::kGLImplementationNone) {
// Not initialized yet.
if (!gfx::GLSurface::InitializeOneOff()) {
return false;
}
implementation = gfx::GetGLImplementation();
}
return (implementation == gfx::kGLImplementationDesktopGL);
}
#endif
PluginHost::PluginHost() {
InitializeHostFuncs();
}
PluginHost::~PluginHost() {
}
PluginHost *PluginHost::Singleton() {
CR_DEFINE_STATIC_LOCAL(scoped_refptr<PluginHost>, singleton, ());
if (singleton.get() == NULL) {
singleton = new PluginHost();
}
DCHECK(singleton.get() != NULL);
return singleton.get();
}
void PluginHost::InitializeHostFuncs() {
memset(&host_funcs_, 0, sizeof(host_funcs_));
host_funcs_.size = sizeof(host_funcs_);
host_funcs_.version = (NP_VERSION_MAJOR << 8) | (NP_VERSION_MINOR);
// The "basic" functions
host_funcs_.geturl = &NPN_GetURL;
host_funcs_.posturl = &NPN_PostURL;
host_funcs_.requestread = &NPN_RequestRead;
host_funcs_.newstream = &NPN_NewStream;
host_funcs_.write = &NPN_Write;
host_funcs_.destroystream = &NPN_DestroyStream;
host_funcs_.status = &NPN_Status;
host_funcs_.uagent = &NPN_UserAgent;
host_funcs_.memalloc = &NPN_MemAlloc;
host_funcs_.memfree = &NPN_MemFree;
host_funcs_.memflush = &NPN_MemFlush;
host_funcs_.reloadplugins = &NPN_ReloadPlugins;
// Stubs for deprecated Java functions
host_funcs_.getJavaEnv = &NPN_GetJavaEnv;
host_funcs_.getJavaPeer = &NPN_GetJavaPeer;
// Advanced functions we implement
host_funcs_.geturlnotify = &NPN_GetURLNotify;
host_funcs_.posturlnotify = &NPN_PostURLNotify;
host_funcs_.getvalue = &NPN_GetValue;
host_funcs_.setvalue = &NPN_SetValue;
host_funcs_.invalidaterect = &NPN_InvalidateRect;
host_funcs_.invalidateregion = &NPN_InvalidateRegion;
host_funcs_.forceredraw = &NPN_ForceRedraw;
// These come from the Javascript Engine
host_funcs_.getstringidentifier = WebBindings::getStringIdentifier;
host_funcs_.getstringidentifiers = WebBindings::getStringIdentifiers;
host_funcs_.getintidentifier = WebBindings::getIntIdentifier;
host_funcs_.identifierisstring = WebBindings::identifierIsString;
host_funcs_.utf8fromidentifier = WebBindings::utf8FromIdentifier;
host_funcs_.intfromidentifier = WebBindings::intFromIdentifier;
host_funcs_.createobject = WebBindings::createObject;
host_funcs_.retainobject = WebBindings::retainObject;
host_funcs_.releaseobject = WebBindings::releaseObject;
host_funcs_.invoke = WebBindings::invoke;
host_funcs_.invokeDefault = WebBindings::invokeDefault;
host_funcs_.evaluate = WebBindings::evaluate;
host_funcs_.getproperty = WebBindings::getProperty;
host_funcs_.setproperty = WebBindings::setProperty;
host_funcs_.removeproperty = WebBindings::removeProperty;
host_funcs_.hasproperty = WebBindings::hasProperty;
host_funcs_.hasmethod = WebBindings::hasMethod;
host_funcs_.releasevariantvalue = WebBindings::releaseVariantValue;
host_funcs_.setexception = WebBindings::setException;
host_funcs_.pushpopupsenabledstate = NPN_PushPopupsEnabledState;
host_funcs_.poppopupsenabledstate = NPN_PopPopupsEnabledState;
host_funcs_.enumerate = WebBindings::enumerate;
host_funcs_.pluginthreadasynccall = NPN_PluginThreadAsyncCall;
host_funcs_.construct = WebBindings::construct;
host_funcs_.getvalueforurl = NPN_GetValueForURL;
host_funcs_.setvalueforurl = NPN_SetValueForURL;
host_funcs_.getauthenticationinfo = NPN_GetAuthenticationInfo;
host_funcs_.scheduletimer = NPN_ScheduleTimer;
host_funcs_.unscheduletimer = NPN_UnscheduleTimer;
host_funcs_.popupcontextmenu = NPN_PopUpContextMenu;
host_funcs_.convertpoint = NPN_ConvertPoint;
host_funcs_.handleevent = NPN_HandleEvent;
host_funcs_.unfocusinstance = NPN_UnfocusInstance;
host_funcs_.urlredirectresponse = NPN_URLRedirectResponse;
}
void PluginHost::PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides) {
// When running in the plugin process, we need to patch the NPN functions
// that the plugin calls to interact with NPObjects that we give. Otherwise
// the plugin will call the v8 NPN functions, which won't work since we have
// an NPObjectProxy and not a real v8 implementation.
if (overrides->invoke)
host_funcs_.invoke = overrides->invoke;
if (overrides->invokeDefault)
host_funcs_.invokeDefault = overrides->invokeDefault;
if (overrides->evaluate)
host_funcs_.evaluate = overrides->evaluate;
if (overrides->getproperty)
host_funcs_.getproperty = overrides->getproperty;
if (overrides->setproperty)
host_funcs_.setproperty = overrides->setproperty;
if (overrides->removeproperty)
host_funcs_.removeproperty = overrides->removeproperty;
if (overrides->hasproperty)
host_funcs_.hasproperty = overrides->hasproperty;
if (overrides->hasmethod)
host_funcs_.hasmethod = overrides->hasmethod;
if (overrides->setexception)
host_funcs_.setexception = overrides->setexception;
if (overrides->enumerate)
host_funcs_.enumerate = overrides->enumerate;
}
bool PluginHost::SetPostData(const char* buf,
uint32 length,
std::vector<std::string>* names,
std::vector<std::string>* values,
std::vector<char>* body) {
// Use a state table to do the parsing. Whitespace must be
// trimmed after the fact if desired. In our case, we actually
// don't care about the whitespace, because we're just going to
// pass this back into another POST. This function strips out the
// "Content-length" header and does not append it to the request.
//
// This parser takes action only on state changes.
//
// Transition table:
// : \n NULL Other
// 0 GetHeader 1 2 4 0
// 1 GetValue 1 0 3 1
// 2 GetData 2 2 3 2
// 3 DONE
// 4 ERR
//
enum { INPUT_COLON=0, INPUT_NEWLINE, INPUT_NULL, INPUT_OTHER };
enum { GETNAME, GETVALUE, GETDATA, DONE, ERR };
int statemachine[3][4] = { { GETVALUE, GETDATA, GETDATA, GETNAME },
{ GETVALUE, GETNAME, DONE, GETVALUE },
{ GETDATA, GETDATA, DONE, GETDATA } };
std::string name, value;
const char* ptr = static_cast<const char*>(buf);
const char* start = ptr;
int state = GETNAME; // initial state
bool done = false;
bool err = false;
do {
int input;
// Translate the current character into an input
// for the state table.
switch (*ptr) {
case ':' :
input = INPUT_COLON;
break;
case '\n':
input = INPUT_NEWLINE;
break;
case 0 :
input = INPUT_NULL;
break;
default :
input = INPUT_OTHER;
break;
}
int newstate = statemachine[state][input];
// Take action based on the new state.
if (state != newstate) {
switch (newstate) {
case GETNAME:
// Got a value.
value = std::string(start, ptr - start);
TrimWhitespace(value, TRIM_ALL, &value);
// If the name field is empty, we'll skip this header
// but we won't error out.
if (!name.empty() && name != "content-length") {
names->push_back(name);
values->push_back(value);
}
start = ptr + 1;
break;
case GETVALUE:
// Got a header.
name = StringToLowerASCII(std::string(start, ptr - start));
TrimWhitespace(name, TRIM_ALL, &name);
start = ptr + 1;
break;
case GETDATA: {
// Finished headers, now get body
if (*ptr)
start = ptr + 1;
size_t previous_size = body->size();
size_t new_body_size = length - static_cast<int>(start - buf);
body->resize(previous_size + new_body_size);
if (!body->empty())
memcpy(&body->front() + previous_size, start, new_body_size);
done = true;
break;
}
case ERR:
// error
err = true;
done = true;
break;
}
}
state = newstate;
ptr++;
} while (!done);
return !err;
}
} // namespace content
extern "C" {
using content::FindInstance;
using content::PluginHost;
using content::PluginInstance;
using content::WebPlugin;
// Allocates memory from the host's memory space.
void* NPN_MemAlloc(uint32_t size) {
// Note: We must use the same allocator/deallocator
// that is used by the javascript library, as some of the
// JS APIs will pass memory to the plugin which the plugin
// will attempt to free.
return malloc(size);
}
// Deallocates memory from the host's memory space
void NPN_MemFree(void* ptr) {
if (ptr != NULL && ptr != reinterpret_cast<void*>(-1))
free(ptr);
}
// Requests that the host free a specified amount of memory.
uint32_t NPN_MemFlush(uint32_t size) {
// This is not relevant on Windows; MAC specific
return size;
}
// This is for dynamic discovery of new plugins.
// Should force a re-scan of the plugins directory to load new ones.
void NPN_ReloadPlugins(NPBool reload_pages) {
blink::resetPluginCache(reload_pages ? true : false);
}
// Requests a range of bytes for a seekable stream.
NPError NPN_RequestRead(NPStream* stream, NPByteRange* range_list) {
if (!stream || !range_list)
return NPERR_GENERIC_ERROR;
scoped_refptr<PluginInstance> plugin(
reinterpret_cast<PluginInstance*>(stream->ndata));
if (!plugin.get())
return NPERR_GENERIC_ERROR;
plugin->RequestRead(stream, range_list);
return NPERR_NO_ERROR;
}
// Generic form of GetURL for common code between GetURL and GetURLNotify.
static NPError GetURLNotify(NPP id,
const char* url,
const char* target,
bool notify,
void* notify_data) {
if (!url)
return NPERR_INVALID_URL;
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (!plugin.get()) {
return NPERR_GENERIC_ERROR;
}
plugin->RequestURL(url, "GET", target, NULL, 0, notify, notify_data);
return NPERR_NO_ERROR;
}
// Requests creation of a new stream with the contents of the
// specified URL; gets notification of the result.
NPError NPN_GetURLNotify(NPP id,
const char* url,
const char* target,
void* notify_data) {
// This is identical to NPN_GetURL, but after finishing, the
// browser will call NPP_URLNotify to inform the plugin that
// it has completed.
// According to the NPAPI documentation, if target == _self
// or a parent to _self, the browser should return NPERR_INVALID_PARAM,
// because it can't notify the plugin once deleted. This is
// absolutely false; firefox doesn't do this, and Flash relies on
// being able to use this.
// Also according to the NPAPI documentation, we should return
// NPERR_INVALID_URL if the url requested is not valid. However,
// this would require that we synchronously start fetching the
// URL. That just isn't practical. As such, there really is
// no way to return this error. From looking at the Firefox
// implementation, it doesn't look like Firefox does this either.
return GetURLNotify(id, url, target, true, notify_data);
}
NPError NPN_GetURL(NPP id, const char* url, const char* target) {
// Notes:
// Request from the Plugin to fetch content either for the plugin
// or to be placed into a browser window.
//
// If target == null, the browser fetches content and streams to plugin.
// otherwise, the browser loads content into an existing browser frame.
// If the target is the window/frame containing the plugin, the plugin
// may be destroyed.
// If the target is _blank, a mailto: or news: url open content in a new
// browser window
// If the target is _self, no other instance of the plugin is created. The
// plugin continues to operate in its own window
return GetURLNotify(id, url, target, false, 0);
}
// Generic form of PostURL for common code between PostURL and PostURLNotify.
static NPError PostURLNotify(NPP id,
const char* url,
const char* target,
uint32_t len,
const char* buf,
NPBool file,
bool notify,
void* notify_data) {
if (!url)
return NPERR_INVALID_URL;
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (!plugin.get()) {
NOTREACHED();
return NPERR_GENERIC_ERROR;
}
std::string post_file_contents;
if (file) {
// Post data to be uploaded from a file. This can be handled in two
// ways.
// 1. Read entire file and send the contents as if it was a post data
// specified in the argument
// 2. Send just the file details and read them in the browser at the
// time of sending the request.
// Approach 2 is more efficient but complicated. Approach 1 has a major
// drawback of sending potentially large data over two IPC hops. In a way
// 'large data over IPC' problem exists as it is in case of plugin giving
// the data directly instead of in a file.
// Currently we are going with the approach 1 to get the feature working.
// We can optimize this later with approach 2.
// TODO(joshia): Design a scheme to send a file descriptor instead of
// entire file contents across.
// Security alert:
// ---------------
// Here we are blindly uploading whatever file requested by a plugin.
// This is risky as someone could exploit a plugin to send private
// data in arbitrary locations.
// A malicious (non-sandboxed) plugin has unfeterred access to OS
// resources and can do this anyway without using browser's HTTP stack.
// FWIW, Firefox and Safari don't perform any security checks.
if (!buf)
return NPERR_FILE_NOT_FOUND;
std::string file_path_ascii(buf);
base::FilePath file_path;
static const char kFileUrlPrefix[] = "file:";
if (StartsWithASCII(file_path_ascii, kFileUrlPrefix, false)) {
GURL file_url(file_path_ascii);
DCHECK(file_url.SchemeIsFile());
net::FileURLToFilePath(file_url, &file_path);
} else {
file_path = base::FilePath::FromUTF8Unsafe(file_path_ascii);
}
base::PlatformFileInfo post_file_info;
if (!file_util::GetFileInfo(file_path, &post_file_info) ||
post_file_info.is_directory)
return NPERR_FILE_NOT_FOUND;
if (!base::ReadFileToString(file_path, &post_file_contents))
return NPERR_FILE_NOT_FOUND;
buf = post_file_contents.c_str();
len = post_file_contents.size();
}
// The post data sent by a plugin contains both headers
// and post data. Example:
// Content-type: text/html
// Content-length: 200
//
// <200 bytes of content here>
//
// Unfortunately, our stream needs these broken apart,
// so we need to parse the data and set headers and data
// separately.
plugin->RequestURL(url, "POST", target, buf, len, notify, notify_data);
return NPERR_NO_ERROR;
}
NPError NPN_PostURLNotify(NPP id,
const char* url,
const char* target,
uint32_t len,
const char* buf,
NPBool file,
void* notify_data) {
return PostURLNotify(id, url, target, len, buf, file, true, notify_data);
}
NPError NPN_PostURL(NPP id,
const char* url,
const char* target,
uint32_t len,
const char* buf,
NPBool file) {
// POSTs data to an URL, either from a temp file or a buffer.
// If file is true, buf contains a temp file (which host will delete after
// completing), and len contains the length of the filename.
// If file is false, buf contains the data to send, and len contains the
// length of the buffer
//
// If target is null,
// server response is returned to the plugin
// If target is _current, _self, or _top,
// server response is written to the plugin window and plugin is unloaded.
// If target is _new or _blank,
// server response is written to a new browser window
// If target is an existing frame,
// server response goes to that frame.
//
// For protocols other than FTP
// file uploads must be line-end converted from \r\n to \n
//
// Note: you cannot specify headers (even a blank line) in a memory buffer,
// use NPN_PostURLNotify
return PostURLNotify(id, url, target, len, buf, file, false, 0);
}
NPError NPN_NewStream(NPP id,
NPMIMEType type,
const char* target,
NPStream** stream) {
// Requests creation of a new data stream produced by the plugin,
// consumed by the browser.
//
// Browser should put this stream into a window target.
//
// TODO: implement me
DVLOG(1) << "NPN_NewStream is not implemented yet.";
return NPERR_GENERIC_ERROR;
}
int32_t NPN_Write(NPP id, NPStream* stream, int32_t len, void* buffer) {
// Writes data to an existing Plugin-created stream.
// TODO: implement me
DVLOG(1) << "NPN_Write is not implemented yet.";
return NPERR_GENERIC_ERROR;
}
NPError NPN_DestroyStream(NPP id, NPStream* stream, NPReason reason) {
// Destroys a stream (could be created by plugin or browser).
//
// Reasons:
// NPRES_DONE - normal completion
// NPRES_USER_BREAK - user terminated
// NPRES_NETWORK_ERROR - network error (all errors fit here?)
//
//
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (plugin.get() == NULL) {
NOTREACHED();
return NPERR_GENERIC_ERROR;
}
return plugin->NPP_DestroyStream(stream, reason);
}
const char* NPN_UserAgent(NPP id) {
#if defined(OS_WIN)
// Flash passes in a null id during the NP_initialize call. We need to
// default to the Mozilla user agent if we don't have an NPP instance or
// else Flash won't request windowless mode.
bool use_mozilla_user_agent = true;
if (id) {
scoped_refptr<PluginInstance> plugin = FindInstance(id);
if (plugin.get() && !plugin->use_mozilla_user_agent())
use_mozilla_user_agent = false;
}
if (use_mozilla_user_agent)
return "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) "
"Gecko/20061103 Firefox/2.0a1";
#endif
return webkit_glue::GetUserAgent(GURL()).c_str();
}
void NPN_Status(NPP id, const char* message) {
// Displays a message on the status line of the browser window.
// TODO: implement me
DVLOG(1) << "NPN_Status is not implemented yet.";
}
void NPN_InvalidateRect(NPP id, NPRect *invalidRect) {
// Invalidates specified drawing area prior to repainting or refreshing a
// windowless plugin
// Before a windowless plugin can refresh part of its drawing area, it must
// first invalidate it. This function causes the NPP_HandleEvent method to
// pass an update event or a paint message to the plug-in. After calling
// this method, the plug-in recieves a paint message asynchronously.
// The browser redraws invalid areas of the document and any windowless
// plug-ins at regularly timed intervals. To force a paint message, the
// plug-in can call NPN_ForceRedraw after calling this method.
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (plugin.get() && plugin->webplugin()) {
if (invalidRect) {
#if defined(OS_WIN)
if (!plugin->windowless()) {
RECT rect = {0};
rect.left = invalidRect->left;
rect.right = invalidRect->right;
rect.top = invalidRect->top;
rect.bottom = invalidRect->bottom;
::InvalidateRect(plugin->window_handle(), &rect, false);
return;
}
#endif
gfx::Rect rect(invalidRect->left,
invalidRect->top,
invalidRect->right - invalidRect->left,
invalidRect->bottom - invalidRect->top);
plugin->webplugin()->InvalidateRect(rect);
} else {
plugin->webplugin()->Invalidate();
}
}
}
void NPN_InvalidateRegion(NPP id, NPRegion invalidRegion) {
// Invalidates a specified drawing region prior to repainting
// or refreshing a window-less plugin.
//
// Similar to NPN_InvalidateRect.
// TODO: this is overkill--add platform-specific region handling (at the
// very least, fetch the region's bounding box and pass it to InvalidateRect).
scoped_refptr<PluginInstance> plugin(FindInstance(id));
DCHECK(plugin.get() != NULL);
if (plugin.get() && plugin->webplugin())
plugin->webplugin()->Invalidate();
}
void NPN_ForceRedraw(NPP id) {
// Forces repaint for a windowless plug-in.
//
// We deliberately do not implement this; we don't want plugins forcing
// synchronous paints.
}
NPError NPN_GetValue(NPP id, NPNVariable variable, void* value) {
// Allows the plugin to query the browser for information
//
// Variables:
// NPNVxDisplay (unix only)
// NPNVxtAppContext (unix only)
// NPNVnetscapeWindow (win only) - Gets the native window on which the
// plug-in drawing occurs, returns HWND
// NPNVjavascriptEnabledBool: tells whether Javascript is enabled
// NPNVasdEnabledBool: tells whether SmartUpdate is enabled
// NPNVOfflineBool: tells whether offline-mode is enabled
NPError rv = NPERR_GENERIC_ERROR;
switch (static_cast<int>(variable)) {
case NPNVWindowNPObject: {
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (!plugin.get()) {
NOTREACHED();
return NPERR_INVALID_INSTANCE_ERROR;
}
NPObject *np_object = plugin->webplugin()->GetWindowScriptNPObject();
// Return value is expected to be retained, as
// described here:
// <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
if (np_object) {
WebBindings::retainObject(np_object);
void **v = (void **)value;
*v = np_object;
rv = NPERR_NO_ERROR;
} else {
NOTREACHED();
}
break;
}
case NPNVPluginElementNPObject: {
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (!plugin.get()) {
NOTREACHED();
return NPERR_INVALID_INSTANCE_ERROR;
}
NPObject *np_object = plugin->webplugin()->GetPluginElement();
// Return value is expected to be retained, as
// described here:
// <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
if (np_object) {
WebBindings::retainObject(np_object);
void** v = static_cast<void**>(value);
*v = np_object;
rv = NPERR_NO_ERROR;
} else {
NOTREACHED();
}
break;
}
#if !defined(OS_MACOSX) // OS X doesn't have windowed plugins.
case NPNVnetscapeWindow: {
scoped_refptr<PluginInstance> plugin = FindInstance(id);
if (!plugin.get()) {
NOTREACHED();
return NPERR_INVALID_INSTANCE_ERROR;
}
gfx::PluginWindowHandle handle = plugin->window_handle();
*((void**)value) = (void*)handle;
rv = NPERR_NO_ERROR;
break;
}
#endif
case NPNVjavascriptEnabledBool: {
// yes, JS is enabled.
*((void**)value) = (void*)1;
rv = NPERR_NO_ERROR;
break;
}
#if defined(TOOLKIT_GTK)
case NPNVToolkit:
// Tell them we are GTK2. (The alternative is GTK 1.2.)
*reinterpret_cast<int*>(value) = NPNVGtk2;
rv = NPERR_NO_ERROR;
break;
case NPNVSupportsXEmbedBool:
*reinterpret_cast<NPBool*>(value) = true;
rv = NPERR_NO_ERROR;
break;
#endif
case NPNVSupportsWindowless: {
NPBool* supports_windowless = reinterpret_cast<NPBool*>(value);
*supports_windowless = true;
rv = NPERR_NO_ERROR;
break;
}
case NPNVprivateModeBool: {
NPBool* private_mode = reinterpret_cast<NPBool*>(value);
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (!plugin.get()) {
NOTREACHED();
return NPERR_INVALID_INSTANCE_ERROR;
}
*private_mode = plugin->webplugin()->IsOffTheRecord();
rv = NPERR_NO_ERROR;
break;
}
#if defined(OS_MACOSX)
case NPNVpluginDrawingModel: {
// return the drawing model that was negotiated when we initialized.
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (!plugin.get()) {
NOTREACHED();
return NPERR_INVALID_INSTANCE_ERROR;
}
*reinterpret_cast<int*>(value) = plugin->drawing_model();
rv = NPERR_NO_ERROR;
break;
}
case NPNVsupportsCoreGraphicsBool:
case NPNVsupportsCocoaBool: {
// These drawing and event models are always supported.
NPBool* supports_model = reinterpret_cast<NPBool*>(value);
*supports_model = true;
rv = NPERR_NO_ERROR;
break;
}
case NPNVsupportsInvalidatingCoreAnimationBool:
case NPNVsupportsCoreAnimationBool: {
NPBool* supports_model = reinterpret_cast<NPBool*>(value);
*supports_model = content::SupportsCoreAnimationPlugins();
rv = NPERR_NO_ERROR;
break;
}
#ifndef NP_NO_CARBON
case NPNVsupportsCarbonBool:
#endif
#ifndef NP_NO_QUICKDRAW
case NPNVsupportsQuickDrawBool:
#endif
case NPNVsupportsOpenGLBool: {
// These models are never supported. OpenGL was never widely supported,
// and QuickDraw and Carbon have been deprecated for quite some time.
NPBool* supports_model = reinterpret_cast<NPBool*>(value);
*supports_model = false;
rv = NPERR_NO_ERROR;
break;
}
case NPNVsupportsCompositingCoreAnimationPluginsBool: {
NPBool* supports_compositing = reinterpret_cast<NPBool*>(value);
*supports_compositing = content::SupportsCoreAnimationPlugins();
rv = NPERR_NO_ERROR;
break;
}
case NPNVsupportsUpdatedCocoaTextInputBool: {
// We support the clarifications to the Cocoa IME event spec.
NPBool* supports_update = reinterpret_cast<NPBool*>(value);
*supports_update = true;
rv = NPERR_NO_ERROR;
break;
}
#endif // OS_MACOSX
default:
DVLOG(1) << "NPN_GetValue(" << variable << ") is not implemented yet.";
break;
}
return rv;
}
NPError NPN_SetValue(NPP id, NPPVariable variable, void* value) {
// Allows the plugin to set various modes
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (!plugin.get()) {
NOTREACHED();
return NPERR_INVALID_INSTANCE_ERROR;
}
switch(variable) {
case NPPVpluginWindowBool: {
// Sets windowless mode for display of the plugin
// Note: the documentation at
// http://developer.mozilla.org/en/docs/NPN_SetValue is wrong. When
// value is NULL, the mode is set to true. This is the same way Mozilla
// works.
plugin->set_windowless(value == 0);
return NPERR_NO_ERROR;
}
case NPPVpluginTransparentBool: {
// Sets transparent mode for display of the plugin
//
// Transparent plugins require the browser to paint the background
// before having the plugin paint. By default, windowless plugins
// are transparent. Making a windowless plugin opaque means that
// the plugin does not require the browser to paint the background.
bool mode = (value != 0);
plugin->set_transparent(mode);
return NPERR_NO_ERROR;
}
case NPPVjavascriptPushCallerBool:
// Specifies whether you are pushing or popping the JSContext off.
// the stack
// TODO: implement me
DVLOG(1) << "NPN_SetValue(NPPVJavascriptPushCallerBool) is not "
"implemented.";
return NPERR_GENERIC_ERROR;
case NPPVpluginKeepLibraryInMemory:
// Tells browser that plugin library should live longer than usual.
// TODO: implement me
DVLOG(1) << "NPN_SetValue(NPPVpluginKeepLibraryInMemory) is not "
"implemented.";
return NPERR_GENERIC_ERROR;
#if defined(OS_MACOSX)
case NPPVpluginDrawingModel: {
intptr_t model = reinterpret_cast<intptr_t>(value);
if (model == NPDrawingModelCoreGraphics ||
((model == NPDrawingModelInvalidatingCoreAnimation ||
model == NPDrawingModelCoreAnimation) &&
content::SupportsCoreAnimationPlugins())) {
plugin->set_drawing_model(static_cast<NPDrawingModel>(model));
return NPERR_NO_ERROR;
}
return NPERR_GENERIC_ERROR;
}
case NPPVpluginEventModel: {
// Only the Cocoa event model is supported.
intptr_t model = reinterpret_cast<intptr_t>(value);
if (model == NPEventModelCocoa) {
plugin->set_event_model(static_cast<NPEventModel>(model));
return NPERR_NO_ERROR;
}
return NPERR_GENERIC_ERROR;
}
#endif
default:
// TODO: implement me
DVLOG(1) << "NPN_SetValue(" << variable << ") is not implemented.";
break;
}
NOTREACHED();
return NPERR_GENERIC_ERROR;
}
void* NPN_GetJavaEnv() {
// TODO: implement me
DVLOG(1) << "NPN_GetJavaEnv is not implemented.";
return NULL;
}
void* NPN_GetJavaPeer(NPP) {
// TODO: implement me
DVLOG(1) << "NPN_GetJavaPeer is not implemented.";
return NULL;
}
void NPN_PushPopupsEnabledState(NPP id, NPBool enabled) {
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (plugin.get())
plugin->PushPopupsEnabledState(enabled ? true : false);
}
void NPN_PopPopupsEnabledState(NPP id) {
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (plugin.get())
plugin->PopPopupsEnabledState();
}
void NPN_PluginThreadAsyncCall(NPP id,
void (*func)(void*),
void* user_data) {
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (plugin.get())
plugin->PluginThreadAsyncCall(func, user_data);
}
NPError NPN_GetValueForURL(NPP id,
NPNURLVariable variable,
const char* url,
char** value,
uint32_t* len) {
if (!id)
return NPERR_INVALID_PARAM;
if (!url || !*url || !len)
return NPERR_INVALID_URL;
*len = 0;
std::string result;
switch (variable) {
case NPNURLVProxy: {
result = "DIRECT";
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (!plugin.get())
return NPERR_GENERIC_ERROR;
WebPlugin* webplugin = plugin->webplugin();
if (!webplugin)
return NPERR_GENERIC_ERROR;
if (!webplugin->FindProxyForUrl(GURL(std::string(url)), &result))
return NPERR_GENERIC_ERROR;
break;
}
case NPNURLVCookie: {
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (!plugin.get())
return NPERR_GENERIC_ERROR;
WebPlugin* webplugin = plugin->webplugin();
if (!webplugin)
return NPERR_GENERIC_ERROR;
// Bypass third-party cookie blocking by using the url as the
// first_party_for_cookies.
GURL cookies_url((std::string(url)));
result = webplugin->GetCookies(cookies_url, cookies_url);
break;
}
default:
return NPERR_GENERIC_ERROR;
}
// Allocate this using the NPAPI allocator. The plugin will call
// NPN_Free to free this.
*value = static_cast<char*>(NPN_MemAlloc(result.length() + 1));
base::strlcpy(*value, result.c_str(), result.length() + 1);
*len = result.length();
return NPERR_NO_ERROR;
}
NPError NPN_SetValueForURL(NPP id,
NPNURLVariable variable,
const char* url,
const char* value,
uint32_t len) {
if (!id)
return NPERR_INVALID_PARAM;
if (!url || !*url)
return NPERR_INVALID_URL;
switch (variable) {
case NPNURLVCookie: {
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (!plugin.get())
return NPERR_GENERIC_ERROR;
WebPlugin* webplugin = plugin->webplugin();
if (!webplugin)
return NPERR_GENERIC_ERROR;
std::string cookie(value, len);
GURL cookies_url((std::string(url)));
webplugin->SetCookie(cookies_url, cookies_url, cookie);
return NPERR_NO_ERROR;
}
case NPNURLVProxy:
// We don't support setting proxy values, fall through...
break;
default:
// Fall through and return an error...
break;
}
return NPERR_GENERIC_ERROR;
}
NPError NPN_GetAuthenticationInfo(NPP id,
const char* protocol,
const char* host,
int32_t port,
const char* scheme,
const char* realm,
char** username,
uint32_t* ulen,
char** password,
uint32_t* plen) {
if (!id || !protocol || !host || !scheme || !realm || !username ||
!ulen || !password || !plen)
return NPERR_INVALID_PARAM;
// TODO: implement me (bug 23928)
return NPERR_GENERIC_ERROR;
}
uint32_t NPN_ScheduleTimer(NPP id,
uint32_t interval,
NPBool repeat,
void (*func)(NPP id, uint32_t timer_id)) {
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (!plugin.get())
return 0;
return plugin->ScheduleTimer(interval, repeat, func);
}
void NPN_UnscheduleTimer(NPP id, uint32_t timer_id) {
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (plugin.get())
plugin->UnscheduleTimer(timer_id);
}
NPError NPN_PopUpContextMenu(NPP id, NPMenu* menu) {
if (!menu)
return NPERR_INVALID_PARAM;
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (plugin.get()) {
return plugin->PopUpContextMenu(menu);
}
NOTREACHED();
return NPERR_GENERIC_ERROR;
}
NPBool NPN_ConvertPoint(NPP id, double sourceX, double sourceY,
NPCoordinateSpace sourceSpace,
double *destX, double *destY,
NPCoordinateSpace destSpace) {
scoped_refptr<PluginInstance> plugin(FindInstance(id));
if (plugin.get()) {
return plugin->ConvertPoint(
sourceX, sourceY, sourceSpace, destX, destY, destSpace);
}
NOTREACHED();
return false;
}
NPBool NPN_HandleEvent(NPP id, void *event, NPBool handled) {
// TODO: Implement advanced key handling: http://crbug.com/46578
NOTIMPLEMENTED();
return false;
}
NPBool NPN_UnfocusInstance(NPP id, NPFocusDirection direction) {
// TODO: Implement advanced key handling: http://crbug.com/46578
NOTIMPLEMENTED();
return false;
}
void NPN_URLRedirectResponse(NPP instance, void* notify_data, NPBool allow) {
scoped_refptr<PluginInstance> plugin(FindInstance(instance));
if (plugin.get()) {
plugin->URLRedirectResponse(!!allow, notify_data);
}
}
} // extern "C"