Skip to content

Commit

Permalink
refactor(synthetic-shadow): helper methods for differentiating synthe…
Browse files Browse the repository at this point in the history
…tic and native hosts and roots (#2525)
  • Loading branch information
ekashida authored Oct 12, 2021
1 parent 9560942 commit a784e8f
Show file tree
Hide file tree
Showing 18 changed files with 79 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { isNull } from '@lwc/shared';
import { getOwnerDocument } from '../../shared/utils';
import { Node } from '../../env/node';
import { isInstanceOfNativeShadowRoot } from '../../env/shadow-root';
import { SyntheticShadowRoot } from '../../faux-shadow/shadow-root';
import { isSyntheticShadowRoot } from '../../faux-shadow/shadow-root';

export function pathComposer(startNode: EventTarget, composed: boolean): EventTarget[] {
const composedPath: EventTarget[] = [];
Expand All @@ -45,7 +45,7 @@ export function pathComposer(startNode: EventTarget, composed: boolean): EventTa
current = current.parentNode;
}
} else if (
(current instanceof SyntheticShadowRoot || isInstanceOfNativeShadowRoot(current)) &&
(isSyntheticShadowRoot(current) || isInstanceOfNativeShadowRoot(current)) &&
(composed || current !== startRoot)
) {
current = (current as ShadowRoot).host;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
import { isNull, isUndefined } from '@lwc/shared';
import { pathComposer } from './path-composer';
import { SyntheticShadowRoot } from './../../faux-shadow/shadow-root';
import { isSyntheticShadowRoot } from './../../faux-shadow/shadow-root';

/**
@license
Expand All @@ -32,7 +32,7 @@ export function retarget(refNode: EventTarget | null, path: EventTarget[]): Even
rootIdx = refNodePath.indexOf(root);
lastRoot = root;
}
if (!(root instanceof SyntheticShadowRoot) || (!isUndefined(rootIdx) && rootIdx > -1)) {
if (!isSyntheticShadowRoot(root) || (!isUndefined(rootIdx) && rootIdx > -1)) {
return ancestor;
}
}
Expand Down
8 changes: 4 additions & 4 deletions packages/@lwc/synthetic-shadow/src/faux-shadow/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
KEY__SYNTHETIC_MODE,
} from '@lwc/shared';
import featureFlags from '@lwc/features';
import { attachShadow, getShadowRoot, isHostElement } from './shadow-root';
import { attachShadow, getShadowRoot, isSyntheticShadowHost } from './shadow-root';
import {
getNodeOwner,
getAllMatches,
Expand Down Expand Up @@ -84,7 +84,7 @@ function attachShadowPatched(this: Element, options: ShadowRootInit): ShadowRoot
}

function shadowRootGetterPatched(this: Element): ShadowRoot | null {
if (isHostElement(this)) {
if (isSyntheticShadowHost(this)) {
const shadow = getShadowRoot(this);
if (shadow.mode === 'open') {
return shadow;
Expand Down Expand Up @@ -252,7 +252,7 @@ function querySelectorPatched(this: Element /*, selector: string*/): Element | n
const nodeList = arrayFromCollection(
elementQuerySelectorAll.apply(this, ArraySlice.call(arguments) as [string])
);
if (isHostElement(this)) {
if (isSyntheticShadowHost(this)) {
// element with shadowRoot attached
const owner = getNodeOwner(this);
if (isNull(owner)) {
Expand Down Expand Up @@ -310,7 +310,7 @@ function getFilteredArrayOfNodes<T extends Node>(
shadowDomSemantic: ShadowDomSemantic
): T[] {
let filtered: T[];
if (isHostElement(context)) {
if (isSyntheticShadowHost(context)) {
// element with shadowRoot attached
const owner = getNodeOwner(context);
if (isNull(owner)) {
Expand Down
4 changes: 2 additions & 2 deletions packages/@lwc/synthetic-shadow/src/faux-shadow/focus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
toString,
} from '@lwc/shared';

import { isDelegatingFocus, isHostElement } from './shadow-root';
import { isDelegatingFocus, isSyntheticShadowHost } from './shadow-root';
import { windowAddEventListener, windowRemoveEventListener } from '../env/window';
import {
DocumentPrototypeActiveElement,
Expand Down Expand Up @@ -97,7 +97,7 @@ function isVisible(element: HTMLElement): boolean {
// Determines if a particular element is tabbable, as opposed to simply focusable

function isTabbable(element: HTMLElement): boolean {
if (isHostElement(element) && isDelegatingFocus(element)) {
if (isSyntheticShadowHost(element) && isDelegatingFocus(element)) {
return false;
}
return matches.call(element, FocusableSelector) && isVisible(element);
Expand Down
14 changes: 7 additions & 7 deletions packages/@lwc/synthetic-shadow/src/faux-shadow/html-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
import featureFlags from '@lwc/features';
import { isNull, isFalse, defineProperties, defineProperty } from '@lwc/shared';
import { isDelegatingFocus, isHostElement } from './shadow-root';
import { isDelegatingFocus, isSyntheticShadowHost } from './shadow-root';
import {
hasAttribute,
innerTextGetter,
Expand Down Expand Up @@ -136,7 +136,7 @@ function focusPatched(this: HTMLElement) {
disableKeyboardFocusNavigationRoutines();
}

if (isHostElement(this) && isDelegatingFocus(this)) {
if (isSyntheticShadowHost(this) && isDelegatingFocus(this)) {
hostElementFocus.call(this);
return;
}
Expand All @@ -156,13 +156,13 @@ function focusPatched(this: HTMLElement) {
defineProperties(HTMLElement.prototype, {
tabIndex: {
get(this: HTMLElement): number {
if (isHostElement(this)) {
if (isSyntheticShadowHost(this)) {
return tabIndexGetterPatched.call(this);
}
return tabIndexGetter.call(this);
},
set(this: HTMLElement, v: any) {
if (isHostElement(this)) {
if (isSyntheticShadowHost(this)) {
return tabIndexSetterPatched.call(this, v);
}
return tabIndexSetter.call(this, v);
Expand All @@ -172,7 +172,7 @@ defineProperties(HTMLElement.prototype, {
},
blur: {
value(this: HTMLElement) {
if (isHostElement(this)) {
if (isSyntheticShadowHost(this)) {
return blurPatched.call(this);
}
blur.call(this);
Expand Down Expand Up @@ -202,7 +202,7 @@ if (innerTextGetter !== null && innerTextSetter !== null) {
}

if (!featureFlags.ENABLE_ELEMENT_PATCH) {
if (isNodeShadowed(this) || isHostElement(this)) {
if (isNodeShadowed(this) || isSyntheticShadowHost(this)) {
return getInnerText(this);
}

Expand Down Expand Up @@ -235,7 +235,7 @@ if (outerTextGetter !== null && outerTextSetter !== null) {
}

if (!featureFlags.ENABLE_ELEMENT_PATCH) {
if (isNodeShadowed(this) || isHostElement(this)) {
if (isNodeShadowed(this) || isSyntheticShadowHost(this)) {
return getInnerText(this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
import { ArrayFilter, ArraySlice, isNull, isUndefined } from '@lwc/shared';
import { isHostElement } from './shadow-root';
import { isSyntheticShadowHost } from './shadow-root';
import { getAllMatches, getNodeOwner, getAllSlottedMatches } from './traverse';
import { getNodeKey, getNodeNearestOwnerKey, getNodeOwnerKey } from '../shared/node-ownership';
import { isGlobalPatchingSkipped } from '../shared/utils';
Expand All @@ -24,7 +24,7 @@ export function getNonPatchedFilteredArrayOfNodes<T extends Node>(

// a node inside a shadow.
if (!isUndefined(ownerKey)) {
if (isHostElement(context)) {
if (isSyntheticShadowHost(context)) {
// element with shadowRoot attached
const owner = getNodeOwner(context);
if (isNull(owner)) {
Expand Down
14 changes: 9 additions & 5 deletions packages/@lwc/synthetic-shadow/src/faux-shadow/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ import {
isSyntheticSlotElement,
} from './traverse';
import { getTextContent } from '../3rdparty/polymer/text-content';
import { getShadowRoot, isHostElement, getIE11FakeShadowRootPlaceholder } from './shadow-root';
import {
getShadowRoot,
getIE11FakeShadowRootPlaceholder,
isSyntheticShadowHost,
} from './shadow-root';
import {
getNodeNearestOwnerKey,
getNodeOwnerKey,
Expand All @@ -61,7 +65,7 @@ import { isGlobalPatchingSkipped } from '../shared/utils';
* because we don't want to patch the children getters for those elements.
*/
export function hasMountedChildren(node: Node): boolean {
return isSyntheticSlotElement(node) || isHostElement(node);
return isSyntheticSlotElement(node) || isSyntheticShadowHost(node);
}

function getShadowParent(node: Node, value: ParentNode & Node): (Node & ParentNode) | null {
Expand Down Expand Up @@ -173,7 +177,7 @@ function cloneNodePatched(this: Node, deep?: boolean): Node {
* This method only applies to elements with a shadow or slots
*/
function childNodesGetterPatched(this: Node): NodeListOf<Node> {
if (isHostElement(this)) {
if (isSyntheticShadowHost(this)) {
const owner = getNodeOwner(this);
const childNodes = isNull(owner)
? getFilteredChildNodes(this)
Expand Down Expand Up @@ -391,7 +395,7 @@ defineProperties(Node.prototype, {
return false;
}

if (isNodeShadowed(this) || isHostElement(this)) {
if (isNodeShadowed(this) || isSyntheticShadowHost(this)) {
return containsPatched.call(this, otherNode);
}

Expand All @@ -411,7 +415,7 @@ defineProperties(Node.prototype, {
cloneNode: {
value(this: Node, deep?: boolean): Node {
if (!featureFlags.ENABLE_NODE_PATCH) {
if (isNodeShadowed(this) || isHostElement(this)) {
if (isNodeShadowed(this) || isSyntheticShadowHost(this)) {
return cloneNodePatched.call(this, deep);
}

Expand Down
6 changes: 3 additions & 3 deletions packages/@lwc/synthetic-shadow/src/faux-shadow/portal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import { isUndefined, forEach, defineProperty, isTrue } from '@lwc/shared';
import { childNodesGetter, compareDocumentPosition, Node } from '../env/node';
import { MutationObserver, MutationObserverObserve } from '../env/mutation-observer';
import {
getShadowRootResolver,
isSyntheticShadowHost,
setShadowRootResolver,
ShadowRootResolver,
getShadowRootResolver,
isHostElement,
} from './shadow-root';
import { setShadowToken, getShadowToken } from './shadow-token';

Expand Down Expand Up @@ -39,7 +39,7 @@ function adoptChildNode(node: Node, fn: ShadowRootResolver, shadowToken: string
if (node instanceof Element) {
setShadowToken(node, shadowToken);

if (isHostElement(node)) {
if (isSyntheticShadowHost(node)) {
// Root LWC elements can't get content slotted into them, therefore we don't observe their children.
return;
}
Expand Down
16 changes: 11 additions & 5 deletions packages/@lwc/synthetic-shadow/src/faux-shadow/shadow-root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ interface ShadowRootRecord {
}

export function hasInternalSlot(root: unknown): boolean {
return Boolean(InternalSlot.get(root));
return InternalSlot.has(root);
}

function getInternalSlot(root: SyntheticShadowRootInterface | Element): ShadowRootRecord {
Expand Down Expand Up @@ -123,8 +123,14 @@ export function getShadowRoot(elm: Element): SyntheticShadowRootInterface {

// Intentionally adding `Node` here in addition to `Element` since this check is harmless for nodes
// and we can avoid having to cast the type before calling this method in a few places.
export function isHostElement(node: unknown): node is HTMLElement {
return !isUndefined(InternalSlot.get(node));
export function isSyntheticShadowHost(node: unknown): node is HTMLElement {
const shadowRootRecord = InternalSlot.get(node);
return !isUndefined(shadowRootRecord) && node === shadowRootRecord.host;
}

export function isSyntheticShadowRoot(node: unknown): node is SyntheticShadowRootInterface {
const shadowRootRecord = InternalSlot.get(node);
return !isUndefined(shadowRootRecord) && node === shadowRootRecord.shadowRoot;
}

// Return true if any descendant is a host element
Expand All @@ -134,7 +140,7 @@ export function containsHost(node: Node) {
const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT, null, false);
let descendant;
while (!isNull((descendant = walker.nextNode()))) {
if (isHostElement(descendant)) {
if (isSyntheticShadowHost(descendant)) {
return true;
}
}
Expand All @@ -144,7 +150,7 @@ export function containsHost(node: Node) {
let uid = 0;

export function attachShadow(elm: Element, options: ShadowRootInit): SyntheticShadowRootInterface {
if (!isUndefined(InternalSlot.get(elm))) {
if (InternalSlot.has(elm)) {
throw new Error(
`Failed to execute 'attachShadow' on 'Element': Shadow root cannot be created on a host which already hosts a shadow tree.`
);
Expand Down
10 changes: 5 additions & 5 deletions packages/@lwc/synthetic-shadow/src/faux-shadow/traverse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
import { ArrayReduce, ArrayPush, assert, isNull, isUndefined, ArrayFilter } from '@lwc/shared';
import {
getHost,
SyntheticShadowRootInterface,
isHostElement,
getShadowRootResolver,
getShadowRoot,
getShadowRootResolver,
isSyntheticShadowHost,
SyntheticShadowRootInterface,
} from './shadow-root';
import { querySelectorAll } from '../env/element';
import {
Expand Down Expand Up @@ -221,12 +221,12 @@ export function shadowRootQuerySelectorAll(
}

export function getFilteredChildNodes(node: Node): Element[] {
if (!isHostElement(node) && !isSlotElement(node)) {
if (!isSyntheticShadowHost(node) && !isSlotElement(node)) {
// regular element - fast path
const children = childNodesGetter.call(node);
return arrayFromCollection(children);
}
if (isHostElement(node)) {
if (isSyntheticShadowHost(node)) {
// we need to get only the nodes that were slotted
const slots = arrayFromCollection(querySelectorAll.call(node, 'slot'));
const resolver = getShadowRootResolver(getShadowRoot(node));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
addCustomElementEventListener,
removeCustomElementEventListener,
} from '../../faux-shadow/events';
import { isHostElement } from '../../faux-shadow/shadow-root';
import { isSyntheticShadowHost } from '../../faux-shadow/shadow-root';
import { getEventListenerWrapper } from '../../shared/event-target';

function patchedAddEventListener(
Expand All @@ -23,7 +23,7 @@ function patchedAddEventListener(
listener: EventListenerOrEventListenerObject,
optionsOrCapture?: boolean | AddEventListenerOptions
) {
if (isHostElement(this)) {
if (isSyntheticShadowHost(this)) {
// Typescript does not like it when you treat the `arguments` object as an array
// @ts-ignore type-mismatch
return addCustomElementEventListener.apply(this, arguments);
Expand Down Expand Up @@ -53,7 +53,7 @@ function patchedRemoveEventListener(
_listener: EventListenerOrEventListenerObject,
_optionsOrCapture?: boolean | EventListenerOptions
) {
if (isHostElement(this)) {
if (isSyntheticShadowHost(this)) {
// Typescript does not like it when you treat the `arguments` object as an array
// @ts-ignore type-mismatch
return removeCustomElementEventListener.apply(this, arguments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
eventToShadowRootMap,
getShadowRoot,
hasInternalSlot,
isHostElement,
isSyntheticShadowHost,
} from '../../faux-shadow/shadow-root';
import { EventListenerContext, eventToContextMap } from '../../faux-shadow/events';
import { getNodeOwnerKey } from '../../shared/node-ownership';
Expand Down Expand Up @@ -67,15 +67,15 @@ function patchedTargetGetter(this: Event): EventTarget | null {
let actualPath = composedPath;

// Address the possibility that `currentTarget` is a shadow root
if (isHostElement(originalCurrentTarget)) {
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 (isHostElement(originalTarget) && eventToShadowRootMap.has(this)) {
if (isSyntheticShadowHost(originalTarget) && eventToShadowRootMap.has(this)) {
actualPath = pathComposer(getShadowRoot(originalTarget), this.composed);
}

Expand Down Expand Up @@ -112,7 +112,7 @@ function patchedComposedPathValue(this: Event): EventTarget[] {

// Address the possibility that `target` is a shadow root
let actualTarget = originalTarget;
if (isHostElement(originalTarget) && eventToShadowRootMap.has(this)) {
if (isSyntheticShadowHost(originalTarget) && eventToShadowRootMap.has(this)) {
actualTarget = getShadowRoot(originalTarget);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
isNull,
isUndefined,
} from '@lwc/shared';
import { SyntheticShadowRoot } from '../../faux-shadow/shadow-root';
import { isSyntheticShadowRoot } from '../../faux-shadow/shadow-root';
import { getNodeKey, getNodeNearestOwnerKey } from '../../shared/node-ownership';

const OriginalMutationObserver = MutationObserver;
Expand Down Expand Up @@ -255,8 +255,8 @@ function patchedObserve(
ArrayPush.call(targetObservers, this);
} // else There is more bookkeeping to do here https://dom.spec.whatwg.org/#dom-mutationobserver-observe Step #7

// If the target is a SyntheticShadowRoot, observe the host since the shadowRoot is an empty documentFragment
if (target instanceof SyntheticShadowRoot) {
// SyntheticShadowRoot instances are not actually a part of the DOM so observe the host instead.
if (isSyntheticShadowRoot(target)) {
target = (target as ShadowRoot).host;
}

Expand Down
Loading

0 comments on commit a784e8f

Please sign in to comment.