summaryrefslogtreecommitdiffstats
path: root/dom/plugins/test/testplugin
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/plugins/test/testplugin
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/plugins/test/testplugin')
-rw-r--r--dom/plugins/test/testplugin/Info.plist38
-rw-r--r--dom/plugins/test/testplugin/README441
-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.cpp7
-rw-r--r--dom/plugins/test/testplugin/javaplugin/Info.plist38
-rw-r--r--dom/plugins/test/testplugin/javaplugin/moz.build11
-rw-r--r--dom/plugins/test/testplugin/javaplugin/nptest.def7
-rw-r--r--dom/plugins/test/testplugin/javaplugin/nptest.rc42
-rw-r--r--dom/plugins/test/testplugin/javaplugin/nptest_name.cpp7
-rw-r--r--dom/plugins/test/testplugin/moz.build13
-rw-r--r--dom/plugins/test/testplugin/nptest.cpp4064
-rw-r--r--dom/plugins/test/testplugin/nptest.def7
-rw-r--r--dom/plugins/test/testplugin/nptest.h171
-rw-r--r--dom/plugins/test/testplugin/nptest.rc42
-rw-r--r--dom/plugins/test/testplugin/nptest_droid.cpp105
-rw-r--r--dom/plugins/test/testplugin/nptest_gtk2.cpp774
-rw-r--r--dom/plugins/test/testplugin/nptest_macosx.mm312
-rw-r--r--dom/plugins/test/testplugin/nptest_name.cpp6
-rw-r--r--dom/plugins/test/testplugin/nptest_platform.h160
-rw-r--r--dom/plugins/test/testplugin/nptest_utils.cpp113
-rw-r--r--dom/plugins/test/testplugin/nptest_utils.h45
-rw-r--r--dom/plugins/test/testplugin/nptest_windows.cpp878
-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/silverlightplugin/Info.plist38
-rw-r--r--dom/plugins/test/testplugin/silverlightplugin/moz.build11
-rw-r--r--dom/plugins/test/testplugin/silverlightplugin/nptest.def7
-rw-r--r--dom/plugins/test/testplugin/silverlightplugin/nptest.rc42
-rw-r--r--dom/plugins/test/testplugin/silverlightplugin/nptest_name.cpp7
-rw-r--r--dom/plugins/test/testplugin/testplugin.mozbuild72
-rw-r--r--dom/plugins/test/testplugin/thirdplugin/Info.plist38
-rw-r--r--dom/plugins/test/testplugin/thirdplugin/moz.build11
-rw-r--r--dom/plugins/test/testplugin/thirdplugin/nptest.def7
-rw-r--r--dom/plugins/test/testplugin/thirdplugin/nptest.rc42
-rw-r--r--dom/plugins/test/testplugin/thirdplugin/nptest_name.cpp7
41 files changed, 7766 insertions, 0 deletions
diff --git a/dom/plugins/test/testplugin/Info.plist b/dom/plugins/test/testplugin/Info.plist
new file mode 100644
index 000000000..dc6aa5cec
--- /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 000000000..b7ac70363
--- /dev/null
+++ b/dom/plugins/test/testplugin/README
@@ -0,0 +1,441 @@
+= 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:
+
+"streammode": one of "normal", "asfile", "asfileonly", "seek". Sets the
+ stream mode to the specified mode in any call to NPP_NewStream.
+ Defaults to "asfileonly".
+
+"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.
+
+"range": one or more byte ranges, in the format "offset,length;offset,length".
+ Only valid when "streammode" = "seek". When "range" is present, the plugin
+ will request the specified byte ranges from the stream via a call to
+ NPN_RequestRead, which it makes after the browser makes its final call to
+ NPP_Write. The plugin verifies that the browser makes additional calls
+ to NPP_Write according to the requested byte ranges, and that the data
+ received is correct. Any errors will appear in the test "frame", if
+ specified.
+
+"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".
+
+If the plugin is instantiated as a full-page plugin, the following defaults
+are used:
+ streammode="seek" frame="testframe" range="100,100"
+
+* streamTest(url, doPost, postData, writeCallback, notifyCallback, redirectCallback, allowRedirects)
+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
+
+* 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).
+
+== Getting and Setting Cookies ==
+
+* setCookie(string)
+Sets the given string as the cookie for window's URL.
+
+* getCookie()
+Returns the cookie string for the window's URL, the cookie set by setCookie.
+
+== 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 000000000..0e6168e68
--- /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 000000000..3df524a2b
--- /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 000000000..3a62d05d9
--- /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 000000000..e970d2609
--- /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 000000000..140e0225c
--- /dev/null
+++ b/dom/plugins/test/testplugin/flashplugin/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 = "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/javaplugin/Info.plist b/dom/plugins/test/testplugin/javaplugin/Info.plist
new file mode 100644
index 000000000..16a45f264
--- /dev/null
+++ b/dom/plugins/test/testplugin/javaplugin/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>libnptestjava.dylib</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.mozilla.JavaTestPlugin</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>JAVATEST</string>
+ <key>CFBundleVersion</key>
+ <string>1.0.0.0</string>
+ <key>WebPluginName</key>
+ <string>Java Test Plug-in</string>
+ <key>WebPluginDescription</key>
+ <string>Dummy Java plug-in for testing purposes.</string>
+ <key>WebPluginMIMETypes</key>
+ <dict>
+ <key>application/x-java-test</key>
+ <dict>
+ <key>WebPluginExtensions</key>
+ <array>
+ <string>tstjava</string>
+ </array>
+ <key>WebPluginTypeDescription</key>
+ <string>Dummy java type</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/dom/plugins/test/testplugin/javaplugin/moz.build b/dom/plugins/test/testplugin/javaplugin/moz.build
new file mode 100644
index 000000000..4e2abb3ed
--- /dev/null
+++ b/dom/plugins/test/testplugin/javaplugin/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('nptestjava')
+
+relative_path = 'javaplugin'
+cocoa_name = 'JavaTest'
+include('../testplugin.mozbuild')
diff --git a/dom/plugins/test/testplugin/javaplugin/nptest.def b/dom/plugins/test/testplugin/javaplugin/nptest.def
new file mode 100644
index 000000000..da24cc4b6
--- /dev/null
+++ b/dom/plugins/test/testplugin/javaplugin/nptest.def
@@ -0,0 +1,7 @@
+LIBRARY NPJAVATEST
+
+EXPORTS
+ NP_GetEntryPoints @1
+ NP_Initialize @2
+ NP_Shutdown @3
+ NP_GetMIMEDescription @4
diff --git a/dom/plugins/test/testplugin/javaplugin/nptest.rc b/dom/plugins/test/testplugin/javaplugin/nptest.rc
new file mode 100644
index 000000000..61b18ef6e
--- /dev/null
+++ b/dom/plugins/test/testplugin/javaplugin/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"Dummy Java plug-in for testing purposes."
+ VALUE "FileExtents", "tstjava"
+ VALUE "FileOpenName", "Dummy java test type"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "nptestjava"
+ VALUE "MIMEType", "application/x-java-test"
+ VALUE "OriginalFilename", "nptestjava.dll"
+ VALUE "ProductName", "Java 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/javaplugin/nptest_name.cpp b/dom/plugins/test/testplugin/javaplugin/nptest_name.cpp
new file mode 100644
index 000000000..ae3a8d146
--- /dev/null
+++ b/dom/plugins/test/testplugin/javaplugin/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 = "Java Test Plug-in";
+const char *sPluginDescription = "Dummy Java plug-in for testing purposes.";
+const char *sMimeDescription = "application/x-java-test:tstjava:Dummy java type";
diff --git a/dom/plugins/test/testplugin/moz.build b/dom/plugins/test/testplugin/moz.build
new file mode 100644
index 000000000..a79e58c1d
--- /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', 'javaplugin', 'thirdplugin', 'flashplugin', 'silverlightplugin']
+
+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 000000000..aa759ac16
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -0,0 +1,4064 @@
+/* ***** 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 namespace std;
+
+#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;
+
+void
+asyncCallback(void* cookie);
+
+//
+// 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 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 getJavaCodebase(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 setCookie(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool getCookie(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool getAuthInfo(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool asyncCallbackTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool checkGCRace(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 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",
+ "setPluginWantsAllStreams",
+ "crash",
+ "crashOnDestroy",
+ "getObjectValue",
+ "getJavaCodebase",
+ "checkObjectValue",
+ "enableFPExceptions",
+ "setCookie",
+ "getCookie",
+ "getAuthInfo",
+ "asyncCallbackTest",
+ "checkGCRace",
+ "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",
+};
+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,
+ setPluginWantsAllStreams,
+ crashPlugin,
+ crashOnDestroy,
+ getObjectValue,
+ getJavaCodebase,
+ checkObjectValue,
+ enableFPExceptions,
+ setCookie,
+ getCookie,
+ getAuthInfo,
+ asyncCallbackTest,
+ checkGCRace,
+ 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,
+};
+
+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[] = {
+ {-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},
+};
+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 addRange(InstanceData* instanceData, const char* range)
+{
+ /*
+ increased rangestr size from 16 to 17, the 17byte is only for
+ null terminated value, maybe for actual capacity it needs 16 bytes
+ */
+ char rangestr[17];
+ memset(rangestr, 0, sizeof(rangestr));
+ strncpy(rangestr, range, sizeof(rangestr) - sizeof(char));
+ const char* str1 = strtok(rangestr, ",");
+ const char* str2 = str1 ? strtok(nullptr, ",") : nullptr;
+ if (str1 && str2) {
+ TestRange* byterange = new TestRange;
+ byterange->offset = atoi(str1);
+ byterange->length = atoi(str2);
+ byterange->waiting = true;
+ byterange->next = instanceData->testrange;
+ instanceData->testrange = byterange;
+ }
+}
+
+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->streamMode == NP_ASFILE ||
+ instanceData->streamMode == NP_ASFILEONLY) {
+ buf = reinterpret_cast<char *>(instanceData->fileBuf);
+ bufsize = instanceData->fileBufSize;
+ }
+ 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");
+ }
+
+ if (instanceData->npnNewStream &&
+ instanceData->err.str().length() == 0) {
+ char typeHTML[] = "text/html";
+ NPStream* stream;
+ printf("calling NPN_NewStream...");
+ NPError err = NPN_NewStream(instance, typeHTML,
+ instanceData->frame.c_str(), &stream);
+ printf("return value %d\n", err);
+ if (err != NPERR_NO_ERROR) {
+ instanceData->err << "NPN_NewStream returned " << err;
+ return;
+ }
+
+ int32_t bytesToWrite = outbuf.length();
+ int32_t bytesWritten = 0;
+ while ((bytesToWrite - bytesWritten) > 0) {
+ int32_t numBytes = (bytesToWrite - bytesWritten) <
+ instanceData->streamChunkSize ?
+ bytesToWrite - bytesWritten : instanceData->streamChunkSize;
+ int32_t written = NPN_Write(instance, stream,
+ numBytes, (void*)(outbuf.c_str() + bytesWritten));
+ if (written <= 0) {
+ instanceData->err << "NPN_Write returned " << written;
+ break;
+ }
+ bytesWritten += numBytes;
+ printf("%d bytes written, total %d\n", written, bytesWritten);
+ }
+ err = NPN_DestroyStream(instance, stream, NPRES_DONE);
+ if (err != NPERR_NO_ERROR) {
+ instanceData->err << "NPN_DestroyStream returned " << err;
+ }
+ }
+ else {
+ // 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[8];
+ 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 scriptableInvokeDefault(NPObject* npobj, 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->asfile = NPP_StreamAsFile;
+ 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 = (NPInvokeDefaultFunctionPtr)scriptableInvokeDefault;
+ 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->streamMode = NP_ASFILEONLY;
+ instanceData->testFunction = FUNCTION_NONE;
+ instanceData->functionToFail = FUNCTION_NONE;
+ instanceData->failureCode = 0;
+ instanceData->callOnDestroy = nullptr;
+ instanceData->streamChunkSize = 1024;
+ instanceData->streamBuf = nullptr;
+ instanceData->streamBufSize = 0;
+ instanceData->fileBuf = nullptr;
+ instanceData->fileBufSize = 0;
+ instanceData->throwOnNextInvoke = false;
+ instanceData->runScriptOnPaint = false;
+ instanceData->dontTouchElement = false;
+ instanceData->testrange = nullptr;
+ 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->closeStream = false;
+ 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;
+
+ if (NP_FULL == mode) {
+ instanceData->streamMode = NP_SEEK;
+ instanceData->frame = "testframe";
+ addRange(instanceData, "100,100");
+ }
+
+ 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], "streammode") == 0) {
+ if (strcmp(argv[i], "normal") == 0) {
+ instanceData->streamMode = NP_NORMAL;
+ }
+ else if ((strcmp(argv[i], "asfile") == 0) &&
+ strlen(argv[i]) == strlen("asfile")) {
+ instanceData->streamMode = NP_ASFILE;
+ }
+ else if (strcmp(argv[i], "asfileonly") == 0) {
+ instanceData->streamMode = NP_ASFILEONLY;
+ }
+ else if (strcmp(argv[i], "seek") == 0) {
+ instanceData->streamMode = NP_SEEK;
+ }
+ }
+ 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], "range") == 0) {
+ string range = argv[i];
+ size_t semicolon = range.find(';');
+ while (semicolon != string::npos) {
+ addRange(instanceData, range.substr(0, semicolon).c_str());
+ if (semicolon == range.length()) {
+ range = "";
+ break;
+ }
+ range = range.substr(semicolon + 1);
+ semicolon = range.find(';');
+ }
+ if (range.length()) addRange(instanceData, range.c_str());
+ }
+ 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], "closestream")) {
+ instanceData->closeStream = true;
+ }
+ if (strcmp(argn[i], "bugmode") == 0) {
+ instanceData->bugMode = atoi(argv[i]);
+ }
+ // Try to emulate java's codebase handling: Use the last seen codebase
+ // value, regardless of whether it is in attributes or params.
+ if (strcasecmp(argn[i], "codebase") == 0) {
+ instanceData->javaCodebase = 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.xul", 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->fileBuf) {
+ free(instanceData->fileBuf);
+ }
+
+ TestRange* currentrange = instanceData->testrange;
+ TestRange* nextrange;
+ while (currentrange != nullptr) {
+ nextrange = reinterpret_cast<TestRange*>(currentrange->next);
+ delete currentrange;
+ currentrange = nextrange;
+ }
+
+ 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 = instanceData->streamMode;
+
+ 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->streamMode == NP_ASFILE &&
+ instanceData->functionToFail == FUNCTION_NONE) {
+ if (!instanceData->streamBuf) {
+ instanceData->err <<
+ "Error: no data written with NPP_Write";
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (!instanceData->fileBuf) {
+ instanceData->err <<
+ "Error: no data written with NPP_StreamAsFile";
+ return NPERR_GENERIC_ERROR;
+ }
+
+ if (strcmp(reinterpret_cast<char *>(instanceData->fileBuf),
+ reinterpret_cast<char *>(instanceData->streamBuf))) {
+ instanceData->err <<
+ "Error: data passed to NPP_Write and NPP_StreamAsFile differed";
+ }
+ }
+ 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;
+ }
+
+ if (instanceData->closeStream) {
+ instanceData->closeStream = false;
+ if (instanceData->testrange != nullptr) {
+ NPN_RequestRead(stream, instanceData->testrange);
+ }
+ NPN_DestroyStream(instance, stream, NPRES_USER_BREAK);
+ }
+ else if (instanceData->streamMode == NP_SEEK &&
+ stream->end != 0 &&
+ stream->end == ((uint32_t)instanceData->streamBufSize + len)) {
+ // If the complete stream has been written, and we're doing a seek test,
+ // then call NPN_RequestRead.
+ // prevent recursion
+ instanceData->streamMode = NP_NORMAL;
+
+ if (instanceData->testrange != nullptr) {
+ NPError err = NPN_RequestRead(stream, instanceData->testrange);
+ if (err != NPERR_NO_ERROR) {
+ instanceData->err << "NPN_RequestRead returned error %d" << err;
+ }
+ printf("called NPN_RequestRead, return %d\n", err);
+ }
+ }
+
+ char* streamBuf = reinterpret_cast<char *>(instanceData->streamBuf);
+ if (offset + len <= instanceData->streamBufSize) {
+ if (memcmp(buffer, streamBuf + offset, len)) {
+ instanceData->err <<
+ "Error: data written from NPN_RequestRead doesn't match";
+ }
+ else {
+ printf("data matches!\n");
+ }
+ TestRange* range = instanceData->testrange;
+ bool stillwaiting = false;
+ while(range != nullptr) {
+ if (offset == range->offset &&
+ (uint32_t)len == range->length) {
+ range->waiting = false;
+ }
+ if (range->waiting) stillwaiting = true;
+ range = reinterpret_cast<TestRange*>(range->next);
+ }
+ if (!stillwaiting) {
+ NPError err = NPN_DestroyStream(instance, stream, NPRES_DONE);
+ if (err != NPERR_NO_ERROR) {
+ instanceData->err << "Error: NPN_DestroyStream returned " << err;
+ }
+ }
+ }
+ 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_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
+{
+ size_t size;
+
+ InstanceData* instanceData = (InstanceData*)(instance->pdata);
+
+ if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM ||
+ instanceData->functionToFail == FUNCTION_NPP_WRITE) {
+ instanceData->err << "NPP_StreamAsFile called";
+ }
+
+ if (!fname)
+ return;
+
+ FILE *file = fopen(fname, "rb");
+ if (file) {
+ fseek(file, 0, SEEK_END);
+ size = ftell(file);
+ instanceData->fileBuf = malloc((int32_t)size + 1);
+ char* buf = reinterpret_cast<char *>(instanceData->fileBuf);
+ fseek(file, 0, SEEK_SET);
+ size_t sizeRead = fread(instanceData->fileBuf, 1, size, file);
+ if (sizeRead != size) {
+ printf("Unable to read data from file\n");
+ instanceData->err << "Unable to read data from file " << fname;
+ }
+ fclose(file);
+ buf[size] = '\0';
+ instanceData->fileBufSize = (int32_t)size;
+ }
+ else {
+ printf("Unable to open file\n");
+ instanceData->err << "Unable to open file " << fname;
+ }
+}
+
+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_RequestRead(NPStream* stream, NPByteRange* rangeList)
+{
+ return sBrowserFuncs->requestread(stream, rangeList);
+}
+
+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);
+}
+
+NPError
+NPN_DestroyStream(NPP instance, NPStream* stream, NPError reason)
+{
+ return sBrowserFuncs->destroystream(instance, stream, reason);
+}
+
+NPError
+NPN_NewStream(NPP instance,
+ NPMIMEType type,
+ const char* target,
+ NPStream** stream)
+{
+ return sBrowserFuncs->newstream(instance, type, target, stream);
+}
+
+int32_t
+NPN_Write(NPP instance,
+ NPStream* stream,
+ int32_t len,
+ void* buf)
+{
+ return sBrowserFuncs->write(instance, stream, len, buf);
+}
+
+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);
+}
+
+NPError
+NPN_GetAuthenticationInfo(NPP instance,
+ const char *protocol,
+ const char *host, int32_t port,
+ const char *scheme,
+ const char *realm,
+ char **username, uint32_t *ulen,
+ char **password,
+ uint32_t *plen)
+{
+ return sBrowserFuncs->getauthenticationinfo(instance, protocol, host, port, scheme, realm,
+ username, ulen, password, plen);
+}
+
+void
+NPN_PluginThreadAsyncCall(NPP plugin, void (*func)(void*), void* userdata)
+{
+ return sBrowserFuncs->pluginthreadasynccall(plugin, func, userdata);
+}
+
+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
+scriptableInvokeDefault(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->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;
+ }
+
+ 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;
+}
+
+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, doNull, writeCallback, notifyCallback, redirectCallback, allowRedirects)
+ if (7 != argCount)
+ 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]);
+
+ 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,
+ false, 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
+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 getJavaCodebase(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->javaCodebase.c_str());
+ STRINGZ_TO_NPVARIANT(outval, *result);
+ return true;
+}
+
+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
+}
+
+// caller is responsible for freeing return buffer
+static char* URLForInstanceWindow(NPP instance) {
+ char *outString = nullptr;
+
+ NPObject* windowObject = nullptr;
+ NPError err = NPN_GetValue(instance, NPNVWindowNPObject, &windowObject);
+ if (err != NPERR_NO_ERROR || !windowObject)
+ return nullptr;
+
+ NPIdentifier locationIdentifier = NPN_GetStringIdentifier("location");
+ NPVariant locationVariant;
+ if (NPN_GetProperty(instance, windowObject, locationIdentifier, &locationVariant)) {
+ NPObject *locationObject = locationVariant.value.objectValue;
+ if (locationObject) {
+ NPIdentifier hrefIdentifier = NPN_GetStringIdentifier("href");
+ NPVariant hrefVariant;
+ if (NPN_GetProperty(instance, locationObject, hrefIdentifier, &hrefVariant)) {
+ const NPString* hrefString = &NPVARIANT_TO_STRING(hrefVariant);
+ if (hrefString) {
+ outString = (char *)malloc(hrefString->UTF8Length + 1);
+ if (outString) {
+ strcpy(outString, hrefString->UTF8Characters);
+ outString[hrefString->UTF8Length] = '\0';
+ }
+ }
+ NPN_ReleaseVariantValue(&hrefVariant);
+ }
+ }
+ NPN_ReleaseVariantValue(&locationVariant);
+ }
+
+ NPN_ReleaseObject(windowObject);
+
+ return outString;
+}
+
+static bool
+setCookie(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* cookie = &NPVARIANT_TO_STRING(args[0]);
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ char* url = URLForInstanceWindow(npp);
+ if (!url)
+ return false;
+ NPError err = NPN_SetValueForURL(npp, NPNURLVCookie, url, cookie->UTF8Characters, cookie->UTF8Length);
+ free(url);
+
+ return (err == NPERR_NO_ERROR);
+}
+
+static bool
+getCookie(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 0)
+ return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ char* url = URLForInstanceWindow(npp);
+ if (!url)
+ return false;
+ char* cookie = nullptr;
+ unsigned int length = 0;
+ NPError err = NPN_GetValueForURL(npp, NPNURLVCookie, url, &cookie, &length);
+ free(url);
+ if (err != NPERR_NO_ERROR || !cookie)
+ return false;
+
+ STRINGZ_TO_NPVARIANT(cookie, *result);
+ return true;
+}
+
+static bool
+getAuthInfo(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+ if (argCount != 5)
+ return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ if (!NPVARIANT_IS_STRING(args[0]) || !NPVARIANT_IS_STRING(args[1]) ||
+ !NPVARIANT_IS_INT32(args[2]) || !NPVARIANT_IS_STRING(args[3]) ||
+ !NPVARIANT_IS_STRING(args[4]))
+ return false;
+
+ const NPString* protocol = &NPVARIANT_TO_STRING(args[0]);
+ const NPString* host = &NPVARIANT_TO_STRING(args[1]);
+ uint32_t port = NPVARIANT_TO_INT32(args[2]);
+ const NPString* scheme = &NPVARIANT_TO_STRING(args[3]);
+ const NPString* realm = &NPVARIANT_TO_STRING(args[4]);
+
+ char* username = nullptr;
+ char* password = nullptr;
+ uint32_t ulen = 0, plen = 0;
+
+ NPError err = NPN_GetAuthenticationInfo(npp,
+ protocol->UTF8Characters,
+ host->UTF8Characters,
+ port,
+ scheme->UTF8Characters,
+ realm->UTF8Characters,
+ &username,
+ &ulen,
+ &password,
+ &plen);
+
+ if (err != NPERR_NO_ERROR) {
+ return false;
+ }
+
+ char* outstring = (char*)NPN_MemAlloc(ulen + plen + 2);
+ memset(outstring, 0, ulen + plen + 2);
+ strncpy(outstring, username, ulen);
+ strcat(outstring, "|");
+ strncat(outstring, password, plen);
+
+ STRINGZ_TO_NPVARIANT(outstring, *result);
+
+ NPN_MemFree(username);
+ NPN_MemFree(password);
+
+ return true;
+}
+
+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;
+}
+
+#ifdef XP_WIN
+void
+ThreadProc(void* cookie)
+#else
+void*
+ThreadProc(void* cookie)
+#endif
+{
+ NPObject* npobj = (NPObject*)cookie;
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+ id->asyncTestPhase = 1;
+ NPN_PluginThreadAsyncCall(npp, asyncCallback, (void*)npobj);
+#ifndef XP_WIN
+ return nullptr;
+#endif
+}
+
+void
+asyncCallback(void* cookie)
+{
+ NPObject* npobj = (NPObject*)cookie;
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+ InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+
+ switch (id->asyncTestPhase) {
+ // async callback triggered from same thread
+ case 0:
+#ifdef XP_WIN
+ if (_beginthread(ThreadProc, 0, (void*)npobj) == -1)
+ id->asyncCallbackResult = false;
+#else
+ pthread_t tid;
+ if (pthread_create(&tid, 0, ThreadProc, (void*)npobj))
+ id->asyncCallbackResult = false;
+#endif
+ break;
+
+ // async callback triggered from different thread
+ default:
+ NPObject* windowObject;
+ NPN_GetValue(npp, NPNVWindowNPObject, &windowObject);
+ if (!windowObject)
+ return;
+ NPVariant arg, rval;
+ BOOLEAN_TO_NPVARIANT(id->asyncCallbackResult, arg);
+ NPN_Invoke(npp, windowObject, NPN_GetStringIdentifier(id->asyncTestScriptCallback.c_str()), &arg, 1, &rval);
+ NPN_ReleaseVariantValue(&arg);
+ NPN_ReleaseObject(windowObject);
+ break;
+ }
+}
+
+static bool
+asyncCallbackTest(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 (argCount < 1 || !NPVARIANT_IS_STRING(args[0]))
+ return false;
+ const NPString* argstr = &NPVARIANT_TO_STRING(args[0]);
+ id->asyncTestScriptCallback = argstr->UTF8Characters;
+
+ id->asyncTestPhase = 0;
+ id->asyncCallbackResult = true;
+ NPN_PluginThreadAsyncCall(npp, asyncCallback, (void*)npobj);
+
+ return true;
+}
+
+static bool
+GCRaceInvoke(NPObject*, NPIdentifier, const NPVariant*, uint32_t, NPVariant*)
+{
+ return false;
+}
+
+static bool
+GCRaceInvokeDefault(NPObject* o, const NPVariant* args, uint32_t argCount,
+ NPVariant* result)
+{
+ if (1 != argCount || !NPVARIANT_IS_INT32(args[0]) ||
+ 35 != NPVARIANT_TO_INT32(args[0]))
+ return false;
+
+ return true;
+}
+
+static const NPClass kGCRaceClass = {
+ NP_CLASS_STRUCT_VERSION,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ GCRaceInvoke,
+ GCRaceInvokeDefault,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr
+};
+
+struct GCRaceData
+{
+ GCRaceData(NPP npp, NPObject* callback, NPObject* localFunc)
+ : npp_(npp)
+ , callback_(callback)
+ , localFunc_(localFunc)
+ {
+ NPN_RetainObject(callback_);
+ NPN_RetainObject(localFunc_);
+ }
+
+ ~GCRaceData()
+ {
+ NPN_ReleaseObject(callback_);
+ NPN_ReleaseObject(localFunc_);
+ }
+
+ NPP npp_;
+ NPObject* callback_;
+ NPObject* localFunc_;
+};
+
+static void
+FinishGCRace(void* closure)
+{
+ GCRaceData* rd = static_cast<GCRaceData*>(closure);
+
+ XPSleep(5);
+
+ NPVariant arg;
+ OBJECT_TO_NPVARIANT(rd->localFunc_, arg);
+
+ NPVariant result;
+ bool ok = NPN_InvokeDefault(rd->npp_, rd->callback_, &arg, 1, &result);
+ if (!ok)
+ return;
+
+ NPN_ReleaseVariantValue(&result);
+ delete rd;
+}
+
+bool
+checkGCRace(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result)
+{
+ if (1 != argCount || !NPVARIANT_IS_OBJECT(args[0]))
+ return false;
+
+ NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+
+ NPObject* localFunc =
+ NPN_CreateObject(npp, const_cast<NPClass*>(&kGCRaceClass));
+
+ GCRaceData* rd =
+ new GCRaceData(npp, NPVARIANT_TO_OBJECT(args[0]), localFunc);
+ NPN_PluginThreadAsyncCall(npp, FinishGCRace, rd);
+
+ OBJECT_TO_NPVARIANT(localFunc, *result);
+ return true;
+}
+
+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
+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 000000000..4c543d5b9
--- /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 000000000..e77d636a4
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest.h
@@ -0,0 +1,171 @@
+/* ***** 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 TestRange : NPByteRange {
+ bool waiting;
+} TestRange;
+
+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 asyncCallbackResult;
+ 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;
+ int32_t asyncTestPhase;
+ TestFunction testFunction;
+ TestFunction functionToFail;
+ NPError failureCode;
+ NPObject* callOnDestroy;
+ PostMode postMode;
+ std::string testUrl;
+ std::string frame;
+ std::string timerTestScriptCallback;
+ std::string asyncTestScriptCallback;
+ std::ostringstream err;
+ uint16_t streamMode;
+ int32_t streamChunkSize;
+ int32_t streamBufSize;
+ int32_t fileBufSize;
+ TestRange* testrange;
+ void* streamBuf;
+ void* fileBuf;
+ 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;
+ std::string javaCodebase;
+ 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 000000000..948fb846e
--- /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 000000000..9b062e0c2
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_droid.cpp
@@ -0,0 +1,105 @@
+/* ***** 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 {
+};
+ using namespace std;
+
+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, 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 000000000..500db35d2
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_gtk2.cpp
@@ -0,0 +1,774 @@
+/* -*- 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/IntentionalCrash.h"
+
+ using namespace std;
+
+struct _PlatformData {
+#ifdef MOZ_X11
+ Display* display;
+ Visual* visual;
+ Colormap colormap;
+#endif
+ GtkWidget* plug;
+};
+
+bool
+pluginSupportsWindowMode()
+{
+ return true;
+}
+
+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);
+
+ if (!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;
+ // 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 : window.x;
+ int y = instanceData->hasWidget ? 0 : 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;
+
+ if (!instanceData->hasWidget) {
+ 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)
+{
+ 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)
+{
+ 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, string& error)
+{
+}
+
+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);
+ 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 000000000..c38da31dc
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_macosx.mm
@@ -0,0 +1,312 @@
+/* ***** 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>
+
+using namespace std;
+
+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, 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 000000000..f9b57dfa8
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_name.cpp
@@ -0,0 +1,6 @@
+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 000000000..c9542c78b
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_platform.h
@@ -0,0 +1,160 @@
+/* ***** 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 000000000..d33603143
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_utils.cpp
@@ -0,0 +1,113 @@
+/* ***** 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 000000000..78228841e
--- /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 000000000..8b02872e9
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_windows.cpp
@@ -0,0 +1,878 @@
+/* ***** 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>
+
+using namespace std;
+
+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)
+{
+ NPP npp = instanceData->npp;
+
+ 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;
+}
+
+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, 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, 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 000000000..afa83a63c
--- /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 000000000..f95ed4190
--- /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 000000000..c6584387d
--- /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 000000000..835906d0c
--- /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 000000000..12cc68b69
--- /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/silverlightplugin/Info.plist b/dom/plugins/test/testplugin/silverlightplugin/Info.plist
new file mode 100644
index 000000000..7a8094b83
--- /dev/null
+++ b/dom/plugins/test/testplugin/silverlightplugin/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>libnpctrltest.dylib</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.mozilla.SilverlightTestPlugin</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>SILVERLIGHTTEST</string>
+ <key>CFBundleVersion</key>
+ <string>1.0.0.0</string>
+ <key>WebPluginName</key>
+ <string>Silverlight Test Plug-in</string>
+ <key>WebPluginDescription</key>
+ <string>Silverlight plug-in for testing purposes.</string>
+ <key>WebPluginMIMETypes</key>
+ <dict>
+ <key>application/x-silverlight-test</key>
+ <dict>
+ <key>WebPluginExtensions</key>
+ <array>
+ <string>xaml</string>
+ </array>
+ <key>WebPluginTypeDescription</key>
+ <string>Silverlight test type</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/dom/plugins/test/testplugin/silverlightplugin/moz.build b/dom/plugins/test/testplugin/silverlightplugin/moz.build
new file mode 100644
index 000000000..6050e0473
--- /dev/null
+++ b/dom/plugins/test/testplugin/silverlightplugin/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('npctrltest')
+
+relative_path = 'silverlightplugin'
+cocoa_name = 'npctrltest'
+include('../testplugin.mozbuild')
diff --git a/dom/plugins/test/testplugin/silverlightplugin/nptest.def b/dom/plugins/test/testplugin/silverlightplugin/nptest.def
new file mode 100644
index 000000000..b25c6c8c5
--- /dev/null
+++ b/dom/plugins/test/testplugin/silverlightplugin/nptest.def
@@ -0,0 +1,7 @@
+LIBRARY NPCTRLTEST
+
+EXPORTS
+ NP_GetEntryPoints @1
+ NP_Initialize @2
+ NP_Shutdown @3
+ NP_GetMIMEDescription @4
diff --git a/dom/plugins/test/testplugin/silverlightplugin/nptest.rc b/dom/plugins/test/testplugin/silverlightplugin/nptest.rc
new file mode 100644
index 000000000..a48654bc5
--- /dev/null
+++ b/dom/plugins/test/testplugin/silverlightplugin/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"Silverlight plug-in for testing purposes."
+ VALUE "FileExtents", "xaml"
+ VALUE "FileOpenName", "Silverlight test type"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "npctrltest"
+ VALUE "MIMEType", "application/x-silverlight-test"
+ VALUE "OriginalFilename", "npctrltest.dll"
+ VALUE "ProductName", "Silverlight 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/silverlightplugin/nptest_name.cpp b/dom/plugins/test/testplugin/silverlightplugin/nptest_name.cpp
new file mode 100644
index 000000000..2cdfaa5f1
--- /dev/null
+++ b/dom/plugins/test/testplugin/silverlightplugin/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 = "Silverlight Test Plug-in";
+const char *sPluginDescription = "Silverlight plug-in for testing purposes.";
+const char *sMimeDescription = "application/x-silverlight-test:xaml:Silverlight test type";
diff --git a/dom/plugins/test/testplugin/testplugin.mozbuild b/dom/plugins/test/testplugin/testplugin.mozbuild
new file mode 100644
index 000000000..9ed4f8966
--- /dev/null
+++ b/dom/plugins/test/testplugin/testplugin.mozbuild
@@ -0,0 +1,72 @@
+# -*- 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 in ('gtk2', 'gtk3'):
+ UNIFIED_SOURCES += [
+ 'nptest_gtk2.cpp',
+ ]
+elif toolkit == 'android':
+ UNIFIED_SOURCES += [
+ 'nptest_droid.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
+DISABLE_STL_WRAPPING = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ RCFILE = 'nptest.rc'
+ RESFILE = 'nptest.res'
+ DEFFILE = SRCDIR + '/nptest.def'
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' and '64' in CONFIG['OS_TEST']:
+ OS_LIBS += ['-framework Carbon']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
+ 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['_MSC_VER']:
+ # This is intended as a temporary hack to support building with VS2015.
+ # conversion from 'X' to 'Y' requires a narrowing conversion
+ CXXFLAGS += ['-wd4838']
+
+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['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/dom/plugins/test/testplugin/thirdplugin/Info.plist b/dom/plugins/test/testplugin/thirdplugin/Info.plist
new file mode 100644
index 000000000..96e18ba75
--- /dev/null
+++ b/dom/plugins/test/testplugin/thirdplugin/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>libnpthirdtest.dylib</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.mozilla.ThirdTestPlugin</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>THIRDTEST</string>
+ <key>CFBundleVersion</key>
+ <string>1.0.0.0</string>
+ <key>WebPluginName</key>
+ <string>Third Test Plug-in</string>
+ <key>WebPluginDescription</key>
+ <string>Third plug-in for testing purposes.</string>
+ <key>WebPluginMIMETypes</key>
+ <dict>
+ <key>application/x-Third-Test</key>
+ <dict>
+ <key>WebPluginExtensions</key>
+ <array>
+ <string>ts2</string>
+ </array>
+ <key>WebPluginTypeDescription</key>
+ <string>Third test type</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/dom/plugins/test/testplugin/thirdplugin/moz.build b/dom/plugins/test/testplugin/thirdplugin/moz.build
new file mode 100644
index 000000000..f0d7b1a5b
--- /dev/null
+++ b/dom/plugins/test/testplugin/thirdplugin/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('npthirdtest')
+
+relative_path = 'thirdplugin'
+cocoa_name = 'ThirdTest'
+include('../testplugin.mozbuild')
diff --git a/dom/plugins/test/testplugin/thirdplugin/nptest.def b/dom/plugins/test/testplugin/thirdplugin/nptest.def
new file mode 100644
index 000000000..8ea68ba41
--- /dev/null
+++ b/dom/plugins/test/testplugin/thirdplugin/nptest.def
@@ -0,0 +1,7 @@
+LIBRARY NPTHIRDTEST
+
+EXPORTS
+ NP_GetEntryPoints @1
+ NP_Initialize @2
+ NP_Shutdown @3
+ NP_GetMIMEDescription @4
diff --git a/dom/plugins/test/testplugin/thirdplugin/nptest.rc b/dom/plugins/test/testplugin/thirdplugin/nptest.rc
new file mode 100644
index 000000000..de1576920
--- /dev/null
+++ b/dom/plugins/test/testplugin/thirdplugin/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"Third plug-in for testing purposes."
+ VALUE "FileExtents", "ts2"
+ VALUE "FileOpenName", "Third test type"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "npthirdtest"
+ VALUE "MIMEType", "application/x-Third-Test"
+ VALUE "OriginalFilename", "npthirdtest.dll"
+ VALUE "ProductName", "Third 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/thirdplugin/nptest_name.cpp b/dom/plugins/test/testplugin/thirdplugin/nptest_name.cpp
new file mode 100644
index 000000000..34eb5973d
--- /dev/null
+++ b/dom/plugins/test/testplugin/thirdplugin/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 = "Third Test Plug-in";
+const char *sPluginDescription = "Third plug-in for testing purposes.";
+const char *sMimeDescription = "application/x-Third-Test:ts3:Third test type";