Bug 1293174 - [Pointer Event] Implement implicit pointer capture for touch. f=bevistseng. r=smaug
authorStone Shih <sshih@mozilla.com>
Thu, 11 Aug 2016 14:49:15 +0800
changeset 316023 b577f6e25606543a4161c3a589bd288e0051ce02
parent 316022 dd46c03a458c5054e5930a75b7ec0a3da5e9e359
child 316024 06cb6118fd79e14fa3e2573cdfad025b12d7eae6
push id82336
push userryanvm@gmail.com
push dateFri, 30 Sep 2016 19:22:19 +0000
treeherdermozilla-inbound@95c7a910a079 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1293174
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1293174 - [Pointer Event] Implement implicit pointer capture for touch. f=bevistseng. r=smaug
dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_1.html
dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_2.html
dom/events/test/pointerevents/mochitest.ini
dom/events/test/pointerevents/mochitest_support_external.js
dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_1.html
dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_2.html
layout/base/nsPresShell.cpp
modules/libpref/init/all.js
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_1.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+    <head>
+        <title>Pointer Events properties tests</title>
+        <meta name="viewport" content="width=device-width">
+        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+        <script src="/resources/testharness.js"></script>
+        <!--script src="/resources/testharnessreport.js"></script-->
+        <!-- Additional helper script for common checks across event types -->
+        <script type="text/javascript" src="pointerevent_support.js"></script>
+        <script type="text/javascript" src="mochitest_support_internal.js"></script>
+        <script>
+          var detected_pointertypes = {};
+          var test_pointerEvent = async_test("implicit pointer capture for touch");
+          // showPointerTypes is defined in pointerevent_support.js
+          // Requirements: the callback function will reference the test_pointerEvent object and
+          // will fail unless the async_test is created with the var name "test_pointerEvent".
+          add_completion_callback(showPointerTypes);
+
+          function run() {
+            let target0 = window.document.getElementById("target0");
+            let target1 = window.document.getElementById("target1");
+
+            on_event(target0, "pointerdown", function (event) {
+              pointerdown_event = event;
+              detected_pointertypes[event.pointerType] = true;
+              assert_true(true, "target0 receives pointerdown");
+            });
+
+            on_event(target0, "pointermove", function (event) {
+              assert_true(true, "target0 receives pointermove");
+              assert_true(target0.hasPointerCapture(event.pointerId), "target0.hasPointerCapture should be true");
+            });
+
+            on_event(target0, "gotpointercapture", function (event) {
+              assert_true(true, "target0 should receive gotpointercapture");
+            });
+
+            on_event(target0, "lostpointercapture", function (event) {
+              assert_true(true, "target0 should receive lostpointercapture");
+            });
+
+            on_event(target1, "pointermove", function (event) {
+              assert_true(false, "target1 should not receive pointermove");
+            });
+
+            on_event(target0, "pointerup", function (event) {
+              assert_true(true, "target0 receives pointerup");
+              test_pointerEvent.done();
+            });
+          }
+        </script>
+    </head>
+    <body onload="run()">
+        <h1>Pointer Events tests</h1>
+        <div id="target0" style="width: 200px; height: 200px; background: green" touch-action:none></div>
+        <div id="target1" style="width: 200px; height: 200px; background: green" touch-action:none></div>
+        <div id="complete-notice">
+            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+            <p>Refresh the page to run the tests again with a different pointer type.</p>
+        </div>
+        <div id="log"></div>
+    </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_2.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<html>
+    <head>
+        <title>Pointer Events properties tests</title>
+        <meta name="viewport" content="width=device-width">
+        <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+        <script src="/resources/testharness.js"></script>
+        <!--script src="/resources/testharnessreport.js"></script-->
+        <!-- Additional helper script for common checks across event types -->
+        <script type="text/javascript" src="pointerevent_support.js"></script>
+        <script type="text/javascript" src="mochitest_support_internal.js"></script>
+        <script>
+          var detected_pointertypes = {};
+          var test_pointerEvent = async_test("implicit pointer capture for touch");
+          // showPointerTypes is defined in pointerevent_support.js
+          // Requirements: the callback function will reference the test_pointerEvent object and
+          // will fail unless the async_test is created with the var name "test_pointerEvent".
+          add_completion_callback(showPointerTypes);
+
+          function run() {
+            let target0 = window.document.getElementById("target0");
+            let target1 = window.document.getElementById("target1");
+
+            on_event(target0, "pointerdown", function (event) {
+              pointerdown_event = event;
+              detected_pointertypes[event.pointerType] = true;
+              assert_true(true, "target0 receives pointerdown");
+            });
+
+            on_event(target0, "pointermove", function (event) {
+              assert_true(true, "target0 receives pointermove");
+              assert_false(target0.hasPointerCapture(event.pointerId), "target0.hasPointerCapture should be false");
+            });
+
+            on_event(target0, "gotpointercapture", function (event) {
+              assert_unreached("target0 should not receive gotpointercapture");
+            });
+
+            on_event(target0, "lostpointercapture", function (event) {
+              assert_unreached("target0 should not receive lostpointercapture");
+            });
+
+            on_event(target1, "pointermove", function (event) {
+              assert_true(true, "target1 receives pointermove");
+              assert_false(target1.hasPointerCapture(), "target1.hasPointerCapture should be false");
+            });
+
+            on_event(target0, "pointerup", function (event) {
+              assert_true(true, "target0 receives pointerup");
+              test_pointerEvent.done();
+            });
+          }
+        </script>
+    </head>
+    <body onload="run()">
+        <h1>Pointer Events tests</h1>
+        <div id="target0" style="width: 200px; height: 200px; background: green" touch-action:none></div>
+        <div id="target1" style="width: 200px; height: 200px; background: green" touch-action:none></div>
+        <div id="complete-notice">
+            <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+            <p>Refresh the page to run the tests again with a different pointer type.</p>
+        </div>
+        <div id="log"></div>
+    </body>
+</html>
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -144,10 +144,14 @@ support-files =
     pointerevent_touch-action-pan-x-css_touch-manual.html
     pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html
     pointerevent_touch-action-pan-x-pan-y_touch-manual.html
     pointerevent_touch-action-pan-y-css_touch-manual.html
     pointerevent_touch-action-span-test_touch-manual.html
     pointerevent_touch-action-svg-test_touch-manual.html
     pointerevent_touch-action-table-test_touch-manual.html
 [test_bug1285128.html]
+[test_bug1293174_implicit_pointer_capture_for_touch_1.html]
+  support-files = bug1293174_implicit_pointer_capture_for_touch_1.html
+[test_bug1293174_implicit_pointer_capture_for_touch_2.html]
+  support-files = bug1293174_implicit_pointer_capture_for_touch_2.html
 [test_empty_file.html]
   disabled = disabled # Bug 1150091 - Issue with support-files
--- a/dom/events/test/pointerevents/mochitest_support_external.js
+++ b/dom/events/test/pointerevents/mochitest_support_external.js
@@ -10,16 +10,25 @@ addEventListener("load", function(event)
 
 // Function allows to initialize prerequisites before testing
 function prepareTest() {
   SimpleTest.waitForExplicitFinish();
   SimpleTest.requestCompleteLog();
   turnOnPointerEvents(startTest);
 }
 
+function setImplicitPointerCapture(capture, callback) {
+  console.log("SET dom.w3c_pointer_events.implicit_capture as " + capture);
+  SpecialPowers.pushPrefEnv({
+    "set": [
+      ["dom.w3c_pointer_events.implicit_capture", capture]
+    ]
+  }, callback);
+}
+
 function turnOnPointerEvents(callback) {
   console.log("SET dom.w3c_pointer_events.enabled as TRUE");
   console.log("SET layout.css.touch_action.enabled as TRUE");
   SpecialPowers.pushPrefEnv({
     "set": [
       ["dom.w3c_pointer_events.enabled", true],
       ["layout.css.touch_action.enabled", true]
     ]
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_1.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Test for Bug 1293174</title>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+    <script type="text/javascript" src="mochitest_support_external.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+    <script type="text/javascript">
+      SimpleTest.waitForExplicitFinish();
+      function startTest() {
+        setImplicitPointerCapture(true, loadSubFrame);
+      }
+      function loadSubFrame() {
+        var iframe = document.getElementById("testFrame");
+        iframe.src = "bug1293174_implicit_pointer_capture_for_touch_1.html";
+      }
+      function executeTest(int_win) {
+        sendTouchEvent(int_win, "target0", "touchstart");
+        sendTouchEvent(int_win, "target0", "touchmove");
+        sendTouchEvent(int_win, "target1", "touchmove");
+        sendTouchEvent(int_win, "target0", "touchmove");
+        sendTouchEvent(int_win, "target0", "touchend");
+      }
+    </script>
+  </head>
+  <body>
+    <iframe id="testFrame" height="800" width="1000"></iframe>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_2.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Test for Bug 1293174</title>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+    <script type="text/javascript" src="mochitest_support_external.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+    <script type="text/javascript">
+      SimpleTest.waitForExplicitFinish();
+      function startTest() {
+        setImplicitPointerCapture(false, loadSubFrame);
+      }
+      function loadSubFrame() {
+        var iframe = document.getElementById("testFrame");
+        iframe.src = "bug1293174_implicit_pointer_capture_for_touch_2.html";
+      }
+      function executeTest(int_win) {
+        sendTouchEvent(int_win, "target0", "touchstart");
+        sendTouchEvent(int_win, "target0", "touchmove");
+        sendTouchEvent(int_win, "target1", "touchmove");
+        sendTouchEvent(int_win, "target0", "touchmove");
+        sendTouchEvent(int_win, "target0", "touchend");
+      }
+    </script>
+  </head>
+  <body>
+    <iframe id="testFrame" height="800" width="1000"></iframe>
+  </body>
+</html>
+
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -726,16 +726,17 @@ nsIPresShell::FrameSelection()
   return ret.forget();
 }
 
 //----------------------------------------------------------------------
 
 static bool sSynthMouseMove = true;
 static uint32_t sNextPresShellId;
 static bool sPointerEventEnabled = true;
+static bool sPointerEventImplicitCapture = false;
 static bool sAccessibleCaretEnabled = false;
 static bool sAccessibleCaretOnTouch = false;
 static bool sBeforeAfterKeyboardEventEnabled = false;
 
 /* static */ bool
 PresShell::AccessibleCaretEnabled(nsIDocShell* aDocShell)
 {
   static bool initialized = false;
@@ -803,17 +804,23 @@ PresShell::PresShell()
     addedSynthMouseMove = true;
   }
   static bool addedPointerEventEnabled = false;
   if (!addedPointerEventEnabled) {
     Preferences::AddBoolVarCache(&sPointerEventEnabled,
                                  "dom.w3c_pointer_events.enabled", true);
     addedPointerEventEnabled = true;
   }
-
+  static bool addedPointerEventImplicitCapture = false;
+  if (!addedPointerEventImplicitCapture) {
+    Preferences::AddBoolVarCache(&sPointerEventImplicitCapture,
+                                 "dom.w3c_pointer_events.implicit_capture",
+                                 true);
+    addedPointerEventImplicitCapture = true;
+  }
   mPaintingIsFrozen = false;
   mHasCSSBackgroundColor = true;
   mIsLastChromeOnlyEscapeKeyConsumed = false;
   mHasReceivedPaintMessage = false;
 }
 
 NS_IMPL_ISUPPORTS(PresShell, nsIPresShell, nsIDocumentObserver,
                   nsISelectionController,
@@ -7686,16 +7693,29 @@ PresShell::HandleEvent(nsIFrame* aFrame,
         // gotpointercapture / lostpointercapture.
         CheckPointerCaptureState(pointerEvent->pointerId,
                                  pointerEvent->inputSource,
                                  pointerEvent->mIsPrimary);
         // Prevent application crashes, in case damaged frame.
         if (!frameKeeper.IsAlive()) {
           frame = nullptr;
         }
+        // Implicit pointer capture for touch
+        if (sPointerEventImplicitCapture &&
+            pointerEvent->mMessage == ePointerDown &&
+            pointerEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
+          nsCOMPtr<nsIContent> targetContent;
+          frame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
+          while (targetContent && !targetContent->IsElement()) {
+            targetContent = targetContent->GetParent();
+          }
+          if (targetContent) {
+            SetPointerCapturingContent(pointerEvent->pointerId, targetContent);
+          }
+        }
       }
     }
 
     if (aEvent->mClass == ePointerEventClass &&
         aEvent->mMessage != ePointerDown) {
       if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
         uint32_t pointerId = pointerEvent->pointerId;
         nsIContent* pointerCapturingContent = GetPointerCapturingContent(pointerId);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4808,16 +4808,19 @@ pref("dom.w3c_touch_events.enabled", 0);
 pref("dom.w3c_touch_events.enabled", 0);
 #else
 pref("dom.w3c_touch_events.enabled", 2);
 #endif
 
 // W3C draft pointer events
 pref("dom.w3c_pointer_events.enabled", false);
 
+// W3C pointer events draft
+pref("dom.w3c_pointer_events.implicit_capture", false);
+
 // W3C draft ImageCapture API
 pref("dom.imagecapture.enabled", false);
 
 // W3C MediaDevices devicechange event
 pref("media.ondevicechange.enabled", false);
 
 // W3C MediaDevices devicechange fake event
 pref("media.ondevicechange.fakeDeviceChangeEvent.enabled", false);