michael@0: Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "Promise", michael@0: "resource://gre/modules/Promise.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "Task", michael@0: "resource://gre/modules/Task.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", michael@0: "resource://gre/modules/PlacesUtils.jsm"); michael@0: michael@0: function whenDelayedStartupFinished(aWindow, aCallback) { michael@0: Services.obs.addObserver(function observer(aSubject, aTopic) { michael@0: if (aWindow == aSubject) { michael@0: Services.obs.removeObserver(observer, aTopic); michael@0: executeSoon(aCallback); michael@0: } michael@0: }, "browser-delayed-startup-finished", false); michael@0: } michael@0: michael@0: function findChromeWindowByURI(aURI) { michael@0: let windows = Services.wm.getEnumerator(null); michael@0: while (windows.hasMoreElements()) { michael@0: let win = windows.getNext(); michael@0: if (win.location.href == aURI) michael@0: return win; michael@0: } michael@0: return null; michael@0: } michael@0: michael@0: function updateTabContextMenu(tab) { michael@0: let menu = document.getElementById("tabContextMenu"); michael@0: if (!tab) michael@0: tab = gBrowser.selectedTab; michael@0: var evt = new Event(""); michael@0: tab.dispatchEvent(evt); michael@0: menu.openPopup(tab, "end_after", 0, 0, true, false, evt); michael@0: is(TabContextMenu.contextTab, tab, "TabContextMenu context is the expected tab"); michael@0: menu.hidePopup(); michael@0: } michael@0: michael@0: function openToolbarCustomizationUI(aCallback, aBrowserWin) { michael@0: if (!aBrowserWin) michael@0: aBrowserWin = window; michael@0: michael@0: aBrowserWin.gCustomizeMode.enter(); michael@0: michael@0: aBrowserWin.gNavToolbox.addEventListener("customizationready", function UI_loaded() { michael@0: aBrowserWin.gNavToolbox.removeEventListener("customizationready", UI_loaded); michael@0: executeSoon(function() { michael@0: aCallback(aBrowserWin) michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: function closeToolbarCustomizationUI(aCallback, aBrowserWin) { michael@0: aBrowserWin.gNavToolbox.addEventListener("aftercustomization", function unloaded() { michael@0: aBrowserWin.gNavToolbox.removeEventListener("aftercustomization", unloaded); michael@0: executeSoon(aCallback); michael@0: }); michael@0: michael@0: aBrowserWin.gCustomizeMode.exit(); michael@0: } michael@0: michael@0: function waitForCondition(condition, nextTest, errorMsg) { michael@0: var tries = 0; michael@0: var interval = setInterval(function() { michael@0: if (tries >= 30) { michael@0: ok(false, errorMsg); michael@0: moveOn(); michael@0: } michael@0: var conditionPassed; michael@0: try { michael@0: conditionPassed = condition(); michael@0: } catch (e) { michael@0: ok(false, e + "\n" + e.stack); michael@0: conditionPassed = false; michael@0: } michael@0: if (conditionPassed) { michael@0: moveOn(); michael@0: } michael@0: tries++; michael@0: }, 100); michael@0: var moveOn = function() { clearInterval(interval); nextTest(); }; michael@0: } michael@0: michael@0: function getTestPlugin(aName) { michael@0: var pluginName = aName || "Test Plug-in"; michael@0: var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); michael@0: var tags = ph.getPluginTags(); michael@0: michael@0: // Find the test plugin michael@0: for (var i = 0; i < tags.length; i++) { michael@0: if (tags[i].name == pluginName) michael@0: return tags[i]; michael@0: } michael@0: ok(false, "Unable to find plugin"); michael@0: return null; michael@0: } michael@0: michael@0: // call this to set the test plugin(s) initially expected enabled state. michael@0: // it will automatically be reset to it's previous value after the test michael@0: // ends michael@0: function setTestPluginEnabledState(newEnabledState, pluginName) { michael@0: var plugin = getTestPlugin(pluginName); michael@0: var oldEnabledState = plugin.enabledState; michael@0: plugin.enabledState = newEnabledState; michael@0: SimpleTest.registerCleanupFunction(function() { michael@0: getTestPlugin(pluginName).enabledState = oldEnabledState; michael@0: }); michael@0: } michael@0: michael@0: // after a test is done using the plugin doorhanger, we should just clear michael@0: // any permissions that may have crept in michael@0: function clearAllPluginPermissions() { michael@0: let perms = Services.perms.enumerator; michael@0: while (perms.hasMoreElements()) { michael@0: let perm = perms.getNext(); michael@0: if (perm.type.startsWith('plugin')) { michael@0: Services.perms.remove(perm.host, perm.type); michael@0: } michael@0: } michael@0: } michael@0: michael@0: function updateBlocklist(aCallback) { michael@0: var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"] michael@0: .getService(Ci.nsITimerCallback); michael@0: var observer = function() { michael@0: Services.obs.removeObserver(observer, "blocklist-updated"); michael@0: SimpleTest.executeSoon(aCallback); michael@0: }; michael@0: Services.obs.addObserver(observer, "blocklist-updated", false); michael@0: blocklistNotifier.notify(null); michael@0: } michael@0: michael@0: var _originalTestBlocklistURL = null; michael@0: function setAndUpdateBlocklist(aURL, aCallback) { michael@0: if (!_originalTestBlocklistURL) michael@0: _originalTestBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url"); michael@0: Services.prefs.setCharPref("extensions.blocklist.url", aURL); michael@0: updateBlocklist(aCallback); michael@0: } michael@0: michael@0: function resetBlocklist() { michael@0: Services.prefs.setCharPref("extensions.blocklist.url", _originalTestBlocklistURL); michael@0: } michael@0: michael@0: function whenNewWindowLoaded(aOptions, aCallback) { michael@0: let win = OpenBrowserWindow(aOptions); michael@0: win.addEventListener("load", function onLoad() { michael@0: win.removeEventListener("load", onLoad, false); michael@0: aCallback(win); michael@0: }, false); michael@0: } michael@0: michael@0: /** michael@0: * Waits for all pending async statements on the default connection, before michael@0: * proceeding with aCallback. michael@0: * michael@0: * @param aCallback michael@0: * Function to be called when done. michael@0: * @param aScope michael@0: * Scope for the callback. michael@0: * @param aArguments michael@0: * Arguments array for the callback. michael@0: * michael@0: * @note The result is achieved by asynchronously executing a query requiring michael@0: * a write lock. Since all statements on the same connection are michael@0: * serialized, the end of this write operation means that all writes are michael@0: * complete. Note that WAL makes so that writers don't block readers, but michael@0: * this is a problem only across different connections. michael@0: */ michael@0: function waitForAsyncUpdates(aCallback, aScope, aArguments) { michael@0: let scope = aScope || this; michael@0: let args = aArguments || []; michael@0: let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase) michael@0: .DBConnection; michael@0: let begin = db.createAsyncStatement("BEGIN EXCLUSIVE"); michael@0: begin.executeAsync(); michael@0: begin.finalize(); michael@0: michael@0: let commit = db.createAsyncStatement("COMMIT"); michael@0: commit.executeAsync({ michael@0: handleResult: function() {}, michael@0: handleError: function() {}, michael@0: handleCompletion: function(aReason) { michael@0: aCallback.apply(scope, args); michael@0: } michael@0: }); michael@0: commit.finalize(); michael@0: } michael@0: michael@0: /** michael@0: * Asynchronously check a url is visited. michael@0: michael@0: * @param aURI The URI. michael@0: * @param aExpectedValue The expected value. michael@0: * @return {Promise} michael@0: * @resolves When the check has been added successfully. michael@0: * @rejects JavaScript exception. michael@0: */ michael@0: function promiseIsURIVisited(aURI, aExpectedValue) { michael@0: let deferred = Promise.defer(); michael@0: PlacesUtils.asyncHistory.isURIVisited(aURI, function(aURI, aIsVisited) { michael@0: deferred.resolve(aIsVisited); michael@0: }); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: function whenNewTabLoaded(aWindow, aCallback) { michael@0: aWindow.BrowserOpenTab(); michael@0: michael@0: let browser = aWindow.gBrowser.selectedBrowser; michael@0: if (browser.contentDocument.readyState === "complete") { michael@0: aCallback(); michael@0: return; michael@0: } michael@0: michael@0: whenTabLoaded(aWindow.gBrowser.selectedTab, aCallback); michael@0: } michael@0: michael@0: function whenTabLoaded(aTab, aCallback) { michael@0: let browser = aTab.linkedBrowser; michael@0: browser.addEventListener("load", function onLoad() { michael@0: browser.removeEventListener("load", onLoad, true); michael@0: executeSoon(aCallback); michael@0: }, true); michael@0: } michael@0: michael@0: function addVisits(aPlaceInfo, aCallback) { michael@0: let places = []; michael@0: if (aPlaceInfo instanceof Ci.nsIURI) { michael@0: places.push({ uri: aPlaceInfo }); michael@0: } else if (Array.isArray(aPlaceInfo)) { michael@0: places = places.concat(aPlaceInfo); michael@0: } else { michael@0: places.push(aPlaceInfo); michael@0: } michael@0: michael@0: // Create mozIVisitInfo for each entry. michael@0: let now = Date.now(); michael@0: for (let i = 0; i < places.length; i++) { michael@0: if (!places[i].title) { michael@0: places[i].title = "test visit for " + places[i].uri.spec; michael@0: } michael@0: places[i].visits = [{ michael@0: transitionType: places[i].transition === undefined ? Ci.nsINavHistoryService.TRANSITION_LINK michael@0: : places[i].transition, michael@0: visitDate: places[i].visitDate || (now++) * 1000, michael@0: referrerURI: places[i].referrer michael@0: }]; michael@0: } michael@0: michael@0: PlacesUtils.asyncHistory.updatePlaces( michael@0: places, michael@0: { michael@0: handleError: function AAV_handleError() { michael@0: throw("Unexpected error in adding visit."); michael@0: }, michael@0: handleResult: function () {}, michael@0: handleCompletion: function UP_handleCompletion() { michael@0: if (aCallback) michael@0: aCallback(); michael@0: } michael@0: } michael@0: ); michael@0: } michael@0: michael@0: /** michael@0: * Ensures that the specified URIs are either cleared or not. michael@0: * michael@0: * @param aURIs michael@0: * Array of page URIs michael@0: * @param aShouldBeCleared michael@0: * True if each visit to the URI should be cleared, false otherwise michael@0: */ michael@0: function promiseHistoryClearedState(aURIs, aShouldBeCleared) { michael@0: let deferred = Promise.defer(); michael@0: let callbackCount = 0; michael@0: let niceStr = aShouldBeCleared ? "no longer" : "still"; michael@0: function callbackDone() { michael@0: if (++callbackCount == aURIs.length) michael@0: deferred.resolve(); michael@0: } michael@0: aURIs.forEach(function (aURI) { michael@0: PlacesUtils.asyncHistory.isURIVisited(aURI, function(aURI, aIsVisited) { michael@0: is(aIsVisited, !aShouldBeCleared, michael@0: "history visit " + aURI.spec + " should " + niceStr + " exist"); michael@0: callbackDone(); michael@0: }); michael@0: }); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Allows waiting for an observer notification once. michael@0: * michael@0: * @param topic michael@0: * Notification topic to observe. michael@0: * michael@0: * @return {Promise} michael@0: * @resolves The array [subject, data] from the observed notification. michael@0: * @rejects Never. michael@0: */ michael@0: function promiseTopicObserved(topic) michael@0: { michael@0: let deferred = Promise.defer(); michael@0: Services.obs.addObserver(function PTO_observe(subject, topic, data) { michael@0: Services.obs.removeObserver(PTO_observe, topic); michael@0: deferred.resolve([subject, data]); michael@0: }, topic, false); michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Clears history asynchronously. michael@0: * michael@0: * @return {Promise} michael@0: * @resolves When history has been cleared. michael@0: * @rejects Never. michael@0: */ michael@0: function promiseClearHistory() { michael@0: let promise = promiseTopicObserved(PlacesUtils.TOPIC_EXPIRATION_FINISHED); michael@0: PlacesUtils.bhistory.removeAllPages(); michael@0: return promise; michael@0: } michael@0: michael@0: /** michael@0: * Waits for the next top-level document load in the current browser. The URI michael@0: * of the document is compared against aExpectedURL. The load is then stopped michael@0: * before it actually starts. michael@0: * michael@0: * @param aExpectedURL michael@0: * The URL of the document that is expected to load. michael@0: * @return promise michael@0: */ michael@0: function waitForDocLoadAndStopIt(aExpectedURL) { michael@0: let deferred = Promise.defer(); michael@0: let progressListener = { michael@0: onStateChange: function (webProgress, req, flags, status) { michael@0: info("waitForDocLoadAndStopIt: onStateChange: " + req.name); michael@0: let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT | michael@0: Ci.nsIWebProgressListener.STATE_START; michael@0: if ((flags & docStart) && webProgress.isTopLevel) { michael@0: info("waitForDocLoadAndStopIt: Document start: " + michael@0: req.QueryInterface(Ci.nsIChannel).URI.spec); michael@0: is(req.originalURI.spec, aExpectedURL, michael@0: "waitForDocLoadAndStopIt: The expected URL was loaded"); michael@0: req.cancel(Components.results.NS_ERROR_FAILURE); michael@0: gBrowser.removeProgressListener(progressListener); michael@0: deferred.resolve(); michael@0: } michael@0: }, michael@0: }; michael@0: gBrowser.addProgressListener(progressListener); michael@0: info("waitForDocLoadAndStopIt: Waiting for URL: " + aExpectedURL); michael@0: return deferred.promise; michael@0: } michael@0: michael@0: let FullZoomHelper = { michael@0: michael@0: selectTabAndWaitForLocationChange: function selectTabAndWaitForLocationChange(tab) { michael@0: if (!tab) michael@0: throw new Error("tab must be given."); michael@0: if (gBrowser.selectedTab == tab) michael@0: return Promise.resolve(); michael@0: gBrowser.selectedTab = tab; michael@0: return this.waitForLocationChange(); michael@0: }, michael@0: michael@0: removeTabAndWaitForLocationChange: function removeTabAndWaitForLocationChange(tab) { michael@0: tab = tab || gBrowser.selectedTab; michael@0: let selected = gBrowser.selectedTab == tab; michael@0: gBrowser.removeTab(tab); michael@0: if (selected) michael@0: return this.waitForLocationChange(); michael@0: return Promise.resolve(); michael@0: }, michael@0: michael@0: waitForLocationChange: function waitForLocationChange() { michael@0: let deferred = Promise.defer(); michael@0: Services.obs.addObserver(function obs(subj, topic, data) { michael@0: Services.obs.removeObserver(obs, topic); michael@0: deferred.resolve(); michael@0: }, "browser-fullZoom:location-change", false); michael@0: return deferred.promise; michael@0: }, michael@0: michael@0: load: function load(tab, url) { michael@0: let deferred = Promise.defer(); michael@0: let didLoad = false; michael@0: let didZoom = false; michael@0: michael@0: tab.linkedBrowser.addEventListener("load", function (event) { michael@0: event.currentTarget.removeEventListener("load", arguments.callee, true); michael@0: didLoad = true; michael@0: if (didZoom) michael@0: deferred.resolve(); michael@0: }, true); michael@0: michael@0: this.waitForLocationChange().then(function () { michael@0: didZoom = true; michael@0: if (didLoad) michael@0: deferred.resolve(); michael@0: }); michael@0: michael@0: tab.linkedBrowser.loadURI(url); michael@0: michael@0: return deferred.promise; michael@0: }, michael@0: michael@0: zoomTest: function zoomTest(tab, val, msg) { michael@0: is(ZoomManager.getZoomForBrowser(tab.linkedBrowser), val, msg); michael@0: }, michael@0: michael@0: enlarge: function enlarge() { michael@0: let deferred = Promise.defer(); michael@0: FullZoom.enlarge(function () deferred.resolve()); michael@0: return deferred.promise; michael@0: }, michael@0: michael@0: reduce: function reduce() { michael@0: let deferred = Promise.defer(); michael@0: FullZoom.reduce(function () deferred.resolve()); michael@0: return deferred.promise; michael@0: }, michael@0: michael@0: reset: function reset() { michael@0: let deferred = Promise.defer(); michael@0: FullZoom.reset(function () deferred.resolve()); michael@0: return deferred.promise; michael@0: }, michael@0: michael@0: BACK: 0, michael@0: FORWARD: 1, michael@0: navigate: function navigate(direction) { michael@0: let deferred = Promise.defer(); michael@0: let didPs = false; michael@0: let didZoom = false; michael@0: michael@0: gBrowser.addEventListener("pageshow", function (event) { michael@0: gBrowser.removeEventListener("pageshow", arguments.callee, true); michael@0: didPs = true; michael@0: if (didZoom) michael@0: deferred.resolve(); michael@0: }, true); michael@0: michael@0: if (direction == this.BACK) michael@0: gBrowser.goBack(); michael@0: else if (direction == this.FORWARD) michael@0: gBrowser.goForward(); michael@0: michael@0: this.waitForLocationChange().then(function () { michael@0: didZoom = true; michael@0: if (didPs) michael@0: deferred.resolve(); michael@0: }); michael@0: return deferred.promise; michael@0: }, michael@0: michael@0: failAndContinue: function failAndContinue(func) { michael@0: return function (err) { michael@0: ok(false, err); michael@0: func(); michael@0: }; michael@0: }, michael@0: };