mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			671 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			671 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* 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/. */
 | 
						|
 | 
						|
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
 | 
						|
 | 
						|
const lazy = {};
 | 
						|
 | 
						|
ChromeUtils.defineESModuleGetters(lazy, {
 | 
						|
  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
 | 
						|
  UrlbarTokenizer: "resource:///modules/UrlbarTokenizer.sys.mjs",
 | 
						|
});
 | 
						|
 | 
						|
XPCOMUtils.defineLazyServiceGetter(
 | 
						|
  lazy,
 | 
						|
  "touchBarUpdater",
 | 
						|
  "@mozilla.org/widget/touchbarupdater;1",
 | 
						|
  "nsITouchBarUpdater"
 | 
						|
);
 | 
						|
 | 
						|
// For accessing TouchBarHelper methods from static contexts in this file.
 | 
						|
XPCOMUtils.defineLazyServiceGetter(
 | 
						|
  lazy,
 | 
						|
  "touchBarHelper",
 | 
						|
  "@mozilla.org/widget/touchbarhelper;1",
 | 
						|
  "nsITouchBarHelper"
 | 
						|
);
 | 
						|
 | 
						|
/**
 | 
						|
 * Executes a XUL command on the top window. Called by the callbacks in each
 | 
						|
 * TouchBarInput.
 | 
						|
 *
 | 
						|
 * @param {string} commandName
 | 
						|
 *        A XUL command.
 | 
						|
 */
 | 
						|
function execCommand(commandName) {
 | 
						|
  if (!TouchBarHelper.window) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  let command = TouchBarHelper.window.document.getElementById(commandName);
 | 
						|
  if (command) {
 | 
						|
    command.doCommand();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Static helper function to convert a hexadecimal string to its integer
 | 
						|
 * value. Used to convert colours to a format accepted by Apple's NSColor code.
 | 
						|
 *
 | 
						|
 * @param {string} hexString
 | 
						|
 *        A hexadecimal string, optionally beginning with '#'.
 | 
						|
 */
 | 
						|
function hexToInt(hexString) {
 | 
						|
  if (!hexString) {
 | 
						|
    return null;
 | 
						|
  }
 | 
						|
  if (hexString.charAt(0) == "#") {
 | 
						|
    hexString = hexString.slice(1);
 | 
						|
  }
 | 
						|
  let val = parseInt(hexString, 16);
 | 
						|
  return isNaN(val) ? null : val;
 | 
						|
}
 | 
						|
 | 
						|
const kInputTypes = {
 | 
						|
  BUTTON: "button",
 | 
						|
  LABEL: "label",
 | 
						|
  MAIN_BUTTON: "mainButton",
 | 
						|
  POPOVER: "popover",
 | 
						|
  SCROLLVIEW: "scrollView",
 | 
						|
  SCRUBBER: "scrubber",
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * An object containing all implemented TouchBarInput objects.
 | 
						|
 */
 | 
						|
var gBuiltInInputs = {
 | 
						|
  Back: {
 | 
						|
    title: "back",
 | 
						|
    image: "chrome://browser/skin/back.svg",
 | 
						|
    type: kInputTypes.BUTTON,
 | 
						|
    callback: () => {
 | 
						|
      lazy.touchBarHelper.unfocusUrlbar();
 | 
						|
      execCommand("Browser:Back");
 | 
						|
    },
 | 
						|
  },
 | 
						|
  Forward: {
 | 
						|
    title: "forward",
 | 
						|
    image: "chrome://browser/skin/forward.svg",
 | 
						|
    type: kInputTypes.BUTTON,
 | 
						|
    callback: () => {
 | 
						|
      lazy.touchBarHelper.unfocusUrlbar();
 | 
						|
      execCommand("Browser:Forward");
 | 
						|
    },
 | 
						|
  },
 | 
						|
  Reload: {
 | 
						|
    title: "reload",
 | 
						|
    image: "chrome://global/skin/icons/reload.svg",
 | 
						|
    type: kInputTypes.BUTTON,
 | 
						|
    callback: () => {
 | 
						|
      lazy.touchBarHelper.unfocusUrlbar();
 | 
						|
      execCommand("Browser:Reload");
 | 
						|
    },
 | 
						|
  },
 | 
						|
  Home: {
 | 
						|
    title: "home",
 | 
						|
    image: "chrome://browser/skin/home.svg",
 | 
						|
    type: kInputTypes.BUTTON,
 | 
						|
    callback: () => {
 | 
						|
      let win = lazy.BrowserWindowTracker.getTopWindow();
 | 
						|
      win.BrowserCommands.home();
 | 
						|
    },
 | 
						|
  },
 | 
						|
  Fullscreen: {
 | 
						|
    title: "fullscreen",
 | 
						|
    image: "chrome://browser/skin/fullscreen.svg",
 | 
						|
    type: kInputTypes.BUTTON,
 | 
						|
    callback: () => execCommand("View:FullScreen"),
 | 
						|
  },
 | 
						|
  Find: {
 | 
						|
    title: "find",
 | 
						|
    image: "chrome://global/skin/icons/search-glass.svg",
 | 
						|
    type: kInputTypes.BUTTON,
 | 
						|
    callback: () => execCommand("cmd_find"),
 | 
						|
  },
 | 
						|
  NewTab: {
 | 
						|
    title: "new-tab",
 | 
						|
    image: "chrome://global/skin/icons/plus.svg",
 | 
						|
    type: kInputTypes.BUTTON,
 | 
						|
    callback: () => execCommand("cmd_newNavigatorTabNoEvent"),
 | 
						|
  },
 | 
						|
  Sidebar: {
 | 
						|
    title: "open-sidebar",
 | 
						|
    image: "chrome://browser/skin/sidebars.svg",
 | 
						|
    type: kInputTypes.BUTTON,
 | 
						|
    callback: () => {
 | 
						|
      let win = lazy.BrowserWindowTracker.getTopWindow();
 | 
						|
      win.SidebarController.toggle();
 | 
						|
    },
 | 
						|
  },
 | 
						|
  AddBookmark: {
 | 
						|
    title: "add-bookmark",
 | 
						|
    image: "chrome://browser/skin/bookmark-hollow.svg",
 | 
						|
    type: kInputTypes.BUTTON,
 | 
						|
    callback: () => execCommand("Browser:AddBookmarkAs"),
 | 
						|
  },
 | 
						|
  ReaderView: {
 | 
						|
    title: "reader-view",
 | 
						|
    image: "chrome://browser/skin/reader-mode.svg",
 | 
						|
    type: kInputTypes.BUTTON,
 | 
						|
    callback: () => execCommand("View:ReaderView"),
 | 
						|
    disabled: true, // Updated when the page is found to be Reader View-able.
 | 
						|
  },
 | 
						|
  OpenLocation: {
 | 
						|
    key: "open-location",
 | 
						|
    title: "open-location",
 | 
						|
    image: "chrome://global/skin/icons/search-glass.svg",
 | 
						|
    type: kInputTypes.MAIN_BUTTON,
 | 
						|
    callback: () => lazy.touchBarHelper.toggleFocusUrlbar(),
 | 
						|
  },
 | 
						|
  // This is a special-case `type: kInputTypes.SCRUBBER` element.
 | 
						|
  // Scrubbers are not yet generally implemented.
 | 
						|
  // See follow-up bug 1502539.
 | 
						|
  Share: {
 | 
						|
    title: "share",
 | 
						|
    image: "chrome://browser/skin/share.svg",
 | 
						|
    type: kInputTypes.SCRUBBER,
 | 
						|
    callback: () => execCommand("cmd_share"),
 | 
						|
  },
 | 
						|
  SearchPopover: {
 | 
						|
    title: "search-popover",
 | 
						|
    image: "chrome://global/skin/icons/search-glass.svg",
 | 
						|
    type: kInputTypes.POPOVER,
 | 
						|
    children: {
 | 
						|
      SearchScrollViewLabel: {
 | 
						|
        title: "search-search-in",
 | 
						|
        type: kInputTypes.LABEL,
 | 
						|
      },
 | 
						|
      SearchScrollView: {
 | 
						|
        key: "search-scrollview",
 | 
						|
        type: kInputTypes.SCROLLVIEW,
 | 
						|
        children: {
 | 
						|
          Bookmarks: {
 | 
						|
            title: "search-bookmarks",
 | 
						|
            type: kInputTypes.BUTTON,
 | 
						|
            callback: () =>
 | 
						|
              lazy.touchBarHelper.insertRestrictionInUrlbar(
 | 
						|
                lazy.UrlbarTokenizer.RESTRICT.BOOKMARK
 | 
						|
              ),
 | 
						|
          },
 | 
						|
          OpenTabs: {
 | 
						|
            title: "search-opentabs",
 | 
						|
            type: kInputTypes.BUTTON,
 | 
						|
            callback: () =>
 | 
						|
              lazy.touchBarHelper.insertRestrictionInUrlbar(
 | 
						|
                lazy.UrlbarTokenizer.RESTRICT.OPENPAGE
 | 
						|
              ),
 | 
						|
          },
 | 
						|
          History: {
 | 
						|
            title: "search-history",
 | 
						|
            type: kInputTypes.BUTTON,
 | 
						|
            callback: () =>
 | 
						|
              lazy.touchBarHelper.insertRestrictionInUrlbar(
 | 
						|
                lazy.UrlbarTokenizer.RESTRICT.HISTORY
 | 
						|
              ),
 | 
						|
          },
 | 
						|
          Tags: {
 | 
						|
            title: "search-tags",
 | 
						|
            type: kInputTypes.BUTTON,
 | 
						|
            callback: () =>
 | 
						|
              lazy.touchBarHelper.insertRestrictionInUrlbar(
 | 
						|
                lazy.UrlbarTokenizer.RESTRICT.TAG
 | 
						|
              ),
 | 
						|
          },
 | 
						|
        },
 | 
						|
      },
 | 
						|
    },
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
// We create a new flat object to cache strings. Since gBuiltInInputs is a
 | 
						|
// tree, caching/retrieval of localized strings would otherwise require tree
 | 
						|
// traversal.
 | 
						|
var localizedStrings = {};
 | 
						|
 | 
						|
const kHelperObservers = new Set([
 | 
						|
  "bookmark-icon-updated",
 | 
						|
  "fullscreen-painted",
 | 
						|
  "reader-mode-available",
 | 
						|
  "touchbar-location-change",
 | 
						|
  "quit-application",
 | 
						|
  "intl:app-locales-changed",
 | 
						|
  "urlbar-focus",
 | 
						|
  "urlbar-blur",
 | 
						|
]);
 | 
						|
 | 
						|
/**
 | 
						|
 * JS-implemented TouchBarHelper class.
 | 
						|
 * Provides services to the Mac Touch Bar.
 | 
						|
 */
 | 
						|
export class TouchBarHelper {
 | 
						|
  constructor() {
 | 
						|
    for (let topic of kHelperObservers) {
 | 
						|
      Services.obs.addObserver(this, topic);
 | 
						|
    }
 | 
						|
    // We cache our search popover since otherwise it is frequently
 | 
						|
    // created/destroyed for the urlbar-focus/blur events.
 | 
						|
    this._searchPopover = this.getTouchBarInput("SearchPopover");
 | 
						|
 | 
						|
    this._inputsNotUpdated = new Set();
 | 
						|
  }
 | 
						|
 | 
						|
  destructor() {
 | 
						|
    this._searchPopover = null;
 | 
						|
    for (let topic of kHelperObservers) {
 | 
						|
      Services.obs.removeObserver(this, topic);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  get activeTitle() {
 | 
						|
    if (!TouchBarHelper.window) {
 | 
						|
      return "";
 | 
						|
    }
 | 
						|
    let tabbrowser = TouchBarHelper.window.ownerGlobal.gBrowser;
 | 
						|
    let activeTitle;
 | 
						|
    if (tabbrowser) {
 | 
						|
      activeTitle = tabbrowser.selectedBrowser.contentTitle;
 | 
						|
    }
 | 
						|
    return activeTitle;
 | 
						|
  }
 | 
						|
 | 
						|
  get allItems() {
 | 
						|
    let layoutItems = Cc["@mozilla.org/array;1"].createInstance(
 | 
						|
      Ci.nsIMutableArray
 | 
						|
    );
 | 
						|
 | 
						|
    let window = TouchBarHelper.window;
 | 
						|
    if (
 | 
						|
      !window ||
 | 
						|
      !window.isChromeWindow ||
 | 
						|
      window.document.documentElement.getAttribute("windowtype") !=
 | 
						|
        "navigator:browser"
 | 
						|
    ) {
 | 
						|
      return layoutItems;
 | 
						|
    }
 | 
						|
 | 
						|
    // Every input must be updated at least once so that all assets (titles,
 | 
						|
    // icons) are loaded. We keep track of which inputs haven't updated and
 | 
						|
    // run an update on them ASAP.
 | 
						|
    this._inputsNotUpdated.clear();
 | 
						|
 | 
						|
    for (let inputName of Object.keys(gBuiltInInputs)) {
 | 
						|
      let input = this.getTouchBarInput(inputName);
 | 
						|
      if (!input) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      this._inputsNotUpdated.add(inputName);
 | 
						|
      layoutItems.appendElement(input);
 | 
						|
    }
 | 
						|
 | 
						|
    return layoutItems;
 | 
						|
  }
 | 
						|
 | 
						|
  static get window() {
 | 
						|
    return lazy.BrowserWindowTracker.getTopWindow();
 | 
						|
  }
 | 
						|
 | 
						|
  get document() {
 | 
						|
    if (!TouchBarHelper.window) {
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
    return TouchBarHelper.window.document;
 | 
						|
  }
 | 
						|
 | 
						|
  get isUrlbarFocused() {
 | 
						|
    if (!TouchBarHelper.window || !TouchBarHelper.window.gURLBar) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    return TouchBarHelper.window.gURLBar.focused;
 | 
						|
  }
 | 
						|
 | 
						|
  toggleFocusUrlbar() {
 | 
						|
    if (this.isUrlbarFocused) {
 | 
						|
      this.unfocusUrlbar();
 | 
						|
    } else {
 | 
						|
      execCommand("Browser:OpenLocation");
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  unfocusUrlbar() {
 | 
						|
    if (!this.isUrlbarFocused) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    TouchBarHelper.window.gURLBar.blur();
 | 
						|
  }
 | 
						|
 | 
						|
  static get baseWindow() {
 | 
						|
    return TouchBarHelper.window
 | 
						|
      ? TouchBarHelper.window.docShell.treeOwner.QueryInterface(
 | 
						|
          Ci.nsIBaseWindow
 | 
						|
        )
 | 
						|
      : null;
 | 
						|
  }
 | 
						|
 | 
						|
  getTouchBarInput(inputName) {
 | 
						|
    if (inputName == "SearchPopover" && this._searchPopover) {
 | 
						|
      return this._searchPopover;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!inputName || !gBuiltInInputs.hasOwnProperty(inputName)) {
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
 | 
						|
    let inputData = gBuiltInInputs[inputName];
 | 
						|
 | 
						|
    let item = new TouchBarInput(inputData);
 | 
						|
 | 
						|
    // Skip localization if there is already a cached localized title or if
 | 
						|
    // no title is needed.
 | 
						|
    if (
 | 
						|
      !inputData.hasOwnProperty("title") ||
 | 
						|
      localizedStrings[inputData.title]
 | 
						|
    ) {
 | 
						|
      return item;
 | 
						|
    }
 | 
						|
 | 
						|
    // Async l10n fills in the localized input labels after the initial load.
 | 
						|
    this._l10n.formatValue(inputData.title).then(result => {
 | 
						|
      item.title = result;
 | 
						|
      localizedStrings[inputData.title] = result; // Cache result.
 | 
						|
      // Checking TouchBarHelper.window since this callback can fire after all windows are closed.
 | 
						|
      if (TouchBarHelper.window) {
 | 
						|
        if (this._inputsNotUpdated) {
 | 
						|
          this._inputsNotUpdated.delete(inputName);
 | 
						|
        }
 | 
						|
        lazy.touchBarUpdater.updateTouchBarInputs(TouchBarHelper.baseWindow, [
 | 
						|
          item,
 | 
						|
        ]);
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    return item;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Fetches a specific Touch Bar Input by name and updates it on the Touch Bar.
 | 
						|
   *
 | 
						|
   * @param {...*} inputNames
 | 
						|
   *        A key/keys to a value/values in the gBuiltInInputs object in this file.
 | 
						|
   */
 | 
						|
  _updateTouchBarInputs(...inputNames) {
 | 
						|
    if (!TouchBarHelper.window || !inputNames.length) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    let inputs = [];
 | 
						|
    for (let inputName of new Set([...inputNames, ...this._inputsNotUpdated])) {
 | 
						|
      let input = this.getTouchBarInput(inputName);
 | 
						|
      if (!input) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      this._inputsNotUpdated.delete(inputName);
 | 
						|
      inputs.push(input);
 | 
						|
    }
 | 
						|
 | 
						|
    lazy.touchBarUpdater.updateTouchBarInputs(
 | 
						|
      TouchBarHelper.baseWindow,
 | 
						|
      inputs
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Inserts a restriction token into the Urlbar ahead of the current typed
 | 
						|
   * search term.
 | 
						|
   *
 | 
						|
   * @param {string} restrictionToken
 | 
						|
   *        The restriction token to be inserted into the Urlbar. Preferably
 | 
						|
   *        sourced from UrlbarTokenizer.RESTRICT.
 | 
						|
   */
 | 
						|
  insertRestrictionInUrlbar(restrictionToken) {
 | 
						|
    if (!TouchBarHelper.window) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    let searchString = "";
 | 
						|
    if (
 | 
						|
      TouchBarHelper.window.gURLBar.getAttribute("pageproxystate") != "valid"
 | 
						|
    ) {
 | 
						|
      searchString = TouchBarHelper.window.gURLBar.lastSearchString.trimStart();
 | 
						|
      if (
 | 
						|
        Object.values(lazy.UrlbarTokenizer.RESTRICT).includes(searchString[0])
 | 
						|
      ) {
 | 
						|
        searchString = searchString.substring(1).trimStart();
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    TouchBarHelper.window.gURLBar.search(
 | 
						|
      `${restrictionToken} ${searchString}`,
 | 
						|
      { searchModeEntry: "touchbar" }
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  observe(subject, topic, data) {
 | 
						|
    switch (topic) {
 | 
						|
      case "touchbar-location-change":
 | 
						|
        let updatedInputs = ["Back", "Forward"];
 | 
						|
        gBuiltInInputs.Back.disabled =
 | 
						|
          !TouchBarHelper.window.gBrowser.canGoBack;
 | 
						|
        gBuiltInInputs.Forward.disabled =
 | 
						|
          !TouchBarHelper.window.gBrowser.canGoForward;
 | 
						|
        if (subject.QueryInterface(Ci.nsIWebProgress)?.isTopLevel) {
 | 
						|
          this.activeUrl = data;
 | 
						|
          // ReaderView button is disabled on every toplevel location change
 | 
						|
          // since Reader View must determine if the new page can be Reader
 | 
						|
          // Viewed.
 | 
						|
          updatedInputs.push("ReaderView");
 | 
						|
          gBuiltInInputs.ReaderView.disabled = !data.startsWith("about:reader");
 | 
						|
        }
 | 
						|
        this._updateTouchBarInputs(...updatedInputs);
 | 
						|
        break;
 | 
						|
      case "fullscreen-painted":
 | 
						|
        if (TouchBarHelper.window.document.fullscreenElement) {
 | 
						|
          gBuiltInInputs.OpenLocation.title = "touchbar-fullscreen-exit";
 | 
						|
          gBuiltInInputs.OpenLocation.image =
 | 
						|
            "chrome://browser/skin/fullscreen-exit.svg";
 | 
						|
          gBuiltInInputs.OpenLocation.callback = () => {
 | 
						|
            TouchBarHelper.window.windowUtils.exitFullscreen();
 | 
						|
          };
 | 
						|
        } else {
 | 
						|
          gBuiltInInputs.OpenLocation.title = "open-location";
 | 
						|
          gBuiltInInputs.OpenLocation.image =
 | 
						|
            "chrome://global/skin/icons/search-glass.svg";
 | 
						|
          gBuiltInInputs.OpenLocation.callback = () =>
 | 
						|
            execCommand("Browser:OpenLocation", "OpenLocation");
 | 
						|
        }
 | 
						|
        this._updateTouchBarInputs("OpenLocation");
 | 
						|
        break;
 | 
						|
      case "bookmark-icon-updated":
 | 
						|
        gBuiltInInputs.AddBookmark.image =
 | 
						|
          data == "starred"
 | 
						|
            ? "chrome://browser/skin/bookmark.svg"
 | 
						|
            : "chrome://browser/skin/bookmark-hollow.svg";
 | 
						|
        this._updateTouchBarInputs("AddBookmark");
 | 
						|
        break;
 | 
						|
      case "reader-mode-available":
 | 
						|
        gBuiltInInputs.ReaderView.disabled = false;
 | 
						|
        this._updateTouchBarInputs("ReaderView");
 | 
						|
        break;
 | 
						|
      case "urlbar-focus":
 | 
						|
        if (!this._searchPopover) {
 | 
						|
          this._searchPopover = this.getTouchBarInput("SearchPopover");
 | 
						|
        }
 | 
						|
        lazy.touchBarUpdater.showPopover(
 | 
						|
          TouchBarHelper.baseWindow,
 | 
						|
          this._searchPopover,
 | 
						|
          true
 | 
						|
        );
 | 
						|
        break;
 | 
						|
      case "urlbar-blur":
 | 
						|
        if (!this._searchPopover) {
 | 
						|
          this._searchPopover = this.getTouchBarInput("SearchPopover");
 | 
						|
        }
 | 
						|
        lazy.touchBarUpdater.showPopover(
 | 
						|
          TouchBarHelper.baseWindow,
 | 
						|
          this._searchPopover,
 | 
						|
          false
 | 
						|
        );
 | 
						|
        break;
 | 
						|
      case "intl:app-locales-changed":
 | 
						|
        this._searchPopover = null;
 | 
						|
        localizedStrings = {};
 | 
						|
 | 
						|
        // This event can fire before this._l10n updates to switch languages,
 | 
						|
        // so all the new translations are in the old language. To avoid this,
 | 
						|
        // we need to reinitialize this._l10n.
 | 
						|
        this._l10n = new Localization(["browser/touchbar/touchbar.ftl"]);
 | 
						|
        helperProto._l10n = this._l10n;
 | 
						|
 | 
						|
        this._updateTouchBarInputs(...Object.keys(gBuiltInInputs));
 | 
						|
        break;
 | 
						|
      case "quit-application":
 | 
						|
        this.destructor();
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
const helperProto = TouchBarHelper.prototype;
 | 
						|
helperProto.QueryInterface = ChromeUtils.generateQI(["nsITouchBarHelper"]);
 | 
						|
helperProto._l10n = new Localization(["browser/touchbar/touchbar.ftl"]);
 | 
						|
 | 
						|
/**
 | 
						|
 * A representation of a Touch Bar input.
 | 
						|
 *
 | 
						|
 *     @param {object} input
 | 
						|
 *            An object representing a Touch Bar Input.
 | 
						|
 *            Contains listed properties.
 | 
						|
 *     @param {string} input.title
 | 
						|
 *            The lookup key for the button's localized text title.
 | 
						|
 *     @param {string} input.image
 | 
						|
 *            A URL pointing to an SVG internal to Firefox.
 | 
						|
 *     @param {string} input.type
 | 
						|
 *            The type of Touch Bar input represented by the object.
 | 
						|
 *            Must be a value from kInputTypes.
 | 
						|
 *     @param {Function} input.callback
 | 
						|
 *            A callback invoked when a touchbar item is touched.
 | 
						|
 *     @param {string} [input.color]
 | 
						|
 *            A string in hex format specifying the button's background color.
 | 
						|
 *            If omitted, the default background color is used.
 | 
						|
 *     @param {bool} [input.disabled]
 | 
						|
 *            If `true`, the Touch Bar input is greyed out and inoperable.
 | 
						|
 *     @param {Array} [input.children]
 | 
						|
 *            An array of input objects that will be displayed as children of
 | 
						|
 *            this input. Available only for types KInputTypes.POPOVER and
 | 
						|
 *            kInputTypes.SCROLLVIEW.
 | 
						|
 */
 | 
						|
export class TouchBarInput {
 | 
						|
  constructor(input) {
 | 
						|
    this._key = input.key || input.title;
 | 
						|
    this._title = localizedStrings[input.title] || "";
 | 
						|
    this._image = input.image;
 | 
						|
    this._type = input.type;
 | 
						|
    this._callback = input.callback;
 | 
						|
    this._color = hexToInt(input.color);
 | 
						|
    this._disabled = input.hasOwnProperty("disabled") ? input.disabled : false;
 | 
						|
    if (input.children) {
 | 
						|
      this._children = [];
 | 
						|
      let toLocalize = [];
 | 
						|
      for (let childData of Object.values(input.children)) {
 | 
						|
        let initializedChild = new TouchBarInput(childData);
 | 
						|
        if (!initializedChild) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
        // Children's types are prepended by the parent's type. This is so we
 | 
						|
        // can uniquely identify a child input from a standalone input with
 | 
						|
        // the same name. (e.g. a button called "back" in a popover would be a
 | 
						|
        // "popover-button.back" vs. a "button.back").
 | 
						|
        initializedChild.type = input.type + "-" + initializedChild.type;
 | 
						|
        this._children.push(initializedChild);
 | 
						|
        // Skip l10n for inputs without a title or those already localized.
 | 
						|
        if (childData.title && !localizedStrings[childData.title]) {
 | 
						|
          toLocalize.push(initializedChild);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      this._localizeChildren(toLocalize);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  get key() {
 | 
						|
    return this._key;
 | 
						|
  }
 | 
						|
  get title() {
 | 
						|
    return this._title;
 | 
						|
  }
 | 
						|
  set title(title) {
 | 
						|
    this._title = title;
 | 
						|
  }
 | 
						|
  get image() {
 | 
						|
    return this._image ? Services.io.newURI(this._image) : null;
 | 
						|
  }
 | 
						|
  set image(image) {
 | 
						|
    this._image = image;
 | 
						|
  }
 | 
						|
  get type() {
 | 
						|
    return this._type == "" ? "button" : this._type;
 | 
						|
  }
 | 
						|
  set type(type) {
 | 
						|
    this._type = type;
 | 
						|
  }
 | 
						|
  get callback() {
 | 
						|
    return this._callback;
 | 
						|
  }
 | 
						|
  set callback(callback) {
 | 
						|
    this._callback = callback;
 | 
						|
  }
 | 
						|
  get color() {
 | 
						|
    return this._color;
 | 
						|
  }
 | 
						|
  set color(color) {
 | 
						|
    this._color = this.hexToInt(color);
 | 
						|
  }
 | 
						|
  get disabled() {
 | 
						|
    return this._disabled || false;
 | 
						|
  }
 | 
						|
  set disabled(disabled) {
 | 
						|
    this._disabled = disabled;
 | 
						|
  }
 | 
						|
  get children() {
 | 
						|
    if (!this._children) {
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
    let children = Cc["@mozilla.org/array;1"].createInstance(
 | 
						|
      Ci.nsIMutableArray
 | 
						|
    );
 | 
						|
    for (let child of this._children) {
 | 
						|
      children.appendElement(child);
 | 
						|
    }
 | 
						|
    return children;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Apply Fluent l10n to child inputs.
 | 
						|
   *
 | 
						|
   * @param {Array} children
 | 
						|
   *   An array of initialized TouchBarInputs.
 | 
						|
   */
 | 
						|
  async _localizeChildren(children) {
 | 
						|
    if (!children || !children.length) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    let titles = await helperProto._l10n.formatValues(
 | 
						|
      children.map(child => ({ id: child.key }))
 | 
						|
    );
 | 
						|
    // In the TouchBarInput constuctor, we filtered so children contains only
 | 
						|
    // those inputs with titles to be localized. We can be confident that the
 | 
						|
    // results in titles match up with the inputs to be localized.
 | 
						|
    children.forEach(function (child, index) {
 | 
						|
      child.title = titles[index];
 | 
						|
      localizedStrings[child.key] = child.title;
 | 
						|
    });
 | 
						|
 | 
						|
    lazy.touchBarUpdater.updateTouchBarInputs(
 | 
						|
      TouchBarHelper.baseWindow,
 | 
						|
      children
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
TouchBarInput.prototype.QueryInterface = ChromeUtils.generateQI([
 | 
						|
  "nsITouchBarInput",
 | 
						|
]);
 |