browser/devtools/fontinspector/font-inspector.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:15b727c8fe73
1 /* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 "use strict";
8
9 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
10 const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
11
12 function FontInspector(inspector, window)
13 {
14 this.inspector = inspector;
15 this.chromeDoc = window.document;
16 this.init();
17 }
18
19 FontInspector.prototype = {
20 init: function FI_init() {
21 this.update = this.update.bind(this);
22 this.onNewNode = this.onNewNode.bind(this);
23 this.inspector.selection.on("new-node", this.onNewNode);
24 this.inspector.sidebar.on("fontinspector-selected", this.onNewNode);
25 this.showAll = this.showAll.bind(this);
26 this.showAllButton = this.chromeDoc.getElementById("showall");
27 this.showAllButton.addEventListener("click", this.showAll);
28 this.update();
29 },
30
31 /**
32 * Is the fontinspector visible in the sidebar?
33 */
34 isActive: function FI_isActive() {
35 return this.inspector.sidebar &&
36 this.inspector.sidebar.getCurrentTabID() == "fontinspector";
37 },
38
39 /**
40 * Remove listeners.
41 */
42 destroy: function FI_destroy() {
43 this.chromeDoc = null;
44 this.inspector.sidebar.off("layoutview-selected", this.onNewNode);
45 this.inspector.selection.off("new-node", this.onNewNode);
46 this.showAllButton.removeEventListener("click", this.showAll);
47 },
48
49 /**
50 * Selection 'new-node' event handler.
51 */
52 onNewNode: function FI_onNewNode() {
53 if (this.isActive() &&
54 this.inspector.selection.isLocal() &&
55 this.inspector.selection.isConnected() &&
56 this.inspector.selection.isElementNode()) {
57 this.undim();
58 this.update();
59 } else {
60 this.dim();
61 }
62 },
63
64 /**
65 * Hide the font list. No node are selected.
66 */
67 dim: function FI_dim() {
68 this.chromeDoc.body.classList.add("dim");
69 this.chromeDoc.querySelector("#all-fonts").innerHTML = "";
70 },
71
72 /**
73 * Show the font list. A node is selected.
74 */
75 undim: function FI_undim() {
76 this.chromeDoc.body.classList.remove("dim");
77 },
78
79 /**
80 * Retrieve all the font related info we have for the selected
81 * node and display them.
82 */
83 update: function FI_update() {
84 if (!this.isActive() ||
85 !this.inspector.selection.isConnected() ||
86 !this.inspector.selection.isElementNode() ||
87 this.chromeDoc.body.classList.contains("dim")) {
88 return;
89 }
90
91 let node = this.inspector.selection.node;
92 let contentDocument = node.ownerDocument;
93
94 // We don't get fonts for a node, but for a range
95 let rng = contentDocument.createRange();
96 rng.selectNode(node);
97 let fonts = DOMUtils.getUsedFontFaces(rng);
98 let fontsArray = [];
99 for (let i = 0; i < fonts.length; i++) {
100 fontsArray.push(fonts.item(i));
101 }
102 fontsArray = fontsArray.sort(function(a, b) {
103 return a.srcIndex < b.srcIndex;
104 });
105 this.chromeDoc.querySelector("#all-fonts").innerHTML = "";
106 for (let f of fontsArray) {
107 this.render(f, contentDocument);
108 }
109 },
110
111 /**
112 * Display the information of one font.
113 */
114 render: function FI_render(font, document) {
115 let s = this.chromeDoc.querySelector("#template > section");
116 s = s.cloneNode(true);
117
118 s.querySelector(".font-name").textContent = font.name;
119 s.querySelector(".font-css-name").textContent = font.CSSFamilyName;
120 s.querySelector(".font-format").textContent = font.format;
121
122 if (font.srcIndex == -1) {
123 s.classList.add("is-local");
124 } else {
125 s.classList.add("is-remote");
126 }
127
128 s.querySelector(".font-url").value = font.URI;
129
130 let iframe = s.querySelector(".font-preview");
131 if (font.rule) {
132 // This is the @font-face{…} code.
133 let cssText = font.rule.style.parentRule.cssText;
134
135 s.classList.add("has-code");
136 s.querySelector(".font-css-code").textContent = cssText;
137
138 // We guess the base URL of the stylesheet to make
139 // sure the font will be accessible in the preview.
140 // If the font-face is in an inline <style>, we get
141 // the location of the page.
142 let origin = font.rule.style.parentRule.parentStyleSheet.href;
143 if (!origin) { // Inline stylesheet
144 origin = document.location.href;
145 }
146 // We remove the last part of the URL to get a correct base.
147 let base = origin.replace(/\/[^\/]*$/,"/")
148
149 // From all this information, we build a preview.
150 this.buildPreview(iframe, font.CSSFamilyName, cssText, base);
151 } else {
152 this.buildPreview(iframe, font.CSSFamilyName, "", "");
153 }
154
155 this.chromeDoc.querySelector("#all-fonts").appendChild(s);
156 },
157
158 /**
159 * Show a preview of the font in an iframe.
160 */
161 buildPreview: function FI_buildPreview(iframe, name, cssCode, base) {
162 /* The HTML code of the preview is:
163 * <!DOCTYPE HTML>
164 * <head>
165 * <base href="{base}"></base>
166 * </head>
167 * <style>
168 * p {font-family: {name};}
169 * * {font-size: 40px;line-height:60px;padding:0 10px;margin:0};
170 * </style>
171 * <p contenteditable spellcheck='false'>Abc</p>
172 */
173 let extraCSS = "* {padding:0;margin:0}";
174 extraCSS += ".theme-dark {color: white}";
175 extraCSS += "p {font-size: 40px;line-height:60px;padding:0 10px;margin:0;}";
176 cssCode += extraCSS;
177 let src = "data:text/html;charset=utf-8,<!DOCTYPE HTML><head><base></base></head><style></style><p contenteditable spellcheck='false'>Abc</p>";
178 iframe.addEventListener("load", function onload() {
179 iframe.removeEventListener("load", onload, true);
180 let doc = iframe.contentWindow.document;
181 // We could have done that earlier, but we want to avoid any URL-encoding
182 // nightmare.
183 doc.querySelector("base").href = base;
184 doc.querySelector("style").textContent = cssCode;
185 doc.querySelector("p").style.fontFamily = name;
186 // Forward theme
187 doc.documentElement.className = document.documentElement.className;
188 }, true);
189 iframe.src = src;
190 },
191
192 /**
193 * Select the <body> to show all the fonts included in the document.
194 */
195 showAll: function FI_showAll() {
196 if (!this.isActive() ||
197 !this.inspector.selection.isConnected() ||
198 !this.inspector.selection.isElementNode()) {
199 return;
200 }
201
202 // Select the body node to show all fonts
203 let walker = this.inspector.walker;
204
205 walker.getRootNode().then(root => walker.querySelector(root, "body")).then(body => {
206 this.inspector.selection.setNodeFront(body, "fontinspector");
207 });
208 },
209 }
210
211 window.setPanel = function(panel) {
212 window.fontInspector = new FontInspector(panel, window);
213 }
214
215 window.onunload = function() {
216 if (window.fontInspector) {
217 window.fontInspector.destroy();
218 }
219 }

mercurial