From e9e8df3ffd032b43cbc4298418d39f6536076bf1 Mon Sep 17 00:00:00 2001 From: "\"J. Ryan Stinnett\"" Date: Mon, 17 Mar 2014 11:39:00 +0100 Subject: [PATCH] Bug 982322 - Part 1: Use a resolver object to only descend once. r=ochameau --- .../devtools/app-manager/content/template.js | 135 +++++++++--------- .../app-manager/test/test_template.html | 94 ++++++------ 2 files changed, 111 insertions(+), 118 deletions(-) diff --git a/browser/devtools/app-manager/content/template.js b/browser/devtools/app-manager/content/template.js index 60a408cdbe5..4903f392efe 100644 --- a/browser/devtools/app-manager/content/template.js +++ b/browser/devtools/app-manager/content/template.js @@ -57,6 +57,7 @@ const NOT_FOUND_STRING = "n/a"; */ function Template(root, store, l10nResolver) { this._store = store; + this._rootResolver = new Resolver(this._store.object); this._l10n = l10nResolver; // Listeners are stored in Maps. @@ -84,39 +85,6 @@ Template.prototype = { this._doc = null; }, - _resolvePath: function(path, defaultValue=null) { - - // From the store, get the value of an object located - // at @path. - // - // For example, if the store is designed as: - // - // { - // foo: { - // bar: [ - // {}, - // {}, - // {a: 2} - // } - // } - // - // _resolvePath("foo.bar.2.a") will return "2". - // - // Array indexes are not surrounded by brackets. - - let chunks = path.split("."); - let obj = this._store.object; - for (let word of chunks) { - if ((typeof obj) == "object" && - (word in obj)) { - obj = obj[word]; - } else { - return defaultValue; - } - } - return obj; - }, - _storeChanged: function(event, path, value) { // The store has changed (a "set" event has been emitted). @@ -200,22 +168,18 @@ Template.prototype = { set.add(element); }, - _processNode: function(element, rootPath="") { + _processNode: function(element, resolver=this._rootResolver) { // The actual magic. // The element has a template attribute. // The value is supposed to be a JSON string. - // rootPath is the prefex to the path used by - // these elements (if children of template-loop); + // resolver is a helper object that is used to retrieve data + // from the template's data store, give the current path into + // the data store, or descend down another level of the store. + // See the Resolver object below. let e = element; let str = e.getAttribute("template"); - if (rootPath) { - // We will prefix paths with this rootPath. - // It needs to end with a dot. - rootPath = rootPath + "."; - } - try { let json = JSON.parse(str); // Sanity check @@ -225,7 +189,7 @@ Template.prototype = { if (json.rootPath) { // If node has been generated through a loop, we stored // previously its rootPath. - rootPath = json.rootPath; + resolver = this._rootResolver.descend(json.rootPath); } // paths is an array that will store all the paths we needed @@ -240,16 +204,16 @@ Template.prototype = { !("path" in json)) { throw new Error("missing property"); } - e.setAttribute(json.name, this._resolvePath(rootPath + json.path, NOT_FOUND_STRING)); - paths.push(rootPath + json.path); + e.setAttribute(json.name, resolver.get(json.path, NOT_FOUND_STRING)); + paths.push(resolver.rootPathTo(json.path)); break; } case "textContent": { if (!("path" in json)) { throw new Error("missing property"); } - e.textContent = this._resolvePath(rootPath + json.path, NOT_FOUND_STRING); - paths.push(rootPath + json.path); + e.textContent = resolver.get(json.path, NOT_FOUND_STRING); + paths.push(resolver.rootPathTo(json.path)); break; } case "localizedContent": { @@ -258,17 +222,17 @@ Template.prototype = { throw new Error("missing property"); } let params = json.paths.map((p) => { - paths.push(rootPath + p); - let str = this._resolvePath(rootPath + p, NOT_FOUND_STRING); + paths.push(resolver.rootPathTo(p)); + let str = resolver.get(p, NOT_FOUND_STRING); return str; }); e.textContent = this._l10n(json.property, params); break; } } - if (rootPath) { + if (resolver !== this._rootResolver) { // We save the rootPath if any. - json.rootPath = rootPath; + json.rootPath = resolver.path; e.setAttribute("template", JSON.stringify(json)); } if (paths.length > 0) { @@ -281,7 +245,7 @@ Template.prototype = { } }, - _processLoop: function(element, rootPath="") { + _processLoop: function(element, resolver=this._rootResolver) { // The element has a template-loop attribute. // The related path must be an array. We go // through the array, and build one child per @@ -296,9 +260,7 @@ Template.prototype = { !("childSelector" in json)) { throw new Error("missing property"); } - if (rootPath) { - json.arrayPath = rootPath + "." + json.arrayPath; - } + let descendedResolver = resolver.descend(json.arrayPath); let templateParent = this._doc.querySelector(json.childSelector); if (!templateParent) { throw new Error("can't find child"); @@ -306,7 +268,7 @@ Template.prototype = { template = this._doc.createElement("div"); template.innerHTML = templateParent.innerHTML; template = template.firstElementChild; - let array = this._resolvePath(json.arrayPath, []); + let array = descendedResolver.get("", []); if (!Array.isArray(array)) { console.error("referenced array is not an array"); } @@ -315,11 +277,11 @@ Template.prototype = { let fragment = this._doc.createDocumentFragment(); for (let i = 0; i < count; i++) { let node = template.cloneNode(true); - this._processTree(node, json.arrayPath + "." + i); + this._processTree(node, descendedResolver.descend(i)); fragment.appendChild(node); } - this._registerLoop(json.arrayPath, e); - this._registerLoop(json.arrayPath + ".length", e); + this._registerLoop(descendedResolver.path, e); + this._registerLoop(descendedResolver.rootPathTo("length"), e); this._unregisterNodes(e.querySelectorAll("[template]")); e.innerHTML = ""; e.appendChild(fragment); @@ -328,7 +290,7 @@ Template.prototype = { } }, - _processFor: function(element, rootPath="") { + _processFor: function(element, resolver=this._rootResolver) { let e = element; try { let template; @@ -339,10 +301,6 @@ Template.prototype = { throw new Error("missing property"); } - if (rootPath) { - json.path = rootPath + "." + json.path; - } - if (!json.path) { // Nothing to show. this._unregisterNodes(e.querySelectorAll("[template]")); @@ -350,6 +308,7 @@ Template.prototype = { return; } + let descendedResolver = resolver.descend(json.path); let templateParent = this._doc.querySelector(json.childSelector); if (!templateParent) { throw new Error("can't find child"); @@ -358,10 +317,10 @@ Template.prototype = { content.innerHTML = templateParent.innerHTML; content = content.firstElementChild; - this._processTree(content, json.path); + this._processTree(content, descendedResolver); this._unregisterNodes(e.querySelectorAll("[template]")); - this._registerFor(json.path, e); + this._registerFor(descendedResolver.path, e); e.innerHTML = ""; e.appendChild(content); @@ -371,21 +330,55 @@ Template.prototype = { } }, - _processTree: function(parent, rootPath="") { + _processTree: function(parent, resolver=this._rootResolver) { let loops = parent.querySelectorAll(":not(template) [template-loop]"); let fors = parent.querySelectorAll(":not(template) [template-for]"); let nodes = parent.querySelectorAll(":not(template) [template]"); for (let e of loops) { - this._processLoop(e, rootPath); + this._processLoop(e, resolver); } for (let e of fors) { - this._processFor(e, rootPath); + this._processFor(e, resolver); } for (let e of nodes) { - this._processNode(e, rootPath); + this._processNode(e, resolver); } if (parent.hasAttribute("template")) { - this._processNode(parent, rootPath); + this._processNode(parent, resolver); } }, +}; + +function Resolver(object, path = "") { + this._object = object; + this.path = path; } + +Resolver.prototype = { + + get: function(path, defaultValue = null) { + let obj = this._object; + if (path === "") { + return obj; + } + let chunks = path.toString().split("."); + for (let word of chunks) { + if ((typeof obj) == "object" && + (word in obj)) { + obj = obj[word]; + } else { + return defaultValue; + } + } + return obj; + }, + + rootPathTo: function(path) { + return this.path ? this.path + "." + path : path; + }, + + descend: function(path) { + return new Resolver(this.get(path), this.rootPathTo(path)); + } + +}; diff --git a/browser/devtools/app-manager/test/test_template.html b/browser/devtools/app-manager/test/test_template.html index 1830f91f715..b116e226819 100644 --- a/browser/devtools/app-manager/test/test_template.html +++ b/browser/devtools/app-manager/test/test_template.html @@ -22,17 +22,17 @@ ttt foo2:foo_l10n/bar_l10n -
meh
+
meh
- xx0 - a - b + xx0 + a + b
- xx1 - a - b + xx1 + a + b
@@ -42,17 +42,17 @@ xxx foo2:foo2_l10n/bar_l10n -
meh2
+
meh2
- xx0 - a - b + xx0 + a + b
- xx1 - a - b + xx1 + a + b
@@ -64,14 +64,14 @@
- xx0 - a - b + xx0 + a + b
- xx1 - a - b + xx1 + a + b
@@ -83,29 +83,29 @@
- xx0 - a - b + xx0 + a + b
- xx1 - a - b + xx1 + a + b
- xx2 - a - b + xx2 + a + b
- xx3 - a - b + xx3 + a + b
- xx4 - a - b + xx4 + a + b
@@ -117,24 +117,24 @@
- xx0 - a - b + xx0 + a + b
- xx1 - a - b + xx1 + a + b
- xx2 - a - b + xx2 + a + b
- xx3 - a - b + xx3 + a + b