dom/plugins/ipc/PluginModuleChild.cpp
author shindli <shindli@mozilla.com>
Thu, 23 Nov 2017 00:11:44 +0200
changeset 393180 ffc12802d5585e08de1a9ae4f2939e05bbea5767
parent 393162 8a4139fa5dcaf970dabc628f0230f54467f84d61
child 393407 536f8e448230ed175173c8bd801d4430200cb184
permissions -rw-r--r--
Backed out 16 changesets (bug 1402519) for conflicts during merge r=backout on a CLOSED TREE Backed out changeset 07fcf163241a (bug 1402519) Backed out changeset c6d2ad45d8e2 (bug 1402519) Backed out changeset 8a3caca61294 (bug 1402519) Backed out changeset 01425eae2c48 (bug 1402519) Backed out changeset cf298d3815de (bug 1402519) Backed out changeset e1964f4389cd (bug 1402519) Backed out changeset f405337f3569 (bug 1402519) Backed out changeset a76356fd3359 (bug 1402519) Backed out changeset d3bb350d1c34 (bug 1402519) Backed out changeset 9d3bfd9f932c (bug 1402519) Backed out changeset e3dd6e5b073f (bug 1402519) Backed out changeset e801b0c00134 (bug 1402519) Backed out changeset 8a4139fa5dca (bug 1402519) Backed out changeset 8d01c14ac1ca (bug 1402519) Backed out changeset 24e0dcd01898 (bug 1402519) Backed out changeset f8fdf450613f (bug 1402519)

/* -*- 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/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"

#ifdef XP_WIN
#include "nsWindowsDllInterceptor.h"
#include "mozilla/widget/AudioSession.h"
#include <knownfolders.h>
#include <shlobj.h>
#endif

#ifdef MOZ_WIDGET_COCOA
#include "PluginInterposeOSX.h"
#include "PluginUtilsOSX.h"
#endif

#ifdef MOZ_CRASHREPORTER
#include "mozilla/ipc/CrashReporterClient.h"
#endif

#ifdef MOZ_GECKO_PROFILER
#include "ChildProfilerController.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";
const wchar_t * kMozillaWindowClass = L"MozillaWindowClass";
#endif

namespace {
// see PluginModuleChild::GetChrome()
PluginModuleChild* gChromeInstance = nullptr;
} // namespace

#ifdef XP_WIN
// Hooking CreateFileW for protected-mode magic
static WindowsDllInterceptor sKernel32Intercept;
typedef HANDLE (WINAPI *CreateFileWPtr)(LPCWSTR fname, DWORD access,
                                        DWORD share,
                                        LPSECURITY_ATTRIBUTES security,
                                        DWORD creation, DWORD flags,
                                        HANDLE ftemplate);
static CreateFileWPtr sCreateFileWStub = nullptr;
typedef HANDLE (WINAPI *CreateFileAPtr)(LPCSTR fname, DWORD access,
                                        DWORD share,
                                        LPSECURITY_ATTRIBUTES security,
                                        DWORD creation, DWORD flags,
                                        HANDLE ftemplate);
static CreateFileAPtr sCreateFileAStub = nullptr;

// Used with fix for flash fullscreen window loosing focus.
static bool gDelayFlashFocusReplyUntilEval = false;
// Used to fix GetWindowInfo problems with internal flash settings dialogs
static WindowsDllInterceptor sUser32Intercept;
typedef BOOL (WINAPI *GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
static GetWindowInfoPtr sGetWindowInfoPtrStub = nullptr;
static HWND sBrowserHwnd = nullptr;
// sandbox process doesn't get current key states.  So we need get it on chrome.
typedef SHORT (WINAPI *GetKeyStatePtr)(int);
static GetKeyStatePtr sGetKeyStatePtrStub = nullptr;

static WindowsDllInterceptor sComDlg32Intercept;

// proxy GetSaveFileName/GetOpenFileName on chrome so that we can know which
// files the user has given permission to access
// We count on GetOpenFileNameA/GetSaveFileNameA calling
// GetOpenFileNameW/GetSaveFileNameW so we don't proxy them explicitly.
typedef BOOL (WINAPI *GetOpenFileNameWPtr)(LPOPENFILENAMEW lpofn);
static GetOpenFileNameWPtr sGetOpenFileNameWPtrStub = nullptr;
typedef BOOL (WINAPI *GetSaveFileNameWPtr)(LPOPENFILENAMEW lpofn);
static GetSaveFileNameWPtr sGetSaveFileNameWPtrStub = nullptr;

typedef BOOL (WINAPI *SetCursorPosPtr)(int x, int y);
static SetCursorPosPtr sSetCursorPosPtrStub = nullptr;

typedef BOOL (WINAPI *PrintDlgWPtr)(LPPRINTDLGW aDlg);
static PrintDlgWPtr sPrintDlgWPtrStub = nullptr;

#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
{
    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
    HookProtectedMode();
#else
    MOZ_ASSERT(false, "Should not be called");
#endif
    return IPC_OK();
}

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

    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::AnswerInitCrashReporter(Shmem&& aShmem, mozilla::dom::NativeThreadId* aOutId)
{
#ifdef MOZ_CRASHREPORTER
    CrashReporterClient::InitSingletonWithShmem(aShmem);
    *aOutId = CrashReporter::CurrentThreadId();
#endif
    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();
    }

    // doesn't matter why we're being destroyed; it's up to us to
    // initiate (clean) shutdown
#ifdef MOZ_CRASHREPORTER
    CrashReporterClient::DestroySingleton();
#endif
    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;
}

#if defined(XP_WIN)

// Windows 8 RTM (kernelbase's version is 6.2.9200.16384) doesn't call
// CreateFileW from CreateFileA.
// So we hook CreateFileA too to use CreateFileW hook.

static HANDLE WINAPI
CreateFileAHookFn(LPCSTR fname, DWORD access, DWORD share,
                  LPSECURITY_ATTRIBUTES security, DWORD creation, DWORD flags,
                  HANDLE ftemplate)
{
    while (true) { // goto out
        // Our hook is for mms.cfg into \Windows\System32\Macromed\Flash
        // We don't requrie supporting too long path.
        WCHAR unicodeName[MAX_PATH];
        size_t len = strlen(fname);

        if (len >= MAX_PATH) {
            break;
        }

        // We call to CreateFileW for workaround of Windows 8 RTM
        int newLen = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, fname,
                                         len, unicodeName, MAX_PATH);
        if (newLen == 0 || newLen >= MAX_PATH) {
            break;
        }
        unicodeName[newLen] = '\0';

        return CreateFileW(unicodeName, access, share, security, creation, flags, ftemplate);
    }

    return sCreateFileAStub(fname, access, share, security, creation, flags,
                            ftemplate);
}

static bool
GetLocalLowTempPath(size_t aLen, LPWSTR aPath)
{
    NS_NAMED_LITERAL_STRING(tempname, "\\Temp");
    LPWSTR path;
    if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0,
                                       nullptr, &path))) {
        if (wcslen(path) + tempname.Length() < aLen) {
            wcscpy(aPath, path);
            wcscat(aPath, tempname.get());
            ::CoTaskMemFree(path);
            return true;
        }
        ::CoTaskMemFree(path);
    }

    // XP doesn't support SHGetKnownFolderPath and LocalLow
    if (!GetTempPathW(aLen, aPath)) {
        return false;
    }
    return true;
}

HANDLE WINAPI
CreateFileWHookFn(LPCWSTR fname, DWORD access, DWORD share,
                  LPSECURITY_ATTRIBUTES security, DWORD creation, DWORD flags,
                  HANDLE ftemplate)
{
    static const WCHAR kConfigFile[] = L"mms.cfg";
    static const size_t kConfigLength = ArrayLength(kConfigFile) - 1;

    while (true) { // goto out, in sheep's clothing
        size_t len = wcslen(fname);
        if (len < kConfigLength) {
            break;
        }
        if (wcscmp(fname + len - kConfigLength, kConfigFile) != 0) {
            break;
        }

        // This is the config file we want to rewrite
        WCHAR tempPath[MAX_PATH+1];
        if (GetLocalLowTempPath(MAX_PATH, tempPath) == 0) {
            break;
        }
        WCHAR tempFile[MAX_PATH+1];
        if (GetTempFileNameW(tempPath, L"fx", 0, tempFile) == 0) {
            break;
        }
        HANDLE replacement =
            sCreateFileWStub(tempFile, GENERIC_READ | GENERIC_WRITE, share,
                             security, TRUNCATE_EXISTING,
                             FILE_ATTRIBUTE_TEMPORARY |
                               FILE_FLAG_DELETE_ON_CLOSE,
                             NULL);
        if (replacement == INVALID_HANDLE_VALUE) {
            break;
        }

        HANDLE original = sCreateFileWStub(fname, access, share, security,
                                           creation, flags, ftemplate);
        if (original != INVALID_HANDLE_VALUE) {
            // copy original to replacement
            static const size_t kBufferSize = 1024;
            char buffer[kBufferSize];
            DWORD bytes;
            while (ReadFile(original, buffer, kBufferSize, &bytes, NULL)) {
                if (bytes == 0) {
                    break;
                }
                DWORD wbytes;
                WriteFile(replacement, buffer, bytes, &wbytes, NULL);
                if (bytes < kBufferSize) {
                    break;
                }
            }
            CloseHandle(original);
        }
        static const char kSettingString[] = "\nProtectedMode=0\n";
        DWORD wbytes;
        WriteFile(replacement, static_cast<const void*>(kSettingString),
                  sizeof(kSettingString) - 1, &wbytes, NULL);
        SetFilePointer(replacement, 0, NULL, FILE_BEGIN);
        return replacement;
    }
    return sCreateFileWStub(fname, access, share, security, creation, flags,
                            ftemplate);
}

void
PluginModuleChild::HookProtectedMode()
{
    sKernel32Intercept.Init("kernel32.dll");
    sKernel32Intercept.AddHook("CreateFileW",
                               reinterpret_cast<intptr_t>(CreateFileWHookFn),
                               (void**) &sCreateFileWStub);
    sKernel32Intercept.AddHook("CreateFileA",
                               reinterpret_cast<intptr_t>(CreateFileAHookFn),
                               (void**) &sCreateFileAStub);
}

BOOL WINAPI
PMCGetWindowInfoHook(HWND hWnd, PWINDOWINFO pwi)
{
  if (!pwi)
      return FALSE;

  if (!sGetWindowInfoPtrStub) {
     NS_ASSERTION(FALSE, "Something is horribly wrong in PMCGetWindowInfoHook!");
     return FALSE;
  }

  if (!sBrowserHwnd) {
      wchar_t szClass[20];
      if (GetClassNameW(hWnd, szClass, ArrayLength(szClass)) &&
          !wcscmp(szClass, kMozillaWindowClass)) {
          sBrowserHwnd = hWnd;
      }
  }
  // Oddity: flash does strange rect comparisons for mouse input destined for
  // it's internal settings window. Post removing sub widgets for tabs, touch
  // this up so they get the rect they expect.
  // XXX potentially tie this to a specific major version?
  BOOL result = sGetWindowInfoPtrStub(hWnd, pwi);
  if (sBrowserHwnd && sBrowserHwnd == hWnd)
      pwi->rcWindow = pwi->rcClient;
  return result;
}

SHORT WINAPI PMCGetKeyState(int aVirtKey);

// Runnable that performs GetKeyState on the main thread so that it can be
// synchronously run on the PluginModuleParent via IPC.
// The task alerts the given semaphore when it is finished.
class GetKeyStateTask : public Runnable
{
    SHORT* mKeyState;
    int mVirtKey;
    HANDLE mSemaphore;

public:
    explicit GetKeyStateTask(int aVirtKey, HANDLE aSemaphore, SHORT* aKeyState) :
        Runnable("GetKeyStateTask"),
        mKeyState(aKeyState),
        mVirtKey(aVirtKey),
        mSemaphore(aSemaphore)
    {}

    NS_IMETHOD Run() override
    {
        PLUGIN_LOG_DEBUG_METHOD;
        AssertPluginThread();
        *mKeyState = PMCGetKeyState(mVirtKey);
        if (!ReleaseSemaphore(mSemaphore, 1, nullptr)) {
            return NS_ERROR_FAILURE;
        }
        return NS_OK;
    }
};

// static
SHORT WINAPI
PMCGetKeyState(int aVirtKey)
{
    if (!IsPluginThread()) {
        // synchronously request the key state from the main thread

        // Start a semaphore at 0.  We Release the semaphore (bringing its count to 1)
        // when the synchronous call is done.
        HANDLE semaphore = CreateSemaphore(NULL, 0, 1, NULL);
        if (semaphore == nullptr) {
            MOZ_ASSERT(semaphore != nullptr);
            return 0;
        }

        SHORT keyState;
        RefPtr<GetKeyStateTask> task = new GetKeyStateTask(aVirtKey, semaphore, &keyState);
        ProcessChild::message_loop()->PostTask(task.forget());
        DWORD err = WaitForSingleObject(semaphore, INFINITE);
        if (err != WAIT_FAILED) {
            CloseHandle(semaphore);
            return keyState;
        }
        PLUGIN_LOG_DEBUG(("Error while waiting for GetKeyState semaphore: %d",
                          GetLastError()));
        MOZ_ASSERT(err != WAIT_FAILED);
        CloseHandle(semaphore);
        return 0;
    }
    PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
    if (chromeInstance) {
        int16_t ret = 0;
        if (chromeInstance->CallGetKeyState(aVirtKey, &ret)) {
          return ret;
        }
    }
    return sGetKeyStatePtrStub(aVirtKey);
}

class PluginThreadTaskData
{
public:
    virtual bool RunTask() = 0;
};

// Runnable that performs a task on the main thread so that the call can be
// synchronously run on the PluginModuleParent via IPC.
// The task alerts the given semaphore when it is finished.
class PluginThreadTask : public Runnable
{
    bool mSuccess;
    PluginThreadTaskData* mTaskData;
    HANDLE mSemaphore;

public:
    explicit PluginThreadTask(PluginThreadTaskData* aTaskData,
                              HANDLE aSemaphore) :
        Runnable("PluginThreadTask"),
        mSuccess(false),
        mTaskData(aTaskData),
        mSemaphore(aSemaphore)
    {}

    NS_IMETHOD Run() override
    {
        PLUGIN_LOG_DEBUG_METHOD;
        AssertPluginThread();
        mSuccess = mTaskData->RunTask();
        if (!ReleaseSemaphore(mSemaphore, 1, nullptr)) {
            return NS_ERROR_FAILURE;
        }
        return NS_OK;
    }

    bool Success() { return mSuccess; }
};

// static
BOOL
PostToPluginThread(PluginThreadTaskData* aTaskData)
{
    MOZ_ASSERT(!IsPluginThread());

    // Synchronously run GetFileNameTask from the main thread.
    // Start a semaphore at 0.  We release the semaphore (bringing its
    // count to 1) when the synchronous call is done.
    nsAutoHandle semaphore(CreateSemaphore(NULL, 0, 1, NULL));
    if (semaphore == nullptr) {
        MOZ_ASSERT(semaphore != nullptr);
        return FALSE;
    }

    RefPtr<PluginThreadTask> task = new PluginThreadTask(aTaskData, semaphore);
    ProcessChild::message_loop()->PostTask(do_AddRef(task));
    DWORD err = WaitForSingleObject(semaphore, INFINITE);
    if (err != WAIT_FAILED) {
        return task->Success();
    }
    PLUGIN_LOG_DEBUG(("Error while waiting for semaphore: %d",
                      GetLastError()));
    MOZ_ASSERT(err != WAIT_FAILED);
    return FALSE;
}

BOOL WINAPI PMCGetSaveFileNameW(LPOPENFILENAMEW lpofn);
BOOL WINAPI PMCGetOpenFileNameW(LPOPENFILENAMEW lpofn);

class GetFileNameTaskData : public PluginThreadTaskData
{
public:
    GetFileNameTaskData(GetFileNameFunc aFunc, void* aLpOpenFileName) :
        mFunc(aFunc), mLpOpenFileName(aLpOpenFileName)
    {}

    bool RunTask()
    {
        switch (mFunc) {
        case OPEN_FUNC:
            return PMCGetOpenFileNameW(static_cast<LPOPENFILENAMEW>(mLpOpenFileName));
        case SAVE_FUNC:
            return PMCGetSaveFileNameW(static_cast<LPOPENFILENAMEW>(mLpOpenFileName));
        }
        return false;
    }

private:
    GetFileNameFunc mFunc;
    void* mLpOpenFileName;
};

// static
BOOL WINAPI
PMCGetFileNameW(GetFileNameFunc aFunc, LPOPENFILENAMEW aLpofn)
{
    if (!IsPluginThread()) {
        GetFileNameTaskData gfnData(aFunc, aLpofn);
        return PostToPluginThread(&gfnData);
    }

    PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
    if (chromeInstance) {
        bool ret = FALSE;
        OpenFileNameIPC inputOfn;
        inputOfn.CopyFromOfn(aLpofn);
        OpenFileNameRetIPC outputOfn;
        if (chromeInstance->CallGetFileName(aFunc, inputOfn,
                                            &outputOfn, &ret)) {
            if (ret) {
                outputOfn.AddToOfn(aLpofn);
            }
        }
        return ret;
    }

    switch (aFunc) {
    case OPEN_FUNC:
        return sGetOpenFileNameWPtrStub(aLpofn);
    case SAVE_FUNC:
        return sGetSaveFileNameWPtrStub(aLpofn);
    }

    MOZ_ASSERT_UNREACHABLE("Illegal GetFileNameFunc value");
    return FALSE;
}

// static
BOOL WINAPI
PMCGetSaveFileNameW(LPOPENFILENAMEW aLpofn)
{
    return PMCGetFileNameW(SAVE_FUNC, aLpofn);
}
// static
BOOL WINAPI
PMCGetOpenFileNameW(LPOPENFILENAMEW aLpofn)
{
    return PMCGetFileNameW(OPEN_FUNC, aLpofn);
}

//static
BOOL WINAPI
PMCPrintDlgW(LPPRINTDLGW aDlg)
{
  // Zero out the HWND supplied by the plugin.  We are sacrificing window
  // parentage for the ability to run in the NPAPI sandbox.
  HWND hwnd = aDlg->hwndOwner;
  aDlg->hwndOwner = 0;
  BOOL ret = sPrintDlgWPtrStub(aDlg);
  aDlg->hwndOwner = hwnd;
  return ret;
}

BOOL WINAPI PMCSetCursorPos(int x, int y);

class SetCursorPosTaskData : public PluginThreadTaskData
{
public:
    SetCursorPosTaskData(int x, int y) : mX(x), mY(y) {}
    bool RunTask() { return PMCSetCursorPos(mX, mY); }
private:
    int mX, mY;
};

// static
BOOL WINAPI
PMCSetCursorPos(int x, int y)
{
    if (!IsPluginThread()) {
        SetCursorPosTaskData scpData(x, y);
        return PostToPluginThread(&scpData);
    }

    PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
    if (chromeInstance) {
        bool ret = FALSE;
        chromeInstance->CallSetCursorPos(x, y, &ret);
        return ret;
    }

    return sSetCursorPosPtrStub(x, y);
}


#endif

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
    sUser32Intercept.Init("user32.dll");
    if ((mQuirks & QUIRK_FLASH_HOOK_GETWINDOWINFO) &&
        !sGetWindowInfoPtrStub) {
        sUser32Intercept.AddHook("GetWindowInfo", reinterpret_cast<intptr_t>(PMCGetWindowInfoHook),
                                 (void**) &sGetWindowInfoPtrStub);
    }

    if ((mQuirks & QUIRK_FLASH_HOOK_GETKEYSTATE) &&
        !sGetKeyStatePtrStub) {
        sUser32Intercept.AddHook("GetKeyState", reinterpret_cast<intptr_t>(PMCGetKeyState),
                                 (void**) &sGetKeyStatePtrStub);
    }

    if (!sSetCursorPosPtrStub) {
        sUser32Intercept.AddHook("SetCursorPos", reinterpret_cast<intptr_t>(PMCSetCursorPos),
                                 (void**) &sSetCursorPosPtrStub);
    }

    sComDlg32Intercept.Init("comdlg32.dll");
    if (!sGetSaveFileNameWPtrStub) {
        sComDlg32Intercept.AddHook("GetSaveFileNameW", reinterpret_cast<intptr_t>(PMCGetSaveFileNameW),
                                 (void**) &sGetSaveFileNameWPtrStub);
    }

    if (!sGetOpenFileNameWPtrStub) {
        sComDlg32Intercept.AddHook("GetOpenFileNameW", reinterpret_cast<intptr_t>(PMCGetOpenFileNameW),
                                 (void**) &sGetOpenFileNameWPtrStub);
    }

    if ((mQuirks & QUIRK_FLASH_HOOK_PRINTDLGW) &&
        !sPrintDlgWPtrStub) {
        sComDlg32Intercept.AddHook("PrintDlgW", reinterpret_cast<intptr_t>(PMCPrintDlgW),
                                 (void**) &sPrintDlgWPtrStub);
    }
#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) {
        NS_ERROR("Releasing object not in mObjectMap?");
        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
}