v8/tools/turbolizer/code-view.js
Leszek Swirski 7b8d8e9a54 [turbolizer] Use textContent to set code view
Previously code view was set using innerHTML. This would cause problems
for html characters in the code -- in particular, '<' without a space
after it would start new HTML tags, and the code following it wouldn't
be visible.

Now, the source text is set using textContent, which doesn't parse the
value as HTML and implicitly escapes any HTML characters in the code.

Change-Id: I612a18c37bbb4da6a87063bb39d7f7123a3c4c0d
Reviewed-on: https://chromium-review.googlesource.com/461826
Reviewed-by: Daniel Clifford <danno@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#44233}
2017-03-29 14:28:32 +00:00

173 lines
5.6 KiB
JavaScript

// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
"use strict";
class CodeView extends View {
constructor(divID, PR, sourceText, sourcePosition, broker) {
super(divID, broker, null, false);
let view = this;
view.PR = PR;
view.mouseDown = false;
view.broker = broker;
view.allSpans = [];
var selectionHandler = {
clear: function() { broker.clear(selectionHandler); },
select: function(items, selected) {
var handler = this;
var broker = view.broker;
for (let span of items) {
if (selected) {
span.classList.add("selected");
} else {
span.classList.remove("selected");
}
}
var locations = [];
for (var span of items) {
locations.push({pos_start: span.start, pos_end: span.end});
}
broker.clear(selectionHandler);
broker.select(selectionHandler, locations, selected);
},
selectionDifference: function(span1, inclusive1, span2, inclusive2) {
var pos1 = span1.start;
var pos2 = span2.start;
var result = [];
var lineListDiv = view.divNode.firstChild.firstChild.childNodes;
for (var i = 0; i < lineListDiv.length; i++) {
var currentLineElement = lineListDiv[i];
var spans = currentLineElement.childNodes;
for (var j = 0; j < spans.length; ++j) {
var currentSpan = spans[j];
if (currentSpan.start > pos1 ||
(inclusive1 && currentSpan.start == pos1)) {
if (currentSpan.start < pos2 ||
(inclusive2 && currentSpan.start == pos2)) {
result.push(currentSpan);
}
}
}
}
return result;
},
brokeredSelect: function(locations, selected) {
let firstSelect = view.selection.isEmpty();
for (let location of locations) {
let start = location.pos_start;
let end = location.pos_end;
if (start && end) {
let lower = 0;
let upper = view.allSpans.length;
if (upper > 0) {
while ((upper - lower) > 1) {
var middle = Math.floor((upper + lower) / 2);
var lineStart = view.allSpans[middle].start;
if (lineStart < start) {
lower = middle;
} else if (lineStart > start) {
upper = middle;
} else {
lower = middle;
break;
}
}
var currentSpan = view.allSpans[lower];
var currentLineElement = currentSpan.parentNode;
if ((currentSpan.start <= start && start < currentSpan.end) ||
(currentSpan.start <= end && end < currentSpan.end)) {
if (firstSelect) {
makeContainerPosVisible(
view.divNode, currentLineElement.offsetTop);
firstSelect = false;
}
view.selection.select(currentSpan, selected);
}
}
}
}
},
brokeredClear: function() { view.selection.clear(); },
};
view.selection = new Selection(selectionHandler);
broker.addSelectionHandler(selectionHandler);
view.handleSpanMouseDown = function(e) {
e.stopPropagation();
if (!e.shiftKey) {
view.selection.clear();
}
view.selection.select(this, true);
view.mouseDown = true;
}
view.handleSpanMouseMove = function(e) {
if (view.mouseDown) {
view.selection.extendTo(this);
}
}
view.handleCodeMouseDown = function(e) { view.selection.clear(); }
document.addEventListener('mouseup', function(e) {
view.mouseDown = false;
}, false);
view.initializeCode(sourceText, sourcePosition);
}
initializeContent(data, rememberedSelection) { this.data = data; }
initializeCode(sourceText, sourcePosition) {
var view = this;
var codePre = document.createElement("pre");
codePre.classList.add("prettyprint");
view.divNode.innerHTML = "";
view.divNode.appendChild(codePre);
if (sourceText != "") {
codePre.classList.add("linenums");
codePre.textContent = sourceText;
try {
// Wrap in try to work when offline.
view.PR.prettyPrint();
} catch (e) {
}
view.divNode.onmousedown = this.handleCodeMouseDown;
var base = sourcePosition;
var current = 0;
var lineListDiv = view.divNode.firstChild.firstChild.childNodes;
for (let i = 0; i < lineListDiv.length; i++) {
var currentLineElement = lineListDiv[i];
currentLineElement.id = "li" + i;
var pos = base + current;
currentLineElement.pos = pos;
var spans = currentLineElement.childNodes;
for (let j = 0; j < spans.length; ++j) {
var currentSpan = spans[j];
if (currentSpan.nodeType == 1) {
currentSpan.start = pos;
currentSpan.end = pos + currentSpan.textContent.length;
currentSpan.onmousedown = this.handleSpanMouseDown;
currentSpan.onmousemove = this.handleSpanMouseMove;
view.allSpans.push(currentSpan);
}
current += currentSpan.textContent.length;
pos = base + current;
}
while ((current < sourceText.length) &&
(sourceText[current] == '\n' || sourceText[current] == '\r')) {
++current;
}
}
}
view.resizeToParent();
}
deleteContent() {}
}