feat(ivy): add namespace instructions for SVG and others (#23899)
PR Close #23899
This commit is contained in:
parent
c494d3cf60
commit
81e4b2a4bf
|
@ -8,24 +8,27 @@
|
||||||
|
|
||||||
import './ng_dev_mode';
|
import './ng_dev_mode';
|
||||||
|
|
||||||
|
import {Sanitizer} from '../sanitization/security';
|
||||||
|
|
||||||
import {assertEqual, assertLessThan, assertNotEqual, assertNotNull, assertNull, assertSame} from './assert';
|
import {assertEqual, assertLessThan, assertNotEqual, assertNotNull, assertNull, assertSame} from './assert';
|
||||||
|
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
|
||||||
|
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||||
import {LContainer} from './interfaces/container';
|
import {LContainer} from './interfaces/container';
|
||||||
|
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefList, DirectiveDefListOrFactory, PipeDefList, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||||
import {LInjector} from './interfaces/injector';
|
import {LInjector} from './interfaces/injector';
|
||||||
import {CssSelectorList, LProjection, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
import {CssSelectorList, LProjection, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||||
import {LQueries} from './interfaces/query';
|
import {LQueries} from './interfaces/query';
|
||||||
|
import {ObjectOrientedRenderer3, ProceduralRenderer3, RElement, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
|
||||||
import {CurrentMatchesList, LView, LViewFlags, RootContext, TData, TView} from './interfaces/view';
|
import {CurrentMatchesList, LView, LViewFlags, RootContext, TData, TView} from './interfaces/view';
|
||||||
|
|
||||||
import {AttributeMarker, TAttributes, LContainerNode, LElementNode, LNode, TNodeType, TNodeFlags, LProjectionNode, LTextNode, LViewNode, TNode, TContainerNode, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue, TElementNode,} from './interfaces/node';
|
import {AttributeMarker, TAttributes, LContainerNode, LElementNode, LNode, TNodeType, TNodeFlags, LProjectionNode, LTextNode, LViewNode, TNode, TContainerNode, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue, TElementNode,} from './interfaces/node';
|
||||||
import {assertNodeType} from './node_assert';
|
import {assertNodeType} from './node_assert';
|
||||||
import {appendChild, insertView, appendProjectedNode, removeView, canInsertNativeNode, createTextNode, getNextLNode, getChildLNode, getParentLNode, getLViewChild} from './node_manipulation';
|
import {appendChild, insertView, appendProjectedNode, removeView, canInsertNativeNode, createTextNode, getNextLNode, getChildLNode, getParentLNode, getLViewChild} from './node_manipulation';
|
||||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||||
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefList, DirectiveDefListOrFactory, PipeDefList, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
|
||||||
import {RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
|
|
||||||
import {isDifferent, stringify} from './util';
|
import {isDifferent, stringify} from './util';
|
||||||
import {executeHooks, queueLifecycleHooks, queueInitHooks, executeInitHooks} from './hooks';
|
|
||||||
import {ViewRef} from './view_ref';
|
import {ViewRef} from './view_ref';
|
||||||
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
|
|
||||||
import {Sanitizer} from '../sanitization/security';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directive (D) sets a property on all component instances using this constant as a key and the
|
* Directive (D) sets a property on all component instances using this constant as a key and the
|
||||||
|
@ -550,6 +553,28 @@ function getRenderFlags(view: LView): RenderFlags {
|
||||||
RenderFlags.Update;
|
RenderFlags.Update;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
//// Namespace
|
||||||
|
//////////////////////////
|
||||||
|
let _currentNS: string|null = null;
|
||||||
|
|
||||||
|
export function setNS(namespace: string) {
|
||||||
|
_currentNS = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setHtmlNS() {
|
||||||
|
_currentNS = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setSvgNS() {
|
||||||
|
_currentNS = 'http://www.w3.org/2000/svg';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setMathML() {
|
||||||
|
_currentNS = 'http://www.w3.org/1998/Math/MathML';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
//// Element
|
//// Element
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
@ -573,7 +598,11 @@ export function elementStart(
|
||||||
assertEqual(currentView.bindingIndex, -1, 'elements should be created before any bindings');
|
assertEqual(currentView.bindingIndex, -1, 'elements should be created before any bindings');
|
||||||
|
|
||||||
ngDevMode && ngDevMode.rendererCreateElement++;
|
ngDevMode && ngDevMode.rendererCreateElement++;
|
||||||
const native: RElement = renderer.createElement(name);
|
|
||||||
|
const native: RElement = _currentNS === null || isProceduralRenderer(renderer) ?
|
||||||
|
renderer.createElement(name) :
|
||||||
|
(renderer as ObjectOrientedRenderer3).createElementNS(_currentNS, name);
|
||||||
|
|
||||||
ngDevMode && assertDataInRange(index - 1);
|
ngDevMode && assertDataInRange(index - 1);
|
||||||
|
|
||||||
const node: LElementNode =
|
const node: LElementNode =
|
||||||
|
|
|
@ -36,6 +36,7 @@ export type Renderer3 = ObjectOrientedRenderer3 | ProceduralRenderer3;
|
||||||
* */
|
* */
|
||||||
export interface ObjectOrientedRenderer3 {
|
export interface ObjectOrientedRenderer3 {
|
||||||
createElement(tagName: string): RElement;
|
createElement(tagName: string): RElement;
|
||||||
|
createElementNS(namespace: string, name: string): RElement;
|
||||||
createTextNode(data: string): RText;
|
createTextNode(data: string): RText;
|
||||||
|
|
||||||
querySelector(selectors: string): RElement|null;
|
querySelector(selectors: string): RElement|null;
|
||||||
|
|
|
@ -176,6 +176,9 @@
|
||||||
{
|
{
|
||||||
"name": "_currentInjector"
|
"name": "_currentInjector"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "_currentNS"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "_devMode"
|
"name": "_devMode"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1820,7 +1820,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
.toEqual('http://www.w3.org/2000/svg');
|
.toEqual('http://www.w3.org/2000/svg');
|
||||||
|
|
||||||
const firstAttribute = getDOM().getProperty(<Element>use, 'attributes')[0];
|
const firstAttribute = getDOM().getProperty(<Element>use, 'attributes')[0];
|
||||||
expect(firstAttribute.name).toEqual('xlink:href');
|
expect(firstAttribute.name).toEqual('href');
|
||||||
expect(firstAttribute.namespaceURI).toEqual('http://www.w3.org/1999/xlink');
|
expect(firstAttribute.namespaceURI).toEqual('http://www.w3.org/1999/xlink');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {NgForOfContext} from '@angular/common';
|
||||||
|
|
||||||
import {RenderFlags, directiveInject} from '../../src/render3';
|
import {RenderFlags, directiveInject} from '../../src/render3';
|
||||||
import {defineComponent} from '../../src/render3/definition';
|
import {defineComponent} from '../../src/render3/definition';
|
||||||
import {bind, container, elementAttribute, elementClass, elementEnd, elementProperty, elementStart, elementStyle, elementStyleNamed, interpolation1, renderTemplate, text, textBinding} from '../../src/render3/instructions';
|
import {bind, container, elementAttribute, elementClass, elementEnd, elementProperty, elementStart, elementStyle, elementStyleNamed, interpolation1, renderTemplate, setHtmlNS, setSvgNS, text, textBinding} from '../../src/render3/instructions';
|
||||||
import {LElementNode, LNode} from '../../src/render3/interfaces/node';
|
import {LElementNode, LNode} from '../../src/render3/interfaces/node';
|
||||||
import {RElement, domRendererFactory3} from '../../src/render3/interfaces/renderer';
|
import {RElement, domRendererFactory3} from '../../src/render3/interfaces/renderer';
|
||||||
import {TrustedString, bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl, sanitizeHtml, sanitizeResourceUrl, sanitizeScript, sanitizeStyle, sanitizeUrl} from '../../src/sanitization/sanitization';
|
import {TrustedString, bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl, sanitizeHtml, sanitizeResourceUrl, sanitizeScript, sanitizeStyle, sanitizeUrl} from '../../src/sanitization/sanitization';
|
||||||
|
@ -392,6 +392,40 @@ describe('instructions', () => {
|
||||||
expect(s.lastSanitizedValue).toBeFalsy();
|
expect(s.lastSanitizedValue).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('namespace', () => {
|
||||||
|
it('should render SVG', () => {
|
||||||
|
const t = new TemplateFixture(() => {
|
||||||
|
elementStart(0, 'div', ['id', 'container']);
|
||||||
|
setSvgNS();
|
||||||
|
elementStart(1, 'svg', [
|
||||||
|
// id="display"
|
||||||
|
'id',
|
||||||
|
'display',
|
||||||
|
// width="400"
|
||||||
|
'width',
|
||||||
|
'400',
|
||||||
|
// height="300"
|
||||||
|
'height',
|
||||||
|
'300',
|
||||||
|
]);
|
||||||
|
elementStart(2, 'circle', ['cx', '200', 'cy', '150', 'fill', '#0000ff']);
|
||||||
|
elementEnd();
|
||||||
|
elementEnd();
|
||||||
|
setHtmlNS();
|
||||||
|
elementEnd();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Most browsers will print <circle></circle>, some will print <circle />, both are valid
|
||||||
|
const standardHTML =
|
||||||
|
'<div id="container"><svg id="display" width="400" height="300"><circle cx="200" cy="150" fill="#0000ff"></circle></svg></div>';
|
||||||
|
const ie11HTML =
|
||||||
|
'<div id="container"><svg xmlns="http://www.w3.org/2000/svg" id="display" width="400" height="300"><circle fill="#0000ff" cx="200" cy="150" /></svg></div>';
|
||||||
|
|
||||||
|
expect([standardHTML, ie11HTML]).toContain(t.html);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
class LocalSanitizedValue {
|
class LocalSanitizedValue {
|
||||||
|
|
|
@ -110,7 +110,7 @@ class DefaultDomRenderer2 implements Renderer2 {
|
||||||
|
|
||||||
createElement(name: string, namespace?: string): any {
|
createElement(name: string, namespace?: string): any {
|
||||||
if (namespace) {
|
if (namespace) {
|
||||||
return document.createElementNS(NAMESPACE_URIS[namespace], name);
|
return document.createElementNS(NAMESPACE_URIS[namespace] || namespace, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return document.createElement(name);
|
return document.createElement(name);
|
||||||
|
@ -150,26 +150,17 @@ class DefaultDomRenderer2 implements Renderer2 {
|
||||||
|
|
||||||
setAttribute(el: any, name: string, value: string, namespace?: string): void {
|
setAttribute(el: any, name: string, value: string, namespace?: string): void {
|
||||||
if (namespace) {
|
if (namespace) {
|
||||||
name = `${namespace}:${name}`;
|
const namespaceUri = NAMESPACE_URIS[namespace] || namespace;
|
||||||
const namespaceUri = NAMESPACE_URIS[namespace];
|
|
||||||
if (namespaceUri) {
|
|
||||||
el.setAttributeNS(namespaceUri, name, value);
|
el.setAttributeNS(namespaceUri, name, value);
|
||||||
} else {
|
} else {
|
||||||
el.setAttribute(name, value);
|
el.setAttribute(name, value);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
el.setAttribute(name, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAttribute(el: any, name: string, namespace?: string): void {
|
removeAttribute(el: any, name: string, namespace?: string): void {
|
||||||
if (namespace) {
|
if (namespace) {
|
||||||
const namespaceUri = NAMESPACE_URIS[namespace];
|
const namespaceUri = NAMESPACE_URIS[namespace] || namespace;
|
||||||
if (namespaceUri) {
|
|
||||||
el.removeAttributeNS(namespaceUri, name);
|
el.removeAttributeNS(namespaceUri, name);
|
||||||
} else {
|
|
||||||
el.removeAttribute(`${namespace}:${name}`);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
el.removeAttribute(name);
|
el.removeAttribute(name);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue