This commit is contained in:
Wes Kocher 2015-05-01 17:19:28 -07:00
Родитель fe297cf6c4 b9b0d2b3c3
Коммит 5ef0b7b6a7
76 изменённых файлов: 3487 добавлений и 2851 удалений

Просмотреть файл

@ -0,0 +1,78 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cm = Components.manager;
const kBlocklistServiceUUID = "{66354bc9-7ed1-4692-ae1d-8da97d6b205e}";
const kBlocklistServiceContractID = "@mozilla.org/extensions/blocklist;1";
const kBlocklistServiceFactory = Cm.getClassObject(Cc[kBlocklistServiceContractID], Ci.nsIFactory);
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
/*
* A lightweight blocklist proxy for the testing purposes.
*/
let BlocklistProxy = {
_uuid: null,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsIBlocklistService,
Ci.nsITimerCallback]),
init: function() {
if (!this._uuid) {
this._uuid =
Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator)
.generateUUID();
Cm.nsIComponentRegistrar.registerFactory(this._uuid, "",
"@mozilla.org/extensions/blocklist;1",
this);
}
},
uninit: function() {
if (this._uuid) {
Cm.nsIComponentRegistrar.unregisterFactory(this._uuid, this);
Cm.nsIComponentRegistrar.registerFactory(Components.ID(kBlocklistServiceUUID),
"Blocklist Service",
"@mozilla.org/extensions/blocklist;1",
kBlocklistServiceFactory);
this._uuid = null;
}
},
notify: function (aTimer) {
},
observe: function (aSubject, aTopic, aData) {
},
isAddonBlocklisted: function (aAddon, aAppVersion, aToolkitVersion) {
return false;
},
getAddonBlocklistState: function (aAddon, aAppVersion, aToolkitVersion) {
return 0; // STATE_NOT_BLOCKED
},
getPluginBlocklistState: function (aPluginTag, aAppVersion, aToolkitVersion) {
return 0; // STATE_NOT_BLOCKED
},
getAddonBlocklistURL: function (aAddon, aAppVersion, aToolkitVersion) {
return "";
},
getPluginBlocklistURL: function (aPluginTag) {
return "";
},
getPluginInfoURL: function (aPluginTag) {
return "";
},
}
BlocklistProxy.init();
addEventListener("unload", () => {
BlocklistProxy.uninit();
});

Просмотреть файл

@ -1,12 +1,7 @@
[DEFAULT] [DEFAULT]
# These tests all fail with e10s enabled. skip-if = buildapp == "mulet"
# * Bug 899347 - no e10s click-to-play support
# * Bug 921916 - no plugin events
# * Bug XXXXX - no plugins in content processes ("Error: You cannot use the AddonManager in child processes!")
# * Bug 866413 - PageInfo doesn't work in e10s [browser_pageInfo_plugins.js]
# * Bug 921957 - remote webprogress doesn't supply originalURI attribute on the request object [browser_clearplugindata.js]
skip-if = buildapp == "mulet" || e10s
support-files = support-files =
blocklist_proxy.js
blockNoPlugins.xml blockNoPlugins.xml
blockPluginHard.xml blockPluginHard.xml
blockPluginInfoURL.xml blockPluginInfoURL.xml
@ -53,12 +48,14 @@ support-files =
[browser_bug818118.js] [browser_bug818118.js]
[browser_bug820497.js] [browser_bug820497.js]
[browser_clearplugindata.js] [browser_clearplugindata.js]
skip-if = e10s # bug 1149253
[browser_CTP_context_menu.js] [browser_CTP_context_menu.js]
skip-if = toolkit == "gtk2" || toolkit == "gtk3" # browser_CTP_context_menu.js fails intermittently on Linux (bug 909342) skip-if = toolkit == "gtk2" || toolkit == "gtk3" # fails intermittently on Linux (bug 909342)
[browser_CTP_crashreporting.js] [browser_CTP_crashreporting.js]
skip-if = !crashreporter skip-if = !crashreporter
[browser_CTP_data_urls.js] [browser_CTP_data_urls.js]
[browser_CTP_drag_drop.js] [browser_CTP_drag_drop.js]
skip-if = e10s # misc. issues, bug 1156871
[browser_CTP_hide_overlay.js] [browser_CTP_hide_overlay.js]
[browser_CTP_iframe.js] [browser_CTP_iframe.js]
skip-if = os == 'linux' || os == 'mac' # Bug 984821 skip-if = os == 'linux' || os == 'mac' # Bug 984821
@ -69,14 +66,26 @@ skip-if = os == 'linux' || os == 'mac' # Bug 984821
[browser_CTP_remove_navigate.js] [browser_CTP_remove_navigate.js]
[browser_CTP_resize.js] [browser_CTP_resize.js]
[browser_CTP_zoom.js] [browser_CTP_zoom.js]
[browser_globalplugin_crashinfobar.js] [browser_blocking.js]
[browser_pageInfo_plugins.js] [browser_plugins_added_dynamically.js]
[browser_pluginnotification.js] [browser_pluginnotification.js]
[browser_pluginplaypreview.js] [browser_plugin_infolink.js]
[browser_pluginplaypreview2.js] skip-if = e10s # bug 1160166
[browser_pluginplaypreview3.js] [browser_plugin_reloading.js]
[browser_blocklist_content.js]
skip-if = !e10s
[browser_globalplugin_crashinfobar.js]
skip-if = !crashreporter
[browser_pluginCrashCommentAndURL.js] [browser_pluginCrashCommentAndURL.js]
skip-if = !crashreporter skip-if = !crashreporter
[browser_plugins_added_dynamically.js] [browser_pageInfo_plugins.js]
skip-if = e10s # Bug 866413
[browser_pluginplaypreview.js]
skip-if = e10s # bug 1148827
[browser_pluginplaypreview2.js]
skip-if = e10s # bug 1148827
[browser_pluginplaypreview3.js]
skip-if = e10s # bug 1148827
[browser_pluginCrashReportNonDeterminism.js] [browser_pluginCrashReportNonDeterminism.js]
skip-if = !crashreporter || os == 'linux' # Bug 1152811 skip-if = !crashreporter || os == 'linux' # Bug 1152811

Просмотреть файл

@ -1,116 +1,69 @@
var rootDir = getRootDirectory(gTestPath); let rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
var gTestBrowser = null; add_task(function* () {
var gNextTest = null; registerCleanupFunction(function () {
var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions(); clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
gBrowser.removeCurrentTab();
window.focus();
}); });
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); });
let newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
prepareTest(runAfterPluginBindingAttached(test1), gHttpTestRoot + "plugin_test.html");
}
function finishTest() {
clearAllPluginPermissions();
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() {
// The plugin events are async dispatched and can come after the load event
// This just allows the events to fire before we then go on to test the states
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) {
gNextTest = nextTest;
gTestBrowser.contentWindow.location = url;
}
// Due to layout being async, "PluginBindAttached" may trigger later.
// This wraps a function to force a layout flush, thus triggering it,
// and schedules the function execution so they're definitely executed
// afterwards.
function runAfterPluginBindingAttached(func) {
return function() {
let doc = gTestBrowser.contentDocument;
let elems = doc.getElementsByTagName('embed');
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(func);
};
}
// Test that the activate action in content menus for CTP plugins works // Test that the activate action in content menus for CTP plugins works
function test1() { add_task(function* () {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
gBrowser.selectedTab = gBrowser.addTab();
Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
let bindingPromise = waitForEvent(gBrowser.selectedBrowser, "PluginBindingAttached", null, true, true);
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
yield promiseUpdatePluginBindings(gBrowser.selectedBrowser);
yield bindingPromise;
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser);
ok(popupNotification, "Test 1, Should have a click-to-play notification"); ok(popupNotification, "Test 1, Should have a click-to-play notification");
let plugin = gTestBrowser.contentDocument.getElementById("test"); // check plugin state
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); let pluginInfo = yield promiseForPluginInfo("test", gBrowser.selectedBrowser);
ok(!objLoadingContent.activated, "Test 1, Plugin should not be activated"); ok(!pluginInfo.activated, "plugin should not be activated");
// Display a context menu on the test plugin so we can test
// activation menu options.
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
let plugin = content.document.getElementById("test");
let bounds = plugin.getBoundingClientRect();
let left = (bounds.left + bounds.right) / 2;
let top = (bounds.top + bounds.bottom) / 2;
let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendMouseEvent("contextmenu", left, top, 2, 1, 0);
});
// When the popupshown DOM event is fired, the actual showing of the popup popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser);
// may still be pending. Clear the event loop before continuing so that ok(popupNotification, "Should have a click-to-play notification");
// subsequently-opened popups aren't cancelled by accident. ok(popupNotification.dismissed, "notification should be dismissed");
let goToNext = function(aEvent) {
window.document.removeEventListener("popupshown", goToNext, false);
executeSoon(function() {
test2();
aEvent.target.hidePopup();
});
};
window.document.addEventListener("popupshown", goToNext, false);
EventUtils.synthesizeMouseAtCenter(plugin,
{ type: "contextmenu", button: 2 },
gTestBrowser.contentWindow);
}
function test2() { // fixes a occasional test timeout on win7 opt
let activate = window.document.getElementById("context-ctp-play"); yield promiseForCondition(() => document.getElementById("context-ctp-play"));
ok(activate, "Test 2, Should have a context menu entry for activating the plugin");
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); let actMenuItem = document.getElementById("context-ctp-play");
ok(notification, "Test 2, Should have a click-to-play notification"); ok(actMenuItem, "Should have a context menu entry for activating the plugin");
ok(notification.dismissed, "Test 2, notification should be dismissed");
// Trigger the click-to-play popup // Activate the plugin via the context menu
activate.doCommand(); EventUtils.synthesizeMouseAtCenter(actMenuItem, {});
waitForCondition(() => !notification.dismissed, yield promiseForCondition(() => !PopupNotifications.panel.dismissed && PopupNotifications.panel.firstChild);
test3, "Test 2, waited too long for context activation");
}
function test3() {
// Activate the plugin // Activate the plugin
PopupNotifications.panel.firstChild._primaryButton.click(); PopupNotifications.panel.firstChild._primaryButton.click();
let plugin = gTestBrowser.contentDocument.getElementById("test"); // check plugin state
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); pluginInfo = yield promiseForPluginInfo("test", gBrowser.selectedBrowser);
waitForCondition(() => objLoadingContent.activated, test4, "Waited too long for plugin to activate"); ok(pluginInfo.activated, "plugin should not be activated");
} });
function test4() {
finishTest();
}

Просмотреть файл

@ -1,10 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public let rootDir = getRootDirectory(gTestPath);
* License, v. 2.0. If a copy of the MPL was not distributed with this const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs"; const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
const PLUGIN_PAGE = getRootDirectory(gTestPath) + "plugin_big.html"; const PLUGIN_PAGE = gTestRoot + "plugin_big.html";
const PLUGIN_SMALL_PAGE = getRootDirectory(gTestPath) + "plugin_small.html"; const PLUGIN_SMALL_PAGE = gTestRoot + "plugin_small.html";
/** /**
* Takes an nsIPropertyBag and converts it into a JavaScript Object. It * Takes an nsIPropertyBag and converts it into a JavaScript Object. It
@ -30,14 +28,8 @@ function convertPropertyBag(aBag) {
return result; return result;
} }
function promisePopupNotificationShown(notificationID) {
return new Promise((resolve) => {
waitForNotificationShown(notificationID, resolve);
});
}
add_task(function* setup() { add_task(function* setup() {
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
// The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables plugin // The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables plugin
// crash reports. This test needs them enabled. The test also needs a mock // crash reports. This test needs them enabled. The test also needs a mock
@ -51,9 +43,17 @@ add_task(function* setup() {
env.set("MOZ_CRASHREPORTER_NO_REPORT", ""); env.set("MOZ_CRASHREPORTER_NO_REPORT", "");
env.set("MOZ_CRASHREPORTER_URL", SERVER_URL); env.set("MOZ_CRASHREPORTER_URL", SERVER_URL);
Services.prefs.setBoolPref("plugins.click_to_play", true);
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
registerCleanupFunction(function cleanUp() { registerCleanupFunction(function cleanUp() {
clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport); env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport);
env.set("MOZ_CRASHREPORTER_URL", serverURL); env.set("MOZ_CRASHREPORTER_URL", serverURL);
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
window.focus();
}); });
}); });
@ -66,20 +66,15 @@ add_task(function*() {
gBrowser, gBrowser,
url: PLUGIN_PAGE, url: PLUGIN_PAGE,
}, function* (browser) { }, function* (browser) {
let activated = yield ContentTask.spawn(browser, null, function*() { // Work around for delayed PluginBindingAttached
let plugin = content.document.getElementById("test"); yield promiseUpdatePluginBindings(browser);
return plugin.QueryInterface(Ci.nsIObjectLoadingContent).activated;
});
ok(!activated, "Plugin should not be activated");
// Open up the click-to-play notification popup... let pluginInfo = yield promiseForPluginInfo("test", browser);
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", ok(!pluginInfo.activated, "Plugin should not be activated");
browser);
ok(popupNotification, "Should have a click-to-play notification");
yield promisePopupNotificationShown(popupNotification); // Simulate clicking the "Allow Always" button.
let notification = PopupNotifications.getNotification("click-to-play-plugins", browser);
// The primary button in the popup should activate the plugin. yield promiseForNotificationShown(notification, browser);
PopupNotifications.panel.firstChild._primaryButton.click(); PopupNotifications.panel.firstChild._primaryButton.click();
// Prepare a crash report topic observer that only returns when // Prepare a crash report topic observer that only returns when
@ -99,8 +94,9 @@ add_task(function*() {
}, "Waited too long for plugin to activate."); }, "Waited too long for plugin to activate.");
try { try {
plugin.crash(); Components.utils.waiveXrays(plugin).crash();
} catch(e) {} } catch(e) {
}
let doc = plugin.ownerDocument; let doc = plugin.ownerDocument;
@ -177,21 +173,11 @@ add_task(function*() {
gBrowser, gBrowser,
url: PLUGIN_SMALL_PAGE, url: PLUGIN_SMALL_PAGE,
}, function* (browser) { }, function* (browser) {
let activated = yield ContentTask.spawn(browser, null, function*() { // Work around for delayed PluginBindingAttached
let plugin = content.document.getElementById("test"); yield promiseUpdatePluginBindings(browser);
return plugin.QueryInterface(Ci.nsIObjectLoadingContent).activated;
});
ok(!activated, "Plugin should not be activated");
// Open up the click-to-play notification popup... let pluginInfo = yield promiseForPluginInfo("test", browser);
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", ok(pluginInfo.activated, "Plugin should be activated from previous test");
browser);
ok(popupNotification, "Should have a click-to-play notification");
yield promisePopupNotificationShown(popupNotification);
// The primary button in the popup should activate the plugin.
PopupNotifications.panel.firstChild._primaryButton.click();
// Prepare a crash report topic observer that only returns when // Prepare a crash report topic observer that only returns when
// the crash report has been successfully sent. // the crash report has been successfully sent.
@ -210,7 +196,7 @@ add_task(function*() {
}, "Waited too long for plugin to activate."); }, "Waited too long for plugin to activate.");
try { try {
plugin.crash(); Components.utils.waiveXrays(plugin).crash();
} catch(e) {} } catch(e) {}
}); });
@ -244,4 +230,4 @@ add_task(function*() {
is(crashData.extra.PluginContentURL, undefined, is(crashData.extra.PluginContentURL, undefined,
"URL should be absent from extra data when opt-in not checked"); "URL should be absent from extra data when opt-in not checked");
}); });
}); });

Просмотреть файл

@ -1,212 +1,255 @@
var rootDir = getRootDirectory(gTestPath); let rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); let gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
let gTestBrowser = null;
var gTestBrowser = null; add_task(function* () {
var gNextTest = null; registerCleanupFunction(function () {
var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions(); clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
gBrowser.removeCurrentTab();
window.focus();
gTestBrowser = null;
}); });
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
var newTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser; gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
Services.prefs.setBoolPref("plugins.click_to_play", true); Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in"); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
});
prepareTest(test1a, gHttpTestRoot + "plugin_data_url.html");
}
function finishTest() {
clearAllPluginPermissions();
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() {
// The plugin events are async dispatched and can come after the load event
// This just allows the events to fire before we then go on to test the states
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) {
gNextTest = nextTest;
gTestBrowser.contentWindow.location = url;
}
// Due to layout being async, "PluginBindAttached" may trigger later.
// This wraps a function to force a layout flush, thus triggering it,
// and schedules the function execution so they're definitely executed
// afterwards.
function runAfterPluginBindingAttached(func) {
return function() {
let doc = gTestBrowser.contentDocument;
let elems = doc.getElementsByTagName('embed');
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(func);
};
}
// Test that the click-to-play doorhanger still works when navigating to data URLs // Test that the click-to-play doorhanger still works when navigating to data URLs
function test1a() { add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_data_url.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "Test 1a, Should have a click-to-play notification"); ok(popupNotification, "Test 1a, Should have a click-to-play notification");
let plugin = gTestBrowser.contentDocument.getElementById("test"); let pluginInfo = yield promiseForPluginInfo("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); ok(!pluginInfo.activated, "Test 1a, plugin should not be activated");
ok(!objLoadingContent.activated, "Test 1a, Plugin should not be activated");
gNextTest = runAfterPluginBindingAttached(test1b); let loadPromise = promiseTabLoadEvent(gBrowser.selectedTab);
gTestBrowser.contentDocument.getElementById("data-link-1").click(); yield ContentTask.spawn(gTestBrowser, {}, function* () {
} // navigate forward to a page with 'test' in it
content.document.getElementById("data-link-1").click();
});
yield loadPromise;
function test1b() { // Work around for delayed PluginBindingAttached
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); yield promiseUpdatePluginBindings(gTestBrowser);
popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "Test 1b, Should have a click-to-play notification"); ok(popupNotification, "Test 1b, Should have a click-to-play notification");
let plugin = gTestBrowser.contentDocument.getElementById("test"); pluginInfo = yield promiseForPluginInfo("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); ok(!pluginInfo.activated, "Test 1b, plugin should not be activated");
ok(!objLoadingContent.activated, "Test 1b, Plugin should not be activated");
let promise = promisePopupNotification("click-to-play-plugins");
yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementById("test");
let bounds = plugin.getBoundingClientRect();
let left = (bounds.left + bounds.right) / 2;
let top = (bounds.top + bounds.bottom) / 2;
let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
});
yield promise;
// Simulate clicking the "Allow Always" button. // Simulate clicking the "Allow Always" button.
waitForNotificationShown(popupNotification, function() { let condition = function() !PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed &&
PopupNotifications.panel.firstChild._primaryButton.click(); PopupNotifications.panel.firstChild;
yield promiseForCondition(condition);
PopupNotifications.panel.firstChild._primaryButton.click();
let condition = function() objLoadingContent.activated; // check plugin state
waitForCondition(condition, test1c, "Test 1b, Waited too long for plugin to activate"); pluginInfo = yield promiseForPluginInfo("test");
}); ok(pluginInfo.activated, "Test 1b, plugin should be activated");
} });
function test1c() { // Test that the click-to-play notification doesn't break when navigating
// to data URLs with multiple plugins.
add_task(function* () {
// We click activated above
clearAllPluginPermissions(); clearAllPluginPermissions();
prepareTest(runAfterPluginBindingAttached(test2a), gHttpTestRoot + "plugin_data_url.html");
}
// Test that the click-to-play notification doesn't break when navigating to data URLs with multiple plugins yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_data_url.html");
function test2a() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "Test 2a, Should have a click-to-play notification");
let plugin = gTestBrowser.contentDocument.getElementById("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 2a, Plugin should not be activated");
gNextTest = runAfterPluginBindingAttached(test2b); // Work around for delayed PluginBindingAttached
gTestBrowser.contentDocument.getElementById("data-link-2").click(); yield promiseUpdatePluginBindings(gTestBrowser);
}
function test2b() {
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 2a, Should have a click-to-play notification");
let pluginInfo = yield promiseForPluginInfo("test");
ok(!pluginInfo.activated, "Test 2a, plugin should not be activated");
let loadPromise = promiseTabLoadEvent(gBrowser.selectedTab);
yield ContentTask.spawn(gTestBrowser, {}, function* () {
// navigate forward to a page with 'test1' & 'test2' in it
content.document.getElementById("data-link-2").click();
});
yield loadPromise;
// Work around for delayed PluginBindingAttached
yield ContentTask.spawn(gTestBrowser, {}, function* () {
content.document.getElementById("test1").clientTop;
content.document.getElementById("test2").clientTop;
});
pluginInfo = yield promiseForPluginInfo("test1");
ok(!pluginInfo.activated, "Test 2a, test1 should not be activated");
pluginInfo = yield promiseForPluginInfo("test2");
ok(!pluginInfo.activated, "Test 2a, test2 should not be activated");
notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 2b, Should have a click-to-play notification"); ok(notification, "Test 2b, Should have a click-to-play notification");
yield promiseForNotificationShown(notification);
// Simulate choosing "Allow now" for the test plugin // Simulate choosing "Allow now" for the test plugin
waitForNotificationShown(notification, function() { is(notification.options.pluginData.size, 2, "Test 2b, Should have two types of plugin in the notification");
is(notification.options.pluginData.size, 2, "Test 2b, Should have two types of plugin in the notification");
var centerAction = null; let centerAction = null;
for (var action of notification.options.pluginData.values()) { for (let action of notification.options.pluginData.values()) {
if (action.pluginName == "Test") { if (action.pluginName == "Test") {
centerAction = action; centerAction = action;
break; break;
}
} }
ok(centerAction, "Test 2b, found center action for the Test plugin"); }
ok(centerAction, "Test 2b, found center action for the Test plugin");
var centerItem = null; let centerItem = null;
for (var item of PopupNotifications.panel.firstChild.childNodes) { for (let item of PopupNotifications.panel.firstChild.childNodes) {
is(item.value, "block", "Test 2b, all plugins should start out blocked"); is(item.value, "block", "Test 2b, all plugins should start out blocked");
if (item.action == centerAction) { if (item.action == centerAction) {
centerItem = item; centerItem = item;
break; break;
}
} }
ok(centerItem, "Test 2b, found center item for the Test plugin"); }
ok(centerItem, "Test 2b, found center item for the Test plugin");
// "click" the button to activate the Test plugin // "click" the button to activate the Test plugin
centerItem.value = "allownow"; centerItem.value = "allownow";
PopupNotifications.panel.firstChild._primaryButton.click(); PopupNotifications.panel.firstChild._primaryButton.click();
let plugin = gTestBrowser.contentDocument.getElementById("test1"); // Work around for delayed PluginBindingAttached
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); yield promiseUpdatePluginBindings(gTestBrowser);
let condition = function() objLoadingContent.activated;
waitForCondition(condition, test2c, "Test 2b, Waited too long for plugin to activate");
});
}
function test2c() { // check plugin state
let plugin = gTestBrowser.contentDocument.getElementById("test1"); pluginInfo = yield promiseForPluginInfo("test1");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); ok(pluginInfo.activated, "Test 2b, plugin should be activated");
ok(objLoadingContent.activated, "Test 2c, Plugin should be activated"); });
add_task(function* () {
// We click activated above
clearAllPluginPermissions(); clearAllPluginPermissions();
prepareTest(runAfterPluginBindingAttached(test3a), gHttpTestRoot + "plugin_data_url.html");
} yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_data_url.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
});
// Test that when navigating to a data url, the plugin permission is inherited // Test that when navigating to a data url, the plugin permission is inherited
function test3a() { add_task(function* () {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "Test 3a, Should have a click-to-play notification"); ok(notification, "Test 3a, Should have a click-to-play notification");
let plugin = gTestBrowser.contentDocument.getElementById("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); // check plugin state
ok(!objLoadingContent.activated, "Test 3a, Plugin should not be activated"); let pluginInfo = yield promiseForPluginInfo("test");
ok(!pluginInfo.activated, "Test 3a, plugin should not be activated");
let promise = promisePopupNotification("click-to-play-plugins");
yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementById("test");
let bounds = plugin.getBoundingClientRect();
let left = (bounds.left + bounds.right) / 2;
let top = (bounds.top + bounds.bottom) / 2;
let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
});
yield promise;
// Simulate clicking the "Allow Always" button. // Simulate clicking the "Allow Always" button.
waitForNotificationShown(popupNotification, function() { let condition = function() !PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed &&
PopupNotifications.panel.firstChild._primaryButton.click(); PopupNotifications.panel.firstChild;
yield promiseForCondition(condition);
PopupNotifications.panel.firstChild._primaryButton.click();
let condition = function() objLoadingContent.activated; // check plugin state
waitForCondition(condition, test3b, "Test 3a, Waited too long for plugin to activate"); pluginInfo = yield promiseForPluginInfo("test");
ok(pluginInfo.activated, "Test 3a, plugin should be activated");
let loadPromise = promiseTabLoadEvent(gBrowser.selectedTab);
yield ContentTask.spawn(gTestBrowser, {}, function* () {
// navigate forward to a page with 'test' in it
content.document.getElementById("data-link-1").click();
}); });
} yield loadPromise;
function test3b() { // Work around for delayed PluginBindingAttached
gNextTest = test3c; yield promiseUpdatePluginBindings(gTestBrowser);
gTestBrowser.contentDocument.getElementById("data-link-1").click();
}
function test3c() { // check plugin state
let plugin = gTestBrowser.contentDocument.getElementById("test"); pluginInfo = yield promiseForPluginInfo("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); ok(pluginInfo.activated, "Test 3b, plugin should be activated");
ok(objLoadingContent.activated, "Test 3c, Plugin should be activated");
clearAllPluginPermissions(); clearAllPluginPermissions();
prepareTest(runAfterPluginBindingAttached(test4b), });
'data:text/html,<embed id="test" style="width: 200px; height: 200px" type="application/x-test"/>');
}
// Test that the click-to-play doorhanger still works when directly navigating to data URLs // Test that the click-to-play doorhanger still works
function test4a() { // when directly navigating to data URLs.
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); // Fails, bug XXX. Plugins plus a data url don't fire a load event.
ok(popupNotification, "Test 4a, Should have a click-to-play notification"); /*
let plugin = gTestBrowser.contentDocument.getElementById("test"); add_task(function* () {
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); yield promiseTabLoadEvent(gBrowser.selectedTab,
ok(!objLoadingContent.activated, "Test 4a, Plugin should not be activated"); "data:text/html,Hi!<embed id='test' style='width:200px; height:200px' type='application/x-test'/>");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 4a, Should have a click-to-play notification");
// check plugin state
let pluginInfo = yield promiseForPluginInfo("test");
ok(!pluginInfo.activated, "Test 4a, plugin should not be activated");
let promise = promisePopupNotification("click-to-play-plugins");
yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementById("test");
let bounds = plugin.getBoundingClientRect();
let left = (bounds.left + bounds.right) / 2;
let top = (bounds.top + bounds.bottom) / 2;
let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
});
yield promise;
// Simulate clicking the "Allow Always" button. // Simulate clicking the "Allow Always" button.
waitForNotificationShown(popupNotification, function() { let condition = function() !PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed &&
PopupNotifications.panel.firstChild._primaryButton.click(); PopupNotifications.panel.firstChild;
yield promiseForCondition(condition);
PopupNotifications.panel.firstChild._primaryButton.click();
let condition = function() objLoadingContent.activated; // check plugin state
waitForCondition(condition, test4b, "Test 4a, Waited too long for plugin to activate"); pluginInfo = yield promiseForPluginInfo("test");
}); ok(pluginInfo.activated, "Test 4a, plugin should be activated");
} });
*/
function test4b() {
clearAllPluginPermissions();
finishTest();
}

Просмотреть файл

@ -1,102 +1,96 @@
/* Any copyright is dedicated to the Public Domain. let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
* http://creativecommons.org/publicdomain/zero/1.0/ */
let gHttpTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
let gNextTest = null;
let gNewWindow = null; let gNewWindow = null;
Components.utils.import("resource://gre/modules/Services.jsm"); add_task(function* () {
registerCleanupFunction(function () {
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions(); clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play"); Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
gNewWindow.close();
gNewWindow = null;
window.focus();
}); });
});
add_task(function* () {
Services.prefs.setBoolPref("plugins.click_to_play", true); Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("PluginBindingAttached", handleEvent, true, true);
gNextTest = part1;
gBrowser.selectedBrowser.contentDocument.location = gHttpTestRoot + "plugin_test.html";
}
function handleEvent() { setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
gNextTest();
}
function part1() { yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
gBrowser.selectedBrowser.removeEventListener("PluginBindingAttached", handleEvent);
waitForNotificationPopup("click-to-play-plugins", gBrowser.selectedBrowser, () => {
gNextTest = part2;
gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
gNewWindow.addEventListener("load", handleEvent, true);
});
}
function part2() { // Work around for delayed PluginBindingAttached
gNewWindow.removeEventListener("load", handleEvent); yield promiseUpdatePluginBindings(gBrowser.selectedBrowser);
let condition = function() PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser);
waitForCondition(condition, part3, "Waited too long for click-to-play notification"); yield promisePopupNotification("click-to-play-plugins");
} });
add_task(function* () {
gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
// XXX technically can't load fire before we get this call???
yield waitForEvent(gNewWindow, "load", null, true);
yield promisePopupNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser);
function part3() {
ok(PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser), "Should have a click-to-play notification in the tab in the new window"); ok(PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser), "Should have a click-to-play notification in the tab in the new window");
ok(!PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should not have a click-to-play notification in the old window now"); ok(!PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should not have a click-to-play notification in the old window now");
});
add_task(function* () {
gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, gNewWindow.gBrowser.selectedTab); gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, gNewWindow.gBrowser.selectedTab);
let condition = function() PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser);
waitForCondition(condition, part4, "Waited too long for click-to-play notification");
}
function part4() { yield promisePopupNotification("click-to-play-plugins", gBrowser.selectedBrowser);
ok(PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should have a click-to-play notification in the initial tab again"); ok(PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should have a click-to-play notification in the initial tab again");
gBrowser.selectedBrowser.addEventListener("PluginBindingAttached", handleEvent, true, true); // Work around for delayed PluginBindingAttached
gNextTest = part5; yield promiseUpdatePluginBindings(gBrowser.selectedBrowser);
gBrowser.selectedBrowser.contentDocument.location = gHttpTestRoot + "plugin_test.html"; });
}
function part5() { add_task(function* () {
gBrowser.selectedBrowser.removeEventListener("PluginBindingAttached", handleEvent); yield promisePopupNotification("click-to-play-plugins");
waitForNotificationPopup("click-to-play-plugins", gBrowser.selectedBrowser, () => {
gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
waitForFocus(part6, gNewWindow);
});
}
function part6() { gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
let condition = function() PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser);
waitForCondition(condition, part7, "Waited too long for click-to-play notification");
}
function part7() { yield promiseWaitForFocus(gNewWindow);
yield promisePopupNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser);
});
add_task(function* () {
ok(PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser), "Should have a click-to-play notification in the tab in the new window"); ok(PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser), "Should have a click-to-play notification in the tab in the new window");
ok(!PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should not have a click-to-play notification in the old window now"); ok(!PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should not have a click-to-play notification in the old window now");
let plugin = gNewWindow.gBrowser.selectedBrowser.contentDocument.getElementById("test"); let pluginInfo = yield promiseForPluginInfo("test", gNewWindow.gBrowser.selectedBrowser);
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); ok(!pluginInfo.activated, "plugin should not be activated");
ok(!objLoadingContent.activated, "plugin should not be activated");
yield ContentTask.spawn(gNewWindow.gBrowser.selectedBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
let bounds = plugin.getBoundingClientRect();
let left = (bounds.left + bounds.right) / 2;
let top = (bounds.top + bounds.bottom) / 2;
let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
});
EventUtils.synthesizeMouseAtCenter(plugin, {}, gNewWindow.gBrowser.selectedBrowser.contentWindow);
let condition = function() !PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser).dismissed && gNewWindow.PopupNotifications.panel.firstChild; let condition = function() !PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser).dismissed && gNewWindow.PopupNotifications.panel.firstChild;
waitForCondition(condition, part8, "waited too long for plugin to activate"); yield promiseForCondition(condition);
} });
function part8() { add_task(function* () {
// Click the activate button on doorhanger to make sure it works // Click the activate button on doorhanger to make sure it works
gNewWindow.PopupNotifications.panel.firstChild._primaryButton.click(); gNewWindow.PopupNotifications.panel.firstChild._primaryButton.click();
let plugin = gNewWindow.gBrowser.selectedBrowser.contentDocument.getElementById("test"); let pluginInfo = yield promiseForPluginInfo("test", gNewWindow.gBrowser.selectedBrowser);
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); ok(pluginInfo.activated, "plugin should be activated");
waitForCondition(() => objLoadingContent.activated, shutdown, "plugin should be activated now"); });
}
function shutdown() {
gNewWindow.close();
gNewWindow = null;
finish();
}

Просмотреть файл

@ -1,76 +1,54 @@
var rootDir = getRootDirectory(gTestPath); let rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); let gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
var gTestBrowser = null; add_task(function* () {
var gNextTest = null; registerCleanupFunction(function () {
var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions(); clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
gBrowser.removeCurrentTab();
window.focus();
}); });
});
add_task(function* () {
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
let newTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
Services.prefs.setBoolPref("plugins.click_to_play", true); Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_DISABLED);
prepareTest(runAfterPluginBindingAttached(test1), gHttpTestRoot + "plugin_test.html"); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
} setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
function finishTest() { yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
clearAllPluginPermissions();
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() { // Work around for delayed PluginBindingAttached
// The plugin events are async dispatched and can come after the load event yield promiseUpdatePluginBindings(gBrowser.selectedBrowser);
// This just allows the events to fire before we then go on to test the states
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) { // Tests that the overlay can be hidded for disabled plugins using the close icon.
gNextTest = nextTest; yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
gTestBrowser.contentWindow.location = url; let doc = content.document;
} let plugin = doc.getElementById("test");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
let closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon")
let bounds = closeIcon.getBoundingClientRect();
let left = (bounds.left + bounds.right) / 2;
let top = (bounds.top + bounds.bottom) / 2;
let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
});
// Due to layout being async, "PluginBindAttached" may trigger later. let overlayIsVisible = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
// This wraps a function to force a layout flush, thus triggering it, let doc = content.document;
// and schedules the function execution so they're definitely executed let plugin = doc.getElementById("test");
// afterwards. let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
function runAfterPluginBindingAttached(func) { return plugin && overlay.classList.contains("visible");
return function() { });
let doc = gTestBrowser.contentDocument; ok(!overlayIsVisible, "overlay should be hidden.");
let elems = doc.getElementsByTagName('embed'); });
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(func);
};
}
// Tests that the overlay can be hidded for disabled plugins using the close icon.
function test1() {
var doc = gTestBrowser.contentDocument;
var plugin = doc.getElementById("test");
ok(plugin, "Test 1, Found plugin in page");
var overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(overlay.classList.contains("visible"), "Test 1, Plugin overlay should exist, not be hidden");
var closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon")
EventUtils.synthesizeMouseAtCenter(closeIcon, {}, gTestBrowser.contentWindow);
var condition = function() !overlay.classList.contains("visible");
waitForCondition(condition, finishTest, "Test 1, Waited too long for the overlay to become invisible.");
}

Просмотреть файл

@ -1,104 +1,54 @@
let rootDir = getRootDirectory(gTestPath); let rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
let gTestBrowser = null; add_task(function* () {
let gNextTest = null; registerCleanupFunction(function () {
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions(); clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
gBrowser.removeCurrentTab();
window.focus();
}); });
});
add_task(function* () {
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
let newTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
Services.prefs.setBoolPref("plugins.click_to_play", true); Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
prepareTest(delayTest(runAfterPluginBindingAttached(test1)), gHttpTestRoot + "plugin_iframe.html"); yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_iframe.html");
}
function finishTest() { // Tests that the overlays are visible and actionable if the plugin is in an iframe.
clearAllPluginPermissions();
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() { let result = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
gNextTest(); let frame = content.document.getElementById("frame");
}
function prepareTest(nextTest, url) {
gNextTest = nextTest;
gTestBrowser.contentWindow.location = url;
}
// Delay executing a test for one load event to wait for frame loads.
function delayTest(nextTest) {
return () => {
gNextTest = nextTest;
}
}
// Due to layout being async, "PluginBindAttached" may trigger later.
// This wraps a function to force a layout flush, thus triggering it,
// and schedules the function execution so they're definitely executed
// afterwards.
function runAfterPluginBindingAttached(func) {
return () => {
let frame = gTestBrowser.contentDocument.getElementById("frame");
let doc = frame.contentDocument; let doc = frame.contentDocument;
let elems = doc.getElementsByTagName('embed'); let plugin = doc.getElementById("test");
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(func);
};
}
// Tests that the overlays are visible and actionable if the plugin is in an iframe.
function test1() {
let frame = gTestBrowser.contentDocument.getElementById("frame");
let doc = frame.contentDocument;
let plugin = doc.getElementById("test");
ok(plugin, "Test 1, Found plugin in page");
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, () => {
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main"); let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(overlay.classList.contains("visible"), "Test 1, Plugin overlay should exist, not be hidden"); return plugin && overlay.classList.contains("visible");
let closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon");
EventUtils.synthesizeMouseAtCenter(closeIcon, {}, frame.contentWindow);
let condition = () => !overlay.classList.contains("visible");
waitForCondition(condition, test2, "Test 1, Waited too long for the overlay to become invisible.");
}); });
} ok(result, "Test 1, Plugin overlay should exist, not be hidden");
function test2() { result = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
prepareTest(delayTest(runAfterPluginBindingAttached(test3)), gHttpTestRoot + "plugin_iframe.html"); let frame = content.document.getElementById("frame");
} let doc = frame.contentDocument;
let plugin = doc.getElementById("test");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
let closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon");
let bounds = closeIcon.getBoundingClientRect();
let left = (bounds.left + bounds.right) / 2;
let top = (bounds.top + bounds.bottom) / 2;
let utils = doc.defaultView.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
return overlay.classList.contains("visible");
});
ok(!result, "Test 1, Plugin overlay should exist, be hidden");
});
function test3() {
let frame = gTestBrowser.contentDocument.getElementById("frame");
let doc = frame.contentDocument;
let plugin = doc.getElementById("test");
ok(plugin, "Test 3, Found plugin in page");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(overlay.classList.contains("visible"), "Test 3, Plugin overlay should exist, not be hidden");
EventUtils.synthesizeMouseAtCenter(plugin, {}, frame.contentWindow);
let condition = () => PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
waitForCondition(condition, finishTest, "Test 3, Waited too long for the doorhanger to pop up.");
}

Просмотреть файл

@ -1,83 +1,51 @@
var rootDir = getRootDirectory(gTestPath); let rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); let gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
var gTestBrowser = null; add_task(function* () {
var gNextTest = null; registerCleanupFunction(function () {
var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions(); clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
gBrowser.removeCurrentTab();
window.focus();
}); });
});
add_task(function* () {
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
var newTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
Services.prefs.setBoolPref("plugins.click_to_play", true); Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in"); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
prepareTest(test1a, gHttpTestRoot + "plugin_two_types.html"); yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_two_types.html");
}
function finishTest() { // Work around for delayed PluginBindingAttached
clearAllPluginPermissions(); yield promiseUpdatePluginBindings(gBrowser.selectedBrowser);
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() { // Test that the click-to-play doorhanger for multiple plugins shows the correct
// The plugin events are async dispatched and can come after the load event // state when re-opening without reloads or navigation.
// This just allows the events to fire before we then go on to test the states
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) { let pluginInfo = yield promiseForPluginInfo("test", gBrowser.selectedBrowser);
gNextTest = nextTest; ok(!pluginInfo.activated, "plugin should be activated");
gTestBrowser.contentWindow.location = url;
}
// Due to layout being async, "PluginBindAttached" may trigger later. let notification = PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser);
// This wraps a function to force a layout flush, thus triggering it,
// and schedules the function execution so they're definitely executed
// afterwards.
function runAfterPluginBindingAttached(func) {
return function() {
let doc = gTestBrowser.contentDocument;
let elems = doc.getElementsByTagName('embed');
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(func);
};
}
// Test that the click-to-play doorhanger for multiple plugins shows the correct
// state when re-opening without reloads or navigation.
function test1a() {
let doc = gTestBrowser.contentDocument;
let plugin = doc.getElementById("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test1a, Plugin should not be activated");
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 1a, Should have a click-to-play notification"); ok(notification, "Test 1a, Should have a click-to-play notification");
notification.reshow();
yield promiseForNotificationShown(notification);
is(notification.options.pluginData.size, 2, is(notification.options.pluginData.size, 2,
"Test 1a, Should have two types of plugin in the notification"); "Test 1a, Should have two types of plugin in the notification");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gBrowser.selectedBrowser);
is(PopupNotifications.panel.firstChild.childNodes.length, 2, "have child nodes");
let pluginItem = null; let pluginItem = null;
for (let item of PopupNotifications.panel.firstChild.childNodes) { for (let item of PopupNotifications.panel.firstChild.childNodes) {
@ -86,21 +54,20 @@ function test1a() {
pluginItem = item; pluginItem = item;
} }
} }
// Choose "Allow now" for the test plugin // Choose "Allow now" for the test plugin
pluginItem.value = "allownow"; pluginItem.value = "allownow";
PopupNotifications.panel.firstChild._primaryButton.click(); PopupNotifications.panel.firstChild._primaryButton.click();
waitForCondition(() => objLoadingContent.activated, test1b, pluginInfo = yield promiseForPluginInfo("test", gBrowser.selectedBrowser);
"Test 1a, Waited too long for plugin to activate"); ok(pluginInfo.activated, "plugin should be activated");
}
function test1b() { notification = PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser);
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 1b, Should have a click-to-play notification"); ok(notification, "Test 1b, Should have a click-to-play notification");
notification.reshow();
let pluginItem = null; yield promiseForNotificationShown(notification);
pluginItem = null;
for (let item of PopupNotifications.panel.firstChild.childNodes) { for (let item of PopupNotifications.panel.firstChild.childNodes) {
if (item.action.pluginName == "Test") { if (item.action.pluginName == "Test") {
is(item.value, "allownow", "Test 1b, Test plugin should now be set to 'Allow now'"); is(item.value, "allownow", "Test 1b, Test plugin should now be set to 'Allow now'");
@ -114,17 +81,13 @@ function test1b() {
pluginItem.value = "allowalways"; pluginItem.value = "allowalways";
PopupNotifications.panel.firstChild._primaryButton.click(); PopupNotifications.panel.firstChild._primaryButton.click();
let doc = gTestBrowser.contentDocument; pluginInfo = yield promiseForPluginInfo("secondtestA", gBrowser.selectedBrowser);
let plugin = doc.getElementById("secondtestA"); ok(pluginInfo.activated, "plugin should be activated");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
waitForCondition(() => objLoadingContent.activated, test1c,
"Test 1b, Waited too long for plugin to activate");
}
function test1c() { notification = PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser);
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 1c, Should have a click-to-play notification"); ok(notification, "Test 1c, Should have a click-to-play notification");
notification.reshow();
yield promiseForNotificationShown(notification);
for (let item of PopupNotifications.panel.firstChild.childNodes) { for (let item of PopupNotifications.panel.firstChild.childNodes) {
if (item.action.pluginName == "Test") { if (item.action.pluginName == "Test") {
@ -133,6 +96,4 @@ function test1c() {
is(item.value, "allowalways", "Test 1c, Second Test plugin should be set to 'Allow always'"); is(item.value, "allowalways", "Test 1c, Second Test plugin should be set to 'Allow always'");
} }
} }
});
finishTest();
}

Просмотреть файл

@ -1,114 +1,58 @@
var rootDir = getRootDirectory(gTestPath); let rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); let gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
var gTestBrowser = null; add_task(function* () {
var gNextTest = null; registerCleanupFunction(function () {
var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
var gRunNextTestAfterPluginRemoved = false;
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions(); clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
gTestBrowser.removeEventListener("PluginRemoved", handlePluginRemoved, true, true); gBrowser.removeCurrentTab();
window.focus();
}); });
});
add_task(function* () {
Services.prefs.setBoolPref("plugins.click_to_play", true);
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
var newTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
gTestBrowser.addEventListener("PluginRemoved", handlePluginRemoved, true, true);
Services.prefs.setBoolPref("plugins.click_to_play", true); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_DISABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
prepareTest(runAfterPluginBindingAttached(test1), gHttpTestRoot + "plugin_two_types.html"); yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_two_types.html");
}
function finishTest() { // Work around for delayed PluginBindingAttached
clearAllPluginPermissions(); yield promiseUpdatePluginBindings(gBrowser.selectedBrowser);
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() { // Test that the click-to-play notification is not shown for non-plugin object elements
// The plugin events are async dispatched and can come after the load event let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser);
// This just allows the events to fire before we then go on to test the states
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) {
gNextTest = nextTest;
gTestBrowser.contentWindow.location = url;
}
// Due to layout being async, "PluginBindAttached" may trigger later.
// This wraps a function to force a layout flush, thus triggering it,
// and schedules the function execution so they're definitely executed
// afterwards.
function runAfterPluginBindingAttached(func) {
return function() {
let doc = gTestBrowser.contentDocument;
let elems = doc.getElementsByTagName('embed');
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(func);
};
}
function handlePluginRemoved() {
if (gRunNextTestAfterPluginRemoved) {
executeSoon(gNextTest);
gRunNextTestAfterPluginRemoved = false;
}
}
function runAfterPluginRemoved(func) {
gNextTest = func;
gRunNextTestAfterPluginRemoved = true;
}
// Test that the click-to-play notification is not shown for non-plugin object elements
function test1() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "Test 1, Should have a click-to-play notification"); ok(popupNotification, "Test 1, Should have a click-to-play notification");
let plugin = gTestBrowser.contentDocument.getElementById("secondtestA"); let pluginRemovedPromise = waitForEvent(gBrowser.selectedBrowser, "PluginRemoved", null, true, true);
plugin.parentNode.removeChild(plugin); yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
plugin = gTestBrowser.contentDocument.getElementById("secondtestB"); let plugin = content.document.getElementById("secondtestA");
plugin.parentNode.removeChild(plugin); plugin.parentNode.removeChild(plugin);
plugin = content.document.getElementById("secondtestB");
plugin.parentNode.removeChild(plugin);
let image = gTestBrowser.contentDocument.createElement("object"); let image = content.document.createElement("object");
image.type = "image/png"; image.type = "image/png";
image.data = "moz.png"; image.data = "moz.png";
gTestBrowser.contentDocument.body.appendChild(image); content.document.body.appendChild(image);
});
yield pluginRemovedPromise;
runAfterPluginRemoved(test2); popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser);
}
function test2() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "Test 2, Should have a click-to-play notification"); ok(popupNotification, "Test 2, Should have a click-to-play notification");
let plugin = gTestBrowser.contentDocument.getElementById("test"); yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
plugin.parentNode.removeChild(plugin); let plugin = content.document.getElementById("test");
plugin.parentNode.removeChild(plugin);
});
executeSoon(test3); popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser);
}
function test3() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "Test 3, Should still have a click-to-play notification"); ok(popupNotification, "Test 3, Should still have a click-to-play notification");
});
finishTest();
}

Просмотреть файл

@ -1,171 +1,154 @@
var rootDir = getRootDirectory(gTestPath); let rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); let gTestBrowser = null;
var gTestBrowser = null; add_task(function* () {
var gNextTest = null; registerCleanupFunction(function () {
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions(); clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
gBrowser.removeCurrentTab();
window.focus();
gTestBrowser = null;
}); });
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
var newTab = gBrowser.addTab(); let newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab; gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser; gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true); });
add_task(function* () {
Services.prefs.setBoolPref("plugins.click_to_play", true); Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
prepareTest(runAfterPluginBindingAttached(test1), gHttpTestRoot + "plugin_small.html"); yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_small.html");
}
function finishTest() { // Work around for delayed PluginBindingAttached
gTestBrowser.removeEventListener("load", pageLoad, true); yield promiseUpdatePluginBindings(gTestBrowser);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() { yield promisePopupNotification("click-to-play-plugins");
// The plugin events are async dispatched and can come after the load event
// This just allows the events to fire before we then go on to test the states
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) { // Expecting a notification bar for hidden plugins
gNextTest = nextTest; yield promiseForNotificationBar("plugin-hidden", gTestBrowser);
gTestBrowser.contentWindow.location = url; });
}
// Due to layout being async, "PluginBindAttached" may trigger later. add_task(function* () {
// This wraps a function to force a layout flush, thus triggering it, setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
// and schedules the function execution so they're definitely executed
// afterwards.
function runAfterPluginBindingAttached(func) {
return function() {
let doc = gTestBrowser.contentDocument;
let elems = doc.getElementsByTagName('embed');
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(func);
};
}
// Tests for the notification bar for hidden plugins. yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_small.html");
function test1() { // Work around for delayed PluginBindingAttached
info("Test 1 - expecting a notification bar for hidden plugins."); yield promiseUpdatePluginBindings(gTestBrowser);
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, () => {
waitForNotificationBar("plugin-hidden", gTestBrowser, () => {
// Don't use setTestPluginEnabledState here because we already saved the
// prior value
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
prepareTest(test2, gTestRoot + "plugin_small.html");
});
});
}
function test2() {
info("Test 2 - expecting no plugin notification bar on visible plugins.");
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, () => {
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null,
() => {
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
prepareTest(test3, gTestRoot + "plugin_overlayed.html");
},
"expected to not have a plugin notification bar"
);
});
}
function test3() {
info("Test 3 - expecting a plugin notification bar when plugins are overlaid");
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, () => {
waitForNotificationBar("plugin-hidden", gTestBrowser, test3b);
});
}
function test3b()
{
let doc = gTestBrowser.contentDocument;
let plugin = doc.getElementById("test");
ok(plugin, "Test 3b, Found plugin in page");
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 3b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
ok(!plugin.activated, "Test 3b, Plugin should not be activated");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(!overlay.classList.contains("visible"), "Test 3b, Plugin overlay should be hidden");
prepareTest(test4, gTestRoot + "plugin_positioned.html");
}
function test4() {
info("Test 4 - expecting a plugin notification bar when plugins are overlaid offscreen")
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, () => {
waitForNotificationBar("plugin-hidden", gTestBrowser, test4b);
});
}
function test4b() {
let doc = gTestBrowser.contentDocument;
let plugin = doc.getElementById("test");
ok(plugin, "Test 4b, Found plugin in page");
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 4b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
ok(!plugin.activated, "Test 4b, Plugin should not be activated");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(!overlay.classList.contains("visible"), "Test 4b, Plugin overlay should be hidden");
prepareTest(runAfterPluginBindingAttached(test5), gHttpTestRoot + "plugin_small.html");
}
function test5() {
let notificationBox = gBrowser.getNotificationBox(gTestBrowser); let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null, yield promiseForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null);
test6, });
"Test 5, expected a notification bar for hidden plugins");
} add_task(function* () {
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_overlayed.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
// Expecting a plugin notification bar when plugins are overlaid.
yield promiseForNotificationBar("plugin-hidden", gTestBrowser);
});
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_overlayed.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
return plugin.pluginFallbackType;
});
is(result, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 3b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
let pluginInfo = yield promiseForPluginInfo("test");
ok(!pluginInfo.activated, "Test 1a, plugin should not be activated");
result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(!result, "Test 3b, overlay should be hidden.");
});
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_positioned.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
// Expecting a plugin notification bar when plugins are overlaid offscreen.
yield promisePopupNotification("click-to-play-plugins");
yield promiseForNotificationBar("plugin-hidden", gTestBrowser);
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
return plugin.pluginFallbackType;
});
is(result, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 4b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(!result, "Test 4b, overlay should be hidden.");
});
// Test that the notification bar is getting dismissed when directly activating plugins // Test that the notification bar is getting dismissed when directly activating plugins
// via the doorhanger. // via the doorhanger.
function test6() { add_task(function* () {
info("Test 6 - expecting the doorhanger to be dismissed when directly activating plugins."); yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_small.html");
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, (notification) => {
let plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 6, Found plugin in page");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 6, Plugin should be click-to-play");
// simulate "always allow" // Work around for delayed PluginBindingAttached
notification.reshow(); yield promiseUpdatePluginBindings(gTestBrowser);
PopupNotifications.panel.firstChild._primaryButton.click();
let notificationBox = gBrowser.getNotificationBox(gTestBrowser); // Expecting a plugin notification bar when plugins are overlaid offscreen.
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null, yield promisePopupNotification("click-to-play-plugins");
test7,
"Test 6, expected the notification bar for hidden plugins to get dismissed"); let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
return plugin.pluginFallbackType;
}); });
} is(result, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 6, Plugin should be click-to-play");
function test7() { yield promisePopupNotification("click-to-play-plugins");
let plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 7, Found plugin in page"); let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); ok(notification, "Test 6, Should have a click-to-play notification");
waitForCondition(() => objLoadingContent.activated, finishTest,
"Test 7, Waited too long for plugin to activate"); // simulate "always allow"
} yield promiseForNotificationShown(notification);
PopupNotifications.panel.firstChild._primaryButton.click();
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
yield promiseForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null);
let pluginInfo = yield promiseForPluginInfo("test");
ok(pluginInfo.activated, "Test 7, plugin should be activated");
});

Просмотреть файл

@ -1,125 +1,120 @@
var rootDir = getRootDirectory(gTestPath); let rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); let gTestBrowser = null;
let gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
var gTestBrowser = null; add_task(function* () {
var gNextTest = null; registerCleanupFunction(function () {
var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
var gRunNextTestAfterPluginRemoved = false;
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions(); clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
gBrowser.removeCurrentTab();
window.focus();
gTestBrowser = null;
}); });
});
add_task(function* () {
Services.prefs.setBoolPref("plugins.click_to_play", true);
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
var newTab = gBrowser.addTab(); let newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab; gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser; gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
Services.prefs.setBoolPref("plugins.click_to_play", true); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
prepareTest(test1, gHttpTestRoot + "plugin_outsideScrollArea.html"); let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
} ok(!popupNotification, "Test 1, Should not have a click-to-play notification");
});
function finishTest() {
clearAllPluginPermissions();
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() {
// The plugin events are async dispatched and can come after the load event
// This just allows the events to fire before we then go on to test the states
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) {
gNextTest = nextTest;
gTestBrowser.contentWindow.location = url;
}
// Due to layout being async, "PluginBindAttached" may trigger later.
// This wraps a function to force a layout flush, thus triggering it,
// and schedules the function execution so they're definitely executed
// afterwards.
function runAfterPluginBindingAttached(func) {
return function() {
let doc = gTestBrowser.contentDocument;
let elems = doc.getElementsByTagName('embed');
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(func);
};
}
// Add plugin relative to bottom-left corner of #container.
function addPlugin(x, y) {
let doc = gTestBrowser.contentDocument;
let p = doc.createElement('embed');
p.setAttribute('id', 'test');
p.setAttribute('type', 'application/x-test');
p.style.left = x.toString() + 'px';
p.style.bottom = y.toString() + 'px';
doc.getElementById('container').appendChild(p);
}
// Test that the click-to-play overlay is not hidden for elements // Test that the click-to-play overlay is not hidden for elements
// partially or fully outside the viewport. // partially or fully outside the viewport.
function test1() { add_task(function* () {
addPlugin(0, -200); yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_outsideScrollArea.html");
executeSoon(runAfterPluginBindingAttached(test2));
}
function test2() { yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = gTestBrowser.contentDocument; let doc = content.document;
let plugin = doc.getElementById("test"); let p = doc.createElement('embed');
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(overlay, "Test 2, Should have an overlay.");
ok(overlay.classList.contains("visible"), "Test 2, Overlay should be visible");
prepareTest(test3, gHttpTestRoot + "plugin_outsideScrollArea.html"); p.setAttribute('id', 'test');
} p.setAttribute('type', 'application/x-test');
p.style.left = "0";
p.style.bottom = "200px";
function test3() { doc.getElementById('container').appendChild(p);
addPlugin(0, -410); });
executeSoon(runAfterPluginBindingAttached(test4));
}
function test4() { // Work around for delayed PluginBindingAttached
let doc = gTestBrowser.contentDocument; yield promiseUpdatePluginBindings(gTestBrowser);
let plugin = doc.getElementById("test");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(overlay, "Test 4, Should have an overlay.");
ok(overlay.classList.contains("visible"), "Test 4, Overlay should be visible");
prepareTest(test5, gHttpTestRoot + "plugin_outsideScrollArea.html"); yield promisePopupNotification("click-to-play-plugins");
}
function test5() { let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
addPlugin(-600, 0); let plugin = content.document.getElementById("test");
executeSoon(runAfterPluginBindingAttached(test6)); let doc = content.document;
} let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(result, "Test 2, overlay should be visible.");
});
function test6() { add_task(function* () {
let doc = gTestBrowser.contentDocument; yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_outsideScrollArea.html");
let plugin = doc.getElementById("test");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(overlay, "Test 6, Should have an overlay.");
ok(!overlay.classList.contains("visible"), "Test 6, Overlay should be hidden");
finishTest(); yield ContentTask.spawn(gTestBrowser, {}, function* () {
} let doc = content.document;
let p = doc.createElement('embed');
p.setAttribute('id', 'test');
p.setAttribute('type', 'application/x-test');
p.style.left = "0";
p.style.bottom = "-410px";
doc.getElementById('container').appendChild(p);
});
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
yield promisePopupNotification("click-to-play-plugins");
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementById("test");
let doc = content.document;
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(result, "Test 3, overlay should be visible.");
});
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_outsideScrollArea.html");
yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let p = doc.createElement('embed');
p.setAttribute('id', 'test');
p.setAttribute('type', 'application/x-test');
p.style.left = "-600px";
p.style.bottom = "0";
doc.getElementById('container').appendChild(p);
});
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
yield promisePopupNotification("click-to-play-plugins");
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementById("test");
let doc = content.document;
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(!result, "Test 4, overlay should be hidden.");
});

Просмотреть файл

@ -1,17 +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/. */
const gTestRoot = getRootDirectory(gTestPath); const gTestRoot = getRootDirectory(gTestPath);
const gHttpTestRoot = gTestRoot.replace("chrome://mochitests/content/", const gHttpTestRoot = gTestRoot.replace("chrome://mochitests/content/",
"http://127.0.0.1:8888/"); "http://127.0.0.1:8888/");
add_task(function* () { add_task(function* () {
Services.prefs.setBoolPref("plugins.click_to_play", true); registerCleanupFunction(function () {
registerCleanupFunction(() => { clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play"); Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
gBrowser.removeCurrentTab();
window.focus();
}); });
})
Services.prefs.setBoolPref("plugins.click_to_play", true);
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
});
/** /**
* Tests that if a plugin is removed just as we transition to * Tests that if a plugin is removed just as we transition to
@ -19,30 +25,27 @@ add_task(function* () {
* notification bar on the new page. * notification bar on the new page.
*/ */
add_task(function* () { add_task(function* () {
let newTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
let browser = gBrowser.selectedBrowser;
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
// Load up a page with a plugin... // Load up a page with a plugin...
let notificationPromise = let notificationPromise = waitForNotificationBar("plugin-hidden", gBrowser.selectedBrowser);
waitForNotificationBar("plugin-hidden", gBrowser.selectedBrowser); yield promiseTabLoadEvent(gBrowser.selectedTab, gHttpTestRoot + "plugin_small.html");
yield loadPage(browser, gHttpTestRoot + "plugin_small.html") yield promiseUpdatePluginBindings(gBrowser.selectedBrowser);
yield forcePluginBindingAttached(browser);
yield notificationPromise; yield notificationPromise;
// Trigger the PluginRemoved event to be fired, and then immediately // Trigger the PluginRemoved event to be fired, and then immediately
// browse to a new page. // browse to a new page.
let plugin = browser.contentDocument.getElementById("test"); yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
plugin.remove(); let plugin = content.document.getElementById("test");
yield loadPage(browser, "about:mozilla"); plugin.remove();
});
yield promiseTabLoadEvent(gBrowser.selectedTab, "about:mozilla");
// There should be no hidden plugin notification bar at about:mozilla. // There should be no hidden plugin notification bar at about:mozilla.
let notificationBox = gBrowser.getNotificationBox(browser); let notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
is(notificationBox.getNotificationWithValue("plugin-hidden"), null, is(notificationBox.getNotificationWithValue("plugin-hidden"), null,
"Expected no notification box"); "Expected no notification box");
gBrowser.removeTab(newTab);
}); });
/** /**
@ -51,32 +54,26 @@ add_task(function* () {
* for the new page. * for the new page.
*/ */
add_task(function* () { add_task(function* () {
let newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
let browser = gBrowser.selectedBrowser;
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY,
"Second Test Plug-in");
// Load up a page with a plugin... // Load up a page with a plugin...
let notificationPromise = let notificationPromise = waitForNotificationBar("plugin-hidden", gBrowser.selectedBrowser);
waitForNotificationBar("plugin-hidden", browser); yield promiseTabLoadEvent(gBrowser.selectedTab, gHttpTestRoot + "plugin_small.html");
yield loadPage(browser, gHttpTestRoot + "plugin_small.html") yield promiseUpdatePluginBindings(gBrowser.selectedBrowser);
yield forcePluginBindingAttached(browser);
yield notificationPromise; yield notificationPromise;
// Trigger the PluginRemoved event to be fired, and then immediately // Trigger the PluginRemoved event to be fired, and then immediately
// browse to a new page. // browse to a new page.
let plugin = browser.contentDocument.getElementById("test"); yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
plugin.remove(); let plugin = content.document.getElementById("test");
yield loadPage(browser, gTestRoot + "plugin_small_2.html"); plugin.remove();
let notification = yield waitForNotificationBar("plugin-hidden", browser); });
ok(notification, "There should be a notification shown for the new page."); });
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, gHttpTestRoot + "plugin_small_2.html");
let notification = yield waitForNotificationBar("plugin-hidden", gBrowser.selectedBrowser);
ok(notification, "There should be a notification shown for the new page.");
// Ensure that the notification is showing information about // Ensure that the notification is showing information about
// the x-second-test plugin. // the x-second-test plugin.
ok(notification.label.includes("Second Test"), "Should mention the second plugin"); let label = notification.label;
ok(!notification.label.includes("127.0.0.1"), "Should not refer to old principal"); ok(label.includes("Second Test"), "Should mention the second plugin");
ok(notification.label.includes("null"), "Should refer to the new principal");
gBrowser.removeTab(newTab);
}); });

Просмотреть файл

@ -1,116 +1,130 @@
var rootDir = getRootDirectory(gTestPath); let rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); let gTestBrowser = null;
var gTestBrowser = null; add_task(function* () {
var gNextTest = null; registerCleanupFunction(function () {
var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions(); clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
gBrowser.removeCurrentTab();
window.focus();
gTestBrowser = null;
}); });
});
add_task(function* () {
Services.prefs.setBoolPref("plugins.click_to_play", true);
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
var newTab = gBrowser.addTab(); let newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab; gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser; gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
Services.prefs.setBoolPref("plugins.click_to_play", true); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
prepareTest(runAfterPluginBindingAttached(test1), gHttpTestRoot + "plugin_small.html"); let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
} ok(!popupNotification, "Test 1, Should not have a click-to-play notification");
function finishTest() { yield promiseTabLoadEvent(newTab, gTestRoot + "plugin_small.html"); // 10x10 plugin
clearAllPluginPermissions();
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() { // Work around for delayed PluginBindingAttached
// The plugin events are async dispatched and can come after the load event yield promiseUpdatePluginBindings(gTestBrowser);
// This just allows the events to fire before we then go on to test the states
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) { yield promisePopupNotification("click-to-play-plugins");
gNextTest = nextTest; });
gTestBrowser.contentWindow.location = url;
}
// Due to layout being async, "PluginBindAttached" may trigger later.
// This wraps a function to force a layout flush, thus triggering it,
// and schedules the function execution so they're definitely executed
// afterwards.
function runAfterPluginBindingAttached(func) {
return function() {
let doc = gTestBrowser.contentDocument;
let elems = doc.getElementsByTagName('embed');
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(func);
};
}
// Test that the overlay is hidden for "small" plugin elements and is shown // Test that the overlay is hidden for "small" plugin elements and is shown
// once they are resized to a size that can hold the overlay // once they are resized to a size that can hold the overlay
add_task(function* () {
function test1() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "Test 1, Should have a click-to-play notification"); ok(popupNotification, "Test 2, Should have a click-to-play notification");
let plugin = gTestBrowser.contentDocument.getElementById("test"); let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = gTestBrowser.contentDocument; let doc = content.document;
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main"); let plugin = doc.getElementById("test");
ok(overlay, "Test 1, Should have an overlay."); let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(!overlay.classList.contains("visible"), "Test 1, Overlay should be hidden"); return overlay && overlay.classList.contains("visible");
});
ok(!result, "Test 2, overlay should be hidden.");
});
plugin.style.width = '300px'; add_task(function* () {
executeSoon(test2); yield ContentTask.spawn(gTestBrowser, {}, function* () {
} let plugin = content.document.getElementById("test");
plugin.style.width = "300px";
});
function test2() { // Work around for delayed PluginBindingAttached
let plugin = gTestBrowser.contentDocument.getElementById("test"); yield promiseUpdatePluginBindings(gTestBrowser);
let doc = gTestBrowser.contentDocument;
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(overlay, "Test 2, Should have an overlay.");
ok(!overlay.classList.contains("visible"), "Test 2, Overlay should be hidden");
plugin.style.height = '300px'; let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let condition = () => overlay.classList.contains("visible"); let doc = content.document;
waitForCondition(condition, test3, "Test 2, Waited too long for overlay to become visible"); let plugin = doc.getElementById("test");
} let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(!result, "Test 3, overlay should be hidden.");
});
function test3() {
let plugin = gTestBrowser.contentDocument.getElementById("test");
let doc = gTestBrowser.contentDocument;
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(overlay, "Test 3, Should have an overlay.");
ok(overlay.classList.contains("visible"), "Test 3, Overlay should be visible");
plugin.style.width = '10px'; add_task(function* () {
plugin.style.height = '10px'; yield ContentTask.spawn(gTestBrowser, {}, function* () {
let condition = () => !overlay.classList.contains("visible"); let plugin = content.document.getElementById("test");
waitForCondition(condition, test4, "Test 3, Waited too long for overlay to become hidden"); plugin.style.height = "300px";
} });
function test4() { yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = gTestBrowser.contentDocument.getElementById("test"); content.document.getElementById("test").clientTop;
let doc = gTestBrowser.contentDocument; });
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(overlay, "Test 4, Should have an overlay.");
ok(!overlay.classList.contains("visible"), "Test 4, Overlay should be hidden");
clearAllPluginPermissions(); let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
finishTest(); let doc = content.document;
} let plugin = doc.getElementById("test");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(result, "Test 4, overlay should be visible.");
});
add_task(function* () {
yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementById("test");
plugin.style.width = "10px";
plugin.style.height = "10px";
});
yield ContentTask.spawn(gTestBrowser, {}, function* () {
content.document.getElementById("test").clientTop;
});
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(!result, "Test 5, overlay should be hidden.");
});
add_task(function* () {
yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementById("test");
plugin.style.height = "300px";
plugin.style.width = "300px";
});
yield ContentTask.spawn(gTestBrowser, {}, function* () {
content.document.getElementById("test").clientTop;
});
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(result, "Test 6, overlay should be visible.");
});

Просмотреть файл

@ -1,77 +1,62 @@
/* 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/. */
"use strict"; "use strict";
let rootDir = getRootDirectory(gTestPath); let rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/",
"http://127.0.0.1:8888/");
let gTestBrowser = null; let gTestBrowser = null;
Components.utils.import("resource://gre/modules/Services.jsm"); add_task(function* () {
registerCleanupFunction(function () {
function test() {
waitForExplicitFinish();
registerCleanupFunction(() => {
FullZoom.reset();
clearAllPluginPermissions(); clearAllPluginPermissions();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
FullZoom.reset(); // must be called before closing the tab we zoomed!
gBrowser.removeCurrentTab();
window.focus();
gTestBrowser = null;
}); });
});
add_task(function* () {
Services.prefs.setBoolPref("plugins.click_to_play", true);
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
Services.prefs.setBoolPref("plugins.click_to_play", true); gBrowser.selectedTab = gBrowser.addTab();
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
let newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser; gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
gTestBrowser.contentWindow.location = gHttpTestRoot + "plugin_zoom.html"
}
function finishTest() { setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
clearAllPluginPermissions();
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() { let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
// Due to layout being async, "PluginBindAttached" may trigger later. ok(!popupNotification, "Test 1, Should not have a click-to-play notification");
// This wraps a function to force a layout flush, thus triggering it,
// and schedules the function execution so they're definitely executed
// afterwards.
let doc = gTestBrowser.contentDocument;
let elems = doc.getElementsByTagName('embed');
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(testOverlay);
}
let enlargeCount = 4; yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_zoom.html");
// Enlarges the zoom level |enlargeCount| times and tests that the overlay is // Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
yield promisePopupNotification("click-to-play-plugins");
});
// Enlarges the zoom level 4 times and tests that the overlay is
// visible after each enlargement. // visible after each enlargement.
function testOverlay() { add_task(function* () {
let plugin = gTestBrowser.contentDocument.getElementById("test"); for (let count = 0; count < 4; count++) {
let doc = gTestBrowser.contentDocument;
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(overlay, "Overlay should exist");
ok(overlay.classList.contains("visible"), "Overlay should be visible");
if (enlargeCount > 0) {
--enlargeCount;
FullZoom.enlarge(); FullZoom.enlarge();
gTestBrowser.contentWindow.location.reload();
} else { // Reload the page
FullZoom.reset(); yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_zoom.html");
clearAllPluginPermissions(); yield promiseUpdatePluginBindings(gTestBrowser);
finishTest(); let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(result, "Overlay should be visible for zoom change count " + count);
} }
} });

Просмотреть файл

@ -0,0 +1,360 @@
let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
let gTestBrowser = null;
let gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
function updateAllTestPlugins(aState) {
setTestPluginEnabledState(aState, "Test Plug-in");
setTestPluginEnabledState(aState, "Second Test Plug-in");
}
add_task(function* () {
registerCleanupFunction(Task.async(function*() {
clearAllPluginPermissions();
updateAllTestPlugins(Ci.nsIPluginTag.STATE_ENABLED);
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
resetBlocklist();
gBrowser.removeCurrentTab();
window.focus();
gTestBrowser = null;
}));
});
add_task(function* () {
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
Services.prefs.setBoolPref("plugins.click_to_play", true);
// Prime the content process
yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html>hi</html>");
// Make sure the blocklist service(s) are running
Components.classes["@mozilla.org/extensions/blocklist;1"]
.getService(Components.interfaces.nsIBlocklistService);
let exmsg = yield promiseInitContentBlocklistSvc(gBrowser.selectedBrowser);
ok(!exmsg, "exception: " + exmsg);
});
add_task(function* () {
// enable hard blocklisting for the next test
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginHard.xml", gTestBrowser);
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
yield promisePopupNotification("click-to-play-plugins");
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification.dismissed, "Test 5: The plugin notification should be dismissed by default");
yield promiseForNotificationShown(notification);
let pluginInfo = yield promiseForPluginInfo("test");
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED, "Test 5, plugin fallback type should be PLUGIN_BLOCKLISTED");
is(notification.options.pluginData.size, 1, "Test 5: Only the blocked plugin should be present in the notification");
ok(PopupNotifications.panel.firstChild._buttonContainer.hidden, "Part 5: The blocked plugins notification should not have any buttons visible.");
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
});
// Tests a vulnerable, updatable plugin
add_task(function* () {
// enable hard blocklisting of test
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginVulnerableUpdatable.xml", gTestBrowser);
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
yield promisePopupNotification("click-to-play-plugins");
let pluginInfo = yield promiseForPluginInfo("test");
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE,
"Test 18a, plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE");
ok(!pluginInfo.activated, "Test 18a, Plugin should not be activated");
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementById("test");
let doc = content.document;
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(result, "Test 18a, Plugin overlay should exist, not be hidden");
result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
let updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
return updateLink.style.visibility != "hidden";
});
ok(result, "Test 18a, Plugin should have an update link");
let promise = waitForEvent(gBrowser.tabContainer, "TabOpen", null, true);
let pluginUpdateURL = Services.urlFormatter.formatURLPref("plugins.update.url");
info(pluginUpdateURL);
yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
let updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
let bounds = updateLink.getBoundingClientRect();
let left = (bounds.left + bounds.right) / 2;
let top = (bounds.top + bounds.bottom) / 2;
let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
});
yield promise;
promise = waitForEvent(gBrowser.tabContainer, "TabClose", null, true);
gBrowser.removeCurrentTab();
yield promise;
});
add_task(function* () {
// clicking the update link should not activate the plugin
let pluginInfo = yield promiseForPluginInfo("test");
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE,
"Test 18a, plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE");
ok(!pluginInfo.activated, "Test 18b, Plugin should not be activated");
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(result, "Test 18b, Plugin overlay should exist, not be hidden");
});
// Tests a vulnerable plugin with no update
add_task(function* () {
updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginVulnerableNoUpdate.xml", gTestBrowser);
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 18c, Should have a click-to-play notification");
let pluginInfo = yield promiseForPluginInfo("test");
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE,
"Test 18c, plugin fallback type should be PLUGIN_VULNERABLE_NO_UPDATE");
ok(!pluginInfo.activated, "Test 18c, Plugin should not be activated");
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
return overlay && overlay.classList.contains("visible");
});
ok(result, "Test 18c, Plugin overlay should exist, not be hidden");
result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
let updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
return updateLink && updateLink.style.display != "block";
});
ok(result, "Test 18c, Plugin should not have an update link");
// check that click "Always allow" works with blocked plugins
yield promiseForNotificationShown(notification);
PopupNotifications.panel.firstChild._primaryButton.click();
pluginInfo = yield promiseForPluginInfo("test");
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE,
"Test 18c, plugin fallback type should be PLUGIN_VULNERABLE_NO_UPDATE");
ok(pluginInfo.activated, "Test 18c, Plugin should be activated");
let enabledState = getTestPluginEnabledState();
ok(enabledState, "Test 18c, Plugin enabled state should be STATE_CLICKTOPLAY");
});
// continue testing "Always allow", make sure it sticks.
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let pluginInfo = yield promiseForPluginInfo("test");
ok(pluginInfo.activated, "Test 18d, Waited too long for plugin to activate");
clearAllPluginPermissions();
});
// clicking the in-content overlay of a vulnerable plugin should bring
// up the notification and not directly activate the plugin
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 18f, Should have a click-to-play notification");
ok(notification.dismissed, "Test 18f, notification should start dismissed");
let pluginInfo = yield promiseForPluginInfo("test");
ok(!pluginInfo.activated, "Test 18f, Waited too long for plugin to activate");
var oldEventCallback = notification.options.eventCallback;
let promise = promiseForCondition(() => oldEventCallback == null);
notification.options.eventCallback = function() {
if (oldEventCallback) {
oldEventCallback();
}
oldEventCallback = null;
};
yield ContentTask.spawn(gTestBrowser, {}, function* () {
let doc = content.document;
let plugin = doc.getElementById("test");
let bounds = plugin.getBoundingClientRect();
let left = (bounds.left + bounds.right) / 2;
let top = (bounds.top + bounds.bottom) / 2;
let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
});
yield promise;
ok(notification, "Test 18g, Should have a click-to-play notification");
ok(!notification.dismissed, "Test 18g, notification should be open");
pluginInfo = yield promiseForPluginInfo("test");
ok(!pluginInfo.activated, "Test 18g, Plugin should not be activated");
});
// Test that "always allow"-ing a plugin will not allow it when it becomes
// blocklisted.
add_task(function* () {
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 24a, Should have a click-to-play notification");
// Plugin should start as CTP
let pluginInfo = yield promiseForPluginInfo("test");
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 24a, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
ok(!pluginInfo.activated, "Test 24a, Plugin should not be active.");
// simulate "always allow"
yield promiseForNotificationShown(notification);
PopupNotifications.panel.firstChild._primaryButton.click();
pluginInfo = yield promiseForPluginInfo("test");
ok(pluginInfo.activated, "Test 24a, Plugin should be active.");
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginVulnerableUpdatable.xml", gTestBrowser);
});
// the plugin is now blocklisted, so it should not automatically load
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 24b, Should have a click-to-play notification");
let pluginInfo = yield promiseForPluginInfo("test");
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE,
"Test 24b, plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE");
ok(!pluginInfo.activated, "Test 24b, Plugin should not be active.");
// simulate "always allow"
yield promiseForNotificationShown(notification);
PopupNotifications.panel.firstChild._primaryButton.click();
pluginInfo = yield promiseForPluginInfo("test");
ok(pluginInfo.activated, "Test 24b, Plugin should be active.");
clearAllPluginPermissions();
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
});
// Plugin sync removal test. Note this test produces a notification drop down since
// the plugin we add has zero dims.
add_task(function* () {
updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_syncRemoved.html");
// Maybe there some better trick here, we need to wait for the page load, then
// wait for the js to execute in the page.
yield waitForMs(500);
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 25: There should be a plugin notification even if the plugin was immediately removed");
ok(notification.dismissed, "Test 25: The notification should be dismissed by default");
yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html>hi</html>");
});
// Tests a page with a blocked plugin in it and make sure the infoURL property
// the blocklist file gets used.
add_task(function* () {
clearAllPluginPermissions();
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginInfoURL.xml", gTestBrowser);
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let notification = PopupNotifications.getNotification("click-to-play-plugins");
// Since the plugin notification is dismissed by default, reshow it.
yield promiseForNotificationShown(notification);
let pluginInfo = yield promiseForPluginInfo("test");
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED,
"Test 26, plugin fallback type should be PLUGIN_BLOCKLISTED");
let result = ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementById("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
return objLoadingContent.activated;
});
ok(result, "Plugin should be activated.");
const testUrl = "http://test.url.com/";
let firstPanelChild = PopupNotifications.panel.firstChild;
let infoLink = document.getAnonymousElementByAttribute(firstPanelChild, "anonid",
"click-to-play-plugins-notification-link");
is(infoLink.href, testUrl,
"Test 26, the notification URL needs to match the infoURL from the blocklist file.");
});

Просмотреть файл

@ -0,0 +1,108 @@
let gTestBrowser = null;
let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
let gChromeRoot = getRootDirectory(gTestPath);
add_task(function* () {
registerCleanupFunction(Task.async(function*() {
clearAllPluginPermissions();
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
Services.prefs.clearUserPref("plugins.click_to_play");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
resetBlocklist();
gBrowser.removeCurrentTab();
window.focus();
gTestBrowser = null;
}));
});
add_task(function* () {
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
Services.prefs.setBoolPref("plugins.click_to_play", true);
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
// Prime the blocklist service, the remote service doesn't launch on startup.
yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html></html>");
let exmsg = yield promiseInitContentBlocklistSvc(gBrowser.selectedBrowser);
ok(!exmsg, "exception: " + exmsg);
});
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let test = content.document.getElementById("test");
return test.activated;
});
ok(result, "task 1a: test plugin should be activated!");
});
// Load a fresh page, load a new plugin blocklist, then load the same page again.
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html>GO!</html>");
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginHard.xml", gTestBrowser);
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let test = content.document.getElementById("test");
return test.activated;
});
ok(!result, "task 2a: test plugin shouldn't activate!");
});
// Unload the block list and lets do this again, only this time lets
// hack around in the content blocklist service maliciously.
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html>GO!</html>");
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
// Hack the planet! Load our blocklist shim, so we can mess with blocklist
// return results in the content process. Active until we close our tab.
let mm = gTestBrowser.messageManager;
info("test 3a: loading " + gChromeRoot + "blocklist_proxy.js" + "\n");
mm.loadFrameScript(gChromeRoot + "blocklist_proxy.js", true);
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let test = content.document.getElementById("test");
return test.activated;
});
ok(result, "task 3a: test plugin should be activated!");
});
// Load a fresh page, load a new plugin blocklist, then load the same page again.
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html>GO!</html>");
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginHard.xml", gTestBrowser);
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let test = content.document.getElementById("test");
return test.activated;
});
ok(!result, "task 4a: test plugin shouldn't activate!");
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
});

Просмотреть файл

@ -1,114 +1,127 @@
var rootDir = getRootDirectory(gTestPath); let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://mochi.test:8888/"); let gTestBrowser = null;
var gTestBrowser = null; add_task(function* () {
var gNextTest = null; registerCleanupFunction(Task.async(function*() {
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions(); clearAllPluginPermissions();
Services.prefs.clearUserPref("plugins.click_to_play"); Services.prefs.clearUserPref("plugins.click_to_play");
}); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
Services.prefs.setBoolPref("plugins.click_to_play", true); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
resetBlocklist();
gBrowser.removeCurrentTab();
window.focus();
gTestBrowser = null;
}));
});
var newTab = gBrowser.addTab(); add_task(function* () {
let newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab; gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser; gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
prepareTest(test1a, gTestRoot + "plugin_add_dynamically.html");
}
function finishTest() { Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
gTestBrowser.removeEventListener("load", pageLoad, true); Services.prefs.setBoolPref("plugins.click_to_play", true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() { setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
// The plugin events are async dispatched and can come after the load event setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
// This just allows the events to fire before we then go on to test the states
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) { // Prime the blocklist service, the remote service doesn't launch on startup.
gNextTest = nextTest; yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html></html>");
gTestBrowser.contentWindow.location = url;
} let exmsg = yield promiseInitContentBlocklistSvc(gBrowser.selectedBrowser);
ok(!exmsg, "exception: " + exmsg);
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
});
// Tests that navigation within the page and the window.history API doesn't break click-to-play state. // Tests that navigation within the page and the window.history API doesn't break click-to-play state.
function test1a() { add_task(function* () {
var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_add_dynamically.html");
ok(!popupNotification, "Test 1a, Should not have a click-to-play notification");
var plugin = new XPCNativeWrapper(XPCNativeWrapper.unwrap(gTestBrowser.contentWindow).addPlugin());
var condition = function() PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
waitForCondition(condition, test1b, "Test 1a, Waited too long for plugin notification"); ok(!notification, "Test 1a, Should not have a click-to-play notification");
}
function test1b() { yield ContentTask.spawn(gTestBrowser, {}, function* () {
var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin());
ok(popupNotification, "Test 1b, Should have a click-to-play notification"); });
var plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[0];
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); yield promisePopupNotification("click-to-play-plugins");
ok(!objLoadingContent.activated, "Test 1b, Plugin should not be activated"); });
add_task(function* () {
let isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementsByTagName("embed")[0];
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
return objLoadingContent.activated;
});
ok(!isActivated, "Test 1b, Plugin should not be activated");
// Click the activate button on doorhanger to make sure it works // Click the activate button on doorhanger to make sure it works
popupNotification.reshow(); let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
yield promiseForNotificationShown(notification);
PopupNotifications.panel.firstChild._primaryButton.click(); PopupNotifications.panel.firstChild._primaryButton.click();
var condition = function() objLoadingContent.activated;
waitForCondition(condition, test1c, "Test 1b, Waited too long for plugin activation");
}
function test1c() { isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); let plugin = content.document.getElementsByTagName("embed")[0];
ok(popupNotification, "Test 1c, Should still have a click-to-play notification"); let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
var plugin = new XPCNativeWrapper(XPCNativeWrapper.unwrap(gTestBrowser.contentWindow).addPlugin()); return objLoadingContent.activated;
});
ok(isActivated, "Test 1b, Plugin should be activated");
});
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); add_task(function* () {
var condition = function() objLoadingContent.activated; let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
waitForCondition(condition, test1d, "Test 1c, Waited too long for plugin activation"); ok(notification, "Test 1c, Should still have a click-to-play notification");
}
function test1d() { let isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
var plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[1]; new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin());
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); let plugin = content.document.getElementsByTagName("embed")[1];
ok(objLoadingContent.activated, "Test 1d, Plugin should be activated"); let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
return objLoadingContent.activated;
});
ok(isActivated, "Test 1c, Newly inserted plugin in activated page should be activated");
});
gNextTest = test1e; add_task(function* () {
gTestBrowser.contentWindow.addEventListener("hashchange", test1e, false); let isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementsByTagName("embed")[1];
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
return objLoadingContent.activated;
});
ok(isActivated, "Test 1d, Plugin should be activated");
let promise = waitForEvent(gTestBrowser.contentWindow, "hashchange", null);
gTestBrowser.contentWindow.location += "#anchorNavigation"; gTestBrowser.contentWindow.location += "#anchorNavigation";
} yield promise;
});
function test1e() { add_task(function* () {
gTestBrowser.contentWindow.removeEventListener("hashchange", test1e, false); let isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin());
let plugin = content.document.getElementsByTagName("embed")[2];
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
return objLoadingContent.activated;
});
ok(isActivated, "Test 1e, Plugin should be activated");
});
var plugin = new XPCNativeWrapper(XPCNativeWrapper.unwrap(gTestBrowser.contentWindow).addPlugin()); add_task(function* () {
let isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementsByTagName("embed")[2];
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
return objLoadingContent.activated;
});
ok(isActivated, "Test 1f, Plugin should be activated");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
var condition = function() objLoadingContent.activated; content.history.replaceState({}, "", "replacedState");
waitForCondition(condition, test1f, "Test 1e, Waited too long for plugin activation"); new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin());
} let plugin = content.document.getElementsByTagName("embed")[3];
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
function test1f() { return objLoadingContent.activated;
var plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[2]; });
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); ok(isActivated, "Test 1f, Plugin should not be activated");
ok(objLoadingContent.activated, "Test 1f, Plugin should be activated"); });
gTestBrowser.contentWindow.history.replaceState({}, "", "replacedState");
var plugin = new XPCNativeWrapper(XPCNativeWrapper.unwrap(gTestBrowser.contentWindow).addPlugin());
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
var condition = function() objLoadingContent.activated;
waitForCondition(condition, test1g, "Test 1f, Waited too long for plugin activation");
}
function test1g() {
var plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[3];
var objLoadingContent2 = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(objLoadingContent2.activated, "Test 1g, Plugin should be activated");
finishTest();
}

Просмотреть файл

@ -1,43 +1,51 @@
/* This Source Code Form is subject to the terms of the Mozilla Public let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
* License, v. 2.0. If a copy of the MPL was not distributed with this let gTestBrowser = null;
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ let gNumPluginBindingsAttached = 0;
var gTestBrowser = null;
var gNumPluginBindingsAttached = 0;
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
Services.prefs.clearUserPref("plugins.click_to_play");
gTestBrowser.removeEventListener("PluginBindingAttached", pluginBindingAttached, true, true);
gBrowser.removeCurrentTab();
window.focus();
});
Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("PluginBindingAttached", pluginBindingAttached, true, true);
var gHttpTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
gTestBrowser.contentWindow.location = gHttpTestRoot + "plugin_bug744745.html";
}
function pluginBindingAttached() { function pluginBindingAttached() {
gNumPluginBindingsAttached++; gNumPluginBindingsAttached++;
if (gNumPluginBindingsAttached != 1) {
if (gNumPluginBindingsAttached == 1) {
var doc = gTestBrowser.contentDocument;
var testplugin = doc.getElementById("test");
ok(testplugin, "should have test plugin");
var style = getComputedStyle(testplugin);
ok('opacity' in style, "style should have opacity set");
is(style.opacity, 1, "opacity should be 1");
finish();
} else {
ok(false, "if we've gotten here, something is quite wrong"); ok(false, "if we've gotten here, something is quite wrong");
} }
} }
add_task(function* () {
registerCleanupFunction(function () {
gTestBrowser.removeEventListener("PluginBindingAttached", pluginBindingAttached, true, true);
clearAllPluginPermissions();
Services.prefs.clearUserPref("plugins.click_to_play");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
gBrowser.removeCurrentTab();
window.focus();
gTestBrowser = null;
});
});
add_task(function* () {
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
gTestBrowser.addEventListener("PluginBindingAttached", pluginBindingAttached, true, true);
let testRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
yield promiseTabLoadEvent(gBrowser.selectedTab, testRoot + "plugin_bug744745.html");
yield promiseForCondition(function () { return gNumPluginBindingsAttached == 1; });
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementById("test");
if (!plugin) {
return false;
}
// We can't use MochiKit's routine
let style = content.getComputedStyle(plugin);
return 'opacity' in style && style.opacity == 1;
});
ok(result, true, "plugin style properly configured.");
});

Просмотреть файл

@ -1,49 +1,65 @@
const gHttpTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
let gTestBrowser = null; let gTestBrowser = null;
let gWrapperClickCount = 0; let gWrapperClickCount = 0;
function test() { add_task(function* () {
waitForExplicitFinish(); registerCleanupFunction(function () {
registerCleanupFunction(function() { clearAllPluginPermissions();
Services.prefs.clearUserPref("plugins.click_to_play"); Services.prefs.clearUserPref("plugins.click_to_play");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
gBrowser.removeCurrentTab();
window.focus();
gTestBrowser = null;
}); });
});
add_task(function* () {
Services.prefs.setBoolPref("plugins.click_to_play", true); Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser; gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
gTestBrowser.contentWindow.location = gHttpTestRoot + "plugin_bug787619.html";
}
function pageLoad() { setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
let testRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
yield promiseTabLoadEvent(gBrowser.selectedTab, testRoot + "plugin_bug787619.html");
// Due to layout being async, "PluginBindAttached" may trigger later. // Due to layout being async, "PluginBindAttached" may trigger later.
// This forces a layout flush, thus triggering it, and schedules the // This forces a layout flush, thus triggering it, and schedules the
// test so it is definitely executed afterwards. // test so it is definitely executed afterwards.
gTestBrowser.contentDocument.getElementById('plugin').clientTop; yield promiseUpdatePluginBindings(gTestBrowser);
executeSoon(part1);
}
function part1() { // check plugin state
let wrapper = gTestBrowser.contentDocument.getElementById('wrapper'); let pluginInfo = yield promiseForPluginInfo("plugin");
wrapper.addEventListener('click', function() ++gWrapperClickCount, false); ok(!pluginInfo.activated, "1a plugin should not be activated");
let plugin = gTestBrowser.contentDocument.getElementById('plugin'); // click the overlay to prompt
ok(plugin, 'got plugin element'); let promise = promisePopupNotification("click-to-play-plugins");
ok(!plugin.activated, 'plugin should not be activated'); yield ContentTask.spawn(gTestBrowser, {}, function* () {
ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed, "Doorhanger should not be open"); let plugin = content.document.getElementById("plugin");
let bounds = plugin.getBoundingClientRect();
let left = (bounds.left + bounds.right) / 2;
let top = (bounds.top + bounds.bottom) / 2;
let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
});
yield promise;
EventUtils.synthesizeMouseAtCenter(plugin, {}, gTestBrowser.contentWindow); // check plugin state
let condition = function() !PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed; pluginInfo = yield promiseForPluginInfo("plugin");
waitForCondition(condition, part2, ok(!pluginInfo.activated, "1b plugin should not be activated");
'waited too long for plugin to activate');
} let condition = function() !PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed &&
PopupNotifications.panel.firstChild;
yield promiseForCondition(condition);
PopupNotifications.panel.firstChild._primaryButton.click();
// check plugin state
pluginInfo = yield promiseForPluginInfo("plugin");
ok(pluginInfo.activated, "plugin should be activated");
function part2() {
is(gWrapperClickCount, 0, 'wrapper should not have received any clicks'); is(gWrapperClickCount, 0, 'wrapper should not have received any clicks');
gTestBrowser.removeEventListener("load", pageLoad, true); });
gBrowser.removeCurrentTab();
window.focus();
finish();
}

Просмотреть файл

@ -1,47 +1,45 @@
/* This Source Code Form is subject to the terms of the Mozilla Public let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
* License, v. 2.0. If a copy of the MPL was not distributed with this let gTestBrowser = null;
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ let gConsoleErrors = 0;
var rootDir = getRootDirectory(gTestPath);
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const Cc = Components.classes; const Cc = Components.classes;
const Ci = Components.interfaces; const Ci = Components.interfaces;
var gTestBrowser = null;
var gConsoleErrors = 0;
function test() { add_task(function* () {
waitForExplicitFinish(); registerCleanupFunction(function () {
var newTab = gBrowser.addTab(); clearAllPluginPermissions();
gBrowser.selectedTab = newTab; setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
consoleService.unregisterListener(errorListener);
gBrowser.removeCurrentTab();
window.focus();
gTestBrowser = null;
});
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser; gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("PluginBindingAttached", pluginBindingAttached, true, true);
var consoleService = Cc["@mozilla.org/consoleservice;1"] let bindingPromise = waitForEvent(gTestBrowser, "PluginBindingAttached", null, true, true);
let consoleService = Cc["@mozilla.org/consoleservice;1"]
.getService(Ci.nsIConsoleService); .getService(Ci.nsIConsoleService);
var errorListener = { let errorListener = {
observe: function(aMessage) { observe: function(aMessage) {
if (aMessage.message.includes("NS_ERROR")) if (aMessage.message.includes("NS_ERROR_FAILURE"))
gConsoleErrors++; gConsoleErrors++;
} }
}; };
consoleService.registerListener(errorListener); consoleService.registerListener(errorListener);
registerCleanupFunction(function() {
gTestBrowser.removeEventListener("PluginBindingAttached", pluginBindingAttached, true);
consoleService.unregisterListener(errorListener);
gBrowser.removeCurrentTab();
window.focus();
});
gTestBrowser.contentWindow.location = gHttpTestRoot + "plugin_bug797677.html";
}
function pluginBindingAttached() { yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_bug797677.html");
// Let browser-plugins.js handle the PluginNotFound event, then run the test
executeSoon(runTest);
}
function runTest() { yield bindingPromise;
var doc = gTestBrowser.contentDocument;
var plugin = doc.getElementById("plugin"); let pluginInfo = yield promiseForPluginInfo("plugin");
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED, "plugin should not have been found.");
// simple cpows
let plugin = gTestBrowser.contentDocument.getElementById("plugin");
ok(plugin, "plugin should be in the page"); ok(plugin, "plugin should be in the page");
is(gConsoleErrors, 0, "should have no console errors"); is(gConsoleErrors, 0, "should have no console errors");
finish(); });
}

Просмотреть файл

@ -1,93 +1,80 @@
var gHttpTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
var gTestBrowser = null; let gTestBrowser = null;
var gNextTest = null;
Components.utils.import("resource://gre/modules/Services.jsm"); add_task(function* () {
registerCleanupFunction(Task.async(function*() {
function test() { clearAllPluginPermissions();
waitForExplicitFinish(); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
registerCleanupFunction(function() { setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
Services.prefs.clearUserPref("plugins.click_to_play"); yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
});
Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
var newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
setAndUpdateBlocklist(gHttpTestRoot + "blockPluginVulnerableUpdatable.xml",
function() {
prepareTest(function() {
// Due to layout being async, "PluginBindAttached" may trigger later.
// This forces a layout flush, thus triggering it, and schedules the
// test so it is definitely executed afterwards.
gTestBrowser.contentDocument.getElementById('test').clientTop;
testPart1();
},
gHttpTestRoot + "plugin_test.html");
});
}
function finishTest() {
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
setAndUpdateBlocklist(gHttpTestRoot + "blockNoPlugins.xml",
function() {
resetBlocklist(); resetBlocklist();
finish(); Services.prefs.clearUserPref("plugins.click_to_play");
}); gBrowser.removeCurrentTab();
} window.focus();
gTestBrowser = null;
}));
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
function pageLoad(aEvent) { Services.prefs.setBoolPref("plugins.click_to_play", true);
// The plugin events are async dispatched and can come after the load event setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
// This just allows the events to fire before we then go on to test the states
if (gNextTest != null)
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) { // Prime the blocklist service, the remote service doesn't launch on startup.
gNextTest = nextTest; yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html></html>");
gTestBrowser.contentWindow.location = url; let exmsg = yield promiseInitContentBlocklistSvc(gBrowser.selectedBrowser);
} ok(!exmsg, "exception: " + exmsg);
});
// Tests that the going back will reshow the notification for click-to-play // Tests that the going back will reshow the notification for click-to-play
// blocklisted plugins (part 1/4) // blocklisted plugins
function testPart1() { add_task(function* () {
var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); yield asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginVulnerableUpdatable.xml", gTestBrowser);
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "test part 1: Should have a click-to-play notification"); ok(popupNotification, "test part 1: Should have a click-to-play notification");
var plugin = gTestBrowser.contentDocument.getElementById("test");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, "test part 1: plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE");
ok(!objLoadingContent.activated, "test part 1: plugin should not be activated");
prepareTest(testPart2, "about:blank"); let pluginInfo = yield promiseForPluginInfo("test");
} is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, "plugin should be marked as VULNERABLE");
function testPart2() { let found = yield ContentTask.spawn(gTestBrowser, {}, function* () {
var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); return !!content.document.getElementById("test");
});
ok(found, "test part 1: plugin should not be activated");
yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html></html>");
});
add_task(function* () {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(!popupNotification, "test part 2: Should not have a click-to-play notification"); ok(!popupNotification, "test part 2: Should not have a click-to-play notification");
var plugin = gTestBrowser.contentDocument.getElementById("test"); let found = yield ContentTask.spawn(gTestBrowser, {}, function* () {
ok(!plugin, "test part 2: Should not have a plugin in this page"); return !!content.document.getElementById("test");
});
ok(!found, "test part 2: plugin should not be activated");
Services.obs.addObserver(testPart3, "PopupNotifications-updateNotShowing", false); let obsPromise = TestUtils.topicObserved("PopupNotifications-updateNotShowing");
let overlayPromise = promisePopupNotification("click-to-play-plugins");
gTestBrowser.contentWindow.history.back(); gTestBrowser.contentWindow.history.back();
} yield obsPromise;
yield overlayPromise;
});
function testPart3() { add_task(function* () {
Services.obs.removeObserver(testPart3, "PopupNotifications-updateNotShowing"); yield promiseUpdatePluginBindings(gTestBrowser);
var condition = function() PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
waitForCondition(condition, testPart4, "test part 3: waited too long for click-to-play-plugin notification");
}
function testPart4() { let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); ok(popupNotification, "test part 3: Should have a click-to-play notification");
ok(popupNotification, "test part 4: Should have a click-to-play notification");
var plugin = gTestBrowser.contentDocument.getElementById("test");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, "test part 4: plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE");
ok(!objLoadingContent.activated, "test part 4: plugin should not be activated");
finishTest(); let pluginInfo = yield promiseForPluginInfo("test");
} is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, "plugin should be marked as VULNERABLE");
let found = yield ContentTask.spawn(gTestBrowser, {}, function* () {
return !!content.document.getElementById("test");
});
ok(found, "test part 3: plugin should not be activated");
});

Просмотреть файл

@ -1,45 +1,38 @@
var gHttpTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
var gTestBrowser = null; let gTestBrowser = null;
Components.utils.import("resource://gre/modules/Services.jsm"); add_task(function* () {
registerCleanupFunction(function () {
function test() { clearAllPluginPermissions();
waitForExplicitFinish();
registerCleanupFunction(function() {
Services.prefs.clearUserPref("plugins.click_to_play"); Services.prefs.clearUserPref("plugins.click_to_play");
gTestBrowser.removeEventListener("load", pageLoad, true); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
gBrowser.removeCurrentTab();
window.focus();
gTestBrowser = null;
}); });
});
add_task(function* () {
Services.prefs.setBoolPref("plugins.click_to_play", true); Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser; gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
gTestBrowser.contentWindow.location = gHttpTestRoot + "plugin_both.html";
}
function pageLoad(aEvent) { setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
// Due to layout being async, "PluginBindAttached" may trigger later.
// This forces a layout flush, thus triggering it, and schedules the
// test so it is definitely executed afterwards.
gTestBrowser.contentDocument.getElementById('test').clientTop;
executeSoon(actualTest);
}
function actualTest() { yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_both.html");
var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "should have a click-to-play notification"); ok(popupNotification, "should have a click-to-play notification");
var plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "should have known plugin in page");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, "plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
ok(!objLoadingContent.activated, "plugin should not be activated");
var unknown = gTestBrowser.contentDocument.getElementById("unknown"); let pluginInfo = yield promiseForPluginInfo("test");
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, "plugin should be click to play");
ok(!pluginInfo.activated, "plugin should not be activated");
let unknown = gTestBrowser.contentDocument.getElementById("unknown");
ok(unknown, "should have unknown plugin in page"); ok(unknown, "should have unknown plugin in page");
});
gBrowser.removeCurrentTab();
window.focus();
finish();
}

Просмотреть файл

@ -1,61 +1,73 @@
/* This Source Code Form is subject to the terms of the Mozilla Public let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
* License, v. 2.0. If a copy of the MPL was not distributed with this let gTestBrowser = null;
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ let gNumPluginBindingsAttached = 0;
var gTestBrowser = null; add_task(function* () {
var gNumPluginBindingsAttached = 0; registerCleanupFunction(function () {
clearAllPluginPermissions();
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
Services.prefs.clearUserPref("plugins.click_to_play"); Services.prefs.clearUserPref("plugins.click_to_play");
gTestBrowser.removeEventListener("PluginBindingAttached", pluginBindingAttached, true, true); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
gBrowser.removeCurrentTab(); gBrowser.removeCurrentTab();
window.focus(); window.focus();
gTestBrowser = null;
}); });
});
add_task(function* () {
Services.prefs.setBoolPref("plugins.click_to_play", true); Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser; gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("PluginBindingAttached", pluginBindingAttached, true, true);
var gHttpTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
gTestBrowser.contentWindow.location = gHttpTestRoot + "plugin_bug820497.html";
}
function pluginBindingAttached() { setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
gNumPluginBindingsAttached++; setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
if (gNumPluginBindingsAttached == 1) { gTestBrowser.addEventListener("PluginBindingAttached", function () { gNumPluginBindingsAttached++ }, true, true);
var doc = gTestBrowser.contentDocument;
var testplugin = doc.getElementById("test"); yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_bug820497.html");
yield promiseForCondition(function () { return gNumPluginBindingsAttached == 1; });
// cpows
{
// Note we add the second plugin in the code farther down, so there's
// no way we got here with anything but one plugin loaded.
let doc = gTestBrowser.contentDocument;
let testplugin = doc.getElementById("test");
ok(testplugin, "should have test plugin"); ok(testplugin, "should have test plugin");
var secondtestplugin = doc.getElementById("secondtest"); let secondtestplugin = doc.getElementById("secondtest");
ok(!secondtestplugin, "should not yet have second test plugin"); ok(!secondtestplugin, "should not yet have second test plugin");
var notification;
waitForNotificationPopup("click-to-play-plugins", gTestBrowser, (notification => {
ok(notification, "should have popup notification");
// We don't set up the action list until the notification is shown
notification.reshow();
is(notification.options.pluginData.size, 1, "should be 1 type of plugin in the popup notification");
XPCNativeWrapper.unwrap(gTestBrowser.contentWindow).addSecondPlugin();
}));
} else if (gNumPluginBindingsAttached == 2) {
var doc = gTestBrowser.contentDocument;
var testplugin = doc.getElementById("test");
ok(testplugin, "should have test plugin");
var secondtestplugin = doc.getElementById("secondtest");
ok(secondtestplugin, "should have second test plugin");
var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "should have popup notification");
notification.reshow();
let condition = () => (notification.options.pluginData.size == 2);
waitForCondition(condition, finish, "Waited too long for 2 types of plugins in popup notification");
} else {
ok(false, "if we've gotten here, something is quite wrong");
} }
}
yield promisePopupNotification("click-to-play-plugins");
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "should have a click-to-play notification");
yield promiseForNotificationShown(notification);
is(notification.options.pluginData.size, 1, "should be 1 type of plugin in the popup notification");
yield ContentTask.spawn(gTestBrowser, {}, function* () {
XPCNativeWrapper.unwrap(content).addSecondPlugin();
});
yield promiseForCondition(function () { return gNumPluginBindingsAttached == 2; });
// cpows
{
let doc = gTestBrowser.contentDocument;
let testplugin = doc.getElementById("test");
ok(testplugin, "should have test plugin");
let secondtestplugin = doc.getElementById("secondtest");
ok(secondtestplugin, "should have second test plugin");
}
notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "should have popup notification");
yield promiseForNotificationShown(notification);
is(notification.options.pluginData.size, 2, "aited too long for 2 types of plugins in popup notification");
});

Просмотреть файл

@ -21,8 +21,6 @@
"baz.com:1:5," + "baz.com:1:5," +
"qux.com:1:100" "qux.com:1:100"
); );
setTimeout(testFinishedCallback, 0);
} }
</script> </script>
</head> </head>

Просмотреть файл

@ -1,50 +1,63 @@
/** var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
* Any copyright is dedicated to the Public Domain. var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
* http://creativecommons.org/publicdomain/zero/1.0/ var gTestBrowser = null;
*/
var rootDir = getRootDirectory(gTestPath);
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://mochi.test:8888/");
// Test clearing plugin data using sanitize.js. // Test clearing plugin data using sanitize.js.
const testURL1 = gHttpTestRoot + "browser_clearplugindata.html"; const testURL1 = gTestRoot + "browser_clearplugindata.html";
const testURL2 = gHttpTestRoot + "browser_clearplugindata_noage.html"; const testURL2 = gTestRoot + "browser_clearplugindata_noage.html";
let tempScope = {}; var tempScope = {};
Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
.loadSubScript("chrome://browser/content/sanitize.js", tempScope); .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
let Sanitizer = tempScope.Sanitizer; var Sanitizer = tempScope.Sanitizer;
const pluginHostIface = Ci.nsIPluginHost; const pluginHostIface = Ci.nsIPluginHost;
var pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); var pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
pluginHost.QueryInterface(pluginHostIface); pluginHost.QueryInterface(pluginHostIface);
var pluginTag = getTestPlugin(); var pluginTag = getTestPlugin();
var s; var sanitizer = null;
function stored(needles) { function stored(needles) {
var something = pluginHost.siteHasData(this.pluginTag, null); let something = pluginHost.siteHasData(this.pluginTag, null);
if (!needles) if (!needles)
return something; return something;
if (!something) if (!something)
return false; return false;
for (var i = 0; i < needles.length; ++i) { for (let i = 0; i < needles.length; ++i) {
if (!pluginHost.siteHasData(this.pluginTag, needles[i])) if (!pluginHost.siteHasData(this.pluginTag, needles[i]))
return false; return false;
} }
return true; return true;
} }
function test() { add_task(function* () {
waitForExplicitFinish(); registerCleanupFunction(function () {
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED); clearAllPluginPermissions();
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
if (gTestBrowser) {
gBrowser.removeCurrentTab();
}
window.focus();
gTestBrowser = null;
});
});
s = new Sanitizer(); add_task(function* () {
s.ignoreTimespan = false; Services.prefs.setBoolPref("plugins.click_to_play", true);
s.prefDomain = "privacy.cpd.";
var itemPrefs = gPrefService.getBranch(s.prefDomain); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
sanitizer = new Sanitizer();
sanitizer.ignoreTimespan = false;
sanitizer.prefDomain = "privacy.cpd.";
let itemPrefs = gPrefService.getBranch(sanitizer.prefDomain);
itemPrefs.setBoolPref("history", false); itemPrefs.setBoolPref("history", false);
itemPrefs.setBoolPref("downloads", false); itemPrefs.setBoolPref("downloads", false);
itemPrefs.setBoolPref("cache", false); itemPrefs.setBoolPref("cache", false);
@ -54,80 +67,61 @@ function test() {
itemPrefs.setBoolPref("passwords", false); itemPrefs.setBoolPref("passwords", false);
itemPrefs.setBoolPref("sessions", false); itemPrefs.setBoolPref("sessions", false);
itemPrefs.setBoolPref("siteSettings", false); itemPrefs.setBoolPref("siteSettings", false);
});
executeSoon(test_with_age); add_task(function* () {
}
function setFinishedCallback(callback)
{
let testPage = gBrowser.selectedBrowser.contentWindow.wrappedJSObject;
testPage.testFinishedCallback = function() {
setTimeout(function() {
info("got finished callback");
callback();
}, 0);
}
}
function test_with_age()
{
// Load page to set data for the plugin. // Load page to set data for the plugin.
gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function () { gTestBrowser = gBrowser.selectedBrowser;
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
setFinishedCallback(function() { yield promiseTabLoadEvent(gBrowser.selectedTab, testURL1);
ok(stored(["foo.com","bar.com","baz.com","qux.com"]),
"Data stored for sites");
// Clear 20 seconds ago yield promiseUpdatePluginBindings(gTestBrowser);
var now_uSec = Date.now() * 1000;
s.range = [now_uSec - 20*1000000, now_uSec];
s.sanitize();
ok(stored(["bar.com","qux.com"]), "Data stored for sites"); ok(stored(["foo.com","bar.com","baz.com","qux.com"]),
ok(!stored(["foo.com"]), "Data cleared for foo.com"); "Data stored for sites");
ok(!stored(["baz.com"]), "Data cleared for baz.com");
// Clear everything // Clear 20 seconds ago
s.range = null; let now_uSec = Date.now() * 1000;
s.sanitize(); sanitizer.range = [now_uSec - 20*1000000, now_uSec];
sanitizer.sanitize();
ok(!stored(null), "All data cleared"); ok(stored(["bar.com","qux.com"]), "Data stored for sites");
ok(!stored(["foo.com"]), "Data cleared for foo.com");
ok(!stored(["baz.com"]), "Data cleared for baz.com");
gBrowser.removeCurrentTab(); // Clear everything
sanitizer.range = null;
sanitizer.sanitize();
executeSoon(test_without_age); ok(!stored(null), "All data cleared");
});
}, true);
content.location = testURL1;
}
function test_without_age() gBrowser.removeCurrentTab();
{ gTestBrowser = null;
});
add_task(function* () {
// Load page to set data for the plugin. // Load page to set data for the plugin.
gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function () { gTestBrowser = gBrowser.selectedBrowser;
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
setFinishedCallback(function() { yield promiseTabLoadEvent(gBrowser.selectedTab, testURL2);
ok(stored(["foo.com","bar.com","baz.com","qux.com"]),
"Data stored for sites");
// Attempt to clear 20 seconds ago. The plugin will throw yield promiseUpdatePluginBindings(gTestBrowser);
// NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED, which should result in us
// clearing all data regardless of age.
var now_uSec = Date.now() * 1000;
s.range = [now_uSec - 20*1000000, now_uSec];
s.sanitize();
ok(!stored(null), "All data cleared"); ok(stored(["foo.com","bar.com","baz.com","qux.com"]),
"Data stored for sites");
gBrowser.removeCurrentTab(); // Attempt to clear 20 seconds ago. The plugin will throw
// NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED, which should result in us
// clearing all data regardless of age.
let now_uSec = Date.now() * 1000;
sanitizer.range = [now_uSec - 20*1000000, now_uSec];
sanitizer.sanitize();
executeSoon(finish); ok(!stored(null), "All data cleared");
});
}, true); gBrowser.removeCurrentTab();
content.location = testURL2; gTestBrowser = null;
} });

Просмотреть файл

@ -21,8 +21,6 @@
"baz.com:1:5," + "baz.com:1:5," +
"qux.com:1:100" "qux.com:1:100"
); );
setTimeout(testFinishedCallback, 0);
} }
</script> </script>
</head> </head>

Просмотреть файл

@ -1,7 +1,3 @@
/* 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/. */
/** /**
* Test that plugin crash submissions still work properly after * Test that plugin crash submissions still work properly after
* click-to-play activation. * click-to-play activation.

Просмотреть файл

@ -1,9 +1,4 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Cu.import("resource://gre/modules/CrashSubmit.jsm", this); Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
Cu.import("resource://gre/modules/Services.jsm");
const CRASH_URL = "http://example.com/browser/browser/base/content/test/plugins/plugin_crashCommentAndURL.html"; const CRASH_URL = "http://example.com/browser/browser/base/content/test/plugins/plugin_crashCommentAndURL.html";
const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs"; const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";

Просмотреть файл

@ -0,0 +1,77 @@
let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
let gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
let gTestBrowser = null;
add_task(function* () {
registerCleanupFunction(function () {
clearAllPluginPermissions();
Services.prefs.clearUserPref("plugins.click_to_play");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
gTestBrowser = null;
gBrowser.removeCurrentTab();
window.focus();
});
});
add_task(function* () {
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_DISABLED, "Test Plug-in");
// Prime the blocklist service, the remote service doesn't launch on startup.
yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html></html>");
let exmsg = yield promiseInitContentBlocklistSvc(gBrowser.selectedBrowser);
ok(!exmsg, "exception: " + exmsg);
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
});
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
yield promiseUpdatePluginBindings(gTestBrowser);
let pluginInfo = yield promiseForPluginInfo("test");
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_DISABLED,
"Test 1a, plugin fallback type should be PLUGIN_DISABLED");
// This test opens a new tab to about:addons
let promise = waitForEvent(gBrowser.tabContainer, "TabOpen", null, true);
let success = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let pluginNode = content.document.getElementById("test");
let manageLink = content.document.getAnonymousElementByAttribute(pluginNode, "anonid", "managePluginsLink");
let bounds = manageLink.getBoundingClientRect();
let left = (bounds.left + bounds.right) / 2;
let top = (bounds.top + bounds.bottom) / 2;
let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
return true;
});
ok(success, "click on manage link");
yield promise;
promise = waitForEvent(gBrowser.tabContainer, "TabClose", null, true);
// in-process page, no cpows here
let condition = function() {
let win = gBrowser.selectedBrowser.contentWindow;
if (!!win && !!win.wrappedJSObject && !!win.wrappedJSObject.gViewController) {
return win.wrappedJSObject.gViewController.currentViewId == "addons://list/plugin";
}
return false;
}
yield promiseForCondition(condition, "Waited too long for about:addons to display.", 40, 500);
// remove the tab containing about:addons
gBrowser.removeCurrentTab();
yield promise;
});

Просмотреть файл

@ -0,0 +1,85 @@
let gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
let gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
let gTestBrowser = null;
function updateAllTestPlugins(aState) {
setTestPluginEnabledState(aState, "Test Plug-in");
setTestPluginEnabledState(aState, "Second Test Plug-in");
}
add_task(function* () {
registerCleanupFunction(Task.async(function*() {
clearAllPluginPermissions();
Services.prefs.clearUserPref("plugins.click_to_play");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
resetBlocklist();
gTestBrowser = null;
gBrowser.removeCurrentTab();
window.focus();
}));
});
add_task(function* () {
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
Services.prefs.setBoolPref("plugins.click_to_play", true);
updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
// Prime the blocklist service, the remote service doesn't launch on startup.
yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html></html>");
let exmsg = yield promiseInitContentBlocklistSvc(gBrowser.selectedBrowser);
ok(!exmsg, "exception: " + exmsg);
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
});
// Tests that a click-to-play plugin retains its activated state upon reloading
add_task(function* () {
clearAllPluginPermissions();
updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
// Work around for delayed PluginBindingAttached
yield promiseUpdatePluginBindings(gTestBrowser);
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 1, Should have a click-to-play notification");
let pluginInfo = yield promiseForPluginInfo("test");
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 2, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
// run the plugin
yield promisePlayObject("test");
yield promiseUpdatePluginBindings(gTestBrowser);
pluginInfo = yield promiseForPluginInfo("test");
is(pluginInfo.displayedType, Ci.nsIObjectLoadingContent.TYPE_PLUGIN, "Test 3, plugin should have started");
ok(pluginInfo.activated, "Test 4, plugin node should not be activated");
let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
let plugin = content.document.getElementById("test");
let npobj1 = Components.utils.waiveXrays(plugin).getObjectValue();
plugin.src = plugin.src;
let pluginsDiffer = false;
try {
Components.utils.waiveXrays(plugin).checkObjectValue(npobj1);
} catch (e) {
pluginsDiffer = true;
}
return pluginsDiffer;
});
ok(result, "Test 5, plugins differ.");
pluginInfo = yield promiseForPluginInfo("test");
ok(pluginInfo.activated, "Test 6, Plugin should have retained activated state.");
is(pluginInfo.displayedType, Ci.nsIObjectLoadingContent.TYPE_PLUGIN, "Test 7, plugin should have started");
});

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,7 +1,3 @@
/* 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 rootDir = getRootDirectory(gTestPath); var rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir;

Просмотреть файл

@ -1,7 +1,3 @@
/* 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 rootDir = getRootDirectory(gTestPath); var rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir;

Просмотреть файл

@ -1,7 +1,3 @@
/* 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 rootDir = getRootDirectory(gTestPath); var rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir; const gTestRoot = rootDir;

Просмотреть файл

@ -1,178 +1,137 @@
var rootDir = getRootDirectory(gTestPath); var rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://mochi.test:8888/"); const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://mochi.test:8888/");
let gTestBrowser = null;
let gNextTest = null;
let gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost); let gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
Components.utils.import("resource://gre/modules/Services.jsm"); let gTestBrowser = null;
// Let's do the XPCNativeWrapper dance! add_task(function* () {
function addPlugin(browser, type) { registerCleanupFunction(Task.async(function*() {
let contentWindow = XPCNativeWrapper.unwrap(browser.contentWindow);
contentWindow.addPlugin(type);
}
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions(); clearAllPluginPermissions();
Services.prefs.clearUserPref("plugins.click_to_play"); Services.prefs.clearUserPref("plugins.click_to_play");
}); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
Services.prefs.setBoolPref("plugins.click_to_play", true); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in"); resetBlocklist();
gBrowser.removeCurrentTab();
let newTab = gBrowser.addTab(); window.focus();
gBrowser.selectedTab = newTab; gTestBrowser = null;
gTestBrowser = gBrowser.selectedBrowser; }));
gTestBrowser.addEventListener("load", pageLoad, true); });
prepareTest(testActivateAddSameTypePart1, gTestRoot + "plugin_add_dynamically.html");
}
function finishTest() {
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() {
// The plugin events are async dispatched and can come after the load event
// This just allows the events to fire before we then go on to test the states
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) {
gNextTest = nextTest;
gTestBrowser.contentWindow.location = url;
}
// "Activate" of a given type -> plugins of that type dynamically added should // "Activate" of a given type -> plugins of that type dynamically added should
// automatically play. // automatically play.
function testActivateAddSameTypePart1() { add_task(function* () {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); let newTab = gBrowser.addTab();
ok(!popupNotification, "testActivateAddSameTypePart1: should not have a click-to-play notification"); gBrowser.selectedTab = newTab;
addPlugin(gTestBrowser); gTestBrowser = gBrowser.selectedBrowser;
let condition = function() PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
waitForCondition(condition, testActivateAddSameTypePart2, "testActivateAddSameTypePart1: waited too long for click-to-play-plugin notification");
}
function testActivateAddSameTypePart2() { Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); Services.prefs.setBoolPref("plugins.click_to_play", true);
ok(popupNotification, "testActivateAddSameTypePart2: should have a click-to-play notification");
popupNotification.reshow(); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
testActivateAddSameTypePart3(); setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
} });
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_add_dynamically.html");
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(!notification, "Test 1a, Should not have a click-to-play notification");
// Add a plugin of type test
yield ContentTask.spawn(gTestBrowser, {}, function* () {
new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin("pluginone", "application/x-test"));
});
yield promisePopupNotification("click-to-play-plugins");
notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 1a, Should not have a click-to-play notification");
yield promiseForNotificationShown(notification);
function testActivateAddSameTypePart3() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
let centerAction = null; let centerAction = null;
for (let action of popupNotification.options.pluginData.values()) { for (let action of notification.options.pluginData.values()) {
if (action.pluginName == "Test") { if (action.pluginName == "Test") {
centerAction = action; centerAction = action;
break; break;
} }
} }
ok(centerAction, "testActivateAddSameTypePart3: found center action for the Test plugin"); ok(centerAction, "Test 2, found center action for the Test plugin");
let centerItem = null; let centerItem = null;
for (let item of PopupNotifications.panel.firstChild.childNodes) { for (let item of PopupNotifications.panel.firstChild.childNodes) {
if (item.action && item.action == centerAction) { is(item.value, "block", "Test 3, all plugins should start out blocked");
if (item.action == centerAction) {
centerItem = item; centerItem = item;
break; break;
} }
} }
ok(centerItem, "testActivateAddSameTypePart3: found center item for the Test plugin"); ok(centerItem, "Test 4, found center item for the Test plugin");
let plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[0]; // Select the allow now option in the select drop down for Test Plugin
ok(!plugin.activated, "testActivateAddSameTypePart3: plugin should not be activated");
// Change the state and click the ok button to activate the Test plugin
centerItem.value = "allownow"; centerItem.value = "allownow";
PopupNotifications.panel.firstChild._primaryButton.click();
let condition = function() plugin.activated;
waitForCondition(condition, testActivateAddSameTypePart4, "testActivateAddSameTypePart3: waited too long for plugin to activate");
}
function testActivateAddSameTypePart4() {
let plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[0];
ok(plugin.activated, "testActivateAddSameTypePart4: plugin should be activated");
addPlugin(gTestBrowser);
let condition = function() {
let embeds = gTestBrowser.contentDocument.getElementsByTagName("embed");
let allActivated = true;
for (let embed of embeds) {
if (!embed.activated)
allActivated = false;
}
return allActivated && embeds.length == 2;
};
waitForCondition(condition, testActivateAddSameTypePart5, "testActivateAddSameTypePart4: waited too long for second plugin to activate"); }
function testActivateAddSameTypePart5() {
let embeds = gTestBrowser.contentDocument.getElementsByTagName("embed");
for (let embed of embeds) {
ok(embed.activated, "testActivateAddSameTypePart5: all plugins should be activated");
}
clearAllPluginPermissions();
prepareTest(testActivateAddDifferentTypePart1, gTestRoot + "plugin_add_dynamically.html");
}
// "Activate" of a given type -> plugins of other types dynamically added
// should not automatically play.
function testActivateAddDifferentTypePart1() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(!popupNotification, "testActivateAddDifferentTypePart1: should not have a click-to-play notification");
addPlugin(gTestBrowser);
let condition = function() PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
waitForCondition(condition, testActivateAddDifferentTypePart2, "testActivateAddDifferentTypePart1: waited too long for click-to-play-plugin notification");
}
function testActivateAddDifferentTypePart2() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "testActivateAddDifferentTypePart2: should have a click-to-play notification");
// we have to actually show the panel to get the bindings to instantiate
popupNotification.reshow();
testActivateAddDifferentTypePart3();
}
function testActivateAddDifferentTypePart3() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
is(popupNotification.options.pluginData.size, 1, "Should be one plugin action");
let plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[0];
ok(!plugin.activated, "testActivateAddDifferentTypePart3: plugin should not be activated");
// "click" the button to activate the Test plugin // "click" the button to activate the Test plugin
PopupNotifications.panel.firstChild._primaryButton.click(); PopupNotifications.panel.firstChild._primaryButton.click();
let condition = function() plugin.activated; let pluginInfo = yield promiseForPluginInfo("pluginone");
waitForCondition(condition, testActivateAddDifferentTypePart4, "testActivateAddDifferentTypePart3: waited too long for plugin to activate"); ok(pluginInfo.activated, "Test 5, plugin should be activated");
}
function testActivateAddDifferentTypePart4() { // Add another plugin of type test
let plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[0]; yield ContentTask.spawn(gTestBrowser, {}, function* () {
ok(plugin.activated, "testActivateAddDifferentTypePart4: plugin should be activated"); new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin("plugintwo", "application/x-test"));
});
addPlugin(gTestBrowser); pluginInfo = yield promiseForPluginInfo("plugintwo");
let condition = function() PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); ok(pluginInfo.activated, "Test 6, plugins should be activated");
waitForCondition(condition, testActivateAddDifferentTypePart5, "testActivateAddDifferentTypePart5: waited too long for popup notification"); });
}
function testActivateAddDifferentTypePart5() { // "Activate" of a given type -> plugins of other types dynamically added
ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser), "testActivateAddDifferentTypePart5: should have popup notification"); // should not automatically play.
let embeds = gTestBrowser.contentDocument.getElementsByTagName("embed"); add_task(function* () {
for (let embed of embeds) { clearAllPluginPermissions();
if (embed.type == "application/x-test")
ok(embed.activated, "testActivateAddDifferentTypePart5: Test plugin should be activated");
else if (embed.type == "application/x-second-test")
ok(!embed.activated, "testActivateAddDifferentTypePart5: Second Test plugin should not be activated");
}
finishTest(); yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_add_dynamically.html");
}
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(!notification, "Test 7, Should not have a click-to-play notification");
// Add a plugin of type test
yield ContentTask.spawn(gTestBrowser, {}, function* () {
new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin("pluginone", "application/x-test"));
});
yield promisePopupNotification("click-to-play-plugins");
notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 8, Should not have a click-to-play notification");
yield promiseForNotificationShown(notification);
is(notification.options.pluginData.size, 1, "Should be one plugin action");
let pluginInfo = yield promiseForPluginInfo("pluginone");
ok(!pluginInfo.activated, "Test 8, test plugin should be activated");
let condition = function() !notification.dismissed &&
PopupNotifications.panel.firstChild;
yield promiseForCondition(condition);
// "click" the button to activate the Test plugin
PopupNotifications.panel.firstChild._primaryButton.click();
pluginInfo = yield promiseForPluginInfo("pluginone");
ok(pluginInfo.activated, "Test 9, test plugin should be activated");
yield ContentTask.spawn(gTestBrowser, {}, function* () {
new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin("plugintwo", "application/x-second-test"));
});
yield promisePopupNotification("click-to-play-plugins");
pluginInfo = yield promiseForPluginInfo("pluginone");
ok(pluginInfo.activated, "Test 10, plugins should be activated");
pluginInfo = yield promiseForPluginInfo("plugintwo");
ok(!pluginInfo.activated, "Test 11, plugins should be activated");
});

Просмотреть файл

@ -7,33 +7,122 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm"); "resource://gre/modules/PlacesUtils.jsm");
function whenDelayedStartupFinished(aWindow, aCallback) { // The blocklist shim running in the content process does not initialize at
Services.obs.addObserver(function observer(aSubject, aTopic) { // start up, so it's not active until we load content that needs to do a
if (aWindow == aSubject) { // check. This helper bypasses the delay to get the svc up and running
Services.obs.removeObserver(observer, aTopic); // immediately. Note, call this after remote content has loaded.
executeSoon(aCallback); function promiseInitContentBlocklistSvc(aBrowser)
{
return ContentTask.spawn(aBrowser, {}, function* () {
try {
let bls = Cc["@mozilla.org/extensions/blocklist;1"]
.getService(Ci.nsIBlocklistService);
} catch (ex) {
return ex.message;
} }
}, "browser-delayed-startup-finished", false); return null;
});
} }
function findChromeWindowByURI(aURI) { /**
let windows = Services.wm.getEnumerator(null); * Waits a specified number of miliseconds.
while (windows.hasMoreElements()) { *
let win = windows.getNext(); * Usage:
if (win.location.href == aURI) * let wait = yield waitForMs(2000);
return win; * 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) {
let deferred = Promise.defer();
let startTime = Date.now();
setTimeout(done, aMs);
function done() {
deferred.resolve(true);
} }
return null; return deferred.promise;
} }
function waitForCondition(condition, nextTest, errorMsg) {
var tries = 0; // DOM Promise fails for unknown reasons here, so we're using
var interval = setInterval(function() { // resource://gre/modules/Promise.jsm.
if (tries >= 30) { function waitForEvent(subject, eventName, checkFn, useCapture, useUntrusted) {
return new Promise((resolve, reject) => {
subject.addEventListener(eventName, function listener(event) {
try {
if (checkFn && !checkFn(event)) {
return;
}
subject.removeEventListener(eventName, listener, useCapture);
resolve(event);
} catch (ex) {
try {
subject.removeEventListener(eventName, listener, useCapture);
} catch (ex2) {
// Maybe the provided object does not support removeEventListener.
}
reject(ex);
}
}, useCapture, useUntrusted);
});
}
/**
* Waits for a load (or custom) event to finish in a given tab. If provided
* load an uri into the tab.
*
* @param tab
* The tab to load into.
* @param [optional] url
* The url to load, or the current url.
* @param [optional] event
* The load event type to wait for. Defaults to "load".
* @return {Promise} resolved when the event is handled.
* @resolves to the received event
* @rejects if a valid load event is not received within a meaningful interval
*/
function promiseTabLoadEvent(tab, url, eventType="load") {
let deferred = Promise.defer();
info("Wait tab event: " + eventType);
function handle(event) {
if (event.originalTarget != tab.linkedBrowser.contentDocument ||
event.target.location.href == "about:blank" ||
(url && event.target.location.href != url)) {
info("Skipping spurious '" + eventType + "'' event" +
" for " + event.target.location.href);
return;
}
clearTimeout(timeout);
tab.linkedBrowser.removeEventListener(eventType, handle, true);
info("Tab event received: " + eventType);
deferred.resolve(event);
}
let timeout = setTimeout(() => {
tab.linkedBrowser.removeEventListener(eventType, handle, true);
deferred.reject(new Error("Timed out while waiting for a '" + eventType + "'' event"));
}, 30000);
tab.linkedBrowser.addEventListener(eventType, handle, true, true);
if (url) {
tab.linkedBrowser.loadURI(url);
}
return deferred.promise;
}
function waitForCondition(condition, nextTest, errorMsg, aTries, aWait) {
let tries = 0;
let maxTries = aTries || 100; // 100 tries
let maxWait = aWait || 100; // 100 msec x 100 tries = ten seconds
let interval = setInterval(function() {
if (tries >= maxTries) {
ok(false, errorMsg); ok(false, errorMsg);
moveOn(); moveOn();
} }
var conditionPassed; let conditionPassed;
try { try {
conditionPassed = condition(); conditionPassed = condition();
} catch (e) { } catch (e) {
@ -44,17 +133,27 @@ function waitForCondition(condition, nextTest, errorMsg) {
moveOn(); moveOn();
} }
tries++; tries++;
}, 100); }, maxWait);
var moveOn = function() { clearInterval(interval); nextTest(); }; let moveOn = function() { clearInterval(interval); nextTest(); };
} }
// Waits for a conditional function defined by the caller to return true.
function promiseForCondition(aConditionFn, aMessage, aTries, aWait) {
let deferred = Promise.defer();
waitForCondition(aConditionFn, deferred.resolve,
(aMessage || "Condition didn't pass."),
aTries, aWait);
return deferred.promise;
}
// Returns the chrome side nsIPluginTag for this plugin
function getTestPlugin(aName) { function getTestPlugin(aName) {
var pluginName = aName || "Test Plug-in"; let pluginName = aName || "Test Plug-in";
var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
var tags = ph.getPluginTags(); let tags = ph.getPluginTags();
// Find the test plugin // Find the test plugin
for (var i = 0; i < tags.length; i++) { for (let i = 0; i < tags.length; i++) {
if (tags[i].name == pluginName) if (tags[i].name == pluginName)
return tags[i]; return tags[i];
} }
@ -62,15 +161,73 @@ function getTestPlugin(aName) {
return null; return null;
} }
// call this to set the test plugin(s) initially expected enabled state. // Set the 'enabledState' on the nsIPluginTag stored in the main or chrome
// it will automatically be reset to it's previous value after the test // process.
// ends
function setTestPluginEnabledState(newEnabledState, pluginName) { function setTestPluginEnabledState(newEnabledState, pluginName) {
var plugin = getTestPlugin(pluginName); let name = pluginName || "Test Plug-in";
var oldEnabledState = plugin.enabledState; let plugin = getTestPlugin(name);
plugin.enabledState = newEnabledState; plugin.enabledState = newEnabledState;
SimpleTest.registerCleanupFunction(function() { }
getTestPlugin(pluginName).enabledState = oldEnabledState;
// Get the 'enabledState' on the nsIPluginTag stored in the main or chrome
// process.
function getTestPluginEnabledState(pluginName) {
let name = pluginName || "Test Plug-in";
let plugin = getTestPlugin(name);
return plugin.enabledState;
}
// Returns a promise for nsIObjectLoadingContent props data.
function promiseForPluginInfo(aId, aBrowser) {
let browser = aBrowser || gTestBrowser;
return ContentTask.spawn(browser, aId, function* (aId) {
let plugin = content.document.getElementById(aId);
if (!(plugin instanceof Ci.nsIObjectLoadingContent))
throw new Error("no plugin found");
return {
pluginFallbackType: plugin.pluginFallbackType,
activated: plugin.activated,
hasRunningPlugin: plugin.hasRunningPlugin,
displayedType: plugin.displayedType,
};
});
}
// Return a promise and call the plugin's nsIObjectLoadingContent
// playPlugin() method.
function promisePlayObject(aId, aBrowser) {
let browser = aBrowser || gTestBrowser;
return ContentTask.spawn(browser, aId, function* (aId) {
let plugin = content.document.getElementById(aId);
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
objLoadingContent.playPlugin();
});
}
function promiseCrashObject(aId, aBrowser) {
let browser = aBrowser || gTestBrowser;
return ContentTask.spawn(browser, aId, function* (aId) {
let plugin = content.document.getElementById(aId);
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
Components.utils.waiveXrays(plugin).crash();
});
}
// Return a promise and call the plugin's getObjectValue() method.
function promiseObjectValueResult(aId, aBrowser) {
let browser = aBrowser || gTestBrowser;
return ContentTask.spawn(browser, aId, function* (aId) {
let plugin = content.document.getElementById(aId);
return Components.utils.waiveXrays(plugin).getObjectValue();
});
}
// Return a promise and reload the target plugin in the page
function promiseReloadPlugin(aId, aBrowser) {
let browser = aBrowser || gTestBrowser;
return ContentTask.spawn(browser, aId, function* (aId) {
let plugin = content.document.getElementById(aId);
plugin.src = plugin.src;
}); });
} }
@ -81,15 +238,16 @@ function clearAllPluginPermissions() {
while (perms.hasMoreElements()) { while (perms.hasMoreElements()) {
let perm = perms.getNext(); let perm = perms.getNext();
if (perm.type.startsWith('plugin')) { if (perm.type.startsWith('plugin')) {
info("removing permission:" + perm.host + " " + perm.type + "\n");
Services.perms.remove(perm.host, perm.type); Services.perms.remove(perm.host, perm.type);
} }
} }
} }
function updateBlocklist(aCallback) { function updateBlocklist(aCallback) {
var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"] let blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"]
.getService(Ci.nsITimerCallback); .getService(Ci.nsITimerCallback);
var observer = function() { let observer = function() {
Services.obs.removeObserver(observer, "blocklist-updated"); Services.obs.removeObserver(observer, "blocklist-updated");
SimpleTest.executeSoon(aCallback); SimpleTest.executeSoon(aCallback);
}; };
@ -97,28 +255,77 @@ function updateBlocklist(aCallback) {
blocklistNotifier.notify(null); blocklistNotifier.notify(null);
} }
var _originalTestBlocklistURL = null; let _originalTestBlocklistURL = null;
function setAndUpdateBlocklist(aURL, aCallback) { function setAndUpdateBlocklist(aURL, aCallback) {
if (!_originalTestBlocklistURL) if (!_originalTestBlocklistURL) {
_originalTestBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url"); _originalTestBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url");
}
Services.prefs.setCharPref("extensions.blocklist.url", aURL); Services.prefs.setCharPref("extensions.blocklist.url", aURL);
updateBlocklist(aCallback); updateBlocklist(aCallback);
} }
// A generator that insures a new blocklist is loaded (in both
// processes if applicable).
function* asyncSetAndUpdateBlocklist(aURL, aBrowser) {
info("*** loading new blocklist: " + aURL);
let doTestRemote = aBrowser ? aBrowser.isRemoteBrowser : false;
if (!_originalTestBlocklistURL) {
_originalTestBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url");
}
Services.prefs.setCharPref("extensions.blocklist.url", aURL);
let localPromise = TestUtils.topicObserved("blocklist-updated");
let remotePromise;
if (doTestRemote) {
remotePromise = TestUtils.topicObserved("content-blocklist-updated");
}
let blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"]
.getService(Ci.nsITimerCallback);
blocklistNotifier.notify(null);
info("*** waiting on local load");
yield localPromise;
if (doTestRemote) {
info("*** waiting on remote load");
yield remotePromise;
}
info("*** blocklist loaded.");
}
// Reset back to the blocklist we had at the start of the test run.
function resetBlocklist() { function resetBlocklist() {
Services.prefs.setCharPref("extensions.blocklist.url", _originalTestBlocklistURL); Services.prefs.setCharPref("extensions.blocklist.url", _originalTestBlocklistURL);
} }
function waitForNotificationPopup(notificationID, browser, callback) { // Insure there's a popup notification present. This test does not indicate
let notification; // open state. aBrowser can be undefined.
waitForCondition( function promisePopupNotification(aName, aBrowser) {
() => (notification = PopupNotifications.getNotification(notificationID, browser)), let deferred = Promise.defer();
() => {
ok(notification, `Successfully got the ${notificationID} notification popup`); waitForCondition(() => PopupNotifications.getNotification(aName, aBrowser),
callback(notification); () => {
}, ok(!!PopupNotifications.getNotification(aName, aBrowser),
`Waited too long for the ${notificationID} notification popup` aName + " notification appeared");
);
deferred.resolve();
}, "timeout waiting for popup notification " + aName);
return deferred.promise;
}
/**
* Allows setting focus on a window, and waiting for that window to achieve
* focus.
*
* @param aWindow
* The window to focus and wait for.
*
* @return {Promise}
* @resolves When the window is focused.
* @rejects Never.
*/
function promiseWaitForFocus(aWindow) {
return new Promise((resolve) => {
waitForFocus(resolve, aWindow);
});
} }
/** /**
@ -153,6 +360,12 @@ function waitForNotificationBar(notificationID, browser, callback) {
}); });
} }
function promiseForNotificationBar(notificationID, browser) {
let deferred = Promise.defer();
waitForNotificationBar(notificationID, browser, deferred.resolve);
return deferred.promise;
}
/** /**
* Reshow a notification and call a callback when it is reshown. * Reshow a notification and call a callback when it is reshown.
* @param notification * @param notification
@ -160,8 +373,7 @@ function waitForNotificationBar(notificationID, browser, callback) {
* @param callback * @param callback
* A function to be called when the notification has been reshown * A function to be called when the notification has been reshown
*/ */
function waitForNotificationShown(notification, callback) function waitForNotificationShown(notification, callback) {
{
if (PopupNotifications.panel.state == "open") { if (PopupNotifications.panel.state == "open") {
executeSoon(callback); executeSoon(callback);
return; return;
@ -173,46 +385,30 @@ function waitForNotificationShown(notification, callback)
notification.reshow(); notification.reshow();
} }
/** function promiseForNotificationShown(notification) {
* Due to layout being async, "PluginBindAttached" may trigger later. let deferred = Promise.defer();
* This returns a Promise that resolves once we've forced a layout waitForNotificationShown(notification, deferred.resolve);
* flush, which triggers the PluginBindAttached event to fire. return deferred.promise;
*
* @param browser
* The browser to force plugin bindings in.
*
* @return Promise
*/
function forcePluginBindingAttached(browser) {
return new Promise((resolve, reject) => {
let doc = browser.contentDocument;
let elems = doc.getElementsByTagName('embed');
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(resolve);
});
} }
/** /**
* Loads a page in a browser, and returns a Promise that * Due to layout being async, "PluginBindAttached" may trigger later. This
* resolves once the "load" event has been fired for that * returns a Promise that resolves once we've forced a layout flush, which
* browser. * triggers the PluginBindAttached event to fire. This trick only works if
* * there is some sort of plugin in the page.
* @param browser * @param browser
* The browser to load the page in. * The browser to force plugin bindings in.
* @param uri
* The URI to load.
*
* @return Promise * @return Promise
*/ */
function loadPage(browser, uri) { function promiseUpdatePluginBindings(browser) {
return new Promise((resolve, reject) => { return ContentTask.spawn(browser, {}, function* () {
browser.addEventListener("load", function onLoad(event) { let doc = content.document;
browser.removeEventListener("load", onLoad, true); let elems = doc.getElementsByTagName('embed');
resolve(); if (!elems || elems.length < 1) {
}, true); elems = doc.getElementsByTagName('object');
browser.loadURI(uri); }
if (elems && elems.length > 0) {
elems[0].clientTop;
}
}); });
} }

Просмотреть файл

@ -1,15 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>
<body> <body>
<script> <script>
function addPlugin(type="application/x-test") { function addPlugin(aId, aType="application/x-test") {
var embed = document.createElement("embed"); var embed = document.createElement("embed");
embed.setAttribute("id", aId);
embed.style.width = "200px"; embed.style.width = "200px";
embed.style.height = "200px"; embed.style.height = "200px";
embed.setAttribute("type", type); embed.setAttribute("type", aType);
return document.body.appendChild(embed); return document.body.appendChild(embed);
} }
</script> </script>

Просмотреть файл

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>
<body> <body>
<embed id="unknown" style="width: 100px; height: 100px" type="application/x-unknown"> <embed id="unknown" style="width: 100px; height: 100px" type="application/x-unknown">

Просмотреть файл

@ -1,11 +1,11 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>
<body> <body>
<div id="container" style="display: none"> <div id="container" style="display: none">
<object id="plugin" type="application/x-test" style="width: 200px; height: 200px;"></object> <object id="test" type="application/x-test" style="width: 200px; height: 200px;"></object>
</div> </div>
</body> </body>
</html> </html>

Просмотреть файл

@ -1,4 +1,4 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
@ -6,6 +6,7 @@
<script type="text/javascript"> <script type="text/javascript">
// create an embed, insert it in the doc and immediately remove it // create an embed, insert it in the doc and immediately remove it
var embed = document.createElement('embed'); var embed = document.createElement('embed');
embed.setAttribute("id", "test");
embed.setAttribute("type", "application/x-test"); embed.setAttribute("type", "application/x-test");
embed.setAttribute("style", "width: 0px; height: 0px;"); embed.setAttribute("style", "width: 0px; height: 0px;");
document.body.appendChild(embed); document.body.appendChild(embed);

Просмотреть файл

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>
<body> <body>
<embed id="test" style="width: 200px; height: 200px" type="application/x-test"> <embed id="test" style="width: 200px; height: 200px" type="application/x-test">

Просмотреть файл

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>
<body> <body>
<embed id="test" style="width: 0px; height: 0px" type="application/x-test"> <embed id="test" style="width: 0px; height: 0px" type="application/x-test">

Просмотреть файл

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>
<body> <body>
<embed id="unknown" style="width: 100px; height: 100px" type="application/x-unknown"> <embed id="unknown" style="width: 100px; height: 100px" type="application/x-unknown">

Просмотреть файл

@ -428,6 +428,7 @@
@RESPATH@/components/amInstallTrigger.js @RESPATH@/components/amInstallTrigger.js
@RESPATH@/components/amWebInstallListener.js @RESPATH@/components/amWebInstallListener.js
@RESPATH@/components/nsBlocklistService.js @RESPATH@/components/nsBlocklistService.js
@RESPATH@/components/nsBlocklistServiceContent.js
#ifdef MOZ_UPDATER #ifdef MOZ_UPDATER
@RESPATH@/components/nsUpdateService.manifest @RESPATH@/components/nsUpdateService.manifest
@RESPATH@/components/nsUpdateService.js @RESPATH@/components/nsUpdateService.js

Просмотреть файл

@ -3258,8 +3258,9 @@ nsObjectLoadingContent::ShouldPlay(FallbackType &aReason, bool aIgnoreCurrentTyp
} }
// Before we check permissions, get the blocklist state of this plugin to set // Before we check permissions, get the blocklist state of this plugin to set
// the fallback reason correctly. // the fallback reason correctly. In the content process this will involve
uint32_t blocklistState = nsIBlocklistService::STATE_NOT_BLOCKED; // an ipc call to chrome.
uint32_t blocklistState = nsIBlocklistService::STATE_BLOCKED;
pluginHost->GetBlocklistStateForType(mContentType, pluginHost->GetBlocklistStateForType(mContentType,
nsPluginHost::eExcludeNone, nsPluginHost::eExcludeNone,
&blocklistState); &blocklistState);

86
dom/cache/Cache.cpp поставляемый
Просмотреть файл

@ -229,7 +229,10 @@ already_AddRefed<Promise>
Cache::Match(const RequestOrUSVString& aRequest, Cache::Match(const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv) const CacheQueryOptions& aOptions, ErrorResult& aRv)
{ {
MOZ_ASSERT(mActor); if (NS_WARN_IF(!mActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv); nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
@ -253,7 +256,10 @@ already_AddRefed<Promise>
Cache::MatchAll(const Optional<RequestOrUSVString>& aRequest, Cache::MatchAll(const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv) const CacheQueryOptions& aOptions, ErrorResult& aRv)
{ {
MOZ_ASSERT(mActor); if (NS_WARN_IF(!mActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
CacheQueryParams params; CacheQueryParams params;
ToCacheQueryParams(params, aOptions); ToCacheQueryParams(params, aOptions);
@ -280,6 +286,11 @@ already_AddRefed<Promise>
Cache::Add(JSContext* aContext, const RequestOrUSVString& aRequest, Cache::Add(JSContext* aContext, const RequestOrUSVString& aRequest,
ErrorResult& aRv) ErrorResult& aRv)
{ {
if (NS_WARN_IF(!mActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if (!IsValidPutRequestMethod(aRequest, aRv)) { if (!IsValidPutRequestMethod(aRequest, aRv)) {
return nullptr; return nullptr;
} }
@ -290,13 +301,13 @@ Cache::Add(JSContext* aContext, const RequestOrUSVString& aRequest,
nsTArray<nsRefPtr<Request>> requestList(1); nsTArray<nsRefPtr<Request>> requestList(1);
nsRefPtr<Request> request = Request::Constructor(global, aRequest, nsRefPtr<Request> request = Request::Constructor(global, aRequest,
RequestInit(), aRv); RequestInit(), aRv);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
nsAutoString url; nsAutoString url;
request->GetUrl(url); request->GetUrl(url);
if (!IsValidPutRequestURL(url, aRv)) { if (NS_WARN_IF(!IsValidPutRequestURL(url, aRv))) {
return nullptr; return nullptr;
} }
@ -309,6 +320,11 @@ Cache::AddAll(JSContext* aContext,
const Sequence<OwningRequestOrUSVString>& aRequestList, const Sequence<OwningRequestOrUSVString>& aRequestList,
ErrorResult& aRv) ErrorResult& aRv)
{ {
if (NS_WARN_IF(!mActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
GlobalObject global(aContext, mGlobal->GetGlobalJSObject()); GlobalObject global(aContext, mGlobal->GetGlobalJSObject());
MOZ_ASSERT(!global.Failed()); MOZ_ASSERT(!global.Failed());
@ -318,7 +334,8 @@ Cache::AddAll(JSContext* aContext,
if (aRequestList[i].IsRequest()) { if (aRequestList[i].IsRequest()) {
requestOrString.SetAsRequest() = aRequestList[i].GetAsRequest(); requestOrString.SetAsRequest() = aRequestList[i].GetAsRequest();
if (!IsValidPutRequestMethod(requestOrString.GetAsRequest(), aRv)) { if (NS_WARN_IF(!IsValidPutRequestMethod(requestOrString.GetAsRequest(),
aRv))) {
return nullptr; return nullptr;
} }
} else { } else {
@ -329,13 +346,13 @@ Cache::AddAll(JSContext* aContext,
nsRefPtr<Request> request = Request::Constructor(global, requestOrString, nsRefPtr<Request> request = Request::Constructor(global, requestOrString,
RequestInit(), aRv); RequestInit(), aRv);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
nsAutoString url; nsAutoString url;
request->GetUrl(url); request->GetUrl(url);
if (!IsValidPutRequestURL(url, aRv)) { if (NS_WARN_IF(!IsValidPutRequestURL(url, aRv))) {
return nullptr; return nullptr;
} }
@ -349,14 +366,17 @@ already_AddRefed<Promise>
Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse, Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
ErrorResult& aRv) ErrorResult& aRv)
{ {
MOZ_ASSERT(mActor); if (NS_WARN_IF(!mActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if (!IsValidPutRequestMethod(aRequest, aRv)) { if (NS_WARN_IF(!IsValidPutRequestMethod(aRequest, aRv))) {
return nullptr; return nullptr;
} }
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, ReadBody, aRv); nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, ReadBody, aRv);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
@ -364,7 +384,7 @@ Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
args.Add(ir, ReadBody, TypeErrorOnInvalidScheme, args.Add(ir, ReadBody, TypeErrorOnInvalidScheme,
aResponse, aRv); aResponse, aRv);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
@ -375,10 +395,13 @@ already_AddRefed<Promise>
Cache::Delete(const RequestOrUSVString& aRequest, Cache::Delete(const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv) const CacheQueryOptions& aOptions, ErrorResult& aRv)
{ {
MOZ_ASSERT(mActor); if (NS_WARN_IF(!mActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv); nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
@ -388,7 +411,7 @@ Cache::Delete(const RequestOrUSVString& aRequest,
AutoChildOpArgs args(this, CacheDeleteArgs(CacheRequest(), params)); AutoChildOpArgs args(this, CacheDeleteArgs(CacheRequest(), params));
args.Add(ir, IgnoreBody, IgnoreInvalidScheme, aRv); args.Add(ir, IgnoreBody, IgnoreInvalidScheme, aRv);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
@ -399,7 +422,10 @@ already_AddRefed<Promise>
Cache::Keys(const Optional<RequestOrUSVString>& aRequest, Cache::Keys(const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv) const CacheQueryOptions& aOptions, ErrorResult& aRv)
{ {
MOZ_ASSERT(mActor); if (NS_WARN_IF(!mActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
CacheQueryParams params; CacheQueryParams params;
ToCacheQueryParams(params, aOptions); ToCacheQueryParams(params, aOptions);
@ -409,12 +435,12 @@ Cache::Keys(const Optional<RequestOrUSVString>& aRequest,
if (aRequest.WasPassed()) { if (aRequest.WasPassed()) {
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(), nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
IgnoreBody, aRv); IgnoreBody, aRv);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
args.Add(ir, IgnoreBody, IgnoreInvalidScheme, aRv); args.Add(ir, IgnoreBody, IgnoreInvalidScheme, aRv);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
} }
@ -493,9 +519,9 @@ Cache::~Cache()
{ {
NS_ASSERT_OWNINGTHREAD(Cache); NS_ASSERT_OWNINGTHREAD(Cache);
if (mActor) { if (mActor) {
mActor->StartDestroy(); mActor->StartDestroyFromListener();
// DestroyInternal() is called synchronously by StartDestroy(). So we // DestroyInternal() is called synchronously by StartDestroyFromListener().
// should have already cleared the mActor. // So we should have already cleared the mActor.
MOZ_ASSERT(!mActor); MOZ_ASSERT(!mActor);
} }
} }
@ -504,11 +530,11 @@ already_AddRefed<Promise>
Cache::ExecuteOp(AutoChildOpArgs& aOpArgs, ErrorResult& aRv) Cache::ExecuteOp(AutoChildOpArgs& aOpArgs, ErrorResult& aRv)
{ {
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv); nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) { if (NS_WARN_IF(!promise)) {
return nullptr; return nullptr;
} }
mActor->ExecuteOp(mGlobal, promise, aOpArgs.SendAsOpArgs()); mActor->ExecuteOp(mGlobal, promise, this, aOpArgs.SendAsOpArgs());
return promise.forget(); return promise.forget();
} }
@ -521,7 +547,7 @@ Cache::AddAll(const GlobalObject& aGlobal,
// If there is no work to do, then resolve immediately // If there is no work to do, then resolve immediately
if (aRequestList.IsEmpty()) { if (aRequestList.IsEmpty()) {
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv); nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) { if (NS_WARN_IF(!promise)) {
return nullptr; return nullptr;
} }
@ -541,7 +567,7 @@ Cache::AddAll(const GlobalObject& aGlobal,
requestOrString.SetAsRequest() = aRequestList[i]; requestOrString.SetAsRequest() = aRequestList[i];
nsRefPtr<Promise> fetch = FetchRequest(mGlobal, requestOrString, nsRefPtr<Promise> fetch = FetchRequest(mGlobal, requestOrString,
RequestInit(), aRv); RequestInit(), aRv);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
@ -549,7 +575,7 @@ Cache::AddAll(const GlobalObject& aGlobal,
} }
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv); nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
@ -557,7 +583,7 @@ Cache::AddAll(const GlobalObject& aGlobal,
Move(aRequestList), promise); Move(aRequestList), promise);
nsRefPtr<Promise> fetchPromise = Promise::All(aGlobal, fetchList, aRv); nsRefPtr<Promise> fetchPromise = Promise::All(aGlobal, fetchList, aRv);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
fetchPromise->AppendNativeHandler(handler); fetchPromise->AppendNativeHandler(handler);
@ -570,15 +596,19 @@ Cache::PutAll(const nsTArray<nsRefPtr<Request>>& aRequestList,
const nsTArray<nsRefPtr<Response>>& aResponseList, const nsTArray<nsRefPtr<Response>>& aResponseList,
ErrorResult& aRv) ErrorResult& aRv)
{ {
MOZ_ASSERT(mActor);
MOZ_ASSERT(aRequestList.Length() == aResponseList.Length()); MOZ_ASSERT(aRequestList.Length() == aResponseList.Length());
if (NS_WARN_IF(!mActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
AutoChildOpArgs args(this, CachePutAllArgs()); AutoChildOpArgs args(this, CachePutAllArgs());
for (uint32_t i = 0; i < aRequestList.Length(); ++i) { for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
nsRefPtr<InternalRequest> ir = aRequestList[i]->GetInternalRequest(); nsRefPtr<InternalRequest> ir = aRequestList[i]->GetInternalRequest();
args.Add(ir, ReadBody, TypeErrorOnInvalidScheme, *aResponseList[i], aRv); args.Add(ir, ReadBody, TypeErrorOnInvalidScheme, *aResponseList[i], aRv);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
} }

41
dom/cache/CacheChild.cpp поставляемый
Просмотреть файл

@ -33,6 +33,7 @@ DeallocPCacheChild(PCacheChild* aActor)
CacheChild::CacheChild() CacheChild::CacheChild()
: mListener(nullptr) : mListener(nullptr)
, mNumChildActors(0) , mNumChildActors(0)
, mDelayedDestroy(false)
{ {
MOZ_COUNT_CTOR(cache::CacheChild); MOZ_COUNT_CTOR(cache::CacheChild);
} }
@ -64,11 +65,11 @@ CacheChild::ClearListener()
void void
CacheChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise, CacheChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
const CacheOpArgs& aArgs) nsISupports* aParent, const CacheOpArgs& aArgs)
{ {
mNumChildActors += 1; mNumChildActors += 1;
MOZ_ALWAYS_TRUE(SendPCacheOpConstructor( MOZ_ALWAYS_TRUE(SendPCacheOpConstructor(
new CacheOpChild(GetFeature(), aGlobal, aPromise), aArgs)); new CacheOpChild(GetFeature(), aGlobal, aParent, aPromise), aArgs));
} }
CachePushStreamChild* CachePushStreamChild*
@ -81,9 +82,33 @@ CacheChild::CreatePushStream(nsIAsyncInputStream* aStream)
return static_cast<CachePushStreamChild*>(actor); return static_cast<CachePushStreamChild*>(actor);
} }
void
CacheChild::StartDestroyFromListener()
{
NS_ASSERT_OWNINGTHREAD(CacheChild);
// The listener should be held alive by any async operations, so if it
// is going away then there must not be any child actors. This in turn
// ensures that StartDestroy() will not trigger the delayed path.
MOZ_ASSERT(!mNumChildActors);
StartDestroy();
}
void void
CacheChild::StartDestroy() CacheChild::StartDestroy()
{ {
NS_ASSERT_OWNINGTHREAD(CacheChild);
// If we have outstanding child actors, then don't destroy ourself yet.
// The child actors should be short lived and we should allow them to complete
// if possible. NoteDeletedActor() will call back into this Shutdown()
// method when the last child actor is gone.
if (mNumChildActors) {
mDelayedDestroy = true;
return;
}
nsRefPtr<Cache> listener = mListener; nsRefPtr<Cache> listener = mListener;
// StartDestroy() can get called from either Cache or the Feature. // StartDestroy() can get called from either Cache or the Feature.
@ -98,14 +123,6 @@ CacheChild::StartDestroy()
// Cache listener should call ClearListener() in DestroyInternal() // Cache listener should call ClearListener() in DestroyInternal()
MOZ_ASSERT(!mListener); MOZ_ASSERT(!mListener);
// If we have outstanding child actors, then don't destroy ourself yet.
// The child actors should be short lived and we should allow them to complete
// if possible. SendTeardown() will be called when the count drops to zero
// in NoteDeletedActor().
if (mNumChildActors) {
return;
}
// Start actor destruction from parent process // Start actor destruction from parent process
unused << SendTeardown(); unused << SendTeardown();
} }
@ -158,8 +175,8 @@ void
CacheChild::NoteDeletedActor() CacheChild::NoteDeletedActor()
{ {
mNumChildActors -= 1; mNumChildActors -= 1;
if (!mNumChildActors && !mListener) { if (!mNumChildActors && mDelayedDestroy) {
unused << SendTeardown(); StartDestroy();
} }
} }

21
dom/cache/CacheChild.h поставляемый
Просмотреть файл

@ -33,25 +33,27 @@ public:
void SetListener(Cache* aListener); void SetListener(Cache* aListener);
// Must be called by the associated Cache listener in its ActorDestroy() // Must be called by the associated Cache listener in its DestroyInternal()
// method. Also, Cache must Send__delete__() the actor in its destructor to // method. Also, Cache must call StartDestroyFromListener() on the actor in
// trigger ActorDestroy() if it has not been called yet. // its destructor to trigger ActorDestroy() if it has not been called yet.
void ClearListener(); void ClearListener();
void void
ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise, ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
const CacheOpArgs& aArgs); nsISupports* aParent, const CacheOpArgs& aArgs);
CachePushStreamChild* CachePushStreamChild*
CreatePushStream(nsIAsyncInputStream* aStream); CreatePushStream(nsIAsyncInputStream* aStream);
// ActorChild methods // Our parent Listener object has gone out of scope and is being destroyed.
void StartDestroyFromListener();
// Synchronously call ActorDestroy on our Cache listener and then start the
// actor destruction asynchronously from the parent-side.
virtual void StartDestroy() override;
private: private:
// ActorChild methods
// Feature is trying to destroy due to worker shutdown.
virtual void StartDestroy() override;
// PCacheChild methods // PCacheChild methods
virtual void virtual void
ActorDestroy(ActorDestroyReason aReason) override; ActorDestroy(ActorDestroyReason aReason) override;
@ -77,6 +79,7 @@ private:
// destroyed. // destroyed.
Cache* MOZ_NON_OWNING_REF mListener; Cache* MOZ_NON_OWNING_REF mListener;
uint32_t mNumChildActors; uint32_t mNumChildActors;
bool mDelayedDestroy;
NS_DECL_OWNINGTHREAD NS_DECL_OWNINGTHREAD
}; };

6
dom/cache/CacheOpChild.cpp поставляемый
Просмотреть файл

@ -57,11 +57,13 @@ AddFeatureToStreamChild(const CacheRequest& aRequest, Feature* aFeature)
} // anonymous namespace } // anonymous namespace
CacheOpChild::CacheOpChild(Feature* aFeature, nsIGlobalObject* aGlobal, CacheOpChild::CacheOpChild(Feature* aFeature, nsIGlobalObject* aGlobal,
Promise* aPromise) nsISupports* aParent, Promise* aPromise)
: mGlobal(aGlobal) : mGlobal(aGlobal)
, mParent(aParent)
, mPromise(aPromise) , mPromise(aPromise)
{ {
MOZ_ASSERT(mGlobal); MOZ_ASSERT(mGlobal);
MOZ_ASSERT(mParent);
MOZ_ASSERT(mPromise); MOZ_ASSERT(mPromise);
MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature); MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
@ -95,7 +97,7 @@ CacheOpChild::Recv__delete__(const ErrorResult& aRv,
{ {
NS_ASSERT_OWNINGTHREAD(CacheOpChild); NS_ASSERT_OWNINGTHREAD(CacheOpChild);
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
MOZ_ASSERT(aResult.type() == CacheOpResult::Tvoid_t); MOZ_ASSERT(aResult.type() == CacheOpResult::Tvoid_t);
// TODO: Remove this const_cast (bug 1152078). // TODO: Remove this const_cast (bug 1152078).
// It is safe for now since this ErrorResult is handed off to us by IPDL // It is safe for now since this ErrorResult is handed off to us by IPDL

6
dom/cache/CacheOpChild.h поставляемый
Просмотреть файл

@ -31,7 +31,8 @@ class CacheOpChild final : public PCacheOpChild
private: private:
// This class must be constructed by CacheChild or CacheStorageChild using // This class must be constructed by CacheChild or CacheStorageChild using
// their ExecuteOp() factory method. // their ExecuteOp() factory method.
CacheOpChild(Feature* aFeature, nsIGlobalObject* aGlobal, Promise* aPromise); CacheOpChild(Feature* aFeature, nsIGlobalObject* aGlobal,
nsISupports* aParent, Promise* aPromise);
~CacheOpChild(); ~CacheOpChild();
// PCacheOpChild methods // PCacheOpChild methods
@ -68,6 +69,9 @@ private:
HandleRequestList(const nsTArray<CacheRequest>& aRequestList); HandleRequestList(const nsTArray<CacheRequest>& aRequestList);
nsCOMPtr<nsIGlobalObject> mGlobal; nsCOMPtr<nsIGlobalObject> mGlobal;
// Hold the parent Cache or CacheStorage object alive until this async
// operation completes.
nsCOMPtr<nsISupports> mParent;
nsRefPtr<Promise> mPromise; nsRefPtr<Promise> mPromise;
NS_DECL_OWNINGTHREAD NS_DECL_OWNINGTHREAD

2
dom/cache/CacheOpParent.cpp поставляемый
Просмотреть файл

@ -164,7 +164,7 @@ CacheOpParent::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
// Never send an op-specific result if we have an error. Instead, send // Never send an op-specific result if we have an error. Instead, send
// void_t() to ensure that we don't leak actors on the child side. // void_t() to ensure that we don't leak actors on the child side.
if (aRv.Failed()) { if (NS_WARN_IF(aRv.Failed())) {
unused << Send__delete__(this, aRv, void_t()); unused << Send__delete__(this, aRv, void_t());
aRv.SuppressException(); // We serialiazed it, as best we could. aRv.SuppressException(); // We serialiazed it, as best we could.
return; return;

5
dom/cache/CachePushStreamChild.cpp поставляемый
Просмотреть файл

@ -118,8 +118,9 @@ CachePushStreamChild::Start()
void void
CachePushStreamChild::StartDestroy() CachePushStreamChild::StartDestroy()
{ {
// called if we are running on a Worker and the thread gets shutdown // The worker has signaled its shutting down, but continue streaming. The
OnEnd(NS_ERROR_ABORT); // Cache is now designed to hold the worker open until all async operations
// complete.
} }
void void

32
dom/cache/CacheStorage.cpp поставляемый
Просмотреть файл

@ -166,7 +166,7 @@ CacheStorage::CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal,
// wait for the async ActorCreated() callback. // wait for the async ActorCreated() callback.
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
bool ok = BackgroundChild::GetOrCreateForCurrentThread(this); bool ok = BackgroundChild::GetOrCreateForCurrentThread(this);
if (!ok) { if (NS_WARN_IF(!ok)) {
ActorFailed(); ActorFailed();
} }
} }
@ -177,7 +177,7 @@ CacheStorage::Match(const RequestOrUSVString& aRequest,
{ {
NS_ASSERT_OWNINGTHREAD(CacheStorage); NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mFailedActor) { if (NS_WARN_IF(mFailedActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED); aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr; return nullptr;
} }
@ -189,7 +189,7 @@ CacheStorage::Match(const RequestOrUSVString& aRequest,
} }
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv); nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) { if (NS_WARN_IF(!promise)) {
return nullptr; return nullptr;
} }
@ -212,13 +212,13 @@ CacheStorage::Has(const nsAString& aKey, ErrorResult& aRv)
{ {
NS_ASSERT_OWNINGTHREAD(CacheStorage); NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mFailedActor) { if (NS_WARN_IF(mFailedActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED); aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr; return nullptr;
} }
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv); nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) { if (NS_WARN_IF(!promise)) {
return nullptr; return nullptr;
} }
@ -237,13 +237,13 @@ CacheStorage::Open(const nsAString& aKey, ErrorResult& aRv)
{ {
NS_ASSERT_OWNINGTHREAD(CacheStorage); NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mFailedActor) { if (NS_WARN_IF(mFailedActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED); aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr; return nullptr;
} }
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv); nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) { if (NS_WARN_IF(!promise)) {
return nullptr; return nullptr;
} }
@ -262,13 +262,13 @@ CacheStorage::Delete(const nsAString& aKey, ErrorResult& aRv)
{ {
NS_ASSERT_OWNINGTHREAD(CacheStorage); NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mFailedActor) { if (NS_WARN_IF(mFailedActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED); aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr; return nullptr;
} }
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv); nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) { if (NS_WARN_IF(!promise)) {
return nullptr; return nullptr;
} }
@ -287,13 +287,13 @@ CacheStorage::Keys(ErrorResult& aRv)
{ {
NS_ASSERT_OWNINGTHREAD(CacheStorage); NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mFailedActor) { if (NS_WARN_IF(mFailedActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED); aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr; return nullptr;
} }
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv); nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) { if (NS_WARN_IF(!promise)) {
return nullptr; return nullptr;
} }
@ -413,9 +413,9 @@ CacheStorage::~CacheStorage()
{ {
NS_ASSERT_OWNINGTHREAD(CacheStorage); NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mActor) { if (mActor) {
mActor->StartDestroy(); mActor->StartDestroyFromListener();
// DestroyInternal() is called synchronously by StartDestroy(). So we // DestroyInternal() is called synchronously by StartDestroyFromListener().
// should have already cleared the mActor. // So we should have already cleared the mActor.
MOZ_ASSERT(!mActor); MOZ_ASSERT(!mActor);
} }
} }
@ -434,11 +434,11 @@ CacheStorage::MaybeRunPendingRequests()
if (entry->mRequest) { if (entry->mRequest) {
args.Add(entry->mRequest, IgnoreBody, IgnoreInvalidScheme, rv); args.Add(entry->mRequest, IgnoreBody, IgnoreInvalidScheme, rv);
} }
if (rv.Failed()) { if (NS_WARN_IF(rv.Failed())) {
entry->mPromise->MaybeReject(rv); entry->mPromise->MaybeReject(rv);
continue; continue;
} }
mActor->ExecuteOp(mGlobal, entry->mPromise, args.SendAsOpArgs()); mActor->ExecuteOp(mGlobal, entry->mPromise, this, args.SendAsOpArgs());
} }
mPendingRequests.Clear(); mPendingRequests.Clear();
} }

39
dom/cache/CacheStorageChild.cpp поставляемый
Просмотреть файл

@ -25,6 +25,7 @@ DeallocPCacheStorageChild(PCacheStorageChild* aActor)
CacheStorageChild::CacheStorageChild(CacheStorage* aListener, Feature* aFeature) CacheStorageChild::CacheStorageChild(CacheStorage* aListener, Feature* aFeature)
: mListener(aListener) : mListener(aListener)
, mNumChildActors(0) , mNumChildActors(0)
, mDelayedDestroy(false)
{ {
MOZ_COUNT_CTOR(cache::CacheStorageChild); MOZ_COUNT_CTOR(cache::CacheStorageChild);
MOZ_ASSERT(mListener); MOZ_ASSERT(mListener);
@ -49,11 +50,24 @@ CacheStorageChild::ClearListener()
void void
CacheStorageChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise, CacheStorageChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
const CacheOpArgs& aArgs) nsISupports* aParent, const CacheOpArgs& aArgs)
{ {
mNumChildActors += 1; mNumChildActors += 1;
unused << SendPCacheOpConstructor( unused << SendPCacheOpConstructor(
new CacheOpChild(GetFeature(), aGlobal, aPromise), aArgs); new CacheOpChild(GetFeature(), aGlobal, aParent, aPromise), aArgs);
}
void
CacheStorageChild::StartDestroyFromListener()
{
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
// The listener should be held alive by any async operations, so if it
// is going away then there must not be any child actors. This in turn
// ensures that StartDestroy() will not trigger the delayed path.
MOZ_ASSERT(!mNumChildActors);
StartDestroy();
} }
void void
@ -61,6 +75,15 @@ CacheStorageChild::StartDestroy()
{ {
NS_ASSERT_OWNINGTHREAD(CacheStorageChild); NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
// If we have outstanding child actors, then don't destroy ourself yet.
// The child actors should be short lived and we should allow them to complete
// if possible. NoteDeletedActor() will call back into this Shutdown()
// method when the last child actor is gone.
if (mNumChildActors) {
mDelayedDestroy = true;
return;
}
nsRefPtr<CacheStorage> listener = mListener; nsRefPtr<CacheStorage> listener = mListener;
// StartDestroy() can get called from either CacheStorage or the Feature. // StartDestroy() can get called from either CacheStorage or the Feature.
@ -75,14 +98,6 @@ CacheStorageChild::StartDestroy()
// CacheStorage listener should call ClearListener() in DestroyInternal() // CacheStorage listener should call ClearListener() in DestroyInternal()
MOZ_ASSERT(!mListener); MOZ_ASSERT(!mListener);
// If we have outstanding child actors, then don't destroy ourself yet.
// The child actors should be short lived and we should allow them to complete
// if possible. SendTeardown() will be called when the count drops to zero
// in NoteDeletedActor().
if (mNumChildActors) {
return;
}
// Start actor destruction from parent process // Start actor destruction from parent process
unused << SendTeardown(); unused << SendTeardown();
} }
@ -121,8 +136,8 @@ CacheStorageChild::NoteDeletedActor()
{ {
MOZ_ASSERT(mNumChildActors); MOZ_ASSERT(mNumChildActors);
mNumChildActors -= 1; mNumChildActors -= 1;
if (!mNumChildActors && !mListener) { if (!mNumChildActors && mDelayedDestroy) {
unused << SendTeardown(); StartDestroy();
} }
} }

21
dom/cache/CacheStorageChild.h поставляемый
Просмотреть файл

@ -33,22 +33,24 @@ public:
~CacheStorageChild(); ~CacheStorageChild();
// Must be called by the associated CacheStorage listener in its // Must be called by the associated CacheStorage listener in its
// ActorDestroy() method. Also, CacheStorage must call SendDestroy() on the // DestroyInternal() method. Also, CacheStorage must call
// actor in its destructor to trigger ActorDestroy() if it has not been // SendDestroyFromListener() on the actor in its destructor to trigger
// called yet. // ActorDestroy() if it has not been called yet.
void ClearListener(); void ClearListener();
void void
ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise, ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
const CacheOpArgs& aArgs); nsISupports* aParent, const CacheOpArgs& aArgs);
// ActorChild methods // Our parent Listener object has gone out of scope and is being destroyed.
void StartDestroyFromListener();
// Synchronously call ActorDestroy on our CacheStorage listener and then start
// the actor destruction asynchronously from the parent-side.
virtual void StartDestroy() override;
private: private:
// ActorChild methods
// Feature is trying to destroy due to worker shutdown.
virtual void StartDestroy() override;
// PCacheStorageChild methods // PCacheStorageChild methods
virtual void ActorDestroy(ActorDestroyReason aReason) override; virtual void ActorDestroy(ActorDestroyReason aReason) override;
@ -67,6 +69,7 @@ private:
// destroyed. // destroyed.
CacheStorage* MOZ_NON_OWNING_REF mListener; CacheStorage* MOZ_NON_OWNING_REF mListener;
uint32_t mNumChildActors; uint32_t mNumChildActors;
bool mDelayedDestroy;
NS_DECL_OWNINGTHREAD NS_DECL_OWNINGTHREAD
}; };

2
dom/cache/CacheStorageParent.cpp поставляемый
Просмотреть файл

@ -100,7 +100,7 @@ CacheStorageParent::RecvPCacheOpConstructor(PCacheOpParent* aActor,
return true; return true;
} }
if (NS_FAILED(mVerifiedStatus)) { if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
unused << CacheOpParent::Send__delete__(actor, ErrorResult(mVerifiedStatus), unused << CacheOpParent::Send__delete__(actor, ErrorResult(mVerifiedStatus),
void_t()); void_t());
return true; return true;

4
dom/cache/Feature.cpp поставляемый
Просмотреть файл

@ -13,7 +13,7 @@ namespace mozilla {
namespace dom { namespace dom {
namespace cache { namespace cache {
using mozilla::dom::workers::Running; using mozilla::dom::workers::Canceling;
using mozilla::dom::workers::Status; using mozilla::dom::workers::Status;
using mozilla::dom::workers::WorkerPrivate; using mozilla::dom::workers::WorkerPrivate;
@ -73,7 +73,7 @@ Feature::Notify(JSContext* aCx, Status aStatus)
{ {
NS_ASSERT_OWNINGTHREAD(Feature); NS_ASSERT_OWNINGTHREAD(Feature);
if (aStatus <= Running || mNotified) { if (aStatus < Canceling || mNotified) {
return true; return true;
} }

6
dom/cache/Manager.cpp поставляемый
Просмотреть файл

@ -1623,7 +1623,7 @@ Manager::ExecuteCacheOp(Listener* aListener, CacheId aCacheId,
MOZ_ASSERT(aListener); MOZ_ASSERT(aListener);
MOZ_ASSERT(aOpArgs.type() != CacheOpArgs::TCachePutAllArgs); MOZ_ASSERT(aOpArgs.type() != CacheOpArgs::TCachePutAllArgs);
if (mState == Closing) { if (NS_WARN_IF(mState == Closing)) {
aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t()); aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t());
return; return;
} }
@ -1667,7 +1667,7 @@ Manager::ExecuteStorageOp(Listener* aListener, Namespace aNamespace,
NS_ASSERT_OWNINGTHREAD(Manager); NS_ASSERT_OWNINGTHREAD(Manager);
MOZ_ASSERT(aListener); MOZ_ASSERT(aListener);
if (mState == Closing) { if (NS_WARN_IF(mState == Closing)) {
aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t()); aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t());
return; return;
} }
@ -1716,7 +1716,7 @@ Manager::ExecutePutAll(Listener* aListener, CacheId aCacheId,
NS_ASSERT_OWNINGTHREAD(Manager); NS_ASSERT_OWNINGTHREAD(Manager);
MOZ_ASSERT(aListener); MOZ_ASSERT(aListener);
if (mState == Closing) { if (NS_WARN_IF(mState == Closing)) {
aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), CachePutAllResult()); aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), CachePutAllResult());
return; return;
} }

Просмотреть файл

@ -161,6 +161,9 @@
#include "private/pprio.h" #include "private/pprio.h"
#include "ContentProcessManager.h" #include "ContentProcessManager.h"
#include "mozilla/psm/PSMContentListener.h" #include "mozilla/psm/PSMContentListener.h"
#include "nsPluginHost.h"
#include "nsPluginTags.h"
#include "nsIBlocklistService.h"
#include "nsIBidiKeyboard.h" #include "nsIBidiKeyboard.h"
@ -1083,6 +1086,25 @@ ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv)
return mozilla::plugins::SetupBridge(aPluginId, this, true, aRv, &dummy); return mozilla::plugins::SetupBridge(aPluginId, this, true, aRv, &dummy);
} }
bool
ContentParent::RecvGetBlocklistState(const uint32_t& aPluginId,
uint32_t* aState)
{
*aState = nsIBlocklistService::STATE_BLOCKED;
nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
if (!pluginHost) {
return false;
}
nsPluginTag* tag = pluginHost->PluginWithId(aPluginId);
if (!tag) {
return false;
}
return NS_SUCCEEDED(tag->GetBlocklistState(aState));
}
bool bool
ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch, ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch,
nsTArray<PluginTag>* aPlugins, nsTArray<PluginTag>* aPlugins,

Просмотреть файл

@ -173,6 +173,7 @@ public:
virtual bool RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID) override; virtual bool RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID) override;
virtual bool RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) override; virtual bool RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) override;
virtual bool RecvGetBlocklistState(const uint32_t& aPluginId, uint32_t* aIsBlocklisted) override;
virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch, virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch,
nsTArray<PluginTag>* aPlugins, nsTArray<PluginTag>* aPlugins,
uint32_t* aNewPluginEpoch) override; uint32_t* aNewPluginEpoch) override;

Просмотреть файл

@ -699,6 +699,11 @@ parent:
*/ */
sync ConnectPluginBridge(uint32_t aPluginId) returns (nsresult rv); sync ConnectPluginBridge(uint32_t aPluginId) returns (nsresult rv);
/**
* Return the current blocklist state for a particular plugin.
*/
sync GetBlocklistState(uint32_t aPluginId) returns (uint32_t aState);
/** /**
* This call returns the set of plugins loaded in the chrome * This call returns the set of plugins loaded in the chrome
* process. However, in many cases this set will not have changed since the * process. However, in many cases this set will not have changed since the

Просмотреть файл

@ -110,6 +110,8 @@
#include "nsExceptionHandler.h" #include "nsExceptionHandler.h"
#endif #endif
#include "npapi.h"
using namespace mozilla; using namespace mozilla;
using mozilla::TimeStamp; using mozilla::TimeStamp;
using mozilla::plugins::PluginTag; using mozilla::plugins::PluginTag;
@ -1054,7 +1056,6 @@ nsPluginHost::GetBlocklistStateForType(const nsACString &aMimeType,
aExcludeFlags, aExcludeFlags,
getter_AddRefs(tag)); getter_AddRefs(tag));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
return tag->GetBlocklistState(aState); return tag->GetBlocklistState(aState);
} }
@ -1275,6 +1276,13 @@ nsPluginHost::GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPl
nsPluginTag* pluginTag = PluginWithId(aPluginId); nsPluginTag* pluginTag = PluginWithId(aPluginId);
if (pluginTag) { if (pluginTag) {
// When setting up a bridge, double check with chrome to see if this plugin
// is blocked hard. Note this does not protect against vulnerable plugins
// that the user has explicitly allowed. :(
if (pluginTag->IsBlocklisted()) {
return NS_ERROR_PLUGIN_BLOCKLISTED;
}
nsresult rv = EnsurePluginLoaded(pluginTag); nsresult rv = EnsurePluginLoaded(pluginTag);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;

Просмотреть файл

@ -11,7 +11,6 @@
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "prlink.h" #include "prlink.h"
#include "prclist.h" #include "prclist.h"
#include "npapi.h"
#include "nsIPluginTag.h" #include "nsIPluginTag.h"
#include "nsPluginsDir.h" #include "nsPluginsDir.h"
#include "nsPluginDirServiceProvider.h" #include "nsPluginDirServiceProvider.h"
@ -30,6 +29,7 @@
#include "nsCRT.h" #include "nsCRT.h"
#ifdef XP_WIN #ifdef XP_WIN
#include <minwindef.h>
#include "nsIWindowsRegKey.h" #include "nsIWindowsRegKey.h"
#endif #endif
@ -52,6 +52,10 @@ class nsNPAPIPluginStreamListener;
class nsIPluginInstanceOwner; class nsIPluginInstanceOwner;
class nsIInputStream; class nsIInputStream;
class nsIStreamListener; class nsIStreamListener;
#ifndef npapi_h_
struct _NPP;
typedef _NPP* NPP;
#endif
class nsInvalidPluginTag : public nsISupports class nsInvalidPluginTag : public nsISupports
{ {

Просмотреть файл

@ -20,6 +20,7 @@
#include "mozilla/unused.h" #include "mozilla/unused.h"
#include <cctype> #include <cctype>
#include "mozilla/dom/EncodingUtils.h" #include "mozilla/dom/EncodingUtils.h"
#include "mozilla/dom/ContentChild.h"
using mozilla::dom::EncodingUtils; using mozilla::dom::EncodingUtils;
using namespace mozilla; using namespace mozilla;
@ -645,27 +646,33 @@ nsPluginTag::GetBlocklistState(uint32_t *aResult)
return NS_OK; return NS_OK;
} }
nsCOMPtr<nsIBlocklistService> blocklist = if (XRE_GetProcessType() != GeckoProcessType_Default) {
do_GetService("@mozilla.org/extensions/blocklist;1"); *aResult = nsIBlocklistService::STATE_BLOCKED;
dom::ContentChild* cp = dom::ContentChild::GetSingleton();
if (!cp->SendGetBlocklistState(mId, aResult)) {
return NS_OK;
}
} else {
nsCOMPtr<nsIBlocklistService> blocklist =
do_GetService("@mozilla.org/extensions/blocklist;1");
if (!blocklist) { if (!blocklist) {
*aResult = nsIBlocklistService::STATE_NOT_BLOCKED; *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
return NS_OK; return NS_OK;
}
// The EmptyString()s are so we use the currently running application
// and toolkit versions
if (NS_FAILED(blocklist->GetPluginBlocklistState(this, EmptyString(),
EmptyString(), aResult))) {
*aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
return NS_OK;
}
} }
// The EmptyString()s are so we use the currently running application MOZ_ASSERT(*aResult <= UINT16_MAX);
// and toolkit versions mCachedBlocklistState = (uint16_t) *aResult;
uint32_t state;
if (NS_FAILED(blocklist->GetPluginBlocklistState(this, EmptyString(),
EmptyString(), &state))) {
*aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
return NS_OK;
}
MOZ_ASSERT(state <= UINT16_MAX);
mCachedBlocklistState = (uint16_t) state;
mCachedBlocklistStateValid = true; mCachedBlocklistStateValid = true;
*aResult = state;
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -1043,6 +1043,7 @@ ServiceWorkerManager::AppendPendingOperation(nsIRunnable* aRunnable)
class LifecycleEventPromiseHandler final : public PromiseNativeHandler class LifecycleEventPromiseHandler final : public PromiseNativeHandler
{ {
nsMainThreadPtrHandle<ContinueLifecycleTask> mTask; nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
bool mActivateImmediately; bool mActivateImmediately;
virtual virtual
@ -1051,8 +1052,10 @@ class LifecycleEventPromiseHandler final : public PromiseNativeHandler
public: public:
LifecycleEventPromiseHandler(const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask, LifecycleEventPromiseHandler(const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask,
const nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
bool aActivateImmediately) bool aActivateImmediately)
: mTask(aTask) : mTask(aTask)
, mServiceWorker(aServiceWorker)
, mActivateImmediately(aActivateImmediately) , mActivateImmediately(aActivateImmediately)
{ {
MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(!NS_IsMainThread());
@ -1135,7 +1138,7 @@ LifecycleEventWorkerRunnable::DispatchLifecycleEvent(JSContext* aCx, WorkerPriva
} }
nsRefPtr<LifecycleEventPromiseHandler> handler = nsRefPtr<LifecycleEventPromiseHandler> handler =
new LifecycleEventPromiseHandler(mTask, false /* activateImmediately */); new LifecycleEventPromiseHandler(mTask, mServiceWorker, false /* activateImmediately */);
waitUntilPromise->AppendNativeHandler(handler); waitUntilPromise->AppendNativeHandler(handler);
return true; return true;
} }

Просмотреть файл

@ -83,6 +83,7 @@ using namespace mozilla::layout;
using namespace mozilla::gfx; using namespace mozilla::gfx;
typedef FrameMetrics::ViewID ViewID; typedef FrameMetrics::ViewID ViewID;
typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;
#ifdef DEBUG #ifdef DEBUG
static bool static bool
@ -115,7 +116,7 @@ MakeCSSAngle(const nsCSSValue& aValue)
static void AddTransformFunctions(nsCSSValueList* aList, static void AddTransformFunctions(nsCSSValueList* aList,
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
nsRect& aBounds, TransformReferenceBox& aRefBox,
InfallibleTArray<TransformFunction>& aFunctions) InfallibleTArray<TransformFunction>& aFunctions)
{ {
if (aList->mValue.GetUnit() == eCSSUnit_None) { if (aList->mValue.GetUnit() == eCSSUnit_None) {
@ -200,7 +201,7 @@ static void AddTransformFunctions(nsCSSValueList* aList,
{ {
double x = nsStyleTransformMatrix::ProcessTranslatePart( double x = nsStyleTransformMatrix::ProcessTranslatePart(
array->Item(1), aContext, aPresContext, canStoreInRuleTree, array->Item(1), aContext, aPresContext, canStoreInRuleTree,
aBounds.Width()); &aRefBox, &TransformReferenceBox::Width);
aFunctions.AppendElement(Translation(x, 0, 0)); aFunctions.AppendElement(Translation(x, 0, 0));
break; break;
} }
@ -208,7 +209,7 @@ static void AddTransformFunctions(nsCSSValueList* aList,
{ {
double y = nsStyleTransformMatrix::ProcessTranslatePart( double y = nsStyleTransformMatrix::ProcessTranslatePart(
array->Item(1), aContext, aPresContext, canStoreInRuleTree, array->Item(1), aContext, aPresContext, canStoreInRuleTree,
aBounds.Height()); &aRefBox, &TransformReferenceBox::Height);
aFunctions.AppendElement(Translation(0, y, 0)); aFunctions.AppendElement(Translation(0, y, 0));
break; break;
} }
@ -216,7 +217,7 @@ static void AddTransformFunctions(nsCSSValueList* aList,
{ {
double z = nsStyleTransformMatrix::ProcessTranslatePart( double z = nsStyleTransformMatrix::ProcessTranslatePart(
array->Item(1), aContext, aPresContext, canStoreInRuleTree, array->Item(1), aContext, aPresContext, canStoreInRuleTree,
0); nullptr);
aFunctions.AppendElement(Translation(0, 0, z)); aFunctions.AppendElement(Translation(0, 0, z));
break; break;
} }
@ -224,13 +225,13 @@ static void AddTransformFunctions(nsCSSValueList* aList,
{ {
double x = nsStyleTransformMatrix::ProcessTranslatePart( double x = nsStyleTransformMatrix::ProcessTranslatePart(
array->Item(1), aContext, aPresContext, canStoreInRuleTree, array->Item(1), aContext, aPresContext, canStoreInRuleTree,
aBounds.Width()); &aRefBox, &TransformReferenceBox::Width);
// translate(x) is shorthand for translate(x, 0) // translate(x) is shorthand for translate(x, 0)
double y = 0; double y = 0;
if (array->Count() == 3) { if (array->Count() == 3) {
y = nsStyleTransformMatrix::ProcessTranslatePart( y = nsStyleTransformMatrix::ProcessTranslatePart(
array->Item(2), aContext, aPresContext, canStoreInRuleTree, array->Item(2), aContext, aPresContext, canStoreInRuleTree,
aBounds.Height()); &aRefBox, &TransformReferenceBox::Height);
} }
aFunctions.AppendElement(Translation(x, y, 0)); aFunctions.AppendElement(Translation(x, y, 0));
break; break;
@ -239,13 +240,13 @@ static void AddTransformFunctions(nsCSSValueList* aList,
{ {
double x = nsStyleTransformMatrix::ProcessTranslatePart( double x = nsStyleTransformMatrix::ProcessTranslatePart(
array->Item(1), aContext, aPresContext, canStoreInRuleTree, array->Item(1), aContext, aPresContext, canStoreInRuleTree,
aBounds.Width()); &aRefBox, &TransformReferenceBox::Width);
double y = nsStyleTransformMatrix::ProcessTranslatePart( double y = nsStyleTransformMatrix::ProcessTranslatePart(
array->Item(2), aContext, aPresContext, canStoreInRuleTree, array->Item(2), aContext, aPresContext, canStoreInRuleTree,
aBounds.Height()); &aRefBox, &TransformReferenceBox::Height);
double z = nsStyleTransformMatrix::ProcessTranslatePart( double z = nsStyleTransformMatrix::ProcessTranslatePart(
array->Item(3), aContext, aPresContext, canStoreInRuleTree, array->Item(3), aContext, aPresContext, canStoreInRuleTree,
0); nullptr);
aFunctions.AppendElement(Translation(x, y, z)); aFunctions.AppendElement(Translation(x, y, z));
break; break;
@ -324,7 +325,7 @@ static void AddTransformFunctions(nsCSSValueList* aList,
aContext, aContext,
aPresContext, aPresContext,
canStoreInRuleTree, canStoreInRuleTree,
aBounds); aRefBox);
aFunctions.AppendElement(TransformMatrix(gfx::ToMatrix4x4(matrix))); aFunctions.AppendElement(TransformMatrix(gfx::ToMatrix4x4(matrix)));
break; break;
} }
@ -362,7 +363,7 @@ AddAnimationForProperty(nsIFrame* aFrame, const AnimationProperty& aProperty,
"Should not be adding an animation without an effect"); "Should not be adding an animation without an effect");
nsStyleContext* styleContext = aFrame->StyleContext(); nsStyleContext* styleContext = aFrame->StyleContext();
nsPresContext* presContext = aFrame->PresContext(); nsPresContext* presContext = aFrame->PresContext();
nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(aFrame); TransformReferenceBox refBox(aFrame);
layers::Animation* animation = layers::Animation* animation =
aPending ? aPending ?
@ -393,11 +394,11 @@ AddAnimationForProperty(nsIFrame* aFrame, const AnimationProperty& aProperty,
nsCSSValueSharedList* list = nsCSSValueSharedList* list =
segment.mFromValue.GetCSSValueSharedListValue(); segment.mFromValue.GetCSSValueSharedListValue();
AddTransformFunctions(list->mHead, styleContext, presContext, bounds, AddTransformFunctions(list->mHead, styleContext, presContext, refBox,
animSegment->startState().get_ArrayOfTransformFunction()); animSegment->startState().get_ArrayOfTransformFunction());
list = segment.mToValue.GetCSSValueSharedListValue(); list = segment.mToValue.GetCSSValueSharedListValue();
AddTransformFunctions(list->mHead, styleContext, presContext, bounds, AddTransformFunctions(list->mHead, styleContext, presContext, refBox,
animSegment->endState().get_ArrayOfTransformFunction()); animSegment->endState().get_ArrayOfTransformFunction());
} else if (aProperty.mProperty == eCSSProperty_opacity) { } else if (aProperty.mProperty == eCSSProperty_opacity) {
animSegment->startState() = segment.mFromValue.GetFloatValue(); animSegment->startState() = segment.mFromValue.GetFloatValue();
@ -535,7 +536,13 @@ nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,
AnimationData data; AnimationData data;
if (aProperty == eCSSProperty_transform) { if (aProperty == eCSSProperty_transform) {
nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(aFrame); // XXX Performance here isn't ideal for SVG. We'd prefer to avoid resolving
// the dimensions of refBox. That said, we only get here if there are CSS
// animations or transitions on this element, and that is likely to be a
// lot rarer that transforms on SVG (the frequency of which drives the need
// for TransformReferenceBox).
TransformReferenceBox refBox(aFrame);
nsRect bounds(0, 0, refBox.Width(), refBox.Height());
// all data passed directly to the compositor should be in dev pixels // all data passed directly to the compositor should be in dev pixels
int32_t devPixelsToAppUnits = aFrame->PresContext()->AppUnitsPerDevPixel(); int32_t devPixelsToAppUnits = aFrame->PresContext()->AppUnitsPerDevPixel();
float scale = devPixelsToAppUnits; float scale = devPixelsToAppUnits;
@ -4465,7 +4472,8 @@ bool nsDisplayZoom::ComputeVisibility(nsDisplayListBuilder *aBuilder,
// nsDisplayTransform Implementation // nsDisplayTransform Implementation
// //
// Write #define UNIFIED_CONTINUATIONS here to have the transform property try // Write #define UNIFIED_CONTINUATIONS here and in
// TransformReferenceBox::Initialize to have the transform property try
// to transform content with continuations as one unified block instead of // to transform content with continuations as one unified block instead of
// several smaller ones. This is currently disabled because it doesn't work // several smaller ones. This is currently disabled because it doesn't work
// correctly, since when the frames are initially being reflowed, their // correctly, since when the frames are initially being reflowed, their
@ -4476,63 +4484,6 @@ bool nsDisplayZoom::ComputeVisibility(nsDisplayListBuilder *aBuilder,
#undef UNIFIED_CONTINUATIONS #undef UNIFIED_CONTINUATIONS
#undef DEBUG_HIT #undef DEBUG_HIT
/* Returns the bounds of a frame as defined for transforms. If
* UNIFIED_CONTINUATIONS is not defined, this is simply the frame's bounding
* rectangle, translated to the origin. Otherwise, returns the smallest
* rectangle containing a frame and all of its continuations. For example, if
* there is a <span> element with several continuations split over several
* lines, this function will return the rectangle containing all of those
* continuations. This rectangle is relative to the origin of the frame's local
* coordinate space.
*/
#ifndef UNIFIED_CONTINUATIONS
nsRect
nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
{
NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!");
if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
// TODO: SVG needs to define what percentage translations resolve against.
return nsRect();
}
return nsRect(nsPoint(0, 0), aFrame->GetSize());
}
#else
nsRect
nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
{
NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!");
nsRect result;
if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
// TODO: SVG needs to define what percentage translations resolve against.
return result;
}
/* Iterate through the continuation list, unioning together all the
* bounding rects.
*/
for (const nsIFrame *currFrame = aFrame->FirstContinuation();
currFrame != nullptr;
currFrame = currFrame->GetNextContinuation())
{
/* Get the frame rect in local coordinates, then translate back to the
* original coordinates.
*/
result.UnionRect(result, nsRect(currFrame->GetOffsetTo(aFrame),
currFrame->GetSize()));
}
return result;
}
#endif
nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
nsIFrame *aFrame, nsDisplayList *aList, nsIFrame *aFrame, nsDisplayList *aList,
const nsRect& aChildrenVisibleRect, const nsRect& aChildrenVisibleRect,
@ -4633,13 +4584,17 @@ nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame,
* a distance, it's already computed for us! * a distance, it's already computed for us!
*/ */
const nsStyleDisplay* display = aFrame->StyleDisplay(); const nsStyleDisplay* display = aFrame->StyleDisplay();
nsRect boundingRect = (aBoundsOverride ? *aBoundsOverride : TransformReferenceBox refBox;
nsDisplayTransform::GetFrameBoundsForTransform(aFrame)); if (aBoundsOverride) {
refBox.Init(aBoundsOverride->Size());
} else {
refBox.Init(aFrame);
}
/* Allows us to access named variables by index. */ /* Allows us to access dimension getters by index. */
float coords[3]; float coords[3];
const nscoord* dimensions[2] = TransformReferenceBox::DimensionGetter dimensionGetter[] =
{&boundingRect.width, &boundingRect.height}; { &TransformReferenceBox::Width, &TransformReferenceBox::Height };
for (uint8_t index = 0; index < 2; ++index) { for (uint8_t index = 0; index < 2; ++index) {
/* If the -moz-transform-origin specifies a percentage, take the percentage /* If the -moz-transform-origin specifies a percentage, take the percentage
@ -4649,12 +4604,12 @@ nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame,
if (coord.GetUnit() == eStyleUnit_Calc) { if (coord.GetUnit() == eStyleUnit_Calc) {
const nsStyleCoord::Calc *calc = coord.GetCalcValue(); const nsStyleCoord::Calc *calc = coord.GetCalcValue();
coords[index] = coords[index] =
NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * NSAppUnitsToFloatPixels((refBox.*dimensionGetter[index])(), aAppUnitsPerPixel) *
calc->mPercent + calc->mPercent +
NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel); NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
} else if (coord.GetUnit() == eStyleUnit_Percent) { } else if (coord.GetUnit() == eStyleUnit_Percent) {
coords[index] = coords[index] =
NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * NSAppUnitsToFloatPixels((refBox.*dimensionGetter[index])(), aAppUnitsPerPixel) *
coord.GetPercentValue(); coord.GetPercentValue();
} else { } else {
MOZ_ASSERT(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); MOZ_ASSERT(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
@ -4673,9 +4628,11 @@ nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame,
coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(), coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(),
aAppUnitsPerPixel); aAppUnitsPerPixel);
/* Adjust based on the origin of the rectangle. */ if (aBoundsOverride) {
coords[0] += NSAppUnitsToFloatPixels(boundingRect.x, aAppUnitsPerPixel); // Adjust based on the origin of the override:
coords[1] += NSAppUnitsToFloatPixels(boundingRect.y, aAppUnitsPerPixel); coords[0] += NSAppUnitsToFloatPixels(aBoundsOverride->x, aAppUnitsPerPixel);
coords[1] += NSAppUnitsToFloatPixels(aBoundsOverride->y, aAppUnitsPerPixel);
}
return Point3D(coords[0], coords[1], coords[2]); return Point3D(coords[0], coords[1], coords[2]);
} }
@ -4716,14 +4673,14 @@ nsDisplayTransform::GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame,
} }
} }
const nsStyleDisplay* display = psc->StyleDisplay(); const nsStyleDisplay* display = psc->StyleDisplay();
nsRect boundingRect = nsDisplayTransform::GetFrameBoundsForTransform(parent); TransformReferenceBox refBox(parent);
/* Allows us to access named variables by index. */ /* Allows us to access named variables by index. */
Point3D result; Point3D result;
result.z = 0.0f; result.z = 0.0f;
gfx::Float* coords[2] = {&result.x, &result.y}; gfx::Float* coords[2] = {&result.x, &result.y};
const nscoord* dimensions[2] = TransformReferenceBox::DimensionGetter dimensionGetter[] =
{&boundingRect.width, &boundingRect.height}; { &TransformReferenceBox::Width, &TransformReferenceBox::Height };
for (uint8_t index = 0; index < 2; ++index) { for (uint8_t index = 0; index < 2; ++index) {
/* If the -moz-transform-origin specifies a percentage, take the percentage /* If the -moz-transform-origin specifies a percentage, take the percentage
@ -4733,12 +4690,12 @@ nsDisplayTransform::GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame,
if (coord.GetUnit() == eStyleUnit_Calc) { if (coord.GetUnit() == eStyleUnit_Calc) {
const nsStyleCoord::Calc *calc = coord.GetCalcValue(); const nsStyleCoord::Calc *calc = coord.GetCalcValue();
*coords[index] = *coords[index] =
NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * NSAppUnitsToFloatPixels((refBox.*dimensionGetter[index])(), aAppUnitsPerPixel) *
calc->mPercent + calc->mPercent +
NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel); NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
} else if (coord.GetUnit() == eStyleUnit_Percent) { } else if (coord.GetUnit() == eStyleUnit_Percent) {
*coords[index] = *coords[index] =
NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * NSAppUnitsToFloatPixels((refBox.*dimensionGetter[index])(), aAppUnitsPerPixel) *
coord.GetPercentValue(); coord.GetPercentValue();
} else { } else {
MOZ_ASSERT(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); MOZ_ASSERT(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
@ -4821,11 +4778,14 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
*aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(frame); *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(frame);
} }
/* Get the underlying transform matrix. This requires us to get the // Get the underlying transform matrix:
* bounds of the frame.
*/ TransformReferenceBox refBox;
nsRect bounds = (aBoundsOverride ? *aBoundsOverride : if (aBoundsOverride) {
nsDisplayTransform::GetFrameBoundsForTransform(frame)); refBox.Init(aBoundsOverride->Size());
} else {
refBox.Init(frame);
}
/* Get the matrix, then change its basis to factor in the origin. */ /* Get the matrix, then change its basis to factor in the origin. */
bool dummy; bool dummy;
@ -4840,7 +4800,7 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
result = nsStyleTransformMatrix::ReadTransforms(aProperties.mTransformList->mHead, result = nsStyleTransformMatrix::ReadTransforms(aProperties.mTransformList->mHead,
frame ? frame->StyleContext() : nullptr, frame ? frame->StyleContext() : nullptr,
frame ? frame->PresContext() : nullptr, frame ? frame->PresContext() : nullptr,
dummy, bounds, aAppUnitsPerPixel); dummy, refBox, aAppUnitsPerPixel);
} else if (hasSVGTransforms) { } else if (hasSVGTransforms) {
// Correct the translation components for zoom: // Correct the translation components for zoom:
float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() / float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() /

Просмотреть файл

@ -3486,22 +3486,6 @@ public:
static Point3D GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame, static Point3D GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame,
float aAppUnitsPerPixel); float aAppUnitsPerPixel);
/**
* Returns the bounds of a frame as defined for resolving percentage
* <translation-value>s in CSS transforms. If
* UNIFIED_CONTINUATIONS is not defined, this is simply the frame's bounding
* rectangle, translated to the origin. Otherwise, returns the smallest
* rectangle containing a frame and all of its continuations. For example,
* if there is a <span> element with several continuations split over
* several lines, this function will return the rectangle containing all of
* those continuations. This rectangle is relative to the origin of the
* frame's local coordinate space.
*
* @param aFrame The frame to get the bounding rect for.
* @return The frame's bounding rect, as described above.
*/
static nsRect GetFrameBoundsForTransform(const nsIFrame* aFrame);
struct FrameTransformProperties struct FrameTransformProperties
{ {
FrameTransformProperties(const nsIFrame* aFrame, FrameTransformProperties(const nsIFrame* aFrame,
@ -3533,10 +3517,10 @@ public:
* @param aOrigin Relative to which point this transform should be applied. * @param aOrigin Relative to which point this transform should be applied.
* @param aAppUnitsPerPixel The number of app units per graphics unit. * @param aAppUnitsPerPixel The number of app units per graphics unit.
* @param aBoundsOverride [optional] If this is nullptr (the default), the * @param aBoundsOverride [optional] If this is nullptr (the default), the
* computation will use the value of GetFrameBoundsForTransform(aFrame) * computation will use the value of TransformReferenceBox(aFrame).
* for the frame's bounding rectangle. Otherwise, it will use the * Otherwise, it will use the value of aBoundsOverride. This is
* value of aBoundsOverride. This is mostly for internal use and in * mostly for internal use and in most cases you will not need to
* most cases you will not need to specify a value. * specify a value.
* @param aOffsetByOrigin If true, the resulting matrix will be translated * @param aOffsetByOrigin If true, the resulting matrix will be translated
* by aOrigin. This translation is applied *before* the CSS transform. * by aOrigin. This translation is applied *before* the CSS transform.
*/ */

Просмотреть файл

@ -128,6 +128,7 @@ bool nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
#endif // DEBUG #endif // DEBUG
typedef FrameMetrics::ViewID ViewID; typedef FrameMetrics::ViewID ViewID;
typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;
/* static */ uint32_t nsLayoutUtils::sFontSizeInflationEmPerLine; /* static */ uint32_t nsLayoutUtils::sFontSizeInflationEmPerLine;
/* static */ uint32_t nsLayoutUtils::sFontSizeInflationMinTwips; /* static */ uint32_t nsLayoutUtils::sFontSizeInflationMinTwips;
@ -462,12 +463,12 @@ GetScaleForValue(const StyleAnimationValue& aValue, nsIFrame* aFrame)
return gfxSize(); return gfxSize();
} }
nsRect frameBounds = aFrame->GetRect();
bool dontCare; bool dontCare;
TransformReferenceBox refBox(aFrame);
gfx3DMatrix transform = nsStyleTransformMatrix::ReadTransforms( gfx3DMatrix transform = nsStyleTransformMatrix::ReadTransforms(
list->mHead, list->mHead,
aFrame->StyleContext(), aFrame->StyleContext(),
aFrame->PresContext(), dontCare, frameBounds, aFrame->PresContext(), dontCare, refBox,
aFrame->PresContext()->AppUnitsPerDevPixel()); aFrame->PresContext()->AppUnitsPerDevPixel());
gfxMatrix transform2d; gfxMatrix transform2d;

Просмотреть файл

@ -53,6 +53,7 @@ using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;
typedef const nsStyleBackground::Position Position; typedef const nsStyleBackground::Position Position;
typedef const nsStyleBackground::Position::PositionCoord PositionCoord; typedef const nsStyleBackground::Position::PositionCoord PositionCoord;
typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;
#if defined(DEBUG_bzbarsky) || defined(DEBUG_caillon) #if defined(DEBUG_bzbarsky) || defined(DEBUG_caillon)
#define DEBUG_ComputedDOMStyle #define DEBUG_ComputedDOMStyle
@ -1253,7 +1254,9 @@ nsComputedDOMStyle::DoGetTransform()
* store it in a string, and hand it back to the caller. * store it in a string, and hand it back to the caller.
*/ */
/* Use the inner frame for width and height. If we fail, assume zero. /* Use the inner frame for the reference box. If we don't have an inner
* frame we use empty dimensions to allow us to continue (and percentage
* values in the transform will simply give broken results).
* TODO: There is no good way for us to represent the case where there's no * TODO: There is no good way for us to represent the case where there's no
* frame, which is problematic. The reason is that when we have percentage * frame, which is problematic. The reason is that when we have percentage
* transforms, there are a total of four stored matrix entries that influence * transforms, there are a total of four stored matrix entries that influence
@ -1262,9 +1265,7 @@ nsComputedDOMStyle::DoGetTransform()
* using the named transforms. Until a real solution is found, we'll just * using the named transforms. Until a real solution is found, we'll just
* use this approach. * use this approach.
*/ */
nsRect bounds = TransformReferenceBox refBox(mInnerFrame, nsSize(0, 0));
(mInnerFrame ? nsDisplayTransform::GetFrameBoundsForTransform(mInnerFrame) :
nsRect(0, 0, 0, 0));
bool dummy; bool dummy;
gfx3DMatrix matrix = gfx3DMatrix matrix =
@ -1272,7 +1273,7 @@ nsComputedDOMStyle::DoGetTransform()
mStyleContextHolder, mStyleContextHolder,
mStyleContextHolder->PresContext(), mStyleContextHolder->PresContext(),
dummy, dummy,
bounds, refBox,
float(mozilla::AppUnitsPerCSSPixel())); float(mozilla::AppUnitsPerCSSPixel()));
return MatrixToCSSValue(matrix); return MatrixToCSSValue(matrix);
@ -4958,7 +4959,7 @@ nsComputedDOMStyle::GetFrameBoundsWidthForTransform(nscoord& aWidth)
AssertFlushedPendingReflows(); AssertFlushedPendingReflows();
aWidth = nsDisplayTransform::GetFrameBoundsForTransform(mInnerFrame).width; aWidth = TransformReferenceBox(mInnerFrame).Width();
return true; return true;
} }
@ -4972,7 +4973,7 @@ nsComputedDOMStyle::GetFrameBoundsHeightForTransform(nscoord& aHeight)
AssertFlushedPendingReflows(); AssertFlushedPendingReflows();
aHeight = nsDisplayTransform::GetFrameBoundsForTransform(mInnerFrame).height; aHeight = TransformReferenceBox(mInnerFrame).Height();
return true; return true;
} }

Просмотреть файл

@ -26,6 +26,72 @@ namespace nsStyleTransformMatrix {
* involving angles need to be done in 'double'. * involving angles need to be done in 'double'.
*/ */
// Define UNIFIED_CONTINUATIONS here and in nsDisplayList.cpp
// to have the transform property try
// to transform content with continuations as one unified block instead of
// several smaller ones. This is currently disabled because it doesn't work
// correctly, since when the frames are initially being reflowed, their
// continuations all compute their bounding rects independently of each other
// and consequently get the wrong value.
//#define UNIFIED_CONTINUATIONS
void
TransformReferenceBox::EnsureDimensionsAreCached()
{
if (mIsCached) {
return;
}
MOZ_ASSERT(mFrame);
mIsCached = true;
if (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
// TODO: SVG needs to define what percentage translations resolve against.
mWidth = 0;
mHeight = 0;
return;
}
// If UNIFIED_CONTINUATIONS is not defined, this is simply the frame's
// bounding rectangle, translated to the origin. Otherwise, it is the
// smallest rectangle containing a frame and all of its continuations. For
// example, if there is a <span> element with several continuations split
// over several lines, this function will return the rectangle containing all
// of those continuations.
nsRect rect;
#ifndef UNIFIED_CONTINUATIONS
rect = mFrame->GetRect();
#else
// Iterate the continuation list, unioning together the bounding rects:
for (const nsIFrame *currFrame = mFrame->FirstContinuation();
currFrame != nullptr;
currFrame = currFrame->GetNextContinuation())
{
// Get the frame rect in local coordinates, then translate back to the
// original coordinates:
rect.UnionRect(result, nsRect(currFrame->GetOffsetTo(mFrame),
currFrame->GetSize()));
}
#endif
mWidth = rect.Width();
mHeight = rect.Height();
}
void
TransformReferenceBox::Init(const nsSize& aDimensions)
{
MOZ_ASSERT(!mFrame && !mIsCached);
mWidth = aDimensions.width;
mHeight = aDimensions.height;
mIsCached = true;
}
/* Force small values to zero. We do this to avoid having sin(360deg) /* Force small values to zero. We do this to avoid having sin(360deg)
* evaluate to a tiny but nonzero value. * evaluate to a tiny but nonzero value.
*/ */
@ -42,7 +108,8 @@ ProcessTranslatePart(const nsCSSValue& aValue,
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool& aCanStoreInRuleTree, bool& aCanStoreInRuleTree,
nscoord aSize) TransformReferenceBox* aRefBox,
TransformReferenceBox::DimensionGetter aDimensionGetter)
{ {
nscoord offset = 0; nscoord offset = 0;
float percent = 0.0f; float percent = 0.0f;
@ -72,8 +139,16 @@ ProcessTranslatePart(const nsCSSValue& aValue,
aCanStoreInRuleTree); aCanStoreInRuleTree);
} }
return (percent * NSAppUnitsToFloatPixels(aSize, nsPresContext::AppUnitsPerCSSPixel())) + float translation = NSAppUnitsToFloatPixels(offset,
NSAppUnitsToFloatPixels(offset, nsPresContext::AppUnitsPerCSSPixel()); nsPresContext::AppUnitsPerCSSPixel());
// We want to avoid calling aDimensionGetter if there's no percentage to be
// resolved (for performance reasons - see TransformReferenceBox).
if (percent != 0.0f && aRefBox) {
translation += percent *
NSAppUnitsToFloatPixels((aRefBox->*aDimensionGetter)(),
nsPresContext::AppUnitsPerCSSPixel());
}
return translation;
} }
/** /**
@ -89,7 +164,7 @@ ProcessMatrix(gfx3DMatrix& aMatrix,
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool& aCanStoreInRuleTree, bool& aCanStoreInRuleTree,
nsRect& aBounds) TransformReferenceBox& aRefBox)
{ {
NS_PRECONDITION(aData->Count() == 7, "Invalid array!"); NS_PRECONDITION(aData->Count() == 7, "Invalid array!");
@ -108,10 +183,10 @@ ProcessMatrix(gfx3DMatrix& aMatrix,
*/ */
result._31 = ProcessTranslatePart(aData->Item(5), result._31 = ProcessTranslatePart(aData->Item(5),
aContext, aPresContext, aCanStoreInRuleTree, aContext, aPresContext, aCanStoreInRuleTree,
aBounds.Width()); &aRefBox, &TransformReferenceBox::Width);
result._32 = ProcessTranslatePart(aData->Item(6), result._32 = ProcessTranslatePart(aData->Item(6),
aContext, aPresContext, aCanStoreInRuleTree, aContext, aPresContext, aCanStoreInRuleTree,
aBounds.Height()); &aRefBox, &TransformReferenceBox::Height);
aMatrix.PreMultiply(result); aMatrix.PreMultiply(result);
} }
@ -122,7 +197,7 @@ ProcessMatrix3D(gfx3DMatrix& aMatrix,
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool& aCanStoreInRuleTree, bool& aCanStoreInRuleTree,
nsRect& aBounds) TransformReferenceBox& aRefBox)
{ {
NS_PRECONDITION(aData->Count() == 17, "Invalid array!"); NS_PRECONDITION(aData->Count() == 17, "Invalid array!");
@ -144,13 +219,14 @@ ProcessMatrix3D(gfx3DMatrix& aMatrix,
temp._41 = ProcessTranslatePart(aData->Item(13), temp._41 = ProcessTranslatePart(aData->Item(13),
aContext, aPresContext, aCanStoreInRuleTree, aContext, aPresContext, aCanStoreInRuleTree,
aBounds.Width()); &aRefBox, &TransformReferenceBox::Width);
temp._42 = ProcessTranslatePart(aData->Item(14), temp._42 = ProcessTranslatePart(aData->Item(14),
aContext, aPresContext, aCanStoreInRuleTree, aContext, aPresContext, aCanStoreInRuleTree,
aBounds.Height()); &aRefBox, &TransformReferenceBox::Height);
temp._43 = ProcessTranslatePart(aData->Item(15), temp._43 = ProcessTranslatePart(aData->Item(15),
aContext, aPresContext, aCanStoreInRuleTree, aContext, aPresContext, aCanStoreInRuleTree,
aBounds.Height()); &aRefBox, &TransformReferenceBox::Height);
// XXXjwatt why are we using TransformReferenceBox::Height for the z compontent?!
aMatrix.PreMultiply(temp); aMatrix.PreMultiply(temp);
} }
@ -162,7 +238,7 @@ ProcessInterpolateMatrix(gfx3DMatrix& aMatrix,
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool& aCanStoreInRuleTree, bool& aCanStoreInRuleTree,
nsRect& aBounds) TransformReferenceBox& aRefBox)
{ {
NS_PRECONDITION(aData->Count() == 4, "Invalid array!"); NS_PRECONDITION(aData->Count() == 4, "Invalid array!");
@ -171,13 +247,13 @@ ProcessInterpolateMatrix(gfx3DMatrix& aMatrix,
matrix1 = nsStyleTransformMatrix::ReadTransforms(aData->Item(1).GetListValue(), matrix1 = nsStyleTransformMatrix::ReadTransforms(aData->Item(1).GetListValue(),
aContext, aPresContext, aContext, aPresContext,
aCanStoreInRuleTree, aCanStoreInRuleTree,
aBounds, nsPresContext::AppUnitsPerCSSPixel()); aRefBox, nsPresContext::AppUnitsPerCSSPixel());
} }
if (aData->Item(2).GetUnit() == eCSSUnit_List) { if (aData->Item(2).GetUnit() == eCSSUnit_List) {
matrix2 = ReadTransforms(aData->Item(2).GetListValue(), matrix2 = ReadTransforms(aData->Item(2).GetListValue(),
aContext, aPresContext, aContext, aPresContext,
aCanStoreInRuleTree, aCanStoreInRuleTree,
aBounds, nsPresContext::AppUnitsPerCSSPixel()); aRefBox, nsPresContext::AppUnitsPerCSSPixel());
} }
double progress = aData->Item(3).GetPercentValue(); double progress = aData->Item(3).GetPercentValue();
@ -193,7 +269,7 @@ ProcessTranslateX(gfx3DMatrix& aMatrix,
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool& aCanStoreInRuleTree, bool& aCanStoreInRuleTree,
nsRect& aBounds) TransformReferenceBox& aRefBox)
{ {
NS_PRECONDITION(aData->Count() == 2, "Invalid array!"); NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
@ -201,7 +277,7 @@ ProcessTranslateX(gfx3DMatrix& aMatrix,
temp.x = ProcessTranslatePart(aData->Item(1), temp.x = ProcessTranslatePart(aData->Item(1),
aContext, aPresContext, aCanStoreInRuleTree, aContext, aPresContext, aCanStoreInRuleTree,
aBounds.Width()); &aRefBox, &TransformReferenceBox::Width);
aMatrix.Translate(temp); aMatrix.Translate(temp);
} }
@ -212,7 +288,7 @@ ProcessTranslateY(gfx3DMatrix& aMatrix,
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool& aCanStoreInRuleTree, bool& aCanStoreInRuleTree,
nsRect& aBounds) TransformReferenceBox& aRefBox)
{ {
NS_PRECONDITION(aData->Count() == 2, "Invalid array!"); NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
@ -220,7 +296,7 @@ ProcessTranslateY(gfx3DMatrix& aMatrix,
temp.y = ProcessTranslatePart(aData->Item(1), temp.y = ProcessTranslatePart(aData->Item(1),
aContext, aPresContext, aCanStoreInRuleTree, aContext, aPresContext, aCanStoreInRuleTree,
aBounds.Height()); &aRefBox, &TransformReferenceBox::Height);
aMatrix.Translate(temp); aMatrix.Translate(temp);
} }
@ -236,7 +312,8 @@ ProcessTranslateZ(gfx3DMatrix& aMatrix,
Point3D temp; Point3D temp;
temp.z = ProcessTranslatePart(aData->Item(1), aContext, temp.z = ProcessTranslatePart(aData->Item(1), aContext,
aPresContext, aCanStoreInRuleTree, 0); aPresContext, aCanStoreInRuleTree,
nullptr);
aMatrix.Translate(temp); aMatrix.Translate(temp);
} }
@ -247,7 +324,7 @@ ProcessTranslate(gfx3DMatrix& aMatrix,
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool& aCanStoreInRuleTree, bool& aCanStoreInRuleTree,
nsRect& aBounds) TransformReferenceBox& aRefBox)
{ {
NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Invalid array!"); NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Invalid array!");
@ -255,13 +332,13 @@ ProcessTranslate(gfx3DMatrix& aMatrix,
temp.x = ProcessTranslatePart(aData->Item(1), temp.x = ProcessTranslatePart(aData->Item(1),
aContext, aPresContext, aCanStoreInRuleTree, aContext, aPresContext, aCanStoreInRuleTree,
aBounds.Width()); &aRefBox, &TransformReferenceBox::Width);
/* If we read in a Y component, set it appropriately */ /* If we read in a Y component, set it appropriately */
if (aData->Count() == 3) { if (aData->Count() == 3) {
temp.y = ProcessTranslatePart(aData->Item(2), temp.y = ProcessTranslatePart(aData->Item(2),
aContext, aPresContext, aCanStoreInRuleTree, aContext, aPresContext, aCanStoreInRuleTree,
aBounds.Height()); &aRefBox, &TransformReferenceBox::Height);
} }
aMatrix.Translate(temp); aMatrix.Translate(temp);
} }
@ -272,7 +349,7 @@ ProcessTranslate3D(gfx3DMatrix& aMatrix,
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool& aCanStoreInRuleTree, bool& aCanStoreInRuleTree,
nsRect& aBounds) TransformReferenceBox& aRefBox)
{ {
NS_PRECONDITION(aData->Count() == 4, "Invalid array!"); NS_PRECONDITION(aData->Count() == 4, "Invalid array!");
@ -280,15 +357,15 @@ ProcessTranslate3D(gfx3DMatrix& aMatrix,
temp.x = ProcessTranslatePart(aData->Item(1), temp.x = ProcessTranslatePart(aData->Item(1),
aContext, aPresContext, aCanStoreInRuleTree, aContext, aPresContext, aCanStoreInRuleTree,
aBounds.Width()); &aRefBox, &TransformReferenceBox::Width);
temp.y = ProcessTranslatePart(aData->Item(2), temp.y = ProcessTranslatePart(aData->Item(2),
aContext, aPresContext, aCanStoreInRuleTree, aContext, aPresContext, aCanStoreInRuleTree,
aBounds.Height()); &aRefBox, &TransformReferenceBox::Height);
temp.z = ProcessTranslatePart(aData->Item(3), temp.z = ProcessTranslatePart(aData->Item(3),
aContext, aPresContext, aCanStoreInRuleTree, aContext, aPresContext, aCanStoreInRuleTree,
0); nullptr);
aMatrix.Translate(temp); aMatrix.Translate(temp);
} }
@ -481,7 +558,7 @@ ProcessPerspective(gfx3DMatrix& aMatrix,
float depth = ProcessTranslatePart(aData->Item(1), aContext, float depth = ProcessTranslatePart(aData->Item(1), aContext,
aPresContext, aCanStoreInRuleTree, aPresContext, aCanStoreInRuleTree,
0); nullptr);
aMatrix.Perspective(depth); aMatrix.Perspective(depth);
} }
@ -496,7 +573,7 @@ MatrixForTransformFunction(gfx3DMatrix& aMatrix,
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool& aCanStoreInRuleTree, bool& aCanStoreInRuleTree,
nsRect& aBounds) TransformReferenceBox& aRefBox)
{ {
NS_PRECONDITION(aData, "Why did you want to get data from a null array?"); NS_PRECONDITION(aData, "Why did you want to get data from a null array?");
// It's OK if aContext and aPresContext are null if the caller already // It's OK if aContext and aPresContext are null if the caller already
@ -508,11 +585,11 @@ MatrixForTransformFunction(gfx3DMatrix& aMatrix,
switch (TransformFunctionOf(aData)) { switch (TransformFunctionOf(aData)) {
case eCSSKeyword_translatex: case eCSSKeyword_translatex:
ProcessTranslateX(aMatrix, aData, aContext, aPresContext, ProcessTranslateX(aMatrix, aData, aContext, aPresContext,
aCanStoreInRuleTree, aBounds); aCanStoreInRuleTree, aRefBox);
break; break;
case eCSSKeyword_translatey: case eCSSKeyword_translatey:
ProcessTranslateY(aMatrix, aData, aContext, aPresContext, ProcessTranslateY(aMatrix, aData, aContext, aPresContext,
aCanStoreInRuleTree, aBounds); aCanStoreInRuleTree, aRefBox);
break; break;
case eCSSKeyword_translatez: case eCSSKeyword_translatez:
ProcessTranslateZ(aMatrix, aData, aContext, aPresContext, ProcessTranslateZ(aMatrix, aData, aContext, aPresContext,
@ -520,11 +597,11 @@ MatrixForTransformFunction(gfx3DMatrix& aMatrix,
break; break;
case eCSSKeyword_translate: case eCSSKeyword_translate:
ProcessTranslate(aMatrix, aData, aContext, aPresContext, ProcessTranslate(aMatrix, aData, aContext, aPresContext,
aCanStoreInRuleTree, aBounds); aCanStoreInRuleTree, aRefBox);
break; break;
case eCSSKeyword_translate3d: case eCSSKeyword_translate3d:
ProcessTranslate3D(aMatrix, aData, aContext, aPresContext, ProcessTranslate3D(aMatrix, aData, aContext, aPresContext,
aCanStoreInRuleTree, aBounds); aCanStoreInRuleTree, aRefBox);
break; break;
case eCSSKeyword_scalex: case eCSSKeyword_scalex:
ProcessScaleX(aMatrix, aData); ProcessScaleX(aMatrix, aData);
@ -565,15 +642,15 @@ MatrixForTransformFunction(gfx3DMatrix& aMatrix,
break; break;
case eCSSKeyword_matrix: case eCSSKeyword_matrix:
ProcessMatrix(aMatrix, aData, aContext, aPresContext, ProcessMatrix(aMatrix, aData, aContext, aPresContext,
aCanStoreInRuleTree, aBounds); aCanStoreInRuleTree, aRefBox);
break; break;
case eCSSKeyword_matrix3d: case eCSSKeyword_matrix3d:
ProcessMatrix3D(aMatrix, aData, aContext, aPresContext, ProcessMatrix3D(aMatrix, aData, aContext, aPresContext,
aCanStoreInRuleTree, aBounds); aCanStoreInRuleTree, aRefBox);
break; break;
case eCSSKeyword_interpolatematrix: case eCSSKeyword_interpolatematrix:
ProcessInterpolateMatrix(aMatrix, aData, aContext, aPresContext, ProcessInterpolateMatrix(aMatrix, aData, aContext, aPresContext,
aCanStoreInRuleTree, aBounds); aCanStoreInRuleTree, aRefBox);
break; break;
case eCSSKeyword_perspective: case eCSSKeyword_perspective:
ProcessPerspective(aMatrix, aData, aContext, aPresContext, ProcessPerspective(aMatrix, aData, aContext, aPresContext,
@ -600,7 +677,7 @@ ReadTransforms(const nsCSSValueList* aList,
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool &aCanStoreInRuleTree, bool &aCanStoreInRuleTree,
nsRect& aBounds, TransformReferenceBox& aRefBox,
float aAppUnitsPerMatrixUnit) float aAppUnitsPerMatrixUnit)
{ {
gfx3DMatrix result; gfx3DMatrix result;
@ -619,7 +696,7 @@ ReadTransforms(const nsCSSValueList* aList,
/* Read in a single transform matrix. */ /* Read in a single transform matrix. */
MatrixForTransformFunction(result, currElem.GetArrayValue(), aContext, MatrixForTransformFunction(result, currElem.GetArrayValue(), aContext,
aPresContext, aCanStoreInRuleTree, aBounds); aPresContext, aCanStoreInRuleTree, aRefBox);
} }
float scale = float(nsPresContext::AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit; float scale = float(nsPresContext::AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit;

Просмотреть файл

@ -13,6 +13,7 @@
#include "nsCSSValue.h" #include "nsCSSValue.h"
#include "gfx3DMatrix.h" #include "gfx3DMatrix.h"
class nsIFrame;
class nsStyleContext; class nsStyleContext;
class nsPresContext; class nsPresContext;
struct nsRect; struct nsRect;
@ -21,7 +22,86 @@ struct nsRect;
* A helper to generate gfxMatrixes from css transform functions. * A helper to generate gfxMatrixes from css transform functions.
*/ */
namespace nsStyleTransformMatrix { namespace nsStyleTransformMatrix {
/**
* This class provides on-demand access to the 'reference box' for CSS
* transforms (needed to resolve percentage values in 'transform',
* 'transform-origin', etc.):
*
* http://dev.w3.org/csswg/css-transforms/#reference-box
*
* This class helps us to avoid calculating the reference box unless and
* until it is actually needed. This is important for performance when
* transforms are applied to SVG elements since the reference box for SVG is
* much more expensive to calculate (than for elements with a CSS layout box
* where we can use the nsIFrame's cached mRect), much more common (than on
* HTML), and yet very rarely have percentage values that require the
* reference box to be resolved. We also don't want to cause SVG frames to
* cache lots of ObjectBoundingBoxProperty objects that aren't needed.
*
* If UNIFIED_CONTINUATIONS (experimental, and currently broke) is defined,
* we consider the reference box for non-SVG frames to be the smallest
* rectangle containing a frame and all of its continuations. For example,
* if there is a <span> element with several continuations split over
* several lines, this function will return the rectangle containing all of
* those continuations. (This behavior is not currently in a spec.)
*/
class MOZ_STACK_CLASS TransformReferenceBox final {
public:
typedef nscoord (TransformReferenceBox::*DimensionGetter)();
explicit TransformReferenceBox()
: mFrame(nullptr)
, mIsCached(false)
{}
explicit TransformReferenceBox(const nsIFrame* aFrame)
: mFrame(aFrame)
, mIsCached(false)
{
MOZ_ASSERT(mFrame);
}
explicit TransformReferenceBox(const nsIFrame* aFrame,
const nsSize& aFallbackDimensions)
{
mFrame = aFrame;
mIsCached = false;
if (!mFrame) {
Init(aFallbackDimensions);
}
}
void Init(const nsIFrame* aFrame) {
MOZ_ASSERT(!mFrame && !mIsCached);
mFrame = aFrame;
}
void Init(const nsSize& aDimensions);
nscoord Width() {
EnsureDimensionsAreCached();
return mWidth;
}
nscoord Height() {
EnsureDimensionsAreCached();
return mHeight;
}
private:
// We don't really need to prevent copying, but since none of our consumers
// currently need to copy, preventing copying may allow us to catch some
// cases where we use pass-by-value instead of pass-by-reference.
TransformReferenceBox(const TransformReferenceBox&) = delete;
void EnsureDimensionsAreCached();
const nsIFrame* mFrame;
nscoord mWidth, mHeight;
bool mIsCached;
};
/** /**
* Return the transform function, as an nsCSSKeyword, for the given * Return the transform function, as an nsCSSKeyword, for the given
* nsCSSValue::Array from a transform list. * nsCSSValue::Array from a transform list.
@ -32,7 +112,8 @@ namespace nsStyleTransformMatrix {
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool& aCanStoreInRuleTree, bool& aCanStoreInRuleTree,
nscoord aSize); TransformReferenceBox* aRefBox,
TransformReferenceBox::DimensionGetter aDimensionGetter = nullptr);
void void
ProcessInterpolateMatrix(gfx3DMatrix& aMatrix, ProcessInterpolateMatrix(gfx3DMatrix& aMatrix,
@ -40,7 +121,7 @@ namespace nsStyleTransformMatrix {
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool& aCanStoreInRuleTree, bool& aCanStoreInRuleTree,
nsRect& aBounds); TransformReferenceBox& aBounds);
/** /**
* Given an nsCSSValueList containing -moz-transform functions, * Given an nsCSSValueList containing -moz-transform functions,
@ -62,7 +143,7 @@ namespace nsStyleTransformMatrix {
nsStyleContext* aContext, nsStyleContext* aContext,
nsPresContext* aPresContext, nsPresContext* aPresContext,
bool &aCanStoreInRuleTree, bool &aCanStoreInRuleTree,
nsRect& aBounds, TransformReferenceBox& aBounds,
float aAppUnitsPerMatrixUnit); float aAppUnitsPerMatrixUnit);
} // namespace nsStyleTransformMatrix } // namespace nsStyleTransformMatrix

Просмотреть файл

@ -298,7 +298,8 @@ function Blocklist() {
gPref.addObserver(PREF_EM_LOGGING_ENABLED, this, false); gPref.addObserver(PREF_EM_LOGGING_ENABLED, this, false);
this.wrappedJSObject = this; this.wrappedJSObject = this;
// requests from child processes come in here, see receiveMessage. // requests from child processes come in here, see receiveMessage.
Services.ppmm.addMessageListener("Blocklist::getPluginBlocklistState", this); Services.ppmm.addMessageListener("Blocklist:getPluginBlocklistState", this);
Services.ppmm.addMessageListener("Blocklist:content-blocklist-updated", this);
} }
Blocklist.prototype = { Blocklist.prototype = {
@ -322,7 +323,8 @@ Blocklist.prototype = {
shutdown: function () { shutdown: function () {
Services.obs.removeObserver(this, "xpcom-shutdown"); Services.obs.removeObserver(this, "xpcom-shutdown");
Services.ppmm.removeMessageListener("Blocklist::getPluginBlocklistState", this); Services.ppmm.removeMessageListener("Blocklist:getPluginBlocklistState", this);
Services.ppmm.removeMessageListener("Blocklist:content-blocklist-updated", this);
gPref.removeObserver("extensions.blocklist.", this); gPref.removeObserver("extensions.blocklist.", this);
gPref.removeObserver(PREF_EM_LOGGING_ENABLED, this); gPref.removeObserver(PREF_EM_LOGGING_ENABLED, this);
}, },
@ -359,10 +361,13 @@ Blocklist.prototype = {
// Message manager message handlers // Message manager message handlers
receiveMessage: function (aMsg) { receiveMessage: function (aMsg) {
switch (aMsg.name) { switch (aMsg.name) {
case "Blocklist::getPluginBlocklistState": case "Blocklist:getPluginBlocklistState":
return this.getPluginBlocklistState(aMsg.data.addonData, return this.getPluginBlocklistState(aMsg.data.addonData,
aMsg.data.appVersion, aMsg.data.appVersion,
aMsg.data.toolkitVersion); aMsg.data.toolkitVersion);
case "Blocklist:content-blocklist-updated":
Services.obs.notifyObservers(null, "content-blocklist-updated", null);
break;
default: default:
throw new Error("Unknown blocklist message received from content: " + aMsg.name); throw new Error("Unknown blocklist message received from content: " + aMsg.name);
} }
@ -1192,6 +1197,11 @@ Blocklist.prototype = {
return blockEntry.infoURL; return blockEntry.infoURL;
}, },
_notifyObserversBlocklistUpdated: function () {
Services.obs.notifyObservers(this, "blocklist-updated", "");
Services.ppmm.broadcastAsyncMessage("Blocklist:blocklistInvalidated", {});
},
_blocklistUpdated: function Blocklist_blocklistUpdated(oldAddonEntries, oldPluginEntries) { _blocklistUpdated: function Blocklist_blocklistUpdated(oldAddonEntries, oldPluginEntries) {
var addonList = []; var addonList = [];
@ -1296,7 +1306,7 @@ Blocklist.prototype = {
} }
if (addonList.length == 0) { if (addonList.length == 0) {
Services.obs.notifyObservers(self, "blocklist-updated", ""); self._notifyObserversBlocklistUpdated();
return; return;
} }
@ -1308,7 +1318,7 @@ Blocklist.prototype = {
} catch (e) { } catch (e) {
LOG(e); LOG(e);
} }
Services.obs.notifyObservers(self, "blocklist-updated", ""); self._notifyObserversBlocklistUpdated();
return; return;
} }
@ -1342,7 +1352,7 @@ Blocklist.prototype = {
if (args.restart) if (args.restart)
restartApp(); restartApp();
Services.obs.notifyObservers(self, "blocklist-updated", ""); self._notifyObserversBlocklistUpdated();
Services.obs.removeObserver(applyBlocklistChanges, "addon-blocklist-closed"); Services.obs.removeObserver(applyBlocklistChanges, "addon-blocklist-closed");
} }

Просмотреть файл

@ -21,19 +21,51 @@ const kMissingAPIMessage = "Unsupported blocklist call in the child process."
*/ */
function Blocklist() { function Blocklist() {
this.init();
} }
Blocklist.prototype = { Blocklist.prototype = {
classID: Components.ID("{e0a106ed-6ad4-47a4-b6af-2f1c8aa4712d}"), classID: Components.ID("{e0a106ed-6ad4-47a4-b6af-2f1c8aa4712d}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIBlocklistService]), QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsIBlocklistService]),
init: function () {
Services.cpmm.addMessageListener("Blocklist:blocklistInvalidated", this);
Services.obs.addObserver(this, "xpcom-shutdown", false);
},
uninit: function () {
Services.cpmm.removeMessageListener("Blocklist:blocklistInvalidated", this);
Services.obs.removeObserver(this, "xpcom-shutdown", false);
},
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case "xpcom-shutdown":
this.uninit();
break;
}
},
// Message manager message handlers
receiveMessage: function (aMsg) {
switch (aMsg.name) {
case "Blocklist:blocklistInvalidated":
Services.obs.notifyObservers(null, "blocklist-updated", null);
Services.cpmm.sendAsyncMessage("Blocklist:content-blocklist-updated");
break;
default:
throw new Error("Unknown blocklist message received from content: " + aMsg.name);
}
},
/* /*
* A helper that queries key data from a plugin or addon object * A helper that queries key data from a plugin or addon object
* and generates a simple data wrapper suitable for ipc. We hand * and generates a simple data wrapper suitable for ipc. We hand
* these directly to the nsBlockListService in the parent which * these directly to the nsBlockListService in the parent which
* doesn't query for much.. allowing us to get away with this. * doesn't query for much.. allowing us to get away with this.
*/ */
flattenObject: function (aTag) { flattenObject: function (aTag) {
// Based on debugging the nsBlocklistService, these are the props the // Based on debugging the nsBlocklistService, these are the props the
// parent side will check on our objects. // parent side will check on our objects.
@ -49,15 +81,16 @@ Blocklist.prototype = {
// only calls getPluginBlocklistState. // only calls getPluginBlocklistState.
isAddonBlocklisted: function (aAddon, aAppVersion, aToolkitVersion) { isAddonBlocklisted: function (aAddon, aAppVersion, aToolkitVersion) {
throw new Error(kMissingAPIMessage); return true;
}, },
getAddonBlocklistState: function (aAddon, aAppVersion, aToolkitVersion) { getAddonBlocklistState: function (aAddon, aAppVersion, aToolkitVersion) {
throw new Error(kMissingAPIMessage); return Components.interfaces.nsIBlocklistService.STATE_BLOCKED;
}, },
// There are a few callers in layout that rely on this.
getPluginBlocklistState: function (aPluginTag, aAppVersion, aToolkitVersion) { getPluginBlocklistState: function (aPluginTag, aAppVersion, aToolkitVersion) {
return Services.cpmm.sendSyncMessage("Blocklist::getPluginBlocklistState", { return Services.cpmm.sendSyncMessage("Blocklist:getPluginBlocklistState", {
addonData: this.flattenObject(aPluginTag), addonData: this.flattenObject(aPluginTag),
appVersion: aAppVersion, appVersion: aAppVersion,
toolkitVersion: aToolkitVersion toolkitVersion: aToolkitVersion

Просмотреть файл

@ -231,7 +231,7 @@ nsPrimitiveHelpers :: ConvertPlatformPlainTextToUnicode ( const char* inText, in
// be reallocated regardless (disposing the old buffer is taken care of internally, see // be reallocated regardless (disposing the old buffer is taken care of internally, see
// the note below). // the note below).
// //
// NOTE: this assumes that it can use nsMemory to dispose of the old buffer. // NOTE: this assumes that it can use 'free' to dispose of the old buffer.
// //
nsresult nsresult
nsLinebreakHelpers :: ConvertPlatformToDOMLinebreaks ( const char* inFlavor, void** ioData, nsLinebreakHelpers :: ConvertPlatformToDOMLinebreaks ( const char* inFlavor, void** ioData,

Просмотреть файл

@ -56,7 +56,7 @@ public:
// be reallocated regardless (disposing the old buffer is taken care of internally, see // be reallocated regardless (disposing the old buffer is taken care of internally, see
// the note below). // the note below).
// //
// NOTE: this assumes that it can use nsMemory to dispose of the old buffer. // NOTE: this assumes that it can use 'free' to dispose of the old buffer.
static nsresult ConvertPlatformToDOMLinebreaks ( const char* inFlavor, void** ioData, int32_t* ioLengthInBytes ) ; static nsresult ConvertPlatformToDOMLinebreaks ( const char* inFlavor, void** ioData, int32_t* ioLengthInBytes ) ;
}; // class nsLinebreakHelpers }; // class nsLinebreakHelpers