mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-04 07:40:42 +00:00
Backed out 2 changesets (bug 1287209) for failures in browser_ext_pageAction_popup_resize.js
Backed out changeset 50dcca551b63 (bug 1287209) Backed out changeset cc7503f09572 (bug 1287209) MozReview-Commit-ID: A5q4SnWzgOa
This commit is contained in:
parent
6f89530374
commit
ad36def0ad
@ -26,15 +26,6 @@ const POPUP_PRELOAD_TIMEOUT_MS = 200;
|
||||
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
function isAncestorOrSelf(target, node) {
|
||||
for (; node; node = node.parentNode) {
|
||||
if (node === target) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// WeakMap[Extension -> BrowserAction]
|
||||
var browserActionMap = new WeakMap();
|
||||
|
||||
@ -96,8 +87,6 @@ BrowserAction.prototype = {
|
||||
onDestroyed: document => {
|
||||
let view = document.getElementById(this.viewId);
|
||||
if (view) {
|
||||
this.clearPopup();
|
||||
CustomizableUI.hidePanelForNode(view);
|
||||
view.remove();
|
||||
}
|
||||
},
|
||||
@ -209,8 +198,7 @@ BrowserAction.prototype = {
|
||||
// If we have a pending pre-loaded popup, cancel it after we've waited
|
||||
// long enough that we can be relatively certain it won't be opening.
|
||||
if (this.pendingPopup) {
|
||||
let {node} = this.widget.forWindow(window);
|
||||
if (isAncestorOrSelf(node, event.originalTarget)) {
|
||||
if (event.target === this.widget.forWindow(window).node) {
|
||||
this.pendingPopupTimeout = setTimeout(() => this.clearPopup(),
|
||||
POPUP_PRELOAD_TIMEOUT_MS);
|
||||
} else {
|
||||
|
@ -28,10 +28,12 @@ const POPUP_LOAD_TIMEOUT_MS = 200;
|
||||
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
// Minimum time between two resizes.
|
||||
const RESIZE_TIMEOUT = 100;
|
||||
|
||||
var {
|
||||
DefaultWeakMap,
|
||||
EventManager,
|
||||
promiseEvent,
|
||||
} = ExtensionUtils;
|
||||
|
||||
// This file provides some useful code for the |tabs| and |windows|
|
||||
@ -57,11 +59,17 @@ function promisePopupShown(popup) {
|
||||
});
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "popupStylesheets", () => {
|
||||
let stylesheets = ["chrome://browser/content/extension.css"];
|
||||
XPCOMUtils.defineLazyGetter(this, "stylesheets", () => {
|
||||
let styleSheetURI = NetUtil.newURI("chrome://browser/content/extension.css");
|
||||
let styleSheet = styleSheetService.preloadSheet(styleSheetURI,
|
||||
styleSheetService.AGENT_SHEET);
|
||||
let stylesheets = [styleSheet];
|
||||
|
||||
if (AppConstants.platform === "macosx") {
|
||||
stylesheets.push("chrome://browser/content/extension-mac.css");
|
||||
styleSheetURI = NetUtil.newURI("chrome://browser/content/extension-mac.css");
|
||||
let macStyleSheet = styleSheetService.preloadSheet(styleSheetURI,
|
||||
styleSheetService.AGENT_SHEET);
|
||||
stylesheets.push(macStyleSheet);
|
||||
}
|
||||
return stylesheets;
|
||||
});
|
||||
@ -70,10 +78,16 @@ XPCOMUtils.defineLazyGetter(this, "standaloneStylesheets", () => {
|
||||
let stylesheets = [];
|
||||
|
||||
if (AppConstants.platform === "macosx") {
|
||||
stylesheets.push("chrome://browser/content/extension-mac-panel.css");
|
||||
let styleSheetURI = NetUtil.newURI("chrome://browser/content/extension-mac-panel.css");
|
||||
let macStyleSheet = styleSheetService.preloadSheet(styleSheetURI,
|
||||
styleSheetService.AGENT_SHEET);
|
||||
stylesheets.push(macStyleSheet);
|
||||
}
|
||||
if (AppConstants.platform === "win") {
|
||||
stylesheets.push("chrome://browser/content/extension-win-panel.css");
|
||||
let styleSheetURI = NetUtil.newURI("chrome://browser/content/extension-win-panel.css");
|
||||
let winStyleSheet = styleSheetService.preloadSheet(styleSheetURI,
|
||||
styleSheetService.AGENT_SHEET);
|
||||
stylesheets.push(winStyleSheet);
|
||||
}
|
||||
return stylesheets;
|
||||
});
|
||||
@ -95,6 +109,7 @@ class BasePopup {
|
||||
this.window = viewNode.ownerGlobal;
|
||||
this.destroyed = false;
|
||||
this.fixedWidth = fixedWidth;
|
||||
this.ignoreResizes = true;
|
||||
|
||||
this.contentReady = new Promise(resolve => {
|
||||
this._resolveContentReady = resolve;
|
||||
@ -142,16 +157,12 @@ class BasePopup {
|
||||
}
|
||||
|
||||
destroyBrowser(browser) {
|
||||
let mm = browser.messageManager;
|
||||
// If the browser has already been removed from the document, because the
|
||||
// popup was closed externally, there will be no message manager here.
|
||||
if (mm) {
|
||||
mm.removeMessageListener("DOMTitleChanged", this);
|
||||
mm.removeMessageListener("Extension:BrowserBackgroundChanged", this);
|
||||
mm.removeMessageListener("Extension:BrowserContentLoaded", this);
|
||||
mm.removeMessageListener("Extension:BrowserResized", this);
|
||||
mm.removeMessageListener("Extension:DOMWindowClose", this);
|
||||
}
|
||||
browser.removeEventListener("DOMWindowCreated", this, true);
|
||||
browser.removeEventListener("load", this, true);
|
||||
browser.removeEventListener("DOMContentLoaded", this, true);
|
||||
browser.removeEventListener("DOMTitleChanged", this, true);
|
||||
browser.removeEventListener("DOMWindowClose", this, true);
|
||||
browser.removeEventListener("MozScrolledAreaChanged", this, true);
|
||||
}
|
||||
|
||||
// Returns the name of the event fired on `viewNode` when the popup is being
|
||||
@ -160,19 +171,6 @@ class BasePopup {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
get STYLESHEETS() {
|
||||
let sheets = [];
|
||||
|
||||
if (this.browserStyle) {
|
||||
sheets.push(...popupStylesheets);
|
||||
}
|
||||
if (!this.fixedWidth) {
|
||||
sheets.push(...standaloneStylesheets);
|
||||
}
|
||||
|
||||
return sheets;
|
||||
}
|
||||
|
||||
get panel() {
|
||||
let panel = this.viewNode;
|
||||
while (panel && panel.localName != "panel") {
|
||||
@ -181,40 +179,70 @@ class BasePopup {
|
||||
return panel;
|
||||
}
|
||||
|
||||
receiveMessage({name, data}) {
|
||||
switch (name) {
|
||||
case "DOMTitleChanged":
|
||||
this.viewNode.setAttribute("aria-label", this.browser.contentTitle);
|
||||
break;
|
||||
|
||||
case "Extension:BrowserBackgroundChanged":
|
||||
this.setBackground(data.background);
|
||||
break;
|
||||
|
||||
case "Extension:BrowserContentLoaded":
|
||||
this.browserLoadedDeferred.resolve();
|
||||
break;
|
||||
|
||||
case "Extension:BrowserResized":
|
||||
this._resolveContentReady();
|
||||
if (this.ignoreResizes) {
|
||||
this.dimensions = data;
|
||||
} else {
|
||||
this.resizeBrowser(data);
|
||||
}
|
||||
break;
|
||||
|
||||
case "Extension:DOMWindowClose":
|
||||
this.closePopup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case this.DESTROY_EVENT:
|
||||
this.destroy();
|
||||
break;
|
||||
|
||||
case "DOMWindowCreated":
|
||||
if (event.target === this.browser.contentDocument) {
|
||||
let winUtils = this.browser.contentWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
if (this.browserStyle) {
|
||||
for (let stylesheet of stylesheets) {
|
||||
winUtils.addSheet(stylesheet, winUtils.AGENT_SHEET);
|
||||
}
|
||||
}
|
||||
if (!this.fixedWidth) {
|
||||
for (let stylesheet of standaloneStylesheets) {
|
||||
winUtils.addSheet(stylesheet, winUtils.AGENT_SHEET);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "DOMWindowClose":
|
||||
if (event.target === this.browser.contentWindow) {
|
||||
event.preventDefault();
|
||||
this.closePopup();
|
||||
}
|
||||
break;
|
||||
|
||||
case "DOMTitleChanged":
|
||||
this.viewNode.setAttribute("aria-label", this.browser.contentTitle);
|
||||
break;
|
||||
|
||||
case "DOMContentLoaded":
|
||||
this.browserLoadedDeferred.resolve();
|
||||
this.resizeBrowser(true);
|
||||
break;
|
||||
|
||||
case "load":
|
||||
// We use a capturing listener, so we get this event earlier than any
|
||||
// load listeners in the content page. Resizing after a timeout ensures
|
||||
// that we calculate the size after the entire event cycle has completed
|
||||
// (unless someone spins the event loop, anyway), and hopefully after
|
||||
// the content has made any modifications.
|
||||
Promise.resolve().then(() => {
|
||||
this.resizeBrowser(true);
|
||||
});
|
||||
|
||||
// Mutation observer to make sure the panel shrinks when the content does.
|
||||
new this.browser.contentWindow.MutationObserver(this.resizeBrowser.bind(this)).observe(
|
||||
this.browser.contentDocument.documentElement, {
|
||||
attributes: true,
|
||||
characterData: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
break;
|
||||
|
||||
case "MozScrolledAreaChanged":
|
||||
this.resizeBrowser();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,12 +269,12 @@ class BasePopup {
|
||||
viewNode.appendChild(this.browser);
|
||||
|
||||
let initBrowser = browser => {
|
||||
let mm = browser.messageManager;
|
||||
mm.addMessageListener("DOMTitleChanged", this);
|
||||
mm.addMessageListener("Extension:BrowserBackgroundChanged", this);
|
||||
mm.addMessageListener("Extension:BrowserContentLoaded", this);
|
||||
mm.addMessageListener("Extension:BrowserResized", this);
|
||||
mm.addMessageListener("Extension:DOMWindowClose", this, true);
|
||||
browser.addEventListener("DOMWindowCreated", this, true);
|
||||
browser.addEventListener("load", this, true);
|
||||
browser.addEventListener("DOMContentLoaded", this, true);
|
||||
browser.addEventListener("DOMTitleChanged", this, true);
|
||||
browser.addEventListener("DOMWindowClose", this, true);
|
||||
browser.addEventListener("MozScrolledAreaChanged", this, true);
|
||||
};
|
||||
|
||||
if (!popupURL) {
|
||||
@ -254,28 +282,82 @@ class BasePopup {
|
||||
return this.browser;
|
||||
}
|
||||
|
||||
return promiseEvent(this.browser, "load").then(() => {
|
||||
return new Promise(resolve => {
|
||||
// The first load event is for about:blank.
|
||||
// We can't finish setting up the browser until the binding has fully
|
||||
// initialized. Waiting for the first load event guarantees that it has.
|
||||
let loadListener = event => {
|
||||
this.browser.removeEventListener("load", loadListener, true);
|
||||
resolve();
|
||||
};
|
||||
this.browser.addEventListener("load", loadListener, true);
|
||||
}).then(() => {
|
||||
initBrowser(this.browser);
|
||||
|
||||
let mm = this.browser.messageManager;
|
||||
let {contentWindow} = this.browser;
|
||||
|
||||
mm.loadFrameScript(
|
||||
"chrome://extensions/content/ext-browser-content.js", false);
|
||||
|
||||
mm.sendAsyncMessage("Extension:InitBrowser", {
|
||||
allowScriptsToClose: true,
|
||||
fixedWidth: this.fixedWidth,
|
||||
maxWidth: 800,
|
||||
maxHeight: 600,
|
||||
stylesheets: this.STYLESHEETS,
|
||||
});
|
||||
contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.allowScriptsToClose();
|
||||
|
||||
this.browser.setAttribute("src", popupURL);
|
||||
});
|
||||
}
|
||||
|
||||
resizeBrowser({width, height, detail}) {
|
||||
// Resizes the browser to match the preferred size of the content (debounced).
|
||||
resizeBrowser(ignoreThrottling = false) {
|
||||
if (this.ignoreResizes) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ignoreThrottling && this.resizeTimeout) {
|
||||
this.window.clearTimeout(this.resizeTimeout);
|
||||
this.resizeTimeout = null;
|
||||
}
|
||||
|
||||
if (this.resizeTimeout == null) {
|
||||
this.resizeTimeout = this.window.setTimeout(() => {
|
||||
try {
|
||||
this._resizeBrowser();
|
||||
} finally {
|
||||
this.resizeTimeout = null;
|
||||
}
|
||||
}, RESIZE_TIMEOUT);
|
||||
|
||||
this._resizeBrowser();
|
||||
}
|
||||
}
|
||||
|
||||
_resizeBrowser() {
|
||||
let doc = this.browser && this.browser.contentDocument;
|
||||
if (!doc || !doc.documentElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
let root = doc.documentElement;
|
||||
let body = doc.body;
|
||||
if (!body || doc.compatMode == "BackCompat") {
|
||||
// In quirks mode, the root element is used as the scroll frame, and the
|
||||
// body lies about its scroll geometry, and returns the values for the
|
||||
// root instead.
|
||||
body = root;
|
||||
}
|
||||
|
||||
|
||||
if (this.fixedWidth) {
|
||||
// If we're in a fixed-width area (namely a slide-in subview of the main
|
||||
// menu panel), we need to calculate the view height based on the
|
||||
// preferred height of the content document's root scrollable element at the
|
||||
// current width, rather than the complete preferred dimensions of the
|
||||
// content window.
|
||||
|
||||
// Compensate for any offsets (margin, padding, ...) between the scroll
|
||||
// area of the body and the outer height of the document.
|
||||
let getHeight = elem => elem.getBoundingClientRect(elem).height;
|
||||
let bodyPadding = getHeight(root) - getHeight(body);
|
||||
|
||||
let height = Math.ceil(body.scrollHeight + bodyPadding);
|
||||
|
||||
// Figure out how much extra space we have on the side of the panel
|
||||
// opposite the arrow.
|
||||
let side = this.panel.getAttribute("side") == "top" ? "bottom" : "top";
|
||||
@ -292,32 +374,48 @@ class BasePopup {
|
||||
height = Math.max(height, this.viewHeight);
|
||||
this.viewNode.style.maxHeight = `${height}px`;
|
||||
} else {
|
||||
// Copy the background color of the document's body to the panel if it's
|
||||
// fully opaque.
|
||||
let panelBackground = "";
|
||||
let panelArrow = "";
|
||||
|
||||
let background = doc.defaultView.getComputedStyle(body).backgroundColor;
|
||||
if (background != "transparent") {
|
||||
let bgColor = colorUtils.colorToRGBA(background);
|
||||
if (bgColor.a == 1) {
|
||||
panelBackground = background;
|
||||
let borderColor = this.borderColor || background;
|
||||
|
||||
panelArrow = `url("data:image/svg+xml,${encodeURIComponent(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="10">
|
||||
<path d="M 0,10 L 10,0 20,10 z" fill="${borderColor}"/>
|
||||
<path d="M 1,10 L 10,1 19,10 z" fill="${background}"/>
|
||||
</svg>
|
||||
`)}")`;
|
||||
}
|
||||
}
|
||||
|
||||
this.panel.style.setProperty("--arrowpanel-background", panelBackground);
|
||||
this.panel.style.setProperty("--panel-arrow-image-vertical", panelArrow);
|
||||
|
||||
|
||||
// Adjust the size of the browser based on its content's preferred size.
|
||||
let {contentViewer} = this.browser.docShell;
|
||||
let ratio = this.window.devicePixelRatio;
|
||||
|
||||
let w = {}, h = {};
|
||||
contentViewer.getContentSizeConstrained(800 * ratio, 600 * ratio, w, h);
|
||||
let width = Math.ceil(w.value / ratio);
|
||||
let height = Math.ceil(h.value / ratio);
|
||||
|
||||
this.browser.style.width = `${width}px`;
|
||||
this.browser.style.height = `${height}px`;
|
||||
}
|
||||
|
||||
let event = new this.window.CustomEvent("WebExtPopupResized", {detail});
|
||||
let event = new this.window.CustomEvent("WebExtPopupResized");
|
||||
this.browser.dispatchEvent(event);
|
||||
}
|
||||
|
||||
setBackground(background) {
|
||||
let panelBackground = "";
|
||||
let panelArrow = "";
|
||||
|
||||
if (background) {
|
||||
let borderColor = this.borderColor || background;
|
||||
|
||||
panelBackground = background;
|
||||
panelArrow = `url("data:image/svg+xml,${encodeURIComponent(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="10">
|
||||
<path d="M 0,10 L 10,0 20,10 z" fill="${borderColor}"/>
|
||||
<path d="M 1,10 L 10,1 19,10 z" fill="${background}"/>
|
||||
</svg>
|
||||
`)}")`;
|
||||
}
|
||||
|
||||
this.panel.style.setProperty("--arrowpanel-background", panelBackground);
|
||||
this.panel.style.setProperty("--panel-arrow-image-vertical", panelArrow);
|
||||
this._resolveContentReady();
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,7 +426,7 @@ class BasePopup {
|
||||
*/
|
||||
BasePopup.instances = new DefaultWeakMap(() => new WeakMap());
|
||||
|
||||
class PanelPopup extends BasePopup {
|
||||
global.PanelPopup = class PanelPopup extends BasePopup {
|
||||
constructor(extension, imageNode, popupURL, browserStyle) {
|
||||
let document = imageNode.ownerDocument;
|
||||
|
||||
@ -343,14 +441,10 @@ class PanelPopup extends BasePopup {
|
||||
|
||||
super(extension, panel, popupURL, browserStyle);
|
||||
|
||||
this.ignoreResizes = false;
|
||||
|
||||
this.contentReady.then(() => {
|
||||
panel.openPopup(imageNode, "bottomcenter topright", 0, 0, false, false);
|
||||
|
||||
let event = new this.window.CustomEvent("WebExtPopupLoaded", {
|
||||
bubbles: true,
|
||||
detail: {extension},
|
||||
});
|
||||
this.browser.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
@ -371,9 +465,9 @@ class PanelPopup extends BasePopup {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ViewPopup extends BasePopup {
|
||||
global.ViewPopup = class ViewPopup extends BasePopup {
|
||||
constructor(extension, window, popupURL, browserStyle, fixedWidth) {
|
||||
let document = window.document;
|
||||
|
||||
@ -386,8 +480,6 @@ class ViewPopup extends BasePopup {
|
||||
|
||||
super(extension, panel, popupURL, browserStyle, fixedWidth);
|
||||
|
||||
this.ignoreResizes = true;
|
||||
|
||||
this.attached = false;
|
||||
this.tempPanel = panel;
|
||||
|
||||
@ -425,10 +517,6 @@ class ViewPopup extends BasePopup {
|
||||
]),
|
||||
]);
|
||||
|
||||
if (!this.destroyed && !this.panel) {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
if (this.destroyed) {
|
||||
return false;
|
||||
}
|
||||
@ -461,19 +549,11 @@ class ViewPopup extends BasePopup {
|
||||
this.destroyBrowser(browser);
|
||||
|
||||
this.ignoreResizes = false;
|
||||
if (this.dimensions) {
|
||||
this.resizeBrowser(this.dimensions);
|
||||
}
|
||||
this.resizeBrowser(true);
|
||||
|
||||
this.tempPanel.remove();
|
||||
this.tempPanel = null;
|
||||
|
||||
let event = new this.window.CustomEvent("WebExtPopupLoaded", {
|
||||
bubbles: true,
|
||||
detail: {extension: this.extension},
|
||||
});
|
||||
this.browser.dispatchEvent(event);
|
||||
|
||||
return true;
|
||||
}.bind(this));
|
||||
}
|
||||
@ -498,9 +578,7 @@ class ViewPopup extends BasePopup {
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(global, {PanelPopup, ViewPopup});
|
||||
};
|
||||
|
||||
// Manages tab-specific context data, and dispatching tab select events
|
||||
// across all windows.
|
||||
|
@ -8,6 +8,18 @@ function* openPanel(extension, win = window) {
|
||||
return yield awaitExtensionPanel(extension, win);
|
||||
}
|
||||
|
||||
function* awaitResize(browser) {
|
||||
// Debouncing code makes this a bit racy.
|
||||
// Try to skip the first, early resize, and catch the resize event we're
|
||||
// looking for, but don't wait longer than a few seconds.
|
||||
|
||||
return Promise.race([
|
||||
BrowserTestUtils.waitForEvent(browser, "WebExtPopupResized")
|
||||
.then(() => BrowserTestUtils.waitForEvent(browser, "WebExtPopupResized")),
|
||||
new Promise(resolve => setTimeout(resolve, 5000)),
|
||||
]);
|
||||
}
|
||||
|
||||
add_task(function* testBrowserActionPopupResize() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
@ -18,33 +30,33 @@ add_task(function* testBrowserActionPopupResize() {
|
||||
},
|
||||
|
||||
files: {
|
||||
"popup.html": '<!DOCTYPE html><html><head><meta charset="utf-8"></head></html>',
|
||||
"popup.html": '<html><head><meta charset="utf-8"></head></html>',
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
clickBrowserAction(extension, window);
|
||||
|
||||
let browser = yield openPanel(extension);
|
||||
let panelWindow = browser.contentWindow;
|
||||
let panelBody = panelWindow.document.body;
|
||||
|
||||
function* checkSize(expected) {
|
||||
let dims = yield promiseContentDimensions(browser);
|
||||
|
||||
is(dims.window.innerHeight, expected, `Panel window should be ${expected}px tall`);
|
||||
is(dims.body.clientHeight, dims.body.scrollHeight,
|
||||
function checkSize(expected) {
|
||||
is(panelWindow.innerHeight, expected, `Panel window should be ${expected}px tall`);
|
||||
is(panelBody.clientHeight, panelBody.scrollHeight,
|
||||
"Panel body should be tall enough to fit its contents");
|
||||
|
||||
// Tolerate if it is 1px too wide, as that may happen with the current resizing method.
|
||||
ok(Math.abs(dims.window.innerWidth - expected) <= 1, `Panel window should be ${expected}px wide`);
|
||||
is(dims.body.clientWidth, dims.body.scrollWidth,
|
||||
ok(Math.abs(panelWindow.innerWidth - expected) <= 1, `Panel window should be ${expected}px wide`);
|
||||
is(panelBody.clientWidth, panelBody.scrollWidth,
|
||||
"Panel body should be wide enough to fit its contents");
|
||||
}
|
||||
|
||||
/* eslint-disable mozilla/no-cpows-in-tests */
|
||||
function setSize(size) {
|
||||
content.document.body.style.height = `${size}px`;
|
||||
content.document.body.style.width = `${size}px`;
|
||||
panelBody.style.height = `${size}px`;
|
||||
panelBody.style.width = `${size}px`;
|
||||
}
|
||||
/* eslint-enable mozilla/no-cpows-in-tests */
|
||||
|
||||
let sizes = [
|
||||
200,
|
||||
@ -53,8 +65,9 @@ add_task(function* testBrowserActionPopupResize() {
|
||||
];
|
||||
|
||||
for (let size of sizes) {
|
||||
yield alterContent(browser, setSize, size);
|
||||
yield checkSize(size);
|
||||
setSize(size);
|
||||
yield awaitResize(browser);
|
||||
checkSize(size);
|
||||
}
|
||||
|
||||
yield closeBrowserAction(extension);
|
||||
@ -109,32 +122,27 @@ function* testPopupSize(standardsMode, browserWin = window, arrowSide = "top") {
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
/* eslint-disable mozilla/no-cpows-in-tests */
|
||||
|
||||
if (arrowSide == "top") {
|
||||
// Test the standalone panel for a toolbar button.
|
||||
let browser = yield openPanel(extension, browserWin);
|
||||
let win = browser.contentWindow;
|
||||
let body = win.document.body;
|
||||
|
||||
let dims = yield promiseContentDimensions(browser);
|
||||
let isStandards = win.document.compatMode != "BackCompat";
|
||||
is(isStandards, standardsMode, "Document has the expected compat mode");
|
||||
|
||||
is(dims.isStandards, standardsMode, "Document has the expected compat mode");
|
||||
let {innerWidth, innerHeight} = win;
|
||||
|
||||
let {innerWidth, innerHeight} = dims.window;
|
||||
body.classList.add("bigger");
|
||||
yield awaitResize(browser);
|
||||
|
||||
dims = yield alterContent(browser, () => {
|
||||
content.document.body.classList.add("bigger");
|
||||
});
|
||||
|
||||
let win = dims.window;
|
||||
is(win.innerHeight, innerHeight, "Window height should not change");
|
||||
ok(win.innerWidth > innerWidth, `Window width should increase (${win.innerWidth} > ${innerWidth})`);
|
||||
|
||||
|
||||
dims = yield alterContent(browser, () => {
|
||||
content.document.body.classList.remove("bigger");
|
||||
});
|
||||
body.classList.remove("bigger");
|
||||
yield awaitResize(browser);
|
||||
|
||||
win = dims.window;
|
||||
is(win.innerHeight, innerHeight, "Window height should not change");
|
||||
|
||||
// The getContentSize calculation is not always reliable to single-pixel
|
||||
@ -151,6 +159,8 @@ function* testPopupSize(standardsMode, browserWin = window, arrowSide = "top") {
|
||||
CustomizableUI.addWidgetToArea(widget.id, CustomizableUI.AREA_PANEL);
|
||||
|
||||
let browser = yield openPanel(extension, browserWin);
|
||||
let win = browser.contentWindow;
|
||||
let body = win.document.body;
|
||||
|
||||
let {panel} = browserWin.PanelUI;
|
||||
let origPanelRect = panel.getBoundingClientRect();
|
||||
@ -164,7 +174,7 @@ function* testPopupSize(standardsMode, browserWin = window, arrowSide = "top") {
|
||||
ok(panelRect.top, origPanelRect.top, "Panel has not moved downwards");
|
||||
ok(panelRect.bottom >= origPanelRect.bottom, `Panel has not shrunk from original size (${panelRect.bottom} >= ${origPanelRect.bottom})`);
|
||||
|
||||
let screenBottom = browserWin.screen.availTop + browserWin.screen.availHeight;
|
||||
let screenBottom = browserWin.screen.availTop + win.screen.availHeight;
|
||||
let panelBottom = browserWin.mozInnerScreenY + panelRect.bottom;
|
||||
ok(panelBottom <= screenBottom, `Bottom of popup should be on-screen. (${panelBottom} <= ${screenBottom})`);
|
||||
} else {
|
||||
@ -176,33 +186,28 @@ function* testPopupSize(standardsMode, browserWin = window, arrowSide = "top") {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let isStandards = win.document.compatMode != "BackCompat";
|
||||
is(isStandards, standardsMode, "Document has the expected compat mode");
|
||||
|
||||
// Wait long enough to make sure the initial resize debouncing timer has
|
||||
// expired.
|
||||
yield new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
let dims = yield promiseContentDimensions(browser);
|
||||
|
||||
is(dims.isStandards, standardsMode, "Document has the expected compat mode");
|
||||
|
||||
// If the browser's preferred height is smaller than the initial height of the
|
||||
// panel, then it will still take up the full available vertical space. Even
|
||||
// so, we need to check that we've gotten the preferred height calculation
|
||||
// correct, so check that explicitly.
|
||||
let getHeight = () => parseFloat(browser.style.height);
|
||||
|
||||
let {innerWidth, innerHeight} = dims.window;
|
||||
let {innerWidth, innerHeight} = win;
|
||||
let height = getHeight();
|
||||
|
||||
|
||||
let setClass = className => {
|
||||
content.document.body.className = className;
|
||||
};
|
||||
|
||||
info("Increase body children's width. " +
|
||||
"Expect them to wrap, and the frame to grow vertically rather than widen.");
|
||||
|
||||
dims = yield alterContent(browser, setClass, "big");
|
||||
let win = dims.window;
|
||||
body.className = "big";
|
||||
yield awaitResize(browser);
|
||||
|
||||
ok(getHeight() > height, `Browser height should increase (${getHeight()} > ${height})`);
|
||||
|
||||
@ -215,9 +220,8 @@ function* testPopupSize(standardsMode, browserWin = window, arrowSide = "top") {
|
||||
|
||||
info("Increase body children's width and height. " +
|
||||
"Expect them to wrap, and the frame to grow vertically rather than widen.");
|
||||
|
||||
dims = yield alterContent(browser, setClass, "bigger");
|
||||
win = dims.window;
|
||||
body.className = "bigger";
|
||||
yield awaitResize(browser);
|
||||
|
||||
ok(getHeight() > height, `Browser height should increase (${getHeight()} > ${height})`);
|
||||
|
||||
@ -230,9 +234,8 @@ function* testPopupSize(standardsMode, browserWin = window, arrowSide = "top") {
|
||||
|
||||
info("Increase body height beyond the height of the screen. " +
|
||||
"Expect the panel to grow to accommodate, but not larger than the height of the screen.");
|
||||
|
||||
dims = yield alterContent(browser, setClass, "huge");
|
||||
win = dims.window;
|
||||
body.className = "huge";
|
||||
yield awaitResize(browser);
|
||||
|
||||
ok(getHeight() > height, `Browser height should increase (${getHeight()} > ${height})`);
|
||||
|
||||
@ -245,8 +248,8 @@ function* testPopupSize(standardsMode, browserWin = window, arrowSide = "top") {
|
||||
|
||||
|
||||
info("Restore original styling. Expect original dimensions.");
|
||||
dims = yield alterContent(browser, setClass, "");
|
||||
win = dims.window;
|
||||
body.className = "";
|
||||
yield awaitResize(browser);
|
||||
|
||||
is(getHeight(), height, "Browser height should return to its original value");
|
||||
|
||||
|
@ -2,9 +2,19 @@
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
add_task(function* testPageActionPopupResize() {
|
||||
let browser;
|
||||
function* awaitResize(browser) {
|
||||
// Debouncing code makes this a bit racy.
|
||||
// Try to skip the first, early resize, and catch the resize event we're
|
||||
// looking for, but don't wait longer than a few seconds.
|
||||
|
||||
return Promise.race([
|
||||
BrowserTestUtils.waitForEvent(browser, "WebExtPopupResized")
|
||||
.then(() => BrowserTestUtils.waitForEvent(browser, "WebExtPopupResized")),
|
||||
new Promise(resolve => setTimeout(resolve, 5000)),
|
||||
]);
|
||||
}
|
||||
|
||||
add_task(function* testPageActionPopupResize() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"page_action": {
|
||||
@ -13,7 +23,6 @@ add_task(function* testPageActionPopupResize() {
|
||||
},
|
||||
},
|
||||
background: function() {
|
||||
/* global browser */
|
||||
browser.tabs.query({active: true, currentWindow: true}, tabs => {
|
||||
const tabId = tabs[0].id;
|
||||
|
||||
@ -33,31 +42,35 @@ add_task(function* testPageActionPopupResize() {
|
||||
|
||||
clickPageAction(extension, window);
|
||||
|
||||
browser = yield awaitExtensionPanel(extension);
|
||||
let {target: panelDocument} = yield BrowserTestUtils.waitForEvent(document, "load", true, (event) => {
|
||||
info(`Loaded ${event.target.location}`);
|
||||
return event.target.location && event.target.location.href.endsWith("popup.html");
|
||||
});
|
||||
|
||||
function* checkSize(expected) {
|
||||
let dims = yield promiseContentDimensions(browser);
|
||||
let {body, root} = dims;
|
||||
let panelWindow = panelDocument.defaultView;
|
||||
let panelBody = panelDocument.body.firstChild;
|
||||
let body = panelDocument.body;
|
||||
let root = panelDocument.documentElement;
|
||||
|
||||
is(dims.window.innerHeight, expected, `Panel window should be ${expected}px tall`);
|
||||
function checkSize(expected) {
|
||||
is(panelWindow.innerHeight, expected, `Panel window should be ${expected}px tall`);
|
||||
is(body.clientHeight, body.scrollHeight,
|
||||
"Panel body should be tall enough to fit its contents");
|
||||
is(root.clientHeight, root.scrollHeight,
|
||||
"Panel root should be tall enough to fit its contents");
|
||||
|
||||
// Tolerate if it is 1px too wide, as that may happen with the current resizing method.
|
||||
ok(Math.abs(dims.window.innerWidth - expected) <= 1, `Panel window should be ${expected}px wide`);
|
||||
ok(Math.abs(panelWindow.innerWidth - expected) <= 1, `Panel window should be ${expected}px wide`);
|
||||
is(body.clientWidth, body.scrollWidth,
|
||||
"Panel body should be wide enough to fit its contents");
|
||||
}
|
||||
|
||||
/* eslint-disable mozilla/no-cpows-in-tests */
|
||||
function setSize(size) {
|
||||
let elem = content.document.body.firstChild;
|
||||
elem.style.height = `${size}px`;
|
||||
elem.style.width = `${size}px`;
|
||||
panelBody.style.height = `${size}px`;
|
||||
panelBody.style.width = `${size}px`;
|
||||
|
||||
return BrowserTestUtils.waitForEvent(panelWindow, "resize");
|
||||
}
|
||||
/* eslint-enable mozilla/no-cpows-in-tests */
|
||||
|
||||
let sizes = [
|
||||
200,
|
||||
@ -66,25 +79,22 @@ add_task(function* testPageActionPopupResize() {
|
||||
];
|
||||
|
||||
for (let size of sizes) {
|
||||
yield alterContent(browser, setSize, size);
|
||||
yield checkSize(size);
|
||||
yield setSize(size);
|
||||
checkSize(size);
|
||||
}
|
||||
|
||||
yield alterContent(browser, setSize, 1400);
|
||||
|
||||
let dims = yield promiseContentDimensions(browser);
|
||||
let {body, root} = dims;
|
||||
yield setSize(1400);
|
||||
|
||||
if (AppConstants.platform == "win") {
|
||||
ok(dims.window.innerWidth >= 750 && dims.window.innerWidth <= 800,
|
||||
`Panel window width ${dims.window.innerWidth} is in acceptable range`);
|
||||
ok(panelWindow.innerWidth >= 750 && panelWindow.innerWidth <= 800,
|
||||
`Panel window width ${panelWindow.innerWidth} is in acceptable range`);
|
||||
} else {
|
||||
is(dims.window.innerWidth, 800, "Panel window width");
|
||||
is(panelWindow.innerWidth, 800, "Panel window width");
|
||||
}
|
||||
ok(body.clientWidth <= 800, `Panel body width ${body.clientWidth} is less than 800`);
|
||||
is(body.scrollWidth, 1400, "Panel body scroll width");
|
||||
|
||||
is(dims.window.innerHeight, 600, "Panel window height");
|
||||
is(panelWindow.innerHeight, 600, "Panel window height");
|
||||
ok(root.clientHeight <= 600, `Panel root height (${root.clientHeight}px) is less than 600px`);
|
||||
is(root.scrollHeight, 1400, "Panel root scroll height");
|
||||
|
||||
@ -131,27 +141,29 @@ add_task(function* testPageActionPopupReflow() {
|
||||
|
||||
browser = yield awaitExtensionPanel(extension);
|
||||
|
||||
/* eslint-disable mozilla/no-cpows-in-tests */
|
||||
let win = browser.contentWindow;
|
||||
let body = win.document.body;
|
||||
let root = win.document.documentElement;
|
||||
|
||||
function setSize(size) {
|
||||
content.document.body.style.fontSize = `${size}px`;
|
||||
body.style.fontSize = `${size}px`;
|
||||
|
||||
return awaitResize(browser);
|
||||
}
|
||||
/* eslint-enable mozilla/no-cpows-in-tests */
|
||||
|
||||
yield alterContent(browser, setSize, 18);
|
||||
yield setSize(18);
|
||||
|
||||
let dims = yield promiseContentDimensions(browser);
|
||||
|
||||
is(dims.window.innerWidth, 800, "Panel window should be 800px wide");
|
||||
is(dims.body.clientWidth, 800, "Panel body should be 800px wide");
|
||||
is(dims.body.clientWidth, dims.body.scrollWidth,
|
||||
is(win.innerWidth, 800, "Panel window should be 800px wide");
|
||||
is(body.clientWidth, 800, "Panel body should be 800px wide");
|
||||
is(body.clientWidth, body.scrollWidth,
|
||||
"Panel body should be wide enough to fit its contents");
|
||||
|
||||
ok(dims.window.innerHeight > 36,
|
||||
`Panel window height (${dims.window.innerHeight}px) should be taller than two lines of text.`);
|
||||
ok(win.innerHeight > 36,
|
||||
`Panel window height (${win.innerHeight}px) should be taller than two lines of text.`);
|
||||
|
||||
is(dims.body.clientHeight, dims.body.scrollHeight,
|
||||
is(body.clientHeight, body.scrollHeight,
|
||||
"Panel body should be tall enough to fit its contents");
|
||||
is(dims.root.clientHeight, dims.root.scrollHeight,
|
||||
is(root.clientHeight, root.scrollHeight,
|
||||
"Panel root should be tall enough to fit its contents");
|
||||
|
||||
yield extension.unload();
|
||||
|
@ -2,6 +2,18 @@
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
function* awaitResize(browser) {
|
||||
// Debouncing code makes this a bit racy.
|
||||
// Try to skip the first, early resize, and catch the resize event we're
|
||||
// looking for, but don't wait longer than a few seconds.
|
||||
|
||||
return Promise.race([
|
||||
BrowserTestUtils.waitForEvent(browser, "WebExtPopupResized")
|
||||
.then(() => BrowserTestUtils.waitForEvent(browser, "WebExtPopupResized")),
|
||||
new Promise(resolve => setTimeout(resolve, 5000)),
|
||||
]);
|
||||
}
|
||||
|
||||
add_task(function* testPopupBackground() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
background() {
|
||||
@ -67,34 +79,26 @@ add_task(function* testPopupBackground() {
|
||||
isnot(borderIndex, backgroundIndex, "Border and background fills are separate elements");
|
||||
};
|
||||
|
||||
function getBackground(browser) {
|
||||
return ContentTask.spawn(browser, null, function* () {
|
||||
return content.getComputedStyle(content.document.body)
|
||||
.backgroundColor;
|
||||
});
|
||||
}
|
||||
|
||||
/* eslint-disable mozilla/no-cpows-in-tests */
|
||||
let setBackground = color => {
|
||||
content.document.body.style.backgroundColor = color;
|
||||
};
|
||||
/* eslint-enable mozilla/no-cpows-in-tests */
|
||||
let win = browser.contentWindow;
|
||||
let body = win.document.body;
|
||||
|
||||
yield new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
info("Test that initial background color is applied");
|
||||
|
||||
checkArrow(yield getBackground(browser));
|
||||
checkArrow(win.getComputedStyle(body).backgroundColor);
|
||||
|
||||
info("Test that dynamically-changed background color is applied");
|
||||
|
||||
yield alterContent(browser, setBackground, "black");
|
||||
body.style.backgroundColor = "black";
|
||||
yield awaitResize(browser);
|
||||
|
||||
checkArrow(yield getBackground(browser));
|
||||
checkArrow(win.getComputedStyle(body).backgroundColor);
|
||||
|
||||
info("Test that non-opaque background color results in default styling");
|
||||
|
||||
yield alterContent(browser, setBackground, "rgba(1, 2, 3, .9)");
|
||||
body.style.backgroundColor = "rgba(1, 2, 3, .9)";
|
||||
yield awaitResize(browser);
|
||||
|
||||
checkArrow(null);
|
||||
}
|
||||
|
@ -42,24 +42,17 @@ add_task(function* testPopupBorderRadius() {
|
||||
let viewNode = browser.parentNode === panel ? browser : browser.parentNode;
|
||||
let viewStyle = getComputedStyle(viewNode);
|
||||
|
||||
let props = ["borderTopLeftRadius", "borderTopRightRadius",
|
||||
"borderBottomRightRadius", "borderBottomLeftRadius"];
|
||||
let win = browser.contentWindow;
|
||||
let bodyStyle = win.getComputedStyle(win.document.body);
|
||||
|
||||
/* eslint-disable mozilla/no-cpows-in-tests */
|
||||
let bodyStyle = yield ContentTask.spawn(browser, props, function* (props) {
|
||||
let bodyStyle = content.getComputedStyle(content.document.body);
|
||||
|
||||
return new Map(props.map(prop => [prop, bodyStyle[prop]]));
|
||||
});
|
||||
/* eslint-enable mozilla/no-cpows-in-tests */
|
||||
|
||||
for (let prop of props) {
|
||||
for (let prop of ["borderTopLeftRadius", "borderTopRightRadius",
|
||||
"borderBottomRightRadius", "borderBottomLeftRadius"]) {
|
||||
if (standAlone) {
|
||||
is(viewStyle[prop], panelStyle[prop], `Panel and view ${prop} should be the same`);
|
||||
is(bodyStyle.get(prop), panelStyle[prop], `Panel and body ${prop} should be the same`);
|
||||
is(bodyStyle[prop], panelStyle[prop], `Panel and body ${prop} should be the same`);
|
||||
} else {
|
||||
is(viewStyle[prop], "0px", `View node ${prop} should be 0px`);
|
||||
is(bodyStyle.get(prop), "0px", `Body node ${prop} should be 0px`);
|
||||
is(bodyStyle[prop], "0px", `Body node ${prop} should be 0px`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,5 @@ add_task(function* testPageAction() {
|
||||
|
||||
yield extension.unload();
|
||||
|
||||
yield new Promise(resolve => setTimeout(resolve, 0));
|
||||
|
||||
is(panel.parentNode, null, "Panel should be removed from the document");
|
||||
});
|
||||
|
@ -11,8 +11,7 @@
|
||||
* openContextMenu closeContextMenu
|
||||
* openExtensionContextMenu closeExtensionContextMenu
|
||||
* imageBuffer getListStyleImage getPanelForNode
|
||||
* awaitExtensionPanel awaitPopupResize
|
||||
* promiseContentDimensions alterContent
|
||||
* awaitExtensionPanel
|
||||
*/
|
||||
|
||||
var {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
@ -85,44 +84,6 @@ function promisePopupHidden(popup) {
|
||||
});
|
||||
}
|
||||
|
||||
function promiseContentDimensions(browser) {
|
||||
return ContentTask.spawn(browser, null, function* () {
|
||||
function copyProps(obj, props) {
|
||||
let res = {};
|
||||
for (let prop of props) {
|
||||
res[prop] = obj[prop];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
return {
|
||||
window: copyProps(content,
|
||||
["innerWidth", "innerHeight", "outerWidth", "outerHeight",
|
||||
"scrollX", "scrollY", "scrollMaxX", "scrollMaxY"]),
|
||||
body: copyProps(content.document.body,
|
||||
["clientWidth", "clientHeight", "scrollWidth", "scrollHeight"]),
|
||||
root: copyProps(content.document.documentElement,
|
||||
["clientWidth", "clientHeight", "scrollWidth", "scrollHeight"]),
|
||||
|
||||
isStandards: content.document.compatMode !== "BackCompat",
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function* awaitPopupResize(browser) {
|
||||
return BrowserTestUtils.waitForEvent(browser, "WebExtPopupResized",
|
||||
event => event.detail === "delayed");
|
||||
}
|
||||
|
||||
function alterContent(browser, task, arg = null) {
|
||||
return Promise.all([
|
||||
ContentTask.spawn(browser, arg, task),
|
||||
awaitPopupResize(browser),
|
||||
]).then(() => {
|
||||
return promiseContentDimensions(browser);
|
||||
});
|
||||
}
|
||||
|
||||
function getPanelForNode(node) {
|
||||
while (node.localName != "panel") {
|
||||
node = node.parentNode;
|
||||
@ -130,10 +91,19 @@ function getPanelForNode(node) {
|
||||
return node;
|
||||
}
|
||||
|
||||
var awaitExtensionPanel = Task.async(function* (extension, win = window) {
|
||||
let {originalTarget: browser} = yield BrowserTestUtils.waitForEvent(
|
||||
win.document, "WebExtPopupLoaded", true,
|
||||
event => event.detail.extension.id === extension.id);
|
||||
var awaitExtensionPanel = Task.async(function* (extension, win = window, filename = "popup.html") {
|
||||
let {target} = yield BrowserTestUtils.waitForEvent(win.document, "load", true, (event) => {
|
||||
return event.target.location && event.target.location.href.endsWith(filename);
|
||||
});
|
||||
|
||||
let browser = target.defaultView
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler;
|
||||
|
||||
if (browser.matches(".webextension-preload-browser")) {
|
||||
let event = yield BrowserTestUtils.waitForEvent(browser, "SwapDocShells");
|
||||
browser = event.detail;
|
||||
}
|
||||
|
||||
yield promisePopupShown(getPanelForNode(browser));
|
||||
|
||||
|
@ -10,4 +10,5 @@
|
||||
// browser_addons_debug_webextension.js
|
||||
function myWebExtensionPopupAddonFunction() { // eslint-disable-line no-unused-vars
|
||||
console.log("Popup page function called", browser.runtime.getManifest());
|
||||
window.close();
|
||||
}
|
||||
|
@ -54,13 +54,17 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
|
||||
"resource://gre/modules/Preferences.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "require",
|
||||
"resource://devtools/shared/Loader.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
|
||||
"resource://gre/modules/Schemas.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "require", () => {
|
||||
let obj = {};
|
||||
Cu.import("resource://devtools/shared/Loader.jsm", obj);
|
||||
return obj.require;
|
||||
});
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionContent.jsm");
|
||||
Cu.import("resource://gre/modules/ExtensionManagement.jsm");
|
||||
|
||||
|
@ -28,8 +28,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "Locale",
|
||||
"resource://gre/modules/Locale.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
|
||||
"resource://gre/modules/MessageChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
|
||||
"resource://gre/modules/Preferences.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
|
||||
@ -37,10 +35,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
|
||||
"resource://gre/modules/Schemas.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "styleSheetService",
|
||||
"@mozilla.org/content/style-sheet-service;1",
|
||||
"nsIStyleSheetService");
|
||||
|
||||
function getConsole() {
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "extensions.webextensions.log.level",
|
||||
@ -154,20 +148,6 @@ class DefaultWeakMap extends WeakMap {
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultMap extends Map {
|
||||
constructor(defaultConstructor, init) {
|
||||
super(init);
|
||||
this.defaultConstructor = defaultConstructor;
|
||||
}
|
||||
|
||||
get(key) {
|
||||
if (!this.has(key)) {
|
||||
this.set(key, this.defaultConstructor(key));
|
||||
}
|
||||
return super.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
class SpreadArgs extends Array {
|
||||
constructor(args) {
|
||||
super();
|
||||
@ -1137,35 +1117,6 @@ function promiseDocumentLoaded(doc) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Promise which resolves when the given event is dispatched to the
|
||||
* given element.
|
||||
*
|
||||
* @param {Element} element
|
||||
* The element on which to listen.
|
||||
* @param {string} eventName
|
||||
* The event to listen for.
|
||||
* @param {boolean} [useCapture = true]
|
||||
* If true, listen for the even in the capturing rather than
|
||||
* bubbling phase.
|
||||
* @param {Event} [test]
|
||||
* An optional test function which, when called with the
|
||||
* observer's subject and data, should return true if this is the
|
||||
* expected event, false otherwise.
|
||||
* @returns {Promise<Event>}
|
||||
*/
|
||||
function promiseEvent(element, eventName, useCapture = true, test = event => true) {
|
||||
return new Promise(resolve => {
|
||||
function listener(event) {
|
||||
if (test(event)) {
|
||||
element.removeEventListener(eventName, listener, useCapture);
|
||||
resolve(event);
|
||||
}
|
||||
}
|
||||
element.addEventListener(eventName, listener, useCapture);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Promise which resolves the given observer topic has been
|
||||
* observed.
|
||||
@ -2016,11 +1967,6 @@ function normalizeTime(date) {
|
||||
? parseInt(date, 10) : date);
|
||||
}
|
||||
|
||||
const stylesheetMap = new DefaultMap(url => {
|
||||
let uri = NetUtil.newURI(url);
|
||||
return styleSheetService.preloadSheet(uri, styleSheetService.AGENT_SHEET);
|
||||
});
|
||||
|
||||
this.ExtensionUtils = {
|
||||
detectLanguage,
|
||||
extend,
|
||||
@ -2033,13 +1979,11 @@ this.ExtensionUtils = {
|
||||
normalizeTime,
|
||||
promiseDocumentLoaded,
|
||||
promiseDocumentReady,
|
||||
promiseEvent,
|
||||
promiseObserved,
|
||||
runSafe,
|
||||
runSafeSync,
|
||||
runSafeSyncWithoutClone,
|
||||
runSafeWithoutClone,
|
||||
stylesheetMap,
|
||||
BaseContext,
|
||||
DefaultWeakMap,
|
||||
EventEmitter,
|
||||
|
@ -1,217 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "clearTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "require",
|
||||
"resource://devtools/shared/Loader.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "colorUtils", () => {
|
||||
return require("devtools/shared/css/color").colorUtils;
|
||||
});
|
||||
|
||||
const {
|
||||
stylesheetMap,
|
||||
} = ExtensionUtils;
|
||||
|
||||
/* globals addMessageListener, content, docShell, sendAsyncMessage */
|
||||
|
||||
// Minimum time between two resizes.
|
||||
const RESIZE_TIMEOUT = 100;
|
||||
|
||||
const BrowserListener = {
|
||||
init({allowScriptsToClose, fixedWidth, maxHeight, maxWidth, stylesheets}) {
|
||||
this.fixedWidth = fixedWidth;
|
||||
this.stylesheets = stylesheets || [];
|
||||
|
||||
this.maxWidth = maxWidth;
|
||||
this.maxHeight = maxHeight;
|
||||
|
||||
this.oldBackground = null;
|
||||
|
||||
if (allowScriptsToClose) {
|
||||
content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.allowScriptsToClose();
|
||||
}
|
||||
|
||||
addEventListener("DOMWindowCreated", this, true);
|
||||
addEventListener("load", this, true);
|
||||
addEventListener("DOMContentLoaded", this, true);
|
||||
addEventListener("DOMWindowClose", this, true);
|
||||
addEventListener("MozScrolledAreaChanged", this, true);
|
||||
},
|
||||
|
||||
destroy() {
|
||||
removeEventListener("DOMWindowCreated", this, true);
|
||||
removeEventListener("load", this, true);
|
||||
removeEventListener("DOMContentLoaded", this, true);
|
||||
removeEventListener("DOMWindowClose", this, true);
|
||||
removeEventListener("MozScrolledAreaChanged", this, true);
|
||||
},
|
||||
|
||||
receiveMessage({name, data}) {
|
||||
if (name === "Extension:InitBrowser") {
|
||||
this.init(data);
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "DOMWindowCreated":
|
||||
if (event.target === content.document) {
|
||||
let winUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
for (let url of this.stylesheets) {
|
||||
winUtils.addSheet(stylesheetMap.get(url), winUtils.AGENT_SHEET);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "DOMWindowClose":
|
||||
if (event.target === content) {
|
||||
event.preventDefault();
|
||||
|
||||
sendAsyncMessage("Extension:DOMWindowClose");
|
||||
}
|
||||
break;
|
||||
|
||||
case "DOMContentLoaded":
|
||||
if (event.target === content.document) {
|
||||
sendAsyncMessage("Extension:BrowserContentLoaded", {url: content.location.href});
|
||||
this.handleDOMChange(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case "load":
|
||||
if (event.target.contentWindow === content) {
|
||||
// For about:addons inline <browsers>, we currently receive a load
|
||||
// event on the <browser> element, but no load or DOMContentLoaded
|
||||
// events from the content window.
|
||||
sendAsyncMessage("Extension:BrowserContentLoaded", {url: content.location.href});
|
||||
} else if (event.target !== content.document) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We use a capturing listener, so we get this event earlier than any
|
||||
// load listeners in the content page. Resizing after a timeout ensures
|
||||
// that we calculate the size after the entire event cycle has completed
|
||||
// (unless someone spins the event loop, anyway), and hopefully after
|
||||
// the content has made any modifications.
|
||||
Promise.resolve().then(() => {
|
||||
this.handleDOMChange(true);
|
||||
});
|
||||
|
||||
// Mutation observer to make sure the panel shrinks when the content does.
|
||||
new content.MutationObserver(this.handleDOMChange.bind(this)).observe(
|
||||
content.document.documentElement, {
|
||||
attributes: true,
|
||||
characterData: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
break;
|
||||
|
||||
case "MozScrolledAreaChanged":
|
||||
this.handleDOMChange();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// Resizes the browser to match the preferred size of the content (debounced).
|
||||
handleDOMChange(ignoreThrottling = false) {
|
||||
if (ignoreThrottling && this.resizeTimeout) {
|
||||
clearTimeout(this.resizeTimeout);
|
||||
this.resizeTimeout = null;
|
||||
}
|
||||
|
||||
if (this.resizeTimeout == null) {
|
||||
this.resizeTimeout = setTimeout(() => {
|
||||
try {
|
||||
if (content) {
|
||||
this._handleDOMChange("delayed");
|
||||
}
|
||||
} finally {
|
||||
this.resizeTimeout = null;
|
||||
}
|
||||
}, RESIZE_TIMEOUT);
|
||||
|
||||
this._handleDOMChange();
|
||||
}
|
||||
},
|
||||
|
||||
_handleDOMChange(detail) {
|
||||
let doc = content.document;
|
||||
|
||||
let body = doc.body;
|
||||
if (!body || doc.compatMode === "BackCompat") {
|
||||
// In quirks mode, the root element is used as the scroll frame, and the
|
||||
// body lies about its scroll geometry, and returns the values for the
|
||||
// root instead.
|
||||
body = doc.documentElement;
|
||||
}
|
||||
|
||||
|
||||
let result;
|
||||
if (this.fixedWidth) {
|
||||
// If we're in a fixed-width area (namely a slide-in subview of the main
|
||||
// menu panel), we need to calculate the view height based on the
|
||||
// preferred height of the content document's root scrollable element at the
|
||||
// current width, rather than the complete preferred dimensions of the
|
||||
// content window.
|
||||
|
||||
// Compensate for any offsets (margin, padding, ...) between the scroll
|
||||
// area of the body and the outer height of the document.
|
||||
let getHeight = elem => elem.getBoundingClientRect(elem).height;
|
||||
let bodyPadding = getHeight(doc.documentElement) - getHeight(body);
|
||||
|
||||
let height = Math.ceil(body.scrollHeight + bodyPadding);
|
||||
|
||||
result = {height, detail};
|
||||
} else {
|
||||
let background = doc.defaultView.getComputedStyle(body).backgroundColor;
|
||||
let bgColor = colorUtils.colorToRGBA(background);
|
||||
if (bgColor.a !== 1) {
|
||||
// Ignore non-opaque backgrounds.
|
||||
background = null;
|
||||
}
|
||||
|
||||
if (background !== this.oldBackground) {
|
||||
sendAsyncMessage("Extension:BrowserBackgroundChanged", {background});
|
||||
}
|
||||
this.oldBackground = background;
|
||||
|
||||
|
||||
// Adjust the size of the browser based on its content's preferred size.
|
||||
let {contentViewer} = docShell;
|
||||
let ratio = content.devicePixelRatio;
|
||||
|
||||
let w = {}, h = {};
|
||||
contentViewer.getContentSizeConstrained(this.maxWidth * ratio,
|
||||
this.maxHeight * ratio,
|
||||
w, h);
|
||||
|
||||
let width = Math.ceil(w.value / ratio);
|
||||
let height = Math.ceil(h.value / ratio);
|
||||
|
||||
result = {width, height, detail};
|
||||
}
|
||||
|
||||
sendAsyncMessage("Extension:BrowserResized", result);
|
||||
},
|
||||
};
|
||||
|
||||
addMessageListener("Extension:InitBrowser", BrowserListener);
|
@ -6,7 +6,6 @@ toolkit.jar:
|
||||
% content extensions %content/extensions/
|
||||
content/extensions/ext-alarms.js
|
||||
content/extensions/ext-backgroundPage.js
|
||||
content/extensions/ext-browser-content.js
|
||||
content/extensions/ext-cookies.js
|
||||
content/extensions/ext-downloads.js
|
||||
content/extensions/ext-management.js
|
||||
|
@ -83,79 +83,6 @@ XPCOMUtils.defineLazyGetter(gStrings, "appVersion", function() {
|
||||
document.addEventListener("load", initialize, true);
|
||||
window.addEventListener("unload", shutdown, false);
|
||||
|
||||
class MessageDispatcher {
|
||||
constructor(target) {
|
||||
this.listeners = new Map();
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
addMessageListener(name, handler) {
|
||||
if (!this.listeners.has(name)) {
|
||||
this.listeners.set(name, new Set());
|
||||
}
|
||||
|
||||
this.listeners.get(name).add(handler);
|
||||
}
|
||||
|
||||
removeMessageListener(name, handler) {
|
||||
if (this.listeners.has(name)) {
|
||||
this.listeners.get(name).delete(handler);
|
||||
}
|
||||
}
|
||||
|
||||
sendAsyncMessage(name, data) {
|
||||
for (let handler of this.listeners.get(name) || new Set()) {
|
||||
Promise.resolve().then(() => {
|
||||
handler.receiveMessage({
|
||||
name,
|
||||
data,
|
||||
target: this.target,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A mock FrameMessageManager global to allow frame scripts to run in
|
||||
* non-top-level, non-remote <browser>s as if they were top-level or
|
||||
* remote.
|
||||
*
|
||||
* @param {Element} browser
|
||||
* A XUL <browser> element.
|
||||
*/
|
||||
class FakeFrameMessageManager {
|
||||
constructor(browser) {
|
||||
let dispatcher = new MessageDispatcher(browser);
|
||||
let frameDispatcher = new MessageDispatcher(null);
|
||||
|
||||
this.sendAsyncMessage = frameDispatcher.sendAsyncMessage.bind(frameDispatcher);
|
||||
this.addMessageListener = dispatcher.addMessageListener.bind(dispatcher);
|
||||
this.removeMessageListener = dispatcher.removeMessageListener.bind(dispatcher);
|
||||
|
||||
this.frame = {
|
||||
get content() {
|
||||
return browser.contentWindow;
|
||||
},
|
||||
|
||||
get docShell() {
|
||||
return browser.docShell;
|
||||
},
|
||||
|
||||
addEventListener: browser.addEventListener.bind(browser),
|
||||
removeEventListener: browser.removeEventListener.bind(browser),
|
||||
|
||||
sendAsyncMessage: dispatcher.sendAsyncMessage.bind(dispatcher),
|
||||
addMessageListener: frameDispatcher.addMessageListener.bind(frameDispatcher),
|
||||
removeMessageListener: frameDispatcher.removeMessageListener.bind(frameDispatcher),
|
||||
}
|
||||
}
|
||||
|
||||
loadFrameScript(url) {
|
||||
Services.scriptloader.loadSubScript(url, Object.create(this.frame));
|
||||
}
|
||||
}
|
||||
|
||||
var gPendingInitializations = 1;
|
||||
Object.defineProperty(this, "gIsInitializing", {
|
||||
get: () => gPendingInitializations > 0
|
||||
@ -3526,30 +3453,72 @@ var gDetailView = {
|
||||
browser.setAttribute("disableglobalhistory", "true");
|
||||
browser.setAttribute("class", "inline-options-browser");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let messageListener = {
|
||||
receiveMessage({name, data}) {
|
||||
if (name === "Extension:BrowserResized")
|
||||
browser.style.height = `${data.height}px`;
|
||||
else if (name === "Extension:BrowserContentLoaded")
|
||||
resolve(browser);
|
||||
},
|
||||
};
|
||||
// Resize at most 10 times per second.
|
||||
const TIMEOUT = 100;
|
||||
let timeout;
|
||||
|
||||
function resizeBrowser() {
|
||||
if (timeout == null) {
|
||||
_resizeBrowser();
|
||||
timeout = setTimeout(_resizeBrowser, TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
function _resizeBrowser() {
|
||||
timeout = null;
|
||||
|
||||
let doc = browser.contentDocument;
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
let body = doc.body || doc.documentElement;
|
||||
|
||||
let docHeight = doc.documentElement.getBoundingClientRect().height;
|
||||
|
||||
let height = Math.ceil(body.scrollHeight +
|
||||
// Compensate for any offsets between the scroll
|
||||
// area of the body and the outer height of the
|
||||
// document.
|
||||
docHeight - body.clientHeight);
|
||||
|
||||
// Note: This will trigger another MozScrolledAreaChanged event
|
||||
// if it's different from the previous height.
|
||||
browser.style.height = `${height}px`;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let onload = () => {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
|
||||
let mm = new FakeFrameMessageManager(browser);
|
||||
mm.loadFrameScript("chrome://extensions/content/ext-browser-content.js",
|
||||
false);
|
||||
mm.addMessageListener("Extension:BrowserContentLoaded", messageListener);
|
||||
mm.addMessageListener("Extension:BrowserResized", messageListener);
|
||||
mm.sendAsyncMessage("Extension:InitBrowser", {fixedWidth: true});
|
||||
browser.addEventListener("error", reject);
|
||||
browser.addEventListener("load", event => {
|
||||
// We only get load events targetted at one of these elements.
|
||||
// If we're running in a tab, it's the <browser>. If we're
|
||||
// running in a dialog, it's the content document.
|
||||
if (event.target != browser && event.target != browser.contentDocument)
|
||||
return;
|
||||
|
||||
resolve(browser);
|
||||
|
||||
browser.contentWindow.addEventListener("MozScrolledAreaChanged", event => {
|
||||
resizeBrowser();
|
||||
}, true);
|
||||
|
||||
new browser.contentWindow.MutationObserver(resizeBrowser).observe(
|
||||
browser.contentDocument.documentElement, {
|
||||
attributes: true,
|
||||
characterData: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
|
||||
resizeBrowser();
|
||||
}, true);
|
||||
|
||||
browser.setAttribute("src", this._addon.optionsURL);
|
||||
};
|
||||
browser.addEventListener("load", onload, true);
|
||||
browser.addEventListener("error", reject);
|
||||
|
||||
parentNode.appendChild(browser);
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user