summaryrefslogtreecommitdiffstats
path: root/toolkit/components/pdfjs/content/web/debugger.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/pdfjs/content/web/debugger.js')
-rw-r--r--toolkit/components/pdfjs/content/web/debugger.js623
1 files changed, 623 insertions, 0 deletions
diff --git a/toolkit/components/pdfjs/content/web/debugger.js b/toolkit/components/pdfjs/content/web/debugger.js
new file mode 100644
index 0000000000..c79aeeecf9
--- /dev/null
+++ b/toolkit/components/pdfjs/content/web/debugger.js
@@ -0,0 +1,623 @@
+/* Copyright 2012 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* eslint-disable no-var */
+
+"use strict";
+
+var FontInspector = (function FontInspectorClosure() {
+ var fonts;
+ var active = false;
+ var fontAttribute = "data-font-name";
+ function removeSelection() {
+ const divs = document.querySelectorAll(`span[${fontAttribute}]`);
+ for (const div of divs) {
+ div.className = "";
+ }
+ }
+ function resetSelection() {
+ const divs = document.querySelectorAll(`span[${fontAttribute}]`);
+ for (const div of divs) {
+ div.className = "debuggerHideText";
+ }
+ }
+ function selectFont(fontName, show) {
+ const divs = document.querySelectorAll(
+ `span[${fontAttribute}=${fontName}]`
+ );
+ for (const div of divs) {
+ div.className = show ? "debuggerShowText" : "debuggerHideText";
+ }
+ }
+ function textLayerClick(e) {
+ if (
+ !e.target.dataset.fontName ||
+ e.target.tagName.toUpperCase() !== "SPAN"
+ ) {
+ return;
+ }
+ var fontName = e.target.dataset.fontName;
+ var selects = document.getElementsByTagName("input");
+ for (var i = 0; i < selects.length; ++i) {
+ var select = selects[i];
+ if (select.dataset.fontName !== fontName) {
+ continue;
+ }
+ select.checked = !select.checked;
+ selectFont(fontName, select.checked);
+ select.scrollIntoView();
+ }
+ }
+ return {
+ // Properties/functions needed by PDFBug.
+ id: "FontInspector",
+ name: "Font Inspector",
+ panel: null,
+ manager: null,
+ init: function init(pdfjsLib) {
+ var panel = this.panel;
+ var tmp = document.createElement("button");
+ tmp.addEventListener("click", resetSelection);
+ tmp.textContent = "Refresh";
+ panel.appendChild(tmp);
+
+ fonts = document.createElement("div");
+ panel.appendChild(fonts);
+ },
+ cleanup: function cleanup() {
+ fonts.textContent = "";
+ },
+ enabled: false,
+ get active() {
+ return active;
+ },
+ set active(value) {
+ active = value;
+ if (active) {
+ document.body.addEventListener("click", textLayerClick, true);
+ resetSelection();
+ } else {
+ document.body.removeEventListener("click", textLayerClick, true);
+ removeSelection();
+ }
+ },
+ // FontInspector specific functions.
+ fontAdded: function fontAdded(fontObj, url) {
+ function properties(obj, list) {
+ var moreInfo = document.createElement("table");
+ for (var i = 0; i < list.length; i++) {
+ var tr = document.createElement("tr");
+ var td1 = document.createElement("td");
+ td1.textContent = list[i];
+ tr.appendChild(td1);
+ var td2 = document.createElement("td");
+ td2.textContent = obj[list[i]].toString();
+ tr.appendChild(td2);
+ moreInfo.appendChild(tr);
+ }
+ return moreInfo;
+ }
+ var moreInfo = properties(fontObj, ["name", "type"]);
+ const fontName = fontObj.loadedName;
+ var font = document.createElement("div");
+ var name = document.createElement("span");
+ name.textContent = fontName;
+ var download = document.createElement("a");
+ if (url) {
+ url = /url\(['"]?([^)"']+)/.exec(url);
+ download.href = url[1];
+ } else if (fontObj.data) {
+ download.href = URL.createObjectURL(
+ new Blob([fontObj.data], { type: fontObj.mimeType })
+ );
+ }
+ download.textContent = "Download";
+ var logIt = document.createElement("a");
+ logIt.href = "";
+ logIt.textContent = "Log";
+ logIt.addEventListener("click", function (event) {
+ event.preventDefault();
+ console.log(fontObj);
+ });
+ const select = document.createElement("input");
+ select.setAttribute("type", "checkbox");
+ select.dataset.fontName = fontName;
+ select.addEventListener("click", function () {
+ selectFont(fontName, select.checked);
+ });
+ font.appendChild(select);
+ font.appendChild(name);
+ font.appendChild(document.createTextNode(" "));
+ font.appendChild(download);
+ font.appendChild(document.createTextNode(" "));
+ font.appendChild(logIt);
+ font.appendChild(moreInfo);
+ fonts.appendChild(font);
+ // Somewhat of a hack, should probably add a hook for when the text layer
+ // is done rendering.
+ setTimeout(() => {
+ if (this.active) {
+ resetSelection();
+ }
+ }, 2000);
+ },
+ };
+})();
+
+var opMap;
+
+// Manages all the page steppers.
+var StepperManager = (function StepperManagerClosure() {
+ var steppers = [];
+ var stepperDiv = null;
+ var stepperControls = null;
+ var stepperChooser = null;
+ var breakPoints = Object.create(null);
+ return {
+ // Properties/functions needed by PDFBug.
+ id: "Stepper",
+ name: "Stepper",
+ panel: null,
+ manager: null,
+ init: function init(pdfjsLib) {
+ var self = this;
+ stepperControls = document.createElement("div");
+ stepperChooser = document.createElement("select");
+ stepperChooser.addEventListener("change", function (event) {
+ self.selectStepper(this.value);
+ });
+ stepperControls.appendChild(stepperChooser);
+ stepperDiv = document.createElement("div");
+ this.panel.appendChild(stepperControls);
+ this.panel.appendChild(stepperDiv);
+ if (sessionStorage.getItem("pdfjsBreakPoints")) {
+ breakPoints = JSON.parse(sessionStorage.getItem("pdfjsBreakPoints"));
+ }
+
+ opMap = Object.create(null);
+ for (var key in pdfjsLib.OPS) {
+ opMap[pdfjsLib.OPS[key]] = key;
+ }
+ },
+ cleanup: function cleanup() {
+ stepperChooser.textContent = "";
+ stepperDiv.textContent = "";
+ steppers = [];
+ },
+ enabled: false,
+ active: false,
+ // Stepper specific functions.
+ create: function create(pageIndex) {
+ var debug = document.createElement("div");
+ debug.id = "stepper" + pageIndex;
+ debug.setAttribute("hidden", true);
+ debug.className = "stepper";
+ stepperDiv.appendChild(debug);
+ var b = document.createElement("option");
+ b.textContent = "Page " + (pageIndex + 1);
+ b.value = pageIndex;
+ stepperChooser.appendChild(b);
+ var initBreakPoints = breakPoints[pageIndex] || [];
+ var stepper = new Stepper(debug, pageIndex, initBreakPoints);
+ steppers.push(stepper);
+ if (steppers.length === 1) {
+ this.selectStepper(pageIndex, false);
+ }
+ return stepper;
+ },
+ selectStepper: function selectStepper(pageIndex, selectPanel) {
+ var i;
+ pageIndex = pageIndex | 0;
+ if (selectPanel) {
+ this.manager.selectPanel(this);
+ }
+ for (i = 0; i < steppers.length; ++i) {
+ var stepper = steppers[i];
+ if (stepper.pageIndex === pageIndex) {
+ stepper.panel.removeAttribute("hidden");
+ } else {
+ stepper.panel.setAttribute("hidden", true);
+ }
+ }
+ var options = stepperChooser.options;
+ for (i = 0; i < options.length; ++i) {
+ var option = options[i];
+ option.selected = (option.value | 0) === pageIndex;
+ }
+ },
+ saveBreakPoints: function saveBreakPoints(pageIndex, bps) {
+ breakPoints[pageIndex] = bps;
+ sessionStorage.setItem("pdfjsBreakPoints", JSON.stringify(breakPoints));
+ },
+ };
+})();
+
+// The stepper for each page's IRQueue.
+var Stepper = (function StepperClosure() {
+ // Shorter way to create element and optionally set textContent.
+ function c(tag, textContent) {
+ var d = document.createElement(tag);
+ if (textContent) {
+ d.textContent = textContent;
+ }
+ return d;
+ }
+
+ function simplifyArgs(args) {
+ if (typeof args === "string") {
+ var MAX_STRING_LENGTH = 75;
+ return args.length <= MAX_STRING_LENGTH
+ ? args
+ : args.substring(0, MAX_STRING_LENGTH) + "...";
+ }
+ if (typeof args !== "object" || args === null) {
+ return args;
+ }
+ if ("length" in args) {
+ // array
+ var simpleArgs = [],
+ i,
+ ii;
+ var MAX_ITEMS = 10;
+ for (i = 0, ii = Math.min(MAX_ITEMS, args.length); i < ii; i++) {
+ simpleArgs.push(simplifyArgs(args[i]));
+ }
+ if (i < args.length) {
+ simpleArgs.push("...");
+ }
+ return simpleArgs;
+ }
+ var simpleObj = {};
+ for (var key in args) {
+ simpleObj[key] = simplifyArgs(args[key]);
+ }
+ return simpleObj;
+ }
+
+ // eslint-disable-next-line no-shadow
+ function Stepper(panel, pageIndex, initialBreakPoints) {
+ this.panel = panel;
+ this.breakPoint = 0;
+ this.nextBreakPoint = null;
+ this.pageIndex = pageIndex;
+ this.breakPoints = initialBreakPoints;
+ this.currentIdx = -1;
+ this.operatorListIdx = 0;
+ }
+ Stepper.prototype = {
+ init: function init(operatorList) {
+ var panel = this.panel;
+ var content = c("div", "c=continue, s=step");
+ var table = c("table");
+ content.appendChild(table);
+ table.cellSpacing = 0;
+ var headerRow = c("tr");
+ table.appendChild(headerRow);
+ headerRow.appendChild(c("th", "Break"));
+ headerRow.appendChild(c("th", "Idx"));
+ headerRow.appendChild(c("th", "fn"));
+ headerRow.appendChild(c("th", "args"));
+ panel.appendChild(content);
+ this.table = table;
+ this.updateOperatorList(operatorList);
+ },
+ updateOperatorList: function updateOperatorList(operatorList) {
+ var self = this;
+
+ function cboxOnClick() {
+ var x = +this.dataset.idx;
+ if (this.checked) {
+ self.breakPoints.push(x);
+ } else {
+ self.breakPoints.splice(self.breakPoints.indexOf(x), 1);
+ }
+ StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);
+ }
+
+ var MAX_OPERATORS_COUNT = 15000;
+ if (this.operatorListIdx > MAX_OPERATORS_COUNT) {
+ return;
+ }
+
+ var chunk = document.createDocumentFragment();
+ var operatorsToDisplay = Math.min(
+ MAX_OPERATORS_COUNT,
+ operatorList.fnArray.length
+ );
+ for (var i = this.operatorListIdx; i < operatorsToDisplay; i++) {
+ var line = c("tr");
+ line.className = "line";
+ line.dataset.idx = i;
+ chunk.appendChild(line);
+ var checked = this.breakPoints.includes(i);
+ var args = operatorList.argsArray[i] || [];
+
+ var breakCell = c("td");
+ var cbox = c("input");
+ cbox.type = "checkbox";
+ cbox.className = "points";
+ cbox.checked = checked;
+ cbox.dataset.idx = i;
+ cbox.onclick = cboxOnClick;
+
+ breakCell.appendChild(cbox);
+ line.appendChild(breakCell);
+ line.appendChild(c("td", i.toString()));
+ var fn = opMap[operatorList.fnArray[i]];
+ var decArgs = args;
+ if (fn === "showText") {
+ var glyphs = args[0];
+ var newArgs = [];
+ var str = [];
+ for (var j = 0; j < glyphs.length; j++) {
+ var glyph = glyphs[j];
+ if (typeof glyph === "object" && glyph !== null) {
+ str.push(glyph.fontChar);
+ } else {
+ if (str.length > 0) {
+ newArgs.push(str.join(""));
+ str = [];
+ }
+ newArgs.push(glyph); // null or number
+ }
+ }
+ if (str.length > 0) {
+ newArgs.push(str.join(""));
+ }
+ decArgs = [newArgs];
+ }
+ line.appendChild(c("td", fn));
+ line.appendChild(c("td", JSON.stringify(simplifyArgs(decArgs))));
+ }
+ if (operatorsToDisplay < operatorList.fnArray.length) {
+ var lastCell = c("td", "...");
+ lastCell.colspan = 4;
+ chunk.appendChild(lastCell);
+ }
+ this.operatorListIdx = operatorList.fnArray.length;
+ this.table.appendChild(chunk);
+ },
+ getNextBreakPoint: function getNextBreakPoint() {
+ this.breakPoints.sort(function (a, b) {
+ return a - b;
+ });
+ for (var i = 0; i < this.breakPoints.length; i++) {
+ if (this.breakPoints[i] > this.currentIdx) {
+ return this.breakPoints[i];
+ }
+ }
+ return null;
+ },
+ breakIt: function breakIt(idx, callback) {
+ StepperManager.selectStepper(this.pageIndex, true);
+ var self = this;
+ var dom = document;
+ self.currentIdx = idx;
+ var listener = function (e) {
+ switch (e.keyCode) {
+ case 83: // step
+ dom.removeEventListener("keydown", listener);
+ self.nextBreakPoint = self.currentIdx + 1;
+ self.goTo(-1);
+ callback();
+ break;
+ case 67: // continue
+ dom.removeEventListener("keydown", listener);
+ var breakPoint = self.getNextBreakPoint();
+ self.nextBreakPoint = breakPoint;
+ self.goTo(-1);
+ callback();
+ break;
+ }
+ };
+ dom.addEventListener("keydown", listener);
+ self.goTo(idx);
+ },
+ goTo: function goTo(idx) {
+ var allRows = this.panel.getElementsByClassName("line");
+ for (var x = 0, xx = allRows.length; x < xx; ++x) {
+ var row = allRows[x];
+ if ((row.dataset.idx | 0) === idx) {
+ row.style.backgroundColor = "rgb(251,250,207)";
+ row.scrollIntoView();
+ } else {
+ row.style.backgroundColor = null;
+ }
+ }
+ },
+ };
+ return Stepper;
+})();
+
+var Stats = (function Stats() {
+ var stats = [];
+ function clear(node) {
+ while (node.hasChildNodes()) {
+ node.removeChild(node.lastChild);
+ }
+ }
+ function getStatIndex(pageNumber) {
+ for (var i = 0, ii = stats.length; i < ii; ++i) {
+ if (stats[i].pageNumber === pageNumber) {
+ return i;
+ }
+ }
+ return false;
+ }
+ return {
+ // Properties/functions needed by PDFBug.
+ id: "Stats",
+ name: "Stats",
+ panel: null,
+ manager: null,
+ init(pdfjsLib) {},
+ enabled: false,
+ active: false,
+ // Stats specific functions.
+ add(pageNumber, stat) {
+ if (!stat) {
+ return;
+ }
+ var statsIndex = getStatIndex(pageNumber);
+ if (statsIndex !== false) {
+ const b = stats[statsIndex];
+ this.panel.removeChild(b.div);
+ stats.splice(statsIndex, 1);
+ }
+ var wrapper = document.createElement("div");
+ wrapper.className = "stats";
+ var title = document.createElement("div");
+ title.className = "title";
+ title.textContent = "Page: " + pageNumber;
+ var statsDiv = document.createElement("div");
+ statsDiv.textContent = stat.toString();
+ wrapper.appendChild(title);
+ wrapper.appendChild(statsDiv);
+ stats.push({ pageNumber, div: wrapper });
+ stats.sort(function (a, b) {
+ return a.pageNumber - b.pageNumber;
+ });
+ clear(this.panel);
+ for (var i = 0, ii = stats.length; i < ii; ++i) {
+ this.panel.appendChild(stats[i].div);
+ }
+ },
+ cleanup() {
+ stats = [];
+ clear(this.panel);
+ },
+ };
+})();
+
+// Manages all the debugging tools.
+window.PDFBug = (function PDFBugClosure() {
+ var panelWidth = 300;
+ var buttons = [];
+ var activePanel = null;
+
+ return {
+ tools: [FontInspector, StepperManager, Stats],
+ enable(ids) {
+ var all = false,
+ tools = this.tools;
+ if (ids.length === 1 && ids[0] === "all") {
+ all = true;
+ }
+ for (var i = 0; i < tools.length; ++i) {
+ var tool = tools[i];
+ if (all || ids.includes(tool.id)) {
+ tool.enabled = true;
+ }
+ }
+ if (!all) {
+ // Sort the tools by the order they are enabled.
+ tools.sort(function (a, b) {
+ var indexA = ids.indexOf(a.id);
+ indexA = indexA < 0 ? tools.length : indexA;
+ var indexB = ids.indexOf(b.id);
+ indexB = indexB < 0 ? tools.length : indexB;
+ return indexA - indexB;
+ });
+ }
+ },
+ init(pdfjsLib, container) {
+ /*
+ * Basic Layout:
+ * PDFBug
+ * Controls
+ * Panels
+ * Panel
+ * Panel
+ * ...
+ */
+ var ui = document.createElement("div");
+ ui.id = "PDFBug";
+
+ var controls = document.createElement("div");
+ controls.setAttribute("class", "controls");
+ ui.appendChild(controls);
+
+ var panels = document.createElement("div");
+ panels.setAttribute("class", "panels");
+ ui.appendChild(panels);
+
+ container.appendChild(ui);
+ container.style.right = panelWidth + "px";
+
+ // Initialize all the debugging tools.
+ var tools = this.tools;
+ var self = this;
+ for (var i = 0; i < tools.length; ++i) {
+ var tool = tools[i];
+ var panel = document.createElement("div");
+ var panelButton = document.createElement("button");
+ panelButton.textContent = tool.name;
+ panelButton.addEventListener(
+ "click",
+ (function (selected) {
+ return function (event) {
+ event.preventDefault();
+ self.selectPanel(selected);
+ };
+ })(i)
+ );
+ controls.appendChild(panelButton);
+ panels.appendChild(panel);
+ tool.panel = panel;
+ tool.manager = this;
+ if (tool.enabled) {
+ tool.init(pdfjsLib);
+ } else {
+ panel.textContent =
+ tool.name +
+ " is disabled. To enable add " +
+ ' "' +
+ tool.id +
+ '" to the pdfBug parameter ' +
+ "and refresh (separate multiple by commas).";
+ }
+ buttons.push(panelButton);
+ }
+ this.selectPanel(0);
+ },
+ cleanup() {
+ for (var i = 0, ii = this.tools.length; i < ii; i++) {
+ if (this.tools[i].enabled) {
+ this.tools[i].cleanup();
+ }
+ }
+ },
+ selectPanel(index) {
+ if (typeof index !== "number") {
+ index = this.tools.indexOf(index);
+ }
+ if (index === activePanel) {
+ return;
+ }
+ activePanel = index;
+ var tools = this.tools;
+ for (var j = 0; j < tools.length; ++j) {
+ if (j === index) {
+ buttons[j].setAttribute("class", "active");
+ tools[j].active = true;
+ tools[j].panel.removeAttribute("hidden");
+ } else {
+ buttons[j].setAttribute("class", "");
+ tools[j].active = false;
+ tools[j].panel.setAttribute("hidden", "true");
+ }
+ }
+ },
+ };
+})();