PanelMultiView

Allows a popup panel to host multiple subviews. The main view shown when the panel is opened may slide out to display a subview, which in turn may lead to other subviews in a cascade menu pattern.

The <panel> element should contain a <panelmultiview> element. Views are declared using <panelview> elements that are usually children of the main <panelmultiview> element, although they don’t need to be, as views can also be imported into the panel from other panels or popup sets.

The panel should be opened asynchronously using the openPopup static method on the PanelMultiView object. This will display the view specified using the mainViewId attribute on the contained <panelmultiview> element.

Specific subviews can slide in using the showSubView method, and backwards navigation can be done using the goBack method or through a button in the subview headers.

The process of displaying the main view or a new subview requires multiple steps to be completed, hence at any given time the <panelview> element may be in different states:

– Open or closed

All the <panelview> elements start “closed”, meaning that they are not associated to a <panelmultiview> element and can be located anywhere in the document. When the openPopup or showSubView methods are called, the relevant view becomes “open” and the <panelview> element may be moved to ensure it is a descendant of the <panelmultiview> element.

The “ViewShowing” event is fired at this point, when the view is not visible yet. The event is allowed to cancel the operation, in which case the view is closed immediately.

Closing the view does not move the node back to its original position.

– Visible or invisible

This indicates whether the view is visible in the document from a layout perspective, regardless of whether it is currently scrolled into view. In fact, all subviews are already visible before they start sliding in.

Before scrolling into view, a view may become visible but be placed in a special off-screen area of the document where layout and measurements can take place asyncronously.

When navigating forward, an open view may become invisible but stay open after sliding out of view. The last known size of these views is still taken into account for determining the overall panel size.

When navigating backwards, an open subview will first become invisible and then will be closed.

– Active or inactive

This indicates whether the view is fully scrolled into the visible area and ready to receive mouse and keyboard events. An active view is always visible, but a visible view may be inactive. For example, during a scroll transition, both views will be inactive.

When a view becomes active, the ViewShown event is fired synchronously, and the showSubView and goBack methods can be called for navigation.

For the main view of the panel, the ViewShown event is dispatched during the “popupshown” event, which means that other “popupshown” handlers may be called before the view is active. Thus, code that needs to perform further navigation automatically should either use the ViewShown event or wait for an event loop tick, like BrowserTestUtils.waitForEvent does.

– Navigating with the keyboard

An open view may keep state related to keyboard navigation, even if it is invisible. When a view is closed, keyboard navigation state is cleared.

This diagram shows how <panelview> nodes move during navigation:

In this <panelmultiview>     In other panels    Action
          ┌───┬───┬───┐        ┌───┬───┐
          │(A)│ B │ C │        │ D │ E │          Open panel
          └───┴───┴───┘        └───┴───┘
      ┌───┬───┬───┐            ┌───┬───┐
      │{A}│(C)│ B │            │ D │ E │          Show subview C
      └───┴───┴───┘            └───┴───┘
  ┌───┬───┬───┬───┐            ┌───┐
  │{A}│{C}│(D)│ B │            │ E │              Show subview D
  └───┴───┴───┴───┘            └───┘
    │ ┌───┬───┬───┬───┐        ┌───┐
    │ │{A}│(C)│ D │ B │        │ E │              Go back
    │ └───┴───┴───┴───┘        └───┘
    │   │   │
    │   │   └── Currently visible view
    │   │   │
    └───┴───┴── Open views
class AssociatedToNode()

Allows associating an object to a node lazily using a weak map.

Classes deriving from this one may be easily converted to Custom Elements, although they would lose the ability of being associated lazily.

AssociatedToNode._blockersPromise

This promise is resolved when the current set of blockers set by event handlers have all been processed.

AssociatedToNode.document

A shortcut to the document that the node belongs to.

AssociatedToNode.node

Node associated to this object.

AssociatedToNode.window

A shortcut to the window global that the node belongs to.

AssociatedToNode._getBoundsWithoutFlushing(element)

A shortcut to windowUtils.getBoundsWithoutFlushing for the window global associated with the node.

This is a pseudo-private method using an _ because we want it to be used by subclasses. Please don’t use outside of this module.

Arguments:
  • element (DOMNode) – The element to retrieve the bounds for without flushing layout.

Returns:

DOMRect – The bounding rect of the element.

AssociatedToNode.dispatchAsyncEvent(eventName)

Dispatches a custom event on this element and waits for any blocking promises registered using the “addBlocker” function on the details object. If this function is called again, the event is only dispatched after all the previously registered blockers have returned.

The event can be canceled either by resolving any blocking promise to the boolean value “false” or by calling preventDefault on the event. Rejections and exceptions will be reported and will cancel the event.

Blocking should be used sporadically because it slows down the interface. Also, non-reentrancy is not strictly guaranteed because a safety timeout of BLOCKERS_TIMEOUT_MS is implemented, after which the event will be canceled. This helps to prevent deadlocks if any of the event handlers does not resolve a blocker promise.

Note:

Since there is no use case for dispatching different asynchronous events in parallel for the same element, this function will also wait for previous blockers when the event name is different.

Arguments:
  • eventName (string) – Name of the custom event to dispatch.

Returns:

Promise.<boolean> – Resolves to true if the event was canceled by a handler, false otherwise.

AssociatedToNode.dispatchCustomEvent(eventName, detail, cancelable=false)

Dispatches a custom event on this element.

Arguments:
  • eventName (string) – Name of the event to dispatch.

  • detail (object|undefined) – Event detail object. Optional.

  • cancelable (boolean) – True if the event can be canceled.

Returns:

boolean – True if the event was canceled by an event handler, false otherwise.

static AssociatedToNode.forNode(node)

Retrieves the instance associated with the given node, constructing a new one if necessary. When the last reference to the node is released, the object instance will be garbage collected as well.

Arguments:
  • node (DOMNode) – The node to retrieve or construct the AssociatedToNode instance for.

Returns:

AssociatedToNode

class PanelMultiView()

This is associated to <panelmultiview> elements.

PanelMultiView._moveOutKids()

Move any child subviews into the element defined by “viewCacheId” to make sure they will not be removed together with the <panelmultiview> element.

This is “pseudo-private” with the underscore so that the static removePopup method can call it.

PanelMultiView.closeAllViews()

Closes all the views that are currently open.

PanelMultiView.connect()

Binds this PanelMultiView class to the underlying <panelmultiview> element. Also creates the appropriate <panelmultview> child elements, like the viewcontainer and the viewstack. Sets up popup event handlers for the panel.

PanelMultiView.disconnect()

Disconnects this PanelMultiView instance from a <panelmultiview> element. This does not remove any of the nodes created in connect, but does clean up the event listeners that were set up.

PanelMultiView.goBack()

Navigates backwards by sliding out the most recent subview.

PanelMultiView.handleEvent(aEvent)

Centralized event handler for the associated <panelmultiview>.

Arguments:
  • aEvent (Event) – The event being handled.

PanelMultiView.showSubView(viewIdOrNode, anchor)

Slides in the specified view as a subview. This returns synchronously, but may eventually log an error if showing the subview fails for some reason.

Arguments:
  • viewIdOrNode (string|DOMNode) – DOM element or string ID of the <panelview> to display.

  • anchor (DOMNode) – DOM element that triggered the subview, which will be highlighted and whose “label” attribute will be used for the title of the subview when a “title” attribute is not specified.

static PanelMultiView.ensureUnloadHandlerRegistered(window)

Ensures that when the specified window is closed all the <panelmultiview> node it contains are destroyed properly.

Arguments:
  • window (DOMWindow) – The window to add the unload handler to.

static PanelMultiView.getViewNode(doc, id)

Returns the element with the given id. For nodes that are lazily loaded and not yet in the DOM, the node should be retrieved from the view cache template.

Arguments:
  • doc (Document) – The document to retrieve the node for.

  • id (string) – The ID of the element to retrieve from the DOM (or the appMenu-viewCache).

Returns:

DOMNode|null – The found DOMNode or null if no node was found with that ID.

static PanelMultiView.hidePopup(panelNode, animate=false)

Closes the specified <panel> which contains a <panelmultiview> node.

If the panel does not contain a <panelmultiview>, it is closed directly. This allows consumers like page actions to accept different panel types.

See the non-static hidePopup method for details.

Arguments:
  • panelNode (DOMNode) – The <panel> node.

  • animate (boolean) – Whether to show a fade animation.

static PanelMultiView.openPopup(panelNode, ...args)

Tries to open the specified <panel> and displays the main view specified with the “mainViewId” attribute on the <panelmultiview> node it contains.

If the panel does not contain a <panelmultiview>, it is opened directly. This allows consumers like page actions to accept different panel types.

See the non-static openPopup method for details.

Arguments:
  • panelNode (DOMNode) – The <panel> node that is to be opened.

  • args (*) – Additional arguments to be forwarded to the openPopup method of the panel.

Returns:

Promise.<boolean, Exception>|boolean – Returns a Promise that resolves to true if the panel successfully opened, or rejects if the panel cannot open. May also return true immediately if the panel does not contain a <panelmultiview>.

static PanelMultiView.removePopup(panelNode)

Removes the specified <panel> from the document, ensuring that any <panelmultiview> node it contains is destroyed properly.

If the viewCacheId attribute is present on the <panelmultiview> element, imported subviews will be moved out again to the element it specifies, so that the panel element can be removed safely.

If the panel does not contain a <panelmultiview>, it is removed directly. This allows consumers like page actions to accept different panel types.

Arguments:
  • panelNode (DOMNode) – The <panel> to remove.

class PanelView()

This is associated to <panelview> elements.

PanelView._arrowNavigableWalker

Get a TreeWalker which finds elements navigable with up/down arrow keys.

PanelView._tabNavigableWalker

Get a TreeWalker which finds elements navigable with tab/shift+tab.

This is currently pseudo private with the underscore because AccessibilityUtils.js appears to be accessing this.

PanelView.active

Indicates whether the view is active. When this is false, consumers can wait for the ViewShown event to know when the view becomes active.

PanelView.focusWhenActive

Specifies whether the view should be focused when active. When this is true, the first navigable element in the view will be focused when the view becomes active. This should be set to true when the view is activated from the keyboard. It will be set to false once the view is active.

PanelView.headerText

Adds a header with the given title, or removes it if the title is empty.

If an element matching .panel-header is found in the PanelView, then this method will attempt to set the textContent of the first h1 > span underneath that .panel-header to value.

Otherwise, this will attempt to insert a header element and a separator beneath it, with the text of the header set to value.

If value is null, then these elements are cleared and removed.

PanelView.mainview

The “mainview” attribute is set before the panel is opened when this view is displayed as the main view, and is removed before the <panelview> is displayed as a subview. The same view element can be displayed as a main view and as a subview at different times.

PanelView.minMaxHeight

Constrains the height of this view using the “min-height” and “max-height” styles. Setting this to zero removes the constraints.

PanelView.minMaxWidth

Constrains the width of this view using the “min-width” and “max-width” styles. Setting this to zero removes the constraints.

PanelView.selectedElement

type: DOMNode|null

Element that is currently selected with the keyboard, or null if no element is selected. Since the reference is held weakly, it can become null or undefined at any time.

PanelView.visible

Determines whether the view is visible. Setting this to false also resets the “active” property.

PanelView.captureKnownSize()

Populates the “knownWidth” and “knownHeight” properties with the current dimensions of the view. These may be zero if the view is invisible.

These values are relevant during transitions and are retained for backwards navigation if the view is still open but is invisible.

PanelView.clearNavigation()

Clear all traces of keyboard navigation happening right now.

PanelView.createHeaderBackButton()

Creates and returns a panel header back toolbarbutton.

PanelView.dispatchCustomEvent(...args)

Dispatches a custom event on the PanelView, and also makes sure that the correct method is called on CustomizableWidget if applicable.

Arguments:
  • args (*) – Additional arguments to be forwarded to the dispatchCustomEvent method of AssociatedToNode.

PanelView.focusFirstNavigableElement(homeKey=false, skipBack=false)

Focuses and moves keyboard selection to the first navigable element. This is a no-op if there are no navigable elements.

Arguments:
  • homeKey (boolean) – True if this is for the home key.

  • skipBack (boolean) – True if the Back button should be skipped.

PanelView.focusLastNavigableElement(endKey=false)

Focuses and moves keyboard selection to the last navigable element. This is a no-op if there are no navigable elements.

Arguments:
  • endKey (boolean) – True if this is for the end key.

PanelView.focusSelectedElement(byKey=false)

Focus the last selected element in the view, if any.

Arguments:
  • byKey (boolean) – True if focus was moved by the user pressing a key. Needed to ensure we show focus styles in the right cases.

PanelView.isOpenIn(panelMultiView)

Indicates whether the view is open in the specified PanelMultiView object.

Arguments:
  • panelMultiView (PanelMultiView) – The PanelMultiView instance to check.

Returns:

boolean – True if the PanelView is open in the specified PanelMultiView object.

PanelView.keyNavigation(event)

Allow for navigating subview buttons using the arrow keys and the Enter key. The Up and Down keys can be used to navigate the list up and down and the Enter, Right or Left - depending on the text direction - key can be used to simulate a click on the currently selected button. The Right or Left key - depending on the text direction - can be used to navigate to the previous view, functioning as a shortcut for the view’s back button. Thus, in LTR mode:

  • The Right key functions the same as the Enter key, simulating a click

  • The Left key triggers a navigation back to the previous view.

Key navigation is only enabled while the view is active, meaning that this method will return early if it is invoked during a sliding transition.

Arguments:
  • event (KeyEvent) – The KeyEvent to potentially perform navigation for.

PanelView.moveSelection(isDown, arrowKey=false)

Based on going up or down, select the previous or next focusable element.

Arguments:
  • isDown (boolean) – True if the selection is going down, false if going up.

  • arrowKey (boolean) – True if this is for the up/down arrow keys.

Returns:

DOMNode – the element we selected.