Bug 1340987 - (Part 5) Apply changes for subdialogs.js to in-content and fix tests. r=MattN

MozReview-Commit-ID: D729DVbZw9I

--HG--
extra : rebase_source : ef1adc4a610fb2305deff45e4e8e27f133b19115
This commit is contained in:
Scott Wu 2017-06-05 17:00:47 +08:00
Родитель 4f4df688b8
Коммит 61e99c74bd
9 изменённых файлов: 306 добавлений и 126 удалений

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

@ -202,23 +202,22 @@
</hbox>
<vbox id="dialogOverlay" align="center" pack="center">
<groupbox id="dialogBox"
orient="vertical"
pack="end"
role="dialog"
aria-labelledby="dialogTitle">
<caption flex="1" align="center">
<label id="dialogTitle" flex="1"></label>
<button id="dialogClose"
class="close-icon"
aria-label="&preferencesCloseButton.label;"/>
</caption>
<browser id="dialogFrame"
name="dialogFrame"
autoscroll="false"
disablehistory="true"/>
</groupbox>
</vbox>
<stack id="dialogStack" hidden="true"/>
<vbox id="dialogTemplate" class="dialogOverlay" align="center" pack="center" topmost="true" hidden="true">
<groupbox class="dialogBox"
orient="vertical"
pack="end"
role="dialog"
aria-labelledby="dialogTitle">
<caption flex="1" align="center">
<label class="dialogTitle" flex="1"></label>
<button class="dialogClose close-icon"
aria-label="&preferencesCloseButton.label;"/>
</caption>
<browser class="dialogFrame"
autoscroll="false"
disablehistory="true"/>
</groupbox>
</vbox>
</stack>
</page>

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

@ -7,11 +7,38 @@
"use strict";
var gSubDialog = {
/**
* SubDialog constructor creates a new subdialog from a template and appends
* it to the parentElement.
* @param {DOMNode} template: The template is copied to create a new dialog.
* @param {DOMNode} parentElement: New dialog is appended onto parentElement.
* @param {String} id: A unique identifier for the dialog.
*/
function SubDialog({template, parentElement, id}) {
this._id = id;
this._overlay = template.cloneNode(true);
this._frame = this._overlay.querySelector(".dialogFrame");
this._box = this._overlay.querySelector(".dialogBox");
this._closeButton = this._overlay.querySelector(".dialogClose");
this._titleElement = this._overlay.querySelector(".dialogTitle");
this._overlay.id = `dialogOverlay-${id}`;
this._frame.setAttribute("name", `dialogFrame-${id}`);
this._frameCreated = new Promise(resolve => {
this._frame.addEventListener("load", resolve, {once: true});
});
parentElement.appendChild(this._overlay);
this._overlay.hidden = false;
}
SubDialog.prototype = {
_closingCallback: null,
_closingEvent: null,
_isClosing: false,
_frame: null,
_frameCreated: null,
_overlay: null,
_box: null,
_openedURL: null,
@ -22,18 +49,15 @@ var gSubDialog = {
"chrome://browser/skin/preferences/in-content/dialog.css",
],
_resizeObserver: null,
init() {
this._frame = document.getElementById("dialogFrame");
this._overlay = document.getElementById("dialogOverlay");
this._box = document.getElementById("dialogBox");
this._closeButton = document.getElementById("dialogClose");
},
_template: null,
_id: null,
_titleElement: null,
_closeButton: null,
updateTitle(aEvent) {
if (aEvent.target != gSubDialog._frame.contentDocument)
if (aEvent.target != this._frame.contentDocument)
return;
document.getElementById("dialogTitle").textContent = gSubDialog._frame.contentDocument.title;
this._titleElement.textContent = this._frame.contentDocument.title;
},
injectXMLStylesheet(aStylesheetURL) {
@ -45,11 +69,9 @@ var gSubDialog = {
this._frame.contentDocument.documentElement);
},
open(aURL, aFeatures = null, aParams = null, aClosingCallback = null) {
// If we're already open/opening on this URL, do nothing.
if (this._openedURL == aURL && !this._isClosing) {
return;
}
async open(aURL, aFeatures = null, aParams = null, aClosingCallback = null) {
// Wait until frame is ready to prevent browser crash in tests
await this._frameCreated;
// If we're open on some (other) URL or we're closing, open when closing has finished.
if (this._openedURL || this._isClosing) {
if (!this._isClosing) {
@ -64,7 +86,7 @@ var gSubDialog = {
this._addDialogEventListeners();
let features = (aFeatures ? aFeatures + "," : "") + "resizable,dialog=no,centerscreen";
let dialog = window.openDialog(aURL, "dialogFrame", features, aParams);
let dialog = window.openDialog(aURL, `dialogFrame-${this._id}`, features, aParams);
if (aClosingCallback) {
this._closingCallback = aClosingCallback.bind(dialog);
}
@ -109,6 +131,11 @@ var gSubDialog = {
this._box.style.removeProperty("min-height");
this._box.style.removeProperty("min-width");
this._overlay.dispatchEvent(new CustomEvent("dialogclose", {
bubbles: true,
detail: { dialog: this },
}));
setTimeout(() => {
// Unload the dialog after the event listeners run so that the load of about:blank isn't
// cancelled by the ESC <key>.
@ -185,32 +212,32 @@ var gSubDialog = {
this._frame.contentWindow.addEventListener("dialogclosing", this);
let oldResizeBy = this._frame.contentWindow.resizeBy;
this._frame.contentWindow.resizeBy = function(resizeByWidth, resizeByHeight) {
this._frame.contentWindow.resizeBy = (resizeByWidth, resizeByHeight) => {
// Only handle resizeByHeight currently.
let frameHeight = gSubDialog._frame.clientHeight;
let boxMinHeight = parseFloat(getComputedStyle(gSubDialog._box).minHeight, 10);
let frameHeight = this._frame.clientHeight;
let boxMinHeight = parseFloat(getComputedStyle(this._box).minHeight, 10);
gSubDialog._frame.style.height = (frameHeight + resizeByHeight) + "px";
gSubDialog._box.style.minHeight = (boxMinHeight + resizeByHeight) + "px";
this._frame.style.height = (frameHeight + resizeByHeight) + "px";
this._box.style.minHeight = (boxMinHeight + resizeByHeight) + "px";
oldResizeBy.call(gSubDialog._frame.contentWindow, resizeByWidth, resizeByHeight);
oldResizeBy.call(this._frame.contentWindow, resizeByWidth, resizeByHeight);
};
// Make window.close calls work like dialog closing.
let oldClose = this._frame.contentWindow.close;
this._frame.contentWindow.close = function() {
var closingEvent = gSubDialog._closingEvent;
this._frame.contentWindow.close = () => {
var closingEvent = this._closingEvent;
if (!closingEvent) {
closingEvent = new CustomEvent("dialogclosing", {
bubbles: true,
detail: { button: null },
});
gSubDialog._frame.contentWindow.dispatchEvent(closingEvent);
this._frame.contentWindow.dispatchEvent(closingEvent);
}
gSubDialog.close(closingEvent);
oldClose.call(gSubDialog._frame.contentWindow);
this.close(closingEvent);
oldClose.call(this._frame.contentWindow);
};
// XXX: Hack to make focus during the dialog's load functions work. Make the element visible
@ -293,6 +320,10 @@ var gSubDialog = {
(boxVerticalBorder + groupBoxTitleHeight + boxVerticalPadding) +
"px + " + frameMinHeight + ")";
this._overlay.dispatchEvent(new CustomEvent("dialogopen", {
bubbles: true,
detail: { dialog: this },
}));
this._overlay.style.visibility = "visible";
this._overlay.style.opacity = ""; // XXX: focus hack continued from _onContentLoaded
@ -305,7 +336,7 @@ var gSubDialog = {
},
_onResize(mutations) {
let frame = gSubDialog._frame;
let frame = this._frame;
// The width and height styles are needed for the initial
// layout of the frame, but afterward they need to be removed
// or their presence will restrict the contents of the <browser>
@ -348,13 +379,13 @@ var gSubDialog = {
let fm = Services.focus;
function isLastFocusableElement(el) {
let isLastFocusableElement = el => {
// XXXgijs unfortunately there is no way to get the last focusable element without asking
// the focus manager to move focus to it.
let rv = el == fm.moveFocus(gSubDialog._frame.contentWindow, null, fm.MOVEFOCUS_LAST, 0);
let rv = el == fm.moveFocus(this._frame.contentWindow, null, fm.MOVEFOCUS_LAST, 0);
fm.setFocus(el, 0);
return rv;
}
};
let forward = !aEvent.shiftKey;
// check if focus is leaving the frame (incl. the close button):
@ -449,3 +480,104 @@ var gSubDialog = {
.chromeEventHandler;
},
};
var gSubDialog = {
/**
* New dialogs are stacked on top of the existing ones, and they are pushed
* to the end of the _dialogs array.
* @type {Array}
*/
_dialogs: [],
_dialogStack: null,
_dialogTemplate: null,
_nextDialogID: 0,
_preloadDialog: null,
get _topDialog() {
return this._dialogs.length > 0 ? this._dialogs[this._dialogs.length - 1] : undefined;
},
init() {
this._dialogStack = document.getElementById("dialogStack");
this._dialogTemplate = document.getElementById("dialogTemplate");
this._preloadDialog = new SubDialog({template: this._dialogTemplate,
parentElement: this._dialogStack,
id: this._nextDialogID++});
},
open(aURL, aFeatures = null, aParams = null, aClosingCallback = null) {
// If we're already open/opening on this URL, do nothing.
if (this._topDialog && this._topDialog._openedURL == aURL) {
return;
}
this._preloadDialog.open(aURL, aFeatures, aParams, aClosingCallback);
this._dialogs.push(this._preloadDialog);
this._preloadDialog = new SubDialog({template: this._dialogTemplate,
parentElement: this._dialogStack,
id: this._nextDialogID++});
if (this._dialogs.length == 1) {
this._dialogStack.hidden = false;
this._ensureStackEventListeners();
}
},
close() {
this._topDialog.close();
},
handleEvent(aEvent) {
switch (aEvent.type) {
case "dialogopen": {
this._onDialogOpen();
break;
}
case "dialogclose": {
this._onDialogClose(aEvent.detail.dialog);
break;
}
}
},
_onDialogOpen() {
let lowerDialog = this._dialogs.length > 1 ? this._dialogs[this._dialogs.length - 2] : undefined;
if (lowerDialog) {
lowerDialog._overlay.removeAttribute("topmost");
lowerDialog._removeDialogEventListeners();
}
},
_onDialogClose(dialog) {
let fm = Services.focus;
if (this._topDialog == dialog) {
// XXX: When a top-most dialog is closed, we reuse the closed dialog and
// remove the preloadDialog. This is a temporary solution before we
// rewrite all the test cases in Bug 1359023.
this._preloadDialog._overlay.remove();
this._preloadDialog = this._dialogs.pop();
} else {
dialog._overlay.remove();
this._dialogs.splice(this._dialogs.indexOf(dialog), 1);
}
if (this._topDialog) {
fm.moveFocus(this._topDialog._frame.contentWindow, null, fm.MOVEFOCUS_FIRST, fm.FLAG_BYKEY);
this._topDialog._overlay.setAttribute("topmost", true);
this._topDialog._addDialogEventListeners();
} else {
fm.moveFocus(window, null, fm.MOVEFOCUS_ROOT, fm.FLAG_BYKEY);
this._dialogStack.hidden = true;
this._removeStackEventListeners();
}
},
_ensureStackEventListeners() {
this._dialogStack.addEventListener("dialogopen", this);
this._dialogStack.addEventListener("dialogclose", this);
},
_removeStackEventListeners() {
this._dialogStack.removeEventListener("dialogopen", this);
this._dialogStack.removeEventListener("dialogclose", this);
},
};

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

@ -127,9 +127,10 @@ const cacheUsageGetter = {
};
function openSettingsDialog() {
let win = gBrowser.selectedBrowser.contentWindow;
let doc = gBrowser.selectedBrowser.contentDocument;
let settingsBtn = doc.getElementById("siteDataSettings");
let dialogOverlay = doc.getElementById("dialogOverlay");
let dialogOverlay = win.gSubDialog._preloadDialog._overlay;
let dialogLoadPromise = promiseLoadSubDialog("chrome://browser/content/preferences/siteDataSettings.xul");
let dialogInitPromise = TestUtils.topicObserved("sitedata-settings-init", () => true);
let fullyLoadPromise = Promise.all([ dialogLoadPromise, dialogInitPromise ]).then(() => {
@ -141,11 +142,11 @@ function openSettingsDialog() {
function promiseSettingsDialogClose() {
return new Promise(resolve => {
let doc = gBrowser.selectedBrowser.contentDocument;
let dialogOverlay = doc.getElementById("dialogOverlay");
let win = content.gSubDialog._frame.contentWindow;
win.addEventListener("unload", function unload() {
if (win.document.documentURI === "chrome://browser/content/preferences/siteDataSettings.xul") {
let win = gBrowser.selectedBrowser.contentWindow;
let dialogOverlay = win.gSubDialog._topDialog._overlay;
let dialogWin = win.gSubDialog._topDialog._frame.contentWindow;
dialogWin.addEventListener("unload", function unload() {
if (dialogWin.document.documentURI === "chrome://browser/content/preferences/siteDataSettings.xul") {
isnot(dialogOverlay.style.visibility, "visible", "The Settings dialog should be hidden");
resolve();
}
@ -164,7 +165,8 @@ function promiseCookiesCleared() {
}
function assertSitesListed(doc, hosts) {
let frameDoc = doc.getElementById("dialogFrame").contentDocument;
let win = gBrowser.selectedBrowser.contentWindow;
let frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
let removeBtn = frameDoc.getElementById("removeSelected");
let removeAllBtn = frameDoc.getElementById("removeAll");
let sitesList = frameDoc.getElementById("sitesList");
@ -221,7 +223,7 @@ add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
await updatedPromise;
await openSettingsDialog();
let dialogFrame = gBrowser.selectedBrowser.contentDocument.getElementById("dialogFrame");
let dialogFrame = content.gSubDialog._topDialog._frame;
let frameDoc = dialogFrame.contentDocument;
let siteItems = frameDoc.getElementsByTagName("richlistitem");
@ -262,8 +264,7 @@ add_task(async function() {
await openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
await updatedPromise;
await openSettingsDialog();
let doc = gBrowser.selectedBrowser.contentDocument;
let dialogFrame = doc.getElementById("dialogFrame");
let dialogFrame = content.gSubDialog._topDialog._frame;
let frameDoc = dialogFrame.contentDocument;
let siteItems = frameDoc.getElementsByTagName("richlistitem");
@ -431,8 +432,9 @@ add_task(async function() {
await updatePromise;
await openSettingsDialog();
let doc = gBrowser.selectedBrowser.contentDocument;
let dialogFrame = doc.getElementById("dialogFrame");
let win = gBrowser.selectedBrowser.contentWindow;
let dialog = win.gSubDialog._topDialog;
let dialogFrame = dialog._frame;
let frameDoc = dialogFrame.contentDocument;
let hostCol = frameDoc.getElementById("hostCol");
let usageCol = frameDoc.getElementById("usageCol");
@ -556,8 +558,9 @@ add_task(async function() {
await updatePromise;
await openSettingsDialog();
let win = gBrowser.selectedBrowser.contentWindow;
let doc = gBrowser.selectedBrowser.contentDocument;
let frameDoc = doc.getElementById("dialogFrame").contentDocument;
let frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
let searchBox = frameDoc.getElementById("searchBox");
searchBox.value = "xyz";
@ -613,6 +616,7 @@ add_task(async function() {
await updatePromise;
await openSettingsDialog();
let win = gBrowser.selectedBrowser.contentWindow;
let doc = gBrowser.selectedBrowser.contentDocument;
let frameDoc = null;
let saveBtn = null;
@ -624,7 +628,7 @@ add_task(async function() {
// Test the "Cancel" button
settingsDialogClosePromise = promiseSettingsDialogClose();
frameDoc = doc.getElementById("dialogFrame").contentDocument;
frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
cancelBtn = frameDoc.getElementById("cancel");
removeAllSitesOneByOne();
assertAllSitesNotListed();
@ -636,7 +640,7 @@ add_task(async function() {
// Test the "Save Changes" button but cancelling save
let cancelPromise = promiseAlertDialogOpen("cancel");
settingsDialogClosePromise = promiseSettingsDialogClose();
frameDoc = doc.getElementById("dialogFrame").contentDocument;
frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
saveBtn = frameDoc.getElementById("save");
removeAllSitesOneByOne();
assertAllSitesNotListed();
@ -650,7 +654,7 @@ add_task(async function() {
let acceptPromise = promiseAlertDialogOpen("accept");
settingsDialogClosePromise = promiseSettingsDialogClose();
updatePromise = promiseSitesUpdated();
frameDoc = doc.getElementById("dialogFrame").contentDocument;
frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
saveBtn = frameDoc.getElementById("save");
removeAllSitesOneByOne();
assertAllSitesNotListed();
@ -665,7 +669,7 @@ add_task(async function() {
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
function removeAllSitesOneByOne() {
frameDoc = doc.getElementById("dialogFrame").contentDocument;
frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
let removeBtn = frameDoc.getElementById("removeSelected");
let sitesList = frameDoc.getElementById("sitesList");
let sites = sitesList.getElementsByTagName("richlistitem");
@ -676,7 +680,7 @@ add_task(async function() {
}
function assertAllSitesNotListed() {
frameDoc = doc.getElementById("dialogFrame").contentDocument;
frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
let removeBtn = frameDoc.getElementById("removeSelected");
let removeAllBtn = frameDoc.getElementById("removeAll");
let sitesList = frameDoc.getElementById("sitesList");
@ -742,6 +746,7 @@ add_task(async function() {
await updatePromise;
await openSettingsDialog();
let win = gBrowser.selectedBrowser.contentWindow;
let doc = gBrowser.selectedBrowser.contentDocument;
let frameDoc = null;
let saveBtn = null;
@ -754,7 +759,7 @@ add_task(async function() {
// Test the "Cancel" button
settingsDialogClosePromise = promiseSettingsDialogClose();
frameDoc = doc.getElementById("dialogFrame").contentDocument;
frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
cancelBtn = frameDoc.getElementById("cancel");
removeSelectedSite(fakeHosts.slice(0, 2));
assertSitesListed(doc, fakeHosts.slice(2));
@ -766,7 +771,7 @@ add_task(async function() {
// Test the "Save Changes" button but canceling save
removeDialogOpenPromise = promiseWindowDialogOpen("cancel", REMOVE_DIALOG_URL);
settingsDialogClosePromise = promiseSettingsDialogClose();
frameDoc = doc.getElementById("dialogFrame").contentDocument;
frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
saveBtn = frameDoc.getElementById("save");
removeSelectedSite(fakeHosts.slice(0, 2));
assertSitesListed(doc, fakeHosts.slice(2));
@ -779,7 +784,7 @@ add_task(async function() {
// Test the "Save Changes" button and accepting save
removeDialogOpenPromise = promiseWindowDialogOpen("accept", REMOVE_DIALOG_URL);
settingsDialogClosePromise = promiseSettingsDialogClose();
frameDoc = doc.getElementById("dialogFrame").contentDocument;
frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
saveBtn = frameDoc.getElementById("save");
removeSelectedSite(fakeHosts.slice(0, 2));
assertSitesListed(doc, fakeHosts.slice(2));
@ -793,7 +798,7 @@ add_task(async function() {
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
function removeSelectedSite(hosts) {
frameDoc = doc.getElementById("dialogFrame").contentDocument;
frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
let removeBtn = frameDoc.getElementById("removeSelected");
let sitesList = frameDoc.getElementById("sitesList");
hosts.forEach(host => {
@ -846,8 +851,9 @@ add_task(async function() {
await openSettingsDialog();
// Search "foo" to only list foo.com sites
let win = gBrowser.selectedBrowser.contentWindow;
let doc = gBrowser.selectedBrowser.contentDocument;
let frameDoc = doc.getElementById("dialogFrame").contentDocument;
let frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
let searchBox = frameDoc.getElementById("searchBox");
searchBox.value = "xyz";
searchBox.doCommand();

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

@ -116,15 +116,16 @@ add_task(async function() {
let doc = gBrowser.selectedBrowser.contentDocument;
let showBtn = doc.getElementById("showUpdateHistory");
let dialogOverlay = doc.getElementById("dialogOverlay");
let dialogOverlay = content.gSubDialog._preloadDialog._overlay;
// Test the dialog window opens
is(dialogOverlay.style.visibility, "", "The dialog should be invisible");
let promiseSubDialogLoaded = promiseLoadSubDialog("chrome://mozapps/content/update/history.xul");
showBtn.doCommand();
await promiseLoadSubDialog("chrome://mozapps/content/update/history.xul");
await promiseSubDialogLoaded;
is(dialogOverlay.style.visibility, "visible", "The dialog should be visible");
let dialogFrame = doc.getElementById("dialogFrame");
let dialogFrame = dialogOverlay.querySelector(".dialogFrame");
let frameDoc = dialogFrame.contentDocument;
let updates = frameDoc.querySelectorAll("update");
@ -146,7 +147,7 @@ add_task(async function() {
}
// Test the dialog window closes
let closeBtn = doc.getElementById("dialogClose");
let closeBtn = dialogOverlay.querySelector(".dialogClose");
closeBtn.doCommand();
is(dialogOverlay.style.visibility, "", "The dialog should be invisible");

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

@ -20,8 +20,9 @@ add_task(async function() {
let fontSizeField = doc.getElementById("defaultFontSize");
is(fontSizeField.value, defaultFontSize, "Font size should be set correctly.");
let promiseSubDialogLoaded = promiseLoadSubDialog("chrome://browser/content/preferences/fonts.xul");
doc.getElementById("advancedFonts").click();
let win = await promiseLoadSubDialog("chrome://browser/content/preferences/fonts.xul");
let win = await promiseSubDialogLoaded;
doc = win.document;
// Simulate a dumb font backend.

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

@ -339,10 +339,12 @@ var testRunner = {
let historyMode = doc.getElementById("historyMode");
historyMode.value = "custom";
historyMode.doCommand();
let promiseSubDialogLoaded =
promiseLoadSubDialog("chrome://browser/content/preferences/permissions.xul");
doc.getElementById("cookieExceptions").doCommand();
let subDialogURL = "chrome://browser/content/preferences/permissions.xul";
promiseLoadSubDialog(subDialogURL).then(function(win) {
promiseSubDialogLoaded.then(function(win) {
helperFunctions.windowLoad(win);
});
});

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

@ -17,8 +17,8 @@ function open_subdialog_and_test_generic_start_state(browser, domcontentloadedFn
return ContentTask.spawn(browser, {url, domcontentloadedFnStr}, async function(args) {
let rv = { acceptCount: 0 };
let win = content.window;
let subdialog = win.gSubDialog;
subdialog.open(args.url, null, rv);
content.gSubDialog.open(args.url, null, rv);
let subdialog = content.gSubDialog._topDialog;
info("waiting for subdialog DOMFrameContentLoaded");
await ContentTaskUtils.waitForEvent(win, "DOMFrameContentLoaded", true);
@ -29,7 +29,7 @@ function open_subdialog_and_test_generic_start_state(browser, domcontentloadedFn
}
info("waiting for subdialog load");
await ContentTaskUtils.waitForEvent(subdialog._frame, "load");
await ContentTaskUtils.waitForEvent(subdialog._overlay, "dialogopen");
info("subdialog window is loaded");
let expectedStyleSheetURLs = subdialog._injectedStyleSheets.slice(0);
@ -52,9 +52,17 @@ function open_subdialog_and_test_generic_start_state(browser, domcontentloadedFn
}
async function close_subdialog_and_test_generic_end_state(browser, closingFn, closingButton, acceptCount, options) {
let getDialogsCount = () => {
return ContentTask.spawn(browser, null, () =>
content.window.gSubDialog._dialogs.length);
};
let getStackChildrenCount = () => {
return ContentTask.spawn(browser, null, () =>
content.window.gSubDialog._dialogStack.children.length);
};
let dialogclosingPromise = ContentTask.spawn(browser, {closingButton, acceptCount}, async function(expectations) {
let win = content.window;
let subdialog = win.gSubDialog;
let subdialog = win.gSubDialog._topDialog;
let frame = subdialog._frame;
info("waiting for dialogclosing");
let closingEvent =
@ -76,7 +84,8 @@ async function close_subdialog_and_test_generic_end_state(browser, closingFn, cl
Assert.equal(actualAcceptCount, expectations.acceptCount,
"should be 1 if accepted, 0 if canceled, undefined if closed w/out button");
});
let initialDialogsCount = await getDialogsCount();
let initialStackChildrenCount = await getStackChildrenCount();
if (options && options.runClosingFnOutsideOfContentTask) {
await closingFn();
} else {
@ -84,6 +93,12 @@ async function close_subdialog_and_test_generic_end_state(browser, closingFn, cl
}
await dialogclosingPromise;
let endDialogsCount = await getDialogsCount();
let endStackChildrenCount = await getStackChildrenCount();
Assert.equal(initialDialogsCount - 1, endDialogsCount,
"dialog count should decrease by 1");
Assert.equal(initialStackChildrenCount - 1, endStackChildrenCount,
"stack children count should decrease by 1");
}
let tab;
@ -97,27 +112,28 @@ add_task(async function check_titlebar_focus_returnval_titlechanges_accepting()
let domtitlechangedPromise = BrowserTestUtils.waitForEvent(tab.linkedBrowser, "DOMTitleChanged");
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
let dialog = content.window.gSubDialog._frame.contentWindow;
let dialogTitleElement = content.document.getElementById("dialogTitle");
let dialog = content.window.gSubDialog._topDialog;
let dialogWin = dialog._frame.contentWindow;
let dialogTitleElement = dialog._titleElement;
Assert.equal(dialogTitleElement.textContent, "Sample sub-dialog",
"Title should be correct initially");
Assert.equal(dialog.document.activeElement.value, "Default text",
Assert.equal(dialogWin.document.activeElement.value, "Default text",
"Textbox with correct text is focused");
dialog.document.title = "Updated title";
dialogWin.document.title = "Updated title";
});
info("waiting for DOMTitleChanged event");
await domtitlechangedPromise;
ContentTask.spawn(tab.linkedBrowser, null, async function() {
let dialogTitleElement = content.document.getElementById("dialogTitle");
let dialogTitleElement = content.window.gSubDialog._topDialog._titleElement;
Assert.equal(dialogTitleElement.textContent, "Updated title",
"subdialog should have updated title");
});
// Accept the dialog
await close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { content.window.gSubDialog._frame.contentDocument.documentElement.acceptDialog(); },
function() { content.window.gSubDialog._topDialog._frame.contentDocument.documentElement.acceptDialog(); },
"accept", 1);
});
@ -126,7 +142,7 @@ add_task(async function check_canceling_dialog() {
info("canceling the dialog");
await close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { content.window.gSubDialog._frame.contentDocument.documentElement.cancelDialog(); },
function() { content.window.gSubDialog._topDialog._frame.contentDocument.documentElement.cancelDialog(); },
"cancel", 0);
});
@ -134,20 +150,40 @@ add_task(async function check_reopening_dialog() {
await open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
info("opening another dialog which will close the first");
await open_subdialog_and_test_generic_start_state(tab.linkedBrowser, "", gDialogURL2);
info("closing as normal");
ContentTask.spawn(tab.linkedBrowser, null, async function() {
let win = content.window;
let dialogs = win.gSubDialog._dialogs;
let lowerDialog = dialogs[0];
let topDialog = dialogs[1];
Assert.equal(dialogs.length, 2, "There should be two visible dialogs");
Assert.equal(win.getComputedStyle(topDialog._overlay).visibility, "visible",
"The top dialog should be visible");
Assert.equal(win.getComputedStyle(lowerDialog._overlay).visibility, "visible",
"The lower dialog should be visible");
Assert.equal(win.getComputedStyle(topDialog._overlay).backgroundColor, "rgba(0, 0, 0, 0.5)",
"The top dialog should have a semi-transparent overlay");
Assert.equal(win.getComputedStyle(lowerDialog._overlay).backgroundColor, "rgba(0, 0, 0, 0)",
"The lower dialog should not have an overlay");
});
info("closing two dialogs");
await close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { content.window.gSubDialog._frame.contentDocument.documentElement.acceptDialog(); },
function() { content.window.gSubDialog._topDialog._frame.contentDocument.documentElement.acceptDialog(); },
"accept", 1);
await close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { content.window.gSubDialog._topDialog._frame.contentDocument.documentElement.acceptDialog(); },
"accept", 1);
});
add_task(async function check_opening_while_closing() {
await open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
info("closing");
content.window.gSubDialog.close();
content.window.gSubDialog._topDialog.close();
info("reopening immediately after calling .close()");
await open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
await close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { content.window.gSubDialog._frame.contentDocument.documentElement.acceptDialog(); },
function() { content.window.gSubDialog._topDialog._frame.contentDocument.documentElement.acceptDialog(); },
"accept", 1);
});
@ -157,7 +193,7 @@ add_task(async function window_close_on_dialog() {
info("canceling the dialog");
await close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { content.window.gSubDialog._frame.contentWindow.window.close(); },
function() { content.window.gSubDialog._topDialog._frame.contentWindow.window.close(); },
null, 0);
});
@ -166,7 +202,7 @@ add_task(async function click_close_button_on_dialog() {
info("canceling the dialog");
await close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { return BrowserTestUtils.synthesizeMouseAtCenter("#dialogClose", {}, tab.linkedBrowser); },
function() { return BrowserTestUtils.synthesizeMouseAtCenter(".dialogClose", {}, tab.linkedBrowser); },
null, 0, {runClosingFnOutsideOfContentTask: true});
});
@ -176,7 +212,7 @@ add_task(async function background_click_should_close_dialog() {
// Clicking on an inactive part of dialog itself should not close the dialog.
// Click the dialog title bar here to make sure nothing happens.
info("clicking the dialog title bar");
BrowserTestUtils.synthesizeMouseAtCenter("#dialogTitle", {}, tab.linkedBrowser);
BrowserTestUtils.synthesizeMouseAtCenter(".dialogTitle", {}, tab.linkedBrowser);
// Close the dialog by clicking on the overlay background. Simulate a click
// at point (2,2) instead of (0,0) so we are sure we're clicking on the
@ -193,7 +229,7 @@ add_task(async function back_navigation_on_subdialog_should_close_dialog() {
info("canceling the dialog");
await close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { content.window.gSubDialog._frame.goBack(); },
function() { content.window.gSubDialog._topDialog._frame.goBack(); },
null, undefined);
});
@ -219,7 +255,7 @@ add_task(async function correct_width_and_height_should_be_used_for_dialog() {
await open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
let frameStyle = content.window.gSubDialog._frame.style;
let frameStyle = content.window.gSubDialog._topDialog._frame.style;
Assert.equal(frameStyle.width, "32em",
"Width should be set on the frame from the dialog");
Assert.equal(frameStyle.height, "5em",
@ -227,13 +263,13 @@ add_task(async function correct_width_and_height_should_be_used_for_dialog() {
});
await close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { content.window.gSubDialog._frame.contentWindow.window.close(); },
function() { content.window.gSubDialog._topDialog._frame.contentWindow.window.close(); },
null, 0);
});
add_task(async function wrapped_text_in_dialog_should_have_expected_scrollHeight() {
let oldHeight = await open_subdialog_and_test_generic_start_state(tab.linkedBrowser, function domcontentloadedFn() {
let frame = content.window.gSubDialog._frame;
let frame = content.window.gSubDialog._topDialog._frame;
let doc = frame.contentDocument;
let scrollHeight = doc.documentElement.scrollHeight;
doc.documentElement.style.removeProperty("height");
@ -253,7 +289,7 @@ add_task(async function wrapped_text_in_dialog_should_have_expected_scrollHeight
});
await ContentTask.spawn(tab.linkedBrowser, oldHeight, async function(contentOldHeight) {
let frame = content.window.gSubDialog._frame;
let frame = content.window.gSubDialog._topDialog._frame;
let docEl = frame.contentDocument.documentElement;
Assert.equal(frame.style.width, "32em",
"Width should be set on the frame from the dialog");
@ -264,37 +300,37 @@ add_task(async function wrapped_text_in_dialog_should_have_expected_scrollHeight
});
await close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { content.window.gSubDialog._frame.contentWindow.window.close(); },
function() { content.window.gSubDialog._topDialog._frame.contentWindow.window.close(); },
null, 0);
});
add_task(async function dialog_too_tall_should_get_reduced_in_height() {
await open_subdialog_and_test_generic_start_state(tab.linkedBrowser, function domcontentloadedFn() {
let frame = content.window.gSubDialog._frame;
let frame = content.window.gSubDialog._topDialog._frame;
frame.contentDocument.documentElement.style.height = "100000px";
});
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
let frame = content.window.gSubDialog._frame;
let frame = content.window.gSubDialog._topDialog._frame;
Assert.equal(frame.style.width, "32em", "Width should be set on the frame from the dialog");
Assert.ok(parseInt(frame.style.height, 10) < content.window.innerHeight,
"Height on the frame should be smaller than window's innerHeight");
});
await close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { content.window.gSubDialog._frame.contentWindow.window.close(); },
function() { content.window.gSubDialog._topDialog._frame.contentWindow.window.close(); },
null, 0);
});
add_task(async function scrollWidth_and_scrollHeight_from_subdialog_should_size_the_browser() {
await open_subdialog_and_test_generic_start_state(tab.linkedBrowser, function domcontentloadedFn() {
let frame = content.window.gSubDialog._frame;
let frame = content.window.gSubDialog._topDialog._frame;
frame.contentDocument.documentElement.style.removeProperty("height");
frame.contentDocument.documentElement.style.removeProperty("width");
});
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
let frame = content.window.gSubDialog._frame;
let frame = content.window.gSubDialog._topDialog._frame;
Assert.ok(frame.style.width.endsWith("px"),
"Width (" + frame.style.width + ") should be set to a px value of the scrollWidth from the dialog");
Assert.ok(frame.style.height.endsWith("px"),
@ -302,7 +338,7 @@ add_task(async function scrollWidth_and_scrollHeight_from_subdialog_should_size_
});
await close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { content.window.gSubDialog._frame.contentWindow.window.close(); },
function() { content.window.gSubDialog._topDialog._frame.contentWindow.window.close(); },
null, 0);
});

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

@ -54,20 +54,20 @@ function openAndLoadSubDialog(aURL, aFeatures = null, aParams = null, aClosingCa
function promiseLoadSubDialog(aURL) {
return new Promise((resolve, reject) => {
content.gSubDialog._frame.addEventListener("load", function load(aEvent) {
if (aEvent.target.contentWindow.location == "about:blank")
content.gSubDialog._dialogStack.addEventListener("dialogopen", function dialogopen(aEvent) {
if (aEvent.detail.dialog._frame.contentWindow.location == "about:blank")
return;
content.gSubDialog._frame.removeEventListener("load", load);
content.gSubDialog._dialogStack.removeEventListener("dialogopen", dialogopen);
is(content.gSubDialog._frame.contentWindow.location.toString(), aURL,
is(aEvent.detail.dialog._frame.contentWindow.location.toString(), aURL,
"Check the proper URL is loaded");
// Check visibility
is_element_visible(content.gSubDialog._overlay, "Overlay is visible");
is_element_visible(aEvent.detail.dialog._overlay, "Overlay is visible");
// Check that stylesheets were injected
let expectedStyleSheetURLs = content.gSubDialog._injectedStyleSheets.slice(0);
for (let styleSheet of content.gSubDialog._frame.contentDocument.styleSheets) {
let expectedStyleSheetURLs = aEvent.detail.dialog._injectedStyleSheets.slice(0);
for (let styleSheet of aEvent.detail.dialog._frame.contentDocument.styleSheets) {
let i = expectedStyleSheetURLs.indexOf(styleSheet.href);
if (i >= 0) {
info("found " + styleSheet.href);
@ -76,7 +76,7 @@ function promiseLoadSubDialog(aURL) {
}
is(expectedStyleSheetURLs.length, 0, "All expectedStyleSheetURLs should have been found");
resolve(content.gSubDialog._frame.contentWindow);
resolve(aEvent.detail.dialog._frame.contentWindow);
});
});
}

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

@ -291,12 +291,15 @@ description > html|a {
* Dialog
*/
#dialogOverlay {
background-color: rgba(0,0,0,0.5);
.dialogOverlay {
visibility: hidden;
}
#dialogBox {
.dialogOverlay[topmost="true"] {
background-color: rgba(0,0,0,0.5);
}
.dialogBox {
background-color: #fbfbfb;
background-clip: content-box;
color: #424e5a;
@ -311,20 +314,20 @@ description > html|a {
padding: 0;
}
#dialogBox[resizable="true"] {
.dialogBox[resizable="true"] {
resize: both;
overflow: hidden;
min-height: 20em;
min-width: 66ch;
}
#dialogBox > .groupbox-title {
.dialogBox > .groupbox-title {
padding: 3.5px 0;
background-color: #F1F1F1;
border-bottom: 1px solid #C1C1C1;
}
#dialogTitle {
.dialogTitle {
text-align: center;
-moz-user-select: none;
}
@ -339,12 +342,12 @@ description > html|a {
min-width: 0;
}
#dialogBox > .groupbox-body {
.dialogBox > .groupbox-body {
-moz-appearance: none;
padding: 20px;
}
#dialogFrame {
.dialogFrame {
-moz-box-flex: 1;
/* Default dialog dimensions */
width: 66ch;