fune/browser/base/content/test/general/head.js
Jared Wein 43bac26d30 Bug 1331661 - Enable the 'quotes' rule for eslint and fix most of the errors with --fix. r=Gijs
MozReview-Commit-ID: 6tv0Z06CO4a

--HG--
extra : rebase_source : 014c0b04d8538dc5f15bc6dd4ed6bd220c55c5d4
2017-01-17 09:48:17 -06:00

1089 lines
36 KiB
JavaScript

Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
"resource://testing-common/PlacesTestUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TabCrashHandler",
"resource:///modules/ContentCrashHandlers.jsm");
/**
* Wait for a <notification> to be closed then call the specified callback.
*/
function waitForNotificationClose(notification, cb) {
let parent = notification.parentNode;
let observer = new MutationObserver(function onMutatations(mutations) {
for (let mutation of mutations) {
for (let i = 0; i < mutation.removedNodes.length; i++) {
let node = mutation.removedNodes.item(i);
if (node != notification) {
continue;
}
observer.disconnect();
cb();
}
}
});
observer.observe(parent, {childList: true});
}
function closeAllNotifications() {
let notificationBox = document.getElementById("global-notificationbox");
if (!notificationBox || !notificationBox.currentNotification) {
return Promise.resolve();
}
let deferred = Promise.defer();
for (let notification of notificationBox.allNotifications) {
waitForNotificationClose(notification, function() {
if (notificationBox.allNotifications.length === 0) {
deferred.resolve();
}
});
notification.close();
}
return deferred.promise;
}
function whenDelayedStartupFinished(aWindow, aCallback) {
Services.obs.addObserver(function observer(aSubject, aTopic) {
if (aWindow == aSubject) {
Services.obs.removeObserver(observer, aTopic);
executeSoon(aCallback);
}
}, "browser-delayed-startup-finished", false);
}
function updateTabContextMenu(tab, onOpened) {
let menu = document.getElementById("tabContextMenu");
if (!tab)
tab = gBrowser.selectedTab;
var evt = new Event("");
tab.dispatchEvent(evt);
menu.openPopup(tab, "end_after", 0, 0, true, false, evt);
is(TabContextMenu.contextTab, tab, "TabContextMenu context is the expected tab");
const onFinished = () => menu.hidePopup();
if (onOpened) {
return Task.spawn(function*() {
yield onOpened();
onFinished();
});
}
onFinished();
return Promise.resolve();
}
function openToolbarCustomizationUI(aCallback, aBrowserWin) {
if (!aBrowserWin)
aBrowserWin = window;
aBrowserWin.gCustomizeMode.enter();
aBrowserWin.gNavToolbox.addEventListener("customizationready", function UI_loaded() {
aBrowserWin.gNavToolbox.removeEventListener("customizationready", UI_loaded);
executeSoon(function() {
aCallback(aBrowserWin)
});
});
}
function closeToolbarCustomizationUI(aCallback, aBrowserWin) {
aBrowserWin.gNavToolbox.addEventListener("aftercustomization", function unloaded() {
aBrowserWin.gNavToolbox.removeEventListener("aftercustomization", unloaded);
executeSoon(aCallback);
});
aBrowserWin.gCustomizeMode.exit();
}
function waitForCondition(condition, nextTest, errorMsg, retryTimes) {
retryTimes = typeof retryTimes !== "undefined" ? retryTimes : 30;
var tries = 0;
var interval = setInterval(function() {
if (tries >= retryTimes) {
ok(false, errorMsg);
moveOn();
}
var conditionPassed;
try {
conditionPassed = condition();
} catch (e) {
ok(false, e + "\n" + e.stack);
conditionPassed = false;
}
if (conditionPassed) {
moveOn();
}
tries++;
}, 100);
var moveOn = function() { clearInterval(interval); nextTest(); };
}
function promiseWaitForCondition(aConditionFn) {
let deferred = Promise.defer();
waitForCondition(aConditionFn, deferred.resolve, "Condition didn't pass.");
return deferred.promise;
}
function promiseWaitForEvent(object, eventName, capturing = false, chrome = false) {
return new Promise((resolve) => {
function listener(event) {
info("Saw " + eventName);
object.removeEventListener(eventName, listener, capturing, chrome);
resolve(event);
}
info("Waiting for " + eventName);
object.addEventListener(eventName, listener, capturing, chrome);
});
}
/**
* 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);
});
}
function getTestPlugin(aName) {
var pluginName = aName || "Test Plug-in";
var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
var tags = ph.getPluginTags();
// Find the test plugin
for (var i = 0; i < tags.length; i++) {
if (tags[i].name == pluginName)
return tags[i];
}
ok(false, "Unable to find plugin");
return null;
}
// call this to set the test plugin(s) initially expected enabled state.
// it will automatically be reset to it's previous value after the test
// ends
function setTestPluginEnabledState(newEnabledState, pluginName) {
var plugin = getTestPlugin(pluginName);
var oldEnabledState = plugin.enabledState;
plugin.enabledState = newEnabledState;
SimpleTest.registerCleanupFunction(function() {
getTestPlugin(pluginName).enabledState = oldEnabledState;
});
}
// after a test is done using the plugin doorhanger, we should just clear
// any permissions that may have crept in
function clearAllPluginPermissions() {
clearAllPermissionsByPrefix("plugin");
}
function clearAllPermissionsByPrefix(aPrefix) {
let perms = Services.perms.enumerator;
while (perms.hasMoreElements()) {
let perm = perms.getNext();
if (perm.type.startsWith(aPrefix)) {
Services.perms.removePermission(perm);
}
}
}
function pushPrefs(...aPrefs) {
let deferred = Promise.defer();
SpecialPowers.pushPrefEnv({"set": aPrefs}, deferred.resolve);
return deferred.promise;
}
function updateBlocklist(aCallback) {
var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"]
.getService(Ci.nsITimerCallback);
var observer = function() {
Services.obs.removeObserver(observer, "blocklist-updated");
SimpleTest.executeSoon(aCallback);
};
Services.obs.addObserver(observer, "blocklist-updated", false);
blocklistNotifier.notify(null);
}
var _originalTestBlocklistURL = null;
function setAndUpdateBlocklist(aURL, aCallback) {
if (!_originalTestBlocklistURL)
_originalTestBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url");
Services.prefs.setCharPref("extensions.blocklist.url", aURL);
updateBlocklist(aCallback);
}
function resetBlocklist() {
Services.prefs.setCharPref("extensions.blocklist.url", _originalTestBlocklistURL);
}
function whenNewWindowLoaded(aOptions, aCallback) {
let win = OpenBrowserWindow(aOptions);
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad);
aCallback(win);
});
}
function promiseWindowWillBeClosed(win) {
return new Promise((resolve, reject) => {
Services.obs.addObserver(function observe(subject, topic) {
if (subject == win) {
Services.obs.removeObserver(observe, topic);
resolve();
}
}, "domwindowclosed", false);
});
}
function promiseWindowClosed(win) {
let promise = promiseWindowWillBeClosed(win);
win.close();
return promise;
}
function promiseOpenAndLoadWindow(aOptions, aWaitForDelayedStartup = false) {
let deferred = Promise.defer();
let win = OpenBrowserWindow(aOptions);
if (aWaitForDelayedStartup) {
Services.obs.addObserver(function onDS(aSubject, aTopic, aData) {
if (aSubject != win) {
return;
}
Services.obs.removeObserver(onDS, "browser-delayed-startup-finished");
deferred.resolve(win);
}, "browser-delayed-startup-finished", false);
} else {
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad);
deferred.resolve(win);
});
}
return deferred.promise;
}
/**
* Waits for all pending async statements on the default connection, before
* proceeding with aCallback.
*
* @param aCallback
* Function to be called when done.
* @param aScope
* Scope for the callback.
* @param aArguments
* Arguments array for the callback.
*
* @note The result is achieved by asynchronously executing a query requiring
* a write lock. Since all statements on the same connection are
* serialized, the end of this write operation means that all writes are
* complete. Note that WAL makes so that writers don't block readers, but
* this is a problem only across different connections.
*/
function waitForAsyncUpdates(aCallback, aScope, aArguments) {
let scope = aScope || this;
let args = aArguments || [];
let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
.DBConnection;
let begin = db.createAsyncStatement("BEGIN EXCLUSIVE");
begin.executeAsync();
begin.finalize();
let commit = db.createAsyncStatement("COMMIT");
commit.executeAsync({
handleResult() {},
handleError() {},
handleCompletion(aReason) {
aCallback.apply(scope, args);
}
});
commit.finalize();
}
/**
* Asynchronously check a url is visited.
* @param aURI The URI.
* @param aExpectedValue The expected value.
* @return {Promise}
* @resolves When the check has been added successfully.
* @rejects JavaScript exception.
*/
function promiseIsURIVisited(aURI, aExpectedValue) {
let deferred = Promise.defer();
PlacesUtils.asyncHistory.isURIVisited(aURI, function(unused, aIsVisited) {
deferred.resolve(aIsVisited);
});
return deferred.promise;
}
function whenNewTabLoaded(aWindow, aCallback) {
aWindow.BrowserOpenTab();
let browser = aWindow.gBrowser.selectedBrowser;
if (browser.contentDocument.readyState === "complete") {
aCallback();
return;
}
whenTabLoaded(aWindow.gBrowser.selectedTab, aCallback);
}
function whenTabLoaded(aTab, aCallback) {
promiseTabLoadEvent(aTab).then(aCallback);
}
function promiseTabLoaded(aTab) {
let deferred = Promise.defer();
whenTabLoaded(aTab, deferred.resolve);
return deferred.promise;
}
/**
* Ensures that the specified URIs are either cleared or not.
*
* @param aURIs
* Array of page URIs
* @param aShouldBeCleared
* True if each visit to the URI should be cleared, false otherwise
*/
function promiseHistoryClearedState(aURIs, aShouldBeCleared) {
let deferred = Promise.defer();
let callbackCount = 0;
let niceStr = aShouldBeCleared ? "no longer" : "still";
function callbackDone() {
if (++callbackCount == aURIs.length)
deferred.resolve();
}
aURIs.forEach(function(aURI) {
PlacesUtils.asyncHistory.isURIVisited(aURI, function(uri, isVisited) {
is(isVisited, !aShouldBeCleared,
"history visit " + uri.spec + " should " + niceStr + " exist");
callbackDone();
});
});
return deferred.promise;
}
/**
* Waits for the next top-level document load in the current browser. The URI
* of the document is compared against aExpectedURL. The load is then stopped
* before it actually starts.
*
* @param aExpectedURL
* The URL of the document that is expected to load.
* @param aStopFromProgressListener
* Whether to cancel the load directly from the progress listener. Defaults to true.
* If you're using this method to avoid hitting the network, you want the default (true).
* However, the browser UI will behave differently for loads stopped directly from
* the progress listener (effectively in the middle of a call to loadURI) and so there
* are cases where you may want to avoid stopping the load directly from within the
* progress listener callback.
* @return promise
*/
function waitForDocLoadAndStopIt(aExpectedURL, aBrowser = gBrowser.selectedBrowser, aStopFromProgressListener = true) {
function content_script(contentStopFromProgressListener) {
let { interfaces: Ci, utils: Cu } = Components;
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
let wp = docShell.QueryInterface(Ci.nsIWebProgress);
function stopContent(now, uri) {
if (now) {
/* Hammer time. */
content.stop();
/* Let the parent know we're done. */
sendAsyncMessage("Test:WaitForDocLoadAndStopIt", { uri });
} else {
setTimeout(stopContent.bind(null, true, uri), 0);
}
}
let progressListener = {
onStateChange(webProgress, req, flags, status) {
dump("waitForDocLoadAndStopIt: onStateChange " + flags.toString(16) + ": " + req.name + "\n");
if (webProgress.isTopLevel &&
flags & Ci.nsIWebProgressListener.STATE_START) {
wp.removeProgressListener(progressListener);
let chan = req.QueryInterface(Ci.nsIChannel);
dump(`waitForDocLoadAndStopIt: Document start: ${chan.URI.spec}\n`);
stopContent(contentStopFromProgressListener, chan.originalURI.spec);
}
},
QueryInterface: XPCOMUtils.generateQI(["nsISupportsWeakReference"])
};
wp.addProgressListener(progressListener, wp.NOTIFY_STATE_WINDOW);
/**
* As |this| is undefined and we can't extend |docShell|, adding an unload
* event handler is the easiest way to ensure the weakly referenced
* progress listener is kept alive as long as necessary.
*/
addEventListener("unload", function() {
try {
wp.removeProgressListener(progressListener);
} catch (e) { /* Will most likely fail. */ }
});
}
return new Promise((resolve, reject) => {
function complete({ data }) {
is(data.uri, aExpectedURL, "waitForDocLoadAndStopIt: The expected URL was loaded");
mm.removeMessageListener("Test:WaitForDocLoadAndStopIt", complete);
resolve();
}
let mm = aBrowser.messageManager;
mm.loadFrameScript("data:,(" + content_script.toString() + ")(" + aStopFromProgressListener + ");", true);
mm.addMessageListener("Test:WaitForDocLoadAndStopIt", complete);
info("waitForDocLoadAndStopIt: Waiting for URL: " + aExpectedURL);
});
}
/**
* Waits for the next load to complete in any browser or the given browser.
* If a <tabbrowser> is given it waits for a load in any of its browsers.
*
* @return promise
*/
function waitForDocLoadComplete(aBrowser = gBrowser) {
return new Promise(resolve => {
let listener = {
onStateChange(webProgress, req, flags, status) {
let docStop = Ci.nsIWebProgressListener.STATE_IS_NETWORK |
Ci.nsIWebProgressListener.STATE_STOP;
info("Saw state " + flags.toString(16) + " and status " + status.toString(16));
// When a load needs to be retargetted to a new process it is cancelled
// with NS_BINDING_ABORTED so ignore that case
if ((flags & docStop) == docStop && status != Cr.NS_BINDING_ABORTED) {
aBrowser.removeProgressListener(this);
waitForDocLoadComplete.listeners.delete(this);
let chan = req.QueryInterface(Ci.nsIChannel);
info("Browser loaded " + chan.originalURI.spec);
resolve();
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference])
};
aBrowser.addProgressListener(listener);
waitForDocLoadComplete.listeners.add(listener);
info("Waiting for browser load");
});
}
// Keep a set of progress listeners for waitForDocLoadComplete() to make sure
// they're not GC'ed before we saw the page load.
waitForDocLoadComplete.listeners = new Set();
registerCleanupFunction(() => waitForDocLoadComplete.listeners.clear());
var FullZoomHelper = {
selectTabAndWaitForLocationChange: function selectTabAndWaitForLocationChange(tab) {
if (!tab)
throw new Error("tab must be given.");
if (gBrowser.selectedTab == tab)
return Promise.resolve();
return Promise.all([BrowserTestUtils.switchTab(gBrowser, tab),
this.waitForLocationChange()]);
},
removeTabAndWaitForLocationChange: function removeTabAndWaitForLocationChange(tab) {
tab = tab || gBrowser.selectedTab;
let selected = gBrowser.selectedTab == tab;
gBrowser.removeTab(tab);
if (selected)
return this.waitForLocationChange();
return Promise.resolve();
},
waitForLocationChange: function waitForLocationChange() {
return new Promise(resolve => {
Services.obs.addObserver(function obs(subj, topic, data) {
Services.obs.removeObserver(obs, topic);
resolve();
}, "browser-fullZoom:location-change", false);
});
},
load: function load(tab, url) {
return new Promise(resolve => {
let didLoad = false;
let didZoom = false;
promiseTabLoadEvent(tab).then(event => {
didLoad = true;
if (didZoom)
resolve();
}, true);
this.waitForLocationChange().then(function() {
didZoom = true;
if (didLoad)
resolve();
});
tab.linkedBrowser.loadURI(url);
});
},
zoomTest: function zoomTest(tab, val, msg) {
is(ZoomManager.getZoomForBrowser(tab.linkedBrowser), val, msg);
},
enlarge: function enlarge() {
return new Promise(resolve => FullZoom.enlarge(resolve));
},
reduce: function reduce() {
return new Promise(resolve => FullZoom.reduce(resolve));
},
reset: function reset() {
return FullZoom.reset();
},
BACK: 0,
FORWARD: 1,
navigate: function navigate(direction) {
return new Promise(resolve => {
let didPs = false;
let didZoom = false;
gBrowser.addEventListener("pageshow", function listener(event) {
gBrowser.removeEventListener("pageshow", listener, true);
didPs = true;
if (didZoom)
resolve();
}, true);
if (direction == this.BACK)
gBrowser.goBack();
else if (direction == this.FORWARD)
gBrowser.goForward();
this.waitForLocationChange().then(function() {
didZoom = true;
if (didPs)
resolve();
});
});
},
failAndContinue: function failAndContinue(func) {
return function(err) {
ok(false, err);
func();
};
},
};
/**
* 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.
* @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) {
info("Wait tab event: load");
function handle(loadedUrl) {
if (loadedUrl === "about:blank" || (url && loadedUrl !== url)) {
info(`Skipping spurious load event for ${loadedUrl}`);
return false;
}
info("Tab event received: load");
return true;
}
let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle);
if (url)
BrowserTestUtils.loadURI(tab.linkedBrowser, url);
return loaded;
}
/**
* Returns a Promise that resolves once a new tab has been opened in
* a xul:tabbrowser.
*
* @param aTabBrowser
* The xul:tabbrowser to monitor for a new tab.
* @return {Promise}
* Resolved when the new tab has been opened.
* @resolves to the TabOpen event that was fired.
* @rejects Never.
*/
function waitForNewTabEvent(aTabBrowser) {
return promiseWaitForEvent(aTabBrowser.tabContainer, "TabOpen");
}
/**
* Test the state of the identity box and control center to make
* sure they are correctly showing the expected mixed content states.
*
* @note The checks are done synchronously, but new code should wait on the
* returned Promise object to ensure the identity panel has closed.
* Bug 1221114 is filed to fix the existing code.
*
* @param tabbrowser
* @param Object states
* MUST include the following properties:
* {
* activeLoaded: true|false,
* activeBlocked: true|false,
* passiveLoaded: true|false,
* }
*
* @return {Promise}
* @resolves When the operation has finished and the identity panel has closed.
*/
function assertMixedContentBlockingState(tabbrowser, states = {}) {
if (!tabbrowser || !("activeLoaded" in states) ||
!("activeBlocked" in states) || !("passiveLoaded" in states)) {
throw new Error("assertMixedContentBlockingState requires a browser and a states object");
}
let {passiveLoaded, activeLoaded, activeBlocked} = states;
let {gIdentityHandler} = tabbrowser.ownerGlobal;
let doc = tabbrowser.ownerDocument;
let identityBox = gIdentityHandler._identityBox;
let classList = identityBox.classList;
let connectionIcon = doc.getElementById("connection-icon");
let connectionIconImage = tabbrowser.ownerGlobal.getComputedStyle(connectionIcon).
getPropertyValue("list-style-image");
let stateSecure = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_SECURE;
let stateBroken = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
let stateInsecure = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_INSECURE;
let stateActiveBlocked = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
let stateActiveLoaded = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT;
let statePassiveLoaded = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT;
is(activeBlocked, !!stateActiveBlocked, "Expected state for activeBlocked matches UI state");
is(activeLoaded, !!stateActiveLoaded, "Expected state for activeLoaded matches UI state");
is(passiveLoaded, !!statePassiveLoaded, "Expected state for passiveLoaded matches UI state");
if (stateInsecure) {
// HTTP request, there should be no MCB classes for the identity box and the non secure icon
// should always be visible regardless of MCB state.
ok(classList.contains("unknownIdentity"), "unknownIdentity on HTTP page");
is_element_hidden(connectionIcon);
ok(!classList.contains("mixedActiveContent"), "No MCB icon on HTTP page");
ok(!classList.contains("mixedActiveBlocked"), "No MCB icon on HTTP page");
ok(!classList.contains("mixedDisplayContent"), "No MCB icon on HTTP page");
ok(!classList.contains("mixedDisplayContentLoadedActiveBlocked"), "No MCB icon on HTTP page");
} else {
// Make sure the identity box UI has the correct mixedcontent states and icons
is(classList.contains("mixedActiveContent"), activeLoaded,
"identityBox has expected class for activeLoaded");
is(classList.contains("mixedActiveBlocked"), activeBlocked && !passiveLoaded,
"identityBox has expected class for activeBlocked && !passiveLoaded");
is(classList.contains("mixedDisplayContent"), passiveLoaded && !(activeLoaded || activeBlocked),
"identityBox has expected class for passiveLoaded && !(activeLoaded || activeBlocked)");
is(classList.contains("mixedDisplayContentLoadedActiveBlocked"), passiveLoaded && activeBlocked,
"identityBox has expected class for passiveLoaded && activeBlocked");
is_element_visible(connectionIcon);
if (activeLoaded) {
is(connectionIconImage, "url(\"chrome://browser/skin/connection-mixed-active-loaded.svg#icon\")",
"Using active loaded icon");
}
if (activeBlocked && !passiveLoaded) {
is(connectionIconImage, "url(\"chrome://browser/skin/connection-secure.svg\")",
"Using active blocked icon");
}
if (passiveLoaded && !(activeLoaded || activeBlocked)) {
is(connectionIconImage, "url(\"chrome://browser/skin/connection-mixed-passive-loaded.svg#icon\")",
"Using passive loaded icon");
}
if (passiveLoaded && activeBlocked) {
is(connectionIconImage, "url(\"chrome://browser/skin/connection-mixed-passive-loaded.svg#icon\")",
"Using active blocked and passive loaded icon");
}
}
// Make sure the identity popup has the correct mixedcontent states
gIdentityHandler._identityBox.click();
let popupAttr = doc.getElementById("identity-popup").getAttribute("mixedcontent");
let bodyAttr = doc.getElementById("identity-popup-securityView-body").getAttribute("mixedcontent");
is(popupAttr.includes("active-loaded"), activeLoaded,
"identity-popup has expected attr for activeLoaded");
is(bodyAttr.includes("active-loaded"), activeLoaded,
"securityView-body has expected attr for activeLoaded");
is(popupAttr.includes("active-blocked"), activeBlocked,
"identity-popup has expected attr for activeBlocked");
is(bodyAttr.includes("active-blocked"), activeBlocked,
"securityView-body has expected attr for activeBlocked");
is(popupAttr.includes("passive-loaded"), passiveLoaded,
"identity-popup has expected attr for passiveLoaded");
is(bodyAttr.includes("passive-loaded"), passiveLoaded,
"securityView-body has expected attr for passiveLoaded");
// Make sure the correct icon is visible in the Control Center.
// This logic is controlled with CSS, so this helps prevent regressions there.
let securityView = doc.getElementById("identity-popup-securityView");
let securityViewBG = tabbrowser.ownerGlobal.getComputedStyle(securityView).
getPropertyValue("background-image");
let securityContentBG = tabbrowser.ownerGlobal.getComputedStyle(securityView).
getPropertyValue("background-image");
if (stateInsecure) {
is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/conn-not-secure.svg\")",
"CC using 'not secure' icon");
is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/conn-not-secure.svg\")",
"CC using 'not secure' icon");
}
if (stateSecure) {
is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-secure\")",
"CC using secure icon");
is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-secure\")",
"CC using secure icon");
}
if (stateBroken) {
if (activeLoaded) {
is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
"CC using active loaded icon");
is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
"CC using active loaded icon");
} else if (activeBlocked || passiveLoaded) {
is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
"CC using degraded icon");
is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
"CC using degraded icon");
} else {
// There is a case here with weak ciphers, but no bc tests are handling this yet.
is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
"CC using degraded icon");
is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
"CC using degraded icon");
}
}
if (activeLoaded || activeBlocked || passiveLoaded) {
doc.getElementById("identity-popup-security-expander").click();
is(Array.filter(doc.querySelectorAll("[observes=identity-popup-mcb-learn-more]"),
element => !is_hidden(element)).length, 1,
"The 'Learn more' link should be visible once.");
}
gIdentityHandler._identityPopup.hidden = true;
// Wait for the panel to be closed before continuing. The promisePopupHidden
// function cannot be used because it's unreliable unless promisePopupShown is
// also called before closing the panel. This cannot be done until all callers
// are made asynchronous (bug 1221114).
return new Promise(resolve => executeSoon(resolve));
}
function is_hidden(element) {
var style = element.ownerGlobal.getComputedStyle(element);
if (style.display == "none")
return true;
if (style.visibility != "visible")
return true;
if (style.display == "-moz-popup")
return ["hiding", "closed"].indexOf(element.state) != -1;
// Hiding a parent element will hide all its children
if (element.parentNode != element.ownerDocument)
return is_hidden(element.parentNode);
return false;
}
function is_visible(element) {
var style = element.ownerGlobal.getComputedStyle(element);
if (style.display == "none")
return false;
if (style.visibility != "visible")
return false;
if (style.display == "-moz-popup" && element.state != "open")
return false;
// Hiding a parent element will hide all its children
if (element.parentNode != element.ownerDocument)
return is_visible(element.parentNode);
return true;
}
function is_element_visible(element, msg) {
isnot(element, null, "Element should not be null, when checking visibility");
ok(is_visible(element), msg || "Element should be visible");
}
function is_element_hidden(element, msg) {
isnot(element, null, "Element should not be null, when checking visibility");
ok(is_hidden(element), msg || "Element should be hidden");
}
function promisePopupEvent(popup, eventSuffix) {
let endState = {shown: "open", hidden: "closed"}[eventSuffix];
if (popup.state == endState)
return Promise.resolve();
let eventType = "popup" + eventSuffix;
let deferred = Promise.defer();
popup.addEventListener(eventType, function onPopupShown(event) {
popup.removeEventListener(eventType, onPopupShown);
deferred.resolve();
});
return deferred.promise;
}
function promisePopupShown(popup) {
return promisePopupEvent(popup, "shown");
}
function promisePopupHidden(popup) {
return promisePopupEvent(popup, "hidden");
}
function promiseNotificationShown(notification) {
let win = notification.browser.ownerGlobal;
if (win.PopupNotifications.panel.state == "open") {
return Promise.resolve();
}
let panelPromise = promisePopupShown(win.PopupNotifications.panel);
notification.reshow();
return panelPromise;
}
/**
* Allows waiting for an observer notification once.
*
* @param aTopic
* Notification topic to observe.
*
* @return {Promise}
* @resolves An object with subject and data properties from the observed
* notification.
* @rejects Never.
*/
function promiseTopicObserved(aTopic) {
return new Promise((resolve) => {
Services.obs.addObserver(
function PTO_observe(aSubject, aTopic2, aData) {
Services.obs.removeObserver(PTO_observe, aTopic2);
resolve({subject: aSubject, data: aData});
}, aTopic, false);
});
}
function promiseNewSearchEngine(basename) {
return new Promise((resolve, reject) => {
info("Waiting for engine to be added: " + basename);
let url = getRootDirectory(gTestPath) + basename;
Services.search.addEngine(url, null, "", false, {
onSuccess(engine) {
info("Search engine added: " + basename);
registerCleanupFunction(() => Services.search.removeEngine(engine));
resolve(engine);
},
onError(errCode) {
Assert.ok(false, "addEngine failed with error code " + errCode);
reject();
},
});
});
}
// Compares the security state of the page with what is expected
function isSecurityState(expectedState) {
let ui = gTestBrowser.securityUI;
if (!ui) {
ok(false, "No security UI to get the security state");
return;
}
const wpl = Components.interfaces.nsIWebProgressListener;
// determine the security state
let isSecure = ui.state & wpl.STATE_IS_SECURE;
let isBroken = ui.state & wpl.STATE_IS_BROKEN;
let isInsecure = ui.state & wpl.STATE_IS_INSECURE;
let actualState;
if (isSecure && !(isBroken || isInsecure)) {
actualState = "secure";
} else if (isBroken && !(isSecure || isInsecure)) {
actualState = "broken";
} else if (isInsecure && !(isSecure || isBroken)) {
actualState = "insecure";
} else {
actualState = "unknown";
}
is(expectedState, actualState, "Expected state " + expectedState + " and the actual state is " + actualState + ".");
}
/**
* Resolves when a bookmark with the given uri is added.
*/
function promiseOnBookmarkItemAdded(aExpectedURI) {
return new Promise((resolve, reject) => {
let bookmarksObserver = {
onItemAdded(aItemId, aFolderId, aIndex, aItemType, aURI) {
info("Added a bookmark to " + aURI.spec);
PlacesUtils.bookmarks.removeObserver(bookmarksObserver);
if (aURI.equals(aExpectedURI)) {
resolve();
} else {
reject(new Error("Added an unexpected bookmark"));
}
},
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onItemRemoved() {},
onItemChanged() {},
onItemVisited() {},
onItemMoved() {},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsINavBookmarkObserver,
])
};
info("Waiting for a bookmark to be added");
PlacesUtils.bookmarks.addObserver(bookmarksObserver, false);
});
}
function promiseErrorPageLoaded(browser) {
return new Promise(resolve => {
browser.addEventListener("DOMContentLoaded", function onLoad() {
browser.removeEventListener("DOMContentLoaded", onLoad, false, true);
resolve();
}, false, true);
});
}
function* loadBadCertPage(url) {
const EXCEPTION_DIALOG_URI = "chrome://pippki/content/exceptionDialog.xul";
let exceptionDialogResolved = new Promise(function(resolve) {
// When the certificate exception dialog has opened, click the button to add
// an exception.
let certExceptionDialogObserver = {
observe(aSubject, aTopic, aData) {
if (aTopic == "cert-exception-ui-ready") {
Services.obs.removeObserver(this, "cert-exception-ui-ready");
let certExceptionDialog = getCertExceptionDialog(EXCEPTION_DIALOG_URI);
ok(certExceptionDialog, "found exception dialog");
executeSoon(function() {
certExceptionDialog.documentElement.getButton("extra1").click();
resolve();
});
}
}
};
Services.obs.addObserver(certExceptionDialogObserver,
"cert-exception-ui-ready", false);
});
// Sometimes clearing the cert override is not immediately picked up,
// so we reload until we are on an actual cert error page.
yield BrowserTestUtils.waitForCondition(function*() {
yield BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url);
yield promiseErrorPageLoaded(gBrowser.selectedBrowser);
let isErrorPage = yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
return content.document.documentURI.startsWith("about:certerror");
});
return isErrorPage;
}, "Could not load error page", 1000);
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
content.document.getElementById("exceptionDialogButton").click();
});
yield exceptionDialogResolved;
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
}
// Utility function to get a handle on the certificate exception dialog.
// Modified from toolkit/components/passwordmgr/test/prompt_common.js
function getCertExceptionDialog(aLocation) {
let enumerator = Services.wm.getXULWindowEnumerator(null);
while (enumerator.hasMoreElements()) {
let win = enumerator.getNext();
let windowDocShell = win.QueryInterface(Ci.nsIXULWindow).docShell;
let containedDocShells = windowDocShell.getDocShellEnumerator(
Ci.nsIDocShellTreeItem.typeChrome,
Ci.nsIDocShell.ENUMERATE_FORWARDS);
while (containedDocShells.hasMoreElements()) {
// Get the corresponding document for this docshell
let childDocShell = containedDocShells.getNext();
let childDoc = childDocShell.QueryInterface(Ci.nsIDocShell)
.contentViewer
.DOMDocument;
if (childDoc.location.href == aLocation) {
return childDoc;
}
}
}
return undefined;
}
function setupRemoteClientsFixture(fixture) {
let oldRemoteClientsGetter =
Object.getOwnPropertyDescriptor(gFxAccounts, "remoteClients").get;
Object.defineProperty(gFxAccounts, "remoteClients", {
get() { return fixture; }
});
return oldRemoteClientsGetter;
}
function restoreRemoteClients(getter) {
Object.defineProperty(gFxAccounts, "remoteClients", {
get: getter
});
}
function* openMenuItemSubmenu(id) {
let menuPopup = document.getElementById(id).menupopup;
let menuPopupPromise = BrowserTestUtils.waitForEvent(menuPopup, "popupshown");
menuPopup.showPopup();
yield menuPopupPromise;
}