-
Notifications
You must be signed in to change notification settings - Fork 382
/
polyfill.ts
152 lines (135 loc) · 5.89 KB
/
polyfill.ts
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
/*
* Copyright (c) 2018, salesforce.com, inc.
* All rights reserved.
* SPDX-License-Identifier: MIT
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
import { defineProperties, isNull, isUndefined } from '@lwc/shared';
import { pathComposer } from '../../3rdparty/polymer/path-composer';
import { retarget } from '../../3rdparty/polymer/retarget';
import {
composedPath as originalComposedPath,
eventTargetGetter,
eventCurrentTargetGetter,
} from '../../env/dom';
import { Node } from '../../env/node';
import {
eventToShadowRootMap,
getShadowRoot,
hasInternalSlot,
isSyntheticShadowHost,
} from '../../faux-shadow/shadow-root';
import { EventListenerContext, eventToContextMap } from '../../faux-shadow/events';
import { getNodeOwnerKey } from '../../shared/node-ownership';
import { getOwnerDocument } from '../../shared/utils';
function patchedCurrentTargetGetter(this: Event): EventTarget | null {
const currentTarget = eventCurrentTargetGetter.call(this);
if (isNull(currentTarget)) {
return null;
}
if (eventToContextMap.get(this) === EventListenerContext.SHADOW_ROOT_LISTENER) {
return getShadowRoot(currentTarget as Element);
}
return currentTarget;
}
function patchedTargetGetter(this: Event): EventTarget | null {
const originalTarget = eventTargetGetter.call(this);
if (!(originalTarget instanceof Node)) {
return originalTarget;
}
const doc = getOwnerDocument(originalTarget);
const composedPath = pathComposer(originalTarget, this.composed);
const originalCurrentTarget = eventCurrentTargetGetter.call(this);
// Handle cases where the currentTarget is null (for async events), and when an event has been
// added to Window
if (!(originalCurrentTarget instanceof Node)) {
// TODO [#1511]: Special escape hatch to support legacy behavior. Should be fixed.
// If the event's target is being accessed async and originalTarget is not a keyed element, do not retarget
if (isNull(originalCurrentTarget) && isUndefined(getNodeOwnerKey(originalTarget))) {
return originalTarget;
}
return retarget(doc, composedPath);
} else if (originalCurrentTarget === doc || originalCurrentTarget === doc.body) {
// TODO [#1530]: If currentTarget is document or document.body (Third party libraries that have global event listeners)
// and the originalTarget is not a keyed element, do not retarget
if (isUndefined(getNodeOwnerKey(originalTarget))) {
return originalTarget;
}
return retarget(doc, composedPath);
}
let actualCurrentTarget = originalCurrentTarget;
let actualPath = composedPath;
// Address the possibility that `currentTarget` is a shadow root
if (isSyntheticShadowHost(originalCurrentTarget)) {
const context = eventToContextMap.get(this);
if (context === EventListenerContext.SHADOW_ROOT_LISTENER) {
actualCurrentTarget = getShadowRoot(originalCurrentTarget);
}
}
// Address the possibility that `target` is a shadow root
if (isSyntheticShadowHost(originalTarget) && eventToShadowRootMap.has(this)) {
actualPath = pathComposer(getShadowRoot(originalTarget), this.composed);
}
return retarget(actualCurrentTarget, actualPath);
}
function patchedComposedPathValue(this: Event): EventTarget[] {
const originalTarget = eventTargetGetter.call(this);
// Account for events with targets that are not instances of Node (e.g., when a readystatechange
// handler is listening on an instance of XMLHttpRequest).
if (!(originalTarget instanceof Node)) {
return [];
}
// If the original target is inside a native shadow root, then just call the native
// composePath() method. The event is already retargeted and this causes our composedPath()
// polyfill to compute the wrong value. This is only an issue when you have a native web
// component inside an LWC component (see test in same commit) but this scenario is unlikely
// because we don't yet support that. Workaround specifically for W-9846457. Mixed mode solution
// will likely be more involved.
const hasShadowRoot = Boolean((originalTarget as any).shadowRoot);
const hasSyntheticShadowRootAttached = hasInternalSlot(originalTarget);
if (hasShadowRoot && !hasSyntheticShadowRootAttached) {
return originalComposedPath.call(this);
}
const originalCurrentTarget = eventCurrentTargetGetter.call(this);
// If the event has completed propagation, the composedPath should be an empty array.
if (isNull(originalCurrentTarget)) {
return [];
}
// Address the possibility that `target` is a shadow root
let actualTarget = originalTarget;
if (isSyntheticShadowHost(originalTarget) && eventToShadowRootMap.has(this)) {
actualTarget = getShadowRoot(originalTarget);
}
return pathComposer(actualTarget, this.composed);
}
defineProperties(Event.prototype, {
target: {
get: patchedTargetGetter,
enumerable: true,
configurable: true,
},
currentTarget: {
get: patchedCurrentTargetGetter,
enumerable: true,
configurable: true,
},
composedPath: {
value: patchedComposedPathValue,
writable: true,
enumerable: true,
configurable: true,
},
// Non-standard but widely supported for backwards-compatibility
srcElement: {
get: patchedTargetGetter,
enumerable: true,
configurable: true,
},
// Non-standard but implemented in Chrome and continues to exist for backwards-compatibility
// https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/core/dom/events/event.idl;l=58?q=event.idl&ss=chromium
path: {
get: patchedComposedPathValue,
enumerable: true,
configurable: true,
},
});