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

refactor(core): Use the early event contract instead of the event contract in the bootstrap. #55587

Closed
wants to merge 13 commits into from
Closed
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
37 changes: 16 additions & 21 deletions goldens/public-api/core/primitives/event-dispatch/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,22 @@
```ts

// @public
export function bootstrapEarlyEventContract(field: string, container: HTMLElement, appId: string, eventTypes: string[], captureEventTypes: string[], earlyJsactionTracker?: EventContractTracker<EarlyJsactionDataContainer>): void;
export class BaseDispatcher {
constructor(dispatchDelegate: (eventInfoWrapper: EventInfoWrapper) => void, { eventReplayer }?: {
eventReplayer?: Replayer;
});
dispatch(eventInfo: EventInfo): void;
queueEventInfoWrapper(eventInfoWrapper: EventInfoWrapper): void;
scheduleEventReplay(): void;
}

// @public
export function bootstrapEventContract(field: string, container: Element, appId: string, events: string[], earlyJsactionTracker?: EventContractTracker<EventContract>): void;
export function bootstrapEarlyEventContract(field: string, container: HTMLElement, appId: string, eventTypes?: string[], captureEventTypes?: string[], earlyJsactionTracker?: EventContractTracker<EarlyJsactionDataContainer>): void;

// @public
export class Dispatcher {
constructor(getHandler?: ((eventInfoWrapper: EventInfoWrapper) => EventInfoWrapperHandler | void) | undefined, { stopPropagation, eventReplayer, }?: {
stopPropagation?: boolean;
eventReplayer?: Replayer;
});
canDispatch(eventInfoWrapper: EventInfoWrapper): boolean;
dispatch(eventInfo: EventInfo, isGlobalDispatch?: boolean): void;
hasAction(name: string): boolean;
registerEventInfoHandlers<T>(namespace: string, instance: T | null, methods: {
[key: string]: EventInfoWrapperHandler;
}): void;
registerGlobalHandler(eventType: string, handler: GlobalHandler): void;
setEventReplayer(eventReplayer: Replayer): void;
unregisterGlobalHandler(eventType: string, handler: GlobalHandler): void;
unregisterHandler(namespace: string, name: string): void;
// @public (undocumented)
export interface EarlyJsactionDataContainer {
// (undocumented)
_ejsa?: EarlyJsactionData;
}

// @public
Expand All @@ -40,12 +35,12 @@ export class EventContract implements UnrenamedEventContract {
static CUSTOM_EVENT_SUPPORT: boolean;
// (undocumented)
ecaacs?: (updateEventInfoForA11yClick: typeof a11yClickLib.updateEventInfoForA11yClick, preventDefaultForA11yClick: typeof a11yClickLib.preventDefaultForA11yClick, populateClickOnlyAction: typeof a11yClickLib.populateClickOnlyAction) => void;
ecrd(dispatcher: Dispatcher_2, restriction: Restriction): void;
ecrd(dispatcher: Dispatcher, restriction: Restriction): void;
exportAddA11yClickSupport(): void;
handler(eventType: string): EventHandler | undefined;
// (undocumented)
static MOUSE_SPECIAL_SUPPORT: boolean;
registerDispatcher(dispatcher: Dispatcher_2, restriction: Restriction): void;
registerDispatcher(dispatcher: Dispatcher, restriction: Restriction): void;
replayEarlyEvents(earlyJsactionContainer?: EarlyJsactionDataContainer): void;
}

Expand Down Expand Up @@ -106,7 +101,7 @@ export class EventInfoWrapper {
}

// @public
export function registerDispatcher(eventContract: UnrenamedEventContract, dispatcher: Dispatcher): void;
export function registerDispatcher(eventContract: UnrenamedEventContract, dispatcher: BaseDispatcher): void;

// (No @packageDocumentation comment for this package)

Expand Down
4 changes: 2 additions & 2 deletions goldens/size-tracking/integration-payloads.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@
},
"platform-server-hydration/browser": {
"uncompressed": {
"main": 200584,
"main": 207890,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused why main jumped so much. I would have thought this only would impact the event-dispatch size. Any idea what's up there?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we moved logic that was in the event contract to the main bundle.

"polyfills": 33807,
"event-dispatch-contract.min": 11293
"event-dispatch-contract.min": 704
}
}
}
4 changes: 2 additions & 2 deletions packages/core/primitives/event-dispatch/contract_binary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/

import {bootstrapEventContract} from './src/register_events';
import {bootstrapEarlyEventContract} from './src/register_events';

(window as any)['__jsaction_bootstrap'] = bootstrapEventContract;
(window as any)['__jsaction_bootstrap'] = bootstrapEarlyEventContract;
5 changes: 3 additions & 2 deletions packages/core/primitives/event-dispatch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/

export {Dispatcher, registerDispatcher} from './src/dispatcher';
export {BaseDispatcher, registerDispatcher} from './src/base_dispatcher';
export {EventContractContainer} from './src/event_contract_container';
export type {EarlyJsactionDataContainer} from './src/earlyeventcontract';
export {EventContract} from './src/eventcontract';
export {bootstrapEventContract, bootstrapEarlyEventContract} from './src/register_events';
export {bootstrapEarlyEventContract} from './src/register_events';

export type {EventContractTracker} from './src/register_events';
export {EventInfoWrapper} from './src/event_info';
37 changes: 4 additions & 33 deletions packages/core/primitives/event-dispatch/src/register_events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,9 @@
*/

import {EarlyEventContract, EarlyJsactionDataContainer} from './earlyeventcontract';
import {EventContractContainer} from './event_contract_container';
import {EventContract} from './eventcontract';

export type EventContractTracker<T> = {[key: string]: {[appId: string]: T}};

/**
* Provides a factory function for bootstrapping an event contract on a
* specified object (by default, exposed on the `window`).
* @param field The property on the object that the event contract will be placed on.
* @param container The container that listens to events
* @param appId A given identifier for an application. If there are multiple apps on the page
* then this is how contracts can be initialized for each one.
* @param events An array of event names that should be listened to.
* @param earlyJsactionTracker The object that should receive the event contract.
*/
export function bootstrapEventContract(
field: string,
container: Element,
appId: string,
events: string[],
earlyJsactionTracker: EventContractTracker<EventContract> = window as unknown as EventContractTracker<EventContract>,
) {
if (!earlyJsactionTracker[field]) {
earlyJsactionTracker[field] = {};
}
const eventContract = new EventContract(new EventContractContainer(container));
earlyJsactionTracker[field][appId] = eventContract;
for (const ev of events) {
eventContract.addEvent(ev);
}
}

/**
* Provides a factory function for bootstrapping an event contract on a
* specified object (by default, exposed on the `window`).
Expand All @@ -54,15 +25,15 @@ export function bootstrapEarlyEventContract(
field: string,
container: HTMLElement,
appId: string,
eventTypes: string[],
captureEventTypes: string[],
eventTypes?: string[],
captureEventTypes?: string[],
earlyJsactionTracker: EventContractTracker<EarlyJsactionDataContainer> = window as unknown as EventContractTracker<EarlyJsactionDataContainer>,
) {
if (!earlyJsactionTracker[field]) {
earlyJsactionTracker[field] = {};
}
earlyJsactionTracker[field][appId] = {};
const eventContract = new EarlyEventContract(earlyJsactionTracker[field][appId], container);
eventContract.addEvents(eventTypes);
eventContract.addEvents(captureEventTypes, true);
if (eventTypes) eventContract.addEvents(eventTypes);
if (captureEventTypes) eventContract.addEvents(captureEventTypes, true);
}
48 changes: 30 additions & 18 deletions packages/core/src/hydration/event_replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
*/

import {
Dispatcher,
BaseDispatcher,
EarlyJsactionDataContainer,
EventContract,
EventContractContainer,
EventInfoWrapper,
registerDispatcher,
} from '@angular/core/primitives/event-dispatch';
Expand All @@ -32,7 +34,12 @@ export const EVENT_REPLAY_ENABLED_DEFAULT = false;
export const CONTRACT_PROPERTY = 'ngContracts';

declare global {
var ngContracts: {[key: string]: EventContract};
var ngContracts: {[key: string]: EarlyJsactionDataContainer};
}

// TODO: Upstream this back into event-dispatch.
function getJsactionData(container: EarlyJsactionDataContainer) {
return container._ejsa;
}

const JSACTION_ATTRIBUTE = 'jsaction';
Expand Down Expand Up @@ -76,11 +83,27 @@ export function withEventReplay(): Provider[] {
// This is set in packages/platform-server/src/utils.ts
// Note: globalThis[CONTRACT_PROPERTY] may be undefined in case Event Replay feature
// is enabled, but there are no events configured in an application.
const eventContract = globalThis[CONTRACT_PROPERTY]?.[appId] as EventContract;
if (eventContract) {
const dispatcher = new Dispatcher();
setEventReplayer(dispatcher);
// Event replay is kicked off as a side-effect of executing this function.
const container = globalThis[CONTRACT_PROPERTY]?.[appId];
const earlyJsactionData = getJsactionData(container);
if (earlyJsactionData) {
const eventContract = new EventContract(
new EventContractContainer(earlyJsactionData.c),
);
for (const et of earlyJsactionData.et) {
eventContract.addEvent(et);
}
for (const et of earlyJsactionData.etc) {
eventContract.addEvent(et);
}
eventContract.replayEarlyEvents(container);
iteriani marked this conversation as resolved.
Show resolved Hide resolved
const dispatcher = new BaseDispatcher(() => {}, {
eventReplayer: (queue) => {
for (const event of queue) {
handleEvent(event);
}
iteriani marked this conversation as resolved.
Show resolved Hide resolved
queue.length = 0;
},
});
registerDispatcher(eventContract, dispatcher);
for (const el of removeJsactionQueue) {
el.removeAttribute(JSACTION_ATTRIBUTE);
Expand Down Expand Up @@ -154,17 +177,6 @@ export function setJSActionAttribute(
}
}

/**
* Registers a function that should be invoked to replay events.
*/
function setEventReplayer(dispatcher: Dispatcher) {
dispatcher.setEventReplayer((queue) => {
for (const event of queue) {
handleEvent(event);
}
});
}

/**
* Finds an LView that a given DOM element belongs to.
*/
Expand Down
3 changes: 0 additions & 3 deletions packages/core/test/bundling/defer/bundle.golden_symbols.json
Original file line number Diff line number Diff line change
Expand Up @@ -1328,9 +1328,6 @@
{
"name": "init_discovery_utils"
},
{
"name": "init_dispatcher"
},
{
"name": "init_document"
},
Expand Down
25 changes: 24 additions & 1 deletion packages/platform-server/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,33 @@ function insertEventRecordScript(
const eventDispatchScript = findEventDispatchScript(doc);
if (eventDispatchScript) {
const events = Array.from(eventTypesToBeReplayed);
const captureEventTypes = [];
const eventTypes = [];
for (const eventType of events) {
iteriani marked this conversation as resolved.
Show resolved Hide resolved
if (
eventType === 'mouseenter' ||
eventType === 'mouseleave' ||
eventType === 'pointerenter' ||
eventType === 'pointerleave'
) {
continue;
}
if (
eventType === 'focus' ||
eventType === 'blur' ||
eventType === 'error' ||
eventType === 'load' ||
eventType === 'toggle'
) {
captureEventTypes.push(eventType);
Comment on lines +147 to +163
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: just as an idea: we can have a couple helper functions from JSAction code, so that we can keep those lists in sync with how JSAction uses them internally.

Suggested change
for (const eventType of events) {
if (
eventType === 'mouseenter' ||
eventType === 'mouseleave' ||
eventType === 'pointerenter' ||
eventType === 'pointerleave'
) {
continue;
}
if (
eventType === 'focus' ||
eventType === 'blur' ||
eventType === 'error' ||
eventType === 'load' ||
eventType === 'toggle'
) {
captureEventTypes.push(eventType);
for (const eventType of events) {
if (isMouseEvent(eventType) {
continue;
}
if (useCapturePhase(eventType)) {
captureEventTypes.push(eventType);

} else {
eventTypes.push(eventType);
}
}
// This is defined in packages/core/primitives/event-dispatch/contract_binary.ts
const replayScriptContents = `window.__jsaction_bootstrap('ngContracts', document.body, ${JSON.stringify(
appId,
)}, ${JSON.stringify(events)});`;
)}, ${JSON.stringify(eventTypes)}${captureEventTypes.length ? ',' + JSON.stringify(captureEventTypes) : ''});`;

const replayScript = createScript(doc, replayScriptContents, nonce);

Expand Down
2 changes: 1 addition & 1 deletion packages/platform-server/test/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ circular_dependency_test(
ts_library(
name = "test_lib",
testonly = True,
srcs = glob(["**/*.ts"]),
srcs = glob(["*.ts"]),
deps = [
"//packages:types",
"//packages/animations",
Expand Down
Loading