2018-05-19 23:56:06 +03:00
|
|
|
/* 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/. */
|
|
|
|
|
2018-10-26 20:22:15 +03:00
|
|
|
// This file defines these globals on the window object.
|
|
|
|
// Define them here so that ESLint can find them:
|
2019-01-28 21:24:06 +03:00
|
|
|
/* globals BaseControlMixin, MozElementMixin, MozXULElement, MozElements */
|
2018-07-19 15:38:51 +03:00
|
|
|
|
2018-05-19 23:56:06 +03:00
|
|
|
"use strict";
|
|
|
|
|
2018-09-28 02:09:55 +03:00
|
|
|
// This is loaded into chrome windows with the subscript loader. Wrap in
|
|
|
|
// a block to prevent accidentally leaking globals onto `window`.
|
2018-10-26 20:22:15 +03:00
|
|
|
(() => {
|
|
|
|
// Handle customElements.js being loaded as a script in addition to the subscriptLoader
|
|
|
|
// from MainProcessSingleton, to handle pages that can open both before and after
|
|
|
|
// MainProcessSingleton starts. See Bug 1501845.
|
|
|
|
if (window.MozXULElement) {
|
|
|
|
return;
|
|
|
|
}
|
2018-05-19 23:56:06 +03:00
|
|
|
|
2019-01-17 21:18:31 +03:00
|
|
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
|
|
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
2018-05-19 23:56:06 +03:00
|
|
|
|
2018-10-09 00:17:39 +03:00
|
|
|
// The listener of DOMContentLoaded must be set on window, rather than
|
|
|
|
// document, because the window can go away before the event is fired.
|
|
|
|
// In that case, we don't want to initialize anything, otherwise we
|
|
|
|
// may be leaking things because they will never be destroyed after.
|
|
|
|
let gIsDOMContentLoaded = false;
|
|
|
|
const gElementsPendingConnection = new Set();
|
|
|
|
window.addEventListener("DOMContentLoaded", () => {
|
|
|
|
gIsDOMContentLoaded = true;
|
|
|
|
for (let element of gElementsPendingConnection) {
|
|
|
|
try {
|
|
|
|
if (element.isConnected) {
|
2018-11-01 18:03:14 +03:00
|
|
|
element.isRunningDelayedConnectedCallback = true;
|
2018-10-09 00:17:39 +03:00
|
|
|
element.connectedCallback();
|
|
|
|
}
|
|
|
|
} catch (ex) { console.error(ex); }
|
2018-11-01 18:03:14 +03:00
|
|
|
element.isRunningDelayedConnectedCallback = false;
|
2018-10-09 00:17:39 +03:00
|
|
|
}
|
|
|
|
gElementsPendingConnection.clear();
|
|
|
|
}, { once: true, capture: true });
|
|
|
|
|
2018-05-19 23:56:06 +03:00
|
|
|
const gXULDOMParser = new DOMParser();
|
|
|
|
gXULDOMParser.forceEnableXULXBL();
|
|
|
|
|
2018-11-05 16:56:01 +03:00
|
|
|
const MozElements = {};
|
|
|
|
|
2018-10-16 22:59:16 +03:00
|
|
|
const MozElementMixin = Base => class MozElement extends Base {
|
2019-02-16 02:10:18 +03:00
|
|
|
/*
|
|
|
|
* A declarative way to wire up attribute inheritance and automatically generate
|
|
|
|
* the `observedAttributes` getter. For example, if you returned:
|
|
|
|
* {
|
|
|
|
* ".foo": "bar,baz=bat"
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* Then the base class will automatically return ["bar", "bat"] from `observedAttributes`,
|
|
|
|
* and set up an `attributeChangedCallback` to pass those attributes down onto an element
|
|
|
|
* matching the ".foo" selector.
|
|
|
|
*
|
|
|
|
* See the `inheritAttribute` function for more details on the attribute string format.
|
|
|
|
*
|
|
|
|
* @return {Object<string selector, string attributes>}
|
|
|
|
*/
|
|
|
|
static get inheritedAttributes() {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate this array based on `inheritedAttributes`, if any. A class is free to override
|
|
|
|
* this if it needs to do something more complex or wants to opt out of this behavior.
|
|
|
|
*/
|
|
|
|
static get observedAttributes() {
|
|
|
|
let {inheritedAttributes} = this;
|
|
|
|
if (!inheritedAttributes) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
let allAttributes = new Set();
|
|
|
|
for (let sel in inheritedAttributes) {
|
|
|
|
for (let attrName of inheritedAttributes[sel].split(",")) {
|
|
|
|
allAttributes.add(attrName.split("=").pop());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return [...allAttributes];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Provide default lifecycle callback for attribute changes that will inherit attributes
|
|
|
|
* based on the static `inheritedAttributes` Object. This can be overridden by callers.
|
|
|
|
*/
|
|
|
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
|
|
if (!this.isConnectedAndReady || oldValue === newValue || !this.inheritedAttributesCache) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.inheritAttributes();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* After setting content, calling this will cache the elements from selectors in the
|
|
|
|
* static `inheritedAttributes` Object. It'll also do an initial call to `this.inheritAttributes()`,
|
|
|
|
* so in the simple case, this is the only function you need to call.
|
|
|
|
*
|
|
|
|
* This should be called any time the children that are inheriting attributes changes. For instance,
|
|
|
|
* it's common in a connectedCallback to do something like:
|
|
|
|
*
|
|
|
|
* this.textContent = "";
|
|
|
|
* this.append(MozXULElement.parseXULToFragment(`<label />`))
|
|
|
|
* this.initializeAttributeInheritance();
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
initializeAttributeInheritance() {
|
|
|
|
let {inheritedAttributes} = this.constructor;
|
|
|
|
if (!inheritedAttributes) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this._inheritedAttributesValuesCache = null;
|
|
|
|
this.inheritedAttributesCache = new Map();
|
|
|
|
for (let selector in inheritedAttributes) {
|
|
|
|
let el = this.querySelector(selector);
|
|
|
|
// Skip unmatched selectors in case an element omits some elements in certain cases:
|
|
|
|
if (!el) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (this.inheritedAttributesCache.has(el)) {
|
|
|
|
console.error(`Error: duplicate element encountered with ${selector}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.inheritedAttributesCache.set(el, inheritedAttributes[selector]);
|
|
|
|
}
|
|
|
|
this.inheritAttributes();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Loop through the static `inheritedAttributes` Map and inherit attributes to child elements.
|
|
|
|
*
|
|
|
|
* This usually won't need to be called directly - `this.initializeAttributeInheritance()` and
|
|
|
|
* `this.attributeChangedCallback` will call it for you when appropriate.
|
|
|
|
*/
|
|
|
|
inheritAttributes() {
|
|
|
|
let {inheritedAttributes} = this.constructor;
|
|
|
|
if (!inheritedAttributes) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.inheritedAttributesCache) {
|
|
|
|
console.error(`You must call this.initializeAttributeInheritance() for ${this.tagName}`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let [ el, attrs ] of this.inheritedAttributesCache.entries()) {
|
|
|
|
for (let attr of attrs.split(",")) {
|
|
|
|
this.inheritAttribute(el, attr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-30 21:51:40 +03:00
|
|
|
/*
|
|
|
|
* Implements attribute inheritance by a child element. Uses XBL @inherit
|
2019-02-16 02:10:18 +03:00
|
|
|
* syntax of |to=from|. This can be used directly, but for simple cases
|
|
|
|
* you should use the inheritedAttributes getter and let the base class
|
|
|
|
* handle this for you.
|
2018-10-30 21:51:40 +03:00
|
|
|
*
|
|
|
|
* @param {element} child
|
|
|
|
* A child element that inherits an attribute.
|
|
|
|
* @param {string} attr
|
|
|
|
* An attribute to inherit. Optionally in the form of |to=from|, where
|
|
|
|
* |to| is an attribute defined on custom element, whose value will be
|
|
|
|
* inherited to |from| attribute, defined a child element. Note |from| may
|
|
|
|
* take a special value of "text" to propogate attribute value as
|
|
|
|
* a child's text.
|
|
|
|
*/
|
|
|
|
inheritAttribute(child, attr) {
|
|
|
|
let attrName = attr;
|
|
|
|
let attrNewName = attr;
|
|
|
|
let split = attrName.split("=");
|
|
|
|
if (split.length == 2) {
|
|
|
|
attrName = split[1];
|
|
|
|
attrNewName = split[0];
|
|
|
|
}
|
2019-01-29 02:57:24 +03:00
|
|
|
let hasAttr = this.hasAttribute(attrName);
|
|
|
|
let attrValue = this.getAttribute(attrName);
|
2018-10-30 21:51:40 +03:00
|
|
|
|
2019-01-29 02:57:24 +03:00
|
|
|
// If our attribute hasn't changed since we last inherited, we don't want to
|
|
|
|
// propagate it down to the child. This prevents overriding an attribute that's
|
|
|
|
// been changed on the child (for instance, [checked]).
|
2019-02-16 02:10:18 +03:00
|
|
|
if (!this._inheritedAttributesValuesCache) {
|
|
|
|
this._inheritedAttributesValuesCache = new WeakMap();
|
2019-01-29 02:57:24 +03:00
|
|
|
}
|
2019-02-16 02:10:18 +03:00
|
|
|
if (!this._inheritedAttributesValuesCache.has(child)) {
|
|
|
|
this._inheritedAttributesValuesCache.set(child, {});
|
2019-01-29 02:57:24 +03:00
|
|
|
}
|
2019-02-16 02:10:18 +03:00
|
|
|
let lastInheritedAttributes = this._inheritedAttributesValuesCache.get(child);
|
2019-01-29 02:57:24 +03:00
|
|
|
|
|
|
|
if ((hasAttr && attrValue === lastInheritedAttributes[attrName]) ||
|
|
|
|
(!hasAttr && !lastInheritedAttributes.hasOwnProperty(attrName))) {
|
|
|
|
// We got a request to inherit an unchanged attribute - bail.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the value we're about to pass down to the child.
|
|
|
|
if (hasAttr) {
|
|
|
|
lastInheritedAttributes[attrName] = attrValue;
|
|
|
|
} else {
|
|
|
|
delete lastInheritedAttributes[attrName];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Actually set the attribute.
|
2018-10-30 21:51:40 +03:00
|
|
|
if (attrNewName === "text") {
|
2019-01-29 02:57:24 +03:00
|
|
|
child.textContent = hasAttr ? attrValue : "";
|
|
|
|
} else if (hasAttr) {
|
|
|
|
child.setAttribute(attrNewName, attrValue);
|
2018-10-30 21:51:40 +03:00
|
|
|
} else {
|
|
|
|
child.removeAttribute(attrNewName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-09 00:17:39 +03:00
|
|
|
/**
|
|
|
|
* Sometimes an element may not want to run connectedCallback logic during
|
|
|
|
* parse. This could be because we don't want to initialize the element before
|
|
|
|
* the element's contents have been fully parsed, or for performance reasons.
|
|
|
|
* If you'd like to opt-in to this, then add this to the beginning of your
|
|
|
|
* `connectedCallback` and `disconnectedCallback`:
|
|
|
|
*
|
|
|
|
* if (this.delayConnectedCallback()) { return }
|
|
|
|
*
|
|
|
|
* And this at the beginning of your `attributeChangedCallback`
|
|
|
|
*
|
|
|
|
* if (!this.isConnectedAndReady) { return; }
|
|
|
|
*/
|
|
|
|
delayConnectedCallback() {
|
|
|
|
if (gIsDOMContentLoaded) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
gElementsPendingConnection.add(this);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
get isConnectedAndReady() {
|
|
|
|
return gIsDOMContentLoaded && this.isConnected;
|
|
|
|
}
|
|
|
|
|
2018-05-19 23:56:06 +03:00
|
|
|
/**
|
|
|
|
* Allows eager deterministic construction of XUL elements with XBL attached, by
|
|
|
|
* parsing an element tree and returning a DOM fragment to be inserted in the
|
|
|
|
* document before any of the inner elements is referenced by JavaScript.
|
|
|
|
*
|
|
|
|
* This process is required instead of calling the createElement method directly
|
|
|
|
* because bindings get attached when:
|
|
|
|
*
|
2018-08-27 12:40:40 +03:00
|
|
|
* 1. the node gets a layout frame constructed, or
|
|
|
|
* 2. the node gets its JavaScript reflector created, if it's in the document,
|
2018-05-19 23:56:06 +03:00
|
|
|
*
|
|
|
|
* whichever happens first. The createElement method would return a JavaScript
|
|
|
|
* reflector, but the element wouldn't be in the document, so the node wouldn't
|
|
|
|
* get XBL attached. After that point, even if the node is inserted into a
|
|
|
|
* document, it won't get XBL attached until either the frame is constructed or
|
|
|
|
* the reflector is garbage collected and the element is touched again.
|
|
|
|
*
|
2018-08-27 12:40:40 +03:00
|
|
|
* @param {string} str
|
2018-05-19 23:56:06 +03:00
|
|
|
* String with the XML representation of XUL elements.
|
2018-08-27 12:40:40 +03:00
|
|
|
* @param {string[]} [entities]
|
|
|
|
* An array of DTD URLs containing entity definitions.
|
2018-05-19 23:56:06 +03:00
|
|
|
*
|
2018-08-27 12:40:40 +03:00
|
|
|
* @return {DocumentFragment} `DocumentFragment` instance containing
|
|
|
|
* the corresponding element tree, including element nodes
|
|
|
|
* but excluding any text node.
|
2018-05-19 23:56:06 +03:00
|
|
|
*/
|
2018-08-27 12:40:40 +03:00
|
|
|
static parseXULToFragment(str, entities = []) {
|
2018-05-19 23:56:06 +03:00
|
|
|
let doc = gXULDOMParser.parseFromString(`
|
2018-08-27 12:40:40 +03:00
|
|
|
${entities.length ? `<!DOCTYPE bindings [
|
|
|
|
${entities.reduce((preamble, url, index) => {
|
|
|
|
return preamble + `<!ENTITY % _dtd-${index} SYSTEM "${url}">
|
|
|
|
%_dtd-${index};
|
|
|
|
`;
|
|
|
|
}, "")}
|
|
|
|
]>` : ""}
|
2019-01-09 11:51:32 +03:00
|
|
|
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
|
|
xmlns:html="http://www.w3.org/1999/xhtml">
|
2018-05-19 23:56:06 +03:00
|
|
|
${str}
|
|
|
|
</box>
|
|
|
|
`, "application/xml");
|
|
|
|
// The XUL/XBL parser is set to ignore all-whitespace nodes, whereas (X)HTML
|
|
|
|
// does not do this. Most XUL code assumes that the whitespace has been
|
|
|
|
// stripped out, so we simply remove all text nodes after using the parser.
|
|
|
|
let nodeIterator = doc.createNodeIterator(doc, NodeFilter.SHOW_TEXT);
|
|
|
|
let currentNode = nodeIterator.nextNode();
|
|
|
|
while (currentNode) {
|
2019-02-20 02:28:30 +03:00
|
|
|
// Remove whitespace-only nodes. Regex is taken from:
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace_in_the_DOM
|
|
|
|
if (!(/[^\t\n\r ]/.test(currentNode.textContent))) {
|
|
|
|
currentNode.remove();
|
|
|
|
}
|
|
|
|
|
2018-05-19 23:56:06 +03:00
|
|
|
currentNode = nodeIterator.nextNode();
|
|
|
|
}
|
|
|
|
// We use a range here so that we don't access the inner DOM elements from
|
|
|
|
// JavaScript before they are imported and inserted into a document.
|
|
|
|
let range = doc.createRange();
|
2018-07-02 23:33:09 +03:00
|
|
|
range.selectNodeContents(doc.querySelector("box"));
|
2018-05-19 23:56:06 +03:00
|
|
|
return range.extractContents();
|
|
|
|
}
|
2018-07-19 15:38:51 +03:00
|
|
|
|
2018-09-14 03:31:36 +03:00
|
|
|
/**
|
|
|
|
* Insert a localization link to an FTL file. This is used so that
|
|
|
|
* a Custom Element can wait to inject the link until it's connected,
|
|
|
|
* and so that consuming documents don't require the correct <link>
|
|
|
|
* present in the markup.
|
|
|
|
*
|
|
|
|
* @param path
|
|
|
|
* The path to the FTL file
|
|
|
|
*/
|
|
|
|
static insertFTLIfNeeded(path) {
|
|
|
|
let container = document.head || document.querySelector("linkset");
|
|
|
|
if (!container) {
|
2018-09-19 00:45:51 +03:00
|
|
|
if (document.contentType == "application/vnd.mozilla.xul+xml") {
|
|
|
|
container = document.createXULElement("linkset");
|
|
|
|
document.documentElement.appendChild(container);
|
|
|
|
} else if (document.documentURI == AppConstants.BROWSER_CHROME_URL) {
|
|
|
|
// Special case for browser.xhtml. Here `document.head` is null, so
|
|
|
|
// just insert the link at the end of the window.
|
|
|
|
container = document.documentElement;
|
|
|
|
} else {
|
2018-09-14 03:31:36 +03:00
|
|
|
throw new Error("Attempt to inject localization link before document.head is available");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let link of container.querySelectorAll("link")) {
|
|
|
|
if (link.getAttribute("href") == path) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-14 13:43:35 +03:00
|
|
|
let link = document.createElementNS("http://www.w3.org/1999/xhtml",
|
|
|
|
"link");
|
2018-09-14 03:31:36 +03:00
|
|
|
link.setAttribute("rel", "localization");
|
|
|
|
link.setAttribute("href", path);
|
|
|
|
|
|
|
|
container.appendChild(link);
|
|
|
|
}
|
|
|
|
|
2018-07-19 15:38:51 +03:00
|
|
|
/**
|
2018-09-19 13:46:55 +03:00
|
|
|
* Indicate that a class defining a XUL element implements one or more
|
2018-12-04 19:33:06 +03:00
|
|
|
* XPCOM interfaces by adding a getCustomInterface implementation to it,
|
|
|
|
* as well as an implementation of QueryInterface.
|
2018-07-19 15:38:51 +03:00
|
|
|
*
|
|
|
|
* The supplied class should implement the properties and methods of
|
|
|
|
* all of the interfaces that are specified.
|
|
|
|
*
|
|
|
|
* @param cls
|
|
|
|
* The class that implements the interface.
|
|
|
|
* @param names
|
2018-09-19 13:46:55 +03:00
|
|
|
* Array of interface names.
|
2018-07-19 15:38:51 +03:00
|
|
|
*/
|
|
|
|
static implementCustomInterface(cls, ifaces) {
|
2018-12-04 19:33:06 +03:00
|
|
|
if (cls.prototype.customInterfaces) {
|
|
|
|
ifaces.push(...cls.prototype.customInterfaces);
|
2018-10-01 21:01:02 +03:00
|
|
|
}
|
2018-12-04 19:33:06 +03:00
|
|
|
cls.prototype.customInterfaces = ifaces;
|
2018-10-01 21:01:02 +03:00
|
|
|
|
2018-12-04 19:33:06 +03:00
|
|
|
cls.prototype.QueryInterface = ChromeUtils.generateQI(ifaces);
|
2018-12-04 19:33:06 +03:00
|
|
|
cls.prototype.getCustomInterfaceCallback = function getCustomInterfaceCallback(ifaceToCheck) {
|
|
|
|
if (cls.prototype.customInterfaces.some(iface => iface.equals(ifaceToCheck))) {
|
2018-07-19 15:38:51 +03:00
|
|
|
return getInterfaceProxy(this);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
}
|
2018-10-16 22:59:16 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
const MozXULElement = MozElementMixin(XULElement);
|
2018-07-19 15:38:51 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Given an object, add a proxy that reflects interface implementations
|
|
|
|
* onto the object itself.
|
|
|
|
*/
|
|
|
|
function getInterfaceProxy(obj) {
|
2018-10-26 20:22:15 +03:00
|
|
|
/* globals MozQueryInterface */
|
2018-07-19 15:38:51 +03:00
|
|
|
if (!obj._customInterfaceProxy) {
|
|
|
|
obj._customInterfaceProxy = new Proxy(obj, {
|
|
|
|
get(target, prop, receiver) {
|
|
|
|
let propOrMethod = target[prop];
|
|
|
|
if (typeof propOrMethod == "function") {
|
|
|
|
if (propOrMethod instanceof MozQueryInterface) {
|
|
|
|
return Reflect.get(target, prop, receiver);
|
|
|
|
}
|
|
|
|
return function(...args) {
|
|
|
|
return propOrMethod.apply(target, args);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return propOrMethod;
|
2018-08-31 08:59:17 +03:00
|
|
|
},
|
2018-07-19 15:38:51 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return obj._customInterfaceProxy;
|
2018-05-19 23:56:06 +03:00
|
|
|
}
|
|
|
|
|
2019-01-28 21:24:06 +03:00
|
|
|
const BaseControlMixin = Base => {
|
|
|
|
class BaseControl extends Base {
|
|
|
|
get disabled() {
|
|
|
|
return this.getAttribute("disabled") == "true";
|
|
|
|
}
|
2018-10-01 21:01:02 +03:00
|
|
|
|
2019-01-28 21:24:06 +03:00
|
|
|
set disabled(val) {
|
|
|
|
if (val) {
|
|
|
|
this.setAttribute("disabled", "true");
|
|
|
|
} else {
|
|
|
|
this.removeAttribute("disabled");
|
|
|
|
}
|
2018-10-01 21:01:02 +03:00
|
|
|
}
|
|
|
|
|
2019-01-28 21:24:06 +03:00
|
|
|
get tabIndex() {
|
|
|
|
return parseInt(this.getAttribute("tabindex")) || 0;
|
|
|
|
}
|
2018-10-01 21:01:02 +03:00
|
|
|
|
2019-01-28 21:24:06 +03:00
|
|
|
set tabIndex(val) {
|
|
|
|
if (val) {
|
|
|
|
this.setAttribute("tabindex", val);
|
|
|
|
} else {
|
|
|
|
this.removeAttribute("tabindex");
|
|
|
|
}
|
2018-10-01 21:01:02 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-28 21:24:06 +03:00
|
|
|
Base.implementCustomInterface(BaseControl,
|
|
|
|
[Ci.nsIDOMXULControlElement]);
|
|
|
|
return BaseControl;
|
|
|
|
};
|
|
|
|
MozElements.BaseControl = BaseControlMixin(MozXULElement);
|
2018-10-01 21:01:02 +03:00
|
|
|
|
2019-02-11 20:33:07 +03:00
|
|
|
const BaseTextMixin = Base => class extends BaseControlMixin(Base) {
|
2019-01-24 16:40:22 +03:00
|
|
|
set label(val) {
|
|
|
|
this.setAttribute("label", val);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
get label() {
|
|
|
|
return this.getAttribute("label");
|
|
|
|
}
|
|
|
|
|
|
|
|
set crop(val) {
|
|
|
|
this.setAttribute("crop", val);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
get crop() {
|
|
|
|
return this.getAttribute("crop");
|
|
|
|
}
|
|
|
|
|
|
|
|
set image(val) {
|
|
|
|
this.setAttribute("image", val);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
get image() {
|
|
|
|
return this.getAttribute("image");
|
|
|
|
}
|
|
|
|
|
|
|
|
set command(val) {
|
|
|
|
this.setAttribute("command", val);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
get command() {
|
|
|
|
return this.getAttribute("command");
|
|
|
|
}
|
|
|
|
|
|
|
|
set accessKey(val) {
|
|
|
|
// Always store on the control
|
|
|
|
this.setAttribute("accesskey", val);
|
|
|
|
// If there is a label, change the accesskey on the labelElement
|
|
|
|
// if it's also set there
|
|
|
|
if (this.labelElement) {
|
|
|
|
this.labelElement.accessKey = val;
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
get accessKey() {
|
|
|
|
return this.labelElement ? this.labelElement.accessKey : this.getAttribute("accesskey");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
MozElements.BaseText = BaseTextMixin(MozXULElement);
|
|
|
|
|
2018-05-19 23:56:06 +03:00
|
|
|
// Attach the base class to the window so other scripts can use it:
|
2019-01-28 21:24:06 +03:00
|
|
|
window.BaseControlMixin = BaseControlMixin;
|
2018-10-16 22:59:16 +03:00
|
|
|
window.MozElementMixin = MozElementMixin;
|
2018-05-19 23:56:06 +03:00
|
|
|
window.MozXULElement = MozXULElement;
|
2018-11-05 16:56:01 +03:00
|
|
|
window.MozElements = MozElements;
|
2018-05-19 23:56:06 +03:00
|
|
|
|
2019-01-10 04:45:43 +03:00
|
|
|
customElements.setElementCreationCallback("browser", () => {
|
|
|
|
Services.scriptloader.loadSubScript("chrome://global/content/elements/browser-custom-element.js", window);
|
|
|
|
});
|
|
|
|
|
2018-10-01 21:01:00 +03:00
|
|
|
// For now, don't load any elements in the extension dummy document.
|
|
|
|
// We will want to load <browser> when that's migrated (bug 1441935).
|
|
|
|
const isDummyDocument = document.documentURI == "chrome://extensions/content/dummy.xul";
|
|
|
|
if (!isDummyDocument) {
|
|
|
|
for (let script of [
|
|
|
|
"chrome://global/content/elements/general.js",
|
2019-02-12 22:08:25 +03:00
|
|
|
"chrome://global/content/elements/menu.js",
|
2018-11-09 17:58:18 +03:00
|
|
|
"chrome://global/content/elements/notificationbox.js",
|
2019-02-08 01:16:26 +03:00
|
|
|
"chrome://global/content/elements/popupnotification.js",
|
2018-10-01 21:01:02 +03:00
|
|
|
"chrome://global/content/elements/radio.js",
|
2019-01-24 16:40:22 +03:00
|
|
|
"chrome://global/content/elements/richlistbox.js",
|
|
|
|
"chrome://global/content/elements/autocomplete-richlistitem.js",
|
2018-10-01 21:01:00 +03:00
|
|
|
"chrome://global/content/elements/textbox.js",
|
|
|
|
"chrome://global/content/elements/tabbox.js",
|
2018-11-01 18:03:14 +03:00
|
|
|
"chrome://global/content/elements/tree.js",
|
2018-10-01 21:01:00 +03:00
|
|
|
]) {
|
2018-09-14 21:30:31 +03:00
|
|
|
Services.scriptloader.loadSubScript(script, window);
|
2018-10-01 21:01:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
for (let [tag, script] of [
|
|
|
|
["findbar", "chrome://global/content/elements/findbar.js"],
|
2019-01-28 21:24:08 +03:00
|
|
|
["menulist", "chrome://global/content/elements/menulist.js"],
|
2018-10-01 21:01:00 +03:00
|
|
|
["stringbundle", "chrome://global/content/elements/stringbundle.js"],
|
|
|
|
["printpreview-toolbar", "chrome://global/content/printPreviewToolbar.js"],
|
|
|
|
["editor", "chrome://global/content/elements/editor.js"],
|
2019-02-13 03:14:40 +03:00
|
|
|
["text-link", "chrome://global/content/elements/text.js"],
|
2018-10-01 21:01:00 +03:00
|
|
|
]) {
|
|
|
|
customElements.setElementCreationCallback(tag, () => {
|
|
|
|
Services.scriptloader.loadSubScript(script, window);
|
|
|
|
});
|
|
|
|
}
|
2018-09-14 21:30:31 +03:00
|
|
|
}
|
2018-10-26 20:22:15 +03:00
|
|
|
})();
|