fune/dom/plugins/ipc/PluginModuleChild.cpp
Haik Aftandilian 331dda9367 Bug 1444291 - Part 3 - Add read access to the Mac Flash sandbox, support sandbox levels r=Alex_Gaynor
Replace the boolean pref "security.sandbox.mac.flash.enabled"
with "dom.ipc.plugins.sandbox-level.flash" to support sandbox
levels and be consistent with the Windows pref name.

Adds filesystem read access to the sandbox using sandbox extensions
granted by the file dialog machinery (level 1).

Add support for level 2 which blocks read access.

Allow the sandbox to be disabled with MOZ_DISABLE_NPAPI_SANDBOX.


MozReview-Commit-ID: 4rfobEoxQpF

--HG--
extra : rebase_source : 05dc54b46063967e959bc3fced21c61e5463de48
2018-03-15 20:23:19 -07:00

2239 lines
61 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set sw=4 ts=4 et : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/plugins/PluginModuleChild.h"
/* This must occur *after* plugins/PluginModuleChild.h to avoid typedefs conflicts. */
#include "mozilla/ArrayUtils.h"
#include "mozilla/ipc/MessageChannel.h"
#ifdef MOZ_WIDGET_GTK
#include <gtk/gtk.h>
#endif
#include "nsIFile.h"
#include "pratom.h"
#include "nsDebug.h"
#include "nsCOMPtr.h"
#include "nsPluginsDir.h"
#include "nsXULAppAPI.h"
#ifdef MOZ_X11
# include "nsX11ErrorHandler.h"
# include "mozilla/X11Util.h"
#endif
#include "mozilla/ipc/CrashReporterClient.h"
#include "mozilla/ipc/ProcessChild.h"
#include "mozilla/plugins/PluginInstanceChild.h"
#include "mozilla/plugins/StreamNotifyChild.h"
#include "mozilla/plugins/BrowserStreamChild.h"
#include "mozilla/Sprintf.h"
#include "mozilla/Unused.h"
#include "nsNPAPIPlugin.h"
#include "FunctionHook.h"
#include "FunctionBrokerChild.h"
#ifdef XP_WIN
#include "mozilla/widget/AudioSession.h"
#include <knownfolders.h>
#endif
#ifdef MOZ_WIDGET_COCOA
#include "PluginInterposeOSX.h"
#include "PluginUtilsOSX.h"
#endif
#ifdef MOZ_GECKO_PROFILER
#include "ChildProfilerController.h"
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
#include "mozilla/Sandbox.h"
#endif
using namespace mozilla;
using namespace mozilla::ipc;
using namespace mozilla::plugins;
using namespace mozilla::widget;
#if defined(XP_WIN)
const wchar_t * kFlashFullscreenClass = L"ShockwaveFlashFullScreen";
#endif
namespace {
// see PluginModuleChild::GetChrome()
PluginModuleChild* gChromeInstance = nullptr;
} // namespace
#ifdef XP_WIN
// Used with fix for flash fullscreen window loosing focus.
static bool gDelayFlashFocusReplyUntilEval = false;
#endif
/* static */
bool
PluginModuleChild::CreateForContentProcess(Endpoint<PPluginModuleChild>&& aEndpoint)
{
auto* child = new PluginModuleChild(false);
return child->InitForContent(Move(aEndpoint));
}
PluginModuleChild::PluginModuleChild(bool aIsChrome)
: mLibrary(0)
, mPluginFilename("")
, mQuirks(QUIRKS_NOT_INITIALIZED)
, mIsChrome(aIsChrome)
, mHasShutdown(false)
, mShutdownFunc(0)
, mInitializeFunc(0)
#if defined(OS_WIN) || defined(OS_MACOSX)
, mGetEntryPointsFunc(0)
#elif defined(MOZ_WIDGET_GTK)
, mNestedLoopTimerId(0)
#endif
#ifdef OS_WIN
, mNestedEventHook(nullptr)
, mGlobalCallWndProcHook(nullptr)
, mAsyncRenderSupport(false)
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
, mFlashSandboxLevel(0)
, mEnableFlashSandboxLogging(false)
#endif
{
memset(&mFunctions, 0, sizeof(mFunctions));
if (mIsChrome) {
MOZ_ASSERT(!gChromeInstance);
gChromeInstance = this;
}
#ifdef XP_MACOSX
if (aIsChrome) {
mac_plugin_interposing::child::SetUpCocoaInterposing();
}
#endif
}
PluginModuleChild::~PluginModuleChild()
{
if (mIsChrome) {
MOZ_ASSERT(gChromeInstance == this);
// We don't unload the plugin library in case it uses atexit handlers or
// other similar hooks.
DeinitGraphics();
PluginScriptableObjectChild::ClearIdentifiers();
gChromeInstance = nullptr;
}
}
// static
PluginModuleChild*
PluginModuleChild::GetChrome()
{
// A special PluginModuleChild instance that talks to the chrome process
// during startup and shutdown. Synchronous messages to or from this actor
// should be avoided because they may lead to hangs.
MOZ_ASSERT(gChromeInstance);
return gChromeInstance;
}
void
PluginModuleChild::CommonInit()
{
PLUGIN_LOG_DEBUG_METHOD;
// Request Windows message deferral behavior on our channel. This
// applies to the top level and all sub plugin protocols since they
// all share the same channel.
// Bug 1090573 - Don't do this for connections to content processes.
GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
memset((void*) &mFunctions, 0, sizeof(mFunctions));
mFunctions.size = sizeof(mFunctions);
mFunctions.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
}
bool
PluginModuleChild::InitForContent(Endpoint<PPluginModuleChild>&& aEndpoint)
{
CommonInit();
if (!aEndpoint.Bind(this)) {
return false;
}
mLibrary = GetChrome()->mLibrary;
mFunctions = GetChrome()->mFunctions;
return true;
}
mozilla::ipc::IPCResult
PluginModuleChild::RecvInitProfiler(Endpoint<mozilla::PProfilerChild>&& aEndpoint)
{
#ifdef MOZ_GECKO_PROFILER
mProfilerController = ChildProfilerController::Create(Move(aEndpoint));
#endif
return IPC_OK();
}
mozilla::ipc::IPCResult
PluginModuleChild::RecvDisableFlashProtectedMode()
{
MOZ_ASSERT(mIsChrome);
#ifdef XP_WIN
FunctionHook::HookProtectedMode();
#else
MOZ_ASSERT(false, "Should not be called");
#endif
return IPC_OK();
}
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
void
PluginModuleChild::EnableFlashSandbox(int aLevel, bool aShouldEnableLogging)
{
mFlashSandboxLevel = aLevel;
mEnableFlashSandboxLogging = aShouldEnableLogging;
}
#endif
bool
PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
base::ProcessId aParentPid,
MessageLoop* aIOLoop,
IPC::Channel* aChannel)
{
NS_ASSERTION(aChannel, "need a channel");
if (!InitGraphics())
return false;
mPluginFilename = aPluginFilename.c_str();
nsCOMPtr<nsIFile> localFile;
NS_NewLocalFile(NS_ConvertUTF8toUTF16(mPluginFilename),
true,
getter_AddRefs(localFile));
if (!localFile)
return false;
bool exists;
localFile->Exists(&exists);
NS_ASSERTION(exists, "plugin file ain't there");
nsPluginFile pluginFile(localFile);
nsPluginInfo info = nsPluginInfo();
if (NS_FAILED(pluginFile.GetPluginInfo(info, &mLibrary))) {
return false;
}
#if defined(XP_WIN)
// XXX quirks isn't initialized yet
mAsyncRenderSupport = info.fSupportsAsyncRender;
#endif
#if defined(MOZ_X11)
NS_NAMED_LITERAL_CSTRING(flash10Head, "Shockwave Flash 10.");
if (StringBeginsWith(nsDependentCString(info.fDescription), flash10Head)) {
AddQuirk(QUIRK_FLASH_EXPOSE_COORD_TRANSLATION);
}
#endif
#if defined(XP_MACOSX)
const char* namePrefix = "Plugin Content";
char nameBuffer[80];
SprintfLiteral(nameBuffer, "%s (%s)", namePrefix, info.fName);
mozilla::plugins::PluginUtilsOSX::SetProcessName(nameBuffer);
#endif
pluginFile.FreePluginInfo(info);
#if defined(MOZ_X11) || defined(XP_MACOSX)
if (!mLibrary)
#endif
{
nsresult rv = pluginFile.LoadPlugin(&mLibrary);
if (NS_FAILED(rv))
return false;
}
NS_ASSERTION(mLibrary, "couldn't open shared object");
CommonInit();
if (!Open(aChannel, aParentPid, aIOLoop)) {
return false;
}
GetIPCChannel()->SetAbortOnError(true);
#if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
mShutdownFunc =
(NP_PLUGINSHUTDOWN) PR_FindFunctionSymbol(mLibrary, "NP_Shutdown");
// create the new plugin handler
mInitializeFunc =
(NP_PLUGINUNIXINIT) PR_FindFunctionSymbol(mLibrary, "NP_Initialize");
NS_ASSERTION(mInitializeFunc, "couldn't find NP_Initialize()");
#elif defined(OS_WIN) || defined(OS_MACOSX)
mShutdownFunc =
(NP_PLUGINSHUTDOWN)PR_FindFunctionSymbol(mLibrary, "NP_Shutdown");
mGetEntryPointsFunc =
(NP_GETENTRYPOINTS)PR_FindSymbol(mLibrary, "NP_GetEntryPoints");
NS_ENSURE_TRUE(mGetEntryPointsFunc, false);
mInitializeFunc =
(NP_PLUGININIT)PR_FindFunctionSymbol(mLibrary, "NP_Initialize");
NS_ENSURE_TRUE(mInitializeFunc, false);
#else
# error Please copy the initialization code from nsNPAPIPlugin.cpp
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
if (mFlashSandboxLevel > 0) {
MacSandboxInfo flashSandboxInfo;
flashSandboxInfo.type = MacSandboxType_Plugin;
flashSandboxInfo.pluginInfo.type = MacSandboxPluginType_Flash;
flashSandboxInfo.pluginInfo.pluginBinaryPath = aPluginFilename;
flashSandboxInfo.level = mFlashSandboxLevel;
flashSandboxInfo.shouldLog = mEnableFlashSandboxLogging;
std::string sbError;
if (!mozilla::StartMacSandbox(flashSandboxInfo, sbError)) {
fprintf(stderr, "Failed to start sandbox:\n%s\n", sbError.c_str());
return false;
}
}
#endif
return true;
}
#if defined(MOZ_WIDGET_GTK)
typedef void (*GObjectDisposeFn)(GObject*);
typedef gboolean (*GtkWidgetScrollEventFn)(GtkWidget*, GdkEventScroll*);
typedef void (*GtkPlugEmbeddedFn)(GtkPlug*);
static GObjectDisposeFn real_gtk_plug_dispose;
static GtkPlugEmbeddedFn real_gtk_plug_embedded;
static void
undo_bogus_unref(gpointer data, GObject* object, gboolean is_last_ref) {
if (!is_last_ref) // recursion in g_object_ref
return;
g_object_ref(object);
}
static void
wrap_gtk_plug_dispose(GObject* object) {
// Work around Flash Player bug described in bug 538914.
//
// This function is called during gtk_widget_destroy and/or before
// the object's last reference is removed. A reference to the
// object is held during the call so the ref count should not drop
// to zero. However, Flash Player tries to destroy the GtkPlug
// using g_object_unref instead of gtk_widget_destroy. The
// reference that Flash is removing actually belongs to the
// GtkPlug. During real_gtk_plug_dispose, the GtkPlug removes its
// reference.
//
// A toggle ref is added to prevent premature deletion of the object
// caused by Flash Player's extra unref, and to detect when there are
// unexpectedly no other references.
g_object_add_toggle_ref(object, undo_bogus_unref, nullptr);
(*real_gtk_plug_dispose)(object);
g_object_remove_toggle_ref(object, undo_bogus_unref, nullptr);
}
static gboolean
gtk_plug_scroll_event(GtkWidget *widget, GdkEventScroll *gdk_event)
{
if (!gtk_widget_is_toplevel(widget)) // in same process as its GtkSocket
return FALSE; // event not handled; propagate to GtkSocket
GdkWindow* socket_window = gtk_plug_get_socket_window(GTK_PLUG(widget));
if (!socket_window)
return FALSE;
// Propagate the event to the embedder.
GdkScreen* screen = gdk_window_get_screen(socket_window);
GdkWindow* plug_window = gtk_widget_get_window(widget);
GdkWindow* event_window = gdk_event->window;
gint x = gdk_event->x;
gint y = gdk_event->y;
unsigned int button;
unsigned int button_mask = 0;
XEvent xevent;
Display* dpy = GDK_WINDOW_XDISPLAY(socket_window);
/* Translate the event coordinates to the plug window,
* which should be aligned with the socket window.
*/
while (event_window != plug_window)
{
gint dx, dy;
gdk_window_get_position(event_window, &dx, &dy);
x += dx;
y += dy;
event_window = gdk_window_get_parent(event_window);
if (!event_window)
return FALSE;
}
switch (gdk_event->direction) {
case GDK_SCROLL_UP:
button = 4;
button_mask = Button4Mask;
break;
case GDK_SCROLL_DOWN:
button = 5;
button_mask = Button5Mask;
break;
case GDK_SCROLL_LEFT:
button = 6;
break;
case GDK_SCROLL_RIGHT:
button = 7;
break;
default:
return FALSE; // unknown GdkScrollDirection
}
memset(&xevent, 0, sizeof(xevent));
xevent.xbutton.type = ButtonPress;
xevent.xbutton.window = gdk_x11_window_get_xid(socket_window);
xevent.xbutton.root = gdk_x11_window_get_xid(gdk_screen_get_root_window(screen));
xevent.xbutton.subwindow = gdk_x11_window_get_xid(plug_window);
xevent.xbutton.time = gdk_event->time;
xevent.xbutton.x = x;
xevent.xbutton.y = y;
xevent.xbutton.x_root = gdk_event->x_root;
xevent.xbutton.y_root = gdk_event->y_root;
xevent.xbutton.state = gdk_event->state;
xevent.xbutton.button = button;
xevent.xbutton.same_screen = True;
gdk_error_trap_push();
XSendEvent(dpy, xevent.xbutton.window,
True, ButtonPressMask, &xevent);
xevent.xbutton.type = ButtonRelease;
xevent.xbutton.state |= button_mask;
XSendEvent(dpy, xevent.xbutton.window,
True, ButtonReleaseMask, &xevent);
gdk_display_sync(gdk_screen_get_display(screen));
gdk_error_trap_pop();
return TRUE; // event handled
}
static void
wrap_gtk_plug_embedded(GtkPlug* plug) {
GdkWindow* socket_window = gtk_plug_get_socket_window(plug);
if (socket_window) {
if (gtk_check_version(2,18,7) != nullptr // older
&& g_object_get_data(G_OBJECT(socket_window),
"moz-existed-before-set-window")) {
// Add missing reference for
// https://bugzilla.gnome.org/show_bug.cgi?id=607061
g_object_ref(socket_window);
}
// Ensure the window exists to make this GtkPlug behave like an
// in-process GtkPlug for Flash Player. (Bugs 561308 and 539138).
gtk_widget_realize(GTK_WIDGET(plug));
}
if (*real_gtk_plug_embedded) {
(*real_gtk_plug_embedded)(plug);
}
}
//
// The next four constants are knobs that can be tuned. They trade
// off potential UI lag from delayed event processing with CPU time.
//
static const gint kNestedLoopDetectorPriority = G_PRIORITY_HIGH_IDLE;
// 90ms so that we can hopefully break livelocks before the user
// notices UI lag (100ms)
static const guint kNestedLoopDetectorIntervalMs = 90;
static const gint kBrowserEventPriority = G_PRIORITY_HIGH_IDLE;
static const guint kBrowserEventIntervalMs = 10;
// static
gboolean
PluginModuleChild::DetectNestedEventLoop(gpointer data)
{
PluginModuleChild* pmc = static_cast<PluginModuleChild*>(data);
MOZ_ASSERT(0 != pmc->mNestedLoopTimerId,
"callback after descheduling");
MOZ_ASSERT(pmc->mTopLoopDepth < g_main_depth(),
"not canceled before returning to main event loop!");
PLUGIN_LOG_DEBUG(("Detected nested glib event loop"));
// just detected a nested loop; start a timer that will
// periodically rpc-call back into the browser and process some
// events
pmc->mNestedLoopTimerId =
g_timeout_add_full(kBrowserEventPriority,
kBrowserEventIntervalMs,
PluginModuleChild::ProcessBrowserEvents,
data,
nullptr);
// cancel the nested-loop detection timer
return FALSE;
}
// static
gboolean
PluginModuleChild::ProcessBrowserEvents(gpointer data)
{
PluginModuleChild* pmc = static_cast<PluginModuleChild*>(data);
MOZ_ASSERT(pmc->mTopLoopDepth < g_main_depth(),
"not canceled before returning to main event loop!");
pmc->CallProcessSomeEvents();
return TRUE;
}
void
PluginModuleChild::EnteredCxxStack()
{
MOZ_ASSERT(0 == mNestedLoopTimerId,
"previous timer not descheduled");
mNestedLoopTimerId =
g_timeout_add_full(kNestedLoopDetectorPriority,
kNestedLoopDetectorIntervalMs,
PluginModuleChild::DetectNestedEventLoop,
this,
nullptr);
#ifdef DEBUG
mTopLoopDepth = g_main_depth();
#endif
}
void
PluginModuleChild::ExitedCxxStack()
{
MOZ_ASSERT(0 < mNestedLoopTimerId,
"nested loop timeout not scheduled");
g_source_remove(mNestedLoopTimerId);
mNestedLoopTimerId = 0;
}
#endif
mozilla::ipc::IPCResult
PluginModuleChild::RecvSetParentHangTimeout(const uint32_t& aSeconds)
{
#ifdef XP_WIN
SetReplyTimeoutMs(((aSeconds > 0) ? (1000 * aSeconds) : 0));
#endif
return IPC_OK();
}
bool
PluginModuleChild::ShouldContinueFromReplyTimeout()
{
#ifdef XP_WIN
MOZ_CRASH("terminating child process");
#endif
return true;
}
bool
PluginModuleChild::InitGraphics()
{
#if defined(MOZ_WIDGET_GTK)
// Work around plugins that don't interact well with GDK
// client-side windows.
PR_SetEnv("GDK_NATIVE_WINDOWS=1");
gtk_init(0, 0);
// GtkPlug is a static class so will leak anyway but this ref makes sure.
gpointer gtk_plug_class = g_type_class_ref(GTK_TYPE_PLUG);
// The dispose method is a good place to hook into the destruction process
// because the reference count should be 1 the last time dispose is
// called. (Toggle references wouldn't detect if the reference count
// might be higher.)
GObjectDisposeFn* dispose = &G_OBJECT_CLASS(gtk_plug_class)->dispose;
MOZ_ASSERT(*dispose != wrap_gtk_plug_dispose,
"InitGraphics called twice");
real_gtk_plug_dispose = *dispose;
*dispose = wrap_gtk_plug_dispose;
// If we ever stop setting GDK_NATIVE_WINDOWS, we'll also need to
// gtk_widget_add_events GDK_SCROLL_MASK or GDK client-side windows will
// not tell us about the scroll events that it intercepts. With native
// windows, this is called when GDK intercepts the events; if GDK doesn't
// intercept the events, then the X server will instead send them directly
// to an ancestor (embedder) window.
GtkWidgetScrollEventFn* scroll_event =
&GTK_WIDGET_CLASS(gtk_plug_class)->scroll_event;
if (!*scroll_event) {
*scroll_event = gtk_plug_scroll_event;
}
GtkPlugEmbeddedFn* embedded = &GTK_PLUG_CLASS(gtk_plug_class)->embedded;
real_gtk_plug_embedded = *embedded;
*embedded = wrap_gtk_plug_embedded;
#else
// may not be necessary on all platforms
#endif
#ifdef MOZ_X11
// Do this after initializing GDK, or GDK will install its own handler.
InstallX11ErrorHandler();
#endif
return true;
}
void
PluginModuleChild::DeinitGraphics()
{
#if defined(MOZ_X11) && defined(NS_FREE_PERMANENT_DATA)
// We free some data off of XDisplay close hooks, ensure they're
// run. Closing the display is pretty scary, so we only do it to
// silence leak checkers.
XCloseDisplay(DefaultXDisplay());
#endif
}
NPError
PluginModuleChild::NP_Shutdown()
{
AssertPluginThread();
MOZ_ASSERT(mIsChrome);
if (mHasShutdown) {
return NPERR_NO_ERROR;
}
#if defined XP_WIN
mozilla::widget::StopAudioSession();
#endif
// the PluginModuleParent shuts down this process after this interrupt
// call pops off its stack
NPError rv = mShutdownFunc ? mShutdownFunc() : NPERR_NO_ERROR;
// weakly guard against re-entry after NP_Shutdown
memset(&mFunctions, 0, sizeof(mFunctions));
#ifdef OS_WIN
ResetEventHooks();
#endif
GetIPCChannel()->SetAbortOnError(false);
mHasShutdown = true;
return rv;
}
mozilla::ipc::IPCResult
PluginModuleChild::AnswerNP_Shutdown(NPError *rv)
{
*rv = NP_Shutdown();
return IPC_OK();
}
mozilla::ipc::IPCResult
PluginModuleChild::AnswerOptionalFunctionsSupported(bool *aURLRedirectNotify,
bool *aClearSiteData,
bool *aGetSitesWithData)
{
*aURLRedirectNotify = !!mFunctions.urlredirectnotify;
*aClearSiteData = !!mFunctions.clearsitedata;
*aGetSitesWithData = !!mFunctions.getsiteswithdata;
return IPC_OK();
}
mozilla::ipc::IPCResult
PluginModuleChild::RecvNPP_ClearSiteData(const nsCString& aSite,
const uint64_t& aFlags,
const uint64_t& aMaxAge,
const uint64_t& aCallbackId)
{
NPError result =
mFunctions.clearsitedata(NullableStringGet(aSite), aFlags, aMaxAge);
SendReturnClearSiteData(result, aCallbackId);
return IPC_OK();
}
mozilla::ipc::IPCResult
PluginModuleChild::RecvNPP_GetSitesWithData(const uint64_t& aCallbackId)
{
char** result = mFunctions.getsiteswithdata();
InfallibleTArray<nsCString> array;
if (!result) {
SendReturnSitesWithData(array, aCallbackId);
return IPC_OK();
}
char** iterator = result;
while (*iterator) {
array.AppendElement(*iterator);
free(*iterator);
++iterator;
}
SendReturnSitesWithData(array, aCallbackId);
free(result);
return IPC_OK();
}
mozilla::ipc::IPCResult
PluginModuleChild::RecvSetAudioSessionData(const nsID& aId,
const nsString& aDisplayName,
const nsString& aIconPath)
{
#if !defined XP_WIN
MOZ_CRASH("Not Reached!");
#else
nsresult rv = mozilla::widget::RecvAudioSessionData(aId, aDisplayName, aIconPath);
NS_ENSURE_SUCCESS(rv, IPC_OK()); // Bail early if this fails
// Ignore failures here; we can't really do anything about them
mozilla::widget::StartAudioSession();
return IPC_OK();
#endif
}
mozilla::ipc::IPCResult
PluginModuleChild::RecvInitPluginModuleChild(Endpoint<PPluginModuleChild>&& aEndpoint)
{
if (!CreateForContentProcess(Move(aEndpoint))) {
return IPC_FAIL(this, "CreateForContentProcess failed");
}
return IPC_OK();
}
mozilla::ipc::IPCResult
PluginModuleChild::RecvInitPluginFunctionBroker(Endpoint<PFunctionBrokerChild>&& aEndpoint)
{
#if defined(XP_WIN)
MOZ_ASSERT(mIsChrome);
if (!FunctionBrokerChild::Initialize(Move(aEndpoint))) {
return IPC_FAIL(this,
"InitPluginFunctionBroker failed to initialize broker child.");
}
return IPC_OK();
#else
return IPC_FAIL(this, "InitPluginFunctionBroker not supported on this platform.");
#endif
}
mozilla::ipc::IPCResult
PluginModuleChild::AnswerInitCrashReporter(Shmem&& aShmem, mozilla::dom::NativeThreadId* aOutId)
{
CrashReporterClient::InitSingletonWithShmem(aShmem);
*aOutId = CrashReporter::CurrentThreadId();
return IPC_OK();
}
void
PluginModuleChild::ActorDestroy(ActorDestroyReason why)
{
#ifdef MOZ_GECKO_PROFILER
if (mProfilerController) {
mProfilerController->Shutdown();
mProfilerController = nullptr;
}
#endif
if (!mIsChrome) {
PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
if (chromeInstance) {
chromeInstance->SendNotifyContentModuleDestroyed();
}
// Destroy ourselves once we finish other teardown activities.
RefPtr<DeleteTask<PluginModuleChild>> task =
new DeleteTask<PluginModuleChild>(this);
MessageLoop::current()->PostTask(task.forget());
return;
}
if (AbnormalShutdown == why) {
NS_WARNING("shutting down early because of crash!");
ProcessChild::QuickExit();
}
if (!mHasShutdown) {
MOZ_ASSERT(gChromeInstance == this);
NP_Shutdown();
}
#if defined(XP_WIN)
FunctionBrokerChild::Destroy();
FunctionHook::ClearDllInterceptorCache();
#endif
// doesn't matter why we're being destroyed; it's up to us to
// initiate (clean) shutdown
CrashReporterClient::DestroySingleton();
XRE_ShutdownChildProcess();
}
void
PluginModuleChild::CleanUp()
{
}
const char*
PluginModuleChild::GetUserAgent()
{
return NullableStringGet(Settings().userAgent());
}
//-----------------------------------------------------------------------------
// FIXME/cjones: just getting this out of the way for the moment ...
namespace mozilla {
namespace plugins {
namespace child {
static NPError
_requestread(NPStream *pstream, NPByteRange *rangeList);
static NPError
_geturlnotify(NPP aNPP, const char* relativeURL, const char* target,
void* notifyData);
static NPError
_getvalue(NPP aNPP, NPNVariable variable, void *r_value);
static NPError
_setvalue(NPP aNPP, NPPVariable variable, void *r_value);
static NPError
_geturl(NPP aNPP, const char* relativeURL, const char* target);
static NPError
_posturlnotify(NPP aNPP, const char* relativeURL, const char *target,
uint32_t len, const char *buf, NPBool file, void* notifyData);
static NPError
_posturl(NPP aNPP, const char* relativeURL, const char *target, uint32_t len,
const char *buf, NPBool file);
static void
_status(NPP aNPP, const char *message);
static void
_memfree (void *ptr);
static uint32_t
_memflush(uint32_t size);
static void
_reloadplugins(NPBool reloadPages);
static void
_invalidaterect(NPP aNPP, NPRect *invalidRect);
static void
_invalidateregion(NPP aNPP, NPRegion invalidRegion);
static void
_forceredraw(NPP aNPP);
static const char*
_useragent(NPP aNPP);
static void*
_memalloc (uint32_t size);
// Deprecated entry points for the old Java plugin.
static void* /* OJI type: JRIEnv* */
_getjavaenv(void);
// Deprecated entry points for the old Java plugin.
static void* /* OJI type: jref */
_getjavapeer(NPP aNPP);
static bool
_invoke(NPP aNPP, NPObject* npobj, NPIdentifier method, const NPVariant *args,
uint32_t argCount, NPVariant *result);
static bool
_invokedefault(NPP aNPP, NPObject* npobj, const NPVariant *args,
uint32_t argCount, NPVariant *result);
static bool
_evaluate(NPP aNPP, NPObject* npobj, NPString *script, NPVariant *result);
static bool
_getproperty(NPP aNPP, NPObject* npobj, NPIdentifier property,
NPVariant *result);
static bool
_setproperty(NPP aNPP, NPObject* npobj, NPIdentifier property,
const NPVariant *value);
static bool
_removeproperty(NPP aNPP, NPObject* npobj, NPIdentifier property);
static bool
_hasproperty(NPP aNPP, NPObject* npobj, NPIdentifier propertyName);
static bool
_hasmethod(NPP aNPP, NPObject* npobj, NPIdentifier methodName);
static bool
_enumerate(NPP aNPP, NPObject *npobj, NPIdentifier **identifier,
uint32_t *count);
static bool
_construct(NPP aNPP, NPObject* npobj, const NPVariant *args,
uint32_t argCount, NPVariant *result);
static void
_releasevariantvalue(NPVariant *variant);
static void
_setexception(NPObject* npobj, const NPUTF8 *message);
static void
_pushpopupsenabledstate(NPP aNPP, NPBool enabled);
static void
_poppopupsenabledstate(NPP aNPP);
static NPError
_getvalueforurl(NPP npp, NPNURLVariable variable, const char *url,
char **value, uint32_t *len);
static NPError
_setvalueforurl(NPP npp, NPNURLVariable variable, const char *url,
const char *value, uint32_t len);
static uint32_t
_scheduletimer(NPP instance, uint32_t interval, NPBool repeat,
void (*timerFunc)(NPP npp, uint32_t timerID));
static void
_unscheduletimer(NPP instance, uint32_t timerID);
static NPError
_popupcontextmenu(NPP instance, NPMenu* menu);
static NPBool
_convertpoint(NPP instance,
double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
double *destX, double *destY, NPCoordinateSpace destSpace);
static void
_urlredirectresponse(NPP instance, void* notifyData, NPBool allow);
static NPError
_initasyncsurface(NPP instance, NPSize *size,
NPImageFormat format, void *initData,
NPAsyncSurface *surface);
static NPError
_finalizeasyncsurface(NPP instance, NPAsyncSurface *surface);
static void
_setcurrentasyncsurface(NPP instance, NPAsyncSurface *surface, NPRect *changed);
} /* namespace child */
} /* namespace plugins */
} /* namespace mozilla */
const NPNetscapeFuncs PluginModuleChild::sBrowserFuncs = {
sizeof(sBrowserFuncs),
(NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR,
mozilla::plugins::child::_geturl,
mozilla::plugins::child::_posturl,
mozilla::plugins::child::_requestread,
nullptr,
nullptr,
nullptr,
mozilla::plugins::child::_status,
mozilla::plugins::child::_useragent,
mozilla::plugins::child::_memalloc,
mozilla::plugins::child::_memfree,
mozilla::plugins::child::_memflush,
mozilla::plugins::child::_reloadplugins,
mozilla::plugins::child::_getjavaenv,
mozilla::plugins::child::_getjavapeer,
mozilla::plugins::child::_geturlnotify,
mozilla::plugins::child::_posturlnotify,
mozilla::plugins::child::_getvalue,
mozilla::plugins::child::_setvalue,
mozilla::plugins::child::_invalidaterect,
mozilla::plugins::child::_invalidateregion,
mozilla::plugins::child::_forceredraw,
PluginModuleChild::NPN_GetStringIdentifier,
PluginModuleChild::NPN_GetStringIdentifiers,
PluginModuleChild::NPN_GetIntIdentifier,
PluginModuleChild::NPN_IdentifierIsString,
PluginModuleChild::NPN_UTF8FromIdentifier,
PluginModuleChild::NPN_IntFromIdentifier,
PluginModuleChild::NPN_CreateObject,
PluginModuleChild::NPN_RetainObject,
PluginModuleChild::NPN_ReleaseObject,
mozilla::plugins::child::_invoke,
mozilla::plugins::child::_invokedefault,
mozilla::plugins::child::_evaluate,
mozilla::plugins::child::_getproperty,
mozilla::plugins::child::_setproperty,
mozilla::plugins::child::_removeproperty,
mozilla::plugins::child::_hasproperty,
mozilla::plugins::child::_hasmethod,
mozilla::plugins::child::_releasevariantvalue,
mozilla::plugins::child::_setexception,
mozilla::plugins::child::_pushpopupsenabledstate,
mozilla::plugins::child::_poppopupsenabledstate,
mozilla::plugins::child::_enumerate,
nullptr, // pluginthreadasynccall, not used
mozilla::plugins::child::_construct,
mozilla::plugins::child::_getvalueforurl,
mozilla::plugins::child::_setvalueforurl,
nullptr, //NPN GetAuthenticationInfo, not supported
mozilla::plugins::child::_scheduletimer,
mozilla::plugins::child::_unscheduletimer,
mozilla::plugins::child::_popupcontextmenu,
mozilla::plugins::child::_convertpoint,
nullptr, // handleevent, unimplemented
nullptr, // unfocusinstance, unimplemented
mozilla::plugins::child::_urlredirectresponse,
mozilla::plugins::child::_initasyncsurface,
mozilla::plugins::child::_finalizeasyncsurface,
mozilla::plugins::child::_setcurrentasyncsurface,
};
PluginInstanceChild*
InstCast(NPP aNPP)
{
MOZ_ASSERT(!!(aNPP->ndata), "nil instance");
return static_cast<PluginInstanceChild*>(aNPP->ndata);
}
namespace mozilla {
namespace plugins {
namespace child {
NPError
_requestread(NPStream* aStream,
NPByteRange* aRangeList)
{
return NPERR_STREAM_NOT_SEEKABLE;
}
NPError
_geturlnotify(NPP aNPP,
const char* aRelativeURL,
const char* aTarget,
void* aNotifyData)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
if (!aNPP) // nullptr check for nspluginwrapper (bug 561690)
return NPERR_INVALID_INSTANCE_ERROR;
nsCString url = NullableString(aRelativeURL);
auto* sn = new StreamNotifyChild(url);
NPError err;
InstCast(aNPP)->CallPStreamNotifyConstructor(
sn, url, NullableString(aTarget), false, nsCString(), false, &err);
if (NPERR_NO_ERROR == err) {
// If NPN_PostURLNotify fails, the parent will immediately send us
// a PStreamNotifyDestructor, which should not call NPP_URLNotify.
sn->SetValid(aNotifyData);
}
return err;
}
NPError
_getvalue(NPP aNPP,
NPNVariable aVariable,
void* aValue)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
switch (aVariable) {
// Copied from nsNPAPIPlugin.cpp
case NPNVToolkit:
#if defined(MOZ_WIDGET_GTK)
*static_cast<NPNToolkitType*>(aValue) = NPNVGtk2;
return NPERR_NO_ERROR;
#endif
return NPERR_GENERIC_ERROR;
case NPNVjavascriptEnabledBool:
*(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().javascriptEnabled();
return NPERR_NO_ERROR;
case NPNVasdEnabledBool:
*(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().asdEnabled();
return NPERR_NO_ERROR;
case NPNVisOfflineBool:
*(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().isOffline();
return NPERR_NO_ERROR;
case NPNVSupportsXEmbedBool:
// We don't support windowed xembed any more. But we still deliver
// events based on X/GTK, not Xt, so we continue to return true
// (and Flash requires that we return true).
*(NPBool*)aValue = true;
return NPERR_NO_ERROR;
case NPNVSupportsWindowless:
*(NPBool*)aValue = true;
return NPERR_NO_ERROR;
#if defined(MOZ_WIDGET_GTK)
case NPNVxDisplay: {
if (!aNPP) {
return NPERR_INVALID_INSTANCE_ERROR;
}
return InstCast(aNPP)->NPN_GetValue(aVariable, aValue);
}
case NPNVxtAppContext:
return NPERR_GENERIC_ERROR;
#endif
default: {
if (aNPP) {
return InstCast(aNPP)->NPN_GetValue(aVariable, aValue);
}
NS_WARNING("Null NPP!");
return NPERR_INVALID_INSTANCE_ERROR;
}
}
NS_NOTREACHED("Shouldn't get here!");
return NPERR_GENERIC_ERROR;
}
NPError
_setvalue(NPP aNPP,
NPPVariable aVariable,
void* aValue)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
return InstCast(aNPP)->NPN_SetValue(aVariable, aValue);
}
NPError
_geturl(NPP aNPP,
const char* aRelativeURL,
const char* aTarget)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
NPError err;
InstCast(aNPP)->CallNPN_GetURL(NullableString(aRelativeURL),
NullableString(aTarget), &err);
return err;
}
NPError
_posturlnotify(NPP aNPP,
const char* aRelativeURL,
const char* aTarget,
uint32_t aLength,
const char* aBuffer,
NPBool aIsFile,
void* aNotifyData)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
if (!aBuffer)
return NPERR_INVALID_PARAM;
if (aIsFile) {
PLUGIN_LOG_DEBUG(("NPN_PostURLNotify with file=true is no longer supported"));
return NPERR_GENERIC_ERROR;
}
nsCString url = NullableString(aRelativeURL);
auto* sn = new StreamNotifyChild(url);
NPError err;
InstCast(aNPP)->CallPStreamNotifyConstructor(
sn, url, NullableString(aTarget), true,
nsCString(aBuffer, aLength), aIsFile, &err);
if (NPERR_NO_ERROR == err) {
// If NPN_PostURLNotify fails, the parent will immediately send us
// a PStreamNotifyDestructor, which should not call NPP_URLNotify.
sn->SetValid(aNotifyData);
}
return err;
}
NPError
_posturl(NPP aNPP,
const char* aRelativeURL,
const char* aTarget,
uint32_t aLength,
const char* aBuffer,
NPBool aIsFile)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM);
if (aIsFile) {
PLUGIN_LOG_DEBUG(("NPN_PostURL with file=true is no longer supported"));
return NPERR_GENERIC_ERROR;
}
NPError err;
// FIXME what should happen when |aBuffer| is null?
InstCast(aNPP)->CallNPN_PostURL(NullableString(aRelativeURL),
NullableString(aTarget),
nsDependentCString(aBuffer, aLength),
aIsFile, &err);
return err;
}
void
_status(NPP aNPP,
const char* aMessage)
{
// NPN_Status is no longer supported.
}
void
_memfree(void* aPtr)
{
PLUGIN_LOG_DEBUG_FUNCTION;
free(aPtr);
}
uint32_t
_memflush(uint32_t aSize)
{
return 0;
}
void
_reloadplugins(NPBool aReloadPages)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
// Send the reload message to all modules. Chrome will need to reload from
// disk and content will need to request a new list of plugin tags from
// chrome.
PluginModuleChild::GetChrome()->SendNPN_ReloadPlugins(!!aReloadPages);
}
void
_invalidaterect(NPP aNPP,
NPRect* aInvalidRect)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
// nullptr check for nspluginwrapper (bug 548434)
if (aNPP) {
InstCast(aNPP)->InvalidateRect(aInvalidRect);
}
}
void
_invalidateregion(NPP aNPP,
NPRegion aInvalidRegion)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
NS_WARNING("Not yet implemented!");
}
void
_forceredraw(NPP aNPP)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
// We ignore calls to NPN_ForceRedraw. Such calls should
// never be necessary.
}
const char*
_useragent(NPP aNPP)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(nullptr);
return PluginModuleChild::GetChrome()->GetUserAgent();
}
void*
_memalloc(uint32_t aSize)
{
PLUGIN_LOG_DEBUG_FUNCTION;
return moz_xmalloc(aSize);
}
// Deprecated entry points for the old Java plugin.
void* /* OJI type: JRIEnv* */
_getjavaenv(void)
{
PLUGIN_LOG_DEBUG_FUNCTION;
return 0;
}
void* /* OJI type: jref */
_getjavapeer(NPP aNPP)
{
PLUGIN_LOG_DEBUG_FUNCTION;
return 0;
}
bool
_invoke(NPP aNPP,
NPObject* aNPObj,
NPIdentifier aMethod,
const NPVariant* aArgs,
uint32_t aArgCount,
NPVariant* aResult)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(false);
if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->invoke)
return false;
return aNPObj->_class->invoke(aNPObj, aMethod, aArgs, aArgCount, aResult);
}
bool
_invokedefault(NPP aNPP,
NPObject* aNPObj,
const NPVariant* aArgs,
uint32_t aArgCount,
NPVariant* aResult)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(false);
if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->invokeDefault)
return false;
return aNPObj->_class->invokeDefault(aNPObj, aArgs, aArgCount, aResult);
}
bool
_evaluate(NPP aNPP,
NPObject* aObject,
NPString* aScript,
NPVariant* aResult)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(false);
if (!(aNPP && aObject && aScript && aResult)) {
NS_ERROR("Bad arguments!");
return false;
}
PluginScriptableObjectChild* actor =
InstCast(aNPP)->GetActorForNPObject(aObject);
if (!actor) {
NS_ERROR("Failed to create actor?!");
return false;
}
#ifdef XP_WIN
if (gDelayFlashFocusReplyUntilEval) {
ReplyMessage(0);
gDelayFlashFocusReplyUntilEval = false;
}
#endif
return actor->Evaluate(aScript, aResult);
}
bool
_getproperty(NPP aNPP,
NPObject* aNPObj,
NPIdentifier aPropertyName,
NPVariant* aResult)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(false);
if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->getProperty)
return false;
return aNPObj->_class->getProperty(aNPObj, aPropertyName, aResult);
}
bool
_setproperty(NPP aNPP,
NPObject* aNPObj,
NPIdentifier aPropertyName,
const NPVariant* aValue)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(false);
if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->setProperty)
return false;
return aNPObj->_class->setProperty(aNPObj, aPropertyName, aValue);
}
bool
_removeproperty(NPP aNPP,
NPObject* aNPObj,
NPIdentifier aPropertyName)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(false);
if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->removeProperty)
return false;
return aNPObj->_class->removeProperty(aNPObj, aPropertyName);
}
bool
_hasproperty(NPP aNPP,
NPObject* aNPObj,
NPIdentifier aPropertyName)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(false);
if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->hasProperty)
return false;
return aNPObj->_class->hasProperty(aNPObj, aPropertyName);
}
bool
_hasmethod(NPP aNPP,
NPObject* aNPObj,
NPIdentifier aMethodName)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(false);
if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->hasMethod)
return false;
return aNPObj->_class->hasMethod(aNPObj, aMethodName);
}
bool
_enumerate(NPP aNPP,
NPObject* aNPObj,
NPIdentifier** aIdentifiers,
uint32_t* aCount)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(false);
if (!aNPP || !aNPObj || !aNPObj->_class)
return false;
if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(aNPObj->_class) ||
!aNPObj->_class->enumerate) {
*aIdentifiers = 0;
*aCount = 0;
return true;
}
return aNPObj->_class->enumerate(aNPObj, aIdentifiers, aCount);
}
bool
_construct(NPP aNPP,
NPObject* aNPObj,
const NPVariant* aArgs,
uint32_t aArgCount,
NPVariant* aResult)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(false);
if (!aNPP || !aNPObj || !aNPObj->_class ||
!NP_CLASS_STRUCT_VERSION_HAS_CTOR(aNPObj->_class) ||
!aNPObj->_class->construct) {
return false;
}
return aNPObj->_class->construct(aNPObj, aArgs, aArgCount, aResult);
}
void
_releasevariantvalue(NPVariant* aVariant)
{
PLUGIN_LOG_DEBUG_FUNCTION;
// Only assert plugin thread here for consistency with in-process plugins.
AssertPluginThread();
if (NPVARIANT_IS_STRING(*aVariant)) {
NPString str = NPVARIANT_TO_STRING(*aVariant);
free(const_cast<NPUTF8*>(str.UTF8Characters));
}
else if (NPVARIANT_IS_OBJECT(*aVariant)) {
NPObject* object = NPVARIANT_TO_OBJECT(*aVariant);
if (object) {
PluginModuleChild::NPN_ReleaseObject(object);
}
}
VOID_TO_NPVARIANT(*aVariant);
}
void
_setexception(NPObject* aNPObj,
const NPUTF8* aMessage)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
// Do nothing. We no longer support this API.
}
void
_pushpopupsenabledstate(NPP aNPP,
NPBool aEnabled)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
InstCast(aNPP)->CallNPN_PushPopupsEnabledState(aEnabled ? true : false);
}
void
_poppopupsenabledstate(NPP aNPP)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
InstCast(aNPP)->CallNPN_PopPopupsEnabledState();
}
NPError
_getvalueforurl(NPP npp, NPNURLVariable variable, const char *url,
char **value, uint32_t *len)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
if (!url)
return NPERR_INVALID_URL;
if (!npp || !value || !len)
return NPERR_INVALID_PARAM;
if (variable == NPNURLVProxy) {
nsCString v;
NPError result;
InstCast(npp)->
CallNPN_GetValueForURL(variable, nsCString(url), &v, &result);
if (NPERR_NO_ERROR == result) {
*value = ToNewCString(v);
*len = v.Length();
}
return result;
}
return NPERR_INVALID_PARAM;
}
NPError
_setvalueforurl(NPP npp, NPNURLVariable variable, const char *url,
const char *value, uint32_t len)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
if (!value)
return NPERR_INVALID_PARAM;
if (!url)
return NPERR_INVALID_URL;
if (variable == NPNURLVProxy) {
NPError result;
InstCast(npp)->CallNPN_SetValueForURL(variable, nsCString(url),
nsDependentCString(value, len),
&result);
return result;
}
return NPERR_INVALID_PARAM;
}
uint32_t
_scheduletimer(NPP npp, uint32_t interval, NPBool repeat,
void (*timerFunc)(NPP npp, uint32_t timerID))
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
return InstCast(npp)->ScheduleTimer(interval, repeat, timerFunc);
}
void
_unscheduletimer(NPP npp, uint32_t timerID)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
InstCast(npp)->UnscheduleTimer(timerID);
}
#ifdef OS_MACOSX
static void ProcessBrowserEvents(void* pluginModule) {
PluginModuleChild* pmc = static_cast<PluginModuleChild*>(pluginModule);
if (!pmc)
return;
pmc->CallProcessSomeEvents();
}
#endif
NPError
_popupcontextmenu(NPP instance, NPMenu* menu)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
#ifdef MOZ_WIDGET_COCOA
double pluginX, pluginY;
double screenX, screenY;
const NPCocoaEvent* currentEvent = InstCast(instance)->getCurrentEvent();
if (!currentEvent) {
return NPERR_GENERIC_ERROR;
}
// Ensure that the events has an x/y value.
if (currentEvent->type != NPCocoaEventMouseDown &&
currentEvent->type != NPCocoaEventMouseUp &&
currentEvent->type != NPCocoaEventMouseMoved &&
currentEvent->type != NPCocoaEventMouseEntered &&
currentEvent->type != NPCocoaEventMouseExited &&
currentEvent->type != NPCocoaEventMouseDragged) {
return NPERR_GENERIC_ERROR;
}
pluginX = currentEvent->data.mouse.pluginX;
pluginY = currentEvent->data.mouse.pluginY;
if ((pluginX < 0.0) || (pluginY < 0.0))
return NPERR_GENERIC_ERROR;
NPBool success = _convertpoint(instance,
pluginX, pluginY, NPCoordinateSpacePlugin,
&screenX, &screenY, NPCoordinateSpaceScreen);
if (success) {
return mozilla::plugins::PluginUtilsOSX::ShowCocoaContextMenu(menu,
screenX, screenY,
InstCast(instance)->Manager(),
ProcessBrowserEvents);
} else {
NS_WARNING("Convertpoint failed, could not created contextmenu.");
return NPERR_GENERIC_ERROR;
}
#else
NS_WARNING("Not supported on this platform!");
return NPERR_GENERIC_ERROR;
#endif
}
NPBool
_convertpoint(NPP instance,
double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
double *destX, double *destY, NPCoordinateSpace destSpace)
{
PLUGIN_LOG_DEBUG_FUNCTION;
if (!IsPluginThread()) {
NS_WARNING("Not running on the plugin's main thread!");
return false;
}
double rDestX = 0;
bool ignoreDestX = !destX;
double rDestY = 0;
bool ignoreDestY = !destY;
bool result = false;
InstCast(instance)->CallNPN_ConvertPoint(sourceX, ignoreDestX, sourceY, ignoreDestY, sourceSpace, destSpace,
&rDestX, &rDestY, &result);
if (result) {
if (destX)
*destX = rDestX;
if (destY)
*destY = rDestY;
}
return result;
}
void
_urlredirectresponse(NPP instance, void* notifyData, NPBool allow)
{
InstCast(instance)->NPN_URLRedirectResponse(notifyData, allow);
}
NPError
_initasyncsurface(NPP instance, NPSize *size,
NPImageFormat format, void *initData,
NPAsyncSurface *surface)
{
return InstCast(instance)->NPN_InitAsyncSurface(size, format, initData, surface);
}
NPError
_finalizeasyncsurface(NPP instance, NPAsyncSurface *surface)
{
return InstCast(instance)->NPN_FinalizeAsyncSurface(surface);
}
void
_setcurrentasyncsurface(NPP instance, NPAsyncSurface *surface, NPRect *changed)
{
InstCast(instance)->NPN_SetCurrentAsyncSurface(surface, changed);
}
} /* namespace child */
} /* namespace plugins */
} /* namespace mozilla */
//-----------------------------------------------------------------------------
mozilla::ipc::IPCResult
PluginModuleChild::RecvSettingChanged(const PluginSettings& aSettings)
{
mCachedSettings = aSettings;
return IPC_OK();
}
mozilla::ipc::IPCResult
PluginModuleChild::AnswerNP_GetEntryPoints(NPError* _retval)
{
PLUGIN_LOG_DEBUG_METHOD;
AssertPluginThread();
MOZ_ASSERT(mIsChrome);
#if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
return IPC_OK();
#elif defined(OS_WIN) || defined(OS_MACOSX)
*_retval = mGetEntryPointsFunc(&mFunctions);
return IPC_OK();
#else
# error Please implement me for your platform
#endif
}
mozilla::ipc::IPCResult
PluginModuleChild::AnswerNP_Initialize(const PluginSettings& aSettings, NPError* rv)
{
*rv = DoNP_Initialize(aSettings);
return IPC_OK();
}
NPError
PluginModuleChild::DoNP_Initialize(const PluginSettings& aSettings)
{
PLUGIN_LOG_DEBUG_METHOD;
AssertPluginThread();
MOZ_ASSERT(mIsChrome);
mCachedSettings = aSettings;
#ifdef OS_WIN
SetEventHooks();
#endif
#ifdef MOZ_X11
// Send the parent our X socket to act as a proxy reference for our X
// resources.
int xSocketFd = ConnectionNumber(DefaultXDisplay());
SendBackUpXResources(FileDescriptor(xSocketFd));
#endif
NPError result;
#if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
result = mInitializeFunc(&sBrowserFuncs, &mFunctions);
#elif defined(OS_WIN) || defined(OS_MACOSX)
result = mInitializeFunc(&sBrowserFuncs);
#else
# error Please implement me for your platform
#endif
return result;
}
PPluginInstanceChild*
PluginModuleChild::AllocPPluginInstanceChild(const nsCString& aMimeType,
const InfallibleTArray<nsCString>& aNames,
const InfallibleTArray<nsCString>& aValues)
{
PLUGIN_LOG_DEBUG_METHOD;
AssertPluginThread();
// In e10s, gChromeInstance hands out quirks to instances, but never
// allocates an instance on its own. Make sure it gets the latest copy
// of quirks once we have them. Also note, with process-per-tab, we may
// have multiple PluginModuleChilds in the same plugin process, so only
// initialize this once in gChromeInstance, which is a singleton.
GetChrome()->InitQuirksModes(aMimeType);
mQuirks = GetChrome()->mQuirks;
#ifdef XP_WIN
FunctionHook::HookFunctions(mQuirks);
#endif
return new PluginInstanceChild(&mFunctions, aMimeType, aNames,
aValues);
}
void
PluginModuleChild::InitQuirksModes(const nsCString& aMimeType)
{
if (mQuirks != QUIRKS_NOT_INITIALIZED) {
return;
}
mQuirks = GetQuirksFromMimeTypeAndFilename(aMimeType, mPluginFilename);
}
mozilla::ipc::IPCResult
PluginModuleChild::AnswerModuleSupportsAsyncRender(bool* aResult)
{
#if defined(XP_WIN)
*aResult = gChromeInstance->mAsyncRenderSupport;
return IPC_OK();
#else
NS_NOTREACHED("Shouldn't get here!");
return IPC_FAIL_NO_REASON(this);
#endif
}
mozilla::ipc::IPCResult
PluginModuleChild::RecvPPluginInstanceConstructor(PPluginInstanceChild* aActor,
const nsCString& aMimeType,
InfallibleTArray<nsCString>&& aNames,
InfallibleTArray<nsCString>&& aValues)
{
PLUGIN_LOG_DEBUG_METHOD;
AssertPluginThread();
NS_ASSERTION(aActor, "Null actor!");
return IPC_OK();
}
mozilla::ipc::IPCResult
PluginModuleChild::AnswerSyncNPP_New(PPluginInstanceChild* aActor, NPError* rv)
{
PLUGIN_LOG_DEBUG_METHOD;
PluginInstanceChild* childInstance =
reinterpret_cast<PluginInstanceChild*>(aActor);
AssertPluginThread();
*rv = childInstance->DoNPP_New();
return IPC_OK();
}
bool
PluginModuleChild::DeallocPPluginInstanceChild(PPluginInstanceChild* aActor)
{
PLUGIN_LOG_DEBUG_METHOD;
AssertPluginThread();
delete aActor;
return true;
}
NPObject*
PluginModuleChild::NPN_CreateObject(NPP aNPP, NPClass* aClass)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(nullptr);
PluginInstanceChild* i = InstCast(aNPP);
if (i->mDeletingHash) {
NS_ERROR("Plugin used NPP after NPP_Destroy");
return nullptr;
}
NPObject* newObject;
if (aClass && aClass->allocate) {
newObject = aClass->allocate(aNPP, aClass);
}
else {
newObject = reinterpret_cast<NPObject*>(child::_memalloc(sizeof(NPObject)));
}
if (newObject) {
newObject->_class = aClass;
newObject->referenceCount = 1;
NS_LOG_ADDREF(newObject, 1, "NPObject", sizeof(NPObject));
}
PluginScriptableObjectChild::RegisterObject(newObject, i);
return newObject;
}
NPObject*
PluginModuleChild::NPN_RetainObject(NPObject* aNPObj)
{
AssertPluginThread();
#ifdef NS_BUILD_REFCNT_LOGGING
int32_t refCnt =
#endif
PR_ATOMIC_INCREMENT((int32_t*)&aNPObj->referenceCount);
NS_LOG_ADDREF(aNPObj, refCnt, "NPObject", sizeof(NPObject));
return aNPObj;
}
void
PluginModuleChild::NPN_ReleaseObject(NPObject* aNPObj)
{
AssertPluginThread();
PluginInstanceChild* instance = PluginScriptableObjectChild::GetInstanceForNPObject(aNPObj);
if (!instance) {
// The PluginInstanceChild was destroyed
return;
}
DeletingObjectEntry* doe = nullptr;
if (instance->mDeletingHash) {
doe = instance->mDeletingHash->GetEntry(aNPObj);
if (!doe) {
NS_ERROR("An object for a destroyed instance isn't in the instance deletion hash");
return;
}
if (doe->mDeleted)
return;
}
int32_t refCnt = PR_ATOMIC_DECREMENT((int32_t*)&aNPObj->referenceCount);
NS_LOG_RELEASE(aNPObj, refCnt, "NPObject");
if (refCnt == 0) {
DeallocNPObject(aNPObj);
if (doe)
doe->mDeleted = true;
}
}
void
PluginModuleChild::DeallocNPObject(NPObject* aNPObj)
{
if (aNPObj->_class && aNPObj->_class->deallocate) {
aNPObj->_class->deallocate(aNPObj);
} else {
child::_memfree(aNPObj);
}
PluginScriptableObjectChild* actor = PluginScriptableObjectChild::GetActorForNPObject(aNPObj);
if (actor)
actor->NPObjectDestroyed();
PluginScriptableObjectChild::UnregisterObject(aNPObj);
}
NPIdentifier
PluginModuleChild::NPN_GetStringIdentifier(const NPUTF8* aName)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
if (!aName)
return 0;
nsDependentCString name(aName);
PluginIdentifier ident(name);
PluginScriptableObjectChild::StackIdentifier stackID(ident);
stackID.MakePermanent();
return stackID.ToNPIdentifier();
}
void
PluginModuleChild::NPN_GetStringIdentifiers(const NPUTF8** aNames,
int32_t aNameCount,
NPIdentifier* aIdentifiers)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
if (!(aNames && aNameCount > 0 && aIdentifiers)) {
MOZ_CRASH("Bad input! Headed for a crash!");
}
for (int32_t index = 0; index < aNameCount; ++index) {
if (!aNames[index]) {
aIdentifiers[index] = 0;
continue;
}
nsDependentCString name(aNames[index]);
PluginIdentifier ident(name);
PluginScriptableObjectChild::StackIdentifier stackID(ident);
stackID.MakePermanent();
aIdentifiers[index] = stackID.ToNPIdentifier();
}
}
bool
PluginModuleChild::NPN_IdentifierIsString(NPIdentifier aIdentifier)
{
PLUGIN_LOG_DEBUG_FUNCTION;
PluginScriptableObjectChild::StackIdentifier stack(aIdentifier);
return stack.IsString();
}
NPIdentifier
PluginModuleChild::NPN_GetIntIdentifier(int32_t aIntId)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
PluginIdentifier ident(aIntId);
PluginScriptableObjectChild::StackIdentifier stackID(ident);
stackID.MakePermanent();
return stackID.ToNPIdentifier();
}
NPUTF8*
PluginModuleChild::NPN_UTF8FromIdentifier(NPIdentifier aIdentifier)
{
PLUGIN_LOG_DEBUG_FUNCTION;
PluginScriptableObjectChild::StackIdentifier stackID(aIdentifier);
if (stackID.IsString()) {
return ToNewCString(stackID.GetString());
}
return nullptr;
}
int32_t
PluginModuleChild::NPN_IntFromIdentifier(NPIdentifier aIdentifier)
{
PLUGIN_LOG_DEBUG_FUNCTION;
PluginScriptableObjectChild::StackIdentifier stackID(aIdentifier);
if (!stackID.IsString()) {
return stackID.GetInt();
}
return INT32_MIN;
}
#ifdef OS_WIN
void
PluginModuleChild::EnteredCall()
{
mIncallPumpingStack.AppendElement();
}
void
PluginModuleChild::ExitedCall()
{
NS_ASSERTION(mIncallPumpingStack.Length(), "mismatched entered/exited");
uint32_t len = mIncallPumpingStack.Length();
const IncallFrame& f = mIncallPumpingStack[len - 1];
if (f._spinning)
MessageLoop::current()->SetNestableTasksAllowed(f._savedNestableTasksAllowed);
mIncallPumpingStack.TruncateLength(len - 1);
}
LRESULT CALLBACK
PluginModuleChild::CallWindowProcHook(int nCode, WPARAM wParam, LPARAM lParam)
{
// Trap and reply to anything we recognize as the source of a
// potential send message deadlock.
if (nCode >= 0 &&
(InSendMessageEx(nullptr)&(ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) {
CWPSTRUCT* pCwp = reinterpret_cast<CWPSTRUCT*>(lParam);
if (pCwp->message == WM_KILLFOCUS) {
// Fix for flash fullscreen window loosing focus. On single
// core systems, sync killfocus events need to be handled
// after the flash fullscreen window procedure processes this
// message, otherwise fullscreen focus will not work correctly.
wchar_t szClass[26];
if (GetClassNameW(pCwp->hwnd, szClass,
sizeof(szClass)/sizeof(char16_t)) &&
!wcscmp(szClass, kFlashFullscreenClass)) {
gDelayFlashFocusReplyUntilEval = true;
}
}
}
return CallNextHookEx(nullptr, nCode, wParam, lParam);
}
LRESULT CALLBACK
PluginModuleChild::NestedInputEventHook(int nCode, WPARAM wParam, LPARAM lParam)
{
PluginModuleChild* self = GetChrome();
uint32_t len = self->mIncallPumpingStack.Length();
if (nCode >= 0 && len && !self->mIncallPumpingStack[len - 1]._spinning) {
MessageLoop* loop = MessageLoop::current();
self->SendProcessNativeEventsInInterruptCall();
IncallFrame& f = self->mIncallPumpingStack[len - 1];
f._spinning = true;
f._savedNestableTasksAllowed = loop->NestableTasksAllowed();
loop->SetNestableTasksAllowed(true);
loop->set_os_modal_loop(true);
}
return CallNextHookEx(nullptr, nCode, wParam, lParam);
}
void
PluginModuleChild::SetEventHooks()
{
NS_ASSERTION(!mNestedEventHook,
"mNestedEventHook already setup in call to SetNestedInputEventHook?");
NS_ASSERTION(!mGlobalCallWndProcHook,
"mGlobalCallWndProcHook already setup in call to CallWindowProcHook?");
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
// WH_MSGFILTER event hook for detecting modal loops in the child.
mNestedEventHook = SetWindowsHookEx(WH_MSGFILTER,
NestedInputEventHook,
nullptr,
GetCurrentThreadId());
// WH_CALLWNDPROC event hook for trapping sync messages sent from
// parent that can cause deadlocks.
mGlobalCallWndProcHook = SetWindowsHookEx(WH_CALLWNDPROC,
CallWindowProcHook,
nullptr,
GetCurrentThreadId());
}
void
PluginModuleChild::ResetEventHooks()
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
if (mNestedEventHook)
UnhookWindowsHookEx(mNestedEventHook);
mNestedEventHook = nullptr;
if (mGlobalCallWndProcHook)
UnhookWindowsHookEx(mGlobalCallWndProcHook);
mGlobalCallWndProcHook = nullptr;
}
#endif
mozilla::ipc::IPCResult
PluginModuleChild::RecvProcessNativeEventsInInterruptCall()
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
#if defined(OS_WIN)
ProcessNativeEventsInInterruptCall();
return IPC_OK();
#else
MOZ_CRASH(
"PluginModuleChild::RecvProcessNativeEventsInInterruptCall not implemented!");
return IPC_FAIL_NO_REASON(this);
#endif
}
#ifdef MOZ_WIDGET_COCOA
void
PluginModuleChild::ProcessNativeEvents() {
CallProcessSomeEvents();
}
#endif
NPError
PluginModuleChild::PluginRequiresAudioDeviceChanges(
PluginInstanceChild* aInstance,
NPBool aShouldRegister)
{
#ifdef XP_WIN
// Maintain a set of PluginInstanceChildren that we need to tell when the
// default audio device has changed.
NPError rv = NPERR_NO_ERROR;
if (aShouldRegister) {
if (mAudioNotificationSet.IsEmpty()) {
// We are registering the first plugin. Notify the PluginModuleParent
// that it needs to start sending us audio device notifications.
if (!CallNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
aShouldRegister, &rv)) {
return NPERR_GENERIC_ERROR;
}
}
if (rv == NPERR_NO_ERROR) {
mAudioNotificationSet.PutEntry(aInstance);
}
}
else if (!mAudioNotificationSet.IsEmpty()) {
mAudioNotificationSet.RemoveEntry(aInstance);
if (mAudioNotificationSet.IsEmpty()) {
// We released the last plugin. Unregister from the PluginModuleParent.
if (!CallNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
aShouldRegister, &rv)) {
return NPERR_GENERIC_ERROR;
}
}
}
return rv;
#else
MOZ_CRASH("PluginRequiresAudioDeviceChanges is not available on this platform.");
#endif // XP_WIN
}
mozilla::ipc::IPCResult
PluginModuleChild::RecvNPP_SetValue_NPNVaudioDeviceChangeDetails(
const NPAudioDeviceChangeDetailsIPC& detailsIPC)
{
#if defined(XP_WIN)
NPAudioDeviceChangeDetails details;
details.flow = detailsIPC.flow;
details.role = detailsIPC.role;
details.defaultDevice = detailsIPC.defaultDevice.c_str();
for (auto iter = mAudioNotificationSet.ConstIter(); !iter.Done(); iter.Next()) {
PluginInstanceChild* pluginInst = iter.Get()->GetKey();
pluginInst->DefaultAudioDeviceChanged(details);
}
return IPC_OK();
#else
MOZ_CRASH("NPP_SetValue_NPNVaudioDeviceChangeDetails is a Windows-only message");
#endif
}