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
2 changes: 1 addition & 1 deletion integration/platform-server-hydration/size.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"dist/browser/main-[hash].js": 240329,
"dist/browser/main-[hash].js": 240750,
"dist/browser/polyfills-[hash].js": 35726,
"dist/browser/event-dispatch-contract.min.js": 476
}
2 changes: 1 addition & 1 deletion integration/standalone-bootstrap/size.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"dist/main.js": 89237,
"dist/main.js": 93814,
"dist/polyfills.js": 35677
}
4 changes: 4 additions & 0 deletions packages/core/src/core_private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export {
DeferBlockState as ɵDeferBlockState,
} from './defer/interfaces';
export {getDocument as ɵgetDocument} from './render3/interfaces/document';
export {
SHARED_STYLES_HOST as ɵSHARED_STYLES_HOST,
SharedStylesHost as ɵSharedStylesHost,
} from './render3/interfaces/shared_styles_host';
export {
convertToBitFlags as ɵconvertToBitFlags,
setCurrentInjector as ɵsetCurrentInjector,
Expand Down
44 changes: 43 additions & 1 deletion packages/core/src/render3/component_ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,20 @@ import {ProfilerEvent} from '../../primitives/devtools';
import {executeContentQueries} from './queries/query_execution';
import {enterView, leaveView} from './state';
import {debugStringifyTypeForError, stringifyForError} from './util/stringify_utils';
import {getComponentLViewByIndex, getTNode} from './util/view_utils';
import {getComponentLViewByIndex, getTNode, storeLViewOnDestroy} from './util/view_utils';
import {directiveHostEndFirstCreatePass, directiveHostFirstCreatePass} from './view/elements';
import {ViewRef} from './view_ref';
import {createLView, createTView, getInitialLViewFlagsFromDef} from './view/construction';
import {BINDING, Binding, BindingInternal, DirectiveWithBindings} from './dynamic_bindings';
import {NG_REFLECT_ATTRS_FLAG, NG_REFLECT_ATTRS_FLAG_DEFAULT} from '../ng_reflect';
import {TracingService} from '../application/tracing';
import {getComponentName} from '../internal/get_closest_component_name';
import {SHARED_STYLES_HOST} from './interfaces/shared_styles_host';
import {DOCUMENT} from '../document';
import {getDocument} from './interfaces/document';

const shadowRootSupported = typeof ShadowRoot !== 'undefined';
const documentSupported = typeof Document !== 'undefined';

export class ComponentFactoryResolver extends AbstractComponentFactoryResolver {
/**
Expand Down Expand Up @@ -318,6 +324,14 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
const hostElement = rootSelectorOrNode
? locateHostElement(hostRenderer, rootSelectorOrNode, cmpDef.encapsulation, rootViewInjector)
: createHostElement(cmpDef, hostRenderer);

const sharedStylesHost = rootViewInjector.get(SHARED_STYLES_HOST, null);
const styleHost = getStyleHost(
hostElement,
() => rootViewInjector.get(DOCUMENT, null) ?? getDocument(),
);
if (sharedStylesHost) sharedStylesHost.addHost(styleHost);

const hasInputBindings =
componentBindings?.some(isInputBinding) ||
directives?.some((d) => typeof d !== 'function' && d.bindings.some(isInputBinding));
Expand All @@ -336,6 +350,20 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
retrieveHydrationInfo(hostElement, rootViewInjector, true /* isRootView */),
);

// Since we don't reference count host usage, calling `removeHost` is potentially
// breaking since other root components on the page might rely on it (ex. a dialog,
// multiple `ApplicationRef.prototype.bootstrap` calls, etc.).
//
// Instead we only remove shadow roots, under the assumption that a shadow root will
// only contain one Angular root component. This is true for MicA, but not in general.
// Styles in the document light DOM are effectively leaked, since we can't easily
// assume no other components exist on the page outside this one root.
if (sharedStylesHost && shadowRootSupported && styleHost instanceof ShadowRoot) {
storeLViewOnDestroy(rootLView, () => {
sharedStylesHost.removeHost(styleHost);
});
}

rootLView[HEADER_OFFSET] = hostElement;

// rootView is the parent when bootstrapping
Expand Down Expand Up @@ -482,6 +510,20 @@ function createRootTView(
return rootTView;
}

function getStyleHost(node: RNode, doc: () => Document): Node {
const rootNode = node.getRootNode?.();

if (documentSupported && rootNode instanceof Document) {
return rootNode.head; // Connected to document.
} else if (!rootNode) {
return doc().head; // `getRootNode` not supported, Node.js use case.
} else if (shadowRootSupported && rootNode instanceof ShadowRoot) {
return rootNode; // Shadow root
} else {
return doc().head; // Disconnected element, use fallback document.
Comment thread
dgp1130 marked this conversation as resolved.
}
}

function getRootTViewTemplate(
creationBindings: Binding[] | null,
updateBindings: Binding[] | null,
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/render3/interfaces/renderer_dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ export interface RNode {
* Used exclusively for building up DOM which are static (ie not View roots)
*/
appendChild(newChild: RNode): RNode;

/**
* Returns the root node containing this node. This is either:
* 1. The `Document` containing the node, if attached in light DOM.
* 2. The `ShadowRoot` containing the node, if attached in shadow DOM.
* 3. The root node of a detached tree.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode
*/
getRootNode?(options?: {composed?: boolean}): RNode;
}

/**
Expand Down
47 changes: 47 additions & 0 deletions packages/core/src/render3/interfaces/shared_styles_host.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

import {InjectionToken} from '../../di/injection_token';

/** Token used to retrieve the `SharedStylesHost`. */
export const SHARED_STYLES_HOST = new InjectionToken<SharedStylesHost>(
typeof ngDevMode !== 'undefined' && ngDevMode ? 'SHARED_STYLES_HOST' : '',
);

/** Manages stylesheets for components in the application. */
export interface SharedStylesHost {
/**
* Adds embedded styles to the DOM via HTML `style` elements.
* @param styles An array of style content strings.
* @param urls An array of URLs to be added as link tags.
*/
addStyles(styles: string[], urls?: string[]): void;

/**
* Removes embedded styles from the DOM that were added as HTML `style` elements.
* @param styles An array of style content strings.
* @param urls An array of URLs to be removed as link tags.
*/
removeStyles(styles: string[], urls?: string[]): void;

/**
* Adds a host node to contain styles added to the DOM and adds all existing style usage to
* the newly added host node.
*
* @param hostNode The node to contain styles added to the DOM.
*/
addHost(hostNode: Node): void;

/**
* Removes a host node from the set of style hosts and removes all existing style usage from
* the removed host node.
*
* @param hostNode The node to remove from the set of style hosts.
*/
removeHost(hostNode: Node): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@
"SELF_TOKEN",
"SELF_TOKEN_REGEX",
"SHARED_ANIMATION_PROVIDERS",
"SHARED_STYLES_HOST",
"SIGNAL",
"SIMPLE_CHANGES_STORE",
"STABILITY_WARNING_THRESHOLD",
Expand Down Expand Up @@ -450,6 +451,7 @@
"directiveHostEndFirstCreatePass",
"directiveHostFirstCreatePass",
"documentElement",
"documentSupported",
"domOnlyFirstCreatePass",
"elementLikeEndShared",
"elementLikeStartShared",
Expand Down Expand Up @@ -504,6 +506,7 @@
"getDOM",
"getDeclarationTNode",
"getDirectiveDef",
"getDocument",
"getElementDepthCount",
"getFactoryDef",
"getFirstLContainer",
Expand Down Expand Up @@ -549,6 +552,7 @@
"getSelectedIndex",
"getSelectedTNode",
"getSimpleChangesStore",
"getStyleHost",
"getTNode",
"getTNodeFromLView",
"getTView",
Expand Down Expand Up @@ -829,6 +833,7 @@
"setupInitialInputs",
"setupSelectorMatchedInputsOrOutputs",
"setupStaticAttributes",
"shadowRootSupported",
"shimContentAttribute",
"shimHostAttribute",
"shimStylesContent",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
"RuntimeError",
"SCHEDULE_IN_ROOT_ZONE",
"SCHEDULE_IN_ROOT_ZONE_DEFAULT",
"SHARED_STYLES_HOST",
"SIGNAL",
"SIGNAL_NODE",
"SIMPLE_CHANGES_STORE",
Expand Down Expand Up @@ -361,6 +362,7 @@
"diPublicInInjector",
"directiveHostEndFirstCreatePass",
"directiveHostFirstCreatePass",
"documentSupported",
"enterDI",
"enterView",
"epoch",
Expand Down Expand Up @@ -407,6 +409,7 @@
"getDOM",
"getDeclarationTNode",
"getDirectiveDef",
"getDocument",
"getFactoryDef",
"getFirstLContainer",
"getFirstNativeNode",
Expand Down Expand Up @@ -453,6 +456,7 @@
"getSelectedIndex",
"getSelectedTNode",
"getSimpleChangesStore",
"getStyleHost",
"getTNode",
"getTNodeFromLView",
"getTView",
Expand Down Expand Up @@ -679,6 +683,7 @@
"setupInitialInputs",
"setupSelectorMatchedInputsOrOutputs",
"setupStaticAttributes",
"shadowRootSupported",
"shimContentAttribute",
"shimHostAttribute",
"shimStylesContent",
Expand Down
5 changes: 5 additions & 0 deletions packages/core/test/bundling/defer/bundle.golden_symbols.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@
"RuntimeError",
"SCHEDULE_IN_ROOT_ZONE",
"SCHEDULE_IN_ROOT_ZONE_DEFAULT",
"SHARED_STYLES_HOST",
"SIGNAL",
"SIMPLE_CHANGES_STORE",
"SSR_BLOCK_STATE",
Expand Down Expand Up @@ -401,6 +402,7 @@
"diPublicInInjector",
"directiveHostEndFirstCreatePass",
"directiveHostFirstCreatePass",
"documentSupported",
"domOnlyFirstCreatePass",
"elementLikeEndShared",
"elementLikeStartShared",
Expand Down Expand Up @@ -450,6 +452,7 @@
"getDeclarationTNode",
"getDeferBlockDataIndex",
"getDirectiveDef",
"getDocument",
"getElementDepthCount",
"getFactoryDef",
"getFirstLContainer",
Expand Down Expand Up @@ -499,6 +502,7 @@
"getSelectedIndex",
"getSelectedTNode",
"getSimpleChangesStore",
"getStyleHost",
"getTDeferBlockDetails",
"getTNode",
"getTNodeFromLView",
Expand Down Expand Up @@ -722,6 +726,7 @@
"setupInitialInputs",
"setupSelectorMatchedInputsOrOutputs",
"setupStaticAttributes",
"shadowRootSupported",
"shouldAddViewToDom",
"shouldAttachRegularTrigger",
"shouldAttachTrigger",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@
"RuntimeError",
"SCHEDULE_IN_ROOT_ZONE",
"SCHEDULE_IN_ROOT_ZONE_DEFAULT",
"SHARED_STYLES_HOST",
"SIGNAL",
"SIGNAL_NODE",
"SIMPLE_CHANGES_STORE",
Expand Down Expand Up @@ -520,6 +521,7 @@
"diPublicInInjector",
"directiveHostEndFirstCreatePass",
"directiveHostFirstCreatePass",
"documentSupported",
"effect",
"elementAttributeInternal",
"elementLikeEndShared",
Expand Down Expand Up @@ -593,6 +595,7 @@
"getDOM",
"getDeclarationTNode",
"getDirectiveDef",
"getDocument",
"getElementDepthCount",
"getFactoryDef",
"getFactoryOf",
Expand Down Expand Up @@ -646,6 +649,7 @@
"getSelectedIndex",
"getSelectedTNode",
"getSimpleChangesStore",
"getStyleHost",
"getSuperType",
"getSymbolIterator",
"getTNode",
Expand Down Expand Up @@ -1010,6 +1014,7 @@
"setupInitialInputs",
"setupSelectorMatchedInputsOrOutputs",
"setupStaticAttributes",
"shadowRootSupported",
"shimContentAttribute",
"shimHostAttribute",
"shimStylesContent",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@
"RuntimeError",
"SCHEDULE_IN_ROOT_ZONE",
"SCHEDULE_IN_ROOT_ZONE_DEFAULT",
"SHARED_STYLES_HOST",
"SIGNAL",
"SIGNAL_NODE",
"SIMPLE_CHANGES_STORE",
Expand Down Expand Up @@ -517,6 +518,7 @@
"diPublicInInjector",
"directiveHostEndFirstCreatePass",
"directiveHostFirstCreatePass",
"documentSupported",
"effect",
"elementAttributeInternal",
"elementLikeEndShared",
Expand Down Expand Up @@ -589,6 +591,7 @@
"getDOM",
"getDeclarationTNode",
"getDirectiveDef",
"getDocument",
"getElementDepthCount",
"getFactoryDef",
"getFactoryOf",
Expand Down Expand Up @@ -642,6 +645,7 @@
"getSelectedIndex",
"getSelectedTNode",
"getSimpleChangesStore",
"getStyleHost",
"getSuperType",
"getSymbolIterator",
"getTNode",
Expand Down Expand Up @@ -1008,6 +1012,7 @@
"setupInitialInputs",
"setupSelectorMatchedInputsOrOutputs",
"setupStaticAttributes",
"shadowRootSupported",
"shimContentAttribute",
"shimHostAttribute",
"shimStylesContent",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@
"RuntimeError",
"SCHEDULE_IN_ROOT_ZONE",
"SCHEDULE_IN_ROOT_ZONE_DEFAULT",
"SHARED_STYLES_HOST",
"SIGNAL",
"SIMPLE_CHANGES_STORE",
"SKIP_HYDRATION_ATTR_NAME",
Expand Down Expand Up @@ -499,6 +500,7 @@
"diPublicInInjector",
"directiveHostEndFirstCreatePass",
"directiveHostFirstCreatePass",
"documentSupported",
"enableApplyRootElementTransformImpl",
"enableFindMatchingDehydratedViewImpl",
"enableHydrationRuntimeSupport",
Expand Down Expand Up @@ -645,6 +647,7 @@
"getSelectedIndex",
"getSerializedContainerViews",
"getSimpleChangesStore",
"getStyleHost",
"getSymbolIterator",
"getTDeferBlockDetails",
"getTNode",
Expand Down Expand Up @@ -969,6 +972,7 @@
"setupInitialInputs",
"setupSelectorMatchedInputsOrOutputs",
"setupStaticAttributes",
"shadowRootSupported",
"sharedMapFunction",
"sharedStashFunction",
"shimContentAttribute",
Expand Down
Loading
Loading