summaryrefslogtreecommitdiffstats
path: root/browser/components/places/content/places-menupopup.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/places/content/places-menupopup.js')
-rw-r--r--browser/components/places/content/places-menupopup.js771
1 files changed, 771 insertions, 0 deletions
diff --git a/browser/components/places/content/places-menupopup.js b/browser/components/places/content/places-menupopup.js
new file mode 100644
index 0000000000..8915384e3a
--- /dev/null
+++ b/browser/components/places/content/places-menupopup.js
@@ -0,0 +1,771 @@
+/* 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";
+
+/* eslint-env mozilla/browser-window */
+/* import-globals-from controller.js */
+
+// This is loaded into all XUL windows. Wrap in a block to prevent
+// leaking to window scope.
+{
+ const { AppConstants } = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+ );
+
+ class MozPlacesPopup extends MozElements.MozMenuPopup {
+ constructor() {
+ super();
+
+ const event_names = [
+ "DOMMenuItemActive",
+ "DOMMenuItemInactive",
+ "dragstart",
+ "drop",
+ "dragover",
+ "dragexit",
+ "dragend",
+ ];
+ for (let event_name of event_names) {
+ this.addEventListener(event_name, ev => this[`on_${event_name}`](ev));
+ }
+ }
+
+ get markup() {
+ return `
+ <html:link rel="stylesheet" href="chrome://global/skin/global.css" />
+ <hbox flex="1" part="innerbox">
+ <vbox part="drop-indicator-bar" hidden="true">
+ <image part="drop-indicator"/>
+ </vbox>
+ <arrowscrollbox class="menupopup-arrowscrollbox" flex="1" orient="vertical"
+ exportparts="scrollbox: arrowscrollbox-scrollbox"
+ smoothscroll="false" part="arrowscrollbox">
+ <html:slot></html:slot>
+ </arrowscrollbox>
+ </hbox>
+ `;
+ }
+
+ connectedCallback() {
+ if (this.delayConnectedCallback()) {
+ return;
+ }
+
+ /**
+ * Sub-menus should be opened when the mouse drags over them, and closed
+ * when the mouse drags off. The overFolder object manages opening and
+ * closing of folders when the mouse hovers.
+ */
+ this._overFolder = {
+ _self: this,
+ _folder: {
+ elt: null,
+ openTimer: null,
+ hoverTime: 350,
+ closeTimer: null,
+ },
+ _closeMenuTimer: null,
+
+ get elt() {
+ return this._folder.elt;
+ },
+ set elt(val) {
+ this._folder.elt = val;
+ },
+
+ get openTimer() {
+ return this._folder.openTimer;
+ },
+ set openTimer(val) {
+ this._folder.openTimer = val;
+ },
+
+ get hoverTime() {
+ return this._folder.hoverTime;
+ },
+ set hoverTime(val) {
+ this._folder.hoverTime = val;
+ },
+
+ get closeTimer() {
+ return this._folder.closeTimer;
+ },
+ set closeTimer(val) {
+ this._folder.closeTimer = val;
+ },
+
+ get closeMenuTimer() {
+ return this._closeMenuTimer;
+ },
+ set closeMenuTimer(val) {
+ this._closeMenuTimer = val;
+ },
+
+ setTimer: function OF__setTimer(aTime) {
+ var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT);
+ return timer;
+ },
+
+ notify: function OF__notify(aTimer) {
+ // Function to process all timer notifications.
+
+ if (aTimer == this._folder.openTimer) {
+ // Timer to open a submenu that's being dragged over.
+ this._folder.elt.lastElementChild.setAttribute(
+ "autoopened",
+ "true"
+ );
+ this._folder.elt.lastElementChild.openPopup();
+ this._folder.openTimer = null;
+ } else if (aTimer == this._folder.closeTimer) {
+ // Timer to close a submenu that's been dragged off of.
+ // Only close the submenu if the mouse isn't being dragged over any
+ // of its child menus.
+ var draggingOverChild = PlacesControllerDragHelper.draggingOverChildNode(
+ this._folder.elt
+ );
+ if (draggingOverChild) {
+ this._folder.elt = null;
+ }
+ this.clear();
+
+ // Close any parent folders which aren't being dragged over.
+ // (This is necessary because of the above code that keeps a folder
+ // open while its children are being dragged over.)
+ if (!draggingOverChild) {
+ this.closeParentMenus();
+ }
+ } else if (aTimer == this.closeMenuTimer) {
+ // Timer to close this menu after the drag exit.
+ var popup = this._self;
+ // if we are no more dragging we can leave the menu open to allow
+ // for better D&D bookmark organization
+ if (
+ PlacesControllerDragHelper.getSession() &&
+ !PlacesControllerDragHelper.draggingOverChildNode(
+ popup.parentNode
+ )
+ ) {
+ popup.hidePopup();
+ // Close any parent menus that aren't being dragged over;
+ // otherwise they'll stay open because they couldn't close
+ // while this menu was being dragged over.
+ this.closeParentMenus();
+ }
+ this._closeMenuTimer = null;
+ }
+ },
+
+ // Helper function to close all parent menus of this menu,
+ // as long as none of the parent's children are currently being
+ // dragged over.
+ closeParentMenus: function OF__closeParentMenus() {
+ var popup = this._self;
+ var parent = popup.parentNode;
+ while (parent) {
+ if (parent.localName == "menupopup" && parent._placesNode) {
+ if (
+ PlacesControllerDragHelper.draggingOverChildNode(
+ parent.parentNode
+ )
+ ) {
+ break;
+ }
+ parent.hidePopup();
+ }
+ parent = parent.parentNode;
+ }
+ },
+
+ // The mouse is no longer dragging over the stored menubutton.
+ // Close the menubutton, clear out drag styles, and clear all
+ // timers for opening/closing it.
+ clear: function OF__clear() {
+ if (this._folder.elt && this._folder.elt.lastElementChild) {
+ if (!this._folder.elt.lastElementChild.hasAttribute("dragover")) {
+ this._folder.elt.lastElementChild.hidePopup();
+ }
+ // remove menuactive style
+ this._folder.elt.removeAttribute("_moz-menuactive");
+ this._folder.elt = null;
+ }
+ if (this._folder.openTimer) {
+ this._folder.openTimer.cancel();
+ this._folder.openTimer = null;
+ }
+ if (this._folder.closeTimer) {
+ this._folder.closeTimer.cancel();
+ this._folder.closeTimer = null;
+ }
+ },
+ };
+ }
+
+ get _indicatorBar() {
+ if (!this.__indicatorBar) {
+ this.__indicatorBar = this.shadowRoot.querySelector(
+ "[part=drop-indicator-bar]"
+ );
+ }
+ return this.__indicatorBar;
+ }
+
+ /**
+ * This is the view that manages the popup.
+ */
+ get _rootView() {
+ if (!this.__rootView) {
+ this.__rootView = PlacesUIUtils.getViewForNode(this);
+ }
+ return this.__rootView;
+ }
+
+ /**
+ * Check if we should hide the drop indicator for the target
+ */
+ _hideDropIndicator(aEvent) {
+ let target = aEvent.target;
+
+ // Don't draw the drop indicator outside of markers or if current
+ // node is not a Places node.
+ let betweenMarkers =
+ this._startMarker.compareDocumentPosition(target) &
+ Node.DOCUMENT_POSITION_FOLLOWING &&
+ this._endMarker.compareDocumentPosition(target) &
+ Node.DOCUMENT_POSITION_PRECEDING;
+
+ // Hide the dropmarker if current node is not a Places node.
+ return !(target && target._placesNode && betweenMarkers);
+ }
+
+ /**
+ * This function returns information about where to drop when
+ * dragging over this popup insertion point
+ */
+ _getDropPoint(aEvent) {
+ // Can't drop if the menu isn't a folder
+ let resultNode = this._placesNode;
+
+ if (
+ !PlacesUtils.nodeIsFolder(resultNode) ||
+ this._rootView.controller.disallowInsertion(resultNode)
+ ) {
+ return null;
+ }
+
+ var dropPoint = { ip: null, folderElt: null };
+
+ // The element we are dragging over
+ let elt = aEvent.target;
+ if (elt.localName == "menupopup") {
+ elt = elt.parentNode;
+ }
+
+ let eventY = aEvent.clientY;
+ let { y: eltY, height: eltHeight } = elt.getBoundingClientRect();
+
+ if (!elt._placesNode) {
+ // If we are dragging over a non places node drop at the end.
+ dropPoint.ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(resultNode),
+ });
+ // We can set folderElt if we are dropping over a static menu that
+ // has an internal placespopup.
+ let isMenu =
+ elt.localName == "menu" ||
+ (elt.localName == "toolbarbutton" &&
+ elt.getAttribute("type") == "menu");
+ if (
+ isMenu &&
+ elt.lastElementChild &&
+ elt.lastElementChild.hasAttribute("placespopup")
+ ) {
+ dropPoint.folderElt = elt;
+ }
+ return dropPoint;
+ }
+
+ let tagName = PlacesUtils.nodeIsTagQuery(elt._placesNode)
+ ? elt._placesNode.title
+ : null;
+ if (
+ (PlacesUtils.nodeIsFolder(elt._placesNode) &&
+ !PlacesUIUtils.isFolderReadOnly(elt._placesNode)) ||
+ PlacesUtils.nodeIsTagQuery(elt._placesNode)
+ ) {
+ // This is a folder or a tag container.
+ if (eventY - eltY < eltHeight * 0.2) {
+ // If mouse is in the top part of the element, drop above folder.
+ dropPoint.ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(resultNode),
+ orientation: Ci.nsITreeView.DROP_BEFORE,
+ tagName,
+ dropNearNode: elt._placesNode,
+ });
+ return dropPoint;
+ } else if (eventY - eltY < eltHeight * 0.8) {
+ // If mouse is in the middle of the element, drop inside folder.
+ dropPoint.ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(elt._placesNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(elt._placesNode),
+ tagName,
+ });
+ dropPoint.folderElt = elt;
+ return dropPoint;
+ }
+ } else if (eventY - eltY <= eltHeight / 2) {
+ // This is a non-folder node or a readonly folder.
+ // If the mouse is above the middle, drop above this item.
+ dropPoint.ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(resultNode),
+ orientation: Ci.nsITreeView.DROP_BEFORE,
+ tagName,
+ dropNearNode: elt._placesNode,
+ });
+ return dropPoint;
+ }
+
+ // Drop below the item.
+ dropPoint.ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(resultNode),
+ orientation: Ci.nsITreeView.DROP_AFTER,
+ tagName,
+ dropNearNode: elt._placesNode,
+ });
+ return dropPoint;
+ }
+
+ _cleanupDragDetails() {
+ // Called on dragend and drop.
+ PlacesControllerDragHelper.currentDropTarget = null;
+ this._rootView._draggedElt = null;
+ this.removeAttribute("dragover");
+ this.removeAttribute("dragstart");
+ this._indicatorBar.hidden = true;
+ }
+
+ on_DOMMenuItemActive(event) {
+ let elt = event.target;
+ if (elt.parentNode != this) {
+ return;
+ }
+
+ if (AppConstants.platform === "macosx") {
+ // XXX: The following check is a temporary hack until bug 420033 is
+ // resolved.
+ let parentElt = elt.parent;
+ while (parentElt) {
+ if (
+ parentElt.id == "bookmarksMenuPopup" ||
+ parentElt.id == "goPopup"
+ ) {
+ return;
+ }
+ parentElt = parentElt.parentNode;
+ }
+ }
+
+ if (window.XULBrowserWindow) {
+ let placesNode = elt._placesNode;
+
+ var linkURI;
+ if (placesNode && PlacesUtils.nodeIsURI(placesNode)) {
+ linkURI = placesNode.uri;
+ } else if (elt.hasAttribute("targetURI")) {
+ linkURI = elt.getAttribute("targetURI");
+ }
+
+ if (linkURI) {
+ window.XULBrowserWindow.setOverLink(linkURI);
+ }
+ }
+ }
+
+ on_DOMMenuItemInactive(event) {
+ let elt = event.target;
+ if (elt.parentNode != this) {
+ return;
+ }
+
+ if (window.XULBrowserWindow) {
+ window.XULBrowserWindow.setOverLink("");
+ }
+ }
+
+ on_dragstart(event) {
+ let elt = event.target;
+ if (!elt._placesNode) {
+ return;
+ }
+
+ let draggedElt = elt._placesNode;
+
+ // Force a copy action if parent node is a query or we are dragging a
+ // not-removable node.
+ if (!this._rootView.controller.canMoveNode(draggedElt)) {
+ event.dataTransfer.effectAllowed = "copyLink";
+ }
+
+ // Activate the view and cache the dragged element.
+ this._rootView._draggedElt = draggedElt;
+ this._rootView.controller.setDataTransfer(event);
+ this.setAttribute("dragstart", "true");
+ event.stopPropagation();
+ }
+
+ on_drop(event) {
+ PlacesControllerDragHelper.currentDropTarget = event.target;
+
+ let dropPoint = this._getDropPoint(event);
+ if (dropPoint && dropPoint.ip) {
+ PlacesControllerDragHelper.onDrop(
+ dropPoint.ip,
+ event.dataTransfer
+ ).catch(Cu.reportError);
+ event.preventDefault();
+ }
+
+ this._cleanupDragDetails();
+ event.stopPropagation();
+ }
+
+ on_dragover(event) {
+ PlacesControllerDragHelper.currentDropTarget = event.target;
+ let dt = event.dataTransfer;
+
+ let dropPoint = this._getDropPoint(event);
+ if (
+ !dropPoint ||
+ !dropPoint.ip ||
+ !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)
+ ) {
+ this._indicatorBar.hidden = true;
+ event.stopPropagation();
+ return;
+ }
+
+ // Mark this popup as being dragged over.
+ this.setAttribute("dragover", "true");
+
+ if (dropPoint.folderElt) {
+ // We are dragging over a folder.
+ // _overFolder should take the care of opening it on a timer.
+ if (
+ this._overFolder.elt &&
+ this._overFolder.elt != dropPoint.folderElt
+ ) {
+ // We are dragging over a new folder, let's clear old values
+ this._overFolder.clear();
+ }
+ if (!this._overFolder.elt) {
+ this._overFolder.elt = dropPoint.folderElt;
+ // Create the timer to open this folder.
+ this._overFolder.openTimer = this._overFolder.setTimer(
+ this._overFolder.hoverTime
+ );
+ }
+ // Since we are dropping into a folder set the corresponding style.
+ dropPoint.folderElt.setAttribute("_moz-menuactive", true);
+ } else {
+ // We are not dragging over a folder.
+ // Clear out old _overFolder information.
+ this._overFolder.clear();
+ }
+
+ // Autoscroll the popup strip if we drag over the scroll buttons.
+ let scrollDir = 0;
+ if (event.originalTarget == this.scrollBox._scrollButtonUp) {
+ scrollDir = -1;
+ } else if (event.originalTarget == this.scrollBox._scrollButtonDown) {
+ scrollDir = 1;
+ }
+ if (scrollDir != 0) {
+ this.scrollBox.scrollByIndex(scrollDir, true);
+ }
+
+ // Check if we should hide the drop indicator for this target.
+ if (dropPoint.folderElt || this._hideDropIndicator(event)) {
+ this._indicatorBar.hidden = true;
+ event.preventDefault();
+ event.stopPropagation();
+ return;
+ }
+
+ // We should display the drop indicator relative to the arrowscrollbox.
+ let scrollRect = this.scrollBox.getBoundingClientRect();
+ let newMarginTop = 0;
+ if (scrollDir == 0) {
+ let elt = this.firstElementChild;
+ while (
+ elt &&
+ event.screenY > elt.screenY + elt.getBoundingClientRect().height / 2
+ ) {
+ elt = elt.nextElementSibling;
+ }
+ newMarginTop = elt
+ ? elt.screenY - this.scrollBox.screenY
+ : scrollRect.height;
+ } else if (scrollDir == 1) {
+ newMarginTop = scrollRect.height;
+ }
+
+ // Set the new marginTop based on arrowscrollbox.
+ newMarginTop += scrollRect.y - this.scrollBox.getBoundingClientRect().y;
+ this._indicatorBar.firstElementChild.style.marginTop =
+ newMarginTop + "px";
+ this._indicatorBar.hidden = false;
+
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ on_dragexit(event) {
+ PlacesControllerDragHelper.currentDropTarget = null;
+ this.removeAttribute("dragover");
+
+ // If we have not moved to a valid new target clear the drop indicator
+ // this happens when moving out of the popup.
+ let target = event.relatedTarget;
+ if (!target || !this.contains(target)) {
+ this._indicatorBar.hidden = true;
+ }
+
+ // Close any folder being hovered over
+ if (this._overFolder.elt) {
+ this._overFolder.closeTimer = this._overFolder.setTimer(
+ this._overFolder.hoverTime
+ );
+ }
+
+ // The autoopened attribute is set when this folder was automatically
+ // opened after the user dragged over it. If this attribute is set,
+ // auto-close the folder on drag exit.
+ // We should also try to close this popup if the drag has started
+ // from here, the timer will check if we are dragging over a child.
+ if (this.hasAttribute("autoopened") || this.hasAttribute("dragstart")) {
+ this._overFolder.closeMenuTimer = this._overFolder.setTimer(
+ this._overFolder.hoverTime
+ );
+ }
+
+ event.stopPropagation();
+ }
+
+ on_dragend(event) {
+ this._cleanupDragDetails();
+ }
+ }
+
+ customElements.define("places-popup", MozPlacesPopup, {
+ extends: "menupopup",
+ });
+
+ class MozPlacesPopupArrow extends MozPlacesPopup {
+ constructor() {
+ super();
+
+ const event_names = [
+ "popupshowing",
+ "popuppositioned",
+ "popupshown",
+ "transitionend",
+ "popuphiding",
+ "popuphidden",
+ ];
+ for (let event_name of event_names) {
+ this.addEventListener(event_name, ev => this[`on_${event_name}`](ev));
+ }
+ }
+
+ static get inheritedAttributes() {
+ return {
+ ".panel-arrowcontent": "align,dir,orient,pack",
+ };
+ }
+
+ get markup() {
+ return `
+ <html:link rel="stylesheet" href="chrome://global/skin/global.css"/>
+ <vbox class="panel-arrowcontainer" flex="1">
+ <box class="panel-arrowbox" part="arrowbox">
+ <image class="panel-arrow" part="arrow"/>
+ </box>
+ <box class="panel-arrowcontent" part="arrowcontent" flex="1">
+ <vbox part="drop-indicator-bar" hidden="true">
+ <image part="drop-indicator"/>
+ </vbox>
+ <arrowscrollbox class="menupopup-arrowscrollbox" flex="1"
+ orient="vertical" smoothscroll="false"
+ part="arrowscrollbox">
+ <html:slot/>
+ </arrowscrollbox>
+ </box>
+ </vbox>
+ `;
+ }
+
+ connectedCallback() {
+ if (this.delayConnectedCallback()) {
+ return;
+ }
+
+ super.connectedCallback();
+ this.initializeAttributeInheritance();
+
+ this.setAttribute("flip", "both");
+ this.setAttribute("side", "top");
+ this.setAttribute("position", "bottomcenter topright");
+ this.style.pointerEvents = "none";
+ }
+
+ get container() {
+ return this.shadowRoot.querySelector(".panel-arrowcontainer");
+ }
+ get arrowbox() {
+ return this.shadowRoot.querySelector(".panel-arrowbox");
+ }
+ get arrow() {
+ return this.shadowRoot.querySelector(".panel-arrow");
+ }
+
+ adjustArrowPosition(event) {
+ let arrow = this.arrow;
+
+ let anchor = this.anchorNode;
+ if (!anchor) {
+ arrow.hidden = true;
+ return;
+ }
+
+ let container = this.container;
+ let arrowbox = this.arrowbox;
+
+ var position = event.alignmentPosition;
+ var offset = event.alignmentOffset;
+
+ this.setAttribute("arrowposition", position);
+
+ // if this panel has a "sliding" arrow, we may have previously set margins...
+ arrowbox.style.removeProperty("transform");
+ if (position.indexOf("start_") == 0 || position.indexOf("end_") == 0) {
+ container.setAttribute("orient", "horizontal");
+ arrowbox.setAttribute("orient", "vertical");
+ if (position.indexOf("_after") > 0) {
+ arrowbox.setAttribute("pack", "end");
+ } else {
+ arrowbox.setAttribute("pack", "start");
+ }
+ arrowbox.style.transform = "translate(0, " + -offset + "px)";
+
+ // The assigned side stays the same regardless of direction.
+ let isRTL = this.matches(":-moz-locale-dir(rtl)");
+
+ if (position.indexOf("start_") == 0) {
+ container.style.MozBoxDirection = "reverse";
+ this.setAttribute("side", isRTL ? "left" : "right");
+ } else {
+ container.style.removeProperty("-moz-box-direction");
+ this.setAttribute("side", isRTL ? "right" : "left");
+ }
+ } else if (
+ position.indexOf("before_") == 0 ||
+ position.indexOf("after_") == 0
+ ) {
+ container.removeAttribute("orient");
+ arrowbox.removeAttribute("orient");
+ if (position.indexOf("_end") > 0) {
+ arrowbox.setAttribute("pack", "end");
+ } else {
+ arrowbox.setAttribute("pack", "start");
+ }
+ arrowbox.style.transform = "translate(" + -offset + "px, 0)";
+
+ if (position.indexOf("before_") == 0) {
+ container.style.MozBoxDirection = "reverse";
+ this.setAttribute("side", "bottom");
+ } else {
+ container.style.removeProperty("-moz-box-direction");
+ this.setAttribute("side", "top");
+ }
+ }
+
+ arrow.hidden = false;
+ }
+
+ on_popupshowing(event) {
+ if (event.target == this) {
+ this.setAttribute("animate", "open");
+ }
+ }
+
+ on_popuppositioned(event) {
+ if (event.target == this) {
+ this.adjustArrowPosition(event);
+ }
+ }
+
+ on_popupshown(event) {
+ if (event.target != this) {
+ return;
+ }
+
+ this.setAttribute("panelopen", "true");
+ let disablePointerEvents;
+ if (!this.hasAttribute("disablepointereventsfortransition")) {
+ let cs = getComputedStyle(this.container);
+ let transitionProp = cs.transitionProperty;
+ let transitionTime = parseFloat(cs.transitionDuration);
+ disablePointerEvents =
+ (transitionProp.includes("transform") || transitionProp == "all") &&
+ transitionTime > 0;
+ this.setAttribute(
+ "disablepointereventsfortransition",
+ disablePointerEvents
+ );
+ } else {
+ disablePointerEvents =
+ this.getAttribute("disablepointereventsfortransition") == "true";
+ }
+ if (!disablePointerEvents) {
+ this.style.removeProperty("pointer-events");
+ }
+ }
+
+ on_transitionend(event) {
+ if (
+ event.originalTarget.classList.contains("panel-arrowcontainer") &&
+ (event.propertyName == "transform" ||
+ event.propertyName == "-moz-window-transform")
+ ) {
+ this.style.removeProperty("pointer-events");
+ }
+ }
+
+ on_popuphiding(event) {
+ if (event.target == this) {
+ this.setAttribute("animate", "cancel");
+ }
+ }
+
+ on_popuphidden(event) {
+ if (event.target == this) {
+ this.removeAttribute("panelopen");
+ if (this.getAttribute("disablepointereventsfortransition") == "true") {
+ this.style.pointerEvents = "none";
+ }
+ this.removeAttribute("animate");
+ }
+ }
+ }
+
+ customElements.define("places-popup-arrow", MozPlacesPopupArrow, {
+ extends: "menupopup",
+ });
+}