Bug 1355064 - Build the markup-view DOM manually rather than with templater; r=jdescottes

MozReview-Commit-ID: LbULOa7UGq2

--HG--
extra : rebase_source : 6ccac31433e1cc7ad155cf1fbbd8a3c61d6b785b
This commit is contained in:
Patrick Brosset 2017-04-10 14:55:58 +02:00
Родитель f7f53449d6
Коммит 9015908958
6 изменённых файлов: 191 добавлений и 140 удалений

Просмотреть файл

@ -13,7 +13,6 @@ const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants")
const EventEmitter = require("devtools/shared/event-emitter");
const {LocalizationHelper} = require("devtools/shared/l10n");
const {PluralForm} = require("devtools/shared/plural-form");
const {template} = require("devtools/shared/gcli/templater");
const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const {scrollIntoViewIfNeeded} = require("devtools/client/shared/scroll");
@ -465,13 +464,6 @@ MarkupView.prototype = {
return promise.all([onShown, this._briefBoxModelPromise.promise]);
},
template: function (name, dest, options = {stack: "markup.xhtml"}) {
let node = this.doc.getElementById("template-" + name).cloneNode(true);
node.removeAttribute("id");
template(node, dest, options);
return node;
},
/**
* Get the MarkupContainer object for a given node, or undefined if
* none exists.
@ -1668,28 +1660,13 @@ MarkupView.prototype = {
container.children.firstChild.remove();
}
if (!(children.hasFirst && children.hasLast)) {
let nodesCount = container.node.numChildren;
let showAllString = PluralForm.get(nodesCount,
INSPECTOR_L10N.getStr("markupView.more.showAll2"));
let data = {
showing: INSPECTOR_L10N.getStr("markupView.more.showing"),
showAll: showAllString.replace("#1", nodesCount),
allButtonClick: () => {
container.maxChildren = -1;
container.childrenDirty = true;
this._updateChildren(container);
}
};
if (!children.hasFirst) {
let span = this.template("more-nodes", data);
fragment.insertBefore(span, fragment.firstChild);
}
if (!children.hasLast) {
let span = this.template("more-nodes", data);
fragment.appendChild(span);
}
if (!children.hasFirst) {
let topItem = this.buildMoreNodesButtonMarkup(container);
fragment.insertBefore(topItem, fragment.firstChild);
}
if (!children.hasLast) {
let bottomItem = this.buildMoreNodesButtonMarkup(container);
fragment.appendChild(bottomItem);
}
container.children.appendChild(fragment);
@ -1699,6 +1676,30 @@ MarkupView.prototype = {
return updatePromise;
},
buildMoreNodesButtonMarkup: function (container) {
let elt = this.doc.createElement("li");
elt.classList.add("more-nodes", "devtools-class-comment");
let label = this.doc.createElement("span");
label.textContent = INSPECTOR_L10N.getStr("markupView.more.showing");
elt.appendChild(label);
let button = this.doc.createElement("button");
button.setAttribute("href", "#");
let showAllString = PluralForm.get(container.node.numChildren,
INSPECTOR_L10N.getStr("markupView.more.showAll2"));
button.textContent = showAllString.replace("#1", container.node.numChildren);
elt.appendChild(button);
button.addEventListener("click", () => {
container.maxChildren = -1;
container.childrenDirty = true;
this._updateChildren(container);
});
return elt;
},
_waitForChildren: function () {
if (!this._queuedChildUpdates) {
return promise.resolve(undefined);

Просмотреть файл

@ -18,87 +18,8 @@
src="chrome://devtools/content/sourceeditor/codemirror/codemirror.bundle.js"></script>
</head>
<body class="theme-body devtools-monospace" role="application">
<!-- NOTE THAT WE MAKE EXTENSIVE USE OF HTML COMMENTS IN THIS FILE IN ORDER -->
<!-- TO MAKE SPANS READABLE WHILST AVOIDING SIGNIFICANT WHITESPACE -->
<div id="root-wrapper" role="presentation">
<div id="root" role="presentation"></div>
</div>
<div id="templates" style="display:none">
<ul class="children">
<li id="template-elementcontainer" save="${elt}" class="child collapsed" role="presentation">
<div save="${tagLine}" id="${id}" class="tag-line" role="treeitem" aria-level="${level}" aria-grabbed="${isDragging}"><!--
--><span save="${tagState}" class="tag-state" role="presentation"></span><!--
--><span save="${expander}" class="theme-twisty expander" role="presentation"></span><!--
--></div>
<ul save="${children}" class="children" role="group"></ul>
</li>
<li id="template-textcontainer" save="${elt}" class="child collapsed" role="presentation">
<div save="${tagLine}" id="${id}" class="tag-line" role="treeitem" aria-level="${level}" aria-grabbed="${isDragging}"><span save="${tagState}" class="tag-state" role="presentation"></span></div>
<ul save="${children}" class="children" role="group"></ul>
</li>
<li id="template-readonlycontainer" save="${elt}" class="child collapsed" role="presentation">
<div save="${tagLine}" id="${id}" class="tag-line" role="treeitem" aria-level="${level}" aria-grabbed="${isDragging}"><!--
--><span save="${tagState}" class="tag-state" role="presentation"></span><!--
--><span save="${expander}" class="theme-twisty expander" role="presentation"></span><!--
--></div>
<ul save="${children}" class="children" role="group"></ul>
</li>
<li id="template-more-nodes"
class="more-nodes devtools-class-comment"
save="${elt}"><!--
--><span>${showing}</span> <!--
--><button href="#" onclick="${allButtonClick}">${showAll}</button>
</li>
</ul>
<span id="template-generic" save="${elt}" class="editor"><span save="${tag}" class="tag"></span></span>
<span id="template-element" save="${elt}" class="editor"><!--
--><span class="open">&lt;<!--
--><span save="${tag}" class="tag theme-fg-color3" tabindex="-1"></span><!--
--><span save="${attrList}"></span><!--
--><span save="${newAttr}" class="newattr" tabindex="-1"></span><!--
--><span class="closing-bracket">&gt;</span><!--
--></span><!--
--><span class="close">&lt;/<!--
--><span save="${closeTag}" class="tag theme-fg-color3"></span><!--
-->&gt;<!--
--></span><!--
--><div save="${eventNode}" class="markupview-events" data-event="true">ev</div><!--
--></span>
<span id="template-attribute"
save="${attr}"
data-attr="${attrName}"
data-value="${attrValue}"
class="attreditor"
style="display:none"> <!--
--><span class="editable" save="${inner}" tabindex="${tabindex}"><!--
--><span save="${name}" class="attr-name theme-fg-color2"></span><!--
-->=&quot;<!--
--><span save="${val}" class="attr-value theme-fg-color6"></span><!--
-->&quot;<!--
--></span><!--
--></span>
<span id="template-text" save="${elt}" class="editor text"><!--
--><pre save="${value}" style="display:inline-block; white-space: normal;" tabindex="-1"></pre><!--
--></span>
<span id="template-comment"
save="${elt}"
class="editor comment theme-comment"><!--
--><span>&lt;!--</span><!--
--><pre save="${value}" style="display:inline-block; white-space: normal;" tabindex="-1"></pre><!--
--><span>--&gt;</span><!--
--></span>
</div>
</body>
</html>

Просмотреть файл

@ -41,14 +41,12 @@ function ElementEditor(container, node) {
this.container = container;
this.node = node;
this.markup = this.container.markup;
this.template = this.markup.template.bind(this.markup);
this.doc = this.markup.doc;
this._cssProperties = getCssProperties(this.markup.toolbox);
this.attrElements = new Map();
this.animationTimers = {};
// The templates will fill the following properties
this.elt = null;
this.tag = null;
this.closeTag = null;
@ -57,7 +55,7 @@ function ElementEditor(container, node) {
this.closeElt = null;
// Create the main editor
this.template("element", this);
this.buildMarkup();
// Make the tag name editable (unless this is a remote node or
// a document element)
@ -117,6 +115,51 @@ function ElementEditor(container, node) {
}
ElementEditor.prototype = {
buildMarkup: function () {
this.elt = this.doc.createElement("span");
this.elt.classList.add("editor");
let open = this.doc.createElement("span");
open.classList.add("open");
open.appendChild(this.doc.createTextNode("<"));
this.elt.appendChild(open);
this.tag = this.doc.createElement("span");
this.tag.classList.add("tag", "theme-fg-color3");
this.tag.setAttribute("tabindex", "-1");
open.appendChild(this.tag);
this.attrList = this.doc.createElement("span");
open.appendChild(this.attrList);
this.newAttr = this.doc.createElement("span");
this.newAttr.classList.add("newattr");
this.newAttr.setAttribute("tabindex", "-1");
open.appendChild(this.newAttr);
let closingBracket = this.doc.createElement("span");
closingBracket.classList.add("closing-bracket");
closingBracket.textContent = ">";
open.appendChild(closingBracket);
let close = this.doc.createElement("span");
close.classList.add("close");
close.appendChild(this.doc.createTextNode("</"));
this.elt.appendChild(close);
this.closeTag = this.doc.createElement("span");
this.closeTag.classList.add("tag", "theme-fg-color3");
close.appendChild(this.closeTag);
close.appendChild(this.doc.createTextNode(">"));
this.eventNode = this.doc.createElement("div");
this.eventNode.classList.add("markupview-events");
this.eventNode.dataset.event = "true";
this.eventNode.textContent = "ev";
this.elt.appendChild(this.eventNode);
},
set selected(value) {
if (this.textEditor) {
this.textEditor.selected = value;
@ -188,7 +231,7 @@ ElementEditor.prototype = {
if (canSimplyShowEditor) {
// Element already exists and doesn't need to be recreated.
// Just show it (it's hidden by default due to the template).
// Just show it (it's hidden by default).
el.style.removeProperty("display");
} else {
// Create a new editor, because the value of an existing attribute
@ -268,14 +311,32 @@ ElementEditor.prototype = {
},
_createAttribute: function (attribute, before = null) {
// Create the template editor, which will save some variables here.
let data = {
attrName: attribute.name,
attrValue: attribute.value,
tabindex: this.container.canFocus ? "0" : "-1",
};
this.template("attribute", data);
let {attr, inner, name, val} = data;
let attr = this.doc.createElement("span");
attr.dataset.attr = attribute.name;
attr.dataset.value = attribute.value;
attr.classList.add("attreditor");
attr.style.display = "none";
attr.appendChild(this.doc.createTextNode(" "));
let inner = this.doc.createElement("span");
inner.classList.add("editable");
inner.setAttribute("tabindex", this.container.canFocus ? "0" : "-1");
attr.appendChild(inner);
let name = this.doc.createElement("span");
name.classList.add("attr-name");
name.classList.add("theme-fg-color2");
inner.appendChild(name);
inner.appendChild(this.doc.createTextNode('="'));
let val = this.doc.createElement("span");
val.classList.add("attr-value");
val.classList.add("theme-fg-color6");
inner.appendChild(val);
inner.appendChild(this.doc.createTextNode('"'));
// Double quotes need to be handled specially to prevent DOMParser failing.
// name="v"a"l"u"e" when editing -> name='v"a"l"u"e"'

Просмотреть файл

@ -39,10 +39,11 @@ MarkupContainer.prototype = {
* The markup view that owns this container.
* @param {NodeFront} node
* The node to display.
* @param {String} templateID
* Which template to render for this container
* @param {String} type
* The type of container to build. This can be either 'textcontainer',
* 'readonlycontainer' or 'elementcontainer'.
*/
initialize: function (markupView, node, templateID) {
initialize: function (markupView, node, type) {
this.markup = markupView;
this.node = node;
this.undo = this.markup.undo;
@ -50,13 +51,8 @@ MarkupContainer.prototype = {
this.id = "treeitem-" + markupContainerID++;
this.htmlElt = this.win.document.documentElement;
// The template will fill the following properties
this.elt = null;
this.expander = null;
this.tagState = null;
this.tagLine = null;
this.children = null;
this.markup.template(templateID, this);
this.buildMarkup(type);
this.elt.container = this;
this._onMouseDown = this._onMouseDown.bind(this);
@ -78,6 +74,37 @@ MarkupContainer.prototype = {
this.updateIsDisplayed();
},
buildMarkup: function (type) {
this.elt = this.win.document.createElement("li");
this.elt.classList.add("child", "collapsed");
this.elt.setAttribute("role", "presentation");
this.tagLine = this.win.document.createElement("div");
this.tagLine.setAttribute("id", this.id);
this.tagLine.classList.add("tag-line");
this.tagLine.setAttribute("role", "treeitem");
this.tagLine.setAttribute("aria-level", this.level);
this.tagLine.setAttribute("aria-grabbed", this.isDragging);
this.elt.appendChild(this.tagLine);
this.tagState = this.win.document.createElement("span");
this.tagState.classList.add("tag-state");
this.tagState.setAttribute("role", "presentation");
this.tagLine.appendChild(this.tagState);
if (type !== "textcontainer") {
this.expander = this.win.document.createElement("span");
this.expander.classList.add("theme-twisty", "expander");
this.expander.setAttribute("role", "presentation");
this.tagLine.appendChild(this.expander);
}
this.children = this.win.document.createElement("ul");
this.children.classList.add("children");
this.children.setAttribute("role", "group");
this.elt.appendChild(this.children);
},
toString: function () {
return "[MarkupContainer for " + this.node + "]";
},
@ -237,7 +264,7 @@ MarkupContainer.prototype = {
* Set an appropriate DOM tree depth level for a node and its subtree.
*/
updateLevel: function () {
// ARIA level should already be set when container template is rendered.
// ARIA level should already be set when the container markup is created.
let currentLevel = this.tagLine.getAttribute("aria-level");
let newLevel = this.level;
if (currentLevel === newLevel) {

Просмотреть файл

@ -12,9 +12,7 @@ const nodeConstants = require("devtools/shared/dom-node-constants");
function ReadOnlyEditor(container, node) {
this.container = container;
this.markup = this.container.markup;
this.template = this.markup.template.bind(this.markup);
this.elt = null;
this.template("generic", this);
this.buildMarkup();
if (node.isPseudoElement) {
this.tag.classList.add("theme-fg-color5");
@ -28,8 +26,26 @@ function ReadOnlyEditor(container, node) {
}
ReadOnlyEditor.prototype = {
buildMarkup: function () {
let doc = this.markup.doc;
this.elt = doc.createElement("span");
this.elt.classList.add("editor");
this.tag = doc.createElement("span");
this.tag.classList.add("tag");
this.elt.appendChild(this.tag);
},
destroy: function () {
// We might be already destroyed.
if (!this.elt) {
return;
}
this.elt.remove();
this.elt = null;
this.tag = null;
},
/**

Просмотреть файл

@ -20,17 +20,16 @@ const INSPECTOR_L10N =
* The container owning this editor.
* @param {DOMNode} node
* The node being edited.
* @param {String} templateId
* The template id to use to build the editor.
* @param {String} type
* The type of editor to build. This can be either 'text' or 'comment'.
*/
function TextEditor(container, node, templateId) {
function TextEditor(container, node, type) {
this.container = container;
this.markup = this.container.markup;
this.node = node;
this.template = this.markup.template.bind(templateId);
this._selected = false;
this.markup.template(templateId, this);
this.buildMarkup(type);
editableField({
element: this.value,
@ -63,6 +62,32 @@ function TextEditor(container, node, templateId) {
}
TextEditor.prototype = {
buildMarkup: function (type) {
let doc = this.markup.doc;
this.elt = doc.createElement("span");
this.elt.classList.add("editor", type);
if (type === "comment") {
this.elt.classList.add("theme-comment");
let openComment = doc.createElement("span");
openComment.textContent = "<!--";
this.elt.appendChild(openComment);
}
this.value = doc.createElement("pre");
this.value.setAttribute("style", "display:inline-block;white-space: normal;");
this.value.setAttribute("tabindex", "-1");
this.elt.appendChild(this.value);
if (type === "comment") {
let closeComment = doc.createElement("span");
closeComment.textContent = "-->";
this.elt.appendChild(closeComment);
}
},
get selected() {
return this._selected;
},