DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (d1ac49b9eb3e)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
/* 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";

var EXPORTED_SYMBOLS = ["UrlbarContextualTip"];

// These are global listeners; all instances of the contextual tip will
// have click listeners executed when their button or link is clicked.
let clickListeners = new Map([["button", new Set()], ["link", new Set()]]);

/**
 * Calls all the click listeners for the specified element.
 *
 * @param {string} element Either "button" or "link"
 *   Corresponds to the button or link on the contextual tip.
 * @param {object} window The window
 */
function callClickListeners(element, window) {
  for (let clickListener of clickListeners.get(element)) {
    clickListener(window);
  }
}

/**
 * Consumers of this class can create, set, remove, and hide a contextual tip.
 */
class UrlbarContextualTip {
  /**
   * Creates the contextual tip and sets it in the urlbar's view.
   *
   * @param {object} view The urlbar's view
   */
  constructor(view) {
    this.document = view.document;
    this.view = view;
    this._create();
  }

  /**
   * Creates an empty contextual tip in the DOM.
   */
  _create() {
    this._elements = {};

    const fragment = this.document.createDocumentFragment();

    this._elements.container = this.document.createElement("div");
    this._elements.container.id = "urlbar-contextual-tip";

    this._elements.icon = this.document.createElement("div");
    this._elements.icon.id = "urlbar-contextual-tip-icon";

    this._elements.title = this.document.createElement("div");
    this._elements.title.id = "urlbar-contextual-tip-title";

    this._elements.button = this.document.createElement("button");
    this._elements.button.id = "urlbar-contextual-tip-button";

    this._elements.link = this.document.createElement("a");
    this._elements.link.id = "urlbar-contextual-tip-link";

    this._elements.container.appendChild(this._elements.icon);
    this._elements.container.appendChild(this._elements.title);
    this._elements.container.appendChild(this._elements.button);
    this._elements.container.appendChild(this._elements.link);

    fragment.appendChild(this._elements.container);
    this.view.panel.prepend(fragment);

    this._elements.button.addEventListener("click", () => {
      callClickListeners("button", this.document.ownerGlobal);
    });

    this._elements.link.addEventListener("click", () => {
      callClickListeners("link", this.document.ownerGlobal);
    });
  }

  /**
   * Removes the contextual tip from the DOM.
   */
  remove() {
    this._elements.container.remove();
    this._elements = null;
  }

  /**
   * Sets the icon, title, button's title, and link's title
   * for the contextual tip.
   *
   * @param {object} details
   * @param {string} details.title
   *   Main title displayed by the contextual tip.
   * @param {string} [details.buttonTitle]
   *   Title of the button on the contextual tip.
   *   If omitted then the button will be hidden.
   * @param {string} [details.linkTitle]
   *   Title of the link on the contextual tip.
   *   If omitted then the link will be hidden.
   * @param {string} [details.iconStyle]
   *   A non-empty string of styles to add to the icon's style attribute.
   *   These styles set CSS variables to URLs of images;
   *   the CSS variables responsible for the icon's background image are
   *   the variable names containing `--webextension-contextual-tip-icon`
   *   in `browser/base/content/browser.css`.
   *   If ommited, no changes are made to the icon.
   */
  set(details) {
    let { iconStyle, title, buttonTitle, linkTitle } = details;

    if (iconStyle) {
      this._elements.icon.setAttribute("style", iconStyle);
    }

    this._elements.title.textContent = title;

    this._elements.button.textContent = buttonTitle;
    this._elements.button.classList.toggle("hidden", !buttonTitle);

    this._elements.link.textContent = linkTitle;
    this._elements.link.classList.toggle("hidden", !linkTitle);

    // If the contextual tip is not visible, then we'll make it visible
    this._elements.container.classList.remove("hidden");
  }

  /**
   * Hides the contextual tip.
   */
  hide() {
    if (!this._elements.container.classList.contains("hidden")) {
      this._elements.container.classList.add("hidden");
    }
  }

  /**
   * Add a click listener to the specified element (either a button or link)
   * on all windows (both existing and new windows).
   *
   * @param {string} element
   *   The clickListener will be added to the specified element.
   *   Must be either "button" or "link".
   * @param {function} clickListener
   *   The click listener to add to the specified element.
   */
  static addClickListener(element, clickListener) {
    clickListeners.get(element).add(clickListener);
  }

  /**
   * Remove a click listener from the specified element (either a button or
   * link) on all windows (both existing and new windows).
   *
   * @param {string} element
   *   The clickListener will be removed from the specified element.
   *   Must be either "button" or "link".
   * @param {function} clickListener
   *   The click listener to remove from the specified element.
   */
  static removeClickListener(element, clickListener) {
    clickListeners.get(element).delete(clickListener);
  }
}