fix(ivy): DebugNode.attributes not preserving attribute name casing (#30864)

In Ivy the `DebugNode.attributes` is populated directly from the DOM, but the problem is that the browser will lowercase all attribute names. These changes preserve the case by first going through the `TNode.attrs`, populating the map with the case-sensitive names and saving a reference to the lower case name. Afterwards when we're going through the attributes from the DOM, we can check whether we've mapped the attribute by its case-sensitive name already.

This PR resolves FW-1358.

PR Close #30864
This commit is contained in:
crisbeto
2019-06-05 14:39:01 +02:00
committed by Miško Hevery
parent b4b7af86c2
commit 04587a33c5
2 changed files with 95 additions and 5 deletions

View File

@ -276,13 +276,50 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
get attributes(): {[key: string]: string | null;} {
const attributes: {[key: string]: string | null;} = {};
const element = this.nativeElement;
if (element) {
const eAttrs = element.attributes;
for (let i = 0; i < eAttrs.length; i++) {
const attr = eAttrs[i];
if (!element) {
return attributes;
}
const context = loadLContext(element);
const lView = context.lView;
const tNodeAttrs = (lView[TVIEW].data[context.nodeIndex] as TNode).attrs;
const lowercaseTNodeAttrs: string[] = [];
// For debug nodes we take the element's attribute directly from the DOM since it allows us
// to account for ones that weren't set via bindings (e.g. ViewEngine keeps track of the ones
// that are set through `Renderer2`). The problem is that the browser will lowercase all names,
// however since we have the attributes already on the TNode, we can preserve the case by going
// through them once, adding them to the `attributes` map and putting their lower-cased name
// into an array. Afterwards when we're going through the native DOM attributes, we can check
// whether we haven't run into an attribute already through the TNode.
if (tNodeAttrs) {
let i = 0;
while (i < tNodeAttrs.length) {
const attrName = tNodeAttrs[i];
// Stop as soon as we hit a marker. We only care about the regular attributes. Everything
// else will be handled below when we read the final attributes off the DOM.
if (typeof attrName !== 'string') break;
const attrValue = tNodeAttrs[i + 1];
attributes[attrName] = attrValue as string;
lowercaseTNodeAttrs.push(attrName.toLowerCase());
i += 2;
}
}
const eAttrs = element.attributes;
for (let i = 0; i < eAttrs.length; i++) {
const attr = eAttrs[i];
// Make sure that we don't assign the same attribute both in its
// case-sensitive form and the lower-cased one from the browser.
if (lowercaseTNodeAttrs.indexOf(attr.name) === -1) {
attributes[attr.name] = attr.value;
}
}
return attributes;
}