Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
90 changes: 79 additions & 11 deletions packages/react-dom-bindings/src/client/ReactDOMComponentTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import {getParentHydrationBoundary} from './ReactFiberConfigDOM';

import {enableScopeAPI} from 'shared/ReactFeatureFlags';

import {enableInternalInstanceMap} from 'shared/ReactFeatureFlags';

const randomKey = Math.random().toString(36).slice(2);
const internalInstanceKey = '__reactFiber$' + randomKey;
const internalPropsKey = '__reactProps$' + randomKey;
Expand All @@ -49,7 +51,32 @@ const internalRootNodeResourcesKey = '__reactResources$' + randomKey;
const internalHoistableMarker = '__reactMarker$' + randomKey;
const internalScrollTimer = '__reactScroll$' + randomKey;

type InstanceUnion =
| Instance
| TextInstance
| SuspenseInstance
| ActivityInstance
| ReactScopeInstance
| Container;

const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
const internalInstanceMap:
| WeakMap<InstanceUnion, Fiber>
| Map<InstanceUnion, Fiber> = new PossiblyWeakMap();
const internalPropsMap:
| WeakMap<InstanceUnion, Props>
| Map<InstanceUnion, Props> = new PossiblyWeakMap();

export function detachDeletedInstance(node: Instance): void {
if (enableInternalInstanceMap) {
internalInstanceMap.delete(node);
internalPropsMap.delete(node);
delete (node: any)[internalEventHandlersKey];
delete (node: any)[internalEventHandlerListenersKey];
delete (node: any)[internalEventHandlesSetKey];
delete (node: any)[internalRootNodeResourcesKey];
return;
}
// TODO: This function is only called on host components. I don't think all of
// these fields are relevant.
delete (node: any)[internalInstanceKey];
Expand All @@ -68,6 +95,10 @@ export function precacheFiberNode(
| ActivityInstance
| ReactScopeInstance,
): void {
if (enableInternalInstanceMap) {
internalInstanceMap.set(node, hostInst);
return;
}
(node: any)[internalInstanceKey] = hostInst;
}

Expand Down Expand Up @@ -95,7 +126,12 @@ export function isContainerMarkedAsRoot(node: Container): boolean {
// HostRoot back. To get to the HostRoot, you need to pass a child of it.
// The same thing applies to Suspense and Activity boundaries.
export function getClosestInstanceFromNode(targetNode: Node): null | Fiber {
let targetInst = (targetNode: any)[internalInstanceKey];
let targetInst: void | Fiber;
if (enableInternalInstanceMap) {
targetInst = internalInstanceMap.get(((targetNode: any): InstanceUnion));
} else {
targetInst = (targetNode: any)[internalInstanceKey];
}
if (targetInst) {
// Don't return HostRoot, SuspenseComponent or ActivityComponent here.
return targetInst;
Expand All @@ -112,9 +148,15 @@ export function getClosestInstanceFromNode(targetNode: Node): null | Fiber {
// itself because the fibers are conceptually between the container
// node and the first child. It isn't surrounding the container node.
// If it's not a container, we check if it's an instance.
targetInst =
(parentNode: any)[internalContainerInstanceKey] ||
(parentNode: any)[internalInstanceKey];
if (enableInternalInstanceMap) {
targetInst =
(parentNode: any)[internalContainerInstanceKey] ||
internalInstanceMap.get(((parentNode: any): InstanceUnion));
} else {
targetInst =
(parentNode: any)[internalContainerInstanceKey] ||
(parentNode: any)[internalInstanceKey];
}
if (targetInst) {
// Since this wasn't the direct target of the event, we might have
// stepped past dehydrated DOM nodes to get here. However they could
Expand Down Expand Up @@ -147,8 +189,10 @@ export function getClosestInstanceFromNode(targetNode: Node): null | Fiber {
// have had an internalInstanceKey on it.
// Let's get the fiber associated with the SuspenseComponent
// as the deepest instance.
// $FlowFixMe[prop-missing]
const targetFiber = hydrationInstance[internalInstanceKey];
const targetFiber = enableInternalInstanceMap
? internalInstanceMap.get(hydrationInstance)
: // $FlowFixMe[prop-missing]
hydrationInstance[internalInstanceKey];
if (targetFiber) {
return targetFiber;
}
Expand All @@ -175,9 +219,16 @@ export function getClosestInstanceFromNode(targetNode: Node): null | Fiber {
* instance, or null if the node was not rendered by this React.
*/
export function getInstanceFromNode(node: Node): Fiber | null {
const inst =
(node: any)[internalInstanceKey] ||
(node: any)[internalContainerInstanceKey];
let inst: void | null | Fiber;
if (enableInternalInstanceMap) {
inst =
internalInstanceMap.get(((node: any): InstanceUnion)) ||
(node: any)[internalContainerInstanceKey];
} else {
inst =
(node: any)[internalInstanceKey] ||
(node: any)[internalContainerInstanceKey];
}
if (inst) {
const tag = inst.tag;
if (
Expand Down Expand Up @@ -226,16 +277,24 @@ export function getFiberCurrentPropsFromNode(
| TextInstance
| SuspenseInstance
| ActivityInstance,
): Props {
): Props | null {
if (enableInternalInstanceMap) {
return internalPropsMap.get(node) || null;
}
return (node: any)[internalPropsKey] || null;
}

export function updateFiberProps(node: Instance, props: Props): void {
if (enableInternalInstanceMap) {
internalPropsMap.set(node, props);
return;
}
(node: any)[internalPropsKey] = props;
}

export function getEventListenerSet(node: EventTarget): Set<string> {
let elementListenerSet = (node: any)[internalEventHandlersKey];
let elementListenerSet: Set<string> | void;
elementListenerSet = (node: any)[internalEventHandlersKey];
if (elementListenerSet === undefined) {
elementListenerSet = (node: any)[internalEventHandlersKey] = new Set();
}
Expand All @@ -246,6 +305,9 @@ export function getFiberFromScopeInstance(
scope: ReactScopeInstance,
): null | Fiber {
if (enableScopeAPI) {
if (enableInternalInstanceMap) {
return internalInstanceMap.get(((scope: any): InstanceUnion)) || null;
}
return (scope: any)[internalInstanceKey] || null;
}
return null;
Expand Down Expand Up @@ -318,6 +380,12 @@ export function clearScrollEndTimer(node: EventTarget): void {
}

export function isOwnedInstance(node: Node): boolean {
if (enableInternalInstanceMap) {
return !!(
(node: any)[internalHoistableMarker] ||
internalInstanceMap.has((node: any))
);
}
return !!(
(node: any)[internalHoistableMarker] || (node: any)[internalInstanceKey]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1435,8 +1435,13 @@ export function applyViewTransitionName(
className: ?string,
): void {
instance = ((instance: any): HTMLElement);
// If the name isn't valid CSS identifier, base64 encode the name instead.
// This doesn't let you select it in custom CSS selectors but it does work in current
// browsers.
const escapedName =
CSS.escape(name) !== name ? 'r-' + btoa(name).replace(/=/g, '') : name;
// $FlowFixMe[prop-missing]
instance.style.viewTransitionName = name;
instance.style.viewTransitionName = escapedName;
if (className != null) {
// $FlowFixMe[prop-missing]
instance.style.viewTransitionClass = className;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,12 @@ export function revealCompletedBoundariesWithViewTransitions(
const idPrefix = '';
name = '_' + idPrefix + 'T_' + autoNameIdx++ + '_';
}
elementStyle['viewTransitionName'] = name;
// If the name isn't valid CSS identifier, base64 encode the name instead.
// This doesn't let you select it in custom CSS selectors but it does work in current
// browsers.
const escapedName =
CSS.escape(name) !== name ? 'r-' + btoa(name).replace(/=/g, '') : name;
elementStyle['viewTransitionName'] = escapedName;
shouldStartViewTransition = true;
}
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ function commitAppearingPairViewTransitions(placement: Fiber): void {
}
let child = placement.child;
while (child !== null) {
if (child.tag === OffscreenComponent && child.memoizedState === null) {
if (child.tag === OffscreenComponent && child.memoizedState !== null) {
// This tree was already hidden so we skip it.
} else {
commitAppearingPairViewTransitions(child);
Expand Down Expand Up @@ -347,7 +347,7 @@ function commitDeletedPairViewTransitions(deletion: Fiber): void {
}
let child = deletion.child;
while (child !== null) {
if (child.tag === OffscreenComponent && child.memoizedState === null) {
if (child.tag === OffscreenComponent && child.memoizedState !== null) {
// This tree was already hidden so we skip it.
} else {
if (
Expand Down Expand Up @@ -550,7 +550,7 @@ function restorePairedViewTransitions(parent: Fiber): void {
}
let child = parent.child;
while (child !== null) {
if (child.tag === OffscreenComponent && child.memoizedState === null) {
if (child.tag === OffscreenComponent && child.memoizedState !== null) {
// This tree was already hidden so we skip it.
} else {
if (
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/ReactFeatureFlags.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ export const enableFragmentRefs: boolean = true;
export const enableFragmentRefsScrollIntoView: boolean = true;
export const enableFragmentRefsInstanceHandles: boolean = false;

export const enableInternalInstanceMap: boolean = false;

// -----------------------------------------------------------------------------
// Ready for next major.
//
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export const enableComponentPerformanceTrack: boolean =
__PROFILE__ && dynamicFlags.enableComponentPerformanceTrack;
export const enablePerformanceIssueReporting: boolean =
enableComponentPerformanceTrack;
export const enableInternalInstanceMap: boolean = false;

// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
2 changes: 2 additions & 0 deletions packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ export const enableFragmentRefs: boolean = true;
export const enableFragmentRefsScrollIntoView: boolean = false;
export const enableFragmentRefsInstanceHandles: boolean = false;

export const enableInternalInstanceMap: boolean = false;

// Profiling Only
export const enableProfilerTimer: boolean = __PROFILE__;
export const enableProfilerCommitHooks: boolean = __PROFILE__;
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/forks/ReactFeatureFlags.test-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ export const enableFragmentRefs: boolean = true;
export const enableFragmentRefsScrollIntoView: boolean = true;
export const enableFragmentRefsInstanceHandles: boolean = false;

export const enableInternalInstanceMap: boolean = false;

// TODO: This must be in sync with the main ReactFeatureFlags file because
// the Test Renderer's value must be the same as the one used by the
// react package.
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,7 @@ export const enableFragmentRefsScrollIntoView: boolean = false;
export const enableFragmentRefsInstanceHandles: boolean = false;
export const ownerStackLimit = 1e4;

export const enableInternalInstanceMap: boolean = false;

// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
2 changes: 2 additions & 0 deletions packages/shared/forks/ReactFeatureFlags.www-dynamic.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export const enableFragmentRefs: boolean = __VARIANT__;
export const enableFragmentRefsScrollIntoView: boolean = __VARIANT__;
export const enableAsyncDebugInfo: boolean = __VARIANT__;

export const enableInternalInstanceMap: boolean = __VARIANT__;

// TODO: These flags are hard-coded to the default values used in open source.
// Update the tests so that they pass in either mode, then set these
// to __VARIANT__.
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.www.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const {
enableFragmentRefs,
enableFragmentRefsScrollIntoView,
enableAsyncDebugInfo,
enableInternalInstanceMap,
} = dynamicFeatureFlags;

// On WWW, __EXPERIMENTAL__ is used for a new modern build.
Expand Down
4 changes: 4 additions & 0 deletions scripts/flow/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -644,3 +644,7 @@ declare module 'rbush' {
fromJSON(data: any): RBush<T>;
}
}

declare class CSS {
static escape(str: string): string;
}
Loading