diff --git a/dom/plugins/test/mochitest/browser.ini b/dom/plugins/test/mochitest/browser.ini index a0dc6d4807d..b169c46f279 100644 --- a/dom/plugins/test/mochitest/browser.ini +++ b/dom/plugins/test/mochitest/browser.ini @@ -1,8 +1,13 @@ [DEFAULT] support-files = + head.js plugin_test.html [browser_bug1163570.js] skip-if = (!e10s || os != "win") [browser_bug1196539.js] skip-if = (!e10s || os != "win") +[browser_tabswitchbetweenplugins.js] +skip-if = (!e10s || os != "win") +[browser_pluginscroll.js] +skip-if = (!e10s || os != "win") diff --git a/dom/plugins/test/mochitest/browser_bug1163570.js b/dom/plugins/test/mochitest/browser_bug1163570.js index 6f55fe371e0..817776bd933 100644 --- a/dom/plugins/test/mochitest/browser_bug1163570.js +++ b/dom/plugins/test/mochitest/browser_bug1163570.js @@ -1,27 +1,5 @@ var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); -// Returns the chrome side nsIPluginTag for this plugin -function getTestPlugin(aName) { - let pluginName = aName || "Test Plug-in"; - let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.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; -} - -// Set the test plugin state, disabling features like click-to-play -function setTestPluginEnabledState(newEnabledState, pluginName) { - let name = pluginName || "Test Plug-in"; - let plugin = getTestPlugin(name); - plugin.enabledState = newEnabledState; -} - // simple tab load helper, pilfered from browser plugin tests function promiseTabLoad(tab, url, eventType="load") { return new Promise((resolve, reject) => { diff --git a/dom/plugins/test/mochitest/browser_bug1196539.js b/dom/plugins/test/mochitest/browser_bug1196539.js index 874bdd19f92..be84b63f305 100644 --- a/dom/plugins/test/mochitest/browser_bug1196539.js +++ b/dom/plugins/test/mochitest/browser_bug1196539.js @@ -1,45 +1,5 @@ let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); -// Returns the chrome side nsIPluginTag for this plugin -function getTestPlugin(aName) { - let pluginName = aName || "Test Plug-in"; - let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.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; -} - -// Set the test plugin state, disabling features like click-to-play -function setTestPluginEnabledState(newEnabledState, pluginName) { - let name = pluginName || "Test Plug-in"; - let plugin = getTestPlugin(name); - plugin.enabledState = newEnabledState; -} - -function promiseNewTabSwitched() { - return new Promise(resolve => { - gBrowser.addEventListener("TabSwitchDone", function onSwitch() { - gBrowser.removeEventListener("TabSwitchDone", onSwitch); - executeSoon(resolve); - }); - }); -} - -function waitForMs(aMs) { - return new Promise((resolve) => { - setTimeout(done, aMs); - function done() { - resolve(true); - } - }); -} - 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); @@ -82,7 +42,7 @@ add_task(function* () { }); // select plugin tab - tabSwitchedPromise = promiseNewTabSwitched(); + tabSwitchedPromise = waitTabSwitched(); gBrowser.selectedTab = pluginTab; yield tabSwitchedPromise; @@ -105,7 +65,7 @@ add_task(function* () { checkPaintCount(result); // select home tab - tabSwitchedPromise = promiseNewTabSwitched(); + tabSwitchedPromise = waitTabSwitched(); gBrowser.selectedTab = homeTab; yield tabSwitchedPromise; @@ -142,7 +102,7 @@ add_task(function* () { }); // select plugin tab - tabSwitchedPromise = promiseNewTabSwitched(); + tabSwitchedPromise = waitTabSwitched(); gBrowser.selectedTab = pluginTab; yield tabSwitchedPromise; diff --git a/dom/plugins/test/mochitest/browser_pluginscroll.js b/dom/plugins/test/mochitest/browser_pluginscroll.js new file mode 100644 index 00000000000..6851fc76123 --- /dev/null +++ b/dom/plugins/test/mochitest/browser_pluginscroll.js @@ -0,0 +1,173 @@ +let 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) + }; +} + +let apzEnabled = Preferences.get("layers.async-pan-zoom.enabled", false); +let 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. + */ + +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 visibility when scrolling with keyboard shortcuts. This circumvents apz + * and relies on dom scroll, which is what we want to target for this test. Note + * this test should only run with e10s since we do not hide plugin windows when + * scrolling in single process mode. + */ + +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); +}); diff --git a/dom/plugins/test/mochitest/browser_tabswitchbetweenplugins.js b/dom/plugins/test/mochitest/browser_tabswitchbetweenplugins.js new file mode 100644 index 00000000000..9fad17a6e16 --- /dev/null +++ b/dom/plugins/test/mochitest/browser_tabswitchbetweenplugins.js @@ -0,0 +1,105 @@ +let 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/head.js b/dom/plugins/test/mochitest/head.js new file mode 100644 index 00000000000..75117975834 --- /dev/null +++ b/dom/plugins/test/mochitest/head.js @@ -0,0 +1,134 @@ + +/** + * 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"; + let plugin = getTestPlugin(name); + plugin.enabledState = aState; +} + +/** + * 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/plugin_test.html b/dom/plugins/test/mochitest/plugin_test.html index bc2e2f742c7..55904efd2a6 100644 --- a/dom/plugins/test/mochitest/plugin_test.html +++ b/dom/plugins/test/mochitest/plugin_test.html @@ -6,5 +6,6 @@