summaryrefslogtreecommitdiff
path: root/dom/plugins/test
diff options
context:
space:
mode:
Diffstat (limited to 'dom/plugins/test')
-rw-r--r--dom/plugins/test/crashtests/110650-1.html11
-rw-r--r--dom/plugins/test/crashtests/41276-1.html28
-rw-r--r--dom/plugins/test/crashtests/48856-1.html10
-rw-r--r--dom/plugins/test/crashtests/539897-1.html35
-rw-r--r--dom/plugins/test/crashtests/540114-1.html40
-rw-r--r--dom/plugins/test/crashtests/570884.html8
-rw-r--r--dom/plugins/test/crashtests/598862.html77
-rw-r--r--dom/plugins/test/crashtests/626602-1.html109
-rw-r--r--dom/plugins/test/crashtests/752340.html19
-rw-r--r--dom/plugins/test/crashtests/843086.xhtml1
-rw-r--r--dom/plugins/test/crashtests/crashtests.list14
-rw-r--r--dom/plugins/test/mochitest/307-xo-redirect.sjs6
-rw-r--r--dom/plugins/test/mochitest/browser.ini15
-rw-r--r--dom/plugins/test/mochitest/browser_bug1163570.js96
-rw-r--r--dom/plugins/test/mochitest/browser_bug1196539.js119
-rw-r--r--dom/plugins/test/mochitest/browser_pluginscroll.js302
-rw-r--r--dom/plugins/test/mochitest/browser_tabswitchbetweenplugins.js105
-rw-r--r--dom/plugins/test/mochitest/chrome.ini34
-rw-r--r--dom/plugins/test/mochitest/cocoa_focus.html142
-rw-r--r--dom/plugins/test/mochitest/cocoa_window_focus.html95
-rw-r--r--dom/plugins/test/mochitest/crashing_subpage.html4
-rw-r--r--dom/plugins/test/mochitest/dialog_watcher.js183
-rw-r--r--dom/plugins/test/mochitest/file_authident.js6
-rw-r--r--dom/plugins/test/mochitest/file_bug1245545.js22
-rw-r--r--dom/plugins/test/mochitest/file_bug738396.html87
-rw-r--r--dom/plugins/test/mochitest/file_bug771202.html8
-rw-r--r--dom/plugins/test/mochitest/file_bug863792.html45
-rw-r--r--dom/plugins/test/mochitest/file_checkcookie.sjs17
-rw-r--r--dom/plugins/test/mochitest/file_setcookie.html9
-rw-r--r--dom/plugins/test/mochitest/hang_test.js114
-rw-r--r--dom/plugins/test/mochitest/hangui_common.js20
-rw-r--r--dom/plugins/test/mochitest/hangui_iface.js121
-rw-r--r--dom/plugins/test/mochitest/hangui_subpage.html4
-rw-r--r--dom/plugins/test/mochitest/head.js133
-rw-r--r--dom/plugins/test/mochitest/large-pic.jpgbin0 -> 98570 bytes
-rw-r--r--dom/plugins/test/mochitest/loremipsum.txt11
-rw-r--r--dom/plugins/test/mochitest/loremipsum.xtest11
-rw-r--r--dom/plugins/test/mochitest/loremipsum.xtest^headers^1
-rw-r--r--dom/plugins/test/mochitest/loremipsum_file.txt11
-rw-r--r--dom/plugins/test/mochitest/loremipsum_nocache.txt11
-rw-r--r--dom/plugins/test/mochitest/loremipsum_nocache.txt^headers^1
-rw-r--r--dom/plugins/test/mochitest/mixed_case_mime.sjs8
-rw-r--r--dom/plugins/test/mochitest/mochitest.ini150
-rw-r--r--dom/plugins/test/mochitest/neverending.sjs16
-rw-r--r--dom/plugins/test/mochitest/npruntime_identifiers_subpage.html4
-rw-r--r--dom/plugins/test/mochitest/plugin-stream-referer.sjs12
-rw-r--r--dom/plugins/test/mochitest/plugin-utils.js95
-rw-r--r--dom/plugins/test/mochitest/plugin_no_scroll_div.html10
-rw-r--r--dom/plugins/test/mochitest/plugin_scroll_invalidation.html60
-rw-r--r--dom/plugins/test/mochitest/plugin_subframe_test.html10
-rw-r--r--dom/plugins/test/mochitest/plugin_test.html11
-rw-r--r--dom/plugins/test/mochitest/plugin_visibility_loader.html6
-rw-r--r--dom/plugins/test/mochitest/plugin_window.html23
-rw-r--r--dom/plugins/test/mochitest/pluginstream.js39
-rw-r--r--dom/plugins/test/mochitest/post.sjs17
-rw-r--r--dom/plugins/test/mochitest/privatemode_perwindowpb.xul11
-rw-r--r--dom/plugins/test/mochitest/test_CrashService_crash.html28
-rw-r--r--dom/plugins/test/mochitest/test_CrashService_hang.html28
-rw-r--r--dom/plugins/test/mochitest/test_GCrace.html62
-rw-r--r--dom/plugins/test/mochitest/test_NPNVdocumentOrigin.html47
-rw-r--r--dom/plugins/test/mochitest/test_NPPVpluginWantsAllNetworkStreams.html77
-rw-r--r--dom/plugins/test/mochitest/test_bug1092842.html46
-rw-r--r--dom/plugins/test/mochitest/test_bug1165981.html81
-rw-r--r--dom/plugins/test/mochitest/test_bug1245545.html52
-rw-r--r--dom/plugins/test/mochitest/test_bug1307694.html41
-rw-r--r--dom/plugins/test/mochitest/test_bug406541.html100
-rw-r--r--dom/plugins/test/mochitest/test_bug479979.xul38
-rw-r--r--dom/plugins/test/mochitest/test_bug532208.html29
-rw-r--r--dom/plugins/test/mochitest/test_bug539565-1.html91
-rw-r--r--dom/plugins/test/mochitest/test_bug539565-2.html111
-rw-r--r--dom/plugins/test/mochitest/test_bug738396.html88
-rw-r--r--dom/plugins/test/mochitest/test_bug751809.html84
-rw-r--r--dom/plugins/test/mochitest/test_bug771202.html48
-rw-r--r--dom/plugins/test/mochitest/test_bug777098.html58
-rw-r--r--dom/plugins/test/mochitest/test_bug784131.html85
-rw-r--r--dom/plugins/test/mochitest/test_bug813906.html54
-rw-r--r--dom/plugins/test/mochitest/test_bug827160.html54
-rw-r--r--dom/plugins/test/mochitest/test_bug852315.html62
-rw-r--r--dom/plugins/test/mochitest/test_bug854082.html38
-rw-r--r--dom/plugins/test/mochitest/test_bug863792.html40
-rw-r--r--dom/plugins/test/mochitest/test_bug967694.html78
-rw-r--r--dom/plugins/test/mochitest/test_bug985859.html27
-rw-r--r--dom/plugins/test/mochitest/test_bug986930.html20
-rw-r--r--dom/plugins/test/mochitest/test_busy_hang.xul47
-rw-r--r--dom/plugins/test/mochitest/test_clear_site_data.html242
-rw-r--r--dom/plugins/test/mochitest/test_cocoa_focus.html28
-rw-r--r--dom/plugins/test/mochitest/test_cocoa_window_focus.html28
-rw-r--r--dom/plugins/test/mochitest/test_convertpoint.xul83
-rw-r--r--dom/plugins/test/mochitest/test_cookies.html23
-rw-r--r--dom/plugins/test/mochitest/test_copyText.html40
-rw-r--r--dom/plugins/test/mochitest/test_crash_nested_loop.html46
-rw-r--r--dom/plugins/test/mochitest/test_crash_notify.xul106
-rw-r--r--dom/plugins/test/mochitest/test_crash_notify_no_report.xul116
-rw-r--r--dom/plugins/test/mochitest/test_crash_submit.xul157
-rw-r--r--dom/plugins/test/mochitest/test_crashing.html62
-rw-r--r--dom/plugins/test/mochitest/test_crashing2.html74
-rw-r--r--dom/plugins/test/mochitest/test_defaultValue.html38
-rw-r--r--dom/plugins/test/mochitest/test_enumerate.html36
-rw-r--r--dom/plugins/test/mochitest/test_fullpage.html35
-rw-r--r--dom/plugins/test/mochitest/test_getauthenticationinfo.html81
-rw-r--r--dom/plugins/test/mochitest/test_hang_submit.xul165
-rw-r--r--dom/plugins/test/mochitest/test_hanging.html63
-rw-r--r--dom/plugins/test/mochitest/test_hangui.xul262
-rw-r--r--dom/plugins/test/mochitest/test_hidden_plugin.html44
-rw-r--r--dom/plugins/test/mochitest/test_idle_hang.xul47
-rw-r--r--dom/plugins/test/mochitest/test_instance_re-parent.html123
-rw-r--r--dom/plugins/test/mochitest/test_instance_unparent1.html51
-rw-r--r--dom/plugins/test/mochitest/test_instance_unparent2.html62
-rw-r--r--dom/plugins/test/mochitest/test_instance_unparent3.html54
-rw-r--r--dom/plugins/test/mochitest/test_instantiation.html33
-rw-r--r--dom/plugins/test/mochitest/test_mixed_case_mime.html29
-rw-r--r--dom/plugins/test/mochitest/test_multipleinstanceobjects.html24
-rw-r--r--dom/plugins/test/mochitest/test_newstreamondestroy.html36
-rw-r--r--dom/plugins/test/mochitest/test_npn_asynccall.html33
-rw-r--r--dom/plugins/test/mochitest/test_npn_timers.html33
-rw-r--r--dom/plugins/test/mochitest/test_npobject_getters.html21
-rw-r--r--dom/plugins/test/mochitest/test_npruntime.xul29
-rw-r--r--dom/plugins/test/mochitest/test_npruntime_construct.html32
-rw-r--r--dom/plugins/test/mochitest/test_npruntime_identifiers.html67
-rw-r--r--dom/plugins/test/mochitest/test_npruntime_npnevaluate.html101
-rw-r--r--dom/plugins/test/mochitest/test_npruntime_npninvoke.html162
-rw-r--r--dom/plugins/test/mochitest/test_npruntime_npninvokedefault.html153
-rw-r--r--dom/plugins/test/mochitest/test_object.html510
-rw-r--r--dom/plugins/test/mochitest/test_painting.html121
-rw-r--r--dom/plugins/test/mochitest/test_plugin_scroll_invalidation.html109
-rw-r--r--dom/plugins/test/mochitest/test_plugin_scroll_painting.html64
-rw-r--r--dom/plugins/test/mochitest/test_plugin_tag_clicktoplay.html35
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_3rdparty.html76
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_asfile.html33
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_asfileonly.html30
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_err.html165
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_geturl.html31
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_geturlnotify.html30
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_newstream.html32
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_post.html33
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_poststream.html31
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_referer.html55
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_seek.html33
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_seek_close.html45
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_src.html33
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_src_dynamic.html43
-rw-r--r--dom/plugins/test/mochitest/test_pluginstream_src_referer.html30
-rw-r--r--dom/plugins/test/mochitest/test_positioning.html56
-rw-r--r--dom/plugins/test/mochitest/test_privatemode_perwindowpb.xul120
-rw-r--r--dom/plugins/test/mochitest/test_propertyAndMethod.html51
-rw-r--r--dom/plugins/test/mochitest/test_queryCSSZoomFactor.html48
-rw-r--r--dom/plugins/test/mochitest/test_queryContentsScaleFactor.html31
-rw-r--r--dom/plugins/test/mochitest/test_queryContentsScaleFactorWindowed.html31
-rw-r--r--dom/plugins/test/mochitest/test_redirect_handling.html67
-rw-r--r--dom/plugins/test/mochitest/test_refresh_navigator_plugins.html68
-rw-r--r--dom/plugins/test/mochitest/test_secondPlugin.html73
-rw-r--r--dom/plugins/test/mochitest/test_src_url_change.html42
-rw-r--r--dom/plugins/test/mochitest/test_streamNotify.html89
-rw-r--r--dom/plugins/test/mochitest/test_streamatclose.html46
-rw-r--r--dom/plugins/test/mochitest/test_stringHandling.html35
-rw-r--r--dom/plugins/test/mochitest/test_twostreams.html46
-rw-r--r--dom/plugins/test/mochitest/test_visibility.html100
-rw-r--r--dom/plugins/test/mochitest/test_windowed_invalidate.html66
-rw-r--r--dom/plugins/test/mochitest/test_windowless_flash.html33
-rw-r--r--dom/plugins/test/mochitest/test_windowless_ime.html46
-rw-r--r--dom/plugins/test/mochitest/test_wmode.xul38
-rw-r--r--dom/plugins/test/mochitest/test_x11_error_crash.html27
-rw-r--r--dom/plugins/test/mochitest/test_xulbrowser_plugin_visibility.xul24
-rw-r--r--dom/plugins/test/mochitest/test_zero_opacity.html44
-rw-r--r--dom/plugins/test/mochitest/xulbrowser_plugin_visibility.xul139
-rw-r--r--dom/plugins/test/moz.build15
-rw-r--r--dom/plugins/test/reftest/border-padding-1-ref.html10
-rw-r--r--dom/plugins/test/reftest/border-padding-1.html16
-rw-r--r--dom/plugins/test/reftest/border-padding-2-ref.html17
-rw-r--r--dom/plugins/test/reftest/border-padding-2.html17
-rw-r--r--dom/plugins/test/reftest/border-padding-3-ref.html10
-rw-r--r--dom/plugins/test/reftest/border-padding-3.html35
-rw-r--r--dom/plugins/test/reftest/div-alpha-opacity.html28
-rw-r--r--dom/plugins/test/reftest/div-alpha-zindex.html27
-rw-r--r--dom/plugins/test/reftest/div-sanity.html17
-rw-r--r--dom/plugins/test/reftest/plugin-alpha-opacity.html29
-rw-r--r--dom/plugins/test/reftest/plugin-alpha-zindex.html26
-rw-r--r--dom/plugins/test/reftest/plugin-background-1-step.html22
-rw-r--r--dom/plugins/test/reftest/plugin-background-10-step.html22
-rw-r--r--dom/plugins/test/reftest/plugin-background-2-step.html22
-rw-r--r--dom/plugins/test/reftest/plugin-background-5-step.html22
-rw-r--r--dom/plugins/test/reftest/plugin-background-ref.html17
-rw-r--r--dom/plugins/test/reftest/plugin-background.css61
-rw-r--r--dom/plugins/test/reftest/plugin-background.html20
-rw-r--r--dom/plugins/test/reftest/plugin-background.js75
-rw-r--r--dom/plugins/test/reftest/plugin-busy-alpha-zindex.html56
-rw-r--r--dom/plugins/test/reftest/plugin-canvas-alpha-zindex.html41
-rw-r--r--dom/plugins/test/reftest/plugin-sanity.html13
-rw-r--r--dom/plugins/test/reftest/plugin-transform-1-ref.html10
-rw-r--r--dom/plugins/test/reftest/plugin-transform-1.html10
-rw-r--r--dom/plugins/test/reftest/plugin-transform-2-ref.html7
-rw-r--r--dom/plugins/test/reftest/plugin-transform-2.html13
-rw-r--r--dom/plugins/test/reftest/plugin-transform-alpha-zindex.html28
-rw-r--r--dom/plugins/test/reftest/pluginproblemui-direction-1-ref.html21
-rw-r--r--dom/plugins/test/reftest/pluginproblemui-direction-1.html21
-rw-r--r--dom/plugins/test/reftest/pluginproblemui-direction-2-ref.html25
-rw-r--r--dom/plugins/test/reftest/pluginproblemui-direction-2.html25
-rw-r--r--dom/plugins/test/reftest/reftest-stylo.list33
-rw-r--r--dom/plugins/test/reftest/reftest.list26
-rw-r--r--dom/plugins/test/reftest/shrink-1-ref.html12
-rw-r--r--dom/plugins/test/reftest/shrink-1.html25
-rw-r--r--dom/plugins/test/reftest/update-1-ref.html12
-rw-r--r--dom/plugins/test/reftest/update-1.html59
-rw-r--r--dom/plugins/test/reftest/utils.js18
-rw-r--r--dom/plugins/test/reftest/windowless-clipping-1-ref.html14
-rw-r--r--dom/plugins/test/reftest/windowless-clipping-1.html15
-rw-r--r--dom/plugins/test/reftest/windowless-layers-ref.html10
-rw-r--r--dom/plugins/test/reftest/windowless-layers.html15
-rw-r--r--dom/plugins/test/testaddon/Makefile.in25
-rw-r--r--dom/plugins/test/testaddon/install.rdf23
-rw-r--r--dom/plugins/test/testaddon/moz.build5
-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
-rw-r--r--dom/plugins/test/unit/head_plugins.js195
-rw-r--r--dom/plugins/test/unit/test_allowed_types.js142
-rw-r--r--dom/plugins/test/unit/test_bug471245.js23
-rw-r--r--dom/plugins/test/unit/test_bug813245.js87
-rw-r--r--dom/plugins/test/unit/test_bug854467.js40
-rw-r--r--dom/plugins/test/unit/test_nice_plugin_name.js80
-rw-r--r--dom/plugins/test/unit/test_persist_in_prefs.js76
-rw-r--r--dom/plugins/test/unit/test_plugin_default_state.js31
-rw-r--r--dom/plugins/test/unit/test_plugin_default_state_xpi.js118
-rw-r--r--dom/plugins/test/unit/xpcshell.ini29
262 files changed, 19506 insertions, 0 deletions
diff --git a/dom/plugins/test/crashtests/110650-1.html b/dom/plugins/test/crashtests/110650-1.html
new file mode 100644
index 0000000000..9826227b03
--- /dev/null
+++ b/dom/plugins/test/crashtests/110650-1.html
@@ -0,0 +1,11 @@
+<HTML>
+<HEAD>
+<TITLE>123246 testcase</TITLE>
+</HEAD>
+<BODY>
+<object align="right" width=100>
+</object>
+</BODY >
+</HTML>
+
+
diff --git a/dom/plugins/test/crashtests/41276-1.html b/dom/plugins/test/crashtests/41276-1.html
new file mode 100644
index 0000000000..ba35c34fa0
--- /dev/null
+++ b/dom/plugins/test/crashtests/41276-1.html
@@ -0,0 +1,28 @@
+<HTML><HEAD><TITLE>Plugin Limit</TITLE></HEAD>
+<BODY>
+
+Mozilla has a hardcoded limit of 10 simultaneously embedded plugins.<br>
+If that limit is exceeded, plugin instances are prematurely destroyed (see the empty boxes below).<br>
+Leave or reload a page that has a prematurely destroyed plugin and mozilla will crash.<br>
+Sometimes, just loading this page will cause mozilla to crash.<br>
+
+
+<br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player">
+<embed TYPE=audio/x-pn-realaudio-plugin WIDTH=300 HEIGHT=75 CONTROLS="Default" AUTOSTART="FALSE" CONSOLE="RA_Player"><br>
+<br>
+</BODY></HTML>
diff --git a/dom/plugins/test/crashtests/48856-1.html b/dom/plugins/test/crashtests/48856-1.html
new file mode 100644
index 0000000000..cd0de2ab94
--- /dev/null
+++ b/dom/plugins/test/crashtests/48856-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+ <HEAD>
+ <TITLE>Mozilla Bug 48856</TITLE>
+ </HEAD>
+ <BODY>
+ <EMBED></EMBED>
+ </BODY>
+</HTML>
diff --git a/dom/plugins/test/crashtests/539897-1.html b/dom/plugins/test/crashtests/539897-1.html
new file mode 100644
index 0000000000..f280e62e6e
--- /dev/null
+++ b/dom/plugins/test/crashtests/539897-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+function crashplugin() {
+ var plugin = document.getElementById('p');
+ plugin.reinitWidget();
+ plugin.reinitWidget();
+}
+
+function getTestCases() {
+ return [
+ { testPassed:
+ (function () {
+ var plugin = document.getElementById('p');
+ try {
+ plugin.getPaintCount();
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }),
+ testDescription:
+ (function () {
+ return "plugin should not crash";
+ })
+ }
+ ];
+}
+</script>
+</head>
+<body onload="crashplugin();">
+<embed id="p" type="application/x-test" wmode="window"/>
+</body>
+</html>
diff --git a/dom/plugins/test/crashtests/540114-1.html b/dom/plugins/test/crashtests/540114-1.html
new file mode 100644
index 0000000000..8243649aa3
--- /dev/null
+++ b/dom/plugins/test/crashtests/540114-1.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script type="text/javascript">
+function crashplugin() {
+ var plugin = document.getElementById('removeme');
+ var flush_reflow = plugin.offsetHeight; // this may not be necessary
+ document.body.removeChild(plugin);
+ // Give the plugin time to crash
+ setTimeout(function() { document.documentElement.removeAttribute('class') },
+ 1000);
+}
+
+function getTestCases() {
+ return [
+ { testPassed:
+ (function () {
+ // Assuming the same process is used for removeme and checkme
+ var plugin = document.getElementById('checkme');
+ try {
+ plugin.getPaintCount();
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }),
+ testDescription:
+ (function () {
+ return "plugin should not crash";
+ })
+ }
+ ];
+}
+</script>
+</head>
+<body onload="crashplugin();">
+<embed id="checkme" type="application/x-test"/>
+<embed id="removeme" type="application/x-test" wmode="window" cleanupwidget="false"/>
+</body>
+</html>
diff --git a/dom/plugins/test/crashtests/570884.html b/dom/plugins/test/crashtests/570884.html
new file mode 100644
index 0000000000..7af5cdba53
--- /dev/null
+++ b/dom/plugins/test/crashtests/570884.html
@@ -0,0 +1,8 @@
+<body>
+<embed type="application/x-test" width="20"></embed>
+<embed type="application/x-test" width="10"></embed>
+<embed type="application/x-test" width="0"></embed>
+<embed type="application/x-test" width="20" height="20"></embed>
+<embed type="application/x-test" width="10" height="10"></embed>
+<embed type="application/x-test" width="0" height="0"></embed>
+</body>
diff --git a/dom/plugins/test/crashtests/598862.html b/dom/plugins/test/crashtests/598862.html
new file mode 100644
index 0000000000..9b1ad38d75
--- /dev/null
+++ b/dom/plugins/test/crashtests/598862.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <head>
+ <script>
+var unusedScreenX;
+function XSync() {
+ unusedScreenX = window.screenX;
+}
+
+function finish() {
+ document.documentElement.removeAttribute("class");
+}
+
+function cleanup() {
+ try {
+ document.getElementById("plugin").crash();
+ } catch (dontcare) {
+ }
+ window.setTimeout(finish, 100);
+}
+
+function scrollOfDeath() {
+ // Add a listener for the MozAfterPaint after the scrollTo below.
+ // If we don't crash during the scroll, we'll get the
+ // MozAfterPaint.
+ window.addEventListener("MozAfterPaint", cleanup, false);
+ window.scrollTo(0, 50);
+}
+
+//
+// The sequence of events we expect is
+//
+// load (including initial paints of plugin that are cached)
+// destroy X resources
+// [X server has time to observe resource destruction]
+// scrollTo
+// [repaint using cached plugin surface: BOOM if buggy]
+// MozAfterPaint
+// cleanup
+//
+// However, this test is fundamentally nondeterministic. There are
+// two main "failure" modes
+// (1) X server doesn't have time to observer resource destruction
+// before paint-after-scroll
+// (2) plugin's crash notification arrives before
+// paint-after-scroll
+// Both result in spurious passes.
+//
+// This test is anal about cleanup because it must be pretty sure that
+// the plugin subprocess is gone before starting the next test. We
+// double-check that the plugin is gone by the time we're done by
+// trying to crash it again, after we expect it to have crashed already.
+//
+function runTest() {
+ // Have the plugin throw away its X resources, one of which is
+ // probably a drawable to which we hold a reference
+ document.getElementById("plugin").destroySharedGfxStuff();
+
+ // Do something that's (hopefully) equivalent to an XSync() to allow
+ // the resource destruction to propagate to the server
+ XSync();
+
+ // Set up a scroll to happen soon
+ window.setTimeout(scrollOfDeath, 100);
+}
+
+window.addEventListener("MozReftestInvalidate", runTest, false);
+ </script>
+ </head>
+
+ <body style="width: 400px; height: 10000px;">
+ <embed id="plugin" type="application/x-test"
+ style="position:absolute;
+ top:100px; left:50px; width:200px; height:200px;">
+ </embed>
+ </body>
+</html>
diff --git a/dom/plugins/test/crashtests/626602-1.html b/dom/plugins/test/crashtests/626602-1.html
new file mode 100644
index 0000000000..0a878bbd1d
--- /dev/null
+++ b/dom/plugins/test/crashtests/626602-1.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <style type="text/css">
+#one {
+ position:absolute;
+ left:0px; top:0px;
+ z-index:4;
+}
+#two {
+ position:absolute;
+ top:100px; left:100px;
+ background-color:rgb(0,0,0,0);
+ z-index:3;
+}
+#three {
+ position:absolute;
+ left:100px; top:100px;
+ width:200px; height:200px;
+ background-color: rgb(255,0,0);
+ opacity:0.6;
+ z-index:2;
+}
+#four {
+ position:absolute;
+ top:100px; left:100px;
+ z-index:1;
+}
+ </style>
+ <script type="text/javascript">
+var plugin, div, canvas;
+function start() {
+ plugin = document.getElementById("four");
+ div = document.getElementById("three");
+ canvas = document.getElementById("two");
+ paintCanvas();
+
+ requestAnimationFrame(moveSomething);
+}
+
+function paintCanvas() {
+ var ctx = canvas.getContext("2d");
+ ctx.fillStyle = "rgba(255,0,0, 0.6)";
+ ctx.fillRect(0,0, 200,200);
+}
+
+var i = 0, numLoops = 20;
+var pluginIn = true, divIn = true, canvasIn = true;
+function moveSomething() {
+ var didSomething = (0 === (i % 2)) ? moveSomethingOut() : moveSomethingIn();
+ if (!didSomething && ++i >= numLoops) {
+ return finish();
+ }
+
+ requestAnimationFrame(moveSomething);
+}
+
+function finish() {
+ document.documentElement.removeAttribute("class");
+}
+
+function moveSomethingOut() {
+ if (pluginIn) {
+ plugin.style.left = "400px";
+ pluginIn = false;
+ } else if (divIn) {
+ div.style.left = "400px";
+ divIn = false;
+ } else if (canvasIn) {
+ canvas.style.left = "400px";
+ canvasIn = false;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+function moveSomethingIn() {
+ if (!pluginIn) {
+ plugin.style.left = "100px";
+ pluginIn = true;
+ } else if (!divIn) {
+ div.style.left = "100px";
+ divIn = true;
+ } else if (!canvasIn) {
+ canvas.style.left = "100px";
+ canvasIn = true;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+function reset() {
+
+}
+ </script>
+</style>
+</head>
+<body onload="start();">
+ <embed id="four" type="application/x-test" width="200" height="200"
+ drawmode="solid" color="FFFF0000"></embed>
+ <div id="three"></div>
+ <canvas id="two" width="200" height="200"></canvas>
+ <embed id="one" type="application/x-test" width="400" height="400"
+ drawmode="solid" color="9900FF00"></embed>
+</body>
+</html>
+
diff --git a/dom/plugins/test/crashtests/752340.html b/dom/plugins/test/crashtests/752340.html
new file mode 100644
index 0000000000..c4c8c464f5
--- /dev/null
+++ b/dom/plugins/test/crashtests/752340.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="text/javascript">
+ // Failures in this file can manifest as ###!!! ASSERTION: scope has non-empty map: '0 == mWrappedNativeMap->Count()'
+ // followed by an Assertion failure: allocated() crash during the next GC.
+ // It can also manifest as a leak.
+ function breakthings() {
+ var e = document.createElement("embed");
+ var i = document.getElementById("i");
+ i.contentDocument.body.appendChild(e);
+ i.src = "about:blank";
+ }
+ </script>
+</head>
+<body onload="javascript:breakthings();">
+<iframe id="i" />
+</body>
+</html>
diff --git a/dom/plugins/test/crashtests/843086.xhtml b/dom/plugins/test/crashtests/843086.xhtml
new file mode 100644
index 0000000000..a88e2193fc
--- /dev/null
+++ b/dom/plugins/test/crashtests/843086.xhtml
@@ -0,0 +1 @@
+<applet xmlns="http://www.w3.org/1999/xhtml" />
diff --git a/dom/plugins/test/crashtests/crashtests.list b/dom/plugins/test/crashtests/crashtests.list
new file mode 100644
index 0000000000..aec2195f26
--- /dev/null
+++ b/dom/plugins/test/crashtests/crashtests.list
@@ -0,0 +1,14 @@
+load 41276-1.html
+load 48856-1.html
+load 110650-1.html
+skip-if(!haveTestPlugin) script 539897-1.html
+asserts-if(winWidget&&browserIsRemote,0-1) skip-if(!haveTestPlugin) script 540114-1.html
+load 570884.html
+# This test relies on the reading of screenX/Y forcing a round trip to
+# the X server, which is a bad assumption for <browser remote>.
+# Plugin arch is going to change anyway with OOP content so skipping
+# this test for now is OK.
+skip-if(!haveTestPlugin||http.platform!="X11") load 598862.html
+skip-if(Android) load 626602-1.html # bug 908363
+load 752340.html
+load 843086.xhtml
diff --git a/dom/plugins/test/mochitest/307-xo-redirect.sjs b/dom/plugins/test/mochitest/307-xo-redirect.sjs
new file mode 100644
index 0000000000..b880978cea
--- /dev/null
+++ b/dom/plugins/test/mochitest/307-xo-redirect.sjs
@@ -0,0 +1,6 @@
+function handleRequest(request, response)
+{
+ response.setStatusLine(request.httpVersion, 307, "Moved temporarily");
+ response.setHeader("Location", "http://example.org/tests/dom/plugins/test/mochitest/loremipsum.txt");
+ response.setHeader("Content-Type", "text/html");
+}
diff --git a/dom/plugins/test/mochitest/browser.ini b/dom/plugins/test/mochitest/browser.ini
new file mode 100644
index 0000000000..a28a22f108
--- /dev/null
+++ b/dom/plugins/test/mochitest/browser.ini
@@ -0,0 +1,15 @@
+[DEFAULT]
+support-files =
+ head.js
+ plugin_test.html
+ plugin_subframe_test.html
+ plugin_no_scroll_div.html
+
+[browser_bug1163570.js]
+skip-if = true # Bug 1249878
+[browser_bug1196539.js]
+skip-if = (!e10s || os != "win")
+[browser_tabswitchbetweenplugins.js]
+skip-if = (!e10s || os != "win")
+[browser_pluginscroll.js]
+skip-if = (true || !e10s || os != "win") # Bug 1213631
diff --git a/dom/plugins/test/mochitest/browser_bug1163570.js b/dom/plugins/test/mochitest/browser_bug1163570.js
new file mode 100644
index 0000000000..c6a75d2ec8
--- /dev/null
+++ b/dom/plugins/test/mochitest/browser_bug1163570.js
@@ -0,0 +1,96 @@
+var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
+
+// simple tab load helper, pilfered from browser plugin tests
+function promiseTabLoad(tab, url, eventType="load") {
+ return new Promise((resolve) => {
+ function handle(event) {
+ if (event.originalTarget != tab.linkedBrowser.contentDocument ||
+ event.target.location.href == "about:blank" ||
+ (url && event.target.location.href != url)) {
+ return;
+ }
+ tab.linkedBrowser.removeEventListener(eventType, handle, true);
+ resolve(event);
+ }
+
+ tab.linkedBrowser.addEventListener(eventType, handle, true, true);
+ if (url) {
+ tab.linkedBrowser.loadURI(url);
+ }
+ });
+}
+
+// dom event listener helper
+function promiseWaitForEvent(object, eventName, capturing = false, chrome = false) {
+ return new Promise((resolve) => {
+ function listener(event) {
+ object.removeEventListener(eventName, listener, capturing, chrome);
+ resolve(event);
+ }
+ object.addEventListener(eventName, listener, capturing, chrome);
+ });
+}
+
+add_task(function* () {
+ registerCleanupFunction(function () {
+ window.focus();
+ });
+});
+
+add_task(function* () {
+ setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
+
+ let pluginTab = gBrowser.selectedTab = gBrowser.addTab();
+ let prefTab = gBrowser.addTab();
+
+ yield promiseTabLoad(pluginTab, gTestRoot + "plugin_test.html");
+ yield promiseTabLoad(prefTab, "about:preferences");
+
+ yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ Assert.ok(!!plugin, "plugin is loaded");
+ });
+
+ let ppromise = promiseWaitForEvent(window, "MozAfterPaint");
+ gBrowser.selectedTab = prefTab;
+ yield ppromise;
+
+ // We're going to switch tabs using actual mouse clicks, which helps
+ // reproduce this bug.
+ let tabStripContainer = document.getElementById("tabbrowser-tabs");
+
+ // diagnosis if front end layout changes
+ info("-> " + tabStripContainer.tagName); // tabs
+ info("-> " + tabStripContainer.firstChild.tagName); // tab
+ info("-> " + tabStripContainer.childNodes[0].label); // test harness tab
+ info("-> " + tabStripContainer.childNodes[1].label); // plugin tab
+ info("-> " + tabStripContainer.childNodes[2].label); // preferences tab
+
+ for (let iteration = 0; iteration < 5; iteration++) {
+ ppromise = promiseWaitForEvent(window, "MozAfterPaint");
+ EventUtils.synthesizeMouseAtCenter(tabStripContainer.childNodes[1], {}, window);
+ yield ppromise;
+
+ yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ Assert.ok(XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible(),
+ "plugin is visible");
+ });
+
+ ppromise = promiseWaitForEvent(window, "MozAfterPaint");
+ EventUtils.synthesizeMouseAtCenter(tabStripContainer.childNodes[2], {}, window);
+ yield ppromise;
+
+ yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ Assert.ok(!XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible(),
+ "plugin is hidden");
+ });
+ }
+
+ gBrowser.removeTab(prefTab);
+ gBrowser.removeTab(pluginTab);
+});
diff --git a/dom/plugins/test/mochitest/browser_bug1196539.js b/dom/plugins/test/mochitest/browser_bug1196539.js
new file mode 100644
index 0000000000..8b2210ccd0
--- /dev/null
+++ b/dom/plugins/test/mochitest/browser_bug1196539.js
@@ -0,0 +1,119 @@
+var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
+
+function checkPaintCount(aCount) {
+ ok(aCount != 0, "paint count can't be greater than zero, count was " + aCount);
+ ok(aCount < kMaxPaints, "paint count should be within limits, count was " + aCount);
+}
+
+// maximum number of paints we allow before failing. The test plugin doesn't
+// animate so this should really be just 1, but operating systems can
+// occasionally fire a few of these so we give these tests a fudge factor.
+// A bad regression would either be 0, or 100+.
+const kMaxPaints = 10;
+
+add_task(function* () {
+ let result, tabSwitchedPromise;
+
+ setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
+
+ let testTab = gBrowser.selectedTab;
+ let pluginTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gTestRoot + "plugin_test.html");
+ let homeTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home");
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return !!plugin;
+ });
+ is(result, true, "plugin is loaded");
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return !XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin is hidden");
+
+ // reset plugin paint count
+ yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ XPCNativeWrapper.unwrap(plugin).resetPaintCount();
+ });
+
+ // select plugin tab
+ tabSwitchedPromise = waitTabSwitched();
+ gBrowser.selectedTab = pluginTab;
+ yield tabSwitchedPromise;
+
+ // wait a bit for spurious paints
+ yield waitForMs(100);
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin is visible");
+
+ // check for good paint count
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).getPaintCount();
+ });
+ checkPaintCount(result);
+
+ // select home tab
+ tabSwitchedPromise = waitTabSwitched();
+ gBrowser.selectedTab = homeTab;
+ yield tabSwitchedPromise;
+
+ // reset paint count
+ yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ XPCNativeWrapper.unwrap(plugin).resetPaintCount();
+ });
+
+ // wait a bit for spurious paints
+ yield waitForMs(100);
+
+ // check for no paint count
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).getPaintCount();
+ });
+ is(result, 0, "no paints, this is correct.");
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return !XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin is hidden");
+
+ // reset paint count
+ yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ XPCNativeWrapper.unwrap(plugin).resetPaintCount();
+ });
+
+ // select plugin tab
+ tabSwitchedPromise = waitTabSwitched();
+ gBrowser.selectedTab = pluginTab;
+ yield tabSwitchedPromise;
+
+ // check paint count
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).getPaintCount();
+ });
+ checkPaintCount(result);
+
+ gBrowser.removeTab(homeTab);
+ gBrowser.removeTab(pluginTab);
+});
diff --git a/dom/plugins/test/mochitest/browser_pluginscroll.js b/dom/plugins/test/mochitest/browser_pluginscroll.js
new file mode 100644
index 0000000000..7314b84103
--- /dev/null
+++ b/dom/plugins/test/mochitest/browser_pluginscroll.js
@@ -0,0 +1,302 @@
+var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
+
+/**
+ * tests for plugin windows and scroll
+ */
+
+function coordinatesRelativeToWindow(aX, aY, aElement) {
+ var targetWindow = aElement.ownerDocument.defaultView;
+ var scale = targetWindow.devicePixelRatio;
+ var rect = aElement.getBoundingClientRect();
+ return {
+ x: targetWindow.mozInnerScreenX + ((rect.left + aX) * scale),
+ y: targetWindow.mozInnerScreenY + ((rect.top + aY) * scale)
+ };
+}
+
+var apzEnabled = Preferences.get("layers.async-pan-zoom.enabled", false);
+var pluginHideEnabled = Preferences.get("gfx.e10s.hide-plugins-for-scroll", true);
+
+
+add_task(function* () {
+ registerCleanupFunction(function () {
+ setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
+ });
+});
+
+add_task(function*() {
+ yield new Promise((resolve) => {
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["general.smoothScroll", true],
+ ["general.smoothScroll.other", true],
+ ["general.smoothScroll.mouseWheel", true],
+ ["general.smoothScroll.other.durationMaxMS", 2000],
+ ["general.smoothScroll.other.durationMinMS", 1999],
+ ["general.smoothScroll.mouseWheel.durationMaxMS", 2000],
+ ["general.smoothScroll.mouseWheel.durationMinMS", 1999],
+ ]}, resolve);
+ });
+});
+
+/*
+ * test plugin visibility when scrolling with scroll wheel and apz in a top level document.
+ */
+
+add_task(function* () {
+ let result;
+
+ if (!apzEnabled) {
+ ok(true, "nothing to test, need apz");
+ return;
+ }
+
+ if (!pluginHideEnabled) {
+ ok(true, "nothing to test, need gfx.e10s.hide-plugins-for-scroll");
+ return;
+ }
+
+ setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
+
+ let testTab = gBrowser.selectedTab;
+ let pluginTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gTestRoot + "plugin_test.html");
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return !!plugin;
+ });
+ is(result, true, "plugin is loaded");
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin is visible");
+
+ let nativeId = nativeVerticalWheelEventMsg();
+ let utils = SpecialPowers.getDOMWindowUtils(window);
+ let screenCoords = coordinatesRelativeToWindow(10, 10,
+ gBrowser.selectedBrowser);
+ utils.sendNativeMouseScrollEvent(screenCoords.x, screenCoords.y,
+ nativeId, 0, -50, 0, 0, 0,
+ gBrowser.selectedBrowser);
+
+ yield waitScrollStart(gBrowser.selectedBrowser);
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, false, "plugin is hidden");
+
+ yield waitScrollFinish(gBrowser.selectedBrowser);
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin is visible");
+
+ gBrowser.removeTab(pluginTab);
+});
+
+/*
+ * test plugin visibility when scrolling with scroll wheel and apz in a sub document.
+ */
+
+add_task(function* () {
+ let result;
+
+ if (!apzEnabled) {
+ ok(true, "nothing to test, need apz");
+ return;
+ }
+
+ if (!pluginHideEnabled) {
+ ok(true, "nothing to test, need gfx.e10s.hide-plugins-for-scroll");
+ return;
+ }
+
+ setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
+
+ let testTab = gBrowser.selectedTab;
+ let pluginTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gTestRoot + "plugin_subframe_test.html");
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document.getElementById("subframe").contentDocument;
+ let plugin = doc.getElementById("testplugin");
+ return !!plugin;
+ });
+ is(result, true, "plugin is loaded");
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document.getElementById("subframe").contentDocument;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin is visible");
+
+ let nativeId = nativeVerticalWheelEventMsg();
+ let utils = SpecialPowers.getDOMWindowUtils(window);
+ let screenCoords = coordinatesRelativeToWindow(10, 10,
+ gBrowser.selectedBrowser);
+ utils.sendNativeMouseScrollEvent(screenCoords.x, screenCoords.y,
+ nativeId, 0, -50, 0, 0, 0,
+ gBrowser.selectedBrowser);
+
+ yield waitScrollStart(gBrowser.selectedBrowser);
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document.getElementById("subframe").contentDocument;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, false, "plugin is hidden");
+
+ yield waitScrollFinish(gBrowser.selectedBrowser);
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document.getElementById("subframe").contentDocument;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin is visible");
+
+ gBrowser.removeTab(pluginTab);
+});
+
+/*
+ * test visibility when scrolling with keyboard shortcuts for a top level document.
+ * This circumvents apz and relies on dom scroll, which is what we want to target
+ * for this test.
+ */
+
+add_task(function* () {
+ let result;
+
+ if (!pluginHideEnabled) {
+ ok(true, "nothing to test, need gfx.e10s.hide-plugins-for-scroll");
+ return;
+ }
+
+ setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
+
+ let testTab = gBrowser.selectedTab;
+ let pluginTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gTestRoot + "plugin_test.html");
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return !!plugin;
+ });
+ is(result, true, "plugin is loaded");
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin is visible");
+
+ EventUtils.synthesizeKey("VK_END", {});
+
+ yield waitScrollStart(gBrowser.selectedBrowser);
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, false, "plugin is hidden");
+
+ yield waitScrollFinish(gBrowser.selectedBrowser);
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, false, "plugin is hidden");
+
+ EventUtils.synthesizeKey("VK_HOME", {});
+
+ yield waitScrollFinish(gBrowser.selectedBrowser);
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin is visible");
+
+ gBrowser.removeTab(pluginTab);
+});
+
+/*
+ * test visibility when scrolling with keyboard shortcuts for a sub document.
+ */
+
+add_task(function* () {
+ let result;
+
+ if (!pluginHideEnabled) {
+ ok(true, "nothing to test, need gfx.e10s.hide-plugins-for-scroll");
+ return;
+ }
+
+ setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
+
+ let testTab = gBrowser.selectedTab;
+ let pluginTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gTestRoot + "plugin_subframe_test.html");
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document.getElementById("subframe").contentDocument;
+ let plugin = doc.getElementById("testplugin");
+ return !!plugin;
+ });
+ is(result, true, "plugin is loaded");
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document.getElementById("subframe").contentDocument;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin is visible");
+
+ EventUtils.synthesizeKey("VK_END", {});
+
+ yield waitScrollStart(gBrowser.selectedBrowser);
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document.getElementById("subframe").contentDocument;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, false, "plugin is hidden");
+
+ yield waitScrollFinish(gBrowser.selectedBrowser);
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document.getElementById("subframe").contentDocument;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, false, "plugin is hidden");
+
+ EventUtils.synthesizeKey("VK_HOME", {});
+
+ yield waitScrollFinish(gBrowser.selectedBrowser);
+
+ result = yield ContentTask.spawn(pluginTab.linkedBrowser, null, function*() {
+ let doc = content.document.getElementById("subframe").contentDocument;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin is visible");
+
+ gBrowser.removeTab(pluginTab);
+});
diff --git a/dom/plugins/test/mochitest/browser_tabswitchbetweenplugins.js b/dom/plugins/test/mochitest/browser_tabswitchbetweenplugins.js
new file mode 100644
index 0000000000..d1994c209f
--- /dev/null
+++ b/dom/plugins/test/mochitest/browser_tabswitchbetweenplugins.js
@@ -0,0 +1,105 @@
+var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
+
+// tests that we get plugin updates when we flip between tabs that
+// have the same plugin in the same position in the page.
+
+add_task(function* () {
+ let result, tabSwitchedPromise;
+
+ setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
+
+ let testTab = gBrowser.selectedTab;
+ let pluginTab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gTestRoot + "plugin_test.html");
+ let pluginTab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gTestRoot + "plugin_test.html");
+
+ result = yield ContentTask.spawn(pluginTab1.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return !!plugin;
+ });
+ is(result, true, "plugin1 is loaded");
+
+ result = yield ContentTask.spawn(pluginTab2.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return !!plugin;
+ });
+ is(result, true, "plugin2 is loaded");
+
+ // plugin tab 2 should be selected
+ is(gBrowser.selectedTab == pluginTab2, true, "plugin2 is selected");
+
+ result = yield ContentTask.spawn(pluginTab1.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, false, "plugin1 is hidden");
+
+ result = yield ContentTask.spawn(pluginTab2.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin2 is visible");
+
+ // select plugin1 tab
+ tabSwitchedPromise = waitTabSwitched();
+ gBrowser.selectedTab = pluginTab1;
+ yield tabSwitchedPromise;
+
+ result = yield ContentTask.spawn(pluginTab1.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin1 is visible");
+
+ result = yield ContentTask.spawn(pluginTab2.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, false, "plugin2 is hidden");
+
+ // select plugin2 tab
+ tabSwitchedPromise = waitTabSwitched();
+ gBrowser.selectedTab = pluginTab2;
+ yield tabSwitchedPromise;
+
+ result = yield ContentTask.spawn(pluginTab1.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, false, "plugin1 is hidden");
+
+ result = yield ContentTask.spawn(pluginTab2.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, true, "plugin2 is visible");
+
+ // select test tab
+ tabSwitchedPromise = waitTabSwitched();
+ gBrowser.selectedTab = testTab;
+ yield tabSwitchedPromise;
+
+ result = yield ContentTask.spawn(pluginTab1.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, false, "plugin1 is hidden");
+
+ result = yield ContentTask.spawn(pluginTab2.linkedBrowser, null, function*() {
+ let doc = content.document;
+ let plugin = doc.getElementById("testplugin");
+ return XPCNativeWrapper.unwrap(plugin).nativeWidgetIsVisible();
+ });
+ is(result, false, "plugin2 is hidden");
+
+ gBrowser.removeTab(pluginTab1);
+ gBrowser.removeTab(pluginTab2);
+});
diff --git a/dom/plugins/test/mochitest/chrome.ini b/dom/plugins/test/mochitest/chrome.ini
new file mode 100644
index 0000000000..35a1c7ddcd
--- /dev/null
+++ b/dom/plugins/test/mochitest/chrome.ini
@@ -0,0 +1,34 @@
+[DEFAULT]
+support-files =
+ hang_test.js
+ privatemode_perwindowpb.xul
+ plugin-utils.js
+
+[test_bug479979.xul]
+[test_bug751809.html]
+[test_busy_hang.xul]
+skip-if = (!crashreporter) || (os != "win")
+[test_clear_site_data.html]
+[test_convertpoint.xul]
+skip-if = toolkit != "cocoa"
+[test_crash_notify.xul]
+skip-if = !crashreporter
+[test_crash_notify_no_report.xul]
+skip-if = !crashreporter
+[test_crash_submit.xul]
+skip-if = !crashreporter
+[test_hang_submit.xul]
+skip-if = !crashreporter
+[test_hangui.xul]
+skip-if = (!crashreporter) || (os != "win")
+support-files = hangui_subpage.html hangui_common.js hangui_iface.js dialog_watcher.js
+[test_idle_hang.xul]
+skip-if = (!crashreporter) || (os != "win")
+[test_npruntime.xul]
+[test_plugin_tag_clicktoplay.html]
+[test_privatemode_perwindowpb.xul]
+[test_refresh_navigator_plugins.html]
+[test_xulbrowser_plugin_visibility.xul]
+skip-if = (toolkit == "cocoa") || (os == "win")
+support-files = xulbrowser_plugin_visibility.xul plugin_visibility_loader.html
+[test_wmode.xul]
diff --git a/dom/plugins/test/mochitest/cocoa_focus.html b/dom/plugins/test/mochitest/cocoa_focus.html
new file mode 100644
index 0000000000..ca35fd7a1f
--- /dev/null
+++ b/dom/plugins/test/mochitest/cocoa_focus.html
@@ -0,0 +1,142 @@
+<html>
+<head>
+ <title>NPCocoaEventFocusChanged Tests</title>
+</head>
+<body>
+ <embed id="plugin1" type="application/x-test" width="100" height="100"></embed>
+ <embed id="plugin2" type="application/x-test" width="100" height="100"></embed>
+ <script type="application/javascript">
+ function is(aLeft, aRight, aMessage) {
+ window.opener.SimpleTest.is(aLeft, aRight, aMessage);
+ }
+
+ function ok(aValue, aMessage) {
+ window.opener.SimpleTest.ok(aValue, aMessage);
+ }
+
+ function synthesizeNativeMouseEvent(aX, aY, aNativeMsg, aModifiers, aElement, aCallback) {
+ var observer = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aCallback && aTopic == "mouseevent") {
+ aCallback(aData);
+ }
+ }
+ };
+ SpecialPowers.DOMWindowUtils.sendNativeMouseEvent(aX, aY, aNativeMsg, aModifiers, aElement, observer);
+ return true;
+ }
+
+ function* runTests() {
+ var utils = SpecialPowers.DOMWindowUtils;
+ var scale = utils.screenPixelsPerCSSPixel;
+
+ var plugin1 = document.getElementById("plugin1"); // What we're testing.
+ var plugin2 = document.getElementById("plugin2"); // Dummy.
+
+ var plugin1Bounds = plugin1.getBoundingClientRect();
+ var plugin2Bounds = plugin2.getBoundingClientRect();
+
+ var plugin1X = (window.mozInnerScreenX + plugin1Bounds.left + 10);
+ var plugin1Y = (window.mozInnerScreenY + plugin1Bounds.top + 10);
+ var plugin2X = (window.mozInnerScreenX + plugin2Bounds.left + 10);
+ var plugin2Y = (window.mozInnerScreenY + plugin2Bounds.top + 10);
+
+ const NSLeftMouseDown = 1,
+ NSLeftMouseUp = 2;
+
+ if (plugin1.getEventModel() != 1) {
+ window.opener.todo(false, "Skipping this test when not testing the Cocoa event model");
+ return;
+ }
+
+ // Initialize to 0 since there is no initial state event,
+ // plugins should assume they do not initially have focus.
+ var expectedEventCount = 0;
+
+ // Make sure initial event count is correct.
+ is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
+
+ // Make sure initial focus state is unknown (assumed false).
+ var initialStateUnknown = false;
+ try {
+ plugin1.getFocusState();
+ } catch (e) {
+ initialStateUnknown = true;
+ }
+ is(initialStateUnknown, true, "Initial state should be unknown, assumed false.");
+
+ // Give the plugin focus (the window is already focused).
+ yield synthesizeNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseDown, 0, plugin1, continueTest);
+ yield synthesizeNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseUp, 0, plugin1, continueTest);
+ expectedEventCount++;
+
+ is(plugin1.getFocusState(), true, "(1) Plugin should have focus.");
+ is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
+
+ // Make sure window activation state changes don't spontaneously
+ // change plugin focus.
+
+ // Blur the window.
+ SpecialPowers.focus(opener);
+
+ is(plugin1.getFocusState(), true, "(2) Plugin should still have focus.");
+ is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
+
+ // Focus the window.
+ SpecialPowers.focus(window);
+
+ is(plugin1.getFocusState(), true, "(3) Plugin should still have focus.");
+ is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
+
+ // Take focus from the plugin.
+ yield synthesizeNativeMouseEvent(plugin2X * scale, plugin2Y * scale, NSLeftMouseDown, 0, plugin2, continueTest);
+ yield synthesizeNativeMouseEvent(plugin2X * scale, plugin2Y * scale, NSLeftMouseUp, 0, plugin2, continueTest);
+ expectedEventCount++;
+
+ is(plugin1.getFocusState(), false, "(4) Plugin should not have focus.");
+ is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
+
+ // Make sure window activation causes the plugin to be informed of focus
+ // changes that took place while the window was inactive.
+
+ // Give the plugin focus (the window is already focused).
+ yield synthesizeNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseDown, 0, plugin1, continueTest);
+ yield synthesizeNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseUp, 0, plugin1, continueTest);
+ expectedEventCount++;
+
+ // Blur the window.
+ SpecialPowers.focus(opener);
+
+ // Take focus from the plugin while the window is blurred.
+ plugin2.focus();
+
+ is(plugin1.getFocusState(), true, "(5) Plugin should still have focus.");
+ is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
+
+ // Focus the window.
+ SpecialPowers.focus(window);
+ expectedEventCount++;
+
+ is(plugin1.getFocusState(), false, "(6) Plugin should not have focus.");
+ is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
+ }
+
+ var gTestContinuation = null;
+ function continueTest() {
+ if (!gTestContinuation) {
+ gTestContinuation = runTests();
+ }
+ var ret = gTestContinuation.next();
+ if (ret.done) {
+ window.opener.testsFinished();
+ } else {
+ is(ret.value, true, "Mouse event successfully synthesized");
+ }
+ }
+
+ // Onload hander doesn't work for these tests -- no events arrive at the plugin.
+ window.opener.SimpleTest.waitForFocus(continueTest, window);
+
+ </script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/cocoa_window_focus.html b/dom/plugins/test/mochitest/cocoa_window_focus.html
new file mode 100644
index 0000000000..8305b19a4b
--- /dev/null
+++ b/dom/plugins/test/mochitest/cocoa_window_focus.html
@@ -0,0 +1,95 @@
+<html>
+<head>
+ <title>NPCocoaEventWindowFocusChanged Tests</title>
+</head>
+<body onload="runTests()">
+ <embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
+ <embed id="plugin2" type="application/x-test" width="400" height="400"></embed>
+ <script type="application/javascript">
+ function is(aLeft, aRight, aMessage) {
+ window.opener.SimpleTest.is(aLeft, aRight, aMessage);
+ }
+
+ function ok(aValue, aMessage) {
+ window.opener.SimpleTest.ok(aValue, aMessage);
+ }
+
+ function executeSoon(func) {
+ window.opener.SimpleTest.executeSoon(func);
+ }
+
+ function waitForFocus(aCb, aTarget, aBlank) {
+ window.opener.SimpleTest.waitForFocus(aCb, aTarget, aBlank);
+ }
+
+ function runTests() {
+ var plugin1 = document.getElementById("plugin1");
+ var plugin2 = document.getElementById("plugin2");
+
+ if (plugin1.getEventModel() != 1) {
+ window.opener.todo(false, "Skipping this test when not testing the Cocoa event model");
+ window.opener.testsFinished();
+ return;
+ }
+
+ // The first plugin will have in-page focus for these tests.
+ plugin1.focus();
+
+ // The expected event count which applies to all instances.
+ // Initialize to 1 to account for the initial state event.
+ var expectedEventCount = 1;
+
+ // First make sure the plugins got an initial window focus event.
+ // Since this window was just opened it should be in the front. If
+ // the plugin has not been sent an initial window state then it will
+ // be in an unknown state and it will throw an exception.
+ try {
+ is(plugin1.getTopLevelWindowActivationState(), true, "Activation state should be: activated");
+ is(plugin1.getTopLevelWindowActivationEventCount(), expectedEventCount, "Window focus event count should be " + expectedEventCount);
+
+ is(plugin2.getTopLevelWindowActivationState(), true, "Activation state should be: activated");
+ is(plugin2.getTopLevelWindowActivationEventCount(), expectedEventCount, "Window focus event count should be " + expectedEventCount);
+ } catch (e) {
+ ok(false, "Plugin does not know its initial top-level window activation state!");
+ }
+
+ var fm = SpecialPowers.Services.focus;
+
+ waitForFocus(function() {
+ // Make sure the plugin handled the focus event before checking.
+ executeSoon(function() {
+ expectedEventCount++;
+
+ is(plugin1.getTopLevelWindowActivationState(), false, "Activation state should be: deactivated");
+ is(plugin1.getTopLevelWindowActivationEventCount(), expectedEventCount, "Window focus event count should be " + expectedEventCount);
+
+ is(plugin2.getTopLevelWindowActivationState(), false, "Activation state should be: deactivated");
+ is(plugin2.getTopLevelWindowActivationEventCount(), expectedEventCount, "Window focus event count should be " + expectedEventCount);
+
+ // Bring our window back to the front and make sure plugins were properly notified.
+ fm.focusedWindow = window;
+
+ waitForFocus(function() {
+ // Make sure the plugin handled the focus event before checking.
+ executeSoon(function() {
+ expectedEventCount++;
+
+ is(plugin1.getTopLevelWindowActivationState(), true, "Activation state should be: activated");
+ is(plugin1.getTopLevelWindowActivationEventCount(), expectedEventCount, "Window focus event count should be " + expectedEventCount);
+
+ is(plugin2.getTopLevelWindowActivationState(), true, "Activation state should be: activated");
+ is(plugin2.getTopLevelWindowActivationEventCount(), expectedEventCount, "Window focus event count should be " + expectedEventCount);
+
+ window.opener.testsFinished();
+ });
+ }, window);
+ });
+ }, window.opener);
+
+ // Send our window to the back and make sure plugins were properly notified.
+ // Calling window.blur() is not allowed.
+ fm.focusedWindow = window.opener;
+ }
+ </script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/crashing_subpage.html b/dom/plugins/test/mochitest/crashing_subpage.html
new file mode 100644
index 0000000000..7c00f25c4c
--- /dev/null
+++ b/dom/plugins/test/mochitest/crashing_subpage.html
@@ -0,0 +1,4 @@
+<html>
+<body onload="window.parent.frameLoaded()">
+ <h1>Crashing subpage</h1>
+ <embed id="plugin1" type="application/x-test" width="400" height="400" drawmode="solid" color="FF00FFFF"></embed>
diff --git a/dom/plugins/test/mochitest/dialog_watcher.js b/dom/plugins/test/mochitest/dialog_watcher.js
new file mode 100644
index 0000000000..8a8c829974
--- /dev/null
+++ b/dom/plugins/test/mochitest/dialog_watcher.js
@@ -0,0 +1,183 @@
+const EVENT_OBJECT_SHOW = 0x8002;
+const EVENT_OBJECT_HIDE = 0x8003;
+const WINEVENT_OUTOFCONTEXT = 0;
+const WINEVENT_SKIPOWNPROCESS = 2;
+const QS_ALLINPUT = 0x04FF;
+const INFINITE = 0xFFFFFFFF;
+const WAIT_OBJECT_0 = 0;
+const WAIT_TIMEOUT = 258;
+const PM_NOREMOVE = 0;
+
+function DialogWatcher(titleText, onDialogStart, onDialogEnd) {
+ this.titleText = titleText;
+ this.onDialogStart = onDialogStart;
+ this.onDialogEnd = onDialogEnd;
+}
+
+DialogWatcher.prototype.init = function() {
+ this.hwnd = undefined;
+ if (!this.user32) {
+ this.user32 = ctypes.open("user32.dll");
+ }
+ if (!this.findWindow) {
+ this.findWindow = user32.declare("FindWindowW",
+ ctypes.winapi_abi,
+ ctypes.uintptr_t,
+ ctypes.char16_t.ptr,
+ ctypes.char16_t.ptr);
+ }
+ if (!this.winEventProcType) {
+ this.winEventProcType = ctypes.FunctionType(ctypes.stdcall_abi,
+ ctypes.void_t,
+ [ctypes.uintptr_t,
+ ctypes.uint32_t,
+ ctypes.uintptr_t,
+ ctypes.long,
+ ctypes.long,
+ ctypes.uint32_t,
+ ctypes.uint32_t]).ptr;
+ }
+ if (!this.setWinEventHook) {
+ this.setWinEventHook = user32.declare("SetWinEventHook",
+ ctypes.winapi_abi,
+ ctypes.uintptr_t,
+ ctypes.uint32_t,
+ ctypes.uint32_t,
+ ctypes.uintptr_t,
+ this.winEventProcType,
+ ctypes.uint32_t,
+ ctypes.uint32_t,
+ ctypes.uint32_t);
+ }
+ if (!this.unhookWinEvent) {
+ this.unhookWinEvent = user32.declare("UnhookWinEvent",
+ ctypes.winapi_abi,
+ ctypes.int,
+ ctypes.uintptr_t);
+ }
+ if (!this.pointType) {
+ this.pointType = ctypes.StructType("tagPOINT",
+ [ { "x": ctypes.long },
+ { "y": ctypes.long } ] );
+ }
+ if (!this.msgType) {
+ this.msgType = ctypes.StructType("tagMSG",
+ [ { "hwnd": ctypes.uintptr_t },
+ { "message": ctypes.uint32_t },
+ { "wParam": ctypes.uintptr_t },
+ { "lParam": ctypes.intptr_t },
+ { "time": ctypes.uint32_t },
+ { "pt": this.pointType } ] );
+ }
+ if (!this.peekMessage) {
+ this.peekMessage = user32.declare("PeekMessageW",
+ ctypes.winapi_abi,
+ ctypes.int,
+ this.msgType.ptr,
+ ctypes.uintptr_t,
+ ctypes.uint32_t,
+ ctypes.uint32_t,
+ ctypes.uint32_t);
+ }
+ if (!this.msgWaitForMultipleObjects) {
+ this.msgWaitForMultipleObjects = user32.declare("MsgWaitForMultipleObjects",
+ ctypes.winapi_abi,
+ ctypes.uint32_t,
+ ctypes.uint32_t,
+ ctypes.uintptr_t.ptr,
+ ctypes.int,
+ ctypes.uint32_t,
+ ctypes.uint32_t);
+ }
+ if (!this.getWindowTextW) {
+ this.getWindowTextW = user32.declare("GetWindowTextW",
+ ctypes.winapi_abi,
+ ctypes.int,
+ ctypes.uintptr_t,
+ ctypes.char16_t.ptr,
+ ctypes.int);
+ }
+ if (!this.messageBox) {
+ // Handy for debugging this code
+ this.messageBox = user32.declare("MessageBoxW",
+ ctypes.winapi_abi,
+ ctypes.int,
+ ctypes.uintptr_t,
+ ctypes.char16_t.ptr,
+ ctypes.char16_t.ptr,
+ ctypes.uint32_t);
+ }
+};
+
+DialogWatcher.prototype.getWindowText = function(hwnd) {
+ var bufType = ctypes.ArrayType(ctypes.char16_t);
+ var buffer = new bufType(256);
+
+ if (this.getWindowTextW(hwnd, buffer, buffer.length)) {
+ return buffer.readString();
+ }
+};
+
+DialogWatcher.prototype.processWindowEvents = function(timeout) {
+ var onWinEvent = function(self, hook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime) {
+ var nhwnd = Number(hwnd)
+ if (event == EVENT_OBJECT_SHOW) {
+ if (nhwnd == self.hwnd) {
+ // We've already picked up this event via FindWindow
+ return;
+ }
+ var windowText = self.getWindowText(hwnd);
+ if (windowText == self.titleText && self.onDialogStart) {
+ self.hwnd = nhwnd;
+ self.onDialogStart(nhwnd);
+ }
+ } else if (event == EVENT_OBJECT_HIDE && nhwnd == self.hwnd && self.onDialogEnd) {
+ self.onDialogEnd();
+ self.hwnd = null;
+ }
+ };
+ var self = this;
+ var callback = this.winEventProcType(function(hook, event, hwnd, idObject,
+ idChild, dwEventThread,
+ dwmsEventTime) {
+ onWinEvent(self, hook, event, hwnd, idObject, idChild, dwEventThread,
+ dwmsEventTime);
+ } );
+ var hook = this.setWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE,
+ 0, callback, 0, 0,
+ WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
+ if (!hook) {
+ return;
+ }
+ // Check if the window is already showing
+ var hwnd = this.findWindow(null, this.titleText);
+ if (hwnd && hwnd > 0) {
+ this.hwnd = Number(hwnd);
+ if (this.onDialogStart) {
+ this.onDialogStart(this.hwnd);
+ }
+ }
+
+ if (!timeout) {
+ timeout = INFINITE;
+ }
+
+ var waitStatus = WAIT_OBJECT_0;
+ var expectingStart = this.onDialogStart && this.hwnd === undefined;
+ var startWaitTime = Date.now();
+ while (this.hwnd === undefined || this.onDialogEnd && this.hwnd) {
+ waitStatus = this.msgWaitForMultipleObjects(0, null, 0, expectingStart ?
+ INFINITE : timeout, 0);
+ if (waitStatus == WAIT_OBJECT_0) {
+ var msg = new this.msgType;
+ this.peekMessage(msg.address(), 0, 0, 0, PM_NOREMOVE);
+ }
+ if (waitStatus == WAIT_TIMEOUT || (Date.now() - startWaitTime) >= timeout) {
+ break;
+ }
+ }
+
+ this.unhookWinEvent(hook);
+ // Returns true if the hook was successful, something was found, and we never timed out
+ return this.hwnd !== undefined && waitStatus == WAIT_OBJECT_0;
+};
diff --git a/dom/plugins/test/mochitest/file_authident.js b/dom/plugins/test/mochitest/file_authident.js
new file mode 100644
index 0000000000..f255fc30c7
--- /dev/null
+++ b/dom/plugins/test/mochitest/file_authident.js
@@ -0,0 +1,6 @@
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+var am = Cc["@mozilla.org/network/http-auth-manager;1"].
+ getService(Ci.nsIHttpAuthManager);
+am.setAuthIdentity("http", "mochi.test", 8888, "basic", "testrealm", "",
+ "mochi.test", "user1", "password1");
diff --git a/dom/plugins/test/mochitest/file_bug1245545.js b/dom/plugins/test/mochitest/file_bug1245545.js
new file mode 100644
index 0000000000..47cb618eb1
--- /dev/null
+++ b/dom/plugins/test/mochitest/file_bug1245545.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+const { Services } = Cu.import('resource://gre/modules/Services.jsm');
+
+function getTestPlugin(pluginName) {
+ var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+ var tags = ph.getPluginTags();
+ var name = pluginName || "Test Plug-in";
+ for (var tag of tags) {
+ if (tag.name == name) {
+ return tag;
+ }
+ }
+ return null;
+}
+
+addMessageListener('check-plugin-unload', function(message) {
+ var tag = getTestPlugin();
+ var results = sendAsyncMessage("check-plugin-unload", tag.loaded);
+});
diff --git a/dom/plugins/test/mochitest/file_bug738396.html b/dom/plugins/test/mochitest/file_bug738396.html
new file mode 100644
index 0000000000..f91f8f3d9b
--- /dev/null
+++ b/dom/plugins/test/mochitest/file_bug738396.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Helper for test_bug738396.html</title>
+ <meta charset="utf-8">
+</head>
+<body>
+ <!-- Test that the plugin sees "good" in each of these cases -->
+ <div id="codebasevis">
+ <applet codebase="good" codebase="bad" ></applet>
+
+ <applet codebase="bad">
+ <param name="codebase" value="good">
+ </applet>
+
+ <applet codebase="bad">
+ <param name="codebase" value="stillbad">
+ <param name="codebase" value="good">
+ </applet>
+
+ <applet>
+ <param name="codebase" value="good">
+ </applet>
+
+ <object type="application/x-java-test" codebase="good" codebase="bad"></object>
+
+ <object type="application/x-java-test" codebase="bad">
+ <param name="codebase" value="good">
+ </object>
+
+ <object type="application/x-java-test" codebase="bad">
+ <param name="codebase" value="stillbad">
+ <param name="codebase" value="good">
+ </object>
+
+ <object type="application/x-java-test">
+ <param name="codebase" value="good">
+ </object>
+
+ <embed type="application/x-java-test" codebase="good" codebase="bad">
+ </div>
+ <div id="blockedcodebase">
+ <!-- Test that none of these are allowed to load -->
+ <applet codebase="file:///" codebase="notused"></applet>
+
+ <applet codebase="notused">
+ <param name="codebase" value="file:///">
+ </applet>
+
+ <applet codebase="notused">
+ <param name="codebase" value="notused">
+ <param name="codebase" value="file:///">
+ </applet>
+
+ <applet>
+ <param name="codebase" value="file:///">
+ </applet>
+
+ <object type="application/x-java-test" codebase="file:///" codebase="notused"></object>
+
+ <object type="application/x-java-test" codebase="notused">
+ <param name="codebase" value="file:///">
+ </object>
+
+ <object type="application/x-java-test" codebase="notused">
+ <param name="codebase" value="notused">
+ <param name="codebase" value="file:///">
+ </object>
+
+ <object type="application/x-java-test">
+ <param name="codebase" value="file:///">
+ </object>
+
+ <embed type="application/x-java-test" codebase="file:///" codebase="notused">
+ </div>
+ <div id="nocodebase">
+ <applet></applet>
+ <object type="application/x-java-test"></object>
+ <embed type="application/x-java-test">
+ </div>
+ <div id="emptycodebase">
+ <applet codebase=""></applet>
+ <object type="application/x-java-test" codebase=""></object>
+ <embed type="application/x-java-test" codebase="">
+ </div>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/file_bug771202.html b/dom/plugins/test/mochitest/file_bug771202.html
new file mode 100644
index 0000000000..935be65b25
--- /dev/null
+++ b/dom/plugins/test/mochitest/file_bug771202.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+ <embed id="pluginElement" type="application/x-test" width="200" height="200"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/file_bug863792.html b/dom/plugins/test/mochitest/file_bug863792.html
new file mode 100644
index 0000000000..b4d3dc2e07
--- /dev/null
+++ b/dom/plugins/test/mochitest/file_bug863792.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<html>
+<head>
+ <title>File for Bug 863792</title>
+
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <base href="chrome://browser/content/">
+</head>
+<body>
+<script type="application/javascript">
+
+// A plugin that removes itself from the document and inactivates said document
+// inside NPP_New. We should not leak the instance. See also test_bug854082
+
+var outerwindow = window;
+var i = document.createElement("iframe");
+i.width = 500;
+i.height = 500;
+var ob = document.body;
+document.body.appendChild(i);
+i.addEventListener("load", function loaded() {
+ var id = i.contentDocument;
+ var e = id.createElement("embed");
+ var callbackrun = false;
+ e.width = 200;
+ e.height = 200;
+ e.type = "application/x-test";
+ e.__defineSetter__("pluginFoundElement", function() {
+ window.console.log("pluginFoundElement");
+ e.style.display = "none";
+ e.clientTop;
+ i.removeEventListener("load", loaded);
+ ob.removeChild(i);
+ id.body.clientTop;
+ id.body.removeChild(e);
+ callbackrun = true;
+ });
+ id.body.appendChild(e);
+ e.clientTop;
+ e = id = i = ob = null;
+ SpecialPowers.forceCC(); SpecialPowers.forceGC();
+}, false);
+</script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/file_checkcookie.sjs b/dom/plugins/test/mochitest/file_checkcookie.sjs
new file mode 100644
index 0000000000..9bfa04f59a
--- /dev/null
+++ b/dom/plugins/test/mochitest/file_checkcookie.sjs
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+function handleRequest(request, response) {
+ try {
+ var cookie = request.getHeader("Cookie");
+ } catch (e) {
+ cookie = "EMPTY_COOKIE";
+ }
+
+ // avoid confusing cache behaviors.
+ response.setHeader("Cache-Control", "no-cache", false);
+ // allow XHR requests accross origin.
+ response.setHeader("Access-control-allow-origin", "*");
+ response.setHeader("Content-type", "text/plain", false);
+ response.setStatusLine(request.httpVersion, "200", "OK");
+ response.write(cookie);
+}
diff --git a/dom/plugins/test/mochitest/file_setcookie.html b/dom/plugins/test/mochitest/file_setcookie.html
new file mode 100644
index 0000000000..3838db7fee
--- /dev/null
+++ b/dom/plugins/test/mochitest/file_setcookie.html
@@ -0,0 +1,9 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+ <head>
+ <meta charset=UTF-8>
+ <body>
+ <script>document.cookie = "found=another_cookie";</script>
+ </body>
+</html>
diff --git a/dom/plugins/test/mochitest/hang_test.js b/dom/plugins/test/mochitest/hang_test.js
new file mode 100644
index 0000000000..796093fa3e
--- /dev/null
+++ b/dom/plugins/test/mochitest/hang_test.js
@@ -0,0 +1,114 @@
+
+Components.utils.import("resource://gre/modules/KeyValueParser.jsm");
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+var success = false;
+var observerFired = false;
+
+var testObserver = {
+ idleHang: true,
+
+ observe: function(subject, topic, data) {
+ observerFired = true;
+ ok(true, "Observer fired");
+ is(topic, "plugin-crashed", "Checking correct topic");
+ is(data, null, "Checking null data");
+ ok((subject instanceof Ci.nsIPropertyBag2), "got Propbag");
+ ok((subject instanceof Ci.nsIWritablePropertyBag2), "got writable Propbag");
+
+ var pluginId = subject.getPropertyAsAString("pluginDumpID");
+ isnot(pluginId, "", "got a non-empty plugin crash id");
+
+ // check plugin dump and extra files
+ let directoryService =
+ Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+ let profD = directoryService.get("ProfD", Ci.nsIFile);
+ profD.append("minidumps");
+ let pluginDumpFile = profD.clone();
+ pluginDumpFile.append(pluginId + ".dmp");
+ ok(pluginDumpFile.exists(), "plugin minidump exists");
+
+ let pluginExtraFile = profD.clone();
+ pluginExtraFile.append(pluginId + ".extra");
+ ok(pluginExtraFile.exists(), "plugin extra file exists");
+
+ let extraData = parseKeyValuePairsFromFile(pluginExtraFile);
+
+ // check additional dumps
+
+ ok("additional_minidumps" in extraData, "got field for additional minidumps");
+ let additionalDumps = extraData.additional_minidumps.split(',');
+ ok(additionalDumps.indexOf('browser') >= 0, "browser in additional_minidumps");
+
+ let additionalDumpFiles = [];
+ for (let name of additionalDumps) {
+ let file = profD.clone();
+ file.append(pluginId + "-" + name + ".dmp");
+ ok(file.exists(), "additional dump '"+name+"' exists");
+ if (file.exists()) {
+ additionalDumpFiles.push(file);
+ }
+ }
+
+ // check cpu usage field
+
+ ok("PluginCpuUsage" in extraData, "got extra field for plugin cpu usage");
+ let cpuUsage = parseFloat(extraData["PluginCpuUsage"]);
+ if (this.idleHang) {
+ ok(cpuUsage == 0, "plugin cpu usage is 0%");
+ } else {
+ ok(cpuUsage > 0, "plugin cpu usage is >0%");
+ }
+
+ // check processor count field
+ ok("NumberOfProcessors" in extraData, "got extra field for processor count");
+ ok(parseInt(extraData["NumberOfProcessors"]) > 0, "number of processors is >0");
+
+ // cleanup, to be nice
+ pluginDumpFile.remove(false);
+ pluginExtraFile.remove(false);
+ for (let file of additionalDumpFiles) {
+ file.remove(false);
+ }
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIObserver) ||
+ iid.equals(Ci.nsISupportsWeakReference) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+ throw Components.results.NS_NOINTERFACE;
+ }
+};
+
+
+function onPluginCrashed(aEvent) {
+ ok(true, "Plugin crashed notification received");
+ ok(observerFired, "Observer should have fired first");
+ is(aEvent.type, "PluginCrashed", "event is correct type");
+
+ var pluginElement = document.getElementById("plugin1");
+ is (pluginElement, aEvent.target, "Plugin crashed event target is plugin element");
+
+ ok(aEvent instanceof PluginCrashedEvent,
+ "plugin crashed event has the right interface");
+
+ is(typeof aEvent.pluginDumpID, "string", "pluginDumpID is correct type");
+ isnot(aEvent.pluginDumpID, "", "got a non-empty dump ID");
+ is(typeof aEvent.pluginName, "string", "pluginName is correct type");
+ is(aEvent.pluginName, "Test Plug-in", "got correct plugin name");
+ is(typeof aEvent.pluginFilename, "string", "pluginFilename is correct type");
+ isnot(aEvent.pluginFilename, "", "got a non-empty filename");
+ // The app itself may or may not have decided to submit the report, so
+ // allow either true or false here.
+ ok("submittedCrashReport" in aEvent, "submittedCrashReport is a property of event");
+ is(typeof aEvent.submittedCrashReport, "boolean", "submittedCrashReport is correct type");
+
+ var os = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+ os.removeObserver(testObserver, "plugin-crashed");
+
+ SimpleTest.finish();
+}
diff --git a/dom/plugins/test/mochitest/hangui_common.js b/dom/plugins/test/mochitest/hangui_common.js
new file mode 100644
index 0000000000..cf0d049f20
--- /dev/null
+++ b/dom/plugins/test/mochitest/hangui_common.js
@@ -0,0 +1,20 @@
+// Plugin Hang UI constants
+const HANGUIOP_NOTHING = 0;
+const HANGUIOP_CANCEL = 1;
+const HANGUIOP_COMMAND = 2;
+const IDC_CONTINUE = 1001;
+const IDC_STOP = 1002;
+const IDC_NOFUTURE = 1003;
+
+// Windows constants
+const WM_CLOSE = 0x0010;
+const WM_COMMAND = 0x0111;
+const BM_GETCHECK = 0x00F0;
+const BM_SETCHECK = 0x00F1;
+const BN_CLICKED = 0;
+const BST_CHECKED = 1;
+
+// Test-specific constants
+const EPSILON_MS = 1000;
+const STALL_DURATION = 2;
+
diff --git a/dom/plugins/test/mochitest/hangui_iface.js b/dom/plugins/test/mochitest/hangui_iface.js
new file mode 100644
index 0000000000..853d136bb2
--- /dev/null
+++ b/dom/plugins/test/mochitest/hangui_iface.js
@@ -0,0 +1,121 @@
+var user32;
+var sendMessage;
+var getDlgItem;
+var messageBox;
+var watcher;
+
+importScripts("hangui_common.js");
+importScripts("dialog_watcher.js");
+
+function initCTypes() {
+ if (!user32) {
+ user32 = ctypes.open("user32.dll");
+ }
+ if (!getDlgItem) {
+ getDlgItem = user32.declare("GetDlgItem",
+ ctypes.winapi_abi,
+ ctypes.uintptr_t,
+ ctypes.uintptr_t,
+ ctypes.int);
+ }
+ if (!sendMessage) {
+ sendMessage = user32.declare("SendMessageW",
+ ctypes.winapi_abi,
+ ctypes.intptr_t,
+ ctypes.uintptr_t,
+ ctypes.uint32_t,
+ ctypes.uintptr_t,
+ ctypes.intptr_t);
+ }
+ if (!messageBox) {
+ // Handy for debugging the test itself
+ messageBox = user32.declare("MessageBoxW",
+ ctypes.winapi_abi,
+ ctypes.int,
+ ctypes.uintptr_t,
+ ctypes.char16_t.ptr,
+ ctypes.char16_t.ptr,
+ ctypes.uint32_t);
+ }
+ if (!watcher) {
+ watcher = new DialogWatcher("Warning: Unresponsive plugin");
+ }
+}
+
+function postSuccess(params) {
+ self.postMessage({"status": true, "params": params});
+}
+
+function postFail(params, msg) {
+ self.postMessage({"status": false, "params": params, "msg": msg});
+}
+
+function onDialogStart(inparams, hwnd) {
+ var params = Object.create(inparams);
+ params.testName += " (Start)";
+ params.callback = null;
+ if (!params.expectToFind) {
+ postFail(params, "Dialog showed when we weren't expecting it to!");
+ return;
+ }
+ if (params.opCode == HANGUIOP_CANCEL) {
+ sendMessage(hwnd, WM_CLOSE, 0, 0);
+ } else if (params.opCode == HANGUIOP_COMMAND) {
+ if (params.check) {
+ var checkbox = getDlgItem(hwnd, IDC_NOFUTURE);
+ if (!checkbox) {
+ postFail(params, "Couldn't find checkbox");
+ return;
+ }
+ sendMessage(checkbox, BM_SETCHECK, BST_CHECKED, 0);
+ sendMessage(hwnd, WM_COMMAND, (BN_CLICKED << 16) | IDC_NOFUTURE, checkbox);
+ }
+ var button = getDlgItem(hwnd, params.commandId);
+ if (!button) {
+ postFail(params,
+ "GetDlgItem failed to find button with ID " + params.commandId);
+ return;
+ }
+ sendMessage(hwnd, WM_COMMAND, (BN_CLICKED << 16) | params.commandId, button);
+ }
+ postSuccess(params);
+}
+
+function onDialogEnd(inparams) {
+ var params = Object.create(inparams);
+ params.testName += " (End)";
+ params.callback = inparams.callback;
+ postSuccess(params);
+}
+
+self.onmessage = function(event) {
+ initCTypes();
+ watcher.init();
+ var params = event.data;
+ var timeout = params.timeoutMs;
+ if (params.expectToFind) {
+ watcher.onDialogStart = function(hwnd) { onDialogStart(params, hwnd); };
+ if (params.expectToClose) {
+ watcher.onDialogEnd = function() { onDialogEnd(params); };
+ }
+ } else {
+ watcher.onDialogStart = null;
+ watcher.onDialogEnd = null;
+ }
+ var result = watcher.processWindowEvents(timeout);
+ if (result === null) {
+ postFail(params, "Hook failed");
+ } else if (!result) {
+ if (params.expectToFind) {
+ postFail(params, "The dialog didn't show but we were expecting it to");
+ } else {
+ postSuccess(params);
+ }
+ }
+}
+
+self.onerror = function(event) {
+ var msg = "Error: " + event.message + " at " + event.filename + ":" + event.lineno;
+ postFail(null, msg);
+};
+
diff --git a/dom/plugins/test/mochitest/hangui_subpage.html b/dom/plugins/test/mochitest/hangui_subpage.html
new file mode 100644
index 0000000000..401912f68e
--- /dev/null
+++ b/dom/plugins/test/mochitest/hangui_subpage.html
@@ -0,0 +1,4 @@
+<html>
+<body onload="window.parent.frameLoaded()">
+ <embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
+
diff --git a/dom/plugins/test/mochitest/head.js b/dom/plugins/test/mochitest/head.js
new file mode 100644
index 0000000000..42143b84bd
--- /dev/null
+++ b/dom/plugins/test/mochitest/head.js
@@ -0,0 +1,133 @@
+
+/**
+ * Waits for a tab switch.
+ */
+function waitTabSwitched() {
+ return new Promise(resolve => {
+ gBrowser.addEventListener("TabSwitchDone", function onSwitch() {
+ gBrowser.removeEventListener("TabSwitchDone", onSwitch);
+ executeSoon(resolve);
+ });
+ });
+}
+
+/**
+ * Waits a specified number of miliseconds.
+ *
+ * Usage:
+ * let wait = yield waitForMs(2000);
+ * ok(wait, "2 seconds should now have elapsed");
+ *
+ * @param aMs the number of miliseconds to wait for
+ * @returns a Promise that resolves to true after the time has elapsed
+ */
+function waitForMs(aMs) {
+ return new Promise((resolve) => {
+ setTimeout(done, aMs);
+ function done() {
+ resolve(true);
+ }
+ });
+}
+
+/**
+ * Platform string helper for nativeVerticalWheelEventMsg
+ */
+function getPlatform() {
+ if (navigator.platform.indexOf("Win") == 0) {
+ return "windows";
+ }
+ if (navigator.platform.indexOf("Mac") == 0) {
+ return "mac";
+ }
+ if (navigator.platform.indexOf("Linux") == 0) {
+ return "linux";
+ }
+ return "unknown";
+}
+
+/**
+ * Returns a native wheel scroll event id for dom window
+ * uitls sendNativeMouseScrollEvent.
+ */
+function nativeVerticalWheelEventMsg() {
+ switch (getPlatform()) {
+ case "windows": return 0x020A; // WM_MOUSEWHEEL
+ case "mac": return 0; // value is unused, can be anything
+ case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway
+ }
+ throw "Native wheel events not supported on platform " + getPlatform();
+}
+
+/**
+ * Waits for the first dom "scroll" event.
+ */
+function waitScrollStart(aTarget) {
+ return new Promise((resolve, reject) => {
+ aTarget.addEventListener("scroll", function listener(event) {
+ aTarget.removeEventListener("scroll", listener, true);
+ resolve(event);
+ }, true);
+ });
+}
+
+/**
+ * Waits for the last dom "scroll" event which generally indicates
+ * a scroll operation is complete. To detect this the helper waits
+ * 1 second intervals checking for scroll events from aTarget. If
+ * a scroll event is not received during that time, it considers
+ * the scroll operation complete. Not super accurate, be careful.
+ */
+function waitScrollFinish(aTarget) {
+ return new Promise((resolve, reject) => {
+ let recent = false;
+ let count = 0;
+ function listener(event) {
+ recent = true;
+ }
+ aTarget.addEventListener("scroll", listener, true);
+ setInterval(function () {
+ // one second passed and we didn't receive a scroll event.
+ if (!recent) {
+ aTarget.removeEventListener("scroll", listener, true);
+ resolve();
+ return;
+ }
+ recent = false;
+ // ten seconds
+ if (count > 10) {
+ aTarget.removeEventListener("scroll", listener, true);
+ reject();
+ }
+ }, 1000);
+ });
+}
+
+/**
+ * Set a plugin activation state. See nsIPluginTag for
+ * supported states. Affected plugin default to the first
+ * test plugin.
+ */
+function setTestPluginEnabledState(aState, aPluginName) {
+ let name = aPluginName || "Test Plug-in";
+ SpecialPowers.setTestPluginEnabledState(aState, name);
+}
+
+/**
+ * Returns the chrome side nsIPluginTag for this plugin, helper for
+ * setTestPluginEnabledState.
+ */
+function getTestPlugin(aName) {
+ let pluginName = aName || "Test Plug-in";
+ let ph = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
+ let tags = ph.getPluginTags();
+
+ // Find the test plugin
+ for (let i = 0; i < tags.length; i++) {
+ if (tags[i].name == pluginName)
+ return tags[i];
+ }
+ ok(false, "Unable to find plugin");
+ return null;
+}
+
diff --git a/dom/plugins/test/mochitest/large-pic.jpg b/dom/plugins/test/mochitest/large-pic.jpg
new file mode 100644
index 0000000000..b167f6b9ba
--- /dev/null
+++ b/dom/plugins/test/mochitest/large-pic.jpg
Binary files differ
diff --git a/dom/plugins/test/mochitest/loremipsum.txt b/dom/plugins/test/mochitest/loremipsum.txt
new file mode 100644
index 0000000000..c5becca596
--- /dev/null
+++ b/dom/plugins/test/mochitest/loremipsum.txt
@@ -0,0 +1,11 @@
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
+
+Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
+
+Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
+
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut laboreet dolore magna aliquyam erat.
+
+Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. \ No newline at end of file
diff --git a/dom/plugins/test/mochitest/loremipsum.xtest b/dom/plugins/test/mochitest/loremipsum.xtest
new file mode 100644
index 0000000000..c5becca596
--- /dev/null
+++ b/dom/plugins/test/mochitest/loremipsum.xtest
@@ -0,0 +1,11 @@
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
+
+Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
+
+Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
+
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut laboreet dolore magna aliquyam erat.
+
+Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. \ No newline at end of file
diff --git a/dom/plugins/test/mochitest/loremipsum.xtest^headers^ b/dom/plugins/test/mochitest/loremipsum.xtest^headers^
new file mode 100644
index 0000000000..8dd7784af4
--- /dev/null
+++ b/dom/plugins/test/mochitest/loremipsum.xtest^headers^
@@ -0,0 +1 @@
+Content-Type: application/x-test
diff --git a/dom/plugins/test/mochitest/loremipsum_file.txt b/dom/plugins/test/mochitest/loremipsum_file.txt
new file mode 100644
index 0000000000..c5becca596
--- /dev/null
+++ b/dom/plugins/test/mochitest/loremipsum_file.txt
@@ -0,0 +1,11 @@
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
+
+Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
+
+Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
+
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut laboreet dolore magna aliquyam erat.
+
+Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. \ No newline at end of file
diff --git a/dom/plugins/test/mochitest/loremipsum_nocache.txt b/dom/plugins/test/mochitest/loremipsum_nocache.txt
new file mode 100644
index 0000000000..c5becca596
--- /dev/null
+++ b/dom/plugins/test/mochitest/loremipsum_nocache.txt
@@ -0,0 +1,11 @@
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
+
+Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
+
+Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
+
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut laboreet dolore magna aliquyam erat.
+
+Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. \ No newline at end of file
diff --git a/dom/plugins/test/mochitest/loremipsum_nocache.txt^headers^ b/dom/plugins/test/mochitest/loremipsum_nocache.txt^headers^
new file mode 100644
index 0000000000..12a01c4a22
--- /dev/null
+++ b/dom/plugins/test/mochitest/loremipsum_nocache.txt^headers^
@@ -0,0 +1 @@
+Cache-Control: no-store
diff --git a/dom/plugins/test/mochitest/mixed_case_mime.sjs b/dom/plugins/test/mochitest/mixed_case_mime.sjs
new file mode 100644
index 0000000000..3c29b8289e
--- /dev/null
+++ b/dom/plugins/test/mochitest/mixed_case_mime.sjs
@@ -0,0 +1,8 @@
+function handleRequest(request, response)
+{
+ response.processAsync();
+ response.setHeader("Content-Type", "application/x-Second-Test", false);
+
+ response.write("Hello world.\n");
+ response.finish();
+}
diff --git a/dom/plugins/test/mochitest/mochitest.ini b/dom/plugins/test/mochitest/mochitest.ini
new file mode 100644
index 0000000000..0e8587f34d
--- /dev/null
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -0,0 +1,150 @@
+[DEFAULT]
+subsuite = clipboard
+support-files =
+ 307-xo-redirect.sjs
+ crashing_subpage.html
+ file_authident.js
+ file_bug738396.html
+ file_bug771202.html
+ file_bug863792.html
+ file_bug1245545.js
+ large-pic.jpg
+ loremipsum.txt
+ loremipsum.xtest
+ loremipsum.xtest^headers^
+ loremipsum_file.txt
+ loremipsum_nocache.txt
+ loremipsum_nocache.txt^headers^
+ mixed_case_mime.sjs
+ neverending.sjs
+ npruntime_identifiers_subpage.html
+ plugin-stream-referer.sjs
+ plugin_window.html
+ pluginstream.js
+ post.sjs
+ plugin-utils.js
+ !/toolkit/components/passwordmgr/test/authenticate.sjs
+
+[test_bug406541.html]
+[test_bug532208.html]
+[test_bug539565-1.html]
+[test_bug539565-2.html]
+[test_bug738396.html]
+[test_bug771202.html]
+[test_bug777098.html]
+[test_bug784131.html]
+[test_bug813906.html]
+[test_bug827160.html]
+skip-if = toolkit == 'android' # needs plugin support
+[test_bug852315.html]
+[test_bug854082.html]
+[test_bug863792.html]
+[test_bug967694.html]
+[test_bug985859.html]
+[test_bug986930.html]
+[test_bug1092842.html]
+[test_bug1165981.html]
+skip-if = !(os == "win" && processor == "x86_64")
+[test_bug1245545.html]
+[test_bug1307694.html]
+[test_cocoa_focus.html]
+skip-if = toolkit != "cocoa" || e10s # Bug 1194534
+support-files = cocoa_focus.html
+[test_cocoa_window_focus.html]
+skip-if = toolkit != "cocoa" # Bug 1194534
+support-files = cocoa_window_focus.html
+[test_cookies.html]
+[test_copyText.html]
+skip-if = (toolkit != "gtk2") && (toolkit != "gtk3")
+[test_crash_nested_loop.html]
+skip-if = (toolkit != "gtk2") && (toolkit != "gtk3")
+[test_crashing.html]
+skip-if = !crashreporter
+[test_crashing2.html]
+skip-if = (!crashreporter) || true # Bug 566049
+[test_CrashService_crash.html]
+skip-if = !crashreporter || e10s
+[test_CrashService_hang.html]
+skip-if = !crashreporter || e10s
+[test_defaultValue.html]
+[test_enumerate.html]
+[test_fullpage.html]
+[test_GCrace.html]
+[test_getauthenticationinfo.html]
+[test_hanging.html]
+skip-if = !crashreporter || e10s
+[test_hidden_plugin.html]
+[test_instance_re-parent.html]
+skip-if = release_or_beta # Bug 1172627
+[test_instance_unparent1.html]
+[test_instance_unparent2.html]
+[test_instance_unparent3.html]
+[test_instantiation.html]
+[test_mixed_case_mime.html]
+[test_multipleinstanceobjects.html]
+[test_newstreamondestroy.html]
+[test_npn_asynccall.html]
+[test_npn_timers.html]
+[test_npobject_getters.html]
+[test_NPNVdocumentOrigin.html]
+[test_NPPVpluginWantsAllNetworkStreams.html]
+[test_npruntime_construct.html]
+[test_npruntime_identifiers.html]
+[test_npruntime_npnevaluate.html]
+[test_npruntime_npninvoke.html]
+[test_npruntime_npninvokedefault.html]
+[test_object.html]
+skip-if = toolkit == 'android' # needs plugin support
+[test_painting.html]
+skip-if = (toolkit == "cocoa" && e10s) # bug 1252230
+[test_plugin_scroll_invalidation.html]
+skip-if = toolkit != "gtk2"
+support-files = plugin_scroll_invalidation.html
+[test_plugin_scroll_painting.html]
+skip-if = true # Bug 596491
+[test_pluginstream_3rdparty.html]
+support-files =
+ file_checkcookie.sjs
+ file_setcookie.html
+[test_pluginstream_asfile.html]
+[test_pluginstream_asfileonly.html]
+[test_pluginstream_err.html]
+[test_pluginstream_geturl.html]
+skip-if = true # Bug 1267432
+[test_pluginstream_geturlnotify.html]
+skip-if = true # Bug 1267432
+[test_pluginstream_newstream.html]
+[test_pluginstream_post.html]
+[test_pluginstream_poststream.html]
+[test_pluginstream_referer.html]
+[test_pluginstream_seek.html]
+[test_pluginstream_seek_close.html]
+[test_pluginstream_src.html]
+[test_pluginstream_src_dynamic.html]
+[test_pluginstream_src_referer.html]
+[test_positioning.html]
+skip-if = true # disabled due to oddness, perhaps scrolling of the mochitest window?
+[test_propertyAndMethod.html]
+[test_queryCSSZoomFactor.html]
+[test_queryContentsScaleFactor.html]
+skip-if = (toolkit != "cocoa") || (os != "win")
+[test_queryContentsScaleFactorWindowed.html]
+skip-if = (toolkit != "cocoa") || (os != "win")
+[test_redirect_handling.html]
+[test_secondPlugin.html]
+[test_src_url_change.html]
+[test_streamatclose.html]
+[test_streamNotify.html]
+[test_stringHandling.html]
+[test_twostreams.html]
+[test_visibility.html]
+skip-if = toolkit == "cocoa"
+[test_windowed_invalidate.html]
+skip-if = os != "win"
+[test_windowless_flash.html]
+skip-if = !(os == "win" && processor == "x86_64")
+[test_windowless_ime.html]
+skip-if = os != "win"
+[test_x11_error_crash.html]
+skip-if = !crashreporter || e10s || ((toolkit != "gtk2") && (toolkit != "gtk3"))
+[test_zero_opacity.html]
diff --git a/dom/plugins/test/mochitest/neverending.sjs b/dom/plugins/test/mochitest/neverending.sjs
new file mode 100644
index 0000000000..1576ce344c
--- /dev/null
+++ b/dom/plugins/test/mochitest/neverending.sjs
@@ -0,0 +1,16 @@
+var timer = null; // declare timer outside to prevent premature GC
+function handleRequest(request, response)
+{
+ response.processAsync();
+ response.setHeader("Content-Type", "text/plain", false);
+
+ for (var i = 0; i < 1000; ++i)
+ response.write("Hello... ");
+
+ timer = Components.classes["@mozilla.org/timer;1"]
+ .createInstance(Components.interfaces.nsITimer);
+ timer.initWithCallback(function() {
+ response.write("world.\n");
+ response.finish();
+ }, 10 * 1000 /* 10 secs */, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+}
diff --git a/dom/plugins/test/mochitest/npruntime_identifiers_subpage.html b/dom/plugins/test/mochitest/npruntime_identifiers_subpage.html
new file mode 100644
index 0000000000..38c62e017b
--- /dev/null
+++ b/dom/plugins/test/mochitest/npruntime_identifiers_subpage.html
@@ -0,0 +1,4 @@
+<html>
+ <body>
+ <embed id="plugin1" type="application/x-test" width="400" height="100">
+ </embed>
diff --git a/dom/plugins/test/mochitest/plugin-stream-referer.sjs b/dom/plugins/test/mochitest/plugin-stream-referer.sjs
new file mode 100644
index 0000000000..a1c9692c95
--- /dev/null
+++ b/dom/plugins/test/mochitest/plugin-stream-referer.sjs
@@ -0,0 +1,12 @@
+function handleRequest(request, response)
+{
+ response.setHeader('Content-Type', 'text/plain', false);
+ response.setHeader('Cache-Control', 'no-cache', false);
+ response.setHeader('Content-Type', 'application/x-test', false);
+ if (request.hasHeader('Referer')) {
+ response.write('Referer found: ' + request.getHeader('Referer'));
+ }
+ else {
+ response.write('No Referer found');
+ }
+}
diff --git a/dom/plugins/test/mochitest/plugin-utils.js b/dom/plugins/test/mochitest/plugin-utils.js
new file mode 100644
index 0000000000..90d3c285d6
--- /dev/null
+++ b/dom/plugins/test/mochitest/plugin-utils.js
@@ -0,0 +1,95 @@
+function paintCountIs(plugin, expected, msg) {
+ var count = plugin.getPaintCount();
+ var realExpected = expected;
+ ++realExpected; // extra paint at startup for all async-rendering plugins
+ ok(realExpected == count, msg + " (expected " + expected +
+ " independent paints, expected " + realExpected + " logged paints, got " +
+ count + " actual paints)");
+}
+
+function getTestPlugin(pluginName) {
+ var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"]
+ .getService(SpecialPowers.Ci.nsIPluginHost);
+ var tags = ph.getPluginTags();
+ var name = pluginName || "Test Plug-in";
+ for (var tag of tags) {
+ if (tag.name == name) {
+ return tag;
+ }
+ }
+
+ ok(false, "Could not find plugin tag with plugin name '" + name + "'");
+ return null;
+}
+
+// call this to set the test plugin(s) initially expected enabled state.
+// it will automatically be reset to it's previous value after the test
+// ends
+function setTestPluginEnabledState(newEnabledState, pluginName) {
+ var oldEnabledState = SpecialPowers.setTestPluginEnabledState(newEnabledState, pluginName);
+ var plugin = getTestPlugin(pluginName);
+ while (plugin.enabledState != newEnabledState) {
+ // Run a nested event loop to wait for the preference change to
+ // propagate to the child. Yuck!
+ SpecialPowers.Services.tm.currentThread.processNextEvent(true);
+ }
+ SimpleTest.registerCleanupFunction(function() {
+ SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
+ });
+}
+
+function crashAndGetCrashServiceRecord(crashMethodName, callback) {
+ var crashMan =
+ SpecialPowers.Cu.import("resource://gre/modules/Services.jsm").
+ Services.crashmanager;
+
+ // First, clear the crash record store.
+ info("Waiting for pruneOldCrashes");
+ var future = new Date(Date.now() + 1000 * 60 * 60 * 24);
+ crashMan.pruneOldCrashes(future).then(function () {
+
+ var iframe = document.getElementById("iframe1");
+ var p = iframe.contentDocument.getElementById("plugin1");
+
+ var crashDateMS = Date.now();
+ try {
+ p[crashMethodName]();
+ ok(false, "p." + crashMethodName + "() should throw an exception");
+ }
+ catch (e) {
+ ok(true, "p." + crashMethodName + "() should throw an exception");
+ }
+
+ // The crash record store is written and read back asyncly, so poll for
+ // the new record.
+ function tryGetCrash() {
+ info("Waiting for getCrashes");
+ crashMan.getCrashes().then(SpecialPowers.wrapCallback(function (crashes) {
+ if (crashes.length) {
+ is(crashes.length, 1, "There should be only one record");
+ var crash = SpecialPowers.wrap(crashes[0]);
+ ok(!!crash.id, "Record should have an ID");
+ ok(!!crash.crashDate, "Record should have a crash date");
+ var dateMS = crash.crashDate.valueOf();
+ var twoMin = 1000 * 60 * 2;
+ ok(crashDateMS - twoMin <= dateMS &&
+ dateMS <= crashDateMS + twoMin,
+ "Record's crash date should be nowish: " +
+ "now=" + crashDateMS + " recordDate=" + dateMS);
+ callback(crashMan, crash);
+ }
+ else {
+ setTimeout(tryGetCrash, 1000);
+ }
+ }), function (err) {
+ ok(false, "Error getting crashes: " + err);
+ SimpleTest.finish();
+ });
+ }
+ setTimeout(tryGetCrash, 1000);
+
+ }, function () {
+ ok(false, "pruneOldCrashes error");
+ SimpleTest.finish();
+ });
+}
diff --git a/dom/plugins/test/mochitest/plugin_no_scroll_div.html b/dom/plugins/test/mochitest/plugin_no_scroll_div.html
new file mode 100644
index 0000000000..b28f6d6ffb
--- /dev/null
+++ b/dom/plugins/test/mochitest/plugin_no_scroll_div.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+ <embed id="testplugin" type="application/x-test" drawmode="solid" color="ff00ff00" wmode="window"
+ style="position:absolute; top:5px; left:5px; width:500px; height:250px">
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/plugin_scroll_invalidation.html b/dom/plugins/test/mochitest/plugin_scroll_invalidation.html
new file mode 100644
index 0000000000..74a68ff4ac
--- /dev/null
+++ b/dom/plugins/test/mochitest/plugin_scroll_invalidation.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test helper for plugin child widgets not being invalidated by scrolling</title>
+ <style>
+ body {
+ background: linear-gradient(red, black);
+ width: 200px;
+ height: 200px;
+ }
+ embed {
+ position:absolute;
+ }
+ embed#paint-waiter {
+ top: 0px;
+ left: 0px;
+ width: 0px;
+ height: 0px;
+ }
+ embed#e0 {
+ top: 70px;
+ left: 70px;
+ width: 10px;
+ height: 10px;
+ }
+ embed#e1 {
+ top: 60px;
+ left: 60px;
+ width: 10px;
+ height: 20px;
+ }
+ embed#e2 {
+ top: 60px;
+ left: 70px;
+ width: 20px;
+ height: 10px;
+ }
+ embed#e3 {
+ top: 70px;
+ left: 80px;
+ width: 10px;
+ height: 20px;
+ }
+ embed#e4 {
+ top: 80px;
+ left: 60px;
+ width: 20px;
+ height: 10px;
+ }
+ </style>
+</head>
+<body>
+ <embed id="paint-waiter" type="application/x-test" wmode="window">
+ <embed id="e0" type="application/x-test" wmode="window" class="scrolling"/>
+ <embed id="e1" type="application/x-test" wmode="window" class="scrolling"/>
+ <embed id="e2" type="application/x-test" wmode="window" class="scrolling"/>
+ <embed id="e3" type="application/x-test" wmode="window" class="scrolling"/>
+ <embed id="e4" type="application/x-test" wmode="window" class="scrolling"/>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/plugin_subframe_test.html b/dom/plugins/test/mochitest/plugin_subframe_test.html
new file mode 100644
index 0000000000..598521d57e
--- /dev/null
+++ b/dom/plugins/test/mochitest/plugin_subframe_test.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+ <iframe id="subframe" style="width:510px; height:260px;" src="plugin_no_scroll_div.html"></iframe>
+ <div style="display:block; height:3000px;"></div>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/plugin_test.html b/dom/plugins/test/mochitest/plugin_test.html
new file mode 100644
index 0000000000..c7eb376cd0
--- /dev/null
+++ b/dom/plugins/test/mochitest/plugin_test.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+ <embed id="testplugin" type="application/x-test" drawmode="solid" color="ff00ff00" wmode="window"
+ style="position:absolute; top:50px; left:50px; width:500px; height:250px">
+<div style="display:block; height:3000px;"></div>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/plugin_visibility_loader.html b/dom/plugins/test/mochitest/plugin_visibility_loader.html
new file mode 100644
index 0000000000..22802d9a51
--- /dev/null
+++ b/dom/plugins/test/mochitest/plugin_visibility_loader.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body style="background-color: #88AAFF;">
+ <h1>This Page Has a Solid Plugin</h1>
+
+ <p><embed id="p" type="application/x-test" drawmode="solid" color="FFFF0000" width="200" height="200"></embed>
diff --git a/dom/plugins/test/mochitest/plugin_window.html b/dom/plugins/test/mochitest/plugin_window.html
new file mode 100644
index 0000000000..d3a298e89c
--- /dev/null
+++ b/dom/plugins/test/mochitest/plugin_window.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>NPAPI Stream Tests</title>
+</head>
+<body onload="doTest()">
+<p id="display"></p>
+<div id="content" style="display: none">
+This is a plugin test window.
+</div>
+<div id="test">
+<script class="testbody" type="text/javascript">
+
+function doTest() {
+ window.opener.continueTest();
+}
+
+</script>
+</div>
+</body>
+
+</html>
+
diff --git a/dom/plugins/test/mochitest/pluginstream.js b/dom/plugins/test/mochitest/pluginstream.js
new file mode 100644
index 0000000000..9c47cd5f89
--- /dev/null
+++ b/dom/plugins/test/mochitest/pluginstream.js
@@ -0,0 +1,39 @@
+ SimpleTest.waitForExplicitFinish();
+
+ function frameLoaded(finishWhenCalled = true, lastObject = false) {
+ var testframe = document.getElementById('testframe');
+ function getNode(list) {
+ if (list.length === 0)
+ return undefined;
+ return lastObject ? list[list.length - 1] : list[0];
+ }
+ var embed = getNode(document.getElementsByTagName('embed'));
+ if (undefined === embed)
+ embed = getNode(document.getElementsByTagName('object'));
+
+ // In the file:// URI case, this ends up being cross-origin.
+ // Skip these checks in that case.
+ if (testframe.contentDocument) {
+ var content = testframe.contentDocument.body.innerHTML;
+ if (!content.length)
+ return;
+
+ var filename = embed.getAttribute("src") ||
+ embed.getAttribute("geturl") ||
+ embed.getAttribute("geturlnotify") ||
+ embed.getAttribute("data");
+
+ var req = new XMLHttpRequest();
+ req.open('GET', filename, false);
+ req.overrideMimeType('text/plain; charset=x-user-defined');
+ req.send(null);
+ is(req.status, 200, "bad XMLHttpRequest status");
+ is(content, req.responseText.replace(/\r\n/g, "\n"),
+ "content doesn't match");
+ }
+
+ is(embed.getError(), "pass", "plugin reported error");
+ if (finishWhenCalled) {
+ SimpleTest.finish();
+ }
+ }
diff --git a/dom/plugins/test/mochitest/post.sjs b/dom/plugins/test/mochitest/post.sjs
new file mode 100644
index 0000000000..b391dbdd81
--- /dev/null
+++ b/dom/plugins/test/mochitest/post.sjs
@@ -0,0 +1,17 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream");
+
+function handleRequest(request, response)
+{
+ var body = "";
+ var bodyStream = new BinaryInputStream(request.bodyInputStream);
+ var bytes = [], avail = 0;
+ while ((avail = bodyStream.available()) > 0)
+ body += String.fromCharCode.apply(String, bodyStream.readByteArray(avail));
+
+ response.setHeader("Content-Type", "text/html", false);
+ response.write(body);
+}
+
diff --git a/dom/plugins/test/mochitest/privatemode_perwindowpb.xul b/dom/plugins/test/mochitest/privatemode_perwindowpb.xul
new file mode 100644
index 0000000000..ab7f06395c
--- /dev/null
+++ b/dom/plugins/test/mochitest/privatemode_perwindowpb.xul
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="NPAPI Private Mode Tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<body xmlns="http://www.w3.org/1999/xhtml">
+<embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+<embed id="plugin2" type="application/x-test" width="200" height="200"></embed>
+</body>
+</window>
diff --git a/dom/plugins/test/mochitest/test_CrashService_crash.html b/dom/plugins/test/mochitest/test_CrashService_crash.html
new file mode 100644
index 0000000000..84508a6dc0
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_CrashService_crash.html
@@ -0,0 +1,28 @@
+<head>
+ <title>nsICrashService plugin crash</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+<body>
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout(
+ "crashAndGetCrashServiceRecord() polls for async crash recording");
+ SimpleTest.requestCompleteLog();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ window.frameLoaded = function frameLoaded_toCrash() {
+ SimpleTest.expectChildProcessCrash();
+
+ crashAndGetCrashServiceRecord("crash", function (cm, crash) {
+ var isPluginCrash = crash.isOfType(cm.PROCESS_TYPE_PLUGIN, cm.CRASH_TYPE_CRASH);
+ ok(isPluginCrash, "Record should be a plugin crash");
+ if (!isPluginCrash) {
+ dump("Crash type: " + crash.type + "\n");
+ }
+ SimpleTest.finish();
+ });
+
+ }
+ </script>
+ <iframe id="iframe1" src="crashing_subpage.html" width="600" height="600"></iframe>
diff --git a/dom/plugins/test/mochitest/test_CrashService_hang.html b/dom/plugins/test/mochitest/test_CrashService_hang.html
new file mode 100644
index 0000000000..6ecf7d419a
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_CrashService_hang.html
@@ -0,0 +1,28 @@
+<head>
+ <title>nsICrashService plugin hang</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+<body>
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout(
+ "crashAndGetCrashServiceRecord() polls for async crash recording");
+ SimpleTest.requestCompleteLog();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ window.frameLoaded = function frameLoaded_toCrash() {
+ SimpleTest.expectChildProcessCrash();
+
+ // the default timeout is annoying high for mochitest runs
+ var timeoutPref = "dom.ipc.plugins.timeoutSecs";
+ SpecialPowers.setIntPref(timeoutPref, 5);
+
+ crashAndGetCrashServiceRecord("hang", function (cm, crash) {
+ ok(crash.isOfType(cm.PROCESS_TYPE_PLUGIN, cm.CRASH_TYPE_HANG),
+ "Record should be a plugin hang");
+ SimpleTest.finish();
+ });
+ }
+ </script>
+ <iframe id="iframe1" src="crashing_subpage.html" width="600" height="600"></iframe>
diff --git a/dom/plugins/test/mochitest/test_GCrace.html b/dom/plugins/test/mochitest/test_GCrace.html
new file mode 100644
index 0000000000..50b598ef1e
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_GCrace.html
@@ -0,0 +1,62 @@
+<head>
+ <title>GC race with actors on the parent</title>
+
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+<body onload="start()">
+ <p id="display"></p>
+
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function start() {
+ setTimeout(checkGCRace, 1000);
+ }
+
+ var nested = false;
+
+ function cb(f) {
+ ok(!nested, "Callback shouldn't occur in a nested stack frame");
+ try {
+ f(35);
+ ok(true, "Callback was called, no crash");
+ }
+ catch (e) {
+ ok(false, "Exception calling callback object: " + e);
+ }
+ SimpleTest.executeSoon(removePlugin);
+ }
+
+ function removePlugin() {
+ var p = document.getElementById('p');
+ p.parentNode.removeChild(p);
+ p = null;
+ SpecialPowers.Cu.forceGC();
+ SimpleTest.finish();
+ }
+
+ function checkGCRace() {
+ nested = true;
+
+ // The plugin will hand back a function and immediately sleep.
+ // We will lose our only reference to the function and force GC, followed
+ // by calling us with that function object again. We should be able to
+ // call the function and not crash.
+ var p = document.getElementById('p');
+ var f = p.checkGCRace(cb);
+ f = null; // 'f' should be collected next GC
+
+ nested = false;
+
+ setTimeout(function() {
+ SpecialPowers.Cu.forceGC();
+ }, 2000);
+ }
+ </script>
+
+ <embed id="p" type="application/x-test" wmode="window"></embed>
diff --git a/dom/plugins/test/mochitest/test_NPNVdocumentOrigin.html b/dom/plugins/test/mochitest/test_NPNVdocumentOrigin.html
new file mode 100644
index 0000000000..75ec66e138
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_NPNVdocumentOrigin.html
@@ -0,0 +1,47 @@
+<html>
+<head>
+ <title>Test NPNVdocumentOrigin</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+</head>
+<body onload="runTest()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function runTest() {
+ "use strict";
+ var p1 = document.getElementById("plugin1");
+ var realOrigin = "http://mochi.test:8888";
+
+ // Test with no modifications
+ is(p1.getNPNVdocumentOrigin(), realOrigin, "Checking for expected origin.");
+
+ // This used to test that shadowing window.location.toString didn't confuse
+ // getNPNVdocumentOrigin. But now we explicitly throw when that happens. So
+ // just verify that we throw. There's no reason why getNPNVdocumentOrigin _would_
+ // be confused in this case anyway.
+ try {
+ window.location.toString = function() { return 'http://victim.rckc.at/'; }
+ ok(false, "Should throw when shadowing window.location.toString");
+ }
+ catch (e) {
+ ok(true, "Should throw when shadowing window.location.toString");
+ }
+
+ // Create a plugin in a new window with about:blank
+ var newWindow = window.open("about:blank");
+ newWindow.onload = function() {
+ newWindow.document.writeln('<embed id="plugin2" type="application/x-test" width="200" height="200"></embed>');
+ var p2 = newWindow.document.getElementById("plugin2");
+ is(p2.getNPNVdocumentOrigin(), realOrigin, "Checking for expected origin of plugin in new about:blank window.");
+ newWindow.close();
+
+ SimpleTest.finish();
+ };
+ }
+ </script>
+
+ <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_NPPVpluginWantsAllNetworkStreams.html b/dom/plugins/test/mochitest/test_NPPVpluginWantsAllNetworkStreams.html
new file mode 100644
index 0000000000..588e785d30
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_NPPVpluginWantsAllNetworkStreams.html
@@ -0,0 +1,77 @@
+<html>
+<head>
+ <title>Test NPPVpluginWantsAllNetworkStreams</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+</head>
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var p = null;
+
+ var missingDoc = "not-found.html";
+
+ var expectedWriteURL = "";
+ var expectedNotifyStatus = -1;
+
+ var writeHappened = false;
+ var expectedWrite = false;
+
+ function writeCallback(url) {
+ writeHappened = true;
+ }
+
+ function notifyCallback(status, data) {
+ is(writeHappened, expectedWrite, "Test for expected write.");
+ is(status, expectedNotifyStatus, "Test for expected stream notification status.");
+ runNextTest();
+ }
+
+ function test1() {
+ // In this test we do not expect a stream for the missing document.
+ p.setPluginWantsAllStreams(false);
+
+ expectedWriteURL = missingDoc;
+ expectedNotifyStatus = 1;
+
+ writeHappened = false;
+ expectedWrite = false;
+
+ p.streamTest(missingDoc, false, null, writeCallback, notifyCallback, null, false);
+ }
+
+ function test2() {
+ // In this test we expect a stream for the missing document.
+ p.setPluginWantsAllStreams(true);
+
+ expectedWriteURL = missingDoc;
+ expectedNotifyStatus = 0;
+
+ writeHappened = false;
+ expectedWrite = true;
+
+ p.streamTest(missingDoc, false, null, writeCallback, notifyCallback, null, false);
+ }
+
+ var tests = [test1, test2];
+ var currentTest = -1;
+ function runNextTest() {
+ currentTest++;
+ if (currentTest < tests.length) {
+ tests[currentTest]();
+ } else {
+ SimpleTest.finish();
+ }
+ }
+
+ function runTests() {
+ p = document.getElementById("plugin1");
+ runNextTest();
+ }
+ </script>
+
+ <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug1092842.html b/dom/plugins/test/mochitest/test_bug1092842.html
new file mode 100644
index 0000000000..e22d0a3b9f
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug1092842.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Bug 1092842</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<body onload="startTest()">
+ <script type="application/javascript;version=1.8">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var p = null;
+
+ function startTest() {
+ p = document.getElementById('theplugin');
+ if (!p.hasWidget()) {
+ todo(false, "This test is only relevant for windowed plugins");
+ SimpleTest.finish();
+ return;
+ }
+
+ // Wait for the plugin to have painted once.
+ var interval = setInterval(function() {
+ if (!p.getPaintCount())
+ return;
+
+ clearInterval(interval);
+ doTest();
+ SimpleTest.finish();
+ }, 100);
+ }
+
+ function doTest() {
+ is(p.getClipRegionRectCount(), 1, "getClipRegionRectCount should be a single rect");
+ is(p.getClipRegionRectEdge(0,2) - p.getClipRegionRectEdge(0,0), 100, "width of clip region rect");
+ is(p.getClipRegionRectEdge(0,3) - p.getClipRegionRectEdge(0,1), 26, "height of clip region rect");
+ }
+ </script>
+
+ <div style="position:fixed; z-index:1; left:0; right:0; top:0; height:100px; border-bottom:24px solid blue; background:pink; transform:translateZ(0)"></div>
+ <object id="theplugin" type="application/x-test" drawmode="solid" color="ff00ff00" wmode="window"
+ style="position:absolute; top:50px; left:0; width:100px; height:100px"></object>
+
+ <p id="display"></p>
diff --git a/dom/plugins/test/mochitest/test_bug1165981.html b/dom/plugins/test/mochitest/test_bug1165981.html
new file mode 100644
index 0000000000..360d9312ad
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug1165981.html
@@ -0,0 +1,81 @@
+<html>
+ <head>
+ <title>Bug 1165981 Test</title>
+
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <script class="testbody" type="application/javascript">
+ "use strict";
+
+ SimpleTest.waitForExplicitFinish();
+ ok(SpecialPowers.setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED, "Shockwave Flash"), "Should find allowed test flash plugin");
+ ok(SpecialPowers.setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED, "Silverlight Test Plug-in"), "Should find allowed test silverlight plugin");
+ ok(!SpecialPowers.setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED, "Third Test Plug-in"), "Should not find disallowed plugin");
+
+ function findPlugin(pluginName) {
+ for (var i = 0; i < navigator.plugins.length; i++) {
+ var plugin = navigator.plugins[i];
+ if (plugin.name === pluginName) {
+ return plugin;
+ }
+ }
+ return null;
+ }
+
+ function findMimeType(mimeTypeType) {
+ for (var i = 0; i < navigator.mimeTypes.length; i++) {
+ var mimeType = navigator.mimeTypes[i];
+ if (mimeType.type === mimeTypeType) {
+ return mimeType;
+ }
+ }
+ return null;
+ }
+
+ function createNode(id, type) {
+ let obj = document.createElement("object");
+ obj.type = type;
+ obj.id = id;
+ obj.width = 200;
+ obj.height = 200;
+ document.body.appendChild(obj);
+ }
+
+ function run() {
+ createNode("plugin-flash", "application/x-shockwave-flash-test");
+ createNode("plugin-silverlight", "application/x-silverlight-test");
+ createNode("disallowedPlugin", "application/x-third-test");
+ var pluginElement = document.getElementById("plugin-flash");
+ is(pluginElement.identifierToStringTest("foo"), "foo", "Should be able to call a function provided by the plugin");
+
+ pluginElement = document.getElementById("plugin-silverlight");
+ is(pluginElement.identifierToStringTest("foo"), "foo", "Should be able to call a function provided by the plugin");
+
+ pluginElement = document.getElementById("disallowedPlugin");
+ is(typeof pluginElement.identifierToStringTest, "undefined", "Should NOT be able to call a function on a disallowed plugin");
+
+ ok(navigator.plugins["Shockwave Flash"], "Should have queried a plugin named 'Shockwave Flash'");
+ ok(navigator.plugins["Silverlight Test Plug-in"], "Should have queried a plugin named 'Silverlight Test Plug-in'");
+ ok(!navigator.plugins["Third Test Plug-in"], "Should NOT have queried a disallowed plugin named 'Third Test Plug-in'");
+
+ ok(findPlugin("Shockwave Flash"), "Should have found a plugin named 'Shockwave Flash'");
+ ok(findPlugin("Silverlight Test Plug-in"), "Should have found a plugin named 'Silverlight Test Plug-in'");
+ ok(!findPlugin("Third Test Plug-in"), "Should NOT found a disallowed plugin named 'Third Test Plug-in'");
+
+ ok(navigator.mimeTypes["application/x-shockwave-flash-test"], "Should have queried a MIME type named 'application/x-shockwave-flash-test'");
+ ok(navigator.mimeTypes["application/x-silverlight-test"], "Should have queried a MIME type named 'application/x-silverlight-test'");
+ ok(!navigator.mimeTypes["application/x-third-test"], "Should NOT have queried a disallowed type named 'application/x-third-test'");
+
+ ok(findMimeType("application/x-shockwave-flash-test"), "Should have found a MIME type named 'application/x-shockwave-flash-test'");
+ ok(findMimeType("application/x-silverlight-test"), "Should have found a MIME type named 'application/x-silverlight-test'");
+ ok(!findMimeType("application/x-third-test"), "Should NOT have found a disallowed MIME type named 'application/x-third-test'");
+
+ SimpleTest.finish();
+ }
+ </script>
+
+ <body onload="run()">
+ </body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug1245545.html b/dom/plugins/test/mochitest/test_bug1245545.html
new file mode 100644
index 0000000000..23e3b21e0c
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug1245545.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta><charset="utf-8"/>
+ <title>Test Modifying Plugin click-to-play Flag</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body onload="startTest()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ SpecialPowers.pushPrefEnv({ "set": [
+ ['dom.ipc.plugins.unloadTimeoutSecs', 0]
+ ] });
+
+ function startTest() {
+ let url = SimpleTest.getTestFileURL("file_bug1245545.js");
+ let script = SpecialPowers.loadChromeScript(url);
+ script.addMessageListener("check-plugin-unload", testChromeUnload);
+ var testPlugin = getTestPlugin();
+ ok(testPlugin, "Should have Test Plug-in");
+ is(testPlugin.loaded, true, "Test plugin should be loaded");
+ var pluginNode = document.getElementById("theplugin");
+ pluginNode.parentNode.removeChild(pluginNode);
+ // Poll for plugin to unload.
+ function testContentUnload() {
+ if (!testPlugin.loaded) {
+ ok(true, "Test plugin unloaded in client process");
+ // Start the chrome unload test
+ testChromeUnload(true);
+ } else {
+ setTimeout(testContentUnload, 0);
+ }
+ }
+
+ function testChromeUnload(isLoaded) {
+ if (!isLoaded) {
+ ok(true, "Test plugin unloaded in chrome process");
+ SimpleTest.finish();
+ } else {
+ var results = script.sendAsyncMessage("check-plugin-unload");
+ }
+ }
+ testContentUnload();
+ }
+ </script>
+ <object id="theplugin" type="application/x-test"></object>
+
+ </body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug1307694.html b/dom/plugins/test/mochitest/test_bug1307694.html
new file mode 100644
index 0000000000..97ee1b6efc
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug1307694.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onLoad="addPluginElement()">
+
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function addPluginElement() {
+ var p = document.createElement('embed');
+ p.setAttribute('id', 'plugin2');
+ p.setAttribute('type', 'application/x-shockwave-flash-test');
+ p.setAttribute('scale', 'noscale');
+ p.setAttribute('salign', 'lt');
+ document.body.appendChild(p);
+ SimpleTest.executeSoon(function() {
+ runTests();
+ });
+ }
+
+ function runTests() {
+ p = document.getElementById('plugin1');
+ ok(p.setColor != undefined, "Static plugin parameter (salign/scale) ordering were correct");
+ p2 = document.getElementById('plugin2');
+ ok(p2.setColor != undefined, "Dynamic plugin parameter (salign/scale) ordering were correct");
+ SimpleTest.finish();
+ }
+
+ </script>
+ <p id="display"></p>
+
+ <div id="div1">
+ <embed id="plugin1" type="application/x-shockwave-flash-test" width="200" height="200" scale="noscale" salign="lt"></embed>
+ </div>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug406541.html b/dom/plugins/test/mochitest/test_bug406541.html
new file mode 100644
index 0000000000..6ce9d4554f
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug406541.html
@@ -0,0 +1,100 @@
+<!doctype html>
+<html>
+<head>
+ <title>Test for Bug 406541</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+</head>
+<body>
+<script type="application/x-child-payload" id="child-payload">
+ // This is injected into the file:/// origin iframe, see below.
+
+ // appletA should spawn, appletB, with a codebase outside the temp directory,
+ // should not.
+ var appletA = document.createElement("applet");
+ var appletB = document.createElement("applet");
+ var appletC = document.createElement("applet");
+ appletA.type = appletB.type = appletC.type = "application/x-java-test";
+ appletB.setAttribute("codebase", "file:///");
+ appletC.setAttribute("codebase", "./subdir_bug406541/");
+ document.body.appendChild(appletA);
+ document.body.appendChild(appletB);
+ document.body.appendChild(appletC);
+ function isSpawned(plugin) {
+ try {
+ var x = plugin.getJavaCodebase();
+ return true;
+ } catch (e) {}
+ return false;
+ }
+ window.parent.postMessage({ "A": isSpawned(appletA),
+ "B": isSpawned(appletB),
+ "C": isSpawned(appletC) }, "*");
+</script>
+<script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED,
+ "Java Test Plug-in");
+ SpecialPowers.pushPrefEnv({ "set": [
+ ['plugin.java.mime', 'application/x-java-test']
+ ] }, runTest);
+
+ function runTest() {
+ // Create a empty file and point an iframe at it
+ var Cc = SpecialPowers.Cc;
+ var Ci = SpecialPowers.Ci;
+ var file = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("TmpD", Ci.nsIFile);
+ var subdir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("TmpD", Ci.nsIFile);
+ file.append("test_bug406541.html");
+ file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
+ subdir.append("subdir_bug406541");
+ subdir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0600);
+
+ var i = document.createElement("iframe");
+ var loaded = false;
+ i.addEventListener("load", function initialLoad() {
+ if (!loaded) {
+ // Once loaded, use special powers to point it at the file
+ SpecialPowers.wrap(i.contentWindow).location.href = "file://" + file.path;
+ loaded = true;
+ } else {
+ // Inject the child-payload script to the file:/// origin. Let it test
+ // applet spawning and send the results in a postMessage. (Because I
+ // couldn't get SpecialPowers to let me touch applets cross-origin, then
+ // gave up.)
+ var innerdoc = SpecialPowers.wrap(i.contentWindow).document;
+ var s = innerdoc.createElement("script");
+ s.type = "text/javascript";
+ s.textContent = document.getElementById("child-payload").textContent;
+ var finished = false;
+ window.onmessage = function(message) {
+ ok(message.data.A, "Plugin A should spawn");
+ ok(!message.data.B, "Plugin B should NOT spawn");
+ ok(message.data.C, "Plugin C should spawn");
+ file.remove(false);
+ subdir.remove(false);
+ finished = true;
+ SimpleTest.finish();
+ };
+ innerdoc.body.appendChild(s);
+
+ SimpleTest.executeSoon(function() {
+ if (!finished) {
+ ok(finished, "Should have received callback by now");
+ SimpleTest.finish();
+ }
+ });
+ }
+ }, false);
+ document.body.appendChild(i);
+ }
+</script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug479979.xul b/dom/plugins/test/mochitest/test_bug479979.xul
new file mode 100644
index 0000000000..1f295cbeb5
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug479979.xul
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="NPAPI Set Undefined Value Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript" src="plugin-utils.js"></script>
+<body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+function runTests() {
+ var pluginElement1 = document.getElementById("plugin1");
+
+ var rv = true; // we want !true from the test plugin
+ var exceptionThrown = false;
+ try {
+ rv = pluginElement1.setUndefinedValueTest();
+ } catch (e) {
+ exceptionThrown = true;
+ }
+ is(exceptionThrown, false, "Exception thrown setting undefined variable.");
+ is(rv, false, "Setting undefined variable succeeded.");
+
+ // give the UI a chance to settle with the current enabled plugin state
+ // before we finish the test and reset the state to disabled. Otherwise
+ // the UI shows the plugin infobar!
+ SimpleTest.executeSoon(SimpleTest.finish);
+}
+]]>
+</script>
+<embed id="plugin1" type="application/x-test" width="300" height="300"></embed>
+</body>
+</window>
diff --git a/dom/plugins/test/mochitest/test_bug532208.html b/dom/plugins/test/mochitest/test_bug532208.html
new file mode 100644
index 0000000000..0a4cdb985a
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug532208.html
@@ -0,0 +1,29 @@
+<head>
+<title>Test for correct async delivery of large streams, bug
+532208</title>
+
+<script type="application/javascript"
+src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="application/javascript" src="plugin-utils.js"></script>
+
+<body onload="setTimeout(runTests, 2000)">
+
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+function runTests() {
+ try {
+ document.getElementById('plugin1').getPaintCount();
+ ok(true, "Data delivery didn't crash");
+ }
+ catch (e) {
+ ok(false, "Data delivery crashed");
+ }
+ SimpleTest.finish();
+}
+</script>
+
+<embed id="plugin1" type="application/x-test" width="400"
+ height="400" src="large-pic.jpg" functiontofail="npp_write_rpc" streammode="normal"></embed>
diff --git a/dom/plugins/test/mochitest/test_bug539565-1.html b/dom/plugins/test/mochitest/test_bug539565-1.html
new file mode 100644
index 0000000000..022b8cfc61
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug539565-1.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=539565
+-->
+<head>
+ <title>Test #1 for Bug 539565</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script class="testbody" type="text/javascript">
+function runTests() {
+ var moveBy = 17;
+ var waitedForPaint = 0;
+ function waitForPaint(func) {
+ waitedForPaint = 0;
+ var testplugin = $("plugin1");
+ testplugin.last_paint_count = testplugin.getPaintCount ? testplugin.getPaintCount() : -2;
+ function waitForPaintCountIncrement() {
+ waitedForPaint++;
+ moveBy = -moveBy;
+ $("abs").style.left = ($("abs").offsetLeft + moveBy) + 'px';
+ var x = document.documentElement.offsetHeight;
+ var pc = testplugin.getPaintCount ? testplugin.getPaintCount() : -2;
+ if (waitedForPaint == 20 || (pc != testplugin.last_paint_count && pc >= 0)) {
+ setTimeout(func,0);
+ } else
+ setTimeout(waitForPaintCountIncrement, 50);
+ }
+ waitForPaintCountIncrement();
+ }
+
+ function doClick(x,y,func) {
+ synthesizeMouse($("plugin1"), x, y, {}, window);
+ setTimeout(func,0);
+ }
+
+ function verify(test,x,y,next) {
+ var p = $("plugin1").getLastMouseX();
+ const delta = 2;
+ ok(p-delta <= x && x <= p+delta, "test"+test+" LastMouseX got " + p + " expected " + x +
+ " with fullZoom="+SpecialPowers.getFullZoom(window)+" MozTransform='"+$("container").style.MozTransform+"'");
+ p = $("plugin1").getLastMouseY();
+ ok(p-delta <= y && y <= p+delta, "test"+test+" LastMouseY got " + p + " expected " + y +
+ " with fullZoom="+SpecialPowers.getFullZoom(window)+" MozTransform='"+$("container").style.MozTransform+"'");
+ if (next) next();
+ }
+
+ function click(x,y,next) {
+ waitForPaint(function(){doClick(x,y,next);})
+ }
+ function zoom(factor) {
+ SpecialPowers.setFullZoom(window, factor);
+ }
+
+ function test1() { // fullZoom=1 (sanity check)
+ zoom(1);
+ click(55,136, function(){verify("1",55,136,test2)});
+ }
+ function test2() { // fullZoom=2
+ zoom(2);
+ click(40,108, function(){verify("2",80,216,test2b)})
+ }
+ function test2b() {
+ click(108,112, function(){verify("2c",216,224,endTest)})
+ }
+
+ function endTest() {
+ zoom(1);
+ SimpleTest.finish();
+ }
+
+ setTimeout(function(){waitForPaint(test1)},1000);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ </script>
+</head>
+
+<body onload="runTests()">
+ <div id="container" style="position:relative;top: 0px; left: 0px; width: 640px; height: 480px;">
+ <div id="abs" style="position:absolute; left:90px; top:90px; width:20px; height:20px; background:blue; pointer-events:none;"></div>
+ <embed id="plugin1" type="application/x-test" wmode="transparent" width="200" height="200"></embed>
+ </div>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug539565-2.html b/dom/plugins/test/mochitest/test_bug539565-2.html
new file mode 100644
index 0000000000..7d8d02a42e
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug539565-2.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=539565
+-->
+<head>
+ <title>Test #2 for Bug 539565</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script class="testbody" type="text/javascript">
+function runTests() {
+ var moveBy = 17;
+ var waitedForPaint = 0;
+ function waitForPaint(func) {
+ waitedForPaint = 0;
+ var testplugin = $("plugin1");
+ testplugin.last_paint_count = testplugin.getPaintCount ? testplugin.getPaintCount() : -2;
+ function waitForPaintCountIncrement() {
+ waitedForPaint++;
+ moveBy = -moveBy;
+ $("abs").style.left = ($("abs").offsetLeft + moveBy) + 'px';
+ var x = document.documentElement.offsetHeight;
+ var pc = testplugin.getPaintCount ? testplugin.getPaintCount() : -2;
+ if (waitedForPaint == 20 || (pc != testplugin.last_paint_count && pc >= 0)) {
+ setTimeout(func,0);
+ } else
+ setTimeout(waitForPaintCountIncrement, 50);
+ }
+ waitForPaintCountIncrement();
+ }
+
+ function doClick(x,y,func) {
+ synthesizeMouse($("plugin1"), x, y, {}, window);
+ setTimeout(func,0);
+ }
+
+ function verify(test,x,y,next) {
+ var p = $("plugin1").getLastMouseX();
+ const delta = 2;
+ ok(p-delta <= x && x <= p+delta, "test"+test+" LastMouseX got " + p + " expected " + x +
+ " with fullZoom="+SpecialPowers.getFullZoom(window)+" MozTransform='"+$("container").style.MozTransform+"'");
+ p = $("plugin1").getLastMouseY();
+ ok(p-delta <= y && y <= p+delta, "test"+test+" LastMouseY got " + p + " expected " + y +
+ " with fullZoom="+SpecialPowers.getFullZoom(window)+" MozTransform='"+$("container").style.MozTransform+"'");
+ if (next) next();
+ }
+
+ function click(x,y,next) {
+ waitForPaint(function(){doClick(x,y,next);})
+ }
+ function zoom(factor) {
+ SpecialPowers.setFullZoom(window, factor);
+ }
+
+ function test3() { // fullZoom=1 + scale(2)
+ zoom(1);
+ //
+ // ======================== BUG WARNING =========================================
+ // 'container' already has -moz-transform:scale(2) in its style attribute.
+ // Removing that and setting MozTransform dynamically here (as in test4)
+ // makes the test fail ("getLastMouseX is not a function" in verify() above)
+ // Looks like the plugin instance got destroyed and we never recover from that...
+ // ==============================================================================
+ //
+ click(50,136, function(){verify("3",25,68,test3b)});
+ }
+ function test3b() {
+ click(208,212, function(){verify("3b",104,106,test4)});
+ }
+ function test4() { // fullZoom=2 + scale(0.5)
+ zoom(2);
+ var container = $("container");
+ container.style.MozTransformOrigin = "0px 0px";
+ container.style.MozTransform = "scale(0.5)";
+ var x = document.documentElement.offsetHeight;
+ click(60,52, function(){verify("4",240,208,test5)});
+ }
+ function test5() { // fullZoom=2 + scale(2)
+ zoom(2);
+ var container = $("container");
+ container.style.MozTransformOrigin = "0px 0px";
+ container.style.MozTransform = "scale(2)";
+ var x = document.documentElement.offsetHeight;
+ click(108,112, function(){verify("5",108,112,endTest)});
+ }
+
+ function endTest() {
+ zoom(1);
+ SimpleTest.finish();
+ }
+
+ setTimeout(function(){waitForPaint(test3)},1000);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ </script>
+</head>
+
+<body onload="runTests()">
+ <div id="container" style="position:relative;top: 0px; left: 0px; width: 640px; height: 480px; -moz-transform:scale(2); -moz-transform-origin:0px 0px;">
+ <div id="abs" style="position:absolute; left:90px; top:90px; width:20px; height:20px; background:blue; pointer-events:none;"></div>
+ <embed id="plugin1" type="application/x-test" wmode="transparent" width="200" height="200"></embed>
+ </div>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug738396.html b/dom/plugins/test/mochitest/test_bug738396.html
new file mode 100644
index 0000000000..ead7f5c1ab
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug738396.html
@@ -0,0 +1,88 @@
+<!doctype html>
+<html>
+<head>
+ <title>Test for Bug 738396</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+</head>
+<body>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED,
+ "Java Test Plug-in");
+
+ SpecialPowers.pushPrefEnv({ "set": [
+ ['plugin.java.mime', 'application/x-java-test']
+ ] }, loadFrame);
+ SimpleTest.waitForExplicitFinish();
+
+ function loadFrame() {
+ var iframe = document.createElement("iframe");
+ iframe.src = "./file_bug738396.html";
+ iframe.addEventListener("load", function() {
+ runTest(iframe.contentDocument);
+ });
+ document.body.appendChild(iframe);
+ }
+
+ function runTest(doc) {
+ // Check that the canonicalized version of the codebase 'good' was passed
+ // to the plugin in all cases
+ var a = doc.createElement('a');
+ a.href = "good";
+ var goodCodebase = a.href;
+ var codebasevis = doc.getElementById("codebasevis")
+ .querySelectorAll("applet, object, embed");
+ for (var elem of codebasevis) {
+ var codebase = null;
+ try {
+ codebase = elem.getJavaCodebase();
+ } catch (e) {}
+ is(codebase, goodCodebase,
+ "Check that the test plugin sees the proper codebase");
+ }
+ // Check that none of the applets in blockedcodebase were allowed to spawn
+ var blockedcodebase = doc.getElementById("blockedcodebase")
+ .querySelectorAll("applet, object, embed");
+ for (var elem of blockedcodebase) {
+ var spawned = false;
+ try {
+ elem.getObjectValue();
+ spawned = true;
+ } catch (e) {}
+ ok(!spawned, "Plugin should not be allowed to spawn");
+ }
+
+ // With no codebase, the codebase should resolve to "."
+ a.href = ".";
+ goodCodebase = a.href;
+ var nocodebase = doc.getElementById("nocodebase")
+ .querySelectorAll("applet, object, embed");
+ for (var elem of nocodebase) {
+ var codebase = null;
+ try {
+ codebase = elem.getJavaCodebase();
+ } catch (e) {}
+ is(codebase, goodCodebase, "Codebase should resolve to '.'");
+ }
+
+ // With empty codebase, the codebase should resolve to "/"
+ a.href = "/";
+ goodCodebase = a.href;
+ var nocodebase = doc.getElementById("emptycodebase")
+ .querySelectorAll("applet, object, embed");
+ for (var elem of nocodebase) {
+ var codebase = null;
+ try {
+ codebase = elem.getJavaCodebase();
+ } catch (e) {}
+ is(codebase, goodCodebase, "Codebase should resolve to '/'");
+ }
+
+ SimpleTest.finish();
+ }
+</script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug751809.html b/dom/plugins/test/mochitest/test_bug751809.html
new file mode 100644
index 0000000000..8f4987a8af
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug751809.html
@@ -0,0 +1,84 @@
+<html>
+<head>
+ <title>Bug 751809</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript;version=1.7">
+ Components.utils.import("resource://gre/modules/Services.jsm");
+ Services.prefs.setBoolPref("plugins.click_to_play", true);
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+ </script>
+</head>
+
+<body onload="go();">
+ <embed id="plugin" type="application/x-test" width="400" height="400" drawmode="solid" color="FF00FFFF"></embed>
+
+ <script type="application/javascript;version=1.7">
+
+ SimpleTest.waitForExplicitFinish();
+
+ const Ci = Components.interfaces;
+ const utils = window.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIDOMWindowUtils);
+
+ function go() {
+ var plugin = document.getElementById('plugin');
+ var objLoadingContent = SpecialPowers.wrap(plugin);
+ ok(!objLoadingContent.activated, "plugin should not be activated");
+
+ SimpleTest.waitForFocus(afterWindowFocus);
+ }
+
+ function afterWindowFocus() {
+ var plugin = document.getElementById('plugin');
+ var objLoadingContent = SpecialPowers.wrap(plugin);
+
+ objLoadingContent.playPlugin();
+ var condition = () => plugin.setColor !== undefined;
+ SimpleTest.waitForCondition(condition, afterPluginActivation,
+ "Waited too long for plugin to activate");
+ }
+
+ function afterPluginActivation() {
+ var plugin = document.getElementById('plugin');
+ var objLoadingContent = SpecialPowers.wrap(plugin);
+ ok(objLoadingContent.activated, "plugin should be activated now");
+
+ // Triggering a paint and waiting for it to be flushed makes sure
+ // that both plugin and platform see the plugin element as visible.
+ // See bug 805330 for details.
+ plugin.setColor("FF000088");
+ waitForAllPaintsFlushed(afterPaintsFlushed);
+ }
+
+ function afterPaintsFlushed() {
+ var plugin = document.getElementById('plugin');
+ try {
+ is(plugin.getMouseUpEventCount(), 0, "Plugin should not have received mouse events yet.");
+ } catch(e) {
+ ok(false, "plugin.getMouseUpEventCount() shouldn't throw");
+ }
+
+ synthesizeMouseAtCenter(plugin, {});
+ var condition = () => plugin.getMouseUpEventCount() > 0;
+ SimpleTest.waitForCondition(condition, afterFirstClick,
+ "Waited too long for plugin to receive the mouse click");
+ }
+
+ function afterFirstClick() {
+ var plugin = document.getElementById('plugin');
+ try {
+ is(plugin.getMouseUpEventCount(), 1, "Plugin should have received 1 mouse up event.");
+ } catch(e) {
+ ok(false, "plugin.getMouseUpEventCount() shouldn't throw");
+ }
+
+ Services.prefs.clearUserPref("plugins.click_to_play");
+ SimpleTest.finish();
+ }
+
+ </script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug771202.html b/dom/plugins/test/mochitest/test_bug771202.html
new file mode 100644
index 0000000000..712c11de7a
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug771202.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=771202
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 771202</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=771202">Mozilla Bug 771202</a>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for recreating spliced plugin prototype chains after tranplant. **/
+SimpleTest.waitForExplicitFinish();
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+function go() {
+ // Set things up.
+ var win = document.getElementById('ifr').contentWindow;
+ var plugin = win.document.getElementById('pluginElement');
+ var testValue = plugin.getObjectValue();
+
+ function checkPlugin() {
+ dump("About to call checkObjectValue\n");
+ ok(plugin.checkObjectValue(testValue), 'Plugin proto should work correctly');
+ }
+ // First, check things before transplanting.
+ checkPlugin();
+
+ // Adopt the plugin and retest.
+ document.body.appendChild(plugin);
+ checkPlugin();
+
+ // All done.
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+
+<iframe id="ifr" onload="go();" src="file_bug771202.html">
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug777098.html b/dom/plugins/test/mochitest/test_bug777098.html
new file mode 100644
index 0000000000..77ba9ed316
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug777098.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=777098
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 777098</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="go();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=777098">Mozilla Bug 777098</a>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for passing dead NPObjects back into plugins. **/
+SimpleTest.waitForExplicitFinish();
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+function go() {
+ var plugin = document.getElementById('plugin');
+
+ // Get wrapped npobject from plugin (plugin.__proto__)
+ var val = plugin.getObjectValue();
+
+ // Force a re-instantiate by re-setting dummy uri,
+ // making val a wrapper for a dead plugin
+ plugin.data = plugin.data;
+
+ // The correct behavior is an exception, if plugin.checkObjectValue succeeds
+ // the plugin wasn't torn down for some reason, and if we crash... that's bad
+ function pluginCheck() {
+ try {
+ plugin.checkObjectValue(val);
+ } catch (e) {
+ return true;
+ }
+ return false;
+ }
+
+ // Spin the event loop so the instantiation can complete
+ window.setTimeout(function () {
+ ok(pluginCheck(), "Shouldn't explode");
+
+ // All done.
+ SimpleTest.finish();
+ }, 0);
+}
+
+</script>
+</pre>
+
+<object data="data:text/plain,a" width=200 height=200 type="application/x-test" id="plugin"></object>
+
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug784131.html b/dom/plugins/test/mochitest/test_bug784131.html
new file mode 100644
index 0000000000..ff2dd19c17
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug784131.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<html>
+<head>
+ <title>Test for Bug 784131</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <base href="chrome://browser/content/">
+</head>
+<body>
+
+<script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+</script>
+
+<embed id="body" type="application/x-test">
+<div>
+ <embed id="nested" type="application/x-test">
+</div>
+
+<script type="application/javascript">
+ function getObjectValue(obj) {
+ try {
+ return obj.getObjectValue();
+ } catch (e) {
+ return null;
+ }
+ }
+ SimpleTest.waitForExplicitFinish();
+
+ var body_embed = document.querySelector("embed#body");
+ var nested_embed = document.querySelector("embed#nested");
+ var nested_parent = nested_embed.parentNode;
+ // Ensure plugins are spawned
+ var body_obj = getObjectValue(body_embed);
+ var nested_obj = getObjectValue(nested_embed);
+ isnot(body_obj, null, "body plugin spawned");
+ isnot(nested_obj, null, "nested plugin spawned");
+ // Display:none the plugin and the nested plugin's parent
+ body_embed.style.display = "none";
+ nested_parent.style.display = "none";
+ body_embed.clientTop;
+ nested_embed.clientTop;
+
+ // Plugins should still be running the same instance
+ ok(body_embed.checkObjectValue(body_obj), "body plugin still running");
+ ok(nested_embed.checkObjectValue(nested_obj), "nested plugin still running");
+ // Spin event loop
+ SimpleTest.executeSoon(function() {
+ // Plugins should be stopped
+ is(getObjectValue(body_embed), null, "body plugin gone");
+ is(getObjectValue(nested_embed), null, "nested plugin gone");
+
+ // Restart plugins...
+ body_embed.style.display = "inherit";
+ nested_parent.style.display = "inherit";
+
+ // Ensure plugins are spawned
+ var body_obj = getObjectValue(body_embed);
+ var nested_obj = getObjectValue(nested_embed);
+ isnot(body_obj, null, "body plugin spawned");
+ isnot(nested_obj, null, "nested plugin spawned");
+
+ // Take away frames again, flush layout, restore frames
+ body_embed.style.display = "none";
+ nested_parent.style.display = "none";
+ body_embed.clientTop;
+ nested_embed.clientTop;
+ body_embed.style.display = "inherit";
+ nested_parent.style.display = "inherit";
+ body_embed.clientTop;
+ nested_embed.clientTop;
+
+ // Spin event loop, ensure plugin remains running
+ SimpleTest.executeSoon(function() {
+ ok(body_embed.checkObjectValue(body_obj), "body plugin still running");
+ ok(nested_embed.checkObjectValue(nested_obj), "nested plugin still running");
+ SimpleTest.finish();
+ });
+ });
+</script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug813906.html b/dom/plugins/test/mochitest/test_bug813906.html
new file mode 100644
index 0000000000..04c34daafe
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug813906.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<html>
+<head>
+ <title>Test for Bug 813906</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <base href="chrome://browser/content/">
+</head>
+<body>
+
+<script type="application/javascript">
+function f() {
+ document.getElementsByTagName("base")[0].href = "http://www.safe.com/";
+}
+</script>
+
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+var frameLoadCount = 0;
+function frameLoaded() {
+ frameLoadCount++;
+ if (frameLoadCount == 1) {
+ document.getElementsByTagName("object")[0].type = "application/x-test";
+ document.getElementsByTagName("use")[0].setAttributeNS("http://www.w3.org/1999/xlink", "href", location.href + "#a");
+ } else if (frameLoadCount == 2) {
+ isnot(SpecialPowers.wrap(window.frame1).location.href.indexOf('chrome://'),
+ 0, 'plugin shouldnt be able to cause navigation to chrome URLs');
+ SimpleTest.finish();
+ }
+}
+</script>
+
+<!-- Note that <svg:use> ends up creating an anonymous subtree, which means that the plugin
+ reflector gets hoisted into the XBL scope, and isn't accessible to content. We pass
+ the 'donttouchelement' parameter to the plugin to prevent it from trying to define the
+ 'pluginFoundElement' property on the plugin reflector, since doing so would throw a
+ security exception. -->
+<svg>
+ <symbol id="a">
+ <foreignObject>
+ <object bugmode="813906" frame="frame1"><param name="donttouchelement"></param></object>
+ </foreignObject>
+ </symbol>
+ <use />
+</svg>
+
+<iframe name="frame1" onload="frameLoaded()"></iframe>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug827160.html b/dom/plugins/test/mochitest/test_bug827160.html
new file mode 100644
index 0000000000..13cedd62af
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug827160.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=827160
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 827160</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/SpecialPowers.js"></script>
+ <script type="application/javascript" src="utils.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=827160">Mozilla Bug 827160</a>
+
+<script type="application/javascript">
+
+// Make sure the test plugin is not click-to-play
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
+
+</script>
+
+<!-- Should load test plugin application/x-test -->
+<object id="shouldLoad" data="data:application/x-test,foo"></object>
+<!-- Should load test plugin application/x-test2, ignoring type="" -->
+<object id="shouldIgnoreType" type="application/x-test" data="data:application/x-test2,foo"></object>
+<!-- Should load nothing, channel type does not match type and typeMustMatch is present -->
+<object id="shouldNotLoad" type="application/x-test" data="data:application/x-test2,foo" typemustmatch></object>
+<!-- Should not load test plugin application/x-test2, no type field is present -->
+<object id="shouldNotLoadMissingType" data="data:application/x-test2,foo" typemustmatch></object>
+<!-- Should load, no data field is present -->
+<object id="shouldLoadMissingData" type="application/x-test" typemustmatch></object>
+<pre id="test">
+
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+window.addEventListener("load", function () {
+ const OBJLC = SpecialPowers.Ci.nsIObjectLoadingContent;
+ is(SpecialPowers.wrap(document.getElementById("shouldLoad")).displayedType, OBJLC.TYPE_PLUGIN, "Testing object load without type, failed expected load");
+ is(SpecialPowers.wrap(document.getElementById("shouldIgnoreType")).displayedType, OBJLC.TYPE_PLUGIN, "Testing object load with type, failed expected load");
+ is(SpecialPowers.wrap(document.getElementById("shouldNotLoad")).displayedType, OBJLC.TYPE_NULL, "Testing object load with typemustmatch, load success even though failure expected");
+ is(SpecialPowers.wrap(document.getElementById("shouldNotLoadMissingType")).displayedType, OBJLC.TYPE_NULL, "Testing object load with typemustmatch and with type, load success even though failure expected");
+ is(SpecialPowers.wrap(document.getElementById("shouldLoadMissingData")).displayedType, OBJLC.TYPE_PLUGIN, "Testing object load with typemustmatch and without data, failed expected load");
+ SimpleTest.finish();
+}, false);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug852315.html b/dom/plugins/test/mochitest/test_bug852315.html
new file mode 100644
index 0000000000..cbbc3d34a8
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug852315.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<html>
+<head>
+ <title>Test for Bug 852315</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <base href="chrome://browser/content/">
+</head>
+<body>
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+// Tests that the document-inactive notification stopping plugins does not
+// fatally re-enter when adding other plugins to the document.
+
+var i = document.createElement("iframe");
+var ob = document.body;
+i.addEventListener("load", function loadfunc() {
+ var d = i.contentWindow.document;
+ var e = i.contentDocument.createElement("embed");
+ var destroyran = false;
+ e.type = "application/x-test";
+ i.contentDocument.body.appendChild(e);
+
+ // On despawn, append an embed tag to document.
+ e.callOnDestroy(function() {
+ var e2 = d.createElement("embed");
+ d.body.appendChild(e2);
+ destroyran = true;
+ });
+
+ // Navigate the frame to cause the document with the plugin to go inactive
+ i.removeEventListener("load", loadfunc);
+ i.src = "about:blank";
+
+ const MAX_ATTEMPTS = 50;
+ var attempts = 0;
+ function checkPluginDestroyRan() {
+ // We may need to retry a few times until the plugin stop event makes
+ // its way through the event queue.
+ if (attempts < MAX_ATTEMPTS && !destroyran) {
+ ++attempts;
+ SimpleTest.executeSoon(checkPluginDestroyRan);
+ } else {
+ info("Number of retry attempts: " + attempts);
+ ok(destroyran, "OnDestroy callback ran and did not crash");
+ SimpleTest.finish();
+ }
+ }
+
+ SimpleTest.executeSoon(checkPluginDestroyRan);
+});
+document.body.appendChild(i);
+
+</script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug854082.html b/dom/plugins/test/mochitest/test_bug854082.html
new file mode 100644
index 0000000000..ff3d1e1e86
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug854082.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+<head>
+ <title>Test for Bug 854082</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <base href="chrome://browser/content/">
+</head>
+<body>
+<script type="application/javascript">
+ // Tests that destroying the plugin's frame inside NPP_New does not cause a
+ // crash
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ // Create an object that will spawn asynchronously
+ var o = document.createElement("object");
+ o.type = "application/x-test";
+
+ // The test plugin sets pluginFoundElement on its element inside NPP_New,
+ // abuse this to run arbitrary script.
+ var setterCalled;
+ o.__defineSetter__("pluginFoundElement", function() {
+ o.style.display = "none";
+ // Access clientTop to force layout flush
+ o.clientTop;
+ ok(true, "Setter called and did not crash");
+ SimpleTest.finish();
+ setterCalled = true;
+ });
+ document.body.appendChild(o);
+</script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug863792.html b/dom/plugins/test/mochitest/test_bug863792.html
new file mode 100644
index 0000000000..ccd0fc83c9
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug863792.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+<head>
+ <title>Test for Bug 863792</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+</head>
+<body>
+<script type="application/javascript">
+
+// A plugin that removes itself from the document and inactivates said document
+// inside NPP_New. We should not leak the instance. See also test_bug854082
+
+SimpleTest.waitForExplicitFinish();
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+var i = document.createElement("iframe");
+i.src = "file_bug863792.html";
+i.width = 500;
+i.height = 500;
+document.body.appendChild(i);
+
+i.addEventListener("load", function() {
+ SpecialPowers.forceGC();
+ SpecialPowers.forceCC();
+ SimpleTest.executeSoon(function() {
+ SpecialPowers.forceGC();
+ SpecialPowers.forceCC();
+ SimpleTest.executeSoon(function() {
+ ok(true, "Didn't crash");
+ SimpleTest.finish();
+ });
+ });
+}, false);
+</script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug967694.html b/dom/plugins/test/mochitest/test_bug967694.html
new file mode 100644
index 0000000000..8a7602134d
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug967694.html
@@ -0,0 +1,78 @@
+<!doctype html>
+<html>
+<head>
+ <title>Test for Bug 967694</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+</head>
+<body>
+<script type="application/javascript">
+
+// Touching a plugin from chrome scope should not spawn it, bug should
+// synchronously spawn it from content scope
+
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+var plugin;
+var spPlugin;
+
+function recreatePlugin() {
+ if (plugin) {
+ document.body.removeChild(plugin);
+ }
+ plugin = document.createElement("embed");
+ plugin.type = "application/x-test";
+
+ document.body.appendChild(plugin);
+ // Plugin should now be queued for async spawning.
+ spPlugin = SpecialPowers.wrap(plugin);
+
+ is(spPlugin.displayedType, spPlugin.TYPE_PLUGIN, "Should be configured as plugin");
+ ok(!spPlugin.hasRunningPlugin, "Should not be spawned yet");
+}
+
+recreatePlugin();
+
+// Try various JS operations with chrome context
+var thrown = false;
+// Get and set non-existent Property
+var hi = spPlugin._testShouldntExist;
+spPlugin._testShouldntExist = 5;
+// Call some test-plugin function
+try {
+ var val = spPlugin.getObjectValue();
+} catch (e) {
+ thrown = true;
+}
+
+ok(thrown, "Function call should have thrown");
+ok(!spPlugin.hasRunningPlugin, "Plugin should not have spawned");
+
+// Try property access from content
+var hi = plugin._testShouldntExistContent;
+ok(spPlugin.hasRunningPlugin, "Should've caused plugin to spawn");
+
+// Property set
+recreatePlugin();
+plugin._testShouldntExistContent = 5;
+ok(spPlugin.hasRunningPlugin, "Should've caused plugin to spawn");
+
+// Call test plugin function. Should succeed.
+recreatePlugin();
+thrown = false;
+try {
+ var value = plugin.getObjectValue();
+} catch (e) {
+ thrown = true;
+}
+
+ok(!thrown, "Call should have succeeded");
+ok(spPlugin.hasRunningPlugin, "Call should have synchronously spawned plugin");
+ok(plugin.checkObjectValue(value), "Plugin should recognize self");
+
+</script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug985859.html b/dom/plugins/test/mochitest/test_bug985859.html
new file mode 100644
index 0000000000..1a4329ab4a
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug985859.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+<head>
+ <title>Test for Bug 985859</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <base href="chrome://browser/content/">
+</head>
+<body>
+<script type="application/javascript">
+
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+var testType = navigator.mimeTypes["application/x-test"];
+var testTypeCap = navigator.mimeTypes["Application/x-Test"];
+var testTypeCap2 = navigator.mimeTypes["APPLICATION/X-TEST"];
+
+ok(testType, "Test plugin should be found");
+is(testType, testTypeCap, "navigator.mimeTypes should be case insensitive");
+is(testType, testTypeCap2, "navigator.mimeTypes should be case insensitive");
+
+</script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_bug986930.html b/dom/plugins/test/mochitest/test_bug986930.html
new file mode 100644
index 0000000000..f86539e58f
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug986930.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8"/>
+ <title>Test for Bug 986930</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+</head>
+<body>
+ <script class="testbody" type="application/javascript">
+ var testPlugin = getTestPlugin("Test Plug-in");
+
+ var mimeDescriptions = testPlugin.getMimeDescriptions({});
+
+ is(mimeDescriptions[0], "Test \u2122 mimetype",
+ "Plugin should handle non-ascii mime description");
+ </script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_busy_hang.xul b/dom/plugins/test/mochitest/test_busy_hang.xul
new file mode 100644
index 0000000000..824dcbb4da
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_busy_hang.xul
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Basic Plugin Tests"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <title>Plugin Busy Hang Test</title>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hang_test.js" />
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
+ <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+ </body>
+ <script class="testbody" type="application/javascript">
+ <![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ // Default plugin hang timeout is too high for mochitests
+ var prefs = Cc["@mozilla.org/preferences-service;1"]
+ .getService(Ci.nsIPrefBranch);
+ var timeoutPref = "dom.ipc.plugins.timeoutSecs";
+ prefs.setIntPref(timeoutPref, 5);
+
+ var os = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+ os.addObserver(testObserver, "plugin-crashed", true);
+
+ testObserver.idleHang = false;
+ document.addEventListener("PluginCrashed", onPluginCrashed, false);
+
+ var pluginElement = document.getElementById("plugin1");
+ try {
+ pluginElement.hang(true);
+ } catch (e) {
+ }
+}
+]]>
+ </script>
+</window>
+
diff --git a/dom/plugins/test/mochitest/test_clear_site_data.html b/dom/plugins/test/mochitest/test_clear_site_data.html
new file mode 100644
index 0000000000..88ba937d5b
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_clear_site_data.html
@@ -0,0 +1,242 @@
+<html>
+<head>
+ <title>NPAPI ClearSiteData/GetSitesWithData Functionality</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+</head>
+<body>
+ <script type="application/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+
+ <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ const pluginHostIface = Components.interfaces.nsIPluginHost;
+ var pluginHost = Components.classes["@mozilla.org/plugin/host;1"].
+ getService(pluginHostIface);
+ const FLAG_CLEAR_ALL = pluginHostIface.FLAG_CLEAR_ALL;
+ const FLAG_CLEAR_CACHE = pluginHostIface.FLAG_CLEAR_CACHE;
+
+ var p = document.getElementById("plugin1");
+
+ // Since we're running with chrome permissions, accessing the plugin wont
+ // synchronously spawn it -- wait for the async spawning to finish.
+ SimpleTest.executeSoon(function() {
+ // Make sure clearing by timerange is supported.
+ p.setSitesWithDataCapabilities(true);
+ ok(PluginUtils.withTestPlugin(runTest), "Test plugin found");
+ });
+
+ function stored(needles) {
+ var something = pluginHost.siteHasData(this.pluginTag, null);
+ if (!needles)
+ return something;
+
+ if (!something)
+ return false;
+
+ for (var i = 0; i < needles.length; ++i) {
+ if (!pluginHost.siteHasData(this.pluginTag, needles[i]))
+ return false;
+ }
+ return true;
+ }
+
+ function checkThrows(fn, result) {
+ try {
+ fn();
+ throw new Error("bad exception");
+ } catch (e) {
+ is(e.result, result, "Correct exception thrown");
+ }
+ }
+
+ function runTest(pluginTag) {
+ this.pluginTag = pluginTag;
+ p.setSitesWithData(
+ "foo.com:0:5," +
+ "foo.com:0:7," +
+ "bar.com:0:10," +
+ "baz.com:0:10," +
+ "foo.com:1:7," +
+ "qux.com:1:5," +
+ "quz.com:1:8"
+ );
+ ok(stored(["foo.com","bar.com","baz.com","qux.com","quz.com"]),
+ "Data stored for sites");
+
+ // Clear nothing.
+ pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 4, {callback: function() { test1(); }});
+ }
+ function test1() {
+ ok(stored(["foo.com","bar.com","baz.com","qux.com","quz.com"]),
+ "Data stored for sites");
+
+ pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 4, {callback: function() { test2(); }});
+ }
+ function test2() {
+ ok(stored(["foo.com","bar.com","baz.com","qux.com","quz.com"]),
+ "Data stored for sites");
+
+ // Clear cache data 5 seconds or older.
+ pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 5, {callback: function() { test3(); }});
+ }
+ function test3() {
+ ok(stored(["foo.com","bar.com","baz.com","quz.com"]),
+ "Data stored for sites");
+ ok(!stored(["qux.com"]), "Data cleared for qux.com");
+ // Clear cache data for foo.com, but leave non-cache data.
+ pluginHost.clearSiteData(pluginTag, "foo.com", FLAG_CLEAR_CACHE, 20, {callback: function() { test4(); }});
+ }
+ function test4() {
+ ok(stored(["foo.com","bar.com","baz.com","quz.com"]),
+ "Data stored for sites");
+
+ // Clear all data 7 seconds or older.
+ pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 7, {callback: function() { test5(); }});
+ }
+ function test5() {
+ ok(stored(["bar.com","baz.com","quz.com"]), "Data stored for sites");
+ ok(!stored(["foo.com"]), "Data cleared for foo.com");
+ ok(!stored(["qux.com"]), "Data cleared for qux.com");
+
+ // Clear all cache data.
+ pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 20, {callback: function() { test6(); }});
+ }
+ function test6() {
+ ok(stored(["bar.com","baz.com"]), "Data stored for sites");
+ ok(!stored(["quz.com"]), "Data cleared for quz.com");
+
+ // Clear all data for bar.com.
+ pluginHost.clearSiteData(pluginTag, "bar.com", FLAG_CLEAR_ALL, 20, {callback: function(rv) { test7(rv); }});
+ }
+ function test7(rv) {
+ ok(stored(["baz.com"]), "Data stored for baz.com");
+ ok(!stored(["bar.com"]), "Data cleared for bar.com");
+
+ // Disable clearing by age.
+ p.setSitesWithDataCapabilities(false);
+
+ pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 20, {callback: function(rv) {
+ is(rv, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
+ test8(rv);
+ }});
+ }
+ function test8(rv) {
+ pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 20, {callback: function(rv) {
+ is(rv, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
+ test9(rv);
+ }});
+ }
+ function test9(rv) {
+ pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_ALL, 20, {callback: function(rv) {
+ is(rv, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
+ test10(rv);
+ }});
+ }
+ function test10(rv) {
+ pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_CACHE, 20, {callback: function(rv) {
+ is(rv, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
+ test11();
+ }});
+ }
+ function test11() {
+ // Clear cache for baz.com and globally for all ages.
+ pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_CACHE, -1, {callback: function(rv) { test12()}});
+ }
+ function test12() {
+ pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, -1, {callback: function(rv) { test13()}});
+ }
+ function test13() {
+ // Check that all of the above were no-ops.
+ ok(stored(["baz.com"]), "Data stored for baz.com");
+
+ // Clear everything for baz.com.
+ pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test14()}});
+ }
+ function test14() {
+ ok(!stored(["baz.com"]), "Data cleared for baz.com");
+ ok(!stored(null), "All data cleared");
+
+ // Set data to test subdomains, IP literals, and 'localhost'-like hosts.
+ p.setSitesWithData(
+ "foo.com:0:0," +
+ "bar.foo.com:0:0," +
+ "baz.foo.com:0:0," +
+ "bar.com:0:0," +
+ "[192.168.1.1]:0:0," +
+ "localhost:0:0"
+ );
+ ok(stored(["foo.com","nonexistent.foo.com","bar.com","192.168.1.1","localhost"]),
+ "Data stored for sites");
+
+ // Clear data for "foo.com" and its subdomains.
+ pluginHost.clearSiteData(pluginTag, "foo.com", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test15()}});
+ }
+ function test15() {
+ ok(stored(["bar.com","192.168.1.1","localhost"]), "Data stored for sites");
+ ok(!stored(["foo.com"]), "Data cleared for foo.com");
+ ok(!stored(["bar.foo.com"]), "Data cleared for subdomains of foo.com");
+
+ // Clear data for "bar.com" using a subdomain.
+ pluginHost.clearSiteData(pluginTag, "foo.bar.com", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test16()}});
+ }
+ function test16() {
+ ok(!stored(["bar.com"]), "Data cleared for bar.com");
+
+ // Clear data for "192.168.1.1".
+ pluginHost.clearSiteData(pluginTag, "192.168.1.1", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test17()}});
+ }
+ function test17() {
+ ok(!stored(["192.168.1.1"]), "Data cleared for 192.168.1.1");
+
+ // Clear data for "localhost".
+ pluginHost.clearSiteData(pluginTag, "localhost", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test18()}});
+ }
+ function test18() {
+ ok(!stored(null), "All data cleared");
+
+ // Set data to test international domains.
+ p.setSitesWithData(
+ "b\u00FCcher.es:0:0," +
+ "b\u00FCcher.uk:0:0," +
+ "xn--bcher-kva.NZ:0:0"
+ );
+ // Check that both the ACE and UTF-8 representations register.
+ ok(stored(["b\u00FCcher.es","xn--bcher-kva.es","b\u00FCcher.uk","xn--bcher-kva.uk"]),
+ "Data stored for sites");
+
+ // Clear data for the UTF-8 version.
+ pluginHost.clearSiteData(pluginTag, "b\u00FCcher.es", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test19()}});
+ }
+ function test19() {
+ ok(!stored(["b\u00FCcher.es"]), "Data cleared for UTF-8 representation");
+ ok(!stored(["xn--bcher-kva.es"]), "Data cleared for ACE representation");
+
+ // Clear data for the ACE version.
+ pluginHost.clearSiteData(pluginTag, "xn--bcher-kva.uk", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test20()}});
+ }
+ function test20() {
+ ok(!stored(["b\u00FCcher.uk"]), "Data cleared for UTF-8 representation");
+ ok(!stored(["xn--bcher-kva.uk"]), "Data cleared for ACE representation");
+
+ // The NPAPI spec requires that the plugin report sites in normalized
+ // UTF-8. We do happen to normalize the result anyway, so while that's not
+ // strictly required, we test it here.
+ ok(stored(["b\u00FCcher.nz","xn--bcher-kva.nz"]),
+ "Data stored for sites");
+ pluginHost.clearSiteData(pluginTag, "b\u00FCcher.nz", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test21()}});
+ }
+ function test21() {
+ ok(!stored(["b\u00FCcher.nz"]), "Data cleared for UTF-8 representation");
+ ok(!stored(["xn--bcher-kva.nz"]), "Data cleared for ACE representation");
+ ok(!stored(null), "All data cleared");
+ SimpleTest.finish();
+ }
+ </script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_cocoa_focus.html b/dom/plugins/test/mochitest/test_cocoa_focus.html
new file mode 100644
index 0000000000..17043ebae3
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_cocoa_focus.html
@@ -0,0 +1,28 @@
+<html>
+<head>
+ <title>NPCocoaEventFocusChanged Tests</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var gOtherWindow;
+
+ function runTests() {
+ // We have to have two top-level windows in play in order to run these tests.
+ gOtherWindow = window.open("cocoa_focus.html", "", "width=250,height=250");
+ }
+
+ function testsFinished() {
+ // Tests have finished running, close the new window and end tests.
+ gOtherWindow.close();
+ SimpleTest.finish();
+ }
+ </script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_cocoa_window_focus.html b/dom/plugins/test/mochitest/test_cocoa_window_focus.html
new file mode 100644
index 0000000000..3458c24eb9
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_cocoa_window_focus.html
@@ -0,0 +1,28 @@
+<html>
+<head>
+ <title>NPCocoaEventWindowFocusChanged Tests</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var gOtherWindow;
+
+ function runTests() {
+ // We have to have two top-level windows in play in order to run these tests.
+ gOtherWindow = window.open("cocoa_window_focus.html", "", "width=200,height=200");
+ }
+
+ function testsFinished() {
+ // Tests have finished running, close the new window and end tests.
+ gOtherWindow.close();
+ SimpleTest.finish();
+ }
+ </script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_convertpoint.xul b/dom/plugins/test/mochitest/test_convertpoint.xul
new file mode 100644
index 0000000000..5db01a253e
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_convertpoint.xul
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Basic Plugin Tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+<body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
+<embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+</body>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ var pluginElement = document.getElementById("plugin1");
+ // Poll to see if the plugin is in the right place yet.
+ // Check if x-coordinate 0 in plugin space is 0 in window space. If it is,
+ // the plugin hasn't been placed yet.
+ if (pluginElement.convertPointX(1, 0, 0, 2) == 0) {
+ setTimeout(runTests, 0);
+ return;
+ }
+
+ var domWindowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindowUtils);
+
+ var pluginRect = pluginElement.getBoundingClientRect();
+ var pluginX = pluginRect.left + (window.mozInnerScreenX - window.screenX);
+ var pluginY = pluginRect.top + (window.mozInnerScreenY - window.screenY);
+
+ var windowX = window.screenX;
+ var windowY = window.screenY;
+ var windowHeight = window.outerHeight;
+
+ var screenHeight = window.screen.height;
+
+ // arbitrary coordinates of test point in plugin top-left origin terms
+ var xOffset = 5;
+ var yOffset = 5;
+
+ var NPCoordinateSpacePluginX = 0 + xOffset;
+ var NPCoordinateSpacePluginY = 0 + yOffset;
+
+ var NPCoordinateSpaceWindowX = pluginX + xOffset;
+ var NPCoordinateSpaceWindowY = (windowHeight - pluginY) - yOffset;
+
+ var NPCoordinateSpaceFlippedWindowX = pluginX + xOffset;
+ var NPCoordinateSpaceFlippedWindowY = pluginY + yOffset;
+
+ var NPCoordinateSpaceScreenX = windowX + pluginX + xOffset;
+ var NPCoordinateSpaceScreenY = ((screenHeight - windowY) - pluginY) - yOffset;
+
+ var NPCoordinateSpaceFlippedScreenX = windowX + pluginX + xOffset;
+ var NPCoordinateSpaceFlippedScreenY = windowY + pluginY + yOffset;
+
+ // these are in coordinate space enumeration order
+ var xValues = new Array(NPCoordinateSpacePluginX, NPCoordinateSpaceWindowX, NPCoordinateSpaceFlippedWindowX, NPCoordinateSpaceScreenX, NPCoordinateSpaceFlippedScreenX);
+ var yValues = new Array(NPCoordinateSpacePluginY, NPCoordinateSpaceWindowY, NPCoordinateSpaceFlippedWindowY, NPCoordinateSpaceScreenY, NPCoordinateSpaceFlippedScreenY);
+
+ var i;
+ for (i = 0; i < 5; i = i + 1) {
+ var sourceCoordSpaceValue = i + 1;
+ var j;
+ for (j = 0; j < 5; j = j + 1) {
+ var destCoordSpaceValue = j + 1;
+ xResult = pluginElement.convertPointX(sourceCoordSpaceValue, xValues[i], yValues[i], destCoordSpaceValue);
+ yResult = pluginElement.convertPointY(sourceCoordSpaceValue, xValues[i], yValues[i], destCoordSpaceValue);
+ is(xResult, xValues[j], "convertPointX: space " + sourceCoordSpaceValue + " to space " + destCoordSpaceValue + " mismatch");
+ is(yResult, yValues[j], "convertPointY: space " + sourceCoordSpaceValue + " to space " + destCoordSpaceValue + " mismatch");
+ }
+ }
+
+ SimpleTest.finish();
+}
+]]>
+</script>
+</window>
diff --git a/dom/plugins/test/mochitest/test_cookies.html b/dom/plugins/test/mochitest/test_cookies.html
new file mode 100644
index 0000000000..f527967706
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_cookies.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+ <title>NPAPI Cookie Tests</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+</head>
+
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function runTests() {
+ var pluginElement = document.getElementById("plugin1");
+ pluginElement.setCookie("foo");
+ is(pluginElement.getCookie(), "foo", "Cookie was set and retrieved correctly via NPAPI.");
+ SimpleTest.finish();
+ }
+ </script>
+
+ <embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_copyText.html b/dom/plugins/test/mochitest/test_copyText.html
new file mode 100644
index 0000000000..553207e25b
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_copyText.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test copying text from browser to plugin</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+
+ <script class="testbody" type="text/javascript">
+function runTests() {
+ var text = " some text \n to copy 'n paste "
+ var textElt = document.getElementById("input");
+ var plugin = document.getElementById("plugin1");
+
+ // Make sure we wait for the clipboard
+ SimpleTest.waitForClipboard(text, () => {
+ textElt.focus();
+ textElt.value = text;
+ textElt.select();
+ SpecialPowers.wrap(textElt).editor.copy();
+ }, () => {
+ is(plugin.getClipboardText(), text);
+ SimpleTest.finish();
+ }, () => {
+ ok(false, "Failed to set the clipboard text!");
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ </script>
+</head>
+
+<body onload="runTests()">
+ <embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
+ <textarea id="input"></textarea>
+
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_crash_nested_loop.html b/dom/plugins/test/mochitest/test_crash_nested_loop.html
new file mode 100644
index 0000000000..379b693e78
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_crash_nested_loop.html
@@ -0,0 +1,46 @@
+<head>
+ <title>Plugin crashing in nested loop</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+<body>
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ window.frameLoaded = function frameLoaded_toCrash() {
+ var iframe = document.getElementById('iframe1');
+ var p = iframe.contentDocument.getElementById('plugin1');
+
+ // This test is for bug 550026, which is inherently nondeterministic.
+ // If we hit that bug, the browser process would crash when the plugin
+ // crashes in crashInNestedLoop. If not, we'll pass "spuriously".
+ try {
+ p.crashInNestedLoop();
+ // The plugin didn't crash when expected. This happens sometimes. Give
+ // it longer to crash. If it crashes (but not at the apropriate time),
+ // soft fail with a todo; if it doesn't crash then something went wrong,
+ // so fail.
+ SimpleTest.requestFlakyTimeout("sometimes the plugin takes a little longer to crash");
+ setTimeout(
+ function() {
+ try {
+ p.getPaintCount();
+ ok(false, "plugin should crash");
+ } catch (e) {
+ todo(false, "p.crashInNestedLoop() should throw an exception");
+ }
+
+ SimpleTest.finish();
+ },
+ 1000);
+ }
+ catch (e) {
+ ok(true, "p.crashInNestedLoop() should throw an exception");
+ SimpleTest.finish();
+ }
+
+ }
+
+ </script>
+ <iframe id="iframe1" src="crashing_subpage.html" width="600" height="600"></iframe>
diff --git a/dom/plugins/test/mochitest/test_crash_notify.xul b/dom/plugins/test/mochitest/test_crash_notify.xul
new file mode 100644
index 0000000000..fac95b07d9
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_crash_notify.xul
@@ -0,0 +1,106 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Basic Plugin Tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+<body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
+<embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+</body>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+var success = false;
+
+var observerFired = false;
+
+var testObserver = {
+ observe: function(subject, topic, data) {
+ observerFired = true;
+ ok(true, "Observer fired");
+ is(topic, "plugin-crashed", "Checking correct topic");
+ is(data, null, "Checking null data");
+ ok((subject instanceof Components.interfaces.nsIPropertyBag2), "got Propbag");
+ ok((subject instanceof Components.interfaces.nsIWritablePropertyBag2), "got writable Propbag");
+
+ var id = subject.getPropertyAsAString("pluginDumpID");
+ isnot(id, "", "got a non-empty crash id");
+ let directoryService =
+ Components.classes["@mozilla.org/file/directory_service;1"].
+ getService(Components.interfaces.nsIProperties);
+ let profD = directoryService.get("ProfD", Components.interfaces.nsIFile);
+ profD.append("minidumps");
+ let dumpFile = profD.clone();
+ dumpFile.append(id + ".dmp");
+ ok(dumpFile.exists(), "minidump exists");
+ let extraFile = profD.clone();
+ extraFile.append(id + ".extra");
+ ok(extraFile.exists(), "extra file exists");
+ // cleanup, to be nice
+ dumpFile.remove(false);
+ extraFile.remove(false);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsIObserver) ||
+ iid.equals(Components.interfaces.nsISupportsWeakReference) ||
+ iid.equals(Components.interfaces.nsISupports))
+ return this;
+ throw Components.results.NS_NOINTERFACE;
+ }
+};
+
+
+function onPluginCrashed(aEvent) {
+ ok(true, "Plugin crashed notification received");
+ ok(observerFired, "Observer should have fired first");
+ is(aEvent.type, "PluginCrashed", "event is correct type");
+
+ var pluginElement = document.getElementById("plugin1");
+ is (pluginElement, aEvent.target, "Plugin crashed event target is plugin element");
+
+ ok(aEvent instanceof PluginCrashedEvent,
+ "plugin crashed event has the right interface");
+
+ is(typeof aEvent.pluginDumpID, "string", "pluginDumpID is correct type");
+ isnot(aEvent.pluginDumpID, "", "got a non-empty dump ID");
+ is(typeof aEvent.pluginName, "string", "pluginName is correct type");
+ is(aEvent.pluginName, "Test Plug-in", "got correct plugin name");
+ is(typeof aEvent.pluginFilename, "string", "pluginFilename is correct type");
+ isnot(aEvent.pluginFilename, "", "got a non-empty filename");
+ // The app itself may or may not have decided to submit the report, so
+ // allow either true or false here.
+ ok("submittedCrashReport" in aEvent, "submittedCrashReport is a property of event");
+ is(typeof aEvent.submittedCrashReport, "boolean", "submittedCrashReport is correct type");
+
+ var os = Components.classes["@mozilla.org/observer-service;1"].
+ getService(Components.interfaces.nsIObserverService);
+ os.removeObserver(testObserver, "plugin-crashed");
+
+ SimpleTest.finish();
+}
+
+function runTests() {
+ var os = Components.classes["@mozilla.org/observer-service;1"].
+ getService(Components.interfaces.nsIObserverService);
+ os.addObserver(testObserver, "plugin-crashed", true);
+
+ document.addEventListener("PluginCrashed", onPluginCrashed, false);
+
+ var pluginElement = document.getElementById("plugin1");
+ try {
+ pluginElement.crash();
+ } catch (e) {
+ }
+}
+]]>
+</script>
+</window>
+
diff --git a/dom/plugins/test/mochitest/test_crash_notify_no_report.xul b/dom/plugins/test/mochitest/test_crash_notify_no_report.xul
new file mode 100644
index 0000000000..a1344bf0f4
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_crash_notify_no_report.xul
@@ -0,0 +1,116 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Basic Plugin Tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+<body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
+<embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+</body>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+var success = false;
+
+var observerFired = false;
+
+var testObserver = {
+ observe: function(subject, topic, data) {
+ observerFired = true;
+ ok(true, "Observer fired");
+ is(topic, "plugin-crashed", "Checking correct topic");
+ is(data, null, "Checking null data");
+ ok((subject instanceof Components.interfaces.nsIPropertyBag2), "got Propbag");
+ ok((subject instanceof Components.interfaces.nsIWritablePropertyBag2),
+"got writable Propbag");
+
+ var id = subject.getPropertyAsAString("pluginDumpID");
+ isnot(id, "", "got a non-empty crash id");
+ let directoryService =
+ Components.classes["@mozilla.org/file/directory_service;1"].
+ getService(Components.interfaces.nsIProperties);
+ let pendingD = directoryService.get("UAppData",
+ Components.interfaces.nsIFile);
+ pendingD.append("Crash Reports");
+ pendingD.append("pending");
+ let dumpFile = pendingD.clone();
+ dumpFile.append(id + ".dmp");
+ ok(dumpFile.exists(), "minidump exists");
+ let extraFile = pendingD.clone();
+ extraFile.append(id + ".extra");
+ ok(extraFile.exists(), "extra file exists");
+ // cleanup, to be nice
+ dumpFile.remove(false);
+ extraFile.remove(false);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsIObserver) ||
+ iid.equals(Components.interfaces.nsISupportsWeakReference) ||
+ iid.equals(Components.interfaces.nsISupports))
+ return this;
+ throw Components.results.NS_NOINTERFACE;
+ }
+};
+
+
+function onPluginCrashed(aEvent) {
+ ok(true, "Plugin crashed notification received");
+ ok(observerFired, "Observer should have fired first");
+ is(aEvent.type, "PluginCrashed", "event is correct type");
+
+ var pluginElement = document.getElementById("plugin1");
+ is (pluginElement, aEvent.target, "Plugin crashed event target is plugin element");
+
+ ok(aEvent instanceof PluginCrashedEvent,
+ "plugin crashed event has the right interface");
+
+ is(typeof aEvent.pluginName, "string", "pluginName is correct type");
+ is(aEvent.pluginName, "Test Plug-in");
+ // The app itself may or may not have decided to submit the report, so
+ // allow either true or false here.
+ ok("submittedCrashReport" in aEvent, "submittedCrashReport is a property of event");
+ is(typeof aEvent.submittedCrashReport, "boolean", "submittedCrashReport is correct type");
+
+ var os = Components.classes["@mozilla.org/observer-service;1"].
+ getService(Components.interfaces.nsIObserverService);
+ os.removeObserver(testObserver, "plugin-crashed");
+
+ // re-set MOZ_CRASHREPORTER_NO_REPORT
+ let env = Components.classes["@mozilla.org/process/environment;1"]
+ .getService(Components.interfaces.nsIEnvironment);
+ env.set("MOZ_CRASHREPORTER_NO_REPORT", "1");
+ SimpleTest.finish();
+}
+
+function runTests() {
+ // the test harness will have set MOZ_CRASHREPORTER_NO_REPORT,
+ // ensure that we can change the setting and have our minidumps
+ // wind up in Crash Reports/pending
+ let env = Components.classes["@mozilla.org/process/environment;1"]
+ .getService(Components.interfaces.nsIEnvironment);
+ env.set("MOZ_CRASHREPORTER_NO_REPORT", "");
+
+ var os = Components.classes["@mozilla.org/observer-service;1"].
+ getService(Components.interfaces.nsIObserverService);
+ os.addObserver(testObserver, "plugin-crashed", true);
+
+ document.addEventListener("PluginCrashed", onPluginCrashed, false);
+
+ var pluginElement = document.getElementById("plugin1");
+ try {
+ pluginElement.crash();
+ } catch (e) {
+ }
+}
+]]>
+</script>
+</window>
+
diff --git a/dom/plugins/test/mochitest/test_crash_submit.xul b/dom/plugins/test/mochitest/test_crash_submit.xul
new file mode 100644
index 0000000000..22f39384b5
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_crash_submit.xul
@@ -0,0 +1,157 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Basic Plugin Tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+<body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
+<embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+</body>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+SimpleTest.ignoreAllUncaughtExceptions();
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/Task.jsm");
+
+var crashReporter =
+ Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
+ .getService(Components.interfaces.nsICrashReporter);
+var oldServerURL = crashReporter.serverURL;
+
+const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
+
+var testObserver = {
+ observe: function(subject, topic, data) {
+ if (data == "submitting") // not done yet
+ return;
+ is(data, "success", "report should have been submitted successfully");
+ is(topic, "crash-report-status", "Checking correct topic");
+ ok(subject instanceof Components.interfaces.nsIPropertyBag2,
+ "Subject should be a property bag");
+
+ ok(subject.hasKey("minidumpID"), "Should have a local crash ID");
+ let crashID = subject.getPropertyAsAString("minidumpID");
+ isnot(crashID, "", "Local crash ID should not be an empty string");
+
+ ok(subject.hasKey("serverCrashID"), "Should have a server crash ID");
+ let remoteID = subject.getPropertyAsAString("serverCrashID");
+ isnot(remoteID, "", "Server crash ID should not be an empty string");
+
+ // Verify the data. The SJS script will return the data that was POSTed
+ let req = new XMLHttpRequest();
+ req.open("GET", SERVER_URL + "?id=" + remoteID, false);
+ req.send(null);
+ is(req.status, 200, "Server response should be 200 OK");
+ let submitted = JSON.parse(req.responseText);
+ ok(!("Throttleable" in submitted), "Submit request should not be Throttleable");
+ is(submitted.ProcessType, "plugin", "Should specify ProcessType=plugin");
+
+ // Cleanup
+ // First remove our fake submitted report
+ let file = Services.dirsvc.get("UAppData", Components.interfaces.nsILocalFile);
+ file.append("Crash Reports");
+ file.append("submitted");
+ file.append(remoteID + ".txt");
+ file.remove(false);
+
+ // Next unregister our observer
+ var os = Components.classes["@mozilla.org/observer-service;1"].
+ getService(Components.interfaces.nsIObserverService);
+ os.removeObserver(testObserver, "crash-report-status");
+
+ // Then re-set MOZ_CRASHREPORTER_NO_REPORT
+ let env = Components.classes["@mozilla.org/process/environment;1"]
+ .getService(Components.interfaces.nsIEnvironment);
+ env.set("MOZ_CRASHREPORTER_NO_REPORT", "1");
+
+ // Finally re-set crashreporter URL
+ crashReporter.serverURL = oldServerURL;
+
+ // Check and cleanup CrashManager.
+ Task.spawn(function* () {
+ let cm = Services.crashmanager;
+ let store = yield cm._getStore();
+ is(store.crashesCount, 1, "Store should have only 1 item");
+
+ let crash = store.getCrash(crashID);
+ ok(!!crash, "Store should have the crash record");
+ ok(crash.isOfType(cm.PROCESS_TYPE_PLUGIN, cm.CRASH_TYPE_CRASH),
+ "Crash type should be plugin-crash");
+ is(crash.remoteID, remoteID, "Crash remoteID should match");
+
+ is(crash.submissions.size, 1, "Crash should have a submission");
+ let submission = crash.submissions.values().next().value;
+ is(submission.result, cm.SUBMISSION_RESULT_OK,
+ "Submission should be successful");
+
+ store.reset();
+
+ SimpleTest.finish();
+ });
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsIObserver) ||
+ iid.equals(Components.interfaces.nsISupportsWeakReference) ||
+ iid.equals(Components.interfaces.nsISupports))
+ return this;
+ throw Components.results.NS_NOINTERFACE;
+ }
+};
+
+
+function onPluginCrashed(aEvent) {
+ ok(true, "Plugin crashed notification received");
+ is(aEvent.type, "PluginCrashed", "event is correct type");
+
+ let submitButton = document.getAnonymousElementByAttribute(aEvent.target,
+ "class",
+ "submitButton");
+ // try to submit this report
+ sendMouseEvent({type:'click'}, submitButton, window);
+}
+
+function runTests() {
+ // the test harness will have set MOZ_CRASHREPORTER_NO_REPORT,
+ // ensure that we can change the setting and have our minidumps
+ // wind up in Crash Reports/pending
+ let env = Components.classes["@mozilla.org/process/environment;1"]
+ .getService(Components.interfaces.nsIEnvironment);
+ env.set("MOZ_CRASHREPORTER_NO_REPORT", "");
+
+ // Override the crash reporter URL to send to our fake server
+ crashReporter.serverURL = NetUtil.newURI(SERVER_URL);
+
+ var os = Components.classes["@mozilla.org/observer-service;1"].
+ getService(Components.interfaces.nsIObserverService);
+ os.addObserver(testObserver, "crash-report-status", true);
+
+ document.addEventListener("PluginCrashed", onPluginCrashed, false);
+
+ var pluginElement = document.getElementById("plugin1");
+ try {
+ Task.spawn(function* () {
+ // Clear data in CrashManager in case previous tests caused something
+ // to be added.
+ let store = yield Services.crashmanager._getStore();
+ store.reset();
+
+ pluginElement.crash();
+ });
+ } catch (e) {
+ }
+}
+]]>
+</script>
+</window>
diff --git a/dom/plugins/test/mochitest/test_crashing.html b/dom/plugins/test/mochitest/test_crashing.html
new file mode 100644
index 0000000000..eac6e42bef
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_crashing.html
@@ -0,0 +1,62 @@
+<head>
+ <title>Plugin crashing</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+<body>
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ window.frameLoaded = function frameLoaded_toCrash() {
+ SimpleTest.expectChildProcessCrash();
+
+ var iframe = document.getElementById('iframe1');
+ var p = iframe.contentDocument.getElementById('plugin1');
+
+ p.setColor("FFFF00FF");
+
+ try {
+ p.crash();
+ ok(false, "p.crash() should throw an exception");
+ }
+ catch (e) {
+ ok(true, "p.crash() should throw an exception");
+ }
+
+ // Create random identifiers to test bug 560213
+ for (var i = 0; i < 5; ++i) {
+ var r = 'rid_' + Math.floor(Math.random() * 10000 + 1);
+ try {
+ ok(!(r in p), "unknown identifier in crashed plugin should fail silently");
+ }
+ catch (e) {
+ ok(false, "unknown identifier check threw");
+ }
+ }
+
+ try {
+ p.setColor("FFFF0000");
+ ok(false, "p.setColor should throw after the plugin crashes");
+ }
+ catch (e) {
+ ok(true, "p.setColor should throw after the plugin crashes");
+ }
+
+ window.frameLoaded = function reloaded() {
+ var p = iframe.contentDocument.getElementById('plugin1');
+ try {
+ p.setColor('FF00FF00');
+ ok(true, "Reloading worked");
+ }
+ catch (e) {
+ ok(false, "Reloading didn't give us a usable plugin");
+ }
+ SimpleTest.finish();
+ }
+
+ iframe.contentWindow.location.reload();
+ }
+
+ </script>
+ <iframe id="iframe1" src="crashing_subpage.html" width="600" height="600"></iframe>
diff --git a/dom/plugins/test/mochitest/test_crashing2.html b/dom/plugins/test/mochitest/test_crashing2.html
new file mode 100644
index 0000000000..be61f3d501
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_crashing2.html
@@ -0,0 +1,74 @@
+<head>
+ <title>Plugin crashing</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+<body onload="mainLoaded()">
+ <iframe id="iframe1" src="about:blank" width="600" height="600"></iframe>
+
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var iframe = document.getElementById('iframe1');
+
+ function mainLoaded() {
+ SimpleTest.expectChildProcessCrash();
+
+ var p = iframe.contentDocument.createElement('embed');
+ p.setAttribute('id', 'plugin1');
+ p.setAttribute('type', 'application/x-test');
+ p.setAttribute('width', '400');
+ p.setAttribute('height', '400');
+ p.setAttribute('drawmode', 'solid');
+ p.setAttribute('color', 'FF00FFFF');
+ p.setAttribute('newCrash', 'true');
+ iframe.contentDocument.body.appendChild(p);
+
+ // The plugin will now crash when it is instantiated, but
+ // that happens asynchronously. HACK: this should use an
+ // event instead of nested pending runnables, but I don't
+ // know of any DOM event that's fired when a plugin is
+ // instantiated.
+ SimpleTest.executeSoon(function() {
+ SimpleTest.executeSoon(function() {
+ ok(p.setColor === undefined,
+ "Plugin should have crashed on creation");
+
+ window.frameLoaded = reloaded1;
+ iframe.contentWindow.location.replace('crashing_subpage.html');
+ })
+ });
+ }
+
+ function reloaded1() {
+ var p = iframe.contentDocument.getElementById('plugin1');
+ try {
+ p.setColor('FF00FF00');
+ ok(true, "Reloading after crash-on-new worked");
+ }
+ catch (e) {
+ ok(false, "Reloading after crash-on-new didn't give us a usable plugin");
+ }
+ p.crashOnDestroy();
+ // the child crash should happen here
+ p.parentNode.removeChild(p);
+
+ window.frameLoaded = reloaded2;
+ SimpleTest.executeSoon(function() {
+ iframe.contentWindow.location.reload();
+ });
+ }
+
+ function reloaded2() {
+ var p = iframe.contentDocument.getElementById('plugin1');
+ try {
+ p.setColor('FF00FF00');
+ ok(true, "Reloading after crash-on-destroy worked");
+ }
+ catch (e) {
+ ok(false, "Reloading after crash-on-destroy didn't give us a usable plugin");
+ }
+ SimpleTest.finish();
+ }
+ </script>
diff --git a/dom/plugins/test/mochitest/test_defaultValue.html b/dom/plugins/test/mochitest/test_defaultValue.html
new file mode 100644
index 0000000000..2cbe52efe0
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_defaultValue.html
@@ -0,0 +1,38 @@
+<html>
+ <head>
+ <title>NPObject [[DefaultValue]] implementation</title>
+
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+
+ <body onload="run()">
+
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function run() {
+ var plugin = document.getElementById("plugin");
+ var pluginProto = Object.getPrototypeOf(plugin);
+
+ plugin.propertyAndMethod = {};
+ plugin.propertyAndMethod + "baz";
+ ok(true, "|plugin.propertyAndMethod + \"baz\"| shouldn't assert");
+ pluginProto.propertyAndMethod = {};
+ pluginProto.propertyAndMethod + "quux";
+ ok(true, "|pluginProto.propertyAndMethod + \"quux\"| shouldn't assert");
+
+ plugin + "foo";
+ ok(true, "|plugin + \"foo\"| shouldn't assert");
+ pluginProto + "bar";
+ ok(true, "|pluginProto + \"bar\"| shouldn't assert");
+
+ SimpleTest.finish();
+ }
+ </script>
+
+ <embed id="plugin" type="application/x-test" wmode="window"></embed>
+ </body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_enumerate.html b/dom/plugins/test/mochitest/test_enumerate.html
new file mode 100644
index 0000000000..af8431c7cc
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_enumerate.html
@@ -0,0 +1,36 @@
+<html>
+<head>
+ <title>NPAPI Cookie Tests</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function runTests() {
+ var pluginElement = document.getElementById("plugin1");
+ var c = 0;
+ var foundSetColor = false;
+ for (var n in pluginElement) {
+ ++c;
+ ok(n in pluginElement, "Enumerated property which doesn't exist?");
+ if (n == 'setColor')
+ foundSetColor = true;
+ }
+ ok(c > 0, "Should have enumerated some properties");
+ ok(foundSetColor, "Should have enumerated .setColor");
+ SimpleTest.finish();
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_fullpage.html b/dom/plugins/test/mochitest/test_fullpage.html
new file mode 100644
index 0000000000..680eb73a01
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_fullpage.html
@@ -0,0 +1,35 @@
+<head>
+ <title>Full-page seekable stream</title>
+
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css">
+
+<body>
+ <p id="display"></p>
+
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function frameLoaded() {
+ var testframe = document.getElementById('testframe');
+ var content = testframe.contentDocument.body.innerHTML;
+ if (!content.length)
+ return;
+
+ var req = new XMLHttpRequest();
+ req.open('GET', 'loremipsum.xtest', false);
+ req.overrideMimeType('text/plain; charset=x-user-defined');
+ req.send(null);
+ is(req.status, 200, "bad XMLHttpRequest");
+ is(content, req.responseText.replace(/\r\n/g, "\n"),
+ "content doesn't match");
+ SimpleTest.finish();
+ }
+ </script>
+
+ <iframe src="loremipsum.xtest" streamtype="seek"></iframe>
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"></iframe>
diff --git a/dom/plugins/test/mochitest/test_getauthenticationinfo.html b/dom/plugins/test/mochitest/test_getauthenticationinfo.html
new file mode 100644
index 0000000000..f249386c7c
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_getauthenticationinfo.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Login Manager</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+Test for NPN_GetAuthenticationInfo
+<p id="display"></p>
+
+<div id="content">
+ <iframe id="iframe"></iframe>
+</div>
+
+<script class="testbody" type="text/javascript">
+
+const Ci = SpecialPowers.Ci;
+const Cc = SpecialPowers.Cc;
+
+function iframeLoad() {
+ var plugin = iframe.contentDocument.getElementById("embedtest");
+ // valid request
+ is(plugin.getAuthInfo("http", "mochi.test", 8888, "basic", "testrealm"),
+ "user1|password1",
+ "correct user/pass retrieved");
+ try {
+ // invalid request -- wrong host
+ is(plugin.getAuthInfo("http", "example.com", 8888, "basic", "testrealm"),
+ "user1|password1",
+ "correct user/pass retrieved");
+ ok(false, "no exception was thrown");
+ }
+ catch (err) {
+ ok(true, "expected exception caught");
+ }
+ try {
+ // invalid request -- wrong port
+ is(plugin.getAuthInfo("http", "mochi.test", 90, "basic", "testrealm"),
+ "user1|password1",
+ "correct user/pass retrieved");
+ ok(false, "no exception was thrown");
+ }
+ catch (err) {
+ ok(true, "expected exception caught");
+ }
+ try {
+ // invalid request -- wrong realm
+ is(plugin.getAuthInfo("http", "mochi.test", 8888, "basic", "wrongrealm"),
+ "user1|password1",
+ "correct user/pass retrieved");
+ ok(false, "no exception was thrown");
+ }
+ catch (err) {
+ ok(true, "expected exception caught");
+ }
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+// Authentication info is added twice here. In the non-e10s case, this does
+// nothing. In the e10s case, we need to add auth info in both the child process,
+// which the plugin checks for auth validity, and the parent process, which the
+// http network objects use.
+// TODO: Clean this up once HTTPAuthManager is made e10s compliant in bug 1249172
+var iframe = document.getElementById("iframe");
+var am = Cc["@mozilla.org/network/http-auth-manager;1"].
+ getService(Ci.nsIHttpAuthManager);
+am.setAuthIdentity("http", "mochi.test", 8888, "basic", "testrealm", "",
+ "mochi.test", "user1", "password1");
+SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL("file_authident.js"));
+iframe.onload = iframeLoad;
+iframe.src = "http://mochi.test:8888/tests/toolkit/components/passwordmgr/" +
+ "test/authenticate.sjs?user=user1&pass=password1&realm=testrealm&plugin=1";
+
+</script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_hang_submit.xul b/dom/plugins/test/mochitest/test_hang_submit.xul
new file mode 100644
index 0000000000..6c037ecd49
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_hang_submit.xul
@@ -0,0 +1,165 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Basic Plugin Tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+<body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
+<embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+</body>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+SimpleTest.ignoreAllUncaughtExceptions();
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/Task.jsm");
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+const crashReporter = Cc["@mozilla.org/toolkit/crash-reporter;1"].getService(Ci.nsICrashReporter);
+const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
+
+var oldServerURL = crashReporter.serverURL;
+
+const oldTimeoutPref = Services.prefs.getIntPref("dom.ipc.plugins.timeoutSecs");
+
+var testObserver = {
+ observe: function(subject, topic, data) {
+ if (data == "submitting") // not done yet
+ return;
+ is(data, "success", "report should have been submitted successfully");
+ is(topic, "crash-report-status", "Checking correct topic");
+ ok(subject instanceof Ci.nsIPropertyBag2, "Subject should be a property bag");
+
+ ok(subject.hasKey("minidumpID"), "Should have a local crash ID");
+ let crashID = subject.getPropertyAsAString("minidumpID");
+ isnot(crashID, "", "Local crash ID should not be an empty string");
+
+ ok(subject.hasKey("serverCrashID"), "Should have a server crash ID");
+ let remoteID = subject.getPropertyAsAString("serverCrashID");
+ isnot(remoteID, "", "Server crash ID should not be an empty string");
+
+ // Verify the data. The SJS script will return the data that was POSTed
+ let req = new XMLHttpRequest();
+ req.open("GET", SERVER_URL + "?id=" + remoteID, false);
+ req.send(null);
+ is(req.status, 200, "Server response should be 200 OK");
+ let submitted = JSON.parse(req.responseText);
+
+ ok(!("Throttleable" in submitted), "Submit request should not be Throttleable");
+ is(submitted.ProcessType, "plugin", "Should specify ProcessType=plugin");
+ ok("PluginHang" in submitted, "Request should contain PluginHang field");
+ ok("additional_minidumps" in submitted, "Request should contain additional_minidumps field");
+ let dumpNames = submitted.additional_minidumps.split(',');
+ ok(dumpNames.indexOf("browser") != -1, "additional_minidumps should contain browser");
+ info("additional_minidumps="+submitted.additional_minidumps);
+ ok("upload_file_minidump" in submitted, "Request should contain upload_file_minidump field");
+ ok("upload_file_minidump_browser" in submitted, "Request should contain upload_file_minidump_browser field");
+
+ // Cleanup
+ // First remove our fake submitted report
+ let file = Services.dirsvc.get("UAppData", Ci.nsILocalFile);
+ file.append("Crash Reports");
+ file.append("submitted");
+ file.append(remoteID + ".txt");
+ file.remove(false);
+
+ // Next unregister our observer
+ Services.obs.removeObserver(testObserver, "crash-report-status");
+
+ // Then re-set MOZ_CRASHREPORTER_NO_REPORT
+ let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
+ env.set("MOZ_CRASHREPORTER_NO_REPORT", "1");
+
+ // Finally re-set prefs
+ crashReporter.serverURL = oldServerURL;
+ Services.prefs.setIntPref("dom.ipc.plugins.timeoutSecs", oldTimeoutPref);
+
+ // Check and cleanup CrashManager.
+ Task.spawn(function* () {
+ let cm = Services.crashmanager;
+ let store = yield cm._getStore();
+ is(store.crashesCount, 1, "Store should have only 1 item");
+
+ let crash = store.getCrash(crashID);
+ ok(!!crash, "Store should have the crash record");
+ ok(crash.isOfType(cm.PROCESS_TYPE_PLUGIN, cm.CRASH_TYPE_HANG),
+ "Crash type should be plugin-hang");
+ is(crash.remoteID, remoteID, "Crash remoteID should match");
+
+ is(crash.submissions.size, 1, "Crash should have a submission");
+ let submission = crash.submissions.values().next().value;
+ is(submission.result, cm.SUBMISSION_RESULT_OK,
+ "Submission should be successful");
+
+ store.reset();
+
+ SimpleTest.finish();
+ });
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIObserver) ||
+ iid.equals(Ci.nsISupportsWeakReference) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+ throw Components.results.NS_NOINTERFACE;
+ }
+};
+
+function onPluginCrashed(aEvent) {
+ ok(true, "Plugin crashed notification received");
+ is(aEvent.type, "PluginCrashed", "event is correct type");
+
+ let submitButton = document.getAnonymousElementByAttribute(aEvent.target,
+ "class",
+ "submitButton");
+ // try to submit this report
+ sendMouseEvent({type:'click'}, submitButton, window);
+}
+
+function runTests() {
+ // Default plugin hang timeout is too high for mochitests
+ Services.prefs.setIntPref("dom.ipc.plugins.timeoutSecs", 1);
+
+ // the test harness will have set MOZ_CRASHREPORTER_NO_REPORT,
+ // ensure that we can change the setting and have our minidumps
+ // wind up in Crash Reports/pending
+ let env = Cc["@mozilla.org/process/environment;1"]
+ .getService(Ci.nsIEnvironment);
+ env.set("MOZ_CRASHREPORTER_NO_REPORT", "");
+
+ // Override the crash reporter URL to send to our fake server
+ crashReporter.serverURL = NetUtil.newURI(SERVER_URL);
+
+ // Hook into plugin crash events
+ Services.obs.addObserver(testObserver, "crash-report-status", true);
+ document.addEventListener("PluginCrashed", onPluginCrashed, false);
+
+ var pluginElement = document.getElementById("plugin1");
+ try {
+ Task.spawn(function* () {
+ // Clear data in CrashManager in case previous tests caused something
+ // to be added.
+ let store = yield Services.crashmanager._getStore();
+ store.reset();
+
+ pluginElement.hang();
+ });
+ } catch (e) {
+ }
+}
+]]>
+</script>
+</window>
diff --git a/dom/plugins/test/mochitest/test_hanging.html b/dom/plugins/test/mochitest/test_hanging.html
new file mode 100644
index 0000000000..c8ea936cec
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_hanging.html
@@ -0,0 +1,63 @@
+<head>
+ <title>Plugin hanging</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+<body>
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ window.frameLoaded = function frameLoaded_toCrash() {
+ SimpleTest.expectChildProcessCrash();
+
+ // the default timeout is annoying high for mochitest runs
+ var timeoutPref = "dom.ipc.plugins.timeoutSecs";
+ SpecialPowers.setIntPref(timeoutPref, 5);
+
+ var iframe = document.getElementById('iframe1');
+ var p = iframe.contentDocument.getElementById('plugin1');
+
+ p.setColor("FFFF00FF");
+
+ try {
+ p.hang();
+ ok(false, "p.hang() should throw an exception");
+ }
+ catch (e) {
+ ok(true, "p.hang() should throw an exception");
+ }
+
+ try {
+ p.setColor("FFFF0000");
+ ok(false, "p.setColor should throw after the plugin crashes");
+ }
+ catch (e) {
+ ok(true, "p.setColor should throw after the plugin crashes");
+ }
+
+ window.frameLoaded = function reloaded() {
+ var p = iframe.contentDocument.getElementById('plugin1');
+ try {
+ p.setColor('FF00FF00');
+ ok(true, "Reloading worked");
+ }
+ catch (e) {
+ ok(false, "Reloading didn't give us a usable plugin");
+ }
+
+ try {
+ SpecialPowers.clearUserPref(timeoutPref);
+ }
+ catch(e) {
+ ok(false, "Couldn't reset timeout pref");
+ }
+
+ SimpleTest.finish();
+ }
+
+ iframe.contentWindow.location.reload();
+ }
+
+ </script>
+ <iframe id="iframe1" src="crashing_subpage.html" width="600" height="600"></iframe>
diff --git a/dom/plugins/test/mochitest/test_hangui.xul b/dom/plugins/test/mochitest/test_hangui.xul
new file mode 100644
index 0000000000..edb90db006
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_hangui.xul
@@ -0,0 +1,262 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Basic Plugin Tests"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <title>Plugin Hang UI Test</title>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="plugin-utils.js" />
+ <script type="application/javascript"
+ src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hang_test.js" />
+ <script type="application/javascript"
+ src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hangui_common.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <iframe id="iframe1" src="hangui_subpage.html" width="400" height="400"></iframe>
+</body>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const hangUITimeoutPref = "dom.ipc.plugins.hangUITimeoutSecs";
+const hangUIMinDisplayPref = "dom.ipc.plugins.hangUIMinDisplaySecs";
+const timeoutPref = "dom.ipc.plugins.timeoutSecs";
+
+var worker = new ChromeWorker("hangui_iface.js");
+worker.onmessage = function(event) {
+ var result = event.data;
+ var params = result.params;
+ var output = params.testName;
+ if (result.msg) {
+ output += ": " + result.msg;
+ }
+ ok(result.status, output);
+ if (params.callback) {
+ var cb = eval(params.callback);
+ var timeout = setTimeout(function() { clearTimeout(timeout); cb(); }, 100);
+ }
+};
+worker.onerror = function(event) {
+ var output = "Error: " + event.message + " at " + event.filename + ":" + event.lineno;
+ ok(false, output);
+};
+
+var iframe;
+var p;
+var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
+
+function hanguiOperation(testName, timeoutSec, expectFind, expectClose, opCode,
+ commandId, check, cb) {
+ var timeoutMs = timeoutSec * 1000 + EPSILON_MS;
+ worker.postMessage({ "timeoutMs": timeoutMs, "expectToFind": expectFind,
+ "expectToClose": expectClose, "opCode": opCode,
+ "commandId": commandId, "check": check,
+ "testName": testName, "callback": cb });
+}
+
+function hanguiExpect(testName, shouldBeShowing, shouldClose, cb) {
+ var timeoutSec = Services.prefs.getIntPref(hangUITimeoutPref);
+ if (!shouldBeShowing && !timeoutSec) {
+ timeoutSec = Services.prefs.getIntPref(timeoutPref);
+ }
+ hanguiOperation(testName, timeoutSec, shouldBeShowing, shouldClose, HANGUIOP_NOTHING, 0, false, cb);
+}
+
+function hanguiContinue(testName, check, cb) {
+ var timeoutSec = Services.prefs.getIntPref(hangUITimeoutPref);
+ hanguiOperation(testName, timeoutSec, true, true, HANGUIOP_COMMAND, IDC_CONTINUE, check, cb);
+}
+
+function hanguiStop(testName, check, cb) {
+ var timeoutSec = Services.prefs.getIntPref(hangUITimeoutPref);
+ hanguiOperation(testName, timeoutSec, true, true, HANGUIOP_COMMAND, IDC_STOP, check, cb);
+}
+
+function hanguiCancel(testName, cb) {
+ var timeoutSec = Services.prefs.getIntPref(hangUITimeoutPref);
+ hanguiOperation(testName, timeoutSec, true, true, HANGUIOP_CANCEL, 0, false, cb);
+}
+
+function finishTest() {
+ if (obsCount > 0) {
+ os.removeObserver(testObserver, "plugin-crashed");
+ --obsCount;
+ }
+ SpecialPowers.clearUserPref(hangUITimeoutPref);
+ SpecialPowers.clearUserPref(hangUIMinDisplayPref);
+ SpecialPowers.clearUserPref(timeoutPref);
+ SimpleTest.finish();
+}
+
+function runTests() {
+ resetVars();
+
+ hanguiOperation("Prime ChromeWorker", 0, false, false, HANGUIOP_NOTHING, 0,
+ false, "test1");
+}
+
+window.frameLoaded = runTests;
+
+var obsCount = 0;
+
+function onPluginCrashedHangUI(aEvent) {
+ ok(true, "Plugin crashed notification received");
+ is(aEvent.type, "PluginCrashed", "event is correct type");
+
+ is(p, aEvent.target, "Plugin crashed event target is plugin element");
+
+ ok(aEvent instanceof PluginCrashedEvent,
+ "plugin crashed event has the right interface");
+
+ is(typeof aEvent.pluginDumpID, "string", "pluginDumpID is correct type");
+ isnot(aEvent.pluginDumpID, "", "got a non-empty dump ID");
+ is(typeof aEvent.pluginName, "string", "pluginName is correct type");
+ is(aEvent.pluginName, "Test Plug-in", "got correct plugin name");
+ is(typeof aEvent.pluginFilename, "string", "pluginFilename is correct type");
+ isnot(aEvent.pluginFilename, "", "got a non-empty filename");
+ // The app itself may or may not have decided to submit the report, so
+ // allow either true or false here.
+ ok("submittedCrashReport" in aEvent, "submittedCrashReport is a property of event");
+ is(typeof aEvent.submittedCrashReport, "boolean", "submittedCrashReport is correct type");
+
+ os.removeObserver(testObserver, "plugin-crashed");
+ --obsCount;
+}
+
+function resetVars() {
+ iframe = document.getElementById('iframe1');
+ p = iframe.contentDocument.getElementById("plugin1");
+ if (obsCount == 0) {
+ os.addObserver(testObserver, "plugin-crashed", true);
+ ++obsCount;
+ }
+ iframe.contentDocument.addEventListener("PluginCrashed",
+ onPluginCrashedHangUI,
+ false);
+}
+
+function test9b() {
+ hanguiExpect("test9b: Plugin Hang UI is not showing (checkbox)", false);
+ p.stall(STALL_DURATION);
+ hanguiExpect("test9b: Plugin Hang UI is still not showing (checkbox)", false, false, "finishTest");
+ p.stall(STALL_DURATION);
+}
+
+function test9a() {
+ resetVars();
+ SpecialPowers.setIntPref(hangUITimeoutPref, 1);
+ SpecialPowers.setIntPref(hangUIMinDisplayPref, 1);
+ SpecialPowers.setIntPref(timeoutPref, 45);
+ hanguiContinue("test9a: Continue button works with checkbox", true, "test9b");
+ p.stall(STALL_DURATION);
+}
+
+function test9() {
+ window.frameLoaded = test9a;
+ iframe.contentWindow.location.reload();
+}
+
+function test8a() {
+ resetVars();
+ SpecialPowers.setIntPref(hangUITimeoutPref, 1);
+ SpecialPowers.setIntPref(hangUIMinDisplayPref, 4);
+ hanguiExpect("test8a: Plugin Hang UI is not showing (disabled due to hangUIMinDisplaySecs)", false, false, "test9");
+ var exceptionThrown = false;
+ try {
+ p.hang();
+ } catch(e) {
+ exceptionThrown = true;
+ }
+ ok(exceptionThrown, "test8a: Exception thrown from hang() when plugin was terminated");
+}
+
+function test8() {
+ window.frameLoaded = test8a;
+ iframe.contentWindow.location.reload();
+}
+
+function test7a() {
+ resetVars();
+ SpecialPowers.setIntPref(hangUITimeoutPref, 0);
+ hanguiExpect("test7a: Plugin Hang UI is not showing (disabled)", false, false, "test8");
+ var exceptionThrown = false;
+ try {
+ p.hang();
+ } catch(e) {
+ exceptionThrown = true;
+ }
+ ok(exceptionThrown, "test7a: Exception thrown from hang() when plugin was terminated");
+}
+
+function test7() {
+ window.frameLoaded = test7a;
+ iframe.contentWindow.location.reload();
+}
+
+function test6() {
+ SpecialPowers.setIntPref(hangUITimeoutPref, 1);
+ SpecialPowers.setIntPref(hangUIMinDisplayPref, 1);
+ SpecialPowers.setIntPref(timeoutPref, 3);
+ hanguiExpect("test6: Plugin Hang UI is showing", true, true, "test7");
+ var exceptionThrown = false;
+ try {
+ p.hang();
+ } catch(e) {
+ exceptionThrown = true;
+ }
+ ok(exceptionThrown, "test6: Exception thrown from hang() when plugin was terminated (child timeout)");
+}
+
+function test5a() {
+ resetVars();
+ hanguiCancel("test5a: Close button works", "test6");
+ p.stall(STALL_DURATION);
+}
+
+function test5() {
+ window.frameLoaded = test5a;
+ iframe.contentWindow.location.reload();
+}
+
+function test4() {
+ hanguiStop("test4: Stop button works", false, "test5");
+ // We'll get an exception here because the plugin was terminated
+ var exceptionThrown = false;
+ try {
+ p.hang();
+ } catch(e) {
+ exceptionThrown = true;
+ }
+ ok(exceptionThrown, "test4: Exception thrown from hang() when plugin was terminated");
+}
+
+function test3() {
+ hanguiContinue("test3: Continue button works", false, "test4");
+ p.stall(STALL_DURATION);
+}
+
+function test2() {
+ // This test is identical to test1 because there were some bugs where the
+ // Hang UI would show on the first hang but not on subsequent hangs
+ hanguiExpect("test2: Plugin Hang UI is showing", true, true, "test3");
+ p.stall(STALL_DURATION);
+}
+
+function test1() {
+ SpecialPowers.setIntPref(hangUITimeoutPref, 1);
+ SpecialPowers.setIntPref(hangUIMinDisplayPref, 1);
+ SpecialPowers.setIntPref(timeoutPref, 45);
+ hanguiExpect("test1: Plugin Hang UI is showing", true, true, "test2");
+ p.stall(STALL_DURATION);
+}
+
+]]>
+</script>
+</window>
diff --git a/dom/plugins/test/mochitest/test_hidden_plugin.html b/dom/plugins/test/mochitest/test_hidden_plugin.html
new file mode 100644
index 0000000000..446d8cbbd5
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_hidden_plugin.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test whether we are adding the dummy plugin correctly when there is only 1 plugin and its hidden</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<body>
+ <script type="application/javascript;version=1.7">
+ "use strict"
+ {
+ SimpleTest.waitForExplicitFinish();
+ let ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"].getService(SpecialPowers.Ci.nsIPluginHost);
+ let plugins = ph.getPluginTags();
+ let testPluginName = plugins[0].name;
+ let oldPrefVal = null;
+ let prefName = "plugins.navigator.hidden_ctp_plugin";
+ try {
+ oldPrefVal = SpecialPowers.getCharPref(prefName);
+ } catch (ex) {}
+ let promise = SpecialPowers.pushPrefEnv({ set: [[prefName, testPluginName]]});
+ promise.then(function() {
+ for (let i = 0; i < plugins.length; i++) {
+ let plugin = plugins[i];
+ let newState = (plugin.name == testPluginName ? SpecialPowers.Ci.nsIPluginTag.STATE_CLICKTOPLAY :
+ SpecialPowers.Ci.nsIPluginTag.STATE_DISABLED);
+ if (plugin.enabledState != newState) {
+ let oldState = plugin.enabledState;
+ setTestPluginEnabledState(newState, plugin.name);
+ SimpleTest.registerCleanupFunction(function() {
+ if (plugin.enabledState != oldState)
+ setTestPluginEnabledState(oldState, plugin.name);
+ });
+ }
+ }
+ // we have disabled all the plugins except for 1 which is click to play and hidden. The
+ // navigator.plugins list should have only one entry and it should be the dummy plugin.
+ isnot(navigator.plugins.length, 0, "navigator.plugins should not be empty");
+ SimpleTest.finish();
+ });
+ }
+</script>
+</body>
diff --git a/dom/plugins/test/mochitest/test_idle_hang.xul b/dom/plugins/test/mochitest/test_idle_hang.xul
new file mode 100644
index 0000000000..83ff28f52f
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_idle_hang.xul
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Basic Plugin Tests"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <title>Plugin Idle Hang Test</title>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hang_test.js" />
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ </script>
+<body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
+<embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+</body>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ // Default plugin hang timeout is too high for mochitests
+ var prefs = Cc["@mozilla.org/preferences-service;1"]
+ .getService(Ci.nsIPrefBranch);
+ var timeoutPref = "dom.ipc.plugins.timeoutSecs";
+ prefs.setIntPref(timeoutPref, 5);
+
+ var os = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+ os.addObserver(testObserver, "plugin-crashed", true);
+
+ testObserver.idleHang = true;
+ document.addEventListener("PluginCrashed", onPluginCrashed, false);
+
+ var pluginElement = document.getElementById("plugin1");
+ try {
+ pluginElement.hang(false);
+ } catch (e) {
+ }
+}
+]]>
+</script>
+</window>
+
diff --git a/dom/plugins/test/mochitest/test_instance_re-parent.html b/dom/plugins/test/mochitest/test_instance_re-parent.html
new file mode 100644
index 0000000000..1ba87adb5b
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_instance_re-parent.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test re-parentinging an instance's DOM node</title>
+ <script type="text/javascript" src="/MochiKit/packed.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="plugin-utils.js"></script>
+</head>
+<body onload="begin()">
+ <script type="application/javascript;version=1.8">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ const MAX_CHECK_PLUGIN_STOPPED_ATTEMPTS = 50;
+ var numCheckPluginStoppedAttempts = 0;
+ var exceptionThrown = false;
+ var p = null;
+ var d1 = null;
+ var d2 = null;
+
+ var destroyed = false;
+
+ function begin() {
+ runTests(function() {
+ // Callback when finished - set plugin to windowed and repeat the tests
+
+ info("Repeating tests with wmode=window");
+ p.setAttribute("wmode", "window");
+ d1.appendChild(p);
+
+ // Forces the plugin to be respawned
+ p.src = p.src;
+
+ destroyed = false;
+ exceptionThrown = false;
+ runTests(function () {
+ SimpleTest.finish();
+ });
+ });
+ }
+
+ function checkPluginStopped(callback, param) {
+ if (numCheckPluginStoppedAttempts < MAX_CHECK_PLUGIN_STOPPED_ATTEMPTS &&
+ !destroyed) {
+ ++numCheckPluginStoppedAttempts;
+ SimpleTest.executeSoon(function() {
+ checkPluginStopped(callback, param);
+ });
+ } else {
+ info("Number of check plugin stopped attempts: " +
+ numCheckPluginStoppedAttempts);
+ callback(param);
+ }
+ }
+
+ function runTests(callback) {
+ p = document.getElementById('plugin1');
+ d1 = document.getElementById('div1');
+ d2 = document.getElementById('div2');
+
+ // First tests involve moving the instance from one div to another.
+ p.startWatchingInstanceCount();
+ p.callOnDestroy(function() {
+ destroyed = true;
+ });
+
+ try {
+ d1.removeChild(p);
+ } catch (e) {
+ exceptionThrown = true;
+ }
+ is(exceptionThrown, false, "Testing for exception after removeChild.");
+
+ try {
+ d2.appendChild(p);
+ } catch (e) {
+ exceptionThrown = true;
+ }
+ is(exceptionThrown, false, "Testing for exception after appendChild.");
+
+ is(destroyed, false, "No instances should have been destroyed at this point.");
+ is(p.getInstanceCount(), 0, "No new instances should have been created at this point.");
+
+ // Wait for the event loop to spin and ensure the plugin still wasn't touched
+ SimpleTest.executeSoon(function() {
+ is(destroyed, false, "No instances should have been destroyed at this point.");
+ is(p.getInstanceCount(), 0, "No new instances should have been created at this point.");
+
+ d2.removeChild(p);
+ checkPluginStopped(continueTestsAfterPluginDestruction, callback);
+ });
+ }
+
+ function continueTestsAfterPluginDestruction(callback) {
+ d2.appendChild(p);
+ SimpleTest.executeSoon(function() {
+ try {
+ is(p.getInstanceCount(), 1, "One new instance should have been created at this point.");
+ } catch (e) {
+ exceptionThrown = true;
+ }
+ is(exceptionThrown, false, "Testing for exception getting instance count from plugin.");
+
+ p.stopWatchingInstanceCount();
+ callback.apply(null);
+ });
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <div id="div1">
+ <!-- This embed has to have a "src" attribute. Part of this test involves seeing if we
+ properly restart plugins that have been added back to a document without a change
+ in URL. Not re-loading an object when the URL hasn't changed is a shortcut used for
+ some object types. Without a URL, this won't be tested. -->
+ <embed id="plugin1" src="loremipsum.txt" type="application/x-test" width="200" height="200"></embed>
+ </div>
+ <div id="div2">
+ </div>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_instance_unparent1.html b/dom/plugins/test/mochitest/test_instance_unparent1.html
new file mode 100644
index 0000000000..5a11dc5887
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_instance_unparent1.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test removing an instance's DOM node</title>
+ <script type="text/javascript" src="/MochiKit/packed.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="startTest()">
+ <script type="application/javascript;version=1.8">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ const MAX_ATTEMPTS = 50;
+ var attempts = 0;
+ var destroyed = false;
+ function onDestroy() {
+ destroyed = true;
+ }
+
+ function checkPluginAlreadyDestroyed() {
+ // We may need to retry a few times until the plugin stop event makes
+ // its way through the event queue.
+ if (attempts < MAX_ATTEMPTS && !destroyed) {
+ ++attempts;
+ SimpleTest.executeSoon(checkPluginAlreadyDestroyed);
+ } else {
+ info("Number of retry attempts: " + attempts);
+ is(destroyed, true, "Plugin instance should have been destroyed.");
+ SimpleTest.finish();
+ }
+ }
+
+ function startTest() {
+ var p1 = document.getElementById('plugin1');
+ var d1 = document.getElementById('div1');
+
+ p1.callOnDestroy(onDestroy);
+ d1.removeChild(p1);
+ SimpleTest.executeSoon(checkPluginAlreadyDestroyed);
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <div id="div1">
+ <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+ </div>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_instance_unparent2.html b/dom/plugins/test/mochitest/test_instance_unparent2.html
new file mode 100644
index 0000000000..eedbca8e4e
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_instance_unparent2.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test removing an instance's DOM node</title>
+ <script type="text/javascript" src="/MochiKit/packed.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="startTest()">
+ <script type="application/javascript;version=1.8">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ const MAX_ATTEMPTS = 50;
+ var attempts = 0;
+ var destroyed = false;
+ function onDestroy() {
+ destroyed = true;
+ }
+
+ function checkPluginAlreadyDestroyed() {
+ // We may need to retry a few times until the plugin stop event makes
+ // its way through the event queue.
+ if (attempts < MAX_ATTEMPTS && !destroyed) {
+ ++attempts;
+ SimpleTest.executeSoon(checkPluginAlreadyDestroyed);
+ } else {
+ info("Number of retry attempts: " + attempts);
+ is(destroyed, true, "Plugin instance should have been destroyed.");
+ SimpleTest.finish();
+ }
+ }
+
+ function startTest() {
+ var p1 = document.getElementById('plugin1');
+ var d1 = document.getElementById('div1');
+
+ p1.callOnDestroy(onDestroy);
+
+ // Get two parent check events to run.
+ d1.removeChild(p1);
+ d1.appendChild(p1);
+ d1.removeChild(p1);
+
+ SimpleTest.executeSoon(checkPluginAlreadyDestroyed);
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <div id="div1">
+ <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+ </div>
+
+ <div id="div2">
+ <div id="div3">
+ <embed id="plugin2" type="application/x-test" width="200" height="200"></embed>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_instance_unparent3.html b/dom/plugins/test/mochitest/test_instance_unparent3.html
new file mode 100644
index 0000000000..9e55c7ed92
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_instance_unparent3.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test removing an instance's DOM node</title>
+ <script type="text/javascript" src="/MochiKit/packed.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="startTest()">
+ <script type="application/javascript;version=1.8">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ const MAX_ATTEMPTS = 50;
+ var attempts = 0;
+ var destroyed = false;
+ function onDestroy() {
+ destroyed = true;
+ }
+
+ function checkPluginAlreadyDestroyed() {
+ // We may need to retry a few times until the plugin stop event makes
+ // its way through the event queue.
+ if (attempts < MAX_ATTEMPTS && !destroyed) {
+ ++attempts;
+ SimpleTest.executeSoon(checkPluginAlreadyDestroyed);
+ } else {
+ info("Number of retry attempts: " + attempts);
+ is(destroyed, true, "Plugin instance should have been destroyed.");
+ SimpleTest.finish();
+ }
+ }
+
+ function startTest() {
+ var p1 = document.getElementById('plugin1');
+ var d1 = document.getElementById('div1');
+ var d2 = document.getElementById('div2');
+
+ p1.callOnDestroy(onDestroy);
+ d1.removeChild(d2);
+ SimpleTest.executeSoon(checkPluginAlreadyDestroyed);
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <div id="div1">
+ <div id="div2">
+ <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+ </div<
+ </div>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_instantiation.html b/dom/plugins/test/mochitest/test_instantiation.html
new file mode 100644
index 0000000000..e8fc077459
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_instantiation.html
@@ -0,0 +1,33 @@
+<head>
+ <title>Plugin instantiation</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+<body onload="mainLoaded()">
+ <iframe id="iframe1" src="about:blank" width="600" height="600"></iframe>
+
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var iframe = document.getElementById('iframe1');
+
+ function mainLoaded() {
+ var p = iframe.contentDocument.createElement('embed');
+ p.setAttribute('id', 'plugin1');
+ p.setAttribute('type', 'application/x-test');
+ p.setAttribute('width', '400');
+ p.setAttribute('height', '400');
+ p.setAttribute('drawmode', 'solid');
+ p.setAttribute('color', 'FF00FFFF');
+ iframe.contentDocument.body.appendChild(p);
+
+ // Plugin instantiation happens asynchronously
+ SimpleTest.executeSoon(function() {
+ SimpleTest.executeSoon(function() {
+ ok(p.setColor !== undefined, "Dynamic plugin instantiation.");
+ SimpleTest.finish();
+ })
+ });
+ }
+ </script>
diff --git a/dom/plugins/test/mochitest/test_mixed_case_mime.html b/dom/plugins/test/mochitest/test_mixed_case_mime.html
new file mode 100644
index 0000000000..9258541bb7
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_mixed_case_mime.html
@@ -0,0 +1,29 @@
+<body>
+<head>
+ <title>Test mixed case mimetype for plugins</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<script>
+ SimpleTest.expectAssertions(0, 1);
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
+
+ function frameLoaded() {
+ var contentDocument = document.getElementById('testframe').contentDocument;
+ ok(contentDocument.body.innerHTML.length > 0, "Frame content shouldn't be empty.");
+ ok(contentDocument.plugins.length > 0, "Frame content should have a plugin.");
+ var plugin = contentDocument.plugins[0];
+ is(plugin.type.toLowerCase(), "application/x-second-test", "Should have loaded the second test plugin.");
+ SimpleTest.finish();
+ }
+</script>
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()" src="mixed_case_mime.sjs"></iframe>
+
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_multipleinstanceobjects.html b/dom/plugins/test/mochitest/test_multipleinstanceobjects.html
new file mode 100644
index 0000000000..74749e019c
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_multipleinstanceobjects.html
@@ -0,0 +1,24 @@
+<head>
+ <title>NPNV*NPObject accessibility tests</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function runTests() {
+ var p1 = document.getElementById('plugin1');
+ var p2 = document.getElementById('plugin2');
+
+ var o = p1.getObjectValue();
+ ok(p1.checkObjectValue(o), "Plugin objects passed to the same instance are identical.");
+ ok(!p2.checkObjectValue(o), "Plugin objects passed to another instance are double-wrapped.");
+
+ SimpleTest.finish();
+ }
+ </script>
+
+ <embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
+ <embed id="plugin2" type="application/x-test" width="400" height="400"></embed>
diff --git a/dom/plugins/test/mochitest/test_newstreamondestroy.html b/dom/plugins/test/mochitest/test_newstreamondestroy.html
new file mode 100644
index 0000000000..26c4709afb
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_newstreamondestroy.html
@@ -0,0 +1,36 @@
+<head>
+ <title>NPN_GetURL called from NPP_Destroy</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css">
+
+<body onload="runTest()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function runTest() {
+ var p = document.getElementById('plugin1');
+ var destroyed = false;
+ p.callOnDestroy(function() {
+ destroyed = true;
+ ok(!p.streamTest('loremipsum.txt', false, null, null,
+ function(r, t) {
+ ok(false, "get-during-destroy should have failed");
+ }, null, true), "NPN_GetURLNotify should fail during NPP_Destroy");
+ });
+ document.body.removeChild(p);
+
+ setTimeout(function() {
+ ok(destroyed, "callback was fired as expected");
+ SimpleTest.finish();
+ }, 1000);
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="plugin1" type="application/x-test"></embed>
diff --git a/dom/plugins/test/mochitest/test_npn_asynccall.html b/dom/plugins/test/mochitest/test_npn_asynccall.html
new file mode 100644
index 0000000000..4f007aacb0
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_npn_asynccall.html
@@ -0,0 +1,33 @@
+<html>
+<head>
+ <title>NPN_AsyncCallback Tests</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function asyncTestsComplete(result) {
+ ok(result, "asyncCallback tests completed");
+ SimpleTest.finish();
+ }
+
+ function runTests() {
+ var plugin = document.getElementById("plugin1");
+ plugin.asyncCallbackTest("asyncTestsComplete");
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="plugin1" type="application/x-test" width="400" height="100">
+ </embed>
+
+ </body>
+ </html>
diff --git a/dom/plugins/test/mochitest/test_npn_timers.html b/dom/plugins/test/mochitest/test_npn_timers.html
new file mode 100644
index 0000000000..64d5aebbda
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_npn_timers.html
@@ -0,0 +1,33 @@
+<html>
+<head>
+ <title>NPN_Timer Tests</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function pluginTimerTestFinish(result) {
+ ok(result, "timer tests completed");
+ SimpleTest.finish();
+ }
+
+ function runTests() {
+ var plugin = document.getElementById("plugin1");
+ plugin.timerTest("pluginTimerTestFinish");
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="plugin1" type="application/x-test" width="400" height="100">
+ </embed>
+
+ </body>
+ </html>
diff --git a/dom/plugins/test/mochitest/test_npobject_getters.html b/dom/plugins/test/mochitest/test_npobject_getters.html
new file mode 100644
index 0000000000..5a9a47081a
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_npobject_getters.html
@@ -0,0 +1,21 @@
+<head>
+ <title>NPNV*NPObject accessibility tests</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ dump('lastScript\n');
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function runTests() {
+ ok(document.getElementById('plugin1').pluginFoundElement, "plugin1.pluginFoundElement (NPNVPluginElementNPObject)", document.getElementById('plugin1').pluginFoundElement);
+ ok(window.pluginFoundWindow, "window.pluginFoundWindow (NPNVWindowNPObject)", window.pluginFoundWindow);
+
+ SimpleTest.finish();
+ }
+ </script>
+
+ <embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
diff --git a/dom/plugins/test/mochitest/test_npruntime.xul b/dom/plugins/test/mochitest/test_npruntime.xul
new file mode 100644
index 0000000000..7e5cc087f1
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_npruntime.xul
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Basic Plugin Tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+<body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
+<embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
+</body>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ var pluginElement = document.getElementById("plugin1");
+
+ ok(pluginElement.identifierToStringTest('foo') == "foo", "identifierToStringTest failed");
+
+ SimpleTest.finish();
+}
+]]>
+</script>
+</window>
diff --git a/dom/plugins/test/mochitest/test_npruntime_construct.html b/dom/plugins/test/mochitest/test_npruntime_construct.html
new file mode 100644
index 0000000000..bd85930a4f
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_npruntime_construct.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test whether windowless plugins receive correct visible/invisible notifications.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<body>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="theplugin" type="application/x-test"></embed>
+
+ <script type="application/javascript">
+ function MyFunc(arg) {
+ is(arg, "hi", "Argument passed to constructor function");
+ this.localProp = 'local';
+ }
+ MyFunc.prototype.protoProp = 't';
+
+ var theplugin = document.getElementById('theplugin');
+
+ ok(theplugin.constructObject(Array) instanceof Array, "Constructed Array");
+ var o = theplugin.constructObject(MyFunc, "hi");
+ ok(o instanceof MyFunc, "Constructed MyFunc");
+ is(o.localProp, 'local', "this property correct");
+ is(o.protoProp, 't', "prototype property correct");
+ </script>
diff --git a/dom/plugins/test/mochitest/test_npruntime_identifiers.html b/dom/plugins/test/mochitest/test_npruntime_identifiers.html
new file mode 100644
index 0000000000..a19a59be52
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_npruntime_identifiers.html
@@ -0,0 +1,67 @@
+<html>
+<head>
+ <title>NPN_Invoke Tests</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <script class="testbody" type="application/javascript">
+ ////
+ // This test exercises NP identifiers by querying the reflector to make sure
+ // that identifiers are translated to values correctly.
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var testsRun = 0;
+
+ function doTest() {
+ SpecialPowers.gc();
+
+ var reflector = document.getElementById("subframe").contentDocument.getElementById("plugin1").getReflector();
+
+ var i, prop, randomnumber;
+
+ for (i = 0; i < 20; ++i) {
+ randomnumber=Math.floor(Math.random()*1001);
+ prop = "prop" + randomnumber;
+ is(reflector[prop], prop, "Property " + prop);
+ }
+
+ for (i = -10; i < 0; ++i) {
+ is(reflector[i], String(i), "Property " + i);
+ prop = "prop" + i;
+ is(reflector[prop], prop, "Property " + prop);
+ }
+
+ for (i = 0; i < 10; ++i) {
+ is(reflector[i], i, "Property " + i);
+ prop = "prop" + i;
+ is(reflector[prop], prop, "Property " + prop);
+ }
+
+ is(reflector.a, 'a', "Property .a");
+ is(reflector['a'], 'a', "Property ['a']");
+ reflector = null;
+
+ SpecialPowers.gc();
+
+ ++testsRun;
+ if (testsRun == 3) {
+ SimpleTest.finish();
+ }
+ else {
+ document.getElementById('subframe').contentWindow.location.reload(true);
+ }
+ }
+ </script>
+
+ <iframe id="subframe" src="npruntime_identifiers_subpage.html" onload="doTest()"></iframe>
+
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_npruntime_npnevaluate.html b/dom/plugins/test/mochitest/test_npruntime_npnevaluate.html
new file mode 100644
index 0000000000..4a1c22978b
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_npruntime_npnevaluate.html
@@ -0,0 +1,101 @@
+<html>
+<head>
+ <title>NPN_Evaluate Tests</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ // global test function
+ function testMe(arg) {
+ var result = arg+arg;
+ for (var i = 1; i < arguments.length; i++) {
+ result += arguments[i] + arguments[i];
+ }
+ return result;
+ }
+
+ ////
+ // This test exercises NPN_Evaluate using the test plugin's
+ // npnEvaluateTest method. This method calls NPN_Evaluate on
+ // a string argument passed to it, and returns the eval result.
+ // The array below drives the tests; each array member has two
+ // members: the first is a string to eval, and the second is
+ // the expected result of the eval.
+ //
+
+ function runTests() {
+ var tests = [
+ ["3", 3],
+ ["3 + 3", 6],
+ ["'3'", "3"],
+ ["function test() { return 3; } test();", 3],
+ ["testMe(3)", 6],
+ ["testMe(new Object(3))", 6],
+ ["new Object(3)", new Object(3)],
+ ["new Array(1, 2, 3, 4)", [1, 2, 3, 4]],
+ ["document.getElementById('display')",
+ document.getElementById("display")],
+ ["encodeURI('a = b')", "a%20=%20b"],
+ ["document.getElementById('testdiv').innerHTML = 'Hello world!'",
+ "Hello world!"],
+ ["function test2() { var x = {a: '1', b: '2'}; return x; } test2();",
+ {a: '1', b: '2'}],
+ ["(function() { var ret; try { win = window.open(); win.document.writeln('wibble'); ret = 'no error' } catch(e) { ret = e.name; } win.close(); return ret; })()", "no error"],
+ ];
+
+ var plugin = document.getElementById("plugin1");
+
+ // Test calling NPN_Evaluate from within plugin code.
+ for (var test of tests) {
+ var expected = test[1];
+ var result = plugin.npnEvaluateTest(test[0]);
+ // serialize the two values for easy comparison
+ var json_expected = JSON.stringify(expected);
+ var json_result = JSON.stringify(result);
+ if (typeof(result) == "function")
+ json_result = result.toString();
+ if (typeof(expected) == "function")
+ json_expected = expected.toString();
+ is(json_result, json_expected,
+ "npnEvaluateTest returned an unexpected value");
+ is(typeof(result), typeof(expected),
+ "npnEvaluateTest return value was of unexpected type");
+ var success = (json_result == json_expected &&
+ typeof(result) == typeof(expected));
+ $("verbose").appendChild(
+ createEl('span',null, (success ? "pass" : "fail") + ": eval(" + test[0] + ")"));
+ $("verbose").appendChild(
+ createEl('span', null," == " + json_result + "(" +
+ typeof(result) + "), expected " + json_expected + "(" +
+ typeof(expected) + ")"));
+ $("verbose").appendChild(
+ createEl('br')
+ );
+ }
+
+ is(document.getElementById('testdiv').innerHTML, "Hello world!",
+ "innerHTML not set correctly via NPN_Evaluate");
+
+ SimpleTest.finish();
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="plugin1" type="application/x-test" width="400" height="100">
+ </embed>
+
+ <div id="verbose">
+ </div>
+ <div id="testdiv">
+ </div>
+ </body>
+ </html>
diff --git a/dom/plugins/test/mochitest/test_npruntime_npninvoke.html b/dom/plugins/test/mochitest/test_npruntime_npninvoke.html
new file mode 100644
index 0000000000..ff4c92a6d5
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_npruntime_npninvoke.html
@@ -0,0 +1,162 @@
+<html>
+<head>
+ <title>NPN_Invoke Tests</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTests()">
+
+ <script class="testbody" type="application/javascript">
+ ////
+ // This test exercises NPN_Invoke by calling the plugin's npnInvokeTest
+ // method, which in turn invokes a script method with 1 or more parameters,
+ // and then compares the return vale with an expected value. This is good
+ // for verifying various NPVariant values and types moving between
+ // the browser and the plugin.
+ //
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ // This function returns all the arguments passed to it, either as a
+ // single variable (in the caes of 1 argument), or as an array.
+ function returnArgs() {
+ if (arguments.length == 1)
+ return arguments[0];
+ var arr = new Array();
+ for (i = 0; i < arguments.length; i++) {
+ arr.push(arguments[i]);
+ }
+ return arr;
+ }
+
+ // Same as above but explicitly expects two arguments.
+ function returnTwoArgs(arg1, arg2) {
+ return [arg1, arg2];
+ }
+
+ // some objects and arrays used in the tests...
+ var obj = {"key1": "string", "key2": 0, "key3": true, "key4": undefined,
+ "key5": null, "key6": -551235.12552, "key7": false};
+ var arr = ["string", 0, true, false, undefined, null, -1, 55512.1252];
+ var obj2 = {"key1": "string", "key2": 0, "key3": true, "key4": undefined,
+ "key5": null, "key6": -551235.12552, "key7": false, "array": arr};
+ var arr2 = ["string", false, undefined, null, -1, 55512.1252,
+ {"a": "apple", "b": true, "c": undefined}];
+
+ ////
+ // A list of tests to run. Each member of the main array contains
+ // two members: the first contains the arguments passed to npnInvokeTest,
+ // and the second is the expected result.
+ //
+ var tests = [
+ // numeric values
+ [["returnArgs", 0, 0], true],
+ [["returnArgs", 1, 1], true],
+ [["returnArgs", 32768, 32768], true],
+ [["returnArgs", -32768, -32768], true],
+ [["returnArgs", 2147483648, 2147483648], true],
+ [["returnArgs", -2147483648, -2147483648], true],
+ [["returnArgs", 1.0, 1.0], true],
+ [["returnArgs", 1.592786, 1.592786], true],
+ [["returnArgs", 1.592786837, 1.592786837], true],
+ [["returnArgs", -1.592786, -1.592786], true],
+ [["returnArgs", -1.592786837, -1.592786837], true],
+ [["returnArgs", 15235.592786, 15235.592786], true],
+ // null, void, bool
+ [["returnArgs", null, null], true],
+ [["returnArgs", undefined, undefined], true],
+ [["returnArgs", undefined, null], false],
+ [["returnArgs", null, undefined], false],
+ [["returnArgs", 0, undefined], false],
+ [["returnArgs", 0, null], false],
+ [["returnArgs", 0, false], false],
+ [["returnArgs", 1, true], false],
+ [["returnArgs", true, true], true],
+ [["returnArgs", false, false], true],
+ // strings
+ [["returnArgs", "", ""], true],
+ [["returnArgs", "test", "test"], true],
+ [["returnArgs", "test", "testing"], false],
+ [["returnArgs", "test\n", "test\n"], true],
+ [["returnArgs", "test\nline2", "test\nline2"], true],
+ [["returnArgs", "test\nline2", "testline2"], false],
+ [["returnArgs", "test\u000aline2", "test\u000aline2"], true],
+ [["returnArgs", "test\u0020line2", "test line2"], true],
+ [["returnArgs", "test line2", "test\u0020line2"], true],
+ // objects and arrays
+ [["returnArgs", obj, obj], true],
+ [["returnArgs", arr, arr], true],
+ [["returnArgs", obj2, obj2], true],
+ [["returnArgs", arr2, arr2], true],
+ // multiple arguments
+ [["returnArgs", [0, 1, 2], 0, 1, 2], true],
+ [["returnArgs", [5, "a", true, false, null],
+ 5, "a", true, false, null], true],
+ [["returnArgs", [-1500.583, "test string\n",
+ [5, 10, 15, 20], {"a": 1, "b": 2}],
+ -1500.583, "test string\n", [5, 10, 15, 20], {"a": 1, "b": 2}], true],
+ [["returnArgs", [undefined, 0, null, "yes"],
+ undefined, 0, null, "yes"], true],
+ [["returnArgs", [0, undefined, null, "yes"],
+ 0, undefined, null, "yes"], true],
+ // too many/too few args
+ [["returnTwoArgs", ["a", "b"], "a", "b", "c"], true],
+ [["returnTwoArgs", ["a", undefined], "a"], true],
+ [["returnTwoArgs", [undefined, undefined]], true],
+ ];
+
+ function runTests() {
+ var plugin = document.getElementById("plugin1");
+
+ var result;
+ for (var test of tests) {
+ switch (test[0].length) {
+ case 2:
+ result = plugin.npnInvokeTest(test[0][0], test[0][1]);
+ break;
+ case 3:
+ result = plugin.npnInvokeTest(test[0][0], test[0][1], test[0][2]);
+ break;
+ case 4:
+ result = plugin.npnInvokeTest(test[0][0], test[0][1], test[0][2],
+ test[0][3]);
+ break;
+ case 5:
+ result = plugin.npnInvokeTest(test[0][0], test[0][1], test[0][2],
+ test[0][3], test[0][4]);
+ case 6:
+ result = plugin.npnInvokeTest(test[0][0], test[0][1], test[0][2],
+ test[0][3], test[0][4], test[0][5]);
+ break;
+ case 7:
+ result = plugin.npnInvokeTest(test[0][0], test[0][1], test[0][2],
+ test[0][3], test[0][4], test[0][5], test[0][6]);
+ break;
+ default:
+ is(false, "bad number of test arguments");
+ }
+ is(result, test[1], "npnInvokeTestFailed: " + plugin.getError());
+ $("verbose").appendChild(
+ createEl('span', null, ((result == test[1] ? "pass" : "fail") + ": " + test[0])));
+ if (result != test[1])
+ $("verbose").appendChild(createEl("span", null, (" " + plugin.getError())));
+ $("verbose").appendChild(createEl('br'));
+ }
+
+ SimpleTest.finish();
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="plugin1" type="application/x-test" width="400" height="100">
+ </embed>
+
+ <div id="verbose">
+ </div>
+ </body>
+ </html>
diff --git a/dom/plugins/test/mochitest/test_npruntime_npninvokedefault.html b/dom/plugins/test/mochitest/test_npruntime_npninvokedefault.html
new file mode 100644
index 0000000000..79a75e7551
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_npruntime_npninvokedefault.html
@@ -0,0 +1,153 @@
+<html>
+<head>
+ <title>NPN_Invoke_Default Tests</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ // global test function
+ function testMe(arg) {
+ var result = arg+arg;
+ for (var i = 1; i < arguments.length; i++) {
+ result += arguments[i] + arguments[i];
+ }
+ return result;
+ }
+
+ ////
+ // This test exercises NPN_InvokeDefault using the test plugin's
+ // npnInvokeDefaultTest method. This method invokes an object
+ // with a single parameter, and returns the result of the invocation.
+ // The test list below is used to drive the tests. Each member of the
+ // array contains three members: the object to invoke, an argument to
+ // invoke it with, and the expected result of the invocation.
+ //
+ var tests = [
+ // Number object
+ ["Number", 3, 3],
+ ["Number", "3", 3],
+ ["Number", "0x20", 32],
+ ["Number", "three", Number.NaN],
+ ["Number", "1e+3", 1000],
+ ["Number", 5.6612, 5.6612],
+ // Array object
+ ["Array", 3, Array(3)],
+ // Boolean object
+ ["Boolean", 0, false],
+ ["Boolean", null, false],
+ ["Boolean", "", false],
+ ["Boolean", false, false],
+ ["Boolean", true, true],
+ ["Boolean", "true", true],
+ ["Boolean", "false", true],
+ ["Boolean", new Boolean(false), true],
+ ["Boolean", { "value": false }, true],
+ // Function object
+ ["Function", "return 3", Function("return 3")],
+ ["Function", "window.alert('test')", Function("window.alert('test')")],
+ // Object object
+ ["Object", undefined, Object()],
+ ["Object", null, Object()],
+ ["Object", true, new Boolean(true)],
+ ["Object", Boolean(), new Boolean(false)],
+ ["Object", "a string", new String("a string")],
+ ["Object", 3.14, new Number(3.14)],
+ ["Object", { "key1": "test", "key2": 15 }, { "key1": "test", "key2": 15 }],
+ ["Object", [1, 3, 5, 7, 9, 11, 13, 17], [1, 3, 5, 7, 9, 11, 13, 17]],
+ // RegExp object
+ ["RegExp", "...", RegExp("...")],
+ // String object
+ ["String", "testing", "testing"],
+ ["String", "test\u0020me", "test me"],
+ ["String", "314", "314"],
+ ["String", "true", "true"],
+ ["String", "null", "null"],
+ ["String", "2 + 2", String("2 + 2")],
+ ["String", ""],
+ // global functions
+ ["testMe", 3, 6],
+ ["testMe", "string", [1,2], "stringstring1,21,2"],
+ ["testMe", "me", "meme"],
+ ["testMe", undefined, Number.NaN],
+ ["testMe", [1, 2], "1,21,2"],
+ ["testMe", 3, 4, 14],
+ ["isNaN", "junk", true],
+ ["parseInt", "156", 156],
+ ["encodeURI", "a = b", "a%20=%20b"],
+ ];
+
+ function runTests() {
+ var plugin = document.getElementById("plugin1");
+
+ // Test calling NPN_InvokeDefault from within plugin code.
+ for (var test of tests) {
+ var result;
+ var expected = test[test.length - 1];
+ switch (test.length) {
+ case 2:
+ result = plugin.npnInvokeDefaultTest(test[0]);
+ break;
+ case 3:
+ result = plugin.npnInvokeDefaultTest(test[0], test[1]);
+ break;
+ case 4:
+ result = plugin.npnInvokeDefaultTest(test[0], test[1], test[2]);
+ break;
+ }
+ // serialize the two values for easy
+ var json_expected = JSON.stringify(expected);
+ var json_result = JSON.stringify(result);
+ if (typeof(result) == "function")
+ json_result = result.toString();
+ if (typeof(test[2]) == "function")
+ json_expected = expected.toString();
+ is(json_result, json_expected,
+ "npnInvokeDefault returned an unexpected value");
+ is(typeof(result), typeof(expected),
+ "npnInvokeDefaultTest return value was of unexpected type");
+ var success = (json_result == json_expected &&
+ typeof(result) == typeof(expected));
+ $("verbose").appendChild(
+ createEl('span', null, ((success ? "pass" : "fail") + ": " + test[0] + "(")));
+ for (var i = 1; i < test.length - 1; i++) {
+ $("verbose").appendChild(
+ createEl('span', null, (JSON.stringify(test[i]) + (i < test.length - 2 ? "," : ""))));
+ }
+ $("verbose").appendChild(
+ createEl('span', null, (") == " + json_result + "(" +
+ typeof(result) + "), expected " + json_expected + "(" +
+ typeof(expected) + ")")));
+ $("verbose").appendChild(createEl('br'));
+ }
+
+ // Test calling the invokedefault method of plugin-defined object
+ is(plugin(), "Test Plug-in",
+ "calling NPN_InvokeDefault on plugin-defined Object doesn't work");
+ is(plugin(1), "Test Plug-in;1",
+ "calling NPN_InvokeDefault on plugin-defined Object doesn't work");
+ is(plugin("test"), "Test Plug-in;test",
+ "calling NPN_InvokeDefault on plugin-defined Object doesn't work");
+ is(plugin(undefined, -1, null), "Test Plug-in;undefined;-1;null",
+ "calling NPN_InvokeDefault on plugin-defined Object doesn't work");
+
+ SimpleTest.finish();
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="plugin1" type="application/x-test" width="400" height="100">
+ </embed>
+
+ <div id="verbose">
+ </div>
+ </body>
+ </html>
diff --git a/dom/plugins/test/mochitest/test_object.html b/dom/plugins/test/mochitest/test_object.html
new file mode 100644
index 0000000000..093333ea5e
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_object.html
@@ -0,0 +1,510 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Plugin instantiation</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/SpecialPowers.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <meta charset="utf-8">
+ <body onload="onLoad()">
+ <script class="testbody" type="text/javascript;version=1.8">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
+
+ // This can go away once embed also is on WebIDL
+ let OBJLC = SpecialPowers.Ci.nsIObjectLoadingContent;
+
+ // Use string modes in this test to make the test easier to read/debug.
+ // nsIObjectLoadingContent refers to this as "type", but I am using "mode"
+ // in the test to avoid confusing with content-type.
+ let prettyModes = {};
+ prettyModes[OBJLC.TYPE_LOADING] = "loading";
+ prettyModes[OBJLC.TYPE_IMAGE] = "image";
+ prettyModes[OBJLC.TYPE_PLUGIN] = "plugin";
+ prettyModes[OBJLC.TYPE_DOCUMENT] = "document";
+ prettyModes[OBJLC.TYPE_NULL] = "none";
+
+ let body = document.body;
+ // A single-pixel white png
+ let testPNG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AoIFiETNqbNRQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAACklEQVQIHWP4DwABAQEANl9ngAAAAABJRU5ErkJggg==';
+ // An empty, but valid, SVG
+ let testSVG = 'data:image/svg+xml,<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"></svg>';
+ // executeSoon wrapper to count pending callbacks
+ let pendingCalls = 0;
+ let afterPendingCalls = false;
+
+ function runWhenDone(func) {
+ if (!pendingCalls)
+ func();
+ else
+ afterPendingCalls = func;
+ }
+ function runSoon(func) {
+ pendingCalls++;
+ SimpleTest.executeSoon(function() {
+ func();
+ if (--pendingCalls < 1 && afterPendingCalls)
+ afterPendingCalls();
+ });
+ }
+ function src(obj, state, uri) {
+ // If we have a running plugin, src changing should always throw it out,
+ // even if it causes us to load the same plugin again.
+ if (uri && runningPlugin(obj, state)) {
+ if (!state.oldPlugins)
+ state.oldPlugins = [];
+ try {
+ state.oldPlugins.push(obj.getObjectValue());
+ } catch (e) {
+ ok(false, "Running plugin but cannot call getObjectValue?");
+ }
+ }
+
+ var srcattr;
+ if (state.tagName == "object")
+ srcattr = "data";
+ else if (state.tagName == "embed")
+ srcattr = "src";
+ else
+ ok(false, "Internal test fail: Why are we setting the src of an applet");
+
+ // Plugins should always go away immediately on src/data change
+ state.initialPlugin = false;
+ if (uri === null) {
+ removeAttr(obj, srcattr);
+ // TODO Bug 767631 - we don't trigger loadObject on UnsetAttr :(
+ forceReload(obj, state);
+ } else {
+ setAttr(obj, srcattr, uri);
+ }
+ }
+ // We have to be careful not to reach past the nsObjectLoadingContent
+ // prototype to touch generic element attributes, as this will try to
+ // spawn the plugin, breaking our ability to test for that.
+ function getAttr(obj, attr) {
+ return document.body.constructor.prototype.getAttribute.call(obj, attr);
+ }
+ function setAttr(obj, attr, val) {
+ return document.body.constructor.prototype.setAttribute.call(obj, attr, val);
+ }
+ function hasAttr(obj, attr) {
+ return document.body.constructor.prototype.hasAttribute.call(obj, attr);
+ }
+ function removeAttr(obj, attr) {
+ return document.body.constructor.prototype.removeAttribute.call(obj, attr);
+ }
+ function setDisplayed(obj, display) {
+ if (display)
+ removeAttr(obj, 'style');
+ else
+ setAttr(obj, 'style', "display: none;");
+ }
+ function displayed(obj) {
+ // Hacky, but that's all we use style for.
+ return !hasAttr(obj, 'style');
+ }
+ function actualType(obj, state) {
+ return state.getActualType.call(obj);
+ }
+ function getMode(obj, state) {
+ return prettyModes[state.getDisplayedType.call(obj)];
+ }
+ function runningPlugin(obj, state) {
+ return state.getHasRunningPlugin.call(obj);
+ }
+
+ // TODO this is hacky and might hide some failures, but is needed until
+ // Bug 767635 lands -- which itself will probably mean tweaking this test.
+ function forceReload(obj, state) {
+ let attr;
+ if (state.tagName == "object")
+ attr = "data";
+ else if (state.tagName == "embed")
+ attr = "src";
+
+ if (attr && hasAttr(obj, attr)) {
+ src(obj, state, getAttr(obj, attr));
+ } else if (body.contains(obj)) {
+ body.appendChild(obj);
+ } else {
+ // Out of document nodes without data attributes simply can't be
+ // reloaded currently. Bug 767635
+ }
+ };
+
+ // Make a list of combinations of sub-lists, e.g.:
+ // [ [a, b], [c, d] ]
+ // ->
+ // [ [a, c], [a, d], [b, c], [b, d] ]
+ function eachList() {
+ let all = [];
+ if (!arguments.length)
+ return all;
+ let list = Array.prototype.slice.call(arguments, 0);
+ for (let c of list[0]) {
+ if (list.length > 1) {
+ for (let x of eachList.apply(this,list.slice(1))) {
+ all.push((c.length ? [c] : []).concat(x));
+ }
+ } else if (c.length) {
+ all.push([c]);
+ }
+ }
+ return all;
+ }
+
+ let states = {
+ svg: function(obj, state) {
+ removeAttr(obj, "type");
+ src(obj, state, testSVG);
+ state.noChannel = false;
+ state.expectedType = "image/svg";
+ // SVGs are actually image-like subdocuments
+ state.expectedMode = "document";
+ },
+ image: function(obj, state) {
+ removeAttr(obj, "type");
+ src(obj, state, testPNG);
+ state.noChannel = false;
+ state.expectedMode = "image";
+ state.expectedType = "image/png";
+ },
+ plugin: function(obj, state) {
+ removeAttr(obj, "type");
+ src(obj, state, "data:application/x-test,foo");
+ state.noChannel = false;
+ state.expectedType = "application/x-test";
+ state.expectedMode = "plugin";
+ },
+ pluginExtension: function(obj, state) {
+ src(obj, state, "./fake_plugin.tst");
+ state.expectedMode = "plugin";
+ state.pluginExtension = true;
+ state.noChannel = false;
+ },
+ document: function(obj, state) {
+ removeAttr(obj, "type");
+ src(obj, state, "data:text/plain,I am a document");
+ state.noChannel = false;
+ state.expectedType = "text/plain";
+ state.expectedMode = "document";
+ },
+ fallback: function(obj, state) {
+ removeAttr(obj, "type");
+ state.expectedType = "application/x-unknown";
+ state.expectedMode = "none";
+ state.noChannel = true;
+ src(obj, state, null);
+ },
+ addToDoc: function(obj, state) {
+ body.appendChild(obj);
+ },
+ removeFromDoc: function(obj, state) {
+ if (body.contains(obj))
+ body.removeChild(obj);
+ },
+ // Set the proper type
+ setType: function(obj, state) {
+ if (state.expectedType) {
+ state.badType = false;
+ setAttr(obj, 'type', state.expectedType);
+ forceReload(obj, state);
+ }
+ },
+ // Set an improper type
+ setWrongType: function(obj, state) {
+ // This should break no-channel-plugins but nothing else
+ state.badType = true;
+ setAttr(obj, 'type', "application/x-unknown");
+ forceReload(obj, state);
+ },
+ // Set a plugin type
+ setPluginType: function(obj, state) {
+ // If an object/embed has a type set to a plugin type, it should not
+ // use the channel type.
+ state.badType = false;
+ setAttr(obj, 'type', 'application/x-test');
+ state.expectedType = "application/x-test";
+ state.expectedMode = "plugin";
+ forceReload(obj, state);
+ },
+ noChannel: function(obj, state) {
+ src(obj, state, null);
+ state.noChannel = true;
+ state.pluginExtension = false;
+ },
+ displayNone: function(obj, state) {
+ setDisplayed(obj, false);
+ },
+ displayInherit: function(obj, state) {
+ setDisplayed(obj, true);
+ }
+ };
+
+
+ function testObject(obj, state) {
+ // If our test combination both sets noChannel but no explicit type
+ // it shouldn't load ever.
+ let expectedMode = state.expectedMode;
+ let actualMode = getMode(obj, state);
+
+ if (state.noChannel && !getAttr(obj, 'type')) {
+ // Some combinations of test both set no type and no channel. This is
+ // worth testing with the various combinations, but shouldn't load.
+ expectedMode = "none";
+ }
+
+ // Embed tags should always try to load a plugin by type or extension
+ // before falling back to opening a channel. See bug 803159
+ if (state.tagName == "embed" &&
+ (getAttr(obj, 'type') == "application/x-test" || state.pluginExtension)) {
+ state.noChannel = true;
+ }
+
+ // with state.loading, unless we're loading with no channel, these types
+ // should still be in loading state pending a channel.
+ if (state.loading && (expectedMode == "image" || expectedMode == "document" ||
+ (expectedMode == "plugin" && !state.initialPlugin && !state.noChannel))) {
+ expectedMode = "loading";
+ }
+
+ // With the exception of plugins with a proper type, nothing should
+ // load without a channel
+ if (state.noChannel && (expectedMode != "plugin" || state.badType) &&
+ body.contains(obj)) {
+ expectedMode = "none";
+ }
+
+ // embed tags should reject documents, except for SVG images which
+ // render as such
+ if (state.tagName == "embed" && expectedMode == "document" &&
+ actualType(obj, state) != "image/svg+xml") {
+ expectedMode = "none";
+ }
+
+ // Embeds with a plugin type should skip opening a channel prior to
+ // loading, taking only type into account.
+ if (state.tagName == 'embed' && getAttr(obj, 'type') == 'application/x-test' &&
+ body.contains(obj)) {
+ expectedMode = "plugin";
+ }
+
+ if (!body.contains(obj)
+ && (!state.loading || expectedMode != "image")
+ && (!state.initialPlugin || expectedMode != "plugin")) {
+ // Images are handled by nsIImageLoadingContent so we dont track
+ // their state change as they're detached and reattached. All other
+ // types switch to state "loading", and are completely unloaded
+ expectedMode = "loading";
+ }
+
+ is(actualMode, expectedMode, "check loaded mode");
+
+ // If we're a plugin, check that we spawned successfully. state.loading
+ // is set if we haven't had an event loop since applying state, in which
+ // case the plugin would not have stopped yet if it was initially a
+ // plugin.
+ let shouldBeSpawnable = expectedMode == "plugin" && displayed(obj);
+ let shouldSpawn = shouldBeSpawnable && (!state.loading || state.initialPlugin);
+ let didSpawn = runningPlugin(obj, state);
+ is(didSpawn, !!shouldSpawn, "check plugin spawned is " + !!shouldSpawn);
+
+ // If we are a plugin, scripting should work. If we're not spawned we
+ // should spawn synchronously.
+ let scripted = false;
+ try {
+ let x = obj.getObjectValue();
+ scripted = true;
+ } catch(e) {}
+ is(scripted, shouldBeSpawnable, "check plugin scriptability");
+
+ // If this tag previously had other spawned plugins, make sure it
+ // respawned between then and now
+ if (state.oldPlugins && didSpawn) {
+ let didRespawn = false;
+ for (let oldp of state.oldPlugins) {
+ // If this returns false or throws, it's not the same plugin
+ try {
+ didRespawn = !obj.checkObjectValue(oldp);
+ } catch (e) {
+ didRespawn = true;
+ }
+ }
+ is(didRespawn, true, "Plugin should have re-spawned since old state ("+state.oldPlugins.length+")");
+ }
+ }
+
+ let total = 0;
+ let test_modes = {
+ // Just apply from_state then to_state
+ "immediate": function(obj, from_state, to_state, state) {
+ for (let from of from_state)
+ states[from](obj, state);
+ for (let to of to_state)
+ states[to](obj, state);
+
+ // We don't spin the event loop between applying to_state and
+ // running tests, so some types are still loading
+ state.loading = true;
+ info("["+(++total)+"] Testing [ " + from_state + " ] -> [ " + to_state + " ] / " + state.tagName + " / immediate");
+ testObject(obj, state);
+
+ if (body.contains(obj))
+ body.removeChild(obj);
+
+ },
+ // Apply states, spin event loop, run tests.
+ "cycle": function(obj, from_state, to_state, state) {
+ for (let from of from_state)
+ states[from](obj, state);
+ for (let to of to_state)
+ states[to](obj, state);
+ // Because re-appending to the document creates a script blocker, but
+ // plugins spawn asynchronously, we need to return to the event loop
+ // twice to ensure the plugin has been given a chance to lazily spawn.
+ runSoon(function() { runSoon(function() {
+ info("["+(++total)+"] Testing [ " + from_state + " ] -> [ " + to_state + " ] / " + state.tagName + " / cycle");
+ testObject(obj, state);
+
+ if (body.contains(obj))
+ body.removeChild(obj);
+ }); });
+ },
+ // Apply initial state, spin event loop, apply final state, spin event
+ // loop again.
+ "cycleboth": function(obj, from_state, to_state, state) {
+ for (let from of from_state) {
+ states[from](obj, state);
+ }
+ runSoon(function() {
+ for (let to of to_state) {
+ states[to](obj, state);
+ }
+ // Because re-appending to the document creates a script blocker,
+ // but plugins spawn asynchronously, we need to return to the event
+ // loop twice to ensure the plugin has been given a chance to lazily
+ // spawn.
+ runSoon(function() { runSoon(function() {
+ info("["+(++total)+"] Testing [ " + from_state + " ] -> [ " + to_state + " ] / " + state.tagName + " / cycleboth");
+ testObject(obj, state);
+
+ if (body.contains(obj))
+ body.removeChild(obj);
+ }); });
+ });
+ },
+ // Apply initial state, spin event loop, apply later state, test
+ // immediately
+ "cyclefirst": function(obj, from_state, to_state, state) {
+ for (let from of from_state) {
+ states[from](obj, state);
+ }
+ runSoon(function() {
+ state.initialPlugin = runningPlugin(obj, state);
+ for (let to of to_state) {
+ states[to](obj, state);
+ }
+ info("["+(++total)+"] Testing [ " + from_state + " ] -> [ " + to_state + " ] / " + state.tagName + " / cyclefirst");
+ // We don't spin the event loop between applying to_state and
+ // running tests, so some types are still loading
+ state.loading = true;
+ testObject(obj, state);
+
+ if (body.contains(obj))
+ body.removeChild(obj);
+ });
+ },
+ };
+
+ function test(testdat) {
+ // FIXME bug 1291854: Change back to lets when the test is fixed.
+ for (var from_state of testdat['from_states']) {
+ for (var to_state of testdat['to_states']) {
+ for (var mode of testdat['test_modes']) {
+ for (var type of testdat['tag_types']) {
+ runSoon(function () {
+ let obj = document.createElement(type);
+ obj.width = 1; obj.height = 1;
+ let state = {};
+ state.noChannel = true;
+ state.tagName = type;
+ // Part of the test checks whether a plugin spawned or not,
+ // but touching the object prototype will attempt to
+ // synchronously spawn a plugin! We use this terrible hack to
+ // get a privileged getter for the attributes we want to touch
+ // prior to applying any attributes.
+ // TODO when embed goes away we wont need to check for
+ // QueryInterface any longer.
+ var lookup_on = obj.QueryInterface ? obj.QueryInterface(OBJLC): obj;
+ state.getDisplayedType = SpecialPowers.do_lookupGetter(lookup_on, 'displayedType');
+ state.getHasRunningPlugin = SpecialPowers.do_lookupGetter(lookup_on, 'hasRunningPlugin');
+ state.getActualType = SpecialPowers.do_lookupGetter(lookup_on, 'actualType');
+ test_modes[mode](obj, from_state, to_state, state);
+ });
+ }
+ }
+ }
+ }
+ }
+
+ function onLoad() {
+ // Generic tests
+ test({
+ 'tag_types': [ 'embed', 'object' ],
+ // In all three modes
+ 'test_modes': [ 'immediate', 'cycle', 'cyclefirst', 'cycleboth' ],
+ // Starting from a blank tag in and out of the document, a loading
+ // plugin, and no-channel plugin (initial types only really have
+ // odd cases with plugins)
+ 'from_states': [
+ [ 'addToDoc' ],
+ [ 'plugin' ],
+ [ 'plugin', 'addToDoc' ],
+ [ 'plugin', 'noChannel', 'setType', 'addToDoc' ],
+ [],
+ ],
+ // To various combinations of loaded objects
+ 'to_states': eachList(
+ [ 'svg', 'image', 'plugin', 'document', '' ],
+ [ 'setType', 'setWrongType', 'setPluginType', '' ],
+ [ 'noChannel', '' ],
+ [ 'displayNone', 'displayInherit', '' ]
+ )});
+ // Special case test for embed tags with plugin-by-extension
+ // TODO object tags should be tested here too -- they have slightly
+ // different behavior, but waiting on a file load requires a loaded
+ // event handler and wont work with just our event loop spinning.
+ test({
+ 'tag_types': [ 'embed' ],
+ 'test_modes': [ 'immediate', 'cyclefirst', 'cycle', 'cycleboth' ],
+ 'from_states': eachList(
+ [ 'svg', 'plugin', 'image', 'document' ],
+ [ 'addToDoc' ]
+ ),
+ // Set extension along with valid ty
+ 'to_states': [
+ [ 'pluginExtension' ]
+ ]});
+ // Test plugin add/remove from document with adding/removing frame, with
+ // and without a channel.
+ test({
+ 'tag_types': [ 'embed', 'object' ], // Ideally we'd test object too, but this gets exponentially long.
+ 'test_modes': [ 'immediate', 'cyclefirst', 'cycle' ],
+ 'from_states': [ [ 'displayNone', 'plugin', 'addToDoc' ],
+ [ 'displayNone', 'plugin', 'noChannel', 'addToDoc' ],
+ [ 'plugin', 'noChannel', 'addToDoc' ],
+ [ 'plugin', 'noChannel' ] ],
+ 'to_states': eachList(
+ [ 'displayNone', '' ],
+ [ 'removeFromDoc' ],
+ [ 'image', 'displayNone', '' ],
+ [ 'image', 'displayNone', '' ],
+ [ 'addToDoc' ],
+ [ 'displayInherit' ]
+ )});
+ runWhenDone(() => SimpleTest.finish());
+ }
+ </script>
diff --git a/dom/plugins/test/mochitest/test_painting.html b/dom/plugins/test/mochitest/test_painting.html
new file mode 100644
index 0000000000..08ebd46757
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_painting.html
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for windowless plugin invalidation and expose events in clips</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>
+ div#container {
+ position: relative;
+ height: 30px;
+ background: blue;
+ }
+ div#clip {
+ overflow:hidden;
+ position:absolute;
+ left: 10.3px;
+ top: 9.7px;
+ width: 10px;
+ height: 0px;
+ background: red;
+ }
+ embed {
+ position:absolute;
+ }
+ embed#paint-waiter {
+ top: 0px;
+ left: 0px;
+ width: 1px;
+ height: 0px;
+ }
+ embed#clipped {
+ left: -5.3px;
+ top: -4.7px;
+ width: 20px;
+ height: 20px;
+ }
+ </style>
+</head>
+<body onload="initialize()">
+
+<script type="application/javascript" src="plugin-utils.js"></script>
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+var paint_waiter;
+var clip;
+var clipped;
+
+function initialize() {
+ paint_waiter = document.getElementById("paint-waiter");
+ clip = document.getElementById("clip");
+ clipped = document.getElementById("clipped");
+
+ waitForPaint(show);
+}
+
+function show() {
+ paintCountIs(clipped, 0, "fully clipped plugin not painted");
+
+ clip.style.height = "10px";
+
+ // Capturing an image (as in a reftest) would force a repaint and use
+ // different paths for the image surface, so instead check the plugin's
+ // paint count.
+ waitForPaint(invalidate);
+}
+
+function invalidate() {
+ paintCountIs(clipped, 1, "partially clipped plugin painted once");
+
+ clipped.setColor("FF00FF00"); // plugin invalidates
+
+ waitForPaint(done);
+}
+
+function done() {
+ paintCountIs(clipped, 2, "painted after invalidate");
+
+ SimpleTest.finish();
+}
+
+function waitForPaint(func) {
+ paint_waiter.last_paint_count = paint_waiter.getPaintCount();
+ // Ensure the waiter has had a style change, so that this will
+ // change its size and cause a paint.
+ paint_waiter.style.backgroundColor = paint_waiter.style.backgroundColor == "blue" ? "yellow" : "blue";
+ var flush = paint_waiter.offsetHeight;
+ paint_waiter.style.height = "1px";
+ waitForPaintHelper(func);
+}
+
+function waitForPaintHelper(func) {
+ if (paint_waiter.getPaintCount() != paint_waiter.last_paint_count) {
+ // hide the paint waiter
+ paint_waiter.style.height = "0px";
+ setTimeout(func, 0);
+ return;
+ }
+ setTimeout(function() { waitForPaintHelper(func); }, 1000);
+}
+
+</script>
+
+<p id="display"></p>
+<div id="container">
+ <embed id="paint-waiter" type="application/x-test"/>
+ <div id="clip">
+ <embed id="clipped" type="application/x-test"
+ drawmode="solid" color="FF808080"/>
+ </div>
+</div>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_plugin_scroll_invalidation.html b/dom/plugins/test/mochitest/test_plugin_scroll_invalidation.html
new file mode 100644
index 0000000000..65ea3498ac
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_plugin_scroll_invalidation.html
@@ -0,0 +1,109 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for plugin child widgets not being invalidated by scrolling</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="initialize()">
+<script type="application/javascript">
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED,
+ "Test Plug-in");
+</script>
+
+<p id="display">
+ <iframe id="i" src="plugin_scroll_invalidation.html"
+ width="50" height="50" scrolling="no"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var scrolling;
+var scrolling_plugins = [];
+var paint_waiter;
+var last_paint_counts;
+
+function initialize() {
+ scrolling = document.getElementById("i").contentWindow;
+ scrolling_plugins = scrolling.document.querySelectorAll("embed.scrolling");
+ paint_waiter = scrolling.document.getElementById("paint-waiter");
+
+ scrolling.scrollTo(50, 45);
+
+ is(paint_waiter.getPaintCount(), 0, "zero-sized plugin not painted");
+
+ waitForPaint(scrollAround);
+}
+
+function scrollAround() {
+ var paints = getPaintCounts();
+
+ for (var i = 0; i < paints.length; ++i) {
+ isnot(paints[i], 0, "embed " + scrolling_plugins[i].id + " is painted");
+ }
+
+ last_paint_counts = paints;
+
+ scrolling.scrollBy(-5, 5);
+ scrolling.scrollBy(5, 5);
+ scrolling.scrollBy(5, -5);
+ scrolling.scrollBy(-5, -5);
+
+ scrolling.scrollTo(45, 45);
+ scrolling.scrollBy(10, 0);
+ scrolling.scrollBy(0, 10);
+ scrolling.scrollBy(-10, 0);
+ scrolling.scrollBy(0, -10);
+
+ waitForPaint(done);
+}
+
+function done() {
+ var paints = getPaintCounts();
+ for (var i = 0; i < paints.length; ++i) {
+ is(paints[i], last_paint_counts[i], "embed " + scrolling_plugins[i].id + " is not painted on scroll");
+ }
+ SimpleTest.finish();
+}
+
+// Waits for the paint_waiter plugin to be repainted and then
+// calls 'func' to continue.
+function waitForPaint(func) {
+ paint_waiter.last_paint_count = paint_waiter.getPaintCount();
+
+ paint_waiter.style.left = scrolling.scrollX + "px";
+ paint_waiter.style.top = scrolling.scrollY + "px";
+
+ // Fiddle with the style in a way that should force some repainting
+ paint_waiter.style.width =
+ (paint_waiter.getBoundingClientRect().width + 1) + "px";
+ paint_waiter.style.height = "1px";
+
+ function waitForPaintHelper() {
+ if (paint_waiter.getPaintCount() != paint_waiter.last_paint_count) {
+ setTimeout(func, 0);
+ return;
+ }
+ setTimeout(waitForPaintHelper, 0);
+ }
+ waitForPaintHelper();
+}
+
+function getPaintCounts() {
+ var result = [];
+ for (var i = 0; i < scrolling_plugins.length; ++i) {
+ result[i] = scrolling_plugins[i].getPaintCount();
+ }
+ return result;
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_plugin_scroll_painting.html b/dom/plugins/test/mochitest/test_plugin_scroll_painting.html
new file mode 100644
index 0000000000..9626156b6a
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_plugin_scroll_painting.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that scrolling a windowless plugin doesn't force us to repaint it</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTest()">
+<p id="display"></p>
+ <embed id="plugin" type="application/x-test" style="width:50px; height:10px; margin-top:20px;"></embed>
+</div>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var container = document.documentElement;
+container.scrollTop = 0;
+var plugin = document.getElementById("plugin");
+var pluginTop;
+var beforeScrollPaintCount;
+
+function waitForScroll() {
+ if (plugin.getEdge(1) >= pluginTop) {
+ setTimeout(waitForScroll, 0);
+ return;
+ }
+
+ is(plugin.getPaintCount(), beforeScrollPaintCount, "plugin should not paint due to scrolling");
+ SimpleTest.finish();
+}
+
+function waitForInitialScroll() {
+ if (plugin.getEdge(1) >= pluginTop) {
+ setTimeout(waitForInitialScroll, 0);
+ return;
+ }
+
+ pluginTop = plugin.getEdge(1);
+ beforeScrollPaintCount = plugin.getPaintCount();
+ container.scrollTop = 20;
+ waitForScroll();
+}
+
+function runTest() {
+ document.body.offsetTop;
+ pluginTop = plugin.getEdge(1);
+ container.scrollTop = 10;
+ waitForInitialScroll();
+}
+</script>
+
+<div style="height:4000px;"></div>
+
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_plugin_tag_clicktoplay.html b/dom/plugins/test/mochitest/test_plugin_tag_clicktoplay.html
new file mode 100644
index 0000000000..0679e6795e
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_plugin_tag_clicktoplay.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta><charset="utf-8"/>
+ <title>Test Modifying Plugin click-to-play Flag</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ </head>
+ <body>
+ <script class="testbody" type="application/javascript">
+ Components.utils.import("resource://gre/modules/Services.jsm");
+ Services.prefs.setBoolPref("plugins.click_to_play", true);
+ var pluginHost = Components.classes["@mozilla.org/plugin/host;1"]
+ .getService(Components.interfaces.nsIPluginHost);
+
+ var testPlugin = getTestPlugin();
+ var secondTestPlugin = getTestPlugin("Second Test Plug-in");
+ ok(testPlugin, "Should have Test Plug-in");
+ ok(secondTestPlugin, "Should have Second Test Plug-in");
+
+ // make sure both start off as click-to-play
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
+
+ testPlugin.enabledState = Components.interfaces.nsIPluginTag.STATE_ENABLED;
+ is(pluginHost.getStateForType("application/x-test"), Components.interfaces.nsIPluginTag.STATE_ENABLED, "click-to-play should be off for Test Plug-in now");
+ is(pluginHost.getStateForType("application/x-second-test"), Components.interfaces.nsIPluginTag.STATE_CLICKTOPLAY, "click-to-play should still be on for the Second Test Plug-in");
+
+ testPlugin.enabledState = Components.interfaces.nsIPluginTag.STATE_CLICKTOPLAY;
+ is(pluginHost.getStateForType("application/x-test"), Components.interfaces.nsIPluginTag.STATE_CLICKTOPLAY, "click-to-play should be on for Test Plug-in now");
+
+ Services.prefs.clearUserPref("plugins.click_to_play");
+ </script>
+ </body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_pluginstream_3rdparty.html b/dom/plugins/test/mochitest/test_pluginstream_3rdparty.html
new file mode 100644
index 0000000000..aa7dbb5a1c
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_3rdparty.html
@@ -0,0 +1,76 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>NPAPI NPN_GetURL NPStream Test</title>
+ <meta charset=UTF-8>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="pluginstream.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe"></iframe>
+
+ <script>
+ /**
+ * Tests that we still properly do or don't send cookies for requests from
+ * plugins when the user has disabled 3rd-party cookies. See
+ * pluginstream.js where we verify that we get the same content as for XHR
+ * requests.
+ */
+ SimpleTest.waitForExplicitFinish();
+ function get_embed_elt() {
+ var e = document.createElement("embed");
+ e.setAttribute("streammode", "normal");
+ e.setAttribute("streamchunksize", "1024");
+ e.setAttribute("frame", "testframe");
+ e.setAttribute("id", "embedtest");
+ e.setAttribute("style", "width: 400px; height: 100px;");
+ e.setAttribute("type", "application/x-test");
+ return e;
+ }
+
+ function* test_runner() {
+ function create_embed(host) {
+ var e = get_embed_elt();
+
+ const url =
+ `http://${host}/tests/dom/plugins/test/mochitest/file_checkcookie.sjs`;
+ e.setAttribute('geturl', url);
+ document.body.appendChild(e);
+
+ return new Promise(resolve => {
+ $('testframe').addEventListener("load", function loaded() {
+ $('testframe').removeEventListener("load", loaded);
+ resolve();
+ });
+ });
+ }
+
+ // Same origin
+ yield create_embed("mochi.test:8888");
+ yield create_embed("example.org");
+ }
+
+ document.cookie = "found=a_cookie";
+ var example_iframe = document.createElement("iframe");
+ example_iframe.src = "http://example.org/tests/dom/plugins/test/mochitest/file_setcookie.html";
+ example_iframe.addEventListener("load", () => {
+ $('testframe').addEventListener("load", () => frameLoaded(false, true));
+ SpecialPowers.pushPrefEnv({ set: [[ 'network.cookie.cookieBehavior', 1 ]] },
+ () => (spawn_task(test_runner).then(SimpleTest.finish)));
+ });
+ document.body.appendChild(example_iframe);
+ </script>
+ </body>
+ </html>
diff --git a/dom/plugins/test/mochitest/test_pluginstream_asfile.html b/dom/plugins/test/mochitest/test_pluginstream_asfile.html
new file mode 100644
index 0000000000..b021a74ce4
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_asfile.html
@@ -0,0 +1,33 @@
+<html>
+<head>
+ <title>NPAPI NP_ASFILE NPStream Test</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="pluginstream.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"></iframe>
+
+ <!--
+ - Similar to the above tests, but NPP_NewStream sets the stream mode
+ - to NP_ASFILE. In this test, stream data is written in a series of
+ - NPP_Write calls, as per NP_NORMAL, and also written to a file whose
+ - path is passed to NPP_StreamAsFile. The plugin compares the file
+ - and the stream to verify they're indentical, then passes the stream
+ - back to the browser via NPN_GetURL as above.
+ -->
+ <embed src="loremipsum_file.txt" streammode="asfile"
+ frame="testframe"
+ id="embedtest" style="width: 400px; height: 100px;"
+ type="application/x-test"></embed>
+ </body>
+ </html>
diff --git a/dom/plugins/test/mochitest/test_pluginstream_asfileonly.html b/dom/plugins/test/mochitest/test_pluginstream_asfileonly.html
new file mode 100644
index 0000000000..96c9b85dce
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_asfileonly.html
@@ -0,0 +1,30 @@
+<html>
+<head>
+ <title>NPAPI NP_ASFILEONLY NPStream Test</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="pluginstream.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"></iframe>
+
+ <!--
+ - Similar to above, but NPP_NewStream sets to the stream mode
+ - to NP_ASFILEONLY, so the entire stream is written to a file
+ - and the file is read by the plugin when NPP_StreamAsFile is called.
+ -->
+ <embed src="loremipsum_file.txt" streammode="asfileonly"
+ frame="testframe"
+ id="embedtest" style="width: 400px; height: 100px;"
+ type="application/x-test"></embed>
+ </body>
+ </html>
diff --git a/dom/plugins/test/mochitest/test_pluginstream_err.html b/dom/plugins/test/mochitest/test_pluginstream_err.html
new file mode 100644
index 0000000000..0ac2a5efc2
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_err.html
@@ -0,0 +1,165 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=517078
+
+Tests for plugin stream error conditions.
+-->
+<head>
+ <title>NPAPI Stream Error Tests</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runNextTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=517078">
+ Mozilla Bug 517078</a> - Plugin Stream Error Tests
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<div id="test">
+<script class="testbody" type="text/javascript">
+////
+// These tests verify that nothing "bad" happens when a plugin returns an
+// error from one of the NPP_ stream functions. "Bad" is defined here
+// as the plugin being terminated, or NPP_ stream functions being
+// called inappropriately by the browser after the plugin has returned
+// a stream error.
+//
+
+function $(id) { return document.getElementById(id); }
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+
+var tests = [
+ {
+ "src": "loremipsum.txt",
+ "streammode": "normal",
+ "functiontofail": "npp_newstream",
+ "failurecode": "1",
+ "frame": "testframe"
+ },
+ {
+ "src": "loremipsum.txt",
+ "streammode": "normal",
+ "functiontofail": "npp_newstream",
+ "failurecode": "3",
+ "frame": "testframe"
+ },
+ {
+ "src": "loremipsum.txt",
+ "streammode": "normal",
+ "functiontofail": "npp_newstream",
+ "failurecode": "5",
+ "frame": "testframe"
+ },
+ {
+ "geturl": "loremipsum.txt",
+ "streammode": "normal",
+ "functiontofail": "npp_newstream",
+ "failurecode": "1",
+ "frame": "testframe"
+ },
+ {
+ "src": "loremipsum.txt",
+ "streammode": "normal",
+ "functiontofail": "npp_write",
+ "frame": "testframe"
+ },
+ {
+ "src": "loremipsum.txt",
+ "streammode": "asfile",
+ "functiontofail": "npp_write",
+ "frame": "testframe"
+ },
+ {
+ "src": "loremipsum.txt",
+ "streammode": "normal",
+ "functiontofail": "npp_destroystream",
+ "failurecode": "1",
+ "frame": "testframe"
+ },
+];
+
+function iframeonload(evt) {
+ var contentLength = evt.target.contentDocument.body.innerHTML.length;
+ var plugin = gTestWindow.document.getElementById("embedtest");
+ var functionToFail = plugin.getAttribute("functiontofail");
+ if (contentLength > 0) {
+ is(evt.target.contentDocument.body.innerHTML, "pass",
+ "test frame has unexpected content");
+ setTimeout(function() {
+ // This verifies that the plugin hasn't been unloaded, and that
+ // no calls to NPP_ functions have been made unexpectedly.
+ is(plugin.getError(), "pass", "plugin reported an error");
+ gTestWindow.close();
+ setTimeout(runNextTest, 10);
+ }, functionToFail == "npp_newstream" ? 500 : 10);
+ }
+}
+
+var index = 0;
+var gTestWindow;
+function runNextTest() {
+ if (index == tests.length * 2) {
+ SimpleTest.finish();
+ return;
+ }
+
+ gTestWindow = window.open("plugin_window.html",
+ "",
+ "width=620,height=320");
+}
+
+function continueTest() {
+ // We run each test as an embed and an object, as their initial stream
+ // handling differs.
+ var tag = index % 2 ? "embed" : "object";
+ var test = tests[Math.floor(index / 2)];
+
+ var p = gTestWindow.document.createElement("p");
+ p.innerHTML = "Plugin Stream Test " + index;
+ gTestWindow.document.getElementById("test").appendChild(p);
+
+ if (test.frame) {
+ var iframe = gTestWindow.document.createElement("iframe");
+ iframe.name = test.frame;
+ iframe.onload = iframeonload;
+ gTestWindow.document.getElementById("test").appendChild(iframe);
+ }
+
+ var plugin = gTestWindow.document.createElement(tag);
+ plugin.setAttribute("id", "embedtest");
+ plugin.setAttribute("style", "width: 400px; height: 100px;");
+ plugin.setAttribute("type", "application/x-test");
+ for (var name in test) {
+ if (tag == "embed") {
+ plugin.setAttribute(name, test[name]);
+ } else if (name == "src") {
+ plugin.setAttribute("data", test[name]);
+ } else {
+ var param = document.createElement("param");
+ param.name = name;
+ param.value = test[name];
+ plugin.appendChild(param);
+ }
+ }
+ gTestWindow.document.getElementById("test").appendChild(plugin);
+
+ gTestWindow.document.getElementById("test")
+ .appendChild(document.createElement("br"));
+
+ index++;
+}
+
+</script>
+</div>
+</body>
+</html>
+
diff --git a/dom/plugins/test/mochitest/test_pluginstream_geturl.html b/dom/plugins/test/mochitest/test_pluginstream_geturl.html
new file mode 100644
index 0000000000..fe69427a42
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_geturl.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+ <title>NPAPI NPN_GetURL NPStream Test</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="pluginstream.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"></iframe>
+
+ <!--
+ - The plugin reports that data can be sent to
+ - it in 1024-byte chunks, and the stream is initiated by a call to
+ - NPN_GetURL.
+ -->
+ <embed geturl="loremipsum.txt" streammode="normal"
+ streamchunksize="1024" frame="testframe"
+ id="embedtest" style="width: 400px; height: 100px;"
+ type="application/x-test"></embed>
+ </body>
+ </html>
+ \ No newline at end of file
diff --git a/dom/plugins/test/mochitest/test_pluginstream_geturlnotify.html b/dom/plugins/test/mochitest/test_pluginstream_geturlnotify.html
new file mode 100644
index 0000000000..ee4c2b119d
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_geturlnotify.html
@@ -0,0 +1,30 @@
+<body>
+<head>
+ <title>NPAPI NPN_GetURLNotify Test</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="pluginstream.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"></iframe>
+
+ <!--
+ - The stream is requested by
+ - the plugin using NPN_GetURLNotify, and the plugin does not send the
+ - stream back to the browser until NPP_URLNotify is called.
+ -->
+ <embed geturlnotify="loremipsum.txt" streammode="normal"
+ streamchunksize="1024" frame="testframe"
+ id="embedtest" style="width: 400px; height: 100px;"
+ type="application/x-test"></embed>
+ </body>
+ </html>
diff --git a/dom/plugins/test/mochitest/test_pluginstream_newstream.html b/dom/plugins/test/mochitest/test_pluginstream_newstream.html
new file mode 100644
index 0000000000..3972fd7ed4
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_newstream.html
@@ -0,0 +1,32 @@
+<html>
+<head>
+ <title>NPAPI NPN_NewStream NPStream Test</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="pluginstream.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"></iframe>
+
+ <!--
+ - A stream is sent to the browser via NPP_NewStream, NP_NORMAL.
+ - When NPP_DestroyStream is called, the plugin sends the stream
+ - content back to the browser by calling NPN_NewStream and
+ - NPN_Write. The stream content should be displayed in the specified
+ - frame.
+ -->
+ <embed src="loremipsum.txt" streammode="normal"
+ newstream="true" frame="testframe"
+ id="embedtest" style="width: 400px; height: 100px;"
+ type="application/x-test"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_pluginstream_post.html b/dom/plugins/test/mochitest/test_pluginstream_post.html
new file mode 100644
index 0000000000..da12ff3b0e
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_post.html
@@ -0,0 +1,33 @@
+<html>
+<head>
+ <title>NPAPI NPN_PostURL NPStream Test</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="pluginstream.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"></iframe>
+
+ <!--
+ - In this test, a stream is sent to the plugin via NPP_NewStream,
+ - NP_NORMAL. When the stream is destroyed, the plugin posts the
+ - stream content to post.sjs via NPN_PostURL, with a frame specified
+ - to display the post's response. Post.sjs just reflects
+ - the body of the post back in the HTTP response, so the original
+ - stream content should end up being displayed in the frame.
+ -->
+ <embed src="loremipsum.txt" streammode="normal"
+ frame="testframe" posturl="post.sjs" postmode="frame"
+ id="embedtest" style="width: 400px; height: 100px;"
+ type="application/x-test"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_pluginstream_poststream.html b/dom/plugins/test/mochitest/test_pluginstream_poststream.html
new file mode 100644
index 0000000000..ed2e8ce2c7
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_poststream.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+ <title>NPAPI NPN_PostURL NPStream Test</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="pluginstream.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"></iframe>
+
+ <!--
+ - Same as test_pluginstream_post.html, except NULL is passed to NPN_PostURL
+ - as the frame parameter, so the HTTP response to the post is passed to the
+ - plugin via NPP_NewStream. Once this stream is received, it's displayed
+ - in a frame in the browser via a call to NPN_GetURL.
+ -->
+ <embed src="loremipsum.txt" streammode="normal"
+ frame="testframe" posturl="post.sjs" postmode="stream"
+ id="embedtest" style="width: 400px; height: 100px;"
+ type="application/x-test"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_pluginstream_referer.html b/dom/plugins/test/mochitest/test_pluginstream_referer.html
new file mode 100644
index 0000000000..e1b63fb959
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_referer.html
@@ -0,0 +1,55 @@
+<head>
+ <title>Do plugin stream requests send the Referer header correctly?</title>
+ <script type="application/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var pending = 3;
+ function testDone() {
+ --pending;
+ if (0 == pending)
+ SimpleTest.finish()
+ }
+
+ function runTests() {
+ var p = document.getElementById('plugin1');
+ var p2 = document.getElementById('plugin2');
+
+ ok(p.streamTest('plugin-stream-referer.sjs', false, null, null,
+ function(r, t) {
+ is(r, 0, "GET plugin-stream-referer.sjs");
+ is(t, "Referer found: " + window.location,
+ "GET Referer correct");
+ testDone();
+ }, null, true), "referer GET");
+
+ ok(p.streamTest('plugin-stream-referer.sjs', true, "Dummy Data", null,
+ function(r, t) {
+ is(r, 0, "POST plugin-stream-referer.sjs");
+ is(t, "No Referer found", "POST Referer absent");
+ testDone();
+ }, null, true), "referer POST");
+
+ ok(p2.streamTest('plugin-stream-referer.sjs', false, null, null,
+ function(r, t) {
+ is(r, 0, "GET plugin-stream-referer.sjs (2)");
+ var expectedreferer = String(window.location).replace("test_pluginstream_referer.html", "loremipsum.xtest");
+ is(t, "Referer found: " + expectedreferer,
+ "GET Referer correct with plugin src");
+ testDone();
+ }, null, true), "referer GET (2)");
+
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+ <embed id="plugin2" type="application/x-test" src="loremipsum.xtest" width="200" height="200"></embed>
diff --git a/dom/plugins/test/mochitest/test_pluginstream_seek.html b/dom/plugins/test/mochitest/test_pluginstream_seek.html
new file mode 100644
index 0000000000..6915a766e8
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_seek.html
@@ -0,0 +1,33 @@
+<body>
+<head>
+ <title>NPAPI Seekable NPStream Test</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="pluginstream.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"></iframe>
+
+ <!--
+ - Tests a seekable stream. Calls NPN_RequestRead with the specified
+ - range, and verifies that an NPP_Write call is made with the correct
+ - parameters, including the buffer data for the byte range. Once all
+ - calls to NPP_Write have finished, the plugin calls NPN_DestroyStream
+ - and then displays the entire stream's content in a browser frame via
+ - NPN_GetURL.
+ -->
+ <embed src="loremipsum.txt" streammode="seek"
+ frame="testframe" streamchunksize="1024" range="100,100"
+ id="embedtest" style="width: 400px; height: 100px;"
+ type="application/x-test"></embed>
+</body>
+</html> \ No newline at end of file
diff --git a/dom/plugins/test/mochitest/test_pluginstream_seek_close.html b/dom/plugins/test/mochitest/test_pluginstream_seek_close.html
new file mode 100644
index 0000000000..46fd3962ab
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_seek_close.html
@@ -0,0 +1,45 @@
+<body>
+<head>
+ <title>NPAPI Seekable NPStream Test</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+<script>
+ SimpleTest.expectAssertions(0, 1);
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function frameLoaded() {
+ var testframe = document.getElementById('testframe');
+ var content = testframe.contentDocument.body.innerHTML;
+ if (!content.length)
+ return;
+
+ ok(true, "We didn't crash");
+ SimpleTest.finish();
+ }
+</script>
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"></iframe>
+
+ <!--
+ - Tests a seekable stream. Calls NPN_RequestRead with the specified
+ - range, and verifies that an NPP_Write call is made with the correct
+ - parameters, including the buffer data for the byte range. Once all
+ - calls to NPP_Write have finished, the plugin calls NPN_DestroyStream
+ - and then displays the entire stream's content in a browser frame via
+ - NPN_GetURL.
+ -->
+ <embed src="neverending.sjs" streammode="seek" closestream
+ frame="testframe" streamchunksize="1024" range="100,100"
+ id="embedtest" style="width: 400px; height: 100px;"
+ type="application/x-test"></embed>
+
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_pluginstream_src.html b/dom/plugins/test/mochitest/test_pluginstream_src.html
new file mode 100644
index 0000000000..cf934d022d
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_src.html
@@ -0,0 +1,33 @@
+<html>
+<head>
+ <title>NPAPI src="" NPStream Test</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="pluginstream.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"></iframe>
+
+ <!--
+ - A stream is sent to the browser via NPP_NewStream, NP_NORMAL.
+ - The plugin reports that data can only be sent to it in 100-byte
+ - chunks. When NPP_DestroyStream is called, the plugin sends the stream
+ - content back to the browser by passing it as a data: url to
+ - NPN_GetURL, using a frame, so that the stream content should
+ - be displayed in the frame in the browser.
+ -->
+ <embed src="loremipsum.txt" streammode="normal"
+ streamchunksize="100" frame="testframe"
+ id="embedtest" style="width: 400px; height: 100px;"
+ type="application/x-test"></embed>
+</body>
+</html> \ No newline at end of file
diff --git a/dom/plugins/test/mochitest/test_pluginstream_src_dynamic.html b/dom/plugins/test/mochitest/test_pluginstream_src_dynamic.html
new file mode 100644
index 0000000000..2a7db9916d
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_src_dynamic.html
@@ -0,0 +1,43 @@
+<html>
+<head>
+ <title>NPAPI src="" NPStream Test</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript"
+ src="pluginstream.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"></iframe>
+
+ <!--
+ - A stream is sent to the browser via NPP_NewStream, NP_NORMAL.
+ - The plugin reports that data can only be sent to it in 100-byte
+ - chunks. When NPP_DestroyStream is called, the plugin sends the stream
+ - content back to the browser by passing it as a data: url to
+ - NPN_GetURL, using a frame, so that the stream content should
+ - be displayed in the frame in the browser.
+ -
+ - We create the object element dynamically, which in some cases has caused us to deliver the data=""
+ - stream twice. This verifies that we only deliver the data="" stream once.
+ -->
+
+ <script type="text/javascript">
+ var e = document.createElement('object');
+ e.setAttribute('data', 'loremipsum.xtest');
+ e.setAttribute('type', 'application/x-test');
+ e.setAttribute('streammode', 'normal');
+ e.setAttribute('streamchunksize', '100');
+ e.setAttribute('frame', 'testframe');
+ e.setAttribute('style', 'width: 400px; height: 100px;');
+ document.body.appendChild(e);
+ </script>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_pluginstream_src_referer.html b/dom/plugins/test/mochitest/test_pluginstream_src_referer.html
new file mode 100644
index 0000000000..9e96016ff6
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_pluginstream_src_referer.html
@@ -0,0 +1,30 @@
+<head>
+ <title>Do plugin stream src requests send the Referer header correctly?</title>
+ <script type="application/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+
+<body>
+ <p id="display"></p>
+
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function frameLoaded() {
+ var testframe = document.getElementById('pluginframe');
+ var content = testframe.contentDocument.body.innerHTML;
+ if (!content.length)
+ return;
+
+ is(content, "Referer found: " + window.location);
+ SimpleTest.finish();
+ }
+ </script>
+
+ <iframe name="pluginframe" id="pluginframe" onload="frameLoaded()"></iframe>
+
+ <embed id="plugin" type="application/x-test" src="plugin-stream-referer.sjs" width="200" height="200" frame="pluginframe"></embed>
+
diff --git a/dom/plugins/test/mochitest/test_positioning.html b/dom/plugins/test/mochitest/test_positioning.html
new file mode 100644
index 0000000000..4a4fc1d3d0
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_positioning.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test whether windowless plugins receive correct visible/invisible notifications.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <style type="text/css">
+ body {
+ height: 10000px;
+ }
+ </style>
+
+<body onload="startTest()">
+ <p id="display"></p>
+
+ <script type="application/javascript;version=1.8">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var p = null;
+
+ function startTest() {
+ p = document.getElementById('theplugin');
+
+ // Wait for the plugin to have painted once
+ var interval = setInterval(function() {
+ if (!p.getPaintCount())
+ return;
+
+ clearInterval(interval);
+ doScroll();
+ }, 100);
+ }
+
+ const kScrollAmount = 1000;
+ var startY;
+
+ function doScroll() {
+ let [x, y, w, h] = p.getWindowPosition();
+ startY = y;
+
+ scrollBy(0, kScrollAmount);
+ setTimeout(checkScroll, 500);
+ }
+
+ function checkScroll() {
+ let [x, y, w, h] = p.getWindowPosition();
+
+ is(y, startY - kScrollAmount, "Window should be informed of its new position.");
+ SimpleTest.finish();
+ }
+ </script>
+
+ <embed id="theplugin" type="application/x-test" width="200" height="200"></embed>
diff --git a/dom/plugins/test/mochitest/test_privatemode_perwindowpb.xul b/dom/plugins/test/mochitest/test_privatemode_perwindowpb.xul
new file mode 100644
index 0000000000..ae6c9d292f
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_privatemode_perwindowpb.xul
@@ -0,0 +1,120 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="NPAPI Private Mode Tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+<body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
+<embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+<embed id="plugin2" type="application/x-test" width="200" height="200"></embed>
+</body>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+function runTests() {
+ // Allow all cookies, then run the actual tests
+ SpecialPowers.pushPrefEnv({"set": [["network.cookie.cookieBehavior", 0]]}, runTestsCallback);
+}
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function whenDelayedStartupFinished(aWindow, aCallback) {
+ Services.obs.addObserver(function observer(aSubject, aTopic) {
+ if (aWindow == aSubject) {
+ Services.obs.removeObserver(observer, aTopic);
+ SimpleTest.executeSoon(aCallback);
+ }
+ }, "browser-delayed-startup-finished", false);
+}
+
+function runTestsCallback() {
+ var pluginElement1 = document.getElementById("plugin1");
+ var pluginElement2 = document.getElementById("plugin2");
+
+ var state1 = false;
+ var state2 = false;
+ var exceptionThrown = false;
+
+ try {
+ state1 = pluginElement1.queryPrivateModeState();
+ state2 = pluginElement2.queryPrivateModeState();
+ } catch (e) {
+ exceptionThrown = true;
+ }
+ is(exceptionThrown, false, "Exception thrown getting private mode state.");
+ is(state1, false, "Browser returned incorrect private mode state.");
+ is(state2, false, "Browser returned incorrect private mode state.");
+
+ pluginElement1.setCookie("foo");
+ is(pluginElement1.getCookie(), "foo", "Cookie was set and retrieved correctly in public mode.");
+
+ // open a window with private mode and get the references of the elements.
+ var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ var contentPage = getRootDirectory(window.location.href) + "privatemode_perwindowpb.xul";
+
+ function testOnWindow(aIsPrivate, aCallback) {
+ var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
+ whenDelayedStartupFinished(win, function () {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href == "about:privatebrowsing") {
+ win.gBrowser.loadURI(contentPage);
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+ win.gBrowser.selectedBrowser.focus();
+ SimpleTest.executeSoon(function() { aCallback(win); });
+ }, true);
+ SimpleTest.executeSoon(function() { win.gBrowser.loadURI(contentPage); });
+ });
+ }
+
+ testOnWindow(true, function(aWin) {
+ pluginElement1 = aWin.gBrowser.contentDocument.getElementById("plugin1");
+ pluginElement2 = aWin.gBrowser.contentDocument.getElementById("plugin2");
+
+ var officialState1, officialState2;
+ try {
+ officialState1 = pluginElement1.queryPrivateModeState();
+ officialState2 = pluginElement2.queryPrivateModeState();
+ } catch (e) {
+ exceptionThrown = true;
+ }
+ is(exceptionThrown, false, "Exception thrown getting private mode state.");
+ is(officialState1, true, "Querying private mode reported incorrectly");
+ is(officialState2, true, "Querying private mode reported incorrectly");
+
+ // It would be nice to assert that we don't see the public cookie in private mode,
+ // but the NPAPI complains when the resulting string is empty.
+ // is(pluginElement1.getCookie(), "", "Public cookie was not retrieved in private mode.");
+ pluginElement1.setCookie("bar");
+ is(pluginElement1.getCookie(), "bar", "Cookie was set and retrieved correctly in private mode.");
+
+ aWin.close();
+
+ pluginElement1 = document.getElementById("plugin1");
+ is(pluginElement1.getCookie(), "foo", "Private cookie was not retrieved in public mode.");
+
+ SimpleTest.finish();
+ });
+}
+]]>
+</script>
+</window>
diff --git a/dom/plugins/test/mochitest/test_propertyAndMethod.html b/dom/plugins/test/mochitest/test_propertyAndMethod.html
new file mode 100644
index 0000000000..6e638e4191
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_propertyAndMethod.html
@@ -0,0 +1,51 @@
+<html>
+ <head>
+ <title>NPObject with property and method with the same name</title>
+
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+
+ <body onload="run()">
+
+ <script class="testbody" type="application/javascript">
+ if (typeof Object.getPrototypeOf !== "function") {
+ if (typeof "test".__proto__ === "object") {
+ Object.getPrototypeOf = function(object) {
+ return object.__proto__;
+ };
+ } else {
+ Object.getPrototypeOf = function(object) {
+ // May break if the constructor has been tampered with
+ return object.constructor.prototype;
+ };
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function run() {
+ var plugin = document.getElementById("plugin");
+ var pluginProto = Object.getPrototypeOf(plugin);
+
+ delete pluginProto.propertyAndMethod;
+ ok(isNaN(plugin.propertyAndMethod + 0), "Shouldn't be set yet!");
+
+ plugin.propertyAndMethod = 5;
+ is(+plugin.propertyAndMethod, 5, "Should be set to 5!");
+
+ delete pluginProto.propertyAndMethod;
+ ok(isNaN(plugin.propertyAndMethod + 0), "Shouldn't be set any more!");
+
+ var res = plugin.propertyAndMethod();
+ is(res, 5, "Method invocation should return 5!");
+
+ SimpleTest.finish();
+ }
+ </script>
+
+ <embed id="plugin" type="application/x-test" wmode="window"></embed>
+ </body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_queryCSSZoomFactor.html b/dom/plugins/test/mochitest/test_queryCSSZoomFactor.html
new file mode 100644
index 0000000000..682d23ae60
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_queryCSSZoomFactor.html
@@ -0,0 +1,48 @@
+<html>
+ <head>
+ <title>NPAPI NPNVcontentsScaleFactor Test</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ </head>
+
+ <body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function checkZoomFactor(zoomFactor, expectedValue) {
+ // Done as if/ok instead of is() so we don't spam test results
+ if (isNaN(zoomFactor)) {
+ ok(false, "Return should be valid when getting CSS zoom factor");
+ }
+ return (expectedValue - zoomFactor) < 0.00001;
+ }
+
+ function testZoom() {
+ var pluginElement = document.getElementById("plugin");
+ // setTimeout loop on value checks, as zoom value updates can take some
+ // time and we don't have a good event to listen for.
+ if (!checkZoomFactor(pluginElement.queryCSSZoomFactorGetValue(), 2.0) ||
+ !checkZoomFactor(pluginElement.queryCSSZoomFactorSetValue(), 2.0)) {
+ setTimeout(testZoom, 0);
+ return;
+ }
+ ok(true, "Zoom values set to 2.0 as expected");
+ // set back to 1 when we're done otherwise later tests can fail
+ SpecialPowers.setFullZoom(window, 1.0);
+ SimpleTest.finish();
+ }
+
+ function runTests() {
+ var pluginElement = document.getElementById("plugin");
+ // Don't check SetValue yet, needs to happen after zoom has been explicitly set.
+ ok(checkZoomFactor(pluginElement.queryCSSZoomFactorGetValue(), 1.0), "Zoom values set to 1.0 as expected");
+ SpecialPowers.setFullZoom(window, 2.0);
+ // Check for new zoom value sometime after we've spun event loop to repaint.
+ setTimeout(testZoom, 0);
+ }
+ </script>
+
+ <embed id="plugin" type="application/x-test" width="400" height="400"></embed>
+ </body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_queryContentsScaleFactor.html b/dom/plugins/test/mochitest/test_queryContentsScaleFactor.html
new file mode 100644
index 0000000000..c1f8919abb
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_queryContentsScaleFactor.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+ <title>NPAPI NPNVcontentsScaleFactor Test</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+</head>
+
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function runTests() {
+ var pluginElement = document.getElementById("plugin");
+ var contentsScaleFactor;
+ var exceptionThrown = false;
+ try {
+ contentsScaleFactor = pluginElement.queryContentsScaleFactor();
+ } catch (e) {
+ exceptionThrown = true;
+ }
+ is(exceptionThrown, false, "Exception thrown getting contents scale factor.");
+ is(isNaN(contentsScaleFactor), false, "Invalid return getting contents scale factor");
+ ok(true, "Got Scale Factor of " + contentsScaleFactor);
+ SimpleTest.finish();
+ }
+ </script>
+
+ <embed id="plugin" type="application/x-test" width="400" height="400"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_queryContentsScaleFactorWindowed.html b/dom/plugins/test/mochitest/test_queryContentsScaleFactorWindowed.html
new file mode 100644
index 0000000000..6f11332cbf
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_queryContentsScaleFactorWindowed.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+ <title>NPAPI NPNVcontentsScaleFactor Test</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="utils.js"></script>
+</head>
+
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function runTests() {
+ var pluginElement = document.getElementById("plugin");
+ var contentsScaleFactor;
+ var exceptionThrown = false;
+ try {
+ contentsScaleFactor = pluginElement.queryContentsScaleFactor();
+ } catch (e) {
+ exceptionThrown = true;
+ }
+ is(exceptionThrown, false, "Exception thrown getting contents scale factor.");
+ is(isNaN(contentsScaleFactor), false, "Invalid return getting contents scale factor");
+ ok(true, "Got Scale Factor of " + contentsScaleFactor);
+ SimpleTest.finish();
+ }
+ </script>
+
+ <embed id="plugin" type="application/x-test" width="400" height="400" wmode="window"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_redirect_handling.html b/dom/plugins/test/mochitest/test_redirect_handling.html
new file mode 100644
index 0000000000..7d60ec06e9
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_redirect_handling.html
@@ -0,0 +1,67 @@
+<html>
+<head>
+ <title>Basic NPAPI Redirect Handling</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+</head>
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var p = null;
+
+ var redirectingURL = "307-xo-redirect.sjs";
+ var redirectTargetURL = "http://example.org/tests/dom/plugins/test/mochitest/loremipsum.txt";
+
+ var expectedWriteURL = "";
+ var expectedNotifyStatus = -1;
+
+ function redirectCallback(url, httpStatus) {
+ is(url, redirectTargetURL, "Test for expected redirect notify URL.");
+ is(httpStatus, 307, "Test for expected http redirect status.");
+ }
+
+ function writeCallback(url) {
+ is(url, expectedWriteURL, "Test for expected stream write URL.");
+ }
+
+ function notifyCallback(status, data) {
+ is(status, expectedNotifyStatus, "Test for expected stream notification status.");
+ runNextTest();
+ }
+
+ function test1() {
+ expectedWriteURL = "";
+ expectedNotifyStatus = 2;
+
+ p.streamTest(redirectingURL, false, null, writeCallback, notifyCallback, redirectCallback, false);
+ }
+
+ function test2() {
+ expectedWriteURL = redirectTargetURL;
+ expectedNotifyStatus = 0;
+
+ p.streamTest(redirectingURL, false, null, writeCallback, notifyCallback, redirectCallback, true);
+ }
+
+ var tests = [test1, test2];
+ var currentTest = -1;
+ function runNextTest() {
+ currentTest++;
+ if (currentTest < tests.length) {
+ tests[currentTest]();
+ } else {
+ SimpleTest.finish();
+ }
+ }
+
+ function runTests() {
+ p = document.getElementById("plugin1");
+ runNextTest();
+ }
+ </script>
+
+ <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_refresh_navigator_plugins.html b/dom/plugins/test/mochitest/test_refresh_navigator_plugins.html
new file mode 100644
index 0000000000..e39b3217a5
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_refresh_navigator_plugins.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!-- bug 820708 -->
+<html>
+ <head>
+ <meta><charset="utf-8"/>
+ <title>Test Refreshing navigator.plugins (bug 820708)</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ </head>
+ <body>
+ <script class="testbody" type="application/javascript">
+ "use strict";
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var pluginHost = Components.classes["@mozilla.org/plugin/host;1"]
+ .getService(Components.interfaces.nsIPluginHost);
+ var pluginTags = pluginHost.getPluginTags();
+ var nextTest = null;
+ var obsService = Components.classes["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService);
+ var observer = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic == "plugin-info-updated") {
+ SimpleTest.executeSoon(nextTest);
+ }
+ }
+ };
+ obsService.addObserver(observer, "plugin-info-updated", false);
+
+ var navTestPlugin1 = navigator.plugins.namedItem("Test Plug-in");
+ ok(navTestPlugin1, "navigator.plugins should have Test Plug-in");
+ var tagTestPlugin = null;
+ for (var plugin of pluginTags) {
+ if (plugin.name == navTestPlugin1.name) {
+ tagTestPlugin = plugin;
+ break;
+ }
+ }
+ ok(tagTestPlugin, "plugin tags should have Test Plug-in");
+ var mimeType = tagTestPlugin.getMimeTypes()[0];
+ ok(mimeType, "should have a MIME type for Test Plug-in");
+ ok(navigator.mimeTypes[mimeType], "navigator.mimeTypes should have an entry for '" + mimeType + "'");
+ ok(!tagTestPlugin.disabled, "test plugin should not be disabled");
+
+ nextTest = testPart2;
+ tagTestPlugin.enabledState = Components.interfaces.nsIPluginTag.STATE_DISABLED;
+
+ function testPart2() {
+ var navTestPlugin2 = navigator.plugins.namedItem("Test Plug-in");
+ ok(!navTestPlugin2, "now navigator.plugins should not have Test Plug-in");
+ ok(!navigator.mimeTypes[mimeType], "now navigator.mimeTypes should not have an entry for '" + mimeType + "'");
+
+ nextTest = testPart3;
+ tagTestPlugin.enabledState = Components.interfaces.nsIPluginTag.STATE_ENABLED;
+ }
+
+ function testPart3() {
+ var navTestPlugin3 = navigator.plugins.namedItem("Test Plug-in");
+ ok(navTestPlugin3, "now navigator.plugins should have Test Plug-in again");
+ ok(navigator.mimeTypes[mimeType], "now navigator.mimeTypes should have an entry for '" + mimeType + "' again");
+ obsService.removeObserver(observer, "plugin-info-updated");
+ SimpleTest.finish();
+ }
+ </script>
+ </body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_secondPlugin.html b/dom/plugins/test/mochitest/test_secondPlugin.html
new file mode 100644
index 0000000000..4c1f051ca3
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_secondPlugin.html
@@ -0,0 +1,73 @@
+<html>
+ <head>
+ <title>Second Test Plug-in Test</title>
+
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+
+ <body onload="run()">
+ <script class="testbody" type="application/javascript">
+ "use strict";
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_DISABLED, "Second Test Plug-in");
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Java Test Plug-in");
+
+ function findPlugin(pluginName) {
+ for (var i = 0; i < navigator.plugins.length; i++) {
+ var plugin = navigator.plugins[i];
+ if (plugin.name === pluginName) {
+ return plugin;
+ }
+ }
+ return null;
+ }
+
+ function findMimeType(mimeTypeType) {
+ for (var i = 0; i < navigator.mimeTypes.length; i++) {
+ var mimeType = navigator.mimeTypes[i];
+ if (mimeType.type === mimeTypeType) {
+ return mimeType;
+ }
+ }
+ return null;
+ }
+
+ function run() {
+ var pluginElement = document.getElementById("plugin");
+ is(pluginElement.identifierToStringTest("foo"), "foo", "Should be able to call a function provided by the plugin");
+
+ pluginElement = document.getElementById("disabledPlugin");
+ is(typeof pluginElement.identifierToStringTest, "undefined", "Should NOT be able to call a function on a disabled plugin");
+
+ pluginElement = document.getElementById("clickToPlayPlugin");
+ is(typeof pluginElement.identifierToStringTest, "undefined", "Should NOT be able to call a function on a click-to-play plugin");
+
+ ok(navigator.plugins["Test Plug-in"], "Should have queried a plugin named 'Test Plug-in'");
+ ok(!navigator.plugins["Second Test Plug-in"], "Should NOT have queried a disabled plugin named 'Second Test Plug-in'");
+ ok(navigator.plugins["Java Test Plug-in"], "Should have queried a click-to-play plugin named 'Java Test Plug-in'");
+
+ ok(findPlugin("Test Plug-in"), "Should have found a plugin named 'Test Plug-in'");
+ ok(!findPlugin("Second Test Plug-in"), "Should NOT found a disabled plugin named 'Second Test Plug-in'");
+ ok(findPlugin("Java Test Plug-in"), "Should have found a click-to-play plugin named 'Java Test Plug-in'");
+
+ ok(navigator.mimeTypes["application/x-test"], "Should have queried a MIME type named 'application/x-test'");
+ ok(!navigator.mimeTypes["application/x-second-test"], "Should NOT have queried a disabled type named 'application/x-second-test'");
+ ok(navigator.mimeTypes["application/x-java-test"], "Should have queried a click-to-play MIME type named 'application/x-java-test'");
+
+ ok(findMimeType("application/x-test"), "Should have found a MIME type named 'application/x-test'");
+ ok(!findMimeType("application/x-second-test"), "Should NOT have found a disabled MIME type named 'application/x-second-test'");
+ ok(findMimeType("application/x-java-test"), "Should have found a click-to-play MIME type named 'application/x-java-test'");
+
+ SimpleTest.finish();
+ }
+ </script>
+
+ <object id="plugin" type="application/x-test" width=200 height=200></object>
+ <object id="disabledPlugin" type="application/x-second-test" width=200 height=200></object>
+ <object id="clickToPlayPlugin" type="application/x-java-test" width=200 height=200></object>
+ </body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_src_url_change.html b/dom/plugins/test/mochitest/test_src_url_change.html
new file mode 100644
index 0000000000..8e8f4d326a
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_src_url_change.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test changing src attribute</title>
+ <script type="text/javascript" src="/MochiKit/packed.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTests()">
+ <script type="application/javascript;version=1.8">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var destroyed = false;
+ function onDestroy() {
+ destroyed = true;
+ }
+
+ function runTests() {
+ p = document.getElementById('plugin1');
+
+ p.startWatchingInstanceCount();
+ p.callOnDestroy(onDestroy);
+
+ p.setAttribute("src", "loremipsum.txt");
+
+ is(destroyed, true, "Instance should have been destroyed.");
+ is(p.getInstanceCount(), 1, "One new instance should have been created.");
+
+ p.stopWatchingInstanceCount();
+
+ SimpleTest.finish();
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="plugin1" src="about:blank" type="application/x-test" width="200" height="200"></embed>
+</body>
+</html>
+
diff --git a/dom/plugins/test/mochitest/test_streamNotify.html b/dom/plugins/test/mochitest/test_streamNotify.html
new file mode 100644
index 0000000000..d1aa8be8da
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_streamNotify.html
@@ -0,0 +1,89 @@
+<head>
+ <title>NPN_Get/PostURLNotify tests</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var pending = 5;
+ function testDone() {
+ dump("testDone: " + pending + "\n")
+ --pending;
+
+ // Wait for a bit so that any unexpected notifications from the
+ // malformed data or null-post tests are received.
+ if (0 == pending)
+ window.setTimeout(SimpleTest.finish, 2000);
+ }
+
+ function runTests() {
+ var p = document.getElementById('plugin1');
+
+ ok(p.streamTest("loremipsum.txt", false, null, null,
+ function(r, t) {
+ is(r, 0, "GET loremipsum.txt");
+ is(t.substr(0, 11), "Lorem ipsum",
+ "GET loremipsum.txt correct");
+ testDone();
+ }, null, true), "streamTest GET");
+
+ ok(!p.streamTest("post.sjs", true, null, null,
+ function(r, t) {
+ ok(false, "Shouldn't get callback from null post");
+ }, null, true), "streamTest POST null postdata");
+
+ ok(p.streamTest("post.sjs", true, "Something good", null,
+ function(r, t) {
+ is(r, 0, "POST something good");
+ is(t, "Something good", "POST non-null correct");
+ testDone();
+ }, null, true), "streamTest POST valid postdata");
+
+ ok(p.streamTest("http://example.invalid/", false, null, null,
+ function(r, t) {
+ is(r, 1, "Shouldn't load example.invalid DNS name");
+ testDone();
+ }, null, true), "streamTest GET bad DNS");
+
+ ok(!p.streamTest("http://localhost:-8/", false, null, null,
+ function(r, t) {
+ ok(false, "Shouldn't get callback from malformed URI");
+ }, null, true), "streamTest GET invalid URL");
+
+ ok(p.streamTest("javascript:'Hello';", false, null, null,
+ function(r, t) {
+ is(r, 0, "GET javascript: URI");
+ is(t, "Hello", "GET javascript: URI correct");
+ testDone();
+ }, null, true), "streamTest GET javascript: URI");
+
+/*
+ * XXX/cjones: disabled for now because it appears to be hard to make
+ * mochitest ignore the malformed javascript
+
+ ok(!p.streamTest("javascript:syntax##$&*@error-*", false, null,
+ function(r, t) {
+ is(r, 1, "Shouldn't load invalid javascript: URI");
+ testDone();
+ }), "streamTest GET bad javascript: URI");
+*/
+
+ ok(p.streamTest("data:text/plain,World", false, null, null,
+ function(r, t) {
+ is(r, 0, "GET data: URI");
+ is(t, "World", "GET data: URI correct");
+ testDone();
+ }, null, true), "streamTest GET data: URI");
+
+ ok(!p.streamTest("data:malformed?", false, null, null,
+ function(r, t) {
+ todo(false, "Shouldn't get callback for invalid data: URI");
+ }, null, true), "streamTest GET bad data: URI");
+ }
+ </script>
+
+ <embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
diff --git a/dom/plugins/test/mochitest/test_streamatclose.html b/dom/plugins/test/mochitest/test_streamatclose.html
new file mode 100644
index 0000000000..f308d7d796
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_streamatclose.html
@@ -0,0 +1,46 @@
+<html>
+<head>
+ <title>Stream open at NPP_Destroy</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css">
+
+<body onload="startTest()">
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var urlnotified = false;
+
+ var p = null;
+
+ function startTest() {
+ p = document.getElementById('embedtest');
+ ok(p.streamTest("neverending.sjs", false, null, null,
+ function(r, t) {
+ is(r, 2, "Stream should have failed");
+ urlnotified = true;
+ }, null, true), "neverending.sjs started successfully");
+
+ setTimeout(removePlugin, 500);
+ }
+
+ function removePlugin() {
+ document.body.removeChild(p); // Fires NPP_Destroy immediately
+ SimpleTest.executeSoon(done);
+ }
+
+ function done() {
+ ok(urlnotified, "NPP_URLNotify should be called if streams are active at NPP_Destroy");
+ SimpleTest.finish();
+ }
+
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="embedtest"
+ style="width: 400px; height: 100px;" type="application/x-test"></embed>
diff --git a/dom/plugins/test/mochitest/test_stringHandling.html b/dom/plugins/test/mochitest/test_stringHandling.html
new file mode 100644
index 0000000000..023fcfcb05
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_stringHandling.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+ <title>NPAPI string test</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function runTests() {
+ try {
+ var plugin = document.getElementById("plugin1");
+ var badData = 'foo ' + '\x00'.repeat(260000);
+ var ret = plugin.echoString(badData);
+ ok(true, "Did not crash.");
+ is(ret, badData, "Returned string should equal what we passed in.");
+ } catch (e) {
+ ok(false, "Failed to call plugin.echoString() properly.");
+ } finally {
+ SimpleTest.finish();
+ }
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_twostreams.html b/dom/plugins/test/mochitest/test_twostreams.html
new file mode 100644
index 0000000000..c2b3672c83
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_twostreams.html
@@ -0,0 +1,46 @@
+<html>
+<head>
+ <title>Dual NPAPI NP_ASFILEONLY NPStream Test</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+
+ <script type="text/javascript">
+ SimpleTest.expectAssertions(0, 2);
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var framesToLoad = 2;
+ function frameLoaded(id) {
+ var frame = document.getElementById('testframe' + id);
+ if (!frame.contentDocument.body.innerHTML.length)
+ return;
+
+ --framesToLoad;
+ if (0 == framesToLoad) {
+ is(document.getElementById('testframe1').contentDocument.body.innerHTML,
+ document.getElementById('testframe2').contentDocument.body.innerHTML,
+ "Frame contents should match");
+ SimpleTest.finish();
+ }
+ }
+ </script>
+
+ <iframe id="testframe1" name="testframe1" onload="frameLoaded(1)"></iframe>
+ <iframe id="testframe2" name="testframe2" onload="frameLoaded(2)"></iframe>
+
+ <embed src="loremipsum_nocache.txt" streammode="asfileonly"
+ frame="testframe1"
+ id="embedtest" style="width: 400px; height: 100px;"
+ type="application/x-test"></embed>
+ <embed src="loremipsum_nocache.txt" streammode="asfileonly"
+ frame="testframe2"
+ id="embedtest2" style="width: 400px; height: 100px;"
+ type="application/x-test"></embed>
+
diff --git a/dom/plugins/test/mochitest/test_visibility.html b/dom/plugins/test/mochitest/test_visibility.html
new file mode 100644
index 0000000000..943fddaf4e
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_visibility.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test whether windowless plugins receive correct visible/invisible notifications.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <style>
+ .hidden { visibility: hidden; }
+ </style>
+
+<body onload="startTest()">
+ <p id="display"></p>
+
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var didPaint = function() {};
+
+ function startTest() {
+ if (p.getPaintCount() < 1) {
+ setTimeout(startTest, 0);
+ return;
+ }
+
+ didPaint = function() {
+ ok(false, "Plugin should not paint until it is visible!");
+ };
+
+ ok(!p.isVisible(), "Plugin should not be visible.");
+ paintCountIs(p, 0, "Plugin should not have painted.");
+
+ didPaint = part2;
+
+ p.style.visibility = 'visible';
+ }
+
+ function part2() {
+ ok(p.isVisible(), "Plugin should now be visible.");
+ paintCountIs(p, 1, "Plugin should have painted once.");
+
+ didPaint = part3;
+
+ p.setColor('FF0000FF'); // this causes an invalidate/repaint
+ }
+
+ const kTimeout = 5000; // 5 seconds
+ var part4GiveUp;
+ var part4Interval;
+
+ function part3() {
+ ok(p.isVisible(), "Plugin should still be visible.");
+ paintCountIs(p, 2, "Plugin should have painted twice.");
+
+ didPaint = function() {
+ ok(false, "Plugin should not paint when it is invisible.");
+ };
+
+ p.style.visibility = 'hidden';
+
+ part4GiveUp = Date.now() + kTimeout;
+ part4Interval = setInterval(part4, 100);
+ }
+
+ function part4() {
+ if (p.isVisible()) {
+ if (Date.now() < part4GiveUp)
+ return;
+
+ ok(false, "Plugin never became invisible in part4.");
+ SimpleTest.finish();
+ return;
+ }
+
+ clearInterval(part4Interval);
+
+ ok(true, "Plugin became invisible again.");
+ p.setColor('FF00FF00');
+ setTimeout(SimpleTest.finish, 500);
+ // wait to make sure we don't actually paint
+ }
+
+ function inPaint() {
+ // We're actually in the middle of painting the plugin so don't do anything
+ // complex here, for the sake of cases where async plugin painting isn't
+ // enabled yet
+ setTimeout(didPaint, 0);
+ // Don't run that didPaint callback again
+ didPaint = function() {};
+ }
+ </script>
+
+ <embed id="theplugin" class="hidden" type="application/x-test" drawmode="solid" color="FFFF0000" paintscript="inPaint()"></embed>
+
+ <script type="application/javascript">
+ var p = document.getElementById('theplugin');
+ </script>
diff --git a/dom/plugins/test/mochitest/test_windowed_invalidate.html b/dom/plugins/test/mochitest/test_windowed_invalidate.html
new file mode 100644
index 0000000000..a19e4c8c53
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_windowed_invalidate.html
@@ -0,0 +1,66 @@
+<html>
+<head>
+ <title>Test NPN_Invalidate working for a windowed plugin</title>
+ <script type="text/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTests()">
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var lastPaintCount;
+ var p = null;
+
+ function checkPainted() {
+ if (p.getPaintCount() > lastPaintCount) {
+ ok(true, "Plugin did repaint");
+ SimpleTest.finish();
+ } else {
+ setTimeout(checkPainted, 100);
+ }
+ }
+
+ function doTest() {
+ // Cause the plugin to invalidate itself using NPN_Invalidate,
+ // and then wait for the plugin's paintCount to increase. This is the
+ // simplest way to check that a windowed plugin has repainted.
+ p.setColor("FF00FF00");
+ checkPainted();
+ }
+
+ function checkPaintCountStabilized() {
+ // Wait for the paint count to stabilize (i.e. doesn't change for a full
+ // second), so that all buffered-up painting is hopefully finished,
+ // before running the test
+ lastPaintCount = p.getPaintCount();
+ setTimeout(function() {
+ var newCount = p.getPaintCount();
+ if (newCount == lastPaintCount) {
+ doTest();
+ } else {
+ checkPaintCountStabilized();
+ }
+ }, 1000);
+ }
+
+ function runTests() {
+ p = document.getElementById("p");
+ checkPaintCountStabilized();
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="p" type="application/x-test" wmode="window" drawmode="solid"
+ color="FFFF0000">
+ </embed>
+
+ <div id="verbose">
+ </div>
+ </body>
+ </html>
diff --git a/dom/plugins/test/mochitest/test_windowless_flash.html b/dom/plugins/test/mochitest/test_windowless_flash.html
new file mode 100644
index 0000000000..8274a78aeb
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_windowless_flash.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTests()">
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ function runTests() {
+ var p1 = document.getElementById('plugin1');
+ var p2 = document.getElementById('plugin2');
+ var p3 = document.getElementById('plugin3');
+ is(p1.hasWidget(), false, "Flash is always windowless mode even if wmode=window");
+ is(p2.hasWidget(), false, "Flash is always windowless mode even if wmode=anything");
+ is(p3.hasWidget(), false, "Flash is always windowless mode even if no wmode");
+ SimpleTest.finish();
+ }
+
+ </script>
+ <p id="display"></p>
+
+ <div id="div1">
+ <embed id="plugin1" type="application/x-shockwave-flash-test" width="200" height="200" wmode="window"></embed>
+ <embed id="plugin2" type="application/x-shockwave-flash-test" width="200" height="200" wmode="test"></embed>
+ <embed id="plugin3" type="application/x-shockwave-flash-test" width="200" height="200"></embed>
+ </div>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_windowless_ime.html b/dom/plugins/test/mochitest/test_windowless_ime.html
new file mode 100644
index 0000000000..edc9940529
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_windowless_ime.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=539565
+-->
+<head>
+ <title>Test #1 for Bug 539565</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script class="testbody" type="text/javascript">
+function runTests() {
+ var plugin = document.getElementById("plugin1");
+
+ plugin.focus();
+ synthesizeComposition({ type: "compositionstart", data: "" });
+ let data = "composition";
+ synthesizeCompositionChange({
+ composition: {
+ string: data,
+ clauses: [
+ { length: data.length, attr: COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ caret: {start: data.length, length: 0}
+ });
+ is(plugin.getLastCompositionText(), data, "can get composition string");
+ synthesizeComposition({ type: "compositioncommit", data: "" });
+
+ synthesizeComposition({ type: "compositionstart", data: "" });
+ is(plugin.getLastCompositionText(), "", "can get empty composition string");
+ synthesizeComposition({ type: "compositioncommit", data: "" });
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+</head>
+
+<body onload="runTests()">
+ <embed id="plugin1" type="application/x-test" wmode="transparent" width="200" height="200"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/mochitest/test_wmode.xul b/dom/plugins/test/mochitest/test_wmode.xul
new file mode 100644
index 0000000000..b45038835a
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_wmode.xul
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="WMode Tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript" src="plugin-utils.js"></script>
+ <script type="application/javascript">
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+<body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
+<embed id="plugin2" type="application/x-test" width="400" height="400" wmode="window"></embed>
+<embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
+</body>
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ var p1 = document.getElementById("plugin1");
+ is(p1.hasWidget(), false, "Plugin should be windowless by default");
+
+ var p2 = document.getElementById("plugin2");
+ if (navigator.platform.indexOf("Mac") >= 0) {
+ is(p2.hasWidget(), false, "Mac does not support windowed plugins");
+ } else if (navigator.platform.indexOf("Win") >= 0) {
+ is(p2.hasWidget(), true, "Windows supports windowed plugins");
+ } else if (navigator.platform.indexOf("Linux") >= 0) {
+ is(p2.hasWidget(), true, "Linux supports windowed plugins");
+ }
+
+ SimpleTest.finish();
+}
+]]>
+</script>
+</window>
diff --git a/dom/plugins/test/mochitest/test_x11_error_crash.html b/dom/plugins/test/mochitest/test_x11_error_crash.html
new file mode 100644
index 0000000000..1b52d1e6f6
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_x11_error_crash.html
@@ -0,0 +1,27 @@
+<head>
+ <title>Plugin terminating on X11 error</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+<body>
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout(
+ "crashAndGetCrashServiceRecord() polls for async crash recording");
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ window.frameLoaded = function frameLoaded_toCrash() {
+ SimpleTest.expectChildProcessCrash();
+
+ crashAndGetCrashServiceRecord("triggerXError", function (cm, crash) {
+ var isPluginCrash = crash.isOfType(cm.PROCESS_TYPE_PLUGIN, cm.CRASH_TYPE_CRASH);
+ ok(isPluginCrash, "Record should be a plugin crash");
+ if (!isPluginCrash) {
+ dump("Crash type: " + crash.type + "\n");
+ }
+ SimpleTest.finish();
+ });
+
+ }
+ </script>
+ <iframe id="iframe1" src="crashing_subpage.html" width="600" height="600"></iframe>
diff --git a/dom/plugins/test/mochitest/test_xulbrowser_plugin_visibility.xul b/dom/plugins/test/mochitest/test_xulbrowser_plugin_visibility.xul
new file mode 100644
index 0000000000..8cccb8bf7d
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_xulbrowser_plugin_visibility.xul
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript" src="plugin-utils.js"></script>
+
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var w = window.open('xulbrowser_plugin_visibility.xul', '_blank', 'chrome,resizable=yes,width=400,height=600');
+
+ function done()
+ {
+ w.close();
+ SimpleTest.finish();
+ }
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;" />
+</window>
diff --git a/dom/plugins/test/mochitest/test_zero_opacity.html b/dom/plugins/test/mochitest/test_zero_opacity.html
new file mode 100644
index 0000000000..c768cafd53
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_zero_opacity.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test whether windowed plugins with opacity:0 get their window set correctly</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="plugin-utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<body onload="startTest()">
+ <script type="application/javascript;version=1.8">
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ var p = null;
+
+ function startTest() {
+ p = document.getElementById('theplugin');
+ if (!p.hasWidget()) {
+ todo(false, "This test is only relevant for windowed plugins");
+ SimpleTest.finish();
+ return;
+ }
+
+ // Wait for the plugin to have painted once.
+ var interval = setInterval(function() {
+ if (!p.getPaintCount())
+ return;
+
+ clearInterval(interval);
+ doTest();
+ SimpleTest.finish();
+ }, 100);
+ }
+
+ function doTest() {
+ is(p.getClipRegionRectCount(), 1, "getClipRegionRectCount should be a single rect");
+ is(p.getClipRegionRectEdge(0,2) - p.getClipRegionRectEdge(0,0), 100, "width of clip region rect");
+ is(p.getClipRegionRectEdge(0,3) - p.getClipRegionRectEdge(0,1), 50, "height of clip region rect");
+ }
+ </script>
+
+ <p id="display"></p>
+
+ <embed id="theplugin" type="application/x-test" width="100" height="50" style="opacity:0" wmode="window"></embed> \ No newline at end of file
diff --git a/dom/plugins/test/mochitest/xulbrowser_plugin_visibility.xul b/dom/plugins/test/mochitest/xulbrowser_plugin_visibility.xul
new file mode 100644
index 0000000000..d087305200
--- /dev/null
+++ b/dom/plugins/test/mochitest/xulbrowser_plugin_visibility.xul
@@ -0,0 +1,139 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ orient="vertical">
+
+ <tabbox id="tabbox" flex="1">
+ <tabs>
+ <tab id="tab1" label="Tab 1" />
+ <tab id="tab2" label="Tab 2" />
+ </tabs>
+ <tabpanels flex="1">
+ <browser id="browser1" type="content-primary" flex="1" src="about:blank"/>
+ <browser id="browser2" type="content-primary" flex="1" src="about:blank"/>
+ </tabpanels>
+ </tabbox>
+ <script type="application/javascript" src="plugin-utils.js"/>
+ <script type="application/javascript"><![CDATA[
+ const ok = window.opener.wrappedJSObject.ok;
+ const is = window.opener.wrappedJSObject.is;
+ const done = window.opener.wrappedJSObject.done;
+ const SimpleTest = window.opener.wrappedJSObject.SimpleTest;
+
+ const nsIWebProgress = Components.interfaces.nsIWebProgress;
+ const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
+
+ const kURI = 'http://mochi.test:8888/chrome/dom/plugins/test/mochitest/plugin_visibility_loader.html';
+
+ function ProgressListener() {
+ }
+ ProgressListener.prototype.onStateChange =
+ function(progress, req, flags, status) {
+ if ((flags & nsIWebProgressListener.STATE_IS_WINDOW) &&
+ (flags & nsIWebProgressListener.STATE_STOP))
+ browserLoaded();
+ };
+ ProgressListener.prototype.QueryInterface = function(iid) {
+ if (iid.equals(nsIWebProgressListener) ||
+ iid.equals(Components.interfaces.nsISupportsWeakReference))
+ return this;
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ };
+
+ var loadCount = 0;
+ function browserLoaded() {
+ ++loadCount;
+ if (2 == loadCount)
+ startTest();
+ }
+
+ var tabbox = document.getElementById('tabbox');
+ var browser1 = document.getElementById('browser1');
+ var browser2 = document.getElementById('browser2');
+
+ var progressListener1, progressListener2;
+
+ function setup() {
+ progressListener1 = new ProgressListener();
+ browser1.addProgressListener(progressListener1, nsIWebProgress.NOTIFY_STATE_WINDOW);
+ browser1.loadURI(kURI, null, null);
+ progressListener2 = new ProgressListener();
+ browser2.addProgressListener(progressListener2, nsIWebProgress.NOTIFY_STATE_WINDOW);
+ browser2.loadURI(kURI, null, null);
+ }
+
+ window.addEventListener("load", setup, false);
+
+ var plugin1, plugin2;
+
+ const kTimeout = 5000; // 5 seconds
+ var paintGiveUp;
+ var paintInterval;
+
+ function startTest() {
+ plugin1 = browser1.contentDocument.getElementById('p').wrappedJSObject;
+ plugin2 = browser2.contentDocument.getElementById('p').wrappedJSObject;
+
+ paintGiveUp = Date.now() + kTimeout;
+ paintInterval = setInterval(waitForPaint, 100);
+ }
+
+ function waitForPaint() {
+ if (!plugin1.isVisible()) {
+ if (Date.now() < paintGiveUp)
+ return;
+
+ ok(false, "Plugin in tab 1 never became visible.");
+ done();
+ return;
+ }
+
+ clearInterval(paintInterval);
+
+ ok(true, "Plugin in tab 1 should be visible.");
+ paintCountIs(plugin1, 1, "Plugin in tab 1 should have painted once.");
+
+ ok(!plugin2.isVisible(), "Plugin in tab 2 should not be visible.");
+ paintCountIs(plugin2, 0, "Plugin in tab 2 should not have painted.");
+
+ tabbox.selectedIndex = 1;
+ paintGiveUp = Date.now() + kTimeout;
+ paintInterval = setInterval(part2, 100);
+ }
+
+ function part2() {
+ if (!plugin2.isVisible()) {
+ if (Date.now() < paintGiveUp)
+ return;
+
+ ok(false, "Plugin in tab 2 never became visible.");
+ done();
+ return;
+ }
+
+ clearInterval(paintInterval);
+
+ ok(true, "Plugin in tab 2 became visible.");
+ paintCountIs(plugin2, 1, "Plugin in tab 2 should have painted once.");
+
+ ok(!plugin1.isVisible(), "Plugin in tab 1 should have become invisible.");
+ paintCountIs(plugin1, 1, "Plugin in tab 1 should have painted once.");
+
+ // Setcolor invalidates
+ plugin1.setColor('FF00FF00');
+ plugin2.setColor('FF00FF00');
+
+ setTimeout(part3, 500);
+ }
+
+ function part3() {
+ paintCountIs(plugin1, 1,
+ "Plugin in tab 1 should not have repainted after invalidate.");
+ paintCountIs(plugin2, 2,
+ "Plugin in tab 2 should have repainted after invalidate.");
+ done();
+ }
+ ]]></script>
+
+</window>
diff --git a/dom/plugins/test/moz.build b/dom/plugins/test/moz.build
new file mode 100644
index 0000000000..c7f7b01edc
--- /dev/null
+++ b/dom/plugins/test/moz.build
@@ -0,0 +1,15 @@
+# -*- 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 += ['testplugin', 'testaddon']
+
+XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'cocoa', 'windows'):
+ MOCHITEST_MANIFESTS += ['mochitest/mochitest.ini']
+ MOCHITEST_CHROME_MANIFESTS += ['mochitest/chrome.ini']
+ BROWSER_CHROME_MANIFESTS += ['mochitest/browser.ini']
+
diff --git a/dom/plugins/test/reftest/border-padding-1-ref.html b/dom/plugins/test/reftest/border-padding-1-ref.html
new file mode 100644
index 0000000000..1a33644ac4
--- /dev/null
+++ b/dom/plugins/test/reftest/border-padding-1-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<body style="margin:0">
+<div style="width:184px; height:192px; margin:90px 80px; outline:5px dashed blue;
+ border:dotted black; border-width:4px 8px 4px 8px;
+ background:cyan;">
+ <div style="margin:3px 1px; height:186px; background:lime;"></div>
+</div>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/border-padding-1.html b/dom/plugins/test/reftest/border-padding-1.html
new file mode 100644
index 0000000000..6fa2446f40
--- /dev/null
+++ b/dom/plugins/test/reftest/border-padding-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body style="margin:0" onLoad="forceLoadPlugin('p1')">
+<object type="application/x-test" drawmode="solid" color="ff00ff00"
+ style="width:200px; height:200px; display:block; margin:90px 80px;
+ outline:5px dashed blue;
+ background:cyan;
+ border:dotted black; border-width:4px 8px 4px 8px;
+ padding:3px 1px;" id="p1">
+</object>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/border-padding-2-ref.html b/dom/plugins/test/reftest/border-padding-2-ref.html
new file mode 100644
index 0000000000..ae92da4032
--- /dev/null
+++ b/dom/plugins/test/reftest/border-padding-2-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body style="margin:0" onLoad="forceLoadPlugin('plugin')">
+<div style="width:184px; height:192px; margin:90px 80px; outline:5px dashed blue;
+ border:dotted black; border-width:4px 8px 4px 8px;
+ background:cyan;">
+ <object style="margin:3px 1px; height:186px; width:182px; display:block;"
+ type="application/x-test"
+ id="plugin">
+ </object>
+</div>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/border-padding-2.html b/dom/plugins/test/reftest/border-padding-2.html
new file mode 100644
index 0000000000..6a39d2d819
--- /dev/null
+++ b/dom/plugins/test/reftest/border-padding-2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body style="margin:0" onLoad="forceLoadPlugin('plugin')">
+<object type="application/x-test"
+ style="width:200px; height:200px; display:block; margin:90px 80px;
+ outline:5px dashed blue;
+ background:cyan;
+ border:dotted black; border-width:4px 8px 4px 8px;
+ padding:3px 1px;"
+ id="plugin">
+</object>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/border-padding-3-ref.html b/dom/plugins/test/reftest/border-padding-3-ref.html
new file mode 100644
index 0000000000..5c7bb74564
--- /dev/null
+++ b/dom/plugins/test/reftest/border-padding-3-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<body style="margin:0">
+<div style="width:184px; height:192px; margin:90px 80px; outline:5px dashed blue;
+ border:dotted black; border-width:4px 8px 4px 8px;
+ background:cyan;">
+ <div style="margin:3px 1px; height:186px; width:182px; background:rgb(255,128,255);"></object>
+</div>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/border-padding-3.html b/dom/plugins/test/reftest/border-padding-3.html
new file mode 100644
index 0000000000..533445601f
--- /dev/null
+++ b/dom/plugins/test/reftest/border-padding-3.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body style="margin:0" onLoad="forceLoadPlugin('plugin', true)">
+<object type="application/x-test" id="plugin"
+ drawmode="solid" color="00000000"
+ style="width:200px; height:200px; display:block; margin:90px 80px;
+ outline:5px dashed blue;
+ background:cyan;
+ border:dotted black; border-width:4px 8px 4px 8px;
+ padding:3px 1px;">
+</object>
+<script>
+var prevPaintCount = 0;
+function doTestWait() {
+ if (document.getElementById("plugin").getPaintCount() != prevPaintCount) {
+ document.documentElement.removeAttribute('class');
+ } else {
+ setTimeout(doTestWait, 0);
+ }
+}
+
+function doTest() {
+ prevPaintCount = document.getElementById("plugin").getPaintCount();
+ document.getElementById("plugin").setColor("FFFF80FF");
+ setTimeout(doTestWait, 0);
+
+}
+window.addEventListener("MozReftestInvalidate", doTest, false);
+</script>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/div-alpha-opacity.html b/dom/plugins/test/reftest/div-alpha-opacity.html
new file mode 100644
index 0000000000..fec913b640
--- /dev/null
+++ b/dom/plugins/test/reftest/div-alpha-opacity.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+<head>
+<style type="text/css">
+#one {
+ position:absolute;
+ left:0px; top:0px;
+ width:400px; height:400px;
+ border:2px solid blue;
+ background-color: rgb(160,160,160);
+ opacity:0.8;
+ z-index:1;
+}
+#two {
+ position:absolute;
+ top:100px; left:100px;
+ width:200px; height:200px;
+ z-index:0;
+ background-color: rgb(255,0,0);
+}
+</style>
+</head>
+<body>
+<div id="two"></div>
+<div id="one"></div>
+</body>
+</html>
+
diff --git a/dom/plugins/test/reftest/div-alpha-zindex.html b/dom/plugins/test/reftest/div-alpha-zindex.html
new file mode 100644
index 0000000000..e4672b913b
--- /dev/null
+++ b/dom/plugins/test/reftest/div-alpha-zindex.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+<head>
+<style type="text/css">
+#one {
+ position:absolute;
+ left:0px; top:0px;
+ width:400px; height:400px;
+ background-color: rgb(0,255,0);
+ opacity:0.6;
+ z-index:1;
+}
+#two {
+ position:absolute;
+ top:100px; left:100px;
+ width:200px; height:200px;
+ z-index:0;
+ background-color: rgb(255,0,0);
+}
+</style>
+</head>
+<body>
+<div id="two"></div>
+<div id="one"></div>
+</body>
+</html>
+
diff --git a/dom/plugins/test/reftest/div-sanity.html b/dom/plugins/test/reftest/div-sanity.html
new file mode 100644
index 0000000000..9ffa539191
--- /dev/null
+++ b/dom/plugins/test/reftest/div-sanity.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html><head>
+<title>div boxes</title>
+<style>
+div {
+ width: 400px;
+ height: 400px;
+ display: inline-block;
+}
+</style>
+</head>
+<body>
+<div style="background-color: #FF0000;"></div> <!-- red -->
+<div style="background-color: #00FF00;"></div> <!-- green -->
+<div style="background-color: #0000FF;"></div> <!-- blue -->
+<div style="background-color: #999999;"></div> <!-- gray -->
+</body></html>
diff --git a/dom/plugins/test/reftest/plugin-alpha-opacity.html b/dom/plugins/test/reftest/plugin-alpha-opacity.html
new file mode 100644
index 0000000000..2db6cc4de3
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-alpha-opacity.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html class="reftest-wait">
+<head>
+<style type="text/css">
+#one {
+ position:absolute;
+ left:0px; top:0px;
+ width:404px; height:404px;
+ border:2px solid blue;
+ opacity:.8;
+ z-index:1;
+}
+#two {
+ position:absolute;
+ top:100px; left:100px;
+ width:200px; height:200px;
+ z-index:0;
+ background-color: rgb(255,0,0);
+}
+</style>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin('one')">
+<div id="two"></div>
+<embed id="one" type="application/x-test" width="400" height="400" drawmode="solid" color="FFa0a0a0"></embed>
+</body>
+</html>
+
diff --git a/dom/plugins/test/reftest/plugin-alpha-zindex.html b/dom/plugins/test/reftest/plugin-alpha-zindex.html
new file mode 100644
index 0000000000..ead9b6f4ce
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-alpha-zindex.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html class="reftest-wait">
+<head>
+<style type="text/css">
+#one {
+ position:absolute;
+ left:0px; top:0px;
+ z-index:1;
+}
+#two {
+ position:absolute;
+ top:100px; left:100px;
+ width:200px; height:200px;
+ z-index:0;
+ background-color: rgb(255,0,0);
+}
+</style>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin('one')">
+<div id="two"></div>
+<embed id="one" type="application/x-test" width="400" height="400" drawmode="solid" color="9900FF00" id="p1"></embed>
+</body>
+</html>
+
diff --git a/dom/plugins/test/reftest/plugin-background-1-step.html b/dom/plugins/test/reftest/plugin-background-1-step.html
new file mode 100644
index 0000000000..9498633b41
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-background-1-step.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <link rel="stylesheet" type="text/css" href="plugin-background.css"></link>
+ <script type="text/javascript">
+var NUM_STEPS = 1;
+ </script>
+ <script type="text/javascript" src="plugin-background.js"></script>
+</head>
+<body>
+ <div id="bad">Test some plugin stuff.</div>
+ <div id="good"></div>
+
+ <embed id="plugin" type="application/x-test" width="199" height="199"
+ drawmode="solid" color="330000FF"></embed>
+
+ <div id="topbar"></div>
+ <div id="leftbar"></div>
+ <div id="rightbar"></div>
+ <div id="bottombar"></div>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/plugin-background-10-step.html b/dom/plugins/test/reftest/plugin-background-10-step.html
new file mode 100644
index 0000000000..7a0824a565
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-background-10-step.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <link rel="stylesheet" type="text/css" href="plugin-background.css"></link>
+ <script type="text/javascript">
+var NUM_STEPS = 10;
+ </script>
+ <script type="text/javascript" src="plugin-background.js"></script>
+</head>
+<body>
+ <div id="bad">Test some plugin stuff.</div>
+ <div id="good"></div>
+
+ <embed id="plugin" type="application/x-test" width="199" height="199"
+ drawmode="solid" color="330000FF"></embed>
+
+ <div id="topbar"></div>
+ <div id="leftbar"></div>
+ <div id="rightbar"></div>
+ <div id="bottombar"></div>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/plugin-background-2-step.html b/dom/plugins/test/reftest/plugin-background-2-step.html
new file mode 100644
index 0000000000..cc186a5f29
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-background-2-step.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <link rel="stylesheet" type="text/css" href="plugin-background.css"></link>
+ <script type="text/javascript">
+var NUM_STEPS = 2;
+ </script>
+ <script type="text/javascript" src="plugin-background.js"></script>
+</head>
+<body>
+ <div id="bad">Test some plugin stuff.</div>
+ <div id="good"></div>
+
+ <embed id="plugin" type="application/x-test" width="199" height="199"
+ drawmode="solid" color="330000FF"></embed>
+
+ <div id="topbar"></div>
+ <div id="leftbar"></div>
+ <div id="rightbar"></div>
+ <div id="bottombar"></div>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/plugin-background-5-step.html b/dom/plugins/test/reftest/plugin-background-5-step.html
new file mode 100644
index 0000000000..2630719c88
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-background-5-step.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <link rel="stylesheet" type="text/css" href="plugin-background.css"></link>
+ <script type="text/javascript">
+var NUM_STEPS = 5;
+ </script>
+ <script type="text/javascript" src="plugin-background.js"></script>
+</head>
+<body>
+ <div id="bad">Test some plugin stuff.</div>
+ <div id="good"></div>
+
+ <embed id="plugin" type="application/x-test" width="199" height="199"
+ drawmode="solid" color="330000FF"></embed>
+
+ <div id="topbar"></div>
+ <div id="leftbar"></div>
+ <div id="rightbar"></div>
+ <div id="bottombar"></div>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/plugin-background-ref.html b/dom/plugins/test/reftest/plugin-background-ref.html
new file mode 100644
index 0000000000..651fdecef5
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-background-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <link rel="stylesheet" type="text/css" href="plugin-background.css"></link>
+</head>
+<body>
+ <div id="bad">Test some plugin stuff.</div>
+ <div id="good"></div>
+
+ <div id="plugin"></div>
+
+ <div id="topbar"></div>
+ <div id="leftbar"></div>
+ <div id="rightbar"></div>
+ <div id="bottombar"></div>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/plugin-background.css b/dom/plugins/test/reftest/plugin-background.css
new file mode 100644
index 0000000000..f6b251214d
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-background.css
@@ -0,0 +1,61 @@
+div {
+ position: absolute;
+}
+#bad {
+ left:220px; top:0px;
+ z-index: 0;
+}
+#good {
+ left:0px; top:0px;
+ width:220px; height:220px;
+ /* Core Animation alpha blending rounding differs
+ from the Core Graphics blending, adjust with care */
+ background-color: rgba(0,255,0, 0.51);
+ z-index: 0;
+}
+
+#topbar {
+ left:0px; top:0px;
+ width:220px; height:20px;
+ background-color: rgb(0,0,0);
+ z-index: 2;
+}
+#topbar {
+ left:0px; top:0px;
+ width:220px; height:20px;
+ background-color: rgb(0,0,0);
+ z-index: 2;
+}
+#leftbar {
+ left:0px; top:0px;
+ width:20px; height:220px;
+ background-color: rgb(0,0,0);
+ z-index: 2;
+}
+#rightbar {
+ left:200px; top:0px;
+ width:20px; height:220px;
+ background-color: rgb(0,0,0);
+ z-index: 2;
+}
+#bottombar {
+ left:0px; top:200px;
+ width:220px; height:20px;
+ background-color: rgb(0,0,0);
+ z-index: 2;
+}
+
+div#plugin {
+ position: absolute;
+ left:1px; top:1px;
+ width:199px; height:199px;
+ background-color: rgba(0,0,255, 0.2);
+ z-index: 1;
+}
+
+embed#plugin {
+ position: absolute;
+ left:1px; top:1px;
+ z-index: 1;
+}
+
diff --git a/dom/plugins/test/reftest/plugin-background.html b/dom/plugins/test/reftest/plugin-background.html
new file mode 100644
index 0000000000..4cd1e3f538
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-background.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <link rel="stylesheet" type="text/css" href="plugin-background.css"></link>
+</head>
+<script src="utils.js">
+</script>
+<body onLoad="forceLoadPlugin('plugin')">
+ <div id="bad">Test some plugin stuff.</div>
+ <div id="good"></div>
+
+ <embed id="plugin" type="application/x-test" width="199" height="199"
+ drawmode="solid" color="330000FF"></embed>
+
+ <div id="topbar"></div>
+ <div id="leftbar"></div>
+ <div id="rightbar"></div>
+ <div id="bottombar"></div>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/plugin-background.js b/dom/plugins/test/reftest/plugin-background.js
new file mode 100644
index 0000000000..5d24562cff
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-background.js
@@ -0,0 +1,75 @@
+// The including script sets this for us
+//var NUM_STEPS;
+
+var plugin;
+var left = 1, top = 1, width = 199, height = 199;
+function movePluginTo(x, y, w, h) {
+ left = x; top = y; width = w; height = h;
+ plugin.width = w;
+ plugin.height = h;
+ plugin.style.left = left + "px";
+ plugin.style.top = top + "px";
+}
+function deltaInBounds(dx,dy, dw,dh) {
+ var l = dx + left;
+ var r = l + width + dw;
+ var t = dy + top;
+ var b = t + height + dh;
+ return (0 <= l && l <= 20 &&
+ 0 <= t && t <= 20 &&
+ 200 <= r && r <= 220 &&
+ 200 <= b && b <= 220);
+}
+
+var initialFrame;
+function start() {
+ window.removeEventListener("MozReftestInvalidate", start, false);
+
+ window.addEventListener("MozAfterPaint", step, false);
+ window.addEventListener("MozPaintWaitFinished", step, false);
+
+ initialFrame = window.mozPaintCount;
+ plugin = document.getElementById("plugin");
+
+ movePluginTo(0,0, 200,200);
+}
+
+var steps = 0;
+var which = "move"; // or "grow"
+var dx = 1, dy = 1, dw = 1, dh = 1;
+function step() {
+ if (++steps >= NUM_STEPS) {
+ window.removeEventListener("MozAfterPaint", step, false);
+ window.removeEventListener("MozPaintWaitFinished", step, false);
+ return finish();
+ }
+
+ var didSomething = false;
+ if (which == "grow") {
+ if (deltaInBounds(0,0, dw,dh)) {
+ movePluginTo(left,top, width+dw, height+dh);
+ didSomething = true;
+ } else {
+ dw = -dw; dh = -dh;
+ }
+ } else {
+ // "move"
+ if (deltaInBounds(dx,dy, 0,0)) {
+ movePluginTo(left+dx,top+dy, width, height);
+ didSomething = true;
+ } else {
+ dx = -dx; dy = -dy;
+ }
+ }
+ which = (which == "grow") ? "move" : "grow";
+
+ if (!didSomething) {
+ step();
+ }
+}
+
+function finish() {
+ document.documentElement.removeAttribute("class");
+}
+
+window.addEventListener("MozReftestInvalidate", start, false);
diff --git a/dom/plugins/test/reftest/plugin-busy-alpha-zindex.html b/dom/plugins/test/reftest/plugin-busy-alpha-zindex.html
new file mode 100644
index 0000000000..e339dd2669
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-busy-alpha-zindex.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<html class="reftest-wait">
+<head>
+ <style type="text/css">
+#one {
+ position:absolute;
+ left:0px; top:0px;
+ z-index:4;
+}
+#two {
+ position:absolute;
+ top:100px; left:100px;
+ background-color:rgb(0,0,0,0);
+ z-index:3;
+}
+#three {
+ position:absolute;
+ left:100px; top:100px;
+ width:200px; height:200px;
+ background-color: rgb(255,0,0);
+ opacity:0.6;
+ z-index:2;
+}
+#four {
+ position:absolute;
+ top:100px; left:100px;
+ z-index:1;
+}
+ </style>
+ <script src="utils.js" type="text/javascript">
+ </script>
+ <script type="text/javascript">
+function paintCanvas() {
+ var canvas = document.getElementById("two");
+ var ctx = canvas.getContext("2d");
+ ctx.fillStyle = "rgba(255,0,0, 0.6)";
+ ctx.fillRect(0,0, 200,200);
+}
+
+ function doTest() {
+ paintCanvas();
+ forceLoadPlugin(['one', 'four']);
+ }
+ </script>
+</style>
+</head>
+<body onload="doTest();">
+ <embed id="four" type="application/x-test" width="200" height="200"
+ drawmode="solid" color="FFFF0000"></embed>
+ <div id="three"></div>
+ <canvas id="two" width="200" height="200"></canvas>
+ <embed id="one" type="application/x-test" width="400" height="400"
+ drawmode="solid" color="9900FF00"></embed>
+</body>
+</html>
+
diff --git a/dom/plugins/test/reftest/plugin-canvas-alpha-zindex.html b/dom/plugins/test/reftest/plugin-canvas-alpha-zindex.html
new file mode 100644
index 0000000000..517099d1b1
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-canvas-alpha-zindex.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<html class="reftest-wait">
+<head>
+ <style type="text/css">
+#one {
+ position:absolute;
+ left:0px; top:0px;
+ z-index:1;
+}
+#two {
+ position:absolute;
+ top:100px; left:100px;
+// Set these using the canvas API
+// width:200px; height:200px;
+// background-color: rgb(255,0,0);
+ z-index:0;
+}
+ </style>
+ <script src="utils.js" type="text/javascript">
+ </script>
+ <script type="text/javascript">
+function paintCanvas() {
+ var canvas = document.getElementById("two");
+ var ctx = canvas.getContext("2d");
+ ctx.fillStyle = "rgb(255,0,0)";
+ ctx.fillRect(0,0, 200,200);
+}
+
+ function doTest() {
+ paintCanvas();
+ forceLoadPlugin('one');
+ }
+ </script>
+</style>
+</head>
+<body onload="doTest()">
+ <canvas width="200" height="200" id="two"></canvas>
+ <embed id="one" type="application/x-test" width="400" height="400" drawmode="solid" color="9900FF00"></embed>
+</body>
+</html>
+
diff --git a/dom/plugins/test/reftest/plugin-sanity.html b/dom/plugins/test/reftest/plugin-sanity.html
new file mode 100644
index 0000000000..4f9c30eee4
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-sanity.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html class="reftest-wait">
+<head>
+<title>Plugin boxes</title>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin(['p1', 'p2', 'p3', 'p4'])">
+<embed type="application/x-test" width="400" height="400" drawmode="solid" color="FFFF0000" id="p1"></embed> <!-- red -->
+<embed type="application/x-test" width="400" height="400" drawmode="solid" color="FF00FF00" id="p2"></embed> <!-- green -->
+<embed type="application/x-test" width="400" height="400" drawmode="solid" color="FF0000FF" id="p3"></embed> <!-- blue -->
+<embed type="application/x-test" width="400" height="400" drawmode="solid" color="FF999999" id="p4"></embed> <!-- gray -->
+</body></html>
diff --git a/dom/plugins/test/reftest/plugin-transform-1-ref.html b/dom/plugins/test/reftest/plugin-transform-1-ref.html
new file mode 100644
index 0000000000..259a78b41b
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-transform-1-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin('p')">
+<embed type="application/x-test" id="p"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/plugin-transform-1.html b/dom/plugins/test/reftest/plugin-transform-1.html
new file mode 100644
index 0000000000..19f6e8c20f
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-transform-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin('p')">
+<embed type="application/x-test" style="-moz-transform:scale(1)" id="p"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/plugin-transform-2-ref.html b/dom/plugins/test/reftest/plugin-transform-2-ref.html
new file mode 100644
index 0000000000..93a3924d7e
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-transform-2-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div style="width:100px; height:100px; -moz-transform-origin:top left;
+ -moz-transform:scale(2); background:rgb(0,255,0)"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/plugin-transform-2.html b/dom/plugins/test/reftest/plugin-transform-2.html
new file mode 100644
index 0000000000..7f48640c19
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-transform-2.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin('one')">
+<embed type="application/x-test" drawmode="solid" color="FF00FF00"
+ style="width:100px; height:100px; -moz-transform-origin:top left;
+ -moz-transform:scale(2); display:block"
+ id="one"></embed>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/plugin-transform-alpha-zindex.html b/dom/plugins/test/reftest/plugin-transform-alpha-zindex.html
new file mode 100644
index 0000000000..52fda4bcf9
--- /dev/null
+++ b/dom/plugins/test/reftest/plugin-transform-alpha-zindex.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html class="reftest-wait">
+<head>
+<style type="text/css">
+#one {
+ position:absolute;
+ left:0px; top:0px;
+ z-index:1;
+}
+#two {
+ position:absolute;
+ top:0px; left:0px;
+ width:200px; height:200px;
+ z-index:0;
+ background-color: rgb(255,0,0);
+ -moz-transform-origin: 0 0;
+ -moz-transform: translate(100px,100px);
+}
+</style>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin('one')">
+ <div id="two"></div>
+ <embed id="one" type="application/x-test" width="400" height="400" drawmode="solid" color="9900FF00"></embed>
+</body>
+</html>
+
diff --git a/dom/plugins/test/reftest/pluginproblemui-direction-1-ref.html b/dom/plugins/test/reftest/pluginproblemui-direction-1-ref.html
new file mode 100644
index 0000000000..fafec34f43
--- /dev/null
+++ b/dom/plugins/test/reftest/pluginproblemui-direction-1-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html class="reftest-expect-process-crash reftest-wait">
+ <head>
+ <title>Plugin Problem UI directionality test</title>
+ </head>
+ <body dir="ltr" style="text-align: left;">
+ <embed type="application/x-test" width="400" height="400" id="crashme"></embed>
+ <script type="text/javascript">
+ var plugin = document.getElementById("crashme");
+ function checkForCrashUI() {
+ if (getComputedStyle(plugin, null).MozBinding != "none") {
+ document.documentElement.classList.remove("reftest-wait");
+ clearInterval(interval);
+ }
+ }
+
+ var interval = setInterval(checkForCrashUI, 100);
+ setTimeout(function() { plugin.crash(); }, 0);
+ </script>
+ </body>
+</html>
diff --git a/dom/plugins/test/reftest/pluginproblemui-direction-1.html b/dom/plugins/test/reftest/pluginproblemui-direction-1.html
new file mode 100644
index 0000000000..9888850dc9
--- /dev/null
+++ b/dom/plugins/test/reftest/pluginproblemui-direction-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html class="reftest-expect-process-crash reftest-wait">
+ <head>
+ <title>Plugin Problem UI directionality test</title>
+ </head>
+ <body dir="rtl" style="text-align: left;">
+ <embed type="application/x-test" width="400" height="400" id="crashme"></embed>
+ <script type="text/javascript">
+ var plugin = document.getElementById("crashme");
+ function checkForCrashUI() {
+ if (getComputedStyle(plugin, null).MozBinding != "none") {
+ document.documentElement.classList.remove("reftest-wait");
+ clearInterval(interval);
+ }
+ }
+
+ var interval = setInterval(checkForCrashUI, 100);
+ setTimeout(function() { plugin.crash(); }, 0);
+ </script>
+ </body>
+</html>
diff --git a/dom/plugins/test/reftest/pluginproblemui-direction-2-ref.html b/dom/plugins/test/reftest/pluginproblemui-direction-2-ref.html
new file mode 100644
index 0000000000..e807b86b5b
--- /dev/null
+++ b/dom/plugins/test/reftest/pluginproblemui-direction-2-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-expect-process-crash reftest-wait">
+ <head>
+ <title>Plugin Problem UI directionality test</title>
+ </head>
+ <body dir="ltr" style="text-align: left;">
+ <!-- a variant of pluginproblemui-direction-1.html that covers up
+ the spot where we get random variation with d2d, so that we
+ can still test it with d2d -->
+ <div style="position: absolute; width: 1px; height: 1px; background: red; z-index: 1; left: 401px; top: 19px;"></div>
+ <embed type="application/x-test" width="400" height="400" id="crashme"></embed>
+ <script type="text/javascript">
+ var plugin = document.getElementById("crashme");
+ function checkForCrashUI() {
+ if (getComputedStyle(plugin, null).MozBinding != "none") {
+ document.documentElement.classList.remove("reftest-wait");
+ clearInterval(interval);
+ }
+ }
+
+ var interval = setInterval(checkForCrashUI, 100);
+ setTimeout(function() { plugin.crash(); }, 0);
+ </script>
+ </body>
+</html>
diff --git a/dom/plugins/test/reftest/pluginproblemui-direction-2.html b/dom/plugins/test/reftest/pluginproblemui-direction-2.html
new file mode 100644
index 0000000000..95b358e372
--- /dev/null
+++ b/dom/plugins/test/reftest/pluginproblemui-direction-2.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-expect-process-crash reftest-wait">
+ <head>
+ <title>Plugin Problem UI directionality test</title>
+ </head>
+ <body dir="rtl" style="text-align: left;">
+ <!-- a variant of pluginproblemui-direction-1.html that covers up
+ the spot where we get random variation with d2d, so that we
+ can still test it with d2d -->
+ <div style="position: absolute; width: 1px; height: 1px; background: red; z-index: 1; left: 401px; top: 19px;"></div>
+ <embed type="application/x-test" width="400" height="400" id="crashme"></embed>
+ <script type="text/javascript">
+ var plugin = document.getElementById("crashme");
+ function checkForCrashUI() {
+ if (getComputedStyle(plugin, null).MozBinding != "none") {
+ document.documentElement.classList.remove("reftest-wait");
+ clearInterval(interval);
+ }
+ }
+
+ var interval = setInterval(checkForCrashUI, 100);
+ setTimeout(function() { plugin.crash(); }, 0);
+ </script>
+ </body>
+</html>
diff --git a/dom/plugins/test/reftest/reftest-stylo.list b/dom/plugins/test/reftest/reftest-stylo.list
new file mode 100644
index 0000000000..0f900b369c
--- /dev/null
+++ b/dom/plugins/test/reftest/reftest-stylo.list
@@ -0,0 +1,33 @@
+# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
+# basic sanity checking
+# fails random-if(!haveTestPlugin) == plugin-sanity.html plugin-sanity.html
+# fails-if(!haveTestPlugin) == plugin-sanity.html plugin-sanity.html
+skip fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,160000) == plugin-alpha-zindex.html plugin-alpha-zindex.html
+fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,164000) == plugin-alpha-opacity.html plugin-alpha-opacity.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == windowless-clipping-1.html windowless-clipping-1.html
+# bug 631832
+# fuzzy because of anti-aliasing in dashed border
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-1.html border-padding-1.html
+# bug 629430
+skip random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-2.html border-padding-2.html
+# bug 629430
+skip random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) skip-if(!haveTestPlugin) skip-if(Android||B2G) == border-padding-3.html border-padding-3.html
+# bug 629430
+# bug 773482
+# The following two "pluginproblemui-direction" tests are unreliable on all platforms. They should be re-written or replaced.
+#random-if(cocoaWidget||d2d||/^Windows\x20NT\x205\.1/.test(http.oscpu)) fails-if(!haveTestPlugin&&!Android) == pluginproblemui-direction-1.html pluginproblemui-direction-1.html
+# bug 567367
+#random-if(cocoaWidget) fails-if(!haveTestPlugin&&!Android) == pluginproblemui-direction-2.html pluginproblemui-direction-2.html
+fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,160000) == plugin-canvas-alpha-zindex.html plugin-canvas-alpha-zindex.html
+fails fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,160000) == plugin-transform-alpha-zindex.html plugin-transform-alpha-zindex.html
+skip == plugin-busy-alpha-zindex.html plugin-busy-alpha-zindex.html
+skip == plugin-background.html plugin-background.html
+skip == plugin-background-1-step.html plugin-background-1-step.html
+skip == plugin-background-2-step.html plugin-background-2-step.html
+skip == plugin-background-5-step.html plugin-background-5-step.html
+skip == plugin-background-10-step.html plugin-background-10-step.html
+skip == plugin-transform-1.html plugin-transform-1.html
+skip == plugin-transform-2.html plugin-transform-2.html
+skip == shrink-1.html shrink-1.html
+skip skip-if(!haveTestPlugin) == update-1.html update-1.html
+skip skip-if(!haveTestPlugin) == windowless-layers.html windowless-layers.html
diff --git a/dom/plugins/test/reftest/reftest.list b/dom/plugins/test/reftest/reftest.list
new file mode 100644
index 0000000000..e2d8195367
--- /dev/null
+++ b/dom/plugins/test/reftest/reftest.list
@@ -0,0 +1,26 @@
+# basic sanity checking
+random-if(!haveTestPlugin) != plugin-sanity.html about:blank
+fails-if(!haveTestPlugin) == plugin-sanity.html div-sanity.html
+fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-alpha-zindex.html div-alpha-zindex.html
+fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,164000) == plugin-alpha-opacity.html div-alpha-opacity.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == windowless-clipping-1.html windowless-clipping-1-ref.html # bug 631832
+# fuzzy because of anti-aliasing in dashed border
+fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-1.html border-padding-1-ref.html # bug 629430
+fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-2.html border-padding-2-ref.html # bug 629430
+fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) skip-if(!haveTestPlugin||Android) == border-padding-3.html border-padding-3-ref.html # bug 629430 # bug 773482
+# The following two "pluginproblemui-direction" tests are unreliable on all platforms. They should be re-written or replaced.
+#random-if(cocoaWidget||d2d||/^Windows\x20NT\x205\.1/.test(http.oscpu)) fails-if(!haveTestPlugin&&!Android) == pluginproblemui-direction-1.html pluginproblemui-direction-1-ref.html # bug 567367
+#random-if(cocoaWidget) fails-if(!haveTestPlugin&&!Android) == pluginproblemui-direction-2.html pluginproblemui-direction-2-ref.html
+fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-canvas-alpha-zindex.html div-alpha-zindex.html
+fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-transform-alpha-zindex.html div-alpha-zindex.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-busy-alpha-zindex.html div-alpha-zindex.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background.html plugin-background-ref.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background-1-step.html plugin-background-ref.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background-2-step.html plugin-background-ref.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background-5-step.html plugin-background-ref.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background-10-step.html plugin-background-ref.html
+random-if(!haveTestPlugin) == plugin-transform-1.html plugin-transform-1-ref.html
+fails-if(!haveTestPlugin) == plugin-transform-2.html plugin-transform-2-ref.html
+skip-if(!haveTestPlugin) == shrink-1.html shrink-1-ref.html
+skip-if(!haveTestPlugin) == update-1.html update-1-ref.html
+skip-if(!haveTestPlugin) == windowless-layers.html windowless-layers-ref.html
diff --git a/dom/plugins/test/reftest/shrink-1-ref.html b/dom/plugins/test/reftest/shrink-1-ref.html
new file mode 100644
index 0000000000..0906fe5789
--- /dev/null
+++ b/dom/plugins/test/reftest/shrink-1-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin('plugin')">
+ <embed id="plugin" type="application/x-test"
+ width="50px" height="40px">
+ </embed>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/shrink-1.html b/dom/plugins/test/reftest/shrink-1.html
new file mode 100644
index 0000000000..4957056982
--- /dev/null
+++ b/dom/plugins/test/reftest/shrink-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <script src="utils.js">
+ </script>
+ <script>
+function doShrink()
+{
+ var plugin = document.getElementById("plugin");
+ plugin.setSlowPaint(true);
+ plugin.width = "50";
+ plugin.height = "40";
+
+ document.documentElement.removeAttribute("class");
+}
+
+document.addEventListener("MozReftestInvalidate", doShrink, false);
+ </script>
+</head>
+<body onLoad="forceLoadPlugin('plugin', true)">
+ <embed id="plugin" type="application/x-test"
+ width="300" height="500">
+ </embed>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/update-1-ref.html b/dom/plugins/test/reftest/update-1-ref.html
new file mode 100644
index 0000000000..7303d19840
--- /dev/null
+++ b/dom/plugins/test/reftest/update-1-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin('plugin')">
+ <embed id="plugin" type="application/x-test"
+ drawmode="solid" color="FFFF0000" width="30" height="50">
+ </embed>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/update-1.html b/dom/plugins/test/reftest/update-1.html
new file mode 100644
index 0000000000..f307b4394f
--- /dev/null
+++ b/dom/plugins/test/reftest/update-1.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>Test for bugs 807728 and 810426</title>
+ <script src="utils.js">
+ </script>
+ <script>
+function start()
+{
+ document.removeEventListener("MozReftestInvalidate", start, false);
+
+ var plugin = document.getElementById("plugin");
+ var color = "FF000000";
+ var color_count = 0;
+ var last_paint_count = 0;
+ // Enough paints to test reusing a surface after it has been
+ // moved from front to back buffer.
+ var final_paint_count = window.mozPaintCount + 10;
+ var final_color = "FFFF0000";
+
+ // Using mozPaintCount to track when content has been updated as an
+ // indication that the browser has received updates, instead of
+ // plugin.getPaintCount() which tracks when the plugin sends updates or
+ // MozAfterPaint events which track OS paints. Not using
+ // MozPaintWaitFinished because invalidation causes no geometry changes.
+ function wait_for_paints() {
+ var paint_count = window.mozPaintCount;
+ if (paint_count >= final_paint_count && color == final_color) {
+ document.documentElement.removeAttribute("class");
+ return;
+ }
+ if (paint_count != last_paint_count) {
+ last_paint_count = paint_count;
+ if (paint_count + 1 >= final_paint_count) {
+ color = final_color;
+ // Wait for the paint with the final color
+ final_paint_count = paint_count + 1;
+ } else {
+ ++color_count;
+ color = "FF00000" + color_count;
+ }
+ plugin.setColor(color);
+ }
+ setTimeout(wait_for_paints, 0);
+ }
+
+ wait_for_paints();
+}
+
+// MozReftestInvalidate is delivered after initial painting has settled.
+document.addEventListener("MozReftestInvalidate", start, false);
+ </script>
+</head>
+<body onLoad="forceLoadPlugin('plugin', true)">
+ <embed id="plugin" type="application/x-test"
+ drawmode="solid" color="FF000000" width="30" height="50">
+ </embed>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/utils.js b/dom/plugins/test/reftest/utils.js
new file mode 100644
index 0000000000..f046089afc
--- /dev/null
+++ b/dom/plugins/test/reftest/utils.js
@@ -0,0 +1,18 @@
+function forceLoadPluginElement(id) {
+ var e = document.getElementById(id);
+ var found = e.pluginFoundElement;
+}
+
+function forceLoadPlugin(ids, skipRemoveAttribute) {
+ if (Array.isArray(ids)) {
+ ids.forEach(function(element, index, array) {
+ forceLoadPluginElement(element);
+ });
+ } else {
+ forceLoadPluginElement(ids);
+ }
+ if (skipRemoveAttribute) {
+ return;
+ }
+ document.documentElement.removeAttribute("class");
+}
diff --git a/dom/plugins/test/reftest/windowless-clipping-1-ref.html b/dom/plugins/test/reftest/windowless-clipping-1-ref.html
new file mode 100644
index 0000000000..e59ecb79b2
--- /dev/null
+++ b/dom/plugins/test/reftest/windowless-clipping-1-ref.html
@@ -0,0 +1,14 @@
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin('p1')">
+<div style="width:100px; height:100px; overflow:auto;">
+ <div style="width:100px; height:100px; overflow:hidden;">
+ <embed type="application/x-test" style="width:200px;" id="p1"></embed>
+ </div>
+</div>
+<div style="width:100px; height:100px; background-color:lime;"></div>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/windowless-clipping-1.html b/dom/plugins/test/reftest/windowless-clipping-1.html
new file mode 100644
index 0000000000..dc1c25ac10
--- /dev/null
+++ b/dom/plugins/test/reftest/windowless-clipping-1.html
@@ -0,0 +1,15 @@
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin('p1')">
+<div style="width:100px; height:100px; overflow:hidden;">
+ <embed type="application/x-test" style="width:200px;"></embed>
+</div>
+<div style="width:100px; height:100px; overflow:hidden;">
+ <embed type="application/x-test" style="width:200px;"
+ drawmode="solid" color="ff00ff00" id="p1"></embed>
+</div>
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/windowless-layers-ref.html b/dom/plugins/test/reftest/windowless-layers-ref.html
new file mode 100644
index 0000000000..765527b68f
--- /dev/null
+++ b/dom/plugins/test/reftest/windowless-layers-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin('p1')">
+ <embed type="application/x-test" style="width:200px; height:200px;" id="p1">
+</body>
+</html>
diff --git a/dom/plugins/test/reftest/windowless-layers.html b/dom/plugins/test/reftest/windowless-layers.html
new file mode 100644
index 0000000000..9e24c13a68
--- /dev/null
+++ b/dom/plugins/test/reftest/windowless-layers.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script src="utils.js">
+</script>
+</head>
+<body onLoad="forceLoadPlugin(['p1', 'p2'])">
+ <div style="width:200px; height:200px; overflow:hidden; position:absolute; z-index:1">
+ <embed type="application/x-test" style="width:200px; height:200px;" id="p1">
+ </div>
+ <div style="width:200px; height:100px; overflow:hidden; position:absolute; z-index:2">
+ <embed type="application/x-test" style="width:200px; height:200px;" id="p2">
+ </div>
+</body>
+</html>
diff --git a/dom/plugins/test/testaddon/Makefile.in b/dom/plugins/test/testaddon/Makefile.in
new file mode 100644
index 0000000000..7b6dd6bc5f
--- /dev/null
+++ b/dom/plugins/test/testaddon/Makefile.in
@@ -0,0 +1,25 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include $(topsrcdir)/config/rules.mk
+
+ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
+plugin_file_names = Test.plugin SecondTest.plugin ThirdTest.plugin npswftest.plugin npctrltest.plugin
+addon_file_name = testaddon_$(TARGET_XPCOM_ABI).xpi
+else
+plugin_file_names = $(DLL_PREFIX)nptest$(DLL_SUFFIX) $(DLL_PREFIX)npsecondtest$(DLL_SUFFIX) $(DLL_PREFIX)npthirdtest$(DLL_SUFFIX) $(DLL_PREFIX)npswftest$(DLL_SUFFIX) $(DLL_PREFIX)npctrltest$(DLL_SUFFIX)
+addon_file_name = testaddon.xpi
+endif
+
+# This is so hacky. Waiting on bug 988938.
+testdir = $(topobjdir)/_tests/xpcshell/dom/plugins/test/unit/
+addonpath = $(testdir)/$(addon_file_name)
+
+ifdef COMPILE_ENVIRONMENT
+libs::
+ $(NSINSTALL) -D $(testdir)
+ rm -f $(addonpath)
+ cd $(srcdir) && zip -rD $(addonpath) install.rdf
+ cd $(DIST) && zip -rD $(addonpath) $(foreach name,$(plugin_file_names),plugins/$(name))
+endif
diff --git a/dom/plugins/test/testaddon/install.rdf b/dom/plugins/test/testaddon/install.rdf
new file mode 100644
index 0000000000..017badb00a
--- /dev/null
+++ b/dom/plugins/test/testaddon/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>test-plugin-from-xpi@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:name>Test plugin from XPI</em:name>
+ <em:description>This tests shipping a plugin through an extensions.</em:description>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:unpack>true</em:unpack>
+
+ </Description>
+</RDF>
diff --git a/dom/plugins/test/testaddon/moz.build b/dom/plugins/test/testaddon/moz.build
new file mode 100644
index 0000000000..568f361a54
--- /dev/null
+++ b/dom/plugins/test/testaddon/moz.build
@@ -0,0 +1,5 @@
+# -*- 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/.
diff --git a/dom/plugins/test/testplugin/Info.plist b/dom/plugins/test/testplugin/Info.plist
new file mode 100644
index 0000000000..dc6aa5cec3
--- /dev/null
+++ b/dom/plugins/test/testplugin/Info.plist
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>libnptest.dylib</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.mozilla.TestPlugin</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BRPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0.0.0</string>
+ <key>CFBundleSignature</key>
+ <string>TEST</string>
+ <key>CFBundleVersion</key>
+ <string>1.0.0.0</string>
+ <key>WebPluginName</key>
+ <string>Test Plug-in</string>
+ <key>WebPluginDescription</key>
+ <string>Plug-in for testing purposes.™ (हिन्दी 中文 العربية)</string>
+ <key>WebPluginMIMETypes</key>
+ <dict>
+ <key>application/x-test</key>
+ <dict>
+ <key>WebPluginExtensions</key>
+ <array>
+ <string>tst</string>
+ </array>
+ <key>WebPluginTypeDescription</key>
+ <string>Test ™ mimetype</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/dom/plugins/test/testplugin/README b/dom/plugins/test/testplugin/README
new file mode 100644
index 0000000000..b7ac703634
--- /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 0000000000..0e6168e686
--- /dev/null
+++ b/dom/plugins/test/testplugin/flashplugin/Info.plist
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>libnpswftest.dylib</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.mozilla.FlashTestPlugin</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BRPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0.0.0</string>
+ <key>CFBundleSignature</key>
+ <string>FLASHTEST</string>
+ <key>CFBundleVersion</key>
+ <string>1.0.0.0</string>
+ <key>WebPluginName</key>
+ <string>Shockwave Flash</string>
+ <key>WebPluginDescription</key>
+ <string>Flash plug-in for testing purposes.</string>
+ <key>WebPluginMIMETypes</key>
+ <dict>
+ <key>application/x-shockwave-flash-test</key>
+ <dict>
+ <key>WebPluginExtensions</key>
+ <array>
+ <string>swf</string>
+ </array>
+ <key>WebPluginTypeDescription</key>
+ <string>Flash test type</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/dom/plugins/test/testplugin/flashplugin/moz.build b/dom/plugins/test/testplugin/flashplugin/moz.build
new file mode 100644
index 0000000000..3df524a2bc
--- /dev/null
+++ b/dom/plugins/test/testplugin/flashplugin/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SharedLibrary('npswftest')
+
+relative_path = 'flashplugin'
+cocoa_name = 'npswftest'
+include('../testplugin.mozbuild')
diff --git a/dom/plugins/test/testplugin/flashplugin/nptest.def b/dom/plugins/test/testplugin/flashplugin/nptest.def
new file mode 100644
index 0000000000..3a62d05d95
--- /dev/null
+++ b/dom/plugins/test/testplugin/flashplugin/nptest.def
@@ -0,0 +1,7 @@
+LIBRARY NPSWFTEST
+
+EXPORTS
+ NP_GetEntryPoints @1
+ NP_Initialize @2
+ NP_Shutdown @3
+ NP_GetMIMEDescription @4
diff --git a/dom/plugins/test/testplugin/flashplugin/nptest.rc b/dom/plugins/test/testplugin/flashplugin/nptest.rc
new file mode 100644
index 0000000000..e970d26091
--- /dev/null
+++ b/dom/plugins/test/testplugin/flashplugin/nptest.rc
@@ -0,0 +1,42 @@
+#include<winver.h>
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Flash plug-in for testing purposes."
+ VALUE "FileExtents", "swf"
+ VALUE "FileOpenName", "Flash test type"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "npswftest"
+ VALUE "MIMEType", "application/x-shockwave-flash-test"
+ VALUE "OriginalFilename", "npswftest.dll"
+ VALUE "ProductName", "Shockwave Flash"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
diff --git a/dom/plugins/test/testplugin/flashplugin/nptest_name.cpp b/dom/plugins/test/testplugin/flashplugin/nptest_name.cpp
new file mode 100644
index 0000000000..140e0225c2
--- /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 0000000000..16a45f2645
--- /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 0000000000..4e2abb3ed3
--- /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 0000000000..da24cc4b6d
--- /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 0000000000..61b18ef6e4
--- /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 0000000000..ae3a8d1466
--- /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 0000000000..a79e58c1d7
--- /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 0000000000..aa759ac16b
--- /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 0000000000..4c543d5b9f
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest.def
@@ -0,0 +1,7 @@
+LIBRARY NPTEST
+
+EXPORTS
+ NP_GetEntryPoints @1
+ NP_Initialize @2
+ NP_Shutdown @3
+ NP_GetMIMEDescription @4
diff --git a/dom/plugins/test/testplugin/nptest.h b/dom/plugins/test/testplugin/nptest.h
new file mode 100644
index 0000000000..e77d636a4f
--- /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 0000000000..948fb846ef
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest.rc
@@ -0,0 +1,42 @@
+#include<winver.h>
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Plug-in for testing purposes.\x2122 (\x0939\x093f\x0928\x094d\x0926\x0940 \x4e2d\x6587 \x0627\x0644\x0639\x0631\x0628\x064a\x0629)"
+ VALUE "FileExtents", "tst"
+ VALUE "FileOpenName", L"Test \x2122 mimetype"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "nptest"
+ VALUE "MIMEType", "application/x-test"
+ VALUE "OriginalFilename", "nptest.dll"
+ VALUE "ProductName", "Test Plug-in"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
diff --git a/dom/plugins/test/testplugin/nptest_droid.cpp b/dom/plugins/test/testplugin/nptest_droid.cpp
new file mode 100644
index 0000000000..9b062e0c2f
--- /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 0000000000..500db35d24
--- /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 0000000000..c38da31dc8
--- /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 0000000000..f9b57dfa8f
--- /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 0000000000..c9542c78bb
--- /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 0000000000..d33603143a
--- /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 0000000000..78228841eb
--- /dev/null
+++ b/dom/plugins/test/testplugin/nptest_utils.h
@@ -0,0 +1,45 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Contributor(s):
+ * Josh Aas <josh@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nptest_utils_h_
+#define nptest_utils_h_
+
+#include "nptest.h"
+
+NPUTF8* createCStringFromNPVariant(const NPVariant* variant);
+
+NPIdentifier variantToIdentifier(NPVariant variant);
+NPIdentifier stringVariantToIdentifier(NPVariant variant);
+NPIdentifier int32VariantToIdentifier(NPVariant variant);
+NPIdentifier doubleVariantToIdentifier(NPVariant variant);
+
+uint32_t parseHexColor(const char* color, int len);
+
+#endif // nptest_utils_h_
diff --git a/dom/plugins/test/testplugin/nptest_windows.cpp b/dom/plugins/test/testplugin/nptest_windows.cpp
new file mode 100644
index 0000000000..8b02872e95
--- /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 0000000000..afa83a63ce
--- /dev/null
+++ b/dom/plugins/test/testplugin/secondplugin/Info.plist
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>libnpsecondtest.dylib</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.mozilla.SecondTestPlugin</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BRPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0.0.0</string>
+ <key>CFBundleSignature</key>
+ <string>SECONDTEST</string>
+ <key>CFBundleVersion</key>
+ <string>1.0.0.0</string>
+ <key>WebPluginName</key>
+ <string>Second Test Plug-in</string>
+ <key>WebPluginDescription</key>
+ <string>Second plug-in for testing purposes.</string>
+ <key>WebPluginMIMETypes</key>
+ <dict>
+ <key>application/x-Second-Test</key>
+ <dict>
+ <key>WebPluginExtensions</key>
+ <array>
+ <string>ts2</string>
+ </array>
+ <key>WebPluginTypeDescription</key>
+ <string>Second test type</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/dom/plugins/test/testplugin/secondplugin/moz.build b/dom/plugins/test/testplugin/secondplugin/moz.build
new file mode 100644
index 0000000000..f95ed4190e
--- /dev/null
+++ b/dom/plugins/test/testplugin/secondplugin/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SharedLibrary('npsecondtest')
+
+relative_path = 'secondplugin'
+cocoa_name = 'SecondTest'
+include('../testplugin.mozbuild')
diff --git a/dom/plugins/test/testplugin/secondplugin/nptest.def b/dom/plugins/test/testplugin/secondplugin/nptest.def
new file mode 100644
index 0000000000..c6584387d2
--- /dev/null
+++ b/dom/plugins/test/testplugin/secondplugin/nptest.def
@@ -0,0 +1,7 @@
+LIBRARY NPSECONDTEST
+
+EXPORTS
+ NP_GetEntryPoints @1
+ NP_Initialize @2
+ NP_Shutdown @3
+ NP_GetMIMEDescription @4
diff --git a/dom/plugins/test/testplugin/secondplugin/nptest.rc b/dom/plugins/test/testplugin/secondplugin/nptest.rc
new file mode 100644
index 0000000000..835906d0cb
--- /dev/null
+++ b/dom/plugins/test/testplugin/secondplugin/nptest.rc
@@ -0,0 +1,42 @@
+#include<winver.h>
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Second plug-in for testing purposes."
+ VALUE "FileExtents", "ts2"
+ VALUE "FileOpenName", "Second test type"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "npsecondtest"
+ VALUE "MIMEType", "application/x-Second-Test"
+ VALUE "OriginalFilename", "npsecondtest.dll"
+ VALUE "ProductName", "Second Test Plug-in"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
diff --git a/dom/plugins/test/testplugin/secondplugin/nptest_name.cpp b/dom/plugins/test/testplugin/secondplugin/nptest_name.cpp
new file mode 100644
index 0000000000..12cc68b694
--- /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 0000000000..7a8094b83c
--- /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 0000000000..6050e04734
--- /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 0000000000..b25c6c8c52
--- /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 0000000000..a48654bc56
--- /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 0000000000..2cdfaa5f1c
--- /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 0000000000..9ed4f89663
--- /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 0000000000..96e18ba755
--- /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 0000000000..f0d7b1a5b9
--- /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 0000000000..8ea68ba410
--- /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 0000000000..de1576920e
--- /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 0000000000..34eb5973d4
--- /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";
diff --git a/dom/plugins/test/unit/head_plugins.js b/dom/plugins/test/unit/head_plugins.js
new file mode 100644
index 0000000000..4d32a52bf7
--- /dev/null
+++ b/dom/plugins/test/unit/head_plugins.js
@@ -0,0 +1,195 @@
+/* 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/.
+ */
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+const gIsWindows = mozinfo.os == "win";
+const gIsOSX = mozinfo.os == "mac";
+const gIsLinux = mozinfo.os == "linux";
+const gDirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+
+function allow_all_plugins() {
+ Services.prefs.setBoolPref("plugin.load_flash_only", false);
+}
+
+// Finds the test plugin library
+function get_test_plugin(secondplugin=false) {
+ var pluginEnum = gDirSvc.get("APluginsDL", Ci.nsISimpleEnumerator);
+ while (pluginEnum.hasMoreElements()) {
+ let dir = pluginEnum.getNext().QueryInterface(Ci.nsILocalFile);
+ let name = get_platform_specific_plugin_name(secondplugin);
+ let plugin = dir.clone();
+ plugin.append(name);
+ if (plugin.exists()) {
+ plugin.normalize();
+ return plugin;
+ }
+ }
+ return null;
+}
+
+// Finds the test nsIPluginTag
+function get_test_plugintag(aName="Test Plug-in") {
+ const Cc = Components.classes;
+ const Ci = Components.interfaces;
+
+ var name = aName || "Test Plug-in";
+ var host = Cc["@mozilla.org/plugin/host;1"].
+ getService(Ci.nsIPluginHost);
+ var tags = host.getPluginTags();
+
+ for (var i = 0; i < tags.length; i++) {
+ if (tags[i].name == name)
+ return tags[i];
+ }
+ return null;
+}
+
+// Creates a fake ProfDS directory key, copied from do_get_profile
+function do_get_profile_startup() {
+ let env = Components.classes["@mozilla.org/process/environment;1"]
+ .getService(Components.interfaces.nsIEnvironment);
+ // the python harness sets this in the environment for us
+ let profd = env.get("XPCSHELL_TEST_PROFILE_DIR");
+ let file = Components.classes["@mozilla.org/file/local;1"]
+ .createInstance(Components.interfaces.nsILocalFile);
+ file.initWithPath(profd);
+
+ let dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties);
+ let provider = {
+ getFile: function(prop, persistent) {
+ persistent.value = true;
+ if (prop == "ProfDS") {
+ return file.clone();
+ }
+ throw Components.results.NS_ERROR_FAILURE;
+ },
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsIDirectoryServiceProvider) ||
+ iid.equals(Components.interfaces.nsISupports)) {
+ return this;
+ }
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+ };
+ dirSvc.QueryInterface(Components.interfaces.nsIDirectoryService)
+ .registerProvider(provider);
+ return file.clone();
+}
+
+function get_platform_specific_plugin_name(secondplugin=false) {
+ if (secondplugin) {
+ if (gIsWindows) return "npsecondtest.dll";
+ if (gIsOSX) return "SecondTest.plugin";
+ if (gIsLinux) return "libnpsecondtest.so";
+ } else {
+ if (gIsWindows) return "nptest.dll";
+ if (gIsOSX) return "Test.plugin";
+ if (gIsLinux) return "libnptest.so";
+ }
+ return null;
+}
+
+function get_platform_specific_plugin_suffix() {
+ if (gIsWindows) return ".dll";
+ else if (gIsOSX) return ".plugin";
+ else if (gIsLinux) return ".so";
+ else return null;
+}
+
+function get_test_plugin_no_symlink() {
+ let dirSvc = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties);
+ let pluginEnum = dirSvc.get("APluginsDL", Ci.nsISimpleEnumerator);
+ while (pluginEnum.hasMoreElements()) {
+ let dir = pluginEnum.getNext().QueryInterface(Ci.nsILocalFile);
+ let plugin = dir.clone();
+ plugin.append(get_platform_specific_plugin_name());
+ if (plugin.exists()) {
+ return plugin;
+ }
+ }
+ return null;
+}
+
+var gGlobalScope = this;
+function loadAddonManager() {
+ let ns = {};
+ Cu.import("resource://gre/modules/Services.jsm", ns);
+ let head = "../../../../toolkit/mozapps/extensions/test/xpcshell/head_addons.js";
+ let file = do_get_file(head);
+ let uri = ns.Services.io.newFileURI(file);
+ ns.Services.scriptloader.loadSubScript(uri.spec, gGlobalScope);
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+}
+
+// Install addon and return a Promise<boolean> that is
+// resolve with true on success, false otherwise.
+function installAddon(relativePath) {
+ let deferred = Promise.defer();
+ let success = () => deferred.resolve(true);
+ let fail = () => deferred.resolve(false);
+ let listener = {
+ onDownloadCancelled: fail,
+ onDownloadFailed: fail,
+ onInstallCancelled: fail,
+ onInstallFailed: fail,
+ onInstallEnded: success,
+ };
+
+ let installCallback = install => {
+ install.addListener(listener);
+ install.install();
+ };
+
+ let file = do_get_file(relativePath, false);
+ AddonManager.getInstallForFile(file, installCallback,
+ "application/x-xpinstall");
+
+ return deferred.promise;
+}
+
+// Uninstall addon and return a Promise<boolean> that is
+// resolve with true on success, false otherwise.
+function uninstallAddon(id) {
+ let deferred = Promise.defer();
+
+ AddonManager.getAddonByID(id, addon => {
+ if (!addon) {
+ deferred.resolve(false);
+ }
+
+ let listener = {};
+ let handler = addon => {
+ if (addon.id !== id) {
+ return;
+ }
+
+ AddonManager.removeAddonListener(listener);
+ deferred.resolve(true);
+ };
+
+ listener.onUninstalled = handler;
+ listener.onDisabled = handler;
+
+ AddonManager.addAddonListener(listener);
+ addon.uninstall();
+ });
+
+ return deferred.promise;
+}
+
+// Returns a Promise<Addon> that is resolved with
+// the corresponding addon or rejected.
+function getAddonByID(id) {
+ let deferred = Promise.defer();
+ AddonManager.getAddonByID(id, addon => deferred.resolve(addon));
+ return deferred.promise;
+}
diff --git a/dom/plugins/test/unit/test_allowed_types.js b/dom/plugins/test/unit/test_allowed_types.js
new file mode 100644
index 0000000000..09b30b31af
--- /dev/null
+++ b/dom/plugins/test/unit/test_allowed_types.js
@@ -0,0 +1,142 @@
+/* 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/.
+ */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function run_test() {
+ const pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+ const pluginDefaultState = Services.prefs.getIntPref("plugin.default.state");
+
+ Services.prefs.setBoolPref("plugin.load_flash_only", false);
+
+ function reload_plugins_with_allowed_types(allowed_types) {
+ if (typeof allowed_types === "undefined") {
+ // If we didn't get an allowed_types string, then unset the pref,
+ // so the caller can test the behavior when the pref isn't set.
+ Services.prefs.clearUserPref("plugin.allowed_types");
+ } else {
+ Services.prefs.setCharPref("plugin.allowed_types", allowed_types);
+ }
+ pluginHost.reloadPlugins();
+ }
+
+ function get_status_for_type(type) {
+ try {
+ return pluginHost.getStateForType(type);
+ } catch(ex) {
+ // If the type is not allowed, then nsIPluginHost.getStateForType throws
+ // NS_ERROR_NOT_AVAILABLE, for which we return undefined to make it easier
+ // to write assertions about the API.
+ if (ex.result === Cr.NS_ERROR_NOT_AVAILABLE) {
+ return undefined;
+ }
+ throw ex;
+ }
+ }
+
+ // If allowed_types isn't set, then all plugin types are enabled.
+ reload_plugins_with_allowed_types();
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ // If allowed_types is set to the empty string, then all plugin types are enabled.
+ reload_plugins_with_allowed_types("");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ // If allowed_types is set to anything other than the empty string,
+ // then only types that exactly match its comma-separated entries are enabled,
+ // so a single space disables all types.
+ reload_plugins_with_allowed_types(" ");
+ do_check_eq(get_status_for_type("application/x-test"), undefined);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), undefined);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ // The rest of the assertions test various values of allowed_types to ensure
+ // that the correct types are enabled.
+
+ reload_plugins_with_allowed_types("application/x-test");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), undefined);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ reload_plugins_with_allowed_types("application/x-Second-Test");
+ do_check_eq(get_status_for_type("application/x-test"), undefined);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ reload_plugins_with_allowed_types("application/x-nonexistent");
+ do_check_eq(get_status_for_type("application/x-test"), undefined);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), undefined);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ reload_plugins_with_allowed_types("application/x-test,application/x-Second-Test");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ reload_plugins_with_allowed_types("application/x-Second-Test,application/x-test");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ reload_plugins_with_allowed_types("application/x-test,application/x-nonexistent");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), undefined);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ reload_plugins_with_allowed_types("application/x-nonexistent,application/x-test");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), undefined);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ reload_plugins_with_allowed_types("application/x-test,application/x-Second-Test,application/x-nonexistent");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ reload_plugins_with_allowed_types("application/x-test,application/x-nonexistent,application/x-Second-Test");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ reload_plugins_with_allowed_types("application/x-Second-Test,application/x-test,application/x-nonexistent");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ reload_plugins_with_allowed_types("application/x-Second-Test,application/x-nonexistent,application/x-test");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ reload_plugins_with_allowed_types("application/x-nonexistent,application/x-test,application/x-Second-Test");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ reload_plugins_with_allowed_types("application/x-nonexistent,application/x-Second-Test,application/x-test");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-Second-Test"), pluginDefaultState);
+ do_check_eq(get_status_for_type("application/x-nonexistent"), undefined);
+
+ // Plugin types are case-insensitive, so matching should be too.
+ reload_plugins_with_allowed_types("APPLICATION/X-TEST");
+ do_check_eq(get_status_for_type("application/x-test"), pluginDefaultState);
+ reload_plugins_with_allowed_types("application/x-test");
+ do_check_eq(get_status_for_type("APPLICATION/X-TEST"), pluginDefaultState);
+
+ // Types must match exactly, so supersets should not enable subset types.
+ reload_plugins_with_allowed_types("application/x-test-superset");
+ do_check_eq(get_status_for_type("application/x-test"), undefined);
+ reload_plugins_with_allowed_types("superset-application/x-test");
+ do_check_eq(get_status_for_type("application/x-test"), undefined);
+
+ // Clean up.
+ Services.prefs.clearUserPref("plugin.allowed_types");
+ Services.prefs.clearUserPref("plugin.importedState");
+}
diff --git a/dom/plugins/test/unit/test_bug471245.js b/dom/plugins/test/unit/test_bug471245.js
new file mode 100644
index 0000000000..8e38afcdc7
--- /dev/null
+++ b/dom/plugins/test/unit/test_bug471245.js
@@ -0,0 +1,23 @@
+/* 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/.
+ */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function run_test() {
+ allow_all_plugins();
+ do_get_profile_startup();
+
+ var plugin = get_test_plugintag();
+ do_check_true(plugin == null);
+
+ // Initialises a profile folder
+ do_get_profile();
+
+ var plugin = get_test_plugintag();
+ do_check_false(plugin == null);
+
+ // Clean up
+ Services.prefs.clearUserPref("plugin.importedState");
+}
diff --git a/dom/plugins/test/unit/test_bug813245.js b/dom/plugins/test/unit/test_bug813245.js
new file mode 100644
index 0000000000..069e4a0146
--- /dev/null
+++ b/dom/plugins/test/unit/test_bug813245.js
@@ -0,0 +1,87 @@
+/* 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/.
+ */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Plugin registry uses different field delimeters on different platforms
+var DELIM = mozinfo.os == "win" ? "|" : ":";
+
+var gProfD = do_get_profile_startup();
+
+// Writes out some plugin registry to the profile
+function write_registry(version, info) {
+ let runtime = Cc["@mozilla.org/xre/runtime;1"].getService(Ci.nsIXULRuntime);
+
+ var header = "Generated File. Do not edit.\n\n";
+ header += "[HEADER]\n";
+ header += "Version" + DELIM + version + DELIM + "$\n";
+ header += "Arch" + DELIM + runtime.XPCOMABI + DELIM + "$\n";
+ header += "\n";
+ header += "[PLUGINS]\n";
+
+ var registry = gProfD.clone();
+ registry.append("pluginreg.dat");
+ var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Components.interfaces.nsIFileOutputStream);
+ // write, create, truncate
+ foStream.init(registry, 0x02 | 0x08 | 0x20, 0o666, 0);
+
+ var charset = "UTF-8"; // Can be any character encoding name that Mozilla supports
+ var os = Cc["@mozilla.org/intl/converter-output-stream;1"].
+ createInstance(Ci.nsIConverterOutputStream);
+ os.init(foStream, charset, 0, 0x0000);
+
+ os.writeString(header);
+ os.writeString(info);
+ os.close();
+}
+
+function run_test() {
+ allow_all_plugins();
+ var plugin = get_test_plugintag();
+ do_check_true(plugin == null);
+
+ var file = get_test_plugin();
+ if (!file) {
+ do_throw("Plugin library not found");
+ }
+
+ // write plugin registry data
+ let registry = "";
+
+ if (gIsOSX) {
+ registry += file.leafName + DELIM + "$\n";
+ registry += file.path + DELIM + "$\n";
+ } else {
+ registry += file.path + DELIM + "$\n";
+ registry += DELIM + "$\n";
+ }
+ registry += "0.0.0.0" + DELIM + "$\n";
+ registry += "16725225600" + DELIM + "0" + DELIM + "5" + DELIM + "$\n";
+ registry += "Plug-in for testing purposes." + DELIM + "$\n";
+ registry += "Test Plug-in" + DELIM + "$\n";
+ registry += "999999999999999999999999999999999999999999999999|0|5|$\n";
+ registry += "0" + DELIM + "application/x-test" + DELIM + "Test mimetype" +
+ DELIM + "tst" + DELIM + "$\n";
+
+ registry += "\n";
+ registry += "[INVALID]\n";
+ registry += "\n";
+ write_registry("0.15", registry);
+
+ // Initialise profile folder
+ do_get_profile();
+
+ var plugin = get_test_plugintag();
+ if (!plugin)
+ do_throw("Plugin tag not found");
+
+ // The plugin registry should have been rejected.
+ // If not, the test plugin version would be 0.0.0.0
+ do_check_eq(plugin.version, "1.0.0.0");
+
+ // Clean up
+ Services.prefs.clearUserPref("plugin.importedState");
+}
diff --git a/dom/plugins/test/unit/test_bug854467.js b/dom/plugins/test/unit/test_bug854467.js
new file mode 100644
index 0000000000..0487dffc65
--- /dev/null
+++ b/dom/plugins/test/unit/test_bug854467.js
@@ -0,0 +1,40 @@
+/* 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/.
+ */
+
+function check_state(aTag, aExpectedClicktoplay, aExpectedDisabled) {
+ do_check_eq(aTag.clicktoplay, aExpectedClicktoplay);
+ do_check_eq(aTag.disabled, aExpectedDisabled);
+}
+
+function run_test() {
+ allow_all_plugins();
+ let tag = get_test_plugintag();
+ tag.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ check_state(tag, false, false);
+
+ /* test going to click-to-play from always enabled and back */
+ tag.enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
+ check_state(tag, true, false);
+ tag.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ check_state(tag, false, false);
+
+ /* test going to disabled from always enabled and back */
+ tag.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
+ check_state(tag, false, true);
+ tag.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ check_state(tag, false, false);
+
+ /* test going to click-to-play from disabled and back */
+ tag.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
+ check_state(tag, false, true);
+ tag.enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
+ check_state(tag, true, false);
+ tag.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
+ check_state(tag, false, true);
+
+ /* put everything back to normal and check that that succeeded */
+ tag.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ check_state(tag, false, false);
+}
diff --git a/dom/plugins/test/unit/test_nice_plugin_name.js b/dom/plugins/test/unit/test_nice_plugin_name.js
new file mode 100644
index 0000000000..32d2870af0
--- /dev/null
+++ b/dom/plugins/test/unit/test_nice_plugin_name.js
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
+const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+var gAppInfo = null;
+
+function createAppInfo(ID, name, version, platformVersion="1.0") {
+ let tmp = {};
+ Cu.import("resource://testing-common/AppInfo.jsm", tmp);
+ tmp.updateAppInfo({
+ ID, name, version, platformVersion,
+ crashReporter: true
+ });
+ gAppInfo = tmp.getAppInfo();
+}
+
+var gPluginHost = null;
+
+function test_expected_permission_string(aPermString) {
+ gPluginHost.reloadPlugins(false);
+ let plugin = get_test_plugintag();
+ do_check_false(plugin == null);
+ do_check_eq(gPluginHost.getPermissionStringForType("application/x-test"),
+ aPermString);
+}
+
+function run_test() {
+ allow_all_plugins();
+ do_check_true(gIsWindows || gIsOSX || gIsLinux);
+ do_check_true(!(gIsWindows && gIsOSX) && !(gIsWindows && gIsLinux) &&
+ !(gIsOSX && gIsLinux));
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+ gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+
+ let expectedDefaultPermissionString = null;
+ if (gIsWindows) expectedDefaultPermissionString = "plugin:nptest";
+ if (gIsOSX) expectedDefaultPermissionString = "plugin:test";
+ if (gIsLinux) expectedDefaultPermissionString = "plugin:libnptest";
+ test_expected_permission_string(expectedDefaultPermissionString);
+
+ let suffix = get_platform_specific_plugin_suffix();
+ let pluginFile = get_test_plugin_no_symlink();
+ let pluginDir = pluginFile.parent;
+ pluginFile.copyTo(null, "npblah235" + suffix);
+ let pluginCopy = pluginDir.clone();
+ pluginCopy.append("npblah235" + suffix);
+ let tempDir = do_get_tempdir();
+ pluginFile.moveTo(tempDir, null);
+ test_expected_permission_string("plugin:npblah");
+
+ pluginCopy.moveTo(null, "npasdf-3.2.2" + suffix);
+ test_expected_permission_string("plugin:npasdf");
+
+ pluginCopy.moveTo(null, "npasdf_##29387!{}{[][" + suffix);
+ test_expected_permission_string("plugin:npasdf");
+
+ pluginCopy.moveTo(null, "npqtplugin7" + suffix);
+ test_expected_permission_string("plugin:npqtplugin");
+
+ pluginCopy.moveTo(null, "npFoo3d" + suffix);
+ test_expected_permission_string("plugin:npfoo3d");
+
+ pluginCopy.moveTo(null, "NPSWF32_11_5_502_146" + suffix);
+ test_expected_permission_string("plugin:npswf");
+
+ pluginCopy.remove(true);
+ pluginFile.moveTo(pluginDir, null);
+ test_expected_permission_string(expectedDefaultPermissionString);
+
+ // Clean up
+ Services.prefs.clearUserPref("plugin.importedState");
+}
diff --git a/dom/plugins/test/unit/test_persist_in_prefs.js b/dom/plugins/test/unit/test_persist_in_prefs.js
new file mode 100644
index 0000000000..ede0074d39
--- /dev/null
+++ b/dom/plugins/test/unit/test_persist_in_prefs.js
@@ -0,0 +1,76 @@
+/* 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/.
+ */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Plugin registry uses different field delimeters on different platforms
+var DELIM = mozinfo.os == "win" ? "|" : ":";
+
+var gProfD = do_get_profile_startup();
+
+function run_test() {
+ allow_all_plugins();
+
+ do_check_true(gIsWindows || gIsOSX || gIsLinux);
+
+ let file = get_test_plugin_no_symlink();
+ if (!file)
+ do_throw("Plugin library not found");
+
+ const pluginDir = file.parent;
+ const tempDir = do_get_tempdir();
+ const suffix = get_platform_specific_plugin_suffix();
+ const pluginName = file.leafName.substring(0, file.leafName.length - suffix.length).toLowerCase();
+ const pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+ const statePref = "plugin.state." + pluginName;
+
+ // Initialise profile folder
+ do_get_profile();
+
+ let plugin = get_test_plugintag();
+ if (!plugin)
+ do_throw("Plugin tag not found");
+
+ plugin.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
+
+ // prepare a copy of the plugin and backup the original
+ file.copyTo(null, "nptestcopy" + suffix);
+ let copy = pluginDir.clone();
+ copy.append("nptestcopy" + suffix);
+ file.moveTo(tempDir, null);
+
+ // test that the settings persist through a few variations of test-plugin names
+ let testNames = [
+ pluginName + "2",
+ pluginName.toUpperCase() + "_11_5_42_2323",
+ pluginName + "-5.2.7"
+ ];
+ testNames.forEach(function(leafName) {
+ dump("Checking " + leafName + ".\n");
+ copy.moveTo(null, leafName + suffix);
+ pluginHost.reloadPlugins(false);
+ plugin = get_test_plugintag();
+ do_check_false(plugin == null);
+ do_check_true(plugin.disabled);
+ do_check_false(plugin.clicktoplay);
+ });
+
+ // check that the state persists even if the plugin is not always present
+ copy.moveTo(tempDir, null);
+ pluginHost.reloadPlugins(false);
+ copy.moveTo(pluginDir, null);
+ pluginHost.reloadPlugins(false);
+
+ plugin = get_test_plugintag();
+ do_check_false(plugin == null);
+ do_check_true(plugin.disabled);
+ do_check_false(plugin.clicktoplay);
+
+ // clean up
+ Services.prefs.clearUserPref(statePref);
+ Services.prefs.clearUserPref("plugin.importedState");
+ copy.remove(true);
+ file.moveTo(pluginDir, null);
+}
diff --git a/dom/plugins/test/unit/test_plugin_default_state.js b/dom/plugins/test/unit/test_plugin_default_state.js
new file mode 100644
index 0000000000..cb68c61e7e
--- /dev/null
+++ b/dom/plugins/test/unit/test_plugin_default_state.js
@@ -0,0 +1,31 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function run_test() {
+ allow_all_plugins();
+ let pluginDefaultState = Services.prefs.getIntPref("plugin.default.state");
+ // if this fails, we just have to switch around the values we're testing
+ do_check_neq(pluginDefaultState, Ci.nsIPluginTag.STATE_DISABLED);
+ let nonDefaultState = (pluginDefaultState != Ci.nsIPluginTag.STATE_ENABLED ?
+ Ci.nsIPluginTag.STATE_ENABLED :
+ Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+ let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+ let testPlugin = get_test_plugintag();
+ // the test plugin should have the default enabledState
+ do_check_eq(testPlugin.enabledState, pluginDefaultState);
+
+ let secondTestPlugin = get_test_plugintag("Second Test Plug-in");
+ // set an enabledState for the second test plugin
+ secondTestPlugin.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
+ // change what the default enabledState is
+ Services.prefs.setIntPref("plugin.default.state", nonDefaultState);
+ // the test plugin should follow the default (it has no individual pref yet)
+ do_check_eq(testPlugin.enabledState, nonDefaultState);
+ // the second test plugin should retain its preferred state
+ do_check_eq(secondTestPlugin.enabledState, Ci.nsIPluginTag.STATE_DISABLED);
+
+ // clean up
+ testPlugin.enabledState = pluginDefaultState;
+ secondTestPlugin.enabledState = pluginDefaultState;
+ Services.prefs.clearUserPref("plugin.default.state");
+ Services.prefs.clearUserPref("plugin.importedState");
+}
diff --git a/dom/plugins/test/unit/test_plugin_default_state_xpi.js b/dom/plugins/test/unit/test_plugin_default_state_xpi.js
new file mode 100644
index 0000000000..f1aeb3ac9e
--- /dev/null
+++ b/dom/plugins/test/unit/test_plugin_default_state_xpi.js
@@ -0,0 +1,118 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
+
+const ADDON_ID = "test-plugin-from-xpi@tests.mozilla.org";
+const XRE_EXTENSIONS_DIR_LIST = "XREExtDL";
+const NS_APP_PLUGINS_DIR_LIST = "APluginsDL";
+
+const gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+const gXPCOMABI = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).XPCOMABI;
+var gProfileDir = null;
+
+function getAddonRoot(profileDir, id) {
+ let dir = profileDir.clone();
+ dir.append("extensions");
+ Assert.ok(dir.exists(), "Extensions dir should exist: " + dir.path);
+ dir.append(id);
+ return dir;
+}
+
+function getTestaddonFilename() {
+ let abiPart = "";
+ if (gIsOSX) {
+ abiPart = "_" + gXPCOMABI;
+ }
+ return "testaddon" + abiPart + ".xpi";
+}
+
+function run_test() {
+ allow_all_plugins();
+ loadAddonManager();
+ gProfileDir = do_get_profile();
+ do_register_cleanup(() => shutdownManager());
+ run_next_test();
+}
+
+add_task(function* test_state() {
+ // Remove test so we will have only one "Test Plug-in" registered.
+ // xpcshell tests have plugins in per-test profiles, so that's fine.
+ let file = get_test_plugin();
+ file.remove(true);
+ file = get_test_plugin(true);
+ file.remove(true);
+
+ Services.prefs.setIntPref("plugin.default.state", Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+ Services.prefs.setIntPref("plugin.defaultXpi.state", Ci.nsIPluginTag.STATE_ENABLED);
+
+ let success = yield installAddon(getTestaddonFilename());
+ Assert.ok(success, "Should have installed addon.");
+ let addonDir = getAddonRoot(gProfileDir, ADDON_ID);
+
+ let provider = {
+ classID: Components.ID("{0af6b2d7-a06c-49b7-babc-636d292b0dbb}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider,
+ Ci.nsIDirectoryServiceProvider2]),
+
+ getFile: function (prop, persistant) {
+ throw Cr.NS_ERROR_FAILURE;
+ },
+
+ getFiles: function (prop) {
+ let result = [];
+
+ switch (prop) {
+ case XRE_EXTENSIONS_DIR_LIST:
+ result.push(addonDir);
+ break;
+ case NS_APP_PLUGINS_DIR_LIST:
+ let pluginDir = addonDir.clone();
+ pluginDir.append("plugins");
+ result.push(pluginDir);
+ break;
+ default:
+ throw Cr.NS_ERROR_FAILURE;
+ }
+
+ return {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]),
+ hasMoreElements: () => result.length > 0,
+ getNext: () => result.shift(),
+ };
+ },
+ };
+
+ let dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+ dirSvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
+
+ // We installed a non-restartless addon, need to restart the manager.
+ restartManager();
+ gPluginHost.reloadPlugins();
+
+ Assert.ok(addonDir.exists(), "Addon path should exist: " + addonDir.path);
+ Assert.ok(addonDir.isDirectory(), "Addon path should be a directory: " + addonDir.path);
+ let pluginDir = addonDir.clone();
+ pluginDir.append("plugins");
+ Assert.ok(pluginDir.exists(), "Addon plugins path should exist: " + pluginDir.path);
+ Assert.ok(pluginDir.isDirectory(), "Addon plugins path should be a directory: " + pluginDir.path);
+
+ let addon = yield getAddonByID(ADDON_ID);
+ Assert.ok(!addon.appDisabled, "Addon should not be appDisabled");
+ Assert.ok(addon.isActive, "Addon should be active");
+ Assert.ok(addon.isCompatible, "Addon should be compatible");
+ Assert.ok(!addon.userDisabled, "Addon should not be user disabled");
+
+ let testPlugin = get_test_plugintag();
+ Assert.notEqual(testPlugin, null, "Test plugin should have been found");
+ Assert.equal(testPlugin.enabledState, Ci.nsIPluginTag.STATE_ENABLED, "Test plugin from addon should have state enabled");
+
+ pluginDir.append(testPlugin.filename);
+ Assert.ok(pluginDir.exists(), "Plugin file should exist in addon directory: " + pluginDir.path);
+
+ testPlugin = get_test_plugintag("Second Test Plug-in");
+ Assert.notEqual(testPlugin, null, "Second test plugin should have been found");
+ Assert.equal(testPlugin.enabledState, Ci.nsIPluginTag.STATE_ENABLED, "Second test plugin from addon should have state enabled");
+});
diff --git a/dom/plugins/test/unit/xpcshell.ini b/dom/plugins/test/unit/xpcshell.ini
new file mode 100644
index 0000000000..8dae66b208
--- /dev/null
+++ b/dom/plugins/test/unit/xpcshell.ini
@@ -0,0 +1,29 @@
+[DEFAULT]
+skip-if = toolkit == 'android'
+head = head_plugins.js
+tail =
+tags = addons
+firefox-appdir = browser
+support-files =
+ !/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+
+[test_allowed_types.js]
+skip-if = appname == "thunderbird"
+reason = plugins are disabled by default in Thunderbird
+[test_bug471245.js]
+# Bug 676953: test fails consistently on Android
+fail-if = os == "android"
+[test_bug813245.js]
+# Bug 676953: test fails consistently on Android
+fail-if = os == "android"
+[test_nice_plugin_name.js]
+# Bug 676953: test fails consistently on Android
+fail-if = os == "android"
+[test_persist_in_prefs.js]
+skip-if = appname == "thunderbird"
+reason = plugins are disabled by default in Thunderbird
+[test_bug854467.js]
+[test_plugin_default_state.js]
+skip-if = appname == "thunderbird"
+reason = plugins are disabled by default in Thunderbird
+[test_plugin_default_state_xpi.js]