summaryrefslogtreecommitdiffstats
path: root/dom/plugins/base
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/plugins/base
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/plugins/base')
-rw-r--r--dom/plugins/base/components.conf16
-rw-r--r--dom/plugins/base/moz.build95
-rw-r--r--dom/plugins/base/npapi.h922
-rw-r--r--dom/plugins/base/npfunctions.h356
-rw-r--r--dom/plugins/base/npruntime.h382
-rw-r--r--dom/plugins/base/nptypes.h89
-rw-r--r--dom/plugins/base/nsIHTTPHeaderListener.idl29
-rw-r--r--dom/plugins/base/nsIPluginDocument.idl16
-rw-r--r--dom/plugins/base/nsIPluginHost.idl179
-rw-r--r--dom/plugins/base/nsIPluginInputStream.idl20
-rw-r--r--dom/plugins/base/nsIPluginInstanceOwner.idl121
-rw-r--r--dom/plugins/base/nsIPluginTag.idl97
-rw-r--r--dom/plugins/base/nsJSNPRuntime.cpp2183
-rw-r--r--dom/plugins/base/nsJSNPRuntime.h99
-rw-r--r--dom/plugins/base/nsNPAPIPlugin.cpp1884
-rw-r--r--dom/plugins/base/nsNPAPIPlugin.h288
-rw-r--r--dom/plugins/base/nsNPAPIPluginInstance.cpp1203
-rw-r--r--dom/plugins/base/nsNPAPIPluginInstance.h327
-rw-r--r--dom/plugins/base/nsNPAPIPluginStreamListener.cpp775
-rw-r--r--dom/plugins/base/nsNPAPIPluginStreamListener.h126
-rw-r--r--dom/plugins/base/nsPluginHost.cpp2792
-rw-r--r--dom/plugins/base/nsPluginHost.h391
-rw-r--r--dom/plugins/base/nsPluginInstanceOwner.cpp3164
-rw-r--r--dom/plugins/base/nsPluginInstanceOwner.h396
-rw-r--r--dom/plugins/base/nsPluginLogging.h54
-rw-r--r--dom/plugins/base/nsPluginManifestLineReader.h101
-rw-r--r--dom/plugins/base/nsPluginNativeWindow.cpp61
-rw-r--r--dom/plugins/base/nsPluginNativeWindow.h76
-rw-r--r--dom/plugins/base/nsPluginNativeWindowWin.cpp658
-rw-r--r--dom/plugins/base/nsPluginStreamListenerPeer.cpp606
-rw-r--r--dom/plugins/base/nsPluginStreamListenerPeer.h150
-rw-r--r--dom/plugins/base/nsPluginTags.cpp926
-rw-r--r--dom/plugins/base/nsPluginTags.h242
-rw-r--r--dom/plugins/base/nsPluginsCID.h16
-rw-r--r--dom/plugins/base/nsPluginsDir.h80
-rw-r--r--dom/plugins/base/nsPluginsDirDarwin.cpp522
-rw-r--r--dom/plugins/base/nsPluginsDirUnix.cpp188
-rw-r--r--dom/plugins/base/nsPluginsDirUtils.h88
-rw-r--r--dom/plugins/base/nsPluginsDirWin.cpp349
-rw-r--r--dom/plugins/base/nspluginroot.idl31
40 files changed, 20098 insertions, 0 deletions
diff --git a/dom/plugins/base/components.conf b/dom/plugins/base/components.conf
new file mode 100644
index 0000000000..4b90c78f44
--- /dev/null
+++ b/dom/plugins/base/components.conf
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+ {
+ 'cid': '{23e8fd98-a625-4b08-be1a-f7cc18a5b106}',
+ 'contract_ids': ['@mozilla.org/plugin/host;1'],
+ 'singleton': True,
+ 'type': 'nsPluginHost',
+ 'headers': ['nsPluginHost.h'],
+ 'constructor': 'nsPluginHost::GetInst',
+ },
+]
diff --git a/dom/plugins/base/moz.build b/dom/plugins/base/moz.build
new file mode 100644
index 0000000000..e1ee17fc5c
--- /dev/null
+++ b/dom/plugins/base/moz.build
@@ -0,0 +1,95 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_SOURCES += [
+ "nsIHTTPHeaderListener.idl",
+ "nsIPluginDocument.idl",
+ "nsIPluginHost.idl",
+ "nsIPluginInputStream.idl",
+ "nsIPluginInstanceOwner.idl",
+ "nsIPluginTag.idl",
+ "nspluginroot.idl",
+]
+
+XPIDL_MODULE = "plugin"
+
+EXPORTS += [
+ "npapi.h",
+ "npfunctions.h",
+ "npruntime.h",
+ "nptypes.h",
+ "nsJSNPRuntime.h",
+ "nsNPAPIPluginInstance.h",
+ "nsPluginHost.h",
+ "nsPluginInstanceOwner.h",
+ "nsPluginLogging.h",
+ "nsPluginNativeWindow.h",
+ "nsPluginsCID.h",
+ "nsPluginsDir.h",
+ "nsPluginTags.h",
+]
+
+UNIFIED_SOURCES += [
+ "nsJSNPRuntime.cpp",
+ "nsNPAPIPluginInstance.cpp",
+ "nsNPAPIPluginStreamListener.cpp",
+ "nsPluginInstanceOwner.cpp",
+ "nsPluginStreamListenerPeer.cpp",
+ "nsPluginTags.cpp",
+]
+
+SOURCES += [
+ "nsNPAPIPlugin.cpp", # Conflict with X11 headers
+ "nsPluginHost.cpp", # Conflict with NS_NPAPIPLUGIN_CALLBACK
+]
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ UNIFIED_SOURCES += [
+ "nsPluginNativeWindowWin.cpp",
+ "nsPluginsDirWin.cpp",
+ ]
+elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ UNIFIED_SOURCES += [
+ "nsPluginNativeWindow.cpp",
+ ]
+ SOURCES += [
+ "nsPluginsDirDarwin.cpp", # conflict with mozilla::EventPriority
+ ]
+else:
+ UNIFIED_SOURCES += [
+ "nsPluginNativeWindow.cpp",
+ "nsPluginsDirUnix.cpp",
+ ]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+LOCAL_INCLUDES += [
+ "/dom/base",
+ "/dom/plugins/ipc",
+ "/layout/generic",
+ "/layout/xul",
+ "/netwerk/base",
+ "/widget",
+ "/widget/cocoa",
+ "/xpcom/base",
+]
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ LOCAL_INCLUDES += [
+ "/xpcom/base",
+ ]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+CXXFLAGS += CONFIG["MOZ_CAIRO_CFLAGS"]
+CXXFLAGS += CONFIG["TK_CFLAGS"]
+
+if CONFIG["CC_TYPE"] in ("clang", "gcc"):
+ CXXFLAGS += ["-Wno-error=shadow"]
diff --git a/dom/plugins/base/npapi.h b/dom/plugins/base/npapi.h
new file mode 100644
index 0000000000..d6b189baef
--- /dev/null
+++ b/dom/plugins/base/npapi.h
@@ -0,0 +1,922 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef npapi_h_
+#define npapi_h_
+
+#include "nptypes.h"
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+# include <windef.h>
+# ifndef XP_WIN
+# define XP_WIN 1
+# endif
+#endif
+
+#if defined(__SYMBIAN32__)
+# ifndef XP_SYMBIAN
+# define XP_SYMBIAN 1
+# undef XP_WIN
+# endif
+#endif
+
+#if defined(__APPLE_CC__) && !defined(XP_UNIX)
+# ifndef XP_MACOSX
+# define XP_MACOSX 1
+# endif
+#endif
+
+#if defined(XP_MACOSX) && defined(__LP64__)
+# define NP_NO_QUICKDRAW
+# define NP_NO_CARBON
+#endif
+
+#if defined(XP_MACOSX)
+# include <ApplicationServices/ApplicationServices.h>
+# include <OpenGL/OpenGL.h>
+# ifndef NP_NO_CARBON
+# include <Carbon/Carbon.h>
+# endif
+#endif
+
+#if defined(XP_UNIX)
+# include <stdio.h>
+# if defined(MOZ_X11)
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+# include "X11UndefineNone.h"
+# endif
+#endif
+
+#if defined(XP_SYMBIAN)
+# include <QEvent>
+# include <QRegion>
+#endif
+
+/*----------------------------------------------------------------------*/
+/* Plugin Version Constants */
+/*----------------------------------------------------------------------*/
+
+#define NP_VERSION_MAJOR 0
+#define NP_VERSION_MINOR 29
+
+/* clang-format off */
+/* The OS/2 version of Netscape uses RC_DATA to define the
+ mime types, file extensions, etc that are required.
+ Use a vertical bar to separate types, end types with \0.
+ FileVersion and ProductVersion are 32bit ints, all other
+ entries are strings that MUST be terminated with a \0.
+
+AN EXAMPLE:
+
+RCDATA NP_INFO_ProductVersion { 1,0,0,1,}
+
+RCDATA NP_INFO_MIMEType { "video/x-video|",
+ "video/x-flick\0" }
+RCDATA NP_INFO_FileExtents { "avi|",
+ "flc\0" }
+RCDATA NP_INFO_FileOpenName{ "MMOS2 video player(*.avi)|",
+ "MMOS2 Flc/Fli player(*.flc)\0" }
+
+RCDATA NP_INFO_FileVersion { 1,0,0,1 }
+RCDATA NP_INFO_CompanyName { "Netscape Communications\0" }
+RCDATA NP_INFO_FileDescription { "NPAVI32 Extension DLL\0"
+RCDATA NP_INFO_InternalName { "NPAVI32\0" )
+RCDATA NP_INFO_LegalCopyright { "Copyright Netscape Communications \251 1996\0"
+RCDATA NP_INFO_OriginalFilename { "NVAPI32.DLL" }
+RCDATA NP_INFO_ProductName { "NPAVI32 Dynamic Link Library\0" }
+*/
+/* clang-format on */
+/* RC_DATA types for version info - required */
+#define NP_INFO_ProductVersion 1
+#define NP_INFO_MIMEType 2
+#define NP_INFO_FileOpenName 3
+#define NP_INFO_FileExtents 4
+/* RC_DATA types for version info - used if found */
+#define NP_INFO_FileDescription 5
+#define NP_INFO_ProductName 6
+/* RC_DATA types for version info - optional */
+#define NP_INFO_CompanyName 7
+#define NP_INFO_FileVersion 8
+#define NP_INFO_InternalName 9
+#define NP_INFO_LegalCopyright 10
+#define NP_INFO_OriginalFilename 11
+
+#ifndef RC_INVOKED
+
+/*----------------------------------------------------------------------*/
+/* Definition of Basic Types */
+/*----------------------------------------------------------------------*/
+
+typedef unsigned char NPBool;
+typedef int16_t NPError;
+typedef int16_t NPReason;
+typedef char* NPMIMEType;
+
+/*----------------------------------------------------------------------*/
+/* Structures and definitions */
+/*----------------------------------------------------------------------*/
+
+# if !defined(__LP64__)
+# if defined(XP_MACOSX)
+# pragma options align = mac68k
+# endif
+# endif /* __LP64__ */
+
+/*
+ * NPP is a plug-in's opaque instance handle
+ */
+typedef struct _NPP {
+ void* pdata; /* plug-in private data */
+ void* ndata; /* netscape private data */
+} NPP_t;
+
+typedef NPP_t* NPP;
+
+typedef struct _NPStream {
+ void* pdata; /* plug-in private data */
+ void* ndata; /* netscape private data */
+ const char* url;
+ uint32_t end;
+ uint32_t lastmodified;
+ void* notifyData;
+ const char* headers; /* Response headers from host.
+ * Exists only for >= NPVERS_HAS_RESPONSE_HEADERS.
+ * Used for HTTP only; nullptr for non-HTTP.
+ * Available from NPP_NewStream onwards.
+ * Plugin should copy this data before storing it.
+ * Includes HTTP status line and all headers,
+ * preferably verbatim as received from server,
+ * headers formatted as in HTTP ("Header: Value"),
+ * and newlines (\n, NOT \r\n) separating lines.
+ * Terminated by \n\0 (NOT \n\n\0). */
+} NPStream;
+
+typedef struct _NPByteRange {
+ int32_t offset; /* negative offset means from the end */
+ uint32_t length;
+ struct _NPByteRange* next;
+} NPByteRange;
+
+typedef struct _NPSavedData {
+ int32_t len;
+ void* buf;
+} NPSavedData;
+
+typedef struct _NPRect {
+ uint16_t top;
+ uint16_t left;
+ uint16_t bottom;
+ uint16_t right;
+} NPRect;
+
+typedef struct _NPSize {
+ int32_t width;
+ int32_t height;
+} NPSize;
+
+typedef enum { NPFocusNext = 0, NPFocusPrevious = 1 } NPFocusDirection;
+
+/* These formats describe the format in the memory byte-order. This means if
+ * a 32-bit value of a pixel is viewed on a little-endian system the layout will
+ * be 0xAARRGGBB. The Alpha channel will be stored in the most significant
+ * bits. */
+typedef enum {
+ /* 32-bit per pixel 8-bit per channel - premultiplied alpha */
+ NPImageFormatBGRA32 = 0x1,
+ /* 32-bit per pixel 8-bit per channel - 1 unused channel */
+ NPImageFormatBGRX32 = 0x2
+} NPImageFormat;
+
+typedef struct _NPAsyncSurface {
+ uint32_t version;
+ NPSize size;
+ NPImageFormat format;
+ union {
+ struct {
+ uint32_t stride;
+ void* data;
+ } bitmap;
+# if defined(XP_WIN)
+ HANDLE sharedHandle;
+# endif
+ };
+} NPAsyncSurface;
+
+/* Return values for NPP_HandleEvent */
+# define kNPEventNotHandled 0
+# define kNPEventHandled 1
+/* Exact meaning must be spec'd in event model. */
+# define kNPEventStartIME 2
+
+# if defined(XP_UNIX)
+/*
+ * Unix specific structures and definitions
+ */
+
+/*
+ * Callback Structures.
+ *
+ * These are used to pass additional platform specific information.
+ */
+enum { NP_SETWINDOW = 1, NP_PRINT };
+
+typedef struct {
+ int32_t type;
+} NPAnyCallbackStruct;
+
+typedef struct {
+ int32_t type;
+# if defined(MOZ_X11)
+ Display* display;
+ Visual* visual;
+ Colormap colormap;
+ unsigned int depth;
+# endif
+} NPSetWindowCallbackStruct;
+
+typedef struct {
+ int32_t type;
+ FILE* fp;
+} NPPrintCallbackStruct;
+
+# endif /* XP_UNIX */
+
+# if defined(XP_WIN)
+/*
+ * Windows specific structures and definitions
+ */
+
+/*
+ * Information about the default audio device. These values share meaning with
+ * the parameters to the Windows API IMMNotificationClient object.
+ * This is the value of the NPNVaudioDeviceChangeDetails variable.
+ */
+typedef struct _NPAudioDeviceChangeDetails {
+ int32_t flow;
+ int32_t role;
+ const wchar_t* defaultDevice; // this pointer is only valid during the call
+ // to NPPSetValue.
+} NPAudioDeviceChangeDetails;
+
+# endif /* XP_WIN */
+
+/*
+ * This is the value of the NPNVaudioDeviceStateChanged variable.
+ */
+typedef struct _NPAudioDeviceStateChanged {
+ /* Name of device that changed state. This string is only valid during
+ * the call to NPPSetValue.
+ */
+ const wchar_t* device;
+ uint32_t newState;
+} NPAudioDeviceStateChanged;
+
+typedef enum {
+ NPDrawingModelDUMMY
+# if defined(XP_MACOSX)
+# ifndef NP_NO_QUICKDRAW
+ ,
+ NPDrawingModelQuickDraw = 0
+# endif
+ ,
+ NPDrawingModelCoreGraphics = 1,
+ NPDrawingModelOpenGL = 2,
+ NPDrawingModelCoreAnimation = 3,
+ NPDrawingModelInvalidatingCoreAnimation = 4
+# endif
+# if defined(XP_WIN)
+ ,
+ NPDrawingModelSyncWin = 5
+# endif
+# if defined(MOZ_X11)
+ ,
+ NPDrawingModelSyncX = 6
+# endif
+ ,
+ NPDrawingModelAsyncBitmapSurface = 7
+# if defined(XP_WIN)
+ ,
+ NPDrawingModelAsyncWindowsDXGISurface = 8
+# endif
+} NPDrawingModel;
+
+# ifdef XP_MACOSX
+typedef enum {
+# ifndef NP_NO_CARBON
+ NPEventModelCarbon = 0,
+# endif
+ NPEventModelCocoa = 1
+} NPEventModel;
+# endif
+
+/*
+ * The following masks are applied on certain platforms to NPNV and
+ * NPPV selectors that pass around pointers to COM interfaces. Newer
+ * compilers on some platforms may generate vtables that are not
+ * compatible with older compilers. To prevent older plugins from
+ * not understanding a new browser's ABI, these masks change the
+ * values of those selectors on those platforms. To remain backwards
+ * compatible with different versions of the browser, plugins can
+ * use these masks to dynamically determine and use the correct C++
+ * ABI that the browser is expecting. This does not apply to Windows
+ * as Microsoft's COM ABI will likely not change.
+ */
+
+# define NP_ABI_GCC3_MASK 0x10000000
+/*
+ * gcc 3.x generated vtables on UNIX and OSX are incompatible with
+ * previous compilers.
+ */
+# if (defined(XP_UNIX) && defined(__GNUC__) && (__GNUC__ >= 3))
+# define _NP_ABI_MIXIN_FOR_GCC3 NP_ABI_GCC3_MASK
+# else
+# define _NP_ABI_MIXIN_FOR_GCC3 0
+# endif
+
+# if defined(XP_MACOSX)
+# define NP_ABI_MACHO_MASK 0x01000000
+# define _NP_ABI_MIXIN_FOR_MACHO NP_ABI_MACHO_MASK
+# else
+# define _NP_ABI_MIXIN_FOR_MACHO 0
+# endif
+
+# define NP_ABI_MASK (_NP_ABI_MIXIN_FOR_GCC3 | _NP_ABI_MIXIN_FOR_MACHO)
+
+/*
+ * List of variable names for which NPP_GetValue shall be implemented
+ */
+typedef enum {
+ NPPVpluginNameString = 1,
+ NPPVpluginDescriptionString,
+ NPPVpluginWindowBool,
+ NPPVpluginTransparentBool,
+ NPPVjavaClass,
+ NPPVpluginWindowSize,
+ NPPVpluginTimerInterval,
+ NPPVpluginScriptableInstance = (10 | NP_ABI_MASK),
+ NPPVpluginScriptableIID = 11,
+ NPPVjavascriptPushCallerBool = 12,
+ NPPVpluginKeepLibraryInMemory = 13,
+ NPPVpluginNeedsXEmbed = 14,
+
+ /* Get the NPObject for scripting the plugin. Introduced in NPAPI minor
+ * version 14.
+ */
+ NPPVpluginScriptableNPObject = 15,
+
+ /* Get the plugin value (as \0-terminated UTF-8 string data) for
+ * form submission if the plugin is part of a form. Use
+ * NPN_MemAlloc() to allocate memory for the string data. Introduced
+ * in NPAPI minor version 15.
+ */
+ NPPVformValue = 16,
+
+ NPPVpluginUrlRequestsDisplayedBool = 17,
+
+ /* Checks if the plugin is interested in receiving the http body of
+ * all http requests (including failed ones, http status != 200).
+ */
+ NPPVpluginWantsAllNetworkStreams = 18,
+
+ /* Browsers can retrieve a native ATK accessibility plug ID via this variable.
+ */
+ NPPVpluginNativeAccessibleAtkPlugId = 19,
+
+ /* Checks to see if the plug-in would like the browser to load the "src"
+ attribute. */
+ NPPVpluginCancelSrcStream = 20,
+
+ NPPVsupportsAdvancedKeyHandling = 21,
+
+ NPPVpluginUsesDOMForCursorBool = 22,
+
+ /* Used for negotiating drawing models */
+ NPPVpluginDrawingModel = 1000
+# if defined(XP_MACOSX)
+ /* Used for negotiating event models */
+ ,
+ NPPVpluginEventModel = 1001
+ /* In the NPDrawingModelCoreAnimation drawing model, the browser asks the
+ plug-in for a Core Animation layer. */
+ ,
+ NPPVpluginCoreAnimationLayer = 1003
+# endif
+ /* Notification that the plugin just started or stopped playing audio */
+ ,
+ NPPVpluginIsPlayingAudio = 4000
+# if defined(XP_WIN)
+ /* Notification that the plugin requests notification when the default audio
+ device has changed */
+ ,
+ NPPVpluginRequiresAudioDeviceChanges = 4001
+# endif
+
+} NPPVariable;
+
+/*
+ * List of variable names for which NPN_GetValue should be implemented.
+ */
+typedef enum {
+ NPNVxDisplay = 1,
+ NPNVxtAppContext,
+ NPNVnetscapeWindow,
+ NPNVjavascriptEnabledBool,
+ NPNVasdEnabledBool,
+ NPNVisOfflineBool,
+
+ NPNVserviceManager = (10 | NP_ABI_MASK),
+ NPNVDOMElement = (11 | NP_ABI_MASK),
+ NPNVDOMWindow = (12 | NP_ABI_MASK),
+ NPNVToolkit = (13 | NP_ABI_MASK),
+ NPNVSupportsXEmbedBool = 14,
+
+ /* Get the NPObject wrapper for the browser window. */
+ NPNVWindowNPObject = 15,
+
+ /* Get the NPObject wrapper for the plugins DOM element. */
+ NPNVPluginElementNPObject = 16,
+
+ NPNVSupportsWindowless = 17,
+
+ NPNVprivateModeBool = 18,
+
+ NPNVsupportsAdvancedKeyHandling = 21,
+
+ NPNVdocumentOrigin = 22,
+
+ NPNVCSSZoomFactor = 23,
+
+ NPNVpluginDrawingModel =
+ 1000 /* Get the current drawing model (NPDrawingModel) */
+# if defined(XP_MACOSX) || defined(XP_WIN)
+ ,
+ NPNVcontentsScaleFactor = 1001
+# endif
+# if defined(XP_MACOSX)
+# ifndef NP_NO_QUICKDRAW
+ ,
+ NPNVsupportsQuickDrawBool = 2000
+# endif
+ ,
+ NPNVsupportsCoreGraphicsBool = 2001,
+ NPNVsupportsOpenGLBool = 2002,
+ NPNVsupportsCoreAnimationBool = 2003,
+ NPNVsupportsInvalidatingCoreAnimationBool = 2004
+# endif
+ ,
+ NPNVsupportsAsyncBitmapSurfaceBool = 2007
+# if defined(XP_WIN)
+ ,
+ NPNVsupportsAsyncWindowsDXGISurfaceBool = 2008,
+ NPNVpreferredDXGIAdapter = 2009
+# endif
+# if defined(XP_MACOSX)
+# ifndef NP_NO_CARBON
+ ,
+ NPNVsupportsCarbonBool =
+ 3000 /* TRUE if the browser supports the Carbon event model */
+# endif
+ ,
+ NPNVsupportsCocoaBool =
+ 3001 /* TRUE if the browser supports the Cocoa event model */
+ ,
+ NPNVsupportsUpdatedCocoaTextInputBool =
+ 3002 /* TRUE if the browser supports the updated
+ Cocoa text input specification. */
+# endif
+ ,
+ NPNVmuteAudioBool =
+ 4000 /* Request that the browser wants to mute or unmute the plugin */
+# if defined(XP_WIN)
+ ,
+ NPNVaudioDeviceChangeDetails =
+ 4001 /* Provides information about the new default audio device */
+ ,
+ NPNVaudioDeviceStateChanged =
+ 4002 /* Provides information if any audio device changes state */
+# endif
+# if defined(XP_MACOSX)
+ ,
+ NPNVsupportsCompositingCoreAnimationPluginsBool =
+ 74656 /* TRUE if the browser supports
+ CA model compositing */
+# endif
+ ,
+ NPNVLast
+} NPNVariable;
+
+typedef enum { NPNURLVCookie = 501, NPNURLVProxy } NPNURLVariable;
+
+/*
+ * The type of Toolkit the widgets use
+ */
+typedef enum { NPNVGtk12 = 1, NPNVGtk2 } NPNToolkitType;
+
+/*
+ * The type of a NPWindow - it specifies the type of the data structure
+ * returned in the window field.
+ */
+typedef enum { NPWindowTypeWindow = 1, NPWindowTypeDrawable } NPWindowType;
+
+typedef struct _NPWindow {
+ void* window; /* Platform specific window handle */
+ /* OS/2: x - Position of bottom left corner */
+ /* OS/2: y - relative to visible netscape window */
+ int32_t x; /* Position of top left corner relative */
+ int32_t y; /* to a netscape page. */
+ uint32_t width; /* Maximum window size */
+ uint32_t height;
+ NPRect clipRect; /* Clipping rectangle in port coordinates */
+# if (defined(XP_UNIX) || defined(XP_SYMBIAN)) && !defined(XP_MACOSX)
+ void* ws_info; /* Platform-dependent additional data */
+# endif /* XP_UNIX */
+ NPWindowType type; /* Is this a window or a drawable? */
+} NPWindow;
+
+typedef struct _NPImageExpose {
+ char* data; /* image pointer */
+ int32_t stride; /* Stride of data image pointer */
+ int32_t depth; /* Depth of image pointer */
+ int32_t x; /* Expose x */
+ int32_t y; /* Expose y */
+ uint32_t width; /* Expose width */
+ uint32_t height; /* Expose height */
+ NPSize dataSize; /* Data buffer size */
+ float translateX; /* translate X matrix value */
+ float translateY; /* translate Y matrix value */
+ float scaleX; /* scale X matrix value */
+ float scaleY; /* scale Y matrix value */
+} NPImageExpose;
+
+typedef struct _NPFullPrint {
+ NPBool pluginPrinted; /* Set TRUE if plugin handled fullscreen printing */
+ NPBool printOne; /* TRUE if plugin should print one copy to default
+ printer */
+ void* platformPrint; /* Platform-specific printing info */
+} NPFullPrint;
+
+typedef struct _NPEmbedPrint {
+ NPWindow window;
+ void* platformPrint; /* Platform-specific printing info */
+} NPEmbedPrint;
+
+typedef struct _NPPrint {
+ uint16_t mode; /* NP_FULL or NP_EMBED */
+ union {
+ NPFullPrint fullPrint; /* if mode is NP_FULL */
+ NPEmbedPrint embedPrint; /* if mode is NP_EMBED */
+ } print;
+} NPPrint;
+
+# if defined(XP_MACOSX)
+# ifndef NP_NO_CARBON
+typedef EventRecord NPEvent;
+# endif
+# elif defined(XP_SYMBIAN)
+typedef QEvent NPEvent;
+# elif defined(XP_WIN)
+typedef struct _NPEvent {
+ uint16_t event;
+ uintptr_t wParam;
+ intptr_t lParam;
+} NPEvent;
+# elif defined(XP_UNIX) && defined(MOZ_X11)
+typedef XEvent NPEvent;
+# else
+typedef void* NPEvent;
+# endif
+
+# if defined(XP_MACOSX)
+typedef void* NPRegion;
+# ifndef NP_NO_QUICKDRAW
+typedef RgnHandle NPQDRegion;
+# endif
+typedef CGPathRef NPCGRegion;
+# elif defined(XP_WIN)
+typedef HRGN NPRegion;
+# elif defined(XP_UNIX) && defined(MOZ_X11)
+typedef Region NPRegion;
+# elif defined(XP_SYMBIAN)
+typedef QRegion* NPRegion;
+# else
+typedef void* NPRegion;
+# endif
+
+typedef struct _NPNSString NPNSString;
+typedef struct _NPNSWindow NPNSWindow;
+typedef struct _NPNSMenu NPNSMenu;
+
+# if defined(XP_MACOSX)
+typedef NPNSMenu NPMenu;
+# else
+typedef void* NPMenu;
+# endif
+
+typedef enum {
+ NPCoordinateSpacePlugin = 1,
+ NPCoordinateSpaceWindow,
+ NPCoordinateSpaceFlippedWindow,
+ NPCoordinateSpaceScreen,
+ NPCoordinateSpaceFlippedScreen
+} NPCoordinateSpace;
+
+# if defined(XP_MACOSX)
+
+# ifndef NP_NO_QUICKDRAW
+typedef struct NP_Port {
+ CGrafPtr port;
+ int32_t portx; /* position inside the topmost window */
+ int32_t porty;
+} NP_Port;
+# endif /* NP_NO_QUICKDRAW */
+
+/*
+ * NP_CGContext is the type of the NPWindow's 'window' when the plugin specifies
+ * NPDrawingModelCoreGraphics as its drawing model.
+ */
+
+typedef struct NP_CGContext {
+ CGContextRef context;
+ void* window; /* A WindowRef under the Carbon event model. */
+} NP_CGContext;
+
+/*
+ * NP_GLContext is the type of the NPWindow's 'window' when the plugin specifies
+ * NPDrawingModelOpenGL as its drawing model.
+ */
+
+typedef struct NP_GLContext {
+ CGLContextObj context;
+# ifdef NP_NO_CARBON
+ NPNSWindow* window;
+# else
+ void* window; /* Can be either an NSWindow or a WindowRef depending on the
+ event model */
+# endif
+} NP_GLContext;
+
+typedef enum {
+ NPCocoaEventDrawRect = 1,
+ NPCocoaEventMouseDown,
+ NPCocoaEventMouseUp,
+ NPCocoaEventMouseMoved,
+ NPCocoaEventMouseEntered,
+ NPCocoaEventMouseExited,
+ NPCocoaEventMouseDragged,
+ NPCocoaEventKeyDown,
+ NPCocoaEventKeyUp,
+ NPCocoaEventFlagsChanged,
+ NPCocoaEventFocusChanged,
+ NPCocoaEventWindowFocusChanged,
+ NPCocoaEventScrollWheel,
+ NPCocoaEventTextInput
+} NPCocoaEventType;
+
+typedef struct _NPCocoaEvent {
+ NPCocoaEventType type;
+ uint32_t version;
+ union {
+ struct {
+ uint32_t modifierFlags;
+ double pluginX;
+ double pluginY;
+ int32_t buttonNumber;
+ int32_t clickCount;
+ double deltaX;
+ double deltaY;
+ double deltaZ;
+ } mouse;
+ struct {
+ uint32_t modifierFlags;
+ NPNSString* characters;
+ NPNSString* charactersIgnoringModifiers;
+ NPBool isARepeat;
+ uint16_t keyCode;
+ } key;
+ struct {
+ CGContextRef context;
+ double x;
+ double y;
+ double width;
+ double height;
+ } draw;
+ struct {
+ NPBool hasFocus;
+ } focus;
+ struct {
+ NPNSString* text;
+ } text;
+ } data;
+} NPCocoaEvent;
+
+# ifndef NP_NO_CARBON
+/* Non-standard event types that can be passed to HandleEvent */
+enum NPEventType {
+ NPEventType_GetFocusEvent = (osEvt + 16),
+ NPEventType_LoseFocusEvent,
+ NPEventType_AdjustCursorEvent,
+ NPEventType_MenuCommandEvent,
+ NPEventType_ClippingChangedEvent,
+ NPEventType_ScrollingBeginsEvent = 1000,
+ NPEventType_ScrollingEndsEvent
+};
+# endif /* NP_NO_CARBON */
+
+# endif /* XP_MACOSX */
+
+/*
+ * Values for mode passed to NPP_New:
+ */
+# define NP_EMBED 1
+# define NP_FULL 2
+
+/*
+ * Values for stream type passed to NPP_NewStream:
+ */
+# define NP_NORMAL 1
+# define NP_SEEK 2
+# define NP_ASFILE 3
+# define NP_ASFILEONLY 4
+
+# define NP_MAXREADY (((unsigned)(~0) << 1) >> 1)
+
+/*
+ * Flags for NPP_ClearSiteData.
+ */
+# define NP_CLEAR_ALL 0
+# define NP_CLEAR_CACHE (1 << 0)
+
+# if !defined(__LP64__)
+# if defined(XP_MACOSX)
+# pragma options align = reset
+# endif
+# endif /* __LP64__ */
+
+/*----------------------------------------------------------------------*/
+/* Error and Reason Code definitions */
+/*----------------------------------------------------------------------*/
+
+/*
+ * Values of type NPError:
+ */
+# define NPERR_BASE 0
+# define NPERR_NO_ERROR (NPERR_BASE + 0)
+# define NPERR_GENERIC_ERROR (NPERR_BASE + 1)
+# define NPERR_INVALID_INSTANCE_ERROR (NPERR_BASE + 2)
+# define NPERR_INVALID_FUNCTABLE_ERROR (NPERR_BASE + 3)
+# define NPERR_MODULE_LOAD_FAILED_ERROR (NPERR_BASE + 4)
+# define NPERR_OUT_OF_MEMORY_ERROR (NPERR_BASE + 5)
+# define NPERR_INVALID_PLUGIN_ERROR (NPERR_BASE + 6)
+# define NPERR_INVALID_PLUGIN_DIR_ERROR (NPERR_BASE + 7)
+# define NPERR_INCOMPATIBLE_VERSION_ERROR (NPERR_BASE + 8)
+# define NPERR_INVALID_PARAM (NPERR_BASE + 9)
+# define NPERR_INVALID_URL (NPERR_BASE + 10)
+# define NPERR_FILE_NOT_FOUND (NPERR_BASE + 11)
+# define NPERR_NO_DATA (NPERR_BASE + 12)
+# define NPERR_STREAM_NOT_SEEKABLE (NPERR_BASE + 13)
+# define NPERR_TIME_RANGE_NOT_SUPPORTED (NPERR_BASE + 14)
+# define NPERR_MALFORMED_SITE (NPERR_BASE + 15)
+
+/*
+ * Values of type NPReason:
+ */
+# define NPRES_BASE 0
+# define NPRES_DONE (NPRES_BASE + 0)
+# define NPRES_NETWORK_ERR (NPRES_BASE + 1)
+# define NPRES_USER_BREAK (NPRES_BASE + 2)
+
+/*
+ * Don't use these obsolete error codes any more.
+ */
+# define NP_NOERR NP_NOERR_is_obsolete_use_NPERR_NO_ERROR
+# define NP_EINVAL NP_EINVAL_is_obsolete_use_NPERR_GENERIC_ERROR
+# define NP_EABORT NP_EABORT_is_obsolete_use_NPRES_USER_BREAK
+
+/*
+ * Version feature information
+ */
+# define NPVERS_HAS_STREAMOUTPUT 8
+# define NPVERS_HAS_NOTIFICATION 9
+# define NPVERS_HAS_LIVECONNECT 9
+# define NPVERS_68K_HAS_LIVECONNECT 11
+# define NPVERS_HAS_WINDOWLESS 11
+# define NPVERS_HAS_XPCONNECT_SCRIPTING 13
+# define NPVERS_HAS_NPRUNTIME_SCRIPTING 14
+# define NPVERS_HAS_FORM_VALUES 15
+# define NPVERS_HAS_POPUPS_ENABLED_STATE 16
+# define NPVERS_HAS_RESPONSE_HEADERS 17
+# define NPVERS_HAS_NPOBJECT_ENUM 18
+# define NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL 19
+# define NPVERS_HAS_ALL_NETWORK_STREAMS 20
+# define NPVERS_HAS_URL_AND_AUTH_INFO 21
+# define NPVERS_HAS_PRIVATE_MODE 22
+# define NPVERS_MACOSX_HAS_COCOA_EVENTS 23
+# define NPVERS_HAS_ADVANCED_KEY_HANDLING 25
+# define NPVERS_HAS_URL_REDIRECT_HANDLING 26
+# define NPVERS_HAS_CLEAR_SITE_DATA 27
+
+/*----------------------------------------------------------------------*/
+/* Function Prototypes */
+/*----------------------------------------------------------------------*/
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+/* NPP_* functions are provided by the plugin and called by the navigator. */
+
+# if defined(XP_UNIX)
+const char* NPP_GetMIMEDescription(void);
+# endif
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode,
+ int16_t argc, char* argn[], char* argv[], NPSavedData* saved);
+NPError NPP_Destroy(NPP instance, NPSavedData** save);
+NPError NPP_SetWindow(NPP instance, NPWindow* window);
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16_t* stype);
+NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason);
+int32_t NPP_WriteReady(NPP instance, NPStream* stream);
+int32_t NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len,
+ void* buffer);
+void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
+void NPP_Print(NPP instance, NPPrint* platformPrint);
+int16_t NPP_HandleEvent(NPP instance, void* event);
+void NPP_URLNotify(NPP instance, const char* url, NPReason reason,
+ void* notifyData);
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value);
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value);
+NPBool NPP_GotFocus(NPP instance, NPFocusDirection direction);
+void NPP_LostFocus(NPP instance);
+void NPP_URLRedirectNotify(NPP instance, const char* url, int32_t status,
+ void* notifyData);
+NPError NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge);
+char** NPP_GetSitesWithData(void);
+void NPP_DidComposite(NPP instance);
+
+/* NPN_* functions are provided by the navigator and called by the plugin. */
+void NPN_Version(int* plugin_major, int* plugin_minor, int* netscape_major,
+ int* netscape_minor);
+NPError NPN_GetURLNotify(NPP instance, const char* url, const char* target,
+ void* notifyData);
+NPError NPN_GetURL(NPP instance, const char* url, const char* target);
+NPError NPN_PostURLNotify(NPP instance, const char* url, const char* target,
+ uint32_t len, const char* buf, NPBool file,
+ void* notifyData);
+NPError NPN_PostURL(NPP instance, const char* url, const char* target,
+ uint32_t len, const char* buf, NPBool file);
+NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList);
+NPError NPN_NewStream(NPP instance, NPMIMEType type, const char* target,
+ NPStream** stream);
+int32_t NPN_Write(NPP instance, NPStream* stream, int32_t len, void* buffer);
+NPError NPN_DestroyStream(NPP instance, NPStream* stream, NPReason reason);
+void NPN_Status(NPP instance, const char* message);
+const char* NPN_UserAgent(NPP instance);
+void* NPN_MemAlloc(uint32_t size);
+void NPN_MemFree(void* ptr);
+uint32_t NPN_MemFlush(uint32_t size);
+void NPN_ReloadPlugins(NPBool reloadPages);
+NPError NPN_GetValue(NPP instance, NPNVariable variable, void* value);
+NPError NPN_SetValue(NPP instance, NPPVariable variable, void* value);
+void NPN_InvalidateRect(NPP instance, NPRect* invalidRect);
+void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion);
+void NPN_ForceRedraw(NPP instance);
+void NPN_PushPopupsEnabledState(NPP instance, NPBool enabled);
+void NPN_PopPopupsEnabledState(NPP instance);
+void NPN_PluginThreadAsyncCall(NPP instance, void (*func)(void*),
+ void* userData);
+NPError NPN_GetValueForURL(NPP instance, NPNURLVariable variable,
+ const char* url, char** value, uint32_t* len);
+NPError NPN_SetValueForURL(NPP instance, NPNURLVariable variable,
+ const char* url, const char* value, uint32_t len);
+NPError NPN_GetAuthenticationInfo(NPP instance, const char* protocol,
+ const char* host, int32_t port,
+ const char* scheme, const char* realm,
+ char** username, uint32_t* ulen,
+ char** password, uint32_t* plen);
+uint32_t NPN_ScheduleTimer(NPP instance, uint32_t interval, NPBool repeat,
+ void (*timerFunc)(NPP npp, uint32_t timerID));
+void NPN_UnscheduleTimer(NPP instance, uint32_t timerID);
+NPError NPN_PopUpContextMenu(NPP instance, NPMenu* menu);
+NPBool NPN_ConvertPoint(NPP instance, double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace, double* destX,
+ double* destY, NPCoordinateSpace destSpace);
+NPBool NPN_HandleEvent(NPP instance, void* event, NPBool handled);
+NPBool NPN_UnfocusInstance(NPP instance, NPFocusDirection direction);
+void NPN_URLRedirectResponse(NPP instance, void* notifyData, NPBool allow);
+NPError NPN_InitAsyncSurface(NPP instance, NPSize* size, NPImageFormat format,
+ void* initData, NPAsyncSurface* surface);
+NPError NPN_FinalizeAsyncSurface(NPP instance, NPAsyncSurface* surface);
+void NPN_SetCurrentAsyncSurface(NPP instance, NPAsyncSurface* surface,
+ NPRect* changed);
+
+# ifdef __cplusplus
+} /* end extern "C" */
+# endif
+
+#endif /* RC_INVOKED */
+
+#endif /* npapi_h_ */
diff --git a/dom/plugins/base/npfunctions.h b/dom/plugins/base/npfunctions.h
new file mode 100644
index 0000000000..72d279b25f
--- /dev/null
+++ b/dom/plugins/base/npfunctions.h
@@ -0,0 +1,356 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef npfunctions_h_
+#define npfunctions_h_
+
+#include "npapi.h"
+#include "npruntime.h"
+
+typedef NPError (*NPP_NewProcPtr)(NPMIMEType pluginType, NPP instance,
+ uint16_t mode, int16_t argc, char* argn[],
+ char* argv[], NPSavedData* saved);
+typedef NPError (*NPP_DestroyProcPtr)(NPP instance, NPSavedData** save);
+typedef NPError (*NPP_SetWindowProcPtr)(NPP instance, NPWindow* window);
+typedef NPError (*NPP_NewStreamProcPtr)(NPP instance, NPMIMEType type,
+ NPStream* stream, NPBool seekable,
+ uint16_t* stype);
+typedef NPError (*NPP_DestroyStreamProcPtr)(NPP instance, NPStream* stream,
+ NPReason reason);
+typedef int32_t (*NPP_WriteReadyProcPtr)(NPP instance, NPStream* stream);
+typedef int32_t (*NPP_WriteProcPtr)(NPP instance, NPStream* stream,
+ int32_t offset, int32_t len, void* buffer);
+typedef void (*NPP_StreamAsFileProcPtr)(NPP instance, NPStream* stream,
+ const char* fname);
+typedef void (*NPP_PrintProcPtr)(NPP instance, NPPrint* platformPrint);
+typedef int16_t (*NPP_HandleEventProcPtr)(NPP instance, void* event);
+typedef void (*NPP_URLNotifyProcPtr)(NPP instance, const char* url,
+ NPReason reason, void* notifyData);
+/* Any NPObjects returned to the browser via NPP_GetValue should be retained
+ by the plugin on the way out. The browser is responsible for releasing. */
+typedef NPError (*NPP_GetValueProcPtr)(NPP instance, NPPVariable variable,
+ void* ret_value);
+typedef NPError (*NPP_SetValueProcPtr)(NPP instance, NPNVariable variable,
+ void* value);
+typedef NPBool (*NPP_GotFocusPtr)(NPP instance, NPFocusDirection direction);
+typedef void (*NPP_LostFocusPtr)(NPP instance);
+typedef void (*NPP_URLRedirectNotifyPtr)(NPP instance, const char* url,
+ int32_t status, void* notifyData);
+typedef NPError (*NPP_ClearSiteDataPtr)(const char* site, uint64_t flags,
+ uint64_t maxAge);
+typedef char** (*NPP_GetSitesWithDataPtr)(void);
+typedef void (*NPP_DidCompositePtr)(NPP instance);
+
+typedef NPError (*NPN_GetValueProcPtr)(NPP instance, NPNVariable variable,
+ void* ret_value);
+typedef NPError (*NPN_SetValueProcPtr)(NPP instance, NPPVariable variable,
+ void* value);
+typedef NPError (*NPN_GetURLNotifyProcPtr)(NPP instance, const char* url,
+ const char* window,
+ void* notifyData);
+typedef NPError (*NPN_PostURLNotifyProcPtr)(NPP instance, const char* url,
+ const char* window, uint32_t len,
+ const char* buf, NPBool file,
+ void* notifyData);
+typedef NPError (*NPN_GetURLProcPtr)(NPP instance, const char* url,
+ const char* window);
+typedef NPError (*NPN_PostURLProcPtr)(NPP instance, const char* url,
+ const char* window, uint32_t len,
+ const char* buf, NPBool file);
+typedef NPError (*NPN_RequestReadProcPtr)(NPStream* stream,
+ NPByteRange* rangeList);
+typedef NPError (*NPN_NewStreamProcPtr)(NPP instance, NPMIMEType type,
+ const char* window, NPStream** stream);
+typedef int32_t (*NPN_WriteProcPtr)(NPP instance, NPStream* stream, int32_t len,
+ void* buffer);
+typedef NPError (*NPN_DestroyStreamProcPtr)(NPP instance, NPStream* stream,
+ NPReason reason);
+typedef void (*NPN_StatusProcPtr)(NPP instance, const char* message);
+/* Browser manages the lifetime of the buffer returned by NPN_UserAgent, don't
+ depend on it sticking around and don't free it. */
+typedef const char* (*NPN_UserAgentProcPtr)(NPP instance);
+typedef void* (*NPN_MemAllocProcPtr)(uint32_t size);
+typedef void (*NPN_MemFreeProcPtr)(void* ptr);
+typedef uint32_t (*NPN_MemFlushProcPtr)(uint32_t size);
+typedef void (*NPN_ReloadPluginsProcPtr)(NPBool reloadPages);
+typedef void* (*NPN_GetJavaEnvProcPtr)(void);
+typedef void* (*NPN_GetJavaPeerProcPtr)(NPP instance);
+typedef void (*NPN_InvalidateRectProcPtr)(NPP instance, NPRect* rect);
+typedef void (*NPN_InvalidateRegionProcPtr)(NPP instance, NPRegion region);
+typedef void (*NPN_ForceRedrawProcPtr)(NPP instance);
+typedef NPIdentifier (*NPN_GetStringIdentifierProcPtr)(const NPUTF8* name);
+typedef void (*NPN_GetStringIdentifiersProcPtr)(const NPUTF8** names,
+ int32_t nameCount,
+ NPIdentifier* identifiers);
+typedef NPIdentifier (*NPN_GetIntIdentifierProcPtr)(int32_t intid);
+typedef bool (*NPN_IdentifierIsStringProcPtr)(NPIdentifier identifier);
+typedef NPUTF8* (*NPN_UTF8FromIdentifierProcPtr)(NPIdentifier identifier);
+typedef int32_t (*NPN_IntFromIdentifierProcPtr)(NPIdentifier identifier);
+typedef NPObject* (*NPN_CreateObjectProcPtr)(NPP npp, NPClass* aClass);
+typedef NPObject* (*NPN_RetainObjectProcPtr)(NPObject* obj);
+typedef void (*NPN_ReleaseObjectProcPtr)(NPObject* obj);
+typedef bool (*NPN_InvokeProcPtr)(NPP npp, NPObject* obj,
+ NPIdentifier methodName,
+ const NPVariant* args, uint32_t argCount,
+ NPVariant* result);
+typedef bool (*NPN_InvokeDefaultProcPtr)(NPP npp, NPObject* obj,
+ const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+typedef bool (*NPN_EvaluateProcPtr)(NPP npp, NPObject* obj, NPString* script,
+ NPVariant* result);
+typedef bool (*NPN_GetPropertyProcPtr)(NPP npp, NPObject* obj,
+ NPIdentifier propertyName,
+ NPVariant* result);
+typedef bool (*NPN_SetPropertyProcPtr)(NPP npp, NPObject* obj,
+ NPIdentifier propertyName,
+ const NPVariant* value);
+typedef bool (*NPN_RemovePropertyProcPtr)(NPP npp, NPObject* obj,
+ NPIdentifier propertyName);
+typedef bool (*NPN_HasPropertyProcPtr)(NPP npp, NPObject* obj,
+ NPIdentifier propertyName);
+typedef bool (*NPN_HasMethodProcPtr)(NPP npp, NPObject* obj,
+ NPIdentifier propertyName);
+typedef void (*NPN_ReleaseVariantValueProcPtr)(NPVariant* variant);
+typedef void (*NPN_SetExceptionProcPtr)(NPObject* obj, const NPUTF8* message);
+typedef void (*NPN_PushPopupsEnabledStateProcPtr)(NPP npp, NPBool enabled);
+typedef void (*NPN_PopPopupsEnabledStateProcPtr)(NPP npp);
+typedef bool (*NPN_EnumerateProcPtr)(NPP npp, NPObject* obj,
+ NPIdentifier** identifier,
+ uint32_t* count);
+typedef void (*NPN_PluginThreadAsyncCallProcPtr)(NPP instance,
+ void (*func)(void*),
+ void* userData);
+typedef bool (*NPN_ConstructProcPtr)(NPP npp, NPObject* obj,
+ const NPVariant* args, uint32_t argCount,
+ NPVariant* result);
+typedef NPError (*NPN_GetValueForURLPtr)(NPP npp, NPNURLVariable variable,
+ const char* url, char** value,
+ uint32_t* len);
+typedef NPError (*NPN_SetValueForURLPtr)(NPP npp, NPNURLVariable variable,
+ const char* url, const char* value,
+ uint32_t len);
+typedef NPError (*NPN_GetAuthenticationInfoPtr)(
+ NPP npp, const char* protocol, const char* host, int32_t port,
+ const char* scheme, const char* realm, char** username, uint32_t* ulen,
+ char** password, uint32_t* plen);
+typedef uint32_t (*NPN_ScheduleTimerPtr)(NPP instance, uint32_t interval,
+ NPBool repeat,
+ void (*timerFunc)(NPP npp,
+ uint32_t timerID));
+typedef void (*NPN_UnscheduleTimerPtr)(NPP instance, uint32_t timerID);
+typedef NPError (*NPN_PopUpContextMenuPtr)(NPP instance, NPMenu* menu);
+typedef NPBool (*NPN_ConvertPointPtr)(NPP instance, double sourceX,
+ double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double* destX, double* destY,
+ NPCoordinateSpace destSpace);
+typedef NPBool (*NPN_HandleEventPtr)(NPP instance, void* event, NPBool handled);
+typedef NPBool (*NPN_UnfocusInstancePtr)(NPP instance,
+ NPFocusDirection direction);
+typedef void (*NPN_URLRedirectResponsePtr)(NPP instance, void* notifyData,
+ NPBool allow);
+typedef NPError (*NPN_InitAsyncSurfacePtr)(NPP instance, NPSize* size,
+ NPImageFormat format, void* initData,
+ NPAsyncSurface* surface);
+typedef NPError (*NPN_FinalizeAsyncSurfacePtr)(NPP instance,
+ NPAsyncSurface* surface);
+typedef void (*NPN_SetCurrentAsyncSurfacePtr)(NPP instance,
+ NPAsyncSurface* surface,
+ NPRect* changed);
+
+typedef void (*NPN_DummyPtr)(void);
+
+typedef struct _NPPluginFuncs {
+ uint16_t size;
+ uint16_t version;
+ NPP_NewProcPtr newp;
+ NPP_DestroyProcPtr destroy;
+ NPP_SetWindowProcPtr setwindow;
+ NPP_NewStreamProcPtr newstream;
+ NPP_DestroyStreamProcPtr destroystream;
+ NPP_StreamAsFileProcPtr asfile;
+ NPP_WriteReadyProcPtr writeready;
+ NPP_WriteProcPtr write;
+ NPP_PrintProcPtr print;
+ NPP_HandleEventProcPtr event;
+ NPP_URLNotifyProcPtr urlnotify;
+ void* javaClass;
+ NPP_GetValueProcPtr getvalue;
+ NPP_SetValueProcPtr setvalue;
+ NPP_GotFocusPtr gotfocus;
+ NPP_LostFocusPtr lostfocus;
+ NPP_URLRedirectNotifyPtr urlredirectnotify;
+ NPP_ClearSiteDataPtr clearsitedata;
+ NPP_GetSitesWithDataPtr getsiteswithdata;
+ NPP_DidCompositePtr didComposite;
+} NPPluginFuncs;
+
+typedef struct _NPNetscapeFuncs {
+ uint16_t size;
+ uint16_t version;
+ NPN_GetURLProcPtr geturl;
+ NPN_PostURLProcPtr posturl;
+ NPN_RequestReadProcPtr requestread;
+ NPN_NewStreamProcPtr newstream;
+ NPN_WriteProcPtr write;
+ NPN_DestroyStreamProcPtr destroystream;
+ NPN_StatusProcPtr status;
+ NPN_UserAgentProcPtr uagent;
+ NPN_MemAllocProcPtr memalloc;
+ NPN_MemFreeProcPtr memfree;
+ NPN_MemFlushProcPtr memflush;
+ NPN_ReloadPluginsProcPtr reloadplugins;
+ NPN_GetJavaEnvProcPtr getJavaEnv;
+ NPN_GetJavaPeerProcPtr getJavaPeer;
+ NPN_GetURLNotifyProcPtr geturlnotify;
+ NPN_PostURLNotifyProcPtr posturlnotify;
+ NPN_GetValueProcPtr getvalue;
+ NPN_SetValueProcPtr setvalue;
+ NPN_InvalidateRectProcPtr invalidaterect;
+ NPN_InvalidateRegionProcPtr invalidateregion;
+ NPN_ForceRedrawProcPtr forceredraw;
+ NPN_GetStringIdentifierProcPtr getstringidentifier;
+ NPN_GetStringIdentifiersProcPtr getstringidentifiers;
+ NPN_GetIntIdentifierProcPtr getintidentifier;
+ NPN_IdentifierIsStringProcPtr identifierisstring;
+ NPN_UTF8FromIdentifierProcPtr utf8fromidentifier;
+ NPN_IntFromIdentifierProcPtr intfromidentifier;
+ NPN_CreateObjectProcPtr createobject;
+ NPN_RetainObjectProcPtr retainobject;
+ NPN_ReleaseObjectProcPtr releaseobject;
+ NPN_InvokeProcPtr invoke;
+ NPN_InvokeDefaultProcPtr invokeDefault;
+ NPN_EvaluateProcPtr evaluate;
+ NPN_GetPropertyProcPtr getproperty;
+ NPN_SetPropertyProcPtr setproperty;
+ NPN_RemovePropertyProcPtr removeproperty;
+ NPN_HasPropertyProcPtr hasproperty;
+ NPN_HasMethodProcPtr hasmethod;
+ NPN_ReleaseVariantValueProcPtr releasevariantvalue;
+ NPN_SetExceptionProcPtr setexception;
+ NPN_PushPopupsEnabledStateProcPtr pushpopupsenabledstate;
+ NPN_PopPopupsEnabledStateProcPtr poppopupsenabledstate;
+ NPN_EnumerateProcPtr enumerate;
+ NPN_PluginThreadAsyncCallProcPtr pluginthreadasynccall;
+ NPN_ConstructProcPtr construct;
+ NPN_GetValueForURLPtr getvalueforurl;
+ NPN_SetValueForURLPtr setvalueforurl;
+ NPN_GetAuthenticationInfoPtr getauthenticationinfo;
+ NPN_ScheduleTimerPtr scheduletimer;
+ NPN_UnscheduleTimerPtr unscheduletimer;
+ NPN_PopUpContextMenuPtr popupcontextmenu;
+ NPN_ConvertPointPtr convertpoint;
+ NPN_HandleEventPtr handleevent;
+ NPN_UnfocusInstancePtr unfocusinstance;
+ NPN_URLRedirectResponsePtr urlredirectresponse;
+ NPN_InitAsyncSurfacePtr initasyncsurface;
+ NPN_FinalizeAsyncSurfacePtr finalizeasyncsurface;
+ NPN_SetCurrentAsyncSurfacePtr setcurrentasyncsurface;
+} NPNetscapeFuncs;
+
+#ifdef XP_MACOSX
+/*
+ * Mac OS X version(s) of NP_GetMIMEDescription(const char *)
+ * These can be called to retreive MIME information from the plugin dynamically
+ *
+ * Note: For compatibility with Quicktime, BPSupportedMIMEtypes is another way
+ * to get mime info from the plugin only on OSX and may not be supported
+ * in furture version -- use NP_GetMIMEDescription instead
+ */
+enum { kBPSupportedMIMETypesStructVers_1 = 1 };
+typedef struct _BPSupportedMIMETypes {
+ SInt32 structVersion; /* struct version */
+ Handle typeStrings; /* STR# formated handle, allocated by plug-in */
+ Handle infoStrings; /* STR# formated handle, allocated by plug-in */
+} BPSupportedMIMETypes;
+OSErr BP_GetSupportedMIMETypes(BPSupportedMIMETypes* mimeInfo, UInt32 flags);
+# define NP_GETMIMEDESCRIPTION_NAME "NP_GetMIMEDescription"
+typedef const char* (*NP_GetMIMEDescriptionProcPtr)(void);
+typedef OSErr (*BP_GetSupportedMIMETypesProcPtr)(BPSupportedMIMETypes*, UInt32);
+#endif
+
+#if defined(_WIN32)
+# define OSCALL WINAPI
+#else
+# define OSCALL
+#endif
+
+#if defined(XP_UNIX)
+/* GCC 3.3 and later support the visibility attribute. */
+# if defined(__GNUC__) && \
+ ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))
+# define NP_VISIBILITY_DEFAULT __attribute__((visibility("default")))
+# elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+# define NP_VISIBILITY_DEFAULT __global
+# else
+# define NP_VISIBILITY_DEFAULT
+# endif
+# define NP_EXPORT(__type) NP_VISIBILITY_DEFAULT __type
+#endif
+
+#if defined(_WIN32)
+# ifdef __cplusplus
+extern "C" {
+# endif
+/* plugin meta member functions */
+typedef NPError(OSCALL* NP_GetEntryPointsFunc)(NPPluginFuncs*);
+NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs);
+typedef NPError(OSCALL* NP_InitializeFunc)(NPNetscapeFuncs*);
+NPError OSCALL NP_Initialize(NPNetscapeFuncs* bFuncs);
+typedef NPError(OSCALL* NP_ShutdownFunc)(void);
+NPError OSCALL NP_Shutdown(void);
+typedef const char* (*NP_GetMIMEDescriptionFunc)(void);
+const char* NP_GetMIMEDescription(void);
+# ifdef __cplusplus
+}
+# endif
+#endif
+
+#ifdef XP_UNIX
+# ifdef __cplusplus
+extern "C" {
+# endif
+typedef char* (*NP_GetPluginVersionFunc)(void);
+NP_EXPORT(char*) NP_GetPluginVersion(void);
+typedef const char* (*NP_GetMIMEDescriptionFunc)(void);
+NP_EXPORT(const char*) NP_GetMIMEDescription(void);
+# ifdef XP_MACOSX
+typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*);
+NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs);
+typedef NPError (*NP_GetEntryPointsFunc)(NPPluginFuncs*);
+NP_EXPORT(NPError) NP_GetEntryPoints(NPPluginFuncs* pFuncs);
+# else
+typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*, NPPluginFuncs*);
+NP_EXPORT(NPError)
+NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs);
+# endif
+typedef NPError (*NP_ShutdownFunc)(void);
+NP_EXPORT(NPError) NP_Shutdown(void);
+typedef NPError (*NP_GetValueFunc)(void*, NPPVariable, void*);
+NP_EXPORT(NPError)
+NP_GetValue(void* future, NPPVariable aVariable, void* aValue);
+# ifdef __cplusplus
+}
+# endif
+#endif
+
+// clang-format off
+// See bug 1431030
+#if defined(XP_WIN)
+#define NS_NPAPIPLUGIN_CALLBACK(_type, _name) _type (__stdcall * _name)
+#else
+#define NS_NPAPIPLUGIN_CALLBACK(_type, _name) _type (* _name)
+#endif
+// clang-format on
+
+typedef NS_NPAPIPLUGIN_CALLBACK(NPError,
+ NP_GETENTRYPOINTS)(NPPluginFuncs* pCallbacks);
+typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGININIT)(
+ const NPNetscapeFuncs* pCallbacks);
+typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINUNIXINIT)(
+ const NPNetscapeFuncs* pCallbacks, NPPluginFuncs* fCallbacks);
+typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINSHUTDOWN)(void);
+
+#endif /* npfunctions_h_ */
diff --git a/dom/plugins/base/npruntime.h b/dom/plugins/base/npruntime.h
new file mode 100644
index 0000000000..a2c4fa1597
--- /dev/null
+++ b/dom/plugins/base/npruntime.h
@@ -0,0 +1,382 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (c) 2004, Apple Computer, Inc. and The Mozilla Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of Apple Computer, Inc. ("Apple") or The Mozilla
+ * Foundation ("Mozilla") nor the names of their contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE, MOZILLA AND THEIR CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE, MOZILLA OR
+ * THEIR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _NP_RUNTIME_H_
+#define _NP_RUNTIME_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "nptypes.h"
+
+/*
+ This API is used to facilitate binding code written in C to script
+ objects. The API in this header does not assume the presence of a
+ user agent. That is, it can be used to bind C code to scripting
+ environments outside of the context of a user agent.
+
+ However, the normal use of the this API is in the context of a
+ scripting environment running in a browser or other user agent.
+ In particular it is used to support the extended Netscape
+ script-ability API for plugins (NP-SAP). NP-SAP is an extension
+ of the Netscape plugin API. As such we have adopted the use of
+ the "NP" prefix for this API.
+
+ The following NP{N|P}Variables were added to the Netscape plugin
+ API (in npapi.h):
+
+ NPNVWindowNPObject
+ NPNVPluginElementNPObject
+ NPPVpluginScriptableNPObject
+
+ These variables are exposed through NPN_GetValue() and
+ NPP_GetValue() (respectively) and are used to establish the
+ initial binding between the user agent and native code. The DOM
+ objects in the user agent can be examined and manipulated using
+ the NPN_ functions that operate on NPObjects described in this
+ header.
+
+ To the extent possible the assumptions about the scripting
+ language used by the scripting environment have been minimized.
+*/
+
+#define NP_BEGIN_MACRO do {
+#define NP_END_MACRO \
+ } \
+ while (0)
+
+/*
+ Objects (non-primitive data) passed between 'C' and script is
+ always wrapped in an NPObject. The 'interface' of an NPObject is
+ described by an NPClass.
+*/
+typedef struct NPObject NPObject;
+typedef struct NPClass NPClass;
+
+typedef char NPUTF8;
+typedef struct _NPString {
+ const NPUTF8* UTF8Characters;
+ uint32_t UTF8Length;
+} NPString;
+
+typedef enum {
+ NPVariantType_Void,
+ NPVariantType_Null,
+ NPVariantType_Bool,
+ NPVariantType_Int32,
+ NPVariantType_Double,
+ NPVariantType_String,
+ NPVariantType_Object
+} NPVariantType;
+
+typedef struct _NPVariant {
+ NPVariantType type;
+ union {
+ bool boolValue;
+ int32_t intValue;
+ double doubleValue;
+ NPString stringValue;
+ NPObject* objectValue;
+ } value;
+} NPVariant;
+
+/*
+ NPN_ReleaseVariantValue is called on all 'out' parameters
+ references. Specifically it is to be called on variants that own
+ their value, as is the case with all non-const NPVariant*
+ arguments after a successful call to any methods (except this one)
+ in this API.
+
+ After calling NPN_ReleaseVariantValue, the type of the variant
+ will be NPVariantType_Void.
+*/
+void NPN_ReleaseVariantValue(NPVariant* variant);
+
+#define NPVARIANT_IS_VOID(_v) ((_v).type == NPVariantType_Void)
+#define NPVARIANT_IS_NULL(_v) ((_v).type == NPVariantType_Null)
+#define NPVARIANT_IS_BOOLEAN(_v) ((_v).type == NPVariantType_Bool)
+#define NPVARIANT_IS_INT32(_v) ((_v).type == NPVariantType_Int32)
+#define NPVARIANT_IS_DOUBLE(_v) ((_v).type == NPVariantType_Double)
+#define NPVARIANT_IS_STRING(_v) ((_v).type == NPVariantType_String)
+#define NPVARIANT_IS_OBJECT(_v) ((_v).type == NPVariantType_Object)
+
+#define NPVARIANT_TO_BOOLEAN(_v) ((_v).value.boolValue)
+#define NPVARIANT_TO_INT32(_v) ((_v).value.intValue)
+#define NPVARIANT_TO_DOUBLE(_v) ((_v).value.doubleValue)
+#define NPVARIANT_TO_STRING(_v) ((_v).value.stringValue)
+#define NPVARIANT_TO_OBJECT(_v) ((_v).value.objectValue)
+
+#define VOID_TO_NPVARIANT(_v) \
+ NP_BEGIN_MACRO(_v).type = NPVariantType_Void; \
+ (_v).value.objectValue = NULL; \
+ NP_END_MACRO
+
+#define NULL_TO_NPVARIANT(_v) \
+ NP_BEGIN_MACRO(_v).type = NPVariantType_Null; \
+ (_v).value.objectValue = NULL; \
+ NP_END_MACRO
+
+#define BOOLEAN_TO_NPVARIANT(_val, _v) \
+ NP_BEGIN_MACRO(_v).type = NPVariantType_Bool; \
+ (_v).value.boolValue = !!(_val); \
+ NP_END_MACRO
+
+#define INT32_TO_NPVARIANT(_val, _v) \
+ NP_BEGIN_MACRO(_v).type = NPVariantType_Int32; \
+ (_v).value.intValue = _val; \
+ NP_END_MACRO
+
+#define DOUBLE_TO_NPVARIANT(_val, _v) \
+ NP_BEGIN_MACRO(_v).type = NPVariantType_Double; \
+ (_v).value.doubleValue = _val; \
+ NP_END_MACRO
+
+#define STRINGZ_TO_NPVARIANT(_val, _v) \
+ NP_BEGIN_MACRO(_v).type = NPVariantType_String; \
+ NPString str = {_val, (uint32_t)(strlen(_val))}; \
+ (_v).value.stringValue = str; \
+ NP_END_MACRO
+
+#define STRINGN_TO_NPVARIANT(_val, _len, _v) \
+ NP_BEGIN_MACRO(_v).type = NPVariantType_String; \
+ NPString str = {_val, (uint32_t)(_len)}; \
+ (_v).value.stringValue = str; \
+ NP_END_MACRO
+
+#define OBJECT_TO_NPVARIANT(_val, _v) \
+ NP_BEGIN_MACRO(_v).type = NPVariantType_Object; \
+ (_v).value.objectValue = _val; \
+ NP_END_MACRO
+
+/*
+ Type mappings (JavaScript types have been used for illustration
+ purposes):
+
+ JavaScript to C (NPVariant with type:)
+ undefined NPVariantType_Void
+ null NPVariantType_Null
+ Boolean NPVariantType_Bool
+ Number NPVariantType_Double or NPVariantType_Int32
+ String NPVariantType_String
+ Object NPVariantType_Object
+
+ C (NPVariant with type:) to JavaScript
+ NPVariantType_Void undefined
+ NPVariantType_Null null
+ NPVariantType_Bool Boolean
+ NPVariantType_Int32 Number
+ NPVariantType_Double Number
+ NPVariantType_String String
+ NPVariantType_Object Object
+*/
+
+typedef void* NPIdentifier;
+
+/*
+ NPObjects have methods and properties. Methods and properties are
+ identified with NPIdentifiers. These identifiers may be reflected
+ in script. NPIdentifiers can be either strings or integers, IOW,
+ methods and properties can be identified by either strings or
+ integers (i.e. foo["bar"] vs foo[1]). NPIdentifiers can be
+ compared using ==. In case of any errors, the requested
+ NPIdentifier(s) will be NULL. NPIdentifier lifetime is controlled
+ by the browser. Plugins do not need to worry about memory management
+ with regards to NPIdentifiers.
+*/
+NPIdentifier NPN_GetStringIdentifier(const NPUTF8* name);
+void NPN_GetStringIdentifiers(const NPUTF8** names, int32_t nameCount,
+ NPIdentifier* identifiers);
+NPIdentifier NPN_GetIntIdentifier(int32_t intid);
+bool NPN_IdentifierIsString(NPIdentifier identifier);
+
+/*
+ The NPUTF8 returned from NPN_UTF8FromIdentifier SHOULD be freed.
+*/
+NPUTF8* NPN_UTF8FromIdentifier(NPIdentifier identifier);
+
+/*
+ Get the integer represented by identifier. If identifier is not an
+ integer identifier, the behaviour is undefined.
+*/
+int32_t NPN_IntFromIdentifier(NPIdentifier identifier);
+
+/*
+ NPObject behavior is implemented using the following set of
+ callback functions.
+
+ The NPVariant *result argument of these functions (where
+ applicable) should be released using NPN_ReleaseVariantValue().
+*/
+typedef NPObject* (*NPAllocateFunctionPtr)(NPP npp, NPClass* aClass);
+typedef void (*NPDeallocateFunctionPtr)(NPObject* npobj);
+typedef void (*NPInvalidateFunctionPtr)(NPObject* npobj);
+typedef bool (*NPHasMethodFunctionPtr)(NPObject* npobj, NPIdentifier name);
+typedef bool (*NPInvokeFunctionPtr)(NPObject* npobj, NPIdentifier name,
+ const NPVariant* args, uint32_t argCount,
+ NPVariant* result);
+typedef bool (*NPInvokeDefaultFunctionPtr)(NPObject* npobj,
+ const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result);
+typedef bool (*NPHasPropertyFunctionPtr)(NPObject* npobj, NPIdentifier name);
+typedef bool (*NPGetPropertyFunctionPtr)(NPObject* npobj, NPIdentifier name,
+ NPVariant* result);
+typedef bool (*NPSetPropertyFunctionPtr)(NPObject* npobj, NPIdentifier name,
+ const NPVariant* value);
+typedef bool (*NPRemovePropertyFunctionPtr)(NPObject* npobj, NPIdentifier name);
+typedef bool (*NPEnumerationFunctionPtr)(NPObject* npobj, NPIdentifier** value,
+ uint32_t* count);
+typedef bool (*NPConstructFunctionPtr)(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+
+/*
+ NPObjects returned by create, retain, invoke, and getProperty pass
+ a reference count to the caller. That is, the callee adds a
+ reference count which passes to the caller. It is the caller's
+ responsibility to release the returned object.
+
+ NPInvokeFunctionPtr function may return 0 to indicate a void
+ result.
+
+ NPInvalidateFunctionPtr is called by the scripting environment
+ when the native code is shutdown. Any attempt to message a
+ NPObject instance after the invalidate callback has been
+ called will result in undefined behavior, even if the native code
+ is still retaining those NPObject instances. (The runtime
+ will typically return immediately, with 0 or NULL, from an
+ attempt to dispatch to a NPObject, but this behavior should not
+ be depended upon.)
+
+ The NPEnumerationFunctionPtr function may pass an array of
+ NPIdentifiers back to the caller. The callee allocs the memory of
+ the array using NPN_MemAlloc(), and it's the caller's responsibility
+ to release it using NPN_MemFree().
+*/
+struct NPClass {
+ uint32_t structVersion;
+ NPAllocateFunctionPtr allocate;
+ NPDeallocateFunctionPtr deallocate;
+ NPInvalidateFunctionPtr invalidate;
+ NPHasMethodFunctionPtr hasMethod;
+ NPInvokeFunctionPtr invoke;
+ NPInvokeDefaultFunctionPtr invokeDefault;
+ NPHasPropertyFunctionPtr hasProperty;
+ NPGetPropertyFunctionPtr getProperty;
+ NPSetPropertyFunctionPtr setProperty;
+ NPRemovePropertyFunctionPtr removeProperty;
+ NPEnumerationFunctionPtr enumerate;
+ NPConstructFunctionPtr construct;
+};
+
+#define NP_CLASS_STRUCT_VERSION 3
+
+#define NP_CLASS_STRUCT_VERSION_ENUM 2
+#define NP_CLASS_STRUCT_VERSION_CTOR 3
+
+#define NP_CLASS_STRUCT_VERSION_HAS_ENUM(npclass) \
+ ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM)
+
+#define NP_CLASS_STRUCT_VERSION_HAS_CTOR(npclass) \
+ ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR)
+
+struct NPObject {
+ NPClass* _class;
+ uint32_t referenceCount;
+ /*
+ * Additional space may be allocated here by types of NPObjects
+ */
+};
+
+/*
+ If the class has an allocate function, NPN_CreateObject invokes
+ that function, otherwise a NPObject is allocated and
+ returned. This method will initialize the referenceCount member of
+ the NPObject to 1.
+*/
+NPObject* NPN_CreateObject(NPP npp, NPClass* aClass);
+
+/*
+ Increment the NPObject's reference count.
+*/
+NPObject* NPN_RetainObject(NPObject* npobj);
+
+/*
+ Decremented the NPObject's reference count. If the reference
+ count goes to zero, the class's destroy function is invoke if
+ specified, otherwise the object is freed directly.
+*/
+void NPN_ReleaseObject(NPObject* npobj);
+
+/*
+ Functions to access script objects represented by NPObject.
+
+ Calls to script objects are synchronous. If a function returns a
+ value, it will be supplied via the result NPVariant
+ argument. Successful calls will return true, false will be
+ returned in case of an error.
+
+ Calls made from plugin code to script must be made from the thread
+ on which the plugin was initialized.
+*/
+
+bool NPN_Invoke(NPP npp, NPObject* npobj, NPIdentifier methodName,
+ const NPVariant* args, uint32_t argCount, NPVariant* result);
+bool NPN_InvokeDefault(NPP npp, NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+bool NPN_Evaluate(NPP npp, NPObject* npobj, NPString* script,
+ NPVariant* result);
+bool NPN_GetProperty(NPP npp, NPObject* npobj, NPIdentifier propertyName,
+ NPVariant* result);
+bool NPN_SetProperty(NPP npp, NPObject* npobj, NPIdentifier propertyName,
+ const NPVariant* value);
+bool NPN_RemoveProperty(NPP npp, NPObject* npobj, NPIdentifier propertyName);
+bool NPN_HasProperty(NPP npp, NPObject* npobj, NPIdentifier propertyName);
+bool NPN_HasMethod(NPP npp, NPObject* npobj, NPIdentifier methodName);
+bool NPN_Enumerate(NPP npp, NPObject* npobj, NPIdentifier** identifier,
+ uint32_t* count);
+bool NPN_Construct(NPP npp, NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+
+/*
+ NPN_SetException may be called to trigger a script exception upon
+ return from entry points into NPObjects. Typical usage:
+
+ NPN_SetException (npobj, message);
+*/
+void NPN_SetException(NPObject* npobj, const NPUTF8* message);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/dom/plugins/base/nptypes.h b/dom/plugins/base/nptypes.h
new file mode 100644
index 0000000000..02e32ed99c
--- /dev/null
+++ b/dom/plugins/base/nptypes.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nptypes_h_
+#define nptypes_h_
+
+/*
+ * Header file for ensuring that C99 types ([u]int32_t, [u]int64_t and bool) and
+ * true/false macros are available.
+ */
+
+#if defined(WIN32)
+/*
+ * Win32 and OS/2 don't know C99, so define [u]int_16/32/64 here. The bool
+ * is predefined tho, both in C and C++.
+ */
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef long long int64_t;
+typedef unsigned long long uint64_t;
+#elif defined(_AIX) || defined(__sun) || defined(__osf__) || defined(IRIX) || \
+ defined(HPUX)
+/*
+ * AIX and SunOS ship a inttypes.h header that defines [u]int32_t,
+ * but not bool for C.
+ */
+# include <inttypes.h>
+
+# ifndef __cplusplus
+typedef int bool;
+# define true 1
+# define false 0
+# endif
+#elif defined(bsdi) || defined(FREEBSD) || defined(OPENBSD)
+/*
+ * BSD/OS, FreeBSD, and OpenBSD ship sys/types.h that define int32_t and
+ * u_int32_t.
+ */
+# include <sys/types.h>
+
+/*
+ * BSD/OS ships no header that defines uint32_t, nor bool (for C)
+ */
+# if defined(bsdi)
+typedef u_int32_t uint32_t;
+typedef u_int64_t uint64_t;
+
+# if !defined(__cplusplus)
+typedef int bool;
+# define true 1
+# define false 0
+# endif
+# else
+/*
+ * FreeBSD and OpenBSD define uint32_t and bool.
+ */
+# include <inttypes.h>
+# include <stdbool.h>
+# endif
+#elif defined(BEOS)
+# include <inttypes.h>
+#else
+/*
+ * For those that ship a standard C99 stdint.h header file, include
+ * it. Can't do the same for stdbool.h tho, since some systems ship
+ * with a stdbool.h file that doesn't compile!
+ */
+# include <stdint.h>
+
+# ifndef __cplusplus
+# if !defined(__GNUC__) || (__GNUC__ > 2 || __GNUC_MINOR__ > 95)
+# include <stdbool.h>
+# else
+/*
+ * GCC 2.91 can't deal with a typedef for bool, but a #define
+ * works.
+ */
+# define bool int
+# define true 1
+# define false 0
+# endif
+# endif
+#endif
+
+#endif /* nptypes_h_ */
diff --git a/dom/plugins/base/nsIHTTPHeaderListener.idl b/dom/plugins/base/nsIHTTPHeaderListener.idl
new file mode 100644
index 0000000000..4a2f34a798
--- /dev/null
+++ b/dom/plugins/base/nsIHTTPHeaderListener.idl
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsISupports.idl"
+
+/**
+ * The nsIHTTPHeaderListener interface allows plugin authors to
+ * access HTTP Response headers after issuing an
+ * nsIPluginHost::{GetURL,PostURL}() call. <P>
+ */
+
+[scriptable, uuid(ea51e0b8-871c-4b85-92da-6f400394c5ec)]
+interface nsIHTTPHeaderListener : nsISupports
+{
+ /**
+ * Called for each HTTP Response header.
+ * NOTE: You must copy the values of the params.
+ */
+ void newResponseHeader(in string headerName, in string headerValue);
+
+ /**
+ * Called once for the HTTP Response status line.
+ * Value does NOT include a terminating newline.
+ * NOTE: You must copy this value.
+ */
+ void statusLine(in string line);
+};
diff --git a/dom/plugins/base/nsIPluginDocument.idl b/dom/plugins/base/nsIPluginDocument.idl
new file mode 100644
index 0000000000..f91aac621d
--- /dev/null
+++ b/dom/plugins/base/nsIPluginDocument.idl
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+#include "nsIStreamListener.idl"
+
+[uuid(a93a0f0f-24f0-4206-a21b-56a43dcbdd88)]
+interface nsIPluginDocument : nsISupports
+{
+ /**
+ * Causes the plugin to print in full-page mode
+ */
+ void print();
+};
diff --git a/dom/plugins/base/nsIPluginHost.idl b/dom/plugins/base/nsIPluginHost.idl
new file mode 100644
index 0000000000..a459a006c2
--- /dev/null
+++ b/dom/plugins/base/nsIPluginHost.idl
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nspluginroot.idl"
+#include "nsISupports.idl"
+#include "nsIPluginTag.idl"
+
+%{C++
+#define MOZ_PLUGIN_HOST_CONTRACTID \
+ "@mozilla.org/plugin/host;1"
+%}
+
+[scriptable, function, uuid(9c311778-7c2c-4ad8-b439-b8a2786a20dd)]
+interface nsIClearSiteDataCallback : nsISupports
+{
+ /**
+ * callback with the result from a call to clearSiteData
+ */
+ void callback(in nsresult rv);
+};
+
+[scriptable, uuid(f938f5ba-7093-42cd-a559-af8039d99204)]
+interface nsIPluginHost : nsISupports
+{
+ /**
+ * Causes the plugins directory to be searched again for new plugin
+ * libraries.
+ */
+ void reloadPlugins();
+
+ Array<nsIPluginTag> getPluginTags();
+
+ /*
+ * Flags for use with clearSiteData.
+ *
+ * FLAG_CLEAR_ALL: clear all data associated with a site.
+ * FLAG_CLEAR_CACHE: clear cached data that can be retrieved again without
+ * loss of functionality. To be used out of concern for
+ * space and not necessarily privacy.
+ */
+ const uint32_t FLAG_CLEAR_ALL = 0;
+ const uint32_t FLAG_CLEAR_CACHE = 1;
+
+ /*
+ * For use with Get*ForType functions
+ */
+ const uint32_t EXCLUDE_NONE = 0;
+ const uint32_t EXCLUDE_DISABLED = 1 << 0;
+ const uint32_t EXCLUDE_FAKE = 1 << 1;
+
+ /*
+ * Clear site data for a given plugin.
+ *
+ * @param plugin: the plugin to clear data for, such as one returned by
+ * nsIPluginHost.getPluginTags.
+ * @param domain: the domain to clear data for. If this argument is null,
+ * clear data for all domains. Otherwise, it must be a domain
+ * only (not a complete URI or IRI). The base domain for the
+ * given site will be determined; any data for the base domain
+ * or its subdomains will be cleared.
+ * @param flags: a flag value defined above.
+ * @param maxAge: the maximum age in seconds of data to clear, inclusive. If
+ * maxAge is 0, no data is cleared; if it is -1, all data is
+ * cleared.
+ *
+ * @throws NS_ERROR_INVALID_ARG if the domain argument is malformed.
+ * @throws NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED if maxAge is a value other
+ * than -1 and the plugin does not support clearing by timerange in
+ * general or for that particular site and/or flag combination.
+ */
+ void clearSiteData(in nsIPluginTag plugin, in AUTF8String domain,
+ in uint64_t flags, in int64_t maxAge,
+ in nsIClearSiteDataCallback callback);
+
+ /*
+ * Determine if a plugin has stored data for a given site.
+ *
+ * @param plugin: the plugin to query, such as one returned by
+ * nsIPluginHost.getPluginTags.
+ * @param domain: the domain to test. If this argument is null, test if data
+ * is stored for any site. The base domain for the given domain
+ * will be determined; if any data for the base domain or its
+ * subdomains is found, return true.
+ */
+ boolean siteHasData(in nsIPluginTag plugin, in AUTF8String domain);
+
+ /**
+ * Get the "permission string" for the plugin. This is a string that can be
+ * passed to the permission manager to see whether the plugin is allowed to
+ * run, for example. This will typically be based on the plugin's "nice name"
+ * and its blocklist state.
+ *
+ * @mimeType The MIME type we're interested in.
+ * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
+ */
+ ACString getPermissionStringForType(in AUTF8String mimeType,
+ [optional] in uint32_t excludeFlags);
+
+ /**
+ * Get the "permission string" for the plugin. This is a string that can be
+ * passed to the permission manager to see whether the plugin is allowed to
+ * run, for example. This will typically be based on the plugin's "nice name"
+ * and its blocklist state.
+ *
+ * @tag The tage we're interested in
+ * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
+ */
+ ACString getPermissionStringForTag(in nsIPluginTag tag,
+ [optional] in uint32_t excludeFlags);
+
+ /**
+ * Get the nsIPluginTag for this MIME type. This method works with both
+ * enabled and disabled/blocklisted plugins, but an enabled plugin will
+ * always be returned if available.
+ *
+ * A fake plugin tag, if one exists and is available, will be returned in
+ * preference to NPAPI plugin tags unless excluded by the excludeFlags.
+ *
+ * @mimeType The MIME type we're interested in.
+ * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
+ *
+ * @throws NS_ERROR_NOT_AVAILABLE if no plugin is available for this MIME
+ * type.
+ */
+ nsIPluginTag getPluginTagForType(in AUTF8String mimeType,
+ [optional] in uint32_t excludeFlags);
+
+ /**
+ * Get the nsIPluginTag enabled state for this MIME type. See
+ * nsIPluginTag.enabledState.
+ *
+ * @mimeType The MIME type we're interested in.
+ * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
+ */
+ unsigned long getStateForType(in AUTF8String mimeType,
+ [optional] in uint32_t excludeFlags);
+
+ /**
+ * Get the blocklist state for a MIME type. See nsIPluginTag.blocklistState.
+ *
+ * @mimeType The MIME type we're interested in.
+ * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE.
+ */
+ uint32_t getBlocklistStateForType(in AUTF8String aMimeType,
+ [optional] in uint32_t excludeFlags);
+
+ /**
+ * Create a fake plugin tag, register it, and return it. The argument is a
+ * FakePluginTagInit dictionary. See documentation in
+ * FakePluginTagInit.webidl for what it should look like. Will throw
+ * NS_ERROR_UNEXPECTED if there is already a fake plugin registered with the
+ * given handler URI.
+ */
+ [implicit_jscontext]
+ nsIFakePluginTag registerFakePlugin(in jsval initDictionary);
+
+ /**
+ * Create a fake plugin tag without registering it.
+ *
+ * Only for use in tests.
+ */
+ [implicit_jscontext]
+ nsIFakePluginTag createFakePlugin(in jsval initDictionary);
+
+ /**
+ * Get a reference to an existing fake plugin tag for the given MIME type, if
+ * any. Can return null.
+ */
+ nsIFakePluginTag getFakePlugin(in AUTF8String mimeType);
+
+ /**
+ * Unregister a fake plugin. The argument can be the .handlerURI.spec of an
+ * existing nsIFakePluginTag, or just a known handler URI string that was
+ * passed in the FakePluginTagInit when registering.
+ */
+ void unregisterFakePlugin(in AUTF8String handlerURI);
+};
diff --git a/dom/plugins/base/nsIPluginInputStream.idl b/dom/plugins/base/nsIPluginInputStream.idl
new file mode 100644
index 0000000000..8868384417
--- /dev/null
+++ b/dom/plugins/base/nsIPluginInputStream.idl
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIInputStream.idl"
+#include "nspluginroot.idl"
+
+/**
+ * The nsIPluginInputStream interface ...
+ */
+[uuid(af160530-542a-11d2-8164-006008119d7a)]
+interface nsIPluginInputStream : nsIInputStream {
+ /**
+ * Corresponds to NPStream's lastmodified field.)
+ */
+ void getLastModified(out unsigned long aResult);
+
+ void requestRead(out NPByteRange aRangeList);
+};
diff --git a/dom/plugins/base/nsIPluginInstanceOwner.idl b/dom/plugins/base/nsIPluginInstanceOwner.idl
new file mode 100644
index 0000000000..0aa23d2173
--- /dev/null
+++ b/dom/plugins/base/nsIPluginInstanceOwner.idl
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsISupports.idl"
+#include "nspluginroot.idl"
+#include "nsIInputStream.idl"
+
+webidl Document;
+
+%{C++
+#include "npapi.h"
+#include "mozilla/EventForwards.h"
+class nsNPAPIPluginInstance;
+
+enum nsPluginTagType {
+ nsPluginTagType_Unknown,
+ nsPluginTagType_Embed,
+ nsPluginTagType_Object
+};
+%}
+
+[ptr] native nsNPAPIPluginInstancePtr(nsNPAPIPluginInstance);
+
+// Do not make this interface scriptable, because the virtual functions in C++
+// blocks will make script call the wrong functions.
+[uuid(7d65452e-c167-4cba-a0e3-ddc61bdde8c3)]
+interface nsIPluginInstanceOwner : nsISupports
+{
+ /**
+ * Let the owner know what its instance is
+ */
+ void setInstance(in nsNPAPIPluginInstancePtr aInstance);
+
+ /**
+ * Get the instance associated with this owner.
+ */
+ [notxpcom, nostdcall] nsNPAPIPluginInstancePtr getInstance();
+
+ /**
+ * Get a handle to the window structure of the owner.
+ * This pointer cannot be made persistent by the caller.
+ */
+ void getWindow(in NPWindowStarRef aWindow);
+
+ /**
+ * Get the display mode for the plugin instance.
+ */
+ readonly attribute int32_t mode;
+
+ /**
+ * Create a place for the plugin to live in the owner's
+ * environment. this may or may not create a window
+ * depending on the windowless state of the plugin instance.
+ */
+ void createWidget();
+
+%{C++
+ /**
+ * Called when there is a valid target so that the proper
+ * frame can be updated with new content. will not be called
+ * with nullptr aTarget.
+ */
+ NS_IMETHOD
+ GetURL(const char *aURL, const char *aTarget,
+ nsIInputStream *aPostStream,
+ void *aHeadersData, uint32_t aHeadersDataLen,
+ bool aDoCheckLoadURIChecks) = 0;
+%}
+
+ /**
+ * Get the associated document.
+ */
+ readonly attribute Document document;
+
+ /**
+ * Invalidate the rectangle
+ */
+ void invalidateRect(in NPRectPtr aRect);
+
+ /**
+ * Invalidate the region
+ */
+ void invalidateRegion(in NPRegion aRegion);
+
+ /**
+ * Have the plugin recomposited.
+ */
+ void redrawPlugin();
+
+ /**
+ * Get NetscapeWindow, corresponds to NPNVnetscapeWindow
+ */
+ void getNetscapeWindow(in voidPtr aValue);
+
+ /**
+ * Convert between plugin, window, and screen coordinate spaces.
+ */
+%{C++
+ virtual NPBool ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
+ double *destX, double *destY, NPCoordinateSpace destSpace) = 0;
+ virtual NPError InitAsyncSurface(NPSize *size, NPImageFormat format,
+ void *initData, NPAsyncSurface *surface) = 0;
+ virtual NPError FinalizeAsyncSurface(NPAsyncSurface *surface) = 0;
+ virtual void SetCurrentAsyncSurface(NPAsyncSurface *surface, NPRect *changed) = 0;
+%}
+
+ void setEventModel(in int32_t eventModel);
+
+ /**
+ * Call NPP_SetWindow on the plugin.
+ */
+ void callSetWindow();
+
+ /**
+ * Get the contents scale factor for the screen the plugin is
+ * drawn on.
+ */
+ double getContentsScaleFactor();
+};
diff --git a/dom/plugins/base/nsIPluginTag.idl b/dom/plugins/base/nsIPluginTag.idl
new file mode 100644
index 0000000000..68f8da1dc8
--- /dev/null
+++ b/dom/plugins/base/nsIPluginTag.idl
@@ -0,0 +1,97 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+interface nsIURI;
+
+[builtinclass, scriptable, uuid(5daa99d5-265a-4397-b429-c943803e2619)]
+interface nsIPluginTag : nsISupports
+{
+ // enabledState is stored as one of the following as an integer in prefs,
+ // so if new states are added, they must not renumber the existing states.
+ const unsigned long STATE_DISABLED = 0;
+ const unsigned long STATE_CLICKTOPLAY = 1;
+ const unsigned long STATE_ENABLED = 2;
+
+ readonly attribute AUTF8String description;
+ readonly attribute AUTF8String filename;
+ readonly attribute AUTF8String fullpath;
+ readonly attribute AUTF8String version;
+ readonly attribute AUTF8String name;
+
+ // The 'nice' name of this plugin, e.g. 'flash' 'java'
+ readonly attribute AUTF8String niceName;
+
+ /**
+ * true only if this plugin is "hardblocked" and cannot be enabled.
+ */
+ // FIXME-jsplugins QI to fakePluginTag possible
+ // FIXME-jsplugins implement missing + tests (whatever that means)
+ [infallible]
+ readonly attribute boolean blocklisted;
+
+ /**
+ * true if the state is non-default and locked, false otherwise.
+ */
+ [infallible]
+ readonly attribute boolean isEnabledStateLocked;
+
+ // If this plugin is capable of being used (not disabled, blocklisted, etc)
+ [infallible]
+ readonly attribute boolean active;
+
+ // Get a specific nsIBlocklistService::STATE_*
+ [infallible]
+ readonly attribute unsigned long blocklistState;
+
+ [infallible]
+ readonly attribute boolean disabled;
+ [infallible]
+ readonly attribute boolean clicktoplay;
+ [infallible]
+ readonly attribute boolean loaded;
+ // See the STATE_* values above.
+ attribute unsigned long enabledState;
+
+ readonly attribute PRTime lastModifiedTime;
+
+ readonly attribute boolean isFlashPlugin;
+
+ Array<AUTF8String> getMimeTypes();
+ Array<AUTF8String> getMimeDescriptions();
+ Array<AUTF8String> getExtensions();
+
+ /**
+ * An id for this plugin. 0 is a valid id.
+ */
+ readonly attribute unsigned long id;
+};
+
+/**
+ * An interface representing a "fake" plugin: one implemented in JavaScript, not
+ * as a NPAPI plug-in. See nsIPluginHost.registerFakePlugin and the
+ * documentation for the FakePluginTagInit dictionary.
+ */
+[builtinclass, scriptable, uuid(6d22c968-226d-4156-b230-da6ad6bbf6e8)]
+interface nsIFakePluginTag : nsIPluginTag
+{
+ /**
+ * The URI that should be loaded into the tag (as a frame) to handle the
+ * plugin. Note that the original data/src value for the plugin is not loaded
+ * and will need to be requested by the handler via XHR or similar if desired.
+ */
+ readonly attribute nsIURI handlerURI;
+
+ /**
+ * Optional script to run in a sandbox when instantiating a plugin. If this
+ * value is an empty string then no such script will be run.
+ * The script runs in a sandbox with system principal in the process that
+ * contains the element that instantiates the plugin (ie the EMBED or OBJECT
+ * element). The sandbox global has a 'pluginElement' property that the script
+ * can use to access the element that instantiates the plugin.
+ */
+ readonly attribute AString sandboxScript;
+};
diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp
new file mode 100644
index 0000000000..f4d38002fd
--- /dev/null
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -0,0 +1,2183 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "base/basictypes.h"
+
+#include "jsfriendapi.h"
+
+#include "GeckoProfiler.h"
+#include "mozilla/UniquePtr.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsJSNPRuntime.h"
+#include "nsNPAPIPlugin.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsIGlobalObject.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIScriptContext.h"
+#include "nsDOMJSUtils.h"
+#include "nsJSUtils.h"
+#include "mozilla/dom/Document.h"
+#include "xpcpublic.h"
+#include "nsIContent.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsWrapperCacheInlines.h"
+#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
+#include "js/GCHashTable.h"
+#include "js/Object.h" // JS::GetClass, JS::GetCompartment, JS::GetPrivate, JS::SetPrivate
+#include "js/Symbol.h"
+#include "js/TracingAPI.h"
+#include "js/Wrapper.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/dom/ScriptSettings.h"
+
+#define NPRUNTIME_JSCLASS_NAME "NPObject JS wrapper class"
+
+using namespace mozilla::plugins::parent;
+using namespace mozilla;
+
+#include "mozilla/plugins/PluginScriptableObjectParent.h"
+using mozilla::plugins::ParentNPObject;
+using mozilla::plugins::PluginScriptableObjectParent;
+
+struct JSObjWrapperHasher {
+ typedef nsJSObjWrapperKey Key;
+ typedef Key Lookup;
+
+ static uint32_t hash(const Lookup& l) {
+ return js::MovableCellHasher<JS::Heap<JSObject*>>::hash(l.mJSObj) ^
+ HashGeneric(l.mNpp);
+ }
+
+ static bool match(const Key& k, const Lookup& l) {
+ return js::MovableCellHasher<JS::Heap<JSObject*>>::match(k.mJSObj,
+ l.mJSObj) &&
+ k.mNpp == l.mNpp;
+ }
+};
+
+namespace JS {
+template <>
+struct GCPolicy<nsJSObjWrapper*> {
+ static void trace(JSTracer* trc, nsJSObjWrapper** wrapper, const char* name) {
+ MOZ_ASSERT(wrapper);
+ MOZ_ASSERT(*wrapper);
+ (*wrapper)->trace(trc);
+ }
+
+ static bool isValid(const nsJSObjWrapper*& wrapper) { return true; }
+};
+} // namespace JS
+
+class NPObjWrapperHashEntry : public PLDHashEntryHdr {
+ public:
+ NPObject* mNPObj; // Must be the first member for the PLDHash stubs to work
+ JS::TenuredHeap<JSObject*> mJSObj;
+ NPP mNpp;
+};
+
+// Hash of JSObject wrappers that wraps JSObjects as NPObjects. There
+// will be one wrapper per JSObject per plugin instance, i.e. if two
+// plugins access the JSObject x, two wrappers for x will be
+// created. This is needed to be able to properly drop the wrappers
+// when a plugin is torn down in case there's a leak in the plugin (we
+// don't want to leak the world just because a plugin leaks an
+// NPObject).
+typedef JS::GCHashMap<nsJSObjWrapperKey, nsJSObjWrapper*, JSObjWrapperHasher,
+ js::SystemAllocPolicy>
+ JSObjWrapperTable;
+static UniquePtr<JSObjWrapperTable> sJSObjWrappers;
+
+// Whether it's safe to iterate sJSObjWrappers. Set to true when sJSObjWrappers
+// has been initialized and is not currently being enumerated.
+static bool sJSObjWrappersAccessible = false;
+
+// Hash of NPObject wrappers that wrap NPObjects as JSObjects.
+static PLDHashTable* sNPObjWrappers;
+
+// Global wrapper count. This includes JSObject wrappers *and*
+// NPObject wrappers. When this count goes to zero, there are no more
+// wrappers and we can kill off hash tables etc.
+static int32_t sWrapperCount;
+
+static bool sCallbackIsRegistered = false;
+
+static nsTArray<NPObject*>* sDelayedReleases;
+
+namespace {
+
+inline bool NPObjectIsOutOfProcessProxy(NPObject* obj) {
+ return obj->_class == PluginScriptableObjectParent::GetClass();
+}
+
+} // namespace
+
+// Helper class that suppresses any JS exceptions that were thrown while
+// the plugin executed JS, if the nsJSObjWrapper has a destroy pending.
+// Note that this class is the product (vestige?) of a long evolution in how
+// error reporting worked, and hence the mIsDestroyPending check, and hence this
+// class in general, may or may not actually be necessary.
+
+class MOZ_STACK_CLASS AutoJSExceptionSuppressor {
+ public:
+ AutoJSExceptionSuppressor(dom::AutoEntryScript& aes, nsJSObjWrapper* aWrapper)
+ : mAes(aes), mIsDestroyPending(aWrapper->mDestroyPending) {}
+
+ ~AutoJSExceptionSuppressor() {
+ if (mIsDestroyPending) {
+ mAes.ClearException();
+ }
+ }
+
+ protected:
+ dom::AutoEntryScript& mAes;
+ bool mIsDestroyPending;
+};
+
+NPClass nsJSObjWrapper::sJSObjWrapperNPClass = {
+ NP_CLASS_STRUCT_VERSION, nsJSObjWrapper::NP_Allocate,
+ nsJSObjWrapper::NP_Deallocate, nsJSObjWrapper::NP_Invalidate,
+ nsJSObjWrapper::NP_HasMethod, nsJSObjWrapper::NP_Invoke,
+ nsJSObjWrapper::NP_InvokeDefault, nsJSObjWrapper::NP_HasProperty,
+ nsJSObjWrapper::NP_GetProperty, nsJSObjWrapper::NP_SetProperty,
+ nsJSObjWrapper::NP_RemoveProperty, nsJSObjWrapper::NP_Enumerate,
+ nsJSObjWrapper::NP_Construct};
+
+class NPObjWrapperProxyHandler : public js::BaseProxyHandler {
+ static const char family;
+
+ public:
+ static const NPObjWrapperProxyHandler singleton;
+
+ constexpr NPObjWrapperProxyHandler() : BaseProxyHandler(&family) {}
+
+ bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult& result) const override {
+ ::JS_ReportErrorASCII(cx,
+ "Trying to add unsupported property on NPObject!");
+ return false;
+ }
+
+ bool getPrototypeIfOrdinary(
+ JSContext* cx, JS::Handle<JSObject*> proxy, bool* isOrdinary,
+ JS::MutableHandle<JSObject*> proto) const override {
+ *isOrdinary = true;
+ proto.set(js::GetStaticPrototype(proxy));
+ return true;
+ }
+
+ bool isExtensible(JSContext* cx, JS::Handle<JSObject*> proxy,
+ bool* extensible) const override {
+ // Needs to be extensible so nsObjectLoadingContent can mutate our
+ // __proto__.
+ *extensible = true;
+ return true;
+ }
+
+ bool preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::ObjectOpResult& result) const override {
+ result.succeed();
+ return true;
+ }
+
+ bool getOwnPropertyDescriptor(
+ JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
+
+ bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::MutableHandleVector<jsid> properties) const override;
+
+ bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::ObjectOpResult& result) const override;
+
+ bool get(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
+ JS::MutableHandle<JS::Value> vp) const override;
+
+ bool set(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::Handle<JS::Value> vp, JS::Handle<JS::Value> receiver,
+ JS::ObjectOpResult& result) const override;
+
+ bool isCallable(JSObject* obj) const override { return true; }
+ bool call(JSContext* cx, JS::Handle<JSObject*> proxy,
+ const JS::CallArgs& args) const override;
+
+ bool isConstructor(JSObject* obj) const override { return true; }
+ bool construct(JSContext* cx, JS::Handle<JSObject*> proxy,
+ const JS::CallArgs& args) const override;
+
+ bool finalizeInBackground(const JS::Value& priv) const override {
+ return false;
+ }
+ void finalize(JSFreeOp* fop, JSObject* proxy) const override;
+
+ size_t objectMoved(JSObject* obj, JSObject* old) const override;
+};
+
+const char NPObjWrapperProxyHandler::family = 0;
+const NPObjWrapperProxyHandler NPObjWrapperProxyHandler::singleton;
+
+static bool NPObjWrapper_Resolve(JSContext* cx, JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id, bool* resolved,
+ JS::MutableHandle<JSObject*> method);
+
+static bool NPObjWrapper_toPrimitive(JSContext* cx, unsigned argc,
+ JS::Value* vp);
+
+static bool CreateNPObjectMember(NPP npp, JSContext* cx,
+ JS::Handle<JSObject*> obj, NPObject* npobj,
+ JS::Handle<jsid> id,
+ NPVariant* getPropertyResult,
+ JS::MutableHandle<JS::Value> vp);
+
+const JSClass sNPObjWrapperProxyClass =
+ PROXY_CLASS_DEF(NPRUNTIME_JSCLASS_NAME, JSCLASS_HAS_RESERVED_SLOTS(1));
+
+typedef struct NPObjectMemberPrivate {
+ JS::Heap<JSObject*> npobjWrapper;
+ JS::Heap<JS::Value> fieldValue;
+ JS::Heap<jsid> methodName;
+ NPP npp = nullptr;
+} NPObjectMemberPrivate;
+
+static void NPObjectMember_Finalize(JSFreeOp* fop, JSObject* obj);
+
+static bool NPObjectMember_Call(JSContext* cx, unsigned argc, JS::Value* vp);
+
+static void NPObjectMember_Trace(JSTracer* trc, JSObject* obj);
+
+static bool NPObjectMember_toPrimitive(JSContext* cx, unsigned argc,
+ JS::Value* vp);
+
+static const JSClassOps sNPObjectMemberClassOps = {nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ NPObjectMember_Finalize,
+ NPObjectMember_Call,
+ nullptr,
+ nullptr,
+ NPObjectMember_Trace};
+
+static const JSClass sNPObjectMemberClass = {
+ "NPObject Ambiguous Member class",
+ JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE,
+ &sNPObjectMemberClassOps};
+
+static void OnWrapperDestroyed();
+
+static void TraceJSObjWrappers(JSTracer* trc, void* data) {
+ if (sJSObjWrappers) {
+ sJSObjWrappers->trace(trc);
+ }
+}
+
+static void DelayedReleaseGCCallback(JSGCStatus status) {
+ if (JSGC_END == status) {
+ // Take ownership of sDelayedReleases and null it out now. The
+ // _releaseobject call below can reenter GC and double-free these objects.
+ UniquePtr<nsTArray<NPObject*>> delayedReleases(sDelayedReleases);
+ sDelayedReleases = nullptr;
+
+ if (delayedReleases) {
+ for (uint32_t i = 0; i < delayedReleases->Length(); ++i) {
+ NPObject* obj = (*delayedReleases)[i];
+ if (obj) _releaseobject(obj);
+ OnWrapperDestroyed();
+ }
+ }
+ }
+}
+
+static bool RegisterGCCallbacks() {
+ if (sCallbackIsRegistered) {
+ return true;
+ }
+
+ // Register a callback to trace wrapped JSObjects.
+ JSContext* cx = dom::danger::GetJSContext();
+ if (!JS_AddExtraGCRootsTracer(cx, TraceJSObjWrappers, nullptr)) {
+ return false;
+ }
+
+ // Register our GC callback to perform delayed destruction of finalized
+ // NPObjects.
+ xpc::AddGCCallback(DelayedReleaseGCCallback);
+
+ sCallbackIsRegistered = true;
+
+ return true;
+}
+
+static void UnregisterGCCallbacks() {
+ MOZ_ASSERT(sCallbackIsRegistered);
+
+ // Remove tracing callback.
+ JSContext* cx = dom::danger::GetJSContext();
+ JS_RemoveExtraGCRootsTracer(cx, TraceJSObjWrappers, nullptr);
+
+ // Remove delayed destruction callback.
+ if (sCallbackIsRegistered) {
+ xpc::RemoveGCCallback(DelayedReleaseGCCallback);
+ sCallbackIsRegistered = false;
+ }
+}
+
+static bool CreateJSObjWrapperTable() {
+ MOZ_ASSERT(!sJSObjWrappersAccessible);
+ MOZ_ASSERT(!sJSObjWrappers);
+
+ if (!RegisterGCCallbacks()) {
+ return false;
+ }
+
+ sJSObjWrappers = MakeUnique<JSObjWrapperTable>();
+ sJSObjWrappersAccessible = true;
+ return true;
+}
+
+static void DestroyJSObjWrapperTable() {
+ MOZ_ASSERT(sJSObjWrappersAccessible);
+ MOZ_ASSERT(sJSObjWrappers);
+ MOZ_ASSERT(sJSObjWrappers->count() == 0);
+
+ // No more wrappers. Delete the table.
+ sJSObjWrappers = nullptr;
+ sJSObjWrappersAccessible = false;
+}
+
+static bool CreateNPObjWrapperTable() {
+ MOZ_ASSERT(!sNPObjWrappers);
+
+ if (!RegisterGCCallbacks()) {
+ return false;
+ }
+
+ sNPObjWrappers =
+ new PLDHashTable(PLDHashTable::StubOps(), sizeof(NPObjWrapperHashEntry));
+ return true;
+}
+
+static void DestroyNPObjWrapperTable() {
+ MOZ_ASSERT(sNPObjWrappers->EntryCount() == 0);
+
+ delete sNPObjWrappers;
+ sNPObjWrappers = nullptr;
+}
+
+static void OnWrapperCreated() { ++sWrapperCount; }
+
+static void OnWrapperDestroyed() {
+ NS_ASSERTION(sWrapperCount, "Whaaa, unbalanced created/destroyed calls!");
+
+ if (--sWrapperCount == 0) {
+ if (sJSObjWrappersAccessible) {
+ DestroyJSObjWrapperTable();
+ }
+
+ if (sNPObjWrappers) {
+ // No more wrappers, and our hash was initialized. Finish the
+ // hash to prevent leaking it.
+ DestroyNPObjWrapperTable();
+ }
+
+ UnregisterGCCallbacks();
+ }
+}
+
+namespace mozilla::plugins::parent {
+
+static nsIGlobalObject* GetGlobalObject(NPP npp) {
+ NS_ENSURE_TRUE(npp, nullptr);
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)npp->ndata;
+ NS_ENSURE_TRUE(inst, nullptr);
+
+ RefPtr<nsPluginInstanceOwner> owner = inst->GetOwner();
+ NS_ENSURE_TRUE(owner, nullptr);
+
+ nsCOMPtr<dom::Document> doc;
+ owner->GetDocument(getter_AddRefs(doc));
+ NS_ENSURE_TRUE(doc, nullptr);
+
+ return doc->GetScopeObject();
+}
+
+} // namespace mozilla::plugins::parent
+
+static NPP LookupNPP(NPObject* npobj);
+
+static JS::Value NPVariantToJSVal(NPP npp, JSContext* cx,
+ const NPVariant* variant) {
+ switch (variant->type) {
+ case NPVariantType_Void:
+ return JS::UndefinedValue();
+ case NPVariantType_Null:
+ return JS::NullValue();
+ case NPVariantType_Bool:
+ return JS::BooleanValue(NPVARIANT_TO_BOOLEAN(*variant));
+ case NPVariantType_Int32: {
+ // Don't use INT_TO_JSVAL directly to prevent bugs when dealing
+ // with ints larger than what fits in a integer JS::Value.
+ return ::JS_NumberValue(NPVARIANT_TO_INT32(*variant));
+ }
+ case NPVariantType_Double: {
+ return ::JS_NumberValue(NPVARIANT_TO_DOUBLE(*variant));
+ }
+ case NPVariantType_String: {
+ const NPString* s = &NPVARIANT_TO_STRING(*variant);
+ NS_ConvertUTF8toUTF16 utf16String(s->UTF8Characters, s->UTF8Length);
+
+ JSString* str =
+ ::JS_NewUCStringCopyN(cx, utf16String.get(), utf16String.Length());
+
+ if (str) {
+ return JS::StringValue(str);
+ }
+
+ break;
+ }
+ case NPVariantType_Object: {
+ if (npp) {
+ JSObject* obj = nsNPObjWrapper::GetNewOrUsed(
+ npp, cx, NPVARIANT_TO_OBJECT(*variant));
+
+ if (obj) {
+ return JS::ObjectValue(*obj);
+ }
+ }
+
+ NS_ERROR("Error wrapping NPObject!");
+
+ break;
+ }
+ default:
+ NS_ERROR("Unknown NPVariant type!");
+ }
+
+ NS_ERROR("Unable to convert NPVariant to jsval!");
+
+ return JS::UndefinedValue();
+}
+
+bool JSValToNPVariant(NPP npp, JSContext* cx, const JS::Value& val,
+ NPVariant* variant) {
+ NS_ASSERTION(npp, "Must have an NPP to wrap a jsval!");
+
+ if (val.isPrimitive()) {
+ if (val.isUndefined()) {
+ VOID_TO_NPVARIANT(*variant);
+ } else if (val.isNull()) {
+ NULL_TO_NPVARIANT(*variant);
+ } else if (val.isBoolean()) {
+ BOOLEAN_TO_NPVARIANT(val.toBoolean(), *variant);
+ } else if (val.isInt32()) {
+ INT32_TO_NPVARIANT(val.toInt32(), *variant);
+ } else if (val.isDouble()) {
+ double d = val.toDouble();
+ int i;
+ if (JS_DoubleIsInt32(d, &i)) {
+ INT32_TO_NPVARIANT(i, *variant);
+ } else {
+ DOUBLE_TO_NPVARIANT(d, *variant);
+ }
+ } else if (val.isString()) {
+ JSString* jsstr = val.toString();
+
+ nsAutoJSString str;
+ if (!str.init(cx, jsstr)) {
+ return false;
+ }
+
+ uint32_t len;
+ char* p = ToNewUTF8String(str, &len);
+
+ if (!p) {
+ return false;
+ }
+
+ STRINGN_TO_NPVARIANT(p, len, *variant);
+ } else {
+ NS_ERROR("Unknown primitive type!");
+
+ return false;
+ }
+
+ return true;
+ }
+
+ // The reflected plugin object may be in another compartment if the plugin
+ // element has since been adopted into a new document. We don't bother
+ // transplanting the plugin objects, and just do a unwrap with security
+ // checks if we encounter one of them as an argument. If the unwrap fails,
+ // we run with the original wrapped object, since sometimes there are
+ // legitimate cases where a security wrapper ends up here (for example,
+ // Location objects, which are _always_ behind security wrappers).
+ JS::Rooted<JSObject*> obj(cx, &val.toObject());
+ JS::Rooted<JSObject*> global(cx);
+ // CheckedUnwrapStatic is fine here; if we get a Location or WindowProxy,
+ // we'll just use the current global instead.
+ obj = js::CheckedUnwrapStatic(obj);
+ if (obj) {
+ global = JS::GetNonCCWObjectGlobal(obj);
+ } else {
+ obj = &val.toObject();
+ global = JS::CurrentGlobalOrNull(cx);
+ }
+
+ NPObject* npobj = nsJSObjWrapper::GetNewOrUsed(npp, obj, global);
+ if (!npobj) {
+ return false;
+ }
+
+ // Pass over ownership of npobj to *variant
+ OBJECT_TO_NPVARIANT(npobj, *variant);
+
+ return true;
+}
+
+static void ThrowJSExceptionASCII(JSContext* cx, const char* message) {
+ const char* ex = PeekException();
+
+ if (ex) {
+ nsAutoString ucex;
+
+ if (message) {
+ AppendASCIItoUTF16(mozilla::MakeStringSpan(message), ucex);
+
+ ucex.AppendLiteral(" [plugin exception: ");
+ }
+
+ AppendUTF8toUTF16(mozilla::MakeStringSpan(ex), ucex);
+
+ if (message) {
+ ucex.AppendLiteral("].");
+ }
+
+ JSString* str = ::JS_NewUCStringCopyN(cx, ucex.get(), ucex.Length());
+
+ if (str) {
+ JS::Rooted<JS::Value> exn(cx, JS::StringValue(str));
+ ::JS_SetPendingException(cx, exn);
+ }
+
+ PopException();
+ } else {
+ ::JS_ReportErrorASCII(cx, "%s", message);
+ }
+}
+
+static bool ReportExceptionIfPending(JSContext* cx) {
+ const char* ex = PeekException();
+
+ if (!ex) {
+ return true;
+ }
+
+ ThrowJSExceptionASCII(cx, nullptr);
+
+ return false;
+}
+
+nsJSObjWrapper::nsJSObjWrapper(NPP npp)
+ : mJSObj(nullptr),
+ mJSObjGlobal(nullptr),
+ mNpp(npp),
+ mDestroyPending(false) {
+ MOZ_COUNT_CTOR(nsJSObjWrapper);
+ OnWrapperCreated();
+}
+
+nsJSObjWrapper::~nsJSObjWrapper() {
+ MOZ_COUNT_DTOR(nsJSObjWrapper);
+
+ // Invalidate first, since it relies on sJSObjWrappers.
+ NP_Invalidate(this);
+
+ OnWrapperDestroyed();
+}
+
+// static
+NPObject* nsJSObjWrapper::NP_Allocate(NPP npp, NPClass* aClass) {
+ NS_ASSERTION(aClass == &sJSObjWrapperNPClass,
+ "Huh, wrong class passed to NP_Allocate()!!!");
+
+ return new nsJSObjWrapper(npp);
+}
+
+// static
+void nsJSObjWrapper::NP_Deallocate(NPObject* npobj) {
+ // nsJSObjWrapper::~nsJSObjWrapper() will call NP_Invalidate().
+ delete (nsJSObjWrapper*)npobj;
+}
+
+// static
+void nsJSObjWrapper::NP_Invalidate(NPObject* npobj) {
+ nsJSObjWrapper* jsnpobj = (nsJSObjWrapper*)npobj;
+
+ if (jsnpobj && jsnpobj->mJSObj) {
+ if (sJSObjWrappersAccessible) {
+ // Remove the wrapper from the hash
+ nsJSObjWrapperKey key(jsnpobj->mJSObj, jsnpobj->mNpp);
+ JSObjWrapperTable::Ptr ptr = sJSObjWrappers->lookup(key);
+ MOZ_ASSERT(ptr.found());
+ sJSObjWrappers->remove(ptr);
+ }
+
+ // Forget our reference to the JSObject.
+ jsnpobj->mJSObj = nullptr;
+ jsnpobj->mJSObjGlobal = nullptr;
+ }
+}
+
+static bool GetProperty(JSContext* cx, JSObject* objArg, NPIdentifier npid,
+ JS::MutableHandle<JS::Value> rval) {
+ NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
+ "id must be either string or int!\n");
+ JS::Rooted<JSObject*> obj(cx, objArg);
+ JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
+ return ::JS_GetPropertyById(cx, obj, id, rval);
+}
+
+static void MarkCrossZoneNPIdentifier(JSContext* cx, NPIdentifier npid) {
+ JS_MarkCrossZoneId(cx, NPIdentifierToJSId(npid));
+}
+
+// static
+bool nsJSObjWrapper::NP_HasMethod(NPObject* npobj, NPIdentifier id) {
+ NPP npp = NPPStack::Peek();
+ nsIGlobalObject* globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ dom::AutoEntryScript aes(globalObject, "NPAPI HasMethod");
+ JSContext* cx = aes.cx();
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx, "Null npobj in nsJSObjWrapper::NP_HasMethod!");
+
+ return false;
+ }
+
+ nsJSObjWrapper* npjsobj = (nsJSObjWrapper*)npobj;
+
+ JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
+ MarkCrossZoneNPIdentifier(cx, id);
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+
+ JS::Rooted<JS::Value> v(cx);
+ bool ok = GetProperty(cx, npjsobj->mJSObj, id, &v);
+
+ return ok && !v.isPrimitive() && ::JS_ObjectIsFunction(v.toObjectOrNull());
+}
+
+static bool doInvoke(NPObject* npobj, NPIdentifier method,
+ const NPVariant* args, uint32_t argCount, bool ctorCall,
+ NPVariant* result) {
+ NPP npp = NPPStack::Peek();
+
+ nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ AutoAllowLegacyScriptExecution exemption;
+
+ // We're about to run script via JS_CallFunctionValue, so we need an
+ // AutoEntryScript. NPAPI plugins are Gecko-specific and not in any spec.
+ dom::AutoEntryScript aes(globalObject, "NPAPI doInvoke");
+ JSContext* cx = aes.cx();
+
+ if (!npobj || !result) {
+ ThrowJSExceptionASCII(cx, "Null npobj, or result in doInvoke!");
+
+ return false;
+ }
+
+ // Initialize *result
+ VOID_TO_NPVARIANT(*result);
+
+ nsJSObjWrapper* npjsobj = (nsJSObjWrapper*)npobj;
+
+ JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
+ JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
+ MarkCrossZoneNPIdentifier(cx, method);
+ JS::Rooted<JS::Value> fv(cx);
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+
+ if (method != NPIdentifier_VOID) {
+ if (!GetProperty(cx, jsobj, method, &fv) ||
+ ::JS_TypeOfValue(cx, fv) != JSTYPE_FUNCTION) {
+ return false;
+ }
+ } else {
+ fv.setObject(*jsobj);
+ }
+
+ // Convert args
+ JS::RootedVector<JS::Value> jsargs(cx);
+ if (!jsargs.reserve(argCount)) {
+ ::JS_ReportOutOfMemory(cx);
+ return false;
+ }
+ for (uint32_t i = 0; i < argCount; ++i) {
+ jsargs.infallibleAppend(NPVariantToJSVal(npp, cx, args + i));
+ }
+
+ JS::Rooted<JS::Value> v(cx);
+ bool ok = false;
+
+ if (ctorCall) {
+ JSObject* newObj = ::JS_New(cx, jsobj, jsargs);
+
+ if (newObj) {
+ v.setObject(*newObj);
+ ok = true;
+ }
+ } else {
+ ok = ::JS_CallFunctionValue(cx, jsobj, fv, jsargs, &v);
+ }
+
+ if (ok) ok = JSValToNPVariant(npp, cx, v, result);
+
+ return ok;
+}
+
+// static
+bool nsJSObjWrapper::NP_Invoke(NPObject* npobj, NPIdentifier method,
+ const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (method == NPIdentifier_VOID) {
+ return false;
+ }
+
+ return doInvoke(npobj, method, args, argCount, false, result);
+}
+
+// static
+bool nsJSObjWrapper::NP_InvokeDefault(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ return doInvoke(npobj, NPIdentifier_VOID, args, argCount, false, result);
+}
+
+// static
+bool nsJSObjWrapper::NP_HasProperty(NPObject* npobj, NPIdentifier npid) {
+ NPP npp = NPPStack::Peek();
+ nsIGlobalObject* globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ dom::AutoEntryScript aes(globalObject, "NPAPI HasProperty");
+ JSContext* cx = aes.cx();
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx, "Null npobj in nsJSObjWrapper::NP_HasProperty!");
+
+ return false;
+ }
+
+ nsJSObjWrapper* npjsobj = (nsJSObjWrapper*)npobj;
+ bool found, ok = false;
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+ JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
+ JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
+ MarkCrossZoneNPIdentifier(cx, npid);
+
+ NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
+ "id must be either string or int!\n");
+ JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
+ ok = ::JS_HasPropertyById(cx, jsobj, id, &found);
+ return ok && found;
+}
+
+// static
+bool nsJSObjWrapper::NP_GetProperty(NPObject* npobj, NPIdentifier id,
+ NPVariant* result) {
+ NPP npp = NPPStack::Peek();
+
+ nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ // We're about to run script via JS_CallFunctionValue, so we need an
+ // AutoEntryScript. NPAPI plugins are Gecko-specific and not in any spec.
+ dom::AutoEntryScript aes(globalObject, "NPAPI get");
+ JSContext* cx = aes.cx();
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx, "Null npobj in nsJSObjWrapper::NP_GetProperty!");
+
+ return false;
+ }
+
+ nsJSObjWrapper* npjsobj = (nsJSObjWrapper*)npobj;
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+ JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
+ MarkCrossZoneNPIdentifier(cx, id);
+
+ JS::Rooted<JS::Value> v(cx);
+ return (GetProperty(cx, npjsobj->mJSObj, id, &v) &&
+ JSValToNPVariant(npp, cx, v, result));
+}
+
+// static
+bool nsJSObjWrapper::NP_SetProperty(NPObject* npobj, NPIdentifier npid,
+ const NPVariant* value) {
+ NPP npp = NPPStack::Peek();
+
+ nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ // We're about to run script via JS_CallFunctionValue, so we need an
+ // AutoEntryScript. NPAPI plugins are Gecko-specific and not in any spec.
+ dom::AutoEntryScript aes(globalObject, "NPAPI set");
+ JSContext* cx = aes.cx();
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx, "Null npobj in nsJSObjWrapper::NP_SetProperty!");
+
+ return false;
+ }
+
+ nsJSObjWrapper* npjsobj = (nsJSObjWrapper*)npobj;
+ bool ok = false;
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+ JS::Rooted<JSObject*> jsObj(cx, npjsobj->mJSObj);
+ JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
+ MarkCrossZoneNPIdentifier(cx, npid);
+
+ JS::Rooted<JS::Value> v(cx, NPVariantToJSVal(npp, cx, value));
+
+ NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
+ "id must be either string or int!\n");
+ JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
+ ok = ::JS_SetPropertyById(cx, jsObj, id, v);
+
+ return ok;
+}
+
+// static
+bool nsJSObjWrapper::NP_RemoveProperty(NPObject* npobj, NPIdentifier npid) {
+ NPP npp = NPPStack::Peek();
+ nsIGlobalObject* globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ dom::AutoEntryScript aes(globalObject, "NPAPI RemoveProperty");
+ JSContext* cx = aes.cx();
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx,
+ "Null npobj in nsJSObjWrapper::NP_RemoveProperty!");
+
+ return false;
+ }
+
+ nsJSObjWrapper* npjsobj = (nsJSObjWrapper*)npobj;
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+ JS::ObjectOpResult result;
+ JS::Rooted<JSObject*> obj(cx, npjsobj->mJSObj);
+ JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
+ MarkCrossZoneNPIdentifier(cx, npid);
+
+ NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
+ "id must be either string or int!\n");
+ JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
+ if (!::JS_DeletePropertyById(cx, obj, id, result)) return false;
+
+ if (result) {
+ // FIXME: See bug 425823, we shouldn't need to do this, and once
+ // that bug is fixed we can remove this code.
+ bool hasProp;
+ if (!::JS_HasPropertyById(cx, obj, id, &hasProp)) return false;
+ if (!hasProp) return true;
+
+ // The property might have been deleted, but it got
+ // re-resolved, so no, it's not really deleted.
+ result.failCantDelete();
+ }
+
+ return result.reportError(cx, obj, id);
+}
+
+// static
+bool nsJSObjWrapper::NP_Enumerate(NPObject* npobj, NPIdentifier** idarray,
+ uint32_t* count) {
+ NPP npp = NPPStack::Peek();
+ nsIGlobalObject* globalObject = GetGlobalObject(npp);
+ if (NS_WARN_IF(!globalObject)) {
+ return false;
+ }
+
+ dom::AutoEntryScript aes(globalObject, "NPAPI Enumerate");
+ JSContext* cx = aes.cx();
+
+ *idarray = 0;
+ *count = 0;
+
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx, "Null npobj in nsJSObjWrapper::NP_Enumerate!");
+
+ return false;
+ }
+
+ nsJSObjWrapper* npjsobj = (nsJSObjWrapper*)npobj;
+
+ AutoJSExceptionSuppressor suppressor(aes, npjsobj);
+ JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
+ JSAutoRealm ar(cx, npjsobj->mJSObjGlobal);
+
+ JS::Rooted<JS::IdVector> ida(cx, JS::IdVector(cx));
+ if (!JS_Enumerate(cx, jsobj, &ida)) {
+ return false;
+ }
+
+ *count = ida.length();
+ *idarray = (NPIdentifier*)malloc(*count * sizeof(NPIdentifier));
+ if (!*idarray) {
+ ThrowJSExceptionASCII(cx, "Memory allocation failed for NPIdentifier!");
+ return false;
+ }
+
+ for (uint32_t i = 0; i < *count; i++) {
+ JS::Rooted<JS::Value> v(cx);
+ if (!JS_IdToValue(cx, ida[i], &v)) {
+ free(*idarray);
+ return false;
+ }
+
+ NPIdentifier id;
+ if (v.isString()) {
+ JS::Rooted<JSString*> str(cx, v.toString());
+ str = JS_AtomizeAndPinJSString(cx, str);
+ if (!str) {
+ free(*idarray);
+ return false;
+ }
+ id = StringToNPIdentifier(cx, str);
+ } else {
+ NS_ASSERTION(v.isInt32(),
+ "The element in ida must be either string or int!\n");
+ id = IntToNPIdentifier(v.toInt32());
+ }
+
+ (*idarray)[i] = id;
+ }
+
+ return true;
+}
+
+// static
+bool nsJSObjWrapper::NP_Construct(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ return doInvoke(npobj, NPIdentifier_VOID, args, argCount, true, result);
+}
+
+// Look up or create an NPObject that wraps the JSObject obj.
+
+// static
+NPObject* nsJSObjWrapper::GetNewOrUsed(NPP npp, JS::Handle<JSObject*> obj,
+ JS::Handle<JSObject*> objGlobal) {
+ if (!npp) {
+ NS_ERROR("Null NPP passed to nsJSObjWrapper::GetNewOrUsed()!");
+
+ return nullptr;
+ }
+
+ MOZ_ASSERT(JS_IsGlobalObject(objGlobal));
+ MOZ_RELEASE_ASSERT(JS::GetCompartment(obj) == JS::GetCompartment(objGlobal));
+
+ // No need to enter the right compartment here as we only get the
+ // class and private from the JSObject, neither of which cares about
+ // compartments.
+
+ if (nsNPObjWrapper::IsWrapper(obj)) {
+ // obj is one of our own, its private data is the NPObject we're
+ // looking for.
+
+ NPObject* npobj = (NPObject*)js::GetProxyPrivate(obj).toPrivate();
+
+ // If the private is null, that means that the object has already been torn
+ // down, possible because the owning plugin was destroyed (there can be
+ // multiple plugins, so the fact that it was destroyed does not prevent one
+ // of its dead JS objects from being passed to another plugin). There's not
+ // much use in wrapping such a dead object, so we just return null, causing
+ // us to throw.
+ if (!npobj) return nullptr;
+
+ if (LookupNPP(npobj) == npp) return _retainobject(npobj);
+ }
+
+ if (!sJSObjWrappers) {
+ // No hash yet (or any more), initialize it.
+ if (!CreateJSObjWrapperTable()) return nullptr;
+ }
+ MOZ_ASSERT(sJSObjWrappersAccessible);
+
+ JSObjWrapperTable::Ptr p =
+ sJSObjWrappers->lookup(nsJSObjWrapperKey(obj, npp));
+ if (p) {
+ MOZ_ASSERT(p->value());
+ // Found a live nsJSObjWrapper, return it.
+
+ return _retainobject(p->value());
+ }
+
+ // No existing nsJSObjWrapper, create one.
+
+ nsJSObjWrapper* wrapper =
+ (nsJSObjWrapper*)_createobject(npp, &sJSObjWrapperNPClass);
+
+ if (!wrapper) {
+ // Out of memory, entry not yet added to table.
+ return nullptr;
+ }
+
+ wrapper->mJSObj = obj;
+ wrapper->mJSObjGlobal = objGlobal;
+
+ // Insert the new wrapper into the hashtable, rooting the JSObject. Its
+ // lifetime is now tied to that of the NPObject.
+ if (!sJSObjWrappers->putNew(nsJSObjWrapperKey(obj, npp), wrapper)) {
+ // Out of memory, free the wrapper we created.
+ _releaseobject(wrapper);
+ return nullptr;
+ }
+
+ return wrapper;
+}
+
+// Climb the prototype chain, unwrapping as necessary until we find an NP object
+// wrapper.
+//
+// Because this function unwraps, its return value must be wrapped for the cx
+// compartment for callers that plan to hold onto the result or do anything
+// substantial with it.
+static JSObject* GetNPObjectWrapper(JSContext* cx, JS::Handle<JSObject*> aObj,
+ bool wrapResult = true) {
+ JS::Rooted<JSObject*> obj(cx, aObj);
+
+ // We can't have WindowProxy or Location objects with NP object wrapper
+ // objects on their proto chain, since they have immutable prototypes. So
+ // CheckedUnwrapStatic is ok here.
+ while (obj && (obj = js::CheckedUnwrapStatic(obj))) {
+ if (nsNPObjWrapper::IsWrapper(obj)) {
+ if (wrapResult && !JS_WrapObject(cx, &obj)) {
+ return nullptr;
+ }
+ return obj;
+ }
+
+ JSAutoRealm ar(cx, obj);
+ if (!::JS_GetPrototype(cx, obj, &obj)) {
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+static NPObject* GetNPObject(JSContext* cx, JS::Handle<JSObject*> aObj) {
+ JS::Rooted<JSObject*> obj(cx, aObj);
+ obj = GetNPObjectWrapper(cx, obj, /* wrapResult = */ false);
+ if (!obj) {
+ return nullptr;
+ }
+
+ return (NPObject*)js::GetProxyPrivate(obj).toPrivate();
+}
+
+static JSObject* NPObjWrapper_GetResolvedProps(JSContext* cx,
+ JS::Handle<JSObject*> obj) {
+ JS::Value slot = js::GetProxyReservedSlot(obj, 0);
+ if (slot.isObject()) return &slot.toObject();
+
+ MOZ_ASSERT(slot.isUndefined());
+
+ JSObject* res = JS_NewObject(cx, nullptr);
+ if (!res) return nullptr;
+
+ SetProxyReservedSlot(obj, 0, JS::ObjectValue(*res));
+ return res;
+}
+
+bool NPObjWrapperProxyHandler::delete_(JSContext* cx,
+ JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id,
+ JS::ObjectOpResult& result) const {
+ NPObject* npobj = GetNPObject(cx, proxy);
+
+ if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
+ !npobj->_class->removeProperty) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+
+ return false;
+ }
+
+ JS::Rooted<JSObject*> resolvedProps(cx,
+ NPObjWrapper_GetResolvedProps(cx, proxy));
+ if (!resolvedProps) return false;
+ if (!JS_DeletePropertyById(cx, resolvedProps, id, result)) return false;
+
+ PluginDestructionGuard pdg(LookupNPP(npobj));
+
+ NPIdentifier identifier = JSIdToNPIdentifier(id);
+
+ if (!NPObjectIsOutOfProcessProxy(npobj)) {
+ bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
+ if (!ReportExceptionIfPending(cx)) return false;
+
+ if (!hasProperty) return result.succeed();
+ }
+
+ // This removeProperty hook may throw an exception and return false; or just
+ // return false without an exception pending, which behaves like `delete
+ // obj.prop` returning false: in strict mode it becomes a TypeError. Legacy
+ // code---nothing else that uses the JSAPI works this way anymore.
+ bool succeeded = npobj->_class->removeProperty(npobj, identifier);
+ if (!ReportExceptionIfPending(cx)) return false;
+ return succeeded ? result.succeed() : result.failCantDelete();
+}
+
+bool NPObjWrapperProxyHandler::set(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id,
+ JS::Handle<JS::Value> vp,
+ JS::Handle<JS::Value> receiver,
+ JS::ObjectOpResult& result) const {
+ NPObject* npobj = GetNPObject(cx, proxy);
+
+ if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
+ !npobj->_class->setProperty) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+
+ return false;
+ }
+
+ // Find out what plugin (NPP) is the owner of the object we're
+ // manipulating, and make it own any JSObject wrappers created here.
+ NPP npp = LookupNPP(npobj);
+
+ if (!npp) {
+ ThrowJSExceptionASCII(cx, "No NPP found for NPObject!");
+
+ return false;
+ }
+
+ {
+ bool resolved = false;
+ JS::Rooted<JSObject*> method(cx);
+ if (!NPObjWrapper_Resolve(cx, proxy, id, &resolved, &method)) return false;
+ if (!resolved) {
+ // We don't have a property/method with this id. Forward to the prototype
+ // chain.
+ return js::BaseProxyHandler::set(cx, proxy, id, vp, receiver, result);
+ }
+ }
+
+ PluginDestructionGuard pdg(npp);
+
+ NPIdentifier identifier = JSIdToNPIdentifier(id);
+
+ if (!NPObjectIsOutOfProcessProxy(npobj)) {
+ bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
+ if (!ReportExceptionIfPending(cx)) return false;
+
+ if (!hasProperty) {
+ ThrowJSExceptionASCII(cx,
+ "Trying to set unsupported property on NPObject!");
+
+ return false;
+ }
+ }
+
+ NPVariant npv;
+ if (!JSValToNPVariant(npp, cx, vp, &npv)) {
+ ThrowJSExceptionASCII(cx, "Error converting jsval to NPVariant!");
+
+ return false;
+ }
+
+ bool ok = npobj->_class->setProperty(npobj, identifier, &npv);
+ _releasevariantvalue(&npv); // Release the variant
+ if (!ReportExceptionIfPending(cx)) return false;
+
+ if (!ok) {
+ ThrowJSExceptionASCII(cx, "Error setting property on NPObject!");
+
+ return false;
+ }
+
+ return result.succeed();
+}
+
+static bool CallNPMethod(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool NPObjWrapperProxyHandler::get(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<JS::Value> receiver,
+ JS::Handle<jsid> id,
+ JS::MutableHandle<JS::Value> vp) const {
+ NPObject* npobj = GetNPObject(cx, proxy);
+
+ if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
+ !npobj->_class->hasMethod || !npobj->_class->getProperty) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+
+ return false;
+ }
+
+ if (id.isSymbol()) {
+ if (id.isWellKnownSymbol(JS::SymbolCode::toPrimitive)) {
+ JS::RootedObject obj(
+ cx, JS_GetFunctionObject(JS_NewFunction(cx, NPObjWrapper_toPrimitive,
+ 1, 0, "Symbol.toPrimitive")));
+ if (!obj) return false;
+ vp.setObject(*obj);
+ return true;
+ }
+
+ if (id.isWellKnownSymbol(JS::SymbolCode::toStringTag)) {
+ JS::RootedString tag(cx, JS_NewStringCopyZ(cx, NPRUNTIME_JSCLASS_NAME));
+ if (!tag) {
+ return false;
+ }
+
+ vp.setString(tag);
+ return true;
+ }
+
+ return js::BaseProxyHandler::get(cx, proxy, receiver, id, vp);
+ }
+
+ // Find out what plugin (NPP) is the owner of the object we're
+ // manipulating, and make it own any JSObject wrappers created here.
+ NPP npp = LookupNPP(npobj);
+ if (!npp) {
+ ThrowJSExceptionASCII(cx, "No NPP found for NPObject!");
+
+ return false;
+ }
+
+ {
+ bool resolved = false;
+ JS::Rooted<JSObject*> method(cx);
+ if (!NPObjWrapper_Resolve(cx, proxy, id, &resolved, &method)) return false;
+ if (method) {
+ vp.setObject(*method);
+ return true;
+ }
+ if (!resolved) {
+ // We don't have a property/method with this id. Forward to the prototype
+ // chain.
+ return js::BaseProxyHandler::get(cx, proxy, receiver, id, vp);
+ }
+ }
+
+ PluginDestructionGuard pdg(npp);
+
+ bool hasProperty, hasMethod;
+
+ NPVariant npv;
+ VOID_TO_NPVARIANT(npv);
+
+ NPIdentifier identifier = JSIdToNPIdentifier(id);
+
+ if (NPObjectIsOutOfProcessProxy(npobj)) {
+ PluginScriptableObjectParent* actor =
+ static_cast<ParentNPObject*>(npobj)->parent;
+
+ // actor may be null if the plugin crashed.
+ if (!actor) return false;
+
+ bool success =
+ actor->GetPropertyHelper(identifier, &hasProperty, &hasMethod, &npv);
+
+ if (!ReportExceptionIfPending(cx)) {
+ if (success) _releasevariantvalue(&npv);
+ return false;
+ }
+
+ if (success) {
+ // We return NPObject Member class here to support ambiguous members.
+ if (hasProperty && hasMethod)
+ return CreateNPObjectMember(npp, cx, proxy, npobj, id, &npv, vp);
+
+ if (hasProperty) {
+ vp.set(NPVariantToJSVal(npp, cx, &npv));
+ _releasevariantvalue(&npv);
+
+ if (!ReportExceptionIfPending(cx)) return false;
+ return true;
+ }
+ }
+ return js::BaseProxyHandler::get(cx, proxy, receiver, id, vp);
+ }
+
+ hasProperty = npobj->_class->hasProperty(npobj, identifier);
+ if (!ReportExceptionIfPending(cx)) return false;
+
+ hasMethod = npobj->_class->hasMethod(npobj, identifier);
+ if (!ReportExceptionIfPending(cx)) return false;
+
+ // We return NPObject Member class here to support ambiguous members.
+ if (hasProperty && hasMethod)
+ return CreateNPObjectMember(npp, cx, proxy, npobj, id, nullptr, vp);
+
+ if (hasProperty) {
+ if (npobj->_class->getProperty(npobj, identifier, &npv))
+ vp.set(NPVariantToJSVal(npp, cx, &npv));
+
+ _releasevariantvalue(&npv);
+
+ if (!ReportExceptionIfPending(cx)) return false;
+ return true;
+ }
+
+ return js::BaseProxyHandler::get(cx, proxy, receiver, id, vp);
+}
+
+static bool CallNPMethodInternal(JSContext* cx, JS::Handle<JSObject*> obj,
+ unsigned argc, JS::Value* argv,
+ JS::Value* rval, bool ctorCall) {
+ NPObject* npobj = GetNPObject(cx, obj);
+
+ if (!npobj || !npobj->_class) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+
+ return false;
+ }
+
+ // Find out what plugin (NPP) is the owner of the object we're
+ // manipulating, and make it own any JSObject wrappers created here.
+ NPP npp = LookupNPP(npobj);
+
+ if (!npp) {
+ ThrowJSExceptionASCII(cx, "Error finding NPP for NPObject!");
+
+ return false;
+ }
+
+ PluginDestructionGuard pdg(npp);
+
+ NPVariant npargs_buf[8];
+ NPVariant* npargs = npargs_buf;
+
+ if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) {
+ // Our stack buffer isn't large enough to hold all arguments,
+ // malloc a buffer.
+ npargs = (NPVariant*)malloc(argc * sizeof(NPVariant));
+
+ if (!npargs) {
+ ThrowJSExceptionASCII(cx, "Out of memory!");
+
+ return false;
+ }
+ }
+
+ // Convert arguments
+ uint32_t i;
+ for (i = 0; i < argc; ++i) {
+ if (!JSValToNPVariant(npp, cx, argv[i], npargs + i)) {
+ ThrowJSExceptionASCII(cx, "Error converting jsvals to NPVariants!");
+
+ if (npargs != npargs_buf) {
+ free(npargs);
+ }
+
+ return false;
+ }
+ }
+
+ NPVariant v;
+ VOID_TO_NPVARIANT(v);
+
+ JSObject* funobj = argv[-2].toObjectOrNull();
+ bool ok;
+ const char* msg = "Error calling method on NPObject!";
+
+ if (ctorCall) {
+ // construct a new NPObject based on the NPClass in npobj. Fail if
+ // no construct method is available.
+
+ if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npobj->_class) &&
+ npobj->_class->construct) {
+ ok = npobj->_class->construct(npobj, npargs, argc, &v);
+ } else {
+ ok = false;
+
+ msg = "Attempt to construct object from class with no constructor.";
+ }
+ } else if (funobj != obj) {
+ // A obj.function() style call is made, get the method name from
+ // the function object.
+
+ if (npobj->_class->invoke) {
+ JSFunction* fun = ::JS_GetObjectFunction(funobj);
+ JS::Rooted<JSString*> funId(cx, ::JS_GetFunctionId(fun));
+ JSString* name = ::JS_AtomizeAndPinJSString(cx, funId);
+ NPIdentifier id = StringToNPIdentifier(cx, name);
+
+ ok = npobj->_class->invoke(npobj, id, npargs, argc, &v);
+ } else {
+ ok = false;
+
+ msg = "Attempt to call a method on object with no invoke method.";
+ }
+ } else {
+ if (npobj->_class->invokeDefault) {
+ // obj is a callable object that is being called, no method name
+ // available then. Invoke the default method.
+
+ ok = npobj->_class->invokeDefault(npobj, npargs, argc, &v);
+ } else {
+ ok = false;
+
+ msg =
+ "Attempt to call a default method on object with no "
+ "invokeDefault method.";
+ }
+ }
+
+ // Release arguments.
+ for (i = 0; i < argc; ++i) {
+ _releasevariantvalue(npargs + i);
+ }
+
+ if (npargs != npargs_buf) {
+ free(npargs);
+ }
+
+ if (!ok) {
+ // ReportExceptionIfPending returns a return value, which is true
+ // if no exception was thrown. In that case, throw our own.
+ if (ReportExceptionIfPending(cx)) ThrowJSExceptionASCII(cx, msg);
+
+ return false;
+ }
+
+ *rval = NPVariantToJSVal(npp, cx, &v);
+
+ // *rval now owns the value, release our reference.
+ _releasevariantvalue(&v);
+
+ return ReportExceptionIfPending(cx);
+}
+
+static bool CallNPMethod(JSContext* cx, unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ if (!args.thisv().isObject()) {
+ ThrowJSExceptionASCII(cx,
+ "plug-in method called on incompatible non-object");
+ return false;
+ }
+ JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
+ return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, false);
+}
+
+bool NPObjWrapperProxyHandler::getOwnPropertyDescriptor(
+ JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc) const {
+ bool resolved = false;
+ JS::Rooted<JSObject*> method(cx);
+ if (!NPObjWrapper_Resolve(cx, proxy, id, &resolved, &method)) return false;
+ if (!resolved) {
+ // No such property.
+ desc.object().set(nullptr);
+ return true;
+ }
+
+ // This returns a descriptor with |null| JS value if this is a plugin
+ // property (as opposed to a method). That should be fine, hopefully, as the
+ // previous code had very inconsistent behavior in this case as well. The main
+ // reason for returning a descriptor here is to make property enumeration work
+ // correctly (it will call getOwnPropertyDescriptor to check enumerability).
+ JS::Rooted<JS::Value> val(cx, JS::ObjectOrNullValue(method));
+ desc.initFields(proxy, val, JSPROP_ENUMERATE, nullptr, nullptr);
+ return true;
+}
+
+bool NPObjWrapperProxyHandler::ownPropertyKeys(
+ JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::MutableHandleVector<jsid> properties) const {
+ NPObject* npobj = GetNPObject(cx, proxy);
+ if (!npobj || !npobj->_class) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+ return false;
+ }
+
+ PluginDestructionGuard pdg(LookupNPP(npobj));
+
+ if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) ||
+ !npobj->_class->enumerate) {
+ return true;
+ }
+
+ NPIdentifier* identifiers;
+ uint32_t length;
+ if (!npobj->_class->enumerate(npobj, &identifiers, &length)) {
+ if (ReportExceptionIfPending(cx)) {
+ // ReportExceptionIfPending returns a return value, which is true
+ // if no exception was thrown. In that case, throw our own.
+ ThrowJSExceptionASCII(cx,
+ "Error enumerating properties on scriptable "
+ "plugin object");
+ }
+ return false;
+ }
+
+ if (!properties.reserve(length)) return false;
+
+ JS::Rooted<jsid> id(cx);
+ for (uint32_t i = 0; i < length; i++) {
+ id = NPIdentifierToJSId(identifiers[i]);
+ properties.infallibleAppend(id);
+ }
+
+ free(identifiers);
+ return true;
+}
+
+// This function is very similar to a resolve hook for native objects. Instead
+// of defining properties on the object, it defines them on a resolvedProps
+// object (a plain JS object that's never exposed to script) that's stored in
+// the NPObjWrapper proxy's reserved slot. The behavior is as follows:
+//
+// - *resolvedp is set to true iff the plugin object has a property or method
+// (or both) with this id.
+//
+// - If the plugin object has a *property* with this id, the caller is
+// responsible for getting/setting its value. In this case we assign |null|
+// to resolvedProps[id] so we don't have to call hasProperty each time.
+//
+// - If the plugin object has a *method* with this id, we create a JSFunction to
+// call it and assign it to resolvedProps[id]. This function is also assigned
+// to the |method| outparam so callers can return it directly if we're doing a
+// |get|.
+static bool NPObjWrapper_Resolve(JSContext* cx, JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id, bool* resolvedp,
+ JS::MutableHandle<JSObject*> method) {
+ if (JSID_IS_SYMBOL(id)) return true;
+
+ AUTO_PROFILER_LABEL("NPObjWrapper_Resolve", JS);
+
+ NPObject* npobj = GetNPObject(cx, obj);
+
+ if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
+ !npobj->_class->hasMethod) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
+
+ return false;
+ }
+
+ JS::Rooted<JSObject*> resolvedProps(cx,
+ NPObjWrapper_GetResolvedProps(cx, obj));
+ if (!resolvedProps) return false;
+ JS::Rooted<JS::Value> res(cx);
+ if (!JS_GetPropertyById(cx, resolvedProps, id, &res)) return false;
+ if (res.isObjectOrNull()) {
+ method.set(res.toObjectOrNull());
+ *resolvedp = true;
+ return true;
+ }
+
+ PluginDestructionGuard pdg(LookupNPP(npobj));
+
+ NPIdentifier identifier = JSIdToNPIdentifier(id);
+
+ bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
+ if (!ReportExceptionIfPending(cx)) return false;
+
+ if (hasProperty) {
+ if (!JS_SetPropertyById(cx, resolvedProps, id, JS::NullHandleValue))
+ return false;
+ *resolvedp = true;
+
+ return true;
+ }
+
+ bool hasMethod = npobj->_class->hasMethod(npobj, identifier);
+ if (!ReportExceptionIfPending(cx)) return false;
+
+ if (hasMethod) {
+ NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id),
+ "id must be either string or int!\n");
+
+ JSFunction* fnc = ::JS_DefineFunctionById(
+ cx, resolvedProps, id, CallNPMethod, 0, JSPROP_ENUMERATE);
+ if (!fnc) return false;
+
+ method.set(JS_GetFunctionObject(fnc));
+ *resolvedp = true;
+ return true;
+ }
+
+ // no property or method
+ return true;
+}
+
+void NPObjWrapperProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const {
+ JS::AutoAssertGCCallback inCallback;
+
+ NPObject* npobj = (NPObject*)js::GetProxyPrivate(proxy).toPrivate();
+ if (npobj) {
+ if (sNPObjWrappers) {
+ // If the sNPObjWrappers map contains an entry that refers to this
+ // wrapper, remove it.
+ auto entry =
+ static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
+ if (entry && entry->mJSObj == proxy) {
+ sNPObjWrappers->Remove(npobj);
+ }
+ }
+ }
+
+ if (!sDelayedReleases) sDelayedReleases = new nsTArray<NPObject*>;
+ sDelayedReleases->AppendElement(npobj);
+}
+
+size_t NPObjWrapperProxyHandler::objectMoved(JSObject* obj,
+ JSObject* old) const {
+ // The wrapper JSObject has been moved, so we need to update the entry in the
+ // sNPObjWrappers hash table, if present.
+
+ if (!sNPObjWrappers) {
+ return 0;
+ }
+
+ NPObject* npobj = (NPObject*)js::GetProxyPrivate(obj).toPrivate();
+ if (!npobj) {
+ return 0;
+ }
+
+ // Calling PLDHashTable::Search() will not result in GC.
+ JS::AutoSuppressGCAnalysis nogc;
+
+ auto entry =
+ static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
+ MOZ_ASSERT(entry && entry->mJSObj);
+ MOZ_ASSERT(entry->mJSObj == old);
+ entry->mJSObj = obj;
+ return 0;
+}
+
+bool NPObjWrapperProxyHandler::call(JSContext* cx, JS::Handle<JSObject*> proxy,
+ const JS::CallArgs& args) const {
+ return CallNPMethodInternal(cx, proxy, args.length(), args.array(),
+ args.rval().address(), false);
+}
+
+bool NPObjWrapperProxyHandler::construct(JSContext* cx,
+ JS::Handle<JSObject*> proxy,
+ const JS::CallArgs& args) const {
+ return CallNPMethodInternal(cx, proxy, args.length(), args.array(),
+ args.rval().address(), true);
+}
+
+static bool NPObjWrapper_toPrimitive(JSContext* cx, unsigned argc,
+ JS::Value* vp) {
+ // Plugins do not simply use the default OrdinaryToPrimitive behavior,
+ // because that behavior involves calling toString or valueOf on objects
+ // which weren't designed to accommodate this. Usually this wouldn't be a
+ // problem, because the absence of either property, or the presence of either
+ // property with a value that isn't callable, will cause that property to
+ // simply be ignored. But there is a problem in one specific case: Java,
+ // specifically java.lang.Integer. The Integer class has static valueOf
+ // methods, none of which are nullary, so the JS-reflected method will behave
+ // poorly when called with no arguments. We work around this problem by
+ // giving plugins a [Symbol.toPrimitive]() method which uses only toString
+ // and not valueOf.
+
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::RootedValue thisv(cx, args.thisv());
+ if (thisv.isPrimitive()) return true;
+
+ JS::RootedObject obj(cx, &thisv.toObject());
+ JS::RootedValue v(cx);
+ if (!JS_GetProperty(cx, obj, "toString", &v)) return false;
+ if (v.isObject() && JS::IsCallable(&v.toObject())) {
+ if (!JS_CallFunctionValue(cx, obj, v, JS::HandleValueArray::empty(),
+ args.rval()))
+ return false;
+ if (args.rval().isPrimitive()) return true;
+ }
+
+ JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr,
+ JSMSG_CANT_CONVERT_TO, JS::GetClass(obj)->name,
+ "primitive type");
+ return false;
+}
+
+bool nsNPObjWrapper::IsWrapper(JSObject* obj) {
+ return JS::GetClass(obj) == &sNPObjWrapperProxyClass;
+}
+
+// An NPObject is going away, make sure we null out the JS object's
+// private data in case this is an NPObject that came from a plugin
+// and it's destroyed prematurely.
+
+// static
+void nsNPObjWrapper::OnDestroy(NPObject* npobj) {
+ if (!npobj) {
+ return;
+ }
+
+ if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
+ // npobj is one of our own, no private data to clean up here.
+
+ return;
+ }
+
+ if (!sNPObjWrappers) {
+ // No hash yet (or any more), no used wrappers available.
+
+ return;
+ }
+
+ auto entry =
+ static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
+
+ if (entry && entry->mJSObj) {
+ // Found an NPObject wrapper, null out its JSObjects' private data.
+ js::SetProxyPrivate(entry->mJSObj.unbarrieredGetPtr(),
+ JS::PrivateValue(nullptr));
+
+ // Remove the npobj from the hash now that it went away.
+ sNPObjWrappers->RawRemove(entry);
+
+ // The finalize hook will call OnWrapperDestroyed().
+ }
+}
+
+// Look up or create a JSObject that wraps the NPObject npobj. The return value
+// is always in the compartment of the passed-in JSContext (it might be a CCW).
+
+// static
+JSObject* nsNPObjWrapper::GetNewOrUsed(NPP npp, JSContext* cx,
+ NPObject* npobj) {
+ if (!npobj) {
+ NS_ERROR("Null NPObject passed to nsNPObjWrapper::GetNewOrUsed()!");
+
+ return nullptr;
+ }
+
+ if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
+ // npobj is one of our own, return its existing JSObject.
+
+ JS::Rooted<JSObject*> obj(cx, ((nsJSObjWrapper*)npobj)->mJSObj);
+ if (!JS_WrapObject(cx, &obj)) {
+ return nullptr;
+ }
+ return obj;
+ }
+
+ if (!npp) {
+ NS_ERROR("No npp passed to nsNPObjWrapper::GetNewOrUsed()!");
+
+ return nullptr;
+ }
+
+ if (!sNPObjWrappers) {
+ // No hash yet (or any more), initialize it.
+ if (!CreateNPObjWrapperTable()) {
+ return nullptr;
+ }
+ }
+
+ auto entry =
+ static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Add(npobj, fallible));
+
+ if (!entry) {
+ // Out of memory
+ JS_ReportOutOfMemory(cx);
+
+ return nullptr;
+ }
+
+ if (entry->mJSObj) {
+ // Found a NPObject wrapper. First check it is still alive.
+ JSObject* obj = entry->mJSObj.unbarrieredGetPtr();
+ if (js::gc::EdgeNeedsSweepUnbarriered(&obj)) {
+ // The object is dead (finalization will happen at a later time). By the
+ // time we leave this function, this entry will either be updated with a
+ // new wrapper or removed if that fails. Clear it anyway to make sure
+ // nothing touches the dead object.
+ entry->mJSObj = nullptr;
+ } else {
+ // It may not be in the same compartment as cx, so we need to wrap it
+ // before returning it.
+ JS::Rooted<JSObject*> obj(cx, entry->mJSObj);
+ if (!JS_WrapObject(cx, &obj)) {
+ return nullptr;
+ }
+ return obj;
+ }
+ }
+
+ entry->mNPObj = npobj;
+ entry->mNpp = npp;
+
+ uint32_t generation = sNPObjWrappers->Generation();
+
+ // No existing JSObject, create one.
+
+ JS::RootedValue priv(cx, JS::PrivateValue(nullptr));
+ js::ProxyOptions options;
+ options.setClass(&sNPObjWrapperProxyClass);
+ JS::Rooted<JSObject*> obj(
+ cx, js::NewProxyObject(cx, &NPObjWrapperProxyHandler::singleton, priv,
+ nullptr, options));
+
+ if (generation != sNPObjWrappers->Generation()) {
+ // Reload entry if the JS_NewObject call caused a GC and reallocated
+ // the table (see bug 445229). This is guaranteed to succeed.
+
+ entry = static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
+ NS_ASSERTION(entry, "Hashtable didn't find what we just added?");
+ }
+
+ if (!obj) {
+ // OOM? Remove the stale entry from the hash.
+
+ sNPObjWrappers->RawRemove(entry);
+
+ return nullptr;
+ }
+
+ OnWrapperCreated();
+
+ entry->mJSObj = obj;
+
+ js::SetProxyPrivate(obj, JS::PrivateValue(npobj));
+
+ // The new JSObject now holds on to npobj
+ _retainobject(npobj);
+
+ return obj;
+}
+
+// static
+void nsJSNPRuntime::OnPluginDestroy(NPP npp) {
+ if (sJSObjWrappersAccessible) {
+ // Prevent modification of sJSObjWrappers table if we go reentrant.
+ sJSObjWrappersAccessible = false;
+
+ for (auto iter = sJSObjWrappers->modIter(); !iter.done(); iter.next()) {
+ nsJSObjWrapper* npobj = iter.get().value();
+ MOZ_ASSERT(npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass);
+ if (npobj->mNpp == npp) {
+ if (npobj->_class && npobj->_class->invalidate) {
+ npobj->_class->invalidate(npobj);
+ }
+
+ _releaseobject(npobj);
+
+ iter.remove();
+ }
+ }
+
+ sJSObjWrappersAccessible = true;
+ }
+
+ if (sNPObjWrappers) {
+ for (auto i = sNPObjWrappers->Iter(); !i.Done(); i.Next()) {
+ auto entry = static_cast<NPObjWrapperHashEntry*>(i.Get());
+
+ if (entry->mNpp == npp) {
+ // HACK: temporarily hide the table we're enumerating so that
+ // invalidate() and deallocate() don't touch it.
+ PLDHashTable* tmp = sNPObjWrappers;
+ sNPObjWrappers = nullptr;
+
+ NPObject* npobj = entry->mNPObj;
+
+ if (npobj->_class && npobj->_class->invalidate) {
+ npobj->_class->invalidate(npobj);
+ }
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ {
+ int32_t refCnt = npobj->referenceCount;
+ while (refCnt) {
+ --refCnt;
+ NS_LOG_RELEASE(npobj, refCnt, "BrowserNPObject");
+ }
+ }
+#endif
+
+ // Force deallocation of plugin objects since the plugin they came
+ // from is being torn down.
+ if (npobj->_class && npobj->_class->deallocate) {
+ npobj->_class->deallocate(npobj);
+ } else {
+ free(npobj);
+ }
+
+ js::SetProxyPrivate(entry->mJSObj.unbarrieredGetPtr(),
+ JS::PrivateValue(nullptr));
+
+ sNPObjWrappers = tmp;
+
+ if (sDelayedReleases && sDelayedReleases->RemoveElement(npobj)) {
+ OnWrapperDestroyed();
+ }
+
+ i.Remove();
+ }
+ }
+ }
+}
+
+// static
+void nsJSNPRuntime::OnPluginDestroyPending(NPP npp) {
+ if (sJSObjWrappersAccessible) {
+ // Prevent modification of sJSObjWrappers table if we go reentrant.
+ sJSObjWrappersAccessible = false;
+ for (auto iter = sJSObjWrappers->iter(); !iter.done(); iter.next()) {
+ nsJSObjWrapper* npobj = iter.get().value();
+ MOZ_ASSERT(npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass);
+ if (npobj->mNpp == npp) {
+ npobj->mDestroyPending = true;
+ }
+ }
+ sJSObjWrappersAccessible = true;
+ }
+}
+
+// Find the NPP for a NPObject.
+static NPP LookupNPP(NPObject* npobj) {
+ if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
+ nsJSObjWrapper* o = static_cast<nsJSObjWrapper*>(npobj);
+ return o->mNpp;
+ }
+
+ auto entry =
+ static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Add(npobj, fallible));
+
+ if (!entry) {
+ return nullptr;
+ }
+
+ NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!");
+
+ return entry->mNpp;
+}
+
+static bool CreateNPObjectMember(NPP npp, JSContext* cx,
+ JS::Handle<JSObject*> aObj, NPObject* npobj,
+ JS::Handle<jsid> id,
+ NPVariant* getPropertyResult,
+ JS::MutableHandle<JS::Value> vp) {
+ if (!npobj || !npobj->_class || !npobj->_class->getProperty ||
+ !npobj->_class->invoke) {
+ ThrowJSExceptionASCII(cx, "Bad NPObject");
+
+ return false;
+ }
+
+ NPObjectMemberPrivate* memberPrivate = new NPObjectMemberPrivate;
+
+ JS::Rooted<JSObject*> obj(cx, aObj);
+
+ JS::Rooted<JSObject*> memobj(cx, ::JS_NewObject(cx, &sNPObjectMemberClass));
+ if (!memobj) {
+ delete memberPrivate;
+ return false;
+ }
+
+ vp.setObject(*memobj);
+
+ JS::SetPrivate(memobj, (void*)memberPrivate);
+
+ NPIdentifier identifier = JSIdToNPIdentifier(id);
+
+ JS::Rooted<JS::Value> fieldValue(cx);
+ NPVariant npv;
+
+ if (getPropertyResult) {
+ // Plugin has already handed us the value we want here.
+ npv = *getPropertyResult;
+ } else {
+ VOID_TO_NPVARIANT(npv);
+
+ NPBool hasProperty = npobj->_class->getProperty(npobj, identifier, &npv);
+ if (!ReportExceptionIfPending(cx) || !hasProperty) {
+ return false;
+ }
+ }
+
+ fieldValue = NPVariantToJSVal(npp, cx, &npv);
+
+ // npobjWrapper is the JSObject through which we make sure we don't
+ // outlive the underlying NPObject, so make sure it points to the
+ // real JSObject wrapper for the NPObject.
+ obj = GetNPObjectWrapper(cx, obj);
+
+ memberPrivate->npobjWrapper = obj;
+
+ memberPrivate->fieldValue = fieldValue;
+ memberPrivate->methodName = id;
+ memberPrivate->npp = npp;
+
+ // Finally, define the Symbol.toPrimitive property on |memobj|.
+
+ JS::Rooted<jsid> toPrimitiveId(cx);
+ toPrimitiveId =
+ SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::toPrimitive));
+
+ JSFunction* fun = JS_NewFunction(cx, NPObjectMember_toPrimitive, 1, 0,
+ "Symbol.toPrimitive");
+ if (!fun) return false;
+
+ JS::Rooted<JSObject*> funObj(cx, JS_GetFunctionObject(fun));
+ if (!JS_DefinePropertyById(cx, memobj, toPrimitiveId, funObj, 0))
+ return false;
+
+ return true;
+}
+
+static void NPObjectMember_Finalize(JSFreeOp* fop, JSObject* obj) {
+ NPObjectMemberPrivate* memberPrivate;
+
+ memberPrivate = (NPObjectMemberPrivate*)JS::GetPrivate(obj);
+ if (!memberPrivate) return;
+
+ delete memberPrivate;
+}
+
+static bool NPObjectMember_Call(JSContext* cx, unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::Rooted<JSObject*> memobj(cx, &args.callee());
+ NS_ENSURE_TRUE(memobj, false);
+
+ NPObjectMemberPrivate* memberPrivate =
+ (NPObjectMemberPrivate*)::JS_GetInstancePrivate(
+ cx, memobj, &sNPObjectMemberClass, &args);
+ if (!memberPrivate || !memberPrivate->npobjWrapper) return false;
+
+ JS::Rooted<JSObject*> objWrapper(cx, memberPrivate->npobjWrapper);
+ NPObject* npobj = GetNPObject(cx, objWrapper);
+ if (!npobj) {
+ ThrowJSExceptionASCII(cx, "Call on invalid member object");
+
+ return false;
+ }
+
+ NPVariant npargs_buf[8];
+ NPVariant* npargs = npargs_buf;
+
+ if (args.length() > (sizeof(npargs_buf) / sizeof(NPVariant))) {
+ // Our stack buffer isn't large enough to hold all arguments,
+ // malloc a buffer.
+ npargs = (NPVariant*)malloc(args.length() * sizeof(NPVariant));
+
+ if (!npargs) {
+ ThrowJSExceptionASCII(cx, "Out of memory!");
+
+ return false;
+ }
+ }
+
+ // Convert arguments
+ for (uint32_t i = 0; i < args.length(); ++i) {
+ if (!JSValToNPVariant(memberPrivate->npp, cx, args[i], npargs + i)) {
+ ThrowJSExceptionASCII(cx, "Error converting jsvals to NPVariants!");
+
+ if (npargs != npargs_buf) {
+ free(npargs);
+ }
+
+ return false;
+ }
+ }
+
+ NPVariant npv;
+ bool ok = npobj->_class->invoke(npobj,
+ JSIdToNPIdentifier(memberPrivate->methodName),
+ npargs, args.length(), &npv);
+
+ // Release arguments.
+ for (uint32_t i = 0; i < args.length(); ++i) {
+ _releasevariantvalue(npargs + i);
+ }
+
+ if (npargs != npargs_buf) {
+ free(npargs);
+ }
+
+ if (!ok) {
+ // ReportExceptionIfPending returns a return value, which is true
+ // if no exception was thrown. In that case, throw our own.
+ if (ReportExceptionIfPending(cx))
+ ThrowJSExceptionASCII(cx, "Error calling method on NPObject!");
+
+ return false;
+ }
+
+ args.rval().set(NPVariantToJSVal(memberPrivate->npp, cx, &npv));
+
+ // *vp now owns the value, release our reference.
+ _releasevariantvalue(&npv);
+
+ return ReportExceptionIfPending(cx);
+}
+
+static void NPObjectMember_Trace(JSTracer* trc, JSObject* obj) {
+ auto* memberPrivate = (NPObjectMemberPrivate*)JS::GetPrivate(obj);
+ if (!memberPrivate) return;
+
+ // Our NPIdentifier is not always interned, so we must trace it.
+ JS::TraceEdge(trc, &memberPrivate->methodName,
+ "NPObjectMemberPrivate.methodName");
+
+ JS::TraceEdge(trc, &memberPrivate->fieldValue,
+ "NPObject Member => fieldValue");
+
+ // There's no strong reference from our private data to the
+ // NPObject, so make sure to mark the NPObject wrapper to keep the
+ // NPObject alive as long as this NPObjectMember is alive.
+ JS::TraceEdge(trc, &memberPrivate->npobjWrapper,
+ "NPObject Member => npobjWrapper");
+}
+
+static bool NPObjectMember_toPrimitive(JSContext* cx, unsigned argc,
+ JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::RootedValue thisv(cx, args.thisv());
+ if (thisv.isPrimitive()) {
+ args.rval().set(thisv);
+ return true;
+ }
+
+ JS::RootedObject obj(cx, &thisv.toObject());
+ NPObjectMemberPrivate* memberPrivate =
+ (NPObjectMemberPrivate*)::JS_GetInstancePrivate(
+ cx, obj, &sNPObjectMemberClass, &args);
+ if (!memberPrivate) return false;
+
+ JSType hint;
+ if (!JS::GetFirstArgumentAsTypeHint(cx, args, &hint)) return false;
+
+ args.rval().set(memberPrivate->fieldValue);
+ if (args.rval().isObject()) {
+ JS::Rooted<JSObject*> objVal(cx, &args.rval().toObject());
+ return JS::ToPrimitive(cx, objVal, hint, args.rval());
+ }
+ return true;
+}
diff --git a/dom/plugins/base/nsJSNPRuntime.h b/dom/plugins/base/nsJSNPRuntime.h
new file mode 100644
index 0000000000..c1e877f859
--- /dev/null
+++ b/dom/plugins/base/nsJSNPRuntime.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsJSNPRuntime_h_
+#define nsJSNPRuntime_h_
+
+#include "nscore.h"
+#include "npapi.h"
+#include "npruntime.h"
+#include "PLDHashTable.h"
+#include "js/RootingAPI.h"
+
+class nsJSNPRuntime {
+ public:
+ static void OnPluginDestroy(NPP npp);
+ static void OnPluginDestroyPending(NPP npp);
+};
+
+class nsJSObjWrapperKey {
+ public:
+ nsJSObjWrapperKey(JSObject* obj, NPP npp) : mJSObj(obj), mNpp(npp) {}
+
+ bool operator==(const nsJSObjWrapperKey& other) const {
+ return mJSObj == other.mJSObj && mNpp == other.mNpp;
+ }
+ bool operator!=(const nsJSObjWrapperKey& other) const {
+ return !(*this == other);
+ }
+
+ void trace(JSTracer* trc) {
+ JS::TraceEdge(trc, &mJSObj, "nsJSObjWrapperKey");
+ }
+
+ nsJSObjWrapperKey(nsJSObjWrapperKey&& other) = default;
+ nsJSObjWrapperKey& operator=(nsJSObjWrapperKey&& other) = default;
+
+ JS::Heap<JSObject*> mJSObj;
+ NPP mNpp;
+};
+
+class nsJSObjWrapper : public NPObject {
+ public:
+ JS::Heap<JSObject*> mJSObj;
+ // Because mJSObj might be a cross-compartment wrapper, we can't use it to
+ // enter the target realm. We use this global instead (it's always
+ // same-compartment with mJSObj).
+ JS::Heap<JSObject*> mJSObjGlobal;
+ const NPP mNpp;
+ bool mDestroyPending;
+
+ static NPObject* GetNewOrUsed(NPP npp, JS::Handle<JSObject*> obj,
+ JS::Handle<JSObject*> objGlobal);
+
+ void trace(JSTracer* trc) {
+ JS::TraceEdge(trc, &mJSObj, "nsJSObjWrapper::mJSObj");
+ JS::TraceEdge(trc, &mJSObjGlobal, "nsJSObjWrapper::mJSObjGlobal");
+ }
+
+ protected:
+ explicit nsJSObjWrapper(NPP npp);
+ ~nsJSObjWrapper();
+
+ static NPObject* NP_Allocate(NPP npp, NPClass* aClass);
+ static void NP_Deallocate(NPObject* obj);
+ static void NP_Invalidate(NPObject* obj);
+ static bool NP_HasMethod(NPObject*, NPIdentifier identifier);
+ static bool NP_Invoke(NPObject* obj, NPIdentifier method,
+ const NPVariant* args, uint32_t argCount,
+ NPVariant* result);
+ static bool NP_InvokeDefault(NPObject* obj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+ static bool NP_HasProperty(NPObject* obj, NPIdentifier property);
+ static bool NP_GetProperty(NPObject* obj, NPIdentifier property,
+ NPVariant* result);
+ static bool NP_SetProperty(NPObject* obj, NPIdentifier property,
+ const NPVariant* value);
+ static bool NP_RemoveProperty(NPObject* obj, NPIdentifier property);
+ static bool NP_Enumerate(NPObject* npobj, NPIdentifier** identifier,
+ uint32_t* count);
+ static bool NP_Construct(NPObject* obj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+
+ public:
+ static NPClass sJSObjWrapperNPClass;
+};
+
+class nsNPObjWrapper {
+ public:
+ static bool IsWrapper(JSObject* obj);
+ static void OnDestroy(NPObject* npobj);
+ static JSObject* GetNewOrUsed(NPP npp, JSContext* cx, NPObject* npobj);
+};
+
+bool JSValToNPVariant(NPP npp, JSContext* cx, const JS::Value& val,
+ NPVariant* variant);
+
+#endif // nsJSNPRuntime_h_
diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp
new file mode 100644
index 0000000000..6d302573b4
--- /dev/null
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -0,0 +1,1884 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "base/basictypes.h"
+
+/* This must occur *after* layers/PLayerTransaction.h to avoid typedefs
+ * conflicts. */
+#include "mozilla/ArrayUtils.h"
+
+#include "pratom.h"
+#include "prenv.h"
+
+#include "jsfriendapi.h"
+#include "js/friend/WindowProxy.h" // js::ToWindowIfWindowProxy
+#include "js/Object.h" // JS::GetCompartment
+
+#include "nsPluginHost.h"
+#include "nsNPAPIPlugin.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsNPAPIPluginStreamListener.h"
+#include "nsPluginStreamListenerPeer.h"
+#include "nsThreadUtils.h"
+#include "mozilla/CycleCollectedJSContext.h" // for nsAutoMicroTask
+#include "mozilla/Preferences.h"
+#include "nsPluginInstanceOwner.h"
+
+#include "nsPluginsDir.h"
+#include "nsPluginLogging.h"
+
+#include "nsPIDOMWindow.h"
+#include "nsGlobalWindow.h"
+#include "mozilla/dom/Document.h"
+#include "nsIContent.h"
+#include "nsIIDNService.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIScriptContext.h"
+#include "nsDOMJSUtils.h"
+#include "nsIPrincipal.h"
+#include "nsWildCard.h"
+#include "nsContentUtils.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/JSExecutionContext.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "nsIXPConnect.h"
+#include "nsMemory.h"
+
+#include <prinrval.h>
+
+#ifdef MOZ_WIDGET_COCOA
+# include <Carbon/Carbon.h>
+# include <ApplicationServices/ApplicationServices.h>
+# include <OpenGL/OpenGL.h>
+# include "nsCocoaFeatures.h"
+# include "PluginUtilsOSX.h"
+#endif
+
+// needed for nppdf plugin
+#if (MOZ_WIDGET_GTK)
+# include <gdk/gdk.h>
+# include <gdk/gdkx.h>
+#endif
+
+#include "nsJSUtils.h"
+#include "nsJSNPRuntime.h"
+
+#include "nsNetUtil.h"
+#include "nsNetCID.h"
+
+#include "mozilla/Mutex.h"
+#include "mozilla/PluginLibrary.h"
+using mozilla::PluginLibrary;
+
+#include "mozilla/plugins/PluginModuleParent.h"
+using mozilla::plugins::PluginModuleChromeParent;
+using mozilla::plugins::PluginModuleContentParent;
+
+#ifdef MOZ_X11
+# include "mozilla/X11Util.h"
+#endif
+
+#ifdef XP_WIN
+# include <windows.h>
+# include "mozilla/WindowsVersion.h"
+# ifdef ACCESSIBILITY
+# include "mozilla/a11y/Compatibility.h"
+# endif
+#endif
+
+#include "AudioChannelService.h"
+
+using namespace mozilla;
+using namespace mozilla::plugins::parent;
+using mozilla::dom::Document;
+using mozilla::dom::JSExecutionContext;
+
+// We should make this const...
+static NPNetscapeFuncs sBrowserFuncs = {
+ sizeof(sBrowserFuncs),
+ (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR,
+ _geturl,
+ _posturl,
+ _requestread,
+ nullptr, // _newstream, unimplemented
+ nullptr, // _write, unimplemented
+ nullptr, // _destroystream, unimplemented
+ _status,
+ _useragent,
+ _memalloc,
+ _memfree,
+ _memflush,
+ _reloadplugins,
+ _getJavaEnv,
+ _getJavaPeer,
+ _geturlnotify,
+ _posturlnotify,
+ _getvalue,
+ _setvalue,
+ _invalidaterect,
+ _invalidateregion,
+ _forceredraw,
+ _getstringidentifier,
+ _getstringidentifiers,
+ _getintidentifier,
+ _identifierisstring,
+ _utf8fromidentifier,
+ _intfromidentifier,
+ _createobject,
+ _retainobject,
+ _releaseobject,
+ _invoke,
+ _invokeDefault,
+ _evaluate,
+ _getproperty,
+ _setproperty,
+ _removeproperty,
+ _hasproperty,
+ _hasmethod,
+ _releasevariantvalue,
+ _setexception,
+ _pushpopupsenabledstate,
+ _poppopupsenabledstate,
+ _enumerate,
+ nullptr, // pluginthreadasynccall, not used
+ _construct,
+ _getvalueforurl,
+ _setvalueforurl,
+ nullptr, // NPN GetAuthenticationInfo, not supported
+ _scheduletimer,
+ _unscheduletimer,
+ _popupcontextmenu,
+ _convertpoint,
+ nullptr, // handleevent, unimplemented
+ nullptr, // unfocusinstance, unimplemented
+ _urlredirectresponse,
+ _initasyncsurface,
+ _finalizeasyncsurface,
+ _setcurrentasyncsurface};
+
+// POST/GET stream type
+enum eNPPStreamTypeInternal {
+ eNPPStreamTypeInternal_Get,
+ eNPPStreamTypeInternal_Post
+};
+
+void NS_NotifyBeginPluginCall(NSPluginCallReentry aReentryState) {
+ nsNPAPIPluginInstance::BeginPluginCall(aReentryState);
+}
+
+void NS_NotifyPluginCall(NSPluginCallReentry aReentryState) {
+ nsNPAPIPluginInstance::EndPluginCall(aReentryState);
+}
+
+nsNPAPIPlugin::nsNPAPIPlugin() {
+ memset((void*)&mPluginFuncs, 0, sizeof(mPluginFuncs));
+ mPluginFuncs.size = sizeof(mPluginFuncs);
+ mPluginFuncs.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+
+ mLibrary = nullptr;
+}
+
+nsNPAPIPlugin::~nsNPAPIPlugin() {
+ delete mLibrary;
+ mLibrary = nullptr;
+}
+
+void nsNPAPIPlugin::PluginCrashed(const nsAString& aPluginDumpID,
+ const nsACString& aAdditionalMinidumps) {
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ host->PluginCrashed(this, aPluginDumpID, aAdditionalMinidumps);
+}
+
+inline PluginLibrary* GetNewPluginLibrary(nsPluginTag* aPluginTag) {
+ AUTO_PROFILER_LABEL("GetNewPluginLibrary", OTHER);
+
+ if (!aPluginTag) {
+ return nullptr;
+ }
+
+ if (XRE_IsContentProcess()) {
+ return PluginModuleContentParent::LoadModule(aPluginTag->mId, aPluginTag);
+ }
+
+ return PluginModuleChromeParent::LoadModule(aPluginTag->mFullPath.get(),
+ aPluginTag->mId, aPluginTag);
+}
+
+// Creates an nsNPAPIPlugin object. One nsNPAPIPlugin object exists per plugin
+// (not instance).
+nsresult nsNPAPIPlugin::CreatePlugin(nsPluginTag* aPluginTag,
+ nsNPAPIPlugin** aResult) {
+ AUTO_PROFILER_LABEL("nsNPAPIPlugin::CreatePlugin", OTHER);
+ *aResult = nullptr;
+
+ if (!aPluginTag) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<nsNPAPIPlugin> plugin = new nsNPAPIPlugin();
+
+ PluginLibrary* pluginLib = GetNewPluginLibrary(aPluginTag);
+ if (!pluginLib) {
+ return NS_ERROR_FAILURE;
+ }
+
+#if defined(XP_MACOSX)
+ if (!pluginLib->HasRequiredFunctions()) {
+ NS_WARNING(
+ "Not all necessary functions exposed by plugin, it will not load.");
+ delete pluginLib;
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+ plugin->mLibrary = pluginLib;
+ pluginLib->SetPlugin(plugin);
+
+// Exchange NPAPI entry points.
+#if defined(XP_WIN)
+ // NP_GetEntryPoints must be called before NP_Initialize on Windows.
+ NPError pluginCallError;
+ nsresult rv =
+ pluginLib->NP_GetEntryPoints(&plugin->mPluginFuncs, &pluginCallError);
+ if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // NP_Initialize must be called after NP_GetEntryPoints on Windows.
+ rv = pluginLib->NP_Initialize(&sBrowserFuncs, &pluginCallError);
+ if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
+ return NS_ERROR_FAILURE;
+ }
+#elif defined(XP_MACOSX)
+ // NP_Initialize must be called before NP_GetEntryPoints on Mac OS X.
+ // We need to match WebKit's behavior.
+ NPError pluginCallError;
+ nsresult rv = pluginLib->NP_Initialize(&sBrowserFuncs, &pluginCallError);
+ if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = pluginLib->NP_GetEntryPoints(&plugin->mPluginFuncs, &pluginCallError);
+ if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
+ return NS_ERROR_FAILURE;
+ }
+#else
+ NPError pluginCallError;
+ nsresult rv = pluginLib->NP_Initialize(&sBrowserFuncs, &plugin->mPluginFuncs,
+ &pluginCallError);
+ if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+ plugin.forget(aResult);
+ return NS_OK;
+}
+
+PluginLibrary* nsNPAPIPlugin::GetLibrary() { return mLibrary; }
+
+NPPluginFuncs* nsNPAPIPlugin::PluginFuncs() { return &mPluginFuncs; }
+
+nsresult nsNPAPIPlugin::Shutdown() {
+ NPP_PLUGIN_LOG(PLUGIN_LOG_BASIC,
+ ("NPP Shutdown to be called: this=%p\n", this));
+
+ NPError shutdownError;
+ mLibrary->NP_Shutdown(&shutdownError);
+
+ return NS_OK;
+}
+
+nsresult nsNPAPIPlugin::RetainStream(NPStream* pstream,
+ nsISupports** aRetainedPeer) {
+ if (!aRetainedPeer) return NS_ERROR_NULL_POINTER;
+
+ *aRetainedPeer = nullptr;
+
+ if (!pstream || !pstream->ndata) return NS_ERROR_NULL_POINTER;
+
+ nsNPAPIStreamWrapper* streamWrapper =
+ static_cast<nsNPAPIStreamWrapper*>(pstream->ndata);
+ nsNPAPIPluginStreamListener* listener = streamWrapper->GetStreamListener();
+ if (!listener) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsIStreamListener* streamListener = listener->GetStreamListenerPeer();
+ if (!streamListener) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *aRetainedPeer = streamListener;
+ NS_ADDREF(*aRetainedPeer);
+ return NS_OK;
+}
+
+// Create a new NPP GET or POST (given in the type argument) url
+// stream that may have a notify callback
+NPError MakeNewNPAPIStreamInternal(NPP npp, const char* relativeURL,
+ const char* target,
+ eNPPStreamTypeInternal type,
+ bool bDoNotify = false,
+ void* notifyData = nullptr, uint32_t len = 0,
+ const char* buf = nullptr) {
+ if (!npp) return NPERR_INVALID_INSTANCE_ERROR;
+
+ PluginDestructionGuard guard(npp);
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)npp->ndata;
+ if (!inst || !inst->IsRunning()) return NPERR_INVALID_INSTANCE_ERROR;
+
+ nsCOMPtr<nsIPluginHost> pluginHostCOM =
+ do_GetService(MOZ_PLUGIN_HOST_CONTRACTID);
+ nsPluginHost* pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
+ if (!pluginHost) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ RefPtr<nsNPAPIPluginStreamListener> listener;
+ // Set aCallNotify here to false. If pluginHost->GetURL or PostURL fail,
+ // the listener's destructor will do the notification while we are about to
+ // return a failure code.
+ // Call SetCallNotify(true) below after we are sure we cannot return a failure
+ // code.
+ if (!target) {
+ inst->NewStreamListener(relativeURL, notifyData, getter_AddRefs(listener));
+ if (listener) {
+ listener->SetCallNotify(false);
+ }
+ }
+
+ switch (type) {
+ case eNPPStreamTypeInternal_Get: {
+ if (NS_FAILED(pluginHost->GetURL(inst, relativeURL, target, listener,
+ nullptr, nullptr, false)))
+ return NPERR_GENERIC_ERROR;
+ break;
+ }
+ case eNPPStreamTypeInternal_Post: {
+ if (NS_FAILED(pluginHost->PostURL(inst, relativeURL, len, buf, target,
+ listener, nullptr, nullptr, false, 0,
+ nullptr)))
+ return NPERR_GENERIC_ERROR;
+ break;
+ }
+ default:
+ NS_ERROR("how'd I get here");
+ }
+
+ if (listener) {
+ // SetCallNotify(bDoNotify) here, see comment above.
+ listener->SetCallNotify(bDoNotify);
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+#if defined(MOZ_MEMORY) && defined(XP_WIN)
+extern "C" size_t malloc_usable_size(const void* ptr);
+#endif
+
+namespace {
+
+static char* gNPPException;
+
+static Document* GetDocumentFromNPP(NPP npp) {
+ NS_ENSURE_TRUE(npp, nullptr);
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)npp->ndata;
+ NS_ENSURE_TRUE(inst, nullptr);
+
+ PluginDestructionGuard guard(inst);
+
+ RefPtr<nsPluginInstanceOwner> owner = inst->GetOwner();
+ NS_ENSURE_TRUE(owner, nullptr);
+
+ nsCOMPtr<Document> doc;
+ owner->GetDocument(getter_AddRefs(doc));
+
+ return doc;
+}
+
+static NPIdentifier doGetIdentifier(JSContext* cx, const NPUTF8* name) {
+ NS_ConvertUTF8toUTF16 utf16name(name);
+
+ JSString* str =
+ ::JS_AtomizeAndPinUCStringN(cx, utf16name.get(), utf16name.Length());
+
+ if (!str) return nullptr;
+
+ return StringToNPIdentifier(cx, str);
+}
+
+#if defined(MOZ_MEMORY) && defined(XP_WIN)
+BOOL InHeap(HANDLE hHeap, LPVOID lpMem) {
+ BOOL success = FALSE;
+ PROCESS_HEAP_ENTRY he;
+ he.lpData = nullptr;
+ while (HeapWalk(hHeap, &he) != 0) {
+ if (he.lpData == lpMem) {
+ success = TRUE;
+ break;
+ }
+ }
+ HeapUnlock(hHeap);
+ return success;
+}
+#endif
+
+} /* anonymous namespace */
+
+NPPExceptionAutoHolder::NPPExceptionAutoHolder()
+ : mOldException(gNPPException) {
+ gNPPException = nullptr;
+}
+
+NPPExceptionAutoHolder::~NPPExceptionAutoHolder() {
+ NS_ASSERTION(!gNPPException, "NPP exception not properly cleared!");
+
+ gNPPException = mOldException;
+}
+
+NPP NPPStack::sCurrentNPP = nullptr;
+
+const char* PeekException() { return gNPPException; }
+
+void PopException() {
+ NS_ASSERTION(gNPPException, "Uh, no NPP exception to pop!");
+
+ if (gNPPException) {
+ free(gNPPException);
+
+ gNPPException = nullptr;
+ }
+}
+
+//
+// Static callbacks that get routed back through the new C++ API
+//
+
+namespace mozilla::plugins::parent {
+
+NPError _geturl(NPP npp, const char* relativeURL, const char* target) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_geturl called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_GetURL: npp=%p, target=%s, url=%s\n",
+ (void*)npp, target, relativeURL));
+
+ PluginDestructionGuard guard(npp);
+
+ return MakeNewNPAPIStreamInternal(npp, relativeURL, target,
+ eNPPStreamTypeInternal_Get);
+}
+
+NPError _geturlnotify(NPP npp, const char* relativeURL, const char* target,
+ void* notifyData) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_geturlnotify called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_GetURLNotify: npp=%p, target=%s, notify=%p, url=%s\n",
+ (void*)npp, target, notifyData, relativeURL));
+
+ PluginDestructionGuard guard(npp);
+
+ return MakeNewNPAPIStreamInternal(
+ npp, relativeURL, target, eNPPStreamTypeInternal_Get, true, notifyData);
+}
+
+NPError _posturlnotify(NPP npp, const char* relativeURL, const char* target,
+ uint32_t len, const char* buf, NPBool file,
+ void* notifyData) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_posturlnotify called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+ if (!buf) return NPERR_INVALID_PARAM;
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_PostURLNotify: npp=%p, target=%s, len=%d, file=%d, "
+ "notify=%p, url=%s, buf=%s\n",
+ (void*)npp, target, len, file, notifyData, relativeURL, buf));
+
+ PluginDestructionGuard guard(npp);
+
+ return MakeNewNPAPIStreamInternal(npp, relativeURL, target,
+ eNPPStreamTypeInternal_Post, true,
+ notifyData, len, buf);
+}
+
+NPError _posturl(NPP npp, const char* relativeURL, const char* target,
+ uint32_t len, const char* buf, NPBool file) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_posturl called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_PostURL: npp=%p, target=%s, file=%d, len=%d, url=%s, "
+ "buf=%s\n",
+ (void*)npp, target, file, len, relativeURL, buf));
+
+ PluginDestructionGuard guard(npp);
+
+ return MakeNewNPAPIStreamInternal(npp, relativeURL, target,
+ eNPPStreamTypeInternal_Post, false, nullptr,
+ len, buf);
+}
+
+void _status(NPP npp, const char* message) {
+ // NPN_Status is no longer supported.
+}
+
+void _memfree(void* ptr) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_memfree called from the wrong thread\n"));
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, ("NPN_MemFree: ptr=%p\n", ptr));
+
+ if (ptr) free(ptr);
+}
+
+uint32_t _memflush(uint32_t size) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_memflush called from the wrong thread\n"));
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, ("NPN_MemFlush: size=%d\n", size));
+
+ nsMemory::HeapMinimize(true);
+ return 0;
+}
+
+void _reloadplugins(NPBool reloadPages) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_reloadplugins called from the wrong thread\n"));
+ return;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_ReloadPlugins: reloadPages=%d\n", reloadPages));
+
+ nsCOMPtr<nsIPluginHost> pluginHost(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
+ if (!pluginHost) return;
+
+ pluginHost->ReloadPlugins();
+}
+
+void _invalidaterect(NPP npp, NPRect* invalidRect) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_invalidaterect called from the wrong thread\n"));
+ return;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_InvalidateRect: npp=%p, top=%d, left=%d, bottom=%d, "
+ "right=%d\n",
+ (void*)npp, invalidRect->top, invalidRect->left,
+ invalidRect->bottom, invalidRect->right));
+
+ if (!npp || !npp->ndata) {
+ NS_WARNING("_invalidaterect: npp or npp->ndata == 0");
+ return;
+ }
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)npp->ndata;
+
+ PluginDestructionGuard guard(inst);
+
+ inst->InvalidateRect((NPRect*)invalidRect);
+}
+
+void _invalidateregion(NPP npp, NPRegion invalidRegion) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_invalidateregion called from the wrong thread\n"));
+ return;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_InvalidateRegion: npp=%p, region=%p\n", (void*)npp,
+ (void*)invalidRegion));
+
+ if (!npp || !npp->ndata) {
+ NS_WARNING("_invalidateregion: npp or npp->ndata == 0");
+ return;
+ }
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)npp->ndata;
+
+ PluginDestructionGuard guard(inst);
+
+ inst->InvalidateRegion((NPRegion)invalidRegion);
+}
+
+void _forceredraw(NPP npp) {}
+
+NPObject* _getwindowobject(NPP npp) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_getwindowobject called from the wrong thread\n"));
+ return nullptr;
+ }
+
+ // The window want to return here is the outer window, *not* the inner (since
+ // we don't know what the plugin will do with it).
+ Document* doc = GetDocumentFromNPP(npp);
+ NS_ENSURE_TRUE(doc, nullptr);
+ nsCOMPtr<nsPIDOMWindowOuter> outer = doc->GetWindow();
+ NS_ENSURE_TRUE(outer, nullptr);
+
+ JS::Rooted<JSObject*> windowProxy(
+ dom::RootingCx(), nsGlobalWindowOuter::Cast(outer)->GetGlobalJSObject());
+ JS::Rooted<JSObject*> global(dom::RootingCx(),
+ JS::GetNonCCWObjectGlobal(windowProxy));
+ return nsJSObjWrapper::GetNewOrUsed(npp, windowProxy, global);
+}
+
+NPObject* _getpluginelement(NPP npp) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_getpluginelement called from the wrong thread\n"));
+ return nullptr;
+ }
+
+ nsNPAPIPluginInstance* inst = static_cast<nsNPAPIPluginInstance*>(npp->ndata);
+ if (!inst) return nullptr;
+
+ RefPtr<dom::Element> element;
+ inst->GetDOMElement(getter_AddRefs(element));
+
+ if (!element) return nullptr;
+
+ Document* doc = GetDocumentFromNPP(npp);
+ if (NS_WARN_IF(!doc)) {
+ return nullptr;
+ }
+
+ dom::AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(doc->GetInnerWindow()))) {
+ return nullptr;
+ }
+ JSContext* cx = jsapi.cx();
+
+ nsCOMPtr<nsIXPConnect> xpc(nsIXPConnect::XPConnect());
+ NS_ENSURE_TRUE(xpc, nullptr);
+
+ JS::RootedValue val(cx);
+ if (!ToJSValue(cx, element, &val)) {
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(!val.isObject())) {
+ return nullptr;
+ }
+
+ JS::RootedObject obj(cx, &val.toObject());
+ JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
+ return nsJSObjWrapper::GetNewOrUsed(npp, obj, global);
+}
+
+NPIdentifier _getstringidentifier(const NPUTF8* name) {
+ if (!name) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_getstringidentifier: passed null name"));
+ return nullptr;
+ }
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_getstringidentifier called from the wrong thread\n"));
+ }
+
+ AutoSafeJSContext cx;
+ return doGetIdentifier(cx, name);
+}
+
+void _getstringidentifiers(const NPUTF8** names, int32_t nameCount,
+ NPIdentifier* identifiers) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_getstringidentifiers called from the wrong thread\n"));
+ }
+
+ AutoSafeJSContext cx;
+
+ for (int32_t i = 0; i < nameCount; ++i) {
+ if (names[i]) {
+ identifiers[i] = doGetIdentifier(cx, names[i]);
+ } else {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_getstringidentifiers: passed null name"));
+ identifiers[i] = nullptr;
+ }
+ }
+}
+
+NPIdentifier _getintidentifier(int32_t intid) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_getstringidentifier called from the wrong thread\n"));
+ }
+ return IntToNPIdentifier(intid);
+}
+
+NPUTF8* _utf8fromidentifier(NPIdentifier id) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_utf8fromidentifier called from the wrong thread\n"));
+ }
+ if (!id) return nullptr;
+
+ if (!NPIdentifierIsString(id)) {
+ return nullptr;
+ }
+
+ JSString* str = NPIdentifierToString(id);
+ nsAutoString autoStr;
+ AssignJSLinearString(autoStr, JS_ASSERT_STRING_IS_LINEAR(str));
+
+ return ToNewUTF8String(autoStr);
+}
+
+int32_t _intfromidentifier(NPIdentifier id) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_intfromidentifier called from the wrong thread\n"));
+ }
+
+ if (!NPIdentifierIsInt(id)) {
+ return INT32_MIN;
+ }
+
+ return NPIdentifierToInt(id);
+}
+
+bool _identifierisstring(NPIdentifier id) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_identifierisstring called from the wrong thread\n"));
+ }
+
+ return NPIdentifierIsString(id);
+}
+
+NPObject* _createobject(NPP npp, NPClass* aClass) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_createobject called from the wrong thread\n"));
+ return nullptr;
+ }
+ if (!npp) {
+ NS_ERROR("Null npp passed to _createobject()!");
+
+ return nullptr;
+ }
+
+ PluginDestructionGuard guard(npp);
+
+ if (!aClass) {
+ NS_ERROR("Null class passed to _createobject()!");
+
+ return nullptr;
+ }
+
+ NPPAutoPusher nppPusher(npp);
+
+ NPObject* npobj;
+
+ if (aClass->allocate) {
+ npobj = aClass->allocate(npp, aClass);
+ } else {
+ npobj = (NPObject*)malloc(sizeof(NPObject));
+ }
+
+ if (npobj) {
+ npobj->_class = aClass;
+ npobj->referenceCount = 1;
+ NS_LOG_ADDREF(npobj, 1, "BrowserNPObject", sizeof(NPObject));
+ }
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("Created NPObject %p, NPClass %p\n", npobj, aClass));
+
+ return npobj;
+}
+
+NPObject* _retainobject(NPObject* npobj) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_retainobject called from the wrong thread\n"));
+ }
+ if (npobj) {
+#ifdef NS_BUILD_REFCNT_LOGGING
+ int32_t refCnt =
+#endif
+ PR_ATOMIC_INCREMENT((int32_t*)&npobj->referenceCount);
+ NS_LOG_ADDREF(npobj, refCnt, "BrowserNPObject", sizeof(NPObject));
+ }
+
+ return npobj;
+}
+
+void _releaseobject(NPObject* npobj) {
+ // If nothing is passed, just return, even if we're on the wrong thread.
+ if (!npobj) {
+ return;
+ }
+
+ int32_t refCnt = PR_ATOMIC_DECREMENT((int32_t*)&npobj->referenceCount);
+ NS_LOG_RELEASE(npobj, refCnt, "BrowserNPObject");
+
+ if (refCnt == 0) {
+ nsNPObjWrapper::OnDestroy(npobj);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("Deleting NPObject %p, refcount hit 0\n", npobj));
+
+ if (npobj->_class && npobj->_class->deallocate) {
+ npobj->_class->deallocate(npobj);
+ } else {
+ free(npobj);
+ }
+ }
+}
+
+bool _invoke(NPP npp, NPObject* npobj, NPIdentifier method,
+ const NPVariant* args, uint32_t argCount, NPVariant* result) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_invoke called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->invoke) return false;
+
+ PluginDestructionGuard guard(npp);
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_Invoke(npp %p, npobj %p, method %p, args %d\n", npp,
+ npobj, method, argCount));
+
+ return npobj->_class->invoke(npobj, method, args, argCount, result);
+}
+
+bool _invokeDefault(NPP npp, NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_invokedefault called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->invokeDefault)
+ return false;
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(
+ PLUGIN_LOG_NOISY,
+ ("NPN_InvokeDefault(npp %p, npobj %p, args %d\n", npp, npobj, argCount));
+
+ return npobj->_class->invokeDefault(npobj, args, argCount, result);
+}
+
+bool _evaluate(NPP npp, NPObject* npobj, NPString* script, NPVariant* result) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_evaluate called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp) return false;
+
+ NPPAutoPusher nppPusher(npp);
+
+ Document* doc = GetDocumentFromNPP(npp);
+ NS_ENSURE_TRUE(doc, false);
+
+ nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(doc->GetInnerWindow());
+ if (NS_WARN_IF(!win || !win->HasJSGlobal())) {
+ return false;
+ }
+
+ nsAutoMicroTask mt;
+ dom::AutoEntryScript aes(win, "NPAPI NPN_evaluate");
+ JSContext* cx = aes.cx();
+
+ JS::Rooted<JSObject*> obj(cx, nsNPObjWrapper::GetNewOrUsed(npp, cx, npobj));
+
+ if (!obj) {
+ return false;
+ }
+
+ obj = js::ToWindowIfWindowProxy(obj);
+ MOZ_ASSERT(obj, "ToWindowIfWindowProxy should never return null");
+
+ if (result) {
+ // Initialize the out param to void
+ VOID_TO_NPVARIANT(*result);
+ }
+
+ if (!script || !script->UTF8Length || !script->UTF8Characters) {
+ // Nothing to evaluate.
+
+ return true;
+ }
+
+ NS_ConvertUTF8toUTF16 utf16script(script->UTF8Characters, script->UTF8Length);
+
+ nsIPrincipal* principal = doc->NodePrincipal();
+
+ nsCString specStr;
+ const char* spec;
+
+ principal->GetAsciiSpec(specStr);
+ spec = specStr.get();
+
+ if (specStr.IsEmpty()) {
+ // No URI in a principal means it's the system principal. If the
+ // document URI is a chrome:// URI, pass that in as the URI of the
+ // script, else pass in null for the filename as there's no way to
+ // know where this document really came from. Passing in null here
+ // also means that the script gets treated by XPConnect as if it
+ // needs additional protection, which is what we want for unknown
+ // chrome code anyways.
+ nsCOMPtr<nsIURI> uri = doc->GetDocumentURI();
+ if (uri && uri->SchemeIs("chrome")) {
+ uri->GetSpec(specStr);
+ spec = specStr.get();
+ } else {
+ spec = nullptr;
+ }
+ }
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_Evaluate(npp %p, npobj %p, script <<<%s>>>) called\n",
+ npp, npobj, script->UTF8Characters));
+
+ JS::CompileOptions options(cx);
+ options.setFileAndLine(spec, 0);
+ JS::Rooted<JS::Value> rval(cx);
+ JS::RootedVector<JSObject*> scopeChain(cx);
+ if (!JS_IsGlobalObject(obj) && !scopeChain.append(obj)) {
+ return false;
+ }
+ // nsNPObjWrapper::GetNewOrUsed returns an object in the current compartment
+ // of the JSContext (it might be a CCW).
+ MOZ_RELEASE_ASSERT(JS::GetCompartment(obj) == js::GetContextCompartment(cx),
+ "nsNPObjWrapper::GetNewOrUsed must wrap its return value");
+ obj = JS::CurrentGlobalOrNull(cx);
+ MOZ_ASSERT(obj);
+ nsresult rv = NS_OK;
+ {
+ JSExecutionContext exec(cx, obj);
+ exec.SetScopeChain(scopeChain);
+ exec.Compile(options, utf16script);
+ rv = exec.ExecScript(&rval);
+ }
+
+ if (!JS_WrapValue(cx, &rval)) {
+ return false;
+ }
+
+ return NS_SUCCEEDED(rv) &&
+ (!result || JSValToNPVariant(npp, cx, rval, result));
+}
+
+bool _getproperty(NPP npp, NPObject* npobj, NPIdentifier property,
+ NPVariant* result) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_getproperty called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->getProperty)
+ return false;
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_GetProperty(npp %p, npobj %p, property %p) called\n",
+ npp, npobj, property));
+
+ if (!npobj->_class->getProperty(npobj, property, result)) return false;
+
+ return true;
+}
+
+bool _setproperty(NPP npp, NPObject* npobj, NPIdentifier property,
+ const NPVariant* value) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_setproperty called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->setProperty)
+ return false;
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_SetProperty(npp %p, npobj %p, property %p) called\n",
+ npp, npobj, property));
+
+ return npobj->_class->setProperty(npobj, property, value);
+}
+
+bool _removeproperty(NPP npp, NPObject* npobj, NPIdentifier property) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_removeproperty called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->removeProperty)
+ return false;
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_RemoveProperty(npp %p, npobj %p, property %p) called\n",
+ npp, npobj, property));
+
+ return npobj->_class->removeProperty(npobj, property);
+}
+
+bool _hasproperty(NPP npp, NPObject* npobj, NPIdentifier propertyName) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_hasproperty called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->hasProperty)
+ return false;
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_HasProperty(npp %p, npobj %p, property %p) called\n",
+ npp, npobj, propertyName));
+
+ return npobj->_class->hasProperty(npobj, propertyName);
+}
+
+bool _hasmethod(NPP npp, NPObject* npobj, NPIdentifier methodName) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_hasmethod called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class || !npobj->_class->hasMethod)
+ return false;
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_HasMethod(npp %p, npobj %p, property %p) called\n", npp,
+ npobj, methodName));
+
+ return npobj->_class->hasMethod(npobj, methodName);
+}
+
+bool _enumerate(NPP npp, NPObject* npobj, NPIdentifier** identifier,
+ uint32_t* count) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_enumerate called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class) return false;
+
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("NPN_Enumerate(npp %p, npobj %p) called\n", npp, npobj));
+
+ if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) ||
+ !npobj->_class->enumerate) {
+ *identifier = 0;
+ *count = 0;
+ return true;
+ }
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ return npobj->_class->enumerate(npobj, identifier, count);
+}
+
+bool _construct(NPP npp, NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_construct called from the wrong thread\n"));
+ return false;
+ }
+ if (!npp || !npobj || !npobj->_class ||
+ !NP_CLASS_STRUCT_VERSION_HAS_CTOR(npobj->_class) ||
+ !npobj->_class->construct) {
+ return false;
+ }
+
+ NPPExceptionAutoHolder nppExceptionHolder;
+ NPPAutoPusher nppPusher(npp);
+
+ return npobj->_class->construct(npobj, args, argCount, result);
+}
+
+void _releasevariantvalue(NPVariant* variant) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_releasevariantvalue called from the wrong thread\n"));
+ }
+ switch (variant->type) {
+ case NPVariantType_Void:
+ case NPVariantType_Null:
+ case NPVariantType_Bool:
+ case NPVariantType_Int32:
+ case NPVariantType_Double:
+ break;
+ case NPVariantType_String: {
+ const NPString* s = &NPVARIANT_TO_STRING(*variant);
+
+ if (s->UTF8Characters) {
+#if defined(MOZ_MEMORY) && defined(XP_WIN)
+ if (malloc_usable_size((void*)s->UTF8Characters) != 0) {
+ free((void*)s->UTF8Characters);
+ } else {
+ void* p = (void*)s->UTF8Characters;
+ DWORD nheaps = 0;
+ AutoTArray<HANDLE, 50> heaps;
+ nheaps = GetProcessHeaps(0, heaps.Elements());
+ heaps.AppendElements(nheaps);
+ GetProcessHeaps(nheaps, heaps.Elements());
+ for (DWORD i = 0; i < nheaps; i++) {
+ if (InHeap(heaps[i], p)) {
+ HeapFree(heaps[i], 0, p);
+ break;
+ }
+ }
+ }
+#else
+ free((void*)s->UTF8Characters);
+#endif
+ }
+ break;
+ }
+ case NPVariantType_Object: {
+ NPObject* npobj = NPVARIANT_TO_OBJECT(*variant);
+
+ if (npobj) _releaseobject(npobj);
+
+ break;
+ }
+ default:
+ NS_ERROR("Unknown NPVariant type!");
+ }
+
+ VOID_TO_NPVARIANT(*variant);
+}
+
+void _setexception(NPObject* npobj, const NPUTF8* message) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_setexception called from the wrong thread\n"));
+ return;
+ }
+
+ if (!message) return;
+
+ if (gNPPException) {
+ // If a plugin throws multiple exceptions, we'll only report the
+ // last one for now.
+ free(gNPPException);
+ }
+
+ gNPPException = strdup(message);
+}
+
+NPError _getvalue(NPP npp, NPNVariable variable, void* result) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_getvalue called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_GetValue: npp=%p, var=%d\n", (void*)npp, (int)variable));
+
+ nsresult res;
+
+ PluginDestructionGuard guard(npp);
+
+ // Cast NPNVariable enum to int to avoid warnings about including switch
+ // cases for android_npapi.h's non-standard ANPInterface values.
+ switch (static_cast<int>(variable)) {
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ case NPNVxDisplay: {
+# if defined(MOZ_X11)
+ if (npp) {
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)npp->ndata;
+ bool windowless = false;
+ inst->IsWindowless(&windowless);
+ // The documentation on the types for many variables in NP(N|P)_GetValue
+ // is vague. Often boolean values are NPBool (1 byte), but
+ // https://developer.mozilla.org/en/XEmbed_Extension_for_Mozilla_Plugins
+ // treats NPPVpluginNeedsXEmbed as PRBool (int), and
+ // on x86/32-bit, flash stores to this using |movl 0x1,&needsXEmbed|.
+ // thus we can't use NPBool for needsXEmbed, or the three bytes above
+ // it on the stack would get clobbered. so protect with the larger bool.
+ int needsXEmbed = 0;
+ if (!windowless) {
+ res = inst->GetValueFromPlugin(NPPVpluginNeedsXEmbed, &needsXEmbed);
+ // If the call returned an error code make sure we still use our
+ // default value.
+ if (NS_FAILED(res)) {
+ needsXEmbed = 0;
+ }
+ }
+ if (windowless || needsXEmbed) {
+ (*(Display**)result) = mozilla::DefaultXDisplay();
+ return NPERR_NO_ERROR;
+ }
+ }
+# endif
+ return NPERR_GENERIC_ERROR;
+ }
+
+ case NPNVxtAppContext:
+ return NPERR_GENERIC_ERROR;
+#endif
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ case NPNVnetscapeWindow: {
+ if (!npp || !npp->ndata) return NPERR_INVALID_INSTANCE_ERROR;
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)npp->ndata;
+
+ RefPtr<nsPluginInstanceOwner> owner = inst->GetOwner();
+ NS_ENSURE_TRUE(owner, NPERR_NO_ERROR);
+
+ if (NS_SUCCEEDED(owner->GetNetscapeWindow(result))) {
+ return NPERR_NO_ERROR;
+ }
+ return NPERR_GENERIC_ERROR;
+ }
+#endif
+
+ case NPNVjavascriptEnabledBool: {
+ *(NPBool*)result = false;
+ bool js = false;
+ res = Preferences::GetBool("javascript.enabled", &js);
+ if (NS_SUCCEEDED(res)) {
+ *(NPBool*)result = js;
+ }
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVasdEnabledBool:
+ *(NPBool*)result = false;
+ return NPERR_NO_ERROR;
+
+ case NPNVisOfflineBool: {
+ bool offline = false;
+ nsCOMPtr<nsIIOService> ioservice =
+ do_GetService(NS_IOSERVICE_CONTRACTID, &res);
+ if (NS_SUCCEEDED(res)) res = ioservice->GetOffline(&offline);
+ if (NS_FAILED(res)) return NPERR_GENERIC_ERROR;
+
+ *(NPBool*)result = offline;
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVToolkit: {
+#ifdef MOZ_WIDGET_GTK
+ *((NPNToolkitType*)result) = NPNVGtk2;
+#endif
+
+ if (*(NPNToolkitType*)result) return NPERR_NO_ERROR;
+
+ return NPERR_GENERIC_ERROR;
+ }
+
+ case NPNVSupportsXEmbedBool: {
+#ifdef MOZ_WIDGET_GTK
+ *(NPBool*)result = true;
+#else
+ *(NPBool*)result = false;
+#endif
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVWindowNPObject: {
+ *(NPObject**)result = _getwindowobject(npp);
+
+ return *(NPObject**)result ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
+ }
+
+ case NPNVPluginElementNPObject: {
+ *(NPObject**)result = _getpluginelement(npp);
+
+ return *(NPObject**)result ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
+ }
+
+ case NPNVSupportsWindowless: {
+#if defined(XP_WIN) || defined(XP_MACOSX) || \
+ (defined(MOZ_X11) && defined(MOZ_WIDGET_GTK))
+ *(NPBool*)result = true;
+#else
+ *(NPBool*)result = false;
+#endif
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVprivateModeBool: {
+ bool privacy;
+ nsNPAPIPluginInstance* inst =
+ static_cast<nsNPAPIPluginInstance*>(npp->ndata);
+ if (!inst) return NPERR_GENERIC_ERROR;
+
+ nsresult rv = inst->IsPrivateBrowsing(&privacy);
+ if (NS_FAILED(rv)) return NPERR_GENERIC_ERROR;
+ *(NPBool*)result = (NPBool)privacy;
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVdocumentOrigin: {
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)npp->ndata;
+ if (!inst) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ RefPtr<dom::Element> element;
+ inst->GetDOMElement(getter_AddRefs(element));
+ if (!element) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ nsIPrincipal* principal = element->NodePrincipal();
+
+ nsAutoString utf16Origin;
+ res = nsContentUtils::GetUTFOrigin(principal, utf16Origin);
+ if (NS_FAILED(res)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ nsCOMPtr<nsIIDNService> idnService =
+ do_GetService(NS_IDNSERVICE_CONTRACTID);
+ if (!idnService) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // This is a bit messy: we convert to UTF-8 here, but then
+ // nsIDNService::Normalize will convert back to UTF-16 for processing,
+ // and back to UTF-8 again to return the result.
+ // Alternative: perhaps we should add a NormalizeUTF16 version of the API,
+ // and just convert to UTF-8 for the final return (resulting in one
+ // encoding form conversion instead of three).
+ NS_ConvertUTF16toUTF8 utf8Origin(utf16Origin);
+ nsAutoCString normalizedUTF8Origin;
+ res = idnService->Normalize(utf8Origin, normalizedUTF8Origin);
+ if (NS_FAILED(res)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ *(char**)result = ToNewCString(normalizedUTF8Origin);
+ return *(char**)result ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
+ }
+
+#ifdef XP_MACOSX
+ case NPNVpluginDrawingModel: {
+ if (npp) {
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)npp->ndata;
+ if (inst) {
+ NPDrawingModel drawingModel;
+ inst->GetDrawingModel((int32_t*)&drawingModel);
+ *(NPDrawingModel*)result = drawingModel;
+ return NPERR_NO_ERROR;
+ }
+ }
+ return NPERR_GENERIC_ERROR;
+ }
+
+# ifndef NP_NO_QUICKDRAW
+ case NPNVsupportsQuickDrawBool: {
+ *(NPBool*)result = false;
+
+ return NPERR_NO_ERROR;
+ }
+# endif
+
+ case NPNVsupportsCoreGraphicsBool: {
+ *(NPBool*)result = true;
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsCoreAnimationBool: {
+ *(NPBool*)result = true;
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsInvalidatingCoreAnimationBool: {
+ *(NPBool*)result = true;
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsCompositingCoreAnimationPluginsBool: {
+ *(NPBool*)result = PR_TRUE;
+
+ return NPERR_NO_ERROR;
+ }
+
+# ifndef NP_NO_CARBON
+ case NPNVsupportsCarbonBool: {
+ *(NPBool*)result = false;
+
+ return NPERR_NO_ERROR;
+ }
+# endif
+ case NPNVsupportsCocoaBool: {
+ *(NPBool*)result = true;
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsUpdatedCocoaTextInputBool: {
+ *(NPBool*)result = true;
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ case NPNVcontentsScaleFactor: {
+ nsNPAPIPluginInstance* inst =
+ (nsNPAPIPluginInstance*)(npp ? npp->ndata : nullptr);
+ double scaleFactor = inst ? inst->GetContentsScaleFactor() : 1.0;
+ *(double*)result = scaleFactor;
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+ case NPNVCSSZoomFactor: {
+ nsNPAPIPluginInstance* inst =
+ (nsNPAPIPluginInstance*)(npp ? npp->ndata : nullptr);
+ double scaleFactor = inst ? inst->GetCSSZoomFactor() : 1.0;
+ *(double*)result = scaleFactor;
+ return NPERR_NO_ERROR;
+ }
+
+ // we no longer hand out any XPCOM objects
+ case NPNVDOMElement:
+ case NPNVDOMWindow:
+ case NPNVserviceManager:
+ // old XPCOM objects, no longer supported, but null out the out
+ // param to avoid crashing plugins that still try to use this.
+ *(nsISupports**)result = nullptr;
+ [[fallthrough]];
+
+ default:
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_getvalue unhandled get value: %d\n", variable));
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+NPError _setvalue(NPP npp, NPPVariable variable, void* result) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_setvalue called from the wrong thread\n"));
+ return NPERR_INVALID_PARAM;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_SetValue: npp=%p, var=%d\n", (void*)npp, (int)variable));
+
+ if (!npp) return NPERR_INVALID_INSTANCE_ERROR;
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)npp->ndata;
+
+ NS_ASSERTION(inst, "null instance");
+
+ if (!inst) return NPERR_INVALID_INSTANCE_ERROR;
+
+ PluginDestructionGuard guard(inst);
+
+ // Cast NPNVariable enum to int to avoid warnings about including switch
+ // cases for android_npapi.h's non-standard ANPInterface values.
+ switch (static_cast<int>(variable)) {
+ // we should keep backward compatibility with NPAPI where the
+ // actual pointer value is checked rather than its content
+ // when passing booleans
+ case NPPVpluginWindowBool: {
+#ifdef XP_MACOSX
+ // This setting doesn't apply to OS X (only to Windows and Unix/Linux).
+ // See https://developer.mozilla.org/En/NPN_SetValue#section_5. Return
+ // NPERR_NO_ERROR here to conform to other browsers' behavior on OS X
+ // (e.g. Safari and Opera).
+ return NPERR_NO_ERROR;
+#else
+ NPBool bWindowless = (result == nullptr);
+ return inst->SetWindowless(bWindowless);
+#endif
+ }
+ case NPPVpluginTransparentBool: {
+ NPBool bTransparent = (result != nullptr);
+ return inst->SetTransparent(bTransparent);
+ }
+
+ case NPPVjavascriptPushCallerBool: {
+ return NPERR_NO_ERROR;
+ }
+
+ case NPPVpluginKeepLibraryInMemory: {
+ NPBool bCached = (result != nullptr);
+ inst->SetCached(bCached);
+ return NPERR_NO_ERROR;
+ }
+
+ case NPPVpluginUsesDOMForCursorBool: {
+ bool useDOMForCursor = (result != nullptr);
+ return inst->SetUsesDOMForCursor(useDOMForCursor);
+ }
+
+ case NPPVpluginIsPlayingAudio: {
+ const bool isPlaying = result;
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)npp->ndata;
+ MOZ_ASSERT(inst);
+
+ if (!isPlaying && !inst->HasAudioChannelAgent()) {
+ return NPERR_NO_ERROR;
+ }
+
+ if (isPlaying) {
+ inst->NotifyStartedPlaying();
+ } else {
+ inst->NotifyStoppedPlaying();
+ }
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPPVpluginDrawingModel: {
+ if (inst) {
+ inst->SetDrawingModel((NPDrawingModel)NS_PTR_TO_INT32(result));
+ return NPERR_NO_ERROR;
+ }
+ return NPERR_GENERIC_ERROR;
+ }
+
+#ifdef XP_MACOSX
+ case NPPVpluginEventModel: {
+ if (inst) {
+ inst->SetEventModel((NPEventModel)NS_PTR_TO_INT32(result));
+ return NPERR_NO_ERROR;
+ } else {
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+#endif
+ default:
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+NPError _requestread(NPStream* pstream, NPByteRange* rangeList) {
+ return NPERR_STREAM_NOT_SEEKABLE;
+}
+
+// Deprecated, only stubbed out
+void* /* OJI type: JRIEnv* */
+_getJavaEnv() {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_GetJavaEnv\n"));
+ return nullptr;
+}
+
+const char* _useragent(NPP npp) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_useragent called from the wrong thread\n"));
+ return nullptr;
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_UserAgent: npp=%p\n", (void*)npp));
+
+ nsCOMPtr<nsIPluginHost> pluginHostCOM(
+ do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
+ nsPluginHost* pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
+ if (!pluginHost) {
+ return nullptr;
+ }
+
+ const char* retstr;
+ nsresult rv = pluginHost->UserAgent(&retstr);
+ if (NS_FAILED(rv)) return nullptr;
+
+ return retstr;
+}
+
+void* _memalloc(uint32_t size) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPN_memalloc called from the wrong thread\n"));
+ }
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, ("NPN_MemAlloc: size=%d\n", size));
+ return moz_xmalloc(size);
+}
+
+// Deprecated, only stubbed out
+void* /* OJI type: jref */
+_getJavaPeer(NPP npp) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_GetJavaPeer: npp=%p\n", (void*)npp));
+ return nullptr;
+}
+
+void _pushpopupsenabledstate(NPP npp, NPBool enabled) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(
+ PLUGIN_LOG_ALWAYS,
+ ("NPN_pushpopupsenabledstate called from the wrong thread\n"));
+ return;
+ }
+ nsNPAPIPluginInstance* inst =
+ npp ? (nsNPAPIPluginInstance*)npp->ndata : nullptr;
+ if (!inst) return;
+
+ inst->PushPopupsEnabledState(enabled);
+}
+
+void _poppopupsenabledstate(NPP npp) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(
+ PLUGIN_LOG_ALWAYS,
+ ("NPN_poppopupsenabledstate called from the wrong thread\n"));
+ return;
+ }
+ nsNPAPIPluginInstance* inst =
+ npp ? (nsNPAPIPluginInstance*)npp->ndata : nullptr;
+ if (!inst) return;
+
+ inst->PopPopupsEnabledState();
+}
+
+NPError _getvalueforurl(NPP instance, NPNURLVariable variable, const char* url,
+ char** value, uint32_t* len) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_getvalueforurl called from the wrong thread\n"));
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (!instance) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ if (!url || !*url || !len) {
+ return NPERR_INVALID_URL;
+ }
+
+ *len = 0;
+
+ switch (variable) {
+ case NPNURLVProxy:
+ // NPNURLVProxy is no longer supported.
+ *value = nullptr;
+ return NPERR_GENERIC_ERROR;
+
+ case NPNURLVCookie:
+ // NPNURLVCookie is no longer supported.
+ *value = nullptr;
+ return NPERR_GENERIC_ERROR;
+
+ default:
+ // Fall through and return an error...
+ ;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError _setvalueforurl(NPP instance, NPNURLVariable variable, const char* url,
+ const char* value, uint32_t len) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_setvalueforurl called from the wrong thread\n"));
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (!instance) {
+ return NPERR_INVALID_PARAM;
+ }
+
+ if (!url || !*url) {
+ return NPERR_INVALID_URL;
+ }
+
+ switch (variable) {
+ case NPNURLVCookie:
+ // NPNURLVCookie is no longer supported.
+ return NPERR_GENERIC_ERROR;
+
+ case NPNURLVProxy:
+ // We don't support setting proxy values, fall through...
+ default:
+ // Fall through and return an error...
+ ;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+uint32_t _scheduletimer(NPP instance, uint32_t interval, NPBool repeat,
+ PluginTimerFunc timerFunc) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_scheduletimer called from the wrong thread\n"));
+ return 0;
+ }
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ if (!inst) return 0;
+
+ return inst->ScheduleTimer(interval, repeat, timerFunc);
+}
+
+void _unscheduletimer(NPP instance, uint32_t timerID) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_unscheduletimer called from the wrong thread\n"));
+ return;
+ }
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ if (!inst) return;
+
+ inst->UnscheduleTimer(timerID);
+}
+
+NPError _popupcontextmenu(NPP instance, NPMenu* menu) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_popupcontextmenu called from the wrong thread\n"));
+ return 0;
+ }
+
+#ifdef MOZ_WIDGET_COCOA
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+
+ double pluginX, pluginY;
+ double screenX, screenY;
+
+ const NPCocoaEvent* currentEvent =
+ static_cast<NPCocoaEvent*>(inst->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, nullptr, nullptr);
+ } 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) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_convertpoint called from the wrong thread\n"));
+ return 0;
+ }
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ if (!inst) return false;
+
+ return inst->ConvertPoint(sourceX, sourceY, sourceSpace, destX, destY,
+ destSpace);
+}
+
+void _urlredirectresponse(NPP instance, void* notifyData, NPBool allow) {
+ if (!NS_IsMainThread()) {
+ NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
+ ("NPN_convertpoint called from the wrong thread\n"));
+ return;
+ }
+
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ if (!inst) {
+ return;
+ }
+
+ inst->URLRedirectResponse(notifyData, allow);
+}
+
+NPError _initasyncsurface(NPP instance, NPSize* size, NPImageFormat format,
+ void* initData, NPAsyncSurface* surface) {
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ if (!inst) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ return inst->InitAsyncSurface(size, format, initData, surface);
+}
+
+NPError _finalizeasyncsurface(NPP instance, NPAsyncSurface* surface) {
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ if (!inst) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ return inst->FinalizeAsyncSurface(surface);
+}
+
+void _setcurrentasyncsurface(NPP instance, NPAsyncSurface* surface,
+ NPRect* changed) {
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ if (!inst) {
+ return;
+ }
+
+ inst->SetCurrentAsyncSurface(surface, changed);
+}
+
+} // namespace mozilla::plugins::parent
diff --git a/dom/plugins/base/nsNPAPIPlugin.h b/dom/plugins/base/nsNPAPIPlugin.h
new file mode 100644
index 0000000000..ebc0404932
--- /dev/null
+++ b/dom/plugins/base/nsNPAPIPlugin.h
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsNPAPIPlugin_h_
+#define nsNPAPIPlugin_h_
+
+#include "prlink.h"
+#include "npfunctions.h"
+#include "nsPluginHost.h"
+
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/PluginLibrary.h"
+#include "mozilla/RefCounted.h"
+
+// nsNPAPIPlugin is held alive both by active nsPluginTag instances and
+// by active nsNPAPIPluginInstance.
+class nsNPAPIPlugin final {
+ private:
+ typedef mozilla::PluginLibrary PluginLibrary;
+
+ public:
+ nsNPAPIPlugin();
+
+ NS_INLINE_DECL_REFCOUNTING(nsNPAPIPlugin)
+
+ // Constructs and initializes an nsNPAPIPlugin object. A nullptr file path
+ // will prevent this from calling NP_Initialize.
+ static nsresult CreatePlugin(nsPluginTag* aPluginTag,
+ nsNPAPIPlugin** aResult);
+
+ PluginLibrary* GetLibrary();
+ // PluginFuncs() can't fail but results are only valid if GetLibrary()
+ // succeeds
+ NPPluginFuncs* PluginFuncs();
+
+#if defined(XP_MACOSX) && !defined(__LP64__)
+ void SetPluginRefNum(short aRefNum);
+#endif
+
+ // The IPC mechanism notifies the nsNPAPIPlugin if the plugin
+ // crashes and is no longer usable. pluginDumpID is the ID of the minidump
+ // that was written, or empty if no minidump was written.
+ void PluginCrashed(const nsAString& aPluginDumpID,
+ const nsACString& aAdditionalMinidumps);
+
+ nsresult Shutdown();
+
+ static nsresult RetainStream(NPStream* pstream, nsISupports** aRetainedPeer);
+
+ private:
+ ~nsNPAPIPlugin();
+
+ NPPluginFuncs mPluginFuncs;
+ PluginLibrary* mLibrary;
+};
+
+namespace mozilla {
+namespace plugins {
+namespace parent {
+
+static_assert(sizeof(NPIdentifier) == sizeof(jsid),
+ "NPIdentifier must be binary compatible with jsid.");
+
+inline jsid NPIdentifierToJSId(NPIdentifier id) {
+ jsid tmp;
+ JSID_BITS(tmp) = (size_t)id;
+ return tmp;
+}
+
+inline NPIdentifier JSIdToNPIdentifier(jsid id) {
+ return (NPIdentifier)JSID_BITS(id);
+}
+
+inline bool NPIdentifierIsString(NPIdentifier id) {
+ return JSID_IS_STRING(NPIdentifierToJSId(id));
+}
+
+inline JSString* NPIdentifierToString(NPIdentifier id) {
+ return JSID_TO_STRING(NPIdentifierToJSId(id));
+}
+
+inline NPIdentifier StringToNPIdentifier(JSContext* cx, JSString* str) {
+ return JSIdToNPIdentifier(JS::PropertyKey::fromPinnedString(str));
+}
+
+inline bool NPIdentifierIsInt(NPIdentifier id) {
+ return JSID_IS_INT(NPIdentifierToJSId(id));
+}
+
+inline int NPIdentifierToInt(NPIdentifier id) {
+ return JSID_TO_INT(NPIdentifierToJSId(id));
+}
+
+inline NPIdentifier IntToNPIdentifier(int i) {
+ return JSIdToNPIdentifier(INT_TO_JSID(i));
+}
+
+JSContext* GetJSContext(NPP npp);
+
+inline bool NPStringIdentifierIsPermanent(NPIdentifier id) {
+ AutoSafeJSContext cx;
+ return JS_StringHasBeenPinned(cx, NPIdentifierToString(id));
+}
+
+#define NPIdentifier_VOID (JSIdToNPIdentifier(JSID_VOID))
+
+NPObject* _getwindowobject(NPP npp);
+
+NPObject* _getpluginelement(NPP npp);
+
+NPIdentifier _getstringidentifier(const NPUTF8* name);
+
+void _getstringidentifiers(const NPUTF8** names, int32_t nameCount,
+ NPIdentifier* identifiers);
+
+bool _identifierisstring(NPIdentifier identifiers);
+
+NPIdentifier _getintidentifier(int32_t intid);
+
+NPUTF8* _utf8fromidentifier(NPIdentifier identifier);
+
+int32_t _intfromidentifier(NPIdentifier identifier);
+
+NPObject* _createobject(NPP npp, NPClass* aClass);
+
+NPObject* _retainobject(NPObject* npobj);
+
+void _releaseobject(NPObject* npobj);
+
+bool _invoke(NPP npp, NPObject* npobj, NPIdentifier method,
+ const NPVariant* args, uint32_t argCount, NPVariant* result);
+
+bool _invokeDefault(NPP npp, NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+
+bool _evaluate(NPP npp, NPObject* npobj, NPString* script, NPVariant* result);
+
+bool _getproperty(NPP npp, NPObject* npobj, NPIdentifier property,
+ NPVariant* result);
+
+bool _setproperty(NPP npp, NPObject* npobj, NPIdentifier property,
+ const NPVariant* value);
+
+bool _removeproperty(NPP npp, NPObject* npobj, NPIdentifier property);
+
+bool _hasproperty(NPP npp, NPObject* npobj, NPIdentifier propertyName);
+
+bool _hasmethod(NPP npp, NPObject* npobj, NPIdentifier methodName);
+
+bool _enumerate(NPP npp, NPObject* npobj, NPIdentifier** identifier,
+ uint32_t* count);
+
+bool _construct(NPP npp, NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+
+void _releasevariantvalue(NPVariant* variant);
+
+void _setexception(NPObject* npobj, const NPUTF8* message);
+
+void _pushpopupsenabledstate(NPP npp, NPBool enabled);
+
+void _poppopupsenabledstate(NPP npp);
+
+NPError _getvalueforurl(NPP instance, NPNURLVariable variable, const char* url,
+ char** value, uint32_t* len);
+
+NPError _setvalueforurl(NPP instance, NPNURLVariable variable, const char* url,
+ const char* value, uint32_t len);
+
+typedef void (*PluginTimerFunc)(NPP npp, uint32_t timerID);
+
+uint32_t _scheduletimer(NPP instance, uint32_t interval, NPBool repeat,
+ PluginTimerFunc timerFunc);
+
+void _unscheduletimer(NPP instance, uint32_t timerID);
+
+NPError _popupcontextmenu(NPP instance, NPMenu* menu);
+
+NPBool _convertpoint(NPP instance, double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace, double* destX,
+ double* destY, NPCoordinateSpace destSpace);
+
+NPError _requestread(NPStream* pstream, NPByteRange* rangeList);
+
+NPError _geturlnotify(NPP npp, const char* relativeURL, const char* target,
+ void* notifyData);
+
+NPError _getvalue(NPP npp, NPNVariable variable, void* r_value);
+
+NPError _setvalue(NPP npp, NPPVariable variable, void* r_value);
+
+NPError _geturl(NPP npp, const char* relativeURL, const char* target);
+
+NPError _posturlnotify(NPP npp, const char* relativeURL, const char* target,
+ uint32_t len, const char* buf, NPBool file,
+ void* notifyData);
+
+NPError _posturl(NPP npp, const char* relativeURL, const char* target,
+ uint32_t len, const char* buf, NPBool file);
+
+void _status(NPP npp, const char* message);
+
+void _memfree(void* ptr);
+
+uint32_t _memflush(uint32_t size);
+
+void _reloadplugins(NPBool reloadPages);
+
+void _invalidaterect(NPP npp, NPRect* invalidRect);
+
+void _invalidateregion(NPP npp, NPRegion invalidRegion);
+
+void _forceredraw(NPP npp);
+
+const char* _useragent(NPP npp);
+
+void* _memalloc(uint32_t size);
+
+// Deprecated entry points for the old Java plugin.
+void* /* OJI type: JRIEnv* */
+_getJavaEnv();
+
+void* /* OJI type: jref */
+_getJavaPeer(NPP npp);
+
+void _urlredirectresponse(NPP instance, void* notifyData, NPBool allow);
+
+NPError _initasyncsurface(NPP instance, NPSize* size, NPImageFormat format,
+ void* initData, NPAsyncSurface* surface);
+
+NPError _finalizeasyncsurface(NPP instance, NPAsyncSurface* surface);
+
+void _setcurrentasyncsurface(NPP instance, NPAsyncSurface* surface,
+ NPRect* changed);
+
+} /* namespace parent */
+} /* namespace plugins */
+} /* namespace mozilla */
+
+const char* PeekException();
+
+void PopException();
+
+class NPPStack {
+ public:
+ static NPP Peek() { return sCurrentNPP; }
+
+ protected:
+ static NPP sCurrentNPP;
+};
+
+// XXXjst: The NPPAutoPusher stack is a bit redundant now that
+// PluginDestructionGuard exists, and could thus be replaced by code
+// that uses the PluginDestructionGuard list of plugins on the
+// stack. But they're not identical, and to minimize code changes
+// we're keeping both for the moment, and making NPPAutoPusher inherit
+// the PluginDestructionGuard class to avoid having to keep two
+// separate objects on the stack since we always want a
+// PluginDestructionGuard where we use an NPPAutoPusher.
+
+class MOZ_STACK_CLASS NPPAutoPusher : public NPPStack,
+ protected PluginDestructionGuard {
+ public:
+ explicit NPPAutoPusher(NPP aNpp)
+ : PluginDestructionGuard(aNpp), mOldNPP(sCurrentNPP) {
+ NS_ASSERTION(aNpp, "Uh, null aNpp passed to NPPAutoPusher!");
+
+ sCurrentNPP = aNpp;
+ }
+
+ ~NPPAutoPusher() { sCurrentNPP = mOldNPP; }
+
+ private:
+ NPP mOldNPP;
+};
+
+class NPPExceptionAutoHolder {
+ public:
+ NPPExceptionAutoHolder();
+ ~NPPExceptionAutoHolder();
+
+ protected:
+ char* mOldException;
+};
+
+#endif // nsNPAPIPlugin_h_
diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp
new file mode 100644
index 0000000000..a1462f66f5
--- /dev/null
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -0,0 +1,1203 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/DebugOnly.h"
+
+#include "mozilla/Logging.h"
+#include "nscore.h"
+#include "prenv.h"
+
+#include "nsNPAPIPluginInstance.h"
+#include "nsNPAPIPlugin.h"
+#include "nsNPAPIPluginStreamListener.h"
+#include "nsPluginHost.h"
+#include "nsPluginLogging.h"
+#include "nsContentUtils.h"
+#include "nsPluginInstanceOwner.h"
+
+#include "nsThreadUtils.h"
+#include "mozilla/dom/Document.h"
+#include "nsIDocShell.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIScriptContext.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsJSNPRuntime.h"
+#include "nsPluginStreamListenerPeer.h"
+#include "nsSize.h"
+#include "nsNetCID.h"
+#include "nsIContent.h"
+#include "nsVersionComparator.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Unused.h"
+#include "nsILoadContext.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLObjectElementBinding.h"
+#include "AudioChannelService.h"
+#include "GeckoProfiler.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+using namespace mozilla;
+using namespace mozilla::plugins::parent;
+using namespace mozilla::layers;
+
+NS_IMPL_ISUPPORTS(nsNPAPIPluginInstance, nsIAudioChannelAgentCallback)
+
+nsNPAPIPluginInstance::nsNPAPIPluginInstance()
+ : mDrawingModel(kDefaultDrawingModel),
+ mRunning(NOT_STARTED),
+ mWindowless(false),
+ mTransparent(false),
+ mCached(false),
+ mUsesDOMForCursor(false),
+ mInPluginInitCall(false),
+ mPlugin(nullptr),
+ mMIMEType(nullptr),
+ mOwner(nullptr)
+#ifdef XP_MACOSX
+ ,
+ mCurrentPluginEvent(nullptr)
+#endif
+ ,
+ mCachedParamLength(0),
+ mCachedParamNames(nullptr),
+ mCachedParamValues(nullptr) {
+ mNPP.pdata = nullptr;
+ mNPP.ndata = this;
+
+ PLUGIN_LOG(PLUGIN_LOG_BASIC, ("nsNPAPIPluginInstance ctor: this=%p\n", this));
+}
+
+nsNPAPIPluginInstance::~nsNPAPIPluginInstance() {
+ PLUGIN_LOG(PLUGIN_LOG_BASIC, ("nsNPAPIPluginInstance dtor: this=%p\n", this));
+
+ if (mMIMEType) {
+ free(mMIMEType);
+ mMIMEType = nullptr;
+ }
+
+ if (!mCachedParamValues || !mCachedParamNames) {
+ return;
+ }
+ MOZ_ASSERT(mCachedParamValues && mCachedParamNames);
+
+ for (uint32_t i = 0; i < mCachedParamLength; i++) {
+ if (mCachedParamNames[i]) {
+ free(mCachedParamNames[i]);
+ mCachedParamNames[i] = nullptr;
+ }
+ if (mCachedParamValues[i]) {
+ free(mCachedParamValues[i]);
+ mCachedParamValues[i] = nullptr;
+ }
+ }
+
+ free(mCachedParamNames);
+ mCachedParamNames = nullptr;
+
+ free(mCachedParamValues);
+ mCachedParamValues = nullptr;
+}
+
+uint32_t nsNPAPIPluginInstance::gInUnsafePluginCalls = 0;
+
+void nsNPAPIPluginInstance::Destroy() {
+ Stop();
+ mPlugin = nullptr;
+ mAudioChannelAgent = nullptr;
+}
+
+TimeStamp nsNPAPIPluginInstance::StopTime() { return mStopTime; }
+
+nsresult nsNPAPIPluginInstance::Initialize(nsNPAPIPlugin* aPlugin,
+ nsPluginInstanceOwner* aOwner,
+ const nsACString& aMIMEType) {
+ AUTO_PROFILER_LABEL("nsNPAPIPlugin::Initialize", OTHER);
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("nsNPAPIPluginInstance::Initialize this=%p\n", this));
+
+ NS_ENSURE_ARG_POINTER(aPlugin);
+ NS_ENSURE_ARG_POINTER(aOwner);
+
+ mPlugin = aPlugin;
+ mOwner = aOwner;
+
+ if (!aMIMEType.IsEmpty()) {
+ mMIMEType = ToNewCString(aMIMEType);
+ }
+
+ return Start();
+}
+
+nsresult nsNPAPIPluginInstance::Stop() {
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("nsNPAPIPluginInstance::Stop this=%p\n", this));
+
+ // Make sure the plugin didn't leave popups enabled.
+ if (mPopupStates.Length() > 0) {
+ PopupBlocker::PopPopupControlState(PopupBlocker::openAbused);
+ }
+
+ if (RUNNING != mRunning) {
+ return NS_OK;
+ }
+
+ // clean up all outstanding timers
+ for (uint32_t i = mTimers.Length(); i > 0; i--)
+ UnscheduleTimer(mTimers[i - 1]->id);
+
+ // If there's code from this plugin instance on the stack, delay the
+ // destroy.
+ if (PluginDestructionGuard::DelayDestroy(this)) {
+ return NS_OK;
+ }
+
+ mRunning = DESTROYING;
+ mStopTime = TimeStamp::Now();
+
+ // clean up open streams
+ while (mStreamListeners.Length() > 0) {
+ RefPtr<nsNPAPIPluginStreamListener> currentListener(mStreamListeners[0]);
+ currentListener->CleanUpStream(NPRES_USER_BREAK);
+ mStreamListeners.RemoveElement(currentListener);
+ }
+
+ if (!mPlugin || !mPlugin->GetLibrary()) return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
+
+ NPError error = NPERR_GENERIC_ERROR;
+ if (pluginFunctions->destroy) {
+ NPSavedData* sdata = 0;
+
+ NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroy)(&mNPP, &sdata),
+ this, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPP Destroy called: this=%p, npp=%p, return=%d\n", this,
+ &mNPP, error));
+ }
+ mRunning = DESTROYED;
+
+ nsJSNPRuntime::OnPluginDestroy(&mNPP);
+
+ if (error != NPERR_NO_ERROR)
+ return NS_ERROR_FAILURE;
+ else
+ return NS_OK;
+}
+
+already_AddRefed<nsPIDOMWindowOuter> nsNPAPIPluginInstance::GetDOMWindow() {
+ if (!mOwner) return nullptr;
+
+ RefPtr<nsPluginInstanceOwner> kungFuDeathGrip(mOwner);
+
+ nsCOMPtr<Document> doc;
+ kungFuDeathGrip->GetDocument(getter_AddRefs(doc));
+ if (!doc) return nullptr;
+
+ RefPtr<nsPIDOMWindowOuter> window = doc->GetWindow();
+
+ return window.forget();
+}
+
+nsresult nsNPAPIPluginInstance::GetTagType(nsPluginTagType* result) {
+ if (!mOwner) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return mOwner->GetTagType(result);
+}
+
+nsTArray<nsNPAPIPluginStreamListener*>*
+nsNPAPIPluginInstance::StreamListeners() {
+ return &mStreamListeners;
+}
+
+nsTArray<nsPluginStreamListenerPeer*>*
+nsNPAPIPluginInstance::FileCachedStreamListeners() {
+ return &mFileCachedStreamListeners;
+}
+
+nsresult nsNPAPIPluginInstance::Start() {
+ if (mRunning == RUNNING) {
+ return NS_OK;
+ }
+
+ if (!mOwner) {
+ MOZ_ASSERT(false, "Should not be calling Start() on unowned plugin.");
+ return NS_ERROR_FAILURE;
+ }
+
+ PluginDestructionGuard guard(this);
+
+ nsTArray<MozPluginParameter> attributes;
+ nsTArray<MozPluginParameter> params;
+
+ nsPluginTagType tagtype;
+ nsresult rv = GetTagType(&tagtype);
+ if (NS_SUCCEEDED(rv)) {
+ mOwner->GetAttributes(attributes);
+ mOwner->GetParameters(params);
+ } else {
+ MOZ_ASSERT(false, "Failed to get tag type.");
+ }
+
+ mCachedParamLength = attributes.Length() + 1 + params.Length();
+
+ // We add an extra entry "PARAM" as a separator between the attribute
+ // and param values, but we don't count it if there are no <param> entries.
+ // Legacy behavior quirk.
+ uint32_t quirkParamLength =
+ params.Length() ? mCachedParamLength : attributes.Length();
+
+ mCachedParamNames = (char**)moz_xmalloc(sizeof(char*) * mCachedParamLength);
+ mCachedParamValues = (char**)moz_xmalloc(sizeof(char*) * mCachedParamLength);
+
+ for (uint32_t i = 0; i < attributes.Length(); i++) {
+ mCachedParamNames[i] = ToNewUTF8String(attributes[i].mName);
+ mCachedParamValues[i] = ToNewUTF8String(attributes[i].mValue);
+ }
+
+ mCachedParamNames[attributes.Length()] = ToNewUTF8String(u"PARAM"_ns);
+ mCachedParamValues[attributes.Length()] = nullptr;
+
+ for (uint32_t i = 0, pos = attributes.Length() + 1; i < params.Length();
+ i++) {
+ mCachedParamNames[pos] = ToNewUTF8String(params[i].mName);
+ mCachedParamValues[pos] = ToNewUTF8String(params[i].mValue);
+ pos++;
+ }
+
+ const char* mimetype;
+ NPError error = NPERR_GENERIC_ERROR;
+
+ GetMIMEType(&mimetype);
+
+ bool oldVal = mInPluginInitCall;
+ mInPluginInitCall = true;
+
+ // Need this on the stack before calling NPP_New otherwise some callbacks that
+ // the plugin may make could fail (NPN_HasProperty, for example).
+ NPPAutoPusher autopush(&mNPP);
+
+ if (!mPlugin) return NS_ERROR_FAILURE;
+
+ PluginLibrary* library = mPlugin->GetLibrary();
+ if (!library) return NS_ERROR_FAILURE;
+
+ // Mark this instance as running before calling NPP_New because the plugin may
+ // call other NPAPI functions, like NPN_GetURLNotify, that assume this is set
+ // before returning. If the plugin returns failure, we'll clear it out below.
+ mRunning = RUNNING;
+
+ nsresult newResult =
+ library->NPP_New((char*)mimetype, &mNPP, quirkParamLength,
+ mCachedParamNames, mCachedParamValues, nullptr, &error);
+ mInPluginInitCall = oldVal;
+
+ NPP_PLUGIN_LOG(
+ PLUGIN_LOG_NORMAL,
+ ("NPP New called: this=%p, npp=%p, mime=%s, argc=%d, return=%d\n", this,
+ &mNPP, mimetype, quirkParamLength, error));
+
+ if (NS_FAILED(newResult) || error != NPERR_NO_ERROR) {
+ mRunning = DESTROYED;
+ nsJSNPRuntime::OnPluginDestroy(&mNPP);
+ return NS_ERROR_FAILURE;
+ }
+
+ return newResult;
+}
+
+nsresult nsNPAPIPluginInstance::SetWindow(NPWindow* window) {
+ // NPAPI plugins don't want a SetWindow(nullptr).
+ if (!window || RUNNING != mRunning) return NS_OK;
+
+#if MOZ_WIDGET_GTK
+ // bug 108347, flash plugin on linux doesn't like window->width <= 0
+ return NS_OK;
+#endif
+
+ if (!mPlugin || !mPlugin->GetLibrary()) return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
+
+ if (pluginFunctions->setwindow) {
+ PluginDestructionGuard guard(this);
+
+ // XXX Turns out that NPPluginWindow and NPWindow are structurally
+ // identical (on purpose!), so there's no need to make a copy.
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("nsNPAPIPluginInstance::SetWindow (about to call it) this=%p\n",
+ this));
+
+ bool oldVal = mInPluginInitCall;
+ mInPluginInitCall = true;
+
+ NPPAutoPusher nppPusher(&mNPP);
+
+ NPError error;
+ NS_TRY_SAFE_CALL_RETURN(
+ error, (*pluginFunctions->setwindow)(&mNPP, (NPWindow*)window), this,
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+ // 'error' is only used if this is a logging-enabled build.
+ // That is somewhat complex to check, so we just use "unused"
+ // to suppress any compiler warnings in build configurations
+ // where the logging is a no-op.
+ mozilla::Unused << error;
+
+ mInPluginInitCall = oldVal;
+
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPP SetWindow called: this=%p, [x=%d,y=%d,w=%d,h=%d], "
+ "clip[t=%d,b=%d,l=%d,r=%d], return=%d\n",
+ this, window->x, window->y, window->width, window->height,
+ window->clipRect.top, window->clipRect.bottom,
+ window->clipRect.left, window->clipRect.right, error));
+ }
+ return NS_OK;
+}
+
+nsresult nsNPAPIPluginInstance::NewStreamListener(
+ const char* aURL, void* notifyData,
+ nsNPAPIPluginStreamListener** listener) {
+ RefPtr<nsNPAPIPluginStreamListener> sl =
+ new nsNPAPIPluginStreamListener(this, notifyData, aURL);
+
+ mStreamListeners.AppendElement(sl);
+
+ sl.forget(listener);
+
+ return NS_OK;
+}
+
+nsresult nsNPAPIPluginInstance::Print(NPPrint* platformPrint) {
+ NS_ENSURE_TRUE(platformPrint, NS_ERROR_NULL_POINTER);
+
+ PluginDestructionGuard guard(this);
+
+ if (!mPlugin || !mPlugin->GetLibrary()) return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
+
+ NPPrint* thePrint = (NPPrint*)platformPrint;
+
+ // to be compatible with the older SDK versions and to match what
+ // NPAPI and other browsers do, overwrite |window.type| field with one
+ // more copy of |platformPrint|. See bug 113264
+ uint16_t sdkmajorversion = (pluginFunctions->version & 0xff00) >> 8;
+ uint16_t sdkminorversion = pluginFunctions->version & 0x00ff;
+ if ((sdkmajorversion == 0) && (sdkminorversion < 11)) {
+ // Let's copy platformPrint bytes over to where it was supposed to be
+ // in older versions -- four bytes towards the beginning of the struct
+ // but we should be careful about possible misalignments
+ if (sizeof(NPWindowType) >= sizeof(void*)) {
+ void* source = thePrint->print.embedPrint.platformPrint;
+ void** destination = (void**)&(thePrint->print.embedPrint.window.type);
+ *destination = source;
+ } else {
+ NS_ERROR("Incompatible OS for assignment");
+ }
+ }
+
+ if (pluginFunctions->print)
+ NS_TRY_SAFE_CALL_VOID((*pluginFunctions->print)(&mNPP, thePrint), this,
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPP PrintProc called: this=%p, pDC=%p, "
+ "[x=%d,y=%d,w=%d,h=%d], clip[t=%d,b=%d,l=%d,r=%d]\n",
+ this, platformPrint->print.embedPrint.platformPrint,
+ platformPrint->print.embedPrint.window.x,
+ platformPrint->print.embedPrint.window.y,
+ platformPrint->print.embedPrint.window.width,
+ platformPrint->print.embedPrint.window.height,
+ platformPrint->print.embedPrint.window.clipRect.top,
+ platformPrint->print.embedPrint.window.clipRect.bottom,
+ platformPrint->print.embedPrint.window.clipRect.left,
+ platformPrint->print.embedPrint.window.clipRect.right));
+
+ return NS_OK;
+}
+
+nsresult nsNPAPIPluginInstance::HandleEvent(
+ void* event, int16_t* result, NSPluginCallReentry aSafeToReenterGecko) {
+ if (RUNNING != mRunning) return NS_OK;
+
+ AUTO_PROFILER_LABEL("nsNPAPIPluginInstance::HandleEvent", OTHER);
+
+ if (!event) return NS_ERROR_FAILURE;
+
+ PluginDestructionGuard guard(this);
+
+ if (!mPlugin || !mPlugin->GetLibrary()) return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
+
+ int16_t tmpResult = kNPEventNotHandled;
+
+ if (pluginFunctions->event) {
+#ifdef XP_MACOSX
+ mCurrentPluginEvent = event;
+#endif
+#if defined(XP_WIN)
+ NS_TRY_SAFE_CALL_RETURN(tmpResult, (*pluginFunctions->event)(&mNPP, event),
+ this, aSafeToReenterGecko);
+#else
+ tmpResult = (*pluginFunctions->event)(&mNPP, event);
+#endif
+ NPP_PLUGIN_LOG(
+ PLUGIN_LOG_NOISY,
+ ("NPP HandleEvent called: this=%p, npp=%p, event=%p, return=%d\n", this,
+ &mNPP, event, tmpResult));
+
+ if (result) *result = tmpResult;
+#ifdef XP_MACOSX
+ mCurrentPluginEvent = nullptr;
+#endif
+ }
+
+ return NS_OK;
+}
+
+nsresult nsNPAPIPluginInstance::GetValueFromPlugin(NPPVariable variable,
+ void* value) {
+ if (!mPlugin || !mPlugin->GetLibrary()) return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
+
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (pluginFunctions->getvalue && RUNNING == mRunning) {
+ PluginDestructionGuard guard(this);
+
+ NPError pluginError = NPERR_GENERIC_ERROR;
+ NS_TRY_SAFE_CALL_RETURN(
+ pluginError, (*pluginFunctions->getvalue)(&mNPP, variable, value), this,
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+ NPP_PLUGIN_LOG(
+ PLUGIN_LOG_NORMAL,
+ ("NPP GetValue called: this=%p, npp=%p, var=%d, value=%p, return=%d\n",
+ this, &mNPP, variable, value, pluginError));
+
+ if (pluginError == NPERR_NO_ERROR) {
+ rv = NS_OK;
+ }
+ }
+
+ return rv;
+}
+
+nsNPAPIPlugin* nsNPAPIPluginInstance::GetPlugin() { return mPlugin; }
+
+nsresult nsNPAPIPluginInstance::GetNPP(NPP* aNPP) {
+ if (aNPP)
+ *aNPP = &mNPP;
+ else
+ return NS_ERROR_NULL_POINTER;
+
+ return NS_OK;
+}
+
+NPError nsNPAPIPluginInstance::SetWindowless(bool aWindowless) {
+ mWindowless = aWindowless;
+ return NPERR_NO_ERROR;
+}
+
+NPError nsNPAPIPluginInstance::SetTransparent(bool aTransparent) {
+ mTransparent = aTransparent;
+ return NPERR_NO_ERROR;
+}
+
+NPError nsNPAPIPluginInstance::SetUsesDOMForCursor(bool aUsesDOMForCursor) {
+ mUsesDOMForCursor = aUsesDOMForCursor;
+ return NPERR_NO_ERROR;
+}
+
+bool nsNPAPIPluginInstance::UsesDOMForCursor() { return mUsesDOMForCursor; }
+
+void nsNPAPIPluginInstance::SetDrawingModel(NPDrawingModel aModel) {
+ mDrawingModel = aModel;
+}
+
+void nsNPAPIPluginInstance::RedrawPlugin() { mOwner->RedrawPlugin(); }
+
+#if defined(XP_MACOSX)
+void nsNPAPIPluginInstance::SetEventModel(NPEventModel aModel) {
+ // the event model needs to be set for the object frame immediately
+ if (!mOwner) {
+ NS_WARNING("Trying to set event model without a plugin instance owner!");
+ return;
+ }
+
+ mOwner->SetEventModel(aModel);
+}
+#endif
+
+nsresult nsNPAPIPluginInstance::GetDrawingModel(int32_t* aModel) {
+ *aModel = (int32_t)mDrawingModel;
+ return NS_OK;
+}
+
+nsresult nsNPAPIPluginInstance::IsRemoteDrawingCoreAnimation(bool* aDrawing) {
+#ifdef XP_MACOSX
+ if (!mPlugin) return NS_ERROR_FAILURE;
+
+ PluginLibrary* library = mPlugin->GetLibrary();
+ if (!library) return NS_ERROR_FAILURE;
+
+ return library->IsRemoteDrawingCoreAnimation(&mNPP, aDrawing);
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+nsresult nsNPAPIPluginInstance::ContentsScaleFactorChanged(
+ double aContentsScaleFactor) {
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ if (!mPlugin) return NS_ERROR_FAILURE;
+
+ PluginLibrary* library = mPlugin->GetLibrary();
+ if (!library) return NS_ERROR_FAILURE;
+
+ // We only need to call this if the plugin is running OOP.
+ if (!library->IsOOP()) return NS_OK;
+
+ return library->ContentsScaleFactorChanged(&mNPP, aContentsScaleFactor);
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+nsresult nsNPAPIPluginInstance::CSSZoomFactorChanged(float aCSSZoomFactor) {
+ if (RUNNING != mRunning) return NS_OK;
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance informing plugin of "
+ "CSS Zoom Factor change this=%p\n",
+ this));
+
+ if (!mPlugin || !mPlugin->GetLibrary()) return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
+
+ if (!pluginFunctions->setvalue) return NS_ERROR_FAILURE;
+
+ PluginDestructionGuard guard(this);
+
+ NPError error;
+ double value = static_cast<double>(aCSSZoomFactor);
+ NS_TRY_SAFE_CALL_RETURN(
+ error, (*pluginFunctions->setvalue)(&mNPP, NPNVCSSZoomFactor, &value),
+ this, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+ return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsNPAPIPluginInstance::GetJSObject(JSContext* cx,
+ JSObject** outObject) {
+ NPObject* npobj = nullptr;
+ nsresult rv = GetValueFromPlugin(NPPVpluginScriptableNPObject, &npobj);
+ if (NS_FAILED(rv) || !npobj) return NS_ERROR_FAILURE;
+
+ *outObject = nsNPObjWrapper::GetNewOrUsed(&mNPP, cx, npobj);
+
+ _releaseobject(npobj);
+
+ return NS_OK;
+}
+
+void nsNPAPIPluginInstance::SetCached(bool aCache) { mCached = aCache; }
+
+bool nsNPAPIPluginInstance::ShouldCache() { return mCached; }
+
+nsresult nsNPAPIPluginInstance::IsWindowless(bool* isWindowless) {
+#if defined(XP_MACOSX)
+ // All OS X plugins are windowless.
+ *isWindowless = true;
+#else
+ *isWindowless = mWindowless;
+#endif
+ return NS_OK;
+}
+
+class MOZ_STACK_CLASS AutoPluginLibraryCall {
+ public:
+ explicit AutoPluginLibraryCall(nsNPAPIPluginInstance* aThis)
+ : mThis(aThis), mGuard(aThis), mLibrary(nullptr) {
+ nsNPAPIPlugin* plugin = mThis->GetPlugin();
+ if (plugin) mLibrary = plugin->GetLibrary();
+ }
+ explicit operator bool() { return !!mLibrary; }
+ PluginLibrary* operator->() { return mLibrary; }
+
+ private:
+ nsNPAPIPluginInstance* mThis;
+ PluginDestructionGuard mGuard;
+ PluginLibrary* mLibrary;
+};
+
+nsresult nsNPAPIPluginInstance::AsyncSetWindow(NPWindow* window) {
+ if (RUNNING != mRunning) return NS_OK;
+
+ AutoPluginLibraryCall library(this);
+ if (!library) return NS_ERROR_FAILURE;
+
+ return library->AsyncSetWindow(&mNPP, window);
+}
+
+nsresult nsNPAPIPluginInstance::GetImageContainer(ImageContainer** aContainer) {
+ *aContainer = nullptr;
+
+ if (RUNNING != mRunning) return NS_OK;
+
+ AutoPluginLibraryCall library(this);
+ return !library ? NS_ERROR_FAILURE
+ : library->GetImageContainer(&mNPP, aContainer);
+}
+
+nsresult nsNPAPIPluginInstance::GetImageSize(nsIntSize* aSize) {
+ *aSize = nsIntSize(0, 0);
+
+ if (RUNNING != mRunning) return NS_OK;
+
+ AutoPluginLibraryCall library(this);
+ return !library ? NS_ERROR_FAILURE : library->GetImageSize(&mNPP, aSize);
+}
+
+#if defined(XP_WIN)
+nsresult nsNPAPIPluginInstance::GetScrollCaptureContainer(
+ ImageContainer** aContainer) {
+ *aContainer = nullptr;
+
+ if (RUNNING != mRunning) return NS_OK;
+
+ AutoPluginLibraryCall library(this);
+ return !library ? NS_ERROR_FAILURE
+ : library->GetScrollCaptureContainer(&mNPP, aContainer);
+}
+#endif
+
+nsresult nsNPAPIPluginInstance::HandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData, bool aIsConsumed) {
+ if (NS_WARN_IF(!mPlugin)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ PluginLibrary* library = mPlugin->GetLibrary();
+ if (NS_WARN_IF(!library)) {
+ return NS_ERROR_FAILURE;
+ }
+ return library->HandledWindowedPluginKeyEvent(&mNPP, aKeyEventData,
+ aIsConsumed);
+}
+
+void nsNPAPIPluginInstance::DidComposite() {
+ if (RUNNING != mRunning) return;
+
+ AutoPluginLibraryCall library(this);
+ library->DidComposite(&mNPP);
+}
+
+nsresult nsNPAPIPluginInstance::NotifyPainted(void) {
+ MOZ_ASSERT_UNREACHABLE("Dead code, shouldn't be called.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult nsNPAPIPluginInstance::GetIsOOP(bool* aIsAsync) {
+ AutoPluginLibraryCall library(this);
+ if (!library) return NS_ERROR_FAILURE;
+
+ *aIsAsync = library->IsOOP();
+ return NS_OK;
+}
+
+nsresult nsNPAPIPluginInstance::SetBackgroundUnknown() {
+ if (RUNNING != mRunning) return NS_OK;
+
+ AutoPluginLibraryCall library(this);
+ if (!library) return NS_ERROR_FAILURE;
+
+ return library->SetBackgroundUnknown(&mNPP);
+}
+
+nsresult nsNPAPIPluginInstance::BeginUpdateBackground(
+ nsIntRect* aRect, DrawTarget** aDrawTarget) {
+ if (RUNNING != mRunning) return NS_OK;
+
+ AutoPluginLibraryCall library(this);
+ if (!library) return NS_ERROR_FAILURE;
+
+ return library->BeginUpdateBackground(&mNPP, *aRect, aDrawTarget);
+}
+
+nsresult nsNPAPIPluginInstance::EndUpdateBackground(nsIntRect* aRect) {
+ if (RUNNING != mRunning) return NS_OK;
+
+ AutoPluginLibraryCall library(this);
+ if (!library) return NS_ERROR_FAILURE;
+
+ return library->EndUpdateBackground(&mNPP, *aRect);
+}
+
+nsresult nsNPAPIPluginInstance::IsTransparent(bool* isTransparent) {
+ *isTransparent = mTransparent;
+ return NS_OK;
+}
+
+nsresult nsNPAPIPluginInstance::GetFormValue(nsAString& aValue) {
+ aValue.Truncate();
+
+ char* value = nullptr;
+ nsresult rv = GetValueFromPlugin(NPPVformValue, &value);
+ if (NS_FAILED(rv) || !value) return NS_ERROR_FAILURE;
+
+ CopyUTF8toUTF16(MakeStringSpan(value), aValue);
+
+ // NPPVformValue allocates with NPN_MemAlloc(), which uses
+ // nsMemory.
+ free(value);
+
+ return NS_OK;
+}
+
+nsresult nsNPAPIPluginInstance::PushPopupsEnabledState(bool aEnabled) {
+ nsCOMPtr<nsPIDOMWindowOuter> window = GetDOMWindow();
+ if (!window) return NS_ERROR_FAILURE;
+
+ PopupBlocker::PopupControlState oldState =
+ PopupBlocker::PushPopupControlState(
+ aEnabled ? PopupBlocker::openAllowed : PopupBlocker::openAbused,
+ true);
+
+ // XXX(Bug 1631371) Check if this should use a fallible operation as it
+ // pretended earlier.
+ mPopupStates.AppendElement(oldState);
+
+ return NS_OK;
+}
+
+nsresult nsNPAPIPluginInstance::PopPopupsEnabledState() {
+ if (mPopupStates.IsEmpty()) {
+ // Nothing to pop.
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = GetDOMWindow();
+ if (!window) return NS_ERROR_FAILURE;
+
+ PopupBlocker::PopPopupControlState(mPopupStates.PopLastElement());
+
+ return NS_OK;
+}
+
+nsresult nsNPAPIPluginInstance::GetPluginAPIVersion(uint16_t* version) {
+ NS_ENSURE_ARG_POINTER(version);
+
+ if (!mPlugin) return NS_ERROR_FAILURE;
+
+ if (!mPlugin->GetLibrary()) return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
+
+ *version = pluginFunctions->version;
+
+ return NS_OK;
+}
+
+nsresult nsNPAPIPluginInstance::PrivateModeStateChanged(bool enabled) {
+ if (RUNNING != mRunning) return NS_OK;
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance informing plugin of "
+ "private mode state change this=%p\n",
+ this));
+
+ if (!mPlugin || !mPlugin->GetLibrary()) return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
+
+ if (!pluginFunctions->setvalue) return NS_ERROR_FAILURE;
+
+ PluginDestructionGuard guard(this);
+
+ NPError error;
+ NPBool value = static_cast<NPBool>(enabled);
+ NS_TRY_SAFE_CALL_RETURN(
+ error, (*pluginFunctions->setvalue)(&mNPP, NPNVprivateModeBool, &value),
+ this, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+ return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsNPAPIPluginInstance::IsPrivateBrowsing(bool* aEnabled) {
+ if (!mOwner) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<Document> doc;
+ mOwner->GetDocument(getter_AddRefs(doc));
+ NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsPIDOMWindowOuter> domwindow = doc->GetWindow();
+ NS_ENSURE_TRUE(domwindow, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIDocShell> docShell = domwindow->GetDocShell();
+ nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
+ *aEnabled = (loadContext && loadContext->UsePrivateBrowsing());
+ return NS_OK;
+}
+
+static void PluginTimerCallback(nsITimer* aTimer, void* aClosure) {
+ nsNPAPITimer* t = (nsNPAPITimer*)aClosure;
+ NPP npp = t->npp;
+ uint32_t id = t->id;
+
+ PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("nsNPAPIPluginInstance running plugin timer callback this=%p\n",
+ npp->ndata));
+
+ // Some plugins (Flash on Android) calls unscheduletimer
+ // from this callback.
+ t->inCallback = true;
+ (*(t->callback))(npp, id);
+ t->inCallback = false;
+
+ // Make sure we still have an instance and the timer is still alive
+ // after the callback.
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)npp->ndata;
+ if (!inst || !inst->TimerWithID(id, nullptr)) return;
+
+ // use UnscheduleTimer to clean up if this is a one-shot timer
+ uint32_t timerType;
+ t->timer->GetType(&timerType);
+ if (t->needUnschedule || timerType == nsITimer::TYPE_ONE_SHOT)
+ inst->UnscheduleTimer(id);
+}
+
+nsNPAPITimer* nsNPAPIPluginInstance::TimerWithID(uint32_t id, uint32_t* index) {
+ uint32_t len = mTimers.Length();
+ for (uint32_t i = 0; i < len; i++) {
+ if (mTimers[i]->id == id) {
+ if (index) *index = i;
+ return mTimers[i];
+ }
+ }
+ return nullptr;
+}
+
+uint32_t nsNPAPIPluginInstance::ScheduleTimer(
+ uint32_t interval, NPBool repeat,
+ void (*timerFunc)(NPP npp, uint32_t timerID)) {
+ if (RUNNING != mRunning) return 0;
+
+ nsNPAPITimer* newTimer = new nsNPAPITimer();
+
+ newTimer->inCallback = newTimer->needUnschedule = false;
+ newTimer->npp = &mNPP;
+
+ // generate ID that is unique to this instance
+ uint32_t uniqueID = mTimers.Length();
+ while ((uniqueID == 0) || TimerWithID(uniqueID, nullptr)) uniqueID++;
+ newTimer->id = uniqueID;
+
+ // create new xpcom timer, scheduled correctly
+ nsresult rv;
+ const short timerType = (repeat ? (short)nsITimer::TYPE_REPEATING_SLACK
+ : (short)nsITimer::TYPE_ONE_SHOT);
+ rv = NS_NewTimerWithFuncCallback(
+ getter_AddRefs(newTimer->timer), PluginTimerCallback, newTimer, interval,
+ timerType, "nsNPAPIPluginInstance::ScheduleTimer");
+ if (NS_FAILED(rv)) {
+ delete newTimer;
+ return 0;
+ }
+
+ // save callback function
+ newTimer->callback = timerFunc;
+
+ // add timer to timers array
+ mTimers.AppendElement(newTimer);
+
+ return newTimer->id;
+}
+
+void nsNPAPIPluginInstance::UnscheduleTimer(uint32_t timerID) {
+ // find the timer struct by ID
+ uint32_t index;
+ nsNPAPITimer* t = TimerWithID(timerID, &index);
+ if (!t) return;
+
+ if (t->inCallback) {
+ t->needUnschedule = true;
+ return;
+ }
+
+ // cancel the timer
+ t->timer->Cancel();
+
+ // remove timer struct from array
+ mTimers.RemoveElementAt(index);
+
+ // delete timer
+ delete t;
+}
+
+NPBool nsNPAPIPluginInstance::ConvertPoint(double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double* destX, double* destY,
+ NPCoordinateSpace destSpace) {
+ if (mOwner) {
+ return mOwner->ConvertPoint(sourceX, sourceY, sourceSpace, destX, destY,
+ destSpace);
+ }
+
+ return false;
+}
+
+nsresult nsNPAPIPluginInstance::GetDOMElement(Element** result) {
+ if (!mOwner) {
+ *result = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+
+ return mOwner->GetDOMElement(result);
+}
+
+nsresult nsNPAPIPluginInstance::InvalidateRect(NPRect* invalidRect) {
+ if (RUNNING != mRunning) return NS_OK;
+
+ if (!mOwner) return NS_ERROR_FAILURE;
+
+ return mOwner->InvalidateRect(invalidRect);
+}
+
+nsresult nsNPAPIPluginInstance::InvalidateRegion(NPRegion invalidRegion) {
+ if (RUNNING != mRunning) return NS_OK;
+
+ if (!mOwner) return NS_ERROR_FAILURE;
+
+ return mOwner->InvalidateRegion(invalidRegion);
+}
+
+nsresult nsNPAPIPluginInstance::GetMIMEType(const char** result) {
+ if (!mMIMEType)
+ *result = "";
+ else
+ *result = mMIMEType;
+
+ return NS_OK;
+}
+
+nsPluginInstanceOwner* nsNPAPIPluginInstance::GetOwner() { return mOwner; }
+
+void nsNPAPIPluginInstance::SetOwner(nsPluginInstanceOwner* aOwner) {
+ mOwner = aOwner;
+}
+
+nsresult nsNPAPIPluginInstance::AsyncSetWindow(NPWindow& window) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void nsNPAPIPluginInstance::URLRedirectResponse(void* notifyData,
+ NPBool allow) {
+ if (!notifyData) {
+ return;
+ }
+
+ uint32_t listenerCount = mStreamListeners.Length();
+ for (uint32_t i = 0; i < listenerCount; i++) {
+ nsNPAPIPluginStreamListener* currentListener = mStreamListeners[i];
+ if (currentListener->GetNotifyData() == notifyData) {
+ currentListener->URLRedirectResponse(allow);
+ }
+ }
+}
+
+NPError nsNPAPIPluginInstance::InitAsyncSurface(NPSize* size,
+ NPImageFormat format,
+ void* initData,
+ NPAsyncSurface* surface) {
+ if (mOwner) {
+ return mOwner->InitAsyncSurface(size, format, initData, surface);
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError nsNPAPIPluginInstance::FinalizeAsyncSurface(NPAsyncSurface* surface) {
+ if (mOwner) {
+ return mOwner->FinalizeAsyncSurface(surface);
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+void nsNPAPIPluginInstance::SetCurrentAsyncSurface(NPAsyncSurface* surface,
+ NPRect* changed) {
+ if (mOwner) {
+ mOwner->SetCurrentAsyncSurface(surface, changed);
+ }
+}
+
+double nsNPAPIPluginInstance::GetContentsScaleFactor() {
+ double scaleFactor = 1.0;
+ if (mOwner) {
+ mOwner->GetContentsScaleFactor(&scaleFactor);
+ }
+ return scaleFactor;
+}
+
+float nsNPAPIPluginInstance::GetCSSZoomFactor() {
+ float zoomFactor = 1.0;
+ if (mOwner) {
+ mOwner->GetCSSZoomFactor(&zoomFactor);
+ }
+ return zoomFactor;
+}
+
+nsresult nsNPAPIPluginInstance::GetRunID(uint32_t* aRunID) {
+ if (NS_WARN_IF(!aRunID)) {
+ return NS_ERROR_INVALID_POINTER;
+ }
+
+ if (NS_WARN_IF(!mPlugin)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ PluginLibrary* library = mPlugin->GetLibrary();
+ if (!library) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return library->GetRunID(aRunID);
+}
+
+nsresult nsNPAPIPluginInstance::CreateAudioChannelAgentIfNeeded() {
+ if (mAudioChannelAgent) {
+ return NS_OK;
+ }
+
+ mAudioChannelAgent = new AudioChannelAgent();
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = GetDOMWindow();
+ if (NS_WARN_IF(!window)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = mAudioChannelAgent->Init(window->GetCurrentInnerWindow(), this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+void nsNPAPIPluginInstance::NotifyStartedPlaying() {
+ nsresult rv = CreateAudioChannelAgentIfNeeded();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ MOZ_ASSERT(mAudioChannelAgent);
+ rv = mAudioChannelAgent->NotifyStartedPlaying(
+ mIsMuted ? AudioChannelService::AudibleState::eNotAudible
+ : AudioChannelService::AudibleState::eAudible);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ mAudioChannelAgent->PullInitialUpdate();
+}
+
+void nsNPAPIPluginInstance::NotifyStoppedPlaying() {
+ MOZ_ASSERT(mAudioChannelAgent);
+ nsresult rv = mAudioChannelAgent->NotifyStoppedPlaying();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginInstance::WindowVolumeChanged(float aVolume, bool aMuted) {
+ MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+ ("nsNPAPIPluginInstance, WindowVolumeChanged, "
+ "this = %p, aVolume = %f, aMuted = %s\n",
+ this, aVolume, aMuted ? "true" : "false"));
+ // We just support mute/unmute
+ if (mWindowMuted != aMuted) {
+ mWindowMuted = aMuted;
+ return UpdateMutedIfNeeded();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginInstance::WindowSuspendChanged(nsSuspendedTypes aSuspend) {
+ MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+ ("nsNPAPIPluginInstance, WindowSuspendChanged, "
+ "this = %p, aSuspend = %s\n",
+ this, SuspendTypeToStr(aSuspend)));
+ const bool isSuspended = aSuspend != nsISuspendedTypes::NONE_SUSPENDED;
+ if (mWindowSuspended != isSuspended) {
+ mWindowSuspended = isSuspended;
+ // It doesn't support suspending, so we just do something like mute/unmute.
+ return UpdateMutedIfNeeded();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginInstance::WindowAudioCaptureChanged(bool aCapture) {
+ return NS_OK;
+}
+
+void nsNPAPIPluginInstance::NotifyAudibleStateChanged() const {
+ // This happens when global window destroyed, we would notify agent's callback
+ // to mute its volume, but the nsNSNPAPI had released the agent before that.
+ if (!mAudioChannelAgent) {
+ return;
+ }
+ AudioChannelService::AudibleState audibleState =
+ mIsMuted ? AudioChannelService::AudibleState::eNotAudible
+ : AudioChannelService::AudibleState::eAudible;
+ // Because we don't really support suspending nsNPAPI, so all audible changes
+ // come from changing its volume.
+ mAudioChannelAgent->NotifyStartedAudible(
+ audibleState, AudioChannelService::AudibleChangedReasons::eVolumeChanged);
+}
+
+nsresult nsNPAPIPluginInstance::UpdateMutedIfNeeded() {
+ const bool shouldMute = mWindowSuspended || mWindowMuted;
+ if (mIsMuted == shouldMute) {
+ return NS_OK;
+ }
+
+ mIsMuted = shouldMute;
+ NotifyAudibleStateChanged();
+ nsresult rv = SetMuted(mIsMuted);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetMuted failed");
+ return rv;
+}
+
+nsresult nsNPAPIPluginInstance::SetMuted(bool aIsMuted) {
+ if (RUNNING != mRunning) return NS_OK;
+
+ PLUGIN_LOG(
+ PLUGIN_LOG_NORMAL,
+ ("nsNPAPIPluginInstance informing plugin of mute state change this=%p\n",
+ this));
+
+ if (!mPlugin || !mPlugin->GetLibrary()) return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
+
+ if (!pluginFunctions->setvalue) return NS_ERROR_FAILURE;
+
+ PluginDestructionGuard guard(this);
+
+ NPError error;
+ NPBool value = static_cast<NPBool>(aIsMuted);
+ NS_TRY_SAFE_CALL_RETURN(
+ error, (*pluginFunctions->setvalue)(&mNPP, NPNVmuteAudioBool, &value),
+ this, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+ return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE;
+}
diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h
new file mode 100644
index 0000000000..1ca2569ae8
--- /dev/null
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -0,0 +1,327 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsNPAPIPluginInstance_h_
+#define nsNPAPIPluginInstance_h_
+
+#include "nsSize.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsPIDOMWindow.h"
+#include "nsITimer.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsHashKeys.h"
+#include <prinrval.h>
+#include "js/TypeDecls.h"
+#include "AudioChannelAgent.h"
+
+#include "mozilla/EventForwards.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/PluginLibrary.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/WeakPtr.h"
+#include "mozilla/dom/PopupBlocker.h"
+
+class nsPluginStreamListenerPeer; // browser-initiated stream class
+class nsNPAPIPluginStreamListener; // plugin-initiated stream class
+class nsIPluginInstanceOwner;
+class nsIOutputStream;
+class nsPluginInstanceOwner;
+
+namespace mozilla {
+namespace dom {
+class Element;
+} // namespace dom
+} // namespace mozilla
+
+#if defined(OS_WIN)
+const NPDrawingModel kDefaultDrawingModel = NPDrawingModelSyncWin;
+#elif defined(MOZ_X11)
+const NPDrawingModel kDefaultDrawingModel = NPDrawingModelSyncX;
+#elif defined(XP_MACOSX)
+# ifndef NP_NO_QUICKDRAW
+const NPDrawingModel kDefaultDrawingModel =
+ NPDrawingModelQuickDraw; // Not supported
+# else
+const NPDrawingModel kDefaultDrawingModel = NPDrawingModelCoreGraphics;
+# endif
+#else
+const NPDrawingModel kDefaultDrawingModel = static_cast<NPDrawingModel>(0);
+#endif
+
+#if defined(OS_WIN)
+static const DWORD NPAPI_INVALID_WPARAM = 0xffffffff;
+#endif
+
+/**
+ * Used to indicate whether it's OK to reenter Gecko and repaint, flush frames,
+ * run scripts, etc, during this plugin call.
+ * When NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO is set, we try to avoid dangerous
+ * Gecko activities when the plugin spins a nested event loop, on a best-effort
+ * basis.
+ */
+enum NSPluginCallReentry {
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO,
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO
+};
+
+class nsNPAPITimer {
+ public:
+ NPP npp;
+ uint32_t id;
+ nsCOMPtr<nsITimer> timer;
+ void (*callback)(NPP npp, uint32_t timerID);
+ bool inCallback;
+ bool needUnschedule;
+};
+
+class nsNPAPIPluginInstance final : public nsIAudioChannelAgentCallback,
+ public mozilla::SupportsWeakPtr {
+ private:
+ typedef mozilla::PluginLibrary PluginLibrary;
+
+ public:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
+
+ nsresult Initialize(nsNPAPIPlugin* aPlugin, nsPluginInstanceOwner* aOwner,
+ const nsACString& aMIMEType);
+ nsresult Start();
+ nsresult Stop();
+ nsresult SetWindow(NPWindow* window);
+ nsresult NewStreamFromPlugin(const char* type, const char* target,
+ nsIOutputStream** result);
+ nsresult Print(NPPrint* platformPrint);
+ nsresult HandleEvent(void* event, int16_t* result,
+ NSPluginCallReentry aSafeToReenterGecko =
+ NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+ nsresult GetValueFromPlugin(NPPVariable variable, void* value);
+ nsresult GetDrawingModel(int32_t* aModel);
+ nsresult IsRemoteDrawingCoreAnimation(bool* aDrawing);
+ nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
+ nsresult CSSZoomFactorChanged(float aCSSZoomFactor);
+ nsresult GetJSObject(JSContext* cx, JSObject** outObject);
+ bool ShouldCache();
+ nsresult IsWindowless(bool* isWindowless);
+ nsresult AsyncSetWindow(NPWindow* window);
+ nsresult GetImageContainer(mozilla::layers::ImageContainer** aContainer);
+ nsresult GetImageSize(nsIntSize* aSize);
+ nsresult NotifyPainted(void);
+ nsresult GetIsOOP(bool* aIsOOP);
+ nsresult SetBackgroundUnknown();
+ nsresult BeginUpdateBackground(nsIntRect* aRect, DrawTarget** aContext);
+ nsresult EndUpdateBackground(nsIntRect* aRect);
+ nsresult IsTransparent(bool* isTransparent);
+ nsresult GetFormValue(nsAString& aValue);
+ nsresult PushPopupsEnabledState(bool aEnabled);
+ nsresult PopPopupsEnabledState();
+ nsresult GetPluginAPIVersion(uint16_t* version);
+ nsresult InvalidateRect(NPRect* invalidRect);
+ nsresult InvalidateRegion(NPRegion invalidRegion);
+ nsresult GetMIMEType(const char** result);
+#if defined(XP_WIN)
+ nsresult GetScrollCaptureContainer(
+ mozilla::layers::ImageContainer** aContainer);
+#endif
+ nsresult HandledWindowedPluginKeyEvent(
+ const mozilla::NativeEventData& aKeyEventData, bool aIsConsumed);
+ nsPluginInstanceOwner* GetOwner();
+ void SetOwner(nsPluginInstanceOwner* aOwner);
+ void DidComposite();
+
+ bool HasAudioChannelAgent() const { return !!mAudioChannelAgent; }
+
+ void NotifyStartedPlaying();
+ void NotifyStoppedPlaying();
+
+ nsresult SetMuted(bool aIsMuted);
+
+ nsNPAPIPlugin* GetPlugin();
+
+ nsresult GetNPP(NPP* aNPP);
+
+ NPError SetWindowless(bool aWindowless);
+
+ NPError SetTransparent(bool aTransparent);
+
+ NPError SetWantsAllNetworkStreams(bool aWantsAllNetworkStreams);
+
+ NPError SetUsesDOMForCursor(bool aUsesDOMForCursor);
+ bool UsesDOMForCursor();
+
+ void SetDrawingModel(NPDrawingModel aModel);
+ void RedrawPlugin();
+#ifdef XP_MACOSX
+ void SetEventModel(NPEventModel aModel);
+
+ void* GetCurrentEvent() { return mCurrentPluginEvent; }
+#endif
+
+ nsresult NewStreamListener(const char* aURL, void* notifyData,
+ nsNPAPIPluginStreamListener** listener);
+
+ nsNPAPIPluginInstance();
+
+ // To be called when an instance becomes orphaned, when
+ // it's plugin is no longer guaranteed to be around.
+ void Destroy();
+
+ // Indicates whether the plugin is running normally.
+ bool IsRunning() { return RUNNING == mRunning; }
+ bool HasStartedDestroying() { return mRunning >= DESTROYING; }
+
+ // Indicates whether the plugin is running normally or being shut down
+ bool CanFireNotifications() {
+ return mRunning == RUNNING || mRunning == DESTROYING;
+ }
+
+ // return is only valid when the plugin is not running
+ mozilla::TimeStamp StopTime();
+
+ // cache this NPAPI plugin
+ void SetCached(bool aCache);
+
+ already_AddRefed<nsPIDOMWindowOuter> GetDOMWindow();
+
+ nsresult PrivateModeStateChanged(bool aEnabled);
+
+ nsresult IsPrivateBrowsing(bool* aEnabled);
+
+ nsresult GetDOMElement(mozilla::dom::Element** result);
+
+ nsNPAPITimer* TimerWithID(uint32_t id, uint32_t* index);
+ uint32_t ScheduleTimer(uint32_t interval, NPBool repeat,
+ void (*timerFunc)(NPP npp, uint32_t timerID));
+ void UnscheduleTimer(uint32_t timerID);
+ NPBool ConvertPoint(double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace, double* destX,
+ double* destY, NPCoordinateSpace destSpace);
+
+ nsTArray<nsNPAPIPluginStreamListener*>* StreamListeners();
+
+ nsTArray<nsPluginStreamListenerPeer*>* FileCachedStreamListeners();
+
+ nsresult AsyncSetWindow(NPWindow& window);
+
+ void URLRedirectResponse(void* notifyData, NPBool allow);
+
+ NPError InitAsyncSurface(NPSize* size, NPImageFormat format, void* initData,
+ NPAsyncSurface* surface);
+ NPError FinalizeAsyncSurface(NPAsyncSurface* surface);
+ void SetCurrentAsyncSurface(NPAsyncSurface* surface, NPRect* changed);
+
+ // Returns the contents scale factor of the screen the plugin is drawn on.
+ double GetContentsScaleFactor();
+
+ // Returns the css zoom factor of the document the plugin is drawn on.
+ float GetCSSZoomFactor();
+
+ nsresult GetRunID(uint32_t* aRunID);
+
+ static bool InPluginCallUnsafeForReentry() {
+ return gInUnsafePluginCalls > 0;
+ }
+ static void BeginPluginCall(NSPluginCallReentry aReentryState) {
+ if (aReentryState == NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO) {
+ ++gInUnsafePluginCalls;
+ }
+ }
+ static void EndPluginCall(NSPluginCallReentry aReentryState) {
+ if (aReentryState == NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO) {
+ NS_ASSERTION(gInUnsafePluginCalls > 0, "Must be in plugin call");
+ --gInUnsafePluginCalls;
+ }
+ }
+
+ protected:
+ virtual ~nsNPAPIPluginInstance();
+
+ nsresult GetTagType(nsPluginTagType* result);
+
+ nsresult CreateAudioChannelAgentIfNeeded();
+
+ void NotifyAudibleStateChanged() const;
+
+ nsresult UpdateMutedIfNeeded();
+
+ // The structure used to communicate between the plugin instance and
+ // the browser.
+ NPP_t mNPP;
+
+ NPDrawingModel mDrawingModel;
+
+ enum { NOT_STARTED, RUNNING, DESTROYING, DESTROYED } mRunning;
+
+ // these are used to store the windowless properties
+ // which the browser will later query
+ bool mWindowless;
+ bool mTransparent;
+ bool mCached;
+ bool mUsesDOMForCursor;
+
+ public:
+ // True while creating the plugin, or calling NPP_SetWindow() on it.
+ bool mInPluginInitCall;
+
+ private:
+ RefPtr<nsNPAPIPlugin> mPlugin;
+
+ nsTArray<nsNPAPIPluginStreamListener*> mStreamListeners;
+
+ nsTArray<nsPluginStreamListenerPeer*> mFileCachedStreamListeners;
+
+ nsTArray<mozilla::dom::PopupBlocker::PopupControlState> mPopupStates;
+
+ char* mMIMEType;
+
+ // Weak pointer to the owner. The owner nulls this out (by calling
+ // InvalidateOwner()) when it's no longer our owner.
+ nsPluginInstanceOwner* mOwner;
+
+ nsTArray<nsNPAPITimer*> mTimers;
+
+#ifdef XP_MACOSX
+ // non-null during a HandleEvent call
+ void* mCurrentPluginEvent;
+#endif
+
+ // Timestamp for the last time this plugin was stopped.
+ // This is only valid when the plugin is actually stopped!
+ mozilla::TimeStamp mStopTime;
+
+ static uint32_t gInUnsafePluginCalls;
+
+ // The arrays can only be released when the plugin instance is destroyed,
+ // because the plugin, in in-process mode, might keep a reference to them.
+ uint32_t mCachedParamLength;
+ char** mCachedParamNames;
+ char** mCachedParamValues;
+
+ RefPtr<mozilla::dom::AudioChannelAgent> mAudioChannelAgent;
+ bool mIsMuted = false;
+ bool mWindowMuted = false;
+ bool mWindowSuspended = false;
+};
+
+void NS_NotifyBeginPluginCall(NSPluginCallReentry aReentryState);
+void NS_NotifyPluginCall(NSPluginCallReentry aReentryState);
+
+#define NS_TRY_SAFE_CALL_RETURN(ret, fun, pluginInst, pluginCallReentry) \
+ PR_BEGIN_MACRO \
+ NS_NotifyBeginPluginCall(pluginCallReentry); \
+ ret = fun; \
+ NS_NotifyPluginCall(pluginCallReentry); \
+ PR_END_MACRO
+
+#define NS_TRY_SAFE_CALL_VOID(fun, pluginInst, pluginCallReentry) \
+ PR_BEGIN_MACRO \
+ NS_NotifyBeginPluginCall(pluginCallReentry); \
+ fun; \
+ NS_NotifyPluginCall(pluginCallReentry); \
+ PR_END_MACRO
+
+#endif // nsNPAPIPluginInstance_h_
diff --git a/dom/plugins/base/nsNPAPIPluginStreamListener.cpp b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
new file mode 100644
index 0000000000..96ee7d2060
--- /dev/null
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
@@ -0,0 +1,775 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsNPAPIPluginStreamListener.h"
+#include "plstr.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIHttpChannel.h"
+#include "nsNetUtil.h"
+#include "nsPluginHost.h"
+#include "nsNPAPIPlugin.h"
+#include "nsPluginLogging.h"
+#include "nsPluginStreamListenerPeer.h"
+
+#include <stdint.h>
+#include <algorithm>
+
+nsNPAPIStreamWrapper::nsNPAPIStreamWrapper(
+ nsIOutputStream* outputStream,
+ nsNPAPIPluginStreamListener* streamListener) {
+ mOutputStream = outputStream;
+ mStreamListener = streamListener;
+
+ memset(&mNPStream, 0, sizeof(mNPStream));
+ mNPStream.ndata = static_cast<void*>(this);
+}
+
+nsNPAPIStreamWrapper::~nsNPAPIStreamWrapper() {
+ if (mOutputStream) {
+ mOutputStream->Close();
+ }
+}
+
+// nsNPAPIPluginStreamListener Methods
+NS_IMPL_ISUPPORTS(nsNPAPIPluginStreamListener, nsITimerCallback,
+ nsIHTTPHeaderListener, nsINamed)
+
+nsNPAPIPluginStreamListener::nsNPAPIPluginStreamListener(
+ nsNPAPIPluginInstance* inst, void* notifyData, const char* aURL)
+ : mStreamBuffer(nullptr),
+ mNotifyURL(aURL ? PL_strdup(aURL) : nullptr),
+ mInst(inst),
+ mStreamBufferSize(0),
+ mStreamBufferByteCount(0),
+ mStreamState(eStreamStopped),
+ mStreamCleanedUp(false),
+ mCallNotify(notifyData ? true : false),
+ mIsSuspended(false),
+ mIsPluginInitJSStream(
+ mInst->mInPluginInitCall && aURL &&
+ strncmp(aURL, "javascript:", sizeof("javascript:") - 1) == 0),
+ mRedirectDenied(false),
+ mResponseHeaderBuf(nullptr),
+ mStreamStopMode(eNormalStop),
+ mPendingStopBindingStatus(NS_OK) {
+ mNPStreamWrapper = new nsNPAPIStreamWrapper(nullptr, this);
+ mNPStreamWrapper->mNPStream.notifyData = notifyData;
+}
+
+nsNPAPIPluginStreamListener::~nsNPAPIPluginStreamListener() {
+ // remove this from the plugin instance's stream list
+ nsTArray<nsNPAPIPluginStreamListener*>* streamListeners =
+ mInst->StreamListeners();
+ streamListeners->RemoveElement(this);
+
+ // For those cases when NewStream is never called, we still may need
+ // to fire a notification callback. Return network error as fallback
+ // reason because for other cases, notify should have already been
+ // called for other reasons elsewhere.
+ CallURLNotify(NPRES_NETWORK_ERR);
+
+ // lets get rid of the buffer
+ if (mStreamBuffer) {
+ free(mStreamBuffer);
+ mStreamBuffer = nullptr;
+ }
+
+ if (mNotifyURL) PL_strfree(mNotifyURL);
+
+ if (mResponseHeaderBuf) PL_strfree(mResponseHeaderBuf);
+
+ if (mNPStreamWrapper) {
+ delete mNPStreamWrapper;
+ }
+}
+
+nsresult nsNPAPIPluginStreamListener::CleanUpStream(NPReason reason) {
+ nsresult rv = NS_ERROR_FAILURE;
+
+ // Various bits of code in the rest of this method may result in the
+ // deletion of this object. Use a KungFuDeathGrip to keep ourselves
+ // alive during cleanup.
+ RefPtr<nsNPAPIPluginStreamListener> kungFuDeathGrip(this);
+
+ if (mStreamCleanedUp) return NS_OK;
+
+ mStreamCleanedUp = true;
+
+ StopDataPump();
+
+ // Release any outstanding redirect callback.
+ if (mHTTPRedirectCallback) {
+ mHTTPRedirectCallback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
+ mHTTPRedirectCallback = nullptr;
+ }
+
+ if (mStreamListenerPeer) {
+ mStreamListenerPeer->CancelRequests(NS_BINDING_ABORTED);
+ mStreamListenerPeer = nullptr;
+ }
+
+ if (!mInst || !mInst->CanFireNotifications()) return rv;
+
+ PluginDestructionGuard guard(mInst);
+
+ nsNPAPIPlugin* plugin = mInst->GetPlugin();
+ if (!plugin || !plugin->GetLibrary()) return rv;
+
+ NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
+
+ NPP npp;
+ mInst->GetNPP(&npp);
+
+ if (mStreamState >= eNewStreamCalled && pluginFunctions->destroystream) {
+ NPPAutoPusher nppPusher(npp);
+
+ NPError error;
+ NS_TRY_SAFE_CALL_RETURN(error,
+ (*pluginFunctions->destroystream)(
+ npp, &mNPStreamWrapper->mNPStream, reason),
+ mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPP DestroyStream called: this=%p, npp=%p, reason=%d, "
+ "return=%d, url=%s\n",
+ this, npp, reason, error, mNPStreamWrapper->mNPStream.url));
+
+ if (error == NPERR_NO_ERROR) rv = NS_OK;
+ }
+
+ mStreamState = eStreamStopped;
+
+ // fire notification back to plugin, just like before
+ CallURLNotify(reason);
+
+ return rv;
+}
+
+void nsNPAPIPluginStreamListener::CallURLNotify(NPReason reason) {
+ if (!mCallNotify || !mInst || !mInst->CanFireNotifications()) return;
+
+ PluginDestructionGuard guard(mInst);
+
+ mCallNotify = false; // only do this ONCE and prevent recursion
+
+ nsNPAPIPlugin* plugin = mInst->GetPlugin();
+ if (!plugin || !plugin->GetLibrary()) return;
+
+ NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
+
+ if (pluginFunctions->urlnotify) {
+ NPP npp;
+ mInst->GetNPP(&npp);
+
+ NS_TRY_SAFE_CALL_VOID(
+ (*pluginFunctions->urlnotify)(npp, mNotifyURL, reason,
+ mNPStreamWrapper->mNPStream.notifyData),
+ mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPP URLNotify called: this=%p, npp=%p, notify=%p, "
+ "reason=%d, url=%s\n",
+ this, npp, mNPStreamWrapper->mNPStream.notifyData, reason,
+ mNotifyURL));
+ }
+}
+
+nsresult nsNPAPIPluginStreamListener::OnStartBinding(
+ nsPluginStreamListenerPeer* streamPeer) {
+ AUTO_PROFILER_LABEL("nsNPAPIPluginStreamListener::OnStartBinding", OTHER);
+ if (!mInst || !mInst->CanFireNotifications() || mStreamCleanedUp)
+ return NS_ERROR_FAILURE;
+
+ PluginDestructionGuard guard(mInst);
+
+ nsNPAPIPlugin* plugin = mInst->GetPlugin();
+ if (!plugin || !plugin->GetLibrary()) return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
+
+ if (!pluginFunctions->newstream) return NS_ERROR_FAILURE;
+
+ NPP npp;
+ mInst->GetNPP(&npp);
+
+ char* contentType;
+ uint16_t streamType = NP_NORMAL;
+ NPError error;
+
+ streamPeer->GetURL(&mNPStreamWrapper->mNPStream.url);
+ streamPeer->GetLength((uint32_t*)&(mNPStreamWrapper->mNPStream.end));
+ streamPeer->GetLastModified(
+ (uint32_t*)&(mNPStreamWrapper->mNPStream.lastmodified));
+ streamPeer->GetContentType(&contentType);
+
+ if (!mResponseHeaders.IsEmpty()) {
+ mResponseHeaderBuf = PL_strdup(mResponseHeaders.get());
+ mNPStreamWrapper->mNPStream.headers = mResponseHeaderBuf;
+ }
+
+ mStreamListenerPeer = streamPeer;
+
+ NPPAutoPusher nppPusher(npp);
+
+ NS_TRY_SAFE_CALL_RETURN(error,
+ (*pluginFunctions->newstream)(
+ npp, (char*)contentType,
+ &mNPStreamWrapper->mNPStream, false, &streamType),
+ mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, "
+ "type=%d, return=%d, url=%s\n",
+ this, npp, (char*)contentType, false, streamType, error,
+ mNPStreamWrapper->mNPStream.url));
+
+ if (error != NPERR_NO_ERROR) return NS_ERROR_FAILURE;
+
+ mStreamState = eNewStreamCalled;
+
+ if (streamType != NP_NORMAL) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+void nsNPAPIPluginStreamListener::SuspendRequest() {
+ NS_ASSERTION(!mIsSuspended, "Suspending a request that's already suspended!");
+
+ nsresult rv = StartDataPump();
+ if (NS_FAILED(rv)) return;
+
+ mIsSuspended = true;
+
+ if (mStreamListenerPeer) {
+ mStreamListenerPeer->SuspendRequests();
+ }
+}
+
+void nsNPAPIPluginStreamListener::ResumeRequest() {
+ if (mStreamListenerPeer) {
+ mStreamListenerPeer->ResumeRequests();
+ }
+ mIsSuspended = false;
+}
+
+nsresult nsNPAPIPluginStreamListener::StartDataPump() {
+ // Start pumping data to the plugin every 100ms until it obeys and
+ // eats the data.
+ return NS_NewTimerWithCallback(getter_AddRefs(mDataPumpTimer), this, 100,
+ nsITimer::TYPE_REPEATING_SLACK);
+}
+
+void nsNPAPIPluginStreamListener::StopDataPump() {
+ if (mDataPumpTimer) {
+ mDataPumpTimer->Cancel();
+ mDataPumpTimer = nullptr;
+ }
+}
+
+// Return true if a javascript: load that was started while the plugin
+// was being initialized is still in progress.
+bool nsNPAPIPluginStreamListener::PluginInitJSLoadInProgress() {
+ if (!mInst) return false;
+
+ nsTArray<nsNPAPIPluginStreamListener*>* streamListeners =
+ mInst->StreamListeners();
+ for (unsigned int i = 0; i < streamListeners->Length(); i++) {
+ if (streamListeners->ElementAt(i)->mIsPluginInitJSStream) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// This method is called when there's more data available off the
+// network, but it's also called from our data pump when we're feeding
+// the plugin data that we already got off the network, but the plugin
+// was unable to consume it at the point it arrived. In the case when
+// the plugin pump calls this method, the input argument will be null,
+// and the length will be the number of bytes available in our
+// internal buffer.
+nsresult nsNPAPIPluginStreamListener::OnDataAvailable(
+ nsPluginStreamListenerPeer* streamPeer, nsIInputStream* input,
+ uint32_t length) {
+ if (!length || !mInst || !mInst->CanFireNotifications())
+ return NS_ERROR_FAILURE;
+
+ PluginDestructionGuard guard(mInst);
+
+ // Just in case the caller switches plugin info on us.
+ mStreamListenerPeer = streamPeer;
+
+ nsNPAPIPlugin* plugin = mInst->GetPlugin();
+ if (!plugin || !plugin->GetLibrary()) return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
+
+ // check out if plugin implements NPP_Write call
+ if (!pluginFunctions->write)
+ return NS_ERROR_FAILURE; // it'll cancel necko transaction
+
+ if (!mStreamBuffer) {
+ // To optimize the mem usage & performance we have to allocate
+ // mStreamBuffer here in first ODA when length of data available
+ // in input stream is known. mStreamBuffer will be freed in DTOR.
+ // we also have to remember the size of that buff to make safe
+ // consecutive Read() calls form input stream into our buff.
+
+ uint32_t contentLength;
+ streamPeer->GetLength(&contentLength);
+
+ mStreamBufferSize = std::max(length, contentLength);
+
+ // Limit the size of the initial buffer to MAX_PLUGIN_NECKO_BUFFER
+ // (16k). This buffer will grow if needed, as in the case where
+ // we're getting data faster than the plugin can process it.
+ mStreamBufferSize =
+ std::min(mStreamBufferSize, uint32_t(MAX_PLUGIN_NECKO_BUFFER));
+
+ mStreamBuffer = (char*)malloc(mStreamBufferSize);
+ if (!mStreamBuffer) return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // prepare NPP_ calls params
+ NPP npp;
+ mInst->GetNPP(&npp);
+
+ int32_t streamPosition;
+ streamPeer->GetStreamOffset(&streamPosition);
+ int32_t streamOffset = streamPosition;
+
+ if (input) {
+ streamOffset += length;
+
+ // Set new stream offset for the next ODA call regardless of how
+ // following NPP_Write call will behave we pretend to consume all
+ // data from the input stream. It's possible that current steam
+ // position will be overwritten from NPP_RangeRequest call made
+ // from NPP_Write, so we cannot call SetStreamOffset after
+ // NPP_Write.
+ //
+ // Note: there is a special case when data flow should be
+ // temporarily stopped if NPP_WriteReady returns 0 (bug #89270)
+ streamPeer->SetStreamOffset(streamOffset);
+
+ // set new end in case the content is compressed
+ // initial end is less than end of decompressed stream
+ // and some plugins (e.g. acrobat) can fail.
+ if ((int32_t)mNPStreamWrapper->mNPStream.end < streamOffset)
+ mNPStreamWrapper->mNPStream.end = streamOffset;
+ }
+
+ nsresult rv = NS_OK;
+ while (NS_SUCCEEDED(rv) && length > 0) {
+ if (input && length) {
+ if (mStreamBufferSize < mStreamBufferByteCount + length) {
+ // We're in the ::OnDataAvailable() call that we might get
+ // after suspending a request, or we suspended the request
+ // from within this ::OnDataAvailable() call while there's
+ // still data in the input, or we have resumed a previously
+ // suspended request and our buffer is already full, and we
+ // don't have enough space to store what we got off the network.
+ // Reallocate our internal buffer.
+ mStreamBufferSize = mStreamBufferByteCount + length;
+ char* buf = (char*)realloc(mStreamBuffer, mStreamBufferSize);
+ if (!buf) return NS_ERROR_OUT_OF_MEMORY;
+
+ mStreamBuffer = buf;
+ }
+
+ uint32_t bytesToRead =
+ std::min(length, mStreamBufferSize - mStreamBufferByteCount);
+ MOZ_ASSERT(bytesToRead > 0);
+
+ uint32_t amountRead = 0;
+ rv = input->Read(mStreamBuffer + mStreamBufferByteCount, bytesToRead,
+ &amountRead);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (amountRead == 0) {
+ MOZ_ASSERT_UNREACHABLE(
+ "input->Read() returns no data, it's almost "
+ "impossible to get here");
+
+ break;
+ }
+
+ mStreamBufferByteCount += amountRead;
+ length -= amountRead;
+ } else {
+ // No input, nothing to read. Set length to 0 so that we don't
+ // keep iterating through this outer loop any more.
+
+ length = 0;
+ }
+
+ // Temporary pointer to the beginning of the data we're writing as
+ // we loop and feed the plugin data.
+ char* ptrStreamBuffer = mStreamBuffer;
+
+ // it is possible plugin's NPP_Write() returns 0 byte consumed. We
+ // use zeroBytesWriteCount to count situation like this and break
+ // the loop
+ int32_t zeroBytesWriteCount = 0;
+
+ // mStreamBufferByteCount tells us how many bytes there are in the
+ // buffer. WriteReady returns to us how many bytes the plugin is
+ // ready to handle.
+ while (mStreamBufferByteCount > 0) {
+ int32_t numtowrite;
+ if (pluginFunctions->writeready) {
+ NPPAutoPusher nppPusher(npp);
+
+ NS_TRY_SAFE_CALL_RETURN(
+ numtowrite,
+ (*pluginFunctions->writeready)(npp, &mNPStreamWrapper->mNPStream),
+ mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+ NPP_PLUGIN_LOG(
+ PLUGIN_LOG_NOISY,
+ ("NPP WriteReady called: this=%p, npp=%p, "
+ "return(towrite)=%d, url=%s\n",
+ this, npp, numtowrite, mNPStreamWrapper->mNPStream.url));
+
+ if (mStreamState == eStreamStopped) {
+ // The plugin called NPN_DestroyStream() from within
+ // NPP_WriteReady(), kill the stream.
+
+ return NS_BINDING_ABORTED;
+ }
+
+ // if WriteReady returned 0, the plugin is not ready to handle
+ // the data, suspend the stream (if it isn't already
+ // suspended).
+ //
+ // Also suspend the stream if the stream we're loading is not
+ // a javascript: URL load that was initiated during plugin
+ // initialization and there currently is such a stream
+ // loading. This is done to work around a Windows Media Player
+ // plugin bug where it can't deal with being fed data for
+ // other streams while it's waiting for data from the
+ // javascript: URL loads it requests during
+ // initialization. See bug 386493 for more details.
+
+ if (numtowrite <= 0 ||
+ (!mIsPluginInitJSStream && PluginInitJSLoadInProgress())) {
+ if (!mIsSuspended) {
+ SuspendRequest();
+ }
+
+ // Break out of the inner loop, but keep going through the
+ // outer loop in case there's more data to read from the
+ // input stream.
+
+ break;
+ }
+
+ numtowrite = std::min(numtowrite, mStreamBufferByteCount);
+ } else {
+ // if WriteReady is not supported by the plugin, just write
+ // the whole buffer
+ numtowrite = mStreamBufferByteCount;
+ }
+
+ NPPAutoPusher nppPusher(npp);
+
+ int32_t writeCount = 0; // bytes consumed by plugin instance
+ NS_TRY_SAFE_CALL_RETURN(writeCount,
+ (*pluginFunctions->write)(
+ npp, &mNPStreamWrapper->mNPStream,
+ streamPosition, numtowrite, ptrStreamBuffer),
+ mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+
+ NPP_PLUGIN_LOG(
+ PLUGIN_LOG_NOISY,
+ ("NPP Write called: this=%p, npp=%p, pos=%d, len=%d, "
+ "buf=%.*s, return(written)=%d, url=%s\n",
+ this, npp, streamPosition, numtowrite, numtowrite, ptrStreamBuffer,
+ writeCount, mNPStreamWrapper->mNPStream.url));
+
+ if (mStreamState == eStreamStopped) {
+ // The plugin called NPN_DestroyStream() from within
+ // NPP_Write(), kill the stream.
+ return NS_BINDING_ABORTED;
+ }
+
+ if (writeCount > 0) {
+ NS_ASSERTION(writeCount <= mStreamBufferByteCount,
+ "Plugin read past the end of the available data!");
+
+ writeCount = std::min(writeCount, mStreamBufferByteCount);
+ mStreamBufferByteCount -= writeCount;
+
+ streamPosition += writeCount;
+
+ zeroBytesWriteCount = 0;
+
+ if (mStreamBufferByteCount > 0) {
+ // This alignment code is most likely bogus, but we'll leave
+ // it in for now in case it matters for some plugins on some
+ // architectures. Who knows...
+ if (writeCount % sizeof(intptr_t)) {
+ // memmove will take care about alignment
+ memmove(mStreamBuffer, ptrStreamBuffer + writeCount,
+ mStreamBufferByteCount);
+ ptrStreamBuffer = mStreamBuffer;
+ } else {
+ // if aligned we can use ptrStreamBuffer += to eliminate
+ // memmove()
+ ptrStreamBuffer += writeCount;
+ }
+ }
+ } else if (writeCount == 0) {
+ // if NPP_Write() returns writeCount == 0 lets say 3 times in
+ // a row, suspend the request and continue feeding the plugin
+ // the data we got so far. Once that data is consumed, we'll
+ // resume the request.
+ if (mIsSuspended || ++zeroBytesWriteCount == 3) {
+ if (!mIsSuspended) {
+ SuspendRequest();
+ }
+
+ // Break out of the for loop, but keep going through the
+ // while loop in case there's more data to read from the
+ // input stream.
+
+ break;
+ }
+ } else {
+ // Something's really wrong, kill the stream.
+ rv = NS_ERROR_FAILURE;
+
+ break;
+ }
+ } // end of inner while loop
+
+ if (mStreamBufferByteCount && mStreamBuffer != ptrStreamBuffer) {
+ memmove(mStreamBuffer, ptrStreamBuffer, mStreamBufferByteCount);
+ }
+ }
+
+ if (streamPosition != streamOffset) {
+ // The plugin didn't consume all available data, or consumed some
+ // of our cached data while we're pumping cached data. Adjust the
+ // plugin info's stream offset to match reality, except if the
+ // plugin info's stream offset was set by a re-entering
+ // NPN_RequestRead() call.
+
+ int32_t postWriteStreamPosition;
+ streamPeer->GetStreamOffset(&postWriteStreamPosition);
+
+ if (postWriteStreamPosition == streamOffset) {
+ streamPeer->SetStreamOffset(streamPosition);
+ }
+ }
+
+ return rv;
+}
+
+nsresult nsNPAPIPluginStreamListener::OnFileAvailable(
+ nsPluginStreamListenerPeer* streamPeer, const char* fileName) {
+ if (!mInst || !mInst->CanFireNotifications()) return NS_ERROR_FAILURE;
+
+ PluginDestructionGuard guard(mInst);
+
+ nsNPAPIPlugin* plugin = mInst->GetPlugin();
+ if (!plugin || !plugin->GetLibrary()) return NS_ERROR_FAILURE;
+
+ NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
+
+ if (!pluginFunctions->asfile) return NS_ERROR_FAILURE;
+
+ NPP npp;
+ mInst->GetNPP(&npp);
+
+ NS_TRY_SAFE_CALL_VOID(
+ (*pluginFunctions->asfile)(npp, &mNPStreamWrapper->mNPStream, fileName),
+ mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+
+ NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("NPP StreamAsFile called: this=%p, npp=%p, url=%s, file=%s\n",
+ this, npp, mNPStreamWrapper->mNPStream.url, fileName));
+
+ return NS_OK;
+}
+
+nsresult nsNPAPIPluginStreamListener::OnStopBinding(
+ nsPluginStreamListenerPeer* streamPeer, nsresult status) {
+ if (NS_FAILED(status)) {
+ // The stream was destroyed, or died for some reason. Make sure we
+ // cancel the underlying request.
+ if (mStreamListenerPeer) {
+ mStreamListenerPeer->CancelRequests(status);
+ }
+ }
+
+ if (!mInst || !mInst->CanFireNotifications()) {
+ StopDataPump();
+ return NS_ERROR_FAILURE;
+ }
+
+ // We need to detect that the stop is due to async stream init completion.
+ if (mStreamStopMode == eDoDeferredStop) {
+ // We shouldn't be delivering this until async init is done
+ mStreamStopMode = eStopPending;
+ mPendingStopBindingStatus = status;
+ if (!mDataPumpTimer) {
+ StartDataPump();
+ }
+ return NS_OK;
+ }
+
+ StopDataPump();
+
+ NPReason reason = NS_FAILED(status) ? NPRES_NETWORK_ERR : NPRES_DONE;
+ if (mRedirectDenied || status == NS_BINDING_ABORTED) {
+ reason = NPRES_USER_BREAK;
+ }
+
+ // The following code can result in the deletion of 'this'. Don't
+ // assume we are alive after this!
+ return CleanUpStream(reason);
+}
+
+bool nsNPAPIPluginStreamListener::MaybeRunStopBinding() {
+ if (mIsSuspended || mStreamStopMode != eStopPending) {
+ return false;
+ }
+ OnStopBinding(mStreamListenerPeer, mPendingStopBindingStatus);
+ mStreamStopMode = eNormalStop;
+ return true;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginStreamListener::Notify(nsITimer* aTimer) {
+ NS_ASSERTION(aTimer == mDataPumpTimer, "Uh, wrong timer?");
+
+ int32_t oldStreamBufferByteCount = mStreamBufferByteCount;
+
+ nsresult rv =
+ OnDataAvailable(mStreamListenerPeer, nullptr, mStreamBufferByteCount);
+
+ if (NS_FAILED(rv)) {
+ // We ran into an error, no need to keep firing this timer then.
+ StopDataPump();
+ MaybeRunStopBinding();
+ return NS_OK;
+ }
+
+ if (mStreamBufferByteCount != oldStreamBufferByteCount &&
+ ((mStreamState == eStreamTypeSet && mStreamBufferByteCount < 1024) ||
+ mStreamBufferByteCount == 0)) {
+ // The plugin read some data and we've got less than 1024 bytes in
+ // our buffer (or its empty and the stream is already
+ // done). Resume the request so that we get more data off the
+ // network.
+ ResumeRequest();
+ // Necko will pump data now that we've resumed the request.
+ StopDataPump();
+ }
+
+ MaybeRunStopBinding();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginStreamListener::GetName(nsACString& aName) {
+ aName.AssignLiteral("nsNPAPIPluginStreamListener");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginStreamListener::StatusLine(const char* line) {
+ mResponseHeaders.Append(line);
+ mResponseHeaders.Append('\n');
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginStreamListener::NewResponseHeader(const char* headerName,
+ const char* headerValue) {
+ mResponseHeaders.Append(headerName);
+ mResponseHeaders.AppendLiteral(": ");
+ mResponseHeaders.Append(headerValue);
+ mResponseHeaders.Append('\n');
+ return NS_OK;
+}
+
+bool nsNPAPIPluginStreamListener::HandleRedirectNotification(
+ nsIChannel* oldChannel, nsIChannel* newChannel,
+ nsIAsyncVerifyRedirectCallback* callback) {
+ nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(oldChannel);
+ nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(newChannel);
+ if (!oldHttpChannel || !newHttpChannel) {
+ return false;
+ }
+
+ if (!mInst || !mInst->CanFireNotifications()) {
+ return false;
+ }
+
+ nsNPAPIPlugin* plugin = mInst->GetPlugin();
+ if (!plugin || !plugin->GetLibrary()) {
+ return false;
+ }
+
+ NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
+ if (!pluginFunctions->urlredirectnotify) {
+ return false;
+ }
+
+ // A non-null closure is required for redirect handling support.
+ if (mNPStreamWrapper->mNPStream.notifyData) {
+ uint32_t status;
+ if (NS_SUCCEEDED(oldHttpChannel->GetResponseStatus(&status))) {
+ nsCOMPtr<nsIURI> uri;
+ if (NS_SUCCEEDED(newHttpChannel->GetURI(getter_AddRefs(uri))) && uri) {
+ nsAutoCString spec;
+ if (NS_SUCCEEDED(uri->GetAsciiSpec(spec))) {
+ // At this point the plugin will be responsible for making the
+ // callback so save the callback object.
+ mHTTPRedirectCallback = callback;
+
+ NPP npp;
+ mInst->GetNPP(&npp);
+#if defined(XP_WIN)
+ NS_TRY_SAFE_CALL_VOID(
+ (*pluginFunctions->urlredirectnotify)(
+ npp, spec.get(), static_cast<int32_t>(status),
+ mNPStreamWrapper->mNPStream.notifyData),
+ mInst, NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+#else
+ (*pluginFunctions->urlredirectnotify)(
+ npp, spec.get(), static_cast<int32_t>(status),
+ mNPStreamWrapper->mNPStream.notifyData);
+#endif
+ return true;
+ }
+ }
+ }
+ }
+
+ callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
+ return true;
+}
+
+void nsNPAPIPluginStreamListener::URLRedirectResponse(NPBool allow) {
+ if (mHTTPRedirectCallback) {
+ mHTTPRedirectCallback->OnRedirectVerifyCallback(allow ? NS_OK
+ : NS_ERROR_FAILURE);
+ mRedirectDenied = !allow;
+ mHTTPRedirectCallback = nullptr;
+ }
+}
+
+void* nsNPAPIPluginStreamListener::GetNotifyData() {
+ if (mNPStreamWrapper) {
+ return mNPStreamWrapper->mNPStream.notifyData;
+ }
+ return nullptr;
+}
diff --git a/dom/plugins/base/nsNPAPIPluginStreamListener.h b/dom/plugins/base/nsNPAPIPluginStreamListener.h
new file mode 100644
index 0000000000..93e70b515a
--- /dev/null
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsNPAPIPluginStreamListener_h_
+#define nsNPAPIPluginStreamListener_h_
+
+#include "nscore.h"
+#include "nsIHTTPHeaderListener.h"
+#include "nsINamed.h"
+#include "nsITimer.h"
+#include "nsCOMArray.h"
+#include "nsIOutputStream.h"
+#include "nsString.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "mozilla/PluginLibrary.h"
+
+#define MAX_PLUGIN_NECKO_BUFFER 16384
+
+class nsPluginStreamListenerPeer;
+class nsNPAPIPluginStreamListener;
+class nsNPAPIPluginInstance;
+class nsIChannel;
+
+class nsNPAPIStreamWrapper {
+ public:
+ nsNPAPIStreamWrapper(nsIOutputStream* outputStream,
+ nsNPAPIPluginStreamListener* streamListener);
+ ~nsNPAPIStreamWrapper();
+
+ nsIOutputStream* GetOutputStream() { return mOutputStream.get(); }
+ nsNPAPIPluginStreamListener* GetStreamListener() { return mStreamListener; }
+
+ NPStream mNPStream;
+
+ protected:
+ nsCOMPtr<nsIOutputStream>
+ mOutputStream; // only valid if not browser initiated
+ nsNPAPIPluginStreamListener*
+ mStreamListener; // only valid if browser initiated
+};
+
+class nsNPAPIPluginStreamListener : public nsITimerCallback,
+ public nsIHTTPHeaderListener,
+ public nsINamed {
+ private:
+ typedef mozilla::PluginLibrary PluginLibrary;
+
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSIHTTPHEADERLISTENER
+ NS_DECL_NSINAMED
+
+ nsNPAPIPluginStreamListener(nsNPAPIPluginInstance* inst, void* notifyData,
+ const char* aURL);
+
+ nsresult OnStartBinding(nsPluginStreamListenerPeer* streamPeer);
+ nsresult OnDataAvailable(nsPluginStreamListenerPeer* streamPeer,
+ nsIInputStream* input, uint32_t length);
+ nsresult OnFileAvailable(nsPluginStreamListenerPeer* streamPeer,
+ const char* fileName);
+ nsresult OnStopBinding(nsPluginStreamListenerPeer* streamPeer,
+ nsresult status);
+
+ bool IsStarted();
+ nsresult CleanUpStream(NPReason reason);
+ void CallURLNotify(NPReason reason);
+ void SetCallNotify(bool aCallNotify) { mCallNotify = aCallNotify; }
+ void SuspendRequest();
+ void ResumeRequest();
+ nsresult StartDataPump();
+ void StopDataPump();
+ bool PluginInitJSLoadInProgress();
+
+ void* GetNotifyData();
+ nsPluginStreamListenerPeer* GetStreamListenerPeer() {
+ return mStreamListenerPeer;
+ }
+ void SetStreamListenerPeer(nsPluginStreamListenerPeer* aPeer) {
+ mStreamListenerPeer = aPeer;
+ }
+
+ // Returns true if the redirect will be handled by NPAPI, false otherwise.
+ bool HandleRedirectNotification(nsIChannel* oldChannel,
+ nsIChannel* newChannel,
+ nsIAsyncVerifyRedirectCallback* callback);
+ void URLRedirectResponse(NPBool allow);
+
+ protected:
+ enum StreamState {
+ eStreamStopped = 0, // The stream is stopped
+ eNewStreamCalled, // NPP_NewStream was called but has not completed yet
+ eStreamTypeSet // The stream is fully initialized
+ };
+
+ enum StreamStopMode { eNormalStop = 0, eDoDeferredStop, eStopPending };
+
+ virtual ~nsNPAPIPluginStreamListener();
+ bool MaybeRunStopBinding();
+
+ char* mStreamBuffer;
+ char* mNotifyURL;
+ RefPtr<nsNPAPIPluginInstance> mInst;
+ nsNPAPIStreamWrapper* mNPStreamWrapper;
+ uint32_t mStreamBufferSize;
+ int32_t mStreamBufferByteCount;
+ StreamState mStreamState;
+ bool mStreamCleanedUp;
+ bool mCallNotify;
+ bool mIsSuspended;
+ bool mIsPluginInitJSStream;
+ bool mRedirectDenied;
+ nsCString mResponseHeaders;
+ char* mResponseHeaderBuf;
+ nsCOMPtr<nsITimer> mDataPumpTimer;
+ nsCOMPtr<nsIAsyncVerifyRedirectCallback> mHTTPRedirectCallback;
+ StreamStopMode mStreamStopMode;
+ nsresult mPendingStopBindingStatus;
+
+ public:
+ RefPtr<nsPluginStreamListenerPeer> mStreamListenerPeer;
+};
+
+#endif // nsNPAPIPluginStreamListener_h_
diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp
new file mode 100644
index 0000000000..4cc7d89e59
--- /dev/null
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -0,0 +1,2792 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* nsPluginHost.cpp - top-level plugin management code */
+
+#include "nsPluginHost.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <cstdlib>
+#include <new>
+#include <utility>
+#include "GeckoProfiler.h"
+#include "ReferrerInfo.h"
+#include "js/RootingAPI.h"
+#include "mozilla/ArrayIterator.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Logging.h"
+#include "mozilla/MacroForEach.h"
+#include "mozilla/NotNull.h"
+#include "mozilla/PluginLibrary.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TelemetryHistogramEnums.h"
+#include "mozilla/TextUtils.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/FakePluginTagInitBinding.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
+#include "mozilla/dom/ReferrerPolicyBinding.h"
+#include "mozilla/fallible.h"
+#include "mozilla/ipc/URIParams.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/mozalloc.h"
+#include "mozilla/plugins/PluginTypes.h"
+#include "npapi.h"
+#include "npfunctions.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIAsyncShutdown.h"
+#include "nsIBlocklistService.h"
+#include "nsICategoryManager.h"
+#include "nsIChannel.h"
+#include "nsIContent.h"
+#include "nsIContentPolicy.h"
+#include "nsID.h"
+#include "nsIEffectiveTLDService.h"
+#include "nsIFile.h"
+#include "nsIHttpChannel.h"
+#include "nsIHttpProtocolHandler.h"
+#include "nsIIDNService.h"
+#include "nsIInputStream.h"
+#include "nsILoadInfo.h"
+#include "nsIObjectLoadingContent.h"
+#include "nsIObserverService.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsIPluginTag.h"
+#include "nsIPrefBranch.h"
+#include "nsIProtocolHandler.h"
+#include "nsIReferrerInfo.h"
+#include "nsIRequest.h"
+#include "nsIScriptChannel.h"
+#include "nsISeekableStream.h"
+#include "nsIStringStream.h"
+#include "nsISupportsUtils.h"
+#include "nsIURI.h"
+#include "nsIUploadChannel.h"
+#include "nsIWeakReference.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsIWritablePropertyBag2.h"
+#include "nsLiteralString.h"
+#include "nsNPAPIPlugin.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsObjectLoadingContent.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsPluginLogging.h"
+#include "nsPluginNativeWindow.h"
+#include "nsPluginStreamListenerPeer.h"
+#include "nsPluginTags.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsStringFlags.h"
+#include "nsTArray.h"
+#include "nsTLiteralString.h"
+#include "nsTPromiseFlatString.h"
+#include "nsTStringRepr.h"
+#include "nsThreadUtils.h"
+#include "nsVersionComparator.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMCID.h"
+#include "nsXULAppAPI.h"
+#include "nscore.h"
+#include "plstr.h"
+
+#if defined(XP_WIN)
+# include "nsIWindowMediator.h"
+# include "nsIBaseWindow.h"
+# include "windows.h"
+# include "winbase.h"
+#endif
+#if (MOZ_WIDGET_GTK)
+# include <gdk/gdk.h>
+# include <gdk/gdkx.h>
+#endif
+
+using namespace mozilla;
+using mozilla::TimeStamp;
+using mozilla::dom::Document;
+using mozilla::dom::FakePluginMimeEntry;
+using mozilla::dom::FakePluginTagInit;
+using mozilla::dom::Promise;
+using mozilla::plugins::FakePluginTag;
+using mozilla::plugins::PluginTag;
+
+// Null out a strong ref to a linked list iteratively to avoid
+// exhausting the stack (bug 486349).
+#define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_) \
+ { \
+ while (list_) { \
+ type_ temp = list_->mNext_; \
+ list_->mNext_ = nullptr; \
+ list_ = temp; \
+ } \
+ }
+
+static const char* kPrefDisableFullPage =
+ "plugin.disable_full_page_plugin_for_types";
+
+LazyLogModule nsPluginLogging::gNPNLog(NPN_LOG_NAME);
+LazyLogModule nsPluginLogging::gNPPLog(NPP_LOG_NAME);
+LazyLogModule nsPluginLogging::gPluginLog(PLUGIN_LOG_NAME);
+
+// #defines for plugin cache and prefs
+#define NS_PREF_MAX_NUM_CACHED_INSTANCES \
+ "browser.plugins.max_num_cached_plugins"
+// Raise this from '10' to '50' to work around a bug in Apple's current Java
+// plugins on OS X Lion and SnowLeopard. See bug 705931.
+#define DEFAULT_NUMBER_OF_STOPPED_INSTANCES 50
+
+nsIFile* nsPluginHost::sPluginTempDir;
+StaticRefPtr<nsPluginHost> nsPluginHost::sInst;
+
+// Helper to check for a MIME in a comma-delimited preference
+static bool IsTypeInList(const nsCString& aMimeType, nsCString aTypeList) {
+ nsAutoCString searchStr;
+ searchStr.Assign(',');
+ searchStr.Append(aTypeList);
+ searchStr.Append(',');
+
+ nsACString::const_iterator start, end;
+
+ searchStr.BeginReading(start);
+ searchStr.EndReading(end);
+
+ nsAutoCString commaSeparated;
+ commaSeparated.Assign(',');
+ commaSeparated += aMimeType;
+ commaSeparated.Append(',');
+
+ // Lower-case the search string and MIME type to properly handle a mixed-case
+ // type, as MIME types are case insensitive.
+ ToLowerCase(searchStr);
+ ToLowerCase(commaSeparated);
+
+ return FindInReadable(commaSeparated, start, end);
+}
+
+namespace mozilla::plugins {
+class BlocklistPromiseHandler final
+ : public mozilla::dom::PromiseNativeHandler {
+ public:
+ NS_DECL_ISUPPORTS
+
+ BlocklistPromiseHandler(nsPluginTag* aTag, const bool aShouldSoftblock)
+ : mTag(aTag), mShouldDisableWhenSoftblocked(aShouldSoftblock) {
+ MOZ_ASSERT(mTag, "Should always be passed a plugin tag");
+ sPendingBlocklistStateRequests++;
+ }
+
+ void MaybeWriteBlocklistChanges() {
+ // We're called immediately when the promise resolves/rejects, and (as a
+ // backup) when the handler is destroyed. To ensure we only run once, we use
+ // mTag as a sentinel, setting it to nullptr when we run.
+ if (!mTag) {
+ return;
+ }
+ mTag = nullptr;
+ sPendingBlocklistStateRequests--;
+ // If this was the only remaining pending request, check if we need to write
+ // state and if so update the child processes.
+ if (!sPendingBlocklistStateRequests) {
+ if (sPluginBlocklistStatesChangedSinceLastWrite) {
+ sPluginBlocklistStatesChangedSinceLastWrite = false;
+
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ // We update blocklist info in content processes asynchronously
+ // by just sending a new plugin list to content.
+ host->IncrementChromeEpoch();
+ host->BroadcastPluginsToContent();
+ }
+
+ // Now notify observers that we're done updating plugin state.
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ if (obsService) {
+ obsService->NotifyObservers(
+ nullptr, "plugin-blocklist-updates-finished", nullptr);
+ }
+ }
+ }
+
+ void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
+ if (!aValue.isInt32()) {
+ MOZ_ASSERT(false, "Blocklist should always return int32");
+ return;
+ }
+ int32_t newState = aValue.toInt32();
+ MOZ_ASSERT(newState >= 0 && newState < nsIBlocklistService::STATE_MAX,
+ "Shouldn't get an out of bounds blocklist state");
+
+ // Check the old and new state and see if there was a change:
+ uint32_t oldState = mTag->GetBlocklistState();
+ bool changed = oldState != (uint32_t)newState;
+ mTag->SetBlocklistState(newState);
+
+ if (newState == nsIBlocklistService::STATE_SOFTBLOCKED &&
+ mShouldDisableWhenSoftblocked) {
+ mTag->SetEnabledState(nsIPluginTag::STATE_DISABLED);
+ changed = true;
+ }
+ sPluginBlocklistStatesChangedSinceLastWrite |= changed;
+
+ MaybeWriteBlocklistChanges();
+ }
+ void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
+ MOZ_ASSERT(false, "Shouldn't reject plugin blocklist state request");
+ MaybeWriteBlocklistChanges();
+ }
+
+ private:
+ ~BlocklistPromiseHandler() {
+ // If we have multiple plugins and the last pending request is GC'd
+ // and so never resolves/rejects, ensure we still write the blocklist.
+ MaybeWriteBlocklistChanges();
+ }
+
+ RefPtr<nsPluginTag> mTag;
+ bool mShouldDisableWhenSoftblocked;
+
+ // Whether we changed any of the plugins' blocklist states since
+ // we last started fetching them (async). This is reset to false
+ // every time we finish fetching plugin blocklist information.
+ // When this happens, if the previous value was true, we store the
+ // updated list on disk and send it to child processes.
+ static bool sPluginBlocklistStatesChangedSinceLastWrite;
+ // How many pending blocklist state requests we've got
+ static uint32_t sPendingBlocklistStateRequests;
+};
+
+NS_IMPL_ISUPPORTS0(BlocklistPromiseHandler)
+
+bool BlocklistPromiseHandler::sPluginBlocklistStatesChangedSinceLastWrite =
+ false;
+uint32_t BlocklistPromiseHandler::sPendingBlocklistStateRequests = 0;
+} // namespace mozilla::plugins
+
+nsPluginHost::nsPluginHost()
+ : mPluginsLoaded(false),
+ mOverrideInternalTypes(false),
+ mPluginsDisabled(false),
+ mPluginEpoch(0) {
+ // check to see if pref is set at startup to let plugins take over in
+ // full page mode for certain image mime types that we handle internally
+ mOverrideInternalTypes =
+ Preferences::GetBool("plugin.override_internal_types", false);
+
+ bool waylandBackend = false;
+#if defined(MOZ_WIDGET_GTK) && defined(MOZ_X11)
+ GdkDisplay* display = gdk_display_get_default();
+ if (display) {
+ waylandBackend = !GDK_IS_X11_DISPLAY(display);
+ }
+#endif
+ mPluginsDisabled =
+ Preferences::GetBool("plugin.disable", false) || waylandBackend;
+ if (!waylandBackend) {
+ Preferences::AddStrongObserver(this, "plugin.disable");
+ }
+
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ if (obsService) {
+ obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ if (XRE_IsParentProcess()) {
+ obsService->AddObserver(this, "plugin-blocklist-updated", false);
+ }
+ }
+
+#ifdef PLUGIN_LOGGING
+ MOZ_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS,
+ ("NPN Logging Active!\n"));
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS,
+ ("General Plugin Logging Active! (nsPluginHost::ctor)\n"));
+ MOZ_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS,
+ ("NPP Logging Active!\n"));
+
+ PLUGIN_LOG(PLUGIN_LOG_ALWAYS, ("nsPluginHost::ctor\n"));
+ PR_LogFlush();
+#endif
+ // We need to ensure that plugin tag sandbox info is available. This needs to
+ // be done from the main thread:
+ nsPluginTag::EnsureSandboxInformation();
+
+ // Load plugins on creation, as there's a good chance we'll need to send them
+ // to content processes directly after creation.
+ if (XRE_IsParentProcess()) {
+ // Always increment the chrome epoch when we bring up the nsPluginHost in
+ // the parent process. If the only plugins we have are cached in
+ // pluginreg.dat, we won't see any plugin changes in LoadPlugins and the
+ // epoch will stay the same between the parent and child process, meaning
+ // plugins will never update in the child process.
+ IncrementChromeEpoch();
+ LoadPlugins();
+ }
+}
+
+nsPluginHost::~nsPluginHost() {
+ PLUGIN_LOG(PLUGIN_LOG_ALWAYS, ("nsPluginHost::dtor\n"));
+
+ UnloadPlugins();
+}
+
+NS_IMPL_ISUPPORTS(nsPluginHost, nsIPluginHost, nsIObserver, nsITimerCallback,
+ nsISupportsWeakReference, nsINamed)
+
+already_AddRefed<nsPluginHost> nsPluginHost::GetInst() {
+ if (!sInst) {
+ sInst = new nsPluginHost();
+ ClearOnShutdown(&sInst);
+ }
+
+ return do_AddRef(sInst);
+}
+
+bool nsPluginHost::IsRunningPlugin(nsPluginTag* aPluginTag) {
+ if (!aPluginTag || !aPluginTag->mPlugin) {
+ return false;
+ }
+
+ if (aPluginTag->mContentProcessRunningCount) {
+ return true;
+ }
+
+ for (uint32_t i = 0; i < mInstances.Length(); i++) {
+ nsNPAPIPluginInstance* instance = mInstances[i].get();
+ if (instance && instance->GetPlugin() == aPluginTag->mPlugin &&
+ instance->IsRunning()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+nsresult nsPluginHost::ReloadPlugins() {
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::ReloadPlugins\n"));
+ return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED;
+}
+
+void nsPluginHost::ClearNonRunningPlugins() {
+ // shutdown plugins and kill the list if there are no running plugins
+ RefPtr<nsPluginTag> prev;
+ RefPtr<nsPluginTag> next;
+
+ for (RefPtr<nsPluginTag> p = mPlugins; p != nullptr;) {
+ next = p->mNext;
+
+ // only remove our plugin from the list if it's not running.
+ if (!IsRunningPlugin(p)) {
+ if (p == mPlugins)
+ mPlugins = next;
+ else
+ prev->mNext = next;
+
+ p->mNext = nullptr;
+
+ // attempt to unload plugins whenever they are removed from the list
+ p->TryUnloadPlugin(false);
+
+ p = next;
+ continue;
+ }
+
+ prev = p;
+ p = next;
+ }
+}
+
+nsresult nsPluginHost::ActuallyReloadPlugins() {
+ nsresult rv = NS_OK;
+ ClearNonRunningPlugins();
+
+ // set flags
+ mPluginsLoaded = false;
+
+ // load them again
+ rv = LoadPlugins();
+
+ if (XRE_IsParentProcess()) {
+ // If the plugin list changed, update content. If the plugin list changed
+ // for the content process, it will also reload plugins.
+ BroadcastPluginsToContent();
+ }
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::ReloadPlugins End\n"));
+
+ return rv;
+}
+
+#define NS_RETURN_UASTRING_SIZE 128
+
+nsresult nsPluginHost::UserAgent(const char** retstring) {
+ static char resultString[NS_RETURN_UASTRING_SIZE];
+ nsresult res;
+
+ nsCOMPtr<nsIHttpProtocolHandler> http =
+ do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
+ if (NS_FAILED(res)) return res;
+
+ nsAutoCString uaString;
+ res = http->GetUserAgent(uaString);
+
+ if (NS_SUCCEEDED(res)) {
+ if (NS_RETURN_UASTRING_SIZE > uaString.Length()) {
+ PL_strcpy(resultString, uaString.get());
+ } else {
+ // Copy as much of UA string as we can (terminate at right-most space).
+ PL_strncpy(resultString, uaString.get(), NS_RETURN_UASTRING_SIZE);
+ for (int i = NS_RETURN_UASTRING_SIZE - 1; i >= 0; i--) {
+ if (i == 0) {
+ resultString[NS_RETURN_UASTRING_SIZE - 1] = '\0';
+ } else if (resultString[i] == ' ') {
+ resultString[i] = '\0';
+ break;
+ }
+ }
+ }
+ *retstring = resultString;
+ } else {
+ *retstring = nullptr;
+ }
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::UserAgent return=%s\n", *retstring));
+
+ return res;
+}
+
+nsresult nsPluginHost::GetURL(nsISupports* pluginInst, const char* url,
+ const char* target,
+ nsNPAPIPluginStreamListener* streamListener,
+ const char* altHost, const char* referrer,
+ bool forceJSEnabled) {
+ return GetURLWithHeaders(static_cast<nsNPAPIPluginInstance*>(pluginInst), url,
+ target, streamListener, altHost, referrer,
+ forceJSEnabled, 0, nullptr);
+}
+
+nsresult nsPluginHost::GetURLWithHeaders(
+ nsNPAPIPluginInstance* pluginInst, const char* url, const char* target,
+ nsNPAPIPluginStreamListener* streamListener, const char* altHost,
+ const char* referrer, bool forceJSEnabled, uint32_t getHeadersLength,
+ const char* getHeaders) {
+ // we can only send a stream back to the plugin (as specified by a
+ // null target) if we also have a nsNPAPIPluginStreamListener to talk to
+ if (!target && !streamListener) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ nsresult rv = NS_OK;
+
+ if (target) {
+ RefPtr<nsPluginInstanceOwner> owner = pluginInst->GetOwner();
+ if (owner) {
+ rv = owner->GetURL(url, target, nullptr, nullptr, 0, true);
+ }
+ }
+
+ if (streamListener) {
+ rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), pluginInst,
+ streamListener, nullptr, getHeaders,
+ getHeadersLength);
+ }
+ return rv;
+}
+
+nsresult nsPluginHost::PostURL(nsISupports* pluginInst, const char* url,
+ uint32_t postDataLen, const char* postData,
+ const char* target,
+ nsNPAPIPluginStreamListener* streamListener,
+ const char* altHost, const char* referrer,
+ bool forceJSEnabled, uint32_t postHeadersLength,
+ const char* postHeaders) {
+ nsresult rv;
+
+ // we can only send a stream back to the plugin (as specified
+ // by a null target) if we also have a nsNPAPIPluginStreamListener
+ // to talk to also
+ if (!target && !streamListener) return NS_ERROR_ILLEGAL_VALUE;
+
+ nsNPAPIPluginInstance* instance =
+ static_cast<nsNPAPIPluginInstance*>(pluginInst);
+
+ nsCOMPtr<nsIInputStream> postStream;
+ char* dataToPost;
+ uint32_t newDataToPostLen;
+ ParsePostBufferToFixHeaders(postData, postDataLen, &dataToPost,
+ &newDataToPostLen);
+ if (!dataToPost) return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsIStringInputStream> sis =
+ do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
+ if (!sis) {
+ free(dataToPost);
+ return rv;
+ }
+
+ // data allocated by ParsePostBufferToFixHeaders() is managed and
+ // freed by the string stream.
+ postDataLen = newDataToPostLen;
+ sis->AdoptData(dataToPost, postDataLen);
+ postStream = sis;
+
+ if (target) {
+ RefPtr<nsPluginInstanceOwner> owner = instance->GetOwner();
+ if (owner) {
+ rv = owner->GetURL(url, target, postStream, (void*)postHeaders,
+ postHeadersLength, true);
+ }
+ }
+
+ // if we don't have a target, just create a stream.
+ if (streamListener) {
+ rv =
+ NewPluginURLStream(NS_ConvertUTF8toUTF16(url), instance, streamListener,
+ postStream, postHeaders, postHeadersLength);
+ }
+ return rv;
+}
+
+nsresult nsPluginHost::UnloadPlugins() {
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UnloadPlugins Called\n"));
+
+ if (!mPluginsLoaded) return NS_OK;
+
+ // we should call nsIPluginInstance::Stop and nsIPluginInstance::SetWindow
+ // for those plugins who want it
+ DestroyRunningInstances(nullptr);
+
+ nsPluginTag* pluginTag;
+ for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
+ pluginTag->TryUnloadPlugin(true);
+ }
+
+ NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mPlugins, mNext);
+
+ // Lets remove any of the temporary files that we created.
+ if (sPluginTempDir) {
+ sPluginTempDir->Remove(true);
+ NS_RELEASE(sPluginTempDir);
+ }
+ mSerializablePlugins.Clear();
+ mSerializableFakePlugins.Clear();
+
+ mPluginsLoaded = false;
+
+ return NS_OK;
+}
+
+void nsPluginHost::OnPluginInstanceDestroyed(nsPluginTag* aPluginTag) {
+ bool hasInstance = false;
+ for (uint32_t i = 0; i < mInstances.Length(); i++) {
+ if (TagForPlugin(mInstances[i]->GetPlugin()) == aPluginTag) {
+ hasInstance = true;
+ break;
+ }
+ }
+
+ if (!hasInstance) {
+ aPluginTag->TryUnloadPlugin(false);
+ }
+}
+
+nsresult nsPluginHost::InstantiatePluginInstance(
+ const nsACString& aMimeType, nsIURI* aURL, nsObjectLoadingContent* aContent,
+ nsPluginInstanceOwner** aOwner) {
+ NS_ENSURE_ARG_POINTER(aOwner);
+
+#ifdef PLUGIN_LOGGING
+ nsAutoCString urlSpec;
+ if (aURL) aURL->GetAsciiSpec(urlSpec);
+
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::InstantiatePlugin Begin mime=%s, url=%s\n",
+ PromiseFlatCString(aMimeType).get(), urlSpec.get()));
+
+ PR_LogFlush();
+#endif
+
+ if (aMimeType.IsEmpty()) {
+ MOZ_ASSERT_UNREACHABLE("Attempting to spawn a plugin with no mime type");
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<nsPluginInstanceOwner> instanceOwner = new nsPluginInstanceOwner();
+ if (!instanceOwner) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsCOMPtr<nsIContent> ourContent =
+ do_QueryInterface(static_cast<nsIImageLoadingContent*>(aContent));
+ nsresult rv = instanceOwner->Init(ourContent);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsPluginTagType tagType;
+ rv = instanceOwner->GetTagType(&tagType);
+ if (NS_FAILED(rv)) {
+ instanceOwner->Destroy();
+ return rv;
+ }
+
+ if (tagType != nsPluginTagType_Embed && tagType != nsPluginTagType_Object) {
+ instanceOwner->Destroy();
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = SetUpPluginInstance(aMimeType, aURL, instanceOwner);
+ if (NS_FAILED(rv)) {
+ instanceOwner->Destroy();
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<nsNPAPIPluginInstance> instance = instanceOwner->GetInstance();
+
+ if (instance) {
+ CreateWidget(instanceOwner);
+ }
+
+ // At this point we consider instantiation to be successful. Do not return an
+ // error.
+ instanceOwner.forget(aOwner);
+
+#ifdef PLUGIN_LOGGING
+ nsAutoCString urlSpec2;
+ if (aURL != nullptr) aURL->GetAsciiSpec(urlSpec2);
+
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::InstantiatePlugin Finished mime=%s, rv=%" PRIu32
+ ", url=%s\n",
+ PromiseFlatCString(aMimeType).get(), static_cast<uint32_t>(rv),
+ urlSpec2.get()));
+
+ PR_LogFlush();
+#endif
+
+ return NS_OK;
+}
+
+nsPluginTag* nsPluginHost::FindTagForLibrary(PRLibrary* aLibrary) {
+ nsPluginTag* pluginTag;
+ for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
+ if (pluginTag->mLibrary == aLibrary) {
+ return pluginTag;
+ }
+ }
+ return nullptr;
+}
+
+nsPluginTag* nsPluginHost::TagForPlugin(nsNPAPIPlugin* aPlugin) {
+ nsPluginTag* pluginTag;
+ for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
+ if (pluginTag->mPlugin == aPlugin) {
+ return pluginTag;
+ }
+ }
+ // a plugin should never exist without a corresponding tag
+ NS_ERROR("TagForPlugin has failed");
+ return nullptr;
+}
+
+nsresult nsPluginHost::SetUpPluginInstance(const nsACString& aMimeType,
+ nsIURI* aURL,
+ nsPluginInstanceOwner* aOwner) {
+ NS_ENSURE_ARG_POINTER(aOwner);
+
+ nsresult rv = TrySetUpPluginInstance(aMimeType, aURL, aOwner);
+ if (NS_SUCCEEDED(rv)) {
+ return rv;
+ }
+
+ // If we failed to load a plugin instance we'll try again after
+ // reloading our plugin list. Only do that once per document to
+ // avoid redundant high resource usage on pages with multiple
+ // unkown instance types. We'll do that by caching the document.
+ nsCOMPtr<Document> document;
+ aOwner->GetDocument(getter_AddRefs(document));
+
+ nsCOMPtr<Document> currentdocument = do_QueryReferent(mCurrentDocument);
+ if (document == currentdocument) {
+ return rv;
+ }
+
+ mCurrentDocument = do_GetWeakReference(document);
+
+ // Don't try to set up an instance again if nothing changed.
+ if (ReloadPlugins() == NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) {
+ return rv;
+ }
+
+ return TrySetUpPluginInstance(aMimeType, aURL, aOwner);
+}
+
+nsresult nsPluginHost::TrySetUpPluginInstance(const nsACString& aMimeType,
+ nsIURI* aURL,
+ nsPluginInstanceOwner* aOwner) {
+#ifdef PLUGIN_LOGGING
+ MOZ_LOG(
+ nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::TrySetupPluginInstance Begin mime=%s, owner=%p, url=%s\n",
+ PromiseFlatCString(aMimeType).get(), aOwner,
+ aURL ? aURL->GetSpecOrDefault().get() : ""));
+
+ PR_LogFlush();
+#endif
+
+#ifdef XP_WIN
+ bool changed;
+ if ((mRegKeyHKLM && NS_SUCCEEDED(mRegKeyHKLM->HasChanged(&changed)) &&
+ changed) ||
+ (mRegKeyHKCU && NS_SUCCEEDED(mRegKeyHKCU->HasChanged(&changed)) &&
+ changed)) {
+ ReloadPlugins();
+ }
+#endif
+
+ RefPtr<nsNPAPIPlugin> plugin;
+ GetPlugin(aMimeType, getter_AddRefs(plugin));
+ if (!plugin) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPluginTag* pluginTag = FindNativePluginForType(aMimeType, true);
+
+ NS_ASSERTION(pluginTag, "Must have plugin tag here!");
+
+ plugin->GetLibrary()->SetHasLocalInstance();
+
+ RefPtr<nsNPAPIPluginInstance> instance = new nsNPAPIPluginInstance();
+
+ // This will create the owning reference. The connection must be made between
+ // the instance and the instance owner before initialization. Plugins can call
+ // into the browser during initialization.
+ aOwner->SetInstance(instance.get());
+
+ // Add the instance to the instances list before we call NPP_New so that
+ // it is "in play" before NPP_New happens. Take it out if NPP_New fails.
+ mInstances.AppendElement(instance.get());
+
+ // this should not addref the instance or owner
+ // except in some cases not Java, see bug 140931
+ // our COM pointer will free the peer
+ nsresult rv = instance->Initialize(plugin.get(), aOwner, aMimeType);
+ if (NS_FAILED(rv)) {
+ mInstances.RemoveElement(instance.get());
+ aOwner->SetInstance(nullptr);
+ return rv;
+ }
+
+ // Cancel the plugin unload timer since we are creating
+ // an instance for it.
+ if (pluginTag->mUnloadTimer) {
+ pluginTag->mUnloadTimer->Cancel();
+ }
+
+#ifdef PLUGIN_LOGGING
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
+ ("nsPluginHost::TrySetupPluginInstance Finished mime=%s, rv=%" PRIu32
+ ", owner=%p, url=%s\n",
+ PromiseFlatCString(aMimeType).get(), static_cast<uint32_t>(rv),
+ aOwner, aURL ? aURL->GetSpecOrDefault().get() : ""));
+
+ PR_LogFlush();
+#endif
+
+ return rv;
+}
+
+bool nsPluginHost::HavePluginForType(const nsACString& aMimeType,
+ PluginFilter aFilter) {
+ bool checkEnabled = aFilter & eExcludeDisabled;
+ bool allowFake = !(aFilter & eExcludeFake);
+ return FindPluginForType(aMimeType, allowFake, checkEnabled);
+}
+
+nsIInternalPluginTag* nsPluginHost::FindPluginForType(
+ const nsACString& aMimeType, bool aIncludeFake, bool aCheckEnabled) {
+ if (aIncludeFake) {
+ nsFakePluginTag* fakeTag = FindFakePluginForType(aMimeType, aCheckEnabled);
+ if (fakeTag) {
+ return fakeTag;
+ }
+ }
+
+ return FindNativePluginForType(aMimeType, aCheckEnabled);
+}
+
+NS_IMETHODIMP
+nsPluginHost::GetPluginTagForType(const nsACString& aMimeType,
+ uint32_t aExcludeFlags,
+ nsIPluginTag** aResult) {
+ bool includeFake = !(aExcludeFlags & eExcludeFake);
+ bool includeDisabled = !(aExcludeFlags & eExcludeDisabled);
+
+ // First look for an enabled plugin.
+ RefPtr<nsIInternalPluginTag> tag =
+ FindPluginForType(aMimeType, includeFake, true);
+ if (!tag && includeDisabled) {
+ tag = FindPluginForType(aMimeType, includeFake, false);
+ }
+
+ if (tag) {
+ tag.forget(aResult);
+ return NS_OK;
+ }
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsPluginHost::GetStateForType(const nsACString& aMimeType,
+ uint32_t aExcludeFlags, uint32_t* aResult) {
+ nsCOMPtr<nsIPluginTag> tag;
+ nsresult rv =
+ GetPluginTagForType(aMimeType, aExcludeFlags, getter_AddRefs(tag));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return tag->GetEnabledState(aResult);
+}
+
+NS_IMETHODIMP
+nsPluginHost::GetBlocklistStateForType(const nsACString& aMimeType,
+ uint32_t aExcludeFlags,
+ uint32_t* aState) {
+ nsCOMPtr<nsIPluginTag> tag;
+ nsresult rv =
+ GetPluginTagForType(aMimeType, aExcludeFlags, getter_AddRefs(tag));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return tag->GetBlocklistState(aState);
+}
+
+NS_IMETHODIMP
+nsPluginHost::GetPermissionStringForType(const nsACString& aMimeType,
+ uint32_t aExcludeFlags,
+ nsACString& aPermissionString) {
+ nsCOMPtr<nsIPluginTag> tag;
+ nsresult rv =
+ GetPluginTagForType(aMimeType, aExcludeFlags, getter_AddRefs(tag));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return GetPermissionStringForTag(tag, aExcludeFlags, aPermissionString);
+}
+
+NS_IMETHODIMP
+nsPluginHost::GetPermissionStringForTag(nsIPluginTag* aTag,
+ uint32_t aExcludeFlags,
+ nsACString& aPermissionString) {
+ NS_ENSURE_TRUE(aTag, NS_ERROR_FAILURE);
+
+ aPermissionString.Truncate();
+ uint32_t blocklistState;
+ nsresult rv = aTag->GetBlocklistState(&blocklistState);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (blocklistState ==
+ nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
+ blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
+ aPermissionString.AssignLiteral("plugin-vulnerable:");
+ } else {
+ aPermissionString.AssignLiteral("plugin:");
+ }
+
+ nsCString niceName;
+ rv = aTag->GetNiceName(niceName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(!niceName.IsEmpty(), NS_ERROR_FAILURE);
+
+ aPermissionString.Append(niceName);
+
+ return NS_OK;
+}
+
+bool nsPluginHost::HavePluginForExtension(const nsACString& aExtension,
+ /* out */ nsACString& aMimeType,
+ PluginFilter aFilter) {
+ // As of FF 52, we only support flash and test plugins, so if the extension
+ // types don't match for that, exit before we start loading plugins.
+ //
+ // XXX: Remove tst case when bug 1351885 lands.
+ if (!aExtension.LowerCaseEqualsLiteral("swf") &&
+ !aExtension.LowerCaseEqualsLiteral("tst")) {
+ return false;
+ }
+
+ bool checkEnabled = aFilter & eExcludeDisabled;
+ bool allowFake = !(aFilter & eExcludeFake);
+ return FindNativePluginForExtension(aExtension, aMimeType, checkEnabled) ||
+ (allowFake &&
+ FindFakePluginForExtension(aExtension, aMimeType, checkEnabled));
+}
+
+void nsPluginHost::GetPlugins(
+ nsTArray<nsCOMPtr<nsIInternalPluginTag>>& aPluginArray,
+ bool aIncludeDisabled) {
+ aPluginArray.Clear();
+
+ LoadPlugins();
+
+ // Append fake plugins, then normal plugins.
+
+ uint32_t numFake = mFakePlugins.Length();
+ for (uint32_t i = 0; i < numFake; i++) {
+ aPluginArray.AppendElement(mFakePlugins[i]);
+ }
+
+ // Regular plugins
+ nsPluginTag* plugin = mPlugins;
+ while (plugin != nullptr) {
+ if (plugin->IsEnabled() || aIncludeDisabled) {
+ aPluginArray.AppendElement(plugin);
+ }
+ plugin = plugin->mNext;
+ }
+}
+
+// FIXME-jsplugins Check users for order of fake v non-fake
+NS_IMETHODIMP
+nsPluginHost::GetPluginTags(nsTArray<RefPtr<nsIPluginTag>>& aResults) {
+ LoadPlugins();
+
+ for (nsPluginTag* plugin = mPlugins; plugin; plugin = plugin->mNext) {
+ aResults.AppendElement(plugin);
+ }
+
+ for (nsIInternalPluginTag* plugin : mFakePlugins) {
+ aResults.AppendElement(plugin);
+ }
+
+ return NS_OK;
+}
+
+nsPluginTag* nsPluginHost::FindPreferredPlugin(
+ const nsTArray<nsPluginTag*>& matches) {
+ // We prefer the plugin with the highest version number.
+ /// XXX(johns): This seems to assume the only time multiple plugins will have
+ /// the same MIME type is if they're multiple versions of the same
+ /// plugin -- but since plugin filenames and pretty names can both
+ /// update, it's probably less arbitrary than just going at it
+ /// alphabetically.
+
+ if (matches.IsEmpty()) {
+ return nullptr;
+ }
+
+ nsPluginTag* preferredPlugin = matches[0];
+ for (unsigned int i = 1; i < matches.Length(); i++) {
+ if (mozilla::Version(matches[i]->Version().get()) >
+ preferredPlugin->Version().get()) {
+ preferredPlugin = matches[i];
+ }
+ }
+
+ return preferredPlugin;
+}
+
+nsFakePluginTag* nsPluginHost::FindFakePluginForExtension(
+ const nsACString& aExtension,
+ /* out */ nsACString& aMimeType, bool aCheckEnabled) {
+ if (aExtension.IsEmpty()) {
+ return nullptr;
+ }
+
+ int32_t numFakePlugins = mFakePlugins.Length();
+ for (int32_t i = 0; i < numFakePlugins; i++) {
+ nsFakePluginTag* plugin = mFakePlugins[i];
+ bool active;
+ if ((!aCheckEnabled ||
+ (NS_SUCCEEDED(plugin->GetActive(&active)) && active)) &&
+ plugin->HasExtension(aExtension, aMimeType)) {
+ return plugin;
+ }
+ }
+
+ return nullptr;
+}
+
+nsFakePluginTag* nsPluginHost::FindFakePluginForType(
+ const nsACString& aMimeType, bool aCheckEnabled) {
+ int32_t numFakePlugins = mFakePlugins.Length();
+ for (int32_t i = 0; i < numFakePlugins; i++) {
+ nsFakePluginTag* plugin = mFakePlugins[i];
+ bool active;
+ if ((!aCheckEnabled ||
+ (NS_SUCCEEDED(plugin->GetActive(&active)) && active)) &&
+ plugin->HasMimeType(aMimeType)) {
+ return plugin;
+ }
+ }
+
+ return nullptr;
+}
+
+nsPluginTag* nsPluginHost::FindNativePluginForType(const nsACString& aMimeType,
+ bool aCheckEnabled) {
+ if (aMimeType.IsEmpty()) {
+ return nullptr;
+ }
+
+ // As of FF 52, we only support flash and test plugins, so if the mime types
+ // don't match for that, exit before we start loading plugins.
+ if (!nsPluginHost::CanUsePluginForMIMEType(aMimeType)) {
+ return nullptr;
+ }
+
+ LoadPlugins();
+
+ nsTArray<nsPluginTag*> matchingPlugins;
+
+ nsPluginTag* plugin = mPlugins;
+ while (plugin) {
+ if ((!aCheckEnabled || plugin->IsActive()) &&
+ plugin->HasMimeType(aMimeType)) {
+ matchingPlugins.AppendElement(plugin);
+ }
+ plugin = plugin->mNext;
+ }
+
+ return FindPreferredPlugin(matchingPlugins);
+}
+
+nsPluginTag* nsPluginHost::FindNativePluginForExtension(
+ const nsACString& aExtension,
+ /* out */ nsACString& aMimeType, bool aCheckEnabled) {
+ if (aExtension.IsEmpty()) {
+ return nullptr;
+ }
+
+ LoadPlugins();
+
+ nsTArray<nsPluginTag*> matchingPlugins;
+ nsCString matchingMime; // Don't mutate aMimeType unless returning a match
+ nsPluginTag* plugin = mPlugins;
+
+ while (plugin) {
+ if (!aCheckEnabled || plugin->IsActive()) {
+ if (plugin->HasExtension(aExtension, matchingMime)) {
+ matchingPlugins.AppendElement(plugin);
+ }
+ }
+ plugin = plugin->mNext;
+ }
+
+ nsPluginTag* preferredPlugin = FindPreferredPlugin(matchingPlugins);
+ if (!preferredPlugin) {
+ return nullptr;
+ }
+
+ // Re-fetch the matching type because of how FindPreferredPlugin works...
+ preferredPlugin->HasExtension(aExtension, aMimeType);
+ return preferredPlugin;
+}
+
+static nsresult CreateNPAPIPlugin(nsPluginTag* aPluginTag,
+ nsNPAPIPlugin** aOutNPAPIPlugin) {
+ nsresult rv;
+ rv = nsNPAPIPlugin::CreatePlugin(aPluginTag, aOutNPAPIPlugin);
+
+ return rv;
+}
+
+nsresult nsPluginHost::EnsurePluginLoaded(nsPluginTag* aPluginTag) {
+ RefPtr<nsNPAPIPlugin> plugin = aPluginTag->mPlugin;
+ if (!plugin) {
+ nsresult rv = CreateNPAPIPlugin(aPluginTag, getter_AddRefs(plugin));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aPluginTag->mPlugin = plugin;
+ }
+ return NS_OK;
+}
+
+nsresult nsPluginHost::GetPluginForContentProcess(uint32_t aPluginId,
+ nsNPAPIPlugin** aPlugin) {
+ AUTO_PROFILER_LABEL("nsPluginHost::GetPluginForContentProcess", OTHER);
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ // If plugins haven't been scanned yet, do so now
+ LoadPlugins();
+
+ nsPluginTag* pluginTag = PluginWithId(aPluginId);
+ if (pluginTag) {
+ // When setting up a bridge, double check with chrome to see if this plugin
+ // is blocked hard. Note this does not protect against vulnerable plugins
+ // that the user has explicitly allowed. :(
+ if (pluginTag->IsBlocklisted()) {
+ return NS_ERROR_PLUGIN_BLOCKLISTED;
+ }
+
+ nsresult rv = EnsurePluginLoaded(pluginTag);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // We only get here if a content process doesn't have a PluginModuleParent
+ // for the given plugin already. Therefore, this counter is counting the
+ // number of outstanding PluginModuleParents for the plugin, excluding the
+ // one from the chrome process.
+ pluginTag->mContentProcessRunningCount++;
+ NS_ADDREF(*aPlugin = pluginTag->mPlugin);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+class nsPluginUnloadRunnable : public Runnable {
+ public:
+ explicit nsPluginUnloadRunnable(uint32_t aPluginId)
+ : Runnable("nsPluginUnloadRunnable"), mPluginId(aPluginId) {}
+
+ NS_IMETHOD Run() override {
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ if (!host) {
+ return NS_OK;
+ }
+ nsPluginTag* pluginTag = host->PluginWithId(mPluginId);
+ if (!pluginTag) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(pluginTag->mContentProcessRunningCount > 0);
+ pluginTag->mContentProcessRunningCount--;
+
+ if (!pluginTag->mContentProcessRunningCount) {
+ if (!host->IsRunningPlugin(pluginTag)) {
+ pluginTag->TryUnloadPlugin(false);
+ }
+ }
+ return NS_OK;
+ }
+
+ protected:
+ uint32_t mPluginId;
+};
+
+void nsPluginHost::NotifyContentModuleDestroyed(uint32_t aPluginId) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ // This is called in response to a message from the plugin. Don't unload the
+ // plugin until the message handler is off the stack.
+ RefPtr<nsPluginUnloadRunnable> runnable =
+ new nsPluginUnloadRunnable(aPluginId);
+ NS_DispatchToMainThread(runnable);
+}
+
+nsresult nsPluginHost::GetPlugin(const nsACString& aMimeType,
+ nsNPAPIPlugin** aPlugin) {
+ nsresult rv = NS_ERROR_FAILURE;
+ *aPlugin = nullptr;
+
+ // If plugins haven't been scanned yet, do so now
+ LoadPlugins();
+
+ nsPluginTag* pluginTag = FindNativePluginForType(aMimeType, true);
+ if (pluginTag) {
+ rv = NS_OK;
+ PLUGIN_LOG(
+ PLUGIN_LOG_BASIC,
+ ("nsPluginHost::GetPlugin Begin mime=%s, plugin=%s\n",
+ PromiseFlatCString(aMimeType).get(), pluginTag->FileName().get()));
+
+#ifdef DEBUG
+ if (!pluginTag->FileName().IsEmpty())
+ printf("For %s found plugin %s\n", PromiseFlatCString(aMimeType).get(),
+ pluginTag->FileName().get());
+#endif
+
+ rv = EnsurePluginLoaded(pluginTag);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_ADDREF(*aPlugin = pluginTag->mPlugin);
+ return NS_OK;
+ }
+
+ PLUGIN_LOG(
+ PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::GetPlugin End mime=%s, rv=%" PRIu32
+ ", plugin=%p name=%s\n",
+ PromiseFlatCString(aMimeType).get(), static_cast<uint32_t>(rv), *aPlugin,
+ (pluginTag ? pluginTag->FileName().get() : "(not found)")));
+
+ return rv;
+}
+
+// Normalize 'host' to ACE.
+nsresult nsPluginHost::NormalizeHostname(nsCString& host) {
+ if (IsAscii(host)) {
+ ToLowerCase(host);
+ return NS_OK;
+ }
+
+ if (!mIDNService) {
+ nsresult rv;
+ mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return mIDNService->ConvertUTF8toACE(host, host);
+}
+
+// Enumerate a 'sites' array returned by GetSitesWithData and determine if
+// any of them have a base domain in common with 'domain'; if so, append them
+// to the 'result' array. If 'firstMatchOnly' is true, return after finding the
+// first match.
+nsresult nsPluginHost::EnumerateSiteData(const nsACString& domain,
+ const nsTArray<nsCString>& sites,
+ nsTArray<nsCString>& result,
+ bool firstMatchOnly) {
+ NS_ASSERTION(!domain.IsVoid(), "null domain string");
+
+ nsresult rv;
+ if (!mTLDService) {
+ mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Get the base domain from the domain.
+ nsCString baseDomain;
+ rv = mTLDService->GetBaseDomainFromHost(domain, 0, baseDomain);
+ bool isIP = rv == NS_ERROR_HOST_IS_IP_ADDRESS;
+ if (isIP || rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
+ // The base domain is the site itself. However, we must be careful to
+ // normalize.
+ baseDomain = domain;
+ rv = NormalizeHostname(baseDomain);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Enumerate the array of sites with data.
+ for (uint32_t i = 0; i < sites.Length(); ++i) {
+ const nsCString& site = sites[i];
+
+ // Check if the site is an IP address.
+ bool siteIsIP =
+ site.Length() >= 2 && site.First() == '[' && site.Last() == ']';
+ if (siteIsIP != isIP) continue;
+
+ nsCString siteBaseDomain;
+ if (siteIsIP) {
+ // Strip the '[]'.
+ siteBaseDomain = Substring(site, 1, site.Length() - 2);
+ } else {
+ // Determine the base domain of the site.
+ rv = mTLDService->GetBaseDomainFromHost(site, 0, siteBaseDomain);
+ if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
+ // The base domain is the site itself. However, we must be careful to
+ // normalize.
+ siteBaseDomain = site;
+ rv = NormalizeHostname(siteBaseDomain);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ // At this point, we can do an exact comparison of the two domains.
+ if (baseDomain != siteBaseDomain) {
+ continue;
+ }
+
+ // Append the site to the result array.
+ result.AppendElement(site);
+
+ // If we're supposed to return early, do so.
+ if (firstMatchOnly) {
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+static bool MimeTypeIsAllowedForFakePlugin(const nsString& aMimeType) {
+ static const char* const allowedFakePlugins[] = {
+ // Flash
+ "application/x-shockwave-flash",
+ // PDF
+ "application/pdf",
+ "application/vnd.adobe.pdf",
+ "application/vnd.adobe.pdfxml",
+ "application/vnd.adobe.x-mars",
+ "application/vnd.adobe.xdp+xml",
+ "application/vnd.adobe.xfdf",
+ "application/vnd.adobe.xfd+xml",
+ "application/vnd.fdf",
+ };
+
+ for (const auto allowed : allowedFakePlugins) {
+ if (aMimeType.EqualsASCII(allowed)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+NS_IMETHODIMP
+nsPluginHost::RegisterFakePlugin(JS::Handle<JS::Value> aInitDictionary,
+ JSContext* aCx, nsIFakePluginTag** aResult) {
+ FakePluginTagInit initDictionary;
+ if (!initDictionary.Init(aCx, aInitDictionary)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ for (const FakePluginMimeEntry& mimeEntry : initDictionary.mMimeEntries) {
+ if (!MimeTypeIsAllowedForFakePlugin(mimeEntry.mType)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ RefPtr<nsFakePluginTag> newTag;
+ nsresult rv = nsFakePluginTag::Create(initDictionary, getter_AddRefs(newTag));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (const auto& existingTag : mFakePlugins) {
+ if (newTag->HandlerURIMatches(existingTag->HandlerURI())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+
+ mFakePlugins.AppendElement(newTag);
+
+ nsAutoCString disableFullPage;
+ Preferences::GetCString(kPrefDisableFullPage, disableFullPage);
+ for (uint32_t i = 0; i < newTag->MimeTypes().Length(); i++) {
+ if (!IsTypeInList(newTag->MimeTypes()[i], disableFullPage)) {
+ RegisterWithCategoryManager(newTag->MimeTypes()[i], ePluginRegister);
+ }
+ }
+
+ newTag.forget(aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginHost::CreateFakePlugin(JS::Handle<JS::Value> aInitDictionary,
+ JSContext* aCx, nsIFakePluginTag** aResult) {
+ FakePluginTagInit initDictionary;
+ if (!initDictionary.Init(aCx, aInitDictionary)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<nsFakePluginTag> newTag;
+ nsresult rv = nsFakePluginTag::Create(initDictionary, getter_AddRefs(newTag));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ newTag.forget(aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginHost::UnregisterFakePlugin(const nsACString& aHandlerURI) {
+ nsCOMPtr<nsIURI> handlerURI;
+ nsresult rv = NS_NewURI(getter_AddRefs(handlerURI), aHandlerURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (uint32_t i = 0; i < mFakePlugins.Length(); ++i) {
+ if (mFakePlugins[i]->HandlerURIMatches(handlerURI)) {
+ mFakePlugins.RemoveElementAt(i);
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
+
+// FIXME-jsplugins Is this method actually needed?
+NS_IMETHODIMP
+nsPluginHost::GetFakePlugin(const nsACString& aMimeType,
+ nsIFakePluginTag** aResult) {
+ RefPtr<nsFakePluginTag> result = FindFakePluginForType(aMimeType, false);
+ if (result) {
+ result.forget(aResult);
+ return NS_OK;
+ }
+
+ *aResult = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+#define ClearDataFromSitesClosure_CID \
+ { \
+ 0x9fb21761, 0x2403, 0x41ad, { \
+ 0x9e, 0xfd, 0x36, 0x7e, 0xc4, 0x4f, 0xa4, 0x5e \
+ } \
+ }
+
+// Class to hold all the data we need need for IterateMatchesAndClear and
+// ClearDataFromSites
+class ClearDataFromSitesClosure : public nsIClearSiteDataCallback,
+ public nsIGetSitesWithDataCallback {
+ public:
+ ClearDataFromSitesClosure(nsIPluginTag* plugin, const nsACString& domain,
+ uint64_t flags, int64_t maxAge,
+ nsCOMPtr<nsIClearSiteDataCallback> callback,
+ nsPluginHost* host)
+ : domain(domain),
+ callback(callback),
+ tag(plugin),
+ flags(flags),
+ maxAge(maxAge),
+ host(host) {}
+ NS_DECL_ISUPPORTS
+
+ // Callback from NPP_ClearSiteData, continue to iterate the matches and clear
+ NS_IMETHOD Callback(nsresult rv) override {
+ if (NS_FAILED(rv)) {
+ callback->Callback(rv);
+ return NS_OK;
+ }
+ if (!matches.Length()) {
+ callback->Callback(NS_OK);
+ return NS_OK;
+ }
+
+ const nsCString match(matches[0]);
+ matches.RemoveElement(match);
+ PluginLibrary* library =
+ static_cast<nsPluginTag*>(tag)->mPlugin->GetLibrary();
+ rv = library->NPP_ClearSiteData(match.get(), flags, maxAge, this);
+ if (NS_FAILED(rv)) {
+ callback->Callback(rv);
+ return NS_OK;
+ }
+ return NS_OK;
+ }
+
+ // Callback from NPP_GetSitesWithData, kick the iteration off to clear the
+ // data
+ NS_IMETHOD SitesWithData(nsTArray<nsCString>& sites) override {
+ // Enumerate the sites and build a list of matches.
+ nsresult rv = host->EnumerateSiteData(domain, sites, matches, false);
+ Callback(rv);
+ return NS_OK;
+ }
+
+ nsCString domain;
+ nsCOMPtr<nsIClearSiteDataCallback> callback;
+ nsTArray<nsCString> matches;
+ nsIPluginTag* tag;
+ uint64_t flags;
+ int64_t maxAge;
+ nsPluginHost* host;
+ NS_DECLARE_STATIC_IID_ACCESSOR(ClearDataFromSitesClosure_CID)
+ private:
+ virtual ~ClearDataFromSitesClosure() = default;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(ClearDataFromSitesClosure,
+ ClearDataFromSitesClosure_CID)
+
+NS_IMPL_ADDREF(ClearDataFromSitesClosure)
+NS_IMPL_RELEASE(ClearDataFromSitesClosure)
+
+NS_INTERFACE_MAP_BEGIN(ClearDataFromSitesClosure)
+ NS_INTERFACE_MAP_ENTRY(nsIClearSiteDataCallback)
+ NS_INTERFACE_MAP_ENTRY(nsIGetSitesWithDataCallback)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIClearSiteDataCallback)
+NS_INTERFACE_MAP_END
+
+// FIXME-jsplugins what should this do for fake plugins?
+NS_IMETHODIMP
+nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain,
+ uint64_t flags, int64_t maxAge,
+ nsIClearSiteDataCallback* callbackFunc) {
+ nsCOMPtr<nsIClearSiteDataCallback> callback(callbackFunc);
+ // maxAge must be either a nonnegative integer or -1.
+ NS_ENSURE_ARG(maxAge >= 0 || maxAge == -1);
+
+ // Caller may give us a tag object that is no longer live.
+ if (!IsLiveTag(plugin)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
+
+ if (!tag->IsEnabled()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // We only ensure support for clearing Flash site data for now.
+ // We will also attempt to clear data for any plugin that happens
+ // to be loaded already.
+ if (!tag->mIsFlashPlugin && !tag->mPlugin) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Make sure the plugin is loaded.
+ nsresult rv = EnsurePluginLoaded(tag);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ PluginLibrary* library = tag->mPlugin->GetLibrary();
+
+ // If 'domain' is the null string, clear everything.
+ if (domain.IsVoid()) {
+ return library->NPP_ClearSiteData(nullptr, flags, maxAge, callback);
+ }
+ nsCOMPtr<nsIGetSitesWithDataCallback> closure(new ClearDataFromSitesClosure(
+ plugin, domain, flags, maxAge, callback, this));
+ rv = library->NPP_GetSitesWithData(closure);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return NS_OK;
+}
+
+#define GetSitesClosure_CID \
+ { \
+ 0x4c9268ac, 0x2fd1, 0x4f2a, { \
+ 0x9a, 0x10, 0x7a, 0x09, 0xf1, 0xb7, 0x60, 0x3a \
+ } \
+ }
+
+// Closure to contain the data needed to handle the callback from
+// NPP_GetSitesWithData
+class GetSitesClosure : public nsIGetSitesWithDataCallback {
+ public:
+ NS_DECL_ISUPPORTS
+ GetSitesClosure(const nsACString& domain, nsPluginHost* host)
+ : domain(domain),
+ host(host),
+ result{false},
+ keepWaiting(true),
+ retVal(NS_ERROR_NOT_INITIALIZED) {}
+
+ NS_IMETHOD SitesWithData(nsTArray<nsCString>& sites) override {
+ retVal = HandleGetSites(sites);
+ keepWaiting = false;
+ return NS_OK;
+ }
+
+ nsresult HandleGetSites(nsTArray<nsCString>& sites) {
+ // If there's no data, we're done.
+ if (sites.IsEmpty()) {
+ result = false;
+ return NS_OK;
+ }
+
+ // If 'domain' is the null string, and there's data for at least one site,
+ // we're done.
+ if (domain.IsVoid()) {
+ result = true;
+ return NS_OK;
+ }
+
+ // Enumerate the sites and determine if there's a match.
+ nsTArray<nsCString> matches;
+ nsresult rv = host->EnumerateSiteData(domain, sites, matches, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ result = !matches.IsEmpty();
+ return NS_OK;
+ }
+
+ nsCString domain;
+ RefPtr<nsPluginHost> host;
+ bool result;
+ bool keepWaiting;
+ nsresult retVal;
+ NS_DECLARE_STATIC_IID_ACCESSOR(GetSitesClosure_CID)
+ private:
+ virtual ~GetSitesClosure() = default;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(GetSitesClosure, GetSitesClosure_CID)
+
+NS_IMPL_ISUPPORTS(GetSitesClosure, GetSitesClosure, nsIGetSitesWithDataCallback)
+
+// This will spin the event loop while waiting on an async
+// call to GetSitesWithData
+NS_IMETHODIMP
+nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain,
+ bool* result) {
+ // Caller may give us a tag object that is no longer live.
+ if (!IsLiveTag(plugin)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // FIXME-jsplugins audit casts
+ nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
+
+ // We only ensure support for clearing Flash site data for now.
+ // We will also attempt to clear data for any plugin that happens
+ // to be loaded already.
+ if (!tag->mIsFlashPlugin && !tag->mPlugin) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Make sure the plugin is loaded.
+ nsresult rv = EnsurePluginLoaded(tag);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ PluginLibrary* library = tag->mPlugin->GetLibrary();
+
+ // Get the list of sites from the plugin
+ nsCOMPtr<GetSitesClosure> closure(new GetSitesClosure(domain, this));
+ rv = library->NPP_GetSitesWithData(
+ nsCOMPtr<nsIGetSitesWithDataCallback>(closure));
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Spin the event loop while we wait for the async call to GetSitesWithData
+ SpinEventLoopUntil([&]() { return !closure->keepWaiting; });
+ *result = closure->result;
+ return closure->retVal;
+}
+
+nsPluginHost::SpecialType nsPluginHost::GetSpecialType(
+ const nsACString& aMIMEType) {
+ if (aMIMEType.LowerCaseEqualsASCII("application/x-test")) {
+ return eSpecialType_Test;
+ }
+
+ if (aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash") ||
+ aMIMEType.LowerCaseEqualsASCII("application/futuresplash") ||
+ aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash-test")) {
+ return eSpecialType_Flash;
+ }
+
+ return eSpecialType_None;
+}
+
+// Check whether or not a tag is a live, valid tag, and that it's loaded.
+bool nsPluginHost::IsLiveTag(nsIPluginTag* aPluginTag) {
+ nsCOMPtr<nsIInternalPluginTag> internalTag(do_QueryInterface(aPluginTag));
+ uint32_t fakeCount = mFakePlugins.Length();
+ for (uint32_t i = 0; i < fakeCount; i++) {
+ if (mFakePlugins[i] == internalTag) {
+ return true;
+ }
+ }
+
+ nsPluginTag* tag;
+ for (tag = mPlugins; tag; tag = tag->mNext) {
+ if (tag == internalTag) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// FIXME-jsplugins what should happen with jsplugins here, if anything?
+nsPluginTag* nsPluginHost::HaveSamePlugin(const nsPluginTag* aPluginTag) {
+ for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
+ if (tag->HasSameNameAndMimes(aPluginTag)) {
+ return tag;
+ }
+ }
+ return nullptr;
+}
+
+nsPluginTag* nsPluginHost::PluginWithId(uint32_t aId) {
+ for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
+ if (tag->mId == aId) {
+ return tag;
+ }
+ }
+ return nullptr;
+}
+
+void nsPluginHost::AddPluginTag(nsPluginTag* aPluginTag) {
+ aPluginTag->mNext = mPlugins;
+ mPlugins = aPluginTag;
+
+ if (aPluginTag->IsActive()) {
+ nsAutoCString disableFullPage;
+ Preferences::GetCString(kPrefDisableFullPage, disableFullPage);
+ for (uint32_t i = 0; i < aPluginTag->MimeTypes().Length(); i++) {
+ if (!IsTypeInList(aPluginTag->MimeTypes()[i], disableFullPage)) {
+ RegisterWithCategoryManager(aPluginTag->MimeTypes()[i],
+ ePluginRegister);
+ }
+ }
+ }
+}
+
+typedef NS_NPAPIPLUGIN_CALLBACK(char*, NP_GETMIMEDESCRIPTION)(void);
+
+void nsPluginHost::UpdatePluginBlocklistState(nsPluginTag* aPluginTag,
+ bool aShouldSoftblock) {
+ nsCOMPtr<nsIBlocklistService> blocklist =
+ do_GetService("@mozilla.org/extensions/blocklist;1");
+ MOZ_ASSERT(blocklist, "Should be able to access the blocklist");
+ if (!blocklist) {
+ return;
+ }
+ // Asynchronously get the blocklist state.
+ RefPtr<Promise> promise;
+ blocklist->GetPluginBlocklistState(aPluginTag, u""_ns, u""_ns,
+ getter_AddRefs(promise));
+ MOZ_ASSERT(promise,
+ "Should always get a promise for plugin blocklist state.");
+ if (promise) {
+ promise->AppendNativeHandler(new mozilla::plugins::BlocklistPromiseHandler(
+ aPluginTag, aShouldSoftblock));
+ }
+}
+
+void nsPluginHost::IncrementChromeEpoch() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ mPluginEpoch++;
+}
+
+uint32_t nsPluginHost::ChromeEpoch() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ return mPluginEpoch;
+}
+
+uint32_t nsPluginHost::ChromeEpochForContent() {
+ MOZ_ASSERT(XRE_IsContentProcess());
+ return mPluginEpoch;
+}
+
+void nsPluginHost::SetChromeEpochForContent(uint32_t aEpoch) {
+ MOZ_ASSERT(XRE_IsContentProcess());
+ mPluginEpoch = aEpoch;
+}
+
+already_AddRefed<nsIAsyncShutdownClient> GetProfileChangeTeardownPhase() {
+ nsCOMPtr<nsIAsyncShutdownService> asyncShutdownSvc =
+ services::GetAsyncShutdownService();
+ MOZ_ASSERT(asyncShutdownSvc);
+ if (NS_WARN_IF(!asyncShutdownSvc)) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase;
+ DebugOnly<nsresult> rv =
+ asyncShutdownSvc->GetProfileChangeTeardown(getter_AddRefs(shutdownPhase));
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ return shutdownPhase.forget();
+}
+
+nsresult nsPluginHost::LoadPlugins() { return NS_OK; }
+
+void nsPluginHost::FindingFinished() {}
+
+nsresult nsPluginHost::SetPluginsInContent(
+ uint32_t aPluginEpoch, nsTArray<mozilla::plugins::PluginTag>& aPlugins,
+ nsTArray<mozilla::plugins::FakePluginTag>& aFakePlugins) {
+ MOZ_ASSERT(XRE_IsContentProcess());
+
+ nsTArray<PluginTag> plugins;
+
+ nsTArray<FakePluginTag> fakePlugins;
+
+ if (aPluginEpoch != ChromeEpochForContent()) {
+ // Since we know we're going to be repopulating the lists anyways,
+ // clear out all old entries.
+ ClearNonRunningPlugins();
+
+ SetChromeEpochForContent(aPluginEpoch);
+
+ for (auto tag : aPlugins) {
+ // Don't add the same plugin again.
+ if (nsPluginTag* existing = PluginWithId(tag.id())) {
+ UpdateInMemoryPluginInfo(existing);
+ existing->SetBlocklistState(tag.blocklistState());
+ continue;
+ }
+
+ nsPluginTag* pluginTag = new nsPluginTag(
+ tag.id(), tag.name().get(), tag.description().get(),
+ tag.filename().get(),
+ "", // aFullPath
+ tag.version().get(), tag.mimeTypes().Clone(),
+ tag.mimeDescriptions().Clone(), tag.extensions().Clone(),
+ tag.isFlashPlugin(), tag.supportsAsyncRender(),
+ tag.lastModifiedTime(), tag.sandboxLevel(), tag.blocklistState());
+ AddPluginTag(pluginTag);
+ }
+
+ for (const auto& tag : aFakePlugins) {
+ // Don't add the same plugin again.
+ for (const auto& existingTag : mFakePlugins) {
+ if (existingTag->Id() == tag.id()) {
+ continue;
+ }
+ }
+
+ RefPtr<nsFakePluginTag> pluginTag =
+ *mFakePlugins.AppendElement(new nsFakePluginTag(
+ tag.id(), mozilla::ipc::DeserializeURI(tag.handlerURI()),
+ tag.name().get(), tag.description().get(), tag.mimeTypes(),
+ tag.mimeDescriptions(), tag.extensions(), tag.niceName(),
+ tag.sandboxScript()));
+ nsAutoCString disableFullPage;
+ Preferences::GetCString(kPrefDisableFullPage, disableFullPage);
+ for (uint32_t i = 0; i < pluginTag->MimeTypes().Length(); i++) {
+ if (!IsTypeInList(pluginTag->MimeTypes()[i], disableFullPage)) {
+ RegisterWithCategoryManager(pluginTag->MimeTypes()[i],
+ ePluginRegister);
+ }
+ }
+ }
+
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ if (obsService) {
+ obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
+ }
+ }
+
+ mPluginsLoaded = true;
+ return NS_OK;
+}
+
+nsresult nsPluginHost::UpdateCachedSerializablePluginList() {
+ nsTArray<nsCOMPtr<nsIInternalPluginTag>> plugins;
+ GetPlugins(plugins, true);
+ mSerializablePlugins.Clear();
+ mSerializableFakePlugins.Clear();
+
+ for (size_t i = 0; i < plugins.Length(); i++) {
+ nsCOMPtr<nsIInternalPluginTag> basetag = plugins[i];
+
+ nsCOMPtr<nsIFakePluginTag> faketag = do_QueryInterface(basetag);
+ if (faketag) {
+ /// FIXME-jsplugins - We need to add a nsIInternalPluginTag->AsNative() to
+ /// avoid this hacky static cast
+ nsFakePluginTag* tag = static_cast<nsFakePluginTag*>(basetag.get());
+ mozilla::ipc::URIParams handlerURI;
+ SerializeURI(tag->HandlerURI(), handlerURI);
+ mSerializableFakePlugins.AppendElement(FakePluginTag(
+ tag->Id(), handlerURI, tag->Name(), tag->Description(),
+ tag->MimeTypes(), tag->MimeDescriptions(), tag->Extensions(),
+ tag->GetNiceFileName(), tag->SandboxScript()));
+ continue;
+ }
+
+ /// FIXME-jsplugins - We need to cleanup the various plugintag classes
+ /// to be more sane and avoid this dance
+ nsPluginTag* tag = static_cast<nsPluginTag*>(basetag.get());
+
+ uint32_t blocklistState;
+ if (NS_WARN_IF(NS_FAILED(tag->GetBlocklistState(&blocklistState)))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mSerializablePlugins.AppendElement(PluginTag(
+ tag->mId, tag->Name(), tag->Description(), tag->MimeTypes(),
+ tag->MimeDescriptions(), tag->Extensions(), tag->mIsFlashPlugin,
+ tag->mSupportsAsyncRender, tag->FileName(), tag->Version(),
+ tag->mLastModifiedTime, tag->mSandboxLevel, blocklistState));
+ }
+ return NS_OK;
+}
+
+nsresult nsPluginHost::BroadcastPluginsToContent() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ // Load plugins so that the epoch is correct.
+ nsresult rv = LoadPlugins();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = UpdateCachedSerializablePluginList();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ uint32_t newPluginEpoch = ChromeEpoch();
+
+ nsTArray<dom::ContentParent*> parents;
+ dom::ContentParent::GetAll(parents);
+ for (auto p : parents) {
+ Unused << p->SendSetPluginList(newPluginEpoch, mSerializablePlugins,
+ mSerializableFakePlugins);
+ }
+ return NS_OK;
+}
+
+nsresult nsPluginHost::SendPluginsToContent(dom::ContentParent* parent) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(parent);
+ // Load plugins so that the epoch is correct.
+ nsresult rv = LoadPlugins();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ Unused << parent->SendSetPluginList(ChromeEpoch(), mSerializablePlugins,
+ mSerializableFakePlugins);
+ return NS_OK;
+}
+
+void nsPluginHost::UpdateInMemoryPluginInfo(nsPluginTag* aPluginTag) {
+ if (!aPluginTag) {
+ return;
+ }
+
+ // Update types with category manager
+ nsAutoCString disableFullPage;
+ Preferences::GetCString(kPrefDisableFullPage, disableFullPage);
+ for (uint32_t i = 0; i < aPluginTag->MimeTypes().Length(); i++) {
+ nsRegisterType shouldRegister;
+
+ if (IsTypeInList(aPluginTag->MimeTypes()[i], disableFullPage)) {
+ shouldRegister = ePluginUnregister;
+ } else {
+ nsPluginTag* plugin =
+ FindNativePluginForType(aPluginTag->MimeTypes()[i], true);
+ shouldRegister = plugin ? ePluginRegister : ePluginUnregister;
+ }
+
+ RegisterWithCategoryManager(aPluginTag->MimeTypes()[i], shouldRegister);
+ }
+
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ if (obsService)
+ obsService->NotifyObservers(nullptr, "plugin-info-updated", nullptr);
+}
+
+// This function is not relevant for fake plugins.
+void nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ IncrementChromeEpoch();
+
+ UpdateInMemoryPluginInfo(aPluginTag);
+}
+
+void nsPluginHost::RegisterWithCategoryManager(const nsCString& aMimeType,
+ nsRegisterType aType) {
+ PLUGIN_LOG(
+ PLUGIN_LOG_NORMAL,
+ ("nsPluginTag::RegisterWithCategoryManager type = %s, removing = %s\n",
+ aMimeType.get(), aType == ePluginUnregister ? "yes" : "no"));
+
+ nsCOMPtr<nsICategoryManager> catMan =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ if (!catMan) {
+ return;
+ }
+
+ constexpr auto contractId =
+ "@mozilla.org/content/plugin/document-loader-factory;1"_ns;
+
+ if (aType == ePluginRegister) {
+ catMan->AddCategoryEntry("Gecko-Content-Viewers", aMimeType, contractId,
+ false, /* persist: broken by bug 193031 */
+ mOverrideInternalTypes);
+ } else {
+ if (aType == ePluginMaybeUnregister) {
+ // Bail out if this type is still used by an enabled plugin
+ if (HavePluginForType(aMimeType)) {
+ return;
+ }
+ } else {
+ MOZ_ASSERT(aType == ePluginUnregister, "Unknown nsRegisterType");
+ }
+
+ // Only delete the entry if a plugin registered for it
+ nsCString value;
+ nsresult rv =
+ catMan->GetCategoryEntry("Gecko-Content-Viewers", aMimeType, value);
+ if (NS_SUCCEEDED(rv) && value == contractId) {
+ catMan->DeleteCategoryEntry("Gecko-Content-Viewers", aMimeType, true);
+ }
+ }
+}
+
+nsresult nsPluginHost::NewPluginURLStream(
+ const nsString& aURL, nsNPAPIPluginInstance* aInstance,
+ nsNPAPIPluginStreamListener* aListener, nsIInputStream* aPostStream,
+ const char* aHeadersData, uint32_t aHeadersDataLen) {
+ nsCOMPtr<nsIURI> url;
+ nsAutoString absUrl;
+
+ if (aURL.Length() <= 0) return NS_OK;
+
+ // get the base URI for the plugin to create an absolute url
+ // in case aURL is relative
+ RefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
+ if (owner) {
+ NS_MakeAbsoluteURI(absUrl, aURL, owner->GetBaseURI());
+ }
+
+ if (absUrl.IsEmpty()) absUrl.Assign(aURL);
+
+ nsresult rv = NS_NewURI(getter_AddRefs(url), absUrl);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsPluginStreamListenerPeer> listenerPeer =
+ new nsPluginStreamListenerPeer();
+ NS_ENSURE_TRUE(listenerPeer, NS_ERROR_OUT_OF_MEMORY);
+
+ rv = listenerPeer->Initialize(url, aInstance, aListener);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<dom::Element> element;
+ nsCOMPtr<Document> doc;
+ if (owner) {
+ owner->GetDOMElement(getter_AddRefs(element));
+ owner->GetDocument(getter_AddRefs(doc));
+ }
+
+ NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIChannel> channel;
+ // @arg loadgroup:
+ // do not add this internal plugin's channel on the
+ // load group otherwise this channel could be canceled
+ // form |nsDocShell::OnLinkClickSync| bug 166613
+ rv = NS_NewChannel(
+ getter_AddRefs(channel), url, element,
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT |
+ nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
+ nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
+ nullptr, // aPerformanceStorage
+ nullptr, // aLoadGroup
+ listenerPeer,
+ nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (doc) {
+ // And if it's a script allow it to execute against the
+ // document's script context.
+ nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(channel));
+ if (scriptChannel) {
+ scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
+ // Plug-ins seem to depend on javascript: URIs running synchronously
+ scriptChannel->SetExecuteAsync(false);
+ }
+ }
+
+ // deal with headers and post data
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
+ if (httpChannel) {
+ if (!aPostStream) {
+ // Only set the Referer header for GET requests because IIS throws
+ // errors about malformed requests if we include it in POSTs. See
+ // bug 724465.
+ nsCOMPtr<nsIURI> referer;
+ dom::ReferrerPolicy referrerPolicy = dom::ReferrerPolicy::_empty;
+
+ nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(element);
+ if (olc) olc->GetSrcURI(getter_AddRefs(referer));
+
+ if (!referer) {
+ if (!doc) {
+ return NS_ERROR_FAILURE;
+ }
+ referer = doc->GetDocumentURIAsReferrer();
+ referrerPolicy = doc->GetReferrerPolicy();
+ }
+ nsCOMPtr<nsIReferrerInfo> referrerInfo =
+ new dom::ReferrerInfo(referer, referrerPolicy);
+ rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (aPostStream) {
+ // XXX it's a bit of a hack to rewind the postdata stream
+ // here but it has to be done in case the post data is
+ // being reused multiple times.
+ nsCOMPtr<nsISeekableStream> postDataSeekable(
+ do_QueryInterface(aPostStream));
+ if (postDataSeekable)
+ postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+
+ nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
+ NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
+
+ uploadChannel->SetUploadStream(aPostStream, ""_ns, -1);
+ }
+
+ if (aHeadersData) {
+ rv = AddHeadersToChannel(aHeadersData, aHeadersDataLen, httpChannel);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ rv = channel->AsyncOpen(listenerPeer);
+ if (NS_SUCCEEDED(rv)) listenerPeer->TrackRequest(channel);
+ return rv;
+}
+
+nsresult nsPluginHost::AddHeadersToChannel(const char* aHeadersData,
+ uint32_t aHeadersDataLen,
+ nsIChannel* aGenericChannel) {
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIHttpChannel> aChannel = do_QueryInterface(aGenericChannel);
+ if (!aChannel) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // used during the manipulation of the String from the aHeadersData
+ nsAutoCString headersString;
+ nsAutoCString oneHeader;
+ nsAutoCString headerName;
+ nsAutoCString headerValue;
+ int32_t crlf = 0;
+ int32_t colon = 0;
+
+ // Turn the char * buffer into an nsString.
+ headersString = aHeadersData;
+
+ // Iterate over the nsString: for each "\r\n" delimited chunk,
+ // add the value as a header to the nsIHTTPChannel
+ while (true) {
+ crlf = headersString.Find("\r\n", true);
+ if (-1 == crlf) {
+ rv = NS_OK;
+ return rv;
+ }
+ headersString.Mid(oneHeader, 0, crlf);
+ headersString.Cut(0, crlf + 2);
+ oneHeader.StripWhitespace();
+ colon = oneHeader.Find(":");
+ if (-1 == colon) {
+ rv = NS_ERROR_NULL_POINTER;
+ return rv;
+ }
+ oneHeader.Left(headerName, colon);
+ colon++;
+ oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
+
+ // FINALLY: we can set the header!
+
+ rv = aChannel->SetRequestHeader(headerName, headerValue, true);
+ if (NS_FAILED(rv)) {
+ rv = NS_ERROR_NULL_POINTER;
+ return rv;
+ }
+ }
+}
+
+nsresult nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance) {
+ AUTO_PROFILER_LABEL("nsPluginHost::StopPluginInstance", OTHER);
+ if (PluginDestructionGuard::DelayDestroy(aInstance)) {
+ return NS_OK;
+ }
+
+ PLUGIN_LOG(
+ PLUGIN_LOG_NORMAL,
+ ("nsPluginHost::StopPluginInstance called instance=%p\n", aInstance));
+
+ if (aInstance->HasStartedDestroying()) {
+ return NS_OK;
+ }
+
+ Telemetry::AutoTimer<Telemetry::PLUGIN_SHUTDOWN_MS> timer;
+ aInstance->Stop();
+
+ // if the instance does not want to be 'cached' just remove it
+ bool doCache = aInstance->ShouldCache();
+ if (doCache) {
+ // try to get the max cached instances from a pref or use default
+ uint32_t cachedInstanceLimit = Preferences::GetUint(
+ NS_PREF_MAX_NUM_CACHED_INSTANCES, DEFAULT_NUMBER_OF_STOPPED_INSTANCES);
+ if (StoppedInstanceCount() >= cachedInstanceLimit) {
+ nsNPAPIPluginInstance* oldestInstance = FindOldestStoppedInstance();
+ if (oldestInstance) {
+ nsPluginTag* pluginTag = TagForPlugin(oldestInstance->GetPlugin());
+ oldestInstance->Destroy();
+ mInstances.RemoveElement(oldestInstance);
+ // TODO: Remove this check once bug 752422 was investigated
+ if (pluginTag) {
+ OnPluginInstanceDestroyed(pluginTag);
+ }
+ }
+ }
+ } else {
+ nsPluginTag* pluginTag = TagForPlugin(aInstance->GetPlugin());
+ aInstance->Destroy();
+ mInstances.RemoveElement(aInstance);
+ // TODO: Remove this check once bug 752422 was investigated
+ if (pluginTag) {
+ OnPluginInstanceDestroyed(pluginTag);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginHost::NewPluginStreamListener(
+ nsIURI* aURI, nsNPAPIPluginInstance* aInstance,
+ nsIStreamListener** aStreamListener) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ NS_ENSURE_ARG_POINTER(aStreamListener);
+
+ RefPtr<nsPluginStreamListenerPeer> listener =
+ new nsPluginStreamListenerPeer();
+ nsresult rv = listener->Initialize(aURI, aInstance, nullptr);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ listener.forget(aStreamListener);
+
+ return NS_OK;
+}
+
+void nsPluginHost::CreateWidget(nsPluginInstanceOwner* aOwner) {
+ aOwner->CreateWidget();
+
+ // If we've got a native window, the let the plugin know about it.
+ aOwner->CallSetWindow();
+}
+
+NS_IMETHODIMP nsPluginHost::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* someData) {
+ if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
+ UnloadPlugins();
+ }
+ if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
+ mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
+ // Unload or load plugins as needed
+ if (mPluginsDisabled) {
+ UnloadPlugins();
+ } else {
+ LoadPlugins();
+ }
+ }
+ if (XRE_IsParentProcess() && !strcmp("plugin-blocklist-updated", aTopic)) {
+ // The blocklist has updated. Asynchronously get blocklist state for all
+ // items. The promise resolution handler takes care of checking if anything
+ // changed, and writing an updated state to file, as well as sending data to
+ // child processes.
+ nsPluginTag* plugin = mPlugins;
+ while (plugin) {
+ UpdatePluginBlocklistState(plugin);
+ plugin = plugin->mNext;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult nsPluginHost::ParsePostBufferToFixHeaders(const char* inPostData,
+ uint32_t inPostDataLen,
+ char** outPostData,
+ uint32_t* outPostDataLen) {
+ if (!inPostData || !outPostData || !outPostDataLen)
+ return NS_ERROR_NULL_POINTER;
+
+ *outPostData = 0;
+ *outPostDataLen = 0;
+
+ const char CR = '\r';
+ const char LF = '\n';
+ const char CRLFCRLF[] = {CR, LF, CR, LF, '\0'}; // C string"\r\n\r\n"
+ const char ContentLenHeader[] = "Content-length";
+
+ AutoTArray<const char*, 8> singleLF;
+ const char* pSCntlh =
+ 0; // pointer to start of ContentLenHeader in inPostData
+ const char* pSod = 0; // pointer to start of data in inPostData
+ const char* pEoh = 0; // pointer to end of headers in inPostData
+ const char* pEod =
+ inPostData + inPostDataLen; // pointer to end of inPostData
+ if (*inPostData == LF) {
+ // If no custom headers are required, simply add a blank
+ // line ('\n') to the beginning of the file or buffer.
+ // so *inPostData == '\n' is valid
+ pSod = inPostData + 1;
+ } else {
+ const char* s = inPostData; // tmp pointer to sourse inPostData
+ while (s < pEod) {
+ if (!pSCntlh && (*s == 'C' || *s == 'c') &&
+ (s + sizeof(ContentLenHeader) - 1 < pEod) &&
+ (!PL_strncasecmp(s, ContentLenHeader,
+ sizeof(ContentLenHeader) - 1))) {
+ // lets assume this is ContentLenHeader for now
+ const char* p = pSCntlh = s;
+ p += sizeof(ContentLenHeader) - 1;
+ // search for first CR or LF == end of ContentLenHeader
+ for (; p < pEod; p++) {
+ if (*p == CR || *p == LF) {
+ // got delimiter,
+ // one more check; if previous char is a digit
+ // most likely pSCntlh points to the start of ContentLenHeader
+ if (*(p - 1) >= '0' && *(p - 1) <= '9') {
+ s = p;
+ }
+ break; // for loop
+ }
+ }
+ if (pSCntlh == s) { // curret ptr is the same
+ pSCntlh = 0; // that was not ContentLenHeader
+ break; // there is nothing to parse, break *WHILE LOOP* here
+ }
+ }
+
+ if (*s == CR) {
+ if (pSCntlh && // only if ContentLenHeader is found we are looking for
+ // end of headers
+ ((s + sizeof(CRLFCRLF) - 1) <= pEod) &&
+ !memcmp(s, CRLFCRLF, sizeof(CRLFCRLF) - 1)) {
+ s += sizeof(CRLFCRLF) - 1;
+ pEoh = pSod = s; // data stars here
+ break;
+ }
+ } else if (*s == LF) {
+ if (*(s - 1) != CR) {
+ singleLF.AppendElement(s);
+ }
+ if (pSCntlh && (s + 1 < pEod) && (*(s + 1) == LF)) {
+ s++;
+ singleLF.AppendElement(s);
+ s++;
+ pEoh = pSod = s; // data stars here
+ break;
+ }
+ }
+ s++;
+ }
+ }
+
+ // deal with output buffer
+ if (!pSod) { // lets assume whole buffer is a data
+ pSod = inPostData;
+ }
+
+ uint32_t newBufferLen = 0;
+ uint32_t dataLen = pEod - pSod;
+ uint32_t headersLen = pEoh ? pSod - inPostData : 0;
+
+ char* p; // tmp ptr into new output buf
+ if (headersLen) { // we got a headers
+ // this function does not make any assumption on correctness
+ // of ContentLenHeader value in this case.
+
+ newBufferLen = dataLen + headersLen;
+ // in case there were single LFs in headers
+ // reserve an extra space for CR will be added before each single LF
+ int cntSingleLF = singleLF.Length();
+ newBufferLen += cntSingleLF;
+
+ *outPostData = p = (char*)moz_xmalloc(newBufferLen);
+
+ // deal with single LF
+ const char* s = inPostData;
+ if (cntSingleLF) {
+ for (int i = 0; i < cntSingleLF; i++) {
+ const char* plf = singleLF.ElementAt(i); // ptr to single LF in headers
+ int n = plf - s; // bytes to copy
+ if (n) { // for '\n\n' there is nothing to memcpy
+ memcpy(p, s, n);
+ p += n;
+ }
+ *p++ = CR;
+ s = plf;
+ *p++ = *s++;
+ }
+ }
+ // are we done with headers?
+ headersLen = pEoh - s;
+ if (headersLen) { // not yet
+ memcpy(p, s, headersLen); // copy the rest
+ p += headersLen;
+ }
+ } else if (dataLen) { // no ContentLenHeader is found but there is a data
+ // make new output buffer big enough
+ // to keep ContentLenHeader+value followed by data
+ uint32_t l = sizeof(ContentLenHeader) + sizeof(CRLFCRLF) + 32;
+ newBufferLen = dataLen + l;
+ *outPostData = p = (char*)moz_xmalloc(newBufferLen);
+ headersLen =
+ snprintf(p, l, "%s: %u%s", ContentLenHeader, dataLen, CRLFCRLF);
+ if (headersLen ==
+ l) { // if snprintf has ate all extra space consider this as an error
+ free(p);
+ *outPostData = 0;
+ return NS_ERROR_FAILURE;
+ }
+ p += headersLen;
+ newBufferLen = headersLen + dataLen;
+ }
+ // at this point we've done with headers.
+ // there is a possibility that input buffer has only headers info in it
+ // which already parsed and copied into output buffer.
+ // copy the data
+ if (dataLen) {
+ memcpy(p, pSod, dataLen);
+ }
+
+ *outPostDataLen = newBufferLen;
+
+ return NS_OK;
+}
+
+nsresult nsPluginHost::NewPluginNativeWindow(
+ nsPluginNativeWindow** aPluginNativeWindow) {
+ return PLUG_NewPluginNativeWindow(aPluginNativeWindow);
+}
+
+nsresult nsPluginHost::GetPluginName(nsNPAPIPluginInstance* aPluginInstance,
+ const char** aPluginName) {
+ nsNPAPIPluginInstance* instance =
+ static_cast<nsNPAPIPluginInstance*>(aPluginInstance);
+ if (!instance) return NS_ERROR_FAILURE;
+
+ nsNPAPIPlugin* plugin = instance->GetPlugin();
+ if (!plugin) return NS_ERROR_FAILURE;
+
+ *aPluginName = TagForPlugin(plugin)->Name().get();
+
+ return NS_OK;
+}
+
+nsresult nsPluginHost::GetPluginTagForInstance(
+ nsNPAPIPluginInstance* aPluginInstance, nsIPluginTag** aPluginTag) {
+ NS_ENSURE_ARG_POINTER(aPluginInstance);
+ NS_ENSURE_ARG_POINTER(aPluginTag);
+
+ nsNPAPIPlugin* plugin = aPluginInstance->GetPlugin();
+ if (!plugin) return NS_ERROR_FAILURE;
+
+ *aPluginTag = TagForPlugin(plugin);
+
+ NS_ADDREF(*aPluginTag);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPluginHost::Notify(nsITimer* timer) {
+ RefPtr<nsPluginTag> pluginTag = mPlugins;
+ while (pluginTag) {
+ if (pluginTag->mUnloadTimer == timer) {
+ if (!IsRunningPlugin(pluginTag)) {
+ pluginTag->TryUnloadPlugin(false);
+ }
+ return NS_OK;
+ }
+ pluginTag = pluginTag->mNext;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsPluginHost::GetName(nsACString& aName) {
+ aName.AssignLiteral("nsPluginHost");
+ return NS_OK;
+}
+
+#ifdef XP_WIN
+// Re-enable any top level browser windows that were disabled by modal dialogs
+// displayed by the crashed plugin.
+static void CheckForDisabledWindows() {
+ nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (!wm) return;
+
+ nsCOMPtr<nsISimpleEnumerator> windowList;
+ wm->GetAppWindowEnumerator(nullptr, getter_AddRefs(windowList));
+ if (!windowList) return;
+
+ bool haveWindows;
+ do {
+ windowList->HasMoreElements(&haveWindows);
+ if (!haveWindows) return;
+
+ nsCOMPtr<nsISupports> supportsWindow;
+ windowList->GetNext(getter_AddRefs(supportsWindow));
+ nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supportsWindow));
+ if (baseWin) {
+ nsCOMPtr<nsIWidget> widget;
+ baseWin->GetMainWidget(getter_AddRefs(widget));
+ if (widget && !widget->GetParent() && widget->IsVisible() &&
+ !widget->IsEnabled()) {
+ nsIWidget* child = widget->GetFirstChild();
+ bool enable = true;
+ while (child) {
+ if (child->WindowType() == eWindowType_dialog) {
+ enable = false;
+ break;
+ }
+ child = child->GetNextSibling();
+ }
+ if (enable) {
+ widget->Enable(true);
+ }
+ }
+ }
+ } while (haveWindows);
+}
+#endif
+
+void nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin,
+ const nsAString& aPluginDumpID,
+ const nsACString& aAdditionalMinidumps) {
+ nsPluginTag* crashedPluginTag = TagForPlugin(aPlugin);
+ MOZ_ASSERT(crashedPluginTag);
+
+ // Notify the app's observer that a plugin crashed so it can submit
+ // a crashreport.
+ bool submittedCrashReport = false;
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ nsCOMPtr<nsIWritablePropertyBag2> propbag =
+ do_CreateInstance("@mozilla.org/hash-property-bag;1");
+ if (obsService && propbag) {
+ uint32_t runID = 0;
+ PluginLibrary* library = aPlugin->GetLibrary();
+
+ if (!NS_WARN_IF(!library)) {
+ library->GetRunID(&runID);
+ }
+ propbag->SetPropertyAsUint32(u"runID"_ns, runID);
+
+ nsCString pluginName;
+ crashedPluginTag->GetName(pluginName);
+ propbag->SetPropertyAsAString(u"pluginName"_ns,
+ NS_ConvertUTF8toUTF16(pluginName));
+ propbag->SetPropertyAsAString(u"pluginDumpID"_ns, aPluginDumpID);
+ propbag->SetPropertyAsACString(u"additionalMinidumps"_ns,
+ aAdditionalMinidumps);
+ propbag->SetPropertyAsBool(u"submittedCrashReport"_ns,
+ submittedCrashReport);
+ obsService->NotifyObservers(propbag, "plugin-crashed", nullptr);
+ // see if an observer submitted a crash report.
+ propbag->GetPropertyAsBool(u"submittedCrashReport"_ns,
+ &submittedCrashReport);
+ }
+
+ // Invalidate each nsPluginInstanceTag for the crashed plugin
+
+ for (uint32_t i = mInstances.Length(); i > 0; i--) {
+ nsNPAPIPluginInstance* instance = mInstances[i - 1];
+ if (instance->GetPlugin() == aPlugin) {
+ // notify the content node (nsIObjectLoadingContent) that the
+ // plugin has crashed
+ RefPtr<dom::Element> domElement;
+ instance->GetDOMElement(getter_AddRefs(domElement));
+ nsCOMPtr<nsIObjectLoadingContent> objectContent(
+ do_QueryInterface(domElement));
+ if (objectContent) {
+ objectContent->PluginCrashed(crashedPluginTag, aPluginDumpID,
+ submittedCrashReport);
+ }
+
+ instance->Destroy();
+ mInstances.RemoveElement(instance);
+ OnPluginInstanceDestroyed(crashedPluginTag);
+ }
+ }
+
+ // Only after all instances have been invalidated is it safe to null
+ // out nsPluginTag.mPlugin. The next time we try to create an
+ // instance of this plugin we reload it (launch a new plugin process).
+
+ crashedPluginTag->mPlugin = nullptr;
+ crashedPluginTag->mContentProcessRunningCount = 0;
+
+#ifdef XP_WIN
+ CheckForDisabledWindows();
+#endif
+}
+
+nsNPAPIPluginInstance* nsPluginHost::FindInstance(const char* mimetype) {
+ for (uint32_t i = 0; i < mInstances.Length(); i++) {
+ nsNPAPIPluginInstance* instance = mInstances[i];
+
+ const char* mt;
+ nsresult rv = instance->GetMIMEType(&mt);
+ if (NS_FAILED(rv)) continue;
+
+ if (PL_strcasecmp(mt, mimetype) == 0) return instance;
+ }
+
+ return nullptr;
+}
+
+nsNPAPIPluginInstance* nsPluginHost::FindOldestStoppedInstance() {
+ nsNPAPIPluginInstance* oldestInstance = nullptr;
+ TimeStamp oldestTime = TimeStamp::Now();
+ for (uint32_t i = 0; i < mInstances.Length(); i++) {
+ nsNPAPIPluginInstance* instance = mInstances[i];
+ if (instance->IsRunning()) continue;
+
+ TimeStamp time = instance->StopTime();
+ if (time < oldestTime) {
+ oldestTime = time;
+ oldestInstance = instance;
+ }
+ }
+
+ return oldestInstance;
+}
+
+uint32_t nsPluginHost::StoppedInstanceCount() {
+ uint32_t stoppedCount = 0;
+ for (uint32_t i = 0; i < mInstances.Length(); i++) {
+ nsNPAPIPluginInstance* instance = mInstances[i];
+ if (!instance->IsRunning()) stoppedCount++;
+ }
+ return stoppedCount;
+}
+
+nsTArray<RefPtr<nsNPAPIPluginInstance>>* nsPluginHost::InstanceArray() {
+ return &mInstances;
+}
+
+void nsPluginHost::DestroyRunningInstances(nsPluginTag* aPluginTag) {
+ for (int32_t i = mInstances.Length(); i > 0; i--) {
+ nsNPAPIPluginInstance* instance = mInstances[i - 1];
+ if (instance->IsRunning() &&
+ (!aPluginTag || aPluginTag == TagForPlugin(instance->GetPlugin()))) {
+ instance->SetWindow(nullptr);
+ instance->Stop();
+
+ // Get rid of all the instances without the possibility of caching.
+ nsPluginTag* pluginTag = TagForPlugin(instance->GetPlugin());
+ instance->SetWindow(nullptr);
+
+ RefPtr<dom::Element> domElement;
+ instance->GetDOMElement(getter_AddRefs(domElement));
+ nsCOMPtr<nsIObjectLoadingContent> objectContent =
+ do_QueryInterface(domElement);
+
+ instance->Destroy();
+
+ mInstances.RemoveElement(instance);
+ OnPluginInstanceDestroyed(pluginTag);
+
+ // Notify owning content that we destroyed its plugin out from under it
+ if (objectContent) {
+ objectContent->PluginDestroyed();
+ }
+ }
+ }
+}
+
+/* static */
+bool nsPluginHost::CanUsePluginForMIMEType(const nsACString& aMIMEType) {
+ // We only support flash as a plugin, so if the mime types don't match for
+ // those, exit before we start loading plugins.
+ //
+ // XXX: Remove test/java cases when bug 1351885 lands.
+ if (nsPluginHost::GetSpecialType(aMIMEType) ==
+ nsPluginHost::eSpecialType_Flash ||
+ MimeTypeIsAllowedForFakePlugin(NS_ConvertUTF8toUTF16(aMIMEType)) ||
+ aMIMEType.LowerCaseEqualsLiteral("application/x-test")) {
+ return true;
+ }
+
+ return false;
+}
+
+// Runnable that does an async destroy of a plugin.
+
+class nsPluginDestroyRunnable
+ : public Runnable,
+ public mozilla::LinkedListElement<nsPluginDestroyRunnable> {
+ public:
+ explicit nsPluginDestroyRunnable(nsNPAPIPluginInstance* aInstance)
+ : Runnable("nsPluginDestroyRunnable"), mInstance(aInstance) {
+ sRunnableList.insertBack(this);
+ }
+
+ ~nsPluginDestroyRunnable() override { this->remove(); }
+
+ NS_IMETHOD Run() override {
+ RefPtr<nsNPAPIPluginInstance> instance;
+
+ // Null out mInstance to make sure this code in another runnable
+ // will do the right thing even if someone was holding on to this
+ // runnable longer than we expect.
+ instance.swap(mInstance);
+
+ if (PluginDestructionGuard::DelayDestroy(instance)) {
+ // It's still not safe to destroy the plugin, it's now up to the
+ // outermost guard on the stack to take care of the destruction.
+ return NS_OK;
+ }
+
+ for (auto r : sRunnableList) {
+ if (r != this && r->mInstance == instance) {
+ // There's another runnable scheduled to tear down
+ // instance. Let it do the job.
+ return NS_OK;
+ }
+ }
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("Doing delayed destroy of instance %p\n", instance.get()));
+
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ if (host) host->StopPluginInstance(instance);
+
+ PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+ ("Done with delayed destroy of instance %p\n", instance.get()));
+
+ return NS_OK;
+ }
+
+ protected:
+ RefPtr<nsNPAPIPluginInstance> mInstance;
+
+ static mozilla::LinkedList<nsPluginDestroyRunnable> sRunnableList;
+};
+
+mozilla::LinkedList<nsPluginDestroyRunnable>
+ nsPluginDestroyRunnable::sRunnableList;
+
+mozilla::LinkedList<PluginDestructionGuard> PluginDestructionGuard::sList;
+
+PluginDestructionGuard::PluginDestructionGuard(nsNPAPIPluginInstance* aInstance)
+ : mInstance(aInstance) {
+ Init();
+}
+
+PluginDestructionGuard::PluginDestructionGuard(NPP npp)
+ : mInstance(npp ? static_cast<nsNPAPIPluginInstance*>(npp->ndata)
+ : nullptr) {
+ Init();
+}
+
+PluginDestructionGuard::~PluginDestructionGuard() {
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
+
+ this->remove();
+
+ if (mDelayedDestroy) {
+ // We've attempted to destroy the plugin instance we're holding on
+ // to while we were guarding it. Do the actual destroy now, off of
+ // a runnable.
+ RefPtr<nsPluginDestroyRunnable> evt =
+ new nsPluginDestroyRunnable(mInstance);
+
+ NS_DispatchToMainThread(evt);
+ }
+}
+
+// static
+bool PluginDestructionGuard::DelayDestroy(nsNPAPIPluginInstance* aInstance) {
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
+ NS_ASSERTION(aInstance, "Uh, I need an instance!");
+
+ // Find the first guard on the stack and make it do a delayed
+ // destroy upon destruction.
+
+ for (auto g : sList) {
+ if (g->mInstance == aInstance) {
+ g->mDelayedDestroy = true;
+
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/dom/plugins/base/nsPluginHost.h b/dom/plugins/base/nsPluginHost.h
new file mode 100644
index 0000000000..e5b98b5705
--- /dev/null
+++ b/dom/plugins/base/nsPluginHost.h
@@ -0,0 +1,391 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsPluginHost_h_
+#define nsPluginHost_h_
+
+#include "mozilla/LinkedList.h"
+#include "mozilla/StaticPtr.h"
+
+#include "nsIPluginHost.h"
+#include "nsIObserver.h"
+#include "nsCOMPtr.h"
+#include "prlink.h"
+#include "nsIPluginTag.h"
+#include "nsPluginsDir.h"
+#include "nsWeakReference.h"
+#include "MainThreadUtils.h"
+#include "nsTArray.h"
+#include "nsINamed.h"
+#include "nsTObserverArray.h"
+#include "nsITimer.h"
+#include "nsPluginTags.h"
+#include "nsIEffectiveTLDService.h"
+#include "nsIIDNService.h"
+#include "nsCRT.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
+#include "mozilla/plugins/PluginTypes.h"
+
+#ifdef XP_WIN
+# include <minwindef.h>
+# include "nsIWindowsRegKey.h"
+#endif
+
+namespace mozilla {
+namespace plugins {
+class BlocklistPromiseHandler;
+} // namespace plugins
+namespace dom {
+class ContentParent;
+} // namespace dom
+} // namespace mozilla
+
+class nsNPAPIPlugin;
+class nsIFile;
+class nsIChannel;
+class nsPluginNativeWindow;
+class nsObjectLoadingContent;
+class nsPluginInstanceOwner;
+class nsPluginUnloadRunnable;
+class nsNPAPIPluginInstance;
+class nsNPAPIPluginStreamListener;
+class nsIPluginInstanceOwner;
+class nsIInputStream;
+class nsIStreamListener;
+#ifndef npapi_h_
+struct _NPP;
+typedef _NPP* NPP;
+#endif
+
+class nsPluginHost final : public nsIPluginHost,
+ public nsIObserver,
+ public nsITimerCallback,
+ public nsSupportsWeakReference,
+ public nsINamed {
+ friend class nsPluginTag;
+ friend class nsFakePluginTag;
+ virtual ~nsPluginHost();
+
+ public:
+ nsPluginHost();
+
+ static already_AddRefed<nsPluginHost> GetInst();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPLUGINHOST
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSINAMED
+
+ // Acts like a bitfield
+ enum PluginFilter {
+ eExcludeNone = nsIPluginHost::EXCLUDE_NONE,
+ eExcludeDisabled = nsIPluginHost::EXCLUDE_DISABLED,
+ eExcludeFake = nsIPluginHost::EXCLUDE_FAKE
+ };
+ // FIXME-jsplugins comment about fake
+ bool HavePluginForType(const nsACString& aMimeType,
+ PluginFilter aFilter = eExcludeDisabled);
+
+ // FIXME-jsplugins what if fake has different extensions
+ bool HavePluginForExtension(const nsACString& aExtension,
+ /* out */ nsACString& aMimeType,
+ PluginFilter aFilter = eExcludeDisabled);
+
+ void GetPlugins(nsTArray<nsCOMPtr<nsIInternalPluginTag>>& aPluginArray,
+ bool aIncludeDisabled = false);
+
+ nsresult GetURL(nsISupports* pluginInst, const char* url, const char* target,
+ nsNPAPIPluginStreamListener* streamListener,
+ const char* altHost, const char* referrer,
+ bool forceJSEnabled);
+ nsresult PostURL(nsISupports* pluginInst, const char* url,
+ uint32_t postDataLen, const char* postData,
+ const char* target,
+ nsNPAPIPluginStreamListener* streamListener,
+ const char* altHost, const char* referrer,
+ bool forceJSEnabled, uint32_t postHeadersLength,
+ const char* postHeaders);
+
+ nsresult UserAgent(const char** retstring);
+ nsresult ParsePostBufferToFixHeaders(const char* inPostData,
+ uint32_t inPostDataLen,
+ char** outPostData,
+ uint32_t* outPostDataLen);
+ nsresult NewPluginNativeWindow(nsPluginNativeWindow** aPluginNativeWindow);
+
+ void AddIdleTimeTarget(nsIPluginInstanceOwner* objectFrame, bool isVisible);
+ void RemoveIdleTimeTarget(nsIPluginInstanceOwner* objectFrame);
+
+ nsresult GetPluginName(nsNPAPIPluginInstance* aPluginInstance,
+ const char** aPluginName);
+ nsresult StopPluginInstance(nsNPAPIPluginInstance* aInstance);
+ nsresult GetPluginTagForInstance(nsNPAPIPluginInstance* aPluginInstance,
+ nsIPluginTag** aPluginTag);
+
+ nsresult NewPluginURLStream(const nsString& aURL,
+ nsNPAPIPluginInstance* aInstance,
+ nsNPAPIPluginStreamListener* aListener,
+ nsIInputStream* aPostStream = nullptr,
+ const char* aHeadersData = nullptr,
+ uint32_t aHeadersDataLen = 0);
+
+ nsresult GetURLWithHeaders(
+ nsNPAPIPluginInstance* pluginInst, const char* url,
+ const char* target = nullptr,
+ nsNPAPIPluginStreamListener* streamListener = nullptr,
+ const char* altHost = nullptr, const char* referrer = nullptr,
+ bool forceJSEnabled = false, uint32_t getHeadersLength = 0,
+ const char* getHeaders = nullptr);
+
+ nsresult AddHeadersToChannel(const char* aHeadersData,
+ uint32_t aHeadersDataLen,
+ nsIChannel* aGenericChannel);
+
+ // Helper that checks if a type is whitelisted in plugin.allowed_types.
+ // Always returns true if plugin.allowed_types is not set
+ static bool IsTypeWhitelisted(const char* aType);
+
+ /**
+ * Returns true if a plugin can be used to load the requested MIME type. Used
+ * for short circuiting before sending things to plugin code.
+ */
+ static bool CanUsePluginForMIMEType(const nsACString& aMIMEType);
+
+ // checks whether aType is a type we recognize for potential special handling
+ enum SpecialType {
+ eSpecialType_None,
+ // Needed to whitelist for async init support
+ eSpecialType_Test,
+ // Informs some decisions about OOP and quirks
+ eSpecialType_Flash
+ };
+ static SpecialType GetSpecialType(const nsACString& aMIMEType);
+
+ static nsresult PostPluginUnloadEvent(PRLibrary* aLibrary);
+
+ void PluginCrashed(nsNPAPIPlugin* aPlugin, const nsAString& aPluginDumpID,
+ const nsACString& aAdditionalMinidumps);
+
+ nsNPAPIPluginInstance* FindInstance(const char* mimetype);
+ nsNPAPIPluginInstance* FindOldestStoppedInstance();
+ uint32_t StoppedInstanceCount();
+
+ nsTArray<RefPtr<nsNPAPIPluginInstance>>* InstanceArray();
+
+ // Return the tag for |aLibrary| if found, nullptr if not.
+ nsPluginTag* FindTagForLibrary(PRLibrary* aLibrary);
+
+ // The last argument should be false if we already have an in-flight stream
+ // and don't need to set up a new stream.
+ nsresult InstantiatePluginInstance(const nsACString& aMimeType, nsIURI* aURL,
+ nsObjectLoadingContent* aContent,
+ nsPluginInstanceOwner** aOwner);
+
+ // Does not accept nullptr and should never fail.
+ nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin);
+
+ nsPluginTag* PluginWithId(uint32_t aId);
+
+ nsresult GetPlugin(const nsACString& aMimeType, nsNPAPIPlugin** aPlugin);
+ nsresult GetPluginForContentProcess(uint32_t aPluginId,
+ nsNPAPIPlugin** aPlugin);
+ void NotifyContentModuleDestroyed(uint32_t aPluginId);
+
+ nsresult NewPluginStreamListener(nsIURI* aURL,
+ nsNPAPIPluginInstance* aInstance,
+ nsIStreamListener** aStreamListener);
+
+ void CreateWidget(nsPluginInstanceOwner* aOwner);
+
+ nsresult EnumerateSiteData(const nsACString& domain,
+ const nsTArray<nsCString>& sites,
+ nsTArray<nsCString>& result, bool firstMatchOnly);
+
+ nsresult UpdateCachedSerializablePluginList();
+ nsresult SendPluginsToContent(mozilla::dom::ContentParent* parent);
+ nsresult SetPluginsInContent(
+ uint32_t aPluginEpoch, nsTArray<mozilla::plugins::PluginTag>& aPlugins,
+ nsTArray<mozilla::plugins::FakePluginTag>& aFakePlugins);
+
+ void UpdatePluginBlocklistState(nsPluginTag* aPluginTag,
+ bool aShouldSoftblock = false);
+
+ private:
+ nsresult LoadPlugins();
+ nsresult UnloadPlugins();
+
+ nsresult SetUpPluginInstance(const nsACString& aMimeType, nsIURI* aURL,
+ nsPluginInstanceOwner* aOwner);
+
+ friend class nsPluginUnloadRunnable;
+ friend class mozilla::plugins::BlocklistPromiseHandler;
+
+ void DestroyRunningInstances(nsPluginTag* aPluginTag);
+
+ // Writes updated plugins settings to disk and unloads the plugin
+ // if it is now disabled. Should only be called by the plugin tag in question
+ void UpdatePluginInfo(nsPluginTag* aPluginTag);
+
+ nsresult TrySetUpPluginInstance(const nsACString& aMimeType, nsIURI* aURL,
+ nsPluginInstanceOwner* aOwner);
+
+ // FIXME-jsplugins comment here about when things may be fake
+ nsPluginTag* FindPreferredPlugin(const nsTArray<nsPluginTag*>& matches);
+
+ // Find a plugin for the given type. If aIncludeFake is true a fake plugin
+ // will be preferred if one exists; otherwise a fake plugin will never be
+ // returned. If aCheckEnabled is false, disabled plugins can be returned.
+ nsIInternalPluginTag* FindPluginForType(const nsACString& aMimeType,
+ bool aIncludeFake,
+ bool aCheckEnabled);
+
+ // Find specifically a fake plugin for the given type. If aCheckEnabled is
+ // false, disabled plugins can be returned.
+ nsFakePluginTag* FindFakePluginForType(const nsACString& aMimeType,
+ bool aCheckEnabled);
+
+ // Find specifically a fake plugin for the given extension. If aCheckEnabled
+ // is false, disabled plugins can be returned. aMimeType will be filled in
+ // with the MIME type the plugin is registered for.
+ nsFakePluginTag* FindFakePluginForExtension(const nsACString& aExtension,
+ /* out */ nsACString& aMimeType,
+ bool aCheckEnabled);
+
+ // Find specifically a native (NPAPI) plugin for the given type. If
+ // aCheckEnabled is false, disabled plugins can be returned.
+ nsPluginTag* FindNativePluginForType(const nsACString& aMimeType,
+ bool aCheckEnabled);
+
+ // Find specifically a native (NPAPI) plugin for the given extension. If
+ // aCheckEnabled is false, disabled plugins can be returned. aMimeType will
+ // be filled in with the MIME type the plugin is registered for.
+ nsPluginTag* FindNativePluginForExtension(const nsACString& aExtension,
+ /* out */ nsACString& aMimeType,
+ bool aCheckEnabled);
+
+ nsresult FindStoppedPluginForURL(nsIURI* aURL,
+ nsIPluginInstanceOwner* aOwner);
+
+ nsresult BroadcastPluginsToContent();
+
+ // FIXME revisit, no ns prefix
+ // Registers or unregisters the given mime type with the category manager
+ enum nsRegisterType {
+ ePluginRegister,
+ ePluginUnregister,
+ // Checks if this type should still be registered first
+ ePluginMaybeUnregister
+ };
+ void RegisterWithCategoryManager(const nsCString& aMimeType,
+ nsRegisterType aType);
+
+ void AddPluginTag(nsPluginTag* aPluginTag);
+
+ nsresult EnsurePluginLoaded(nsPluginTag* aPluginTag);
+
+ bool IsRunningPlugin(nsPluginTag* aPluginTag);
+
+ // Checks to see if a tag object is in our list of live tags.
+ bool IsLiveTag(nsIPluginTag* tag);
+
+ // Checks our list of live tags for an equivalent tag.
+ nsPluginTag* HaveSamePlugin(const nsPluginTag* aPluginTag);
+
+ void OnPluginInstanceDestroyed(nsPluginTag* aPluginTag);
+
+ // To be used by the chrome process whenever the set of plugins changes.
+ void IncrementChromeEpoch();
+
+ // To be used by the chrome process; returns the current epoch.
+ uint32_t ChromeEpoch();
+
+ // To be used by the content process to get/set the last observed epoch value
+ // from the chrome process.
+ uint32_t ChromeEpochForContent();
+ void SetChromeEpochForContent(uint32_t aEpoch);
+
+ void UpdateInMemoryPluginInfo(nsPluginTag* aPluginTag);
+
+ void ClearNonRunningPlugins();
+ nsresult ActuallyReloadPlugins();
+
+ void FindingFinished();
+
+ RefPtr<nsPluginTag> mPlugins;
+
+ nsTArray<RefPtr<nsFakePluginTag>> mFakePlugins;
+
+ AutoTArray<mozilla::plugins::PluginTag, 1> mSerializablePlugins;
+ nsTArray<mozilla::plugins::FakePluginTag> mSerializableFakePlugins;
+
+ bool mPluginsLoaded;
+
+ // set by pref plugin.override_internal_types
+ bool mOverrideInternalTypes;
+
+ // set by pref plugin.disable
+ bool mPluginsDisabled;
+
+ // Any instances in this array will have valid plugin objects via GetPlugin().
+ // When removing an instance it might not die - be sure to null out it's
+ // plugin.
+ nsTArray<RefPtr<nsNPAPIPluginInstance>> mInstances;
+
+ // An nsIFile for the pluginreg.dat file in the profile.
+#ifdef XP_WIN
+ // In order to reload plugins when they change, we watch the registry via
+ // this object.
+ nsCOMPtr<nsIWindowsRegKey> mRegKeyHKLM;
+ nsCOMPtr<nsIWindowsRegKey> mRegKeyHKCU;
+#endif
+
+ nsCOMPtr<nsIEffectiveTLDService> mTLDService;
+ nsCOMPtr<nsIIDNService> mIDNService;
+
+ // Helpers for ClearSiteData and SiteHasData.
+ nsresult NormalizeHostname(nsCString& host);
+
+ nsWeakPtr mCurrentDocument; // weak reference, we use it to id document only
+
+ // This epoch increases each time we load the list of plugins from disk.
+ // In the chrome process, this stores the actual epoch.
+ // In the content process, this stores the last epoch value observed
+ // when reading plugins from chrome.
+ uint32_t mPluginEpoch;
+
+ static nsIFile* sPluginTempDir;
+
+ // We need to hold a global ptr to ourselves because we register for
+ // two different CIDs for some reason...
+ static mozilla::StaticRefPtr<nsPluginHost> sInst;
+};
+
+class PluginDestructionGuard
+ : public mozilla::LinkedListElement<PluginDestructionGuard> {
+ public:
+ explicit PluginDestructionGuard(nsNPAPIPluginInstance* aInstance);
+ explicit PluginDestructionGuard(NPP npp);
+
+ ~PluginDestructionGuard();
+
+ static bool DelayDestroy(nsNPAPIPluginInstance* aInstance);
+
+ protected:
+ void Init() {
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
+
+ mDelayedDestroy = false;
+
+ sList.insertBack(this);
+ }
+
+ RefPtr<nsNPAPIPluginInstance> mInstance;
+ bool mDelayedDestroy;
+
+ static mozilla::LinkedList<PluginDestructionGuard> sList;
+};
+
+#endif // nsPluginHost_h_
diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp
new file mode 100644
index 0000000000..a1a7a2ab25
--- /dev/null
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -0,0 +1,3164 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+/* 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/. */
+
+#ifdef MOZ_X11
+# include <cairo-xlib.h>
+# include "gfxXlibSurface.h"
+/* X headers suck */
+enum { XKeyPress = KeyPress };
+# include "mozilla/X11Util.h"
+using mozilla::DefaultXDisplay;
+#endif
+
+#include "nsPluginInstanceOwner.h"
+
+#include "gfxUtils.h"
+#include "nsIRunnable.h"
+#include "nsContentUtils.h"
+#include "nsRect.h"
+#include "nsSize.h"
+#include "nsDisplayList.h"
+#include "ImageLayers.h"
+#include "GLImages.h"
+#include "nsPluginFrame.h"
+#include "nsIPluginDocument.h"
+#include "nsIStringStream.h"
+#include "nsNetUtil.h"
+#include "mozilla/Preferences.h"
+#include "nsLayoutUtils.h"
+#include "nsIPluginWidget.h"
+#include "nsViewManager.h"
+#include "nsIAppShell.h"
+#include "nsIObjectLoadingContent.h"
+#include "nsObjectLoadingContent.h"
+#include "nsAttrName.h"
+#include "nsIFocusManager.h"
+#include "nsFocusManager.h"
+#include "nsIProtocolHandler.h"
+#include "nsIScrollableFrame.h"
+#include "nsIDocShell.h"
+#include "ImageContainer.h"
+#include "GLContext.h"
+#include "nsIContentInlines.h"
+#include "mozilla/MiscEvents.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/dom/DocumentInlines.h"
+#include "mozilla/dom/DragEvent.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/HTMLObjectElementBinding.h"
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/dom/WheelEventBinding.h"
+#include "nsFrameSelection.h"
+#include "PuppetWidget.h"
+#include "nsPIWindowRoot.h"
+#include "mozilla/IMEStateManager.h"
+#include "mozilla/TextComposition.h"
+#include "mozilla/AutoRestore.h"
+
+#include "nsContentCID.h"
+#include "nsWidgetsCID.h"
+static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+
+#ifdef XP_WIN
+# include <wtypes.h>
+# include <winuser.h>
+# include "mozilla/widget/WinMessages.h"
+#endif // #ifdef XP_WIN
+
+#ifdef MOZ_WIDGET_GTK
+# include <gdk/gdk.h>
+# include <gtk/gtk.h>
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::layers;
+
+// special class for handeling DOM context menu events because for
+// some reason it starves other mouse events if implemented on the
+// same class
+class nsPluginDOMContextMenuListener : public nsIDOMEventListener {
+ virtual ~nsPluginDOMContextMenuListener();
+
+ public:
+ explicit nsPluginDOMContextMenuListener(nsIContent* aContent);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMEVENTLISTENER
+
+ void Destroy(nsIContent* aContent);
+
+ nsEventStatus ProcessEvent(const WidgetGUIEvent& anEvent) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+};
+
+class AsyncPaintWaitEvent : public Runnable {
+ public:
+ AsyncPaintWaitEvent(nsIContent* aContent, bool aFinished)
+ : Runnable("AsyncPaintWaitEvent"),
+ mContent(aContent),
+ mFinished(aFinished) {}
+
+ NS_IMETHOD Run() override {
+ nsContentUtils::DispatchEventOnlyToChrome(
+ mContent->OwnerDoc(), mContent,
+ mFinished ? u"MozPaintWaitFinished"_ns : u"MozPaintWait"_ns,
+ CanBubble::eYes, Cancelable::eYes);
+ return NS_OK;
+ }
+
+ private:
+ nsCOMPtr<nsIContent> mContent;
+ bool mFinished;
+};
+
+void nsPluginInstanceOwner::NotifyPaintWaiter(nsDisplayListBuilder* aBuilder) {
+ // This is notification for reftests about async plugin paint start
+ if (!mWaitingForPaint && !IsUpToDate() &&
+ aBuilder->ShouldSyncDecodeImages()) {
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ nsCOMPtr<nsIRunnable> event = new AsyncPaintWaitEvent(content, false);
+ // Run this event as soon as it's safe to do so, since listeners need to
+ // receive it immediately
+ nsContentUtils::AddScriptRunner(event);
+ mWaitingForPaint = true;
+ }
+}
+
+bool nsPluginInstanceOwner::NeedsScrollImageLayer() {
+#if defined(XP_WIN)
+ // If this is a windowed plugin and we're doing layout in the content
+ // process, force the creation of an image layer for the plugin. We'll
+ // paint to this when scrolling.
+ return XRE_IsContentProcess() && mPluginWindow &&
+ mPluginWindow->type == NPWindowTypeWindow;
+#else
+ return false;
+#endif
+}
+
+already_AddRefed<ImageContainer> nsPluginInstanceOwner::GetImageContainer() {
+ if (!mInstance) return nullptr;
+
+ RefPtr<ImageContainer> container;
+
+ if (NeedsScrollImageLayer()) {
+ // windowed plugin under e10s
+#if defined(XP_WIN)
+ mInstance->GetScrollCaptureContainer(getter_AddRefs(container));
+#endif
+ } else {
+ // async windowless rendering
+ mInstance->GetImageContainer(getter_AddRefs(container));
+ }
+
+ return container.forget();
+}
+
+void nsPluginInstanceOwner::DidComposite() {
+ if (mInstance) {
+ mInstance->DidComposite();
+ }
+}
+
+void nsPluginInstanceOwner::SetBackgroundUnknown() {
+ if (mInstance) {
+ mInstance->SetBackgroundUnknown();
+ }
+}
+
+already_AddRefed<mozilla::gfx::DrawTarget>
+nsPluginInstanceOwner::BeginUpdateBackground(const nsIntRect& aRect) {
+ nsIntRect rect = aRect;
+ RefPtr<DrawTarget> dt;
+ if (mInstance && NS_SUCCEEDED(mInstance->BeginUpdateBackground(
+ &rect, getter_AddRefs(dt)))) {
+ return dt.forget();
+ }
+ return nullptr;
+}
+
+void nsPluginInstanceOwner::EndUpdateBackground(const nsIntRect& aRect) {
+ nsIntRect rect = aRect;
+ if (mInstance) {
+ mInstance->EndUpdateBackground(&rect);
+ }
+}
+
+bool nsPluginInstanceOwner::UseAsyncRendering() {
+#ifdef XP_MACOSX
+ if (mUseAsyncRendering) {
+ return true;
+ }
+#endif
+
+ bool isOOP;
+ bool result =
+ (mInstance && NS_SUCCEEDED(mInstance->GetIsOOP(&isOOP)) && isOOP
+#ifndef XP_MACOSX
+ && (!mPluginWindow || mPluginWindow->type == NPWindowTypeDrawable)
+#endif
+ );
+
+#ifdef XP_MACOSX
+ if (result) {
+ mUseAsyncRendering = true;
+ }
+#endif
+
+ return result;
+}
+
+nsIntSize nsPluginInstanceOwner::GetCurrentImageSize() {
+ nsIntSize size(0, 0);
+ if (mInstance) {
+ mInstance->GetImageSize(&size);
+ }
+ return size;
+}
+
+nsPluginInstanceOwner::nsPluginInstanceOwner()
+ : mPluginWindow(nullptr), mLastEventloopNestingLevel(0) {
+ // create nsPluginNativeWindow object, it is derived from NPWindow
+ // struct and allows to manipulate native window procedure
+ nsCOMPtr<nsIPluginHost> pluginHostCOM =
+ do_GetService(MOZ_PLUGIN_HOST_CONTRACTID);
+ mPluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
+ if (mPluginHost) mPluginHost->NewPluginNativeWindow(&mPluginWindow);
+
+ mPluginFrame = nullptr;
+ mWidgetCreationComplete = false;
+#ifdef XP_MACOSX
+ mSentInitialTopLevelWindowEvent = false;
+ mLastWindowIsActive = false;
+ mLastContentFocused = false;
+ mLastScaleFactor = 1.0;
+ mShouldBlurOnActivate = false;
+#endif
+ mLastCSSZoomFactor = 1.0;
+ mContentFocused = false;
+ mWidgetVisible = true;
+ mPluginWindowVisible = false;
+ mPluginDocumentActiveState = true;
+ mLastMouseDownButtonType = -1;
+
+#ifdef XP_MACOSX
+# ifndef NP_NO_CARBON
+ // We don't support Carbon, but it is still the default model for i386 NPAPI.
+ mEventModel = NPEventModelCarbon;
+# else
+ mEventModel = NPEventModelCocoa;
+# endif
+ mUseAsyncRendering = false;
+#endif
+
+ mWaitingForPaint = false;
+
+#ifdef XP_WIN
+ mGotCompositionData = false;
+ mSentStartComposition = false;
+ mPluginDidNotHandleIMEComposition = false;
+ // 3 is the Windows default for these values.
+ mWheelScrollLines = 3;
+ mWheelScrollChars = 3;
+#endif
+}
+
+nsPluginInstanceOwner::~nsPluginInstanceOwner() {
+ if (mWaitingForPaint) {
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (content) {
+ // We don't care when the event is dispatched as long as it's "soon",
+ // since whoever needs it will be waiting for it.
+ nsCOMPtr<nsIRunnable> event = new AsyncPaintWaitEvent(content, true);
+ NS_DispatchToMainThread(event);
+ }
+ }
+
+ mPluginFrame = nullptr;
+
+ PLUG_DeletePluginNativeWindow(mPluginWindow);
+ mPluginWindow = nullptr;
+
+ if (mInstance) {
+ mInstance->SetOwner(nullptr);
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsPluginInstanceOwner, nsIPluginInstanceOwner,
+ nsIDOMEventListener, nsIPrivacyTransitionObserver,
+ nsIKeyEventInPluginCallback, nsISupportsWeakReference)
+
+nsresult nsPluginInstanceOwner::SetInstance(nsNPAPIPluginInstance* aInstance) {
+ NS_ASSERTION(!mInstance || !aInstance,
+ "mInstance should only be set or unset!");
+
+ // If we're going to null out mInstance after use, be sure to call
+ // mInstance->SetOwner(nullptr) here, since it now won't be called
+ // from our destructor. This fixes bug 613376.
+ if (mInstance && !aInstance) {
+ mInstance->SetOwner(nullptr);
+ }
+
+ mInstance = aInstance;
+
+ nsCOMPtr<Document> doc;
+ GetDocument(getter_AddRefs(doc));
+ if (doc) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = doc->GetWindow()) {
+ nsCOMPtr<nsIDocShell> docShell = domWindow->GetDocShell();
+ if (docShell) docShell->AddWeakPrivacyTransitionObserver(this);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::GetWindow(NPWindow*& aWindow) {
+ NS_ASSERTION(mPluginWindow,
+ "the plugin window object being returned is null");
+ aWindow = mPluginWindow;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::GetMode(int32_t* aMode) {
+ nsCOMPtr<Document> doc;
+ nsresult rv = GetDocument(getter_AddRefs(doc));
+ nsCOMPtr<nsIPluginDocument> pDoc(do_QueryInterface(doc));
+
+ if (pDoc) {
+ *aMode = NP_FULL;
+ } else {
+ *aMode = NP_EMBED;
+ }
+
+ return rv;
+}
+
+void nsPluginInstanceOwner::GetAttributes(
+ nsTArray<MozPluginParameter>& attributes) {
+ nsCOMPtr<nsIObjectLoadingContent> content = do_QueryReferent(mContent);
+ nsObjectLoadingContent* loadingContent =
+ static_cast<nsObjectLoadingContent*>(content.get());
+
+ loadingContent->GetPluginAttributes(attributes);
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::GetDOMElement(Element** result) {
+ return CallQueryReferent(mContent.get(), result);
+}
+
+nsNPAPIPluginInstance* nsPluginInstanceOwner::GetInstance() {
+ return mInstance;
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::GetURL(
+ const char* aURL, const char* aTarget, nsIInputStream* aPostStream,
+ void* aHeadersData, uint32_t aHeadersDataLen, bool aDoCheckLoadURIChecks) {
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (!content) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (content->IsEditable()) {
+ return NS_OK;
+ }
+
+ Document* doc = content->GetComposedDoc();
+ if (!doc) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPresContext* presContext = doc->GetPresContext();
+ if (!presContext) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // the container of the pres context will give us the link handler
+ nsCOMPtr<nsIDocShell> container = presContext->GetDocShell();
+ NS_ENSURE_TRUE(container, NS_ERROR_FAILURE);
+
+ nsAutoString unitarget;
+ if ((0 == PL_strcmp(aTarget, "newwindow")) ||
+ (0 == PL_strcmp(aTarget, "_new"))) {
+ unitarget.AssignLiteral("_blank");
+ } else if (0 == PL_strcmp(aTarget, "_current")) {
+ unitarget.AssignLiteral("_self");
+ } else {
+ unitarget.AssignASCII(aTarget); // XXX could this be nonascii?
+ }
+
+ // Create an absolute URL
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, GetBaseURI());
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIInputStream> headersDataStream;
+ if (aPostStream && aHeadersData) {
+ if (!aHeadersDataLen) return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsIStringInputStream> sis =
+ do_CreateInstance("@mozilla.org/io/string-input-stream;1");
+ if (!sis) return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = sis->SetData((char*)aHeadersData, aHeadersDataLen);
+ NS_ENSURE_SUCCESS(rv, rv);
+ headersDataStream = sis;
+ }
+
+ int32_t blockPopups =
+ Preferences::GetInt("privacy.popups.disable_from_plugins");
+ AutoPopupStatePusher popupStatePusher(
+ (PopupBlocker::PopupControlState)blockPopups);
+
+ // if security checks (in particular CheckLoadURIWithPrincipal) needs
+ // to be skipped we are creating a contentPrincipal from the target URI
+ // to make sure that security checks succeed.
+ // Please note that we do not want to fall back to using the
+ // systemPrincipal, because that would also bypass ContentPolicy checks
+ // which should still be enforced.
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ if (!aDoCheckLoadURIChecks) {
+ mozilla::OriginAttributes attrs =
+ BasePrincipal::Cast(content->NodePrincipal())->OriginAttributesRef();
+ triggeringPrincipal = BasePrincipal::CreateContentPrincipal(uri, attrs);
+ } else {
+ bool useParentContentPrincipal = false;
+ nsCOMPtr<nsINetUtil> netUtil = do_GetNetUtil();
+ // For protocols loadable by anyone, it doesn't matter what principal
+ // we use for the security check. However, for external URIs, we check
+ // whether the browsing context in which they load can be accessed by
+ // the triggering principal that is doing the loading, to avoid certain
+ // types of spoofing attacks. In this case, the load would never be
+ // allowed with the newly minted null principal, when all the plugin is
+ // trying to do is load a URL in its own browsing context. So we use
+ // the content principal of the plugin's node in this case.
+ netUtil->ProtocolHasFlags(uri,
+ nsIProtocolHandler::URI_LOADABLE_BY_ANYONE |
+ nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
+ &useParentContentPrincipal);
+ if (useParentContentPrincipal) {
+ triggeringPrincipal = content->NodePrincipal();
+ } else {
+ triggeringPrincipal = NullPrincipal::CreateWithInheritedAttributes(
+ content->NodePrincipal());
+ }
+ }
+
+ nsCOMPtr<nsIContentSecurityPolicy> csp = content->GetCsp();
+
+ rv = nsDocShell::Cast(container)->OnLinkClick(
+ content, uri, unitarget, VoidString(), aPostStream, headersDataStream,
+ /* isUserTriggered */ false, /* isTrusted */ true, triggeringPrincipal,
+ csp);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::GetDocument(Document** aDocument) {
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (!aDocument || !content) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // XXX sXBL/XBL2 issue: current doc or owner doc?
+ // But keep in mind bug 322414 comment 33
+ NS_ADDREF(*aDocument = content->OwnerDoc());
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRect(NPRect* invalidRect) {
+ // If our object frame has gone away, we won't be able to determine
+ // up-to-date-ness, so just fire off the event.
+ if (mWaitingForPaint && (!mPluginFrame || IsUpToDate())) {
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ // We don't care when the event is dispatched as long as it's "soon",
+ // since whoever needs it will be waiting for it.
+ nsCOMPtr<nsIRunnable> event = new AsyncPaintWaitEvent(content, true);
+ NS_DispatchToMainThread(event);
+ mWaitingForPaint = false;
+ }
+
+ if (!mPluginFrame || !invalidRect || !mWidgetVisible) return NS_ERROR_FAILURE;
+
+#if defined(XP_MACOSX)
+ // Each time an asynchronously-drawing plugin sends a new surface to display,
+ // the image in the ImageContainer is updated and InvalidateRect is called.
+ RefPtr<ImageContainer> container;
+ mInstance->GetImageContainer(getter_AddRefs(container));
+#endif
+
+#ifndef XP_MACOSX
+ // Invalidate for windowed plugins needs to work.
+ if (mWidget) {
+ mWidget->Invalidate(
+ LayoutDeviceIntRect(invalidRect->left, invalidRect->top,
+ invalidRect->right - invalidRect->left,
+ invalidRect->bottom - invalidRect->top));
+ // Plugin instances also call invalidate when plugin windows are hidden
+ // during scrolling. In this case fall through so we invalidate the
+ // underlying layer.
+ if (!NeedsScrollImageLayer()) {
+ return NS_OK;
+ }
+ }
+#endif
+ nsIntRect rect(invalidRect->left, invalidRect->top,
+ invalidRect->right - invalidRect->left,
+ invalidRect->bottom - invalidRect->top);
+ // invalidRect is in "display pixels". In non-HiDPI modes "display pixels"
+ // are device pixels. But in HiDPI modes each display pixel corresponds
+ // to more than one device pixel.
+ double scaleFactor = 1.0;
+ GetContentsScaleFactor(&scaleFactor);
+ rect.ScaleRoundOut(scaleFactor);
+ mPluginFrame->InvalidateLayer(DisplayItemType::TYPE_PLUGIN, &rect);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRegion(NPRegion invalidRegion) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPluginInstanceOwner::RedrawPlugin() {
+ if (mPluginFrame) {
+ mPluginFrame->InvalidateLayer(DisplayItemType::TYPE_PLUGIN);
+ }
+ return NS_OK;
+}
+
+#if defined(XP_WIN)
+nsIWidget* nsPluginInstanceOwner::GetContainingWidgetIfOffset() {
+ MOZ_ASSERT(mPluginFrame, "Caller should have checked for null mPluginFrame.");
+
+ // This property is provided to allow a "windowless" plugin to determine the
+ // window it is drawing in, so it can translate mouse coordinates it receives
+ // directly from the operating system to coordinates relative to itself.
+
+ // The original code returns the document's window, which is OK if the window
+ // the "windowless" plugin is drawing into has the same origin as the
+ // document's window, but this is not the case for "windowless" plugins inside
+ // of scrolling DIVs etc
+
+ // To make sure "windowless" plugins always get the right origin for
+ // translating mouse coordinates, this code determines the window handle of
+ // the mozilla window containing the "windowless" plugin.
+
+ // Given that this HWND may not be that of the document's window, there is a
+ // slight risk of confusing a plugin that is using this HWND for illicit
+ // purposes, but since the documentation does not suggest this HWND IS that of
+ // the document window, rather that of the window the plugin is drawn in, this
+ // seems like a safe fix.
+
+ // we only attempt to get the nearest window if this really is a "windowless"
+ // plugin so as not to change any behaviour for the much more common windowed
+ // plugins, though why this method would even be being called for a windowed
+ // plugin escapes me.
+ if (!XRE_IsContentProcess() && mPluginWindow &&
+ mPluginWindow->type == NPWindowTypeDrawable) {
+ // it turns out that flash also uses this window for determining focus, and
+ // is currently unable to show a caret correctly if we return the enclosing
+ // window. Therefore for now we only return the enclosing window when there
+ // is an actual offset which would otherwise cause coordinates to be offset
+ // incorrectly. (i.e. if the enclosing window if offset from the document
+ // window)
+ //
+ // fixing both the caret and ability to interact issues for a windowless
+ // control in a non document aligned windw does not seem to be possible
+ // without a change to the flash plugin
+
+ nsIWidget* win = mPluginFrame->GetNearestWidget();
+ if (win) {
+ nsView* view = nsView::GetViewFor(win);
+ NS_ASSERTION(view, "No view for widget");
+ nsPoint offset = view->GetOffsetTo(nullptr);
+
+ if (offset.x || offset.y) {
+ // in the case the two windows are offset from eachother, we do go ahead
+ // and return the correct enclosing window so that mouse co-ordinates
+ // are not messed up.
+ return win;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+static already_AddRefed<nsIWidget> GetRootWidgetForPluginFrame(
+ const nsPluginFrame* aPluginFrame) {
+ MOZ_ASSERT(aPluginFrame);
+
+ nsViewManager* vm =
+ aPluginFrame->PresContext()->GetPresShell()->GetViewManager();
+ if (!vm) {
+ NS_WARNING("Could not find view manager for plugin frame.");
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIWidget> rootWidget;
+ vm->GetRootWidget(getter_AddRefs(rootWidget));
+ return rootWidget.forget();
+}
+#endif
+
+NS_IMETHODIMP nsPluginInstanceOwner::GetNetscapeWindow(void* value) {
+ if (!mPluginFrame) {
+ NS_WARNING("plugin owner has no owner in getting doc's window handle");
+ return NS_ERROR_FAILURE;
+ }
+
+#if defined(XP_WIN)
+ void** pvalue = (void**)value;
+ nsIWidget* offsetContainingWidget = GetContainingWidgetIfOffset();
+ if (offsetContainingWidget) {
+ *pvalue = (void*)offsetContainingWidget->GetNativeData(NS_NATIVE_WINDOW);
+ if (*pvalue) {
+ return NS_OK;
+ }
+ }
+
+ // simply return the topmost document window
+ nsCOMPtr<nsIWidget> widget = GetRootWidgetForPluginFrame(mPluginFrame);
+ if (widget) {
+ *pvalue = widget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW);
+ } else {
+ NS_ASSERTION(widget,
+ "couldn't get doc's widget in getting doc's window handle");
+ }
+
+ return NS_OK;
+#elif defined(MOZ_WIDGET_GTK) && defined(MOZ_X11)
+ // X11 window managers want the toplevel window for WM_TRANSIENT_FOR.
+ nsIWidget* win = mPluginFrame->GetNearestWidget();
+ if (!win) return NS_ERROR_FAILURE;
+ *static_cast<Window*>(value) =
+ (long unsigned int)win->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW);
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+#if defined(XP_WIN)
+void nsPluginInstanceOwner::SetWidgetWindowAsParent(HWND aWindowToAdopt) {
+ if (!mWidget) {
+ NS_ERROR("mWidget should exist before this gets called.");
+ return;
+ }
+
+ mWidget->SetNativeData(NS_NATIVE_CHILD_WINDOW,
+ reinterpret_cast<uintptr_t>(aWindowToAdopt));
+}
+
+nsresult nsPluginInstanceOwner::SetNetscapeWindowAsParent(HWND aWindowToAdopt) {
+ if (!mPluginFrame) {
+ NS_WARNING("Plugin owner has no plugin frame.");
+ return NS_ERROR_FAILURE;
+ }
+
+ // If there is a containing window that is offset then ask that to adopt.
+ nsIWidget* offsetWidget = GetContainingWidgetIfOffset();
+ if (offsetWidget) {
+ offsetWidget->SetNativeData(NS_NATIVE_CHILD_WINDOW,
+ reinterpret_cast<uintptr_t>(aWindowToAdopt));
+ return NS_OK;
+ }
+
+ // Otherwise ask the topmost document window to adopt.
+ nsCOMPtr<nsIWidget> rootWidget = GetRootWidgetForPluginFrame(mPluginFrame);
+ if (!rootWidget) {
+ NS_ASSERTION(rootWidget, "Couldn't get topmost document's widget.");
+ return NS_ERROR_FAILURE;
+ }
+
+ rootWidget->SetNativeData(NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW,
+ reinterpret_cast<uintptr_t>(aWindowToAdopt));
+ return NS_OK;
+}
+
+bool nsPluginInstanceOwner::GetCompositionString(uint32_t aType,
+ nsTArray<uint8_t>* aDist,
+ int32_t* aLength) {
+ // Mark pkugin calls ImmGetCompositionStringW correctly
+ mGotCompositionData = true;
+
+ RefPtr<TextComposition> composition = GetTextComposition();
+ if (NS_WARN_IF(!composition)) {
+ return false;
+ }
+
+ switch (aType) {
+ case GCS_COMPSTR: {
+ if (!composition->IsComposing()) {
+ *aLength = 0;
+ return true;
+ }
+
+ uint32_t len = composition->LastData().Length() * sizeof(char16_t);
+ if (len) {
+ aDist->SetLength(len);
+ memcpy(aDist->Elements(), composition->LastData().get(), len);
+ }
+ *aLength = len;
+ return true;
+ }
+
+ case GCS_RESULTSTR: {
+ if (composition->IsComposing()) {
+ *aLength = 0;
+ return true;
+ }
+
+ uint32_t len = composition->LastData().Length() * sizeof(char16_t);
+ if (len) {
+ aDist->SetLength(len);
+ memcpy(aDist->Elements(), composition->LastData().get(), len);
+ }
+ *aLength = len;
+ return true;
+ }
+
+ case GCS_CURSORPOS: {
+ *aLength = 0;
+ TextRangeArray* ranges = composition->GetLastRanges();
+ if (!ranges) {
+ return true;
+ }
+ *aLength = ranges->GetCaretPosition();
+ if (*aLength < 0) {
+ return false;
+ }
+ return true;
+ }
+
+ case GCS_COMPATTR: {
+ TextRangeArray* ranges = composition->GetLastRanges();
+ if (!ranges || ranges->IsEmpty()) {
+ *aLength = 0;
+ return true;
+ }
+
+ aDist->SetLength(composition->LastData().Length());
+ memset(aDist->Elements(), ATTR_INPUT, aDist->Length());
+
+ for (TextRange& range : *ranges) {
+ uint8_t type = ATTR_INPUT;
+ switch (range.mRangeType) {
+ case TextRangeType::eRawClause:
+ type = ATTR_INPUT;
+ break;
+ case TextRangeType::eSelectedRawClause:
+ type = ATTR_TARGET_NOTCONVERTED;
+ break;
+ case TextRangeType::eConvertedClause:
+ type = ATTR_CONVERTED;
+ break;
+ case TextRangeType::eSelectedClause:
+ type = ATTR_TARGET_CONVERTED;
+ break;
+ default:
+ continue;
+ }
+
+ size_t minLen = std::min<size_t>(range.mEndOffset, aDist->Length());
+ for (size_t i = range.mStartOffset; i < minLen; i++) {
+ (*aDist)[i] = type;
+ }
+ }
+ *aLength = aDist->Length();
+ return true;
+ }
+
+ case GCS_COMPCLAUSE: {
+ RefPtr<TextRangeArray> ranges = composition->GetLastRanges();
+ if (!ranges || ranges->IsEmpty()) {
+ aDist->SetLength(sizeof(uint32_t));
+ memset(aDist->Elements(), 0, sizeof(uint32_t));
+ *aLength = aDist->Length();
+ return true;
+ }
+ AutoTArray<uint32_t, 16> clauses;
+ clauses.AppendElement(0);
+ for (TextRange& range : *ranges) {
+ if (!range.IsClause()) {
+ continue;
+ }
+ clauses.AppendElement(range.mEndOffset);
+ }
+
+ aDist->SetLength(clauses.Length() * sizeof(uint32_t));
+ memcpy(aDist->Elements(), clauses.Elements(), aDist->Length());
+ *aLength = aDist->Length();
+ return true;
+ }
+
+ case GCS_RESULTREADSTR: {
+ // When returning error causes unexpected error, so we return 0 instead.
+ *aLength = 0;
+ return true;
+ }
+
+ case GCS_RESULTCLAUSE: {
+ // When returning error causes unexpected error, so we return 0 instead.
+ *aLength = 0;
+ return true;
+ }
+
+ default:
+ NS_WARNING(
+ nsPrintfCString(
+ "Unsupported type %x of ImmGetCompositionStringW hook", aType)
+ .get());
+ break;
+ }
+
+ return false;
+}
+
+bool nsPluginInstanceOwner::RequestCommitOrCancel(bool aCommitted) {
+ nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
+ if (!widget) {
+ widget = GetRootWidgetForPluginFrame(mPluginFrame);
+ if (NS_WARN_IF(!widget)) {
+ return false;
+ }
+ }
+
+ // Retrieve TextComposition for the widget with IMEStateManager instead of
+ // using GetTextComposition() because we cannot know whether the method
+ // failed due to no widget or no composition.
+ RefPtr<TextComposition> composition =
+ IMEStateManager::GetTextCompositionFor(widget);
+ if (!composition) {
+ // If there is composition, we should just ignore this request since
+ // the composition may have been committed after the plugin process
+ // sent this request.
+ return true;
+ }
+
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (content != composition->GetEventTargetNode()) {
+ // If the composition is handled in different node, that means that
+ // the composition for the plugin has gone and new composition has
+ // already started. So, request from the plugin should be ignored
+ // since user inputs different text now.
+ return true;
+ }
+
+ // If active composition is being handled in the plugin, let's request to
+ // commit/cancel the composition via both IMEStateManager and TextComposition
+ // for avoid breaking the status management of composition. I.e., don't
+ // call nsIWidget::NotifyIME() directly from here.
+ IMEStateManager::NotifyIME(aCommitted ? widget::REQUEST_TO_COMMIT_COMPOSITION
+ : widget::REQUEST_TO_CANCEL_COMPOSITION,
+ widget, composition->GetBrowserParent());
+ // FYI: This instance may have been destroyed. Be careful if you need to
+ // access members of this class.
+ return true;
+}
+
+#endif // #ifdef XP_WIN
+
+void nsPluginInstanceOwner::HandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData, bool aIsConsumed) {
+ if (NS_WARN_IF(!mInstance)) {
+ return;
+ }
+ DebugOnly<nsresult> rv =
+ mInstance->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HandledWindowedPluginKeyEvent fail");
+}
+
+void nsPluginInstanceOwner::OnWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData) {
+ if (NS_WARN_IF(!mPluginFrame)) {
+ // Notifies the plugin process of the key event being not consumed by us.
+ HandledWindowedPluginKeyEvent(aKeyEventData, false);
+ return;
+ }
+
+ nsCOMPtr<nsIWidget> widget = mPluginFrame->PresContext()->GetRootWidget();
+ if (NS_WARN_IF(!widget)) {
+ // Notifies the plugin process of the key event being not consumed by us.
+ HandledWindowedPluginKeyEvent(aKeyEventData, false);
+ return;
+ }
+
+ nsresult rv = widget->OnWindowedPluginKeyEvent(aKeyEventData, this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Notifies the plugin process of the key event being not consumed by us.
+ HandledWindowedPluginKeyEvent(aKeyEventData, false);
+ return;
+ }
+
+ // If the key event is posted to another process, we need to wait a call
+ // of HandledWindowedPluginKeyEvent(). So, nothing to do here in this case.
+ if (rv == NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY) {
+ return;
+ }
+
+ // Otherwise, the key event is handled synchronously. Let's notify the
+ // plugin process of the key event's result.
+ bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
+ HandledWindowedPluginKeyEvent(aKeyEventData, consumed);
+}
+
+NS_IMETHODIMP nsPluginInstanceOwner::SetEventModel(int32_t eventModel) {
+#ifdef XP_MACOSX
+ mEventModel = static_cast<NPEventModel>(eventModel);
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+#ifdef XP_MACOSX
+NPBool nsPluginInstanceOwner::ConvertPointPuppet(PuppetWidget* widget,
+ nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double* destX, double* destY,
+ NPCoordinateSpace destSpace) {
+ NS_ENSURE_TRUE(widget && widget->GetOwningBrowserChild() && pluginFrame,
+ false);
+ // Caller has to want a result.
+ NS_ENSURE_TRUE(destX || destY, false);
+
+ if (sourceSpace == destSpace) {
+ if (destX) {
+ *destX = sourceX;
+ }
+ if (destY) {
+ *destY = sourceY;
+ }
+ return true;
+ }
+
+ nsPresContext* presContext = pluginFrame->PresContext();
+ CSSToLayoutDeviceScale scaleFactor(
+ double(AppUnitsPerCSSPixel()) /
+ presContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
+
+ PuppetWidget* puppetWidget = static_cast<PuppetWidget*>(widget);
+ PuppetWidget* rootWidget =
+ static_cast<PuppetWidget*>(widget->GetTopLevelWidget());
+ if (!rootWidget) {
+ return false;
+ }
+ CSSIntPoint chromeSize =
+ CSSIntPoint::Truncate(rootWidget->GetChromeOffset() / scaleFactor);
+ nsIntSize intScreenDims = rootWidget->GetScreenDimensions();
+ CSSIntSize screenDims = CSSIntSize::Truncate(
+ LayoutDeviceIntSize::FromUnknownSize(intScreenDims) / scaleFactor);
+ int32_t screenH = screenDims.height;
+ CSSIntPoint windowPosition =
+ CSSIntPoint::Truncate(rootWidget->GetWindowPosition() / scaleFactor);
+
+ // Window size is tab size + chrome size.
+ LayoutDeviceIntRect tabContentBounds = puppetWidget->GetBounds();
+ tabContentBounds.ScaleInverseRoundOut(scaleFactor.scale);
+ int32_t windowH = tabContentBounds.height + int(chromeSize.y);
+
+ CSSIntPoint pluginPosition = pluginFrame->GetScreenRect().TopLeft();
+
+ // Convert (sourceX, sourceY) to 'real' (not PuppetWidget) screen space.
+ // In OSX, the Y-axis increases upward, which is the reverse of ours.
+ // We want OSX coordinates for window and screen so those equations are
+ // swapped.
+ CSSIntPoint sourcePoint = CSSIntPoint::Truncate(sourceX, sourceY);
+ CSSIntPoint screenPoint;
+ switch (sourceSpace) {
+ case NPCoordinateSpacePlugin:
+ screenPoint = sourcePoint + pluginPosition +
+ CSSIntPoint::Truncate(CSSPoint::FromAppUnits(
+ pluginFrame->GetContentRectRelativeToSelf().TopLeft()));
+ break;
+ case NPCoordinateSpaceWindow:
+ screenPoint =
+ CSSIntPoint(sourcePoint.x, windowH - sourcePoint.y) + windowPosition;
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ screenPoint = sourcePoint + windowPosition;
+ break;
+ case NPCoordinateSpaceScreen:
+ screenPoint = CSSIntPoint(sourcePoint.x, screenH - sourcePoint.y);
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ screenPoint = sourcePoint;
+ break;
+ default:
+ return false;
+ }
+
+ // Convert from screen to dest space.
+ CSSIntPoint destPoint;
+ switch (destSpace) {
+ case NPCoordinateSpacePlugin:
+ destPoint = screenPoint - pluginPosition -
+ CSSIntPoint::Truncate(CSSPoint::FromAppUnits(
+ pluginFrame->GetContentRectRelativeToSelf().TopLeft()));
+ break;
+ case NPCoordinateSpaceWindow:
+ destPoint = screenPoint - windowPosition;
+ destPoint.y = windowH - destPoint.y;
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ destPoint = screenPoint - windowPosition;
+ break;
+ case NPCoordinateSpaceScreen:
+ destPoint = CSSIntPoint(screenPoint.x, screenH - screenPoint.y);
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ destPoint = screenPoint;
+ break;
+ default:
+ return false;
+ }
+
+ if (destX) {
+ *destX = destPoint.x;
+ }
+ if (destY) {
+ *destY = destPoint.y;
+ }
+
+ return true;
+}
+
+NPBool nsPluginInstanceOwner::ConvertPointNoPuppet(
+ nsIWidget* widget, nsPluginFrame* pluginFrame, double sourceX,
+ double sourceY, NPCoordinateSpace sourceSpace, double* destX, double* destY,
+ NPCoordinateSpace destSpace) {
+ NS_ENSURE_TRUE(widget && pluginFrame, false);
+ // Caller has to want a result.
+ NS_ENSURE_TRUE(destX || destY, false);
+
+ if (sourceSpace == destSpace) {
+ if (destX) {
+ *destX = sourceX;
+ }
+ if (destY) {
+ *destY = sourceY;
+ }
+ return true;
+ }
+
+ nsPresContext* presContext = pluginFrame->PresContext();
+ double scaleFactor =
+ double(AppUnitsPerCSSPixel()) /
+ presContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
+
+ nsCOMPtr<nsIScreen> screen = widget->GetWidgetScreen();
+ if (!screen) {
+ return false;
+ }
+
+ int32_t screenX, screenY, screenWidth, screenHeight;
+ screen->GetRect(&screenX, &screenY, &screenWidth, &screenHeight);
+ screenHeight /= scaleFactor;
+
+ LayoutDeviceIntRect windowScreenBounds = widget->GetScreenBounds();
+ windowScreenBounds.ScaleInverseRoundOut(scaleFactor);
+ int32_t windowX = windowScreenBounds.x;
+ int32_t windowY = windowScreenBounds.y;
+ int32_t windowHeight = windowScreenBounds.height;
+
+ CSSIntRect pluginScreenRect = pluginFrame->GetScreenRect();
+
+ double screenXGecko, screenYGecko;
+ switch (sourceSpace) {
+ case NPCoordinateSpacePlugin:
+ screenXGecko = pluginScreenRect.x + sourceX;
+ screenYGecko = pluginScreenRect.y + sourceY;
+ break;
+ case NPCoordinateSpaceWindow:
+ screenXGecko = windowX + sourceX;
+ screenYGecko = windowY + (windowHeight - sourceY);
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ screenXGecko = windowX + sourceX;
+ screenYGecko = windowY + sourceY;
+ break;
+ case NPCoordinateSpaceScreen:
+ screenXGecko = sourceX;
+ screenYGecko = screenHeight - sourceY;
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ screenXGecko = sourceX;
+ screenYGecko = sourceY;
+ break;
+ default:
+ return false;
+ }
+
+ double destXCocoa, destYCocoa;
+ switch (destSpace) {
+ case NPCoordinateSpacePlugin:
+ destXCocoa = screenXGecko - pluginScreenRect.x;
+ destYCocoa = screenYGecko - pluginScreenRect.y;
+ break;
+ case NPCoordinateSpaceWindow:
+ destXCocoa = screenXGecko - windowX;
+ destYCocoa = windowHeight - (screenYGecko - windowY);
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ destXCocoa = screenXGecko - windowX;
+ destYCocoa = screenYGecko - windowY;
+ break;
+ case NPCoordinateSpaceScreen:
+ destXCocoa = screenXGecko;
+ destYCocoa = screenHeight - screenYGecko;
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ destXCocoa = screenXGecko;
+ destYCocoa = screenYGecko;
+ break;
+ default:
+ return false;
+ }
+
+ if (destX) {
+ *destX = destXCocoa;
+ }
+ if (destY) {
+ *destY = destYCocoa;
+ }
+
+ return true;
+}
+#endif // XP_MACOSX
+
+NPBool nsPluginInstanceOwner::ConvertPoint(double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double* destX, double* destY,
+ NPCoordinateSpace destSpace) {
+#ifdef XP_MACOSX
+ if (!mPluginFrame) {
+ return false;
+ }
+
+ MOZ_ASSERT(mPluginFrame->GetNearestWidget());
+
+ if (nsIWidget::UsePuppetWidgets()) {
+ return ConvertPointPuppet(
+ static_cast<PuppetWidget*>(mPluginFrame->GetNearestWidget()),
+ mPluginFrame, sourceX, sourceY, sourceSpace, destX, destY, destSpace);
+ }
+
+ return ConvertPointNoPuppet(mPluginFrame->GetNearestWidget(), mPluginFrame,
+ sourceX, sourceY, sourceSpace, destX, destY,
+ destSpace);
+#else
+ return false;
+#endif
+}
+
+NPError nsPluginInstanceOwner::InitAsyncSurface(NPSize* size,
+ NPImageFormat format,
+ void* initData,
+ NPAsyncSurface* surface) {
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+}
+
+NPError nsPluginInstanceOwner::FinalizeAsyncSurface(NPAsyncSurface*) {
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+}
+
+void nsPluginInstanceOwner::SetCurrentAsyncSurface(NPAsyncSurface*, NPRect*) {}
+
+NS_IMETHODIMP nsPluginInstanceOwner::GetTagType(nsPluginTagType* result) {
+ NS_ENSURE_ARG_POINTER(result);
+
+ *result = nsPluginTagType_Unknown;
+
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (content->IsHTMLElement(nsGkAtoms::embed))
+ *result = nsPluginTagType_Embed;
+ else if (content->IsHTMLElement(nsGkAtoms::object))
+ *result = nsPluginTagType_Object;
+
+ return NS_OK;
+}
+
+void nsPluginInstanceOwner::GetParameters(
+ nsTArray<MozPluginParameter>& parameters) {
+ nsCOMPtr<nsIObjectLoadingContent> content = do_QueryReferent(mContent);
+ nsObjectLoadingContent* loadingContent =
+ static_cast<nsObjectLoadingContent*>(content.get());
+
+ loadingContent->GetPluginParameters(parameters);
+}
+
+#ifdef XP_MACOSX
+
+static void InitializeNPCocoaEvent(NPCocoaEvent* event) {
+ memset(event, 0, sizeof(NPCocoaEvent));
+}
+
+NPDrawingModel nsPluginInstanceOwner::GetDrawingModel() {
+# ifndef NP_NO_QUICKDRAW
+ // We don't support the Quickdraw drawing model any more but it's still
+ // the default model for i386 per NPAPI.
+ NPDrawingModel drawingModel = NPDrawingModelQuickDraw;
+# else
+ NPDrawingModel drawingModel = NPDrawingModelCoreGraphics;
+# endif
+
+ if (!mInstance) return drawingModel;
+
+ mInstance->GetDrawingModel((int32_t*)&drawingModel);
+ return drawingModel;
+}
+
+bool nsPluginInstanceOwner::IsRemoteDrawingCoreAnimation() {
+ if (!mInstance) return false;
+
+ bool coreAnimation;
+ if (!NS_SUCCEEDED(mInstance->IsRemoteDrawingCoreAnimation(&coreAnimation)))
+ return false;
+
+ return coreAnimation;
+}
+
+NPEventModel nsPluginInstanceOwner::GetEventModel() { return mEventModel; }
+
+# define DEFAULT_REFRESH_RATE 20 // 50 FPS
+StaticRefPtr<nsITimer> nsPluginInstanceOwner::sCATimer;
+nsTArray<nsPluginInstanceOwner*>* nsPluginInstanceOwner::sCARefreshListeners =
+ nullptr;
+
+void nsPluginInstanceOwner::CARefresh(nsITimer* aTimer, void* aClosure) {
+ if (!sCARefreshListeners) {
+ return;
+ }
+ for (size_t i = 0; i < sCARefreshListeners->Length(); i++) {
+ nsPluginInstanceOwner* instanceOwner = (*sCARefreshListeners)[i];
+ NPWindow* window;
+ instanceOwner->GetWindow(window);
+ if (!window) {
+ continue;
+ }
+ NPRect r;
+ r.left = 0;
+ r.top = 0;
+ r.right = window->width;
+ r.bottom = window->height;
+ instanceOwner->InvalidateRect(&r);
+ }
+}
+
+void nsPluginInstanceOwner::AddToCARefreshTimer() {
+ if (!mInstance) {
+ return;
+ }
+
+ // Flash invokes InvalidateRect for us.
+ const char* mime = nullptr;
+ if (NS_SUCCEEDED(mInstance->GetMIMEType(&mime)) && mime &&
+ nsPluginHost::GetSpecialType(nsDependentCString(mime)) ==
+ nsPluginHost::eSpecialType_Flash) {
+ return;
+ }
+
+ if (!sCARefreshListeners) {
+ sCARefreshListeners = new nsTArray<nsPluginInstanceOwner*>();
+ }
+
+ if (sCARefreshListeners->Contains(this)) {
+ return;
+ }
+
+ sCARefreshListeners->AppendElement(this);
+
+ if (sCARefreshListeners->Length() == 1) {
+ nsCOMPtr<nsITimer> timer;
+ NS_NewTimerWithFuncCallback(
+ getter_AddRefs(timer), CARefresh, nullptr, DEFAULT_REFRESH_RATE,
+ nsITimer::TYPE_REPEATING_SLACK, "nsPluginInstanceOwner::CARefresh");
+ sCATimer = timer.forget();
+ }
+}
+
+void nsPluginInstanceOwner::RemoveFromCARefreshTimer() {
+ if (!sCARefreshListeners || sCARefreshListeners->Contains(this) == false) {
+ return;
+ }
+
+ sCARefreshListeners->RemoveElement(this);
+
+ if (sCARefreshListeners->Length() == 0) {
+ if (sCATimer) {
+ sCATimer->Cancel();
+ sCATimer = nullptr;
+ }
+ delete sCARefreshListeners;
+ sCARefreshListeners = nullptr;
+ }
+}
+
+void nsPluginInstanceOwner::SetPluginPort() {
+ void* pluginPort = GetPluginPort();
+ if (!pluginPort || !mPluginWindow) return;
+ mPluginWindow->window = pluginPort;
+}
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+nsresult nsPluginInstanceOwner::ContentsScaleFactorChanged(
+ double aContentsScaleFactor) {
+ if (!mInstance) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ return mInstance->ContentsScaleFactorChanged(aContentsScaleFactor);
+}
+#endif
+
+// static
+uint32_t nsPluginInstanceOwner::GetEventloopNestingLevel() {
+ nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+ uint32_t currentLevel = 0;
+ if (appShell) {
+ appShell->GetEventloopNestingLevel(&currentLevel);
+#ifdef XP_MACOSX
+ // Cocoa widget code doesn't process UI events through the normal
+ // appshell event loop, so it needs an additional count here.
+ currentLevel++;
+#endif
+ }
+
+ // No idea how this happens... but Linux doesn't consistently
+ // process UI events through the appshell event loop. If we get a 0
+ // here on any platform we increment the level just in case so that
+ // we make sure we always tear the plugin down eventually.
+ if (!currentLevel) {
+ currentLevel++;
+ }
+
+ return currentLevel;
+}
+
+nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(Event* aFocusEvent) {
+#ifndef XP_MACOSX
+ if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) {
+ // continue only for cases without child window
+ aFocusEvent->PreventDefault(); // consume event
+ return NS_OK;
+ }
+#endif
+
+ WidgetEvent* theEvent = aFocusEvent->WidgetEventPtr();
+ if (theEvent) {
+ WidgetGUIEvent focusEvent(theEvent->IsTrusted(), theEvent->mMessage,
+ nullptr);
+ nsEventStatus rv = ProcessEvent(focusEvent);
+ if (nsEventStatus_eConsumeNoDefault == rv) {
+ aFocusEvent->PreventDefault();
+ aFocusEvent->StopPropagation();
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginInstanceOwner::ProcessKeyPress(Event* aKeyEvent) {
+ // ProcessKeyPress() may be called twice with same eKeyPress event. One is
+ // by the event listener in the default event group and the other is by the
+ // event listener in the system event group. When this is called in the
+ // latter case and the event must be fired in the default event group too,
+ // we don't need to do nothing anymore.
+ // XXX Do we need to check whether the document is in chrome? In strictly
+ // speaking, it must be yes. However, our UI must not use plugin in
+ // chrome.
+ if (!aKeyEvent->WidgetEventPtr()->mFlags.mOnlySystemGroupDispatchInContent &&
+ aKeyEvent->WidgetEventPtr()->mFlags.mInSystemGroup) {
+ return NS_OK;
+ }
+
+#ifdef XP_MACOSX
+ return DispatchKeyToPlugin(aKeyEvent);
+#else
+ if (SendNativeEvents()) DispatchKeyToPlugin(aKeyEvent);
+
+ if (mInstance) {
+ // If this event is going to the plugin, we want to kill it.
+ // Not actually sending keypress to the plugin, since we didn't before.
+ aKeyEvent->PreventDefault();
+ aKeyEvent->StopPropagation();
+ }
+ return NS_OK;
+#endif
+}
+
+nsresult nsPluginInstanceOwner::DispatchKeyToPlugin(Event* aKeyEvent) {
+#if !defined(XP_MACOSX)
+ if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) {
+ aKeyEvent->PreventDefault(); // consume event
+ return NS_OK;
+ }
+ // continue only for cases without child window
+#endif
+
+ if (mInstance) {
+ WidgetKeyboardEvent* keyEvent =
+ aKeyEvent->WidgetEventPtr()->AsKeyboardEvent();
+ if (keyEvent && keyEvent->mClass == eKeyboardEventClass) {
+ nsEventStatus rv = ProcessEvent(*keyEvent);
+ if (nsEventStatus_eConsumeNoDefault == rv) {
+ aKeyEvent->PreventDefault();
+ aKeyEvent->StopPropagation();
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginInstanceOwner::ProcessMouseDown(Event* aMouseEvent) {
+#if !defined(XP_MACOSX)
+ if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) {
+ aMouseEvent->PreventDefault(); // consume event
+ return NS_OK;
+ }
+ // continue only for cases without child window
+#endif
+
+ // if the plugin is windowless, we need to set focus ourselves
+ // otherwise, we might not get key events
+ if (mPluginFrame && mPluginWindow &&
+ mPluginWindow->type == NPWindowTypeDrawable) {
+ if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
+ nsCOMPtr<Element> elem = do_QueryReferent(mContent);
+ fm->SetFocus(elem, 0);
+ }
+ }
+
+ WidgetMouseEvent* mouseEvent = aMouseEvent->WidgetEventPtr()->AsMouseEvent();
+ if (mouseEvent && mouseEvent->mClass == eMouseEventClass) {
+ mLastMouseDownButtonType = mouseEvent->mButton;
+ nsEventStatus rv = ProcessEvent(*mouseEvent);
+ if (nsEventStatus_eConsumeNoDefault == rv) {
+ aMouseEvent->PreventDefault(); // consume event
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginInstanceOwner::DispatchMouseToPlugin(Event* aMouseEvent,
+ bool aAllowPropagate) {
+#if !defined(XP_MACOSX)
+ if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) {
+ aMouseEvent->PreventDefault(); // consume event
+ return NS_OK;
+ }
+ // continue only for cases without child window
+#endif
+ // don't send mouse events if we are hidden
+ if (!mWidgetVisible) return NS_OK;
+
+ WidgetMouseEvent* mouseEvent = aMouseEvent->WidgetEventPtr()->AsMouseEvent();
+ if (mouseEvent && mouseEvent->mClass == eMouseEventClass) {
+ nsEventStatus rv = ProcessEvent(*mouseEvent);
+ if (nsEventStatus_eConsumeNoDefault == rv) {
+ aMouseEvent->PreventDefault();
+ if (!aAllowPropagate) {
+ aMouseEvent->StopPropagation();
+ }
+ }
+ if (mouseEvent->mMessage == eMouseUp) {
+ mLastMouseDownButtonType = -1;
+ }
+ }
+ return NS_OK;
+}
+
+#ifdef XP_WIN
+already_AddRefed<TextComposition> nsPluginInstanceOwner::GetTextComposition() {
+ if (NS_WARN_IF(!mPluginFrame)) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
+ if (!widget) {
+ widget = GetRootWidgetForPluginFrame(mPluginFrame);
+ if (NS_WARN_IF(!widget)) {
+ return nullptr;
+ }
+ }
+
+ RefPtr<TextComposition> composition =
+ IMEStateManager::GetTextCompositionFor(widget);
+ if (NS_WARN_IF(!composition)) {
+ return nullptr;
+ }
+
+ return composition.forget();
+}
+
+void nsPluginInstanceOwner::HandleNoConsumedCompositionMessage(
+ WidgetCompositionEvent* aCompositionEvent, const NPEvent* aPluginEvent) {
+ nsCOMPtr<nsIWidget> widget = GetContainingWidgetIfOffset();
+ if (!widget) {
+ widget = GetRootWidgetForPluginFrame(mPluginFrame);
+ if (NS_WARN_IF(!widget)) {
+ return;
+ }
+ }
+
+ if (aPluginEvent->lParam & GCS_RESULTSTR) {
+ return;
+ }
+ if (!mSentStartComposition) {
+ mSentStartComposition = true;
+ }
+}
+#endif
+
+nsresult nsPluginInstanceOwner::DispatchCompositionToPlugin(Event* aEvent) {
+#ifdef XP_WIN
+ if (!mPluginWindow) {
+ // CompositionEvent isn't cancellable. So it is unnecessary to call
+ // PreventDefaults() to consume event
+ return NS_OK;
+ }
+ WidgetCompositionEvent* compositionEvent =
+ aEvent->WidgetEventPtr()->AsCompositionEvent();
+ if (NS_WARN_IF(!compositionEvent)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (compositionEvent->mMessage == eCompositionChange) {
+ RefPtr<TextComposition> composition = GetTextComposition();
+ if (NS_WARN_IF(!composition)) {
+ return NS_ERROR_FAILURE;
+ }
+ TextComposition::CompositionChangeEventHandlingMarker
+ compositionChangeEventHandlingMarker(composition, compositionEvent);
+ }
+
+ const NPEvent* pPluginEvent =
+ static_cast<const NPEvent*>(compositionEvent->mPluginEvent);
+ if (pPluginEvent && pPluginEvent->event == WM_IME_COMPOSITION &&
+ mPluginDidNotHandleIMEComposition) {
+ // This is a workaround when running windowed and windowless Flash on
+ // same process.
+ // Flash with protected mode calls IMM APIs on own render process. This
+ // is a bug of Flash's protected mode.
+ // ImmGetCompositionString with GCS_RESULTSTR returns *LAST* committed
+ // string. So when windowed mode Flash handles IME composition,
+ // windowless plugin can get windowed mode's commited string by that API.
+ // So we never post WM_IME_COMPOSITION when plugin doesn't call
+ // ImmGetCompositionString() during WM_IME_COMPOSITION correctly.
+ HandleNoConsumedCompositionMessage(compositionEvent, pPluginEvent);
+ aEvent->StopImmediatePropagation();
+ return NS_OK;
+ }
+
+ // Protected mode Flash returns noDefault by NPP_HandleEvent, but
+ // composition information into plugin is invalid because plugin's bug.
+ // So if plugin doesn't get composition data by WM_IME_COMPOSITION, we
+ // recongnize it isn't handled
+ AutoRestore<bool> restore(mGotCompositionData);
+ mGotCompositionData = false;
+
+ nsEventStatus status = ProcessEvent(*compositionEvent);
+ aEvent->StopImmediatePropagation();
+
+ // Composition event isn't handled by plugin, so we have to call default proc.
+
+ if (NS_WARN_IF(!pPluginEvent)) {
+ return NS_OK;
+ }
+
+ if (pPluginEvent->event == WM_IME_STARTCOMPOSITION) {
+ if (nsEventStatus_eConsumeNoDefault != status) {
+ mSentStartComposition = true;
+ } else {
+ mSentStartComposition = false;
+ }
+ mPluginDidNotHandleIMEComposition = false;
+ return NS_OK;
+ }
+
+ if (pPluginEvent->event == WM_IME_ENDCOMPOSITION) {
+ return NS_OK;
+ }
+
+ if (pPluginEvent->event == WM_IME_COMPOSITION && !mGotCompositionData) {
+ // If plugin doesn't handle WM_IME_COMPOSITION correctly, we don't send
+ // composition event until end composition.
+ mPluginDidNotHandleIMEComposition = true;
+
+ HandleNoConsumedCompositionMessage(compositionEvent, pPluginEvent);
+ }
+#endif // #ifdef XP_WIN
+ return NS_OK;
+}
+
+nsresult nsPluginInstanceOwner::HandleEvent(Event* aEvent) {
+ NS_ASSERTION(mInstance,
+ "Should have a valid plugin instance or not receive events.");
+
+ nsAutoString eventType;
+ aEvent->GetType(eventType);
+
+#ifdef XP_MACOSX
+ if (eventType.EqualsLiteral("activate") ||
+ eventType.EqualsLiteral("deactivate")) {
+ WindowFocusMayHaveChanged();
+ return NS_OK;
+ }
+ if (eventType.EqualsLiteral("MozPerformDelayedBlur")) {
+ if (mShouldBlurOnActivate) {
+ WidgetGUIEvent blurEvent(true, eBlur, nullptr);
+ ProcessEvent(blurEvent);
+ mShouldBlurOnActivate = false;
+ }
+ return NS_OK;
+ }
+#endif
+
+ if (eventType.EqualsLiteral("focus")) {
+ mContentFocused = true;
+ return DispatchFocusToPlugin(aEvent);
+ }
+ if (eventType.EqualsLiteral("blur")) {
+ mContentFocused = false;
+ return DispatchFocusToPlugin(aEvent);
+ }
+ if (eventType.EqualsLiteral("mousedown")) {
+ return ProcessMouseDown(aEvent);
+ }
+ if (eventType.EqualsLiteral("mouseup")) {
+ return DispatchMouseToPlugin(aEvent);
+ }
+ if (eventType.EqualsLiteral("mousemove")) {
+ return DispatchMouseToPlugin(aEvent, true);
+ }
+ if (eventType.EqualsLiteral("click") || eventType.EqualsLiteral("dblclick") ||
+ eventType.EqualsLiteral("mouseover") ||
+ eventType.EqualsLiteral("mouseout")) {
+ return DispatchMouseToPlugin(aEvent);
+ }
+ if (eventType.EqualsLiteral("keydown") || eventType.EqualsLiteral("keyup")) {
+ return DispatchKeyToPlugin(aEvent);
+ }
+ if (eventType.EqualsLiteral("keypress")) {
+ return ProcessKeyPress(aEvent);
+ }
+ if (eventType.EqualsLiteral("compositionstart") ||
+ eventType.EqualsLiteral("compositionend") ||
+ eventType.EqualsLiteral("text")) {
+ return DispatchCompositionToPlugin(aEvent);
+ }
+
+ DragEvent* dragEvent = aEvent->AsDragEvent();
+ if (dragEvent && mInstance) {
+ WidgetEvent* ievent = aEvent->WidgetEventPtr();
+ if (ievent && ievent->IsTrusted() && ievent->mMessage != eDragEnter &&
+ ievent->mMessage != eDragOver) {
+ aEvent->PreventDefault();
+ }
+
+ // Let the plugin handle drag events.
+ aEvent->StopPropagation();
+ }
+ return NS_OK;
+}
+
+#ifdef MOZ_X11
+static unsigned int XInputEventState(const WidgetInputEvent& anEvent) {
+ unsigned int state = 0;
+ if (anEvent.IsShift()) state |= ShiftMask;
+ if (anEvent.IsControl()) state |= ControlMask;
+ if (anEvent.IsAlt()) state |= Mod1Mask;
+ if (anEvent.IsMeta()) state |= Mod4Mask;
+ return state;
+}
+#endif
+
+#ifdef XP_MACOSX
+
+// Returns whether or not content is the content that is or would be
+// focused if the top-level chrome window was active.
+static bool ContentIsFocusedWithinWindow(nsIContent* aContent) {
+ nsPIDOMWindowOuter* outerWindow = aContent->OwnerDoc()->GetWindow();
+ if (!outerWindow) {
+ return false;
+ }
+
+ nsPIDOMWindowOuter* rootWindow = outerWindow->GetPrivateRoot();
+ if (!rootWindow) {
+ return false;
+ }
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (!fm) {
+ return false;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> focusedFrame;
+ nsCOMPtr<nsIContent> focusedContent = nsFocusManager::GetFocusedDescendant(
+ rootWindow, nsFocusManager::eIncludeAllDescendants,
+ getter_AddRefs(focusedFrame));
+ return (focusedContent.get() == aContent);
+}
+
+static NPCocoaEventType CocoaEventTypeForEvent(const WidgetGUIEvent& anEvent,
+ nsIFrame* aObjectFrame) {
+ const NPCocoaEvent* event =
+ static_cast<const NPCocoaEvent*>(anEvent.mPluginEvent);
+ if (event) {
+ return event->type;
+ }
+
+ switch (anEvent.mMessage) {
+ case eMouseOver:
+ return NPCocoaEventMouseEntered;
+ case eMouseOut:
+ return NPCocoaEventMouseExited;
+ case eMouseMove: {
+ // We don't know via information on events from the widget code whether or
+ // not we're dragging. The widget code just generates mouse move events
+ // from native drag events. If anybody is capturing, this is a drag event.
+ if (PresShell::GetCapturingContent()) {
+ return NPCocoaEventMouseDragged;
+ }
+
+ return NPCocoaEventMouseMoved;
+ }
+ case eMouseDown:
+ return NPCocoaEventMouseDown;
+ case eMouseUp:
+ return NPCocoaEventMouseUp;
+ case eKeyDown:
+ return NPCocoaEventKeyDown;
+ case eKeyUp:
+ return NPCocoaEventKeyUp;
+ case eFocus:
+ case eBlur:
+ return NPCocoaEventFocusChanged;
+ case eLegacyMouseLineOrPageScroll:
+ return NPCocoaEventScrollWheel;
+ default:
+ return (NPCocoaEventType)0;
+ }
+}
+
+static NPCocoaEvent TranslateToNPCocoaEvent(WidgetGUIEvent* anEvent,
+ nsIFrame* aObjectFrame) {
+ NPCocoaEvent cocoaEvent;
+ InitializeNPCocoaEvent(&cocoaEvent);
+ cocoaEvent.type = CocoaEventTypeForEvent(*anEvent, aObjectFrame);
+
+ if (anEvent->mMessage == eMouseMove || anEvent->mMessage == eMouseDown ||
+ anEvent->mMessage == eMouseUp ||
+ anEvent->mMessage == eLegacyMouseLineOrPageScroll ||
+ anEvent->mMessage == eMouseOver || anEvent->mMessage == eMouseOut) {
+ nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
+ anEvent, RelativeTo{aObjectFrame}) -
+ aObjectFrame->GetContentRectRelativeToSelf().TopLeft();
+ nsPresContext* presContext = aObjectFrame->PresContext();
+ // Plugin event coordinates need to be translated from device pixels
+ // into "display pixels" in HiDPI modes.
+ double scaleFactor = double(AppUnitsPerCSSPixel()) /
+ aObjectFrame->PresContext()
+ ->DeviceContext()
+ ->AppUnitsPerDevPixelAtUnitFullZoom();
+ size_t intScaleFactor = ceil(scaleFactor);
+ nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x) / intScaleFactor,
+ presContext->AppUnitsToDevPixels(pt.y) / intScaleFactor);
+ cocoaEvent.data.mouse.pluginX = double(ptPx.x);
+ cocoaEvent.data.mouse.pluginY = double(ptPx.y);
+ }
+
+ switch (anEvent->mMessage) {
+ case eMouseDown:
+ case eMouseUp: {
+ WidgetMouseEvent* mouseEvent = anEvent->AsMouseEvent();
+ if (mouseEvent) {
+ switch (mouseEvent->mButton) {
+ case MouseButton::ePrimary:
+ cocoaEvent.data.mouse.buttonNumber = 0;
+ break;
+ case MouseButton::eSecondary:
+ cocoaEvent.data.mouse.buttonNumber = 1;
+ break;
+ case MouseButton::eMiddle:
+ cocoaEvent.data.mouse.buttonNumber = 2;
+ break;
+ default:
+ NS_WARNING("Mouse mButton we don't know about?");
+ }
+ cocoaEvent.data.mouse.clickCount = mouseEvent->mClickCount;
+ } else {
+ NS_WARNING("eMouseUp/DOWN is not a WidgetMouseEvent?");
+ }
+ break;
+ }
+ case eLegacyMouseLineOrPageScroll: {
+ WidgetWheelEvent* wheelEvent = anEvent->AsWheelEvent();
+ if (wheelEvent) {
+ cocoaEvent.data.mouse.deltaX = wheelEvent->mLineOrPageDeltaX;
+ cocoaEvent.data.mouse.deltaY = wheelEvent->mLineOrPageDeltaY;
+ } else {
+ NS_WARNING(
+ "eLegacyMouseLineOrPageScroll is not a WidgetWheelEvent? "
+ "(could be, haven't checked)");
+ }
+ break;
+ }
+ case eKeyDown:
+ case eKeyUp:
+ break;
+ case eFocus:
+ case eBlur:
+ cocoaEvent.data.focus.hasFocus = (anEvent->mMessage == eFocus);
+ break;
+ default:
+ break;
+ }
+ return cocoaEvent;
+}
+
+void nsPluginInstanceOwner::PerformDelayedBlurs() {
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ nsCOMPtr<EventTarget> windowRoot =
+ content->OwnerDoc()->GetWindow()->GetTopWindowRoot();
+ nsContentUtils::DispatchEventOnlyToChrome(
+ content->OwnerDoc(), windowRoot, u"MozPerformDelayedBlur"_ns,
+ CanBubble::eNo, Cancelable::eNo, nullptr);
+}
+
+#endif
+
+nsEventStatus nsPluginInstanceOwner::ProcessEvent(
+ const WidgetGUIEvent& anEvent) {
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ if (!mInstance || !mPluginFrame) {
+ return nsEventStatus_eIgnore;
+ }
+
+#ifdef XP_MACOSX
+ NPEventModel eventModel = GetEventModel();
+ if (eventModel != NPEventModelCocoa) {
+ return nsEventStatus_eIgnore;
+ }
+
+ // In the Cocoa event model, focus is per-window. Don't tell a plugin it lost
+ // focus unless it lost focus within the window. For example, ignore a blur
+ // event if it's coming due to the plugin's window deactivating.
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (anEvent.mMessage == eBlur && ContentIsFocusedWithinWindow(content)) {
+ mShouldBlurOnActivate = true;
+ return nsEventStatus_eIgnore;
+ }
+
+ // Also, don't tell the plugin it gained focus again after we've already given
+ // it focus. This might happen if it has focus, its window is blurred, then
+ // the window is made active again. The plugin never lost in-window focus, so
+ // it shouldn't get a focus event again.
+ if (anEvent.mMessage == eFocus && mLastContentFocused == true) {
+ mShouldBlurOnActivate = false;
+ return nsEventStatus_eIgnore;
+ }
+
+ // Now, if we're going to send a focus event, update mLastContentFocused and
+ // tell any plugins in our window that we have taken focus, so they should
+ // perform any delayed blurs.
+ if (anEvent.mMessage == eFocus || anEvent.mMessage == eBlur) {
+ mLastContentFocused = (anEvent.mMessage == eFocus);
+ mShouldBlurOnActivate = false;
+ PerformDelayedBlurs();
+ }
+
+ NPCocoaEvent cocoaEvent = TranslateToNPCocoaEvent(
+ const_cast<WidgetGUIEvent*>(&anEvent), mPluginFrame);
+ if (cocoaEvent.type == (NPCocoaEventType)0) {
+ return nsEventStatus_eIgnore;
+ }
+
+ if (cocoaEvent.type == NPCocoaEventTextInput) {
+ mInstance->HandleEvent(&cocoaEvent, nullptr);
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ int16_t response = kNPEventNotHandled;
+ mInstance->HandleEvent(&cocoaEvent, &response,
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+
+ bool handled = (response == kNPEventHandled || response == kNPEventStartIME);
+ bool leftMouseButtonDown =
+ (anEvent.mMessage == eMouseDown) &&
+ (anEvent.AsMouseEvent()->mButton == MouseButton::ePrimary);
+ if (handled && !(leftMouseButtonDown && !mContentFocused)) {
+ rv = nsEventStatus_eConsumeNoDefault;
+ }
+#endif
+
+#ifdef XP_WIN
+ // this code supports windowless plugins
+ const NPEvent* pPluginEvent =
+ static_cast<const NPEvent*>(anEvent.mPluginEvent);
+ // we can get synthetic events from the EventStateManager... these
+ // have no pluginEvent
+ NPEvent pluginEvent;
+ if (anEvent.mClass == eMouseEventClass ||
+ anEvent.mClass == eWheelEventClass) {
+ if (!pPluginEvent) {
+ // XXX Should extend this list to synthesize events for more event
+ // types
+ pluginEvent.event = 0;
+ bool initWParamWithCurrentState = true;
+ switch (anEvent.mMessage) {
+ case eMouseMove: {
+ pluginEvent.event = WM_MOUSEMOVE;
+ break;
+ }
+ case eMouseDown: {
+ static const int downMsgs[] = {WM_LBUTTONDOWN, WM_MBUTTONDOWN,
+ WM_RBUTTONDOWN};
+ static const int dblClickMsgs[] = {WM_LBUTTONDBLCLK, WM_MBUTTONDBLCLK,
+ WM_RBUTTONDBLCLK};
+ const WidgetMouseEvent* mouseEvent = anEvent.AsMouseEvent();
+ if (mouseEvent->mClickCount == 2) {
+ pluginEvent.event = dblClickMsgs[mouseEvent->mButton];
+ } else {
+ pluginEvent.event = downMsgs[mouseEvent->mButton];
+ }
+ break;
+ }
+ case eMouseUp: {
+ static const int upMsgs[] = {WM_LBUTTONUP, WM_MBUTTONUP,
+ WM_RBUTTONUP};
+ const WidgetMouseEvent* mouseEvent = anEvent.AsMouseEvent();
+ pluginEvent.event = upMsgs[mouseEvent->mButton];
+ break;
+ }
+ // For plugins which don't support high-resolution scroll, we should
+ // generate legacy resolution wheel messages. I.e., the delta value
+ // should be WHEEL_DELTA * n.
+ case eWheel: {
+ const WidgetWheelEvent* wheelEvent = anEvent.AsWheelEvent();
+ int32_t delta = 0;
+ if (wheelEvent->mLineOrPageDeltaY) {
+ switch (wheelEvent->mDeltaMode) {
+ case WheelEvent_Binding::DOM_DELTA_PAGE:
+ pluginEvent.event = WM_MOUSEWHEEL;
+ delta = -WHEEL_DELTA * wheelEvent->mLineOrPageDeltaY;
+ break;
+ case WheelEvent_Binding::DOM_DELTA_LINE: {
+ UINT linesPerWheelDelta = mWheelScrollLines;
+ if (!linesPerWheelDelta) {
+ break;
+ }
+ pluginEvent.event = WM_MOUSEWHEEL;
+ delta = -WHEEL_DELTA / linesPerWheelDelta;
+ delta *= wheelEvent->mLineOrPageDeltaY;
+ break;
+ }
+ case WheelEvent_Binding::DOM_DELTA_PIXEL:
+ default:
+ // We don't support WM_GESTURE with this path.
+ MOZ_ASSERT(!pluginEvent.event);
+ break;
+ }
+ } else if (wheelEvent->mLineOrPageDeltaX) {
+ switch (wheelEvent->mDeltaMode) {
+ case WheelEvent_Binding::DOM_DELTA_PAGE:
+ pluginEvent.event = WM_MOUSEHWHEEL;
+ delta = -WHEEL_DELTA * wheelEvent->mLineOrPageDeltaX;
+ break;
+ case WheelEvent_Binding::DOM_DELTA_LINE: {
+ pluginEvent.event = WM_MOUSEHWHEEL;
+ UINT charsPerWheelDelta = mWheelScrollChars;
+ if (!charsPerWheelDelta) {
+ break;
+ }
+ delta = WHEEL_DELTA / charsPerWheelDelta;
+ delta *= wheelEvent->mLineOrPageDeltaX;
+ break;
+ }
+ case WheelEvent_Binding::DOM_DELTA_PIXEL:
+ default:
+ // We don't support WM_GESTURE with this path.
+ MOZ_ASSERT(!pluginEvent.event);
+ break;
+ }
+ }
+
+ if (!pluginEvent.event) {
+ break;
+ }
+
+ initWParamWithCurrentState = false;
+ int32_t modifiers =
+ (wheelEvent->IsControl() ? MK_CONTROL : 0) |
+ (wheelEvent->IsShift() ? MK_SHIFT : 0) |
+ (wheelEvent->IsLeftButtonPressed() ? MK_LBUTTON : 0) |
+ (wheelEvent->IsMiddleButtonPressed() ? MK_MBUTTON : 0) |
+ (wheelEvent->IsRightButtonPressed() ? MK_RBUTTON : 0) |
+ (wheelEvent->Is4thButtonPressed() ? MK_XBUTTON1 : 0) |
+ (wheelEvent->Is5thButtonPressed() ? MK_XBUTTON2 : 0);
+ pluginEvent.wParam = MAKEWPARAM(modifiers, delta);
+ pPluginEvent = &pluginEvent;
+ break;
+ }
+ // don't synthesize anything for eMouseDoubleClick, since that
+ // is a synthetic event generated on mouse-up, and Windows WM_*DBLCLK
+ // messages are sent on mouse-down
+ default:
+ break;
+ }
+ if (pluginEvent.event && initWParamWithCurrentState) {
+ // We created one of the messages caught above but didn't fill in
+ // wParam. Mark it with an invalid wParam value so that HandleEvent can
+ // figure it out.
+ pPluginEvent = &pluginEvent;
+ pluginEvent.wParam = NPAPI_INVALID_WPARAM;
+ }
+ }
+ if (pPluginEvent) {
+ // Make event coordinates relative to our enclosing widget,
+ // not the widget they were received on.
+ // See use of NPEvent in widget/windows/nsWindow.cpp
+ // for why this assert should be safe
+ NS_ASSERTION(
+ anEvent.mMessage == eMouseDown || anEvent.mMessage == eMouseUp ||
+ anEvent.mMessage == eMouseDoubleClick ||
+ anEvent.mMessage == eMouseAuxClick ||
+ anEvent.mMessage == eMouseOver || anEvent.mMessage == eMouseOut ||
+ anEvent.mMessage == eMouseMove || anEvent.mMessage == eWheel,
+ "Incorrect event type for coordinate translation");
+ nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
+ &anEvent, RelativeTo{mPluginFrame}) -
+ mPluginFrame->GetContentRectRelativeToSelf().TopLeft();
+ nsPresContext* presContext = mPluginFrame->PresContext();
+ nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x),
+ presContext->AppUnitsToDevPixels(pt.y));
+ nsIntPoint widgetPtPx =
+ ptPx + mPluginFrame->GetWindowOriginInPixels(true);
+ const_cast<NPEvent*>(pPluginEvent)->lParam =
+ MAKELPARAM(widgetPtPx.x, widgetPtPx.y);
+ }
+ } else if (!pPluginEvent) {
+ switch (anEvent.mMessage) {
+ case eFocus:
+ pluginEvent.event = WM_SETFOCUS;
+ pluginEvent.wParam = 0;
+ pluginEvent.lParam = 0;
+ pPluginEvent = &pluginEvent;
+ break;
+ case eBlur:
+ pluginEvent.event = WM_KILLFOCUS;
+ pluginEvent.wParam = 0;
+ pluginEvent.lParam = 0;
+ pPluginEvent = &pluginEvent;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (pPluginEvent && !pPluginEvent->event) {
+ // Don't send null events to plugins.
+ NS_WARNING(
+ "nsPluginFrame ProcessEvent: trying to send null event to plugin.");
+ return rv;
+ }
+
+ // We don't need to tell the plugin about changes to the scroll wheel
+ // settings but we do need to remember them for future mouse move
+ // calculations. We put the scroll wheel setting in the lParam field.
+ if (pPluginEvent && pPluginEvent->event == WM_SETTINGCHANGE) {
+ switch (pPluginEvent->wParam) {
+ case SPI_SETWHEELSCROLLLINES:
+ mWheelScrollLines = static_cast<uint32_t>(pPluginEvent->lParam);
+ break;
+ case SPI_SETWHEELSCROLLCHARS:
+ mWheelScrollChars = static_cast<uint32_t>(pPluginEvent->lParam);
+ break;
+ default:
+ break;
+ }
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ if (pPluginEvent) {
+ int16_t response = kNPEventNotHandled;
+ mInstance->HandleEvent(const_cast<NPEvent*>(pPluginEvent), &response,
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+ if (response == kNPEventHandled) rv = nsEventStatus_eConsumeNoDefault;
+ }
+#endif
+
+#ifdef MOZ_X11
+ // this code supports windowless plugins
+ nsIWidget* widget = anEvent.mWidget;
+ XEvent pluginEvent = XEvent();
+ pluginEvent.type = 0;
+
+ switch (anEvent.mClass) {
+ case eMouseEventClass: {
+ switch (anEvent.mMessage) {
+ case eMouseClick:
+ case eMouseDoubleClick:
+ case eMouseAuxClick:
+ // Button up/down events sent instead.
+ return rv;
+ default:
+ break;
+ }
+
+ // Get reference point relative to plugin origin.
+ const nsPresContext* presContext = mPluginFrame->PresContext();
+ nsPoint appPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
+ &anEvent, RelativeTo{mPluginFrame}) -
+ mPluginFrame->GetContentRectRelativeToSelf().TopLeft();
+ nsIntPoint pluginPoint(presContext->AppUnitsToDevPixels(appPoint.x),
+ presContext->AppUnitsToDevPixels(appPoint.y));
+ const WidgetMouseEvent& mouseEvent = *anEvent.AsMouseEvent();
+ // Get reference point relative to screen:
+ LayoutDeviceIntPoint rootPoint(-1, -1);
+ if (widget) {
+ rootPoint = anEvent.mRefPoint + widget->WidgetToScreenOffset();
+ }
+# ifdef MOZ_WIDGET_GTK
+ Window root = GDK_ROOT_WINDOW();
+# else
+ Window root = X11None; // Could XQueryTree, but this is not important.
+# endif
+
+ switch (anEvent.mMessage) {
+ case eMouseOver:
+ case eMouseOut: {
+ XCrossingEvent& event = pluginEvent.xcrossing;
+ event.type =
+ anEvent.mMessage == eMouseOver ? EnterNotify : LeaveNotify;
+ event.root = root;
+ event.time = anEvent.mTime;
+ event.x = pluginPoint.x;
+ event.y = pluginPoint.y;
+ event.x_root = rootPoint.x;
+ event.y_root = rootPoint.y;
+ event.state = XInputEventState(mouseEvent);
+ // information lost
+ event.subwindow = X11None;
+ event.mode = -1;
+ event.detail = NotifyDetailNone;
+ event.same_screen = X11True;
+ event.focus = mContentFocused;
+ } break;
+ case eMouseMove: {
+ XMotionEvent& event = pluginEvent.xmotion;
+ event.type = MotionNotify;
+ event.root = root;
+ event.time = anEvent.mTime;
+ event.x = pluginPoint.x;
+ event.y = pluginPoint.y;
+ event.x_root = rootPoint.x;
+ event.y_root = rootPoint.y;
+ event.state = XInputEventState(mouseEvent);
+ // information lost
+ event.subwindow = X11None;
+ event.is_hint = NotifyNormal;
+ event.same_screen = X11True;
+ } break;
+ case eMouseDown:
+ case eMouseUp: {
+ XButtonEvent& event = pluginEvent.xbutton;
+ event.type =
+ anEvent.mMessage == eMouseDown ? ButtonPress : ButtonRelease;
+ event.root = root;
+ event.time = anEvent.mTime;
+ event.x = pluginPoint.x;
+ event.y = pluginPoint.y;
+ event.x_root = rootPoint.x;
+ event.y_root = rootPoint.y;
+ event.state = XInputEventState(mouseEvent);
+ switch (mouseEvent.mButton) {
+ case MouseButton::eMiddle:
+ event.button = 2;
+ break;
+ case MouseButton::eSecondary:
+ event.button = 3;
+ break;
+ default: // MouseButton::eLeft;
+ event.button = 1;
+ break;
+ }
+ // information lost:
+ event.subwindow = X11None;
+ event.same_screen = X11True;
+ } break;
+ default:
+ break;
+ }
+ } break;
+
+ // XXX case eMouseScrollEventClass: not received.
+
+ case eKeyboardEventClass:
+ if (anEvent.mPluginEvent) {
+ XKeyEvent& event = pluginEvent.xkey;
+# ifdef MOZ_WIDGET_GTK
+ event.root = GDK_ROOT_WINDOW();
+ event.time = anEvent.mTime;
+ const GdkEventKey* gdkEvent =
+ static_cast<const GdkEventKey*>(anEvent.mPluginEvent);
+ event.keycode = gdkEvent->hardware_keycode;
+ event.state = gdkEvent->state;
+ switch (anEvent.mMessage) {
+ case eKeyDown:
+ // Handle eKeyDown for modifier key presses
+ // For non-modifiers we get eKeyPress
+ if (gdkEvent->is_modifier) event.type = XKeyPress;
+ break;
+ case eKeyPress:
+ event.type = XKeyPress;
+ break;
+ case eKeyUp:
+ event.type = KeyRelease;
+ break;
+ default:
+ break;
+ }
+# endif
+
+ // Information that could be obtained from pluginEvent but we may not
+ // want to promise to provide:
+ event.subwindow = X11None;
+ event.x = 0;
+ event.y = 0;
+ event.x_root = -1;
+ event.y_root = -1;
+ event.same_screen = X11False;
+ } else {
+ // If we need to send synthesized key events, then
+ // DOMKeyCodeToGdkKeyCode(keyEvent.keyCode) and
+ // gdk_keymap_get_entries_for_keyval will be useful, but the
+ // mappings will not be unique.
+ NS_WARNING("Synthesized key event not sent to plugin");
+ }
+ break;
+
+ default:
+ switch (anEvent.mMessage) {
+ case eFocus:
+ case eBlur: {
+ XFocusChangeEvent& event = pluginEvent.xfocus;
+ event.type = anEvent.mMessage == eFocus ? FocusIn : FocusOut;
+ // information lost:
+ event.mode = -1;
+ event.detail = NotifyDetailNone;
+ } break;
+ default:
+ break;
+ }
+ }
+
+ if (!pluginEvent.type) {
+ return rv;
+ }
+
+ // Fill in (useless) generic event information.
+ XAnyEvent& event = pluginEvent.xany;
+ event.display =
+ widget ? static_cast<Display*>(widget->GetNativeData(NS_NATIVE_DISPLAY))
+ : nullptr;
+ event.window = X11None; // not a real window
+ // information lost:
+ event.serial = 0;
+ event.send_event = X11False;
+
+ int16_t response = kNPEventNotHandled;
+ mInstance->HandleEvent(&pluginEvent, &response,
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+ if (response == kNPEventHandled) rv = nsEventStatus_eConsumeNoDefault;
+#endif
+
+ return rv;
+}
+
+nsresult nsPluginInstanceOwner::Destroy() {
+ SetFrame(nullptr);
+
+#ifdef XP_MACOSX
+ RemoveFromCARefreshTimer();
+#endif
+
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+
+ // unregister context menu listener
+ if (mCXMenuListener) {
+ mCXMenuListener->Destroy(content);
+ mCXMenuListener = nullptr;
+ }
+
+ content->RemoveEventListener(u"focus"_ns, this, false);
+ content->RemoveEventListener(u"blur"_ns, this, false);
+ content->RemoveEventListener(u"mouseup"_ns, this, false);
+ content->RemoveEventListener(u"mousedown"_ns, this, false);
+ content->RemoveEventListener(u"mousemove"_ns, this, false);
+ content->RemoveEventListener(u"click"_ns, this, false);
+ content->RemoveEventListener(u"dblclick"_ns, this, false);
+ content->RemoveEventListener(u"mouseover"_ns, this, false);
+ content->RemoveEventListener(u"mouseout"_ns, this, false);
+ content->RemoveEventListener(u"keypress"_ns, this, true);
+ content->RemoveSystemEventListener(u"keypress"_ns, this, true);
+ content->RemoveEventListener(u"keydown"_ns, this, true);
+ content->RemoveEventListener(u"keyup"_ns, this, true);
+ content->RemoveEventListener(u"drop"_ns, this, true);
+ content->RemoveEventListener(u"drag"_ns, this, true);
+ content->RemoveEventListener(u"dragenter"_ns, this, true);
+ content->RemoveEventListener(u"dragover"_ns, this, true);
+ content->RemoveEventListener(u"dragleave"_ns, this, true);
+ content->RemoveEventListener(u"dragexit"_ns, this, true);
+ content->RemoveEventListener(u"dragstart"_ns, this, true);
+ content->RemoveEventListener(u"dragend"_ns, this, true);
+ content->RemoveSystemEventListener(u"compositionstart"_ns, this, true);
+ content->RemoveSystemEventListener(u"compositionend"_ns, this, true);
+ content->RemoveSystemEventListener(u"text"_ns, this, true);
+
+ if (mWidget) {
+ if (mPluginWindow) {
+ mPluginWindow->SetPluginWidget(nullptr);
+ }
+
+ nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
+ if (pluginWidget) {
+ pluginWidget->SetPluginInstanceOwner(nullptr);
+ }
+ mWidget->Destroy();
+ }
+
+ return NS_OK;
+}
+
+// Paints are handled differently, so we just simulate an update event.
+
+#ifdef XP_MACOSX
+void nsPluginInstanceOwner::Paint(const gfxRect& aDirtyRect,
+ CGContextRef cgContext) {
+ if (!mInstance || !mPluginFrame) return;
+
+ gfxRect dirtyRectCopy = aDirtyRect;
+ double scaleFactor = 1.0;
+ GetContentsScaleFactor(&scaleFactor);
+ if (scaleFactor != 1.0) {
+ ::CGContextScaleCTM(cgContext, scaleFactor, scaleFactor);
+ // Convert aDirtyRect from device pixels to "display pixels"
+ // for HiDPI modes
+ dirtyRectCopy.ScaleRoundOut(1.0 / scaleFactor);
+ }
+
+ DoCocoaEventDrawRect(dirtyRectCopy, cgContext);
+}
+
+void nsPluginInstanceOwner::DoCocoaEventDrawRect(const gfxRect& aDrawRect,
+ CGContextRef cgContext) {
+ if (!mInstance || !mPluginFrame) return;
+
+ // The context given here is only valid during the HandleEvent call.
+ NPCocoaEvent updateEvent;
+ InitializeNPCocoaEvent(&updateEvent);
+ updateEvent.type = NPCocoaEventDrawRect;
+ updateEvent.data.draw.context = cgContext;
+ updateEvent.data.draw.x = aDrawRect.X();
+ updateEvent.data.draw.y = aDrawRect.Y();
+ updateEvent.data.draw.width = aDrawRect.Width();
+ updateEvent.data.draw.height = aDrawRect.Height();
+
+ mInstance->HandleEvent(&updateEvent, nullptr);
+}
+#endif
+
+#ifdef XP_WIN
+void nsPluginInstanceOwner::Paint(const RECT& aDirty, HDC aDC) {
+ if (!mInstance || !mPluginFrame) return;
+
+ NPEvent pluginEvent;
+ pluginEvent.event = WM_PAINT;
+ pluginEvent.wParam = WPARAM(aDC);
+ pluginEvent.lParam = LPARAM(&aDirty);
+ mInstance->HandleEvent(&pluginEvent, nullptr);
+}
+#endif
+
+#if defined(MOZ_X11)
+void nsPluginInstanceOwner::Paint(gfxContext* aContext,
+ const gfxRect& aFrameRect,
+ const gfxRect& aDirtyRect) {
+ if (!mInstance || !mPluginFrame) return;
+
+ // to provide crisper and faster drawing.
+ gfxRect pluginRect = aFrameRect;
+ if (aContext->UserToDevicePixelSnapped(pluginRect)) {
+ pluginRect = aContext->DeviceToUser(pluginRect);
+ }
+
+ // Round out the dirty rect to plugin pixels to ensure the plugin draws
+ // enough pixels for interpolation to device pixels.
+ gfxRect dirtyRect = aDirtyRect - pluginRect.TopLeft();
+ dirtyRect.RoundOut();
+
+ // Plugins can only draw an integer number of pixels.
+ //
+ // With translation-only transformation matrices, pluginRect is already
+ // pixel-aligned.
+ //
+ // With more complex transformations, modifying the scales in the
+ // transformation matrix could retain subpixel accuracy and let the plugin
+ // draw a suitable number of pixels for interpolation to device pixels in
+ // Renderer::Draw, but such cases are not common enough to warrant the
+ // effort now.
+ nsIntSize pluginSize(NS_lround(pluginRect.width),
+ NS_lround(pluginRect.height));
+
+ // Determine what the plugin needs to draw.
+ nsIntRect pluginDirtyRect(int32_t(dirtyRect.x), int32_t(dirtyRect.y),
+ int32_t(dirtyRect.width),
+ int32_t(dirtyRect.height));
+ if (!pluginDirtyRect.IntersectRect(
+ nsIntRect(0, 0, pluginSize.width, pluginSize.height),
+ pluginDirtyRect))
+ return;
+
+ NPWindow* window;
+ GetWindow(window);
+
+ uint32_t rendererFlags = 0;
+ if (!mFlash10Quirks) {
+ rendererFlags |= Renderer::DRAW_SUPPORTS_CLIP_RECT |
+ Renderer::DRAW_SUPPORTS_ALTERNATE_VISUAL;
+ }
+
+ bool transparent;
+ mInstance->IsTransparent(&transparent);
+ if (!transparent) rendererFlags |= Renderer::DRAW_IS_OPAQUE;
+
+ // Renderer::Draw() draws a rectangle with top-left at the aContext origin.
+ gfxContextAutoSaveRestore autoSR(aContext);
+ aContext->SetMatrixDouble(
+ aContext->CurrentMatrixDouble().PreTranslate(pluginRect.TopLeft()));
+
+ Renderer renderer(window, this, pluginSize, pluginDirtyRect);
+
+ Display* dpy = mozilla::DefaultXDisplay();
+ Screen* screen = DefaultScreenOfDisplay(dpy);
+ Visual* visual = DefaultVisualOfScreen(screen);
+
+ renderer.Draw(aContext, nsIntSize(window->width, window->height),
+ rendererFlags, screen, visual);
+}
+nsresult nsPluginInstanceOwner::Renderer::DrawWithXlib(
+ cairo_surface_t* xsurface, nsIntPoint offset, nsIntRect* clipRects,
+ uint32_t numClipRects) {
+ Screen* screen = cairo_xlib_surface_get_screen(xsurface);
+ Colormap colormap;
+ Visual* visual;
+ if (!gfxXlibSurface::GetColormapAndVisual(xsurface, &colormap, &visual)) {
+ NS_ERROR("Failed to get visual and colormap");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsNPAPIPluginInstance* instance = mInstanceOwner->mInstance;
+ if (!instance) return NS_ERROR_FAILURE;
+
+ // See if the plugin must be notified of new window parameters.
+ bool doupdatewindow = false;
+
+ if (mWindow->x != offset.x || mWindow->y != offset.y) {
+ mWindow->x = offset.x;
+ mWindow->y = offset.y;
+ doupdatewindow = true;
+ }
+
+ if (nsIntSize(mWindow->width, mWindow->height) != mPluginSize) {
+ mWindow->width = mPluginSize.width;
+ mWindow->height = mPluginSize.height;
+ doupdatewindow = true;
+ }
+
+ // The clip rect is relative to drawable top-left.
+ NS_ASSERTION(numClipRects <= 1, "We don't support multiple clip rectangles!");
+ nsIntRect clipRect;
+ if (numClipRects) {
+ clipRect.x = clipRects[0].x;
+ clipRect.y = clipRects[0].y;
+ clipRect.width = clipRects[0].width;
+ clipRect.height = clipRects[0].height;
+ // NPRect members are unsigned, but clip rectangles should be contained by
+ // the surface.
+ NS_ASSERTION(clipRect.x >= 0 && clipRect.y >= 0,
+ "Clip rectangle offsets are negative!");
+ } else {
+ clipRect.x = offset.x;
+ clipRect.y = offset.y;
+ clipRect.width = mWindow->width;
+ clipRect.height = mWindow->height;
+ // Don't ask the plugin to draw outside the drawable.
+ // This also ensures that the unsigned clip rectangle offsets won't be -ve.
+ clipRect.IntersectRect(
+ clipRect, nsIntRect(0, 0, cairo_xlib_surface_get_width(xsurface),
+ cairo_xlib_surface_get_height(xsurface)));
+ }
+
+ NPRect newClipRect;
+ newClipRect.left = clipRect.x;
+ newClipRect.top = clipRect.y;
+ newClipRect.right = clipRect.XMost();
+ newClipRect.bottom = clipRect.YMost();
+ if (mWindow->clipRect.left != newClipRect.left ||
+ mWindow->clipRect.top != newClipRect.top ||
+ mWindow->clipRect.right != newClipRect.right ||
+ mWindow->clipRect.bottom != newClipRect.bottom) {
+ mWindow->clipRect = newClipRect;
+ doupdatewindow = true;
+ }
+
+ NPSetWindowCallbackStruct* ws_info =
+ static_cast<NPSetWindowCallbackStruct*>(mWindow->ws_info);
+# ifdef MOZ_X11
+ if (ws_info->visual != visual || ws_info->colormap != colormap) {
+ ws_info->visual = visual;
+ ws_info->colormap = colormap;
+ ws_info->depth = gfxXlibSurface::DepthOfVisual(screen, visual);
+ doupdatewindow = true;
+ }
+# endif
+
+ {
+ if (doupdatewindow) instance->SetWindow(mWindow);
+ }
+
+ // Translate the dirty rect to drawable coordinates.
+ nsIntRect dirtyRect = mDirtyRect + offset;
+ if (mInstanceOwner->mFlash10Quirks) {
+ // Work around a bug in Flash up to 10.1 d51 at least, where expose event
+ // top left coordinates within the plugin-rect and not at the drawable
+ // origin are misinterpreted. (We can move the top left coordinate
+ // provided it is within the clipRect.)
+ dirtyRect.SetRect(offset.x, offset.y, mDirtyRect.XMost(),
+ mDirtyRect.YMost());
+ }
+ // Intersect the dirty rect with the clip rect to ensure that it lies within
+ // the drawable.
+ if (!dirtyRect.IntersectRect(dirtyRect, clipRect)) return NS_OK;
+
+ {
+ XEvent pluginEvent = XEvent();
+ XGraphicsExposeEvent& exposeEvent = pluginEvent.xgraphicsexpose;
+ // set the drawing info
+ exposeEvent.type = GraphicsExpose;
+ exposeEvent.display = DisplayOfScreen(screen);
+ exposeEvent.drawable = cairo_xlib_surface_get_drawable(xsurface);
+ exposeEvent.x = dirtyRect.x;
+ exposeEvent.y = dirtyRect.y;
+ exposeEvent.width = dirtyRect.width;
+ exposeEvent.height = dirtyRect.height;
+ exposeEvent.count = 0;
+ // information not set:
+ exposeEvent.serial = 0;
+ exposeEvent.send_event = X11False;
+ exposeEvent.major_code = 0;
+ exposeEvent.minor_code = 0;
+
+ instance->HandleEvent(&pluginEvent, nullptr);
+ }
+ return NS_OK;
+}
+#endif
+
+nsresult nsPluginInstanceOwner::Init(nsIContent* aContent) {
+ mLastEventloopNestingLevel = GetEventloopNestingLevel();
+
+ mContent = do_GetWeakReference(aContent);
+
+ // Get a frame, don't reflow. If a reflow was necessary it should have been
+ // done at a higher level than this (content).
+ nsIFrame* frame = aContent->GetPrimaryFrame();
+ nsIObjectFrame* iObjFrame = do_QueryFrame(frame);
+ nsPluginFrame* objFrame = static_cast<nsPluginFrame*>(iObjFrame);
+ if (objFrame) {
+ SetFrame(objFrame);
+ // Some plugins require a specific sequence of shutdown and startup when
+ // a page is reloaded. Shutdown happens usually when the last instance
+ // is destroyed. Here we make sure the plugin instance in the old
+ // document is destroyed before we try to create the new one.
+ objFrame->PresContext()->EnsureVisible();
+ } else {
+ MOZ_ASSERT_UNREACHABLE("Should not be initializing plugin without a frame");
+ return NS_ERROR_FAILURE;
+ }
+
+ // register context menu listener
+ mCXMenuListener = new nsPluginDOMContextMenuListener(aContent);
+
+ aContent->AddEventListener(u"focus"_ns, this, false, false);
+ aContent->AddEventListener(u"blur"_ns, this, false, false);
+ aContent->AddEventListener(u"mouseup"_ns, this, false, false);
+ aContent->AddEventListener(u"mousedown"_ns, this, false, false);
+ aContent->AddEventListener(u"mousemove"_ns, this, false, false);
+ aContent->AddEventListener(u"click"_ns, this, false, false);
+ aContent->AddEventListener(u"dblclick"_ns, this, false, false);
+ aContent->AddEventListener(u"mouseover"_ns, this, false, false);
+ aContent->AddEventListener(u"mouseout"_ns, this, false, false);
+ // "keypress" event should be handled when it's in the default event group
+ // if the event is fired in content. Otherwise, it should be handled when
+ // it's in the system event group.
+ aContent->AddEventListener(u"keypress"_ns, this, true);
+ aContent->AddSystemEventListener(u"keypress"_ns, this, true);
+ aContent->AddEventListener(u"keydown"_ns, this, true);
+ aContent->AddEventListener(u"keyup"_ns, this, true);
+ aContent->AddEventListener(u"drop"_ns, this, true);
+ aContent->AddEventListener(u"drag"_ns, this, true);
+ aContent->AddEventListener(u"dragenter"_ns, this, true);
+ aContent->AddEventListener(u"dragover"_ns, this, true);
+ aContent->AddEventListener(u"dragleave"_ns, this, true);
+ aContent->AddEventListener(u"dragexit"_ns, this, true);
+ aContent->AddEventListener(u"dragstart"_ns, this, true);
+ aContent->AddEventListener(u"dragend"_ns, this, true);
+ aContent->AddSystemEventListener(u"compositionstart"_ns, this, true);
+ aContent->AddSystemEventListener(u"compositionend"_ns, this, true);
+ aContent->AddSystemEventListener(u"text"_ns, this, true);
+
+ return NS_OK;
+}
+
+void* nsPluginInstanceOwner::GetPluginPort() {
+ void* result = nullptr;
+ if (mWidget) {
+#ifdef XP_WIN
+ if (!mPluginWindow || mPluginWindow->type == NPWindowTypeWindow)
+#endif
+ result =
+ mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT); // HWND/gdk window
+ }
+
+ return result;
+}
+
+void nsPluginInstanceOwner::ReleasePluginPort(void* pluginPort) {}
+
+NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void) {
+ NS_ENSURE_TRUE(mPluginWindow, NS_ERROR_NULL_POINTER);
+
+ // Can't call this twice!
+ if (mWidget) {
+ NS_WARNING("Trying to create a plugin widget twice!");
+ return NS_ERROR_FAILURE;
+ }
+
+ bool windowless = false;
+ mInstance->IsWindowless(&windowless);
+ if (!windowless) {
+#ifndef XP_WIN
+ // Only Windows supports windowed mode!
+ MOZ_ASSERT_UNREACHABLE();
+ return NS_ERROR_FAILURE;
+#else
+ // Try to get a parent widget, on some platforms widget creation will fail
+ // without a parent.
+ nsresult rv = NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIWidget> parentWidget;
+ Document* doc = nullptr;
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (content) {
+ doc = content->OwnerDoc();
+ parentWidget = nsContentUtils::WidgetForDocument(doc);
+ // If we're running in the content process, we need a remote widget
+ // created in chrome.
+ if (XRE_IsContentProcess()) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> window = doc->GetWindow()) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> topWindow =
+ window->GetInProcessTop()) {
+ dom::BrowserChild* tc = dom::BrowserChild::GetFrom(topWindow);
+ if (tc) {
+ // This returns a PluginWidgetProxy which remotes a number of
+ // calls.
+ rv = tc->CreatePluginWidget(parentWidget.get(),
+ getter_AddRefs(mWidget));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // A failure here is terminal since we can't fall back on the non-e10s code
+ // path below.
+ if (!mWidget && XRE_IsContentProcess()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!mWidget) {
+ // native (single process)
+ mWidget = nsIWidget::CreateChildWindow();
+ nsWidgetInitData initData;
+ initData.mWindowType = eWindowType_plugin;
+ initData.clipChildren = true;
+ initData.clipSiblings = true;
+ rv = mWidget->Create(parentWidget.get(), nullptr,
+ LayoutDeviceIntRect(0, 0, 0, 0), &initData);
+ if (NS_FAILED(rv)) {
+ mWidget->Destroy();
+ mWidget = nullptr;
+ return rv;
+ }
+ }
+
+ mWidget->EnableDragDrop(true);
+ mWidget->Show(false);
+ mWidget->Enable(false);
+#endif // XP_WIN
+ }
+
+ if (mPluginFrame) {
+ // nullptr widget is fine, will result in windowless setup.
+ mPluginFrame->PrepForDrawing(mWidget);
+ }
+
+ if (windowless) {
+ mPluginWindow->type = NPWindowTypeDrawable;
+
+ // this needs to be a HDC according to the spec, but I do
+ // not see the right way to release it so let's postpone
+ // passing HDC till paint event when it is really
+ // needed. Change spec?
+ mPluginWindow->window = nullptr;
+#ifdef MOZ_X11
+ // Fill in the display field.
+ NPSetWindowCallbackStruct* ws_info =
+ static_cast<NPSetWindowCallbackStruct*>(mPluginWindow->ws_info);
+ ws_info->display = DefaultXDisplay();
+
+ nsAutoCString description;
+ GetPluginDescription(description);
+ constexpr auto flash10Head = "Shockwave Flash 10."_ns;
+ mFlash10Quirks = StringBeginsWith(description, flash10Head);
+#endif
+ } else if (mWidget) {
+ // mPluginWindow->type is used in |GetPluginPort| so it must
+ // be initialized first
+ mPluginWindow->type = NPWindowTypeWindow;
+ mPluginWindow->window = GetPluginPort();
+ // tell the plugin window about the widget
+ mPluginWindow->SetPluginWidget(mWidget);
+
+ // tell the widget about the current plugin instance owner.
+ nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
+ if (pluginWidget) {
+ pluginWidget->SetPluginInstanceOwner(this);
+ }
+ }
+
+#ifdef XP_MACOSX
+ if (GetDrawingModel() == NPDrawingModelCoreAnimation) {
+ AddToCARefreshTimer();
+ }
+#endif
+
+ mWidgetCreationComplete = true;
+
+ return NS_OK;
+}
+
+// Mac specific code to fix up the port location and clipping region
+#ifdef XP_MACOSX
+
+void nsPluginInstanceOwner::FixUpPluginWindow(int32_t inPaintState) {
+ if (!mPluginWindow || !mInstance || !mPluginFrame) {
+ return;
+ }
+
+ SetPluginPort();
+
+ LayoutDeviceIntSize widgetClip = mPluginFrame->GetWidgetlessClipRect().Size();
+
+ mPluginWindow->x = 0;
+ mPluginWindow->y = 0;
+
+ NPRect oldClipRect = mPluginWindow->clipRect;
+
+ // fix up the clipping region
+ mPluginWindow->clipRect.top = 0;
+ mPluginWindow->clipRect.left = 0;
+
+ if (inPaintState == ePluginPaintDisable) {
+ mPluginWindow->clipRect.bottom = mPluginWindow->clipRect.top;
+ mPluginWindow->clipRect.right = mPluginWindow->clipRect.left;
+ } else if (inPaintState == ePluginPaintEnable) {
+ mPluginWindow->clipRect.bottom =
+ mPluginWindow->clipRect.top + widgetClip.height;
+ mPluginWindow->clipRect.right =
+ mPluginWindow->clipRect.left + widgetClip.width;
+ }
+
+ // if the clip rect changed, call SetWindow()
+ // (RealPlayer needs this to draw correctly)
+ if (mPluginWindow->clipRect.left != oldClipRect.left ||
+ mPluginWindow->clipRect.top != oldClipRect.top ||
+ mPluginWindow->clipRect.right != oldClipRect.right ||
+ mPluginWindow->clipRect.bottom != oldClipRect.bottom) {
+ if (UseAsyncRendering()) {
+ mInstance->AsyncSetWindow(mPluginWindow);
+ } else {
+ mPluginWindow->CallSetWindow(mInstance);
+ }
+ }
+
+ // After the first NPP_SetWindow call we need to send an initial
+ // top-level window focus event.
+ if (!mSentInitialTopLevelWindowEvent) {
+ // Set this before calling ProcessEvent to avoid endless recursion.
+ mSentInitialTopLevelWindowEvent = true;
+
+ bool isActive = WindowIsActive();
+ SendWindowFocusChanged(isActive);
+ mLastWindowIsActive = isActive;
+ }
+}
+
+void nsPluginInstanceOwner::WindowFocusMayHaveChanged() {
+ if (!mSentInitialTopLevelWindowEvent) {
+ return;
+ }
+
+ bool isActive = WindowIsActive();
+ if (isActive != mLastWindowIsActive) {
+ SendWindowFocusChanged(isActive);
+ mLastWindowIsActive = isActive;
+ }
+}
+
+bool nsPluginInstanceOwner::WindowIsActive() {
+ if (!mPluginFrame) {
+ return false;
+ }
+
+ EventStates docState =
+ mPluginFrame->GetContent()->OwnerDoc()->GetDocumentState();
+ return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
+}
+
+void nsPluginInstanceOwner::SendWindowFocusChanged(bool aIsActive) {
+ if (!mInstance) {
+ return;
+ }
+
+ NPCocoaEvent cocoaEvent;
+ InitializeNPCocoaEvent(&cocoaEvent);
+ cocoaEvent.type = NPCocoaEventWindowFocusChanged;
+ cocoaEvent.data.focus.hasFocus = aIsActive;
+ mInstance->HandleEvent(&cocoaEvent, nullptr,
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+}
+
+void nsPluginInstanceOwner::HidePluginWindow() {
+ if (!mPluginWindow || !mInstance) {
+ return;
+ }
+
+ mPluginWindow->clipRect.bottom = mPluginWindow->clipRect.top;
+ mPluginWindow->clipRect.right = mPluginWindow->clipRect.left;
+ mWidgetVisible = false;
+ if (UseAsyncRendering()) {
+ mInstance->AsyncSetWindow(mPluginWindow);
+ } else {
+ mInstance->SetWindow(mPluginWindow);
+ }
+}
+
+#else // XP_MACOSX
+
+void nsPluginInstanceOwner::UpdateWindowPositionAndClipRect(bool aSetWindow) {
+ if (!mPluginWindow) return;
+
+ // For windowless plugins a non-empty clip rectangle will be
+ // passed to the plugin during paint, an additional update
+ // of the the clip rectangle here is not required
+ if (aSetWindow && !mWidget && mPluginWindowVisible && !UseAsyncRendering())
+ return;
+
+ const NPWindow oldWindow = *mPluginWindow;
+
+ bool windowless = (mPluginWindow->type == NPWindowTypeDrawable);
+ nsIntPoint origin = mPluginFrame->GetWindowOriginInPixels(windowless);
+
+ mPluginWindow->x = origin.x;
+ mPluginWindow->y = origin.y;
+
+ mPluginWindow->clipRect.left = 0;
+ mPluginWindow->clipRect.top = 0;
+
+ if (mPluginWindowVisible && mPluginDocumentActiveState) {
+ mPluginWindow->clipRect.right = mPluginWindow->width;
+ mPluginWindow->clipRect.bottom = mPluginWindow->height;
+ } else {
+ mPluginWindow->clipRect.right = 0;
+ mPluginWindow->clipRect.bottom = 0;
+ }
+
+ if (!aSetWindow) return;
+
+ if (mPluginWindow->x != oldWindow.x || mPluginWindow->y != oldWindow.y ||
+ mPluginWindow->clipRect.left != oldWindow.clipRect.left ||
+ mPluginWindow->clipRect.top != oldWindow.clipRect.top ||
+ mPluginWindow->clipRect.right != oldWindow.clipRect.right ||
+ mPluginWindow->clipRect.bottom != oldWindow.clipRect.bottom) {
+ CallSetWindow();
+ }
+}
+
+void nsPluginInstanceOwner::UpdateWindowVisibility(bool aVisible) {
+ mPluginWindowVisible = aVisible;
+ UpdateWindowPositionAndClipRect(true);
+}
+#endif // XP_MACOSX
+
+void nsPluginInstanceOwner::ResolutionMayHaveChanged() {
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ double scaleFactor = 1.0;
+ GetContentsScaleFactor(&scaleFactor);
+ if (scaleFactor != mLastScaleFactor) {
+ ContentsScaleFactorChanged(scaleFactor);
+ mLastScaleFactor = scaleFactor;
+ }
+#endif
+ float zoomFactor = 1.0;
+ GetCSSZoomFactor(&zoomFactor);
+ if (zoomFactor != mLastCSSZoomFactor) {
+ if (mInstance) {
+ mInstance->CSSZoomFactorChanged(zoomFactor);
+ }
+ mLastCSSZoomFactor = zoomFactor;
+ }
+}
+
+void nsPluginInstanceOwner::UpdateDocumentActiveState(bool aIsActive) {
+ AUTO_PROFILER_LABEL("nsPluginInstanceOwner::UpdateDocumentActiveState",
+ OTHER);
+
+ mPluginDocumentActiveState = aIsActive;
+#ifndef XP_MACOSX
+ UpdateWindowPositionAndClipRect(true);
+
+ // We don't have a connection to PluginWidgetParent in the chrome
+ // process when dealing with tab visibility changes, so this needs
+ // to be forwarded over after the active state is updated. If we
+ // don't hide plugin widgets in hidden tabs, the native child window
+ // in chrome will remain visible after a tab switch.
+ if (mWidget && XRE_IsContentProcess()) {
+ mWidget->Show(aIsActive);
+ mWidget->Enable(aIsActive);
+ }
+#endif // #ifndef XP_MACOSX
+}
+
+NS_IMETHODIMP
+nsPluginInstanceOwner::CallSetWindow() {
+ if (!mWidgetCreationComplete) {
+ // No widget yet, we can't run this code
+ return NS_OK;
+ }
+ if (mPluginFrame) {
+ mPluginFrame->CallSetWindow(false);
+ } else if (mInstance) {
+ if (UseAsyncRendering()) {
+ mInstance->AsyncSetWindow(mPluginWindow);
+ } else {
+ mInstance->SetWindow(mPluginWindow);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginInstanceOwner::GetContentsScaleFactor(double* result) {
+ NS_ENSURE_ARG_POINTER(result);
+ double scaleFactor = 1.0;
+ // On Mac, device pixels need to be translated to (and from) "display pixels"
+ // for plugins. On other platforms, plugin coordinates are always in device
+ // pixels.
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ PresShell* presShell =
+ nsContentUtils::FindPresShellForDocument(content->OwnerDoc());
+ if (presShell) {
+ scaleFactor = double(AppUnitsPerCSSPixel()) /
+ presShell->GetPresContext()
+ ->DeviceContext()
+ ->AppUnitsPerDevPixelAtUnitFullZoom();
+ }
+#endif
+ *result = scaleFactor;
+ return NS_OK;
+}
+
+void nsPluginInstanceOwner::GetCSSZoomFactor(float* result) {
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ PresShell* presShell =
+ nsContentUtils::FindPresShellForDocument(content->OwnerDoc());
+ if (presShell) {
+ *result = presShell->GetPresContext()->DeviceContext()->GetFullZoom();
+ } else {
+ *result = 1.0;
+ }
+}
+
+void nsPluginInstanceOwner::SetFrame(nsPluginFrame* aFrame) {
+ // Don't do anything if the frame situation hasn't changed.
+ if (mPluginFrame == aFrame) {
+ return;
+ }
+
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+
+ // If we already have a frame that is changing or going away...
+ if (mPluginFrame) {
+ if (content && content->OwnerDoc()->GetWindow()) {
+ nsCOMPtr<EventTarget> windowRoot =
+ content->OwnerDoc()->GetWindow()->GetTopWindowRoot();
+ if (windowRoot) {
+ windowRoot->RemoveEventListener(u"activate"_ns, this, false);
+ windowRoot->RemoveEventListener(u"deactivate"_ns, this, false);
+ windowRoot->RemoveEventListener(u"MozPerformDelayedBlur"_ns, this,
+ false);
+ }
+ }
+
+ // Make sure the old frame isn't holding a reference to us.
+ mPluginFrame->SetInstanceOwner(nullptr);
+ }
+
+ // Swap in the new frame (or no frame)
+ mPluginFrame = aFrame;
+
+ // Set up a new frame
+ if (mPluginFrame) {
+ mPluginFrame->SetInstanceOwner(this);
+ // Can only call PrepForDrawing on an object frame once. Don't do it here
+ // unless widget creation is complete. Doesn't matter if we actually have a
+ // widget.
+ if (mWidgetCreationComplete) {
+ mPluginFrame->PrepForDrawing(mWidget);
+ }
+ mPluginFrame->FixupWindow(
+ mPluginFrame->GetContentRectRelativeToSelf().Size());
+ mPluginFrame->InvalidateFrame();
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ const nsIContent* content = aFrame->GetContent();
+ if (fm && content) {
+ mContentFocused = (content == fm->GetFocusedElement());
+ }
+
+ // Register for widget-focus events on the window root.
+ if (content && content->OwnerDoc()->GetWindow()) {
+ nsCOMPtr<EventTarget> windowRoot =
+ content->OwnerDoc()->GetWindow()->GetTopWindowRoot();
+ if (windowRoot) {
+ windowRoot->AddEventListener(u"activate"_ns, this, false, false);
+ windowRoot->AddEventListener(u"deactivate"_ns, this, false, false);
+ windowRoot->AddEventListener(u"MozPerformDelayedBlur"_ns, this, false,
+ false);
+ }
+ }
+ }
+}
+
+nsPluginFrame* nsPluginInstanceOwner::GetFrame() { return mPluginFrame; }
+
+NS_IMETHODIMP nsPluginInstanceOwner::PrivateModeChanged(bool aEnabled) {
+ return mInstance ? mInstance->PrivateModeStateChanged(aEnabled) : NS_OK;
+}
+
+nsIURI* nsPluginInstanceOwner::GetBaseURI() const {
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (!content) {
+ return nullptr;
+ }
+ return content->GetBaseURI();
+}
+
+// static
+void nsPluginInstanceOwner::GeneratePluginEvent(
+ const WidgetCompositionEvent* aSrcCompositionEvent,
+ WidgetCompositionEvent* aDistCompositionEvent) {
+#ifdef XP_WIN
+ NPEvent newEvent;
+ switch (aDistCompositionEvent->mMessage) {
+ case eCompositionChange: {
+ newEvent.event = WM_IME_COMPOSITION;
+ newEvent.wParam = 0;
+ if (aSrcCompositionEvent &&
+ (aSrcCompositionEvent->mMessage == eCompositionCommit ||
+ aSrcCompositionEvent->mMessage == eCompositionCommitAsIs)) {
+ newEvent.lParam = GCS_RESULTSTR;
+ } else {
+ newEvent.lParam = GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE;
+ }
+ TextRangeArray* ranges = aDistCompositionEvent->mRanges;
+ if (ranges && ranges->HasCaret()) {
+ newEvent.lParam |= GCS_CURSORPOS;
+ }
+ break;
+ }
+
+ case eCompositionStart:
+ newEvent.event = WM_IME_STARTCOMPOSITION;
+ newEvent.wParam = 0;
+ newEvent.lParam = 0;
+ break;
+
+ case eCompositionEnd:
+ newEvent.event = WM_IME_ENDCOMPOSITION;
+ newEvent.wParam = 0;
+ newEvent.lParam = 0;
+ break;
+
+ default:
+ return;
+ }
+ aDistCompositionEvent->mPluginEvent.Copy(newEvent);
+#endif
+}
+
+// nsPluginDOMContextMenuListener class implementation
+
+nsPluginDOMContextMenuListener::nsPluginDOMContextMenuListener(
+ nsIContent* aContent) {
+ aContent->AddEventListener(u"contextmenu"_ns, this, true);
+}
+
+nsPluginDOMContextMenuListener::~nsPluginDOMContextMenuListener() = default;
+
+NS_IMPL_ISUPPORTS(nsPluginDOMContextMenuListener, nsIDOMEventListener)
+
+NS_IMETHODIMP
+nsPluginDOMContextMenuListener::HandleEvent(Event* aEvent) {
+ aEvent->PreventDefault(); // consume event
+
+ return NS_OK;
+}
+
+void nsPluginDOMContextMenuListener::Destroy(nsIContent* aContent) {
+ // Unregister context menu listener
+ aContent->RemoveEventListener(u"contextmenu"_ns, this, true);
+}
diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h
new file mode 100644
index 0000000000..a868c22f49
--- /dev/null
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+/* 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/. */
+
+#ifndef nsPluginInstanceOwner_h_
+#define nsPluginInstanceOwner_h_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/HTMLObjectElementBinding.h"
+#include "npapi.h"
+#include "nsCOMPtr.h"
+#include "nsIKeyEventInPluginCallback.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsIPrivacyTransitionObserver.h"
+#include "nsIDOMEventListener.h"
+#include "nsPluginHost.h"
+#include "nsPluginNativeWindow.h"
+#include "nsWeakReference.h"
+#include "gfxRect.h"
+
+#ifdef XP_MACOSX
+# include "mozilla/gfx/QuartzSupport.h"
+# include <ApplicationServices/ApplicationServices.h>
+#endif
+
+class nsIInputStream;
+class nsPluginDOMContextMenuListener;
+class nsPluginFrame;
+class nsDisplayListBuilder;
+
+#if defined(MOZ_X11)
+class gfxContext;
+#endif
+
+namespace mozilla {
+class TextComposition;
+namespace dom {
+class Element;
+class Event;
+} // namespace dom
+namespace widget {
+class PuppetWidget;
+} // namespace widget
+} // namespace mozilla
+
+using mozilla::widget::PuppetWidget;
+
+#ifdef MOZ_X11
+# include "gfxXlibNativeRenderer.h"
+#endif
+
+class nsPluginInstanceOwner final : public nsIPluginInstanceOwner,
+ public nsIDOMEventListener,
+ public nsIPrivacyTransitionObserver,
+ public nsIKeyEventInPluginCallback,
+ public nsSupportsWeakReference {
+ public:
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ nsPluginInstanceOwner();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPLUGININSTANCEOWNER
+ NS_DECL_NSIPRIVACYTRANSITIONOBSERVER
+
+ NS_IMETHOD GetURL(const char* aURL, const char* aTarget,
+ nsIInputStream* aPostStream, void* aHeadersData,
+ uint32_t aHeadersDataLen,
+ bool aDoCheckLoadURIChecks) override;
+
+ NPBool ConvertPoint(double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace, double* destX,
+ double* destY, NPCoordinateSpace destSpace) override;
+
+ NPError InitAsyncSurface(NPSize* size, NPImageFormat format, void* initData,
+ NPAsyncSurface* surface) override;
+ NPError FinalizeAsyncSurface(NPAsyncSurface* surface) override;
+ void SetCurrentAsyncSurface(NPAsyncSurface* surface,
+ NPRect* changed) override;
+
+ /**
+ * Get the type of the HTML tag that was used to instantiate this
+ * plugin. Currently supported tags are EMBED or OBJECT.
+ */
+ NS_IMETHOD GetTagType(nsPluginTagType* aResult);
+
+ void GetParameters(nsTArray<mozilla::dom::MozPluginParameter>& parameters);
+ void GetAttributes(nsTArray<mozilla::dom::MozPluginParameter>& attributes);
+
+ /**
+ * Returns the DOM element corresponding to the tag which references
+ * this plugin in the document.
+ *
+ * @param aDOMElement - resulting DOM element
+ * @result - NS_OK if this operation was successful
+ */
+ NS_IMETHOD GetDOMElement(mozilla::dom::Element** aResult);
+
+ // nsIDOMEventListener interfaces
+ NS_DECL_NSIDOMEVENTLISTENER
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
+ ProcessMouseDown(mozilla::dom::Event* aMouseEvent);
+ nsresult ProcessKeyPress(mozilla::dom::Event* aKeyEvent);
+ nsresult Destroy();
+
+#ifdef XP_WIN
+ void Paint(const RECT& aDirty, HDC aDC);
+#elif defined(XP_MACOSX)
+ void Paint(const gfxRect& aDirtyRect, CGContextRef cgContext);
+ void RenderCoreAnimation(CGContextRef aCGContext, int aWidth, int aHeight);
+ void DoCocoaEventDrawRect(const gfxRect& aDrawRect, CGContextRef cgContext);
+#elif defined(MOZ_X11)
+ void Paint(gfxContext* aContext, const gfxRect& aFrameRect,
+ const gfxRect& aDirtyRect);
+#endif
+
+ // locals
+
+ nsresult Init(nsIContent* aContent);
+
+ void* GetPluginPort();
+ void ReleasePluginPort(void* pluginPort);
+
+ nsEventStatus ProcessEvent(const mozilla::WidgetGUIEvent& anEvent);
+
+ static void GeneratePluginEvent(
+ const mozilla::WidgetCompositionEvent* aSrcCompositionEvent,
+ mozilla::WidgetCompositionEvent* aDistCompositionEvent);
+
+#if defined(XP_WIN)
+ void SetWidgetWindowAsParent(HWND aWindowToAdopt);
+ nsresult SetNetscapeWindowAsParent(HWND aWindowToAdopt);
+#endif
+
+#ifdef XP_MACOSX
+ enum {ePluginPaintEnable, ePluginPaintDisable};
+
+ void WindowFocusMayHaveChanged();
+
+ bool WindowIsActive();
+ void SendWindowFocusChanged(bool aIsActive);
+ NPDrawingModel GetDrawingModel();
+ bool IsRemoteDrawingCoreAnimation();
+
+ NPEventModel GetEventModel();
+ static void CARefresh(nsITimer* aTimer, void* aClosure);
+ void AddToCARefreshTimer();
+ void RemoveFromCARefreshTimer();
+ // This calls into the plugin (NPP_SetWindow) and can run script.
+ void FixUpPluginWindow(int32_t inPaintState);
+ void HidePluginWindow();
+ // Set plugin port info in the plugin (in the 'window' member of the
+ // NPWindow structure passed to the plugin by SetWindow()).
+ void SetPluginPort();
+#else // XP_MACOSX
+ void UpdateWindowPositionAndClipRect(bool aSetWindow);
+ void UpdateWindowVisibility(bool aVisible);
+#endif // XP_MACOSX
+
+ void ResolutionMayHaveChanged();
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
+#endif
+
+ void UpdateDocumentActiveState(bool aIsActive);
+
+ void SetFrame(nsPluginFrame* aFrame);
+ nsPluginFrame* GetFrame();
+
+ uint32_t GetLastEventloopNestingLevel() const {
+ return mLastEventloopNestingLevel;
+ }
+
+ static uint32_t GetEventloopNestingLevel();
+
+ void ConsiderNewEventloopNestingLevel() {
+ uint32_t currentLevel = GetEventloopNestingLevel();
+
+ if (currentLevel < mLastEventloopNestingLevel) {
+ mLastEventloopNestingLevel = currentLevel;
+ }
+ }
+
+ const char* GetPluginName() {
+ if (mInstance && mPluginHost) {
+ const char* name = nullptr;
+ if (NS_SUCCEEDED(mPluginHost->GetPluginName(mInstance, &name)) && name)
+ return name;
+ }
+ return "";
+ }
+
+#ifdef MOZ_X11
+ void GetPluginDescription(nsACString& aDescription) {
+ aDescription.Truncate();
+ if (mInstance && mPluginHost) {
+ nsCOMPtr<nsIPluginTag> pluginTag;
+
+ mPluginHost->GetPluginTagForInstance(mInstance,
+ getter_AddRefs(pluginTag));
+ if (pluginTag) {
+ pluginTag->GetDescription(aDescription);
+ }
+ }
+ }
+#endif
+
+ bool SendNativeEvents() {
+#ifdef XP_WIN
+ // XXX we should remove the plugin name check
+ return mPluginWindow->type == NPWindowTypeDrawable &&
+ (MatchPluginName("Shockwave Flash") ||
+ MatchPluginName("Test Plug-in"));
+#elif defined(MOZ_X11) || defined(XP_MACOSX)
+ return true;
+#else
+ return false;
+#endif
+ }
+
+ bool MatchPluginName(const char* aPluginName) {
+ return strncmp(GetPluginName(), aPluginName, strlen(aPluginName)) == 0;
+ }
+
+ void NotifyPaintWaiter(nsDisplayListBuilder* aBuilder);
+
+ // Returns the image container that has our currently displayed image.
+ already_AddRefed<mozilla::layers::ImageContainer> GetImageContainer();
+ // Returns true if this is windowed plugin that can return static captures
+ // for scroll operations.
+ bool NeedsScrollImageLayer();
+
+ void DidComposite();
+
+ /**
+ * Returns the bounds of the current async-rendered surface. This can only
+ * change in response to messages received by the event loop (i.e. not during
+ * painting).
+ */
+ nsIntSize GetCurrentImageSize();
+
+ // Methods to update the background image we send to async plugins.
+ // The eventual target of these operations is PluginInstanceParent,
+ // but it takes several hops to get there.
+ void SetBackgroundUnknown();
+ already_AddRefed<DrawTarget> BeginUpdateBackground(const nsIntRect& aRect);
+ void EndUpdateBackground(const nsIntRect& aRect);
+
+ bool UseAsyncRendering();
+
+ nsIURI* GetBaseURI() const;
+
+ bool GetCompositionString(uint32_t aIndex, nsTArray<uint8_t>* aString,
+ int32_t* aLength);
+ bool RequestCommitOrCancel(bool aCommitted);
+
+ // See nsIKeyEventInPluginCallback
+ virtual void HandledWindowedPluginKeyEvent(
+ const mozilla::NativeEventData& aKeyEventData, bool aIsConsumed) override;
+
+ /**
+ * OnWindowedPluginKeyEvent() is called when the plugin process receives
+ * native key event directly.
+ *
+ * @param aNativeKeyData The key event which was received by the
+ * plugin process directly.
+ */
+ void OnWindowedPluginKeyEvent(const mozilla::NativeEventData& aNativeKeyData);
+
+ void GetCSSZoomFactor(float* result);
+
+ private:
+ virtual ~nsPluginInstanceOwner();
+
+ // return FALSE if LayerSurface dirty (newly created and don't have valid
+ // plugin content yet)
+ bool IsUpToDate() {
+ nsIntSize size;
+ return NS_SUCCEEDED(mInstance->GetImageSize(&size)) &&
+ size == nsIntSize(mPluginWindow->width, mPluginWindow->height);
+ }
+
+#if defined(XP_WIN)
+ nsIWidget* GetContainingWidgetIfOffset();
+ already_AddRefed<mozilla::TextComposition> GetTextComposition();
+ void HandleNoConsumedCompositionMessage(
+ mozilla::WidgetCompositionEvent* aCompositionEvent,
+ const NPEvent* aPluginEvent);
+ bool mGotCompositionData;
+ bool mSentStartComposition;
+ bool mPluginDidNotHandleIMEComposition;
+ uint32_t mWheelScrollLines;
+ uint32_t mWheelScrollChars;
+#endif
+
+ nsPluginNativeWindow* mPluginWindow;
+ RefPtr<nsNPAPIPluginInstance> mInstance;
+ nsPluginFrame* mPluginFrame;
+ nsWeakPtr mContent; // WEAK, content owns us
+ nsCString mDocumentBase;
+ bool mWidgetCreationComplete;
+ nsCOMPtr<nsIWidget> mWidget;
+ RefPtr<nsPluginHost> mPluginHost;
+
+#ifdef XP_MACOSX
+ static mozilla::StaticRefPtr<nsITimer> sCATimer;
+ static nsTArray<nsPluginInstanceOwner*>* sCARefreshListeners;
+ bool mSentInitialTopLevelWindowEvent;
+ bool mLastWindowIsActive;
+ bool mLastContentFocused;
+ // True if, the next time the window is activated, we should blur ourselves.
+ bool mShouldBlurOnActivate;
+#endif
+ double mLastScaleFactor;
+ double mLastCSSZoomFactor;
+ // Initially, the event loop nesting level we were created on, it's updated
+ // if we detect the appshell is on a lower level as long as we're not stopped.
+ // We delay DoStopPlugin() until the appshell reaches this level or lower.
+ uint32_t mLastEventloopNestingLevel;
+ bool mContentFocused;
+ bool mWidgetVisible; // used on Mac to store our widget's visible state
+#ifdef MOZ_X11
+ // Used with windowless plugins only, initialized in CreateWidget().
+ bool mFlash10Quirks;
+#endif
+ bool mPluginWindowVisible;
+ bool mPluginDocumentActiveState;
+
+#ifdef XP_MACOSX
+ NPEventModel mEventModel;
+ // This is a hack! UseAsyncRendering() can incorrectly return false
+ // when we don't have an object frame (possible as of bug 90268).
+ // We hack around this by always returning true if we've ever
+ // returned true.
+ bool mUseAsyncRendering;
+#endif
+
+ // pointer to wrapper for nsIDOMContextMenuListener
+ RefPtr<nsPluginDOMContextMenuListener> mCXMenuListener;
+
+ nsresult DispatchKeyToPlugin(mozilla::dom::Event* aKeyEvent);
+ nsresult DispatchMouseToPlugin(mozilla::dom::Event* aMouseEvent,
+ bool aAllowPropagate = false);
+ nsresult DispatchFocusToPlugin(mozilla::dom::Event* aFocusEvent);
+ nsresult DispatchCompositionToPlugin(mozilla::dom::Event* aEvent);
+
+#ifdef XP_WIN
+ void CallDefaultProc(const mozilla::WidgetGUIEvent* aEvent);
+#endif
+
+#ifdef XP_MACOSX
+ static NPBool ConvertPointPuppet(PuppetWidget* widget,
+ nsPluginFrame* pluginFrame, double sourceX,
+ double sourceY,
+ NPCoordinateSpace sourceSpace, double* destX,
+ double* destY, NPCoordinateSpace destSpace);
+ static NPBool ConvertPointNoPuppet(nsIWidget* widget,
+ nsPluginFrame* pluginFrame, double sourceX,
+ double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double* destX, double* destY,
+ NPCoordinateSpace destSpace);
+ void PerformDelayedBlurs();
+#endif // XP_MACOSX
+
+ int mLastMouseDownButtonType;
+
+#ifdef MOZ_X11
+ class Renderer : public gfxXlibNativeRenderer {
+ public:
+ Renderer(NPWindow* aWindow, nsPluginInstanceOwner* aInstanceOwner,
+ const nsIntSize& aPluginSize, const nsIntRect& aDirtyRect)
+ : mWindow(aWindow),
+ mInstanceOwner(aInstanceOwner),
+ mPluginSize(aPluginSize),
+ mDirtyRect(aDirtyRect) {}
+ virtual nsresult DrawWithXlib(cairo_surface_t* surface, nsIntPoint offset,
+ nsIntRect* clipRects,
+ uint32_t numClipRects) override;
+
+ private:
+ NPWindow* mWindow;
+ nsPluginInstanceOwner* mInstanceOwner;
+ const nsIntSize& mPluginSize;
+ const nsIntRect& mDirtyRect;
+ };
+#endif
+
+ bool mWaitingForPaint;
+};
+
+#endif // nsPluginInstanceOwner_h_
diff --git a/dom/plugins/base/nsPluginLogging.h b/dom/plugins/base/nsPluginLogging.h
new file mode 100644
index 0000000000..53c76bb25d
--- /dev/null
+++ b/dom/plugins/base/nsPluginLogging.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* Plugin Module Logging usage instructions and includes */
+////////////////////////////////////////////////////////////////////////////////
+#ifndef nsPluginLogging_h__
+#define nsPluginLogging_h__
+
+#include "mozilla/Logging.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Basic Plugin Logging Usage Instructions
+//
+// 1. Set this environment variable: MOZ_LOG=<name>:<level>
+
+// Choose the <name> and <level> from this list (no quotes):
+
+// Log Names <name>
+#define NPN_LOG_NAME "PluginNPN"
+#define NPP_LOG_NAME "PluginNPP"
+#define PLUGIN_LOG_NAME "Plugin"
+
+// Levels <level>
+#define PLUGIN_LOG_ALWAYS mozilla::LogLevel::Error
+#define PLUGIN_LOG_BASIC mozilla::LogLevel::Info
+#define PLUGIN_LOG_NORMAL mozilla::LogLevel::Debug
+#define PLUGIN_LOG_NOISY mozilla::LogLevel::Verbose
+
+// 2. You can combine logs and levels by separating them with a comma:
+// My favorite Win32 Example: SET MOZ_LOG=Plugin:5,PluginNPP:5,PluginNPN:5
+
+// 3. Instead of output going to the console, you can log to a file.
+// Additionally, set the MOZ_LOG_FILE environment variable to point to the
+// full path of a file.
+// My favorite Win32 Example: SET MOZ_LOG_FILE=c:\temp\pluginLog.txt
+
+// 4. For complete information see the Gecko Developer guide:
+// https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Gecko_Logging
+
+class nsPluginLogging {
+ public:
+ static mozilla::LazyLogModule gNPNLog; // 4.x NP API, calls into navigator
+ static mozilla::LazyLogModule gNPPLog; // 4.x NP API, calls into plugin
+ static mozilla::LazyLogModule gPluginLog; // general plugin log
+};
+
+// Quick-use macros
+#define NPN_PLUGIN_LOG(a, b) MOZ_LOG(nsPluginLogging::gNPNLog, a, b)
+#define NPP_PLUGIN_LOG(a, b) MOZ_LOG(nsPluginLogging::gNPPLog, a, b)
+#define PLUGIN_LOG(a, b) MOZ_LOG(nsPluginLogging::gPluginLog, a, b)
+
+#endif // nsPluginLogging_h__
diff --git a/dom/plugins/base/nsPluginManifestLineReader.h b/dom/plugins/base/nsPluginManifestLineReader.h
new file mode 100644
index 0000000000..4e2f6f3d54
--- /dev/null
+++ b/dom/plugins/base/nsPluginManifestLineReader.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsPluginManifestLineReader_h_
+#define nsPluginManifestLineReader_h_
+
+#include "nspr.h"
+#include "nsDebug.h"
+
+#ifdef XP_WIN
+# define PLUGIN_REGISTRY_FIELD_DELIMITER '|'
+#else
+# define PLUGIN_REGISTRY_FIELD_DELIMITER ':'
+#endif
+
+#define PLUGIN_REGISTRY_END_OF_LINE_MARKER '$'
+
+class nsPluginManifestLineReader {
+ public:
+ nsPluginManifestLineReader() : mLength(0) {
+ mBase = mCur = mNext = mLimit = 0;
+ }
+ ~nsPluginManifestLineReader() {
+ if (mBase) delete[] mBase;
+ mBase = 0;
+ }
+
+ char* Init(uint32_t flen) {
+ mBase = mCur = mNext = new char[flen + 1];
+ if (mBase) {
+ mLimit = mBase + flen;
+ *mLimit = 0;
+ }
+ mLength = 0;
+ return mBase;
+ }
+
+ bool NextLine() {
+ if (mNext >= mLimit) return false;
+
+ mCur = mNext;
+ mLength = 0;
+
+ char* lastDelimiter = 0;
+ while (mNext < mLimit) {
+ if (IsEOL(*mNext)) {
+ if (lastDelimiter) {
+ if (lastDelimiter &&
+ *(mNext - 1) != PLUGIN_REGISTRY_END_OF_LINE_MARKER)
+ return false;
+ *lastDelimiter = '\0';
+ } else {
+ *mNext = '\0';
+ }
+
+ for (++mNext; mNext < mLimit; ++mNext) {
+ if (!IsEOL(*mNext)) break;
+ }
+ return true;
+ }
+ if (*mNext == PLUGIN_REGISTRY_FIELD_DELIMITER) lastDelimiter = mNext;
+ ++mNext;
+ ++mLength;
+ }
+ return false;
+ }
+
+ int ParseLine(char** chunks, int maxChunks) {
+ NS_ASSERTION(mCur && maxChunks && chunks, "bad call to ParseLine");
+ int found = 0;
+ chunks[found++] = mCur;
+
+ if (found < maxChunks) {
+ for (char* cur = mCur; *cur; cur++) {
+ if (*cur == PLUGIN_REGISTRY_FIELD_DELIMITER) {
+ *cur = 0;
+ chunks[found++] = cur + 1;
+ if (found == maxChunks) break;
+ }
+ }
+ }
+ return found;
+ }
+
+ char* LinePtr() { return mCur; }
+ uint32_t LineLength() { return mLength; }
+
+ bool IsEOL(char c) { return c == '\n' || c == '\r'; }
+
+ char* mBase;
+
+ private:
+ char* mCur;
+ uint32_t mLength;
+ char* mNext;
+ char* mLimit;
+};
+
+#endif
diff --git a/dom/plugins/base/nsPluginNativeWindow.cpp b/dom/plugins/base/nsPluginNativeWindow.cpp
new file mode 100644
index 0000000000..73da098cd4
--- /dev/null
+++ b/dom/plugins/base/nsPluginNativeWindow.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/**
+ * This file is the default implementation of plugin native window
+ * empty stubs, it should be replaced with real platform implementation
+ * for every platform
+ */
+
+#include "nsDebug.h"
+#include "nsPluginNativeWindow.h"
+
+class nsPluginNativeWindowImpl : public nsPluginNativeWindow {
+ public:
+ nsPluginNativeWindowImpl();
+ virtual ~nsPluginNativeWindowImpl();
+
+#if defined(MOZ_WIDGET_GTK) && defined(MOZ_X11)
+ NPSetWindowCallbackStruct mWsInfo;
+#endif
+};
+
+nsPluginNativeWindowImpl::nsPluginNativeWindowImpl() : nsPluginNativeWindow() {
+ // initialize the struct fields
+ window = nullptr;
+ x = 0;
+ y = 0;
+ width = 0;
+ height = 0;
+ memset(&clipRect, 0, sizeof(clipRect));
+ type = NPWindowTypeWindow;
+
+#if defined(MOZ_WIDGET_GTK) && defined(MOZ_X11)
+ ws_info = &mWsInfo;
+ mWsInfo.type = 0;
+ mWsInfo.display = nullptr;
+ mWsInfo.visual = nullptr;
+ mWsInfo.colormap = 0;
+ mWsInfo.depth = 0;
+#elif defined(XP_UNIX) && !defined(XP_MACOSX)
+ ws_info = nullptr;
+#endif
+}
+
+nsPluginNativeWindowImpl::~nsPluginNativeWindowImpl() = default;
+
+nsresult PLUG_NewPluginNativeWindow(
+ nsPluginNativeWindow** aPluginNativeWindow) {
+ NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
+ *aPluginNativeWindow = new nsPluginNativeWindowImpl();
+ return NS_OK;
+}
+
+nsresult PLUG_DeletePluginNativeWindow(
+ nsPluginNativeWindow* aPluginNativeWindow) {
+ NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
+ delete static_cast<nsPluginNativeWindowImpl*>(aPluginNativeWindow);
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nsPluginNativeWindow.h b/dom/plugins/base/nsPluginNativeWindow.h
new file mode 100644
index 0000000000..b880a74535
--- /dev/null
+++ b/dom/plugins/base/nsPluginNativeWindow.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef _nsPluginNativeWindow_h_
+#define _nsPluginNativeWindow_h_
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsISupportsImpl.h"
+#include "nsNPAPIPluginInstance.h"
+#include "npapi.h"
+#include "nsIWidget.h"
+
+/**
+ * base class for native plugin window implementations
+ */
+class nsPluginNativeWindow : public NPWindow {
+ public:
+ nsPluginNativeWindow() : NPWindow() { MOZ_COUNT_CTOR(nsPluginNativeWindow); }
+
+ MOZ_COUNTED_DTOR_VIRTUAL(nsPluginNativeWindow)
+
+ /**
+ * !!! CAUTION !!!
+ *
+ * The base class |nsPluginWindow| is defined as a struct in nsplugindefs.h,
+ * thus it does not have a destructor of its own.
+ * One should never attempt to delete |nsPluginNativeWindow| object instance
+ * (or derivatives) using a pointer of |nsPluginWindow *| type. Should such
+ * necessity occur it must be properly casted first.
+ */
+
+ public:
+ nsresult GetPluginInstance(RefPtr<nsNPAPIPluginInstance>& aPluginInstance) {
+ aPluginInstance = mPluginInstance;
+ return NS_OK;
+ }
+ nsresult SetPluginInstance(nsNPAPIPluginInstance* aPluginInstance) {
+ if (mPluginInstance != aPluginInstance) mPluginInstance = aPluginInstance;
+ return NS_OK;
+ }
+
+ nsresult GetPluginWidget(nsIWidget** aWidget) const {
+ NS_IF_ADDREF(*aWidget = mWidget);
+ return NS_OK;
+ }
+ nsresult SetPluginWidget(nsIWidget* aWidget) {
+ mWidget = aWidget;
+ return NS_OK;
+ }
+
+ public:
+ virtual nsresult CallSetWindow(
+ RefPtr<nsNPAPIPluginInstance>& aPluginInstance) {
+ // null aPluginInstance means that we want to call SetWindow(null)
+ if (aPluginInstance)
+ aPluginInstance->SetWindow(this);
+ else if (mPluginInstance)
+ mPluginInstance->SetWindow(nullptr);
+
+ SetPluginInstance(aPluginInstance);
+ return NS_OK;
+ }
+
+ protected:
+ RefPtr<nsNPAPIPluginInstance> mPluginInstance;
+ nsCOMPtr<nsIWidget> mWidget;
+};
+
+nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow** aPluginNativeWindow);
+nsresult PLUG_DeletePluginNativeWindow(
+ nsPluginNativeWindow* aPluginNativeWindow);
+
+#endif //_nsPluginNativeWindow_h_
diff --git a/dom/plugins/base/nsPluginNativeWindowWin.cpp b/dom/plugins/base/nsPluginNativeWindowWin.cpp
new file mode 100644
index 0000000000..f8e6e44140
--- /dev/null
+++ b/dom/plugins/base/nsPluginNativeWindowWin.cpp
@@ -0,0 +1,658 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/BasicEvents.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/WeakPtr.h"
+
+#include "windows.h"
+#include "windowsx.h"
+
+// XXXbz windowsx.h defines GetFirstChild, GetNextSibling,
+// GetPrevSibling are macros, apparently... Eeevil. We have functions
+// called that on some classes, so undef them.
+#undef GetFirstChild
+#undef GetNextSibling
+#undef GetPrevSibling
+
+#include "nsDebug.h"
+
+#include "nsWindowsDllInterceptor.h"
+#include "nsPluginNativeWindow.h"
+#include "nsThreadUtils.h"
+#include "nsCrashOnException.h"
+
+using namespace mozilla;
+
+#define NP_POPUP_API_VERSION 16
+
+#define nsMajorVersion(v) (((int32_t)(v) >> 16) & 0xffff)
+#define nsMinorVersion(v) ((int32_t)(v)&0xffff)
+#define versionOK(suppliedV, requiredV) \
+ (nsMajorVersion(suppliedV) == nsMajorVersion(requiredV) && \
+ nsMinorVersion(suppliedV) >= nsMinorVersion(requiredV))
+
+#define NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION \
+ TEXT("MozillaPluginWindowPropertyAssociation")
+#define NS_PLUGIN_CUSTOM_MSG_ID TEXT("MozFlashUserRelay")
+#define WM_USER_FLASH WM_USER + 1
+static UINT sWM_FLASHBOUNCEMSG = 0;
+
+class nsPluginNativeWindowWin;
+
+/**
+ * PLEvent handling code
+ */
+class PluginWindowEvent : public Runnable {
+ public:
+ PluginWindowEvent();
+ void Init(WeakPtr<nsPluginNativeWindowWin> aRef, HWND aWnd, UINT aMsg,
+ WPARAM aParam, LPARAM aLParam);
+ void Clear();
+ HWND GetWnd() { return mWnd; };
+ UINT GetMsg() { return mMsg; };
+ WPARAM GetWParam() { return mWParam; };
+ LPARAM GetLParam() { return mLParam; };
+ bool InUse() { return mWnd != nullptr; };
+
+ NS_DECL_NSIRUNNABLE
+
+ protected:
+ WeakPtr<nsPluginNativeWindowWin> mPluginWindowRef;
+ HWND mWnd;
+ UINT mMsg;
+ WPARAM mWParam;
+ LPARAM mLParam;
+};
+
+PluginWindowEvent::PluginWindowEvent() : Runnable("PluginWindowEvent") {
+ Clear();
+}
+
+void PluginWindowEvent::Clear() {
+ mWnd = nullptr;
+ mMsg = 0;
+ mWParam = 0;
+ mLParam = 0;
+}
+
+void PluginWindowEvent::Init(WeakPtr<nsPluginNativeWindowWin> aRef, HWND aWnd,
+ UINT aMsg, WPARAM aWParam, LPARAM aLParam) {
+ NS_ASSERTION(aWnd != nullptr, "invalid plugin event value");
+ NS_ASSERTION(mWnd == nullptr, "event already in use");
+ mPluginWindowRef = aRef;
+ mWnd = aWnd;
+ mMsg = aMsg;
+ mWParam = aWParam;
+ mLParam = aLParam;
+}
+
+/**
+ * nsPluginNativeWindow Windows specific class declaration
+ */
+
+class nsPluginNativeWindowWin : public nsPluginNativeWindow,
+ public SupportsWeakPtr {
+ public:
+ nsPluginNativeWindowWin();
+
+ virtual nsresult CallSetWindow(
+ RefPtr<nsNPAPIPluginInstance>& aPluginInstance);
+
+ private:
+ nsresult SubclassAndAssociateWindow();
+ nsresult UndoSubclassAndAssociateWindow();
+
+ public:
+ // locals
+ WNDPROC GetPrevWindowProc();
+ void SetPrevWindowProc(WNDPROC proc) { mPluginWinProc = proc; }
+ WNDPROC GetWindowProc();
+ PluginWindowEvent* GetPluginWindowEvent(HWND aWnd, UINT aMsg, WPARAM aWParam,
+ LPARAM aLParam);
+
+ private:
+ WNDPROC mPluginWinProc;
+ WNDPROC mPrevWinProc;
+ WeakPtr<nsPluginNativeWindowWin> mWeakRef;
+ RefPtr<PluginWindowEvent> mCachedPluginWindowEvent;
+
+ HWND mParentWnd;
+ LONG_PTR mParentProc;
+
+ public:
+ nsPluginHost::SpecialType mPluginType;
+};
+
+static bool sInPreviousMessageDispatch = false;
+
+static bool ProcessFlashMessageDelayed(nsPluginNativeWindowWin* aWin,
+ nsNPAPIPluginInstance* aInst, HWND hWnd,
+ UINT msg, WPARAM wParam, LPARAM lParam) {
+ NS_ENSURE_TRUE(aWin, false);
+ NS_ENSURE_TRUE(aInst, false);
+
+ if (msg == sWM_FLASHBOUNCEMSG) {
+ // See PluginWindowEvent::Run() below.
+ NS_ASSERTION((sWM_FLASHBOUNCEMSG != 0),
+ "RegisterWindowMessage failed in flash plugin WM_USER message "
+ "handling!");
+ ::CallWindowProc((WNDPROC)aWin->GetWindowProc(), hWnd, WM_USER_FLASH,
+ wParam, lParam);
+ return true;
+ }
+
+ if (msg != WM_USER_FLASH) return false; // no need to delay
+
+ // do stuff
+ nsCOMPtr<nsIRunnable> pwe =
+ aWin->GetPluginWindowEvent(hWnd, msg, wParam, lParam);
+ if (pwe) {
+ NS_DispatchToCurrentThread(pwe);
+ return true;
+ }
+ return false;
+}
+
+class nsDelayedPopupsEnabledEvent : public Runnable {
+ public:
+ explicit nsDelayedPopupsEnabledEvent(nsNPAPIPluginInstance* inst)
+ : Runnable("nsDelayedPopupsEnabledEvent"), mInst(inst) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ RefPtr<nsNPAPIPluginInstance> mInst;
+};
+
+NS_IMETHODIMP nsDelayedPopupsEnabledEvent::Run() {
+ mInst->PushPopupsEnabledState(false);
+ return NS_OK;
+}
+
+static LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT msg, WPARAM wParam,
+ LPARAM lParam);
+
+/**
+ * New plugin window procedure
+ *
+ * e10s note - this subclass, and the hooks we set below using
+ * WindowsDllInterceptor are currently not in use when running with e10s.
+ * (Utility calls like CallSetWindow are still in use in the content process.)
+ * We would like to keep things this away, essentially making all the hacks here
+ * obsolete. Some of the mitigation work here has already been supplanted by
+ * code in PluginInstanceChild. The rest we eventually want to rip out.
+ */
+static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg,
+ WPARAM wParam, LPARAM lParam) {
+ nsPluginNativeWindowWin* win = (nsPluginNativeWindowWin*)::GetProp(
+ hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
+ if (!win) return TRUE;
+
+ // The DispatchEvent(ePluginActivate) below can trigger a reentrant focus
+ // event which might destroy us. Hold a strong ref on the plugin instance
+ // to prevent that, bug 374229.
+ RefPtr<nsNPAPIPluginInstance> inst;
+ win->GetPluginInstance(inst);
+
+ bool enablePopups = false;
+
+ // Activate/deactivate mouse capture on the plugin widget
+ // here, before we pass the Windows event to the plugin
+ // because its possible our widget won't get paired events
+ // (see bug 131007) and we'll look frozen. Note that this
+ // is also done in ChildWindow::DispatchMouseEvent.
+ switch (msg) {
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN: {
+ nsCOMPtr<nsIWidget> widget;
+ win->GetPluginWidget(getter_AddRefs(widget));
+ if (widget) widget->CaptureMouse(true);
+ break;
+ }
+ case WM_LBUTTONUP:
+ enablePopups = true;
+
+ // fall through
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP: {
+ nsCOMPtr<nsIWidget> widget;
+ win->GetPluginWidget(getter_AddRefs(widget));
+ if (widget) widget->CaptureMouse(false);
+ break;
+ }
+ case WM_KEYDOWN:
+ // Ignore repeating keydown messages...
+ if ((lParam & 0x40000000) != 0) {
+ break;
+ }
+
+ // fall through
+ case WM_KEYUP:
+ enablePopups = true;
+
+ break;
+
+ case WM_MOUSEACTIVATE: {
+ // If a child window of this plug-in is already focused,
+ // don't focus the parent to avoid focus dance. We'll
+ // receive a follow up WM_SETFOCUS which will notify
+ // the appropriate window anyway.
+ HWND focusedWnd = ::GetFocus();
+ if (!::IsChild((HWND)win->window, focusedWnd)) {
+ // Notify the dom / focus manager the plugin has focus when one of
+ // it's child windows receives it. OOPP specific - this code is
+ // critical in notifying the dom of focus changes when the plugin
+ // window in the child process receives focus via a mouse click.
+ // WM_MOUSEACTIVATE is sent by nsWindow via a custom window event
+ // sent from PluginInstanceParent in response to focus events sent
+ // from the child. (bug 540052) Note, this gui event could also be
+ // sent directly from widget.
+ nsCOMPtr<nsIWidget> widget;
+ win->GetPluginWidget(getter_AddRefs(widget));
+ if (widget) {
+ WidgetGUIEvent event(true, ePluginActivate, widget);
+ nsEventStatus status;
+ widget->DispatchEvent(&event, status);
+ }
+ }
+ } break;
+
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS: {
+ // Make sure setfocus and killfocus get through to the widget procedure
+ // even if they are eaten by the plugin. Also make sure we aren't calling
+ // recursively.
+ WNDPROC prevWndProc = win->GetPrevWindowProc();
+ if (prevWndProc && !sInPreviousMessageDispatch) {
+ sInPreviousMessageDispatch = true;
+ ::CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);
+ sInPreviousMessageDispatch = false;
+ }
+ break;
+ }
+ }
+
+ // Macromedia Flash plugin may flood the message queue with some special
+ // messages (WM_USER+1) causing 100% CPU consumption and GUI freeze, see
+ // mozilla bug 132759; we can prevent this from happening by delaying the
+ // processing such messages;
+ if (win->mPluginType == nsPluginHost::eSpecialType_Flash) {
+ if (ProcessFlashMessageDelayed(win, inst, hWnd, msg, wParam, lParam))
+ return TRUE;
+ }
+
+ if (enablePopups && inst) {
+ uint16_t apiVersion;
+ if (NS_SUCCEEDED(inst->GetPluginAPIVersion(&apiVersion)) &&
+ !versionOK(apiVersion, NP_POPUP_API_VERSION)) {
+ inst->PushPopupsEnabledState(true);
+ }
+ }
+
+ LRESULT res;
+ WNDPROC proc = (WNDPROC)win->GetWindowProc();
+ if (PluginWndProc == proc) {
+ NS_WARNING(
+ "Previous plugin window procedure references PluginWndProc! "
+ "Report this bug!");
+ res = CallWindowProc(DefWindowProc, hWnd, msg, wParam, lParam);
+ } else {
+ res = CallWindowProc(proc, hWnd, msg, wParam, lParam);
+ }
+
+ if (inst) {
+ // Popups are enabled (were enabled before the call to
+ // CallWindowProc()). Some plugins (at least the flash player)
+ // post messages from their key handlers etc that delay the actual
+ // processing, so we need to delay the disabling of popups so that
+ // popups remain enabled when the flash player ends up processing
+ // the actual key handlers. We do this by posting an event that
+ // does the disabling, this way our disabling will happen after
+ // the handlers in the plugin are done.
+
+ // Note that it's not fatal if any of this fails (which won't
+ // happen unless we're out of memory anyways) since the plugin
+ // code will pop any popup state pushed by this plugin on
+ // destruction.
+
+ nsCOMPtr<nsIRunnable> event = new nsDelayedPopupsEnabledEvent(inst);
+ if (event) NS_DispatchToCurrentThread(event);
+ }
+
+ return res;
+}
+
+static LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT msg, WPARAM wParam,
+ LPARAM lParam) {
+ return mozilla::CallWindowProcCrashProtected(PluginWndProcInternal, hWnd, msg,
+ wParam, lParam);
+}
+
+/*
+ * Flash will reset the subclass of our widget at various times.
+ * (Notably when entering and exiting full screen mode.) This
+ * occurs independent of the main plugin window event procedure.
+ * We trap these subclass calls to prevent our subclass hook from
+ * getting dropped.
+ * Note, ascii versions can be nixed once flash versions < 10.1
+ * are considered obsolete.
+ */
+static WindowsDllInterceptor sUser32Intercept;
+
+#ifdef _WIN64
+typedef LONG_PTR(WINAPI* User32SetWindowLongPtrA)(HWND hWnd, int nIndex,
+ LONG_PTR dwNewLong);
+typedef LONG_PTR(WINAPI* User32SetWindowLongPtrW)(HWND hWnd, int nIndex,
+ LONG_PTR dwNewLong);
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongPtrA>
+ sUser32SetWindowLongAHookStub;
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongPtrW>
+ sUser32SetWindowLongWHookStub;
+#else
+typedef LONG(WINAPI* User32SetWindowLongA)(HWND hWnd, int nIndex,
+ LONG dwNewLong);
+typedef LONG(WINAPI* User32SetWindowLongW)(HWND hWnd, int nIndex,
+ LONG dwNewLong);
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongA>
+ sUser32SetWindowLongAHookStub;
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongW>
+ sUser32SetWindowLongWHookStub;
+#endif
+static inline bool SetWindowLongHookCheck(HWND hWnd, int nIndex,
+ LONG_PTR newLong) {
+ nsPluginNativeWindowWin* win = (nsPluginNativeWindowWin*)GetProp(
+ hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
+ if (!win || (win && win->mPluginType != nsPluginHost::eSpecialType_Flash) ||
+ (nIndex == GWLP_WNDPROC &&
+ newLong == reinterpret_cast<LONG_PTR>(PluginWndProc)))
+ return true;
+ return false;
+}
+
+#ifdef _WIN64
+LONG_PTR WINAPI SetWindowLongPtrAHook(HWND hWnd, int nIndex, LONG_PTR newLong)
+#else
+LONG WINAPI SetWindowLongAHook(HWND hWnd, int nIndex, LONG newLong)
+#endif
+{
+ if (SetWindowLongHookCheck(hWnd, nIndex, newLong))
+ return sUser32SetWindowLongAHookStub(hWnd, nIndex, newLong);
+
+ // Set flash's new subclass to get the result.
+ LONG_PTR proc = sUser32SetWindowLongAHookStub(hWnd, nIndex, newLong);
+
+ // We already checked this in SetWindowLongHookCheck
+ nsPluginNativeWindowWin* win = (nsPluginNativeWindowWin*)GetProp(
+ hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
+
+ // Hook our subclass back up, just like we do on setwindow.
+ win->SetPrevWindowProc(
+ reinterpret_cast<WNDPROC>(sUser32SetWindowLongWHookStub(
+ hWnd, nIndex, reinterpret_cast<LONG_PTR>(PluginWndProc))));
+ return proc;
+}
+
+#ifdef _WIN64
+LONG_PTR WINAPI SetWindowLongPtrWHook(HWND hWnd, int nIndex, LONG_PTR newLong)
+#else
+LONG WINAPI SetWindowLongWHook(HWND hWnd, int nIndex, LONG newLong)
+#endif
+{
+ if (SetWindowLongHookCheck(hWnd, nIndex, newLong))
+ return sUser32SetWindowLongWHookStub(hWnd, nIndex, newLong);
+
+ // Set flash's new subclass to get the result.
+ LONG_PTR proc = sUser32SetWindowLongWHookStub(hWnd, nIndex, newLong);
+
+ // We already checked this in SetWindowLongHookCheck
+ nsPluginNativeWindowWin* win = (nsPluginNativeWindowWin*)GetProp(
+ hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
+
+ // Hook our subclass back up, just like we do on setwindow.
+ win->SetPrevWindowProc(
+ reinterpret_cast<WNDPROC>(sUser32SetWindowLongWHookStub(
+ hWnd, nIndex, reinterpret_cast<LONG_PTR>(PluginWndProc))));
+ return proc;
+}
+
+static void HookSetWindowLongPtr() {
+ sUser32Intercept.Init("user32.dll");
+#ifdef _WIN64
+ sUser32SetWindowLongAHookStub.Set(sUser32Intercept, "SetWindowLongPtrA",
+ &SetWindowLongPtrAHook);
+ sUser32SetWindowLongWHookStub.Set(sUser32Intercept, "SetWindowLongPtrW",
+ &SetWindowLongPtrWHook);
+#else
+ sUser32SetWindowLongAHookStub.Set(sUser32Intercept, "SetWindowLongA",
+ &SetWindowLongAHook);
+ sUser32SetWindowLongWHookStub.Set(sUser32Intercept, "SetWindowLongW",
+ &SetWindowLongWHook);
+#endif
+}
+
+/**
+ * nsPluginNativeWindowWin implementation
+ */
+nsPluginNativeWindowWin::nsPluginNativeWindowWin() : nsPluginNativeWindow() {
+ // initialize the struct fields
+ window = nullptr;
+ x = 0;
+ y = 0;
+ width = 0;
+ height = 0;
+ type = NPWindowTypeWindow;
+
+ mPrevWinProc = nullptr;
+ mPluginWinProc = nullptr;
+ mPluginType = nsPluginHost::eSpecialType_None;
+
+ mParentWnd = nullptr;
+ mParentProc = 0;
+}
+
+WNDPROC nsPluginNativeWindowWin::GetPrevWindowProc() { return mPrevWinProc; }
+
+WNDPROC nsPluginNativeWindowWin::GetWindowProc() { return mPluginWinProc; }
+
+NS_IMETHODIMP PluginWindowEvent::Run() {
+ nsPluginNativeWindowWin* win = mPluginWindowRef;
+ if (!win) return NS_OK;
+
+ HWND hWnd = GetWnd();
+ if (!hWnd) return NS_OK;
+
+ RefPtr<nsNPAPIPluginInstance> inst;
+ win->GetPluginInstance(inst);
+
+ if (GetMsg() == WM_USER_FLASH) {
+ // XXX Unwind issues related to runnable event callback depth for this
+ // event and destruction of the plugin. (Bug 493601)
+ ::PostMessage(hWnd, sWM_FLASHBOUNCEMSG, GetWParam(), GetLParam());
+ } else {
+ // Currently not used, but added so that processing events here
+ // is more generic.
+ ::CallWindowProc(win->GetWindowProc(), hWnd, GetMsg(), GetWParam(),
+ GetLParam());
+ }
+
+ Clear();
+ return NS_OK;
+}
+
+PluginWindowEvent* nsPluginNativeWindowWin::GetPluginWindowEvent(
+ HWND aWnd, UINT aMsg, WPARAM aWParam, LPARAM aLParam) {
+ if (!mWeakRef) {
+ mWeakRef = this;
+ if (!mWeakRef) return nullptr;
+ }
+
+ PluginWindowEvent* event;
+
+ // We have the ability to alloc if needed in case in the future some plugin
+ // should post multiple PostMessages. However, this could lead to many
+ // alloc's per second which could become a performance issue. See bug 169247.
+ if (!mCachedPluginWindowEvent) {
+ event = new PluginWindowEvent();
+ mCachedPluginWindowEvent = event;
+ } else if (mCachedPluginWindowEvent->InUse()) {
+ event = new PluginWindowEvent();
+ } else {
+ event = mCachedPluginWindowEvent;
+ }
+
+ event->Init(mWeakRef, aWnd, aMsg, aWParam, aLParam);
+ return event;
+}
+
+nsresult nsPluginNativeWindowWin::CallSetWindow(
+ RefPtr<nsNPAPIPluginInstance>& aPluginInstance) {
+ // Note, 'window' can be null
+
+ // check the incoming instance, null indicates that window is going away and
+ // we are not interested in subclassing business any more, undo and don't
+ // subclass
+ if (!aPluginInstance) {
+ UndoSubclassAndAssociateWindow();
+ // release plugin instance
+ SetPluginInstance(nullptr);
+ nsPluginNativeWindow::CallSetWindow(aPluginInstance);
+ return NS_OK;
+ }
+
+ // check plugin mime type and cache it if it will need special treatment later
+ if (mPluginType == nsPluginHost::eSpecialType_None) {
+ const char* mimetype = nullptr;
+ if (NS_SUCCEEDED(aPluginInstance->GetMIMEType(&mimetype)) && mimetype) {
+ mPluginType = nsPluginHost::GetSpecialType(nsDependentCString(mimetype));
+ }
+ }
+
+ // With e10s we execute in the content process and as such we don't
+ // have access to native widgets. CallSetWindow and skip native widget
+ // subclassing.
+ if (!XRE_IsParentProcess()) {
+ nsPluginNativeWindow::CallSetWindow(aPluginInstance);
+ return NS_OK;
+ }
+
+ if (!sWM_FLASHBOUNCEMSG) {
+ sWM_FLASHBOUNCEMSG = ::RegisterWindowMessage(NS_PLUGIN_CUSTOM_MSG_ID);
+ }
+
+ if (window) {
+ // grab the widget procedure before the plug-in does a subclass in
+ // setwindow. We'll use this in PluginWndProc for forwarding focus
+ // events to the widget.
+ WNDPROC currentWndProc =
+ (WNDPROC)::GetWindowLongPtr((HWND)window, GWLP_WNDPROC);
+ if (!mPrevWinProc && currentWndProc != PluginWndProc)
+ mPrevWinProc = currentWndProc;
+ }
+
+ nsPluginNativeWindow::CallSetWindow(aPluginInstance);
+
+ SubclassAndAssociateWindow();
+
+ if (window && mPluginType == nsPluginHost::eSpecialType_Flash &&
+ !GetPropW((HWND)window, L"PluginInstanceParentProperty")) {
+ HookSetWindowLongPtr();
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginNativeWindowWin::SubclassAndAssociateWindow() {
+ if (type != NPWindowTypeWindow || !window) return NS_ERROR_FAILURE;
+
+ HWND hWnd = (HWND)window;
+
+ // check if we need to subclass
+ WNDPROC currentWndProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC);
+ if (currentWndProc == PluginWndProc) return NS_OK;
+
+ // If the plugin reset the subclass, set it back.
+ if (mPluginWinProc) {
+#ifdef DEBUG
+ NS_WARNING("A plugin cleared our subclass - resetting.");
+ if (currentWndProc != mPluginWinProc) {
+ NS_WARNING("Procedures do not match up, discarding old subclass value.");
+ }
+ if (mPrevWinProc && currentWndProc == mPrevWinProc) {
+ NS_WARNING("The new procedure is our widget procedure?");
+ }
+#endif
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)PluginWndProc);
+ return NS_OK;
+ }
+
+ LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE);
+ // Out of process plugins must not have the WS_CLIPCHILDREN style set on their
+ // parent windows or else synchronous paints (via UpdateWindow() and others)
+ // will cause deadlocks.
+ if (::GetPropW(hWnd, L"PluginInstanceParentProperty"))
+ style &= ~WS_CLIPCHILDREN;
+ else
+ style |= WS_CLIPCHILDREN;
+ SetWindowLongPtr(hWnd, GWL_STYLE, style);
+
+ mPluginWinProc =
+ (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)PluginWndProc);
+ if (!mPluginWinProc) return NS_ERROR_FAILURE;
+
+ DebugOnly<nsPluginNativeWindowWin*> win = (nsPluginNativeWindowWin*)::GetProp(
+ hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
+ NS_ASSERTION(!win || (win == this),
+ "plugin window already has property and this is not us");
+
+ if (!::SetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION, (HANDLE)this))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+nsresult nsPluginNativeWindowWin::UndoSubclassAndAssociateWindow() {
+ // remove window property
+ HWND hWnd = (HWND)window;
+ if (IsWindow(hWnd)) ::RemoveProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
+
+ // restore the original win proc
+ // but only do this if this were us last time
+ if (mPluginWinProc) {
+ WNDPROC currentWndProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC);
+ if (currentWndProc == PluginWndProc)
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)mPluginWinProc);
+ mPluginWinProc = nullptr;
+
+ LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE);
+ style &= ~WS_CLIPCHILDREN;
+ SetWindowLongPtr(hWnd, GWL_STYLE, style);
+ }
+
+ if (mPluginType == nsPluginHost::eSpecialType_Flash && mParentWnd) {
+ ::SetWindowLongPtr(mParentWnd, GWLP_WNDPROC, mParentProc);
+ mParentWnd = nullptr;
+ mParentProc = 0;
+ }
+
+ return NS_OK;
+}
+
+nsresult PLUG_NewPluginNativeWindow(
+ nsPluginNativeWindow** aPluginNativeWindow) {
+ NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
+
+ *aPluginNativeWindow = new nsPluginNativeWindowWin();
+ return NS_OK;
+}
+
+nsresult PLUG_DeletePluginNativeWindow(
+ nsPluginNativeWindow* aPluginNativeWindow) {
+ NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
+ nsPluginNativeWindowWin* p = (nsPluginNativeWindowWin*)aPluginNativeWindow;
+ delete p;
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nsPluginStreamListenerPeer.cpp b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
new file mode 100644
index 0000000000..df54a66d67
--- /dev/null
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
@@ -0,0 +1,606 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsPluginStreamListenerPeer.h"
+#include "nsIContentPolicy.h"
+#include "nsContentPolicyUtils.h"
+#include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIFileChannel.h"
+#include "nsMimeTypes.h"
+#include "nsNetCID.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsPluginLogging.h"
+#include "nsIURI.h"
+#include "nsPluginHost.h"
+#include "nsIMultiPartChannel.h"
+#include "nsPrintfCString.h"
+#include "nsIScriptGlobalObject.h"
+#include "mozilla/dom/Document.h"
+#include "nsIWebNavigation.h"
+#include "nsContentUtils.h"
+#include "nsNetUtil.h"
+#include "nsPluginNativeWindow.h"
+#include "GeckoProfiler.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsDataHashtable.h"
+#include "mozilla/NullPrincipal.h"
+
+// nsPluginStreamListenerPeer
+
+NS_IMPL_ISUPPORTS(nsPluginStreamListenerPeer, nsIStreamListener,
+ nsIRequestObserver, nsIHttpHeaderVisitor,
+ nsISupportsWeakReference, nsIInterfaceRequestor,
+ nsIChannelEventSink)
+
+nsPluginStreamListenerPeer::nsPluginStreamListenerPeer() : mLength(0) {
+ mStreamType = NP_NORMAL;
+ mStartBinding = false;
+ mRequestFailed = false;
+
+ mPendingRequests = 0;
+ mHaveFiredOnStartRequest = false;
+
+ mUseLocalCache = false;
+ mModified = 0;
+ mStreamOffset = 0;
+ mStreamComplete = 0;
+}
+
+nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer() {
+#ifdef PLUGIN_LOGGING
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
+ ("nsPluginStreamListenerPeer::dtor this=%p, url=%s\n", this,
+ mURLSpec.get()));
+#endif
+
+ if (mPStreamListener) {
+ mPStreamListener->SetStreamListenerPeer(nullptr);
+ }
+}
+
+// Called as a result of GetURL and PostURL, or by the host in the case of the
+// initial plugin stream.
+nsresult nsPluginStreamListenerPeer::Initialize(
+ nsIURI* aURL, nsNPAPIPluginInstance* aInstance,
+ nsNPAPIPluginStreamListener* aListener) {
+#ifdef PLUGIN_LOGGING
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
+ ("nsPluginStreamListenerPeer::Initialize instance=%p, url=%s\n",
+ aInstance, aURL ? aURL->GetSpecOrDefault().get() : ""));
+
+ PR_LogFlush();
+#endif
+
+ // Not gonna work out
+ if (!aInstance) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mURL = aURL;
+
+ NS_ASSERTION(
+ mPluginInstance == nullptr,
+ "nsPluginStreamListenerPeer::Initialize mPluginInstance != nullptr");
+ mPluginInstance = aInstance;
+
+ // If the plugin did not request this stream, e.g. the initial stream, we wont
+ // have a nsNPAPIPluginStreamListener yet - this will be handled by
+ // SetUpStreamListener
+ if (aListener) {
+ mPStreamListener = aListener;
+ mPStreamListener->SetStreamListenerPeer(this);
+ }
+
+ mPendingRequests = 1;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginStreamListenerPeer::OnStartRequest(nsIRequest* request) {
+ nsresult rv = NS_OK;
+ AUTO_PROFILER_LABEL("nsPluginStreamListenerPeer::OnStartRequest", OTHER);
+
+ if (mRequests.IndexOfObject(request) == -1) {
+ NS_ASSERTION(mRequests.Count() == 0,
+ "Only our initial stream should be unknown!");
+ TrackRequest(request);
+ }
+
+ if (mHaveFiredOnStartRequest) {
+ return NS_OK;
+ }
+
+ mHaveFiredOnStartRequest = true;
+
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+ NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
+
+ // deal with 404 (Not Found) HTTP response,
+ // just return, this causes the request to be ignored.
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
+ if (httpChannel) {
+ uint32_t responseCode = 0;
+ rv = httpChannel->GetResponseStatus(&responseCode);
+ if (NS_FAILED(rv)) {
+ // NPP_Notify() will be called from OnStopRequest
+ // in nsNPAPIPluginStreamListener::CleanUpStream
+ // return error will cancel this request
+ // ...and we also need to tell the plugin that
+ mRequestFailed = true;
+ return NS_ERROR_FAILURE;
+ }
+
+ if (responseCode > 206) { // not normal
+ uint32_t wantsAllNetworkStreams = 0;
+
+ // We don't always have an instance here already, but if we do, check
+ // to see if it wants all streams.
+ if (mPluginInstance) {
+ rv = mPluginInstance->GetValueFromPlugin(
+ NPPVpluginWantsAllNetworkStreams, &wantsAllNetworkStreams);
+ // If the call returned an error code make sure we still use our default
+ // value.
+ if (NS_FAILED(rv)) {
+ wantsAllNetworkStreams = 0;
+ }
+ }
+
+ if (!wantsAllNetworkStreams) {
+ mRequestFailed = true;
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+
+ nsAutoCString contentType;
+ rv = channel->GetContentType(contentType);
+ if (NS_FAILED(rv)) return rv;
+
+ // Check ShouldProcess with content policy
+ nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
+
+ int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+ rv = NS_CheckContentProcessPolicy(mURL, loadInfo, contentType, &shouldLoad);
+ if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
+ mRequestFailed = true;
+ return NS_ERROR_CONTENT_BLOCKED;
+ }
+
+ // Get the notification callbacks from the channel and save it as
+ // week ref we'll use it in nsPluginStreamInfo::RequestRead() when
+ // we'll create channel for byte range request.
+ nsCOMPtr<nsIInterfaceRequestor> callbacks;
+ channel->GetNotificationCallbacks(getter_AddRefs(callbacks));
+ if (callbacks) mWeakPtrChannelCallbacks = do_GetWeakReference(callbacks);
+
+ nsCOMPtr<nsILoadGroup> loadGroup;
+ channel->GetLoadGroup(getter_AddRefs(loadGroup));
+ if (loadGroup) mWeakPtrChannelLoadGroup = do_GetWeakReference(loadGroup);
+
+ int64_t length;
+ rv = channel->GetContentLength(&length);
+
+ // it's possible for the server to not send a Content-Length.
+ // we should still work in this case.
+ if (NS_FAILED(rv) || length < 0 || length > UINT32_MAX) {
+ // check out if this is file channel
+ nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel);
+ if (fileChannel) {
+ // file does not exist
+ mRequestFailed = true;
+ return NS_ERROR_FAILURE;
+ }
+ mLength = 0;
+ } else {
+ mLength = uint32_t(length);
+ }
+
+ nsCOMPtr<nsIURI> aURL;
+ rv = channel->GetURI(getter_AddRefs(aURL));
+ if (NS_FAILED(rv)) return rv;
+
+ aURL->GetSpec(mURLSpec);
+
+ if (!contentType.IsEmpty()) mContentType = contentType;
+
+#ifdef PLUGIN_LOGGING
+ MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NOISY,
+ ("nsPluginStreamListenerPeer::OnStartRequest this=%p request=%p "
+ "mime=%s, url=%s\n",
+ this, request, contentType.get(), mURLSpec.get()));
+
+ PR_LogFlush();
+#endif
+
+ // Set up the stream listener...
+ rv = SetUpStreamListener(request, aURL);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsPluginStreamListenerPeer::OnProgress(nsIRequest* request,
+ int64_t aProgress,
+ int64_t aProgressMax) {
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+NS_IMETHODIMP nsPluginStreamListenerPeer::OnStatus(nsIRequest* request,
+ nsresult aStatus,
+ const char16_t* aStatusArg) {
+ return NS_OK;
+}
+
+nsresult nsPluginStreamListenerPeer::GetContentType(char** result) {
+ *result = const_cast<char*>(mContentType.get());
+ return NS_OK;
+}
+
+nsresult nsPluginStreamListenerPeer::GetLength(uint32_t* result) {
+ *result = mLength;
+ return NS_OK;
+}
+
+nsresult nsPluginStreamListenerPeer::GetLastModified(uint32_t* result) {
+ *result = mModified;
+ return NS_OK;
+}
+
+nsresult nsPluginStreamListenerPeer::GetURL(const char** result) {
+ *result = mURLSpec.get();
+ return NS_OK;
+}
+
+nsresult nsPluginStreamListenerPeer::GetStreamOffset(int32_t* result) {
+ *result = mStreamOffset;
+ return NS_OK;
+}
+
+nsresult nsPluginStreamListenerPeer::SetStreamOffset(int32_t value) {
+ mStreamOffset = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(
+ nsIRequest* request, nsIInputStream* aIStream, uint64_t sourceOffset,
+ uint32_t aLength) {
+ if (mRequests.IndexOfObject(request) == -1) {
+ MOZ_ASSERT(false, "Received OnDataAvailable for untracked request.");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (mRequestFailed) return NS_ERROR_FAILURE;
+
+ nsresult rv = NS_OK;
+
+ if (!mPStreamListener) return NS_ERROR_FAILURE;
+
+ const char* url = nullptr;
+ GetURL(&url);
+
+ PLUGIN_LOG(PLUGIN_LOG_NOISY,
+ ("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, "
+ "offset=%" PRIu64 ", length=%u, url=%s\n",
+ this, request, sourceOffset, aLength, url ? url : "no url set"));
+
+ nsCOMPtr<nsIInputStream> stream = aIStream;
+ rv = mPStreamListener->OnDataAvailable(this, stream, aLength);
+
+ // if a plugin returns an error, the peer must kill the stream
+ // else the stream and PluginStreamListener leak
+ if (NS_FAILED(rv)) {
+ request->Cancel(rv);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest* request,
+ nsresult aStatus) {
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(request);
+ if (!mp) {
+ bool found = mRequests.RemoveObject(request);
+ if (!found) {
+ NS_ERROR("Received OnStopRequest for untracked request.");
+ }
+ }
+
+ PLUGIN_LOG(
+ PLUGIN_LOG_NOISY,
+ ("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%" PRIu32
+ " request=%p\n",
+ this, static_cast<uint32_t>(aStatus), request));
+
+ // if we still have pending stuff to do, lets not close the plugin socket.
+ if (--mPendingRequests > 0) return NS_OK;
+
+ if (!mPStreamListener) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+ if (!channel) return NS_ERROR_FAILURE;
+ // Set the content type to ensure we don't pass null to the plugin
+ nsAutoCString aContentType;
+ rv = channel->GetContentType(aContentType);
+ if (NS_FAILED(rv) && !mRequestFailed) return rv;
+
+ if (!aContentType.IsEmpty()) mContentType = aContentType;
+
+ // set error status if stream failed so we notify the plugin
+ if (mRequestFailed) aStatus = NS_ERROR_FAILURE;
+
+ if (NS_FAILED(aStatus)) {
+ // on error status cleanup the stream
+ // and return w/o OnFileAvailable()
+ mPStreamListener->OnStopBinding(this, aStatus);
+ return NS_OK;
+ }
+
+ if (mStartBinding) {
+ // On start binding has been called
+ mPStreamListener->OnStopBinding(this, aStatus);
+ } else {
+ // OnStartBinding hasn't been called, so complete the action.
+ mPStreamListener->OnStartBinding(this);
+ mPStreamListener->OnStopBinding(this, aStatus);
+ }
+
+ if (NS_SUCCEEDED(aStatus)) {
+ mStreamComplete = true;
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest* request,
+ nsIURI* aURL) {
+ nsresult rv = NS_OK;
+
+ // If we don't yet have a stream listener, we need to get
+ // one from the plugin.
+ // NOTE: this should only happen when a stream was NOT created
+ // with GetURL or PostURL (i.e. it's the initial stream we
+ // send to the plugin as determined by the SRC or DATA attribute)
+ if (!mPStreamListener) {
+ if (!mPluginInstance) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<nsNPAPIPluginStreamListener> streamListener;
+ rv = mPluginInstance->NewStreamListener(nullptr, nullptr,
+ getter_AddRefs(streamListener));
+ if (NS_FAILED(rv) || !streamListener) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mPStreamListener =
+ static_cast<nsNPAPIPluginStreamListener*>(streamListener.get());
+ }
+
+ mPStreamListener->SetStreamListenerPeer(this);
+
+ // get httpChannel to retrieve some info we need for nsIPluginStreamInfo setup
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
+
+ /*
+ * Assumption
+ * By the time nsPluginStreamListenerPeer::OnDataAvailable() gets
+ * called, all the headers have been read.
+ */
+ if (httpChannel) {
+ // Reassemble the HTTP response status line and provide it to our
+ // listener. Would be nice if we could get the raw status line,
+ // but nsIHttpChannel doesn't currently provide that.
+ // Status code: required; the status line isn't useful without it.
+ uint32_t statusNum;
+ if (NS_SUCCEEDED(httpChannel->GetResponseStatus(&statusNum)) &&
+ statusNum < 1000) {
+ // HTTP version: provide if available. Defaults to empty string.
+ nsCString ver;
+ nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
+ do_QueryInterface(channel);
+ if (httpChannelInternal) {
+ uint32_t major, minor;
+ if (NS_SUCCEEDED(
+ httpChannelInternal->GetResponseVersion(&major, &minor))) {
+ ver = nsPrintfCString("/%" PRIu32 ".%" PRIu32, major, minor);
+ }
+ }
+
+ // Status text: provide if available. Defaults to "OK".
+ nsCString statusText;
+ if (NS_FAILED(httpChannel->GetResponseStatusText(statusText))) {
+ statusText = "OK";
+ }
+
+ // Assemble everything and pass to listener.
+ nsPrintfCString status("HTTP%s %" PRIu32 " %s", ver.get(), statusNum,
+ statusText.get());
+ static_cast<nsIHTTPHeaderListener*>(mPStreamListener)
+ ->StatusLine(status.get());
+ }
+
+ // Also provide all HTTP response headers to our listener.
+ rv = httpChannel->VisitResponseHeaders(this);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ // we require a content len
+ // get Last-Modified header for plugin info
+ nsAutoCString lastModified;
+ if (NS_SUCCEEDED(
+ httpChannel->GetResponseHeader("last-modified"_ns, lastModified)) &&
+ !lastModified.IsEmpty()) {
+ PRTime time64;
+ PR_ParseTimeString(lastModified.get(), true,
+ &time64); // convert string time to integer time
+
+ // Convert PRTime to unix-style time_t, i.e. seconds since the epoch
+ double fpTime = double(time64);
+ mModified = (uint32_t)(fpTime * 1e-6 + 0.5);
+ }
+ }
+
+ MOZ_ASSERT(!mRequest);
+ mRequest = request;
+
+ rv = mPStreamListener->OnStartBinding(this);
+
+ mStartBinding = true;
+
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginStreamListenerPeer::VisitHeader(const nsACString& header,
+ const nsACString& value) {
+ return mPStreamListener->NewResponseHeader(PromiseFlatCString(header).get(),
+ PromiseFlatCString(value).get());
+}
+
+nsresult nsPluginStreamListenerPeer::GetInterfaceGlobal(const nsIID& aIID,
+ void** result) {
+ if (!mPluginInstance) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<nsPluginInstanceOwner> owner = mPluginInstance->GetOwner();
+ if (!owner) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<mozilla::dom::Document> doc;
+ nsresult rv = owner->GetDocument(getter_AddRefs(doc));
+ if (NS_FAILED(rv) || !doc) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPIDOMWindowOuter* window = doc->GetWindow();
+ if (!window) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
+ nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(webNav);
+ if (!ir) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return ir->GetInterface(aIID, result);
+}
+
+NS_IMETHODIMP
+nsPluginStreamListenerPeer::GetInterface(const nsIID& aIID, void** result) {
+ // Provide nsIChannelEventSink ourselves, otherwise let our document's
+ // script global object owner provide the interface.
+ if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
+ return QueryInterface(aIID, result);
+ }
+
+ return GetInterfaceGlobal(aIID, result);
+}
+
+/**
+ * Proxy class which forwards async redirect notifications back to the necko
+ * callback, keeping nsPluginStreamListenerPeer::mRequests in sync with
+ * which channel is active.
+ */
+class ChannelRedirectProxyCallback : public nsIAsyncVerifyRedirectCallback {
+ public:
+ ChannelRedirectProxyCallback(nsPluginStreamListenerPeer* listener,
+ nsIAsyncVerifyRedirectCallback* parent,
+ nsIChannel* oldChannel, nsIChannel* newChannel)
+ : mWeakListener(
+ do_GetWeakReference(static_cast<nsIStreamListener*>(listener))),
+ mParent(parent),
+ mOldChannel(oldChannel),
+ mNewChannel(newChannel) {}
+
+ ChannelRedirectProxyCallback() = default;
+
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD OnRedirectVerifyCallback(nsresult aResult) override {
+ if (NS_SUCCEEDED(aResult)) {
+ nsCOMPtr<nsIStreamListener> listener = do_QueryReferent(mWeakListener);
+ if (listener)
+ static_cast<nsPluginStreamListenerPeer*>(listener.get())
+ ->ReplaceRequest(mOldChannel, mNewChannel);
+ }
+ return mParent->OnRedirectVerifyCallback(aResult);
+ }
+
+ private:
+ virtual ~ChannelRedirectProxyCallback() = default;
+
+ nsWeakPtr mWeakListener;
+ nsCOMPtr<nsIAsyncVerifyRedirectCallback> mParent;
+ nsCOMPtr<nsIChannel> mOldChannel;
+ nsCOMPtr<nsIChannel> mNewChannel;
+};
+
+NS_IMPL_ISUPPORTS(ChannelRedirectProxyCallback, nsIAsyncVerifyRedirectCallback)
+
+NS_IMETHODIMP
+nsPluginStreamListenerPeer::AsyncOnChannelRedirect(
+ nsIChannel* oldChannel, nsIChannel* newChannel, uint32_t flags,
+ nsIAsyncVerifyRedirectCallback* callback) {
+ // Disallow redirects if we don't have a stream listener.
+ if (!mPStreamListener) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Don't allow cross-origin 307/308 POST redirects.
+ nsCOMPtr<nsIHttpChannel> oldHttpChannel(do_QueryInterface(oldChannel));
+ if (oldHttpChannel) {
+ uint32_t responseStatus;
+ nsresult rv = oldHttpChannel->GetResponseStatus(&responseStatus);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (responseStatus == 307 || responseStatus == 308) {
+ nsAutoCString method;
+ rv = oldHttpChannel->GetRequestMethod(method);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (method.EqualsLiteral("POST")) {
+ rv = nsContentUtils::CheckSameOrigin(oldChannel, newChannel);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+ }
+
+ nsCOMPtr<nsIAsyncVerifyRedirectCallback> proxyCallback =
+ new ChannelRedirectProxyCallback(this, callback, oldChannel, newChannel);
+
+ // Give NPAPI a chance to control redirects.
+ bool notificationHandled = mPStreamListener->HandleRedirectNotification(
+ oldChannel, newChannel, proxyCallback);
+ if (notificationHandled) {
+ return NS_OK;
+ }
+
+ // Fall back to channel event sink for window.
+ nsCOMPtr<nsIChannelEventSink> channelEventSink;
+ nsresult rv = GetInterfaceGlobal(NS_GET_IID(nsIChannelEventSink),
+ getter_AddRefs(channelEventSink));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return channelEventSink->AsyncOnChannelRedirect(oldChannel, newChannel, flags,
+ proxyCallback);
+}
diff --git a/dom/plugins/base/nsPluginStreamListenerPeer.h b/dom/plugins/base/nsPluginStreamListenerPeer.h
new file mode 100644
index 0000000000..73a956ee23
--- /dev/null
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.h
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsPluginStreamListenerPeer_h_
+#define nsPluginStreamListenerPeer_h_
+
+#include "nscore.h"
+#include "nsIFile.h"
+#include "nsIRequest.h"
+#include "nsIStreamListener.h"
+#include "nsIProgressEventSink.h"
+#include "nsIHttpHeaderVisitor.h"
+#include "nsWeakReference.h"
+#include "nsNPAPIPluginStreamListener.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIChannelEventSink.h"
+
+class nsIChannel;
+
+/**
+ * When a plugin requests opens multiple requests to the same URL and
+ * the request must be satified by saving a file to disk, each stream
+ * listener holds a reference to the backing file: the file is only removed
+ * when all the listeners are done.
+ */
+class CachedFileHolder {
+ public:
+ explicit CachedFileHolder(nsIFile* cacheFile);
+ ~CachedFileHolder();
+
+ void AddRef();
+ void Release();
+
+ nsIFile* file() const { return mFile; }
+
+ private:
+ nsAutoRefCnt mRefCnt;
+ nsCOMPtr<nsIFile> mFile;
+};
+
+class nsPluginStreamListenerPeer : public nsIStreamListener,
+ public nsIProgressEventSink,
+ public nsIHttpHeaderVisitor,
+ public nsSupportsWeakReference,
+ public nsIInterfaceRequestor,
+ public nsIChannelEventSink {
+ virtual ~nsPluginStreamListenerPeer();
+
+ public:
+ nsPluginStreamListenerPeer();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPROGRESSEVENTSINK
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIHTTPHEADERVISITOR
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSICHANNELEVENTSINK
+
+ // Called by GetURL and PostURL (via NewStream) or by the host in the case of
+ // the initial plugin stream.
+ nsresult Initialize(nsIURI* aURL, nsNPAPIPluginInstance* aInstance,
+ nsNPAPIPluginStreamListener* aListener);
+
+ nsNPAPIPluginInstance* GetPluginInstance() { return mPluginInstance; }
+
+ nsresult GetLength(uint32_t* result);
+ nsresult GetURL(const char** result);
+ nsresult GetLastModified(uint32_t* result);
+ nsresult GetContentType(char** result);
+ nsresult GetStreamOffset(int32_t* result);
+ nsresult SetStreamOffset(int32_t value);
+
+ void TrackRequest(nsIRequest* request) { mRequests.AppendObject(request); }
+
+ void ReplaceRequest(nsIRequest* oldRequest, nsIRequest* newRequest) {
+ int32_t i = mRequests.IndexOfObject(oldRequest);
+ if (i == -1) {
+ NS_ASSERTION(mRequests.Count() == 0,
+ "Only our initial stream should be unknown!");
+ mRequests.AppendObject(oldRequest);
+ } else {
+ mRequests.ReplaceObjectAt(newRequest, i);
+ }
+ }
+
+ void CancelRequests(nsresult status) {
+ // Copy the array to avoid modification during the loop.
+ nsCOMArray<nsIRequest> requestsCopy(mRequests);
+ for (int32_t i = 0; i < requestsCopy.Count(); ++i)
+ requestsCopy[i]->Cancel(status);
+ }
+
+ void SuspendRequests() {
+ nsCOMArray<nsIRequest> requestsCopy(mRequests);
+ for (int32_t i = 0; i < requestsCopy.Count(); ++i)
+ requestsCopy[i]->Suspend();
+ }
+
+ void ResumeRequests() {
+ nsCOMArray<nsIRequest> requestsCopy(mRequests);
+ for (int32_t i = 0; i < requestsCopy.Count(); ++i)
+ requestsCopy[i]->Resume();
+ }
+
+ private:
+ nsresult SetUpStreamListener(nsIRequest* request, nsIURI* aURL);
+ nsresult GetInterfaceGlobal(const nsIID& aIID, void** result);
+
+ nsCOMPtr<nsIURI> mURL;
+ nsCString
+ mURLSpec; // Have to keep this member because GetURL hands out char*
+ RefPtr<nsNPAPIPluginStreamListener> mPStreamListener;
+
+ // Set to true if we request failed (like with a HTTP response of 404)
+ bool mRequestFailed;
+
+ /*
+ * Set to true after nsNPAPIPluginStreamListener::OnStartBinding() has
+ * been called. Checked in ::OnStopRequest so we can call the
+ * plugin's OnStartBinding if, for some reason, it has not already
+ * been called.
+ */
+ bool mStartBinding;
+ bool mHaveFiredOnStartRequest;
+ // these get passed to the plugin stream listener
+ uint32_t mLength;
+ int32_t mStreamType;
+
+ nsCString mContentType;
+ bool mUseLocalCache;
+ nsCOMPtr<nsIRequest> mRequest;
+ uint32_t mModified;
+ RefPtr<nsNPAPIPluginInstance> mPluginInstance;
+ int32_t mStreamOffset;
+ bool mStreamComplete;
+
+ public:
+ int32_t mPendingRequests;
+ nsWeakPtr mWeakPtrChannelCallbacks;
+ nsWeakPtr mWeakPtrChannelLoadGroup;
+ nsCOMArray<nsIRequest> mRequests;
+};
+
+#endif // nsPluginStreamListenerPeer_h_
diff --git a/dom/plugins/base/nsPluginTags.cpp b/dom/plugins/base/nsPluginTags.cpp
new file mode 100644
index 0000000000..df4c83b881
--- /dev/null
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -0,0 +1,926 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsPluginTags.h"
+
+#include "prlink.h"
+#include "plstr.h"
+#include "nsPluginsDir.h"
+#include "nsPluginHost.h"
+#include "nsIBlocklistService.h"
+#include "nsPluginLogging.h"
+#include "nsNPAPIPlugin.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Unused.h"
+#include "nsNetUtil.h"
+#include <cctype>
+#include "mozilla/Encoding.h"
+#include "mozilla/dom/FakePluginTagInitBinding.h"
+#include "mozilla/StaticPrefs_plugin.h"
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+# include "mozilla/SandboxSettings.h"
+# include "nsCocoaFeatures.h"
+#endif
+
+using mozilla::dom::FakePluginTagInit;
+using namespace mozilla;
+
+// These legacy flags are used in the plugin registry. The states are now
+// stored in prefs, but we still need to be able to import them.
+#define NS_PLUGIN_FLAG_ENABLED 0x0001 // is this plugin enabled?
+// no longer used 0x0002 // reuse only if regenerating
+// pluginreg.dat
+#define NS_PLUGIN_FLAG_FROMCACHE \
+ 0x0004 // this plugintag info was loaded from cache
+// no longer used 0x0008 // reuse only if regenerating
+// pluginreg.dat
+#define NS_PLUGIN_FLAG_CLICKTOPLAY 0x0020 // this is a click-to-play plugin
+
+static const char kPrefDefaultEnabledState[] = "plugin.default.state";
+
+// The defaults here will be read from prefs and overwritten
+#if defined(MOZ_SANDBOX)
+# if defined(XP_WIN) || defined(XP_MACOSX)
+static int32_t sFlashSandboxLevel = 3;
+static int32_t sDefaultSandboxLevel = 0;
+# endif
+# if defined(XP_MACOSX)
+static bool sEnableSandboxLogging = false;
+# endif
+#endif /* MOZ_SANDBOX */
+static bool sInitializedSandboxingInfo = false;
+
+// check comma delimited extensions
+static bool ExtensionInList(const nsCString& aExtensionList,
+ const nsACString& aExtension) {
+ for (const nsACString& extension :
+ nsCCharSeparatedTokenizer(aExtensionList, ',').ToRange()) {
+ if (extension.Equals(aExtension, nsCaseInsensitiveCStringComparator)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Search for an extension in an extensions array, and return its
+// matching mime type
+static bool SearchExtensions(const nsTArray<nsCString>& aExtensions,
+ const nsTArray<nsCString>& aMimeTypes,
+ const nsACString& aFindExtension,
+ nsACString& aMatchingType) {
+ uint32_t mimes = aMimeTypes.Length();
+ MOZ_ASSERT(mimes == aExtensions.Length(),
+ "These arrays should have matching elements");
+
+ aMatchingType.Truncate();
+
+ for (uint32_t i = 0; i < mimes; i++) {
+ if (ExtensionInList(aExtensions[i], aFindExtension)) {
+ aMatchingType = aMimeTypes[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static nsCString MakeNiceFileName(const nsCString& aFileName) {
+ nsCString niceName = aFileName;
+ int32_t niceNameLength = aFileName.RFind(".");
+ NS_ASSERTION(niceNameLength != kNotFound, "aFileName doesn't have a '.'?");
+ while (niceNameLength > 0) {
+ char chr = aFileName[niceNameLength - 1];
+ if (!std::isalpha(chr))
+ niceNameLength--;
+ else
+ break;
+ }
+
+ // If it turns out that niceNameLength <= 0, we'll fall back and use the
+ // entire aFileName (which we've already taken care of, a few lines back).
+ if (niceNameLength > 0) {
+ niceName.Truncate(niceNameLength);
+ }
+
+ ToLowerCase(niceName);
+ return niceName;
+}
+
+static nsCString MakePrefNameForPlugin(const char* const subname,
+ nsIInternalPluginTag* aTag) {
+ nsCString pref;
+ nsAutoCString pluginName(aTag->GetNiceFileName());
+
+ if (pluginName.IsEmpty()) {
+ // Use filename if nice name fails
+ pluginName = aTag->FileName();
+ if (pluginName.IsEmpty()) {
+ MOZ_ASSERT_UNREACHABLE("Plugin with no filename or nice name in list");
+ pluginName.AssignLiteral("unknown-plugin-name");
+ }
+ }
+
+ pref.AssignLiteral("plugin.");
+ pref.Append(subname);
+ pref.Append('.');
+ pref.Append(pluginName);
+
+ return pref;
+}
+
+static nsCString GetStatePrefNameForPlugin(nsIInternalPluginTag* aTag) {
+ return MakePrefNameForPlugin("state", aTag);
+}
+
+static nsresult IsEnabledStateLockedForPlugin(nsIInternalPluginTag* aTag,
+ bool* aIsEnabledStateLocked) {
+ *aIsEnabledStateLocked = false;
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+
+ if (NS_WARN_IF(!prefs)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ Unused << prefs->PrefIsLocked(GetStatePrefNameForPlugin(aTag).get(),
+ aIsEnabledStateLocked);
+
+ return NS_OK;
+}
+
+/* nsIInternalPluginTag */
+
+uint32_t nsIInternalPluginTag::sNextId;
+
+nsIInternalPluginTag::nsIInternalPluginTag() = default;
+
+nsIInternalPluginTag::nsIInternalPluginTag(const char* aName,
+ const char* aDescription,
+ const char* aFileName,
+ const char* aVersion)
+ : mName(aName),
+ mDescription(aDescription),
+ mFileName(aFileName),
+ mVersion(aVersion) {}
+
+nsIInternalPluginTag::nsIInternalPluginTag(
+ const char* aName, const char* aDescription, const char* aFileName,
+ const char* aVersion, const nsTArray<nsCString>& aMimeTypes,
+ const nsTArray<nsCString>& aMimeDescriptions,
+ const nsTArray<nsCString>& aExtensions)
+ : mName(aName),
+ mDescription(aDescription),
+ mFileName(aFileName),
+ mVersion(aVersion),
+ mMimeTypes(aMimeTypes.Clone()),
+ mMimeDescriptions(aMimeDescriptions.Clone()),
+ mExtensions(aExtensions.Clone()) {}
+
+nsIInternalPluginTag::~nsIInternalPluginTag() = default;
+
+bool nsIInternalPluginTag::HasExtension(const nsACString& aExtension,
+ nsACString& aMatchingType) const {
+ return SearchExtensions(mExtensions, mMimeTypes, aExtension, aMatchingType);
+}
+
+bool nsIInternalPluginTag::HasMimeType(const nsACString& aMimeType) const {
+ return mMimeTypes.Contains(aMimeType,
+ nsCaseInsensitiveCStringArrayComparator());
+}
+
+/* nsPluginTag */
+
+nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo, int64_t aLastModifiedTime,
+ uint32_t aBlocklistState)
+ : nsIInternalPluginTag(aPluginInfo->fName, aPluginInfo->fDescription,
+ aPluginInfo->fFileName, aPluginInfo->fVersion),
+ mId(sNextId++),
+ mContentProcessRunningCount(0),
+ mHadLocalInstance(false),
+ mLibrary(nullptr),
+ mIsFlashPlugin(false),
+ mSupportsAsyncRender(false),
+ mFullPath(aPluginInfo->fFullPath),
+ mLastModifiedTime(aLastModifiedTime),
+ mSandboxLevel(0),
+ mIsSandboxLoggingEnabled(false),
+ mBlocklistState(aBlocklistState) {
+ InitMime(aPluginInfo->fMimeTypeArray, aPluginInfo->fMimeDescriptionArray,
+ aPluginInfo->fExtensionArray, aPluginInfo->fVariantCount);
+ InitSandboxLevel();
+ EnsureMembersAreUTF8();
+ FixupVersion();
+}
+
+nsPluginTag::nsPluginTag(const char* aName, const char* aDescription,
+ const char* aFileName, const char* aFullPath,
+ const char* aVersion, const char* const* aMimeTypes,
+ const char* const* aMimeDescriptions,
+ const char* const* aExtensions, int32_t aVariants,
+ int64_t aLastModifiedTime, uint32_t aBlocklistState,
+ bool aArgsAreUTF8)
+ : nsIInternalPluginTag(aName, aDescription, aFileName, aVersion),
+ mId(sNextId++),
+ mContentProcessRunningCount(0),
+ mHadLocalInstance(false),
+ mLibrary(nullptr),
+ mIsFlashPlugin(false),
+ mSupportsAsyncRender(false),
+ mFullPath(aFullPath),
+ mLastModifiedTime(aLastModifiedTime),
+ mSandboxLevel(0),
+ mIsSandboxLoggingEnabled(false),
+ mBlocklistState(aBlocklistState) {
+ InitMime(aMimeTypes, aMimeDescriptions, aExtensions,
+ static_cast<uint32_t>(aVariants));
+ InitSandboxLevel();
+ if (!aArgsAreUTF8) EnsureMembersAreUTF8();
+ FixupVersion();
+}
+
+nsPluginTag::nsPluginTag(uint32_t aId, const char* aName,
+ const char* aDescription, const char* aFileName,
+ const char* aFullPath, const char* aVersion,
+ nsTArray<nsCString> aMimeTypes,
+ nsTArray<nsCString> aMimeDescriptions,
+ nsTArray<nsCString> aExtensions, bool aIsFlashPlugin,
+ bool aSupportsAsyncRender, int64_t aLastModifiedTime,
+ int32_t aSandboxLevel, uint32_t aBlocklistState)
+ : nsIInternalPluginTag(aName, aDescription, aFileName, aVersion, aMimeTypes,
+ aMimeDescriptions, aExtensions),
+ mId(aId),
+ mContentProcessRunningCount(0),
+ mHadLocalInstance(false),
+ mLibrary(nullptr),
+ mIsFlashPlugin(aIsFlashPlugin),
+ mSupportsAsyncRender(aSupportsAsyncRender),
+ mLastModifiedTime(aLastModifiedTime),
+ mSandboxLevel(aSandboxLevel),
+ mIsSandboxLoggingEnabled(false),
+ mNiceFileName(),
+ mBlocklistState(aBlocklistState) {}
+
+nsPluginTag::~nsPluginTag() {
+ NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349");
+}
+
+NS_IMPL_ISUPPORTS(nsPluginTag, nsPluginTag, nsIInternalPluginTag, nsIPluginTag)
+
+void nsPluginTag::InitMime(const char* const* aMimeTypes,
+ const char* const* aMimeDescriptions,
+ const char* const* aExtensions,
+ uint32_t aVariantCount) {
+ if (!aMimeTypes) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < aVariantCount; i++) {
+ if (!aMimeTypes[i]) {
+ continue;
+ }
+
+ nsAutoCString mimeType(aMimeTypes[i]);
+
+ // Convert the MIME type, which is case insensitive, to lowercase in order
+ // to properly handle a mixed-case type.
+ ToLowerCase(mimeType);
+
+ // Look for certain special plugins.
+ switch (nsPluginHost::GetSpecialType(mimeType)) {
+ case nsPluginHost::eSpecialType_Flash:
+ // VLC sometimes claims to implement the Flash MIME type, and we want
+ // to allow users to control that separately from Adobe Flash.
+ if (Name().EqualsLiteral("Shockwave Flash")) {
+ mIsFlashPlugin = true;
+ }
+ break;
+ case nsPluginHost::eSpecialType_Test:
+ case nsPluginHost::eSpecialType_None:
+ default:
+ break;
+ }
+
+ // Fill in our MIME type array.
+ mMimeTypes.AppendElement(mimeType);
+
+ // Now fill in the MIME descriptions.
+ if (aMimeDescriptions && aMimeDescriptions[i]) {
+ // we should cut off the list of suffixes which the mime
+ // description string may have, see bug 53895
+ // it is usually in form "some description (*.sf1, *.sf2)"
+ // so we can search for the opening round bracket
+ char cur = '\0';
+ char pre = '\0';
+ char* p = PL_strrchr(aMimeDescriptions[i], '(');
+ if (p && (p != aMimeDescriptions[i])) {
+ if ((p - 1) && *(p - 1) == ' ') {
+ pre = *(p - 1);
+ *(p - 1) = '\0';
+ } else {
+ cur = *p;
+ *p = '\0';
+ }
+ }
+ mMimeDescriptions.AppendElement(nsCString(aMimeDescriptions[i]));
+ // restore the original string
+ if (cur != '\0') {
+ *p = cur;
+ }
+ if (pre != '\0') {
+ *(p - 1) = pre;
+ }
+ } else {
+ mMimeDescriptions.AppendElement(nsCString());
+ }
+
+ // Now fill in the extensions.
+ if (aExtensions && aExtensions[i]) {
+ mExtensions.AppendElement(nsCString(aExtensions[i]));
+ } else {
+ mExtensions.AppendElement(nsCString());
+ }
+ }
+}
+
+void nsPluginTag::InitSandboxLevel() {
+ MOZ_ASSERT(sInitializedSandboxingInfo,
+ "Should have initialized global sandboxing info");
+#if defined(MOZ_SANDBOX)
+# if defined(XP_MACOSX)
+ mSandboxLevel = mIsFlashPlugin ? sFlashSandboxLevel : sDefaultSandboxLevel;
+ mIsSandboxLoggingEnabled = mIsFlashPlugin && sEnableSandboxLogging;
+# elif defined(XP_WIN)
+ mSandboxLevel = mIsFlashPlugin ? sFlashSandboxLevel : sDefaultSandboxLevel;
+# endif /* defined(XP_MACOSX) / defined(XP_WIN) */
+#endif /* defined(MOZ_SANDBOX) */
+}
+
+#if !defined(XP_WIN) && !defined(XP_MACOSX)
+static void ConvertToUTF8(nsCString& aString) {
+ Unused << UTF_8_ENCODING->DecodeWithoutBOMHandling(aString, aString);
+}
+#endif
+
+nsresult nsPluginTag::EnsureMembersAreUTF8() {
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ return NS_OK;
+#else
+ ConvertToUTF8(mFileName);
+ ConvertToUTF8(mFullPath);
+ ConvertToUTF8(mName);
+ ConvertToUTF8(mDescription);
+ for (uint32_t i = 0; i < mMimeDescriptions.Length(); ++i) {
+ ConvertToUTF8(mMimeDescriptions[i]);
+ }
+ return NS_OK;
+#endif
+}
+
+void nsPluginTag::FixupVersion() {
+#if defined(XP_LINUX)
+ if (mIsFlashPlugin) {
+ mVersion.ReplaceChar(',', '.');
+ }
+#endif
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetDescription(nsACString& aDescription) {
+ aDescription = mDescription;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetIsFlashPlugin(bool* aIsFlash) {
+ *aIsFlash = mIsFlashPlugin;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetFilename(nsACString& aFileName) {
+ aFileName = mFileName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetFullpath(nsACString& aFullPath) {
+ aFullPath = mFullPath;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetVersion(nsACString& aVersion) {
+ aVersion = mVersion;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetName(nsACString& aName) {
+ aName = mName;
+ return NS_OK;
+}
+
+bool nsPluginTag::IsActive() { return IsEnabled() && !IsBlocklisted(); }
+
+NS_IMETHODIMP
+nsPluginTag::GetActive(bool* aResult) {
+ *aResult = IsActive();
+ return NS_OK;
+}
+
+bool nsPluginTag::IsEnabled() {
+ const PluginState state = GetPluginState();
+ return (state == ePluginState_Enabled) || (state == ePluginState_Clicktoplay);
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetDisabled(bool* aDisabled) {
+ *aDisabled = !IsEnabled();
+ return NS_OK;
+}
+
+bool nsPluginTag::IsBlocklisted() {
+ return mBlocklistState == nsIBlocklistService::STATE_BLOCKED;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetBlocklisted(bool* aBlocklisted) {
+ *aBlocklisted = IsBlocklisted();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetIsEnabledStateLocked(bool* aIsEnabledStateLocked) {
+ return IsEnabledStateLockedForPlugin(this, aIsEnabledStateLocked);
+}
+
+bool nsPluginTag::IsClicktoplay() {
+ const PluginState state = GetPluginState();
+ return (state == ePluginState_Clicktoplay);
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetClicktoplay(bool* aClicktoplay) {
+ *aClicktoplay = IsClicktoplay();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetEnabledState(uint32_t* aEnabledState) {
+ int32_t enabledState;
+ nsresult rv = NS_OK;
+ if (mIsFlashPlugin) {
+ enabledState = StaticPrefs::plugin_state_flash();
+ if (enabledState == nsIPluginTag::STATE_ENABLED) {
+ enabledState = nsIPluginTag::STATE_CLICKTOPLAY;
+ }
+ } else {
+ rv = Preferences::GetInt(GetStatePrefNameForPlugin(this).get(),
+ &enabledState);
+ }
+ if (NS_SUCCEEDED(rv) && enabledState >= nsIPluginTag::STATE_DISABLED &&
+ enabledState <= nsIPluginTag::STATE_ENABLED) {
+ *aEnabledState = (uint32_t)enabledState;
+ return rv;
+ }
+
+ // Something went wrong fetching the plugin's state (e.g. it wasn't flash
+ // and the preference was not present) - use the default state:
+ enabledState = Preferences::GetInt(kPrefDefaultEnabledState,
+ nsIPluginTag::STATE_ENABLED);
+ if (enabledState == nsIPluginTag::STATE_ENABLED && mIsFlashPlugin) {
+ enabledState = nsIPluginTag::STATE_CLICKTOPLAY;
+ }
+ if (enabledState >= nsIPluginTag::STATE_DISABLED &&
+ enabledState <= nsIPluginTag::STATE_ENABLED) {
+ *aEnabledState = (uint32_t)enabledState;
+ return NS_OK;
+ }
+
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+nsPluginTag::SetEnabledState(uint32_t aEnabledState) {
+ if (aEnabledState >= ePluginState_MaxValue) return NS_ERROR_ILLEGAL_VALUE;
+ if (aEnabledState == nsIPluginTag::STATE_ENABLED && mIsFlashPlugin) {
+ aEnabledState = nsIPluginTag::STATE_CLICKTOPLAY;
+ }
+ uint32_t oldState = nsIPluginTag::STATE_DISABLED;
+ GetEnabledState(&oldState);
+ if (oldState != aEnabledState) {
+ Preferences::SetInt(GetStatePrefNameForPlugin(this).get(), aEnabledState);
+ if (RefPtr<nsPluginHost> host = nsPluginHost::GetInst()) {
+ host->UpdatePluginInfo(this);
+ }
+ }
+ return NS_OK;
+}
+
+nsPluginTag::PluginState nsPluginTag::GetPluginState() {
+ uint32_t enabledState = nsIPluginTag::STATE_DISABLED;
+ GetEnabledState(&enabledState);
+ return (PluginState)enabledState;
+}
+
+void nsPluginTag::SetPluginState(PluginState state) {
+ static_assert((uint32_t)nsPluginTag::ePluginState_Disabled ==
+ nsIPluginTag::STATE_DISABLED,
+ "nsPluginTag::ePluginState_Disabled must match "
+ "nsIPluginTag::STATE_DISABLED");
+ static_assert((uint32_t)nsPluginTag::ePluginState_Clicktoplay ==
+ nsIPluginTag::STATE_CLICKTOPLAY,
+ "nsPluginTag::ePluginState_Clicktoplay must match "
+ "nsIPluginTag::STATE_CLICKTOPLAY");
+ static_assert((uint32_t)nsPluginTag::ePluginState_Enabled ==
+ nsIPluginTag::STATE_ENABLED,
+ "nsPluginTag::ePluginState_Enabled must match "
+ "nsIPluginTag::STATE_ENABLED");
+ SetEnabledState((uint32_t)state);
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetMimeTypes(nsTArray<nsCString>& aResults) {
+ aResults = mMimeTypes.Clone();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetMimeDescriptions(nsTArray<nsCString>& aResults) {
+ aResults = mMimeDescriptions.Clone();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetExtensions(nsTArray<nsCString>& aResults) {
+ aResults = mExtensions.Clone();
+ return NS_OK;
+}
+
+bool nsPluginTag::HasSameNameAndMimes(const nsPluginTag* aPluginTag) const {
+ NS_ENSURE_TRUE(aPluginTag, false);
+
+ if ((!mName.Equals(aPluginTag->mName)) ||
+ (mMimeTypes.Length() != aPluginTag->mMimeTypes.Length())) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < mMimeTypes.Length(); i++) {
+ if (!mMimeTypes[i].Equals(aPluginTag->mMimeTypes[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetLoaded(bool* aIsLoaded) {
+ *aIsLoaded = !!mPlugin;
+ return NS_OK;
+}
+
+void nsPluginTag::TryUnloadPlugin(bool inShutdown) {
+ // We never want to send NPP_Shutdown to an in-process plugin unless
+ // this process is shutting down.
+ if (!mPlugin) {
+ return;
+ }
+ if (inShutdown || mPlugin->GetLibrary()->IsOOP()) {
+ mPlugin->Shutdown();
+ mPlugin = nullptr;
+ }
+}
+
+/* static */ void nsPluginTag::EnsureSandboxInformation() {
+ if (sInitializedSandboxingInfo) {
+ return;
+ }
+ MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread.");
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ Preferences::GetInt("dom.ipc.plugins.sandbox-level.default",
+ &sDefaultSandboxLevel);
+ Preferences::GetInt("dom.ipc.plugins.sandbox-level.flash",
+ &sFlashSandboxLevel);
+# if defined(_AMD64_)
+ // Level 3 is now the default NPAPI sandbox level for 64-bit flash.
+ // We permit the user to drop the sandbox level by at most 1. This should
+ // be kept up to date with the default value in the firefox.js pref file.
+ sFlashSandboxLevel = std::max(sFlashSandboxLevel, 2);
+# endif
+#elif defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ int legacyOSMinorMax = Preferences::GetInt(
+ "dom.ipc.plugins.sandbox-level.flash.max-legacy-os-minor", 10);
+ const char* levelPref = "dom.ipc.plugins.sandbox-level.flash";
+
+ if (PR_GetEnv("MOZ_DISABLE_NPAPI_SANDBOX")) {
+ // Flash sandbox disabled
+ sFlashSandboxLevel = 0;
+ } else if (nsCocoaFeatures::macOSVersionMajor() == 10 &&
+ nsCocoaFeatures::macOSVersionMinor() <= legacyOSMinorMax) {
+ const char* legacyLevelPref = "dom.ipc.plugins.sandbox-level.flash.legacy";
+ int32_t compatLevel = Preferences::GetInt(legacyLevelPref, 0);
+ int32_t level = Preferences::GetInt(levelPref, 0);
+ sFlashSandboxLevel = std::min(compatLevel, level);
+ } else {
+ sFlashSandboxLevel = Preferences::GetInt(levelPref, 0);
+ }
+ sFlashSandboxLevel = ClampFlashSandboxLevel(sFlashSandboxLevel);
+
+ // At present, Flash is the only supported plugin on macOS.
+ // Other test plugins are used during testing and they will use
+ // the default plugin sandbox level.
+ Preferences::GetInt("dom.ipc.plugins.sandbox-level.default",
+ &sDefaultSandboxLevel);
+
+ // Enable sandbox logging in the plugin process if it has
+ // been turned on via prefs or environment variables.
+ sEnableSandboxLogging =
+ sFlashSandboxLevel > 0 &&
+ (Preferences::GetBool("security.sandbox.logging.enabled") ||
+ PR_GetEnv("MOZ_SANDBOX_LOGGING") ||
+ PR_GetEnv("MOZ_SANDBOX_MAC_FLASH_LOGGING"));
+#endif
+ sInitializedSandboxingInfo = true;
+}
+
+const nsCString& nsPluginTag::GetNiceFileName() {
+ if (!mNiceFileName.IsEmpty()) {
+ return mNiceFileName;
+ }
+
+ if (mIsFlashPlugin) {
+ mNiceFileName.AssignLiteral("flash");
+ return mNiceFileName;
+ }
+
+ mNiceFileName = MakeNiceFileName(mFileName);
+ return mNiceFileName;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetNiceName(nsACString& aResult) {
+ aResult = GetNiceFileName();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetBlocklistState(uint32_t* aResult) {
+ *aResult = mBlocklistState;
+ return NS_OK;
+}
+
+void nsPluginTag::SetBlocklistState(uint32_t aBlocklistState) {
+ mBlocklistState = aBlocklistState;
+}
+
+uint32_t nsPluginTag::BlocklistState() { return mBlocklistState; }
+
+NS_IMETHODIMP
+nsPluginTag::GetLastModifiedTime(PRTime* aLastModifiedTime) {
+ MOZ_ASSERT(aLastModifiedTime);
+ *aLastModifiedTime = mLastModifiedTime;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::GetId(uint32_t* aId) {
+ *aId = mId;
+ return NS_OK;
+}
+
+/* nsFakePluginTag */
+
+nsFakePluginTag::nsFakePluginTag()
+ : mId(sNextId++), mState(nsPluginTag::ePluginState_Disabled) {}
+
+nsFakePluginTag::nsFakePluginTag(uint32_t aId,
+ already_AddRefed<nsIURI>&& aHandlerURI,
+ const char* aName, const char* aDescription,
+ const nsTArray<nsCString>& aMimeTypes,
+ const nsTArray<nsCString>& aMimeDescriptions,
+ const nsTArray<nsCString>& aExtensions,
+ const nsCString& aNiceName,
+ const nsString& aSandboxScript)
+ : nsIInternalPluginTag(aName, aDescription, nullptr, nullptr, aMimeTypes,
+ aMimeDescriptions, aExtensions),
+ mId(aId),
+ mHandlerURI(aHandlerURI),
+ mNiceName(aNiceName),
+ mSandboxScript(aSandboxScript),
+ mState(nsPluginTag::ePluginState_Enabled) {}
+
+nsFakePluginTag::~nsFakePluginTag() = default;
+
+NS_IMPL_ADDREF(nsFakePluginTag)
+NS_IMPL_RELEASE(nsFakePluginTag)
+NS_INTERFACE_TABLE_HEAD(nsFakePluginTag)
+ NS_INTERFACE_TABLE_BEGIN
+ NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsFakePluginTag, nsIPluginTag,
+ nsIInternalPluginTag)
+ NS_INTERFACE_TABLE_ENTRY(nsFakePluginTag, nsIInternalPluginTag)
+ NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsFakePluginTag, nsISupports,
+ nsIInternalPluginTag)
+ NS_INTERFACE_TABLE_ENTRY(nsFakePluginTag, nsIFakePluginTag)
+ NS_INTERFACE_TABLE_END
+NS_INTERFACE_TABLE_TAIL
+
+/* static */
+nsresult nsFakePluginTag::Create(const FakePluginTagInit& aInitDictionary,
+ nsFakePluginTag** aPluginTag) {
+ NS_ENSURE_TRUE(sNextId <= PR_INT32_MAX, NS_ERROR_OUT_OF_MEMORY);
+ NS_ENSURE_TRUE(!aInitDictionary.mMimeEntries.IsEmpty(), NS_ERROR_INVALID_ARG);
+
+ RefPtr<nsFakePluginTag> tag = new nsFakePluginTag();
+ nsresult rv =
+ NS_NewURI(getter_AddRefs(tag->mHandlerURI), aInitDictionary.mHandlerURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CopyUTF16toUTF8(aInitDictionary.mNiceName, tag->mNiceName);
+ CopyUTF16toUTF8(aInitDictionary.mFullPath, tag->mFullPath);
+ CopyUTF16toUTF8(aInitDictionary.mName, tag->mName);
+ CopyUTF16toUTF8(aInitDictionary.mDescription, tag->mDescription);
+ CopyUTF16toUTF8(aInitDictionary.mFileName, tag->mFileName);
+ CopyUTF16toUTF8(aInitDictionary.mVersion, tag->mVersion);
+ tag->mSandboxScript = aInitDictionary.mSandboxScript;
+
+ for (const mozilla::dom::FakePluginMimeEntry& mimeEntry :
+ aInitDictionary.mMimeEntries) {
+ CopyUTF16toUTF8(mimeEntry.mType, *tag->mMimeTypes.AppendElement());
+ CopyUTF16toUTF8(mimeEntry.mDescription,
+ *tag->mMimeDescriptions.AppendElement());
+ CopyUTF16toUTF8(mimeEntry.mExtension, *tag->mExtensions.AppendElement());
+ }
+
+ tag.forget(aPluginTag);
+ return NS_OK;
+}
+
+bool nsFakePluginTag::HandlerURIMatches(nsIURI* aURI) {
+ bool equals = false;
+ return NS_SUCCEEDED(mHandlerURI->Equals(aURI, &equals)) && equals;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetHandlerURI(nsIURI** aResult) {
+ NS_IF_ADDREF(*aResult = mHandlerURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetSandboxScript(nsAString& aSandboxScript) {
+ aSandboxScript = mSandboxScript;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetDescription(/* utf-8 */ nsACString& aResult) {
+ aResult = mDescription;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetIsFlashPlugin(bool* aIsFlash) {
+ *aIsFlash = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetFilename(/* utf-8 */ nsACString& aResult) {
+ aResult = mFileName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetFullpath(/* utf-8 */ nsACString& aResult) {
+ aResult = mFullPath;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetVersion(/* utf-8 */ nsACString& aResult) {
+ aResult = mVersion;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetName(/* utf-8 */ nsACString& aResult) {
+ aResult = mName;
+ return NS_OK;
+}
+
+const nsCString& nsFakePluginTag::GetNiceFileName() {
+ // We don't try to mimic the special-cased flash/java names if the fake plugin
+ // claims one of their MIME types, but do allow directly setting niceName if
+ // emulating those is desired.
+ if (mNiceName.IsEmpty() && !mFileName.IsEmpty()) {
+ mNiceName = MakeNiceFileName(mFileName);
+ }
+
+ return mNiceName;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetNiceName(/* utf-8 */ nsACString& aResult) {
+ aResult = GetNiceFileName();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetBlocklistState(uint32_t* aResult) {
+ // Fake tags don't currently support blocklisting
+ *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetBlocklisted(bool* aBlocklisted) {
+ // Fake tags can't be blocklisted
+ *aBlocklisted = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetIsEnabledStateLocked(bool* aIsEnabledStateLocked) {
+ return IsEnabledStateLockedForPlugin(this, aIsEnabledStateLocked);
+}
+
+bool nsFakePluginTag::IsEnabled() {
+ return mState == nsPluginTag::ePluginState_Enabled ||
+ mState == nsPluginTag::ePluginState_Clicktoplay;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetDisabled(bool* aDisabled) {
+ *aDisabled = !IsEnabled();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetClicktoplay(bool* aClicktoplay) {
+ *aClicktoplay = (mState == nsPluginTag::ePluginState_Clicktoplay);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetEnabledState(uint32_t* aEnabledState) {
+ *aEnabledState = (uint32_t)mState;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::SetEnabledState(uint32_t aEnabledState) {
+ // There are static asserts above enforcing that this enum matches
+ mState = (nsPluginTag::PluginState)aEnabledState;
+ // FIXME-jsplugins update
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetMimeTypes(nsTArray<nsCString>& aResults) {
+ aResults = mMimeTypes.Clone();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetMimeDescriptions(nsTArray<nsCString>& aResults) {
+ aResults = mMimeDescriptions.Clone();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetExtensions(nsTArray<nsCString>& aResults) {
+ aResults = mExtensions.Clone();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetActive(bool* aResult) {
+ // Fake plugins can't be blocklisted, so this is just !Disabled
+ *aResult = IsEnabled();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetLastModifiedTime(PRTime* aLastModifiedTime) {
+ // FIXME-jsplugins What should this return, if anything?
+ MOZ_ASSERT(aLastModifiedTime);
+ *aLastModifiedTime = 0;
+ return NS_OK;
+}
+
+// We don't load fake plugins out of a library, so they should always be there.
+NS_IMETHODIMP
+nsFakePluginTag::GetLoaded(bool* ret) {
+ *ret = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFakePluginTag::GetId(uint32_t* aId) {
+ *aId = mId;
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nsPluginTags.h b/dom/plugins/base/nsPluginTags.h
new file mode 100644
index 0000000000..73e597cb2f
--- /dev/null
+++ b/dom/plugins/base/nsPluginTags.h
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsPluginTags_h_
+#define nsPluginTags_h_
+
+#include "mozilla/Attributes.h"
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsIPluginTag.h"
+#include "nsITimer.h"
+#include "nsString.h"
+
+class nsIURI;
+struct PRLibrary;
+struct nsPluginInfo;
+class nsNPAPIPlugin;
+
+namespace mozilla {
+namespace dom {
+struct FakePluginTagInit;
+} // namespace dom
+} // namespace mozilla
+
+// An interface representing plugin tags internally.
+#define NS_IINTERNALPLUGINTAG_IID \
+ { \
+ 0xe8fdd227, 0x27da, 0x46ee, { \
+ 0xbe, 0xf3, 0x1a, 0xef, 0x5a, 0x8f, 0xc5, 0xb4 \
+ } \
+ }
+
+#define NS_PLUGINTAG_IID \
+ { \
+ 0xcce2e8b9, 0x9702, 0x4d4b, { \
+ 0xbe, 0xa4, 0x7c, 0x1e, 0x13, 0x1f, 0xaf, 0x78 \
+ } \
+ }
+class nsIInternalPluginTag : public nsIPluginTag {
+ public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IINTERNALPLUGINTAG_IID)
+
+ nsIInternalPluginTag();
+ nsIInternalPluginTag(const char* aName, const char* aDescription,
+ const char* aFileName, const char* aVersion);
+ nsIInternalPluginTag(const char* aName, const char* aDescription,
+ const char* aFileName, const char* aVersion,
+ const nsTArray<nsCString>& aMimeTypes,
+ const nsTArray<nsCString>& aMimeDescriptions,
+ const nsTArray<nsCString>& aExtensions);
+
+ virtual bool IsEnabled() = 0;
+ virtual const nsCString& GetNiceFileName() = 0;
+
+ const nsCString& Name() const { return mName; }
+ const nsCString& Description() const { return mDescription; }
+
+ const nsTArray<nsCString>& MimeTypes() const { return mMimeTypes; }
+
+ const nsTArray<nsCString>& MimeDescriptions() const {
+ return mMimeDescriptions;
+ }
+
+ const nsTArray<nsCString>& Extensions() const { return mExtensions; }
+
+ const nsCString& FileName() const { return mFileName; }
+
+ const nsCString& Version() const { return mVersion; }
+
+ // Returns true if this plugin claims it supports this MIME type. The
+ // comparison is done ASCII-case-insensitively.
+ bool HasMimeType(const nsACString& aMimeType) const;
+
+ // Returns true if this plugin claims it supports the given extension. In
+ // that case, aMatchingType is set to the MIME type the plugin claims
+ // corresponds to this extension. The match on aExtension is done
+ // ASCII-case-insensitively.
+ bool HasExtension(const nsACString& aExtension,
+ /* out */ nsACString& aMatchingType) const;
+
+ protected:
+ ~nsIInternalPluginTag();
+
+ nsCString mName; // UTF-8
+ nsCString mDescription; // UTF-8
+ nsCString mFileName; // UTF-8
+ nsCString mVersion; // UTF-8
+ nsTArray<nsCString> mMimeTypes; // UTF-8
+ nsTArray<nsCString> mMimeDescriptions; // UTF-8
+ nsTArray<nsCString> mExtensions; // UTF-8
+
+ static uint32_t sNextId;
+};
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIInternalPluginTag, NS_IINTERNALPLUGINTAG_IID)
+
+// A linked-list of plugin information that is used for instantiating plugins
+// and reflecting plugin information into JavaScript.
+class nsPluginTag final : public nsIInternalPluginTag {
+ public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_PLUGINTAG_IID)
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIPLUGINTAG
+
+ // These must match the STATE_* values in nsIPluginTag.idl
+ enum PluginState {
+ ePluginState_Disabled = 0,
+ ePluginState_Clicktoplay = 1,
+ ePluginState_Enabled = 2,
+ ePluginState_MaxValue = 3,
+ };
+
+ nsPluginTag(nsPluginInfo* aPluginInfo, int64_t aLastModifiedTime,
+ uint32_t aBlocklistState);
+ nsPluginTag(const char* aName, const char* aDescription,
+ const char* aFileName, const char* aFullPath,
+ const char* aVersion, const char* const* aMimeTypes,
+ const char* const* aMimeDescriptions,
+ const char* const* aExtensions, int32_t aVariants,
+ int64_t aLastModifiedTime, uint32_t aBlocklistState,
+ bool aArgsAreUTF8 = false);
+ nsPluginTag(uint32_t aId, const char* aName, const char* aDescription,
+ const char* aFileName, const char* aFullPath,
+ const char* aVersion, nsTArray<nsCString> aMimeTypes,
+ nsTArray<nsCString> aMimeDescriptions,
+ nsTArray<nsCString> aExtensions, bool aIsFlashPlugin,
+ bool aSupportsAsyncRender, int64_t aLastModifiedTime,
+ int32_t aSandboxLevel, uint32_t aBlocklistState);
+
+ void TryUnloadPlugin(bool inShutdown);
+
+ static void EnsureSandboxInformation();
+
+ // plugin is enabled and not blocklisted
+ bool IsActive();
+
+ bool IsEnabled() override;
+ void SetEnabled(bool enabled);
+ bool IsClicktoplay();
+ bool IsBlocklisted();
+ uint32_t BlocklistState();
+
+ PluginState GetPluginState();
+ void SetPluginState(PluginState state);
+ void SetBlocklistState(uint32_t aBlocklistState);
+
+ bool HasSameNameAndMimes(const nsPluginTag* aPluginTag) const;
+ const nsCString& GetNiceFileName() override;
+
+ RefPtr<nsPluginTag> mNext;
+ uint32_t mId;
+
+ // Number of PluginModuleParents living in all content processes.
+ size_t mContentProcessRunningCount;
+
+ // True if we've ever created an instance of this plugin in the current
+ // process.
+ bool mHadLocalInstance;
+
+ PRLibrary* mLibrary;
+ RefPtr<nsNPAPIPlugin> mPlugin;
+ bool mIsFlashPlugin;
+ bool mSupportsAsyncRender;
+ nsCString mFullPath; // UTF-8
+ int64_t mLastModifiedTime;
+ nsCOMPtr<nsITimer> mUnloadTimer;
+ int32_t mSandboxLevel;
+ bool mIsSandboxLoggingEnabled;
+
+ private:
+ virtual ~nsPluginTag();
+
+ nsCString mNiceFileName; // UTF-8
+ uint32_t mBlocklistState;
+
+ void InitMime(const char* const* aMimeTypes,
+ const char* const* aMimeDescriptions,
+ const char* const* aExtensions, uint32_t aVariantCount);
+ void InitSandboxLevel();
+ nsresult EnsureMembersAreUTF8();
+ void FixupVersion();
+};
+NS_DEFINE_STATIC_IID_ACCESSOR(nsPluginTag, NS_PLUGINTAG_IID)
+
+// A class representing "fake" plugin tags; that is plugin tags not
+// corresponding to actual NPAPI plugins. In practice these are all
+// JS-implemented plugins; maybe we want a better name for this class?
+class nsFakePluginTag : public nsIInternalPluginTag, public nsIFakePluginTag {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPLUGINTAG
+ NS_DECL_NSIFAKEPLUGINTAG
+
+ static nsresult Create(const mozilla::dom::FakePluginTagInit& aInitDictionary,
+ nsFakePluginTag** aPluginTag);
+ nsFakePluginTag(uint32_t aId, already_AddRefed<nsIURI>&& aHandlerURI,
+ const char* aName, const char* aDescription,
+ const nsTArray<nsCString>& aMimeTypes,
+ const nsTArray<nsCString>& aMimeDescriptions,
+ const nsTArray<nsCString>& aExtensions,
+ const nsCString& aNiceName, const nsString& aSandboxScript);
+
+ bool IsEnabled() override;
+ const nsCString& GetNiceFileName() override;
+
+ bool HandlerURIMatches(nsIURI* aURI);
+
+ nsIURI* HandlerURI() const { return mHandlerURI; }
+
+ uint32_t Id() const { return mId; }
+
+ const nsString& SandboxScript() const { return mSandboxScript; }
+
+ static const int32_t NOT_JSPLUGIN = -1;
+
+ private:
+ nsFakePluginTag();
+ virtual ~nsFakePluginTag();
+
+ // A unique id for this JS-implemented plugin. Registering a plugin through
+ // nsPluginHost::RegisterFakePlugin assigns a new id. The id is transferred
+ // through IPC when getting the list of JS-implemented plugins from child
+ // processes, so it should be consistent across processes.
+ // 0 is a valid id.
+ uint32_t mId;
+
+ // The URI of the handler for our fake plugin.
+ // FIXME-jsplugins do we need to sanity check these?
+ nsCOMPtr<nsIURI> mHandlerURI;
+
+ nsCString mFullPath;
+ nsCString mNiceName;
+
+ nsString mSandboxScript;
+
+ nsPluginTag::PluginState mState;
+};
+
+#endif // nsPluginTags_h_
diff --git a/dom/plugins/base/nsPluginsCID.h b/dom/plugins/base/nsPluginsCID.h
new file mode 100644
index 0000000000..14fb48642e
--- /dev/null
+++ b/dom/plugins/base/nsPluginsCID.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsPluginsCID_h_
+#define nsPluginsCID_h_
+
+#define NS_PLUGIN_HOST_CID \
+ { \
+ 0x23E8FD98, 0xA625, 0x4B08, { \
+ 0xBE, 0x1A, 0xF7, 0xCC, 0x18, 0xA5, 0xB1, 0x06 \
+ } \
+ }
+
+#endif // nsPluginsCID_h_
diff --git a/dom/plugins/base/nsPluginsDir.h b/dom/plugins/base/nsPluginsDir.h
new file mode 100644
index 0000000000..42a24f8382
--- /dev/null
+++ b/dom/plugins/base/nsPluginsDir.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsPluginsDir_h_
+#define nsPluginsDir_h_
+
+#include "nsError.h"
+#include "nsIFile.h"
+
+/**
+ * nsPluginsDir is nearly obsolete. Directory Service should be used instead.
+ * It exists for the sake of one static function.
+ */
+
+class nsPluginsDir {
+ public:
+ /**
+ * Determines whether or not the given file is actually a plugin file.
+ */
+ static bool IsPluginFile(nsIFile* file);
+};
+
+struct PRLibrary;
+
+struct nsPluginInfo {
+ char* fName; // name of the plugin
+ char* fDescription; // etc.
+ uint32_t fVariantCount;
+ char** fMimeTypeArray;
+ char** fMimeDescriptionArray;
+ char** fExtensionArray;
+ char* fFileName;
+ char* fFullPath;
+ char* fVersion;
+ bool fSupportsAsyncRender;
+};
+
+/**
+ * Provides cross-platform access to a plugin file. Deals with reading
+ * properties from the plugin file, and loading the plugin's shared
+ * library. Insulates core nsIPluginHost implementations from these
+ * details.
+ */
+class nsPluginFile {
+#ifndef XP_WIN
+ PRLibrary* pLibrary;
+#endif
+ nsCOMPtr<nsIFile> mPlugin;
+
+ public:
+ /**
+ * If spec corresponds to a valid plugin file, constructs a reference
+ * to a plugin file on disk. Plugins are typically located using the
+ * nsPluginsDir class.
+ */
+ explicit nsPluginFile(nsIFile* spec);
+ virtual ~nsPluginFile();
+
+ /**
+ * Loads the plugin into memory using NSPR's shared-library loading
+ * mechanism. Handles platform differences in loading shared libraries.
+ */
+ nsresult LoadPlugin(PRLibrary** outLibrary);
+
+ /**
+ * Obtains all of the information currently available for this plugin.
+ * Has a library outparam which will be non-null if a library load was
+ * required.
+ */
+ nsresult GetPluginInfo(nsPluginInfo& outPluginInfo, PRLibrary** outLibrary);
+
+ /**
+ * Should be called after GetPluginInfo to free all allocated stuff
+ */
+ nsresult FreePluginInfo(nsPluginInfo& PluginInfo);
+};
+
+#endif /* nsPluginsDir_h_ */
diff --git a/dom/plugins/base/nsPluginsDirDarwin.cpp b/dom/plugins/base/nsPluginsDirDarwin.cpp
new file mode 100644
index 0000000000..8efe40f698
--- /dev/null
+++ b/dom/plugins/base/nsPluginsDirDarwin.cpp
@@ -0,0 +1,522 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/*
+ nsPluginsDirDarwin.cpp
+
+ Mac OS X implementation of the nsPluginsDir/nsPluginsFile classes.
+
+ by Patrick C. Beard.
+ */
+
+#include "GeckoChildProcessHost.h"
+#include "base/process_util.h"
+
+#include "prlink.h"
+#include "prnetdb.h"
+#include "nsXPCOM.h"
+
+#include "nsPluginsDir.h"
+#include "nsNPAPIPlugin.h"
+#include "nsPluginsDirUtils.h"
+
+#include "mozilla/UniquePtr.h"
+
+#include "nsCocoaFeatures.h"
+#include "nsExceptionHandler.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <Carbon/Carbon.h>
+#include <CoreServices/CoreServices.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+
+typedef NS_NPAPIPLUGIN_CALLBACK(const char*, NP_GETMIMEDESCRIPTION)();
+typedef NS_NPAPIPLUGIN_CALLBACK(OSErr, BP_GETSUPPORTEDMIMETYPES)(
+ BPSupportedMIMETypes* mimeInfo, UInt32 flags);
+
+/*
+** Returns a CFBundleRef if the path refers to a Mac OS X bundle directory.
+** The caller is responsible for calling CFRelease() to deallocate.
+*/
+static CFBundleRef getPluginBundle(const char* path) {
+ CFBundleRef bundle = nullptr;
+ CFStringRef pathRef =
+ ::CFStringCreateWithCString(nullptr, path, kCFStringEncodingUTF8);
+ if (pathRef) {
+ CFURLRef bundleURL = ::CFURLCreateWithFileSystemPath(
+ nullptr, pathRef, kCFURLPOSIXPathStyle, true);
+ if (bundleURL) {
+ bundle = ::CFBundleCreate(nullptr, bundleURL);
+ ::CFRelease(bundleURL);
+ }
+ ::CFRelease(pathRef);
+ }
+ return bundle;
+}
+
+bool nsPluginsDir::IsPluginFile(nsIFile* file) {
+ nsCString fileName;
+ file->GetNativeLeafName(fileName);
+ /*
+ * Don't load the VDP fake plugin, to avoid tripping a bad bug in OS X
+ * 10.5.3 (see bug 436575).
+ */
+ if (!strcmp(fileName.get(), "VerifiedDownloadPlugin.plugin")) {
+ NS_WARNING(
+ "Preventing load of VerifiedDownloadPlugin.plugin (see bug 436575)");
+ return false;
+ }
+ return true;
+}
+
+// Caller is responsible for freeing returned buffer.
+static char* CFStringRefToUTF8Buffer(CFStringRef cfString) {
+ const char* buffer = ::CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8);
+ if (buffer) {
+ return PL_strdup(buffer);
+ }
+
+ int64_t bufferLength =
+ ::CFStringGetMaximumSizeForEncoding(::CFStringGetLength(cfString),
+ kCFStringEncodingUTF8) +
+ 1;
+ char* newBuffer = static_cast<char*>(moz_xmalloc(bufferLength));
+
+ if (!::CFStringGetCString(cfString, newBuffer, bufferLength,
+ kCFStringEncodingUTF8)) {
+ free(newBuffer);
+ return nullptr;
+ }
+
+ newBuffer =
+ static_cast<char*>(moz_xrealloc(newBuffer, strlen(newBuffer) + 1));
+ return newBuffer;
+}
+
+class AutoCFTypeObject {
+ public:
+ explicit AutoCFTypeObject(CFTypeRef aObject) { mObject = aObject; }
+ ~AutoCFTypeObject() { ::CFRelease(mObject); }
+
+ private:
+ CFTypeRef mObject;
+};
+
+static Boolean MimeTypeEnabled(CFDictionaryRef mimeDict) {
+ if (!mimeDict) {
+ return true;
+ }
+
+ CFTypeRef value;
+ if (::CFDictionaryGetValueIfPresent(mimeDict, CFSTR("WebPluginTypeEnabled"),
+ &value)) {
+ if (value && ::CFGetTypeID(value) == ::CFBooleanGetTypeID()) {
+ return ::CFBooleanGetValue(static_cast<CFBooleanRef>(value));
+ }
+ }
+ return true;
+}
+
+static CFDictionaryRef ParsePlistForMIMETypesFilename(CFBundleRef bundle) {
+ CFTypeRef mimeFileName = ::CFBundleGetValueForInfoDictionaryKey(
+ bundle, CFSTR("WebPluginMIMETypesFilename"));
+ if (!mimeFileName || ::CFGetTypeID(mimeFileName) != ::CFStringGetTypeID()) {
+ return nullptr;
+ }
+
+ FSRef homeDir;
+ if (::FSFindFolder(kUserDomain, kCurrentUserFolderType, kDontCreateFolder,
+ &homeDir) != noErr) {
+ return nullptr;
+ }
+
+ CFURLRef userDirURL = ::CFURLCreateFromFSRef(kCFAllocatorDefault, &homeDir);
+ if (!userDirURL) {
+ return nullptr;
+ }
+
+ AutoCFTypeObject userDirURLAutorelease(userDirURL);
+ CFStringRef mimeFilePath = ::CFStringCreateWithFormat(
+ kCFAllocatorDefault, nullptr, CFSTR("Library/Preferences/%@"),
+ static_cast<CFStringRef>(mimeFileName));
+ if (!mimeFilePath) {
+ return nullptr;
+ }
+
+ AutoCFTypeObject mimeFilePathAutorelease(mimeFilePath);
+ CFURLRef mimeFileURL = ::CFURLCreateWithFileSystemPathRelativeToBase(
+ kCFAllocatorDefault, mimeFilePath, kCFURLPOSIXPathStyle, false,
+ userDirURL);
+ if (!mimeFileURL) {
+ return nullptr;
+ }
+
+ AutoCFTypeObject mimeFileURLAutorelease(mimeFileURL);
+ SInt32 errorCode = 0;
+ CFDataRef mimeFileData = nullptr;
+ Boolean result = ::CFURLCreateDataAndPropertiesFromResource(
+ kCFAllocatorDefault, mimeFileURL, &mimeFileData, nullptr, nullptr,
+ &errorCode);
+ if (!result) {
+ return nullptr;
+ }
+
+ AutoCFTypeObject mimeFileDataAutorelease(mimeFileData);
+ if (errorCode != 0) {
+ return nullptr;
+ }
+
+ CFPropertyListRef propertyList = ::CFPropertyListCreateFromXMLData(
+ kCFAllocatorDefault, mimeFileData, kCFPropertyListImmutable, nullptr);
+ if (!propertyList) {
+ return nullptr;
+ }
+
+ AutoCFTypeObject propertyListAutorelease(propertyList);
+ if (::CFGetTypeID(propertyList) != ::CFDictionaryGetTypeID()) {
+ return nullptr;
+ }
+
+ CFTypeRef mimeTypes = ::CFDictionaryGetValue(
+ static_cast<CFDictionaryRef>(propertyList), CFSTR("WebPluginMIMETypes"));
+ if (!mimeTypes || ::CFGetTypeID(mimeTypes) != ::CFDictionaryGetTypeID() ||
+ ::CFDictionaryGetCount(static_cast<CFDictionaryRef>(mimeTypes)) == 0) {
+ return nullptr;
+ }
+
+ return static_cast<CFDictionaryRef>(::CFRetain(mimeTypes));
+}
+
+static void ParsePlistPluginInfo(nsPluginInfo& info, CFBundleRef bundle) {
+ CFDictionaryRef mimeDict = ParsePlistForMIMETypesFilename(bundle);
+
+ if (!mimeDict) {
+ CFTypeRef mimeTypes = ::CFBundleGetValueForInfoDictionaryKey(
+ bundle, CFSTR("WebPluginMIMETypes"));
+ if (!mimeTypes || ::CFGetTypeID(mimeTypes) != ::CFDictionaryGetTypeID() ||
+ ::CFDictionaryGetCount(static_cast<CFDictionaryRef>(mimeTypes)) == 0)
+ return;
+ mimeDict = static_cast<CFDictionaryRef>(::CFRetain(mimeTypes));
+ }
+
+ AutoCFTypeObject mimeDictAutorelease(mimeDict);
+ int mimeDictKeyCount = ::CFDictionaryGetCount(mimeDict);
+
+ // Allocate memory for mime data
+ int mimeDataArraySize = mimeDictKeyCount * sizeof(char*);
+ info.fMimeTypeArray = static_cast<char**>(moz_xmalloc(mimeDataArraySize));
+ memset(info.fMimeTypeArray, 0, mimeDataArraySize);
+ info.fExtensionArray = static_cast<char**>(moz_xmalloc(mimeDataArraySize));
+ memset(info.fExtensionArray, 0, mimeDataArraySize);
+ info.fMimeDescriptionArray =
+ static_cast<char**>(moz_xmalloc(mimeDataArraySize));
+ memset(info.fMimeDescriptionArray, 0, mimeDataArraySize);
+
+ // Allocate memory for mime dictionary keys and values
+ mozilla::UniquePtr<CFTypeRef[]> keys(new CFTypeRef[mimeDictKeyCount]);
+ mozilla::UniquePtr<CFTypeRef[]> values(new CFTypeRef[mimeDictKeyCount]);
+
+ info.fVariantCount = 0;
+
+ ::CFDictionaryGetKeysAndValues(mimeDict, keys.get(), values.get());
+ for (int i = 0; i < mimeDictKeyCount; i++) {
+ CFTypeRef mimeString = keys[i];
+ if (!mimeString || ::CFGetTypeID(mimeString) != ::CFStringGetTypeID()) {
+ continue;
+ }
+ CFTypeRef mimeDict = values[i];
+ if (mimeDict && ::CFGetTypeID(mimeDict) == ::CFDictionaryGetTypeID()) {
+ if (!MimeTypeEnabled(static_cast<CFDictionaryRef>(mimeDict))) {
+ continue;
+ }
+ info.fMimeTypeArray[info.fVariantCount] =
+ CFStringRefToUTF8Buffer(static_cast<CFStringRef>(mimeString));
+ if (!info.fMimeTypeArray[info.fVariantCount]) {
+ continue;
+ }
+ CFTypeRef extensions = ::CFDictionaryGetValue(
+ static_cast<CFDictionaryRef>(mimeDict), CFSTR("WebPluginExtensions"));
+ if (extensions && ::CFGetTypeID(extensions) == ::CFArrayGetTypeID()) {
+ int extensionCount =
+ ::CFArrayGetCount(static_cast<CFArrayRef>(extensions));
+ CFMutableStringRef extensionList =
+ ::CFStringCreateMutable(kCFAllocatorDefault, 0);
+ for (int j = 0; j < extensionCount; j++) {
+ CFTypeRef extension =
+ ::CFArrayGetValueAtIndex(static_cast<CFArrayRef>(extensions), j);
+ if (extension && ::CFGetTypeID(extension) == ::CFStringGetTypeID()) {
+ if (j > 0) ::CFStringAppend(extensionList, CFSTR(","));
+ ::CFStringAppend(static_cast<CFMutableStringRef>(extensionList),
+ static_cast<CFStringRef>(extension));
+ }
+ }
+ info.fExtensionArray[info.fVariantCount] =
+ CFStringRefToUTF8Buffer(static_cast<CFStringRef>(extensionList));
+ ::CFRelease(extensionList);
+ }
+ CFTypeRef description =
+ ::CFDictionaryGetValue(static_cast<CFDictionaryRef>(mimeDict),
+ CFSTR("WebPluginTypeDescription"));
+ if (description && ::CFGetTypeID(description) == ::CFStringGetTypeID())
+ info.fMimeDescriptionArray[info.fVariantCount] =
+ CFStringRefToUTF8Buffer(static_cast<CFStringRef>(description));
+ }
+ info.fVariantCount++;
+ }
+}
+
+nsPluginFile::nsPluginFile(nsIFile* spec) : pLibrary(nullptr), mPlugin(spec) {}
+
+nsPluginFile::~nsPluginFile() {}
+
+nsresult nsPluginFile::LoadPlugin(PRLibrary** outLibrary) {
+ if (!mPlugin) return NS_ERROR_NULL_POINTER;
+
+ // 64-bit NSPR does not (yet) support bundles. So in 64-bit builds we need
+ // (for now) to load the bundle's executable. However this can cause
+ // problems: CFBundleCreate() doesn't run the bundle's executable's
+ // initialization code, while NSAddImage() and dlopen() do run it. So using
+ // NSPR's dyld loading mechanisms here (NSAddImage() or dlopen()) can cause
+ // a bundle's initialization code to run earlier than expected, and lead to
+ // crashes. See bug 577967.
+#ifdef __LP64__
+ char executablePath[PATH_MAX];
+ executablePath[0] = '\0';
+ nsAutoCString bundlePath;
+ mPlugin->GetNativePath(bundlePath);
+ CFStringRef pathRef = ::CFStringCreateWithCString(nullptr, bundlePath.get(),
+ kCFStringEncodingUTF8);
+ if (pathRef) {
+ CFURLRef bundleURL = ::CFURLCreateWithFileSystemPath(
+ nullptr, pathRef, kCFURLPOSIXPathStyle, true);
+ if (bundleURL) {
+ CFBundleRef bundle = ::CFBundleCreate(nullptr, bundleURL);
+ if (bundle) {
+ CFURLRef executableURL = ::CFBundleCopyExecutableURL(bundle);
+ if (executableURL) {
+ if (!::CFURLGetFileSystemRepresentation(
+ executableURL, true, (UInt8*)&executablePath, PATH_MAX))
+ executablePath[0] = '\0';
+ ::CFRelease(executableURL);
+ }
+ ::CFRelease(bundle);
+ }
+ ::CFRelease(bundleURL);
+ }
+ ::CFRelease(pathRef);
+ }
+#else
+ nsAutoCString bundlePath;
+ mPlugin->GetNativePath(bundlePath);
+ const char* executablePath = bundlePath.get();
+#endif
+
+ *outLibrary = PR_LoadLibrary(executablePath);
+ pLibrary = *outLibrary;
+ if (!pLibrary) {
+ return NS_ERROR_FAILURE;
+ }
+#ifdef DEBUG
+ printf("[loaded plugin %s]\n", bundlePath.get());
+#endif
+ return NS_OK;
+}
+
+static char* p2cstrdup(StringPtr pstr) {
+ int len = pstr[0];
+ char* cstr = static_cast<char*>(moz_xmalloc(len + 1));
+ memmove(cstr, pstr + 1, len);
+ cstr[len] = '\0';
+ return cstr;
+}
+
+static char* GetNextPluginStringFromHandle(Handle h, short* index) {
+ char* ret = p2cstrdup((unsigned char*)(*h + *index));
+ *index += (ret ? strlen(ret) : 0) + 1;
+ return ret;
+}
+
+/**
+ * Obtains all of the information currently available for this plugin.
+ */
+nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info,
+ PRLibrary** outLibrary) {
+ *outLibrary = nullptr;
+
+ nsresult rv = NS_OK;
+
+ // clear out the info, except for the first field.
+ memset(&info, 0, sizeof(info));
+
+ // Try to get a bundle reference.
+ nsAutoCString path;
+ if (NS_FAILED(rv = mPlugin->GetNativePath(path))) return rv;
+ CFBundleRef bundle = getPluginBundle(path.get());
+
+ // fill in full path
+ info.fFullPath = PL_strdup(path.get());
+
+ // fill in file name
+ nsAutoCString fileName;
+ if (NS_FAILED(rv = mPlugin->GetNativeLeafName(fileName))) return rv;
+ info.fFileName = PL_strdup(fileName.get());
+
+ // Get fName
+ if (bundle) {
+ CFTypeRef name =
+ ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginName"));
+ if (name && ::CFGetTypeID(name) == ::CFStringGetTypeID())
+ info.fName = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(name));
+ }
+
+ // Get fDescription
+ if (bundle) {
+ CFTypeRef description = ::CFBundleGetValueForInfoDictionaryKey(
+ bundle, CFSTR("WebPluginDescription"));
+ if (description && ::CFGetTypeID(description) == ::CFStringGetTypeID())
+ info.fDescription =
+ CFStringRefToUTF8Buffer(static_cast<CFStringRef>(description));
+ }
+
+ // Get fVersion
+ if (bundle) {
+ // Look for the release version first
+ CFTypeRef version = ::CFBundleGetValueForInfoDictionaryKey(
+ bundle, CFSTR("CFBundleShortVersionString"));
+ if (!version) // try the build version
+ version =
+ ::CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey);
+ if (version && ::CFGetTypeID(version) == ::CFStringGetTypeID())
+ info.fVersion =
+ CFStringRefToUTF8Buffer(static_cast<CFStringRef>(version));
+ }
+
+ // The last thing we need to do is get MIME data
+ // fVariantCount, fMimeTypeArray, fExtensionArray, fMimeDescriptionArray
+
+ // First look for data in a bundle plist
+ if (bundle) {
+ ParsePlistPluginInfo(info, bundle);
+ ::CFRelease(bundle);
+ if (info.fVariantCount > 0) return NS_OK;
+ }
+
+ // Don't load "fbplugin" or any plugins whose name starts with "fbplugin_"
+ // (Facebook plugins) if we're running on OS X 10.10 (Yosemite) or later.
+ // A "fbplugin" file crashes on load, in the call to LoadPlugin() below.
+ // See bug 1086977.
+ if (fileName.EqualsLiteral("fbplugin") ||
+ StringBeginsWith(fileName, "fbplugin_"_ns)) {
+ nsAutoCString msg;
+ msg.AppendPrintf("Preventing load of %s (see bug 1086977)", fileName.get());
+ NS_WARNING(msg.get());
+ return NS_ERROR_FAILURE;
+ }
+
+ // The block above assumes that "fbplugin" is the filename of the plugin
+ // to be blocked, or that the filename starts with "fbplugin_". But we
+ // don't yet know for sure if this is always true. So for the time being
+ // record extra information in our crash logs.
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::Bug_1086977,
+ fileName);
+
+ // It's possible that our plugin has 2 entry points that'll give us mime type
+ // info. Quicktime does this to get around the need of having admin rights to
+ // change mime info in the resource fork. We need to use this info instead of
+ // the resource. See bug 113464.
+
+ // Sadly we have to load the library for this to work.
+ rv = LoadPlugin(outLibrary);
+
+ // If we didn't crash in LoadPlugin(), remove the annotation so we don't
+ // sow confusion.
+ CrashReporter::RemoveCrashReportAnnotation(
+ CrashReporter::Annotation::Bug_1086977);
+
+ if (NS_FAILED(rv)) return rv;
+
+ // Try to get data from NP_GetMIMEDescription
+ if (pLibrary) {
+ NP_GETMIMEDESCRIPTION pfnGetMimeDesc =
+ (NP_GETMIMEDESCRIPTION)PR_FindFunctionSymbol(
+ pLibrary, NP_GETMIMEDESCRIPTION_NAME);
+ if (pfnGetMimeDesc) ParsePluginMimeDescription(pfnGetMimeDesc(), info);
+ if (info.fVariantCount) return NS_OK;
+ }
+
+ // We'll fill this in using BP_GetSupportedMIMETypes and/or resource fork data
+ BPSupportedMIMETypes mi = {kBPSupportedMIMETypesStructVers_1, nullptr,
+ nullptr};
+
+ // Try to get data from BP_GetSupportedMIMETypes
+ if (pLibrary) {
+ BP_GETSUPPORTEDMIMETYPES pfnMime =
+ (BP_GETSUPPORTEDMIMETYPES)PR_FindFunctionSymbol(
+ pLibrary, "BP_GetSupportedMIMETypes");
+ if (pfnMime && noErr == pfnMime(&mi, 0) && mi.typeStrings) {
+ info.fVariantCount = (**(short**)mi.typeStrings) / 2;
+ ::HLock(mi.typeStrings);
+ if (mi.infoStrings) // it's possible some plugins have infoStrings
+ // missing
+ ::HLock(mi.infoStrings);
+ }
+ }
+
+ // Fill in the info struct based on the data in the BPSupportedMIMETypes
+ // struct
+ int variantCount = info.fVariantCount;
+ info.fMimeTypeArray =
+ static_cast<char**>(moz_xmalloc(variantCount * sizeof(char*)));
+ info.fExtensionArray =
+ static_cast<char**>(moz_xmalloc(variantCount * sizeof(char*)));
+ if (mi.infoStrings) {
+ info.fMimeDescriptionArray =
+ static_cast<char**>(moz_xmalloc(variantCount * sizeof(char*)));
+ }
+ short mimeIndex = 2;
+ short descriptionIndex = 2;
+ for (int i = 0; i < variantCount; i++) {
+ info.fMimeTypeArray[i] =
+ GetNextPluginStringFromHandle(mi.typeStrings, &mimeIndex);
+ info.fExtensionArray[i] =
+ GetNextPluginStringFromHandle(mi.typeStrings, &mimeIndex);
+ if (mi.infoStrings)
+ info.fMimeDescriptionArray[i] =
+ GetNextPluginStringFromHandle(mi.infoStrings, &descriptionIndex);
+ }
+
+ ::HUnlock(mi.typeStrings);
+ ::DisposeHandle(mi.typeStrings);
+ if (mi.infoStrings) {
+ ::HUnlock(mi.infoStrings);
+ ::DisposeHandle(mi.infoStrings);
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info) {
+ free(info.fName);
+ free(info.fDescription);
+ int variantCount = info.fVariantCount;
+ for (int i = 0; i < variantCount; i++) {
+ free(info.fMimeTypeArray[i]);
+ free(info.fExtensionArray[i]);
+ free(info.fMimeDescriptionArray[i]);
+ }
+ free(info.fMimeTypeArray);
+ free(info.fMimeDescriptionArray);
+ free(info.fExtensionArray);
+ free(info.fFileName);
+ free(info.fFullPath);
+ free(info.fVersion);
+
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nsPluginsDirUnix.cpp b/dom/plugins/base/nsPluginsDirUnix.cpp
new file mode 100644
index 0000000000..62df31daea
--- /dev/null
+++ b/dom/plugins/base/nsPluginsDirUnix.cpp
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsNPAPIPlugin.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsPluginsDir.h"
+#include "nsPluginsDirUtils.h"
+#include "prenv.h"
+#include "prerror.h"
+#include "prio.h"
+#include <sys/stat.h>
+#include "nsString.h"
+#include "nsIFile.h"
+
+#define LOCAL_PLUGIN_DLL_SUFFIX ".so"
+#if defined(__hpux)
+# define DEFAULT_X11_PATH "/usr/lib/X11R6/"
+# undef LOCAL_PLUGIN_DLL_SUFFIX
+# define LOCAL_PLUGIN_DLL_SUFFIX ".sl"
+# define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".so"
+#elif defined(_AIX)
+# define DEFAULT_X11_PATH "/usr/lib"
+# define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".a"
+#elif defined(SOLARIS)
+# define DEFAULT_X11_PATH "/usr/openwin/lib/"
+#elif defined(LINUX)
+# define DEFAULT_X11_PATH "/usr/X11R6/lib/"
+#elif defined(__APPLE__)
+# define DEFAULT_X11_PATH "/usr/X11R6/lib"
+# undef LOCAL_PLUGIN_DLL_SUFFIX
+# define LOCAL_PLUGIN_DLL_SUFFIX ".dylib"
+# define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".so"
+#else
+# define DEFAULT_X11_PATH ""
+#endif
+
+/* nsPluginsDir implementation */
+
+bool nsPluginsDir::IsPluginFile(nsIFile* file) {
+ nsAutoCString filename;
+ if (NS_FAILED(file->GetNativeLeafName(filename))) return false;
+
+ constexpr auto dllSuffix = nsLiteralCString{LOCAL_PLUGIN_DLL_SUFFIX};
+ if (filename.Length() > dllSuffix.Length() &&
+ StringEndsWith(filename, dllSuffix))
+ return true;
+
+#ifdef LOCAL_PLUGIN_DLL_ALT_SUFFIX
+ constexpr auto dllAltSuffix = nsLiteralCString{LOCAL_PLUGIN_DLL_ALT_SUFFIX};
+ if (filename.Length() > dllAltSuffix.Length() &&
+ StringEndsWith(filename, dllAltSuffix))
+ return true;
+#endif
+ return false;
+}
+
+/* nsPluginFile implementation */
+
+nsPluginFile::nsPluginFile(nsIFile* file) : mPlugin(file) {}
+
+nsPluginFile::~nsPluginFile() = default;
+
+nsresult nsPluginFile::LoadPlugin(PRLibrary** outLibrary) {
+ PRLibSpec libSpec;
+ libSpec.type = PR_LibSpec_Pathname;
+ bool exists = false;
+ mPlugin->Exists(&exists);
+ if (!exists) return NS_ERROR_FILE_NOT_FOUND;
+
+ nsresult rv;
+ nsAutoCString path;
+ rv = mPlugin->GetNativePath(path);
+ if (NS_FAILED(rv)) return rv;
+
+ libSpec.value.pathname = path.get();
+
+ *outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
+ pLibrary = *outLibrary;
+
+#ifdef DEBUG
+ printf("LoadPlugin() %s returned %lx\n", libSpec.value.pathname,
+ (unsigned long)pLibrary);
+#endif
+
+ if (!pLibrary) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info,
+ PRLibrary** outLibrary) {
+ *outLibrary = nullptr;
+
+ info.fVersion = nullptr;
+
+ // Sadly we have to load the library for this to work.
+ nsresult rv = LoadPlugin(outLibrary);
+ if (NS_FAILED(rv)) return rv;
+
+ const char* (*npGetPluginVersion)() =
+ (const char* (*)())PR_FindFunctionSymbol(pLibrary, "NP_GetPluginVersion");
+ if (npGetPluginVersion) {
+ info.fVersion = PL_strdup(npGetPluginVersion());
+ }
+
+ const char* (*npGetMIMEDescription)() =
+ (const char* (*)())PR_FindFunctionSymbol(pLibrary,
+ "NP_GetMIMEDescription");
+ if (!npGetMIMEDescription) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const char* mimedescr = npGetMIMEDescription();
+ if (!mimedescr) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = ParsePluginMimeDescription(mimedescr, info);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsAutoCString path;
+ if (NS_FAILED(rv = mPlugin->GetNativePath(path))) return rv;
+ info.fFullPath = PL_strdup(path.get());
+
+ nsAutoCString fileName;
+ if (NS_FAILED(rv = mPlugin->GetNativeLeafName(fileName))) return rv;
+ info.fFileName = PL_strdup(fileName.get());
+
+ NP_GetValueFunc npGetValue =
+ (NP_GetValueFunc)PR_FindFunctionSymbol(pLibrary, "NP_GetValue");
+ if (!npGetValue) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const char* name = nullptr;
+ npGetValue(nullptr, NPPVpluginNameString, &name);
+ if (name) {
+ info.fName = PL_strdup(name);
+ } else {
+ info.fName = PL_strdup(fileName.get());
+ }
+
+ const char* description = nullptr;
+ npGetValue(nullptr, NPPVpluginDescriptionString, &description);
+ if (description) {
+ info.fDescription = PL_strdup(description);
+ } else {
+ info.fDescription = PL_strdup("");
+ }
+
+ return NS_OK;
+}
+
+nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info) {
+ if (info.fName != nullptr) PL_strfree(info.fName);
+
+ if (info.fDescription != nullptr) PL_strfree(info.fDescription);
+
+ for (uint32_t i = 0; i < info.fVariantCount; i++) {
+ if (info.fMimeTypeArray[i] != nullptr) PL_strfree(info.fMimeTypeArray[i]);
+
+ if (info.fMimeDescriptionArray[i] != nullptr)
+ PL_strfree(info.fMimeDescriptionArray[i]);
+
+ if (info.fExtensionArray[i] != nullptr) PL_strfree(info.fExtensionArray[i]);
+ }
+
+ free(info.fMimeTypeArray);
+ info.fMimeTypeArray = nullptr;
+ free(info.fMimeDescriptionArray);
+ info.fMimeDescriptionArray = nullptr;
+ free(info.fExtensionArray);
+ info.fExtensionArray = nullptr;
+
+ if (info.fFullPath != nullptr) PL_strfree(info.fFullPath);
+
+ if (info.fFileName != nullptr) PL_strfree(info.fFileName);
+
+ if (info.fVersion != nullptr) PL_strfree(info.fVersion);
+
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nsPluginsDirUtils.h b/dom/plugins/base/nsPluginsDirUtils.h
new file mode 100644
index 0000000000..e4d929d1fc
--- /dev/null
+++ b/dom/plugins/base/nsPluginsDirUtils.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsPluginsDirUtils_h___
+#define nsPluginsDirUtils_h___
+
+#include "nsPluginsDir.h"
+#include "nsTArray.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Output format from NPP_GetMIMEDescription: "...mime
+// type[;version]:[extension]:[desecription];..." The ambiguity of mime
+// description could cause the browser fail to parse the MIME types correctly.
+// E.g. "mime type::desecription;" // correct w/o ext
+// "mime type:desecription;" // wrong w/o ext
+//
+static nsresult ParsePluginMimeDescription(const char* mdesc,
+ nsPluginInfo& info) {
+ nsresult rv = NS_ERROR_FAILURE;
+ if (!mdesc || !*mdesc) return rv;
+
+ char* mdescDup =
+ PL_strdup(mdesc); // make a dup of intput string we'll change it content
+ char anEmptyString[] = "";
+ AutoTArray<char*, 8> tmpMimeTypeArr;
+ char delimiters[] = {':', ':', ';'};
+ int mimeTypeVariantCount = 0;
+ char* p = mdescDup; // make a dup of intput string we'll change it content
+ while (p) {
+ char* ptrMimeArray[] = {anEmptyString, anEmptyString, anEmptyString};
+
+ // It's easy to point out ptrMimeArray[0] to the string sounds like
+ // "Mime type is not specified, plugin will not function properly."
+ // and show this on "about:plugins" page, but we have to mark this
+ // particular mime type of given plugin as disable on "about:plugins" page,
+ // which feature is not implemented yet.
+ // So we'll ignore, without any warnings, an empty description strings,
+ // in other words, if after parsing ptrMimeArray[0] == anEmptyString is
+ // true. It is possible do not to registry a plugin at all if it returns an
+ // empty string on GetMIMEDescription() call, e.g. plugger returns "" if
+ // pluggerrc file is not found.
+
+ char* s = p;
+ int i;
+ for (i = 0;
+ i < (int)sizeof(delimiters) && (p = PL_strchr(s, delimiters[i]));
+ i++) {
+ ptrMimeArray[i] = s; // save start ptr
+ *p++ = 0; // overwrite delimiter
+ s = p; // move forward
+ }
+ if (i == 2) ptrMimeArray[i] = s;
+ // fill out the temp array
+ // the order is important, it should be the same in for loop below
+ if (ptrMimeArray[0] != anEmptyString) {
+ tmpMimeTypeArr.AppendElement(ptrMimeArray[0]);
+ tmpMimeTypeArr.AppendElement(ptrMimeArray[1]);
+ tmpMimeTypeArr.AppendElement(ptrMimeArray[2]);
+ mimeTypeVariantCount++;
+ }
+ }
+
+ // fill out info structure
+ if (mimeTypeVariantCount) {
+ info.fVariantCount = mimeTypeVariantCount;
+ // we can do these 3 mallocs at once, later on code cleanup
+ info.fMimeTypeArray = (char**)malloc(mimeTypeVariantCount * sizeof(char*));
+ info.fMimeDescriptionArray =
+ (char**)malloc(mimeTypeVariantCount * sizeof(char*));
+ info.fExtensionArray = (char**)malloc(mimeTypeVariantCount * sizeof(char*));
+
+ int j, i;
+ for (j = i = 0; i < mimeTypeVariantCount; i++) {
+ // the order is important, do not change it
+ // we can get rid of PL_strdup here, later on code cleanup
+ info.fMimeTypeArray[i] = PL_strdup(tmpMimeTypeArr.ElementAt(j++));
+ info.fExtensionArray[i] = PL_strdup(tmpMimeTypeArr.ElementAt(j++));
+ info.fMimeDescriptionArray[i] = PL_strdup(tmpMimeTypeArr.ElementAt(j++));
+ }
+ rv = NS_OK;
+ }
+ if (mdescDup) PL_strfree(mdescDup);
+ return rv;
+}
+
+#endif /* nsPluginsDirUtils_h___ */
diff --git a/dom/plugins/base/nsPluginsDirWin.cpp b/dom/plugins/base/nsPluginsDirWin.cpp
new file mode 100644
index 0000000000..04e20f46d0
--- /dev/null
+++ b/dom/plugins/base/nsPluginsDirWin.cpp
@@ -0,0 +1,349 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/*
+ nsPluginsDirWin.cpp
+
+ Windows implementation of the nsPluginsDir/nsPluginsFile classes.
+
+ by Alex Musil
+ */
+
+#include "mozilla/ArrayUtils.h" // ArrayLength
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Printf.h"
+
+#include "nsPluginsDir.h"
+#include "prlink.h"
+#include "plstr.h"
+
+#include "windows.h"
+#include "winbase.h"
+
+#include "nsString.h"
+#include "nsIFile.h"
+#include "nsUnicharUtils.h"
+
+using namespace mozilla;
+
+/* Local helper functions */
+
+static char* GetKeyValue(void* verbuf, const WCHAR* key, UINT language,
+ UINT codepage) {
+ WCHAR keybuf[64]; // plenty for the template below, with the longest key
+ // we use (currently "FileDescription")
+ const WCHAR keyFormat[] = L"\\StringFileInfo\\%04X%04X\\%ls";
+ WCHAR* buf = nullptr;
+ UINT blen;
+
+ if (_snwprintf_s(keybuf, ArrayLength(keybuf), _TRUNCATE, keyFormat, language,
+ codepage, key) < 0) {
+ MOZ_ASSERT_UNREACHABLE("plugin info key too long for buffer!");
+ return nullptr;
+ }
+
+ if (::VerQueryValueW(verbuf, keybuf, (void**)&buf, &blen) == 0 ||
+ buf == nullptr || blen == 0) {
+ return nullptr;
+ }
+
+ return PL_strdup(NS_ConvertUTF16toUTF8(buf, blen).get());
+}
+
+static char* GetVersion(void* verbuf) {
+ VS_FIXEDFILEINFO* fileInfo;
+ UINT fileInfoLen;
+
+ ::VerQueryValueW(verbuf, L"\\", (void**)&fileInfo, &fileInfoLen);
+
+ if (fileInfo) {
+ return mozilla::Smprintf("%ld.%ld.%ld.%ld",
+ HIWORD(fileInfo->dwFileVersionMS),
+ LOWORD(fileInfo->dwFileVersionMS),
+ HIWORD(fileInfo->dwFileVersionLS),
+ LOWORD(fileInfo->dwFileVersionLS))
+ .release();
+ }
+
+ return nullptr;
+}
+
+// Returns a boolean indicating if the key's value contains a string
+// entry equal to "1" or "0". No entry for the key returns false.
+static bool GetBooleanFlag(void* verbuf, const WCHAR* key, UINT language,
+ UINT codepage) {
+ char* flagStr = GetKeyValue(verbuf, key, language, codepage);
+ if (!flagStr) {
+ return false;
+ }
+ bool result = (PL_strncmp("1", flagStr, 1) == 0);
+ PL_strfree(flagStr);
+ return result;
+}
+
+static uint32_t CalculateVariantCount(char* mimeTypes) {
+ uint32_t variants = 1;
+
+ if (!mimeTypes) return 0;
+
+ char* index = mimeTypes;
+ while (*index) {
+ if (*index == '|') variants++;
+
+ ++index;
+ }
+ return variants;
+}
+
+static char** MakeStringArray(uint32_t variants, char* data) {
+ // The number of variants has been calculated based on the mime
+ // type array. Plugins are not explicitely required to match
+ // this number in two other arrays: file extention array and mime
+ // description array, and some of them actually don't.
+ // We should handle such situations gracefully
+
+ if ((variants <= 0) || !data) return nullptr;
+
+ char** array = (char**)calloc(variants, sizeof(char*));
+ if (!array) return nullptr;
+
+ char* start = data;
+
+ for (uint32_t i = 0; i < variants; i++) {
+ char* p = PL_strchr(start, '|');
+ if (p) *p = 0;
+
+ array[i] = PL_strdup(start);
+
+ if (!p) {
+ // nothing more to look for, fill everything left
+ // with empty strings and break
+ while (++i < variants) array[i] = PL_strdup("");
+
+ break;
+ }
+
+ start = ++p;
+ }
+ return array;
+}
+
+static void FreeStringArray(uint32_t variants, char** array) {
+ if ((variants == 0) || !array) return;
+
+ for (uint32_t i = 0; i < variants; i++) {
+ if (array[i]) {
+ PL_strfree(array[i]);
+ array[i] = nullptr;
+ }
+ }
+ free(array);
+}
+
+static bool CanLoadPlugin(char16ptr_t aBinaryPath) {
+#if defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64)
+ bool canLoad = false;
+
+ HANDLE file =
+ CreateFileW(aBinaryPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (file != INVALID_HANDLE_VALUE) {
+ HANDLE map = CreateFileMappingW(file, nullptr, PAGE_READONLY, 0,
+ GetFileSize(file, nullptr), nullptr);
+ if (map != nullptr) {
+ LPVOID mapView = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
+ if (mapView != nullptr) {
+ if (((IMAGE_DOS_HEADER*)mapView)->e_magic == IMAGE_DOS_SIGNATURE) {
+ long peImageHeaderStart = (((IMAGE_DOS_HEADER*)mapView)->e_lfanew);
+ if (peImageHeaderStart != 0L) {
+ DWORD arch =
+ (((IMAGE_NT_HEADERS*)((LPBYTE)mapView + peImageHeaderStart))
+ ->FileHeader.Machine);
+# ifdef _M_IX86
+ canLoad = (arch == IMAGE_FILE_MACHINE_I386);
+# elif defined(_M_X64)
+ canLoad = (arch == IMAGE_FILE_MACHINE_AMD64);
+# elif defined(_M_IA64)
+ canLoad = (arch == IMAGE_FILE_MACHINE_IA64);
+# endif
+ }
+ }
+ UnmapViewOfFile(mapView);
+ }
+ CloseHandle(map);
+ }
+ CloseHandle(file);
+ }
+
+ return canLoad;
+#else
+ // Assume correct binaries for unhandled cases.
+ return true;
+#endif
+}
+
+/* nsPluginsDir implementation */
+
+// The file name must be in the form "np*.dll"
+bool nsPluginsDir::IsPluginFile(nsIFile* file) {
+ nsAutoString path;
+ if (NS_FAILED(file->GetPath(path))) return false;
+
+ // this is most likely a path, so skip to the filename
+ auto filename = Substring(path, path.RFindChar('\\') + 1);
+ // The file name must have at least one character between "np" and ".dll".
+ if (filename.Length() < 7) {
+ return false;
+ }
+
+ ToLowerCase(filename);
+ if (StringBeginsWith(filename, u"np"_ns) &&
+ StringEndsWith(filename, u".dll"_ns)) {
+ // don't load OJI-based Java plugins
+ if (StringBeginsWith(filename, u"npoji"_ns) ||
+ StringBeginsWith(filename, u"npjava"_ns))
+ return false;
+ return true;
+ }
+
+ return false;
+}
+
+/* nsPluginFile implementation */
+
+nsPluginFile::nsPluginFile(nsIFile* file) : mPlugin(file) {
+ // nada
+}
+
+nsPluginFile::~nsPluginFile() {
+ // nada
+}
+
+/**
+ * Loads the plugin into memory using NSPR's shared-library loading
+ * mechanism. Handles platform differences in loading shared libraries.
+ */
+nsresult nsPluginFile::LoadPlugin(PRLibrary** outLibrary) {
+ if (!mPlugin) return NS_ERROR_NULL_POINTER;
+
+ nsAutoString pluginFilePath;
+ mPlugin->GetPath(pluginFilePath);
+
+ nsAutoString pluginFolderPath = pluginFilePath;
+ int32_t idx = pluginFilePath.RFindChar('\\');
+ pluginFolderPath.SetLength(idx);
+
+ BOOL restoreOrigDir = FALSE;
+ WCHAR aOrigDir[MAX_PATH + 1];
+ DWORD dwCheck = GetCurrentDirectoryW(MAX_PATH, aOrigDir);
+ NS_ASSERTION(dwCheck <= MAX_PATH + 1, "Error in Loading plugin");
+
+ if (dwCheck <= MAX_PATH + 1) {
+ restoreOrigDir = SetCurrentDirectoryW(pluginFolderPath.get());
+ NS_ASSERTION(restoreOrigDir, "Error in Loading plugin");
+ }
+
+ // Temporarily add the current directory back to the DLL load path.
+ SetDllDirectory(nullptr);
+
+ nsresult rv = mPlugin->Load(outLibrary);
+ if (NS_FAILED(rv)) *outLibrary = nullptr;
+
+ SetDllDirectory(L"");
+
+ if (restoreOrigDir) {
+ DebugOnly<BOOL> bCheck = SetCurrentDirectoryW(aOrigDir);
+ NS_ASSERTION(bCheck, "Error in Loading plugin");
+ }
+
+ return rv;
+}
+
+/**
+ * Obtains all of the information currently available for this plugin.
+ */
+nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info,
+ PRLibrary** outLibrary) {
+ *outLibrary = nullptr;
+
+ nsresult rv = NS_OK;
+ DWORD zerome, versionsize;
+ void* verbuf = nullptr;
+
+ if (!mPlugin) return NS_ERROR_NULL_POINTER;
+
+ nsAutoString fullPath;
+ if (NS_FAILED(rv = mPlugin->GetPath(fullPath))) return rv;
+
+ if (!CanLoadPlugin(fullPath.get())) return NS_ERROR_FAILURE;
+
+ nsAutoString fileName;
+ if (NS_FAILED(rv = mPlugin->GetLeafName(fileName))) return rv;
+
+ LPCWSTR lpFilepath = fullPath.get();
+
+ versionsize = ::GetFileVersionInfoSizeW(lpFilepath, &zerome);
+
+ if (versionsize > 0) verbuf = malloc(versionsize);
+ if (!verbuf) return NS_ERROR_OUT_OF_MEMORY;
+
+ if (::GetFileVersionInfoW(lpFilepath, 0, versionsize, verbuf)) {
+ // TODO: get appropriately-localized info from plugin file
+ UINT lang = 1033; // language = English, 0x409
+ UINT cp = 1252; // codepage = Western, 0x4E4
+ info.fName = GetKeyValue(verbuf, L"ProductName", lang, cp);
+ info.fDescription = GetKeyValue(verbuf, L"FileDescription", lang, cp);
+ info.fSupportsAsyncRender =
+ GetBooleanFlag(verbuf, L"AsyncDrawingSupport", lang, cp);
+
+ char* mimeType = GetKeyValue(verbuf, L"MIMEType", lang, cp);
+ char* mimeDescription = GetKeyValue(verbuf, L"FileOpenName", lang, cp);
+ char* extensions = GetKeyValue(verbuf, L"FileExtents", lang, cp);
+
+ info.fVariantCount = CalculateVariantCount(mimeType);
+ info.fMimeTypeArray = MakeStringArray(info.fVariantCount, mimeType);
+ info.fMimeDescriptionArray =
+ MakeStringArray(info.fVariantCount, mimeDescription);
+ info.fExtensionArray = MakeStringArray(info.fVariantCount, extensions);
+ info.fFullPath = PL_strdup(NS_ConvertUTF16toUTF8(fullPath).get());
+ info.fFileName = PL_strdup(NS_ConvertUTF16toUTF8(fileName).get());
+ info.fVersion = GetVersion(verbuf);
+
+ PL_strfree(mimeType);
+ PL_strfree(mimeDescription);
+ PL_strfree(extensions);
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+
+ free(verbuf);
+
+ return rv;
+}
+
+nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info) {
+ if (info.fName) PL_strfree(info.fName);
+
+ if (info.fDescription) PL_strfree(info.fDescription);
+
+ if (info.fMimeTypeArray)
+ FreeStringArray(info.fVariantCount, info.fMimeTypeArray);
+
+ if (info.fMimeDescriptionArray)
+ FreeStringArray(info.fVariantCount, info.fMimeDescriptionArray);
+
+ if (info.fExtensionArray)
+ FreeStringArray(info.fVariantCount, info.fExtensionArray);
+
+ if (info.fFullPath) PL_strfree(info.fFullPath);
+
+ if (info.fFileName) PL_strfree(info.fFileName);
+
+ if (info.fVersion) free(info.fVersion);
+
+ ZeroMemory((void*)&info, sizeof(info));
+
+ return NS_OK;
+}
diff --git a/dom/plugins/base/nspluginroot.idl b/dom/plugins/base/nspluginroot.idl
new file mode 100644
index 0000000000..6c931d3711
--- /dev/null
+++ b/dom/plugins/base/nspluginroot.idl
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+native REFNSIID(REFNSIID);
+native nativeVoid(void *);
+native nativeChar(const char * *);
+[ptr] native constVoidPtr(const void);
+[ref] native PRUint32Ref(uint32_t);
+[ref] native PRUint16Ref(uint16_t);
+[ref] native constCharStarConstStar(const char* const*);
+[ptr] native constCharPtr(const char);
+[ref] native constCharStarRef(const char *);
+
+native NPWindowType(NPWindowType);
+native NPWindow(NPWindow);
+[ptr] native NPWindowPtr(NPWindow);
+[ref] native NPWindowStarRef(NPWindow *);
+[ptr] native NPPrintPtr(NPPrint);
+native NPByteRange(NPByteRange);
+[ptr] native NPByteRangePtr(NPByteRange);
+native NPPVariable(NPPVariable);
+native NPNVariable(NPNVariable);
+[ptr] native NPRectPtr(NPRect);
+native NPRegion(NPRegion);
+native NPDrawingModel(NPDrawingModel);
+native NPEventModel(NPEventModel);
+
+[ptr] native JRIEnvPtr(JRIEnv);
+native jref(jref);