Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(engine): dom patching #688

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 0 additions & 23 deletions packages/lwc-engine/src/faux-shadow/__tests__/iframe.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,27 +111,4 @@ describe('wrapped iframe window', () => {
expect(contentWindow.blur).toHaveBeenCalled();
});
});

describe('unwrapping', () => {
it('should return original object', () => {
const myComponentTmpl = compileTemplate(`
<template>
<iframe src="https://salesforce.com"></iframe>
</template>
`);
class MyComponent extends LightningElement {
render() {
return myComponentTmpl;
}
}

const elm = createElement('x-foo', { is: MyComponent });
document.body.appendChild(elm);

const nativeIframeContentWindow = document.querySelector('iframe').contentWindow;
const wrappedIframe = getHostShadowRoot(elm).querySelector('iframe'); // will return monkey patched contentWindow
const contentWindowGetter = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'contentWindow').get;
expect(nativeIframeContentWindow === contentWindowGetter.call(wrappedIframe)).toBe(true);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ describe('#childNodes', () => {
expect(() => {
const child = getHostShadowRoot(elm).querySelector('div');
const childNodes = child.childNodes;
}).toLogWarning(`childNodes on [object HTMLDivElement] returns a live NodeList which is not stable. Use querySelectorAll instead.`);
}).toLogWarning(`Discouraged access to property 'childNodes' on 'Node': It returns a live NodeList and should not be relied upon. Instead, use 'querySelectorAll' which returns a static NodeList.`);
});

it('should return correct elements for custom elements when no children present', () => {
Expand Down
11 changes: 10 additions & 1 deletion packages/lwc-engine/src/faux-shadow/custom-element.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineProperties } from "../shared/language";
import { attachShadow } from "./shadow-root";
import { attachShadow, getShadowRoot } from "./shadow-root";
import { addCustomElementEventListener, removeCustomElementEventListener } from "./events";

function addEventListenerPatchedValue(this: EventTarget, type: string, listener: EventListener, options?: boolean | AddEventListenerOptions) {
Expand All @@ -14,6 +14,10 @@ function attachShadowGetter(this: HTMLElement, options: ShadowRootInit): ShadowR
return attachShadow(this, options);
}

function getShadowRootPatchedValue(this: HTMLElement) {
return getShadowRoot(this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should take into consideration the mode

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to match native.

}

const CustomElementPatchDescriptors: PropertyDescriptorMap = {
attachShadow: {
value: attachShadowGetter,
Expand All @@ -31,6 +35,11 @@ const CustomElementPatchDescriptors: PropertyDescriptorMap = {
configurable: true,
enumerable: true,
},
shadowRoot: {
value: getShadowRootPatchedValue,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a getter instead.

configurable: true,
enumerable: true,
}
};

export function patchCustomElement(elm: HTMLElement) {
Expand Down
22 changes: 3 additions & 19 deletions packages/lwc-engine/src/faux-shadow/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import {
getRootNode,
parentNodeGetter,
} from "./node";
import { ArraySlice, ArraySplice, ArrayIndexOf, create, ArrayPush, isUndefined, isFunction, getOwnPropertyDescriptor, defineProperties, isNull, toString, forEach, defineProperty, isFalse } from "../shared/language";
import { patchShadowDomTraversalMethods } from "./traverse";
import { ArraySlice, ArraySplice, ArrayIndexOf, create, ArrayPush, isUndefined, isFunction, getOwnPropertyDescriptor, defineProperties, toString, forEach, defineProperty, isFalse } from "../shared/language";
import { compareDocumentPosition, DOCUMENT_POSITION_CONTAINED_BY, getNodeOwnerKey, getNodeKey } from "./node";
import { getHost } from "./shadow-root";

Expand All @@ -33,18 +32,6 @@ const eventCurrentTargetGetter: (this: Event) => Element | null = getOwnProperty
const GET_ROOT_NODE_CONFIG_FALSE = { composed: false };

const EventPatchDescriptors: PropertyDescriptorMap = {
currentTarget: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice!

get(this: Event): EventTarget | null {
const currentTarget: EventTarget = eventCurrentTargetGetter.call(this);
if (isNull(currentTarget) || isUndefined(getNodeOwnerKey(currentTarget as Node))) {
// event is already beyond the boundaries of our controlled shadow roots
return currentTarget;
}
return patchShadowDomTraversalMethods(currentTarget as Element);
},
enumerable: true,
configurable: true,
},
target: {
get(this: Event): EventTarget {
const currentTarget: EventTarget = eventCurrentTargetGetter.call(this);
Expand All @@ -71,7 +58,7 @@ const EventPatchDescriptors: PropertyDescriptorMap = {
const eventContext = eventToContextMap.get(this);
// Executing event listener on component, target is always currentTarget
if (eventContext === EventListenerContext.CUSTOM_ELEMENT_LISTENER) {
return patchShadowDomTraversalMethods(currentTarget as Element);
return currentTarget as Element;
}
const currentTargetRootNode = getRootNode.call(currentTarget, GET_ROOT_NODE_CONFIG_FALSE); // x-child

Expand Down Expand Up @@ -160,10 +147,7 @@ const EventPatchDescriptors: PropertyDescriptorMap = {
* while the event is patched because the component is listening for it internally
* via this.addEventListener('click') in constructor or something similar
*/
if (isUndefined(getNodeOwnerKey(closestTarget as Node))) {
return closestTarget;
}
return patchShadowDomTraversalMethods(closestTarget as Element);
return closestTarget as Element;
pmdartus marked this conversation as resolved.
Show resolved Hide resolved
},
enumerable: true,
configurable: true,
Expand Down
4 changes: 0 additions & 4 deletions packages/lwc-engine/src/faux-shadow/faux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ export { patchEvent } from "./events";

export { patchCustomElement } from "./custom-element";

export {
unwrap as getRawNode,
} from "./traverse-membrane";

export {
lightDomQuerySelectorAll,
lightDomQuerySelector,
Expand Down
7 changes: 2 additions & 5 deletions packages/lwc-engine/src/faux-shadow/shadow-root.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import assert from "../shared/assert";
import { create, assign, isUndefined, getOwnPropertyDescriptor, ArrayReduce, isNull } from "../shared/language";
import { addShadowRootEventListener, removeShadowRootEventListener } from "./events";
import { shadowRootQuerySelector, shadowRootQuerySelectorAll, shadowRootChildNodes, isNodeOwnedBy, patchShadowDomTraversalMethods } from "./traverse";
import { shadowRootQuerySelector, shadowRootQuerySelectorAll, shadowRootChildNodes, isNodeOwnedBy } from "./traverse";
import { getInternalField, setInternalField, createFieldName } from "../shared/fields";
import { getInnerHTML } from "../3rdparty/polymer/inner-html";
import { getTextContent } from "../3rdparty/polymer/text-content";
import { compareDocumentPosition, DOCUMENT_POSITION_CONTAINED_BY } from "./node";
// it is ok to import from the polyfill since they always go hand-to-hand anyways.
import { ElementPrototypeAriaPropertyNames } from "../polyfills/aria-properties/polyfill";
import { unwrap } from "./traverse-membrane";
import { DocumentPrototypeActiveElement } from "./document";

let ArtificialShadowRootPrototype;
Expand Down Expand Up @@ -115,7 +114,7 @@ function activeElementGetter(this: ShadowRoot): Element | null {
// activeElement must be child of the host and owned by it
// TODO: what happen with delegatesFocus is true for a child component?
return (compareDocumentPosition.call(host, activeElement) & DOCUMENT_POSITION_CONTAINED_BY) !== 0 &&
isNodeOwnedBy(host, activeElement) ? patchShadowDomTraversalMethods(activeElement) : null;
isNodeOwnedBy(host, activeElement) ? activeElement : null;
}

function hostGetter(this: ShadowRoot): HTMLElement {
Expand Down Expand Up @@ -203,7 +202,6 @@ const ArtificialShadowRootDescriptors: PropertyDescriptorMap = {
compareDocumentPosition: {
value(this: ShadowRoot, otherNode: Node): number {
// this API might be called with proxies
otherNode = unwrap(otherNode);
const host = getHost(this);
if (this === otherNode) {
// it is the root itself
Expand All @@ -226,7 +224,6 @@ const ArtificialShadowRootDescriptors: PropertyDescriptorMap = {
contains: {
value(this: ShadowRoot, otherNode: Node): boolean {
// this API might be called with proxies
otherNode = unwrap(otherNode);
const host = getHost(this);
// must be child of the host and owned by it.
return (compareDocumentPosition.call(host, otherNode) & DOCUMENT_POSITION_CONTAINED_BY) !== 0 &&
Expand Down
93 changes: 0 additions & 93 deletions packages/lwc-engine/src/faux-shadow/traverse-membrane.ts

This file was deleted.

Loading