fix(ivy): ensure that the correct `document` is available (#33712)
Most of the use of `document` in the framework is within the DI so they just inject the `DOCUMENT` token and are done. Ivy is special because it does not rely upon the DI and must get hold of the document some other way. There are a limited number of places relevant to ivy that currently consume a global document object. The solution is modelled on the `LOCALE_ID` approach, which has `getLocaleId()` and `setLocaleId()` top-level functions for ivy (see `core/src/render3/i18n.ts`). In the rest of Angular (i.e. using DI) the `LOCALE_ID` token has a provider that also calls setLocaleId() to ensure that ivy has the same value. This commit defines `getDocument()` and `setDocument() `top-level functions for ivy. Wherever ivy needs the global `document`, it calls `getDocument()` instead. Each of the platforms (e.g. Browser, Server, WebWorker) have providers for `DOCUMENT`. In each of those providers they also call `setDocument()` accordingly. Fixes #33651 PR Close #33712
This commit is contained in:
parent
c303371b26
commit
e511bfcab5
|
@ -243,6 +243,10 @@ export {
|
|||
LContext as ɵLContext,
|
||||
} from './render3/interfaces/context';
|
||||
|
||||
export {
|
||||
setDocument as ɵsetDocument
|
||||
} from './render3/interfaces/document';
|
||||
|
||||
// we reexport these symbols just so that they are retained during the dead code elimination
|
||||
// performed by rollup while it's creating fesm files.
|
||||
//
|
||||
|
|
|
@ -20,6 +20,7 @@ import {setDelayProjection} from './instructions/all';
|
|||
import {attachI18nOpCodesDebug} from './instructions/lview_debug';
|
||||
import {TsickleIssue1009, allocExpando, elementAttributeInternal, elementPropertyInternal, getOrCreateTNode, setInputsForProperty, setNgReflectProperties, textBindingInternal} from './instructions/shared';
|
||||
import {LContainer, NATIVE} from './interfaces/container';
|
||||
import {getDocument} from './interfaces/document';
|
||||
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, IcuType, TI18n, TIcu} from './interfaces/i18n';
|
||||
import {TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjectionNode} from './interfaces/node';
|
||||
import {RComment, RElement, RText} from './interfaces/renderer';
|
||||
|
@ -1180,7 +1181,7 @@ function icuStart(
|
|||
function parseIcuCase(
|
||||
unsafeHtml: string, parentIndex: number, nestedIcus: IcuExpression[], tIcus: TIcu[],
|
||||
expandoStartIndex: number): IcuCase {
|
||||
const inertBodyHelper = new InertBodyHelper(document);
|
||||
const inertBodyHelper = new InertBodyHelper(getDocument());
|
||||
const inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml);
|
||||
if (!inertBodyElement) {
|
||||
throw new Error('Unable to generate inert body element');
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* Most of the use of `document` in Angular is from within the DI system so it is possible to simply
|
||||
* inject the `DOCUMENT` token and are done.
|
||||
*
|
||||
* Ivy is special because it does not rely upon the DI and must get hold of the document some other
|
||||
* way.
|
||||
*
|
||||
* The solution is to define `getDocument()` and `setDocument()` top-level functions for ivy.
|
||||
* Wherever ivy needs the global document, it calls `getDocument()` instead.
|
||||
*
|
||||
* When running ivy outside of a browser environment, it is necessary to call `setDocument()` to
|
||||
* tell ivy what the global `document` is.
|
||||
*
|
||||
* Angular does this for us in each of the standard platforms (`Browser`, `Server`, and `WebWorker`)
|
||||
* by calling `setDocument()` when providing the `DOCUMENT` token.
|
||||
*/
|
||||
let DOCUMENT: Document|undefined = undefined;
|
||||
|
||||
/**
|
||||
* Tell ivy what the `document` is for this platform.
|
||||
*
|
||||
* It is only necessary to call this if the current platform is not a browser.
|
||||
*
|
||||
* @param document The object representing the global `document` in this environment.
|
||||
*/
|
||||
export function setDocument(document: Document | undefined): void {
|
||||
DOCUMENT = document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the object that represents the `document` for this platform.
|
||||
*
|
||||
* Ivy calls this whenever it needs to access the `document` object.
|
||||
* For example to create the renderer or to do sanitization.
|
||||
*/
|
||||
export function getDocument(): Document {
|
||||
if (DOCUMENT !== undefined) {
|
||||
return DOCUMENT;
|
||||
} else if (typeof document !== 'undefined') {
|
||||
return document;
|
||||
}
|
||||
// No "document" can be found. This should only happen if we are running ivy outside Angular and
|
||||
// the current platform is not a browser. Since this is not a supported scenario at the moment
|
||||
// this should not happen in Angular apps.
|
||||
// Once we support running ivy outside of Angular we will need to publish `setDocument()` as a
|
||||
// public API. Meanwhile we just return `undefined` and let the application fail.
|
||||
return undefined !;
|
||||
}
|
|
@ -8,15 +8,15 @@
|
|||
|
||||
/**
|
||||
* The goal here is to make sure that the browser DOM API is the Renderer.
|
||||
* We do this by defining a subset of DOM API to be the renderer and than
|
||||
* use that time for rendering.
|
||||
* We do this by defining a subset of DOM API to be the renderer and then
|
||||
* use that at runtime for rendering.
|
||||
*
|
||||
* At runtime we can then use the DOM api directly, in server or web-worker
|
||||
* it will be easy to implement such API.
|
||||
*/
|
||||
|
||||
import {RendererStyleFlags2, RendererType2} from '../../render/api';
|
||||
|
||||
import {getDocument} from './document';
|
||||
|
||||
// TODO: cleanup once the code is merged in angular/angular
|
||||
export enum RendererStyleFlags3 {
|
||||
|
@ -105,7 +105,7 @@ export interface RendererFactory3 {
|
|||
|
||||
export const domRendererFactory3: RendererFactory3 = {
|
||||
createRenderer: (hostElement: RElement | null, rendererType: RendererType2 | null):
|
||||
Renderer3 => { return document;}
|
||||
Renderer3 => { return getDocument();}
|
||||
};
|
||||
|
||||
/** Subset of API needed for appending elements and text nodes. */
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {getDocument} from '../render3/interfaces/document';
|
||||
import {SANITIZER} from '../render3/interfaces/view';
|
||||
import {getLView} from '../render3/state';
|
||||
import {renderStringify} from '../render3/util/misc_utils';
|
||||
|
@ -42,7 +43,7 @@ export function ɵɵsanitizeHtml(unsafeHtml: any): string {
|
|||
if (allowSanitizationBypassAndThrow(unsafeHtml, BypassType.Html)) {
|
||||
return unwrapSafeValue(unsafeHtml);
|
||||
}
|
||||
return _sanitizeHtml(document, renderStringify(unsafeHtml));
|
||||
return _sanitizeHtml(getDocument(), renderStringify(unsafeHtml));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
{
|
||||
"name": "DECLARATION_VIEW"
|
||||
},
|
||||
{
|
||||
"name": "DOCUMENT"
|
||||
},
|
||||
{
|
||||
"name": "DepComponent"
|
||||
},
|
||||
|
@ -308,6 +311,9 @@
|
|||
{
|
||||
"name": "getDirectiveDef"
|
||||
},
|
||||
{
|
||||
"name": "getDocument"
|
||||
},
|
||||
{
|
||||
"name": "getElementDepthCount"
|
||||
},
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
{
|
||||
"name": "DECLARATION_VIEW"
|
||||
},
|
||||
{
|
||||
"name": "DOCUMENT"
|
||||
},
|
||||
{
|
||||
"name": "EMPTY_ARRAY"
|
||||
},
|
||||
|
@ -245,6 +248,9 @@
|
|||
{
|
||||
"name": "getDirectiveDef"
|
||||
},
|
||||
{
|
||||
"name": "getDocument"
|
||||
},
|
||||
{
|
||||
"name": "getFactoryDef"
|
||||
},
|
||||
|
|
|
@ -47,6 +47,9 @@
|
|||
{
|
||||
"name": "DEFAULT_TOTAL_SOURCES"
|
||||
},
|
||||
{
|
||||
"name": "DOCUMENT"
|
||||
},
|
||||
{
|
||||
"name": "DefaultIterableDiffer"
|
||||
},
|
||||
|
@ -656,6 +659,9 @@
|
|||
{
|
||||
"name": "getDirectiveDef"
|
||||
},
|
||||
{
|
||||
"name": "getDocument"
|
||||
},
|
||||
{
|
||||
"name": "getElementDepthCount"
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import {CommonModule, DOCUMENT, PlatformLocation, ɵPLATFORM_BROWSER_ID as PLATFORM_BROWSER_ID} from '@angular/common';
|
||||
import {APP_ID, ApplicationModule, ErrorHandler, Inject, ModuleWithProviders, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, RendererFactory2, Sanitizer, SkipSelf, StaticProvider, Testability, createPlatformFactory, platformCore, ɵConsole as Console, ɵINJECTOR_SCOPE as INJECTOR_SCOPE} from '@angular/core';
|
||||
import {APP_ID, ApplicationModule, ErrorHandler, Inject, ModuleWithProviders, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, RendererFactory2, Sanitizer, SkipSelf, StaticProvider, Testability, createPlatformFactory, platformCore, ɵConsole as Console, ɵINJECTOR_SCOPE as INJECTOR_SCOPE, ɵsetDocument} from '@angular/core';
|
||||
import {BrowserDomAdapter} from './browser/browser_adapter';
|
||||
import {SERVER_TRANSITION_PROVIDERS, TRANSITION_ID} from './browser/server-transition';
|
||||
import {BrowserGetTestability} from './browser/testability';
|
||||
|
@ -57,6 +57,8 @@ export function errorHandler(): ErrorHandler {
|
|||
}
|
||||
|
||||
export function _document(): any {
|
||||
// Tell ivy about the global document
|
||||
ɵsetDocument(document);
|
||||
return document;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import {ɵAnimationEngine} from '@angular/animations/browser';
|
||||
import {DOCUMENT, PlatformLocation, ViewportScroller, ɵNullViewportScroller as NullViewportScroller, ɵPLATFORM_SERVER_ID as PLATFORM_SERVER_ID, ɵgetDOM as getDOM} from '@angular/common';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {Injector, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, Provider, RendererFactory2, StaticProvider, Testability, createPlatformFactory, platformCore, ɵALLOW_MULTIPLE_PLATFORMS as ALLOW_MULTIPLE_PLATFORMS} from '@angular/core';
|
||||
import {Injector, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, Provider, RendererFactory2, StaticProvider, Testability, createPlatformFactory, platformCore, ɵALLOW_MULTIPLE_PLATFORMS as ALLOW_MULTIPLE_PLATFORMS, ɵsetDocument} from '@angular/core';
|
||||
import {BrowserModule, EVENT_MANAGER_PLUGINS, ɵSharedStylesHost as SharedStylesHost} from '@angular/platform-browser';
|
||||
import {ɵplatformCoreDynamic as platformCoreDynamic} from '@angular/platform-browser-dynamic';
|
||||
import {NoopAnimationsModule, ɵAnimationRendererFactory} from '@angular/platform-browser/animations';
|
||||
|
@ -81,11 +81,11 @@ export class ServerModule {
|
|||
|
||||
function _document(injector: Injector) {
|
||||
let config: PlatformConfig|null = injector.get(INITIAL_CONFIG, null);
|
||||
if (config && config.document) {
|
||||
return parseDocument(config.document, config.url);
|
||||
} else {
|
||||
return getDOM().createHtmlDocument();
|
||||
}
|
||||
const document = config && config.document ? parseDocument(config.document, config.url) :
|
||||
getDOM().createHtmlDocument();
|
||||
// Tell ivy about the global document
|
||||
ɵsetDocument(document);
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import {DOCUMENT, ɵPLATFORM_WORKER_UI_ID as PLATFORM_WORKER_UI_ID} from '@angular/common';
|
||||
import {ErrorHandler, Injectable, InjectionToken, Injector, NgZone, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, RendererFactory2, StaticProvider, Testability, createPlatformFactory, isDevMode, platformCore, ɵAPP_ID_RANDOM_PROVIDER as APP_ID_RANDOM_PROVIDER} from '@angular/core';
|
||||
import {ErrorHandler, Injectable, InjectionToken, Injector, NgZone, PLATFORM_ID, PLATFORM_INITIALIZER, RendererFactory2, StaticProvider, Testability, createPlatformFactory, isDevMode, platformCore, ɵAPP_ID_RANDOM_PROVIDER as APP_ID_RANDOM_PROVIDER, ɵsetDocument} from '@angular/core';
|
||||
import {EVENT_MANAGER_PLUGINS, EventManager, HAMMER_GESTURE_CONFIG, HammerGestureConfig, ɵBROWSER_SANITIZATION_PROVIDERS as BROWSER_SANITIZATION_PROVIDERS, ɵBrowserDomAdapter as BrowserDomAdapter, ɵBrowserGetTestability as BrowserGetTestability, ɵDomEventsPlugin as DomEventsPlugin, ɵDomRendererFactory2 as DomRendererFactory2, ɵDomSharedStylesHost as DomSharedStylesHost, ɵHammerGesturesPlugin as HammerGesturesPlugin, ɵKeyEventsPlugin as KeyEventsPlugin, ɵSharedStylesHost as SharedStylesHost} from '@angular/platform-browser';
|
||||
|
||||
import {ON_WEB_WORKER} from './web_workers/shared/api';
|
||||
|
@ -159,6 +159,8 @@ function _exceptionHandler(): ErrorHandler {
|
|||
}
|
||||
|
||||
function _document(): any {
|
||||
// Tell ivy about the global document
|
||||
ɵsetDocument(document);
|
||||
return document;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ export interface MessageBusSource {
|
|||
/** @deprecated */
|
||||
export declare const platformWorkerApp: (extraProviders?: StaticProvider[] | undefined) => PlatformRef;
|
||||
|
||||
export declare const platformWorkerUi: (extraProviders?: StaticProvider[] | undefined) => PlatformRef;
|
||||
export declare const platformWorkerUi: (extraProviders?: StaticProvider[] | undefined) => import("@angular/core").PlatformRef;
|
||||
|
||||
/** @deprecated */
|
||||
export interface ReceivedMessage {
|
||||
|
|
Loading…
Reference in New Issue