-
Notifications
You must be signed in to change notification settings - Fork 400
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(engine): patching events from lwc to be deterministic (#870)
* fix(engine): patching events from lwc to be deterministic * fix(engine): PR #870 feedback * fix(engine): new license headers
- Loading branch information
Showing
10 changed files
with
172 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# event-listener polyfill | ||
|
||
This polyfill is needed to support re-targeting for synthetic shadow dom. | ||
|
||
It will patch addEventListener and removeEventListener to make sure that whenever you are listening for an event at any level, that event is patched accordingly but only if the event is triggered from within a shadow root. If the event is not coming from a synthetic shadow, the event | ||
don't need to be patched. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/* | ||
* 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 | ||
*/ | ||
export default function detect(): boolean { | ||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* | ||
* 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 detect from './detect'; | ||
import apply from './polyfill'; | ||
|
||
if (detect()) { | ||
apply(); | ||
} |
104 changes: 104 additions & 0 deletions
104
packages/@lwc/engine/src/polyfills/event-listener/polyfill.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/* | ||
* 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 { | ||
windowRemoveEventListener as nativeWindowRemoveEventListener, | ||
windowAddEventListener as nativeWindowAddEventListener, | ||
} from '../../env/window'; | ||
import { | ||
removeEventListener as nativeRemoveEventListener, | ||
addEventListener as nativeAddEventListener, | ||
} from '../../env/element'; | ||
import { eventTargetGetter } from '../../env/dom'; | ||
import { DOCUMENT_POSITION_CONTAINED_BY, compareDocumentPosition } from '../../env/node'; | ||
import { getNodeOwnerKey } from '../../faux-shadow/node'; | ||
import { patchEvent } from '../../faux-shadow/events'; | ||
|
||
function doesEventNeedsPatch(e: Event): boolean { | ||
const originalTarget = eventTargetGetter.call(e); | ||
if (originalTarget instanceof Node) { | ||
if ((compareDocumentPosition.call(document, originalTarget) & DOCUMENT_POSITION_CONTAINED_BY) !== 0 && getNodeOwnerKey(originalTarget)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
function getEventListenerWrapper(fnOrObj): EventListener | null { | ||
let wrapperFn: EventListener | null = null; | ||
try { | ||
wrapperFn = fnOrObj.$$lwcEventWrapper$$; | ||
if (!wrapperFn) { | ||
wrapperFn = fnOrObj.$$lwcEventWrapper$$ = function(this: EventTarget, e: Event) { | ||
// we don't want to patch every event, only when the original target is coming | ||
// from inside a synthetic shadow | ||
if (doesEventNeedsPatch(e)) { | ||
patchEvent(e); | ||
} | ||
return fnOrObj.call(this, e); | ||
}; | ||
} | ||
} catch (e) { /** ignore */ } | ||
return wrapperFn; | ||
} | ||
|
||
function windowAddEventListener(this: EventTarget, type, fnOrObj, optionsOrCapture) { | ||
const handlerType = typeof fnOrObj; | ||
// bail if `fnOrObj` is not a function, not an object | ||
if (handlerType !== 'function' && handlerType !== 'object') { | ||
return; | ||
} | ||
// bail if `fnOrObj` is an object without a `handleEvent` method | ||
if (handlerType === 'object' && (!fnOrObj.handleEvent || typeof fnOrObj.handleEvent !== 'function')) { | ||
return; | ||
} | ||
const wrapperFn = getEventListenerWrapper(fnOrObj); | ||
nativeWindowAddEventListener.call(this, type, wrapperFn as EventListener, optionsOrCapture); | ||
} | ||
|
||
function windowRemoveEventListener(this: EventTarget, type, fnOrObj, optionsOrCapture) { | ||
const wrapperFn = getEventListenerWrapper(fnOrObj); | ||
nativeWindowRemoveEventListener.call(this, type, wrapperFn || fnOrObj, optionsOrCapture); | ||
} | ||
|
||
function addEventListener(this: EventTarget, type, fnOrObj, optionsOrCapture) { | ||
const handlerType = typeof fnOrObj; | ||
// bail if `fnOrObj` is not a function, not an object | ||
if (handlerType !== 'function' && handlerType !== 'object') { | ||
return; | ||
} | ||
// bail if `fnOrObj` is an object without a `handleEvent` method | ||
if (handlerType === 'object' && (!fnOrObj.handleEvent || typeof fnOrObj.handleEvent !== 'function')) { | ||
return; | ||
} | ||
const wrapperFn = getEventListenerWrapper(fnOrObj); | ||
nativeAddEventListener.call(this, type, wrapperFn as EventListener, optionsOrCapture); | ||
} | ||
|
||
function removeEventListener(this: EventTarget, type, fnOrObj, optionsOrCapture) { | ||
const wrapperFn = getEventListenerWrapper(fnOrObj); | ||
nativeRemoveEventListener.call(this, type, wrapperFn || fnOrObj, optionsOrCapture); | ||
} | ||
|
||
addEventListener.__lwcOriginal__ = nativeAddEventListener; | ||
removeEventListener.__lwcOriginal__ = nativeRemoveEventListener; | ||
windowAddEventListener.__lwcOriginal__ = nativeWindowAddEventListener; | ||
windowRemoveEventListener.__lwcOriginal__ = nativeWindowRemoveEventListener; | ||
|
||
function windowPatchListeners() { | ||
window.addEventListener = windowAddEventListener; | ||
window.removeEventListener = windowRemoveEventListener; | ||
} | ||
|
||
function nodePatchListeners() { | ||
Node.prototype.addEventListener = addEventListener; | ||
Node.prototype.removeEventListener = removeEventListener; | ||
} | ||
|
||
export default function apply() { | ||
windowPatchListeners(); | ||
nodePatchListeners(); | ||
} |