<commit_before>/* 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/. */

var EXPORTED_SYMBOLS = ["MouseGestureObserver"];

const {classes: Cc, interfaces: Ci, utils: Cu} = Components;

Cu.import("resource://gre/modules/Services.jsm")
Cu.import("resource://gesticulate/log.jsm");

const platform = Cc["@mozilla.org/system-info;1"]
                   .getService(Ci.nsIPropertyBag2).getProperty("name");

/**
 * Map a button bitmask containing only one set bit to a button index
 * (effectively, Event.buttons => Event.button).
 *
 * @param x A button bitmask
 * @return The mapped button index
 */
function fromButtons(x) {
  switch (x) {
  case 1:  return 0;
  case 2:  return 2;
  case 4:  return 1;
  case 8:  return 3;
  case 16: return 4;
  default: return undefined;
  }
}

/**
 * Map a button index to a button bitmask (effectively, Event.button =>
 * Event.buttons).
 *
 * @param x A button index
 * @return The mapped button bitmask
 */
function toButtons(x) {
  switch (x) {
  case 0:  return 1;
  case 1:  return 4;
  case 2:  return 2;
  case 3:  return 8;
  case 4:  return 16;
  default: return undefined;
  }
}

/**
 * Create a new mouse gesture observer.
 *
 * @param window The window to observe gestures for
 */
function MouseGestureObserver(window) {
  this._window = window;

  this._bound = new Map();
  for (var i of ["mousedown", "mouseup", "click", "contextmenu", "wheel"]) {
    var bound = this[i].bind(this);
    this._bound.set(i, bound);
    this._window.addEventListener(i, bound, true);
  }
}

MouseGestureObserver.prototype = {
  // True if we're performing a gesture; this lets us know when to suppress
  // mouse events.
  _performingGesture: false,

  // True if the browser tried to open a context menu and we want to delay it
  // until the corresponding click event.
  _wantContextMenu: false,

  /**
   * Clean up the mouse gesture observer, detaching all event listeners.
   */
  cleanup: function() {
    for (var [k, v] of this._bound.entries()) {
      this._window.removeEventListener(k, v, true);
    }
  },

  /**
   * Determine if the context menu's popup should be delayed. This is necessary
   * on non-Windows platforms because it pops up on mousedown, which can
   * visually interrupt a rocker gesture.
   */
  get _delayContextMenu() {
    return platform !== "Windows_NT" && Services.prefs.getBoolPref(
      "extensions.gesticulate.delay_context_menu"
    );
  },

  /**
   * Get the button to use for mouse gestures.
   */
  get _gestureButton() {
    return Services.prefs.getIntPref("extensions.gesticulate.gesture_button");
  },

  /**
   * Reset the internal state to the default. This is necessary because Firefox
   * swallows pending events during page unloads
   */
  _resetState: function() {
    this._performingGesture = false;
    this._wantContextMenu = false;
  },

  /**
   * Handle the mousedown event. Currently, we consider something to be a rocker
   * gesture if a single button was pressed before this event, and now two
   * buttons are pressed.
   *
   * Note: This does mean that it's possible to change the "base" of the rocker
   * gesture (e.g hold RMB, hold LMB ("go back") release RMB, hold RMB ("go
   * forward")). I'm not convinced this is really a problem, though.
   *
   * @param event The event to handle
   */
  mousedown: function(event) {
    if (!event.isTrusted)
      return;

    let oldState = event.buttons - toButtons(event.button);
    debug(this._window, "mousedown", oldState, event.buttons, event);

    if (oldState === 0) {
      // Firefox swallows pending events during page unloads; make sure we're
      // in the proper state if we exited the gesture.
      this._resetState();
    } else if (fromButtons(oldState) !== undefined) {
      event.target.dispatchEvent(new this._window.CustomEvent(
        "gesture", { detail: {
          subtype: "rocker",
          firstButton: fromButtons(oldState),
          secondButton: event.button,
          id: "rocker:" + fromButtons(oldState) + "," + event.button<commit_msg>Fix contextmenu-delaying code on recent (e10s?) Firefox builds<commit_after>/* 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/. */

var EXPORTED_SYMBOLS = ["MouseGestureObserver"];

const {classes: Cc, interfaces: Ci, utils: Cu} = Components;

Cu.import("resource://gre/modules/Services.jsm")
Cu.import("resource://gesticulate/log.jsm");

const platform = Cc["@mozilla.org/system-info;1"]
                   .getService(Ci.nsIPropertyBag2).getProperty("name");

/**
 * Map a button bitmask containing only one set bit to a button index
 * (effectively, Event.buttons => Event.button).
 *
 * @param x A button bitmask
 * @return The mapped button index
 */
function fromButtons(x) {
  switch (x) {
  case 1:  return 0;
  case 2:  return 2;
  case 4:  return 1;
  case 8:  return 3;
  case 16: return 4;
  default: return undefined;
  }
}

/**
 * Map a button index to a button bitmask (effectively, Event.button =>
 * Event.buttons).
 *
 * @param x A button index
 * @return The mapped button bitmask
 */
function toButtons(x) {
  switch (x) {
  case 0:  return 1;
  case 1:  return 4;
  case 2:  return 2;
  case 3:  return 8;
  case 4:  return 16;
  default: return undefined;
  }
}

/**
 * Create a new mouse gesture observer.
 *
 * @param window The window to observe gestures for
 */
function MouseGestureObserver(window) {
  this._window = window;

  this._bound = new Map();
  for (var i of ["mousedown", "mouseup", "click", "contextmenu", "wheel"]) {
    var bound = this[i].bind(this);
    this._bound.set(i, bound);
    this._window.addEventListener(i, bound, true);
  }
}

MouseGestureObserver.prototype = {
  // True if we're performing a gesture; this lets us know when to suppress
  // mouse events.
  _performingGesture: false,

  // True if the browser tried to open a context menu and we want to delay it
  // until the corresponding click event.
  _wantContextMenu: false,

  /**
   * Clean up the mouse gesture observer, detaching all event listeners.
   */
  cleanup: function() {
    for (var [k, v] of this._bound.entries()) {
      this._window.removeEventListener(k, v, true);
    }
  },

  /**
   * Determine if the context menu's popup should be delayed. This is necessary
   * on non-Windows platforms because it pops up on mousedown, which can
   * visually interrupt a rocker gesture.
   */
  get _delayContextMenu() {
    return platform !== "Windows_NT" && Services.prefs.getBoolPref(
      "extensions.gesticulate.delay_context_menu"
    );
  },

  /**
   * Get the button to use for mouse gestures.
   */
  get _gestureButton() {
    return Services.prefs.getIntPref("extensions.gesticulate.gesture_button");
  },

  /**
   * Reset the internal state to the default if we were performing a gesture.
   * This is necessary because Firefox swallows pending events during page
   * unloads.
   */
  _resetState: function() {
    // We only reset when we know we were already performing a gesture because
    // recent Firefox builds on Linux fire the contextmenu event *before* the
    // mousedown event. If we don't do this, we'll forget that we had a context
    // menu queued up.
    if (this._performingGesture) {
      this._performingGesture = false;
      this._wantContextMenu = false;
    }
  },

  /**
   * Handle the mousedown event. Currently, we consider something to be a rocker
   * gesture if a single button was pressed before this event, and now two
   * buttons are pressed.
   *
   * Note: This does mean that it's possible to change the "base" of the rocker
   * gesture (e.g hold RMB, hold LMB ("go back") release RMB, hold RMB ("go
   * forward")). I'm not convinced this is really a problem, though.
   *
   * @param event The event to handle
   */
  mousedown: function(event) {
    if (!event.isTrusted)
      return;

    let oldState = event.buttons - toButtons(event.button);
    debug(this._window, "mousedown", oldState, event.buttons, event);

    if (oldState === 0) {
      // Firefox swallows pending events during page unloads; make sure we're
      // in the proper state if we exited the gesture.
      this._resetState();
    } else if (fromButtons(oldState) !== undefined) {
      event.target.dispatchEvent(new this._window.CustomEvent(
        "gesture", { detail: {
          subtype: "rocker",
          firstButton: fromButtons(oldState),
          secondButton: event.button,
          id: "rocker:" + fromButtons(oldState) + "," + event.button
{'fraction_non_alphanumeric': 0.112, 'fraction_numerical': 0.006857142857142857, 'mean_word_length': 3.0589053803339517, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7572233', 'n_tokens_mistral': 2748, 'n_tokens_neox': 2628, 'n_words': 955}