mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-24 02:35:41 +00:00
Backed out changeset 68806639c031 (bug 1287209) for failing browser-chrome test browser_ext_popup_corners.js. r=backout
This commit is contained in:
parent
304f2d4ce5
commit
0c23c32b56
@ -87,7 +87,6 @@ BrowserAction.prototype = {
|
||||
onDestroyed: document => {
|
||||
let view = document.getElementById(this.viewId);
|
||||
if (view) {
|
||||
CustomizableUI.hidePanelForNode(view);
|
||||
view.remove();
|
||||
}
|
||||
},
|
||||
|
@ -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,6 +441,8 @@ class PanelPopup extends BasePopup {
|
||||
|
||||
super(extension, panel, popupURL, browserStyle);
|
||||
|
||||
this.ignoreResizes = false;
|
||||
|
||||
this.contentReady.then(() => {
|
||||
panel.openPopup(imageNode, "bottomcenter topright", 0, 0, false, false);
|
||||
});
|
||||
@ -365,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;
|
||||
|
||||
@ -380,8 +480,6 @@ class ViewPopup extends BasePopup {
|
||||
|
||||
super(extension, panel, popupURL, browserStyle, fixedWidth);
|
||||
|
||||
this.ignoreResizes = true;
|
||||
|
||||
this.attached = false;
|
||||
this.tempPanel = panel;
|
||||
|
||||
@ -451,9 +549,7 @@ 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;
|
||||
@ -482,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.
|
||||
|
@ -14,7 +14,8 @@ function* awaitResize(browser) {
|
||||
// looking for, but don't wait longer than a few seconds.
|
||||
|
||||
return Promise.race([
|
||||
BrowserTestUtils.waitForEvent(browser, "WebExtPopupResized", event => event.detail === "delayed"),
|
||||
BrowserTestUtils.waitForEvent(browser, "WebExtPopupResized")
|
||||
.then(() => BrowserTestUtils.waitForEvent(browser, "WebExtPopupResized")),
|
||||
new Promise(resolve => setTimeout(resolve, 5000)),
|
||||
]);
|
||||
}
|
||||
|
@ -15,8 +15,6 @@ function* awaitResize(browser) {
|
||||
}
|
||||
|
||||
add_task(function* testPageActionPopupResize() {
|
||||
let browser;
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"page_action": {
|
||||
@ -25,7 +23,6 @@ add_task(function* testPageActionPopupResize() {
|
||||
},
|
||||
},
|
||||
background: function() {
|
||||
/* global browser */
|
||||
browser.tabs.query({active: true, currentWindow: true}, tabs => {
|
||||
const tabId = tabs[0].id;
|
||||
|
||||
@ -45,10 +42,12 @@ 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");
|
||||
});
|
||||
|
||||
let panelWindow = browser.contentWindow;
|
||||
let panelDocument = panelWindow.document;
|
||||
let panelWindow = panelDocument.defaultView;
|
||||
let panelBody = panelDocument.body.firstChild;
|
||||
let body = panelDocument.body;
|
||||
let root = panelDocument.documentElement;
|
||||
@ -70,7 +69,7 @@ add_task(function* testPageActionPopupResize() {
|
||||
panelBody.style.height = `${size}px`;
|
||||
panelBody.style.width = `${size}px`;
|
||||
|
||||
return BrowserTestUtils.waitForEvent(browser, "WebExtPopupResized");
|
||||
return BrowserTestUtils.waitForEvent(panelWindow, "resize");
|
||||
}
|
||||
|
||||
let sizes = [
|
||||
|
@ -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");
|
||||
});
|
||||
|
@ -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…
Reference in New Issue
Block a user