summaryrefslogtreecommitdiffstats
path: root/dom/plugins/test/testplugin
diff options
context:
space:
mode:
Diffstat (limited to 'dom/plugins/test/testplugin')
-rw-r--r--dom/plugins/test/testplugin/Info.plist38
-rw-r--r--dom/plugins/test/testplugin/README424
-rw-r--r--dom/plugins/test/testplugin/flashplugin/Info.plist38
-rw-r--r--dom/plugins/test/testplugin/flashplugin/moz.build11
-rw-r--r--dom/plugins/test/testplugin/flashplugin/nptest.def7
-rw-r--r--dom/plugins/test/testplugin/flashplugin/nptest.rc42
-rw-r--r--dom/plugins/test/testplugin/flashplugin/nptest_name.cpp8
-rw-r--r--dom/plugins/test/testplugin/moz.build13
-rw-r--r--dom/plugins/test/testplugin/nptest.cpp3282
-rw-r--r--dom/plugins/test/testplugin/nptest.def7
-rw-r--r--dom/plugins/test/testplugin/nptest.h150
-rw-r--r--dom/plugins/test/testplugin/nptest.rc42
-rw-r--r--dom/plugins/test/testplugin/nptest_droid.cpp80
-rw-r--r--dom/plugins/test/testplugin/nptest_gtk2.cpp707
-rw-r--r--dom/plugins/test/testplugin/nptest_macosx.mm275
-rw-r--r--dom/plugins/test/testplugin/nptest_name.cpp8
-rw-r--r--dom/plugins/test/testplugin/nptest_platform.h155
-rw-r--r--dom/plugins/test/testplugin/nptest_utils.cpp100
-rw-r--r--dom/plugins/test/testplugin/nptest_utils.h45
-rw-r--r--dom/plugins/test/testplugin/nptest_windows.cpp797
-rw-r--r--dom/plugins/test/testplugin/secondplugin/Info.plist38
-rw-r--r--dom/plugins/test/testplugin/secondplugin/moz.build11
-rw-r--r--dom/plugins/test/testplugin/secondplugin/nptest.def7
-rw-r--r--dom/plugins/test/testplugin/secondplugin/nptest.rc42
-rw-r--r--dom/plugins/test/testplugin/secondplugin/nptest_name.cpp7
-rw-r--r--dom/plugins/test/testplugin/testplugin.mozbuild64
26 files changed, 6398 insertions, 0 deletions
diff --git a/dom/plugins/test/testplugin/Info.plist b/dom/plugins/test/testplugin/Info.plist
new file mode 100644
index 0000000000..dc6aa5cec3
--- /dev/null
+++ b/dom/plugins/test/testplugin/Info.plist
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>libnptest.dylib</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.mozilla.TestPlugin</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BRPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0.0.0</string>
+ <key>CFBundleSignature</key>
+ <string>TEST</string>
+ <key>CFBundleVersion</key>
+ <string>1.0.0.0</string>
+ <key>WebPluginName</key>
+ <string>Test Plug-in</string>
+ <key>WebPluginDescription</key>
+ <string>Plug-in for testing purposes.™ (हिन्दी 中文 العربية)</string>
+ <key>WebPluginMIMETypes</key>
+ <dict>
+ <key>application/x-test</key>
+ <dict>
+ <key>WebPluginExtensions</key>
+ <array>
+ <string>tst</string>
+ </array>
+ <key>WebPluginTypeDescription</key>
+ <string>Test ™ mimetype</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/dom/plugins/test/testplugin/README b/dom/plugins/test/testplugin/README
new file mode 100644
index 0000000000..993de360ce
--- /dev/null
+++ b/dom/plugins/test/testplugin/README
@@ -0,0 +1,424 @@
+= Instructions for using the test plugin =
+
+== MIME type ==
+
+The test plugin registers itself for the MIME type "application/x-test".
+
+== Event Model ==
+
+* getEventModel()
+Returns the NPAPI event model in use. On platforms without event models,
+simply returns 0;
+
+== Rendering ==
+
+By default, the plugin fills its rectangle with gray, with a black border, and
+renders the user-agent string (obtained from NPN_UserAgent) centered in black.
+This rendering method is not supported for the async drawing models.
+
+The test plugin supports the following parameters:
+
+* drawmode="solid"
+The plugin will draw a solid color instead of the default rendering described
+above. The default solid color is completely transparent black (i.e., nothing).
+This should be specified when using one of the async models.
+
+* asyncmodel="bitmap"
+The plugin will use the NPAPI Async Bitmap drawing model extension. On
+unsupported platforms this will fallback to non-async rendering.
+
+* asyncmodel="dxgi"
+The plugin will use the NPAPI Async DXGI drawing model extension. Only
+supported on Windows Vista or higher. On unsupported platforms this will
+fallback to non-async rendering.
+
+* color
+This specifies the color to use for drawmode="solid". The value should be 8 hex
+digits, 2 per channel in AARRGGBB format.
+
+== Generic API Tests ==
+
+* setUndefinedValueTest
+Attempts to set the value of an undefined variable (0x0) via NPN_SetValue,
+returns true if it succeeds and false if it doesn't. It should never succeed.
+
+* .getReflector()
+Hands back an object which reflects properties as values, e.g.
+ .getReflector().foo = 'foo'
+ .getReflector()['foo'] = 'foo'
+ .getReflector()[1] = 1
+
+* .getNPNVdocumentOrigin()
+Returns the origin string retrieved from the browser by a NPNVdocumentOrigin
+variable request. Does not cache the value, gets it from the browser every time.
+
+== NPN_ConvertPoint testing ==
+
+* convertPointX(sourceSpace, sourceX, sourceY, destSpace)
+* convertPointY(sourceSpace, sourceX, sourceY, destSpace)
+The plugin uses NPN_ConvertPoint to convert sourceX and sourceY from the source
+to dest space and returns the X or Y result based on the call.
+
+== NPCocoaEventWindowFocusChanged ==
+
+* getTopLevelWindowActivationState()
+Returns the activation state for the top-level window as set by the last
+NPCocoaEventWindowFocusChanged event. Returns true for active, false for
+inactive, and throws an exception if the state is unknown (uninitialized).
+
+* getTopLevelWindowActivationEventCount()
+Returns the number of NPCocoaEventWindowFocusChanged events received by
+the instance.
+
+== Focus Tests ==
+
+* getFocusState()
+Returns the plugin's focus state. Returns true for focused, false for unfocused,
+and throws an exception if the state is unknown (uninitialized). This does not
+necessarily correspond to actual input focus - this corresponds to focus as
+defined by the NPAPI event model in use.
+
+* getFocusEventCount()
+Returns the number of focus events received by the instance.
+
+== NPRuntime testing ==
+
+The test plugin object supports the following scriptable methods:
+
+* identifierToStringTest(ident)
+Converts a string, int32 or double parameter 'ident' to an NPIdentifier and
+then to a string, which is returned.
+
+* npnEvaluateTest(script)
+Calls NPN_Evaluate on the 'script' argument, which is a string containing
+some script to be executed. Returns the result of the evaluation.
+
+* npnInvokeTest(method, expected, args...)
+Causes the plugin to call the specified script method using NPN_Invoke,
+passing it 1 or more arguments specified in args. The return value of this
+call is compared against 'expected', and if they match, npnInvokeTest will
+return true. Otherwise, it will return false.
+
+* npnInvokeDefaultTest(object, argument)
+Causes the plugin to call NPN_InvokeDefault on the specified object,
+with the specified argument. Returns the result of the invocation.
+
+* getError()
+If an error has occurred during the last stream or npruntime function,
+this will return a string error message, otherwise it returns "pass".
+
+* throwExceptionNextInvoke()
+Sets a flag which causes the next call to a scriptable method to throw
+one or more exceptions. If no parameters are passed to the next
+scriptable method call, it will cause a generic exception to be thrown.
+Otherwise there will be one exception thrown per argument, with the argument
+used as the exception message. Example:
+
+ plugin.throwExceptionNextInvoke();
+ plugin.npnInvokeTest("first exception message", "second exception message");
+
+* () - default method
+Returns a string consisting of the plugin name, concatenated with any
+arguments passed to the method.
+
+* .crash() - Crashes the plugin
+
+* getObjectValue() - Returns a custom plugin-implemented scriptable object.
+* checkObjectValue(obj) - Returns true if the object from getObjectValue() is
+ the same object passed into this function. It should return true when
+ the object is passed to the same plugin instance, and false when passed
+ to other plugin instances, see bug 532246 and
+ test_multipleinstanceobjects.html.
+
+* callOnDestroy(fn) - Calls `fn` when the plugin instance is being destroyed
+
+* getAuthInfo(protocol, host, port, scheme, realm) - a wrapper for
+NPN_GetAuthenticationInfo(). Returns a string "username|password" for
+the specified auth criteria, or throws an exception if no data is
+available.
+
+* timerTest(callback) - initiates tests of NPN_ScheduleTimer &
+NPN_UnscheduleTimer. When finished, calls the script callback
+with a boolean value, indicating whether the tests were successful.
+
+* asyncCallbackTest(callback) - initiates tests of
+NPN_PluginThreadAsyncCall. When finished, calls the script callback
+with a boolean value, indicating whether the tests were successful.
+
+* paintscript="..." content attribute
+If the "paintscript" attribute is set on the plugin element during plugin
+initialization, then every time the plugin paints it gets the contents of that
+attribute and evaluates it as a script in the context of the plugin's DOM
+window. This is useful for testing evil plugin code that might, for example,
+modify the DOM during painting.
+
+== Private browsing ==
+
+The test plugin object supports the following scriptable methods:
+
+* queryPrivateModeState
+Returns the value of NPN_GetValue(NPNVprivateModeBool).
+
+* lastReportedPrivateModeState
+Returns the last value set by NPP_SetValue(NPNVprivateModeBool).
+
+== Windowed/windowless mode ==
+
+The test plugin is windowless by default.
+
+The test plugin supports the following parameter:
+
+* wmode="window"
+The plugin will be given a native widget on platforms where we support this
+(Windows and X).
+
+The test plugin object supports the following scriptable method:
+
+* hasWidget()
+Returns true if the plugin has an associated widget. This will return true if
+wmode="window" was specified and the platform supports windowed plugins.
+
+== Plugin invalidation ==
+
+* setColor(colorString)
+Sets the color used for drawmode="solid" and invalidates the plugin area.
+This calls NPN_Invalidate, even for windowed plugins, since that should work
+for windowed plugins too (Silverlight depends on it).
+
+* getPaintCount()
+Returns the number of times this plugin instance has processed a paint request.
+This can be used to detect whether painting has happened in a plugin's
+window.
+
+* getWidthAtLastPaint()
+Returns the window width that was current when the plugin last painted.
+
+* setInvalidateDuringPaint(value)
+When value is true, every time the plugin paints, it will invalidate
+itself *during the paint* using NPN_Invalidate.
+
+* setSlowPaint(value)
+When value is true, the instance will sleep briefly during paint.
+
+== Plugin geometry ==
+
+The test plugin supports the following scriptable methods:
+
+* getEdge(edge)
+Returns the integer screen pixel coordinate of an edge of the plugin's
+area:
+-- edge=0: returns left edge coordinate
+-- edge=1: returns top edge coordinate
+-- edge=2: returns right edge coordinate
+-- edge=3: returns bottom edge coordinate
+The coordinates are relative to the top-left corner of the top-level window
+containing the plugin, including the window decorations. Therefore:
+-- On Mac, they're relative to the top-left corner of the toplevel Cocoa
+window.
+-- On Windows, they're relative to the top-left corner of the toplevel HWND's
+non-client area.
+-- On GTK2, they're relative to the top-left corner of the toplevel window's
+window manager frame.
+This means they can be added to Gecko's window.screenX/screenY (if DPI is set
+to 96) to get screen coordinates.
+On the platforms that support window-mode plugins (Windows/GTK2), this only
+works for window-mode plugins. It will throw an error for windowless plugins.
+
+* getClipRegionRectCount()
+Returns the number of rectangles in the plugin's clip region.
+For plugins with widgets, the clip region is computed as the intersection of the
+clip region for the widget (if the platform does not support clip regions
+on native widgets, this would just be the widget's rectangle) with the
+clip regions of all ancestor widgets which would clip this widget.
+On the platforms that support window-mode plugins (Windows/GTK2), this only
+works for window-mode plugins. It will throw an error for windowless plugins.
+On Mac, all plugins have a clip region containing just a single clip
+rectangle only. So if you request wmode="window" but the plugin reports
+!hasWidget, you can assume that complex clip regions are not supported.
+
+* getClipRegionRectEdge(i, edge)
+Returns the integer screen pixel coordinate of an edge of a rectangle from the
+plugin's clip region. If i is less than zero or greater than or equal to
+getClipRegionRectCount(), this will throw an error. The coordinates are
+the same as for getEdge. See getClipRegionRectCount() above for
+notes on platform plugin limitations.
+
+== Keyboard events ==
+
+* getLastKeyText()
+Returns the text which was inputted by last keyboard events. This is cleared at
+every keydown event.
+NOTE: Currently, this is implemented only on Windows.
+
+== Mouse events ==
+
+The test plugin supports the following scriptable methods:
+
+* getLastMouseX()
+Returns the X coordinate of the last mouse event (move, button up, or
+button down), relative to the left edge of the plugin, or -1 if no mouse
+event has been received.
+
+* getLastMouseX()
+Returns the Y coordinate of the last mouse event (move, button up, or
+button down), relative to the top edge of the plugin, or -1 if no mouse
+event has been received.
+
+== Instance lifecycle ==
+
+The test plugin supports the following scriptable methods:
+
+* startWatchingInstanceCount()
+Marks all currently running instances as "ignored". Throws an exception if
+there is already a watch (startWatchingInstanceCount has already been
+called on some instance without a corresponding stopWatchingInstanceCount).
+
+* getInstanceCount()
+Returns the count of currently running instances that are not ignored.
+Throws an exception if there is no watch.
+
+* stopWatchingInstanceCount()
+Stops watching. Throws an exception if there is no watch.
+
+== NPAPI Timers ==
+
+* unscheduleAllTimers()
+Instructs the plugin instance to cancel all timers created via
+NPN_ScheduleTimer.
+
+== Stream Functionality ==
+
+The test plugin enables a variety of NPAPI streaming tests, which are
+initiated by passing a variety of attributes to the <embed> element which
+causes the plugin to be initialized. The plugin stream test code is
+designed to receive a stream from the browser (by specifying a "src",
+"geturl", or "geturlnotify" attribute), and then (if a "frame" attribute
+is specified) send the data from that stream back to the browser in another
+stream, whereupon it will be displayed in the specified frame. If some
+error occurs during stream processing, an error message will appear in the
+frame instead of the stream data. If no "frame" attribute is present, a
+stream can still be received by the plugin, but the plugin will do nothing
+with it.
+
+The attributes which control stream tests are:
+
+"streamchunksize": the number of bytes the plugin reports it can accept
+ in calls to NPP_WriteReady. Defaults to 1,024.
+
+"src": a url. If specified, the browser will call NPP_NewStream for
+ this url as soon as the plugin is initialized.
+
+"geturl": a url. If specified, the plugin will request this url
+ from the browser when the plugin is initialized, via a call to
+ NPN_GetURL.
+
+"geturlnotify": a url. If specified, the plugin will request this url
+ from the browser when the plugin is initialized, via a call to
+ NPN_GetURLNotify. The plugin passes some "notifyData" to
+ NPN_GetURLNotify, which it verifies is present in the call to
+ NPP_URLNotify. If the "notifyData" does not match, an error
+ will be displayed in the test frame (if any), instead of the stream
+ data.
+
+"frame": the name of a frame in the same HTML document as the <embed>
+ element which instantiated the plugin. For any of the preceding three
+ attributes, a stream is received by the plugin via calls to NPP_NewStream,
+ NPP_WriteReady, NPP_Write, and NPP_DestroyStream. When NPP_DestroyStream
+ is called (or NPP_UrlNotify, in the case of "geturlnotify"), and a
+ "frame" attribute is present, the data from the stream is converted into a
+ data: url, and sent back to the browser in another stream via a call to
+ NPN_GetURL, whereupon it should be displayed in the specified frame.
+
+"posturl": a url. After the plugin receives a stream, and NPP_DestroyStream
+ is called, if "posturl" is specified, the plugin will post the contents
+ of the stream to the specified url via NPN_PostURL. See "postmode" for
+ additional details.
+
+"postmode": either "frame" or "stream". If "frame", and a "frame" attribute
+ is present, the plugin will pass the frame name to calls to NPN_PostURL,
+ so that the HTTP response from that operation will be displayed in the
+ specified frame. If "stream", the HTTP response is delivered to the plugin
+ via calls to NPP_NewStream etc, and if a "frame" attribute is present, the
+ contents of that stream will be passed back to the browser and displayed
+ in the specified frame via NPN_GetURL.
+
+"newstream": if "true", then any stream which is sent to a frame in the browser
+ is sent via calls to NPN_NewStream and NPN_Write. Doing so will cause
+ the browser to store the stream data in a file, and set the frame's
+ location to the corresponding file:// url.
+
+"functiontofail": one of "npp_newstream", "npp_write", "npp_destroystream".
+ When specified, the given function will return an error code (-1 for
+ NPP_Write, or else the value of the "failurecode" attribute) the first time
+ it is called by the browser.
+
+"failurecode": one of the NPError constants. Used to specify the error
+ that will be returned by the "functiontofail".
+
+* streamTest(url, doPost, postData, writeCallback, notifyCallback, redirectCallback, allowRedirects, postFile = false)
+This will test how NPN_GetURLNotify and NPN_PostURLNotify behave when they are
+called with arbitrary (malformed) URLs. The function will return `true` if
+NPN_[Get/Post]URLNotify succeeds, and `false` if it fails.
+@url url to request
+@param doPost whether to call NPN_PostURLNotify
+@param postData null, or a string to send a postdata
+@writeCallback will be called when data is received for the stream
+@notifyCallback will be called when the urlnotify is received with the notify result
+@redirectCallback will be called from urlredirectnotify if a redirect is attempted
+@allowRedirects boolean value indicating whether or not to allow redirects
+@postFile boolean optional, defaults to false, set to true if postData contains a filename
+
+* postFileToURLTest(url)
+Calls NPN_PostURL/NPN_PostURLNotify to make a POST request to the URL with
+request from postFile.
+The function will return `0` if NPN_PostURL/NPN_PostURLNotify succeeds, and
+the error code if it fails.
+@param url string, url to request
+
+* setPluginWantsAllStreams(wantsAllStreams)
+Set the value returned by the plugin for NPPVpluginWantsAllNetworkStreams.
+
+== Internal consistency ==
+
+* doInternalConsistencyCheck()
+Does internal consistency checking, returning an empty string if everything is
+OK, otherwise returning some kind of error string. On Windows, in windowed
+mode, this checks that the position of the plugin's internal child
+window has not been disturbed relative to the plugin window.
+
+== Windows native widget message behaviour ==
+
+* Mouse events are handled (saving the last mouse event coordinate) and not
+passed to the overridden windowproc.
+
+* WM_MOUSEWHEEL events are handled and not passed to the parent window or the
+overridden windowproc.
+
+* WM_MOUSEACTIVATE events are handled by calling SetFocus on the plugin's
+widget, if the plugin is windowed. If it's not windowed they're passed to
+the overriden windowproc (but hopefully never sent by the browser anyway).
+
+== FPU Control ==
+
+x86-only on some OSes:
+
+* The .enableFPExceptions() method will enable floating-point exceptions,
+ as evil plugins or extensions might do.
+
+== HiDPI Mode ==
+
+* queryContentsScaleFactor()
+Returns the contents scale factor. On platforms without support for this query
+always returns 1.0 (a double value). Likewise on hardware without HiDPI mode
+support.
+
+== Plugin audio channel support ==
+
+* startAudioPlayback()
+Simulates the plugin starting to play back audio.
+
+* stopAudioPlayback()
+Simulates the plugin stopping to play back audio.
+
+* audioMuted()
+Returns the last value set by NPP_SetValue(NPNVmuteAudioBool).
diff --git a/dom/plugins/test/testplugin/flashplugin/Info.plist b/dom/plugins/test/testplugin/flashplugin/Info.plist
new file mode 100644
index 0000000000..0e6168e686
--- /dev/null
+++ b/dom/plugins/test/testplugin/flashplugin/Info.plist
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>libnpswftest.dylib</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.mozilla.FlashTestPlugin</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BRPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0.0.0</string>
+ <key>CFBundleSignature</key>
+ <string>FLASHTEST</string>
+ <key>CFBundleVersion</key>
+ <string>1.0.0.0</string>
+ <key>WebPluginName</key>
+ <string>Shockwave Flash</string>
+ <key>WebPluginDescription</key>
+ <string>Flash plug-in for testing purposes.</string>
+ <key>WebPluginMIMETypes</key>
+ <dict>
+ <key>application/x-shockwave-flash-test</key>
+ <dict>
+ <key>WebPluginExtensions</key>
+ <array>
+ <string>swf</string>
+ </array>
+ <key>WebPluginTypeDescription</key>
+ <string>Flash test type</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/dom/plugins/test/testplugin/flashplugin/moz.build b/dom/plugins/test/testplugin/flashplugin/moz.build
new file mode 100644
index 0000000000..f66fb6eca4
--- /dev/null
+++ b/dom/plugins/test/testplugin/flashplugin/moz.build
@@ -0,0 +1,11 @@
+# -*- 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/.
+
+SharedLibrary("npswftest")
+
+relative_path = "flashplugin"
+cocoa_name = "npswftest"
+include("../testplugin.mozbuild")
diff --git a/dom/plugins/test/testplugin/flashplugin/nptest.def b/dom/plugins/test/testplugin/flashplugin/nptest.def
new file mode 100644
index 0000000000..3a62d05d95
--- /dev/null
+++ b/dom/plugins/test/testplugin/flashplugin/nptest.def
@@ -0,0 +1,7 @@
+LIBRARY NPSWFTEST
+
+EXPORTS
+ NP_GetEntryPoints @1
+ NP_Initialize @2
+ NP_Shutdown @3
+ NP_GetMIMEDescription @4
diff --git a/dom/plugins/test/testplugin/flashplugin/nptest.rc b/dom/plugins/test/testplugin/flashplugin/nptest.rc
new file mode 100644
index 0000000000..e970d26091
--- /dev/null
+++ b/dom/plugins/test/testplugin/flashplugin/nptest.rc
@@ -0,0 +1,42 @@
+#include<winver.h>
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Flash plug-in for testing purposes."
+ VALUE "FileExtents", "swf"
+ VALUE "FileOpenName", "Flash test type"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "npswftest"
+ VALUE "MIMEType", "application/x-shockwave-flash-test"
+ VALUE "OriginalFilename", "npswftest.dll"
+ VALUE "ProductName", "Shockwave Flash"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
diff --git a/dom/plugins/test/testplugin/flashplugin/nptest_name.cpp b/dom/plugins/test/testplugin/flashplugin/nptest_name.cpp
new file mode 100644
index 0000000000..31f4f6321f
--- /dev/null
+++ b/dom/plugins/test/testplugin/flashplugin/nptest_name.cpp
@@ -0,0 +1,8 @@
+/* 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/. */
+
+const char* sPluginName = "Shockwave Flash";
+const char* sPluginDescription = "Flash plug-in for testing purposes.";
+const char* sMimeDescription =
+ "application/x-shockwave-flash-test:swf:Flash test type";
diff --git a/dom/plugins/test/testplugin/moz.build b/dom/plugins/test/testplugin/moz.build
new file mode 100644
index 0000000000..cb380e2db8
--- /dev/null
+++ b/dom/plugins/test/testplugin/moz.build
@@ -0,0 +1,13 @@
+# -*- 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/.
+
+DIRS += ["secondplugin", "flashplugin"]
+
+SharedLibrary("nptest")
+
+relative_path = "."
+cocoa_name = "Test"
+include("testplugin.mozbuild")
diff --git a/dom/plugins/test/testplugin/nptest.cpp b/dom/plugins/test/testplugin/nptest.cpp
new file mode 100644
index 0000000000..2a9d20cdca
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -0,0 +1,3282 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT OWNER OR 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.
+ *
+ * Contributor(s):
+ * Dave Townsend <dtownsend@oxymoronical.com>
+ * Josh Aas <josh@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nptest.h"
+#include "nptest_utils.h"
+#include "nptest_platform.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/IntentionalCrash.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <list>
+#include <ctime>
+
+#ifdef XP_WIN
+# include <process.h>
+# include <float.h>
+# include <windows.h>
+# define getpid _getpid
+# define strcasecmp _stricmp
+#else
+# include <unistd.h>
+# include <pthread.h>
+#endif
+
+using std::list;
+using std::ostringstream;
+using std::string;
+
+#define PLUGIN_VERSION "1.0.0.0"
+
+extern const char* sPluginName;
+extern const char* sPluginDescription;
+static char sPluginVersion[] = PLUGIN_VERSION;
+
+//
+// Intentional crash
+//
+
+int gCrashCount = 0;
+
+static void Crash() {
+ int* pi = nullptr;
+ *pi = 55; // Crash dereferencing null pointer
+ ++gCrashCount;
+}
+
+static void IntentionalCrash() {
+ mozilla::NoteIntentionalCrash("plugin");
+ Crash();
+}
+
+//
+// static data
+//
+
+static NPNetscapeFuncs* sBrowserFuncs = nullptr;
+static NPClass sNPClass;
+
+//
+// identifiers
+//
+
+typedef bool (*ScriptableFunction)(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+
+static bool npnEvaluateTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool npnInvokeTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool npnInvokeDefaultTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool setUndefinedValueTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool identifierToStringTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool timerTest(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result);
+static bool queryPrivateModeState(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool lastReportedPrivateModeState(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool hasWidget(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result);
+static bool getEdge(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result);
+static bool getClipRegionRectCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getClipRegionRectEdge(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool startWatchingInstanceCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getInstanceCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool stopWatchingInstanceCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getLastMouseX(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getLastMouseY(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getPaintCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool resetPaintCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getWidthAtLastPaint(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool setInvalidateDuringPaint(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool setSlowPaint(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getError(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result);
+static bool doInternalConsistencyCheck(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool setColor(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result);
+static bool throwExceptionNextInvoke(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool convertPointX(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool convertPointY(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool streamTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool postFileToURLTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool setPluginWantsAllStreams(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool crashPlugin(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool crashOnDestroy(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getObjectValue(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool checkObjectValue(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool enableFPExceptions(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool hangPlugin(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool stallPlugin(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getClipboardText(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool callOnDestroy(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool reinitWidget(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool crashPluginInNestedLoop(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool triggerXError(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool destroySharedGfxStuff(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool propertyAndMethod(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getTopLevelWindowActivationState(NPObject* npobj,
+ const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result);
+static bool getTopLevelWindowActivationEventCount(NPObject* npobj,
+ const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result);
+static bool getFocusState(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getFocusEventCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getEventModel(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getReflector(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool isVisible(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result);
+static bool getWindowPosition(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool constructObject(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool setSitesWithData(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool setSitesWithDataCapabilities(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getLastKeyText(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getNPNVdocumentOrigin(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getMouseUpEventCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool queryContentsScaleFactor(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool queryCSSZoomFactorGetValue(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool queryCSSZoomFactorSetValue(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool echoString(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool startAudioPlayback(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool stopAudioPlayback(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getAudioMuted(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool nativeWidgetIsVisible(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getLastCompositionText(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+static bool getInvokeDefaultObject(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+
+static const NPUTF8* sPluginMethodIdentifierNames[] = {
+ "npnEvaluateTest",
+ "npnInvokeTest",
+ "npnInvokeDefaultTest",
+ "setUndefinedValueTest",
+ "identifierToStringTest",
+ "timerTest",
+ "queryPrivateModeState",
+ "lastReportedPrivateModeState",
+ "hasWidget",
+ "getEdge",
+ "getClipRegionRectCount",
+ "getClipRegionRectEdge",
+ "startWatchingInstanceCount",
+ "getInstanceCount",
+ "stopWatchingInstanceCount",
+ "getLastMouseX",
+ "getLastMouseY",
+ "getPaintCount",
+ "resetPaintCount",
+ "getWidthAtLastPaint",
+ "setInvalidateDuringPaint",
+ "setSlowPaint",
+ "getError",
+ "doInternalConsistencyCheck",
+ "setColor",
+ "throwExceptionNextInvoke",
+ "convertPointX",
+ "convertPointY",
+ "streamTest",
+ "postFileToURLTest",
+ "setPluginWantsAllStreams",
+ "crash",
+ "crashOnDestroy",
+ "getObjectValue",
+ "checkObjectValue",
+ "enableFPExceptions",
+ "hang",
+ "stall",
+ "getClipboardText",
+ "callOnDestroy",
+ "reinitWidget",
+ "crashInNestedLoop",
+ "triggerXError",
+ "destroySharedGfxStuff",
+ "propertyAndMethod",
+ "getTopLevelWindowActivationState",
+ "getTopLevelWindowActivationEventCount",
+ "getFocusState",
+ "getFocusEventCount",
+ "getEventModel",
+ "getReflector",
+ "isVisible",
+ "getWindowPosition",
+ "constructObject",
+ "setSitesWithData",
+ "setSitesWithDataCapabilities",
+ "getLastKeyText",
+ "getNPNVdocumentOrigin",
+ "getMouseUpEventCount",
+ "queryContentsScaleFactor",
+ "queryCSSZoomFactorSetValue",
+ "queryCSSZoomFactorGetValue",
+ "echoString",
+ "startAudioPlayback",
+ "stopAudioPlayback",
+ "audioMuted",
+ "nativeWidgetIsVisible",
+ "getLastCompositionText",
+ "getInvokeDefaultObject",
+};
+static NPIdentifier
+ sPluginMethodIdentifiers[MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames)];
+static const ScriptableFunction sPluginMethodFunctions[] = {
+ npnEvaluateTest,
+ npnInvokeTest,
+ npnInvokeDefaultTest,
+ setUndefinedValueTest,
+ identifierToStringTest,
+ timerTest,
+ queryPrivateModeState,
+ lastReportedPrivateModeState,
+ hasWidget,
+ getEdge,
+ getClipRegionRectCount,
+ getClipRegionRectEdge,
+ startWatchingInstanceCount,
+ getInstanceCount,
+ stopWatchingInstanceCount,
+ getLastMouseX,
+ getLastMouseY,
+ getPaintCount,
+ resetPaintCount,
+ getWidthAtLastPaint,
+ setInvalidateDuringPaint,
+ setSlowPaint,
+ getError,
+ doInternalConsistencyCheck,
+ setColor,
+ throwExceptionNextInvoke,
+ convertPointX,
+ convertPointY,
+ streamTest,
+ postFileToURLTest,
+ setPluginWantsAllStreams,
+ crashPlugin,
+ crashOnDestroy,
+ getObjectValue,
+ checkObjectValue,
+ enableFPExceptions,
+ hangPlugin,
+ stallPlugin,
+ getClipboardText,
+ callOnDestroy,
+ reinitWidget,
+ crashPluginInNestedLoop,
+ triggerXError,
+ destroySharedGfxStuff,
+ propertyAndMethod,
+ getTopLevelWindowActivationState,
+ getTopLevelWindowActivationEventCount,
+ getFocusState,
+ getFocusEventCount,
+ getEventModel,
+ getReflector,
+ isVisible,
+ getWindowPosition,
+ constructObject,
+ setSitesWithData,
+ setSitesWithDataCapabilities,
+ getLastKeyText,
+ getNPNVdocumentOrigin,
+ getMouseUpEventCount,
+ queryContentsScaleFactor,
+ queryCSSZoomFactorGetValue,
+ queryCSSZoomFactorSetValue,
+ echoString,
+ startAudioPlayback,
+ stopAudioPlayback,
+ getAudioMuted,
+ nativeWidgetIsVisible,
+ getLastCompositionText,
+ getInvokeDefaultObject,
+};
+
+static_assert(MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames) ==
+ MOZ_ARRAY_LENGTH(sPluginMethodFunctions),
+ "Arrays should have the same size");
+
+static const NPUTF8* sPluginPropertyIdentifierNames[] = {"propertyAndMethod"};
+static NPIdentifier sPluginPropertyIdentifiers[MOZ_ARRAY_LENGTH(
+ sPluginPropertyIdentifierNames)];
+static NPVariant
+ sPluginPropertyValues[MOZ_ARRAY_LENGTH(sPluginPropertyIdentifierNames)];
+
+struct URLNotifyData {
+ const char* cookie;
+ NPObject* writeCallback;
+ NPObject* notifyCallback;
+ NPObject* redirectCallback;
+ bool allowRedirects;
+ uint32_t size;
+ char* data;
+};
+
+static URLNotifyData kNotifyData = {"static-cookie", nullptr, nullptr, nullptr,
+ false, 0, nullptr};
+
+static const char* SUCCESS_STRING = "pass";
+
+static bool sIdentifiersInitialized = false;
+
+struct timerEvent {
+ int32_t timerIdReceive;
+ int32_t timerIdSchedule;
+ uint32_t timerInterval;
+ bool timerRepeat;
+ int32_t timerIdUnschedule;
+};
+static timerEvent timerEvents[] = {
+ // clang-format off
+ {-1, 0, 200, false, -1},
+ {0, 0, 400, false, -1},
+ {0, 0, 200, true, -1},
+ {0, 1, 400, true, -1},
+ {0, -1, 0, false, 0},
+ {1, -1, 0, false, -1},
+ {1, -1, 0, false, 1},
+ // clang-format on
+};
+static uint32_t currentTimerEventCount = 0;
+static uint32_t totalTimerEvents = sizeof(timerEvents) / sizeof(timerEvent);
+
+/**
+ * Incremented for every startWatchingInstanceCount.
+ */
+static int32_t sCurrentInstanceCountWatchGeneration = 0;
+/**
+ * Tracks the number of instances created or destroyed since the last
+ * startWatchingInstanceCount.
+ */
+static int32_t sInstanceCount = 0;
+/**
+ * True when we've had a startWatchingInstanceCount with no corresponding
+ * stopWatchingInstanceCount.
+ */
+static bool sWatchingInstanceCount = false;
+
+/**
+ * A list representing sites for which the plugin has stored data. See
+ * NPP_ClearSiteData and NPP_GetSitesWithData.
+ */
+struct siteData {
+ string site;
+ uint64_t flags;
+ uint64_t age;
+};
+static list<siteData>* sSitesWithData;
+static bool sClearByAgeSupported;
+
+static void initializeIdentifiers() {
+ if (!sIdentifiersInitialized) {
+ NPN_GetStringIdentifiers(sPluginMethodIdentifierNames,
+ MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames),
+ sPluginMethodIdentifiers);
+ NPN_GetStringIdentifiers(sPluginPropertyIdentifierNames,
+ MOZ_ARRAY_LENGTH(sPluginPropertyIdentifierNames),
+ sPluginPropertyIdentifiers);
+
+ sIdentifiersInitialized = true;
+
+ // Check whether nullptr is handled in NPN_GetStringIdentifiers
+ NPIdentifier IDList[2];
+ static char const* const kIDNames[2] = {nullptr, "setCookie"};
+ NPN_GetStringIdentifiers(const_cast<const NPUTF8**>(kIDNames), 2, IDList);
+ }
+}
+
+static void clearIdentifiers() {
+ memset(sPluginMethodIdentifiers, 0,
+ MOZ_ARRAY_LENGTH(sPluginMethodIdentifiers) * sizeof(NPIdentifier));
+ memset(sPluginPropertyIdentifiers, 0,
+ MOZ_ARRAY_LENGTH(sPluginPropertyIdentifiers) * sizeof(NPIdentifier));
+
+ sIdentifiersInitialized = false;
+}
+
+static void sendBufferToFrame(NPP instance) {
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+ string outbuf;
+ if (!instanceData->npnNewStream) outbuf = "data:text/html,";
+ const char* buf = reinterpret_cast<char*>(instanceData->streamBuf);
+ int32_t bufsize = instanceData->streamBufSize;
+ if (instanceData->err.str().length() > 0) {
+ outbuf.append(instanceData->err.str());
+ } else if (bufsize > 0) {
+ outbuf.append(buf);
+ } else {
+ outbuf.append("Error: no data in buffer");
+ }
+
+ // Convert CRLF to LF, and escape most other non-alphanumeric chars.
+ for (size_t i = 0; i < outbuf.length(); i++) {
+ if (outbuf[i] == '\n') {
+ outbuf.replace(i, 1, "%0a");
+ i += 2;
+ } else if (outbuf[i] == '\r') {
+ outbuf.replace(i, 1, "");
+ i -= 1;
+ } else {
+ int ascii = outbuf[i];
+ if (!((ascii >= ',' && ascii <= ';') || (ascii >= 'A' && ascii <= 'Z') ||
+ (ascii >= 'a' && ascii <= 'z'))) {
+ char hex[10];
+ sprintf(hex, "%%%x", ascii);
+ outbuf.replace(i, 1, hex);
+ i += 2;
+ }
+ }
+ }
+
+ NPError err =
+ NPN_GetURL(instance, outbuf.c_str(), instanceData->frame.c_str());
+ if (err != NPERR_NO_ERROR) {
+ instanceData->err << "NPN_GetURL returned " << err;
+ }
+}
+
+static void XPSleep(unsigned int seconds) {
+#ifdef XP_WIN
+ Sleep(1000 * seconds);
+#else
+ sleep(seconds);
+#endif
+}
+
+TestFunction getFuncFromString(const char* funcname) {
+ FunctionTable funcTable[] = {
+ {FUNCTION_NPP_NEWSTREAM, "npp_newstream"},
+ {FUNCTION_NPP_WRITEREADY, "npp_writeready"},
+ {FUNCTION_NPP_WRITE, "npp_write"},
+ {FUNCTION_NPP_DESTROYSTREAM, "npp_destroystream"},
+ {FUNCTION_NPP_WRITE_RPC, "npp_write_rpc"},
+ {FUNCTION_NONE, nullptr}};
+ int32_t i = 0;
+ while (funcTable[i].funcName) {
+ if (!strcmp(funcname, funcTable[i].funcName)) return funcTable[i].funcId;
+ i++;
+ }
+ return FUNCTION_NONE;
+}
+
+static void DuplicateNPVariant(NPVariant& aDest, const NPVariant& aSrc) {
+ if (NPVARIANT_IS_STRING(aSrc)) {
+ NPString src = NPVARIANT_TO_STRING(aSrc);
+ char* buf = new char[src.UTF8Length];
+ strncpy(buf, src.UTF8Characters, src.UTF8Length);
+ STRINGN_TO_NPVARIANT(buf, src.UTF8Length, aDest);
+ } else if (NPVARIANT_IS_OBJECT(aSrc)) {
+ NPObject* obj = NPN_RetainObject(NPVARIANT_TO_OBJECT(aSrc));
+ OBJECT_TO_NPVARIANT(obj, aDest);
+ } else {
+ aDest = aSrc;
+ }
+}
+
+static bool bug813906(NPP npp, const char* const function,
+ const char* const url, const char* const frame) {
+ NPObject* windowObj = nullptr;
+ NPError err = NPN_GetValue(npp, NPNVWindowNPObject, &windowObj);
+ if (err != NPERR_NO_ERROR) {
+ return false;
+ }
+
+ NPVariant result;
+ bool res = NPN_Invoke(npp, windowObj, NPN_GetStringIdentifier(function),
+ nullptr, 0, &result);
+ NPN_ReleaseObject(windowObj);
+ if (!res) {
+ return false;
+ }
+
+ NPN_ReleaseVariantValue(&result);
+
+ err = NPN_GetURL(npp, url, frame);
+ if (err != NPERR_NO_ERROR) {
+ err = NPN_GetURL(npp, "about:blank", frame);
+ return false;
+ }
+
+ return true;
+}
+
+void drawAsyncBitmapColor(InstanceData* instanceData) {
+ NPP npp = instanceData->npp;
+
+ uint32_t* pixelData = (uint32_t*)instanceData->backBuffer->bitmap.data;
+
+ uint32_t rgba = instanceData->scriptableObject->drawColor;
+
+ unsigned char subpixels[4];
+ memcpy(subpixels, &rgba, sizeof(subpixels));
+
+ subpixels[0] = uint8_t(float(subpixels[3] * subpixels[0]) / 0xFF);
+ subpixels[1] = uint8_t(float(subpixels[3] * subpixels[1]) / 0xFF);
+ subpixels[2] = uint8_t(float(subpixels[3] * subpixels[2]) / 0xFF);
+ uint32_t premultiplied;
+ memcpy(&premultiplied, subpixels, sizeof(premultiplied));
+
+ for (uint32_t* lastPixel =
+ pixelData + instanceData->backBuffer->size.width *
+ instanceData->backBuffer->size.height;
+ pixelData < lastPixel; ++pixelData) {
+ *pixelData = premultiplied;
+ }
+
+ NPN_SetCurrentAsyncSurface(npp, instanceData->backBuffer, NULL);
+ NPAsyncSurface* oldFront = instanceData->frontBuffer;
+ instanceData->frontBuffer = instanceData->backBuffer;
+ instanceData->backBuffer = oldFront;
+}
+
+//
+// function signatures
+//
+
+NPObject* scriptableAllocate(NPP npp, NPClass* aClass);
+void scriptableDeallocate(NPObject* npobj);
+void scriptableInvalidate(NPObject* npobj);
+bool scriptableHasMethod(NPObject* npobj, NPIdentifier name);
+bool scriptableInvoke(NPObject* npobj, NPIdentifier name, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+bool scriptableHasProperty(NPObject* npobj, NPIdentifier name);
+bool scriptableGetProperty(NPObject* npobj, NPIdentifier name,
+ NPVariant* result);
+bool scriptableSetProperty(NPObject* npobj, NPIdentifier name,
+ const NPVariant* value);
+bool scriptableRemoveProperty(NPObject* npobj, NPIdentifier name);
+bool scriptableEnumerate(NPObject* npobj, NPIdentifier** identifier,
+ uint32_t* count);
+bool scriptableConstruct(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result);
+
+//
+// npapi plugin functions
+//
+
+#ifdef XP_UNIX
+NP_EXPORT(char*)
+NP_GetPluginVersion() { return sPluginVersion; }
+#endif
+
+extern const char* sMimeDescription;
+
+#if defined(XP_UNIX)
+NP_EXPORT(const char*)
+NP_GetMIMEDescription()
+#elif defined(XP_WIN)
+const char* NP_GetMIMEDescription()
+#endif
+{
+ return sMimeDescription;
+}
+
+#ifdef XP_UNIX
+NP_EXPORT(NPError)
+NP_GetValue(void* future, NPPVariable aVariable, void* aValue) {
+ switch (aVariable) {
+ case NPPVpluginNameString:
+ *((const char**)aValue) = sPluginName;
+ break;
+ case NPPVpluginDescriptionString:
+ *((const char**)aValue) = sPluginDescription;
+ break;
+ default:
+ return NPERR_INVALID_PARAM;
+ }
+ return NPERR_NO_ERROR;
+}
+#endif
+
+static bool fillPluginFunctionTable(NPPluginFuncs* pFuncs) {
+ // Check the size of the provided structure based on the offset of the
+ // last member we need.
+ if (pFuncs->size <
+ (offsetof(NPPluginFuncs, getsiteswithdata) + sizeof(void*)))
+ return false;
+
+ pFuncs->newp = NPP_New;
+ pFuncs->destroy = NPP_Destroy;
+ pFuncs->setwindow = NPP_SetWindow;
+ pFuncs->newstream = NPP_NewStream;
+ pFuncs->destroystream = NPP_DestroyStream;
+ pFuncs->writeready = NPP_WriteReady;
+ pFuncs->write = NPP_Write;
+ pFuncs->print = NPP_Print;
+ pFuncs->event = NPP_HandleEvent;
+ pFuncs->urlnotify = NPP_URLNotify;
+ pFuncs->getvalue = NPP_GetValue;
+ pFuncs->setvalue = NPP_SetValue;
+ pFuncs->urlredirectnotify = NPP_URLRedirectNotify;
+ pFuncs->clearsitedata = NPP_ClearSiteData;
+ pFuncs->getsiteswithdata = NPP_GetSitesWithData;
+
+ return true;
+}
+
+#if defined(XP_MACOSX)
+NP_EXPORT(NPError)
+NP_Initialize(NPNetscapeFuncs* bFuncs)
+#elif defined(XP_WIN)
+NPError OSCALL NP_Initialize(NPNetscapeFuncs* bFuncs)
+#elif defined(XP_UNIX)
+NP_EXPORT(NPError)
+NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs)
+#endif
+{
+ sBrowserFuncs = bFuncs;
+
+ initializeIdentifiers();
+
+ for (unsigned int i = 0; i < MOZ_ARRAY_LENGTH(sPluginPropertyValues); i++) {
+ VOID_TO_NPVARIANT(sPluginPropertyValues[i]);
+ }
+
+ memset(&sNPClass, 0, sizeof(NPClass));
+ sNPClass.structVersion = NP_CLASS_STRUCT_VERSION;
+ sNPClass.allocate = (NPAllocateFunctionPtr)scriptableAllocate;
+ sNPClass.deallocate = (NPDeallocateFunctionPtr)scriptableDeallocate;
+ sNPClass.invalidate = (NPInvalidateFunctionPtr)scriptableInvalidate;
+ sNPClass.hasMethod = (NPHasMethodFunctionPtr)scriptableHasMethod;
+ sNPClass.invoke = (NPInvokeFunctionPtr)scriptableInvoke;
+ sNPClass.invokeDefault = nullptr;
+ sNPClass.hasProperty = (NPHasPropertyFunctionPtr)scriptableHasProperty;
+ sNPClass.getProperty = (NPGetPropertyFunctionPtr)scriptableGetProperty;
+ sNPClass.setProperty = (NPSetPropertyFunctionPtr)scriptableSetProperty;
+ sNPClass.removeProperty =
+ (NPRemovePropertyFunctionPtr)scriptableRemoveProperty;
+ sNPClass.enumerate = (NPEnumerationFunctionPtr)scriptableEnumerate;
+ sNPClass.construct = (NPConstructFunctionPtr)scriptableConstruct;
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ if (!fillPluginFunctionTable(pFuncs)) {
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+ }
+#endif
+
+ return NPERR_NO_ERROR;
+}
+
+#if defined(XP_MACOSX)
+NP_EXPORT(NPError)
+NP_GetEntryPoints(NPPluginFuncs* pFuncs)
+#elif defined(XP_WIN)
+NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs)
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
+{
+ if (!fillPluginFunctionTable(pFuncs)) {
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+ }
+
+ return NPERR_NO_ERROR;
+}
+#endif
+
+#if defined(XP_UNIX)
+NP_EXPORT(NPError)
+NP_Shutdown()
+#elif defined(XP_WIN)
+ NPError OSCALL NP_Shutdown()
+#endif
+{
+ clearIdentifiers();
+
+ for (unsigned int i = 0; i < MOZ_ARRAY_LENGTH(sPluginPropertyValues); i++) {
+ NPN_ReleaseVariantValue(&sPluginPropertyValues[i]);
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode,
+ int16_t argc, char* argn[], char* argv[], NPSavedData* saved) {
+ // Make sure our pdata field is nullptr at this point. If it isn't, that
+ // probably means the browser gave us uninitialized memory.
+ if (instance->pdata) {
+ printf("NPP_New called with non-NULL NPP->pdata pointer!\n");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // Make sure we can render this plugin
+ NPBool browserSupportsWindowless = false;
+ NPN_GetValue(instance, NPNVSupportsWindowless, &browserSupportsWindowless);
+ if (!browserSupportsWindowless && !pluginSupportsWindowMode()) {
+ printf(
+ "Windowless mode not supported by the browser, windowed mode not "
+ "supported by the plugin!\n");
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // set up our our instance data
+ InstanceData* instanceData = new InstanceData;
+ instanceData->npp = instance;
+ instanceData->testFunction = FUNCTION_NONE;
+ instanceData->functionToFail = FUNCTION_NONE;
+ instanceData->failureCode = 0;
+ instanceData->callOnDestroy = nullptr;
+ instanceData->streamChunkSize = 1024;
+ instanceData->streamBuf = nullptr;
+ instanceData->streamBufSize = 0;
+ instanceData->throwOnNextInvoke = false;
+ instanceData->runScriptOnPaint = false;
+ instanceData->dontTouchElement = false;
+ instanceData->hasWidget = false;
+ instanceData->npnNewStream = false;
+ instanceData->invalidateDuringPaint = false;
+ instanceData->slowPaint = false;
+ instanceData->playingAudio = false;
+ instanceData->audioMuted = false;
+ instanceData->writeCount = 0;
+ instanceData->writeReadyCount = 0;
+ memset(&instanceData->window, 0, sizeof(instanceData->window));
+ instanceData->crashOnDestroy = false;
+ instanceData->cleanupWidget = true; // only used by nptest_gtk
+ instanceData->topLevelWindowActivationState = ACTIVATION_STATE_UNKNOWN;
+ instanceData->topLevelWindowActivationEventCount = 0;
+ instanceData->focusState = ACTIVATION_STATE_UNKNOWN;
+ instanceData->focusEventCount = 0;
+ instanceData->eventModel = 0;
+ instanceData->wantsAllStreams = false;
+ instanceData->mouseUpEventCount = 0;
+ instanceData->bugMode = -1;
+ instanceData->asyncDrawing = AD_NONE;
+ instanceData->frontBuffer = nullptr;
+ instanceData->backBuffer = nullptr;
+ instanceData->placeholderWnd = nullptr;
+ instanceData->cssZoomFactor = 1.0;
+ instance->pdata = instanceData;
+
+ TestNPObject* scriptableObject =
+ (TestNPObject*)NPN_CreateObject(instance, &sNPClass);
+ if (!scriptableObject) {
+ printf(
+ "NPN_CreateObject failed to create an object, can't create a plugin "
+ "instance\n");
+ delete instanceData;
+ return NPERR_GENERIC_ERROR;
+ }
+ scriptableObject->npp = instance;
+ scriptableObject->drawMode = DM_DEFAULT;
+ scriptableObject->drawColor = 0;
+ instanceData->scriptableObject = scriptableObject;
+
+ instanceData->instanceCountWatchGeneration =
+ sCurrentInstanceCountWatchGeneration;
+
+ AsyncDrawing requestAsyncDrawing = AD_NONE;
+
+ bool requestWindow = false;
+ bool alreadyHasSalign = false;
+ // handle extra params
+ for (int i = 0; i < argc; i++) {
+ if (strcmp(argn[i], "drawmode") == 0) {
+ if (strcmp(argv[i], "solid") == 0)
+ scriptableObject->drawMode = DM_SOLID_COLOR;
+ } else if (strcmp(argn[i], "color") == 0) {
+ scriptableObject->drawColor = parseHexColor(argv[i], strlen(argv[i]));
+ } else if (strcmp(argn[i], "wmode") == 0) {
+ if (strcmp(argv[i], "window") == 0) {
+ requestWindow = true;
+ }
+ } else if (strcmp(argn[i], "asyncmodel") == 0) {
+ if (strcmp(argv[i], "bitmap") == 0) {
+ requestAsyncDrawing = AD_BITMAP;
+ } else if (strcmp(argv[i], "dxgi") == 0) {
+ requestAsyncDrawing = AD_DXGI;
+ }
+ }
+ if (strcmp(argn[i], "streamchunksize") == 0) {
+ instanceData->streamChunkSize = atoi(argv[i]);
+ }
+ if (strcmp(argn[i], "failurecode") == 0) {
+ instanceData->failureCode = atoi(argv[i]);
+ }
+ if (strcmp(argn[i], "functiontofail") == 0) {
+ instanceData->functionToFail = getFuncFromString(argv[i]);
+ }
+ if (strcmp(argn[i], "geturl") == 0) {
+ instanceData->testUrl = argv[i];
+ instanceData->testFunction = FUNCTION_NPP_GETURL;
+ }
+ if (strcmp(argn[i], "posturl") == 0) {
+ instanceData->testUrl = argv[i];
+ instanceData->testFunction = FUNCTION_NPP_POSTURL;
+ }
+ if (strcmp(argn[i], "geturlnotify") == 0) {
+ instanceData->testUrl = argv[i];
+ instanceData->testFunction = FUNCTION_NPP_GETURLNOTIFY;
+ }
+ if (strcmp(argn[i], "postmode") == 0) {
+ if (strcmp(argv[i], "frame") == 0) {
+ instanceData->postMode = POSTMODE_FRAME;
+ } else if (strcmp(argv[i], "stream") == 0) {
+ instanceData->postMode = POSTMODE_STREAM;
+ }
+ }
+ if (strcmp(argn[i], "frame") == 0) {
+ instanceData->frame = argv[i];
+ }
+ if (strcmp(argn[i], "newstream") == 0 && strcmp(argv[i], "true") == 0) {
+ instanceData->npnNewStream = true;
+ }
+ if (strcmp(argn[i], "newcrash") == 0) {
+ IntentionalCrash();
+ }
+ if (strcmp(argn[i], "paintscript") == 0) {
+ instanceData->runScriptOnPaint = true;
+ }
+
+ if (strcmp(argn[i], "donttouchelement") == 0) {
+ instanceData->dontTouchElement = true;
+ }
+ // "cleanupwidget" is only used with nptest_gtk, defaulting to true. It
+ // indicates whether the plugin should destroy its window in response to
+ // NPP_Destroy (or let the platform destroy the widget when the parent
+ // window gets destroyed).
+ if (strcmp(argn[i], "cleanupwidget") == 0 &&
+ strcmp(argv[i], "false") == 0) {
+ instanceData->cleanupWidget = false;
+ }
+ if (strcmp(argn[i], "bugmode") == 0) {
+ instanceData->bugMode = atoi(argv[i]);
+ }
+
+ // Bug 1307694 - There are two flash parameters that are order dependent for
+ // scaling/sizing the plugin. If they ever change from what is expected, it
+ // breaks flash on the web. In a test, if the scale tag ever happens
+ // with an salign before it, fail the plugin creation.
+ if (strcmp(argn[i], "scale") == 0) {
+ if (alreadyHasSalign) {
+ // If salign came before this parameter, error out now.
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+ if (strcmp(argn[i], "salign") == 0) {
+ alreadyHasSalign = true;
+ }
+ }
+
+ if (!browserSupportsWindowless || !pluginSupportsWindowlessMode()) {
+ requestWindow = true;
+ } else if (!pluginSupportsWindowMode()) {
+ requestWindow = false;
+ }
+ if (requestWindow) {
+ instanceData->hasWidget = true;
+ } else {
+ // NPPVpluginWindowBool should default to true, so we may as well
+ // test that by not setting it in the window case
+ NPN_SetValue(instance, NPPVpluginWindowBool, (void*)false);
+ }
+
+ if (scriptableObject->drawMode == DM_SOLID_COLOR &&
+ (scriptableObject->drawColor & 0xFF000000) != 0xFF000000) {
+ NPN_SetValue(instance, NPPVpluginTransparentBool, (void*)true);
+ }
+
+ if (requestAsyncDrawing == AD_BITMAP) {
+ NPBool supportsAsyncBitmap = false;
+ if ((NPN_GetValue(instance, NPNVsupportsAsyncBitmapSurfaceBool,
+ &supportsAsyncBitmap) == NPERR_NO_ERROR) &&
+ supportsAsyncBitmap) {
+ if (NPN_SetValue(instance, NPPVpluginDrawingModel,
+ (void*)NPDrawingModelAsyncBitmapSurface) ==
+ NPERR_NO_ERROR) {
+ instanceData->asyncDrawing = AD_BITMAP;
+ }
+ }
+ }
+#ifdef XP_WIN
+ else if (requestAsyncDrawing == AD_DXGI) {
+ NPBool supportsAsyncDXGI = false;
+ if ((NPN_GetValue(instance, NPNVsupportsAsyncWindowsDXGISurfaceBool,
+ &supportsAsyncDXGI) == NPERR_NO_ERROR) &&
+ supportsAsyncDXGI) {
+ if (NPN_SetValue(instance, NPPVpluginDrawingModel,
+ (void*)NPDrawingModelAsyncWindowsDXGISurface) ==
+ NPERR_NO_ERROR) {
+ instanceData->asyncDrawing = AD_DXGI;
+ }
+ }
+ }
+#endif
+
+ // If we can't get the right drawing mode, we fail, otherwise our tests might
+ // appear to be passing when they shouldn't. Real plugins should not do this.
+ if (instanceData->asyncDrawing != requestAsyncDrawing) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ instanceData->lastReportedPrivateModeState = false;
+ instanceData->lastMouseX = instanceData->lastMouseY = -1;
+ instanceData->widthAtLastPaint = -1;
+ instanceData->paintCount = 0;
+
+ // do platform-specific initialization
+ NPError err = pluginInstanceInit(instanceData);
+ if (err != NPERR_NO_ERROR) {
+ NPN_ReleaseObject(scriptableObject);
+ delete instanceData;
+ return err;
+ }
+
+ NPVariant variantTrue;
+ BOOLEAN_TO_NPVARIANT(true, variantTrue);
+ NPObject* o = nullptr;
+
+ // Set a property on NPNVPluginElementNPObject, unless the consumer explicitly
+ // opted out of this behavior.
+ if (!instanceData->dontTouchElement) {
+ err = NPN_GetValue(instance, NPNVPluginElementNPObject, &o);
+ if (err == NPERR_NO_ERROR) {
+ NPN_SetProperty(instance, o,
+ NPN_GetStringIdentifier("pluginFoundElement"),
+ &variantTrue);
+ NPN_ReleaseObject(o);
+ o = nullptr;
+ }
+ }
+
+ // Set a property on NPNVWindowNPObject
+ err = NPN_GetValue(instance, NPNVWindowNPObject, &o);
+ if (err == NPERR_NO_ERROR) {
+ NPN_SetProperty(instance, o, NPN_GetStringIdentifier("pluginFoundWindow"),
+ &variantTrue);
+ NPN_ReleaseObject(o);
+ o = nullptr;
+ }
+
+ ++sInstanceCount;
+
+ if (instanceData->testFunction == FUNCTION_NPP_GETURL) {
+ NPError err = NPN_GetURL(instance, instanceData->testUrl.c_str(), nullptr);
+ if (err != NPERR_NO_ERROR) {
+ instanceData->err << "NPN_GetURL returned " << err;
+ }
+ } else if (instanceData->testFunction == FUNCTION_NPP_GETURLNOTIFY) {
+ NPError err = NPN_GetURLNotify(instance, instanceData->testUrl.c_str(),
+ nullptr, static_cast<void*>(&kNotifyData));
+ if (err != NPERR_NO_ERROR) {
+ instanceData->err << "NPN_GetURLNotify returned " << err;
+ }
+ }
+
+ if ((instanceData->bugMode == 813906) && instanceData->frame.length()) {
+ bug813906(instance, "f", "browser.xhtml", instanceData->frame.c_str());
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_Destroy(NPP instance, NPSavedData** save) {
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+
+ if (instanceData->crashOnDestroy) IntentionalCrash();
+
+ if (instanceData->callOnDestroy) {
+ NPVariant result;
+ NPN_InvokeDefault(instance, instanceData->callOnDestroy, nullptr, 0,
+ &result);
+ NPN_ReleaseVariantValue(&result);
+ NPN_ReleaseObject(instanceData->callOnDestroy);
+ }
+
+ if (instanceData->streamBuf) {
+ free(instanceData->streamBuf);
+ }
+
+ if (instanceData->frontBuffer) {
+ NPN_SetCurrentAsyncSurface(instance, nullptr, nullptr);
+ NPN_FinalizeAsyncSurface(instance, instanceData->frontBuffer);
+ NPN_MemFree(instanceData->frontBuffer);
+ }
+ if (instanceData->backBuffer) {
+ NPN_FinalizeAsyncSurface(instance, instanceData->backBuffer);
+ NPN_MemFree(instanceData->backBuffer);
+ }
+
+ pluginInstanceShutdown(instanceData);
+ NPN_ReleaseObject(instanceData->scriptableObject);
+
+ if (sCurrentInstanceCountWatchGeneration ==
+ instanceData->instanceCountWatchGeneration) {
+ --sInstanceCount;
+ }
+ delete instanceData;
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_SetWindow(NPP instance, NPWindow* window) {
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+
+ if (instanceData->scriptableObject->drawMode == DM_DEFAULT &&
+ (instanceData->window.width != window->width ||
+ instanceData->window.height != window->height)) {
+ NPRect r;
+ r.left = r.top = 0;
+ r.right = window->width;
+ r.bottom = window->height;
+ NPN_InvalidateRect(instance, &r);
+ }
+
+ void* oldWindow = instanceData->window.window;
+ pluginDoSetWindow(instanceData, window);
+ if (instanceData->hasWidget && oldWindow != instanceData->window.window) {
+ pluginWidgetInit(instanceData, oldWindow);
+ }
+
+ if (instanceData->asyncDrawing != AD_NONE) {
+ if (instanceData->frontBuffer &&
+ instanceData->frontBuffer->size.width >= 0 &&
+ (uint32_t)instanceData->frontBuffer->size.width == window->width &&
+ instanceData->frontBuffer->size.height >= 0 &&
+ (uint32_t)instanceData->frontBuffer->size.height == window->height) {
+ return NPERR_NO_ERROR;
+ }
+ if (instanceData->frontBuffer) {
+ NPN_FinalizeAsyncSurface(instance, instanceData->frontBuffer);
+ NPN_MemFree(instanceData->frontBuffer);
+ }
+ if (instanceData->backBuffer) {
+ NPN_FinalizeAsyncSurface(instance, instanceData->backBuffer);
+ NPN_MemFree(instanceData->backBuffer);
+ }
+ instanceData->frontBuffer =
+ (NPAsyncSurface*)NPN_MemAlloc(sizeof(NPAsyncSurface));
+ instanceData->backBuffer =
+ (NPAsyncSurface*)NPN_MemAlloc(sizeof(NPAsyncSurface));
+
+ NPSize size;
+ size.width = window->width;
+ size.height = window->height;
+
+ memcpy(instanceData->backBuffer, instanceData->frontBuffer,
+ sizeof(NPAsyncSurface));
+
+ NPN_InitAsyncSurface(instance, &size, NPImageFormatBGRA32, nullptr,
+ instanceData->frontBuffer);
+ NPN_InitAsyncSurface(instance, &size, NPImageFormatBGRA32, nullptr,
+ instanceData->backBuffer);
+
+#if defined(XP_WIN)
+ if (instanceData->asyncDrawing == AD_DXGI) {
+ if (!setupDxgiSurfaces(instance, instanceData)) {
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+#endif
+ }
+
+ if (instanceData->asyncDrawing == AD_BITMAP) {
+ drawAsyncBitmapColor(instanceData);
+ }
+#if defined(XP_WIN)
+ else if (instanceData->asyncDrawing == AD_DXGI) {
+ drawDxgiBitmapColor(instanceData);
+ }
+#endif
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16_t* stype) {
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+
+ if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM &&
+ instanceData->failureCode) {
+ instanceData->err << SUCCESS_STRING;
+ if (instanceData->frame.length() > 0) {
+ sendBufferToFrame(instance);
+ }
+ return instanceData->failureCode;
+ }
+
+ if (stream->notifyData &&
+ static_cast<URLNotifyData*>(stream->notifyData) != &kNotifyData) {
+ // stream from streamTest
+ *stype = NP_NORMAL;
+ } else {
+ *stype = NP_NORMAL;
+
+ if (instanceData->streamBufSize) {
+ free(instanceData->streamBuf);
+ instanceData->streamBufSize = 0;
+ if (instanceData->testFunction == FUNCTION_NPP_POSTURL &&
+ instanceData->postMode == POSTMODE_STREAM) {
+ instanceData->testFunction = FUNCTION_NPP_GETURL;
+ } else {
+ // We already got a stream and didn't ask for another one.
+ instanceData->err << "Received unexpected multiple NPP_NewStream";
+ }
+ }
+ }
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason) {
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+
+ if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM) {
+ instanceData->err << "NPP_DestroyStream called";
+ }
+
+ if (instanceData->functionToFail == FUNCTION_NPP_WRITE) {
+ if (instanceData->writeCount == 1)
+ instanceData->err << SUCCESS_STRING;
+ else
+ instanceData->err << "NPP_Write called after returning -1";
+ }
+
+ if (instanceData->functionToFail == FUNCTION_NPP_DESTROYSTREAM &&
+ instanceData->failureCode) {
+ instanceData->err << SUCCESS_STRING;
+ if (instanceData->frame.length() > 0) {
+ sendBufferToFrame(instance);
+ }
+ return instanceData->failureCode;
+ }
+
+ URLNotifyData* nd = static_cast<URLNotifyData*>(stream->notifyData);
+ if (nd && nd != &kNotifyData) {
+ return NPERR_NO_ERROR;
+ }
+
+ if (instanceData->frame.length() > 0 &&
+ instanceData->testFunction != FUNCTION_NPP_GETURLNOTIFY &&
+ instanceData->testFunction != FUNCTION_NPP_POSTURL) {
+ sendBufferToFrame(instance);
+ }
+ if (instanceData->testFunction == FUNCTION_NPP_POSTURL) {
+ NPError err = NPN_PostURL(
+ instance, instanceData->testUrl.c_str(),
+ instanceData->postMode == POSTMODE_FRAME ? instanceData->frame.c_str()
+ : nullptr,
+ instanceData->streamBufSize,
+ reinterpret_cast<char*>(instanceData->streamBuf), false);
+ if (err != NPERR_NO_ERROR)
+ instanceData->err << "Error: NPN_PostURL returned error value " << err;
+ }
+ return NPERR_NO_ERROR;
+}
+
+int32_t NPP_WriteReady(NPP instance, NPStream* stream) {
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+ instanceData->writeReadyCount++;
+ if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM) {
+ instanceData->err << "NPP_WriteReady called";
+ }
+
+ // temporarily disabled per bug 519870
+ // if (instanceData->writeReadyCount == 1) {
+ // return 0;
+ //}
+
+ return instanceData->streamChunkSize;
+}
+
+int32_t NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len,
+ void* buffer) {
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+ instanceData->writeCount++;
+
+ // temporarily disabled per bug 519870
+ // if (instanceData->writeReadyCount == 1) {
+ // instanceData->err << "NPP_Write called even though NPP_WriteReady " <<
+ // "returned 0";
+ //}
+
+ if (instanceData->functionToFail == FUNCTION_NPP_WRITE_RPC) {
+ // Make an RPC call and pretend to consume the data
+ NPObject* windowObject = nullptr;
+ NPN_GetValue(instance, NPNVWindowNPObject, &windowObject);
+ if (windowObject) NPN_ReleaseObject(windowObject);
+
+ return len;
+ }
+
+ if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM) {
+ instanceData->err << "NPP_Write called";
+ }
+
+ if (instanceData->functionToFail == FUNCTION_NPP_WRITE) {
+ return -1;
+ }
+
+ URLNotifyData* nd = static_cast<URLNotifyData*>(stream->notifyData);
+
+ if (nd && nd->writeCallback) {
+ NPVariant args[1];
+ STRINGN_TO_NPVARIANT(stream->url, strlen(stream->url), args[0]);
+
+ NPVariant result;
+ NPN_InvokeDefault(instance, nd->writeCallback, args, 1, &result);
+ NPN_ReleaseVariantValue(&result);
+ }
+
+ if (nd && nd != &kNotifyData) {
+ uint32_t newsize = nd->size + len;
+ nd->data = (char*)realloc(nd->data, newsize);
+ memcpy(nd->data + nd->size, buffer, len);
+ nd->size = newsize;
+ return len;
+ }
+
+ char* streamBuf = reinterpret_cast<char*>(instanceData->streamBuf);
+ if (offset + len <= instanceData->streamBufSize) {
+ if (memcmp(buffer, streamBuf + offset, len)) {
+ instanceData->err << "Error: data written doesn't match";
+ } else {
+ printf("data matches!\n");
+ }
+ } else {
+ if (instanceData->streamBufSize == 0) {
+ instanceData->streamBuf = malloc(len + 1);
+ streamBuf = reinterpret_cast<char*>(instanceData->streamBuf);
+ } else {
+ instanceData->streamBuf =
+ realloc(reinterpret_cast<char*>(instanceData->streamBuf),
+ instanceData->streamBufSize + len + 1);
+ streamBuf = reinterpret_cast<char*>(instanceData->streamBuf);
+ }
+ memcpy(streamBuf + instanceData->streamBufSize, buffer, len);
+ instanceData->streamBufSize = instanceData->streamBufSize + len;
+ streamBuf[instanceData->streamBufSize] = '\0';
+ }
+ return len;
+}
+
+void NPP_Print(NPP instance, NPPrint* platformPrint) {}
+
+int16_t NPP_HandleEvent(NPP instance, void* event) {
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+ return pluginHandleEvent(instanceData, event);
+}
+
+void NPP_URLNotify(NPP instance, const char* url, NPReason reason,
+ void* notifyData) {
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+ URLNotifyData* ndata = static_cast<URLNotifyData*>(notifyData);
+
+ if (&kNotifyData == ndata) {
+ if (instanceData->frame.length() > 0) {
+ sendBufferToFrame(instance);
+ }
+ } else if (!strcmp(ndata->cookie, "dynamic-cookie")) {
+ if (ndata->notifyCallback) {
+ NPVariant args[2];
+ INT32_TO_NPVARIANT(reason, args[0]);
+ if (ndata->data) {
+ STRINGN_TO_NPVARIANT(ndata->data, ndata->size, args[1]);
+ } else {
+ STRINGN_TO_NPVARIANT("", 0, args[1]);
+ }
+
+ NPVariant result;
+ NPN_InvokeDefault(instance, ndata->notifyCallback, args, 2, &result);
+ NPN_ReleaseVariantValue(&result);
+ }
+
+ // clean up the URLNotifyData
+ if (ndata->writeCallback) {
+ NPN_ReleaseObject(ndata->writeCallback);
+ }
+ if (ndata->notifyCallback) {
+ NPN_ReleaseObject(ndata->notifyCallback);
+ }
+ if (ndata->redirectCallback) {
+ NPN_ReleaseObject(ndata->redirectCallback);
+ }
+ free(ndata->data);
+ delete ndata;
+ } else {
+ printf("ERROR! NPP_URLNotify called with wrong cookie\n");
+ instanceData->err << "Error: NPP_URLNotify called with wrong cookie";
+ }
+}
+
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value) {
+ InstanceData* instanceData = (InstanceData*)instance->pdata;
+ if (variable == NPPVpluginScriptableNPObject) {
+ NPObject* object = instanceData->scriptableObject;
+ NPN_RetainObject(object);
+ *((NPObject**)value) = object;
+ return NPERR_NO_ERROR;
+ }
+ if (variable == NPPVpluginNeedsXEmbed) {
+ // Only relevant for X plugins
+ // use 4-byte writes like some plugins may do
+ *(uint32_t*)value = instanceData->hasWidget;
+ return NPERR_NO_ERROR;
+ }
+ if (variable == NPPVpluginWantsAllNetworkStreams) {
+ // use 4-byte writes like some plugins may do
+ *(uint32_t*)value = instanceData->wantsAllStreams;
+ return NPERR_NO_ERROR;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value) {
+ if (variable == NPNVprivateModeBool) {
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+ instanceData->lastReportedPrivateModeState =
+ bool(*static_cast<NPBool*>(value));
+ return NPERR_NO_ERROR;
+ }
+ if (variable == NPNVmuteAudioBool) {
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+ instanceData->audioMuted = bool(*static_cast<NPBool*>(value));
+ return NPERR_NO_ERROR;
+ }
+ if (variable == NPNVCSSZoomFactor) {
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+ instanceData->cssZoomFactor = *static_cast<double*>(value);
+ return NPERR_NO_ERROR;
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+void NPP_URLRedirectNotify(NPP instance, const char* url, int32_t status,
+ void* notifyData) {
+ if (notifyData) {
+ URLNotifyData* nd = static_cast<URLNotifyData*>(notifyData);
+ if (nd->redirectCallback) {
+ NPVariant args[2];
+ STRINGN_TO_NPVARIANT(url, strlen(url), args[0]);
+ INT32_TO_NPVARIANT(status, args[1]);
+
+ NPVariant result;
+ NPN_InvokeDefault(instance, nd->redirectCallback, args, 2, &result);
+ NPN_ReleaseVariantValue(&result);
+ }
+ NPN_URLRedirectResponse(instance, notifyData, nd->allowRedirects);
+ return;
+ }
+ NPN_URLRedirectResponse(instance, notifyData, true);
+}
+
+NPError NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge) {
+ if (!sSitesWithData) return NPERR_NO_ERROR;
+
+ // Error condition: no support for clear-by-age
+ if (!sClearByAgeSupported && maxAge != uint64_t(int64_t(-1)))
+ return NPERR_TIME_RANGE_NOT_SUPPORTED;
+
+ // Iterate over list and remove matches
+ list<siteData>::iterator iter = sSitesWithData->begin();
+ list<siteData>::iterator end = sSitesWithData->end();
+ while (iter != end) {
+ const siteData& data = *iter;
+ list<siteData>::iterator next = iter;
+ ++next;
+ if ((!site || data.site.compare(site) == 0) &&
+ (flags == NP_CLEAR_ALL || data.flags & flags) && data.age <= maxAge) {
+ sSitesWithData->erase(iter);
+ }
+ iter = next;
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+char** NPP_GetSitesWithData() {
+ int length = 0;
+ char** result;
+
+ if (sSitesWithData) length = sSitesWithData->size();
+
+ // Allocate the maximum possible size the list could be.
+ result = static_cast<char**>(NPN_MemAlloc((length + 1) * sizeof(char*)));
+ result[length] = nullptr;
+
+ if (length == 0) {
+ // Represent the no site data case as an array of length 1 with a nullptr
+ // entry.
+ return result;
+ }
+
+ // Iterate the list of stored data, and build a list of strings.
+ list<string> sites;
+ {
+ list<siteData>::iterator iter = sSitesWithData->begin();
+ list<siteData>::iterator end = sSitesWithData->end();
+ for (; iter != end; ++iter) {
+ const siteData& data = *iter;
+ sites.push_back(data.site);
+ }
+ }
+
+ // Remove duplicate strings.
+ sites.sort();
+ sites.unique();
+
+ // Add strings to the result array, and null terminate.
+ {
+ int i = 0;
+ list<string>::iterator iter = sites.begin();
+ list<string>::iterator end = sites.end();
+ for (; iter != end; ++iter, ++i) {
+ const string& site = *iter;
+ result[i] = static_cast<char*>(NPN_MemAlloc(site.length() + 1));
+ memcpy(result[i], site.c_str(), site.length() + 1);
+ }
+ }
+ result[sites.size()] = nullptr;
+
+ return result;
+}
+
+//
+// npapi browser functions
+//
+
+bool NPN_SetProperty(NPP instance, NPObject* obj, NPIdentifier propertyName,
+ const NPVariant* value) {
+ return sBrowserFuncs->setproperty(instance, obj, propertyName, value);
+}
+
+NPIdentifier NPN_GetIntIdentifier(int32_t intid) {
+ return sBrowserFuncs->getintidentifier(intid);
+}
+
+NPIdentifier NPN_GetStringIdentifier(const NPUTF8* name) {
+ return sBrowserFuncs->getstringidentifier(name);
+}
+
+void NPN_GetStringIdentifiers(const NPUTF8** names, int32_t nameCount,
+ NPIdentifier* identifiers) {
+ return sBrowserFuncs->getstringidentifiers(names, nameCount, identifiers);
+}
+
+bool NPN_IdentifierIsString(NPIdentifier identifier) {
+ return sBrowserFuncs->identifierisstring(identifier);
+}
+
+NPUTF8* NPN_UTF8FromIdentifier(NPIdentifier identifier) {
+ return sBrowserFuncs->utf8fromidentifier(identifier);
+}
+
+int32_t NPN_IntFromIdentifier(NPIdentifier identifier) {
+ return sBrowserFuncs->intfromidentifier(identifier);
+}
+
+NPError NPN_GetValue(NPP instance, NPNVariable variable, void* value) {
+ return sBrowserFuncs->getvalue(instance, variable, value);
+}
+
+NPError NPN_SetValue(NPP instance, NPPVariable variable, void* value) {
+ return sBrowserFuncs->setvalue(instance, variable, value);
+}
+
+void NPN_InvalidateRect(NPP instance, NPRect* rect) {
+ sBrowserFuncs->invalidaterect(instance, rect);
+}
+
+bool NPN_HasProperty(NPP instance, NPObject* obj, NPIdentifier propertyName) {
+ return sBrowserFuncs->hasproperty(instance, obj, propertyName);
+}
+
+NPObject* NPN_CreateObject(NPP instance, NPClass* aClass) {
+ return sBrowserFuncs->createobject(instance, aClass);
+}
+
+bool NPN_Invoke(NPP npp, NPObject* obj, NPIdentifier methodName,
+ const NPVariant* args, uint32_t argCount, NPVariant* result) {
+ return sBrowserFuncs->invoke(npp, obj, methodName, args, argCount, result);
+}
+
+bool NPN_InvokeDefault(NPP npp, NPObject* obj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ return sBrowserFuncs->invokeDefault(npp, obj, args, argCount, result);
+}
+
+bool NPN_Construct(NPP npp, NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ return sBrowserFuncs->construct(npp, npobj, args, argCount, result);
+}
+
+const char* NPN_UserAgent(NPP instance) {
+ return sBrowserFuncs->uagent(instance);
+}
+
+NPObject* NPN_RetainObject(NPObject* obj) {
+ return sBrowserFuncs->retainobject(obj);
+}
+
+void NPN_ReleaseObject(NPObject* obj) {
+ return sBrowserFuncs->releaseobject(obj);
+}
+
+void* NPN_MemAlloc(uint32_t size) { return sBrowserFuncs->memalloc(size); }
+
+char* NPN_StrDup(const char* str) {
+ return strcpy((char*)sBrowserFuncs->memalloc(strlen(str) + 1), str);
+}
+
+void NPN_MemFree(void* ptr) { return sBrowserFuncs->memfree(ptr); }
+
+uint32_t NPN_ScheduleTimer(NPP instance, uint32_t interval, NPBool repeat,
+ void (*timerFunc)(NPP npp, uint32_t timerID)) {
+ return sBrowserFuncs->scheduletimer(instance, interval, repeat, timerFunc);
+}
+
+void NPN_UnscheduleTimer(NPP instance, uint32_t timerID) {
+ return sBrowserFuncs->unscheduletimer(instance, timerID);
+}
+
+void NPN_ReleaseVariantValue(NPVariant* variant) {
+ return sBrowserFuncs->releasevariantvalue(variant);
+}
+
+NPError NPN_GetURLNotify(NPP instance, const char* url, const char* target,
+ void* notifyData) {
+ return sBrowserFuncs->geturlnotify(instance, url, target, notifyData);
+}
+
+NPError NPN_GetURL(NPP instance, const char* url, const char* target) {
+ return sBrowserFuncs->geturl(instance, url, target);
+}
+
+NPError NPN_PostURLNotify(NPP instance, const char* url, const char* target,
+ uint32_t len, const char* buf, NPBool file,
+ void* notifyData) {
+ return sBrowserFuncs->posturlnotify(instance, url, target, len, buf, file,
+ notifyData);
+}
+
+NPError NPN_PostURL(NPP instance, const char* url, const char* target,
+ uint32_t len, const char* buf, NPBool file) {
+ return sBrowserFuncs->posturl(instance, url, target, len, buf, file);
+}
+
+bool NPN_Enumerate(NPP instance, NPObject* npobj, NPIdentifier** identifiers,
+ uint32_t* identifierCount) {
+ return sBrowserFuncs->enumerate(instance, npobj, identifiers,
+ identifierCount);
+}
+
+bool NPN_GetProperty(NPP instance, NPObject* npobj, NPIdentifier propertyName,
+ NPVariant* result) {
+ return sBrowserFuncs->getproperty(instance, npobj, propertyName, result);
+}
+
+bool NPN_Evaluate(NPP instance, NPObject* npobj, NPString* script,
+ NPVariant* result) {
+ return sBrowserFuncs->evaluate(instance, npobj, script, result);
+}
+
+void NPN_SetException(NPObject* npobj, const NPUTF8* message) {
+ return sBrowserFuncs->setexception(npobj, message);
+}
+
+NPBool NPN_ConvertPoint(NPP instance, double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace, double* destX,
+ double* destY, NPCoordinateSpace destSpace) {
+ return sBrowserFuncs->convertpoint(instance, sourceX, sourceY, sourceSpace,
+ destX, destY, destSpace);
+}
+
+NPError NPN_SetValueForURL(NPP instance, NPNURLVariable variable,
+ const char* url, const char* value, uint32_t len) {
+ return sBrowserFuncs->setvalueforurl(instance, variable, url, value, len);
+}
+
+NPError NPN_GetValueForURL(NPP instance, NPNURLVariable variable,
+ const char* url, char** value, uint32_t* len) {
+ return sBrowserFuncs->getvalueforurl(instance, variable, url, value, len);
+}
+
+void NPN_URLRedirectResponse(NPP instance, void* notifyData, NPBool allow) {
+ return sBrowserFuncs->urlredirectresponse(instance, notifyData, allow);
+}
+
+NPError NPN_InitAsyncSurface(NPP instance, NPSize* size, NPImageFormat format,
+ void* initData, NPAsyncSurface* surface) {
+ return sBrowserFuncs->initasyncsurface(instance, size, format, initData,
+ surface);
+}
+
+NPError NPN_FinalizeAsyncSurface(NPP instance, NPAsyncSurface* surface) {
+ return sBrowserFuncs->finalizeasyncsurface(instance, surface);
+}
+
+void NPN_SetCurrentAsyncSurface(NPP instance, NPAsyncSurface* surface,
+ NPRect* changed) {
+ sBrowserFuncs->setcurrentasyncsurface(instance, surface, changed);
+}
+
+//
+// npruntime object functions
+//
+
+NPObject* scriptableAllocate(NPP npp, NPClass* aClass) {
+ TestNPObject* object = (TestNPObject*)NPN_MemAlloc(sizeof(TestNPObject));
+ if (!object) return nullptr;
+ memset(object, 0, sizeof(TestNPObject));
+ return object;
+}
+
+void scriptableDeallocate(NPObject* npobj) { NPN_MemFree(npobj); }
+
+void scriptableInvalidate(NPObject* npobj) {}
+
+bool scriptableHasMethod(NPObject* npobj, NPIdentifier name) {
+ for (int i = 0; i < int(MOZ_ARRAY_LENGTH(sPluginMethodIdentifiers)); i++) {
+ if (name == sPluginMethodIdentifiers[i]) return true;
+ }
+ return false;
+}
+
+bool scriptableInvoke(NPObject* npobj, NPIdentifier name, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ if (id->throwOnNextInvoke) {
+ id->throwOnNextInvoke = false;
+ if (argCount == 0) {
+ NPN_SetException(npobj, nullptr);
+ } else {
+ for (uint32_t i = 0; i < argCount; i++) {
+ const NPString* argstr = &NPVARIANT_TO_STRING(args[i]);
+ NPN_SetException(npobj, argstr->UTF8Characters);
+ }
+ }
+ return false;
+ }
+
+ for (int i = 0; i < int(MOZ_ARRAY_LENGTH(sPluginMethodIdentifiers)); i++) {
+ if (name == sPluginMethodIdentifiers[i])
+ return sPluginMethodFunctions[i](npobj, args, argCount, result);
+ }
+ return false;
+}
+
+bool scriptableHasProperty(NPObject* npobj, NPIdentifier name) {
+ if (NPN_IdentifierIsString(name)) {
+ NPUTF8* asUTF8 = NPN_UTF8FromIdentifier(name);
+ if (NPN_GetStringIdentifier(asUTF8) != name) {
+ Crash();
+ }
+ NPN_MemFree(asUTF8);
+ } else {
+ if (NPN_GetIntIdentifier(NPN_IntFromIdentifier(name)) != name) {
+ Crash();
+ }
+ }
+ for (int i = 0; i < int(MOZ_ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
+ if (name == sPluginPropertyIdentifiers[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool scriptableGetProperty(NPObject* npobj, NPIdentifier name,
+ NPVariant* result) {
+ for (int i = 0; i < int(MOZ_ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
+ if (name == sPluginPropertyIdentifiers[i]) {
+ DuplicateNPVariant(*result, sPluginPropertyValues[i]);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool scriptableSetProperty(NPObject* npobj, NPIdentifier name,
+ const NPVariant* value) {
+ for (int i = 0; i < int(MOZ_ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
+ if (name == sPluginPropertyIdentifiers[i]) {
+ NPN_ReleaseVariantValue(&sPluginPropertyValues[i]);
+ DuplicateNPVariant(sPluginPropertyValues[i], *value);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool scriptableRemoveProperty(NPObject* npobj, NPIdentifier name) {
+ for (int i = 0; i < int(MOZ_ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
+ if (name == sPluginPropertyIdentifiers[i]) {
+ NPN_ReleaseVariantValue(&sPluginPropertyValues[i]);
+
+ // Avoid double frees (see test_propertyAndMethod.html, which deletes a
+ // property that doesn't exist).
+ VOID_TO_NPVARIANT(sPluginPropertyValues[i]);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool scriptableEnumerate(NPObject* npobj, NPIdentifier** identifier,
+ uint32_t* count) {
+ const int bufsize =
+ sizeof(NPIdentifier) * MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames);
+ NPIdentifier* ids = (NPIdentifier*)NPN_MemAlloc(bufsize);
+ if (!ids) return false;
+
+ memcpy(ids, sPluginMethodIdentifiers, bufsize);
+ *identifier = ids;
+ *count = MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames);
+ return true;
+}
+
+bool scriptableConstruct(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ return false;
+}
+
+//
+// test functions
+//
+
+static bool compareVariants(NPP instance, const NPVariant* var1,
+ const NPVariant* var2) {
+ bool success = true;
+ InstanceData* id = static_cast<InstanceData*>(instance->pdata);
+ if (var1->type != var2->type) {
+ id->err << "Variant types don't match; got " << var1->type << " expected "
+ << var2->type;
+ return false;
+ }
+
+ // Cast var1->type from NPVariantType to int to avoid compiler warnings about
+ // not needing a default case when we have cases for every enum value.
+ switch (static_cast<int>(var1->type)) {
+ case NPVariantType_Int32: {
+ int32_t result = NPVARIANT_TO_INT32(*var1);
+ int32_t expected = NPVARIANT_TO_INT32(*var2);
+ if (result != expected) {
+ id->err << "Variant values don't match; got " << result << " expected "
+ << expected;
+ success = false;
+ }
+ break;
+ }
+ case NPVariantType_Double: {
+ double result = NPVARIANT_TO_DOUBLE(*var1);
+ double expected = NPVARIANT_TO_DOUBLE(*var2);
+ if (result != expected) {
+ id->err << "Variant values don't match (double)";
+ success = false;
+ }
+ break;
+ }
+ case NPVariantType_Void: {
+ // void values are always equivalent
+ break;
+ }
+ case NPVariantType_Null: {
+ // null values are always equivalent
+ break;
+ }
+ case NPVariantType_Bool: {
+ bool result = NPVARIANT_TO_BOOLEAN(*var1);
+ bool expected = NPVARIANT_TO_BOOLEAN(*var2);
+ if (result != expected) {
+ id->err << "Variant values don't match (bool)";
+ success = false;
+ }
+ break;
+ }
+ case NPVariantType_String: {
+ const NPString* result = &NPVARIANT_TO_STRING(*var1);
+ const NPString* expected = &NPVARIANT_TO_STRING(*var2);
+ if (strcmp(result->UTF8Characters, expected->UTF8Characters) ||
+ strlen(result->UTF8Characters) != strlen(expected->UTF8Characters)) {
+ id->err << "Variant values don't match; got " << result->UTF8Characters
+ << " expected " << expected->UTF8Characters;
+ success = false;
+ }
+ break;
+ }
+ case NPVariantType_Object: {
+ uint32_t i, identifierCount = 0;
+ NPIdentifier* identifiers;
+ NPObject* result = NPVARIANT_TO_OBJECT(*var1);
+ NPObject* expected = NPVARIANT_TO_OBJECT(*var2);
+ bool enumerate_result =
+ NPN_Enumerate(instance, expected, &identifiers, &identifierCount);
+ if (!enumerate_result) {
+ id->err << "NPN_Enumerate failed";
+ success = false;
+ }
+ for (i = 0; i < identifierCount; i++) {
+ NPVariant resultVariant, expectedVariant;
+ if (!NPN_GetProperty(instance, expected, identifiers[i],
+ &expectedVariant)) {
+ id->err << "NPN_GetProperty returned false";
+ success = false;
+ } else {
+ if (!NPN_HasProperty(instance, result, identifiers[i])) {
+ id->err << "NPN_HasProperty returned false";
+ success = false;
+ } else {
+ if (!NPN_GetProperty(instance, result, identifiers[i],
+ &resultVariant)) {
+ id->err << "NPN_GetProperty 2 returned false";
+ success = false;
+ } else {
+ success =
+ compareVariants(instance, &resultVariant, &expectedVariant);
+ NPN_ReleaseVariantValue(&expectedVariant);
+ }
+ }
+ NPN_ReleaseVariantValue(&resultVariant);
+ }
+ }
+ NPN_MemFree(identifiers);
+ break;
+ }
+ default:
+ id->err << "Unknown variant type";
+ success = false;
+ MOZ_ASSERT_UNREACHABLE("Unknown variant type?!");
+ }
+
+ return success;
+}
+
+static bool throwExceptionNextInvoke(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ id->throwOnNextInvoke = true;
+ BOOLEAN_TO_NPVARIANT(true, *result);
+ return true;
+}
+
+static bool npnInvokeDefaultTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ bool success = false;
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ NPObject* windowObject;
+ NPN_GetValue(npp, NPNVWindowNPObject, &windowObject);
+ if (!windowObject) return false;
+
+ NPIdentifier objectIdentifier = variantToIdentifier(args[0]);
+ if (!objectIdentifier) return false;
+
+ NPVariant objectVariant;
+ if (NPN_GetProperty(npp, windowObject, objectIdentifier, &objectVariant)) {
+ if (NPVARIANT_IS_OBJECT(objectVariant)) {
+ NPObject* selfObject = NPVARIANT_TO_OBJECT(objectVariant);
+ if (selfObject != nullptr) {
+ NPVariant resultVariant;
+ if (NPN_InvokeDefault(npp, selfObject,
+ argCount > 1 ? &args[1] : nullptr, argCount - 1,
+ &resultVariant)) {
+ *result = resultVariant;
+ success = true;
+ }
+ }
+ }
+ NPN_ReleaseVariantValue(&objectVariant);
+ }
+
+ NPN_ReleaseObject(windowObject);
+ return success;
+}
+
+static bool npnInvokeTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ id->err.str("");
+ if (argCount < 2) return false;
+
+ NPIdentifier function = variantToIdentifier(args[0]);
+ if (!function) return false;
+
+ NPObject* windowObject;
+ NPN_GetValue(npp, NPNVWindowNPObject, &windowObject);
+ if (!windowObject) return false;
+
+ NPVariant invokeResult;
+ bool invokeReturn =
+ NPN_Invoke(npp, windowObject, function, argCount > 2 ? &args[2] : nullptr,
+ argCount - 2, &invokeResult);
+
+ bool compareResult = compareVariants(npp, &invokeResult, &args[1]);
+
+ NPN_ReleaseObject(windowObject);
+ NPN_ReleaseVariantValue(&invokeResult);
+ BOOLEAN_TO_NPVARIANT(invokeReturn && compareResult, *result);
+ return true;
+}
+
+static bool npnEvaluateTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ bool success = false;
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ if (argCount != 1) return false;
+
+ if (!NPVARIANT_IS_STRING(args[0])) return false;
+
+ NPObject* windowObject;
+ NPN_GetValue(npp, NPNVWindowNPObject, &windowObject);
+ if (!windowObject) return false;
+
+ success = NPN_Evaluate(npp, windowObject,
+ (NPString*)&NPVARIANT_TO_STRING(args[0]), result);
+
+ NPN_ReleaseObject(windowObject);
+ return success;
+}
+
+static bool setUndefinedValueTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ NPError err = NPN_SetValue(npp, (NPPVariable)0x0, 0x0);
+ BOOLEAN_TO_NPVARIANT((err == NPERR_NO_ERROR), *result);
+ return true;
+}
+
+static bool identifierToStringTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 1) return false;
+ NPIdentifier identifier = variantToIdentifier(args[0]);
+ if (!identifier) return false;
+
+ NPUTF8* utf8String = NPN_UTF8FromIdentifier(identifier);
+ if (!utf8String) return false;
+ STRINGZ_TO_NPVARIANT(utf8String, *result);
+ return true;
+}
+
+static bool queryPrivateModeState(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPBool pms = false;
+ NPN_GetValue(static_cast<TestNPObject*>(npobj)->npp, NPNVprivateModeBool,
+ &pms);
+ BOOLEAN_TO_NPVARIANT(pms, *result);
+ return true;
+}
+
+static bool lastReportedPrivateModeState(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ InstanceData* id =
+ static_cast<InstanceData*>(static_cast<TestNPObject*>(npobj)->npp->pdata);
+ BOOLEAN_TO_NPVARIANT(id->lastReportedPrivateModeState, *result);
+ return true;
+}
+
+static bool hasWidget(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (argCount != 0) return false;
+
+ InstanceData* id =
+ static_cast<InstanceData*>(static_cast<TestNPObject*>(npobj)->npp->pdata);
+ BOOLEAN_TO_NPVARIANT(id->hasWidget, *result);
+ return true;
+}
+
+static bool getEdge(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (argCount != 1) return false;
+ if (!NPVARIANT_IS_INT32(args[0])) return false;
+ int32_t edge = NPVARIANT_TO_INT32(args[0]);
+ if (edge < EDGE_LEFT || edge > EDGE_BOTTOM) return false;
+
+ InstanceData* id =
+ static_cast<InstanceData*>(static_cast<TestNPObject*>(npobj)->npp->pdata);
+ int32_t r = pluginGetEdge(id, RectEdge(edge));
+ if (r == NPTEST_INT32_ERROR) return false;
+ INT32_TO_NPVARIANT(r, *result);
+ return true;
+}
+
+static bool getClipRegionRectCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ InstanceData* id =
+ static_cast<InstanceData*>(static_cast<TestNPObject*>(npobj)->npp->pdata);
+ int32_t r = pluginGetClipRegionRectCount(id);
+ if (r == NPTEST_INT32_ERROR) return false;
+ INT32_TO_NPVARIANT(r, *result);
+ return true;
+}
+
+static bool getClipRegionRectEdge(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 2) return false;
+ if (!NPVARIANT_IS_INT32(args[0])) return false;
+ int32_t rectIndex = NPVARIANT_TO_INT32(args[0]);
+ if (rectIndex < 0) return false;
+ if (!NPVARIANT_IS_INT32(args[1])) return false;
+ int32_t edge = NPVARIANT_TO_INT32(args[1]);
+ if (edge < EDGE_LEFT || edge > EDGE_BOTTOM) return false;
+
+ InstanceData* id =
+ static_cast<InstanceData*>(static_cast<TestNPObject*>(npobj)->npp->pdata);
+ int32_t r = pluginGetClipRegionRectEdge(id, rectIndex, RectEdge(edge));
+ if (r == NPTEST_INT32_ERROR) return false;
+ INT32_TO_NPVARIANT(r, *result);
+ return true;
+}
+
+static bool startWatchingInstanceCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+ if (sWatchingInstanceCount) return false;
+
+ sWatchingInstanceCount = true;
+ sInstanceCount = 0;
+ ++sCurrentInstanceCountWatchGeneration;
+ return true;
+}
+
+static bool getInstanceCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+ if (!sWatchingInstanceCount) return false;
+
+ INT32_TO_NPVARIANT(sInstanceCount, *result);
+ return true;
+}
+
+static bool stopWatchingInstanceCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+ if (!sWatchingInstanceCount) return false;
+
+ sWatchingInstanceCount = false;
+ return true;
+}
+
+static bool getLastMouseX(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ INT32_TO_NPVARIANT(id->lastMouseX, *result);
+ return true;
+}
+
+static bool getLastMouseY(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ INT32_TO_NPVARIANT(id->lastMouseY, *result);
+ return true;
+}
+
+static bool getPaintCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ INT32_TO_NPVARIANT(id->paintCount, *result);
+ return true;
+}
+
+static bool resetPaintCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ id->paintCount = 0;
+ return true;
+}
+
+static bool getWidthAtLastPaint(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ INT32_TO_NPVARIANT(id->widthAtLastPaint, *result);
+ return true;
+}
+
+static bool setInvalidateDuringPaint(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 1) return false;
+
+ if (!NPVARIANT_IS_BOOLEAN(args[0])) return false;
+ bool doInvalidate = NPVARIANT_TO_BOOLEAN(args[0]);
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ id->invalidateDuringPaint = doInvalidate;
+ return true;
+}
+
+static bool setSlowPaint(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 1) return false;
+
+ if (!NPVARIANT_IS_BOOLEAN(args[0])) return false;
+ bool slow = NPVARIANT_TO_BOOLEAN(args[0]);
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ id->slowPaint = slow;
+ return true;
+}
+
+static bool getError(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ if (id->err.str().length() == 0) {
+ char* outval = NPN_StrDup(SUCCESS_STRING);
+ STRINGZ_TO_NPVARIANT(outval, *result);
+ } else {
+ char* outval = NPN_StrDup(id->err.str().c_str());
+ STRINGZ_TO_NPVARIANT(outval, *result);
+ }
+ return true;
+}
+
+static bool doInternalConsistencyCheck(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ string error;
+ pluginDoInternalConsistencyCheck(id, error);
+ NPUTF8* utf8String = (NPUTF8*)NPN_MemAlloc(error.length() + 1);
+ if (!utf8String) {
+ return false;
+ }
+ memcpy(utf8String, error.c_str(), error.length() + 1);
+ STRINGZ_TO_NPVARIANT(utf8String, *result);
+ return true;
+}
+
+static bool convertPointX(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 4) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ if (!NPVARIANT_IS_INT32(args[0])) return false;
+ int32_t sourceSpace = NPVARIANT_TO_INT32(args[0]);
+
+ if (!NPVARIANT_IS_INT32(args[1])) return false;
+ double sourceX = static_cast<double>(NPVARIANT_TO_INT32(args[1]));
+
+ if (!NPVARIANT_IS_INT32(args[2])) return false;
+ double sourceY = static_cast<double>(NPVARIANT_TO_INT32(args[2]));
+
+ if (!NPVARIANT_IS_INT32(args[3])) return false;
+ int32_t destSpace = NPVARIANT_TO_INT32(args[3]);
+
+ double resultX, resultY;
+ NPN_ConvertPoint(npp, sourceX, sourceY, (NPCoordinateSpace)sourceSpace,
+ &resultX, &resultY, (NPCoordinateSpace)destSpace);
+
+ DOUBLE_TO_NPVARIANT(resultX, *result);
+ return true;
+}
+
+static bool convertPointY(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 4) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ if (!NPVARIANT_IS_INT32(args[0])) return false;
+ int32_t sourceSpace = NPVARIANT_TO_INT32(args[0]);
+
+ if (!NPVARIANT_IS_INT32(args[1])) return false;
+ double sourceX = static_cast<double>(NPVARIANT_TO_INT32(args[1]));
+
+ if (!NPVARIANT_IS_INT32(args[2])) return false;
+ double sourceY = static_cast<double>(NPVARIANT_TO_INT32(args[2]));
+
+ if (!NPVARIANT_IS_INT32(args[3])) return false;
+ int32_t destSpace = NPVARIANT_TO_INT32(args[3]);
+
+ double resultX, resultY;
+ NPN_ConvertPoint(npp, sourceX, sourceY, (NPCoordinateSpace)sourceSpace,
+ &resultX, &resultY, (NPCoordinateSpace)destSpace);
+
+ DOUBLE_TO_NPVARIANT(resultY, *result);
+ return true;
+}
+
+static bool streamTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ // .streamTest(url, doPost, postData, writeCallback, notifyCallback,
+ // redirectCallback, allowRedirects, postFile = false)
+ if (!(7 <= argCount && argCount <= 8)) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ if (!NPVARIANT_IS_STRING(args[0])) return false;
+ NPString url = NPVARIANT_TO_STRING(args[0]);
+
+ if (!NPVARIANT_IS_BOOLEAN(args[1])) return false;
+ bool doPost = NPVARIANT_TO_BOOLEAN(args[1]);
+
+ NPString postData = {nullptr, 0};
+ if (NPVARIANT_IS_STRING(args[2])) {
+ postData = NPVARIANT_TO_STRING(args[2]);
+ } else {
+ if (!NPVARIANT_IS_NULL(args[2])) {
+ return false;
+ }
+ }
+
+ NPObject* writeCallback = nullptr;
+ if (NPVARIANT_IS_OBJECT(args[3])) {
+ writeCallback = NPVARIANT_TO_OBJECT(args[3]);
+ } else {
+ if (!NPVARIANT_IS_NULL(args[3])) {
+ return false;
+ }
+ }
+
+ NPObject* notifyCallback = nullptr;
+ if (NPVARIANT_IS_OBJECT(args[4])) {
+ notifyCallback = NPVARIANT_TO_OBJECT(args[4]);
+ } else {
+ if (!NPVARIANT_IS_NULL(args[4])) {
+ return false;
+ }
+ }
+
+ NPObject* redirectCallback = nullptr;
+ if (NPVARIANT_IS_OBJECT(args[5])) {
+ redirectCallback = NPVARIANT_TO_OBJECT(args[5]);
+ } else {
+ if (!NPVARIANT_IS_NULL(args[5])) {
+ return false;
+ }
+ }
+
+ if (!NPVARIANT_IS_BOOLEAN(args[6])) return false;
+ bool allowRedirects = NPVARIANT_TO_BOOLEAN(args[6]);
+
+ bool postFile = false;
+ if (argCount >= 8) {
+ if (!NPVARIANT_IS_BOOLEAN(args[7])) {
+ return false;
+ }
+ postFile = NPVARIANT_TO_BOOLEAN(args[7]);
+ }
+
+ URLNotifyData* ndata = new URLNotifyData;
+ ndata->cookie = "dynamic-cookie";
+ ndata->writeCallback = writeCallback;
+ ndata->notifyCallback = notifyCallback;
+ ndata->redirectCallback = redirectCallback;
+ ndata->size = 0;
+ ndata->data = nullptr;
+ ndata->allowRedirects = allowRedirects;
+
+ /* null-terminate "url" */
+ char* urlstr = (char*)malloc(url.UTF8Length + 1);
+ strncpy(urlstr, url.UTF8Characters, url.UTF8Length);
+ urlstr[url.UTF8Length] = '\0';
+
+ NPError err;
+ if (doPost) {
+ err = NPN_PostURLNotify(npp, urlstr, nullptr, postData.UTF8Length,
+ postData.UTF8Characters, postFile, ndata);
+ } else {
+ err = NPN_GetURLNotify(npp, urlstr, nullptr, ndata);
+ }
+
+ free(urlstr);
+
+ if (NPERR_NO_ERROR == err) {
+ if (ndata->writeCallback) {
+ NPN_RetainObject(ndata->writeCallback);
+ }
+ if (ndata->notifyCallback) {
+ NPN_RetainObject(ndata->notifyCallback);
+ }
+ if (ndata->redirectCallback) {
+ NPN_RetainObject(ndata->redirectCallback);
+ }
+ BOOLEAN_TO_NPVARIANT(true, *result);
+ } else {
+ delete ndata;
+ BOOLEAN_TO_NPVARIANT(false, *result);
+ }
+
+ return true;
+}
+
+static bool postFileToURLTest(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (1 != argCount) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ string url;
+ {
+ if (!NPVARIANT_IS_STRING(args[0])) return false;
+ NPString npurl = NPVARIANT_TO_STRING(args[0]);
+ // make a copy to ensure that the url string is null-terminated
+ url = string(npurl.UTF8Characters, npurl.UTF8Length);
+ }
+
+ NPError err;
+ {
+ string buf("/path/to/file");
+ err = NPN_PostURL(npp, url.c_str(), nullptr /* target */, buf.length(),
+ buf.c_str(), true /* file */);
+ }
+
+ BOOLEAN_TO_NPVARIANT(NPERR_NO_ERROR == err, *result);
+ return true;
+}
+
+static bool setPluginWantsAllStreams(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (1 != argCount) return false;
+
+ if (!NPVARIANT_IS_BOOLEAN(args[0])) return false;
+ bool wantsAllStreams = NPVARIANT_TO_BOOLEAN(args[0]);
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ id->wantsAllStreams = wantsAllStreams;
+
+ return true;
+}
+
+static bool crashPlugin(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ IntentionalCrash();
+ VOID_TO_NPVARIANT(*result);
+ return true;
+}
+
+static bool crashOnDestroy(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ id->crashOnDestroy = true;
+ VOID_TO_NPVARIANT(*result);
+ return true;
+}
+
+static bool setColor(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (argCount != 1) return false;
+ if (!NPVARIANT_IS_STRING(args[0])) return false;
+ const NPString* str = &NPVARIANT_TO_STRING(args[0]);
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ id->scriptableObject->drawColor =
+ parseHexColor(str->UTF8Characters, str->UTF8Length);
+
+ NPRect r;
+ r.left = 0;
+ r.top = 0;
+ r.right = id->window.width;
+ r.bottom = id->window.height;
+ if (id->asyncDrawing == AD_NONE) {
+ NPN_InvalidateRect(npp, &r);
+ } else if (id->asyncDrawing == AD_BITMAP) {
+ drawAsyncBitmapColor(id);
+ }
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+}
+
+void notifyDidPaint(InstanceData* instanceData) {
+ ++instanceData->paintCount;
+ instanceData->widthAtLastPaint = instanceData->window.width;
+
+ if (instanceData->invalidateDuringPaint) {
+ NPRect r;
+ r.left = 0;
+ r.top = 0;
+ r.right = instanceData->window.width;
+ r.bottom = instanceData->window.height;
+ NPN_InvalidateRect(instanceData->npp, &r);
+ }
+
+ if (instanceData->slowPaint) {
+ XPSleep(1);
+ }
+
+ if (instanceData->runScriptOnPaint) {
+ NPObject* o = nullptr;
+ NPN_GetValue(instanceData->npp, NPNVPluginElementNPObject, &o);
+ if (o) {
+ NPVariant param;
+ STRINGZ_TO_NPVARIANT("paintscript", param);
+ NPVariant result;
+ NPN_Invoke(instanceData->npp, o, NPN_GetStringIdentifier("getAttribute"),
+ &param, 1, &result);
+
+ if (NPVARIANT_IS_STRING(result)) {
+ NPObject* windowObject;
+ NPN_GetValue(instanceData->npp, NPNVWindowNPObject, &windowObject);
+ if (windowObject) {
+ NPVariant evalResult;
+ NPN_Evaluate(instanceData->npp, windowObject,
+ (NPString*)&NPVARIANT_TO_STRING(result), &evalResult);
+ NPN_ReleaseVariantValue(&evalResult);
+ NPN_ReleaseObject(windowObject);
+ }
+ }
+
+ NPN_ReleaseVariantValue(&result);
+ NPN_ReleaseObject(o);
+ }
+ }
+}
+
+static const NPClass kTestSharedNPClass = {
+ NP_CLASS_STRUCT_VERSION,
+ // Everything else is nullptr
+};
+
+static bool getObjectValue(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ NPObject* o =
+ NPN_CreateObject(npp, const_cast<NPClass*>(&kTestSharedNPClass));
+ if (!o) return false;
+
+ OBJECT_TO_NPVARIANT(o, *result);
+ return true;
+}
+
+static bool checkObjectValue(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ VOID_TO_NPVARIANT(*result);
+
+ if (1 != argCount) return false;
+
+ if (!NPVARIANT_IS_OBJECT(args[0])) return false;
+
+ NPObject* o = NPVARIANT_TO_OBJECT(args[0]);
+
+ BOOLEAN_TO_NPVARIANT(o->_class == &kTestSharedNPClass, *result);
+ return true;
+}
+
+static bool enableFPExceptions(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ VOID_TO_NPVARIANT(*result);
+
+#if defined(XP_WIN) && defined(_M_IX86)
+ _control87(0, _MCW_EM);
+ return true;
+#else
+ return false;
+#endif
+}
+
+static void timerCallback(NPP npp, uint32_t timerID) {
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ currentTimerEventCount++;
+ timerEvent event = timerEvents[currentTimerEventCount];
+
+ NPObject* windowObject;
+ NPN_GetValue(npp, NPNVWindowNPObject, &windowObject);
+ if (!windowObject) return;
+
+ NPVariant rval;
+ if (timerID != id->timerID[event.timerIdReceive]) {
+ id->timerTestResult = false;
+ }
+
+ if (currentTimerEventCount == totalTimerEvents - 1) {
+ NPVariant arg;
+ BOOLEAN_TO_NPVARIANT(id->timerTestResult, arg);
+ NPN_Invoke(npp, windowObject,
+ NPN_GetStringIdentifier(id->timerTestScriptCallback.c_str()),
+ &arg, 1, &rval);
+ NPN_ReleaseVariantValue(&arg);
+ }
+
+ NPN_ReleaseObject(windowObject);
+
+ if (event.timerIdSchedule > -1) {
+ id->timerID[event.timerIdSchedule] = NPN_ScheduleTimer(
+ npp, event.timerInterval, event.timerRepeat, timerCallback);
+ }
+ if (event.timerIdUnschedule > -1) {
+ NPN_UnscheduleTimer(npp, id->timerID[event.timerIdUnschedule]);
+ }
+}
+
+static bool timerTest(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ currentTimerEventCount = 0;
+
+ if (argCount < 1 || !NPVARIANT_IS_STRING(args[0])) return false;
+ const NPString* argstr = &NPVARIANT_TO_STRING(args[0]);
+ id->timerTestScriptCallback = argstr->UTF8Characters;
+
+ id->timerTestResult = true;
+ timerEvent event = timerEvents[currentTimerEventCount];
+
+ id->timerID[event.timerIdSchedule] = NPN_ScheduleTimer(
+ npp, event.timerInterval, event.timerRepeat, timerCallback);
+
+ return id->timerID[event.timerIdSchedule] != 0;
+}
+
+bool hangPlugin(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ mozilla::NoteIntentionalCrash("plugin");
+
+ bool busyHang = false;
+ if ((argCount == 1) && NPVARIANT_IS_BOOLEAN(args[0])) {
+ busyHang = NPVARIANT_TO_BOOLEAN(args[0]);
+ }
+
+ if (busyHang) {
+ const time_t start = std::time(nullptr);
+ while ((std::time(nullptr) - start) < 100000) {
+ volatile int dummy = 0;
+ for (int i = 0; i < 1000; ++i) {
+ dummy++;
+ }
+ }
+ } else {
+#ifdef XP_WIN
+ Sleep(100000000);
+ Sleep(100000000);
+#else
+ pause();
+ pause();
+#endif
+ }
+
+ // NB: returning true here means that we weren't terminated, and
+ // thus the hang detection/handling didn't work correctly. The
+ // test harness will succeed in calling this function, and the
+ // test will fail.
+ return true;
+}
+
+bool stallPlugin(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ uint32_t stallTimeSeconds = 0;
+ if ((argCount == 1) && NPVARIANT_IS_INT32(args[0])) {
+ stallTimeSeconds = (uint32_t)NPVARIANT_TO_INT32(args[0]);
+ }
+
+#ifdef XP_WIN
+ Sleep(stallTimeSeconds * 1000U);
+#else
+ sleep(stallTimeSeconds);
+#endif
+
+ return true;
+}
+
+#if defined(MOZ_WIDGET_GTK)
+bool getClipboardText(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ string sel = pluginGetClipboardText(id);
+
+ uint32_t len = sel.size();
+ char* selCopy = static_cast<char*>(NPN_MemAlloc(1 + len));
+ if (!selCopy) return false;
+
+ memcpy(selCopy, sel.c_str(), len);
+ selCopy[len] = '\0';
+
+ STRINGN_TO_NPVARIANT(selCopy, len, *result);
+ // *result owns str now
+
+ return true;
+}
+
+bool crashPluginInNestedLoop(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ return pluginCrashInNestedLoop(id);
+}
+
+bool triggerXError(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ return pluginTriggerXError(id);
+}
+
+bool destroySharedGfxStuff(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ return pluginDestroySharedGfxStuff(id);
+}
+
+#else
+bool getClipboardText(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ // XXX Not implemented!
+ return false;
+}
+
+bool crashPluginInNestedLoop(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ // XXX Not implemented!
+ return false;
+}
+
+bool triggerXError(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ // XXX Not implemented!
+ return false;
+}
+
+bool destroySharedGfxStuff(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ // XXX Not implemented!
+ return false;
+}
+#endif
+
+#if defined(XP_WIN)
+bool nativeWidgetIsVisible(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ bool visible = pluginNativeWidgetIsVisible(id);
+ BOOLEAN_TO_NPVARIANT(visible, *result);
+ return true;
+}
+#else
+bool nativeWidgetIsVisible(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ // XXX Not implemented!
+ return false;
+}
+#endif
+
+bool getLastCompositionText(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+#ifdef XP_WIN
+ if (argCount != 0) {
+ return false;
+ }
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ char* outval = NPN_StrDup(id->lastComposition.c_str());
+ STRINGZ_TO_NPVARIANT(outval, *result);
+ return true;
+#else
+ // XXX not implemented
+ return false;
+#endif
+}
+
+bool scriptableInvokeDefault(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ ostringstream value;
+ value << sPluginName;
+ for (uint32_t i = 0; i < argCount; i++) {
+ switch (args[i].type) {
+ case NPVariantType_Int32:
+ value << ";" << NPVARIANT_TO_INT32(args[i]);
+ break;
+ case NPVariantType_String: {
+ const NPString* argstr = &NPVARIANT_TO_STRING(args[i]);
+ value << ";" << argstr->UTF8Characters;
+ break;
+ }
+ case NPVariantType_Void:
+ value << ";undefined";
+ break;
+ case NPVariantType_Null:
+ value << ";null";
+ break;
+ default:
+ value << ";other";
+ }
+ }
+
+ char* outval = NPN_StrDup(value.str().c_str());
+ STRINGZ_TO_NPVARIANT(outval, *result);
+ return true;
+}
+
+static const NPClass kInvokeDefaultClass = {NP_CLASS_STRUCT_VERSION,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ scriptableInvokeDefault,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr};
+
+bool getInvokeDefaultObject(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (0 != argCount) {
+ return false;
+ }
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ NPObject* testObject =
+ NPN_CreateObject(npp, const_cast<NPClass*>(&kInvokeDefaultClass));
+ OBJECT_TO_NPVARIANT(testObject, *result);
+ return true;
+}
+
+bool callOnDestroy(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ if (id->callOnDestroy) return false;
+
+ if (1 != argCount || !NPVARIANT_IS_OBJECT(args[0])) return false;
+
+ id->callOnDestroy = NPVARIANT_TO_OBJECT(args[0]);
+ NPN_RetainObject(id->callOnDestroy);
+
+ return true;
+}
+
+// On Linux at least, a windowed plugin resize causes Flash Player to
+// reconnect to the browser window. This method simulates that.
+bool reinitWidget(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ if (!id->hasWidget) return false;
+
+ pluginWidgetInit(id, id->window.window);
+ return true;
+}
+
+bool propertyAndMethod(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ INT32_TO_NPVARIANT(5, *result);
+ return true;
+}
+
+// Returns top-level window activation state as indicated by Cocoa NPAPI's
+// NPCocoaEventWindowFocusChanged events - 'true' if active, 'false' if not.
+// Throws an exception if no events have been received and thus this state
+// is unknown.
+bool getTopLevelWindowActivationState(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ // Throw an exception for unknown state.
+ if (id->topLevelWindowActivationState == ACTIVATION_STATE_UNKNOWN) {
+ return false;
+ }
+
+ if (id->topLevelWindowActivationState == ACTIVATION_STATE_ACTIVATED) {
+ BOOLEAN_TO_NPVARIANT(true, *result);
+ } else if (id->topLevelWindowActivationState ==
+ ACTIVATION_STATE_DEACTIVATED) {
+ BOOLEAN_TO_NPVARIANT(false, *result);
+ }
+
+ return true;
+}
+
+bool getTopLevelWindowActivationEventCount(NPObject* npobj,
+ const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ INT32_TO_NPVARIANT(id->topLevelWindowActivationEventCount, *result);
+
+ return true;
+}
+
+// Returns top-level window activation state as indicated by Cocoa NPAPI's
+// NPCocoaEventFocusChanged events - 'true' if active, 'false' if not.
+// Throws an exception if no events have been received and thus this state
+// is unknown.
+bool getFocusState(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ // Throw an exception for unknown state.
+ if (id->focusState == ACTIVATION_STATE_UNKNOWN) {
+ return false;
+ }
+
+ if (id->focusState == ACTIVATION_STATE_ACTIVATED) {
+ BOOLEAN_TO_NPVARIANT(true, *result);
+ } else if (id->focusState == ACTIVATION_STATE_DEACTIVATED) {
+ BOOLEAN_TO_NPVARIANT(false, *result);
+ }
+
+ return true;
+}
+
+bool getFocusEventCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ INT32_TO_NPVARIANT(id->focusEventCount, *result);
+
+ return true;
+}
+
+bool getEventModel(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ INT32_TO_NPVARIANT(id->eventModel, *result);
+
+ return true;
+}
+
+static bool ReflectorHasMethod(NPObject* npobj, NPIdentifier name) {
+ return false;
+}
+
+static bool ReflectorHasProperty(NPObject* npobj, NPIdentifier name) {
+ return true;
+}
+
+static bool ReflectorGetProperty(NPObject* npobj, NPIdentifier name,
+ NPVariant* result) {
+ if (NPN_IdentifierIsString(name)) {
+ char* s = NPN_UTF8FromIdentifier(name);
+ STRINGZ_TO_NPVARIANT(s, *result);
+ return true;
+ }
+
+ INT32_TO_NPVARIANT(NPN_IntFromIdentifier(name), *result);
+ return true;
+}
+
+static const NPClass kReflectorNPClass = {NP_CLASS_STRUCT_VERSION,
+ nullptr,
+ nullptr,
+ nullptr,
+ ReflectorHasMethod,
+ nullptr,
+ nullptr,
+ ReflectorHasProperty,
+ ReflectorGetProperty,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr};
+
+bool getReflector(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (0 != argCount) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ NPObject* reflector =
+ NPN_CreateObject(npp,
+ const_cast<NPClass*>(&kReflectorNPClass)); // retains
+ OBJECT_TO_NPVARIANT(reflector, *result);
+ return true;
+}
+
+bool isVisible(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ BOOLEAN_TO_NPVARIANT(
+ id->window.clipRect.top != 0 || id->window.clipRect.left != 0 ||
+ id->window.clipRect.bottom != 0 || id->window.clipRect.right != 0,
+ *result);
+ return true;
+}
+
+bool getWindowPosition(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ NPObject* window = nullptr;
+ NPError err = NPN_GetValue(npp, NPNVWindowNPObject, &window);
+ if (NPERR_NO_ERROR != err || !window) return false;
+
+ NPIdentifier arrayID = NPN_GetStringIdentifier("Array");
+ NPVariant arrayFunctionV;
+ bool ok = NPN_GetProperty(npp, window, arrayID, &arrayFunctionV);
+
+ NPN_ReleaseObject(window);
+
+ if (!ok) return false;
+
+ if (!NPVARIANT_IS_OBJECT(arrayFunctionV)) {
+ NPN_ReleaseVariantValue(&arrayFunctionV);
+ return false;
+ }
+ NPObject* arrayFunction = NPVARIANT_TO_OBJECT(arrayFunctionV);
+
+ NPVariant elements[4];
+ INT32_TO_NPVARIANT(id->window.x, elements[0]);
+ INT32_TO_NPVARIANT(id->window.y, elements[1]);
+ INT32_TO_NPVARIANT(id->window.width, elements[2]);
+ INT32_TO_NPVARIANT(id->window.height, elements[3]);
+
+ ok = NPN_InvokeDefault(npp, arrayFunction, elements, 4, result);
+
+ NPN_ReleaseObject(arrayFunction);
+
+ return ok;
+}
+
+bool constructObject(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (argCount == 0 || !NPVARIANT_IS_OBJECT(args[0])) return false;
+
+ NPObject* ctor = NPVARIANT_TO_OBJECT(args[0]);
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ return NPN_Construct(npp, ctor, args + 1, argCount - 1, result);
+}
+
+bool setSitesWithData(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (argCount != 1 || !NPVARIANT_IS_STRING(args[0])) return false;
+
+ // Clear existing data.
+ delete sSitesWithData;
+
+ const NPString* str = &NPVARIANT_TO_STRING(args[0]);
+ if (str->UTF8Length == 0) return true;
+
+ // Parse the comma-delimited string into a vector.
+ sSitesWithData = new list<siteData>;
+ const char* iterator = str->UTF8Characters;
+ const char* end = iterator + str->UTF8Length;
+ while (1) {
+ const char* next = strchr(iterator, ',');
+ if (!next) next = end;
+
+ // Parse out the three tokens into a siteData struct.
+ const char* siteEnd = strchr(iterator, ':');
+ *((char*)siteEnd) = '\0';
+ const char* flagsEnd = strchr(siteEnd + 1, ':');
+ *((char*)flagsEnd) = '\0';
+ *((char*)next) = '\0';
+
+ siteData data;
+ data.site = string(iterator);
+ data.flags = atoi(siteEnd + 1);
+ data.age = atoi(flagsEnd + 1);
+
+ sSitesWithData->push_back(data);
+
+ if (next == end) break;
+
+ iterator = next + 1;
+ }
+
+ return true;
+}
+
+bool setSitesWithDataCapabilities(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 1 || !NPVARIANT_IS_BOOLEAN(args[0])) return false;
+
+ sClearByAgeSupported = NPVARIANT_TO_BOOLEAN(args[0]);
+ return true;
+}
+
+bool getLastKeyText(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (argCount != 0) {
+ return false;
+ }
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ char* outval = NPN_StrDup(id->lastKeyText.c_str());
+ STRINGZ_TO_NPVARIANT(outval, *result);
+ return true;
+}
+
+bool getNPNVdocumentOrigin(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) {
+ return false;
+ }
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ char* origin = nullptr;
+ NPError err = NPN_GetValue(npp, NPNVdocumentOrigin, &origin);
+ if (err != NPERR_NO_ERROR) {
+ return false;
+ }
+
+ STRINGZ_TO_NPVARIANT(origin, *result);
+ return true;
+}
+
+bool getMouseUpEventCount(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) {
+ return false;
+ }
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ INT32_TO_NPVARIANT(id->mouseUpEventCount, *result);
+ return true;
+}
+
+bool queryContentsScaleFactor(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ double scaleFactor = 1.0;
+#if defined(XP_MACOSX) || defined(XP_WIN)
+ NPError err = NPN_GetValue(static_cast<TestNPObject*>(npobj)->npp,
+ NPNVcontentsScaleFactor, &scaleFactor);
+ if (err != NPERR_NO_ERROR) {
+ return false;
+ }
+#endif
+ DOUBLE_TO_NPVARIANT(scaleFactor, *result);
+ return true;
+}
+
+bool queryCSSZoomFactorSetValue(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ if (!npp) {
+ return false;
+ }
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ if (!id) {
+ return false;
+ }
+ DOUBLE_TO_NPVARIANT(id->cssZoomFactor, *result);
+ return true;
+}
+
+bool queryCSSZoomFactorGetValue(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) return false;
+
+ double zoomFactor = 1.0;
+ NPError err = NPN_GetValue(static_cast<TestNPObject*>(npobj)->npp,
+ NPNVCSSZoomFactor, &zoomFactor);
+ if (err != NPERR_NO_ERROR) {
+ return false;
+ }
+ DOUBLE_TO_NPVARIANT(zoomFactor, *result);
+ return true;
+}
+
+bool echoString(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ if (argCount != 1) {
+ return false;
+ }
+
+ if (!NPVARIANT_IS_STRING(args[0])) {
+ return false;
+ }
+
+ const NPString& arg = NPVARIANT_TO_STRING(args[0]);
+ NPUTF8* buffer =
+ static_cast<NPUTF8*>(NPN_MemAlloc(sizeof(NPUTF8) * arg.UTF8Length));
+ if (!buffer) {
+ return false;
+ }
+
+ std::copy(arg.UTF8Characters, arg.UTF8Characters + arg.UTF8Length, buffer);
+ STRINGN_TO_NPVARIANT(buffer, arg.UTF8Length, *result);
+
+ return true;
+}
+
+static bool toggleAudioPlayback(NPObject* npobj, uint32_t argCount,
+ bool playingAudio, NPVariant* result) {
+ if (argCount != 0) {
+ return false;
+ }
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ id->playingAudio = playingAudio;
+
+ NPN_SetValue(npp, NPPVpluginIsPlayingAudio, (void*)playingAudio);
+
+ VOID_TO_NPVARIANT(*result);
+ return true;
+}
+
+static bool startAudioPlayback(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ return toggleAudioPlayback(npobj, argCount, true, result);
+}
+
+static bool stopAudioPlayback(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ return toggleAudioPlayback(npobj, argCount, false, result);
+}
+
+static bool getAudioMuted(NPObject* npobj, const NPVariant* args,
+ uint32_t argCount, NPVariant* result) {
+ if (argCount != 0) {
+ return false;
+ }
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ BOOLEAN_TO_NPVARIANT(id->audioMuted, *result);
+ return true;
+}
diff --git a/dom/plugins/test/testplugin/nptest.def b/dom/plugins/test/testplugin/nptest.def
new file mode 100644
index 0000000000..4c543d5b9f
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest.def
@@ -0,0 +1,7 @@
+LIBRARY NPTEST
+
+EXPORTS
+ NP_GetEntryPoints @1
+ NP_Initialize @2
+ NP_Shutdown @3
+ NP_GetMIMEDescription @4
diff --git a/dom/plugins/test/testplugin/nptest.h b/dom/plugins/test/testplugin/nptest.h
new file mode 100644
index 0000000000..3bac9cb65b
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest.h
@@ -0,0 +1,150 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT OWNER OR 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.
+ *
+ * Contributor(s):
+ * Josh Aas <josh@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nptest_h_
+#define nptest_h_
+
+#include "mozilla-config.h"
+
+#include "npapi.h"
+#include "npfunctions.h"
+#include "npruntime.h"
+#include <stdint.h>
+#include <string>
+#include <sstream>
+
+typedef enum { DM_DEFAULT, DM_SOLID_COLOR } DrawMode;
+
+typedef enum {
+ FUNCTION_NONE,
+ FUNCTION_NPP_GETURL,
+ FUNCTION_NPP_GETURLNOTIFY,
+ FUNCTION_NPP_POSTURL,
+ FUNCTION_NPP_POSTURLNOTIFY,
+ FUNCTION_NPP_NEWSTREAM,
+ FUNCTION_NPP_WRITEREADY,
+ FUNCTION_NPP_WRITE,
+ FUNCTION_NPP_DESTROYSTREAM,
+ FUNCTION_NPP_WRITE_RPC
+} TestFunction;
+
+typedef enum { AD_NONE, AD_BITMAP, AD_DXGI } AsyncDrawing;
+
+typedef enum {
+ ACTIVATION_STATE_UNKNOWN,
+ ACTIVATION_STATE_ACTIVATED,
+ ACTIVATION_STATE_DEACTIVATED
+} ActivationState;
+
+typedef struct FunctionTable {
+ TestFunction funcId;
+ const char* funcName;
+} FunctionTable;
+
+typedef enum { POSTMODE_FRAME, POSTMODE_STREAM } PostMode;
+
+typedef struct TestNPObject : NPObject {
+ NPP npp;
+ DrawMode drawMode;
+ uint32_t drawColor; // 0xAARRGGBB
+} TestNPObject;
+
+typedef struct _PlatformData PlatformData;
+
+typedef struct InstanceData {
+ NPP npp;
+ NPWindow window;
+ TestNPObject* scriptableObject;
+ PlatformData* platformData;
+ int32_t instanceCountWatchGeneration;
+ bool lastReportedPrivateModeState;
+ bool hasWidget;
+ bool npnNewStream;
+ bool throwOnNextInvoke;
+ bool runScriptOnPaint;
+ bool dontTouchElement;
+ uint32_t timerID[2];
+ bool timerTestResult;
+ bool invalidateDuringPaint;
+ bool slowPaint;
+ bool playingAudio;
+ bool audioMuted;
+ int32_t winX;
+ int32_t winY;
+ int32_t lastMouseX;
+ int32_t lastMouseY;
+ int32_t widthAtLastPaint;
+ int32_t paintCount;
+ int32_t writeCount;
+ int32_t writeReadyCount;
+ TestFunction testFunction;
+ TestFunction functionToFail;
+ NPError failureCode;
+ NPObject* callOnDestroy;
+ PostMode postMode;
+ std::string testUrl;
+ std::string frame;
+ std::string timerTestScriptCallback;
+ std::ostringstream err;
+ int32_t streamChunkSize;
+ int32_t streamBufSize;
+ void* streamBuf;
+ bool crashOnDestroy;
+ bool cleanupWidget;
+ ActivationState topLevelWindowActivationState;
+ int32_t topLevelWindowActivationEventCount;
+ ActivationState focusState;
+ int32_t focusEventCount;
+ int32_t eventModel;
+ bool closeStream;
+ std::string lastKeyText;
+ bool wantsAllStreams;
+ int32_t mouseUpEventCount;
+ int32_t bugMode;
+ AsyncDrawing asyncDrawing;
+ NPAsyncSurface* frontBuffer;
+ NPAsyncSurface* backBuffer;
+ std::string lastComposition;
+ void* placeholderWnd;
+ double cssZoomFactor;
+} InstanceData;
+
+void notifyDidPaint(InstanceData* instanceData);
+
+#if defined(XP_WIN)
+bool setupDxgiSurfaces(NPP npp, InstanceData* instanceData);
+void drawDxgiBitmapColor(InstanceData* instanceData);
+#endif
+
+#endif // nptest_h_
diff --git a/dom/plugins/test/testplugin/nptest.rc b/dom/plugins/test/testplugin/nptest.rc
new file mode 100644
index 0000000000..948fb846ef
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest.rc
@@ -0,0 +1,42 @@
+#include<winver.h>
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Plug-in for testing purposes.\x2122 (\x0939\x093f\x0928\x094d\x0926\x0940 \x4e2d\x6587 \x0627\x0644\x0639\x0631\x0628\x064a\x0629)"
+ VALUE "FileExtents", "tst"
+ VALUE "FileOpenName", L"Test \x2122 mimetype"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "nptest"
+ VALUE "MIMEType", "application/x-test"
+ VALUE "OriginalFilename", "nptest.dll"
+ VALUE "ProductName", "Test Plug-in"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
diff --git a/dom/plugins/test/testplugin/nptest_droid.cpp b/dom/plugins/test/testplugin/nptest_droid.cpp
new file mode 100644
index 0000000000..733f18befe
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_droid.cpp
@@ -0,0 +1,80 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (c) 2010, Mozilla Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT OWNER OR 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.
+ *
+ * Contributor(s):
+ * Brad Lassey <blassey@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+#include "nptest_platform.h"
+#include "npapi.h"
+
+struct _PlatformData {};
+
+bool pluginSupportsWindowMode() { return false; }
+
+bool pluginSupportsWindowlessMode() { return true; }
+
+NPError pluginInstanceInit(InstanceData* instanceData) {
+ printf("NPERR_INCOMPATIBLE_VERSION_ERROR\n");
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+}
+
+void pluginInstanceShutdown(InstanceData* instanceData) {
+ NPN_MemFree(instanceData->platformData);
+ instanceData->platformData = 0;
+}
+
+void pluginDoSetWindow(InstanceData* instanceData, NPWindow* newWindow) {
+ instanceData->window = *newWindow;
+}
+
+void pluginWidgetInit(InstanceData* instanceData, void* oldWindow) {
+ // XXX nothing here yet since we don't support windowed plugins
+}
+
+int16_t pluginHandleEvent(InstanceData* instanceData, void* event) { return 0; }
+
+int32_t pluginGetEdge(InstanceData* instanceData, RectEdge edge) {
+ // XXX nothing here yet since we don't support windowed plugins
+ return NPTEST_INT32_ERROR;
+}
+
+int32_t pluginGetClipRegionRectCount(InstanceData* instanceData) {
+ // XXX nothing here yet since we don't support windowed plugins
+ return NPTEST_INT32_ERROR;
+}
+
+int32_t pluginGetClipRegionRectEdge(InstanceData* instanceData,
+ int32_t rectIndex, RectEdge edge) {
+ // XXX nothing here yet since we don't support windowed plugins
+ return NPTEST_INT32_ERROR;
+}
+
+void pluginDoInternalConsistencyCheck(InstanceData* instanceData,
+ std::string& error) {}
diff --git a/dom/plugins/test/testplugin/nptest_gtk2.cpp b/dom/plugins/test/testplugin/nptest_gtk2.cpp
new file mode 100644
index 0000000000..c7779b1d29
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_gtk2.cpp
@@ -0,0 +1,707 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT OWNER OR 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.
+ *
+ * Contributor(s):
+ * Josh Aas <josh@mozilla.com>
+ * Michael Ventnor <mventnor@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nptest_platform.h"
+#include "npapi.h"
+#include <pthread.h>
+#include <gdk/gdk.h>
+#ifdef MOZ_X11
+# include <gdk/gdkx.h>
+# include <X11/extensions/shape.h>
+#endif
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <unistd.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/IntentionalCrash.h"
+
+struct _PlatformData {
+#ifdef MOZ_X11
+ Display* display;
+ Visual* visual;
+ Colormap colormap;
+#endif
+ GtkWidget* plug;
+};
+
+bool pluginSupportsWindowMode() { return false; }
+
+bool pluginSupportsWindowlessMode() { return true; }
+
+NPError pluginInstanceInit(InstanceData* instanceData) {
+#ifdef MOZ_X11
+ instanceData->platformData =
+ static_cast<PlatformData*>(NPN_MemAlloc(sizeof(PlatformData)));
+ if (!instanceData->platformData) return NPERR_OUT_OF_MEMORY_ERROR;
+
+ instanceData->platformData->display = nullptr;
+ instanceData->platformData->visual = nullptr;
+ instanceData->platformData->colormap = X11None;
+ instanceData->platformData->plug = nullptr;
+
+ return NPERR_NO_ERROR;
+#else
+ // we only support X11 here, since thats what the plugin system uses
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+#endif
+}
+
+void pluginInstanceShutdown(InstanceData* instanceData) {
+ if (instanceData->hasWidget) {
+ Window window = reinterpret_cast<XID>(instanceData->window.window);
+
+ if (window != X11None) {
+ // This window XID should still be valid.
+ // See bug 429604 and bug 454756.
+ XWindowAttributes attributes;
+ if (!XGetWindowAttributes(instanceData->platformData->display, window,
+ &attributes))
+ g_error("XGetWindowAttributes failed at plugin instance shutdown");
+ }
+ }
+
+ GtkWidget* plug = instanceData->platformData->plug;
+ if (plug) {
+ instanceData->platformData->plug = 0;
+ if (instanceData->cleanupWidget) {
+ // Default/tidy behavior
+ gtk_widget_destroy(plug);
+ } else {
+ // Flash Player style: let the GtkPlug destroy itself on disconnect.
+ g_signal_handlers_disconnect_matched(plug, G_SIGNAL_MATCH_DATA, 0, 0,
+ nullptr, nullptr, instanceData);
+ }
+ }
+
+ NPN_MemFree(instanceData->platformData);
+ instanceData->platformData = 0;
+}
+
+static void SetCairoRGBA(cairo_t* cairoWindow, uint32_t rgba) {
+ float b = (rgba & 0xFF) / 255.0;
+ float g = ((rgba & 0xFF00) >> 8) / 255.0;
+ float r = ((rgba & 0xFF0000) >> 16) / 255.0;
+ float a = ((rgba & 0xFF000000) >> 24) / 255.0;
+
+ cairo_set_source_rgba(cairoWindow, r, g, b, a);
+}
+
+static void pluginDrawSolid(InstanceData* instanceData, GdkDrawable* gdkWindow,
+ int x, int y, int width, int height) {
+ cairo_t* cairoWindow = gdk_cairo_create(gdkWindow);
+
+ MOZ_RELEASE_ASSERT(!instanceData->hasWidget);
+
+ NPRect* clip = &instanceData->window.clipRect;
+ cairo_rectangle(cairoWindow, clip->left, clip->top, clip->right - clip->left,
+ clip->bottom - clip->top);
+ cairo_clip(cairoWindow);
+
+ GdkRectangle windowRect = {x, y, width, height};
+ gdk_cairo_rectangle(cairoWindow, &windowRect);
+ SetCairoRGBA(cairoWindow, instanceData->scriptableObject->drawColor);
+
+ cairo_fill(cairoWindow);
+ cairo_destroy(cairoWindow);
+}
+
+static void pluginDrawWindow(InstanceData* instanceData, GdkDrawable* gdkWindow,
+ const GdkRectangle& invalidRect) {
+ NPWindow& window = instanceData->window;
+ MOZ_RELEASE_ASSERT(!instanceData->hasWidget);
+
+ int x = window.x;
+ int y = window.y;
+ int width = window.width;
+ int height = window.height;
+
+ notifyDidPaint(instanceData);
+
+ if (instanceData->scriptableObject->drawMode == DM_SOLID_COLOR) {
+ // drawing a solid color for reftests
+ pluginDrawSolid(instanceData, gdkWindow, invalidRect.x, invalidRect.y,
+ invalidRect.width, invalidRect.height);
+ return;
+ }
+
+ NPP npp = instanceData->npp;
+ if (!npp) return;
+
+ const char* uaString = NPN_UserAgent(npp);
+ if (!uaString) return;
+
+ GdkGC* gdkContext = gdk_gc_new(gdkWindow);
+ if (!gdkContext) return;
+
+ NPRect* clip = &window.clipRect;
+ GdkRectangle gdkClip = {clip->left, clip->top, clip->right - clip->left,
+ clip->bottom - clip->top};
+ gdk_gc_set_clip_rectangle(gdkContext, &gdkClip);
+
+ // draw a grey background for the plugin frame
+ GdkColor grey;
+ grey.red = grey.blue = grey.green = 32767;
+ gdk_gc_set_rgb_fg_color(gdkContext, &grey);
+ gdk_draw_rectangle(gdkWindow, gdkContext, TRUE, x, y, width, height);
+
+ // draw a 3-pixel-thick black frame around the plugin
+ GdkColor black;
+ black.red = black.green = black.blue = 0;
+ gdk_gc_set_rgb_fg_color(gdkContext, &black);
+ gdk_gc_set_line_attributes(gdkContext, 3, GDK_LINE_SOLID, GDK_CAP_NOT_LAST,
+ GDK_JOIN_MITER);
+ gdk_draw_rectangle(gdkWindow, gdkContext, FALSE, x + 1, y + 1, width - 3,
+ height - 3);
+
+ // paint the UA string
+ PangoContext* pangoContext = gdk_pango_context_get();
+ PangoLayout* pangoTextLayout = pango_layout_new(pangoContext);
+ pango_layout_set_width(pangoTextLayout, (width - 10) * PANGO_SCALE);
+ pango_layout_set_text(pangoTextLayout, uaString, -1);
+ gdk_draw_layout(gdkWindow, gdkContext, x + 5, y + 5, pangoTextLayout);
+ g_object_unref(pangoTextLayout);
+
+ g_object_unref(gdkContext);
+}
+
+static gboolean ExposeWidget(GtkWidget* widget, GdkEventExpose* event,
+ gpointer user_data) {
+ InstanceData* instanceData = static_cast<InstanceData*>(user_data);
+ pluginDrawWindow(instanceData, event->window, event->area);
+ return TRUE;
+}
+
+static gboolean MotionEvent(GtkWidget* widget, GdkEventMotion* event,
+ gpointer user_data) {
+ InstanceData* instanceData = static_cast<InstanceData*>(user_data);
+ instanceData->lastMouseX = event->x;
+ instanceData->lastMouseY = event->y;
+ return TRUE;
+}
+
+static gboolean ButtonEvent(GtkWidget* widget, GdkEventButton* event,
+ gpointer user_data) {
+ InstanceData* instanceData = static_cast<InstanceData*>(user_data);
+ instanceData->lastMouseX = event->x;
+ instanceData->lastMouseY = event->y;
+ if (event->type == GDK_BUTTON_RELEASE) {
+ instanceData->mouseUpEventCount++;
+ }
+ return TRUE;
+}
+
+static gboolean DeleteWidget(GtkWidget* widget, GdkEvent* event,
+ gpointer user_data) {
+ InstanceData* instanceData = static_cast<InstanceData*>(user_data);
+ // Some plugins do not expect the plug to be removed from the socket before
+ // the plugin instance is destroyed. e.g. bug 485125
+ if (instanceData->platformData->plug) g_error("plug removed"); // this aborts
+
+ return FALSE;
+}
+
+void pluginDoSetWindow(InstanceData* instanceData, NPWindow* newWindow) {
+ instanceData->window = *newWindow;
+
+#ifdef MOZ_X11
+ NPSetWindowCallbackStruct* ws_info =
+ static_cast<NPSetWindowCallbackStruct*>(newWindow->ws_info);
+ instanceData->platformData->display = ws_info->display;
+ instanceData->platformData->visual = ws_info->visual;
+ instanceData->platformData->colormap = ws_info->colormap;
+#endif
+}
+
+void pluginWidgetInit(InstanceData* instanceData, void* oldWindow) {
+#ifdef MOZ_X11
+ GtkWidget* oldPlug = instanceData->platformData->plug;
+ if (oldPlug) {
+ instanceData->platformData->plug = 0;
+ gtk_widget_destroy(oldPlug);
+ }
+
+ GdkNativeWindow nativeWinId =
+ reinterpret_cast<XID>(instanceData->window.window);
+
+ /* create a GtkPlug container */
+ GtkWidget* plug = gtk_plug_new(nativeWinId);
+
+ // Test for bugs 539138 and 561308
+ if (!plug->window) g_error("Plug has no window"); // aborts
+
+ /* make sure the widget is capable of receiving focus */
+ GTK_WIDGET_SET_FLAGS(GTK_WIDGET(plug), GTK_CAN_FOCUS);
+
+ /* all the events that our widget wants to receive */
+ gtk_widget_add_events(plug, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK);
+ g_signal_connect(plug, "expose-event", G_CALLBACK(ExposeWidget),
+ instanceData);
+ g_signal_connect(plug, "motion_notify_event", G_CALLBACK(MotionEvent),
+ instanceData);
+ g_signal_connect(plug, "button_press_event", G_CALLBACK(ButtonEvent),
+ instanceData);
+ g_signal_connect(plug, "button_release_event", G_CALLBACK(ButtonEvent),
+ instanceData);
+ g_signal_connect(plug, "delete-event", G_CALLBACK(DeleteWidget),
+ instanceData);
+ gtk_widget_show(plug);
+
+ instanceData->platformData->plug = plug;
+#endif
+}
+
+int16_t pluginHandleEvent(InstanceData* instanceData, void* event) {
+#ifdef MOZ_X11
+ XEvent* nsEvent = (XEvent*)event;
+
+ switch (nsEvent->type) {
+ case GraphicsExpose: {
+ const XGraphicsExposeEvent& expose = nsEvent->xgraphicsexpose;
+ NPWindow& window = instanceData->window;
+ window.window = (void*)(expose.drawable);
+
+ GdkNativeWindow nativeWinId = reinterpret_cast<XID>(window.window);
+
+ GdkDisplay* gdkDisplay = gdk_x11_lookup_xdisplay(expose.display);
+ if (!gdkDisplay) {
+ g_warning("Display not opened by GDK");
+ return 0;
+ }
+ // gdk_pixmap_foreign_new() doesn't check whether a GdkPixmap already
+ // exists, so check first.
+ // https://bugzilla.gnome.org/show_bug.cgi?id=590690
+ GdkPixmap* gdkDrawable =
+ GDK_DRAWABLE(gdk_pixmap_lookup_for_display(gdkDisplay, nativeWinId));
+ // If there is no existing GdkPixmap or it doesn't have a colormap then
+ // create our own.
+ if (gdkDrawable) {
+ GdkColormap* gdkColormap = gdk_drawable_get_colormap(gdkDrawable);
+ if (!gdkColormap) {
+ g_warning("No GdkColormap on GdkPixmap");
+ return 0;
+ }
+ if (gdk_x11_colormap_get_xcolormap(gdkColormap) !=
+ instanceData->platformData->colormap) {
+ g_warning("wrong Colormap");
+ return 0;
+ }
+ if (gdk_x11_visual_get_xvisual(gdk_colormap_get_visual(gdkColormap)) !=
+ instanceData->platformData->visual) {
+ g_warning("wrong Visual");
+ return 0;
+ }
+ g_object_ref(gdkDrawable);
+ } else {
+ gdkDrawable = GDK_DRAWABLE(
+ gdk_pixmap_foreign_new_for_display(gdkDisplay, nativeWinId));
+ VisualID visualID = instanceData->platformData->visual->visualid;
+ GdkVisual* gdkVisual = gdk_x11_screen_lookup_visual(
+ gdk_drawable_get_screen(gdkDrawable), visualID);
+ GdkColormap* gdkColormap = gdk_x11_colormap_foreign_new(
+ gdkVisual, instanceData->platformData->colormap);
+ gdk_drawable_set_colormap(gdkDrawable, gdkColormap);
+ g_object_unref(gdkColormap);
+ }
+
+ const NPRect& clip = window.clipRect;
+ if (expose.x < clip.left || expose.y < clip.top ||
+ expose.x + expose.width > clip.right ||
+ expose.y + expose.height > clip.bottom) {
+ g_warning(
+ "expose rectangle (x=%d,y=%d,w=%d,h=%d) not in clip rectangle "
+ "(l=%d,t=%d,r=%d,b=%d)",
+ expose.x, expose.y, expose.width, expose.height, clip.left,
+ clip.top, clip.right, clip.bottom);
+ return 0;
+ }
+ if (expose.x < window.x || expose.y < window.y ||
+ expose.x + expose.width > window.x + int32_t(window.width) ||
+ expose.y + expose.height > window.y + int32_t(window.height)) {
+ g_warning(
+ "expose rectangle (x=%d,y=%d,w=%d,h=%d) not in plugin rectangle "
+ "(x=%d,y=%d,w=%d,h=%d)",
+ expose.x, expose.y, expose.width, expose.height, window.x, window.y,
+ window.width, window.height);
+ return 0;
+ }
+
+ GdkRectangle invalidRect = {expose.x, expose.y, expose.width,
+ expose.height};
+ pluginDrawWindow(instanceData, gdkDrawable, invalidRect);
+ g_object_unref(gdkDrawable);
+ break;
+ }
+ case MotionNotify: {
+ XMotionEvent* motion = &nsEvent->xmotion;
+ instanceData->lastMouseX = motion->x;
+ instanceData->lastMouseY = motion->y;
+ break;
+ }
+ case ButtonPress:
+ case ButtonRelease: {
+ XButtonEvent* button = &nsEvent->xbutton;
+ instanceData->lastMouseX = button->x;
+ instanceData->lastMouseY = button->y;
+ if (nsEvent->type == ButtonRelease) {
+ instanceData->mouseUpEventCount++;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+
+ return 0;
+}
+
+int32_t pluginGetEdge(InstanceData* instanceData, RectEdge edge) {
+ MOZ_RELEASE_ASSERT(!instanceData->hasWidget);
+ if (!instanceData->hasWidget) return NPTEST_INT32_ERROR;
+
+ GtkWidget* plug = instanceData->platformData->plug;
+ if (!plug) return NPTEST_INT32_ERROR;
+ GdkWindow* plugWnd = plug->window;
+ if (!plugWnd) return NPTEST_INT32_ERROR;
+
+ GdkWindow* toplevelGdk = 0;
+#ifdef MOZ_X11
+ Window toplevel = 0;
+ NPN_GetValue(instanceData->npp, NPNVnetscapeWindow, &toplevel);
+ if (!toplevel) return NPTEST_INT32_ERROR;
+ toplevelGdk = gdk_window_foreign_new(toplevel);
+#endif
+ if (!toplevelGdk) return NPTEST_INT32_ERROR;
+
+ GdkRectangle toplevelFrameExtents;
+ gdk_window_get_frame_extents(toplevelGdk, &toplevelFrameExtents);
+ g_object_unref(toplevelGdk);
+
+ gint pluginWidth, pluginHeight;
+ gdk_drawable_get_size(GDK_DRAWABLE(plugWnd), &pluginWidth, &pluginHeight);
+ gint pluginOriginX, pluginOriginY;
+ gdk_window_get_origin(plugWnd, &pluginOriginX, &pluginOriginY);
+ gint pluginX = pluginOriginX - toplevelFrameExtents.x;
+ gint pluginY = pluginOriginY - toplevelFrameExtents.y;
+
+ switch (edge) {
+ case EDGE_LEFT:
+ return pluginX;
+ case EDGE_TOP:
+ return pluginY;
+ case EDGE_RIGHT:
+ return pluginX + pluginWidth;
+ case EDGE_BOTTOM:
+ return pluginY + pluginHeight;
+ }
+ MOZ_CRASH("Unexpected RectEdge?!");
+}
+
+#ifdef MOZ_X11
+static void intersectWithShapeRects(Display* display, Window window, int kind,
+ GdkRegion* region) {
+ int count = -1, order;
+ XRectangle* shapeRects =
+ XShapeGetRectangles(display, window, kind, &count, &order);
+ // The documentation says that shapeRects will be nullptr when the
+ // extension is not supported. Unfortunately XShapeGetRectangles
+ // also returns nullptr when the region is empty, so we can't treat
+ // nullptr as failure. I hope this way is OK.
+ if (count < 0) return;
+
+ GdkRegion* shapeRegion = gdk_region_new();
+ if (!shapeRegion) {
+ XFree(shapeRects);
+ return;
+ }
+
+ for (int i = 0; i < count; ++i) {
+ XRectangle* r = &shapeRects[i];
+ GdkRectangle rect = {r->x, r->y, r->width, r->height};
+ gdk_region_union_with_rect(shapeRegion, &rect);
+ }
+ XFree(shapeRects);
+
+ gdk_region_intersect(region, shapeRegion);
+ gdk_region_destroy(shapeRegion);
+}
+#endif
+
+static GdkRegion* computeClipRegion(InstanceData* instanceData) {
+ MOZ_RELEASE_ASSERT(!instanceData->hasWidget);
+ if (!instanceData->hasWidget) return 0;
+
+ GtkWidget* plug = instanceData->platformData->plug;
+ if (!plug) return 0;
+ GdkWindow* plugWnd = plug->window;
+ if (!plugWnd) return 0;
+
+ gint plugWidth, plugHeight;
+ gdk_drawable_get_size(GDK_DRAWABLE(plugWnd), &plugWidth, &plugHeight);
+ GdkRectangle pluginRect = {0, 0, plugWidth, plugHeight};
+ GdkRegion* region = gdk_region_rectangle(&pluginRect);
+ if (!region) return 0;
+
+ int pluginX = 0, pluginY = 0;
+
+#ifdef MOZ_X11
+ Display* display = GDK_WINDOW_XDISPLAY(plugWnd);
+ Window window = GDK_WINDOW_XWINDOW(plugWnd);
+
+ Window toplevel = 0;
+ NPN_GetValue(instanceData->npp, NPNVnetscapeWindow, &toplevel);
+ if (!toplevel) return 0;
+
+ for (;;) {
+ Window root;
+ int x, y;
+ unsigned int width, height, border_width, depth;
+ if (!XGetGeometry(display, window, &root, &x, &y, &width, &height,
+ &border_width, &depth)) {
+ gdk_region_destroy(region);
+ return 0;
+ }
+
+ GdkRectangle windowRect = {0, 0, static_cast<gint>(width),
+ static_cast<gint>(height)};
+ GdkRegion* windowRgn = gdk_region_rectangle(&windowRect);
+ if (!windowRgn) {
+ gdk_region_destroy(region);
+ return 0;
+ }
+ intersectWithShapeRects(display, window, ShapeBounding, windowRgn);
+ intersectWithShapeRects(display, window, ShapeClip, windowRgn);
+ gdk_region_offset(windowRgn, -pluginX, -pluginY);
+ gdk_region_intersect(region, windowRgn);
+ gdk_region_destroy(windowRgn);
+
+ // Stop now if we've reached the toplevel. Stopping here means
+ // clipping performed by the toplevel window is taken into account.
+ if (window == toplevel) break;
+
+ Window parent;
+ Window* children;
+ unsigned int nchildren;
+ if (!XQueryTree(display, window, &root, &parent, &children, &nchildren)) {
+ gdk_region_destroy(region);
+ return 0;
+ }
+ XFree(children);
+
+ pluginX += x;
+ pluginY += y;
+
+ window = parent;
+ }
+#endif
+ // pluginX and pluginY are now relative to the toplevel. Make them
+ // relative to the window frame top-left.
+ GdkWindow* toplevelGdk = gdk_window_foreign_new(window);
+ if (!toplevelGdk) return 0;
+ GdkRectangle toplevelFrameExtents;
+ gdk_window_get_frame_extents(toplevelGdk, &toplevelFrameExtents);
+ gint toplevelOriginX, toplevelOriginY;
+ gdk_window_get_origin(toplevelGdk, &toplevelOriginX, &toplevelOriginY);
+ g_object_unref(toplevelGdk);
+
+ pluginX += toplevelOriginX - toplevelFrameExtents.x;
+ pluginY += toplevelOriginY - toplevelFrameExtents.y;
+
+ gdk_region_offset(region, pluginX, pluginY);
+ return region;
+}
+
+int32_t pluginGetClipRegionRectCount(InstanceData* instanceData) {
+ GdkRegion* region = computeClipRegion(instanceData);
+ if (!region) return NPTEST_INT32_ERROR;
+
+ GdkRectangle* rects;
+ gint nrects;
+ gdk_region_get_rectangles(region, &rects, &nrects);
+ gdk_region_destroy(region);
+ g_free(rects);
+ return nrects;
+}
+
+int32_t pluginGetClipRegionRectEdge(InstanceData* instanceData,
+ int32_t rectIndex, RectEdge edge) {
+ GdkRegion* region = computeClipRegion(instanceData);
+ if (!region) return NPTEST_INT32_ERROR;
+
+ GdkRectangle* rects;
+ gint nrects;
+ gdk_region_get_rectangles(region, &rects, &nrects);
+ gdk_region_destroy(region);
+ if (rectIndex >= nrects) {
+ g_free(rects);
+ return NPTEST_INT32_ERROR;
+ }
+
+ GdkRectangle rect = rects[rectIndex];
+ g_free(rects);
+
+ switch (edge) {
+ case EDGE_LEFT:
+ return rect.x;
+ case EDGE_TOP:
+ return rect.y;
+ case EDGE_RIGHT:
+ return rect.x + rect.width;
+ case EDGE_BOTTOM:
+ return rect.y + rect.height;
+ }
+ return NPTEST_INT32_ERROR;
+}
+
+void pluginDoInternalConsistencyCheck(InstanceData* instanceData,
+ std::string& error) {}
+
+std::string pluginGetClipboardText(InstanceData* instanceData) {
+ GtkClipboard* cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ // XXX this is a BAD WAY to interact with GtkClipboard. We use this
+ // deprecated interface only to test nested event loop handling.
+ gchar* text = gtk_clipboard_wait_for_text(cb);
+ std::string retText = text ? text : "";
+
+ g_free(text);
+
+ return retText;
+}
+
+//-----------------------------------------------------------------------------
+// NB: this test is quite gross in that it's not only
+// nondeterministic, but dependent on the guts of the nested glib
+// event loop handling code in PluginModule. We first sleep long
+// enough to make sure that the "detection timer" will be pending when
+// we enter the nested glib loop, then similarly for the "process browser
+// events" timer. Then we "schedule" the crasher thread to run at about the
+// same time we expect that the PluginModule "process browser events" task
+// will run. If all goes well, the plugin process will crash and generate the
+// XPCOM "plugin crashed" task, and the browser will run that task while still
+// in the "process some events" loop.
+
+static void* CrasherThread(void* data) {
+ // Give the parent thread a chance to send the message.
+ usleep(200);
+
+ // Exit (without running atexit hooks) rather than crashing with a signal
+ // so as to make timing more reliable. The process terminates immediately
+ // rather than waiting for a thread in the parent process to attach and
+ // generate a minidump.
+ _exit(1);
+
+ // not reached
+ return (nullptr);
+}
+
+bool pluginCrashInNestedLoop(InstanceData* instanceData) {
+ // wait at least long enough for nested loop detector task to be pending ...
+ sleep(1);
+
+ // Run the nested loop detector by processing all events that are waiting.
+ bool found_event = false;
+ while (g_main_context_iteration(nullptr, FALSE)) {
+ found_event = true;
+ }
+ if (!found_event) {
+ g_warning("DetectNestedEventLoop did not fire");
+ return true; // trigger a test failure
+ }
+
+ // wait at least long enough for the "process browser events" task to be
+ // pending ...
+ sleep(1);
+
+ // we'll be crashing soon, note that fact now to avoid messing with
+ // timing too much
+ mozilla::NoteIntentionalCrash("plugin");
+
+ // schedule the crasher thread ...
+ pthread_t crasherThread;
+ if (0 != pthread_create(&crasherThread, nullptr, CrasherThread, nullptr)) {
+ g_warning("Failed to create thread");
+ return true; // trigger a test failure
+ }
+
+ // .. and hope it crashes at about the same time as the "process browser
+ // events" task (that should run in this loop) is being processed in the
+ // parent.
+ found_event = false;
+ while (g_main_context_iteration(nullptr, FALSE)) {
+ found_event = true;
+ }
+ if (found_event) {
+ g_warning("Should have crashed in ProcessBrowserEvents");
+ } else {
+ g_warning("ProcessBrowserEvents did not fire");
+ }
+
+ // if we get here without crashing, then we'll trigger a test failure
+ return true;
+}
+
+bool pluginTriggerXError(InstanceData* instanceData) {
+ mozilla::NoteIntentionalCrash("plugin");
+ int num_prop_return;
+ // Window parameter is None to generate a fatal error, and this function
+ // should not return.
+ XListProperties(GDK_DISPLAY(), X11None, &num_prop_return);
+
+ // if we get here without crashing, then we'll trigger a test failure
+ return true;
+}
+
+static int SleepThenDie(Display* display) {
+ mozilla::NoteIntentionalCrash("plugin");
+ fprintf(stderr, "[testplugin:%d] SleepThenDie: sleeping\n", getpid());
+ sleep(1);
+
+ fprintf(stderr, "[testplugin:%d] SleepThenDie: dying\n", getpid());
+ _exit(1);
+}
+
+bool pluginDestroySharedGfxStuff(InstanceData* instanceData) {
+ // Closing the X socket results in the gdk error handler being
+ // invoked, which exit()s us. We want to give the parent process a
+ // little while to do whatever it wanted to do, so steal the IO
+ // handler from gdk and set up our own that delays seppuku.
+ XSetIOErrorHandler(SleepThenDie);
+ close(ConnectionNumber(GDK_DISPLAY()));
+ return true;
+}
diff --git a/dom/plugins/test/testplugin/nptest_macosx.mm b/dom/plugins/test/testplugin/nptest_macosx.mm
new file mode 100644
index 0000000000..191b8f5c6f
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_macosx.mm
@@ -0,0 +1,275 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT OWNER OR 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.
+ *
+ * Contributor(s):
+ * Josh Aas <josh@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nptest_platform.h"
+#include "nsAlgorithm.h"
+#include <CoreServices/CoreServices.h>
+#include <algorithm>
+
+bool pluginSupportsWindowMode() { return false; }
+
+bool pluginSupportsWindowlessMode() { return true; }
+
+NPError pluginInstanceInit(InstanceData* instanceData) {
+ NPP npp = instanceData->npp;
+
+ NPBool supportsCoreGraphics = false;
+ if ((NPN_GetValue(npp, NPNVsupportsCoreGraphicsBool, &supportsCoreGraphics) == NPERR_NO_ERROR) &&
+ supportsCoreGraphics) {
+ NPN_SetValue(npp, NPPVpluginDrawingModel, (void*)NPDrawingModelCoreGraphics);
+ } else {
+ printf("CoreGraphics drawing model not supported, can't create a plugin instance.\n");
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+ }
+
+ NPBool supportsCocoaEvents = false;
+ if ((NPN_GetValue(npp, NPNVsupportsCocoaBool, &supportsCocoaEvents) == NPERR_NO_ERROR) &&
+ supportsCocoaEvents) {
+ NPN_SetValue(npp, NPPVpluginEventModel, (void*)NPEventModelCocoa);
+ instanceData->eventModel = NPEventModelCocoa;
+ } else {
+ printf("Cocoa event model not supported, can't create a plugin instance.\n");
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+void pluginInstanceShutdown(InstanceData* instanceData) {}
+
+static bool RectEquals(const NPRect& r1, const NPRect& r2) {
+ return r1.left == r2.left && r1.top == r2.top && r1.right == r2.right && r1.bottom == r2.bottom;
+}
+
+void pluginDoSetWindow(InstanceData* instanceData, NPWindow* newWindow) {
+ // Ugh. Due to a terrible Gecko bug, we have to ignore position changes
+ // when the clip rect doesn't change; the position can be wrong
+ // when set by a path other than nsPluginFrame::FixUpPluginWindow.
+ int32_t oldX = instanceData->window.x;
+ int32_t oldY = instanceData->window.y;
+ bool clipChanged = !RectEquals(instanceData->window.clipRect, newWindow->clipRect);
+ instanceData->window = *newWindow;
+ if (!clipChanged) {
+ instanceData->window.x = oldX;
+ instanceData->window.y = oldY;
+ }
+}
+
+void pluginWidgetInit(InstanceData* instanceData, void* oldWindow) {
+ // Should never be called since we don't support window mode
+}
+
+static void GetColorsFromRGBA(uint32_t rgba, float* r, float* g, float* b, float* a) {
+ *b = (rgba & 0xFF) / 255.0;
+ *g = ((rgba & 0xFF00) >> 8) / 255.0;
+ *r = ((rgba & 0xFF0000) >> 16) / 255.0;
+ *a = ((rgba & 0xFF000000) >> 24) / 255.0;
+}
+
+static void pluginDraw(InstanceData* instanceData, NPCocoaEvent* event) {
+ if (!instanceData) return;
+
+ notifyDidPaint(instanceData);
+
+ NPP npp = instanceData->npp;
+ if (!npp) return;
+
+ const char* uaString = NPN_UserAgent(npp);
+ if (!uaString) return;
+
+ NPWindow window = instanceData->window;
+
+ CGContextRef cgContext = event->data.draw.context;
+
+ float windowWidth = window.width;
+ float windowHeight = window.height;
+
+ switch (instanceData->scriptableObject->drawMode) {
+ case DM_DEFAULT: {
+ CFStringRef uaCFString =
+ CFStringCreateWithCString(kCFAllocatorDefault, uaString, kCFStringEncodingASCII);
+ // save the cgcontext gstate
+ CGContextSaveGState(cgContext);
+
+ // we get a flipped context
+ CGContextTranslateCTM(cgContext, 0.0, windowHeight);
+ CGContextScaleCTM(cgContext, 1.0, -1.0);
+
+ // draw a gray background for the plugin
+ CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight));
+ CGContextSetGrayFillColor(cgContext, 0.5, 1.0);
+ CGContextDrawPath(cgContext, kCGPathFill);
+
+ // draw a black frame around the plugin
+ CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight));
+ CGContextSetGrayStrokeColor(cgContext, 0.0, 1.0);
+ CGContextSetLineWidth(cgContext, 6.0);
+ CGContextStrokePath(cgContext);
+
+ // draw the UA string using Core Text
+ CGContextSetTextMatrix(cgContext, CGAffineTransformIdentity);
+
+ // Initialize a rectangular path.
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGRect bounds = CGRectMake(10.0, 10.0, std::max(0.0, windowWidth - 20.0),
+ std::max(0.0, windowHeight - 20.0));
+ CGPathAddRect(path, NULL, bounds);
+
+ // Initialize an attributed string.
+ CFMutableAttributedStringRef attrString =
+ CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
+ CFAttributedStringReplaceString(attrString, CFRangeMake(0, 0), uaCFString);
+
+ // Create a color and add it as an attribute to the string.
+ CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
+ CGFloat components[] = {0.0, 0.0, 0.0, 1.0};
+ CGColorRef red = CGColorCreate(rgbColorSpace, components);
+ CGColorSpaceRelease(rgbColorSpace);
+ CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 50),
+ kCTForegroundColorAttributeName, red);
+
+ // Create the framesetter with the attributed string.
+ CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
+ CFRelease(attrString);
+
+ // Create the frame and draw it into the graphics context
+ CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
+ CFRelease(framesetter);
+ if (frame) {
+ CTFrameDraw(frame, cgContext);
+ CFRelease(frame);
+ }
+
+ // restore the cgcontext gstate
+ CGContextRestoreGState(cgContext);
+ break;
+ }
+ case DM_SOLID_COLOR: {
+ // save the cgcontext gstate
+ CGContextSaveGState(cgContext);
+
+ // we get a flipped context
+ CGContextTranslateCTM(cgContext, 0.0, windowHeight);
+ CGContextScaleCTM(cgContext, 1.0, -1.0);
+
+ // draw a solid background for the plugin
+ CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight));
+
+ float r, g, b, a;
+ GetColorsFromRGBA(instanceData->scriptableObject->drawColor, &r, &g, &b, &a);
+ CGContextSetRGBFillColor(cgContext, r, g, b, a);
+ CGContextDrawPath(cgContext, kCGPathFill);
+
+ // restore the cgcontext gstate
+ CGContextRestoreGState(cgContext);
+ break;
+ }
+ }
+}
+
+int16_t pluginHandleEvent(InstanceData* instanceData, void* event) {
+ NPCocoaEvent* cocoaEvent = (NPCocoaEvent*)event;
+ if (!cocoaEvent) return kNPEventNotHandled;
+
+ switch (cocoaEvent->type) {
+ case NPCocoaEventDrawRect:
+ pluginDraw(instanceData, cocoaEvent);
+ break;
+ case NPCocoaEventMouseDown:
+ case NPCocoaEventMouseUp:
+ case NPCocoaEventMouseMoved:
+ case NPCocoaEventMouseDragged:
+ instanceData->lastMouseX = (int32_t)cocoaEvent->data.mouse.pluginX;
+ instanceData->lastMouseY = (int32_t)cocoaEvent->data.mouse.pluginY;
+ if (cocoaEvent->type == NPCocoaEventMouseUp) {
+ instanceData->mouseUpEventCount++;
+ }
+ break;
+ case NPCocoaEventWindowFocusChanged:
+ instanceData->topLevelWindowActivationState = cocoaEvent->data.focus.hasFocus
+ ? ACTIVATION_STATE_ACTIVATED
+ : ACTIVATION_STATE_DEACTIVATED;
+ instanceData->topLevelWindowActivationEventCount =
+ instanceData->topLevelWindowActivationEventCount + 1;
+ break;
+ case NPCocoaEventFocusChanged:
+ instanceData->focusState = cocoaEvent->data.focus.hasFocus ? ACTIVATION_STATE_ACTIVATED
+ : ACTIVATION_STATE_DEACTIVATED;
+ instanceData->focusEventCount = instanceData->focusEventCount + 1;
+ break;
+ default:
+ return kNPEventNotHandled;
+ }
+
+ return kNPEventHandled;
+}
+
+int32_t pluginGetEdge(InstanceData* instanceData, RectEdge edge) {
+ NPWindow* w = &instanceData->window;
+ switch (edge) {
+ case EDGE_LEFT:
+ return w->x;
+ case EDGE_TOP:
+ return w->y;
+ case EDGE_RIGHT:
+ return w->x + w->width;
+ case EDGE_BOTTOM:
+ return w->y + w->height;
+ }
+ MOZ_CRASH("Unexpected RectEdge?!");
+}
+
+int32_t pluginGetClipRegionRectCount(InstanceData* instanceData) { return 1; }
+
+int32_t pluginGetClipRegionRectEdge(InstanceData* instanceData, int32_t rectIndex, RectEdge edge) {
+ if (rectIndex != 0) return NPTEST_INT32_ERROR;
+
+ // We have to add the Cocoa titlebar height here since the clip rect
+ // is being returned relative to that
+ static const int COCOA_TITLEBAR_HEIGHT = 22;
+
+ NPWindow* w = &instanceData->window;
+ switch (edge) {
+ case EDGE_LEFT:
+ return w->clipRect.left;
+ case EDGE_TOP:
+ return w->clipRect.top + COCOA_TITLEBAR_HEIGHT;
+ case EDGE_RIGHT:
+ return w->clipRect.right;
+ case EDGE_BOTTOM:
+ return w->clipRect.bottom + COCOA_TITLEBAR_HEIGHT;
+ }
+ MOZ_CRASH("Unexpected RectEdge?!");
+}
+
+void pluginDoInternalConsistencyCheck(InstanceData* instanceData, std::string& error) {}
diff --git a/dom/plugins/test/testplugin/nptest_name.cpp b/dom/plugins/test/testplugin/nptest_name.cpp
new file mode 100644
index 0000000000..3fc6f1fb3c
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_name.cpp
@@ -0,0 +1,8 @@
+const char* sPluginName = "Test Plug-in";
+const char* sPluginDescription =
+ "Plug-in for testing purposes.\xE2\x84\xA2 "
+ "(\xe0\xa4\xb9\xe0\xa4\xbf\xe0\xa4\xa8\xe0\xa5\x8d\xe0\xa4\xa6\xe0\xa5\x80 "
+ "\xe4\xb8\xad\xe6\x96\x87 "
+ "\xd8\xa7\xd9\x84\xd8\xb9\xd8\xb1\xd8\xa8\xd9\x8a\xd8\xa9)";
+const char* sMimeDescription =
+ "application/x-test:tst:Test \xE2\x84\xA2 mimetype";
diff --git a/dom/plugins/test/testplugin/nptest_platform.h b/dom/plugins/test/testplugin/nptest_platform.h
new file mode 100644
index 0000000000..4b9584932d
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_platform.h
@@ -0,0 +1,155 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT OWNER OR 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.
+ *
+ * Contributor(s):
+ * Josh Aas <josh@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nptest_platform_h_
+#define nptest_platform_h_
+
+#include "nptest.h"
+
+/**
+ * Returns true if the plugin supports windowed mode
+ */
+bool pluginSupportsWindowMode();
+
+/**
+ * Returns true if the plugin supports windowless mode. At least one of
+ * "pluginSupportsWindowMode" and "pluginSupportsWindowlessMode" must
+ * return true.
+ */
+bool pluginSupportsWindowlessMode();
+
+/**
+ * Initialize the plugin instance. Returning an error here will cause the
+ * plugin instantiation to fail.
+ */
+NPError pluginInstanceInit(InstanceData* instanceData);
+
+/**
+ * Shutdown the plugin instance.
+ */
+void pluginInstanceShutdown(InstanceData* instanceData);
+
+/**
+ * Set the instanceData's window to newWindow.
+ */
+void pluginDoSetWindow(InstanceData* instanceData, NPWindow* newWindow);
+
+/**
+ * Initialize the window for a windowed plugin. oldWindow is the old
+ * native window value. This will never be called for windowless plugins.
+ */
+void pluginWidgetInit(InstanceData* instanceData, void* oldWindow);
+
+/**
+ * Handle an event for a windowless plugin. (Windowed plugins are
+ * responsible for listening for their own events.)
+ */
+int16_t pluginHandleEvent(InstanceData* instanceData, void* event);
+
+enum RectEdge { EDGE_LEFT = 0, EDGE_TOP = 1, EDGE_RIGHT = 2, EDGE_BOTTOM = 3 };
+
+enum { NPTEST_INT32_ERROR = 0x7FFFFFFF };
+
+/**
+ * Return the coordinate of the given edge of the plugin's area, relative
+ * to the top-left corner of the toplevel window containing the plugin,
+ * including window decorations. Only works for window-mode plugins
+ * and Mac plugins.
+ * Returns NPTEST_ERROR on error.
+ */
+int32_t pluginGetEdge(InstanceData* instanceData, RectEdge edge);
+
+/**
+ * Return the number of rectangles in the plugin's clip region. Only
+ * works for window-mode plugins and Mac plugins.
+ * Returns NPTEST_ERROR on error.
+ */
+int32_t pluginGetClipRegionRectCount(InstanceData* instanceData);
+
+/**
+ * Return the coordinate of the given edge of a rectangle in the plugin's
+ * clip region, relative to the top-left corner of the toplevel window
+ * containing the plugin, including window decorations. Only works for
+ * window-mode plugins and Mac plugins.
+ * Returns NPTEST_ERROR on error.
+ */
+int32_t pluginGetClipRegionRectEdge(InstanceData* instanceData,
+ int32_t rectIndex, RectEdge edge);
+
+/**
+ * Check that the platform-specific plugin state is internally consistent.
+ * Just return if everything is OK, otherwise append error messages
+ * to 'error' separated by \n.
+ */
+void pluginDoInternalConsistencyCheck(InstanceData* instanceData,
+ std::string& error);
+
+/**
+ * Get the current clipboard item as text. If the clipboard item
+ * isn't text, the returned value is undefined.
+ */
+std::string pluginGetClipboardText(InstanceData* instanceData);
+
+/**
+ * Crash while in a nested event loop. The goal is to catch the
+ * browser processing the XPCOM event generated from the plugin's
+ * crash while other plugin code is still on the stack.
+ * See https://bugzilla.mozilla.org/show_bug.cgi?id=550026.
+ */
+bool pluginCrashInNestedLoop(InstanceData* instanceData);
+
+/**
+ * Generate an X11 protocol error to terminate the plugin process.
+ */
+bool pluginTriggerXError(InstanceData* instanceData);
+
+/**
+ * Destroy gfx things that might be shared with the parent process
+ * when we're run out-of-process. It's not expected that this
+ * function will be called when the test plugin is loaded in-process,
+ * and bad things will happen if it is called.
+ *
+ * This call leaves the plugin subprocess in an undefined state. It
+ * must not be used after this call or weird things will happen.
+ */
+bool pluginDestroySharedGfxStuff(InstanceData* instanceData);
+
+/**
+ * Checks to see if the native widget is marked as visible. Works
+ * in e10s and non-e10s. Useful in testing e10s related compositor
+ * plugin window functionality. Supported on Windows.
+ */
+bool pluginNativeWidgetIsVisible(InstanceData* instanceData);
+
+#endif // nptest_platform_h_
diff --git a/dom/plugins/test/testplugin/nptest_utils.cpp b/dom/plugins/test/testplugin/nptest_utils.cpp
new file mode 100644
index 0000000000..39a3f4b7b7
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_utils.cpp
@@ -0,0 +1,100 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ *
+ * Contributor(s):
+ * Josh Aas <josh@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nptest_utils.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+NPUTF8* createCStringFromNPVariant(const NPVariant* variant) {
+ size_t length = NPVARIANT_TO_STRING(*variant).UTF8Length;
+ NPUTF8* result = (NPUTF8*)malloc(length + 1);
+ memcpy(result, NPVARIANT_TO_STRING(*variant).UTF8Characters, length);
+ result[length] = '\0';
+ return result;
+}
+
+NPIdentifier variantToIdentifier(NPVariant variant) {
+ if (NPVARIANT_IS_STRING(variant))
+ return stringVariantToIdentifier(variant);
+ else if (NPVARIANT_IS_INT32(variant))
+ return int32VariantToIdentifier(variant);
+ else if (NPVARIANT_IS_DOUBLE(variant))
+ return doubleVariantToIdentifier(variant);
+ return 0;
+}
+
+NPIdentifier stringVariantToIdentifier(NPVariant variant) {
+ assert(NPVARIANT_IS_STRING(variant));
+ NPUTF8* utf8String = createCStringFromNPVariant(&variant);
+ NPIdentifier identifier = NPN_GetStringIdentifier(utf8String);
+ free(utf8String);
+ return identifier;
+}
+
+NPIdentifier int32VariantToIdentifier(NPVariant variant) {
+ assert(NPVARIANT_IS_INT32(variant));
+ int32_t integer = NPVARIANT_TO_INT32(variant);
+ return NPN_GetIntIdentifier(integer);
+}
+
+NPIdentifier doubleVariantToIdentifier(NPVariant variant) {
+ assert(NPVARIANT_IS_DOUBLE(variant));
+ double value = NPVARIANT_TO_DOUBLE(variant);
+ // sadly there is no "getdoubleidentifier"
+ int32_t integer = static_cast<int32_t>(value);
+ return NPN_GetIntIdentifier(integer);
+}
+
+/*
+ * Parse a color in hex format, #AARRGGBB or AARRGGBB.
+ */
+uint32_t parseHexColor(const char* color, int len) {
+ uint8_t bgra[4] = {0, 0, 0, 0xFF};
+ int i = 0;
+
+ // Ignore unsupported formats.
+ if (len != 9 && len != 8) return 0;
+
+ // start from the right and work to the left
+ while (len >= 2) { // we have at least #AA or AA left.
+ char byte[3];
+ // parse two hex digits
+ byte[0] = color[len - 2];
+ byte[1] = color[len - 1];
+ byte[2] = '\0';
+
+ bgra[i] = (uint8_t)(strtoul(byte, nullptr, 16) & 0xFF);
+ i++;
+ len -= 2;
+ }
+ return (bgra[3] << 24) | (bgra[2] << 16) | (bgra[1] << 8) | bgra[0];
+}
diff --git a/dom/plugins/test/testplugin/nptest_utils.h b/dom/plugins/test/testplugin/nptest_utils.h
new file mode 100644
index 0000000000..cb2ca5a803
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_utils.h
@@ -0,0 +1,45 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ *
+ * Contributor(s):
+ * Josh Aas <josh@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nptest_utils_h_
+#define nptest_utils_h_
+
+#include "nptest.h"
+
+NPUTF8* createCStringFromNPVariant(const NPVariant* variant);
+
+NPIdentifier variantToIdentifier(NPVariant variant);
+NPIdentifier stringVariantToIdentifier(NPVariant variant);
+NPIdentifier int32VariantToIdentifier(NPVariant variant);
+NPIdentifier doubleVariantToIdentifier(NPVariant variant);
+
+uint32_t parseHexColor(const char* color, int len);
+
+#endif // nptest_utils_h_
diff --git a/dom/plugins/test/testplugin/nptest_windows.cpp b/dom/plugins/test/testplugin/nptest_windows.cpp
new file mode 100644
index 0000000000..0490d23367
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_windows.cpp
@@ -0,0 +1,797 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (c) 2008, Mozilla Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the Mozilla Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT OWNER OR 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.
+ *
+ * Contributor(s):
+ * Josh Aas <josh@mozilla.com>
+ * Jim Mathies <jmathies@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nptest_platform.h"
+
+#include <windows.h>
+#include <windowsx.h>
+#include <stdio.h>
+
+#include <d3d10_1.h>
+#include <d2d1.h>
+
+void SetSubclass(HWND hWnd, InstanceData* instanceData);
+void ClearSubclass(HWND hWnd);
+LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
+ LPARAM lParam);
+
+struct _PlatformData {
+ HWND childWindow;
+ IDXGIAdapter1* adapter;
+ ID3D10Device1* device;
+ ID3D10Texture2D* frontBuffer;
+ ID3D10Texture2D* backBuffer;
+ ID2D1Factory* d2d1Factory;
+};
+
+bool pluginSupportsWindowMode() { return true; }
+
+bool pluginSupportsWindowlessMode() { return true; }
+
+NPError pluginInstanceInit(InstanceData* instanceData) {
+ instanceData->platformData =
+ static_cast<PlatformData*>(NPN_MemAlloc(sizeof(PlatformData)));
+ if (!instanceData->platformData) return NPERR_OUT_OF_MEMORY_ERROR;
+
+ instanceData->platformData->childWindow = nullptr;
+ instanceData->platformData->device = nullptr;
+ instanceData->platformData->frontBuffer = nullptr;
+ instanceData->platformData->backBuffer = nullptr;
+ instanceData->platformData->adapter = nullptr;
+ instanceData->platformData->d2d1Factory = nullptr;
+ return NPERR_NO_ERROR;
+}
+
+static inline bool openSharedTex2D(ID3D10Device* device, HANDLE handle,
+ ID3D10Texture2D** out) {
+ HRESULT hr = device->OpenSharedResource(handle, __uuidof(ID3D10Texture2D),
+ (void**)out);
+ if (FAILED(hr) || !*out) {
+ return false;
+ }
+ return true;
+}
+
+// This is overloaded in d2d1.h so we can't use decltype().
+typedef HRESULT(WINAPI* D2D1CreateFactoryFunc)(
+ D2D1_FACTORY_TYPE factoryType, REFIID iid,
+ CONST D2D1_FACTORY_OPTIONS* pFactoryOptions, void** factory);
+
+static IDXGIAdapter1* FindDXGIAdapter(NPP npp, IDXGIFactory1* factory) {
+ DXGI_ADAPTER_DESC preferred;
+ if (NPN_GetValue(npp, NPNVpreferredDXGIAdapter, &preferred) !=
+ NPERR_NO_ERROR) {
+ return nullptr;
+ }
+
+ UINT index = 0;
+ for (;;) {
+ IDXGIAdapter1* adapter = nullptr;
+ if (FAILED(factory->EnumAdapters1(index, &adapter)) || !adapter) {
+ return nullptr;
+ }
+
+ DXGI_ADAPTER_DESC desc;
+ if (SUCCEEDED(adapter->GetDesc(&desc)) &&
+ desc.AdapterLuid.LowPart == preferred.AdapterLuid.LowPart &&
+ desc.AdapterLuid.HighPart == preferred.AdapterLuid.HighPart &&
+ desc.VendorId == preferred.VendorId &&
+ desc.DeviceId == preferred.DeviceId) {
+ return adapter;
+ }
+
+ adapter->Release();
+ index++;
+ }
+}
+
+// Note: we leak modules since we need them anyway.
+bool setupDxgiSurfaces(NPP npp, InstanceData* instanceData) {
+ HMODULE dxgi = LoadLibraryA("dxgi.dll");
+ if (!dxgi) {
+ return false;
+ }
+ decltype(CreateDXGIFactory1)* createDXGIFactory1 =
+ (decltype(CreateDXGIFactory1)*)GetProcAddress(dxgi, "CreateDXGIFactory1");
+ if (!createDXGIFactory1) {
+ return false;
+ }
+
+ IDXGIFactory1* factory1 = nullptr;
+ HRESULT hr = createDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory1);
+ if (FAILED(hr) || !factory1) {
+ return false;
+ }
+
+ instanceData->platformData->adapter = FindDXGIAdapter(npp, factory1);
+ if (!instanceData->platformData->adapter) {
+ return false;
+ }
+
+ HMODULE d3d10 = LoadLibraryA("d3d10_1.dll");
+ if (!d3d10) {
+ return false;
+ }
+
+ decltype(D3D10CreateDevice1)* createDevice =
+ (decltype(D3D10CreateDevice1)*)GetProcAddress(d3d10,
+ "D3D10CreateDevice1");
+ if (!createDevice) {
+ return false;
+ }
+
+ hr = createDevice(
+ instanceData->platformData->adapter, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
+ D3D10_CREATE_DEVICE_BGRA_SUPPORT |
+ D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+ D3D10_FEATURE_LEVEL_10_1, D3D10_1_SDK_VERSION,
+ &instanceData->platformData->device);
+ if (FAILED(hr) || !instanceData->platformData->device) {
+ return false;
+ }
+
+ if (!openSharedTex2D(instanceData->platformData->device,
+ instanceData->frontBuffer->sharedHandle,
+ &instanceData->platformData->frontBuffer)) {
+ return false;
+ }
+ if (!openSharedTex2D(instanceData->platformData->device,
+ instanceData->backBuffer->sharedHandle,
+ &instanceData->platformData->backBuffer)) {
+ return false;
+ }
+
+ HMODULE d2d1 = LoadLibraryA("D2d1.dll");
+ if (!d2d1) {
+ return false;
+ }
+ auto d2d1CreateFactory =
+ (D2D1CreateFactoryFunc)GetProcAddress(d2d1, "D2D1CreateFactory");
+ if (!d2d1CreateFactory) {
+ return false;
+ }
+
+ D2D1_FACTORY_OPTIONS options;
+ options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
+
+ hr = d2d1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
+ __uuidof(ID2D1Factory), &options,
+ (void**)&instanceData->platformData->d2d1Factory);
+ if (FAILED(hr) || !instanceData->platformData->d2d1Factory) {
+ return false;
+ }
+
+ return true;
+}
+
+void drawDxgiBitmapColor(InstanceData* instanceData) {
+ NPP npp = instanceData->npp;
+
+ HRESULT hr;
+
+ IDXGISurface* surface = nullptr;
+ hr = instanceData->platformData->backBuffer->QueryInterface(
+ __uuidof(IDXGISurface), (void**)&surface);
+ if (FAILED(hr) || !surface) {
+ return;
+ }
+
+ D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
+ D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED));
+
+ ID2D1RenderTarget* target = nullptr;
+ hr = instanceData->platformData->d2d1Factory->CreateDxgiSurfaceRenderTarget(
+ surface, &props, &target);
+ if (FAILED(hr) || !target) {
+ surface->Release();
+ return;
+ }
+
+ IDXGIKeyedMutex* mutex = nullptr;
+ hr = instanceData->platformData->backBuffer->QueryInterface(
+ __uuidof(IDXGIKeyedMutex), (void**)&mutex);
+ if (mutex) {
+ mutex->AcquireSync(0, 0);
+ }
+
+ target->BeginDraw();
+
+ unsigned char subpixels[4];
+ memcpy(subpixels, &instanceData->scriptableObject->drawColor,
+ sizeof(subpixels));
+
+ auto rect = D2D1::RectF(0, 0, instanceData->backBuffer->size.width,
+ instanceData->backBuffer->size.height);
+ auto color = D2D1::ColorF(float(subpixels[3] * subpixels[2]) / 0xFF,
+ float(subpixels[3] * subpixels[1]) / 0xFF,
+ float(subpixels[3] * subpixels[0]) / 0xFF,
+ float(subpixels[3]) / 0xff);
+
+ ID2D1SolidColorBrush* brush = nullptr;
+ hr = target->CreateSolidColorBrush(color, &brush);
+ if (SUCCEEDED(hr) && brush) {
+ target->FillRectangle(rect, brush);
+ brush->Release();
+ brush = nullptr;
+ }
+ hr = target->EndDraw();
+
+ if (mutex) {
+ mutex->ReleaseSync(0);
+ mutex->Release();
+ mutex = nullptr;
+ }
+
+ target->Release();
+ surface->Release();
+ target = nullptr;
+ surface = nullptr;
+
+ NPN_SetCurrentAsyncSurface(npp, instanceData->backBuffer, NULL);
+ std::swap(instanceData->backBuffer, instanceData->frontBuffer);
+ std::swap(instanceData->platformData->backBuffer,
+ instanceData->platformData->frontBuffer);
+}
+
+void pluginInstanceShutdown(InstanceData* instanceData) {
+ PlatformData* pd = instanceData->platformData;
+ if (pd->frontBuffer) {
+ pd->frontBuffer->Release();
+ }
+ if (pd->backBuffer) {
+ pd->backBuffer->Release();
+ }
+ if (pd->d2d1Factory) {
+ pd->d2d1Factory->Release();
+ }
+ if (pd->device) {
+ pd->device->Release();
+ }
+ if (pd->adapter) {
+ pd->adapter->Release();
+ }
+ NPN_MemFree(instanceData->platformData);
+ instanceData->platformData = 0;
+ ClearSubclass((HWND)instanceData->window.window);
+}
+
+void pluginDoSetWindow(InstanceData* instanceData, NPWindow* newWindow) {
+ instanceData->window = *newWindow;
+}
+
+#define CHILD_WIDGET_SIZE 10
+
+void pluginWidgetInit(InstanceData* instanceData, void* oldWindow) {
+ HWND hWnd = (HWND)instanceData->window.window;
+ if (oldWindow) {
+ // chrashtests/539897-1.html excercises this code
+ HWND hWndOld = (HWND)oldWindow;
+ ClearSubclass(hWndOld);
+ if (instanceData->platformData->childWindow) {
+ ::DestroyWindow(instanceData->platformData->childWindow);
+ }
+ }
+
+ SetSubclass(hWnd, instanceData);
+
+ instanceData->platformData->childWindow = ::CreateWindowW(
+ L"SCROLLBAR", L"Dummy child window", WS_CHILD, 0, 0, CHILD_WIDGET_SIZE,
+ CHILD_WIDGET_SIZE, hWnd, nullptr, nullptr, nullptr);
+}
+
+static void drawToDC(InstanceData* instanceData, HDC dc, int x, int y,
+ int width, int height) {
+ switch (instanceData->scriptableObject->drawMode) {
+ case DM_DEFAULT: {
+ const RECT fill = {x, y, x + width, y + height};
+
+ int oldBkMode = ::SetBkMode(dc, TRANSPARENT);
+ HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
+ if (brush) {
+ ::FillRect(dc, &fill, brush);
+ ::DeleteObject(brush);
+ }
+ if (width > 6 && height > 6) {
+ brush = ::CreateSolidBrush(RGB(192, 192, 192));
+ if (brush) {
+ RECT inset = {x + 3, y + 3, x + width - 3, y + height - 3};
+ ::FillRect(dc, &inset, brush);
+ ::DeleteObject(brush);
+ }
+ }
+
+ const char* uaString = NPN_UserAgent(instanceData->npp);
+ if (uaString && width > 10 && height > 10) {
+ HFONT font = ::CreateFontA(20, 0, 0, 0, 400, FALSE, FALSE, FALSE,
+ DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
+ CLIP_DEFAULT_PRECIS, 5, // CLEARTYPE_QUALITY
+ DEFAULT_PITCH, "Arial");
+ if (font) {
+ HFONT oldFont = (HFONT)::SelectObject(dc, font);
+ RECT inset = {x + 5, y + 5, x + width - 5, y + height - 5};
+ ::DrawTextA(dc, uaString, -1, &inset,
+ DT_LEFT | DT_TOP | DT_NOPREFIX | DT_WORDBREAK);
+ ::SelectObject(dc, oldFont);
+ ::DeleteObject(font);
+ }
+ }
+ ::SetBkMode(dc, oldBkMode);
+ } break;
+
+ case DM_SOLID_COLOR: {
+ HDC offscreenDC = ::CreateCompatibleDC(dc);
+ if (!offscreenDC) return;
+
+ const BITMAPV4HEADER bitmapheader = {
+ sizeof(BITMAPV4HEADER),
+ width,
+ height,
+ 1, // planes
+ 32, // bits
+ BI_BITFIELDS,
+ 0, // unused size
+ 0,
+ 0, // unused metrics
+ 0,
+ 0, // unused colors used/important
+ 0x00FF0000,
+ 0x0000FF00,
+ 0x000000FF,
+ 0xFF000000, // ARGB masks
+ };
+ uint32_t* pixelData;
+ HBITMAP offscreenBitmap = ::CreateDIBSection(
+ dc, reinterpret_cast<const BITMAPINFO*>(&bitmapheader), 0,
+ reinterpret_cast<void**>(&pixelData), 0, 0);
+ if (!offscreenBitmap) return;
+
+ uint32_t rgba = instanceData->scriptableObject->drawColor;
+ unsigned int alpha = ((rgba & 0xFF000000) >> 24);
+ BYTE r = ((rgba & 0xFF0000) >> 16);
+ BYTE g = ((rgba & 0xFF00) >> 8);
+ BYTE b = (rgba & 0xFF);
+
+ // Windows expects premultiplied
+ r = BYTE(float(alpha * r) / 0xFF);
+ g = BYTE(float(alpha * g) / 0xFF);
+ b = BYTE(float(alpha * b) / 0xFF);
+ uint32_t premultiplied = (alpha << 24) + (r << 16) + (g << 8) + b;
+
+ for (uint32_t* lastPixel = pixelData + width * height;
+ pixelData < lastPixel; ++pixelData)
+ *pixelData = premultiplied;
+
+ ::SelectObject(offscreenDC, offscreenBitmap);
+ BLENDFUNCTION blendFunc;
+ blendFunc.BlendOp = AC_SRC_OVER;
+ blendFunc.BlendFlags = 0;
+ blendFunc.SourceConstantAlpha = 255;
+ blendFunc.AlphaFormat = AC_SRC_ALPHA;
+ ::AlphaBlend(dc, x, y, width, height, offscreenDC, 0, 0, width, height,
+ blendFunc);
+
+ ::DeleteObject(offscreenDC);
+ ::DeleteObject(offscreenBitmap);
+ } break;
+ }
+}
+
+void pluginDraw(InstanceData* instanceData) {
+ NPP npp = instanceData->npp;
+ if (!npp) return;
+
+ HDC hdc = nullptr;
+ PAINTSTRUCT ps;
+
+ notifyDidPaint(instanceData);
+
+ if (instanceData->hasWidget)
+ hdc = ::BeginPaint((HWND)instanceData->window.window, &ps);
+ else
+ hdc = (HDC)instanceData->window.window;
+
+ if (hdc == nullptr) return;
+
+ // Push the browser's hdc on the resource stack. If this test plugin is
+ // windowless, we share the drawing surface with the rest of the browser.
+ int savedDCID = SaveDC(hdc);
+
+ // When we have a widget, window.x/y are meaningless since our widget
+ // is always positioned correctly and we just draw into it at 0,0.
+ int x = instanceData->hasWidget ? 0 : instanceData->window.x;
+ int y = instanceData->hasWidget ? 0 : instanceData->window.y;
+ int width = instanceData->window.width;
+ int height = instanceData->window.height;
+ drawToDC(instanceData, hdc, x, y, width, height);
+
+ // Pop our hdc changes off the resource stack
+ RestoreDC(hdc, savedDCID);
+
+ if (instanceData->hasWidget)
+ ::EndPaint((HWND)instanceData->window.window, &ps);
+}
+
+/* script interface */
+
+int32_t pluginGetEdge(InstanceData* instanceData, RectEdge edge) {
+ if (!instanceData || !instanceData->hasWidget) return NPTEST_INT32_ERROR;
+
+ // Get the plugin client rect in screen coordinates
+ RECT rect = {0};
+ if (!::GetClientRect((HWND)instanceData->window.window, &rect))
+ return NPTEST_INT32_ERROR;
+ ::MapWindowPoints((HWND)instanceData->window.window, nullptr, (LPPOINT)&rect,
+ 2);
+
+ // Get the toplevel window frame rect in screen coordinates
+ HWND rootWnd = ::GetAncestor((HWND)instanceData->window.window, GA_ROOT);
+ if (!rootWnd) return NPTEST_INT32_ERROR;
+ RECT rootRect;
+ if (!::GetWindowRect(rootWnd, &rootRect)) return NPTEST_INT32_ERROR;
+
+ switch (edge) {
+ case EDGE_LEFT:
+ return rect.left - rootRect.left;
+ case EDGE_TOP:
+ return rect.top - rootRect.top;
+ case EDGE_RIGHT:
+ return rect.right - rootRect.left;
+ case EDGE_BOTTOM:
+ return rect.bottom - rootRect.top;
+ }
+
+ return NPTEST_INT32_ERROR;
+}
+
+static BOOL getWindowRegion(HWND wnd, HRGN rgn) {
+ if (::GetWindowRgn(wnd, rgn) != ERROR) return TRUE;
+
+ RECT clientRect;
+ if (!::GetClientRect(wnd, &clientRect)) return FALSE;
+ return ::SetRectRgn(rgn, 0, 0, clientRect.right, clientRect.bottom);
+}
+
+static RGNDATA* computeClipRegion(InstanceData* instanceData) {
+ HWND wnd = (HWND)instanceData->window.window;
+ HRGN rgn = ::CreateRectRgn(0, 0, 0, 0);
+ if (!rgn) return nullptr;
+ HRGN ancestorRgn = ::CreateRectRgn(0, 0, 0, 0);
+ if (!ancestorRgn) {
+ ::DeleteObject(rgn);
+ return nullptr;
+ }
+ if (!getWindowRegion(wnd, rgn)) {
+ ::DeleteObject(ancestorRgn);
+ ::DeleteObject(rgn);
+ return nullptr;
+ }
+
+ HWND ancestor = wnd;
+ for (;;) {
+ ancestor = ::GetAncestor(ancestor, GA_PARENT);
+ if (!ancestor || ancestor == ::GetDesktopWindow()) {
+ ::DeleteObject(ancestorRgn);
+
+ DWORD size = ::GetRegionData(rgn, 0, nullptr);
+ if (!size) {
+ ::DeleteObject(rgn);
+ return nullptr;
+ }
+
+ HANDLE heap = ::GetProcessHeap();
+ RGNDATA* data = static_cast<RGNDATA*>(::HeapAlloc(heap, 0, size));
+ if (!data) {
+ ::DeleteObject(rgn);
+ return nullptr;
+ }
+ DWORD result = ::GetRegionData(rgn, size, data);
+ ::DeleteObject(rgn);
+ if (!result) {
+ ::HeapFree(heap, 0, data);
+ return nullptr;
+ }
+
+ return data;
+ }
+
+ if (!getWindowRegion(ancestor, ancestorRgn)) {
+ ::DeleteObject(ancestorRgn);
+ ::DeleteObject(rgn);
+ return 0;
+ }
+
+ POINT pt = {0, 0};
+ ::MapWindowPoints(ancestor, wnd, &pt, 1);
+ if (::OffsetRgn(ancestorRgn, pt.x, pt.y) == ERROR ||
+ ::CombineRgn(rgn, rgn, ancestorRgn, RGN_AND) == ERROR) {
+ ::DeleteObject(ancestorRgn);
+ ::DeleteObject(rgn);
+ return 0;
+ }
+ }
+}
+
+int32_t pluginGetClipRegionRectCount(InstanceData* instanceData) {
+ RGNDATA* data = computeClipRegion(instanceData);
+ if (!data) return NPTEST_INT32_ERROR;
+
+ int32_t result = data->rdh.nCount;
+ ::HeapFree(::GetProcessHeap(), 0, data);
+ return result;
+}
+
+static int32_t addOffset(LONG coord, int32_t offset) {
+ if (offset == NPTEST_INT32_ERROR) return NPTEST_INT32_ERROR;
+ return coord + offset;
+}
+
+int32_t pluginGetClipRegionRectEdge(InstanceData* instanceData,
+ int32_t rectIndex, RectEdge edge) {
+ RGNDATA* data = computeClipRegion(instanceData);
+ if (!data) return NPTEST_INT32_ERROR;
+
+ HANDLE heap = ::GetProcessHeap();
+ if (rectIndex >= int32_t(data->rdh.nCount)) {
+ ::HeapFree(heap, 0, data);
+ return NPTEST_INT32_ERROR;
+ }
+
+ RECT rect = reinterpret_cast<RECT*>(data->Buffer)[rectIndex];
+ ::HeapFree(heap, 0, data);
+
+ switch (edge) {
+ case EDGE_LEFT:
+ return addOffset(rect.left, pluginGetEdge(instanceData, EDGE_LEFT));
+ case EDGE_TOP:
+ return addOffset(rect.top, pluginGetEdge(instanceData, EDGE_TOP));
+ case EDGE_RIGHT:
+ return addOffset(rect.right, pluginGetEdge(instanceData, EDGE_LEFT));
+ case EDGE_BOTTOM:
+ return addOffset(rect.bottom, pluginGetEdge(instanceData, EDGE_TOP));
+ }
+
+ return NPTEST_INT32_ERROR;
+}
+
+static void createDummyWindowForIME(InstanceData* instanceData) {
+ WNDCLASSW wndClass;
+ wndClass.style = 0;
+ wndClass.lpfnWndProc = DefWindowProcW;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hInstance = GetModuleHandleW(NULL);
+ wndClass.hIcon = nullptr;
+ wndClass.hCursor = nullptr;
+ wndClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
+ wndClass.lpszMenuName = NULL;
+ wndClass.lpszClassName = L"SWFlash_PlaceholderX";
+ RegisterClassW(&wndClass);
+
+ instanceData->placeholderWnd = static_cast<void*>(
+ CreateWindowW(L"SWFlash_PlaceholderX", L"", WS_CHILD, 0, 0, 0, 0,
+ HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL));
+}
+
+/* windowless plugin events */
+
+static bool handleEventInternal(InstanceData* instanceData, NPEvent* pe,
+ LRESULT* result) {
+ switch ((UINT)pe->event) {
+ case WM_PAINT:
+ pluginDraw(instanceData);
+ return true;
+
+ case WM_MOUSEACTIVATE:
+ if (instanceData->hasWidget) {
+ ::SetFocus((HWND)instanceData->window.window);
+ *result = MA_ACTIVATEANDEAT;
+ return true;
+ }
+ return false;
+
+ case WM_MOUSEWHEEL:
+ return true;
+
+ case WM_WINDOWPOSCHANGED: {
+ WINDOWPOS* pPos = (WINDOWPOS*)pe->lParam;
+ instanceData->winX = instanceData->winY = 0;
+ if (pPos) {
+ instanceData->winX = pPos->x;
+ instanceData->winY = pPos->y;
+ return true;
+ }
+ return false;
+ }
+
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP: {
+ int x = instanceData->hasWidget ? 0 : instanceData->winX;
+ int y = instanceData->hasWidget ? 0 : instanceData->winY;
+ instanceData->lastMouseX = GET_X_LPARAM(pe->lParam) - x;
+ instanceData->lastMouseY = GET_Y_LPARAM(pe->lParam) - y;
+ if ((UINT)pe->event == WM_LBUTTONUP) {
+ instanceData->mouseUpEventCount++;
+ }
+ return true;
+ }
+
+ case WM_KEYDOWN:
+ instanceData->lastKeyText.erase();
+ *result = 0;
+ return true;
+
+ case WM_CHAR: {
+ *result = 0;
+ wchar_t uniChar = static_cast<wchar_t>(pe->wParam);
+ if (!uniChar) {
+ return true;
+ }
+ char utf8Char[6];
+ int len = ::WideCharToMultiByte(CP_UTF8, 0, &uniChar, 1, utf8Char, 6,
+ nullptr, nullptr);
+ if (len == 0 || len > 6) {
+ return true;
+ }
+ instanceData->lastKeyText.append(utf8Char, len);
+ return true;
+ }
+
+ case WM_IME_STARTCOMPOSITION:
+ instanceData->lastComposition.erase();
+ if (!instanceData->placeholderWnd) {
+ createDummyWindowForIME(instanceData);
+ }
+ return true;
+
+ case WM_IME_ENDCOMPOSITION:
+ instanceData->lastComposition.erase();
+ return true;
+
+ case WM_IME_COMPOSITION: {
+ if (pe->lParam & GCS_COMPSTR) {
+ HIMC hIMC = ImmGetContext((HWND)instanceData->placeholderWnd);
+ if (!hIMC) {
+ return false;
+ }
+ WCHAR compStr[256];
+ LONG len = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, compStr,
+ 256 * sizeof(WCHAR));
+ CHAR buffer[256];
+ len = ::WideCharToMultiByte(CP_UTF8, 0, compStr, len / sizeof(WCHAR),
+ buffer, 256, nullptr, nullptr);
+ instanceData->lastComposition.append(buffer, len);
+ ::ImmReleaseContext((HWND)instanceData->placeholderWnd, hIMC);
+ }
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+
+int16_t pluginHandleEvent(InstanceData* instanceData, void* event) {
+ NPEvent* pe = (NPEvent*)event;
+
+ if (pe == nullptr || instanceData == nullptr ||
+ instanceData->window.type != NPWindowTypeDrawable)
+ return 0;
+
+ LRESULT result = 0;
+ return handleEventInternal(instanceData, pe, &result);
+}
+
+/* windowed plugin events */
+
+LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
+ LPARAM lParam) {
+ WNDPROC wndProc = (WNDPROC)GetProp(hWnd, "MozillaWndProc");
+ if (!wndProc) return 0;
+ InstanceData* pInstance = (InstanceData*)GetProp(hWnd, "InstanceData");
+ if (!pInstance) return 0;
+
+ NPEvent event = {static_cast<uint16_t>(uMsg), wParam, lParam};
+
+ LRESULT result = 0;
+ if (handleEventInternal(pInstance, &event, &result)) return result;
+
+ if (uMsg == WM_CLOSE) {
+ ClearSubclass((HWND)pInstance->window.window);
+ }
+
+ return CallWindowProc(wndProc, hWnd, uMsg, wParam, lParam);
+}
+
+void ClearSubclass(HWND hWnd) {
+ if (GetProp(hWnd, "MozillaWndProc")) {
+ ::SetWindowLongPtr(hWnd, GWLP_WNDPROC,
+ (LONG_PTR)GetProp(hWnd, "MozillaWndProc"));
+ RemoveProp(hWnd, "MozillaWndProc");
+ RemoveProp(hWnd, "InstanceData");
+ }
+}
+
+void SetSubclass(HWND hWnd, InstanceData* instanceData) {
+ // Subclass the plugin window so we can handle our own windows events.
+ SetProp(hWnd, "InstanceData", (HANDLE)instanceData);
+ WNDPROC origProc =
+ (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)PluginWndProc);
+ SetProp(hWnd, "MozillaWndProc", (HANDLE)origProc);
+}
+
+static void checkEquals(int a, int b, const char* msg, std::string& error) {
+ if (a == b) {
+ return;
+ }
+
+ error.append(msg);
+ char buf[100];
+ sprintf(buf, " (got %d, expected %d)\n", a, b);
+ error.append(buf);
+}
+
+void pluginDoInternalConsistencyCheck(InstanceData* instanceData,
+ std::string& error) {
+ if (instanceData->platformData->childWindow) {
+ RECT childRect;
+ ::GetWindowRect(instanceData->platformData->childWindow, &childRect);
+ RECT ourRect;
+ HWND hWnd = (HWND)instanceData->window.window;
+ ::GetWindowRect(hWnd, &ourRect);
+ checkEquals(childRect.left, ourRect.left, "Child widget left", error);
+ checkEquals(childRect.top, ourRect.top, "Child widget top", error);
+ checkEquals(childRect.right, childRect.left + CHILD_WIDGET_SIZE,
+ "Child widget width", error);
+ checkEquals(childRect.bottom, childRect.top + CHILD_WIDGET_SIZE,
+ "Child widget height", error);
+ }
+}
+
+bool pluginNativeWidgetIsVisible(InstanceData* instanceData) {
+ HWND hWnd = (HWND)instanceData->window.window;
+ wchar_t className[60];
+ if (::GetClassNameW(hWnd, className, sizeof(className) / sizeof(char16_t)) &&
+ !wcsicmp(className, L"GeckoPluginWindow")) {
+ return ::IsWindowVisible(hWnd);
+ }
+ // something isn't right, fail the check
+ return false;
+}
diff --git a/dom/plugins/test/testplugin/secondplugin/Info.plist b/dom/plugins/test/testplugin/secondplugin/Info.plist
new file mode 100644
index 0000000000..afa83a63ce
--- /dev/null
+++ b/dom/plugins/test/testplugin/secondplugin/Info.plist
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>libnpsecondtest.dylib</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.mozilla.SecondTestPlugin</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BRPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0.0.0</string>
+ <key>CFBundleSignature</key>
+ <string>SECONDTEST</string>
+ <key>CFBundleVersion</key>
+ <string>1.0.0.0</string>
+ <key>WebPluginName</key>
+ <string>Second Test Plug-in</string>
+ <key>WebPluginDescription</key>
+ <string>Second plug-in for testing purposes.</string>
+ <key>WebPluginMIMETypes</key>
+ <dict>
+ <key>application/x-Second-Test</key>
+ <dict>
+ <key>WebPluginExtensions</key>
+ <array>
+ <string>ts2</string>
+ </array>
+ <key>WebPluginTypeDescription</key>
+ <string>Second test type</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/dom/plugins/test/testplugin/secondplugin/moz.build b/dom/plugins/test/testplugin/secondplugin/moz.build
new file mode 100644
index 0000000000..29c12260b4
--- /dev/null
+++ b/dom/plugins/test/testplugin/secondplugin/moz.build
@@ -0,0 +1,11 @@
+# -*- 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/.
+
+SharedLibrary("npsecondtest")
+
+relative_path = "secondplugin"
+cocoa_name = "SecondTest"
+include("../testplugin.mozbuild")
diff --git a/dom/plugins/test/testplugin/secondplugin/nptest.def b/dom/plugins/test/testplugin/secondplugin/nptest.def
new file mode 100644
index 0000000000..c6584387d2
--- /dev/null
+++ b/dom/plugins/test/testplugin/secondplugin/nptest.def
@@ -0,0 +1,7 @@
+LIBRARY NPSECONDTEST
+
+EXPORTS
+ NP_GetEntryPoints @1
+ NP_Initialize @2
+ NP_Shutdown @3
+ NP_GetMIMEDescription @4
diff --git a/dom/plugins/test/testplugin/secondplugin/nptest.rc b/dom/plugins/test/testplugin/secondplugin/nptest.rc
new file mode 100644
index 0000000000..835906d0cb
--- /dev/null
+++ b/dom/plugins/test/testplugin/secondplugin/nptest.rc
@@ -0,0 +1,42 @@
+#include<winver.h>
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Second plug-in for testing purposes."
+ VALUE "FileExtents", "ts2"
+ VALUE "FileOpenName", "Second test type"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "npsecondtest"
+ VALUE "MIMEType", "application/x-Second-Test"
+ VALUE "OriginalFilename", "npsecondtest.dll"
+ VALUE "ProductName", "Second Test Plug-in"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
diff --git a/dom/plugins/test/testplugin/secondplugin/nptest_name.cpp b/dom/plugins/test/testplugin/secondplugin/nptest_name.cpp
new file mode 100644
index 0000000000..23b821ae61
--- /dev/null
+++ b/dom/plugins/test/testplugin/secondplugin/nptest_name.cpp
@@ -0,0 +1,7 @@
+/* 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/. */
+
+const char* sPluginName = "Second Test Plug-in";
+const char* sPluginDescription = "Second plug-in for testing purposes.";
+const char* sMimeDescription = "application/x-Second-Test:ts2:Second test type";
diff --git a/dom/plugins/test/testplugin/testplugin.mozbuild b/dom/plugins/test/testplugin/testplugin.mozbuild
new file mode 100644
index 0000000000..2c466409ea
--- /dev/null
+++ b/dom/plugins/test/testplugin/testplugin.mozbuild
@@ -0,0 +1,64 @@
+# -*- 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/.
+
+UNIFIED_SOURCES += [
+ 'nptest.cpp',
+ 'nptest_utils.cpp',
+]
+
+UNIFIED_SOURCES += [
+ '%s/nptest_name.cpp' % relative_path,
+]
+
+toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
+if toolkit == 'cocoa':
+ UNIFIED_SOURCES += [
+ 'nptest_macosx.mm'
+ ]
+elif toolkit == 'gtk':
+ UNIFIED_SOURCES += [
+ 'nptest_gtk2.cpp',
+ ]
+elif toolkit == 'windows':
+ UNIFIED_SOURCES += [
+ 'nptest_windows.cpp',
+ ]
+ OS_LIBS += [
+ 'msimg32',
+ 'imm32'
+ ]
+
+# must link statically with the CRT; nptest isn't Gecko code
+USE_STATIC_LIBS = True
+
+# Don't use STL wrappers; nptest isn't Gecko code
+DisableStlWrapping()
+
+NO_PGO = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ RCFILE = 'nptest.rc'
+ DEFFILE = 'nptest.def'
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' and CONFIG['TARGET_CPU'] == 'x86_64':
+ OS_LIBS += ['-framework Carbon']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk':
+ CXXFLAGS += CONFIG['MOZ_GTK2_CFLAGS']
+ CFLAGS += CONFIG['MOZ_GTK2_CFLAGS']
+ OS_LIBS += CONFIG['MOZ_GTK2_LIBS']
+ OS_LIBS += CONFIG['XLDFLAGS']
+ OS_LIBS += CONFIG['XLIBS']
+ OS_LIBS += CONFIG['XEXT_LIBS']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ FINAL_TARGET = 'dist/plugins/%s.plugin/Contents/MacOS' % cocoa_name
+ OBJDIR_FILES.dist.plugins['%s.plugin' % cocoa_name].Contents += ['%s/Info.plist' % relative_path]
+else:
+ FINAL_TARGET = 'dist/plugins'
+
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
+ CXXFLAGS += ['-Wno-error=shadow']