Set Event.composed flag correctly for some of UA UIEvents

See https://github.com/w3c/webcomponents/issues/513 for the context.

See also the previous CL: https://codereview.chromium.org/2012423004, where
Event.composed was introduced.

The change is visible only when Shadow DOM v1 is used.

BUG=531990,616654

Review-Url: https://codereview.chromium.org/2030243004
Cr-Commit-Position: refs/heads/master@{#397681}
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index a81eda0..43aa5e5 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -508,6 +508,7 @@
 crbug.com/505364 imported/wpt/shadow-dom/untriaged/user-interaction/focus-navigation/test-002.html [ Failure ]
 crbug.com/505364 imported/wpt/shadow-dom/untriaged/user-interaction/focus-navigation/test-003.html [ Failure ]
 crbug.com/505364 imported/wpt/shadow-dom/untriaged/user-interaction/focus-navigation/test-004.html [ Failure ]
+crbug.com/505364 imported/wpt/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-event-interface/event-path-001.html [ Failure ]
 crbug.com/505364 imported/wpt/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-interface-type-is-a-type-extension.html [ Failure ]
 crbug.com/505364 imported/csswg-test/css-scoping-1/css-scoping-shadow-slot-display-override.html [ Failure ]
 
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-dir-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-dir-expected.txt
index fa12bbe..679c8f5 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-dir-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-dir-expected.txt
@@ -68,7 +68,7 @@
     bubbles: false
     cancelBubble: false
     cancelable: false
-    composed: true
+    composed: false
     currentTarget: null
     defaultPrevented: false
     eventPhase: 0
diff --git a/third_party/WebKit/LayoutTests/shadow-dom/event-composed.html b/third_party/WebKit/LayoutTests/shadow-dom/event-composed.html
index c933247..3321868 100644
--- a/third_party/WebKit/LayoutTests/shadow-dom/event-composed.html
+++ b/third_party/WebKit/LayoutTests/shadow-dom/event-composed.html
@@ -18,22 +18,62 @@
   assert_equals(e.composed, true);
 }, 'Users should be able to set a composed value.');
 
-test(() => {
+function assertScoped(event) {
   let nodes = createTestTree(host);
-  let log = dispatchEventWithLog(nodes, nodes.target, new Event('test', {bubbles: true}));
+  let log = dispatchEventWithLog(nodes, nodes.target, event);
   let expectedPath = ['target', 'shadowRoot'];
   assert_event_path_equals(log,
                            [['target', null, expectedPath],
                             ['shadowRoot', null, expectedPath]]);
-}, 'An event should be scoped by default');
+}
 
-test(() => {
+function assertComposed(event) {
   let nodes = createTestTree(host);
-  let log = dispatchEventWithLog(nodes, nodes.target, new Event('test', {bubbles: true, composed: true}));
+  let log = dispatchEventWithLog(nodes, nodes.target, event);
   let expectedPath = ['target', 'shadowRoot', 'host'];
   assert_event_path_equals(log,
                            [['target', null, expectedPath],
                             ['shadowRoot', null, expectedPath],
                             ['host', null, expectedPath]]);
+}
+
+test(() => {
+  assertScoped(new Event('test', { bubbles: true }));
+}, 'An event should be scoped by default');
+
+test(() => {
+  assertComposed(new Event('test', { bubbles: true, composed: true }));
 }, 'An event should not be scoped if composed is specified');
+
+test(() => {
+  assertScoped(new MouseEvent('click', { bubbles: true }));
+}, 'A synthetic MouseEvent should be scoped by default');
+
+test(() => {
+  assertComposed(new MouseEvent('click', { bubbles: true, composed: true }));
+}, 'A synthetic MouseEvent with composed=true should not be scoped');
+
+test(() => {
+  assertScoped(new FocusEvent('focus', { bubbles: true }));
+}, 'A synthetic FocusEvent should be scoped by default');
+
+test(() => {
+  assertComposed(new FocusEvent('focus', { bubbles: true, composed: true }));
+}, 'A synthetic FocusEvent with composed=true should not be scoped');
+
+function assertUAComposed(eventType, callback) {
+  let nodes = createTestTree(host);
+  let log = dispatchUAEventWithLog(nodes, nodes.target, eventType, callback);
+  let expectedPath = ['target', 'shadowRoot', 'host'];
+  assert_event_path_equals(log,
+                           [['target', null, expectedPath],
+                            ['shadowRoot', null, expectedPath],
+                            ['host', null, expectedPath]]);
+}
+
+test(() => {
+  assertUAComposed('click', (target) => { target.click(); });
+}, 'A UA click event should not be scoped');
+
+// TODO(hayato): Test other UIEvents.
 </script>
diff --git a/third_party/WebKit/LayoutTests/shadow-dom/resources/shadow-dom.js b/third_party/WebKit/LayoutTests/shadow-dom/resources/shadow-dom.js
index 488283f..7aeb5fc 100644
--- a/third_party/WebKit/LayoutTests/shadow-dom/resources/shadow-dom.js
+++ b/third_party/WebKit/LayoutTests/shadow-dom/resources/shadow-dom.js
@@ -128,6 +128,36 @@
   return log;
 }
 
+function dispatchUAEventWithLog(nodes, target, eventType, callback) {
+
+  function labelFor(e) {
+    return e.id || e.tagName;
+  }
+
+  let log = [];
+  let attachedNodes = [];
+  for (let label in nodes) {
+    let startingNode = nodes[label];
+    for (let node = startingNode; node; node = node.parentNode) {
+      if (attachedNodes.indexOf(node) >= 0)
+        continue;
+      let id = node.id;
+      if (!id)
+        continue;
+      attachedNodes.push(node);
+      node.addEventListener(eventType, (e) => {
+        log.push([id,
+                  event.relatedTarget ? labelFor(event.relatedTarget) : null,
+                  event.composedPath().map((n) => {
+                    return labelFor(n);
+                  })]);
+      });
+    }
+  }
+  callback(target);
+  return log;
+}
+
 // This function assumes that testharness.js is available.
 function assert_event_path_equals(actual, expected) {
   assert_equals(actual.length, expected.length);
diff --git a/third_party/WebKit/Source/core/events/CompositionEvent.cpp b/third_party/WebKit/Source/core/events/CompositionEvent.cpp
index d5ca38b..f81a716 100644
--- a/third_party/WebKit/Source/core/events/CompositionEvent.cpp
+++ b/third_party/WebKit/Source/core/events/CompositionEvent.cpp
@@ -33,7 +33,7 @@
 }
 
 CompositionEvent::CompositionEvent(const AtomicString& type, AbstractView* view, const String& data)
-    : UIEvent(type, true, true, view, 0, InputDeviceCapabilities::doesntFireTouchEventsSourceCapabilities())
+    : UIEvent(type, true, true, ComposedMode::Composed, view, 0, InputDeviceCapabilities::doesntFireTouchEventsSourceCapabilities())
     , m_data(data)
 {
 }
diff --git a/third_party/WebKit/Source/core/events/Event.cpp b/third_party/WebKit/Source/core/events/Event.cpp
index 5d50666..5295064 100644
--- a/third_party/WebKit/Source/core/events/Event.cpp
+++ b/third_party/WebKit/Source/core/events/Event.cpp
@@ -34,44 +34,27 @@
 
 namespace blink {
 
-static bool isScoped(const AtomicString& eventType)
-{
-    // WebKit never allowed selectstart event to cross the the shadow DOM boundary.
-    // Changing this breaks existing sites.
-    // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details.
-    return (eventType == EventTypeNames::abort
-        || eventType == EventTypeNames::change
-        || eventType == EventTypeNames::error
-        || eventType == EventTypeNames::load
-        || eventType == EventTypeNames::reset
-        || eventType == EventTypeNames::resize
-        || eventType == EventTypeNames::scroll
-        || eventType == EventTypeNames::select
-        || eventType == EventTypeNames::selectstart
-        || eventType == EventTypeNames::slotchange);
-}
-
 Event::Event()
     : Event("", false, false)
 {
     m_wasInitialized = false;
 }
 
-Event::Event(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg)
-    : Event(eventType, canBubbleArg, cancelableArg, !isScoped(eventType), monotonicallyIncreasingTime())
-{
-}
-
 Event::Event(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, double platformTimeStamp)
-    : Event(eventType, canBubbleArg, cancelableArg, !isScoped(eventType), platformTimeStamp)
+    : Event(eventType, canBubbleArg, cancelableArg, ComposedMode::Scoped, platformTimeStamp)
 {
 }
 
-Event::Event(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, bool composed, double platformTimeStamp)
+Event::Event(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, ComposedMode composedMode)
+    : Event(eventType, canBubbleArg, cancelableArg, composedMode, monotonicallyIncreasingTime())
+{
+}
+
+Event::Event(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, ComposedMode composedMode, double platformTimeStamp)
     : m_type(eventType)
     , m_canBubble(canBubbleArg)
     , m_cancelable(cancelableArg)
-    , m_composed(composed)
+    , m_composed(composedMode == ComposedMode::Composed)
     , m_propagationStopped(false)
     , m_immediatePropagationStopped(false)
     , m_defaultPrevented(false)
@@ -88,7 +71,7 @@
 }
 
 Event::Event(const AtomicString& eventType, const EventInit& initializer)
-    : Event(eventType, initializer.bubbles(), initializer.cancelable(), initializer.composed(), monotonicallyIncreasingTime())
+    : Event(eventType, initializer.bubbles(), initializer.cancelable(), initializer.composed() ? ComposedMode::Composed : ComposedMode::Scoped, monotonicallyIncreasingTime())
 {
 }
 
@@ -98,7 +81,20 @@
 
 bool Event::isScopedInV0() const
 {
-    return isTrusted() && isScoped(m_type);
+    // WebKit never allowed selectstart event to cross the the shadow DOM boundary.
+    // Changing this breaks existing sites.
+    // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details.
+    return isTrusted()
+        && (m_type == EventTypeNames::abort
+            || m_type == EventTypeNames::change
+            || m_type == EventTypeNames::error
+            || m_type == EventTypeNames::load
+            || m_type == EventTypeNames::reset
+            || m_type == EventTypeNames::resize
+            || m_type == EventTypeNames::scroll
+            || m_type == EventTypeNames::select
+            || m_type == EventTypeNames::selectstart
+            || m_type == EventTypeNames::slotchange);
 }
 
 void Event::initEvent(const AtomicString& eventTypeArg, bool canBubbleArg, bool cancelableArg)
diff --git a/third_party/WebKit/Source/core/events/Event.h b/third_party/WebKit/Source/core/events/Event.h
index ef7e89b..84ac4da 100644
--- a/third_party/WebKit/Source/core/events/Event.h
+++ b/third_party/WebKit/Source/core/events/Event.h
@@ -74,6 +74,11 @@
         RailsModeVertical   = 2
     };
 
+    enum class ComposedMode {
+        Composed,
+        Scoped,
+    };
+
     static Event* create()
     {
         return new Event;
@@ -215,8 +220,9 @@
 
 protected:
     Event();
+    Event(const AtomicString& type, bool canBubble, bool cancelable, ComposedMode, double platformTimeStamp);
     Event(const AtomicString& type, bool canBubble, bool cancelable, double platformTimeStamp);
-    Event(const AtomicString& type, bool canBubble, bool cancelable);
+    Event(const AtomicString& type, bool canBubble, bool cancelable, ComposedMode = ComposedMode::Scoped);
     Event(const AtomicString& type, const EventInit&);
 
     virtual void receivedTarget();
@@ -224,7 +230,6 @@
     void setCanBubble(bool bubble) { m_canBubble = bubble; }
 
 private:
-    Event(const AtomicString& type, bool canBubble, bool cancelable, bool composed, double platformTimeStamp);
 
     enum EventPathMode {
         EmptyAfterDispatch,
diff --git a/third_party/WebKit/Source/core/events/FocusEvent.cpp b/third_party/WebKit/Source/core/events/FocusEvent.cpp
index 99984e4..c94aac1 100644
--- a/third_party/WebKit/Source/core/events/FocusEvent.cpp
+++ b/third_party/WebKit/Source/core/events/FocusEvent.cpp
@@ -45,7 +45,7 @@
 }
 
 FocusEvent::FocusEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, int detail, EventTarget* relatedTarget, InputDeviceCapabilities* sourceCapabilities)
-    : UIEvent(type, canBubble, cancelable, view, detail, sourceCapabilities)
+    : UIEvent(type, canBubble, cancelable, ComposedMode::Composed, view, detail, sourceCapabilities)
     , m_relatedTarget(relatedTarget)
 {
 }
diff --git a/third_party/WebKit/Source/core/events/TextEvent.cpp b/third_party/WebKit/Source/core/events/TextEvent.cpp
index 65ff7dc..3fca37d 100644
--- a/third_party/WebKit/Source/core/events/TextEvent.cpp
+++ b/third_party/WebKit/Source/core/events/TextEvent.cpp
@@ -63,7 +63,7 @@
 }
 
 TextEvent::TextEvent(AbstractView* view, const String& data, TextEventInputType inputType)
-    : UIEvent(EventTypeNames::textInput, true, true, view, 0)
+    : UIEvent(EventTypeNames::textInput, true, true, ComposedMode::Composed, view, 0)
     , m_inputType(inputType)
     , m_data(data)
     , m_pastingFragment(nullptr)
@@ -74,7 +74,7 @@
 
 TextEvent::TextEvent(AbstractView* view, const String& data, DocumentFragment* pastingFragment,
                      bool shouldSmartReplace, bool shouldMatchStyle)
-    : UIEvent(EventTypeNames::textInput, true, true, view, 0)
+    : UIEvent(EventTypeNames::textInput, true, true, ComposedMode::Composed, view, 0)
     , m_inputType(TextEventInputPaste)
     , m_data(data)
     , m_pastingFragment(pastingFragment)
diff --git a/third_party/WebKit/Source/core/events/UIEvent.cpp b/third_party/WebKit/Source/core/events/UIEvent.cpp
index 6e5bb55..734c404 100644
--- a/third_party/WebKit/Source/core/events/UIEvent.cpp
+++ b/third_party/WebKit/Source/core/events/UIEvent.cpp
@@ -32,16 +32,16 @@
 }
 
 // TODO(lanwei): Will add sourceCapabilities to all the subclass of UIEvent later, see https://crbug.com/476530.
-UIEvent::UIEvent(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, AbstractView* viewArg, int detailArg, InputDeviceCapabilities* sourceCapabilitiesArg)
-    : Event(eventType, canBubbleArg, cancelableArg)
+UIEvent::UIEvent(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, ComposedMode composedMode, AbstractView* viewArg, int detailArg, InputDeviceCapabilities* sourceCapabilitiesArg)
+    : Event(eventType, canBubbleArg, cancelableArg, composedMode)
     , m_view(viewArg)
     , m_detail(detailArg)
     , m_sourceCapabilities(sourceCapabilitiesArg)
 {
 }
 
-UIEvent::UIEvent(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, double platformTimeStamp, AbstractView* viewArg, int detailArg, InputDeviceCapabilities* sourceCapabilitiesArg)
-    : Event(eventType, canBubbleArg, cancelableArg, platformTimeStamp)
+UIEvent::UIEvent(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, ComposedMode composedMode, double platformTimeStamp, AbstractView* viewArg, int detailArg, InputDeviceCapabilities* sourceCapabilitiesArg)
+    : Event(eventType, canBubbleArg, cancelableArg, composedMode, platformTimeStamp)
     , m_view(viewArg)
     , m_detail(detailArg)
     , m_sourceCapabilities(sourceCapabilitiesArg)
diff --git a/third_party/WebKit/Source/core/events/UIEvent.h b/third_party/WebKit/Source/core/events/UIEvent.h
index b4d91ac..a9cb261 100644
--- a/third_party/WebKit/Source/core/events/UIEvent.h
+++ b/third_party/WebKit/Source/core/events/UIEvent.h
@@ -45,7 +45,7 @@
     }
     static UIEvent* create(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, int detail)
     {
-        return new UIEvent(type, canBubble, cancelable, view, detail);
+        return new UIEvent(type, canBubble, cancelable, ComposedMode::Scoped, view, detail);
     }
     static UIEvent* create(const AtomicString& type, const UIEventInit& initializer)
     {
@@ -70,8 +70,8 @@
 protected:
     UIEvent();
     // TODO(crbug.com/563542): Remove of this ctor in favor of making platformTimeStamp (and perhaps sourceCapabilities) required in all constructions sites
-    UIEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView*, int detail, InputDeviceCapabilities* sourceCapabilities = nullptr);
-    UIEvent(const AtomicString& type, bool canBubble, bool cancelable, double platformTimeStamp, AbstractView*, int detail, InputDeviceCapabilities* sourceCapabilities);
+    UIEvent(const AtomicString& type, bool canBubble, bool cancelable, ComposedMode, AbstractView*, int detail, InputDeviceCapabilities* sourceCapabilities = nullptr);
+    UIEvent(const AtomicString& type, bool canBubble, bool cancelable, ComposedMode, double platformTimeStamp, AbstractView*, int detail, InputDeviceCapabilities* sourceCapabilities);
     UIEvent(const AtomicString&, const UIEventInit&);
 
 private:
diff --git a/third_party/WebKit/Source/core/events/UIEventWithKeyState.cpp b/third_party/WebKit/Source/core/events/UIEventWithKeyState.cpp
index 11aeb67..05e7250 100644
--- a/third_party/WebKit/Source/core/events/UIEventWithKeyState.cpp
+++ b/third_party/WebKit/Source/core/events/UIEventWithKeyState.cpp
@@ -24,7 +24,7 @@
 
 UIEventWithKeyState::UIEventWithKeyState(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view,
     int detail, PlatformEvent::Modifiers modifiers, double platformTimeStamp, InputDeviceCapabilities* sourceCapabilities)
-    : UIEvent(type, canBubble, cancelable, platformTimeStamp, view, detail, sourceCapabilities)
+    : UIEvent(type, canBubble, cancelable, ComposedMode::Composed, platformTimeStamp, view, detail, sourceCapabilities)
     , m_modifiers(modifiers)
 {
 }