forked from mirrors/gecko-dev
This patch does the following: * Moves most logic for initiating about:home / about:newtab into AboutNewTab.jsm * Makes AboutNewTab the API surface for overriding the default about:newtab URLs. * Reduces the surface of nsIAboutNewTabService, and makes the properties read-only * Splits the remaining code in the nsIAboutNewTabService into an implementation for the parent process, and one for content processes. This split will hopefully help reduce confusion about which code in AboutNewTabService is running in which process. Differential Revision: https://phabricator.services.mozilla.com/D65569 --HG-- rename : browser/components/newtab/test/xpcshell/test_AboutNewTabService.js => browser/components/newtab/test/xpcshell/test_AboutNewTab.js extra : moz-landing-system : lando
524 lines
13 KiB
JavaScript
524 lines
13 KiB
JavaScript
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
|
|
|
ChromeUtils.defineModuleGetter(
|
|
this,
|
|
"AboutNewTab",
|
|
"resource:///modules/AboutNewTab.jsm"
|
|
);
|
|
ChromeUtils.defineModuleGetter(
|
|
this,
|
|
"PlacesUtils",
|
|
"resource://gre/modules/PlacesUtils.jsm"
|
|
);
|
|
ChromeUtils.defineModuleGetter(
|
|
this,
|
|
"PlacesTestUtils",
|
|
"resource://testing-common/PlacesTestUtils.jsm"
|
|
);
|
|
ChromeUtils.defineModuleGetter(
|
|
this,
|
|
"BrowserTestUtils",
|
|
"resource://testing-common/BrowserTestUtils.jsm"
|
|
);
|
|
ChromeUtils.defineModuleGetter(
|
|
this,
|
|
"TabCrashHandler",
|
|
"resource:///modules/ContentCrashHandlers.jsm"
|
|
);
|
|
|
|
/**
|
|
* Wait for a <notification> to be closed then call the specified callback.
|
|
*/
|
|
function waitForNotificationClose(notification, cb) {
|
|
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(notification.control.stack, { childList: true });
|
|
}
|
|
|
|
function closeAllNotifications() {
|
|
if (!gNotificationBox.currentNotification) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
return new Promise(resolve => {
|
|
for (let notification of gNotificationBox.allNotifications) {
|
|
waitForNotificationClose(notification, function() {
|
|
if (gNotificationBox.allNotifications.length === 0) {
|
|
resolve();
|
|
}
|
|
});
|
|
notification.close();
|
|
}
|
|
});
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
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) {
|
|
return new Promise(resolve => {
|
|
waitForCondition(aConditionFn, resolve, "Condition didn't pass.");
|
|
});
|
|
}
|
|
|
|
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) {
|
|
return SpecialPowers.pushPrefEnv({ set: aPrefs });
|
|
}
|
|
|
|
function popPrefs() {
|
|
return SpecialPowers.popPrefEnv();
|
|
}
|
|
|
|
function promiseWindowClosed(win) {
|
|
let promise = BrowserTestUtils.domWindowClosed(win);
|
|
win.close();
|
|
return promise;
|
|
}
|
|
|
|
function promiseOpenAndLoadWindow(aOptions, aWaitForDelayedStartup = false) {
|
|
return new Promise(resolve => {
|
|
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");
|
|
resolve(win);
|
|
}, "browser-delayed-startup-finished");
|
|
} else {
|
|
win.addEventListener(
|
|
"load",
|
|
function() {
|
|
resolve(win);
|
|
},
|
|
{ once: true }
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
async function whenNewTabLoaded(aWindow, aCallback) {
|
|
aWindow.BrowserOpenTab();
|
|
|
|
let expectedURL = AboutNewTab.newTabURL;
|
|
let browser = aWindow.gBrowser.selectedBrowser;
|
|
let loadPromise = BrowserTestUtils.browserLoaded(browser, false, expectedURL);
|
|
let alreadyLoaded = await SpecialPowers.spawn(browser, [expectedURL], url => {
|
|
let doc = content.document;
|
|
return doc && doc.readyState === "complete" && doc.location.href == url;
|
|
});
|
|
if (!alreadyLoaded) {
|
|
await loadPromise;
|
|
}
|
|
aCallback();
|
|
}
|
|
|
|
function whenTabLoaded(aTab, aCallback) {
|
|
promiseTabLoadEvent(aTab).then(aCallback);
|
|
}
|
|
|
|
function promiseTabLoaded(aTab) {
|
|
return new Promise(resolve => {
|
|
whenTabLoaded(aTab, resolve);
|
|
});
|
|
}
|
|
|
|
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");
|
|
});
|
|
},
|
|
|
|
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();
|
|
}
|
|
});
|
|
|
|
BrowserTestUtils.loadURI(tab.linkedBrowser, 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;
|
|
|
|
BrowserTestUtils.waitForContentEvent(
|
|
gBrowser.selectedBrowser,
|
|
"pageshow",
|
|
true
|
|
).then(() => {
|
|
didPs = true;
|
|
if (didZoom) {
|
|
resolve();
|
|
}
|
|
});
|
|
|
|
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 BrowserTestUtils.waitForEvent(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"].includes(element.state);
|
|
}
|
|
|
|
// Hiding a parent element will hide all its children
|
|
if (element.parentNode != element.ownerDocument) {
|
|
return is_hidden(element.parentNode);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function is_element_visible(element, msg) {
|
|
isnot(element, null, "Element should not be null, when checking visibility");
|
|
ok(BrowserTestUtils.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 promisePopupShown(popup) {
|
|
return BrowserTestUtils.waitForPopupEvent(popup, "shown");
|
|
}
|
|
|
|
function promisePopupHidden(popup) {
|
|
return BrowserTestUtils.waitForPopupEvent(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;
|
|
}
|
|
|
|
/**
|
|
* Resolves when a bookmark with the given uri is added.
|
|
*/
|
|
function promiseOnBookmarkItemAdded(aExpectedURI) {
|
|
return new Promise((resolve, reject) => {
|
|
let listener = events => {
|
|
is(events.length, 1, "Should only receive one event.");
|
|
info("Added a bookmark to " + events[0].url);
|
|
PlacesUtils.observers.removeListener(["bookmark-added"], listener);
|
|
if (events[0].url == aExpectedURI.spec) {
|
|
resolve();
|
|
} else {
|
|
reject(new Error("Added an unexpected bookmark"));
|
|
}
|
|
};
|
|
info("Waiting for a bookmark to be added");
|
|
PlacesUtils.observers.addListener(["bookmark-added"], listener);
|
|
});
|
|
}
|
|
|
|
async function loadBadCertPage(url) {
|
|
let loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
|
|
await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url);
|
|
await loaded;
|
|
|
|
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() {
|
|
content.document.getElementById("exceptionDialogButton").click();
|
|
});
|
|
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
|
}
|
|
|
|
/**
|
|
* Waits for the stylesheets to be loaded into the browser menu.
|
|
*
|
|
* @param tab
|
|
* The tab that contains the webpage we're testing.
|
|
* @param styleSheetCount
|
|
* How many stylesheets we expect to be loaded.
|
|
* @return Promise
|
|
*/
|
|
async function promiseStylesheetsLoaded(tab, styleSheetCount) {
|
|
let styleMenu = tab.ownerGlobal.gPageStyleMenu;
|
|
let permanentKey = tab.permanentKey;
|
|
|
|
await TestUtils.waitForCondition(() => {
|
|
let menu = styleMenu._pageStyleSheets.get(permanentKey);
|
|
return menu && menu.filteredStyleSheets.length >= styleSheetCount;
|
|
}, "waiting for style sheets to load");
|
|
}
|