tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

index.rst (15421B)


      1 Touch Bar
      2 =========
      3 
      4 The Touch Bar is a hardware component on some MacBook Pros released from 2016.
      5 It is a display above the keyboard that allows more flexible types of
      6 input than is otherwise possible with a normal keyboard. Apple offers Touch Bar
      7 APIs so developers can extend the Touch Bar to display inputs specific to their
      8 application. Firefox consumes these APIs to offer a customizable row of inputs
      9 in the Touch Bar.
     10 
     11 In Apple's documentation, the term "the Touch Bar" refers to the hardware.
     12 The term "a Touch Bar" refers not to the hardware but to a collection of inputs
     13 shown on the Touch Bar. This means that there can be multiple "Touch Bars" that
     14 switch out as the user switches contexts. The same naming convention is used in
     15 this document.
     16 
     17 In this document and in the code, the word "input" is used to refer to
     18 an interactive element in the Touch Bar. It is often interchangeable with
     19 "button", but "input" can also refer to any element displayed in the Touch Bar.
     20 
     21 The Touch Bar should never offer functionality unavailable to Firefox users
     22 without the Touch Bar. Most macOS Firefox users do not have the Touch Bar and
     23 some choose to disable it. Apple's own `Human Interface Guidelines`_ (HIG)
     24 forbids this kind of Touch Bar functionality. Please read the HIG for more
     25 design considerations before you plan on implementing a new Touch Bar feature.
     26 
     27 If you have questions about the Touch Bar that are not answered in this
     28 document, feel free to reach out to `Harry Twyford`_ (:harry on Slack).
     29 He wrote this document and Firefox's initial Touch Bar implementation.
     30 
     31 .. _Human Interface Guidelines: https://developer.apple.com/design/human-interface-guidelines/macos/touch-bar/touch-bar-overview/
     32 
     33 .. _Harry Twyford: mailto:harry@mozilla.com
     34 
     35 .. contents:: Table of Contents
     36 
     37 Overview
     38 ~~~~~~~~
     39 
     40 Firefox's Touch Bar implementation is equal parts JavaScript and Cocoa
     41 (Objective-C++). The JavaScript code lives in ``browser/components/touchbar``
     42 and the Cocoa code lives in ``widget/cocoa``, mostly in ``nsTouchBar.mm``. The
     43 Cocoa code is a consumer of Apple's Touch Bar APIs and defines what types of
     44 Touch Bar inputs are available to its own consumers. The JS code in
     45 ``browser/components/touchbar`` provides services to ``nsTouchBar.mm`` and
     46 defines what inputs the user actually sees in the Touch Bar. There is two-way
     47 communication between the JS and the Cocoa: the Cocoa code asks the JS what
     48 inputs it should display, and the JS asks the Cocoa code to update those inputs
     49 when needed.
     50 
     51 JavaScript API
     52 ~~~~~~~~~~~~~~
     53 
     54 ``browser/components/touchbar/MacTouchBar.sys.mjs`` defines what specific inputs are
     55 available to the user, what icon they will have, what action they will perform,
     56 and so on. Inputs are defined in the ``gBuiltInInputs`` object `in that file`_.
     57 When creating a new object in ``gBuiltInInputs``, the available properties are
     58 documented in the JSDoc for ``TouchBarInput``:
     59 
     60 .. code:: JavaScript
     61 
     62  /**
     63   * A representation of a Touch Bar input.
     64   *     @param {string} input.title
     65   *            The lookup key for the button's localized text title.
     66   *     @param {string} input.image
     67   *            A URL pointing to an SVG internal to Firefox.
     68   *     @param {string} input.type
     69   *            The type of Touch Bar input represented by the object.
     70   *            Must be a value from kInputTypes.
     71   *     @param {Function} input.callback
     72   *            A callback invoked when a touchbar item is touched.
     73   *     @param {string} [input.color]
     74   *            A string in hex format specifying the button's background color.
     75   *            If omitted, the default background color is used.
     76   *     @param {bool} [input.disabled]
     77   *            If `true`, the Touch Bar input is greyed out and inoperable.
     78   *     @param {Array} [input.children]
     79   *            An array of input objects that will be displayed as children of
     80   *            this input. Available only for types KInputTypes.POPOVER and
     81   *            kInputTypes.SCROLLVIEW.
     82   */
     83 
     84 Clarification on some of these properties is warranted.
     85 
     86 * ``title`` is the key to a Fluent translation defined in ``browser/locales/<LOCALE>/browser/touchbar/touchbar.ftl``.
     87 * ``type`` must be a value from the ``kInputTypes`` enum in ``MacTouchBar.sys.mjs``.
     88  For example, ``kInputTypes.BUTTON``. More information on input types follows
     89  below.
     90 * ``callback`` points to a JavaScript function. Any chrome-level JavaScript can
     91  be executed. ``execCommand`` is a convenience method in ``MacTouchBar.sys.mjs``
     92  that takes a XUL command as a string and executes that command. For instance,
     93  one input sets ``callback`` to ``execCommand("Browser:Back")``.
     94 * ``children`` is an array of objects with the same properties as members of
     95  ``gBuiltInInputs``. When used with an input of type
     96  ``kInputTypes.SCROLLVIEW``, ``children`` can only contain inputs of type
     97  ``kInputTypes.BUTTON``. When used with an input of type
     98  ``kInputTypes.POPOVER``, any input type except another ``kInputTypes.POPOVER``
     99  can be used.
    100 
    101 .. _in that file: https://searchfox.org/mozilla-central/rev/669fac9888b173c02baa4c036e980c0c204dfe02/browser/components/touchbar/MacTouchBar.sys.mjs#76
    102 
    103 Input types
    104 -----------
    105 
    106 Button
    107  A simple button. If ``image`` is not specified, the buttons displays the text
    108  label from ``title``. If both ``image`` and ``title`` are specified, only the
    109  ``image`` is shown. The action specified in ``callback`` is executed when the
    110  button is pressed.
    111 
    112  .. caution::
    113 
    114    Even if the ``title`` will not be shown in the Touch Bar, you must still
    115    define a ``title`` property.
    116 
    117 Main Button
    118  Similar to a button, but displayed at double the width. A main button
    119  displays both the string in ``title`` and the icon in ``image``. Only one
    120  main button should be shown in the Touch Bar at any time, although this is
    121  not enforced.
    122 
    123 Label
    124  A non-interactive text label. This input takes only the attributes ``title``
    125  and ``type``.
    126 
    127 Popover
    128  Initially represented in the Touch Bar as a button, a popover will display an
    129  entirely different set of inputs when pressed. These different inputs should
    130  be defined in the ``children`` property of the parent. Popovers can also be
    131  shown and hidden programmatically, by calling
    132 
    133  .. code:: JavaScript
    134 
    135    gTouchBarUpdater.showPopover(
    136      TouchBarHelper.baseWindow,
    137      [POPOVER],
    138      {true | false}
    139    );
    140 
    141  where the second argument is a reference to a popover TouchBarInput and
    142  the third argument is whether the popover should be shown or hidden.
    143 
    144 Scroll View
    145  A Scroll View is a scrolling list of buttons. The buttons should be defined
    146  in the Scroll View's ``children`` array.
    147 
    148  .. note::
    149 
    150    In Firefox, a list of search shortcuts appears in the Touch Bar when the
    151    address bar is focused. This is an example of a ScrollView contained within
    152    a popover. The popover is opened programmatically with
    153    ``gTouchBarUpdater.showPopover`` when the address bar is focused and it is
    154    hidden when the address bar is blurred.
    155 
    156 Examples
    157 --------
    158 Some examples of ``gBuiltInInputs`` objects follow.
    159 
    160 A simple button
    161 
    162  .. code:: JavaScript
    163 
    164    Back: {
    165      title: "back",
    166      image: "chrome://browser/skin/back.svg",
    167      type: kInputTypes.BUTTON,
    168      callback: () => execCommand("Browser:Back", "Back"),
    169    },
    170 
    171  A button is defined with a title, icon, type, and a callback. The callback
    172  simply calls the XUL command to go back.
    173 
    174 The search popover
    175  This is the input that occupies the Touch Bar when the address bar is focused.
    176 
    177  .. code:: JavaScript
    178 
    179    SearchPopover: {
    180      title: "search-popover",
    181      image: "chrome://global/skin/icons/search-glass.svg",
    182      type: kInputTypes.POPOVER,
    183      children: {
    184        SearchScrollViewLabel: {
    185          title: "search-search-in",
    186          type: kInputTypes.LABEL,
    187        },
    188        SearchScrollView: {
    189          key: "search-scrollview",
    190          type: kInputTypes.SCROLLVIEW,
    191          children: {
    192            Bookmarks: {
    193              title: "search-bookmarks",
    194              type: kInputTypes.BUTTON,
    195              callback: () =>
    196                gTouchBarHelper.insertRestrictionInUrlbar(
    197                  UrlbarTokenizer.RESTRICT.BOOKMARK
    198                ),
    199            },
    200            History: {
    201              title: "search-history",
    202              type: kInputTypes.BUTTON,
    203              callback: () =>
    204                gTouchBarHelper.insertRestrictionInUrlbar(
    205                  UrlbarTokenizer.RESTRICT.HISTORY
    206                ),
    207            },
    208            OpenTabs: {
    209              title: "search-opentabs",
    210              type: kInputTypes.BUTTON,
    211              callback: () =>
    212                gTouchBarHelper.insertRestrictionInUrlbar(
    213                  UrlbarTokenizer.RESTRICT.OPENPAGE
    214                ),
    215            },
    216            Tags: {
    217              title: "search-tags",
    218              type: kInputTypes.BUTTON,
    219              callback: () =>
    220                gTouchBarHelper.insertRestrictionInUrlbar(
    221                  UrlbarTokenizer.RESTRICT.TAG
    222                ),
    223            },
    224          },
    225        },
    226      },
    227    },
    228 
    229  At the top level, a Popover is defined. This allows a collection of children
    230  to be shown in a separate Touch Bar. The Popover has two children: a Label,
    231  and a Scroll View. The Scroll View displays five similar buttons that call a
    232  helper method to insert search shortcut symbols into the address bar.
    233 
    234 Adding a new input
    235 ------------------
    236 Adding a new input is easy: just add a new object to ``gBuiltInInputs``. This
    237 will make the input available in the Touch Bar customization window (accessible
    238 from the Firefox menu bar item).
    239 
    240 If you want to to add your new input to the default set, add its identifier
    241 here_, where ``type`` is a value from ``kAllowedInputTypes`` in that
    242 file and ``key`` is the value you set for ``title`` in ``gBuiltInInputs``.
    243 You should request approval from UX before changing the default set of inputs.
    244 
    245 .. _here: https://searchfox.org/mozilla-central/rev/ebe492edacc75bb122a2b380e4cafcca3470864c/widget/cocoa/nsTouchBar.mm#100
    246 
    247 If you are interested in adding new features to Firefox's implementation of the
    248 Touch Bar API, read on!
    249 
    250 
    251 Cocoa API
    252 ~~~~~~~~~
    253 Firefox implements Apple's Touch Bar API in its Widget: Cocoa code with an
    254 ``nsTouchBar`` class. ``nsTouchBar`` interfaces between Apple's Touch Bar API
    255 and the ``TouchBarHelper`` JavaScript API.
    256 
    257 The best resource to understand the Touch Bar API is Apple's
    258 `official documentation`_. This documentation will cover how Firefox implements
    259 these APIs and how one might extend ``nsTouchBar`` to enable new Touch Bar
    260 features.
    261 
    262 Every new Firefox window initializes ``nsTouchBar`` (link_). The function
    263 ``makeTouchBar`` is looked for automatically on every new instance of an
    264 ``NSWindow*``. If ``makeTouchBar`` is defined, that window will own a new
    265 instance of ``nsTouchBar``.
    266 
    267 At the time of this writing, every window initializes ``nsTouchBar`` with a
    268 default set of inputs. In the future, Firefox windows other than the main
    269 browser window (such as the Library window or DevTools) may initialize
    270 ``nsTouchBar`` with a different set of inputs.
    271 
    272 ``nsTouchBar`` has two different initialization methods: ``init`` and
    273 ``initWithInputs``. The former is a convenience method for the latter, calling
    274 ``initWithInputs`` with a nil argument. When that happens, a Touch Bar is
    275 created containing a default set of inputs. ``initWithInputs`` can also take an
    276 ``NSArray<TouchBarInput*>*``. In that case, a non-customizable Touch Bar will be
    277 initialized with only those inputs available.
    278 
    279 .. _official documentation: https://developer.apple.com/documentation/appkit/nstouchbar?language=objc
    280 .. _link: https://searchfox.org/mozilla-central/rev/ebe492edacc75bb122a2b380e4cafcca3470864c/widget/cocoa/nsCocoaWindow.mm#2877
    281 
    282 NSTouchBarItemIdentifiers
    283 -------------------------
    284 The architecture of the Touch Bar is based largely around an ``NSString*``
    285 wrapper class called ``NSTouchBarItemIdentifier``. Every input in the Touch Bar
    286 has a unique ``NSTouchBarItemIdentifier``. They are structured in reverse-URI
    287 format like so:
    288 
    289 ``com.mozilla.firefox.touchbar.[TYPE].[KEY]``
    290 
    291 [TYPE] is a string indicating the type of the input, e.g. "button". If an
    292 input is a child of another input, the parent's type is prepended to the child's
    293 type, e.g. "scrubber.button" indicates a button contained in a scrubber.
    294 
    295 [KEY] is the ``title`` attribute defined for that input on the JS side.
    296 
    297 If you need to generate an identifier, use the convenience method
    298 ``[TouchBarInput nativeIdentifierWithType:withKey:]``.
    299 
    300 .. caution::
    301 
    302  Do not create a new input that would have the same identifier as any other
    303  input. All identifiers must be unique.
    304 
    305 .. warning::
    306 
    307  ``NSTouchBarItemIdentifier`` `is used in one other place`_: setting
    308  ``customizationIdentifier``. Do not ever change this string. If it is changed,
    309  any customizations users have made to the layout of their Touch Bar in Firefox
    310  will be erased.
    311 
    312 Each identifier is tied to a ``TouchBarInput``. ``TouchBarInput`` is a class
    313 that holds the properties specified for each input in ``gBuiltInInputs``.
    314 ``nsTouchBar`` uses them to create instances of ``NSTouchBarItem``
    315 which are the actual objects used by Apple's Touch Bar API and displayed in the
    316 Touch Bar. It is important to understand the difference between
    317 ``TouchBarInput`` and ``NSTouchBarItem``!
    318 
    319 .. _is used in one other place: https://searchfox.org/mozilla-central/rev/ebe492edacc75bb122a2b380e4cafcca3470864c/widget/cocoa/nsTouchBar.mm#71
    320 
    321 TouchBarInput creation flow
    322 ---------------------------
    323 Creating a Touch Bar and its ``TouchBarInputs`` flows as follows:
    324 
    325 #. ``[nsTouchBar init]`` is called from ``[NSWindow makeTouchBar]``.
    326 
    327 #. ``init`` populates two NSArrays: ``customizationAllowedItemIdentifiers`` and
    328   ``defaultItemIdentifiers``. It also initializes a ``TouchBarInput`` object
    329   for every element in the union of the two arrays and stores them in
    330   ``NSMutableDictionary<NSTouchBarItemIdentifier, TouchBarInput*>* mappedLayoutItems``.
    331 
    332 #. ``touchBar:makeItemForIdentifier:`` is called for every element in the union
    333   of the two arrays of identifiers. This method retrieves the ``TouchBarInput``
    334   for the given identifier and uses it to initialize a ``NSTouchBarItem``.
    335   ``touchBar:makeItemForIdentifier:`` reads the ``type`` attribute from the
    336   ``TouchBarInput`` to determine what ``NSTouchBarItem`` subclass should be
    337   initialized. Our Touch Bar code currently supports ``NSCustomTouchBarItem``
    338   (buttons, main buttons); ``NSPopoverTouchBarItem`` (popovers);
    339   ``NSTextField`` (labels); and ``NSScrollView`` (ScrollViews).
    340 
    341 #. Once the ``NSTouchBarItem`` is initialized, its properties are populated with
    342   an assortment of "update" methods. These include ``updateButton``,
    343   ``updateMainButton``, ``updateLabel``, ``updatePopover``, and
    344   ``updateScrollView``.
    345 
    346 #. Since the localization of ``TouchBarInput`` titles happens asynchronously in
    347   JavaScript code, the l10n callback executes
    348   ``[nsTouchBarUpdater updateTouchBarInputs:]``. This method reads the
    349   identifier of the input(s) that need to be updated and calls their respective
    350   "update" methods. This method is most often used to update ``title`` after
    351   l10n is complete. It can also be used to update any property of a
    352   ``TouchBarInput``;  for instance, one might wish to change ``color``
    353   when a specific event occurs in the browser.