Skip to content

Commit

Permalink
Make events propagate through shadow DOMs.
Browse files Browse the repository at this point in the history
  • Loading branch information
jimfb committed Jul 2, 2015
1 parent d268a9f commit 4a465fb
Show file tree
Hide file tree
Showing 27 changed files with 167 additions and 68 deletions.
51 changes: 50 additions & 1 deletion src/renderers/dom/client/ReactEventListener.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ var assign = require('Object.assign');
var getEventTarget = require('getEventTarget');
var getUnboundedScrollPosition = require('getUnboundedScrollPosition');

var DOCUMENT_FRAGMENT_NODE_TYPE = 11;

/**
* Finds the parent React component of `node`.
*
Expand Down Expand Up @@ -60,6 +62,17 @@ PooledClass.addPoolingTo(
);

function handleTopLevelImpl(bookKeeping) {
if (bookKeeping.nativeEvent.path) {
// New browsers have a path attribute on native events
handleTopLevelWithPath(bookKeeping);
} else {
// Legacy browsers don't have a path attribute on native events
handleTopLevelWithoutPath(bookKeeping);
}
}

// Legacy browsers don't have a path attribute on native events
function handleTopLevelWithoutPath(bookKeeping) {
var topLevelTarget = ReactMount.getFirstReactDOM(
getEventTarget(bookKeeping.nativeEvent)
) || window;
Expand All @@ -81,11 +94,47 @@ function handleTopLevelImpl(bookKeeping) {
bookKeeping.topLevelType,
topLevelTarget,
topLevelTargetID,
bookKeeping.nativeEvent
bookKeeping.nativeEvent,
getEventTarget(bookKeeping.nativeEvent)
);
}
}

// New browsers have a path attribute on native events
function handleTopLevelWithPath(bookKeeping) {
var path = bookKeeping.nativeEvent.path;
var currentNativeTarget = path[0];
for (var i = 0; i < path.length; i++) {
var currentPathElement = path[i];
var currentPathElemenId = ReactMount.getID(currentPathElement);
if (currentPathElement.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE) {
currentNativeTarget = path[i + 1];
}
if (ReactMount.isRenderedByReact(currentPathElement)) {
var newRootId = ReactInstanceHandles.getReactRootIDFromNodeID(
currentPathElemenId
);
bookKeeping.ancestors.push(currentPathElement);

var topLevelTargetID = ReactMount.getID(currentPathElement) || '';
ReactEventListener._handleTopLevel(
bookKeeping.topLevelType,
currentPathElement,
topLevelTargetID,
bookKeeping.nativeEvent,
currentNativeTarget
);

// Jump to the root of this React render tree
while (currentPathElemenId !== newRootId) {
i++;
currentPathElement = path[i];
currentPathElemenId = ReactMount.getID(currentPathElement);
}
}
}
}

function scrollValueMonitor(cb) {
var scrollPosition = getUnboundedScrollPosition(window);
cb(scrollPosition);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ var EnterLeaveEventPlugin = {
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent) {
nativeEvent,
nativeEventTarget) {
if (topLevelType === topLevelTypes.topMouseOver &&
(nativeEvent.relatedTarget || nativeEvent.fromElement)) {
return null;
Expand Down Expand Up @@ -110,7 +111,8 @@ var EnterLeaveEventPlugin = {
var leave = SyntheticMouseEvent.getPooled(
eventTypes.mouseLeave,
fromID,
nativeEvent
nativeEvent,
nativeEventTarget
);
leave.type = 'mouseleave';
leave.target = from;
Expand All @@ -119,7 +121,8 @@ var EnterLeaveEventPlugin = {
var enter = SyntheticMouseEvent.getPooled(
eventTypes.mouseEnter,
toID,
nativeEvent
nativeEvent,
nativeEventTarget
);
enter.type = 'mouseenter';
enter.target = to;
Expand Down
13 changes: 7 additions & 6 deletions src/renderers/dom/client/eventPlugins/SelectEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ function getSelection(node) {
* @param {object} nativeEvent
* @return {?SyntheticEvent}
*/
function constructSelectEvent(nativeEvent) {
function constructSelectEvent(nativeEvent, nativeEventTarget) {
// Ensure we have the right element, and that the user is not dragging a
// selection (this matches native `select` event behavior). In HTML5, select
// fires only on input and textarea thus if there's no focused element we
Expand All @@ -111,7 +111,8 @@ function constructSelectEvent(nativeEvent) {
var syntheticEvent = SyntheticEvent.getPooled(
eventTypes.select,
activeElementID,
nativeEvent
nativeEvent,
nativeEventTarget
);

syntheticEvent.type = 'select';
Expand Down Expand Up @@ -155,8 +156,8 @@ var SelectEventPlugin = {
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent) {

nativeEvent,
nativeEventTarget) {
if (!hasListener) {
return null;
}
Expand Down Expand Up @@ -185,7 +186,7 @@ var SelectEventPlugin = {
case topLevelTypes.topContextMenu:
case topLevelTypes.topMouseUp:
mouseDown = false;
return constructSelectEvent(nativeEvent);
return constructSelectEvent(nativeEvent, nativeEventTarget);

// Chrome and IE fire non-standard event when selection is changed (and
// sometimes when it hasn't).
Expand All @@ -196,7 +197,7 @@ var SelectEventPlugin = {
case topLevelTypes.topSelectionChange:
case topLevelTypes.topKeyDown:
case topLevelTypes.topKeyUp:
return constructSelectEvent(nativeEvent);
return constructSelectEvent(nativeEvent, nativeEventTarget);
}

return null;
Expand Down
6 changes: 4 additions & 2 deletions src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,8 @@ var SimpleEventPlugin = {
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent) {
nativeEvent,
nativeEventTarget) {
var dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];
if (!dispatchConfig) {
return null;
Expand Down Expand Up @@ -418,7 +419,8 @@ var SimpleEventPlugin = {
var event = EventConstructor.getPooled(
dispatchConfig,
topLevelTargetID,
nativeEvent
nativeEvent,
nativeEventTarget
);
EventPropagators.accumulateTwoPhaseDispatches(event);
return event;
Expand Down
6 changes: 4 additions & 2 deletions src/renderers/dom/client/eventPlugins/TapEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ var TapEventPlugin = {
topLevelType,
topLevelTarget,
topLevelTargetID,
nativeEvent) {
nativeEvent,
nativeEventTarget) {
if (!isStartish(topLevelType) && !isEndish(topLevelType)) {
return null;
}
Expand All @@ -122,7 +123,8 @@ var TapEventPlugin = {
event = SyntheticUIEvent.getPooled(
eventTypes.touchTap,
topLevelTargetID,
nativeEvent
nativeEvent,
nativeEventTarget
);
}
if (isStartish(topLevelType)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ describe('EnterLeaveEventPlugin', function() {
topLevelTypes.topMouseOver,
div,
ReactMount.getID(div),
{target: div}
{target: div},
div
);
expect(extracted.length).toBe(2);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ describe('SelectEventPlugin', function() {
topLevelEvent,
node,
ReactMount.getID(node),
{target: node}
{target: node},
node
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ var ClipboardEventInterface = {
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticClipboardEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
function SyntheticClipboardEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticClipboardEvent, ClipboardEventInterface);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ var CompositionEventInterface = {
function SyntheticCompositionEvent(
dispatchConfig,
dispatchMarker,
nativeEvent) {
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
nativeEvent,
nativeEventTarget) {
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ var DragEventInterface = {
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticDragEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
function SyntheticDragEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticMouseEvent.augmentClass(SyntheticDragEvent, DragEventInterface);
Expand Down
11 changes: 6 additions & 5 deletions src/renderers/dom/client/syntheticEvents/SyntheticEvent.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ var PooledClass = require('PooledClass');

var assign = require('Object.assign');
var emptyFunction = require('emptyFunction');
var getEventTarget = require('getEventTarget');

/**
* @interface Event
* @see http://www.w3.org/TR/DOM-Level-3-Events/
*/
var EventInterface = {
path: null,
type: null,
target: getEventTarget,
// currentTarget is set when dispatching; no use in copying it here
currentTarget: emptyFunction.thatReturnsNull,
eventPhase: null,
Expand Down Expand Up @@ -54,10 +53,12 @@ var EventInterface = {
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
*/
function SyntheticEvent(dispatchConfig, dispatchMarker, nativeEvent) {
function SyntheticEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
this.dispatchConfig = dispatchConfig;
this.dispatchMarker = dispatchMarker;
this.nativeEvent = nativeEvent;
this.target = nativeEventTarget;
this.currentTarget = nativeEventTarget;

var Interface = this.constructor.Interface;
for (var propName in Interface) {
Expand Down Expand Up @@ -156,9 +157,9 @@ SyntheticEvent.augmentClass = function(Class, Interface) {
Class.Interface = assign({}, Super.Interface, Interface);
Class.augmentClass = Super.augmentClass;

PooledClass.addPoolingTo(Class, PooledClass.threeArgumentPooler);
PooledClass.addPoolingTo(Class, PooledClass.fourArgumentPooler);
};

PooledClass.addPoolingTo(SyntheticEvent, PooledClass.threeArgumentPooler);
PooledClass.addPoolingTo(SyntheticEvent, PooledClass.fourArgumentPooler);

module.exports = SyntheticEvent;
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ var FocusEventInterface = {
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticFocusEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
function SyntheticFocusEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticUIEvent.augmentClass(SyntheticFocusEvent, FocusEventInterface);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ var InputEventInterface = {
function SyntheticInputEvent(
dispatchConfig,
dispatchMarker,
nativeEvent) {
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
nativeEvent,
nativeEventTarget) {
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ var KeyboardEventInterface = {
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticKeyboardEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
function SyntheticKeyboardEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticUIEvent.augmentClass(SyntheticKeyboardEvent, KeyboardEventInterface);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ var MouseEventInterface = {
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticMouseEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
function SyntheticMouseEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticUIEvent.augmentClass(SyntheticMouseEvent, MouseEventInterface);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ var TouchEventInterface = {
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticUIEvent}
*/
function SyntheticTouchEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
function SyntheticTouchEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticUIEvent.augmentClass(SyntheticTouchEvent, TouchEventInterface);
Expand Down
4 changes: 2 additions & 2 deletions src/renderers/dom/client/syntheticEvents/SyntheticUIEvent.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ var UIEventInterface = {
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticEvent}
*/
function SyntheticUIEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
function SyntheticUIEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(SyntheticUIEvent, UIEventInterface);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ var WheelEventInterface = {
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticMouseEvent}
*/
function SyntheticWheelEvent(dispatchConfig, dispatchMarker, nativeEvent) {
SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
function SyntheticWheelEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) {
SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticMouseEvent.augmentClass(SyntheticWheelEvent, WheelEventInterface);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ describe('SyntheticEvent', function() {
SyntheticEvent = require('SyntheticEvent');

createEvent = function(nativeEvent) {
return SyntheticEvent.getPooled({}, '', nativeEvent);
var target = require('getEventTarget')(nativeEvent);
return SyntheticEvent.getPooled({}, '', nativeEvent, target);
};
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ describe('SyntheticWheelEvent', function() {
SyntheticWheelEvent = require('SyntheticWheelEvent');

createEvent = function(nativeEvent) {
return SyntheticWheelEvent.getPooled({}, '', nativeEvent);
var target = require('getEventTarget')(nativeEvent);
return SyntheticWheelEvent.getPooled({}, '', nativeEvent, target);
};
});

Expand Down
Loading

0 comments on commit 4a465fb

Please sign in to comment.