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.

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

mercurial