summaryrefslogtreecommitdiffstats
path: root/browser/components/resistfingerprinting/test/mochitest/test_pointer_event.html
blob: 5b8271274e13f0c68324d5ed7b9bd9c4ac0ed13a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1363508
-->
<head>
  <meta charset="utf-8">
  <title>Test for Pointer Events spoofing</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script src="/tests/SimpleTest/EventUtils.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<div id="target0" style="width: 50px; height: 50px; background: green"></div>
<div id="target1" style="width: 50px; height: 50px; background: black"></div>
<script type="application/javascript">

  /** Test for Bug 1363508 */
  SimpleTest.waitForExplicitFinish();

  var target0 = window.document.getElementById("target0");
  var target1 = window.document.getElementById("target1");
  var utils = SpecialPowers.Ci.nsIDOMWindowUtils;

  // A helper function to check that whether the pointer is spoofed correctly.
  function checkPointerEvent(aEvent) {
    is(aEvent.pointerId, utils.DEFAULT_MOUSE_POINTER_ID,
       "The spoofed pointer event should always have the mouse pointer id.");
    is(aEvent.width, 1, "The spoofed pointer event should always have width as 1.");
    is(aEvent.height, 1, "The spoofed pointer event should always have width as 1.");
    if (aEvent.buttons === 0) {
      is(aEvent.pressure, 0.0,
         "The spoofed pointer event should have pressure as 0.0 if it is not in a active buttons state.");
    } else {
      is(aEvent.pressure, 0.5,
         "The spoofed pointer event should have pressure as 0.5 if it is in a active buttons state.");
    }
    is(aEvent.tangentialPressure, 0, "The spoofed pointer event should always have tangentialPressure as 0.");
    is(aEvent.tiltX, 0, "The spoofed pointer event should always have tiltX as 0.");
    is(aEvent.tiltY, 0, "The spoofed pointer event should always have tiltY as 0.");
    is(aEvent.twist, 0, "The spoofed pointer event should always have twist as 0.");
    is(aEvent.pointerType, "mouse", "The spoofed pointer event should always has mouse pointerType.");
    is(aEvent.isPrimary, true, "The spoofed pointer event should only receive primary pointer events.");
  }

  // A helper function to create a promise for waiting the event.
  function promiseForEvent(aEventType, aCheckFunc) {
    return new Promise(resolve => {
      target0.addEventListener(aEventType, (event) => {
        is(event.type, aEventType, "receive " + event.type + " on target0");
        aCheckFunc(event);
        resolve();
      }, { once: true });
    });
  }

  // A test for pointer events from touch interface.
  async function doTestForTouchPointerEvent() {
    let eventPromises = [
      promiseForEvent("pointerover", checkPointerEvent),
      promiseForEvent("pointerenter", checkPointerEvent),
      promiseForEvent("pointerdown", checkPointerEvent),
      promiseForEvent("pointermove", checkPointerEvent),
      promiseForEvent("pointerup", checkPointerEvent),
      promiseForEvent("pointerout", checkPointerEvent),
      promiseForEvent("pointerleave", checkPointerEvent),
    ];

    synthesizeMouse(target0, 5, 5, { type: "mousedown", inputSource: MouseEvent.MOZ_SOURCE_TOUCH, pressure: 0.75 });
    synthesizeMouse(target0, 5, 5, { type: "mousemove", inputSource: MouseEvent.MOZ_SOURCE_TOUCH, pressure: 0.75 });
    synthesizeMouse(target0, 5, 5, { type: "mouseup", inputSource: MouseEvent.MOZ_SOURCE_TOUCH, pressure: 0.75 });

    await Promise.all(eventPromises);
  }

  // A test for pointercancel event.
  async function doTestForTouchPointerCancelEvent() {
    let eventPromises = [
      promiseForEvent("pointerover", checkPointerEvent),
      promiseForEvent("pointerenter", checkPointerEvent),
      promiseForEvent("pointerdown", checkPointerEvent),
      promiseForEvent("pointermove", checkPointerEvent),
      promiseForEvent("pointercancel", checkPointerEvent),
      promiseForEvent("pointerout", checkPointerEvent),
      promiseForEvent("pointerleave", checkPointerEvent),
    ];

    synthesizeTouch(target0, 5, 5, { type: "touchstart" });
    synthesizeTouch(target0, 6, 6, { type: "touchmove" });
    synthesizeTouch(target0, 6, 6, { type: "touchcancel" });

    await Promise.all(eventPromises);
  }

  // A test for pointer events from pen interface.
  async function doTestForPenPointerEvent() {
    let eventPromises = [
      promiseForEvent("pointerover", checkPointerEvent),
      promiseForEvent("pointerenter", checkPointerEvent),
      promiseForEvent("pointerdown", checkPointerEvent),
      promiseForEvent("pointermove", checkPointerEvent),
      promiseForEvent("pointerup", checkPointerEvent),
      promiseForEvent("pointerout", checkPointerEvent),
      promiseForEvent("pointerleave", checkPointerEvent),
    ];

    synthesizeMouse(target0, 5, 5, { type: "mousedown", inputSource: MouseEvent.MOZ_SOURCE_PEN });
    synthesizeMouse(target0, 5, 5, { type: "mousemove", inputSource: MouseEvent.MOZ_SOURCE_PEN });
    synthesizeMouse(target0, 5, 5, { type: "mouseup", inputSource: MouseEvent.MOZ_SOURCE_PEN });
    synthesizeMouse(target1, 5, 5, { type: "mousemove", inputSource: MouseEvent.MOZ_SOURCE_PEN });

    await Promise.all(eventPromises);
  }

  // A test for gotpointercapture and lostpointercapture events.
  // We would also test releasePointerCapture for only accepting spoofed pointer
  // Id here.
  async function doTestForPointerCapture() {
    // We test for both mouse and touch to see whether the capture events are
    // filed properly. We don't check pen here since it won't file capture
    // events.
    let inputSources = [ MouseEvent.MOZ_SOURCE_MOUSE,
                         MouseEvent.MOZ_SOURCE_TOUCH ];

    for (let inputSource of inputSources) {
      function eventHandler(event) {
        checkPointerEvent(event);
        if (event.type === "pointerdown") {
          target0.setPointerCapture(event.pointerId);
        } else if (event.type === "pointermove") {
          if (inputSource === MouseEvent.MOZ_SOURCE_TOUCH) {
            try {
              target0.releasePointerCapture(utils.DEFAULT_TOUCH_POINTER_ID);
              ok(false, "The releasePointerCapture should fail here, but it is not.");
            } catch (e) {
              ok(true, "The releasePointerCapture fails properly.");
            }
          }
          target0.releasePointerCapture(event.pointerId);
        }
      }

      let eventPromises = [
        promiseForEvent("pointerover", eventHandler),
        promiseForEvent("pointerenter", eventHandler),
        promiseForEvent("pointerdown", eventHandler),
        promiseForEvent("gotpointercapture", eventHandler),
        promiseForEvent("pointermove", eventHandler),
        promiseForEvent("lostpointercapture", eventHandler),
        promiseForEvent("pointerup", eventHandler),
        promiseForEvent("pointerout", eventHandler),
        promiseForEvent("pointerleave", eventHandler),
      ];

      synthesizeMouse(target0, 5, 5, { type: "mousedown", inputSource });
      synthesizeMouse(target0, 5, 5, { type: "mousemove", inputSource });
      synthesizeMouse(target0, 5, 5, { type: "mouseup", inputSource });
      synthesizeMouse(target1, 5, 5, { type: "mousemove", inputSource });

      await Promise.all(eventPromises);
    }
  }

  // A test for setPointerCapture() for only accepting spoofed pointer id.
  async function doTestForSetPointerCapture() {
    function eventHandler(event) {
      checkPointerEvent(event);
      if (event.type === "pointerdown") {
        try {
          target0.setPointerCapture(utils.DEFAULT_TOUCH_POINTER_ID);
          ok(false, "The setPointerCapture should fail here, but it is not.");
        } catch (e) {
          ok(true, "The setPointerCapture fails properly.");
        }
      }
    }

    let eventPromises = [
      promiseForEvent("pointerover", eventHandler),
      promiseForEvent("pointerenter", eventHandler),
      promiseForEvent("pointerdown", eventHandler),
      promiseForEvent("pointermove", eventHandler),
      promiseForEvent("pointerup", eventHandler),
      promiseForEvent("pointerout", eventHandler),
      promiseForEvent("pointerleave", eventHandler),
    ];

    synthesizeMouse(target0, 5, 5, { type: "mousedown", inputSource: MouseEvent.MOZ_SOURCE_TOUCH });
    synthesizeMouse(target0, 5, 5, { type: "mousemove", inputSource: MouseEvent.MOZ_SOURCE_TOUCH });
    synthesizeMouse(target0, 5, 5, { type: "mouseup", inputSource: MouseEvent.MOZ_SOURCE_TOUCH });

    await Promise.all(eventPromises);
  }

  // A test for assuring that script generated events won't be spoofed.
  function doTestNoSpoofingForScriptGeneratedEvent() {
    return new Promise(resolve => {
      // Generate a custom pointer event by script.
      let pointerEventCustom = new PointerEvent("pointerover", {
        pointerId: utils.DEFAULT_TOUCH_POINTER_ID,
        pointerType: "touch",
        width: 5,
        height: 5,
        pressure: 0.75,
        tangentialPressure: 0.5,
        isPrimary: false,
      });

      target0.addEventListener("pointerover", (event) => {
        // Check that script generated event is not spoofed.
        is(event.pointerType, "touch", "The pointerEvent.pointerType is not spoofed.");
        is(event.width, 5, "The pointerEvent.width is not spoofed.");
        is(event.height, 5, "The pointerEvent.height is not spoofed.");
        is(event.pressure, 0.75, "The pointerEvent.pressure is not spoofed.");
        is(event.tangentialPressure, 0.5, "The pointerEvent.tangentialPressure is not spoofed.");
        is(event.isPrimary, false, "The pointerEvent.isPrimary is not spoofed.");
        resolve();
      }, { once: true });

      target0.dispatchEvent(pointerEventCustom);
    });
  }

  async function doTests() {
    await doTestForTouchPointerEvent();
    await doTestForTouchPointerCancelEvent();
    await doTestForPenPointerEvent();
    await doTestForPointerCapture();
    await doTestForSetPointerCapture();
    await doTestNoSpoofingForScriptGeneratedEvent();

    SimpleTest.finish();
  }

  SimpleTest.waitForFocus(() => {
    SpecialPowers.pushPrefEnv({"set": [["privacy.resistFingerprinting", true]]},
                              doTests);
  });

</script>
</body>
</html>