fix(platform-server): provide Domino DOM types globally (#24116)

Fixes #23280, #23133.

This fix lets code access DOM types like Node, HTMLElement in the code. These are invariant across requests and the corresponding classes from Domino can be safely provided during platform initialization.

This is needed for the current sanitizer to work properly on platform-server. Also allows HTML types in injection - Ex. `@inject(DOCUMENT) doc: Document`.

PR Close #24116
This commit is contained in:
Vikram Subramanian 2018-05-24 16:04:04 -07:00 committed by Victor Berchet
parent d6595ebd39
commit c73196eb59
2 changed files with 38 additions and 2 deletions

View File

@ -13,6 +13,12 @@ function _notImplemented(methodName: string) {
return new Error('This method is not implemented in DominoAdapter: ' + methodName); return new Error('This method is not implemented in DominoAdapter: ' + methodName);
} }
function setDomTypes() {
// Make all Domino types available as types in the global env.
Object.assign(global, domino.impl);
(global as any)['KeyboardEvent'] = domino.impl.Event;
}
/** /**
* Parses a document string to a Document object. * Parses a document string to a Document object.
*/ */
@ -33,7 +39,10 @@ export function serializeDocument(doc: Document): string {
* DOM Adapter for the server platform based on https://github.com/fgnass/domino. * DOM Adapter for the server platform based on https://github.com/fgnass/domino.
*/ */
export class DominoAdapter extends BrowserDomAdapter { export class DominoAdapter extends BrowserDomAdapter {
static makeCurrent() { setRootDomAdapter(new DominoAdapter()); } static makeCurrent() {
setDomTypes();
setRootDomAdapter(new DominoAdapter());
}
private static defaultDoc: Document; private static defaultDoc: Document;

View File

@ -10,7 +10,7 @@ import {AnimationBuilder, animate, style, transition, trigger} from '@angular/an
import {APP_BASE_HREF, PlatformLocation, isPlatformServer} from '@angular/common'; import {APP_BASE_HREF, PlatformLocation, isPlatformServer} from '@angular/common';
import {HttpClient, HttpClientModule} from '@angular/common/http'; import {HttpClient, HttpClientModule} from '@angular/common/http';
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
import {ApplicationRef, CompilerFactory, Component, HostListener, Input, NgModule, NgModuleRef, NgZone, PLATFORM_ID, PlatformRef, ViewEncapsulation, destroyPlatform, getPlatform} from '@angular/core'; import {ApplicationRef, CompilerFactory, Component, HostListener, Inject, Input, NgModule, NgModuleRef, NgZone, PLATFORM_ID, PlatformRef, ViewEncapsulation, destroyPlatform, getPlatform} from '@angular/core';
import {TestBed, async, inject} from '@angular/core/testing'; import {TestBed, async, inject} from '@angular/core/testing';
import {Http, HttpModule, Response, ResponseOptions, XHRBackend} from '@angular/http'; import {Http, HttpModule, Response, ResponseOptions, XHRBackend} from '@angular/http';
import {MockBackend, MockConnection} from '@angular/http/testing'; import {MockBackend, MockConnection} from '@angular/http/testing';
@ -254,6 +254,20 @@ class MyInputComponent {
class NameModule { class NameModule {
} }
@Component({selector: 'app', template: '<div [innerHTML]="html"></div>'})
class HTMLTypesApp {
html = '<b>foo</b> bar';
constructor(@Inject(DOCUMENT) doc: Document) {}
}
@NgModule({
declarations: [HTMLTypesApp],
imports: [BrowserModule.withServerTransition({appId: 'inner-html'}), ServerModule],
bootstrap: [HTMLTypesApp]
})
class HTMLTypesModule {
}
const TEST_KEY = makeStateKey<number>('test'); const TEST_KEY = makeStateKey<number>('test');
const STRING_KEY = makeStateKey<string>('testString'); const STRING_KEY = makeStateKey<string>('testString');
@ -552,6 +566,19 @@ class EscapedTransferStoreModule {
}); });
})); }));
it('should work with sanitizer to handle "innerHTML"', async(() => {
// Clear out any global states. These should be set when platform-server
// is initialized.
(global as any).Node = undefined;
(global as any).Document = undefined;
renderModule(HTMLTypesModule, {document: doc}).then(output => {
expect(output).toBe(
'<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER">' +
'<div innerhtml="<b>foo</b> bar"><b>foo</b> bar</div></app></body></html>');
called = true;
});
}));
it('should call render hook', async(() => { it('should call render hook', async(() => {
renderModule(RenderHookModule, {document: doc}).then(output => { renderModule(RenderHookModule, {document: doc}).then(output => {
// title should be added by the render hook. // title should be added by the render hook.