Bug 1527680 - Add MozElement `inheritedAttributes` helper for automatically inheriting attributes based on a map of selectors to attributes r=paolo

This allows elements to skip explicitly declaring `observedAttributes` and then imperatively
calling `inheritAttribute` on the appropriate child nodes. For simple cases this means less
boilerplate and moving this logic into the base class.

This is an opt-in feature, so more complex cases can continue to manually implement inheriting
behavior as before.

Differential Revision: https://phabricator.services.mozilla.com/D19702

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Brian Grinstead 2019-02-15 16:33:00 +00:00
Родитель 6888b2b9c6
Коммит 4bcb2ab728
8 изменённых файлов: 278 добавлений и 305 удалений

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

@ -9,46 +9,15 @@
// This is loaded into chrome windows with the subscript loader. Wrap in
// a block to prevent accidentally leaking globals onto `window`.
{
const inheritsMap = {
".searchbar-textbox": ["disabled", "disableautocomplete", "searchengine", "src", "newlines"],
".searchbar-search-button": ["addengines"],
};
function inheritAttribute(parent, child, attr) {
if (!parent.hasAttribute(attr)) {
child.removeAttribute(attr);
} else {
child.setAttribute(attr, parent.getAttribute(attr));
}
}
/**
* Defines the search bar element.
*/
class MozSearchbar extends MozXULElement {
static get observedAttributes() {
let unique = new Set();
for (let i in inheritsMap) {
inheritsMap[i].forEach(attr => unique.add(attr));
}
return Array.from(unique);
}
attributeChangedCallback() {
this.inheritAttributes();
}
inheritAttributes() {
if (!this.isConnected) {
return;
}
for (let sel in inheritsMap) {
let node = this.querySelector(sel);
for (let attr of inheritsMap[sel]) {
inheritAttribute(this, node, attr);
}
}
static get inheritedAttributes() {
return {
".searchbar-textbox": "disabled,disableautocomplete,searchengine,src,newlines",
".searchbar-search-button": "addengines",
};
}
constructor() {
@ -72,9 +41,9 @@ class MozSearchbar extends MozXULElement {
};
this.content = MozXULElement.parseXULToFragment(`
<stringbundle src="chrome://browser/locale/search.properties"></stringbundle>
<textbox class="searchbar-textbox" type="autocomplete" inputtype="search" placeholder="&searchInput.placeholder;" flex="1" autocompletepopup="PopupSearchAutoComplete" autocompletesearch="search-autocomplete" autocompletesearchparam="searchbar-history" maxrows="10" completeselectedindex="true" minresultsforpopup="0" inherits="disabled,disableautocomplete,searchengine,src,newlines">
<textbox class="searchbar-textbox" type="autocomplete" inputtype="search" placeholder="&searchInput.placeholder;" flex="1" autocompletepopup="PopupSearchAutoComplete" autocompletesearch="search-autocomplete" autocompletesearchparam="searchbar-history" maxrows="10" completeselectedindex="true" minresultsforpopup="0">
<box>
<hbox class="searchbar-search-button" inherits="addengines" tooltiptext="&searchIcon.tooltip;">
<hbox class="searchbar-search-button" tooltiptext="&searchIcon.tooltip;">
<image class="searchbar-search-icon"></image>
<image class="searchbar-search-icon-overlay"></image>
</hbox>
@ -93,7 +62,7 @@ class MozSearchbar extends MozXULElement {
}
this.appendChild(document.importNode(this.content, true));
this.inheritAttributes();
this.initializeAttributeInheritance();
window.addEventListener("unload", this.destroy);
this._ignoreFocus = false;

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

@ -47,9 +47,120 @@ gXULDOMParser.forceEnableXULXBL();
const MozElements = {};
const MozElementMixin = Base => class MozElement extends Base {
/*
* 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);
}
}
}
/*
* Implements attribute inheritance by a child element. Uses XBL @inherit
* syntax of |to=from|.
* 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.
*
* @param {element} child
* A child element that inherits an attribute.
@ -74,13 +185,13 @@ const MozElementMixin = Base => class MozElement extends Base {
// 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]).
if (!this._inheritedAttributesMap) {
this._inheritedAttributesMap = new WeakMap();
if (!this._inheritedAttributesValuesCache) {
this._inheritedAttributesValuesCache = new WeakMap();
}
if (!this._inheritedAttributesMap.has(child)) {
this._inheritedAttributesMap.set(child, {});
if (!this._inheritedAttributesValuesCache.has(child)) {
this._inheritedAttributesValuesCache.set(child, {});
}
let lastInheritedAttributes = this._inheritedAttributesMap.get(child);
let lastInheritedAttributes = this._inheritedAttributesValuesCache.get(child);
if ((hasAttr && attrValue === lastInheritedAttributes[attrName]) ||
(!hasAttr && !lastInheritedAttributes.hasOwnProperty(attrName))) {

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

@ -16,7 +16,8 @@
<simpleelement id="two" style="-moz-user-focus: normal;"/>
<simpleelement id="three" disabled="true" style="-moz-user-focus: normal;"/>
<button id="four"/>
<inherited-element foo="fuagra"></inherited-element>
<inherited-element-declarative foo="fuagra"></inherited-element-declarative>
<inherited-element-imperative foo="fuagra"></inherited-element-imperative>
<!-- test code goes here -->
@ -31,7 +32,7 @@
testBaseControlMixin();
testBaseText();
testParseXULToFragment();
testInherits();
testInheritAttributes();
await testCustomInterface();
let htmlWin = await new Promise(resolve => {
@ -88,8 +89,26 @@
deck.remove();
}
function testInherits() {
class InheritsElement extends MozXULElement {
function testInheritAttributes() {
class InheritsElementDeclarative extends MozXULElement {
static get inheritedAttributes() {
return {
"label": "text=label,foo,boo,bardo=bar",
"unmatched": "foo", // Make sure we don't throw on unmatched selectors
};
}
connectedCallback() {
this.append(MozXULElement.parseXULToFragment(`<label />`));
this.label = this.querySelector("label");
this.initializeAttributeInheritance();
}
}
customElements.define("inherited-element-declarative", InheritsElementDeclarative);
let declarativeEl = document.querySelector("inherited-element-declarative");
ok(declarativeEl, "declarative inheritance element exists");
class InheritsElementImperative extends MozXULElement {
static get observedAttributes() {
return [ "label", "foo", "boo", "bar" ];
}
@ -113,10 +132,12 @@
}
}
customElements.define("inherited-element", InheritsElement);
let el = document.querySelector("inherited-element");
ok(el, "element exists");
customElements.define("inherited-element-imperative", InheritsElementImperative);
let imperativeEl = document.querySelector("inherited-element-imperative");
ok(imperativeEl, "imperative inheritance element exists");
for (let el of [declarativeEl, imperativeEl]) {
info(`Running checks for ${el.tagName}`);
is(el.label.getAttribute("foo"), "fuagra", "predefined attribute @foo");
ok(!el.label.hasAttribute("boo"), "predefined attribute @boo");
ok(!el.label.hasAttribute("bardo"), "predefined attribute @bardo");
@ -135,12 +156,10 @@
"attribute inheritance: `=` mapping");
el.label.setAttribute("bardo", "changed-from-child");
el.inherit();
is(el.label.getAttribute("bardo"), "changed-from-child",
"attribute inheritance: doesn't apply when host attr hasn't changed and child attr was changed");
el.label.removeAttribute("bardo");
el.inherit();
ok(!el.label.hasAttribute("bardo"),
"attribute inheritance: doesn't apply when host attr hasn't changed and child attr was removed");
@ -155,7 +174,7 @@
el.setAttribute("bar", "changed-from-host-2");
is(el.label.getAttribute("bardo"), "changed-from-host-2",
"attribute inheritance: does apply when host attr has changed after being removed");
}
}
async function testCustomInterface() {

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

@ -61,56 +61,28 @@ MozElements.MozAutocompleteRichlistitem = class MozAutocompleteRichlistitem exte
this.textContent = "";
this.appendChild(MozXULElement.parseXULToFragment(this._markup));
this.initializeAttributeInheritance();
this._boundaryCutoff = null;
this._inOverflow = false;
this._updateAttributes();
this._adjustAcItem();
}
static get observedAttributes() {
return [
"actiontype",
"current",
"selected",
"image",
"type",
];
}
get inheritedAttributeMap() {
if (!this.__inheritedAttributeMap) {
this.__inheritedAttributeMap = new Map([
[ this.querySelector(".ac-type-icon"), [ "selected", "current", "type" ] ],
[ this.querySelector(".ac-site-icon"), [ "src=image", "selected", "type" ] ],
[ this.querySelector(".ac-title"), [ "selected" ] ],
[ this.querySelector(".ac-title-text"), [ "selected" ] ],
[ this.querySelector(".ac-tags"), [ "selected" ] ],
[ this.querySelector(".ac-tags-text"), [ "selected" ] ],
[ this.querySelector(".ac-separator"), [ "selected", "actiontype", "type" ] ],
[ this.querySelector(".ac-url"), [ "selected", "actiontype" ] ],
[ this.querySelector(".ac-url-text"), [ "selected" ] ],
[ this.querySelector(".ac-action"), [ "selected", "actiontype" ] ],
[ this.querySelector(".ac-action-text"), [ "selected" ] ],
]);
}
return this.__inheritedAttributeMap;
}
attributeChangedCallback(name, oldValue, newValue) {
if (this.isConnectedAndReady && oldValue != newValue &&
this.constructor.observedAttributes.includes(name)) {
this._updateAttributes();
}
}
_updateAttributes() {
for (let [ el, attrs ] of this.inheritedAttributeMap.entries()) {
for (let attr of attrs) {
this.inheritAttribute(el, attr);
}
}
static get inheritedAttributes() {
return {
".ac-type-icon": "selected,current,type",
".ac-site-icon": "src=image,selected,type",
".ac-title": "selected",
".ac-title-text": "selected",
".ac-tags": "selected",
".ac-tags-text": "selected",
".ac-separator": "selected,actiontype,type",
".ac-url": "selected,actiontype",
".ac-url-text": "selected",
".ac-action": "selected,actiontype",
".ac-action-text": "selected",
};
}
get _markup() {
@ -942,31 +914,18 @@ class MozAutocompleteRichlistitemInsecureWarning extends MozElements.MozAutocomp
this.classList.add("forceHandleUnderflow");
}
static get observedAttributes() {
return [
"actiontype",
"current",
"selected",
"image",
"type",
];
}
get inheritedAttributeMap() {
if (!this.__inheritedAttributeMap) {
this.__inheritedAttributeMap = new Map([
[ this.querySelector(".ac-type-icon"), [ "selected", "current", "type" ] ],
[ this.querySelector(".ac-site-icon"), [ "src=image", "selected", "type" ] ],
[ this.querySelector(".ac-title-text"), [ "selected" ] ],
[ this.querySelector(".ac-tags-text"), [ "selected" ] ],
[ this.querySelector(".ac-separator"), [ "selected", "actiontype", "type" ] ],
[ this.querySelector(".ac-url"), [ "selected", "actiontype" ] ],
[ this.querySelector(".ac-url-text"), [ "selected" ] ],
[ this.querySelector(".ac-action"), [ "selected", "actiontype" ] ],
[ this.querySelector(".ac-action-text"), [ "selected" ] ],
]);
}
return this.__inheritedAttributeMap;
static get inheritedAttributes() {
return {
".ac-type-icon": "selected,current,type",
".ac-site-icon": "src=image,selected,type",
".ac-title-text": "selected",
".ac-tags-text": "selected",
".ac-separator": "selected,actiontype,type",
".ac-url": "selected,actiontype",
".ac-url-text": "selected",
".ac-action": "selected,actiontype",
".ac-action-text": "selected",
};
}
get _markup() {

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

@ -111,18 +111,13 @@ MozXULElement.implementCustomInterface(MozMenuBase, [Ci.nsIDOMXULContainerElemen
// The <menucaption> element is used for rendering <html:optgroup> inside of <html:select>,
// See SelectParentHelper.jsm.
class MozMenuCaption extends MozMenuBase {
static get observedAttributes() {
return [
"selected",
"disabled",
"checked",
"image",
"validate",
"src",
"label",
"crop",
"highlightable",
];
static get inheritedAttributes() {
return {
".menu-iconic-left": "selected,disabled,checked",
".menu-iconic-icon": "src=image,validate,src",
".menu-iconic-text": "value=label,crop,highlightable",
".menu-iconic-highlightable-text": "text=label,crop,highlightable",
};
}
_updateAttributes() {
@ -148,17 +143,13 @@ class MozMenuCaption extends MozMenuBase {
connectedCallback() {
this.textContent = "";
this.appendChild(MozXULElement.parseXULToFragment(`
<hbox class="menu-iconic-left" align="center" pack="center" inherits="selected,disabled,checked" role="none">
<image class="menu-iconic-icon" inherits="src=image,validate,src" role="none"></image>
<hbox class="menu-iconic-left" align="center" pack="center" role="none">
<image class="menu-iconic-icon" role="none"></image>
</hbox>
<label class="menu-iconic-text" flex="1" inherits="value=label,crop,highlightable" crop="right" role="none"></label>
<label class="menu-iconic-highlightable-text" inherits="text=label,crop,highlightable" crop="right" role="none"></label>
<label class="menu-iconic-text" flex="1" crop="right" role="none"></label>
<label class="menu-iconic-highlightable-text" crop="right" role="none"></label>
`));
this._inheritedAttributeMap = new Map();
for (let el of this.querySelectorAll("[inherits]")) {
this._inheritedAttributeMap.set(el, el.getAttribute("inherits").split(","));
}
this._updateAttributes();
this.initializeAttributeInheritance();
}
}

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

@ -56,6 +56,15 @@ class MozMenuList extends MenuBaseControl {
}, { mozSystemGroup: true });
}
static get inheritedAttributes() {
return {
".menulist-icon": "src=image",
".menulist-label": "value=label,crop,accesskey,highlightable",
".menulist-highlightable-label": "text=label,crop,accesskey,highlightable",
".menulist-dropmarker": "disabled,open",
};
}
connectedCallback() {
if (this.delayConnectedCallback()) {
return;
@ -65,10 +74,9 @@ class MozMenuList extends MenuBaseControl {
this.prepend(MozMenuList.fragment.cloneNode(true));
this._labelBox = this.children[0];
this._dropmarker = this.children[1];
this.initializeAttributeInheritance();
}
this._updateAttributes();
this.mSelectedInternal = null;
this.mAttributeObserver = null;
this.setInitialSelection();
@ -361,41 +369,6 @@ class MozMenuList extends MenuBaseControl {
this._dropmarker = null;
}
}
static get observedAttributes() {
return ["label", "crop", "accesskey", "highlightable", "image", "disabled",
"open"];
}
attributeChangedCallback() {
if (this.isConnectedAndReady) {
this._updateAttributes();
}
}
_updateAttributes() {
if (!this._labelBox) {
return;
}
let icon = this._labelBox.querySelector(".menulist-icon");
this.inheritAttribute(icon, "src=image");
let label = this._labelBox.querySelector(".menulist-label");
this.inheritAttribute(label, "value=label");
this.inheritAttribute(label, "crop");
this.inheritAttribute(label, "accesskey");
this.inheritAttribute(label, "highlightable");
let highlightableLabel = this._labelBox.querySelector(".menulist-highlightable-label");
this.inheritAttribute(highlightableLabel, "text=label");
this.inheritAttribute(highlightableLabel, "crop");
this.inheritAttribute(highlightableLabel, "accesskey");
this.inheritAttribute(highlightableLabel, "highlightable");
this.inheritAttribute(this._dropmarker, "disabled");
this.inheritAttribute(this._dropmarker, "open");
}
}
MenuBaseControl.implementCustomInterface(MozMenuList, [Ci.nsIDOMXULMenuListElement,

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

@ -8,63 +8,34 @@
// leaking to window scope.
{
class MozPopupNotification extends MozXULElement {
static get observedAttributes() {
return [
"buttonaccesskey",
"buttoncommand",
"buttonhighlight",
"buttonlabel",
"closebuttoncommand",
"closebuttonhidden",
"dropmarkerhidden",
"dropmarkerpopupshown",
"endlabel",
"icon",
"iconclass",
"label",
"learnmoreclick",
"learnmoreurl",
"mainactiondisabled",
"menucommand",
"name",
"origin",
"origin",
"popupid",
"secondarybuttonaccesskey",
"secondarybuttoncommand",
"secondarybuttonhidden",
"secondarybuttonlabel",
"secondendlabel",
"secondname",
"warninghidden",
"warninglabel",
];
}
_updateAttributes() {
for (let [ el, attrs ] of this._inheritedAttributeMap.entries()) {
for (let attr of attrs) {
this.inheritAttribute(el, attr);
}
}
}
get _inheritedAttributeMap() {
if (!this.__inheritedAttributeMap) {
this.__inheritedAttributeMap = new Map();
for (let el of this.querySelectorAll("[inherits]")) {
this.__inheritedAttributeMap.set(el, el.getAttribute("inherits").split(","));
}
}
return this.__inheritedAttributeMap;
static get inheritedAttributes() {
return {
".popup-notification-icon": "popupid,src=icon,class=iconclass",
".popup-notification-origin": "value=origin,tooltiptext=origin",
".popup-notification-description": "popupid",
".popup-notification-description > span:first-of-type": "text=label,popupid",
".popup-notification-description > b:first-of-type": "text=name,popupid",
".popup-notification-description > span:nth-of-type(2)": "text=endlabel,popupid",
".popup-notification-description > b:last-of-type": "text=secondname,popupid",
".popup-notification-description > span:last-of-type": "secondendlabel,popupid",
".popup-notification-closebutton": "oncommand=closebuttoncommand,hidden=closebuttonhidden",
".popup-notification-learnmore-link": "onclick=learnmoreclick,href=learnmoreurl",
".popup-notification-warning": "hidden=warninghidden,text=warninglabel",
".popup-notification-button-container > .popup-notification-secondary-button":
"oncommand=secondarybuttoncommand,label=secondarybuttonlabel,accesskey=secondarybuttonaccesskey,hidden=secondarybuttonhidden",
".popup-notification-button-container > toolbarseparator": "hidden=dropmarkerhidden",
".popup-notification-dropmarker": "onpopupshown=dropmarkerpopupshown,hidden=dropmarkerhidden",
".popup-notification-dropmarker > menupopup": "oncommand=menucommand",
".popup-notification-primary-button": "oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey,default=buttonhighlight,disabled=mainactiondisabled",
};
}
attributeChangedCallback(name, oldValue, newValue) {
if (!this._hasSlotted || oldValue === newValue) {
if (!this._hasSlotted) {
return;
}
this._updateAttributes();
super.attributeChangedCallback(name, oldValue, newValue);
}
show() {
@ -89,33 +60,32 @@ class MozPopupNotification extends MozXULElement {
this.appendChild(MozXULElement.parseXULToFragment(`
<hbox class="popup-notification-header-container"></hbox>
<hbox align="start" class="popup-notification-body-container">
<image class="popup-notification-icon"
inherits="popupid,src=icon,class=iconclass"/>
<image class="popup-notification-icon"/>
<vbox flex="1" pack="start" class="popup-notification-body">
<hbox align="start">
<vbox flex="1">
<label class="popup-notification-origin header" inherits="value=origin,tooltiptext=origin" crop="center"></label>
<label class="popup-notification-origin header" crop="center"></label>
<!-- These need to be on the same line to avoid creating
whitespace between them (whitespace is added in the
localization file, if necessary). -->
<description class="popup-notification-description" inherits="popupid"><html:span inherits="text=label,popupid"></html:span><html:b inherits="text=name,popupid"></html:b><html:span inherits="text=endlabel,popupid"></html:span><html:b inherits="text=secondname,popupid"></html:b><html:span inherits="text=secondendlabel,popupid"></html:span></description>
<description class="popup-notification-description"><html:span></html:span><html:b></html:b><html:span></html:span><html:b></html:b><html:span></html:span></description>
</vbox>
<toolbarbutton class="messageCloseButton close-icon popup-notification-closebutton tabbable" inherits="oncommand=closebuttoncommand,hidden=closebuttonhidden" tooltiptext="&closeNotification.tooltip;"></toolbarbutton>
<toolbarbutton class="messageCloseButton close-icon popup-notification-closebutton tabbable" tooltiptext="&closeNotification.tooltip;"></toolbarbutton>
</hbox>
<label class="text-link popup-notification-learnmore-link" inherits="onclick=learnmoreclick,href=learnmoreurl">&learnMore;</label>
<label class="text-link popup-notification-learnmore-link">&learnMore;</label>
<checkbox class="popup-notification-checkbox" oncommand="PopupNotifications._onCheckboxCommand(event)"></checkbox>
<description class="popup-notification-warning" inherits="hidden=warninghidden,text=warninglabel"></description>
<description class="popup-notification-warning"></description>
</vbox>
</hbox>
<hbox class="popup-notification-footer-container"></hbox>
<hbox class="popup-notification-button-container panel-footer">
<button class="popup-notification-button popup-notification-secondary-button" inherits="oncommand=secondarybuttoncommand,label=secondarybuttonlabel,accesskey=secondarybuttonaccesskey,hidden=secondarybuttonhidden"></button>
<toolbarseparator inherits="hidden=dropmarkerhidden"></toolbarseparator>
<button type="menu" class="popup-notification-button popup-notification-dropmarker" aria-label="&moreActionsButton.accessibleLabel;" inherits="onpopupshown=dropmarkerpopupshown,hidden=dropmarkerhidden">
<menupopup position="after_end" aria-label="&moreActionsButton.accessibleLabel;" inherits="oncommand=menucommand">
<button class="popup-notification-button popup-notification-secondary-button"></button>
<toolbarseparator></toolbarseparator>
<button type="menu" class="popup-notification-button popup-notification-dropmarker" aria-label="&moreActionsButton.accessibleLabel;">
<menupopup position="after_end" aria-label="&moreActionsButton.accessibleLabel;">
</menupopup>
</button>
<button class="popup-notification-button popup-notification-primary-button" label="&defaultButton.label;" accesskey="&defaultButton.accesskey;" inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey,default=buttonhighlight,disabled=mainactiondisabled"></button>
<button class="popup-notification-button popup-notification-primary-button" label="&defaultButton.label;" accesskey="&defaultButton.accesskey;"></button>
</hbox>
`, ["chrome://global/locale/notification.dtd"]));
@ -140,7 +110,7 @@ class MozPopupNotification extends MozXULElement {
this.appendNotificationContent(popupnotificationcontent);
}
this._updateAttributes();
this.initializeAttributeInheritance();
}
appendNotificationContent(el) {

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

@ -244,13 +244,11 @@
customElements.define("treecolpicker", MozTreecolPicker);
class MozTreecol extends MozElements.BaseControl {
static get observedAttributes() {
return [
"label",
"sortdirection",
"hideheader",
"crop",
];
static get inheritedAttributes() {
return {
".treecol-sortdirection": "sortdirection,hidden=hideheader",
".treecol-text": "value=label,crop",
};
}
get content() {
@ -316,28 +314,7 @@
this.textContent = "";
this.appendChild(this.content);
this._updateAttributes();
}
attributeChangedCallback() {
if (this.isConnectedAndReady) {
this._updateAttributes();
}
}
_updateAttributes() {
let image = this.querySelector(".treecol-sortdirection");
let label = this.querySelector(".treecol-text");
this.inheritAttribute(image, "sortdirection");
this.inheritAttribute(image, "hidden=hideheader");
this.inheritAttribute(label, "value=label");
// Don't remove the attribute on the child if it's los on the host.
if (this.hasAttribute("crop")) {
this.inheritAttribute(label, "crop");
}
this.initializeAttributeInheritance();
}
set ordinal(val) {
@ -489,6 +466,12 @@
customElements.define("treecol", MozTreecol);
class MozTreecols extends MozElements.BaseControl {
static get inheritedAttributes() {
return {
"treecolpicker": "tooltiptext=pickertooltiptext",
};
}
connectedCallback() {
if (this.delayConnectedCallback()) {
return;
@ -498,11 +481,9 @@
this.appendChild(MozXULElement.parseXULToFragment(`
<treecolpicker class="treecol-image" fixed="true"></treecolpicker>
`));
this.initializeAttributeInheritance();
}
let treecolpicker = this.querySelector("treecolpicker");
this.inheritAttribute(treecolpicker, "tooltiptext=pickertooltiptext");
// Set resizeafter="farthest" on the splitters if nothing else has been
// specified.
Array.forEach(this.getElementsByTagName("splitter"), function(splitter) {