Fix multi-touch to work in SkyShell.

Before this patch it crashed.

Also updated the touch-demo to support multi-touch.

R=abarth@chromium.org
BUG=

Review URL: https://codereview.chromium.org/961483004
diff --git a/sky/engine/core/dom/ParentNode.idl b/sky/engine/core/dom/ParentNode.idl
index 1e031c4..a1f0c75 100644
--- a/sky/engine/core/dom/ParentNode.idl
+++ b/sky/engine/core/dom/ParentNode.idl
@@ -14,9 +14,9 @@
   sequence<Element> getChildElements();
 
   [CustomElementCallbacks, RaisesException] void append(sequence<Node> nodes);
-  [CustomElementCallbacks, RaisesException] Node appendChild(Node nodes);
+  [CustomElementCallbacks, RaisesException] Node appendChild(Node node);
   [CustomElementCallbacks, RaisesException] void prepend(sequence<Node> nodes);
-  [CustomElementCallbacks, RaisesException] Node prependChild(Node nodes);
+  [CustomElementCallbacks, RaisesException] Node prependChild(Node node);
 
   [CustomElementCallbacks] void removeChildren();
   [CustomElementCallbacks, RaisesException] Node setChild(Node node);
diff --git a/sky/engine/core/frame/NewEventHandler.cpp b/sky/engine/core/frame/NewEventHandler.cpp
index 9154289..a7e6c26 100644
--- a/sky/engine/core/frame/NewEventHandler.cpp
+++ b/sky/engine/core/frame/NewEventHandler.cpp
@@ -121,7 +121,7 @@
 void NewEventHandler::updateSelectionForPointerDown(const HitTestResult& hitTestResult, const WebPointerEvent& event)
 {
     Node* innerNode = hitTestResult.innerNode();
-    if (!innerNode->renderer())
+    if (!innerNode || !innerNode->renderer())
         return;
     if (Position::nodeIsUserSelectNone(innerNode))
         return;
@@ -191,14 +191,14 @@
     // pointer ID, but for mice, we don't get a pointer cancel when you
     // drag outside the window frame on Linux. For now, send the pointer
     // cancel at this point.
-    if (event.kind == WebPointerEvent::Mouse
-        && m_stateForPointer.find(event.pointer) != m_stateForPointer.end()) {
+    bool alreadyDown = m_stateForPointer.find(event.pointer) != m_stateForPointer.end();
+    if (event.kind == WebPointerEvent::Mouse && alreadyDown) {
         WebPointerEvent fakeCancel = event;
         fakeCancel.type = WebInputEvent::PointerCancel;
         handlePointerCancelEvent(fakeCancel);
     }
 
-    ASSERT(m_stateForPointer.find(event.pointer) == m_stateForPointer.end());
+    DCHECK(!alreadyDown) << "Pointer id " << event.pointer << "already down!";
     HitTestResult hitTestResult = performHitTest(positionForEvent(event));
     RefPtr<Node> target = targetForHitTestResult(hitTestResult);
     if (!target)
diff --git a/sky/examples/touch-demo.sky b/sky/examples/touch-demo.sky
index e9482ab..57a8e41 100644
--- a/sky/examples/touch-demo.sky
+++ b/sky/examples/touch-demo.sky
@@ -6,27 +6,54 @@
     height: 100px;
     width: 100px;
     background-color: #00FF00;
+    border-radius: 50px;
 }
 </style>
-<dot />
 <log>Ready</log>
 <script>
 import "dart:sky";
 
-final Element dot = document.querySelector("dot");
+// Material design colors. :p
+List<String> colors = [
+  "#009688",
+  "#FFC107",
+  "#9C27B0",
+  "#03A9F4",
+  "#673AB7",
+  "#CDDC39",
+];
 
-void moveDot(event) {
-  double x = event.x;
-  double y = event.y;
+Element whichDot(event) {
+  return document.querySelector('dot[id="${event.pointer}"]');
+}
 
-  dot.style["transform"] = "translate(${x-50}px,${y-50}px)";
+void moreDots(event) {
+  Element dot = document.createElement('dot');
+  dot.setAttribute('id', "${event.pointer}");
+  dot.style['background-color'] = colors[event.pointer.remainder(colors.length)];
+  document.querySelector('sky').appendChild(dot);
+  runToTheCenter(event);
+}
+
+void goAway(event) {
+  whichDot(event).remove();
+}
+
+void stopDots(event) {
+  for (Element e in document.querySelectorAll('dot'))
+    e.remove();
+}
+
+void runToTheCenter(event) {
+  whichDot(event).style["transform"] =
+      "translate(${event.x-50}px,${event.y-50}px)";
 }
 
 void main() {
-  document.addEventListener("pointerdown", moveDot);
-  document.addEventListener("pointermove", moveDot);
-  document.addEventListener("pointerup", moveDot);
-  document.addEventListener("pointercancel", moveDot);
+  document.addEventListener("pointerdown", moreDots);
+  document.addEventListener("pointermove", runToTheCenter);
+  document.addEventListener("pointerup", goAway);
+  document.addEventListener("pointercancel", stopDots);
 }
 </script>
 </sky>
diff --git a/sky/shell/org/domokit/sky/shell/PlatformView.java b/sky/shell/org/domokit/sky/shell/PlatformView.java
index 2f646c9..e24cbdf 100644
--- a/sky/shell/org/domokit/sky/shell/PlatformView.java
+++ b/sky/shell/org/domokit/sky/shell/PlatformView.java
@@ -81,10 +81,17 @@
     }
 
     private int getTypeForAction(int maskedAction) {
+        // Primary pointer:
         if (maskedAction == MotionEvent.ACTION_DOWN)
             return EventType.POINTER_DOWN;
         if (maskedAction == MotionEvent.ACTION_UP)
             return EventType.POINTER_UP;
+        // Secondary pointer:
+        if (maskedAction == MotionEvent.ACTION_POINTER_DOWN)
+            return EventType.POINTER_DOWN;
+        if (maskedAction == MotionEvent.ACTION_POINTER_UP)
+            return EventType.POINTER_UP;
+        // All pointers:
         if (maskedAction == MotionEvent.ACTION_MOVE)
             return EventType.POINTER_MOVE;
         if (maskedAction == MotionEvent.ACTION_CANCEL)
@@ -92,13 +99,12 @@
         return EventType.UNKNOWN;
     }
 
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
+    private void sendInputEventForIndex(MotionEvent event, int pointerIndex) {
         PointerData pointerData = new PointerData();
-        pointerData.pointer = event.getPointerId(0);
+        pointerData.pointer = event.getPointerId(pointerIndex);
         pointerData.kind = PointerKind.TOUCH;
-        pointerData.x = event.getX();
-        pointerData.y = event.getY();
+        pointerData.x = event.getX(pointerIndex);
+        pointerData.y = event.getY(pointerIndex);
 
         InputEvent inputEvent = new InputEvent();
         inputEvent.type = getTypeForAction(event.getActionMasked());
@@ -106,6 +112,26 @@
         inputEvent.pointerData = pointerData;
 
         mViewportObserver.onInputEvent(inputEvent);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        int maskedAction = event.getActionMasked();
+        // ACTION_UP, ACTION_POINTER_UP, ACTION_DOWN, and ACTION_POINTER_DOWN
+        // only apply to a single pointer, other events apply to all pointers.
+        if (maskedAction == MotionEvent.ACTION_UP
+                || maskedAction == MotionEvent.ACTION_POINTER_UP
+                || maskedAction == MotionEvent.ACTION_DOWN
+                || maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
+            sendInputEventForIndex(event, event.getActionIndex());
+        } else {
+            // ACTION_MOVE may not actually mean all pointers have moved
+            // but it's the responsibility of a later part of the system to
+            // ignore 0-deltas if desired.
+            for (int p = 0; p < event.getPointerCount(); p++) {
+                sendInputEventForIndex(event, p);
+            }
+        }
         return true;
     }