new file mode 100644
--- /dev/null
+++ b/browser/base/content/customelements/custom-elements-polyfill.js
@@ -0,0 +1,48 @@
+(function(){
+'use strict';var g={},h=new function(){};g.default=h;var k={},l=new Set("annotation-xml color-profile font-face font-face-src font-face-uri font-face-format font-face-name missing-glyph".split(" "));k.isValidCustomElementName=function(b){return!l.has(b)&&!0};k.isConnected=function(b){var a=b.isConnected;if(void 0!==a)return a;for(;b&&!(b.__CE_isImportDocument||b instanceof Document);)b=b.parentNode||(window.ShadowRoot&&b instanceof ShadowRoot?b.host:void 0);return!(!b||!(b.__CE_isImportDocument||b instanceof Document))};
+k.walkDeepDescendantElements=function(b,a,f){void 0===f&&new Set;f=Components.interfaces;var c=Components.classes["@mozilla.org/inspector/deep-tree-walker;1"].createInstance(Components.interfaces.inIDeepTreeWalker);c.showAnonymousContent=!0;c.showSubDocuments=!1;c.showDocumentsAsNodes=!1;c.init(b,f.nsIDOMNodeFilter.SHOW_ELEMENT);for(a(b);c.nextNode();)c.currentNode instanceof Element&&a(c.currentNode)};k.setPropertyUnchecked=function(b,a,f){b[a]=f};var m={default:{custom:1,failed:2}};var r={};function t(){this._localNameToDefinition=new Map;this._constructorToDefinition=new Map;this._patches=[];this._hasPatches=!1}t.prototype.setDefinition=function(b,a){this._localNameToDefinition.set(b,a);this._constructorToDefinition.set(a.constructor,a)};t.prototype.localNameToDefinition=function(b){return this._localNameToDefinition.get(b)};t.prototype.constructorToDefinition=function(b){return this._constructorToDefinition.get(b)};t.prototype.addPatch=function(b){this._hasPatches=!0;this._patches.push(b)};
+t.prototype.patchTree=function(b){var a=this;this._hasPatches&&k.walkDeepDescendantElements(b,function(b){return a.patch(b)})};t.prototype.patch=function(b){if(this._hasPatches&&!b.__CE_patched){b.__CE_patched=!0;for(var a=0;a<this._patches.length;a++)this._patches[a](b)}};t.prototype.connectTree=function(b){var a=[];k.walkDeepDescendantElements(b,function(b){return a.push(b)});for(b=0;b<a.length;b++){var f=a[b];f.__CE_state===m.default.custom?this.connectedCallback(f):this.upgradeElement(f)}};
+t.prototype.disconnectTree=function(b){var a=[];k.walkDeepDescendantElements(b,function(b){return a.push(b)});for(b=0;b<a.length;b++){var f=a[b];f.__CE_state===m.default.custom&&this.disconnectedCallback(f)}};
+t.prototype.patchAndUpgradeTree=function(b,a){a=void 0===a?{}:a;var f=this,c=a.visitedImports||new Set,e=a.upgrade||function(b){return f.upgradeElement(b)},d=[];k.walkDeepDescendantElements(b,function(b){if("link"===b.localName&&"import"===b.getAttribute("rel")){var a=b.import;a instanceof Node&&"complete"===a.readyState?(a.__CE_isImportDocument=!0,a.__CE_hasRegistry=!0):b.addEventListener("load",function(){var a=b.import;a.__CE_documentLoadHandled||(a.__CE_documentLoadHandled=!0,a.__CE_isImportDocument=
+!0,a.__CE_hasRegistry=!0,new Set(c),c.delete(a),f.patchAndUpgradeTree(a,{visitedImports:c,upgrade:e}))})}else d.push(b)},c);if(this._hasPatches)for(b=0;b<d.length;b++)this.patch(d[b]);for(b=0;b<d.length;b++)e(d[b])};
+t.prototype.upgradeElement=function(b){if(void 0===b.__CE_state){var a=this.localNameToDefinition(b.localName);if(a){a.constructionStack.push(b);var f=a.constructor;try{try{if(new f!==b)throw Error("The custom element constructor did not produce the element being upgraded.");}finally{a.constructionStack.pop()}}catch(d){throw b.__CE_state=m.default.failed,d;}b.__CE_state=m.default.custom;b.__CE_definition=a;if(a.attributeChangedCallback)for(a=a.observedAttributes,f=0;f<a.length;f++){var c=a[f],e=b.getAttribute(c);
+null!==e&&this.attributeChangedCallback(b,c,null,e,null)}k.isConnected(b)&&this.connectedCallback(b)}}};t.prototype.connectedCallback=function(b){var a=b.__CE_definition;a.connectedCallback&&a.connectedCallback.call(b)};t.prototype.disconnectedCallback=function(b){var a=b.__CE_definition;a.disconnectedCallback&&a.disconnectedCallback.call(b)};
+t.prototype.attributeChangedCallback=function(b,a,f,c,e){var d=b.__CE_definition;d.attributeChangedCallback&&-1<d.observedAttributes.indexOf(a)&&d.attributeChangedCallback.call(b,a,f,c,e)};r.default=t;var u={};function v(b,a){this._internals=b;this._document=a;this._observer=void 0;this._internals.patchAndUpgradeTree(this._document);"loading"===this._document.readyState&&(this._observer=new MutationObserver(this._handleMutations.bind(this)),this._observer.observe(this._document,{childList:!0,subtree:!0}))}v.prototype.disconnect=function(){this._observer&&this._observer.disconnect()};
+v.prototype._handleMutations=function(b){var a=this._document.readyState;"interactive"!==a&&"complete"!==a||this.disconnect();for(a=0;a<b.length;a++)for(var f=b[a].addedNodes,c=0;c<f.length;c++)this._internals.patchAndUpgradeTree(f[c])};u.default=v;var w={};function x(){var b=this;this._resolve=this._value=void 0;this._promise=new Promise(function(a){b._resolve=a;b._value&&a(b._value)})}x.prototype.resolve=function(b){if(this._value)throw Error("Already resolved.");this._value=b;this._resolve&&this._resolve(b)};x.prototype.toPromise=function(){return this._promise};w.default=x;var y={};function z(b){this._elementDefinitionIsRunning=!1;this._internals=b;this._whenDefinedDeferred=new Map;this._flushCallback=function(b){return b()};this._flushPending=!1;this._pendingDefinitions=[];this._documentConstructionObserver=new u.default(b,document)}
+z.prototype.define=function(b,a){var f=this;if(!(a instanceof Function))throw new TypeError("Custom element constructors must be functions.");if(!k.isValidCustomElementName(b))throw new SyntaxError("The element name '"+b+"' is not valid.");if(this._internals.localNameToDefinition(b))throw Error("A custom element with name '"+b+"' has already been defined.");if(this._elementDefinitionIsRunning)throw Error("A custom element is already being defined.");this._elementDefinitionIsRunning=!0;var c,e,d,q,
+p;try{var n=function(b){var a=B[b];if(void 0!==a&&!(a instanceof Function))throw Error("The '"+b+"' callback must be a function.");return a},B=a.prototype;if(!(B instanceof Object))throw new TypeError("The custom element constructor's prototype is not an object.");c=n("connectedCallback");e=n("disconnectedCallback");d=n("adoptedCallback");q=n("attributeChangedCallback");p=a.observedAttributes||[]}catch(L){return}finally{this._elementDefinitionIsRunning=!1}this._pendingDefinitions.push({localName:b,
+constructor:a,connectedCallback:c,disconnectedCallback:e,adoptedCallback:d,attributeChangedCallback:q,observedAttributes:p,constructionStack:[]});this._flushPending||(this._flushPending=!0,this._flushCallback(function(){return f._flush()}))};
+z.prototype._flush=function(){var b=this;if(!1!==this._flushPending){this._flushPending=!1;for(var a=this._pendingDefinitions,f=new Map,c=0;c<a.length;c++)f.set(a[c].localName,[]);for(this._internals.patchAndUpgradeTree(document,{upgrade:function(a){b._internals.upgradeElement(a);if(void 0===a.__CE_state){var e=f.get(a.localName);e&&e.push(a)}}});0<a.length;){var e=a.shift(),c=e.localName;this._internals.setDefinition(c,e);for(var e=f.get(e.localName),d=0;d<e.length;d++)this._internals.upgradeElement(e[d]);
+(c=this._whenDefinedDeferred.get(c))&&c.resolve(void 0)}}};z.prototype.get=function(b){if(b=this._internals.localNameToDefinition(b))return b.constructor};
+z.prototype.whenDefined=function(b){if(!k.isValidCustomElementName(b))return Promise.reject(new SyntaxError("'"+b+"' is not a valid custom element name."));var a=this._whenDefinedDeferred.get(b);if(a)return a.toPromise();a=new w.default;this._whenDefinedDeferred.set(b,a);this._internals.localNameToDefinition(b)&&!this._pendingDefinitions.some(function(a){return a.localName===b})&&a.resolve(void 0);return a.toPromise()};
+z.prototype.polyfillWrapFlushCallback=function(b){this._documentConstructionObserver.disconnect();var a=this._flushCallback;this._flushCallback=function(f){return b(function(){return a(f)})}};window.CustomElementRegistry=z;z.prototype.define=z.prototype.define;z.prototype.get=z.prototype.get;z.prototype.whenDefined=z.prototype.whenDefined;z.prototype.polyfillWrapFlushCallback=z.prototype.polyfillWrapFlushCallback;y.default=z;var A={},C={Document_createElement:window.Document.prototype.createElement,Document_createElementNS:window.Document.prototype.createElementNS,Document_importNode:window.Document.prototype.importNode,Document_prepend:window.Document.prototype.prepend,Document_append:window.Document.prototype.append,Node_cloneNode:window.Node.prototype.cloneNode,Node_appendChild:window.Node.prototype.appendChild,Node_insertBefore:window.Node.prototype.insertBefore,Node_removeChild:window.Node.prototype.removeChild,
+Node_replaceChild:window.Node.prototype.replaceChild,Node_textContent:Object.getOwnPropertyDescriptor(window.Node.prototype,"textContent"),Element_attachShadow:window.Element.prototype.attachShadow,Element_innerHTML:Object.getOwnPropertyDescriptor(window.Element.prototype,"innerHTML"),Element_getAttribute:window.Element.prototype.getAttribute,Element_setAttribute:window.Element.prototype.setAttribute,Element_removeAttribute:window.Element.prototype.removeAttribute,Element_getAttributeNS:window.Element.prototype.getAttributeNS,
+Element_setAttributeNS:window.Element.prototype.setAttributeNS,Element_removeAttributeNS:window.Element.prototype.removeAttributeNS,Element_insertAdjacentElement:window.Element.prototype.insertAdjacentElement,Element_prepend:window.Element.prototype.prepend,Element_append:window.Element.prototype.append,Element_before:window.Element.prototype.before,Element_after:window.Element.prototype.after,Element_replaceWith:window.Element.prototype.replaceWith,Element_remove:window.Element.prototype.remove,
+XULElement:window.XULElement,XULElement_innerHTML:Object.getOwnPropertyDescriptor(window.XULElement.prototype,"innerHTML"),XULElement_insertAdjacentElement:window.XULElement.prototype.insertAdjacentElement};A.default=C;var D={default:function(b){window.XULElement=function(){function a(){var a=this.constructor,c=b.constructorToDefinition(a);if(!c)throw Error("The custom element being constructed was not registered with `customElements`.");var e=c.constructionStack;if(0===e.length)return e=A.default.Document_createElement.call(document,c.localName),Object.setPrototypeOf(e,a.prototype),e.__CE_state=m.default.custom,e.__CE_definition=c,b.patch(e),e;var c=e.length-1,d=e[c];if(d===g.default)throw Error("The XULElement constructor was either called reentrantly for this constructor or called multiple times.");
+e[c]=g.default;Object.setPrototypeOf(d,a.prototype);b.patch(d);return d}a.prototype=A.default.XULElement.prototype;return a}()}};var E={default:function(b,a,f){a.prepend=function(a){for(var e=[],d=0;d<arguments.length;++d)e[d-0]=arguments[d];d=e.filter(function(b){return b instanceof Node&&k.isConnected(b)});f.prepend.apply(this,e);for(var c=0;c<d.length;c++)b.disconnectTree(d[c]);if(k.isConnected(this))for(d=0;d<e.length;d++)c=e[d],c instanceof Element&&b.connectTree(c)};a.append=function(a){for(var e=[],d=0;d<arguments.length;++d)e[d-0]=arguments[d];d=e.filter(function(b){return b instanceof Node&&k.isConnected(b)});f.append.apply(this,
+e);for(var c=0;c<d.length;c++)b.disconnectTree(d[c]);if(k.isConnected(this))for(d=0;d<e.length;d++)c=e[d],c instanceof Element&&b.connectTree(c)}}};var F={default:function(b){k.setPropertyUnchecked(Document.prototype,"createElement",function(a){if(this.__CE_hasRegistry){var f=b.localNameToDefinition(a);if(f)return new f.constructor}a=A.default.Document_createElement.call(this,a);b.patch(a);return a});k.setPropertyUnchecked(Document.prototype,"importNode",function(a,f){a=A.default.Document_importNode.call(this,a,f);this.__CE_hasRegistry?b.patchAndUpgradeTree(a):b.patchTree(a);return a});k.setPropertyUnchecked(Document.prototype,"createElementNS",
+function(a,f){if(this.__CE_hasRegistry&&(null===a||"http://www.w3.org/1999/xhtml"===a)){var c=b.localNameToDefinition(f);if(c)return new c.constructor}a=A.default.Document_createElementNS.call(this,a,f);b.patch(a);return a});E.default(b,Document.prototype,{prepend:A.default.Document_prepend,append:A.default.Document_append})}};var G={default:function(b){function a(a,c){Object.defineProperty(a,"textContent",{enumerable:c.enumerable,configurable:!0,get:c.get,set:function(a){if(this.nodeType===Node.TEXT_NODE)c.set.call(this,a);else{var d=void 0;if(this.firstChild){var e=this.childNodes,f=e.length;if(0<f&&k.isConnected(this))for(var d=Array(f),n=0;n<f;n++)d[n]=e[n]}c.set.call(this,a);if(d)for(a=0;a<d.length;a++)b.disconnectTree(d[a])}}})}k.setPropertyUnchecked(Node.prototype,"insertBefore",function(a,c){if(a instanceof DocumentFragment){var e=
+Array.prototype.slice.apply(a.childNodes);a=A.default.Node_insertBefore.call(this,a,c);if(k.isConnected(this))for(c=0;c<e.length;c++)b.connectTree(e[c]);return a}e=k.isConnected(a);c=A.default.Node_insertBefore.call(this,a,c);e&&b.disconnectTree(a);k.isConnected(this)&&b.connectTree(a);return c});k.setPropertyUnchecked(Node.prototype,"appendChild",function(a){if(a instanceof DocumentFragment){var c=Array.prototype.slice.apply(a.childNodes);a=A.default.Node_appendChild.call(this,a);if(k.isConnected(this))for(var e=
+0;e<c.length;e++)b.connectTree(c[e]);return a}c=k.isConnected(a);e=A.default.Node_appendChild.call(this,a);c&&b.disconnectTree(a);k.isConnected(this)&&b.connectTree(a);return e});k.setPropertyUnchecked(Node.prototype,"cloneNode",function(a){a=A.default.Node_cloneNode.call(this,a);this.ownerDocument.__CE_hasRegistry?b.patchAndUpgradeTree(a):b.patchTree(a);return a});k.setPropertyUnchecked(Node.prototype,"removeChild",function(a){var c=k.isConnected(a),e=A.default.Node_removeChild.call(this,a);c&&b.disconnectTree(a);
+return e});k.setPropertyUnchecked(Node.prototype,"replaceChild",function(a,c){if(a instanceof DocumentFragment){var e=Array.prototype.slice.apply(a.childNodes);a=A.default.Node_replaceChild.call(this,a,c);if(k.isConnected(this))for(b.disconnectTree(c),c=0;c<e.length;c++)b.connectTree(e[c]);return a}var e=k.isConnected(a),d=A.default.Node_replaceChild.call(this,a,c),f=k.isConnected(this);f&&b.disconnectTree(c);e&&b.disconnectTree(a);f&&b.connectTree(a);return d});A.default.Node_textContent&&A.default.Node_textContent.get?
+a(Node.prototype,A.default.Node_textContent):b.addPatch(function(b){a(b,{enumerable:!0,configurable:!0,get:function(){for(var a=[],b=0;b<this.childNodes.length;b++)a.push(this.childNodes[b].textContent);return a.join("")},set:function(a){for(;this.firstChild;)A.default.Node_removeChild.call(this,this.firstChild);A.default.Node_appendChild.call(this,document.createTextNode(a))}})})}};var H={default:function(b,a,f){a.before=function(a){for(var e=[],d=0;d<arguments.length;++d)e[d-0]=arguments[d];d=e.filter(function(a){return a instanceof Node&&k.isConnected(a)});f.before.apply(this,e);for(var c=0;c<d.length;c++)b.disconnectTree(d[c]);if(k.isConnected(this))for(d=0;d<e.length;d++)c=e[d],c instanceof Element&&b.connectTree(c)};a.after=function(a){for(var e=[],d=0;d<arguments.length;++d)e[d-0]=arguments[d];d=e.filter(function(a){return a instanceof Node&&k.isConnected(a)});f.after.apply(this,
+e);for(var c=0;c<d.length;c++)b.disconnectTree(d[c]);if(k.isConnected(this))for(d=0;d<e.length;d++)c=e[d],c instanceof Element&&b.connectTree(c)};a.replaceWith=function(a){for(var e=[],d=0;d<arguments.length;++d)e[d-0]=arguments[d];var d=e.filter(function(a){return a instanceof Node&&k.isConnected(a)}),c=k.isConnected(this);f.replaceWith.apply(this,e);for(var p=0;p<d.length;p++)b.disconnectTree(d[p]);if(c)for(b.disconnectTree(this),d=0;d<e.length;d++)c=e[d],c instanceof Element&&b.connectTree(c)};
+a.remove=function(){var a=k.isConnected(this);f.remove.call(this);a&&b.disconnectTree(this)}}};var I={default:function(b){function a(a,d){Object.defineProperty(a,"innerHTML",{enumerable:d.enumerable,configurable:!0,get:d.get,set:function(a){var e=this,c=void 0;k.isConnected(this)&&(c=[],k.walkDeepDescendantElements(this,function(a){a!==e&&c.push(a)}));d.set.call(this,a);if(c)for(var f=0;f<c.length;f++){var q=c[f];q.__CE_state===m.default.custom&&b.disconnectedCallback(q)}this.ownerDocument.__CE_hasRegistry?b.patchAndUpgradeTree(this):b.patchTree(this);return a}})}function f(a,d){k.setPropertyUnchecked(a,
+"insertAdjacentElement",function(a,c){var e=k.isConnected(c);a=d.call(this,a,c);e&&b.disconnectTree(c);k.isConnected(a)&&b.connectTree(c);return a})}A.default.Element_attachShadow?k.setPropertyUnchecked(Element.prototype,"attachShadow",function(a){return this.__CE_shadowRoot=a=A.default.Element_attachShadow.call(this,a)}):console.warn("Custom Elements: `Element#attachShadow` was not patched.");if(A.default.Element_innerHTML&&A.default.Element_innerHTML.get)a(Element.prototype,A.default.Element_innerHTML);
+else if(A.default.XULElement_innerHTML&&A.default.XULElement_innerHTML.get)a(XULElement.prototype,A.default.XULElement_innerHTML);else{var c=A.default.Document_createElement.call(document,"div");b.addPatch(function(b){a(b,{enumerable:!0,configurable:!0,get:function(){return A.default.Node_cloneNode.call(this,!0).innerHTML},set:function(a){var b="template"===this.localName?this.content:this;for(c.innerHTML=a;0<b.childNodes.length;)A.default.Node_removeChild.call(b,b.childNodes[0]);for(;0<c.childNodes.length;)A.default.Node_appendChild.call(b,
+c.childNodes[0])}})})}k.setPropertyUnchecked(Element.prototype,"setAttribute",function(a,c){if(this.__CE_state!==m.default.custom)return A.default.Element_setAttribute.call(this,a,c);var d=A.default.Element_getAttribute.call(this,a);A.default.Element_setAttribute.call(this,a,c);c=A.default.Element_getAttribute.call(this,a);b.attributeChangedCallback(this,a,d,c,null)});k.setPropertyUnchecked(Element.prototype,"setAttributeNS",function(a,c,f){if(this.__CE_state!==m.default.custom)return A.default.Element_setAttributeNS.call(this,
+a,c,f);var d=A.default.Element_getAttributeNS.call(this,a,c);A.default.Element_setAttributeNS.call(this,a,c,f);f=A.default.Element_getAttributeNS.call(this,a,c);b.attributeChangedCallback(this,c,d,f,a)});k.setPropertyUnchecked(Element.prototype,"removeAttribute",function(a){if(this.__CE_state!==m.default.custom)return A.default.Element_removeAttribute.call(this,a);var c=A.default.Element_getAttribute.call(this,a);A.default.Element_removeAttribute.call(this,a);null!==c&&b.attributeChangedCallback(this,
+a,c,null,null)});k.setPropertyUnchecked(Element.prototype,"removeAttributeNS",function(a,c){if(this.__CE_state!==m.default.custom)return A.default.Element_removeAttributeNS.call(this,a,c);var d=A.default.Element_getAttributeNS.call(this,a,c);A.default.Element_removeAttributeNS.call(this,a,c);var e=A.default.Element_getAttributeNS.call(this,a,c);d!==e&&b.attributeChangedCallback(this,c,d,e,a)});A.default.XULElement_insertAdjacentElement?f(XULElement.prototype,A.default.XULElement_insertAdjacentElement):
+A.default.Element_insertAdjacentElement?f(Element.prototype,A.default.Element_insertAdjacentElement):console.warn("Custom Elements: `Element#insertAdjacentElement` was not patched.");E.default(b,Element.prototype,{prepend:A.default.Element_prepend,append:A.default.Element_append});H.default(b,Element.prototype,{before:A.default.Element_before,after:A.default.Element_after,replaceWith:A.default.Element_replaceWith,remove:A.default.Element_remove})}};/*
+
+ Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
+ This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ Code distributed by Google as part of the polymer project is also
+ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+*/
+var J=window.customElements;if(!J||J.forcePolyfill||"function"!=typeof J.define||"function"!=typeof J.get){var K=new r.default;D.default(K);F.default(K);G.default(K);I.default(K);document.__CE_hasRegistry=!0;var customElements=new y.default(K);Object.defineProperty(window,"customElements",{configurable:!0,enumerable:!0,value:customElements})};
+}).call(self);
+
+//# sourceMappingURL=custom-elements.min.js.map
new file mode 100644
--- /dev/null
+++ b/browser/base/content/customelements/firefox-panel.js
@@ -0,0 +1,540 @@
+// XXX: "downloadsPanel" doesn't get recognized by CE polyfill (xul overlay?)
+
+
+
+class FirefoxPopupBase extends BaseElement {
+
+ // XXX: Do we need to generateQI for nsIDOMXULPopupElement? The hamburger menu
+ // seems to open without this.
+ // get QueryInterface() {
+ // return XPCOMUtils.generateQI([Ci.nsIDOMXULPopupElement]);
+ // }
+
+ constructor() {
+ super();
+ }
+ connectedCallback() {
+ console.log(this, "connected");
+
+ let comment = document.createComment("Creating firefox-popup-base");
+ this.prepend(comment);
+ }
+ disconnectedCallback() {}
+
+ set label(val) {
+ this.setAttribute("label", val);
+ return val;
+ }
+
+ get label() {
+ return this.getAttribute("label");
+ }
+
+ set position(val) {
+ this.setAttribute("position", val);
+ return val;
+ }
+
+ get position() {
+ return this.getAttribute("position");
+ }
+
+ get popupBoxObject() {
+ return this.boxObject;
+ }
+
+ get state() {
+ return this.popupBoxObject.popupState;
+ }
+
+ get triggerNode() {
+ return this.popupBoxObject.triggerNode;
+ }
+
+ get anchorNode() {
+ return this.popupBoxObject.anchorNode;
+ }
+
+ set autoPosition(val) {
+ return (this.popupBoxObject.autoPosition = val);
+ }
+
+ get autoPosition() {
+ return this.popupBoxObject.autoPosition;
+ }
+
+ get alignmentPosition() {
+ return this.popupBoxObject.alignmentPosition;
+ }
+
+ get alignmentOffset() {
+ return this.popupBoxObject.alignmentOffset;
+ }
+ openPopup(
+ aAnchorElement,
+ aPosition,
+ aX,
+ aY,
+ aIsContextMenu,
+ aAttributesOverride,
+ aTriggerEvent
+ ) {
+ // Allow for passing an options object as the second argument.
+ if (
+ arguments.length == 2 &&
+ arguments[1] != null &&
+ typeof arguments[1] == "object"
+ ) {
+ let params = arguments[1];
+ aPosition = params.position;
+ aX = params.x;
+ aY = params.y;
+ aIsContextMenu = params.isContextMenu;
+ aAttributesOverride = params.attributesOverride;
+ aTriggerEvent = params.triggerEvent;
+ }
+
+ dump(`openPopup ${aX} ${aY} ${this.popupBoxObject.openPopup}\n`);
+ try {
+ var popupBox = this.popupBoxObject;
+ if (popupBox)
+ popupBox.openPopup(
+ aAnchorElement,
+ aPosition,
+ aX,
+ aY,
+ aIsContextMenu,
+ aAttributesOverride,
+ aTriggerEvent
+ );
+ } catch (e) {
+ dump(`Caught ${e} \n`);
+ }
+ }
+ openPopupAtScreen(aX, aY, aIsContextMenu, aTriggerEvent) {
+ try {
+ var popupBox = this.popupBoxObject;
+ if (popupBox)
+ popupBox.openPopupAtScreen(aX, aY, aIsContextMenu, aTriggerEvent);
+ } catch (e) {}
+ }
+ openPopupAtScreenRect(
+ aPosition,
+ aX,
+ aY,
+ aWidth,
+ aHeight,
+ aIsContextMenu,
+ aAttributesOverride,
+ aTriggerEvent
+ ) {
+ try {
+ var popupBox = this.popupBoxObject;
+ if (popupBox)
+ popupBox.openPopupAtScreenRect(
+ aPosition,
+ aX,
+ aY,
+ aWidth,
+ aHeight,
+ aIsContextMenu,
+ aAttributesOverride,
+ aTriggerEvent
+ );
+ } catch (e) {}
+ }
+ showPopup(element, xpos, ypos, popuptype, anchoralignment, popupalignment) {
+ var popupBox = null;
+ var menuBox = null;
+ try {
+ popupBox = this.popupBoxObject;
+ } catch (e) {}
+ try {
+ menuBox = this.parentNode.boxObject;
+ } catch (e) {}
+ if (menuBox instanceof MenuBoxObject) menuBox.openMenu(true);
+ else if (popupBox)
+ popupBox.showPopup(
+ element,
+ this,
+ xpos,
+ ypos,
+ popuptype,
+ anchoralignment,
+ popupalignment
+ );
+ }
+ hidePopup(cancel) {
+ var popupBox = null;
+ var menuBox = null;
+ try {
+ popupBox = this.popupBoxObject;
+ } catch (e) {}
+ try {
+ menuBox = this.parentNode.boxObject;
+ } catch (e) {}
+ if (menuBox instanceof MenuBoxObject) menuBox.openMenu(false);
+ else if (popupBox instanceof PopupBoxObject) popupBox.hidePopup(cancel);
+ }
+ enableKeyboardNavigator(aEnableKeyboardNavigator) {
+ this.popupBoxObject.enableKeyboardNavigator(aEnableKeyboardNavigator);
+ }
+ enableRollup(aEnableRollup) {
+ this.popupBoxObject.enableRollup(aEnableRollup);
+ }
+ sizeTo(aWidth, aHeight) {
+ this.popupBoxObject.sizeTo(aWidth, aHeight);
+ }
+ moveTo(aLeft, aTop) {
+ this.popupBoxObject.moveTo(aLeft, aTop);
+ }
+ moveToAnchor(aAnchorElement, aPosition, aX, aY, aAttributesOverride) {
+ this.popupBoxObject.moveToAnchor(
+ aAnchorElement,
+ aPosition,
+ aX,
+ aY,
+ aAttributesOverride
+ );
+ }
+ getOuterScreenRect() {
+ return this.popupBoxObject.getOuterScreenRect();
+ }
+ setConstraintRect(aRect) {
+ this.popupBoxObject.setConstraintRect(aRect);
+ }
+}
+customElements.define("firefox-popup-base", FirefoxPopupBase);
+
+
+class FirefoxPanel extends FirefoxPopupBase {
+
+
+ // XXX: Do we need to generateQI for nsIDOMXULPopupElement? The hamburger menu
+ // seems to open without this.
+ // get QueryInterface() {
+ // return XPCOMUtils.generateQI([Ci.nsIDOMXULPopupElement]);
+ // }
+
+ constructor() {
+ super();
+ }
+ connectedCallback() {
+ super.connectedCallback();
+ dump(`connectedCallback FirefoxPanel ${this.id}\n`);
+
+ let comment = document.createComment("Creating firefox-panel");
+ this.prepend(comment);
+
+ Object.defineProperty(this, "_prevFocus", {
+ configurable: true,
+ enumerable: true,
+ get() {
+ delete this._prevFocus;
+ return (this._prevFocus = 0);
+ },
+ set(val) {
+ delete this._prevFocus;
+ return (this._prevFocus = val);
+ }
+ });
+ Object.defineProperty(this, "_dragBindingAlive", {
+ configurable: true,
+ enumerable: true,
+ get() {
+ delete this._dragBindingAlive;
+ return (this._dragBindingAlive = true);
+ },
+ set(val) {
+ delete this._dragBindingAlive;
+ return (this._dragBindingAlive = val);
+ }
+ });
+
+ if (this.getAttribute("backdrag") == "true" && !this._draggableStarted) {
+ this._draggableStarted = true;
+ try {
+ let tmp = {};
+ Components.utils.import(
+ "resource://gre/modules/WindowDraggingUtils.jsm",
+ tmp
+ );
+ let draghandle = new tmp.WindowDraggingElement(this);
+ draghandle.mouseDownCheck = function() {
+ return this._dragBindingAlive;
+ };
+ } catch (e) {}
+ }
+
+ this.addEventListener("popupshowing", event => {
+ // Capture the previous focus before has a chance to get set inside the panel
+ try {
+ this._prevFocus = Components.utils.getWeakReference(
+ document.commandDispatcher.focusedElement
+ );
+ if (this._prevFocus.get()) return;
+ } catch (ex) {}
+
+ this._prevFocus = Components.utils.getWeakReference(
+ document.activeElement
+ );
+ });
+
+ this.addEventListener("popupshown", event => {
+ // Fire event for accessibility APIs
+ var alertEvent = document.createEvent("Events");
+ alertEvent.initEvent("AlertActive", true, true);
+ this.dispatchEvent(alertEvent);
+ });
+
+ this.addEventListener("popuphiding", event => {
+ try {
+ this._currentFocus = document.commandDispatcher.focusedElement;
+ } catch (e) {
+ this._currentFocus = document.activeElement;
+ }
+ });
+
+ this.addEventListener("popuphidden", event => {
+ function doFocus() {
+ // Focus was set on an element inside this panel,
+ // so we need to move it back to where it was previously
+ try {
+ let fm = Components.classes[
+ "@mozilla.org/focus-manager;1"
+ ].getService(Components.interfaces.nsIFocusManager);
+ fm.setFocus(prevFocus, fm.FLAG_NOSCROLL);
+ } catch (e) {
+ prevFocus.focus();
+ }
+ }
+ var currentFocus = this._currentFocus;
+ var prevFocus = this._prevFocus ? this._prevFocus.get() : null;
+ this._currentFocus = null;
+ this._prevFocus = null;
+
+ // Avoid changing focus if focus changed while we hide the popup
+ // (This can happen e.g. if the popup is hiding as a result of a
+ // click/keypress that focused something)
+ let nowFocus;
+ try {
+ nowFocus = document.commandDispatcher.focusedElement;
+ } catch (e) {
+ nowFocus = document.activeElement;
+ }
+ if (nowFocus && nowFocus != currentFocus) return;
+
+ if (prevFocus && this.getAttribute("norestorefocus") != "true") {
+ // Try to restore focus
+ try {
+ if (document.commandDispatcher.focusedWindow != window) return; // Focus has already been set to a window outside of this panel
+ } catch (ex) {}
+
+ if (!currentFocus) {
+ doFocus();
+ return;
+ }
+ while (currentFocus) {
+ if (currentFocus == this) {
+ doFocus();
+ return;
+ }
+ currentFocus = currentFocus.parentNode;
+ }
+ }
+ });
+
+ // Special case for other bindings like arrowpanel, because platform has code
+ // looking for the 'panel' tag but custom elements don't let us register an element
+ // based on an attribute value. So we need to fold in the arrow functionality in that case.
+ if (this.getAttribute("type") === "arrow") {
+ dump(`Got an arrow popup ${this.id}\n`);
+ ArrowPanelConnectedCallback.call(this);
+ }
+ }
+ disconnectedCallback() {}
+}
+
+customElements.define("panel", FirefoxPanel);
+
+
+function ArrowPanelConnectedCallback() {
+ console.log(this, "connected");
+
+ // XXX: This simulates the <children> tag inside the panel-arrowcontent
+ let frag = document.createElement("box");
+ frag.innerHTML = `<vbox anonid="container" class="panel-arrowcontainer" flex="1" inherits="side,panelopen">
+<box anonid="arrowbox" class="panel-arrowbox">
+<image anonid="arrow" class="panel-arrow" inherits="side">
+</image>
+</box>
+<box class="panel-arrowcontent" inherits="side,align,dir,orient,pack" flex="1">
+</box>
+</vbox>`
+
+ let nodes = [...this.childNodes];
+ for (var i = 0; i < nodes.length; i++) {
+ dump("Adding " + i + " " + nodes[i].localName + "\n");
+ frag.querySelector(".panel-arrowcontent").appendChild(nodes[i]);
+ }
+
+ this.innerHTML = '';
+
+ nodes = [...frag.childNodes];
+ for (var i = 0; i < nodes.length; i++) {
+ this.appendChild(nodes[i]);
+ }
+
+ let comment = document.createComment("Creating firefox-arrowpanel");
+ this.prepend(comment);
+
+ Object.defineProperty(this, "_fadeTimer", {
+ configurable: true,
+ enumerable: true,
+ get() {
+ delete this._fadeTimer;
+ return (this._fadeTimer = null);
+ },
+ set(val) {
+ delete this._fadeTimer;
+ return (this._fadeTimer = val);
+ }
+ });
+
+ this.addEventListener("popupshowing", event => {
+ dump("popupshowing\n");
+ var arrow = this.querySelector("[anonid=arrow]");
+ arrow.hidden = this.anchorNode == null;
+ this.querySelector("[anonid=arrowbox]").style.removeProperty("transform");
+
+ this.adjustArrowPosition();
+
+ if (this.getAttribute("animate") != "false") {
+ this.setAttribute("animate", "open");
+ // the animating attribute prevents user interaction during transition
+ // it is removed when popupshown fires
+ this.setAttribute("animating", "true");
+ }
+
+ // set fading
+ var fade = this.getAttribute("fade");
+ var fadeDelay = 0;
+ if (fade == "fast") {
+ fadeDelay = 1;
+ } else if (fade == "slow") {
+ fadeDelay = 4000;
+ } else {
+ return;
+ }
+
+ this._fadeTimer = setTimeout(() => this.hidePopup(true), fadeDelay, this);
+ });
+
+ this.addEventListener("popuphiding", event => {
+ dump("popuphiding\n");
+ let animate = this.getAttribute("animate") != "false";
+
+ if (this._fadeTimer) {
+ clearTimeout(this._fadeTimer);
+ if (animate) {
+ this.setAttribute("animate", "fade");
+ }
+ } else if (animate) {
+ this.setAttribute("animate", "cancel");
+ }
+ });
+
+ this.addEventListener("popupshown", event => {
+ dump("popupshown\n");
+ this.removeAttribute("animating");
+ this.setAttribute("panelopen", "true");
+ });
+
+ this.addEventListener("popuphidden", event => {
+ dump("popuphidden\n");
+ this.removeAttribute("panelopen");
+ if (this.getAttribute("animate") != "false") {
+ this.removeAttribute("animate");
+ }
+ });
+
+ this.addEventListener("popuppositioned", event => {
+ dump("popuppositioned\n");
+ this.adjustArrowPosition();
+ });
+
+ this.sizeTo = (aWidth, aHeight) => {
+ this.popupBoxObject.sizeTo(aWidth, aHeight);
+ if (this.state == "open") {
+ this.adjustArrowPosition();
+ }
+ }
+
+ this.moveToAnchor = (aAnchorElement, aPosition, aX, aY, aAttributesOverride) => {
+ this.popupBoxObject.moveToAnchor(
+ aAnchorElement,
+ aPosition,
+ aX,
+ aY,
+ aAttributesOverride
+ );
+ }
+
+
+ this.adjustArrowPosition = () => {
+ var anchor = this.anchorNode;
+ if (!anchor) {
+ return;
+ }
+
+ var container = this.querySelector("[anonid=container]");
+ var arrowbox = this.querySelector("[anonid=arrowbox]");
+
+ var position = this.alignmentPosition;
+ var offset = this.alignmentOffset;
+
+ this.setAttribute("arrowposition", position);
+
+ if (position.indexOf("start_") == 0 || position.indexOf("end_") == 0) {
+ container.orient = "horizontal";
+ arrowbox.orient = "vertical";
+ if (position.indexOf("_after") > 0) {
+ arrowbox.pack = "end";
+ } else {
+ arrowbox.pack = "start";
+ }
+ arrowbox.style.transform = "translate(0, " + -offset + "px)";
+
+ // The assigned side stays the same regardless of direction.
+ var isRTL = window.getComputedStyle(this).direction == "rtl";
+
+ if (position.indexOf("start_") == 0) {
+ container.dir = "reverse";
+ this.setAttribute("side", isRTL ? "left" : "right");
+ } else {
+ container.dir = "";
+ this.setAttribute("side", isRTL ? "right" : "left");
+ }
+ } else if (
+ position.indexOf("before_") == 0 ||
+ position.indexOf("after_") == 0
+ ) {
+ container.orient = "";
+ arrowbox.orient = "";
+ if (position.indexOf("_end") > 0) {
+ arrowbox.pack = "end";
+ } else {
+ arrowbox.pack = "start";
+ }
+ arrowbox.style.transform = "translate(" + -offset + "px, 0)";
+
+ if (position.indexOf("before_") == 0) {
+ container.dir = "reverse";
+ this.setAttribute("side", "bottom");
+ } else {
+ container.dir = "";
+ this.setAttribute("side", "top");
+ }
+ }
+ }
+}