// Copyright (c) 2010 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 "webkit/glue/plugins/plugin_host.h" #include "base/file_util.h" #include "base/logging.h" #include "base/scoped_ptr.h" #include "base/string_piece.h" #include "base/string_util.h" #if defined(OS_MACOSX) #include "base/sys_info.h" #endif #include "base/sys_string_conversions.h" #include "net/base/net_util.h" #include "third_party/WebKit/WebKit/chromium/public/WebBindings.h" #include "webkit/default_plugin/default_plugin_shared.h" #include "webkit/glue/webkit_glue.h" #include "webkit/glue/plugins/npapi_extension_thunk.h" #include "webkit/glue/plugins/plugin_instance.h" #include "webkit/glue/plugins/plugin_lib.h" #include "webkit/glue/plugins/plugin_list.h" #include "webkit/glue/plugins/plugin_stream_url.h" #include "webkit/glue/plugins/webplugin_delegate.h" #include "webkit/glue/plugins/webplugininfo.h" #include "third_party/npapi/bindings/npapi_extensions.h" #include "third_party/npapi/bindings/npruntime.h" using WebKit::WebBindings; // Finds a PluginInstance from an NPP. // The caller must take a reference if needed. static NPAPI::PluginInstance* FindInstance(NPP id) { if (id == NULL) { NOTREACHED(); return NULL; } return reinterpret_cast(id->ndata); } #if defined(OS_MACOSX) // Returns true if the OS supports shared accelerated surfaces via IOSurface. // This is true on Snow Leopard and higher. static bool SupportsSharingAcceleratedSurfaces() { int32 major, minor, bugfix; base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix); return major > 10 || (major == 10 && minor > 5); } #endif namespace NPAPI { scoped_refptr PluginHost::singleton_; PluginHost::PluginHost() { InitializeHostFuncs(); } PluginHost::~PluginHost() { } PluginHost *PluginHost::Singleton() { if (singleton_.get() == NULL) { singleton_ = new PluginHost(); } DCHECK(singleton_.get() != NULL); return singleton_; } 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; // We don't implement java yet 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; } 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* names, std::vector* values, std::vector* 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(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(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 NPAPI extern "C" { // Allocates memory from the host's memory space. void* NPN_MemAlloc(uint32 size) { scoped_refptr host = NPAPI::PluginHost::Singleton(); if (host != NULL) { // 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); } return NULL; } // Deallocates memory from the host's memory space void NPN_MemFree(void* ptr) { scoped_refptr host = NPAPI::PluginHost::Singleton(); if (host != NULL) { if (ptr != NULL && ptr != reinterpret_cast(-1)) free(ptr); } } // Requests that the host free a specified amount of memory. uint32 NPN_MemFlush(uint32 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 reloadPages) { // TODO: implement me DLOG(INFO) << "NPN_ReloadPlugin is not implemented yet."; } // 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 plugin = reinterpret_cast(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 plugin = FindInstance(id); if (!plugin.get()) { NOTREACHED(); 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 len, const char* buf, NPBool file, bool notify, void* notify_data) { if (!url) return NPERR_INVALID_URL; scoped_refptr 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); std::wstring file_path; static const char kFileUrlPrefix[] = "file:"; if (StartsWithASCII(file_path_ascii, kFileUrlPrefix, false)) { GURL file_url(file_path_ascii); DCHECK(file_url.SchemeIsFile()); FilePath path; net::FileURLToFilePath(file_url, &path); file_path = path.ToWStringHack(); } else { file_path = base::SysNativeMBToWide(file_path_ascii); } file_util::FileInfo post_file_info = {0}; if (!file_util::GetFileInfo(file_path.c_str(), &post_file_info) || post_file_info.is_directory) return NPERR_FILE_NOT_FOUND; if (!file_util::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 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 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 DLOG(INFO) << "NPN_NewStream is not implemented yet."; return NPERR_GENERIC_ERROR; } int32 NPN_Write(NPP id, NPStream* stream, int32 len, void* buffer) { // Writes data to an existing Plugin-created stream. // TODO: implement me DLOG(INFO) << "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 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. if (id) { scoped_refptr plugin = FindInstance(id); if (plugin.get() && !plugin->use_mozilla_user_agent()) return webkit_glue::GetUserAgent(GURL()).c_str(); } return "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) Gecko/20061103 Firefox/2.0a1"; #else // TODO(port): For now we always use our real useragent on Mac and Linux. // We might eventually need to spoof for some plugins. return webkit_glue::GetUserAgent(GURL()).c_str(); #endif } void NPN_Status(NPP id, const char* message) { // Displays a message on the status line of the browser window. // TODO: implement me DLOG(INFO) << "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 plugin = FindInstance(id); DCHECK(plugin.get() != NULL); 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 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 (variable) { case NPNVWindowNPObject: { scoped_refptr plugin = FindInstance(id); NPObject *np_object = plugin->webplugin()->GetWindowScriptNPObject(); // Return value is expected to be retained, as // described here: // 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 plugin = FindInstance(id); NPObject *np_object = plugin->webplugin()->GetPluginElement(); // Return value is expected to be retained, as // described here: // if (np_object) { WebBindings::retainObject(np_object); void** v = static_cast(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 plugin = FindInstance(id); 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_USES_GTK) case NPNVToolkit: // Tell them we are GTK2. (The alternative is GTK 1.2.) *reinterpret_cast(value) = NPNVGtk2; rv = NPERR_NO_ERROR; break; case NPNVSupportsXEmbedBool: *reinterpret_cast(value) = TRUE; rv = NPERR_NO_ERROR; break; #endif case NPNVSupportsWindowless: { NPBool* supports_windowless = reinterpret_cast(value); *supports_windowless = TRUE; rv = NPERR_NO_ERROR; break; } case NPNVprivateModeBool: { NPBool* private_mode = reinterpret_cast(value); scoped_refptr plugin = FindInstance(id); *private_mode = plugin->webplugin()->IsOffTheRecord(); rv = NPERR_NO_ERROR; break; } case default_plugin::kMissingPluginStatusStart + default_plugin::MISSING_PLUGIN_AVAILABLE: // fall through case default_plugin::kMissingPluginStatusStart + default_plugin::MISSING_PLUGIN_USER_STARTED_DOWNLOAD: { // This is a hack for the default plugin to send notification to // renderer. Even though we check if the plugin is the default plugin, // we still need to worry about future standard change that may conflict // with the variable definition, in order to avoid duplicate case clauses // in this big switch statement. scoped_refptr plugin = FindInstance(id); if (plugin->plugin_lib()->plugin_info().path.value() == kDefaultPluginLibraryName) { plugin->webplugin()->OnMissingPluginStatus( variable - default_plugin::kMissingPluginStatusStart); } break; } #if defined(OS_MACOSX) case NPNVpluginDrawingModel: { // return the drawing model that was negotiated when we initialized. scoped_refptr plugin = FindInstance(id); *reinterpret_cast(value) = plugin->drawing_model(); rv = NPERR_NO_ERROR; break; } #ifndef NP_NO_QUICKDRAW case NPNVsupportsQuickDrawBool: { // We do not admit to supporting the QuickDraw drawing model. The logic // here is that our QuickDraw plugin support is so rudimentary that we // only want to use it as a fallback to keep plugins from crashing: if a // plugin knows enough to ask, we want them to use CoreGraphics. NPBool* supports_qd = reinterpret_cast(value); *supports_qd = FALSE; rv = NPERR_NO_ERROR; break; } #endif case NPNVsupportsCoreGraphicsBool: #ifndef NP_NO_CARBON case NPNVsupportsCarbonBool: #endif case NPNVsupportsCocoaBool: { // we do support these drawing and event models. NPBool* supports_model = reinterpret_cast(value); *supports_model = TRUE; rv = NPERR_NO_ERROR; break; } case NPNVsupportsCoreAnimationBool: { // We only support the Core Animation model on 10.6 and higher NPBool* supports_model = reinterpret_cast(value); *supports_model = SupportsSharingAcceleratedSurfaces() ? TRUE : FALSE; rv = NPERR_NO_ERROR; break; } case NPNVsupportsOpenGLBool: { // This drawing model was never widely supported, and we don't plan to // support it. NPBool* supports_model = reinterpret_cast(value); *supports_model = FALSE; rv = NPERR_NO_ERROR; break; } #endif // OS_MACOSX case NPNVPepperExtensions: // Available for any plugin that attempts to get it. // If the plugin is not started in a Pepper implementation, it // will likely fail when it tries to use any of the functions // attached to the extension vector. rv = NPAPI::GetPepperExtensionsFunctions(value); break; default: DLOG(INFO) << "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 plugin = FindInstance(id); 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 DLOG(INFO) << "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 DLOG(INFO) << "NPN_SetValue(NPPVpluginKeepLibraryInMemory) is not implemented."; return NPERR_GENERIC_ERROR; #if defined(OS_MACOSX) case NPPVpluginDrawingModel: { int model = reinterpret_cast(value); if (model == NPDrawingModelCoreGraphics) { plugin->set_drawing_model(static_cast(model)); return NPERR_NO_ERROR; } else if (model == NPDrawingModelCoreAnimation && SupportsSharingAcceleratedSurfaces()) { plugin->set_drawing_model(static_cast(model)); return NPERR_NO_ERROR; } return NPERR_GENERIC_ERROR; } case NPPVpluginEventModel: { // we support Carbon and Cocoa event models int model = reinterpret_cast(value); switch (model) { #ifndef NP_NO_CARBON case NPEventModelCarbon: #endif case NPEventModelCocoa: plugin->set_event_model(static_cast(model)); return NPERR_NO_ERROR; break; } return NPERR_GENERIC_ERROR; } #endif default: // TODO: implement me DLOG(INFO) << "NPN_SetValue(" << variable << ") is not implemented."; break; } NOTREACHED(); return NPERR_GENERIC_ERROR; } void* NPN_GetJavaEnv() { // TODO: implement me DLOG(INFO) << "NPN_GetJavaEnv is not implemented."; return NULL; } void* NPN_GetJavaPeer(NPP) { // TODO: implement me DLOG(INFO) << "NPN_GetJavaPeer is not implemented."; return NULL; } void NPN_PushPopupsEnabledState(NPP id, NPBool enabled) { scoped_refptr plugin = FindInstance(id); if (plugin) plugin->PushPopupsEnabledState(enabled); } void NPN_PopPopupsEnabledState(NPP id) { scoped_refptr plugin = FindInstance(id); if (plugin) plugin->PopPopupsEnabledState(); } void NPN_PluginThreadAsyncCall(NPP id, void (*func)(void*), void* user_data) { scoped_refptr plugin = FindInstance(id); if (plugin) plugin->PluginThreadAsyncCall(func, user_data); } NPError NPN_GetValueForURL(NPP id, NPNURLVariable variable, const char* url, char** value, uint32* 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"; if (!webkit_glue::FindProxyForUrl(GURL((std::string(url))), &result)) return NPERR_GENERIC_ERROR; break; } case NPNURLVCookie: { scoped_refptr plugin = FindInstance(id); if (!plugin) return NPERR_GENERIC_ERROR; webkit_glue::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(NPN_MemAlloc(result.length() + 1)); strncpy(*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 len) { if (!id) return NPERR_INVALID_PARAM; if (!url || !*url) return NPERR_INVALID_URL; switch (variable) { case NPNURLVCookie: { scoped_refptr plugin = FindInstance(id); if (!plugin) return NPERR_GENERIC_ERROR; webkit_glue::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* ulen, char** password, uint32* 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 NPN_ScheduleTimer(NPP id, uint32 interval, NPBool repeat, void (*func)(NPP id, uint32 timer_id)) { scoped_refptr plugin = FindInstance(id); if (!plugin) return 0; return plugin->ScheduleTimer(interval, repeat, func); } void NPN_UnscheduleTimer(NPP id, uint32 timer_id) { scoped_refptr plugin = FindInstance(id); if (plugin) plugin->UnscheduleTimer(timer_id); } NPError NPN_PopUpContextMenu(NPP id, NPMenu* menu) { if (!menu) return NPERR_INVALID_PARAM; scoped_refptr 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 plugin = FindInstance(id); if (plugin.get()) { return plugin->ConvertPoint(sourceX, sourceY, sourceSpace, destX, destY, destSpace); } NOTREACHED(); return FALSE; } } // extern "C"