mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-12 06:08:24 +02:00
This reduces the amount of places where we need to specify the mozilla/frame-script environment. It does have the side effect of allowing those globals in the whole file, but that is what specifying the environment would do, and this is also for mochitest test files only. MozReview-Commit-ID: 1LLFbn6fFJR --HG-- extra : rebase_source : 82a6934d90bbbbd25f91b7b06bf4f9354e38865a
858 lines
26 KiB
JavaScript
858 lines
26 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() {
|
|
executeSoon(function() {
|
|
aCallback(aBrowserWin)
|
|
});
|
|
}, {once: true});
|
|
}
|
|
|
|
function closeToolbarCustomizationUI(aCallback, aBrowserWin) {
|
|
aBrowserWin.gNavToolbox.addEventListener("aftercustomization", function() {
|
|
executeSoon(aCallback);
|
|
}, {once: true});
|
|
|
|
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;
|
|
});
|
|
}
|
|
|
|
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() {
|
|
aCallback(win);
|
|
}, {once: true});
|
|
}
|
|
|
|
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() {
|
|
deferred.resolve(win);
|
|
}, {once: true});
|
|
}
|
|
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(event) {
|
|
didPs = true;
|
|
if (didZoom)
|
|
resolve();
|
|
}, {capture: true, once: 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");
|
|
}
|
|
|
|
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(event) {
|
|
deferred.resolve();
|
|
}, {once: true});
|
|
|
|
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();
|
|
},
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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* 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);
|
|
});
|
|
|
|
let loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
|
|
yield BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url);
|
|
yield loaded;
|
|
|
|
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;
|
|
}
|