toolkit/devtools/touch-events.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 let {CC, Cc, Ci, Cu, Cr} = require('chrome');
     8 Cu.import('resource://gre/modules/Services.jsm');
    10 let handlerCount = 0;
    12 let orig_w3c_touch_events = Services.prefs.getIntPref('dom.w3c_touch_events.enabled');
    14 let trackedWindows = new WeakMap();
    16 // =================== Touch ====================
    17 // Simulate touch events on desktop
    18 function TouchEventHandler (window) {
    19   // Returns an already instanciated handler for this window
    20   let cached = trackedWindows.get(window);
    21   if (cached) {
    22     return cached;
    23   }
    25   let contextMenuTimeout = 0;
    28   let threshold = 25;
    29   try {
    30     threshold = Services.prefs.getIntPref('ui.dragThresholdX');
    31   } catch(e) {}
    33   let delay = 500;
    34   try {
    35     delay = Services.prefs.getIntPref('ui.click_hold_context_menus.delay');
    36   } catch(e) {}
    38   let TouchEventHandler = {
    39     enabled: false,
    40     events: ['mousedown', 'mousemove', 'mouseup'],
    41     start: function teh_start() {
    42       if (this.enabled)
    43         return false;
    44       this.enabled = true;
    45       let isReloadNeeded = Services.prefs.getIntPref('dom.w3c_touch_events.enabled') != 1;
    46       Services.prefs.setIntPref('dom.w3c_touch_events.enabled', 1);
    47       this.events.forEach((function(evt) {
    48         // Only listen trusted events to prevent messing with
    49         // event dispatched manually within content documents
    50         window.addEventListener(evt, this, true, false);
    51       }).bind(this));
    52       return isReloadNeeded;
    53     },
    54     stop: function teh_stop() {
    55       if (!this.enabled)
    56         return;
    57       this.enabled = false;
    58       Services.prefs.setIntPref('dom.w3c_touch_events.enabled', orig_w3c_touch_events);
    59       this.events.forEach((function(evt) {
    60         window.removeEventListener(evt, this, true);
    61       }).bind(this));
    62     },
    63     handleEvent: function teh_handleEvent(evt) {
    64       // Ignore all but real mouse event coming from physical mouse
    65       // (especially ignore mouse event being dispatched from a touch event)
    66       if (evt.button || evt.mozInputSource != Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE || evt.isSynthesized) {
    67         return;
    68       }
    70       // The gaia system window use an hybrid system even on the device which is
    71       // a mix of mouse/touch events. So let's not cancel *all* mouse events
    72       // if it is the current target.
    73       let content = this.getContent(evt.target);
    74       let isSystemWindow = content.location.toString().indexOf("system.gaiamobile.org") != -1;
    76       let eventTarget = this.target;
    77       let type = '';
    78       switch (evt.type) {
    79         case 'mousedown':
    80           this.target = evt.target;
    82           contextMenuTimeout =
    83             this.sendContextMenu(evt.target, evt.pageX, evt.pageY, delay);
    85           this.cancelClick = false;
    86           this.startX = evt.pageX;
    87           this.startY = evt.pageY;
    89           // Capture events so if a different window show up the events
    90           // won't be dispatched to something else.
    91           evt.target.setCapture(false);
    93           type = 'touchstart';
    94           break;
    96         case 'mousemove':
    97           if (!eventTarget)
    98             return;
   100           if (!this.cancelClick) {
   101             if (Math.abs(this.startX - evt.pageX) > threshold ||
   102                 Math.abs(this.startY - evt.pageY) > threshold) {
   103               this.cancelClick = true;
   104               content.clearTimeout(contextMenuTimeout);
   105             }
   106           }
   108           type = 'touchmove';
   109           break;
   111         case 'mouseup':
   112           if (!eventTarget)
   113             return;
   114           this.target = null;
   116           content.clearTimeout(contextMenuTimeout);
   117           type = 'touchend';
   119           // Only register click listener after mouseup to ensure
   120           // catching only real user click. (Especially ignore click
   121           // being dispatched on form submit)
   122           if (evt.detail == 1) {
   123             window.addEventListener('click', this, true, false);
   124           }
   125           break;
   127         case 'click':
   128           // Mouse events has been cancelled so dispatch a sequence
   129           // of events to where touchend has been fired
   130           evt.preventDefault();
   131           evt.stopImmediatePropagation();
   133           window.removeEventListener('click', this, true, false);
   135           if (this.cancelClick)
   136             return;
   138           ignoreEvents = true;
   139           content.setTimeout(function dispatchMouseEvents(self) {
   140             try {
   141               self.fireMouseEvent('mousedown', evt);
   142               self.fireMouseEvent('mousemove', evt);
   143               self.fireMouseEvent('mouseup', evt);
   144             } catch(e) {
   145               Cu.reportError('Exception in touch event helper: ' + e);
   146             }
   147             ignoreEvents = false;
   148          }, 0, this);
   150           return;
   151       }
   153       let target = eventTarget || this.target;
   154       if (target && type) {
   155         this.sendTouchEvent(evt, target, type);
   156       }
   158       if (!isSystemWindow) {
   159         evt.preventDefault();
   160         evt.stopImmediatePropagation();
   161       }
   162     },
   163     fireMouseEvent: function teh_fireMouseEvent(type, evt)  {
   164       let content = this.getContent(evt.target);
   165       var utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
   166                          .getInterface(Ci.nsIDOMWindowUtils);
   167       utils.sendMouseEvent(type, evt.clientX, evt.clientY, 0, 1, 0, true, 0, Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
   168     },
   169     sendContextMenu: function teh_sendContextMenu(target, x, y, delay) {
   170       let doc = target.ownerDocument;
   171       let evt = doc.createEvent('MouseEvent');
   172       evt.initMouseEvent('contextmenu', true, true, doc.defaultView,
   173                          0, x, y, x, y, false, false, false, false,
   174                          0, null);
   176       let content = this.getContent(target);
   177       let timeout = content.setTimeout((function contextMenu() {
   178         target.dispatchEvent(evt);
   179         this.cancelClick = true;
   180       }).bind(this), delay);
   182       return timeout;
   183     },
   184     sendTouchEvent: function teh_sendTouchEvent(evt, target, name) {
   185       // When running OOP b2g desktop, we need to send the touch events
   186       // using the mozbrowser api on the unwrapped frame.
   187       if (target.localName == "iframe" && target.mozbrowser === true) {
   188         if (name == "touchstart") {
   189           this.touchstartTime = Date.now();
   190         } else if (name == "touchend") {
   191           // If we have a 'fast' tap, don't send a click as both will be turned
   192           // into a click and that breaks eg. checkboxes.
   193           if (Date.now() - this.touchstartTime < delay) {
   194             this.cancelClick = true;
   195           }
   196         }
   197         let unwraped = XPCNativeWrapper.unwrap(target);
   198         unwraped.sendTouchEvent(name, [0],                    // event type, id
   199                                 [evt.clientX], [evt.clientY], // x, y
   200                                 [1], [1],                     // rx, ry
   201                                 [0], [0],                     // rotation, force
   202                                 1);                           // count
   203         return;
   204       }
   205       let document = target.ownerDocument;
   206       let content = this.getContent(target);
   208       let touchEvent = document.createEvent('touchevent');
   209       let point = document.createTouch(content, target, 0,
   210                                        evt.pageX, evt.pageY,
   211                                        evt.screenX, evt.screenY,
   212                                        evt.clientX, evt.clientY,
   213                                        1, 1, 0, 0);
   214       let touches = document.createTouchList(point);
   215       let targetTouches = touches;
   216       let changedTouches = touches;
   217       touchEvent.initTouchEvent(name, true, true, content, 0,
   218                                 false, false, false, false,
   219                                 touches, targetTouches, changedTouches);
   220       target.dispatchEvent(touchEvent);
   221       return touchEvent;
   222     },
   223     getContent: function teh_getContent(target) {
   224       let win = target.ownerDocument.defaultView;
   225       return win;
   226     }
   227   };
   228   trackedWindows.set(window, TouchEventHandler);
   230   return TouchEventHandler;
   231 }
   233 exports.TouchEventHandler = TouchEventHandler;

mercurial