parent
81e4b2a4bf
commit
d6989c80d3
@ -260,8 +260,11 @@ export function injectAttribute(attrNameToInject: string): string|undefined {
|
|||||||
const attrs = tElement.attrs;
|
const attrs = tElement.attrs;
|
||||||
if (attrs) {
|
if (attrs) {
|
||||||
for (let i = 0; i < attrs.length; i = i + 2) {
|
for (let i = 0; i < attrs.length; i = i + 2) {
|
||||||
const attrName = attrs[i];
|
let attrName = attrs[i];
|
||||||
if (attrName === AttributeMarker.SELECT_ONLY) break;
|
if (attrName === AttributeMarker.SELECT_ONLY) break;
|
||||||
|
if (attrName === 0) { // NS.FULL
|
||||||
|
attrName = attrs[i += 2];
|
||||||
|
}
|
||||||
if (attrName == attrNameToInject) {
|
if (attrName == attrNameToInject) {
|
||||||
return attrs[i + 1] as string;
|
return attrs[i + 1] as string;
|
||||||
}
|
}
|
||||||
|
@ -842,7 +842,19 @@ export function createTView(
|
|||||||
function setUpAttributes(native: RElement, attrs: TAttributes): void {
|
function setUpAttributes(native: RElement, attrs: TAttributes): void {
|
||||||
const isProc = isProceduralRenderer(renderer);
|
const isProc = isProceduralRenderer(renderer);
|
||||||
for (let i = 0; i < attrs.length; i += 2) {
|
for (let i = 0; i < attrs.length; i += 2) {
|
||||||
const attrName = attrs[i];
|
let attrName = attrs[i];
|
||||||
|
if (attrName === 0) { // NS.FULL
|
||||||
|
// Namespaced attribute
|
||||||
|
const attrNS = attrs[i + 1] as string;
|
||||||
|
attrName = attrs[i + 2] as string;
|
||||||
|
const attrVal = attrs[i + 3] as string;
|
||||||
|
i += 2;
|
||||||
|
if (isProc) {
|
||||||
|
(renderer as ProceduralRenderer3).setAttribute(native, attrName, attrVal, attrNS);
|
||||||
|
} else {
|
||||||
|
native.setAttributeNS(attrNS, attrName, attrVal);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (attrName === AttributeMarker.SELECT_ONLY) break;
|
if (attrName === AttributeMarker.SELECT_ONLY) break;
|
||||||
if (attrName !== NG_PROJECT_AS_ATTR_NAME) {
|
if (attrName !== NG_PROJECT_AS_ATTR_NAME) {
|
||||||
const attrVal = attrs[i + 1];
|
const attrVal = attrs[i + 1];
|
||||||
@ -853,6 +865,7 @@ function setUpAttributes(native: RElement, attrs: TAttributes): void {
|
|||||||
native.setAttribute(attrName as string, attrVal as string);
|
native.setAttribute(attrName as string, attrVal as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createError(text: string, token: any) {
|
export function createError(text: string, token: any) {
|
||||||
@ -1493,7 +1506,8 @@ function generateInitialInputs(
|
|||||||
|
|
||||||
const attrs = tNode.attrs !;
|
const attrs = tNode.attrs !;
|
||||||
for (let i = 0; i < attrs.length; i += 2) {
|
for (let i = 0; i < attrs.length; i += 2) {
|
||||||
const attrName = attrs[i];
|
const first = attrs[i];
|
||||||
|
const attrName = first === 0 ? attrs[i += 2] : first; // 0 = NS.FULL
|
||||||
const minifiedInputName = inputs[attrName];
|
const minifiedInputName = inputs[attrName];
|
||||||
const attrValue = attrs[i + 1];
|
const attrValue = attrs[i + 1];
|
||||||
|
|
||||||
@ -1906,7 +1920,7 @@ function appendToProjectionNode(
|
|||||||
* - 1 based index of the selector from the {@link projectionDef}
|
* - 1 based index of the selector from the {@link projectionDef}
|
||||||
*/
|
*/
|
||||||
export function projection(
|
export function projection(
|
||||||
nodeIndex: number, localIndex: number, selectorIndex: number = 0, attrs?: string[]): void {
|
nodeIndex: number, localIndex: number, selectorIndex: number = 0, attrs?: TAttributes): void {
|
||||||
const node = createLNode(
|
const node = createLNode(
|
||||||
nodeIndex, TNodeType.Projection, null, null, attrs || null, {head: null, tail: null});
|
nodeIndex, TNodeType.Projection, null, null, attrs || null, {head: null, tail: null});
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {LContainer} from './container';
|
import {LContainer} from './container';
|
||||||
import {LInjector} from './injector';
|
import {LInjector} from './injector';
|
||||||
import {LProjection} from './projection';
|
import {LProjection} from './projection';
|
||||||
@ -14,6 +13,16 @@ import {RElement, RNode, RText} from './renderer';
|
|||||||
import {LView, TData, TView} from './view';
|
import {LView, TData, TView} from './view';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace attribute flags.
|
||||||
|
*/
|
||||||
|
export const enum NS {
|
||||||
|
/**
|
||||||
|
* Use the next value as the full namespaces URI, the values after that
|
||||||
|
* are then the name and the value, respectively.
|
||||||
|
*/
|
||||||
|
FULL = 0,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TNodeType corresponds to the TNode.type property. It contains information
|
* TNodeType corresponds to the TNode.type property. It contains information
|
||||||
@ -179,7 +188,7 @@ export const enum AttributeMarker {
|
|||||||
* - attribute names and values
|
* - attribute names and values
|
||||||
* - special markers acting as flags to alter attributes processing.
|
* - special markers acting as flags to alter attributes processing.
|
||||||
*/
|
*/
|
||||||
export type TAttributes = (string | AttributeMarker)[];
|
export type TAttributes = (string | AttributeMarker | NS)[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LNode binding data (flyweight) for a particular node that is shared between all templates
|
* LNode binding data (flyweight) for a particular node that is shared between all templates
|
||||||
|
@ -106,8 +106,12 @@ function findAttrIndexInNode(name: string, attrs: TAttributes | null): number {
|
|||||||
if (attrs === null) return -1;
|
if (attrs === null) return -1;
|
||||||
for (let i = 0; i < attrs.length; i += step) {
|
for (let i = 0; i < attrs.length; i += step) {
|
||||||
const attrName = attrs[i];
|
const attrName = attrs[i];
|
||||||
if (attrName === name) return i;
|
if (attrName === 0) {
|
||||||
if (attrName === AttributeMarker.SELECT_ONLY) {
|
// NS.FULL
|
||||||
|
step = 2;
|
||||||
|
} else if (attrName === name) {
|
||||||
|
return i;
|
||||||
|
} else if (attrName === AttributeMarker.SELECT_ONLY) {
|
||||||
step = 1;
|
step = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,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, setHtmlNS, setSvgNS, 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, NS} 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';
|
||||||
import {Sanitizer, SecurityContext} from '../../src/sanitization/security';
|
import {Sanitizer, SecurityContext} from '../../src/sanitization/security';
|
||||||
@ -91,6 +91,43 @@ describe('instructions', () => {
|
|||||||
rendererSetAttribute: 2
|
rendererSetAttribute: 2
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should use sanitizer function even on elements with namespaced attributes', () => {
|
||||||
|
const t = new TemplateFixture(() => {
|
||||||
|
elementStart(0, 'div', [
|
||||||
|
NS.FULL,
|
||||||
|
'http://www.example.com/2004/test',
|
||||||
|
'whatever',
|
||||||
|
'abc',
|
||||||
|
]);
|
||||||
|
elementEnd();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.update(() => elementAttribute(0, 'title', 'javascript:true', sanitizeUrl));
|
||||||
|
|
||||||
|
|
||||||
|
let standardHTML = '<div whatever="abc" title="unsafe:javascript:true"></div>';
|
||||||
|
let ieHTML = '<div title="unsafe:javascript:true" whatever="abc"></div>';
|
||||||
|
|
||||||
|
expect([standardHTML, ieHTML]).toContain(t.html);
|
||||||
|
|
||||||
|
t.update(
|
||||||
|
() => elementAttribute(
|
||||||
|
0, 'title', bypassSanitizationTrustUrl('javascript:true'), sanitizeUrl));
|
||||||
|
|
||||||
|
standardHTML = '<div whatever="abc" title="javascript:true"></div>';
|
||||||
|
ieHTML = '<div title="javascript:true" whatever="abc"></div>';
|
||||||
|
|
||||||
|
expect([standardHTML, ieHTML]).toContain(t.html);
|
||||||
|
|
||||||
|
expect(ngDevMode).toHaveProperties({
|
||||||
|
firstTemplatePass: 1,
|
||||||
|
tNode: 2,
|
||||||
|
tView: 1,
|
||||||
|
rendererCreateElement: 1,
|
||||||
|
rendererSetAttribute: 2
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('elementProperty', () => {
|
describe('elementProperty', () => {
|
||||||
@ -408,6 +445,11 @@ describe('instructions', () => {
|
|||||||
// height="300"
|
// height="300"
|
||||||
'height',
|
'height',
|
||||||
'300',
|
'300',
|
||||||
|
// test:title="abc"
|
||||||
|
NS.FULL,
|
||||||
|
'http://www.example.com/2014/test',
|
||||||
|
'title',
|
||||||
|
'abc',
|
||||||
]);
|
]);
|
||||||
elementStart(2, 'circle', ['cx', '200', 'cy', '150', 'fill', '#0000ff']);
|
elementStart(2, 'circle', ['cx', '200', 'cy', '150', 'fill', '#0000ff']);
|
||||||
elementEnd();
|
elementEnd();
|
||||||
@ -419,12 +461,71 @@ describe('instructions', () => {
|
|||||||
|
|
||||||
// Most browsers will print <circle></circle>, some will print <circle />, both are valid
|
// Most browsers will print <circle></circle>, some will print <circle />, both are valid
|
||||||
const standardHTML =
|
const standardHTML =
|
||||||
'<div id="container"><svg id="display" width="400" height="300"><circle cx="200" cy="150" fill="#0000ff"></circle></svg></div>';
|
'<div id="container"><svg id="display" width="400" height="300" title="abc"><circle cx="200" cy="150" fill="#0000ff"></circle></svg></div>';
|
||||||
const ie11HTML =
|
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>';
|
'<div id="container"><svg xmlns="http://www.w3.org/2000/svg" xmlns:NS1="http://www.example.com/2014/test" NS1:title="abc" id="display" width="400" height="300"><circle fill="#0000ff" cx="200" cy="150" /></svg></div>';
|
||||||
|
|
||||||
expect([standardHTML, ie11HTML]).toContain(t.html);
|
expect([standardHTML, ie11HTML]).toContain(t.html);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set an attribute with a namespace', () => {
|
||||||
|
const t = new TemplateFixture(() => {
|
||||||
|
elementStart(0, 'div', [
|
||||||
|
'id',
|
||||||
|
'container',
|
||||||
|
// test:title="abc"
|
||||||
|
NS.FULL,
|
||||||
|
'http://www.example.com/2014/test',
|
||||||
|
'title',
|
||||||
|
'abc',
|
||||||
|
]);
|
||||||
|
elementEnd();
|
||||||
|
});
|
||||||
|
|
||||||
|
const standardHTML = '<div id="container" title="abc"></div>';
|
||||||
|
const ie11HTML =
|
||||||
|
'<div id="container" xmlns:NS1="https://www.example.com/2014/test" NS1:title="abc"></div>';
|
||||||
|
expect([standardHTML, ie11HTML]).toContain(t.html);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set attributes including more than one namespaced attribute', () => {
|
||||||
|
const t = new TemplateFixture(() => {
|
||||||
|
elementStart(0, 'div', [
|
||||||
|
'id',
|
||||||
|
'container',
|
||||||
|
|
||||||
|
// NS1:title="abc"
|
||||||
|
NS.FULL,
|
||||||
|
'http://www.example.com/2014/test',
|
||||||
|
'title',
|
||||||
|
'abc',
|
||||||
|
|
||||||
|
// style="background: #dead11"
|
||||||
|
'style',
|
||||||
|
'background: #dead11',
|
||||||
|
|
||||||
|
// NS1:whatever="wee"
|
||||||
|
NS.FULL,
|
||||||
|
'http://www.example.com/2014/test',
|
||||||
|
'whatever',
|
||||||
|
'wee',
|
||||||
|
|
||||||
|
// NS2:shazbot="wocka wocka"
|
||||||
|
NS.FULL,
|
||||||
|
'http://www.whatever.com/2016/blah',
|
||||||
|
'shazbot',
|
||||||
|
'wocka wocka',
|
||||||
|
]);
|
||||||
|
elementEnd();
|
||||||
|
});
|
||||||
|
|
||||||
|
const standardHTML =
|
||||||
|
'<div id="container" title="abc" style="background: #dead11" whatever="wee" shazbot="wocka wocka"></div>';
|
||||||
|
const ieHTML =
|
||||||
|
'<div id="container" style="background: rgb(222, 173, 17);" title="abc" whatever="wee" shazbot="wocka wocka"></div>';
|
||||||
|
|
||||||
|
expect([standardHTML, ieHTML]).toContain(t.html);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -28,23 +28,14 @@ import {NAMESPACE_URIS} from '../../src/dom/dom_renderer';
|
|||||||
|
|
||||||
describe('setAttribute', () => {
|
describe('setAttribute', () => {
|
||||||
describe('with namespace', () => {
|
describe('with namespace', () => {
|
||||||
it('xmlns', () => shouldSetAttributeWithNs('xmlns'));
|
|
||||||
it('xml', () => shouldSetAttributeWithNs('xml'));
|
it('xml', () => shouldSetAttributeWithNs('xml'));
|
||||||
it('svg', () => shouldSetAttributeWithNs('svg'));
|
it('svg', () => shouldSetAttributeWithNs('svg'));
|
||||||
it('xhtml', () => shouldSetAttributeWithNs('xhtml'));
|
it('xhtml', () => shouldSetAttributeWithNs('xhtml'));
|
||||||
it('xlink', () => shouldSetAttributeWithNs('xlink'));
|
it('xlink', () => shouldSetAttributeWithNs('xlink'));
|
||||||
|
it('custom', () => shouldSetAttributeWithNs('custom'));
|
||||||
it('unknown', () => {
|
|
||||||
const div = document.createElement('div');
|
|
||||||
expect(div.hasAttribute('unknown:name')).toBe(false);
|
|
||||||
|
|
||||||
renderer.setAttribute(div, 'name', 'value', 'unknown');
|
|
||||||
|
|
||||||
expect(div.getAttribute('unknown:name')).toBe('value');
|
|
||||||
});
|
|
||||||
|
|
||||||
function shouldSetAttributeWithNs(namespace: string): void {
|
function shouldSetAttributeWithNs(namespace: string): void {
|
||||||
const namespaceUri = NAMESPACE_URIS[namespace];
|
const namespaceUri = NAMESPACE_URIS[namespace] || namespace;
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
expect(div.hasAttributeNS(namespaceUri, 'name')).toBe(false);
|
expect(div.hasAttributeNS(namespaceUri, 'name')).toBe(false);
|
||||||
|
|
||||||
@ -57,26 +48,16 @@ import {NAMESPACE_URIS} from '../../src/dom/dom_renderer';
|
|||||||
|
|
||||||
describe('removeAttribute', () => {
|
describe('removeAttribute', () => {
|
||||||
describe('with namespace', () => {
|
describe('with namespace', () => {
|
||||||
it('xmlns', () => shouldRemoveAttributeWithNs('xmlns'));
|
|
||||||
it('xml', () => shouldRemoveAttributeWithNs('xml'));
|
it('xml', () => shouldRemoveAttributeWithNs('xml'));
|
||||||
it('svg', () => shouldRemoveAttributeWithNs('svg'));
|
it('svg', () => shouldRemoveAttributeWithNs('svg'));
|
||||||
it('xhtml', () => shouldRemoveAttributeWithNs('xhtml'));
|
it('xhtml', () => shouldRemoveAttributeWithNs('xhtml'));
|
||||||
it('xlink', () => shouldRemoveAttributeWithNs('xlink'));
|
it('xlink', () => shouldRemoveAttributeWithNs('xlink'));
|
||||||
|
it('custom', () => shouldRemoveAttributeWithNs('custom'));
|
||||||
it('unknown', () => {
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.setAttribute('unknown:name', 'value');
|
|
||||||
expect(div.hasAttribute('unknown:name')).toBe(true);
|
|
||||||
|
|
||||||
renderer.removeAttribute(div, 'name', 'unknown');
|
|
||||||
|
|
||||||
expect(div.hasAttribute('unknown:name')).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
function shouldRemoveAttributeWithNs(namespace: string): void {
|
function shouldRemoveAttributeWithNs(namespace: string): void {
|
||||||
const namespaceUri = NAMESPACE_URIS[namespace];
|
const namespaceUri = NAMESPACE_URIS[namespace] || namespace;
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.setAttributeNS(namespaceUri, `${namespace}:name`, 'value');
|
div.setAttributeNS(namespaceUri, 'name', 'value');
|
||||||
expect(div.hasAttributeNS(namespaceUri, 'name')).toBe(true);
|
expect(div.hasAttributeNS(namespaceUri, 'name')).toBe(true);
|
||||||
|
|
||||||
renderer.removeAttribute(div, 'name', namespace);
|
renderer.removeAttribute(div, 'name', namespace);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user