Bug 697983 - Implement a Font Inspector. r=jwalker

This commit is contained in:
Paul Rouget 2013-03-07 05:57:00 +01:00
parent b4ecee97e5
commit 74ab902709
23 changed files with 706 additions and 1 deletions

View File

@ -1146,6 +1146,9 @@ pref("devtools.editor.expandtab", true);
// indenting and bracket recognition.
pref("devtools.editor.component", "orion");
// Enable the Font Inspector
pref("devtools.fontinspector.enabled", true);
// Whether the character encoding menu is under the main Firefox button. This
// preference is a string so that localizers can alter it.
pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties");

View File

@ -0,0 +1,13 @@
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
.dim > #root,
.font:not(.has-code) .font-css-code,
.font-is-local,
.font-is-remote,
.font.is-local .font-format-url {
display: none;
}
.font.is-remote .font-is-remote,
.font.is-local .font-is-local {
display: inline;
}

View File

@ -0,0 +1,226 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
function FontInspector(inspector, window)
{
this.inspector = inspector;
this.chromeDoc = window.document;
this.init();
}
FontInspector.prototype = {
init: function FI_init() {
this.update = this.update.bind(this);
this.onNewNode = this.onNewNode.bind(this);
this.onHighlighterLocked = this.onHighlighterLocked.bind(this);
this.inspector.selection.on("new-node", this.onNewNode);
this.inspector.sidebar.on("fontinspector-selected", this.onNewNode);
if (this.inspector.highlighter) {
this.inspector.highlighter.on("locked", this.onHighlighterLocked);
}
this.update();
},
/**
* Is the fontinspector visible in the sidebar?
*/
isActive: function FI_isActive() {
return this.inspector.sidebar &&
this.inspector.sidebar.getCurrentTabID() == "fontinspector";
},
/**
* Remove listeners.
*/
destroy: function FI_destroy() {
this.chromeDoc = null;
this.inspector.sidebar.off("layoutview-selected", this.onNewNode);
this.inspector.selection.off("new-node", this.onNewNode);
if (this.inspector.highlighter) {
this.inspector.highlighter.off("locked", this.onHighlighterLocked);
}
},
/**
* Selection 'new-node' event handler.
*/
onNewNode: function FI_onNewNode() {
if (this.isActive() &&
this.inspector.selection.isConnected() &&
this.inspector.selection.isElementNode() &&
this.inspector.selection.reason != "highlighter") {
this.undim();
this.update();
} else {
this.dim();
}
},
/**
* Highlighter 'locked' event handler
*/
onHighlighterLocked: function FI_onHighlighterLocked() {
this.undim();
this.update();
},
/**
* Hide the font list. No node are selected.
*/
dim: function FI_dim() {
this.chromeDoc.body.classList.add("dim");
this.chromeDoc.querySelector("#all-fonts").innerHTML = "";
},
/**
* Show the font list. A node is selected.
*/
undim: function FI_undim() {
this.chromeDoc.body.classList.remove("dim");
},
/**
* Retrieve all the font related info we have for the selected
* node and display them.
*/
update: function FI_update() {
if (!this.isActive() ||
!this.inspector.selection.isConnected() ||
!this.inspector.selection.isElementNode() ||
this.chromeDoc.body.classList.contains("dim")) {
return;
}
let node = this.inspector.selection.node;
let contentDocument = node.ownerDocument;
// We don't get fonts for a node, but for a range
let rng = contentDocument.createRange();
rng.selectNode(node);
let fonts = DOMUtils.getUsedFontFaces(rng);
let fontsArray = [];
for (let i = 0; i < fonts.length; i++) {
fontsArray.push(fonts.item(i));
}
fontsArray = fontsArray.sort(function(a, b) {
return a.srcIndex < b.srcIndex;
});
this.chromeDoc.querySelector("#all-fonts").innerHTML = "";
for (let f of fontsArray) {
this.render(f, contentDocument);
}
},
/**
* Display the information of one font.
*/
render: function FI_render(font, document) {
let s = this.chromeDoc.querySelector("#template > section");
s = s.cloneNode(true);
s.querySelector(".font-name").textContent = font.name;
s.querySelector(".font-css-name").textContent = font.CSSFamilyName;
s.querySelector(".font-format").textContent = font.format;
if (font.srcIndex == -1) {
s.classList.add("is-local");
} else {
s.classList.add("is-remote");
}
s.querySelector(".font-url").value = font.URI;
let iframe = s.querySelector(".font-preview");
if (font.rule) {
// This is the @font-face{…} code.
let cssText = font.rule.style.parentRule.cssText;
s.classList.add("has-code");
s.querySelector(".font-css-code").textContent = cssText;
// We guess the base URL of the stylesheet to make
// sure the font will be accessible in the preview.
// If the font-face is in an inline <style>, we get
// the location of the page.
let origin = font.rule.style.parentRule.parentStyleSheet.href;
if (!origin) { // Inline stylesheet
origin = document.location.href;
}
// We remove the last part of the URL to get a correct base.
let base = origin.replace(/\/[^\/]*$/,"/")
// From all this information, we build a preview.
this.buildPreview(iframe, font.CSSFamilyName, cssText, base);
} else {
this.buildPreview(iframe, font.CSSFamilyName, "", "");
}
this.chromeDoc.querySelector("#all-fonts").appendChild(s);
},
/**
* Show a preview of the font in an iframe.
*/
buildPreview: function FI_buildPreview(iframe, name, cssCode, base) {
/* The HTML code of the preview is:
* <!DOCTYPE HTML>
* <head>
* <base href="{base}"></base>
* </head>
* <style>
* p {font-family: {name};}
* * {font-size: 40px;line-height:60px;padding:0 10px;margin:0};
* </style>
* <p contenteditable>Abc</p>
*/
let extraCSS = "* {padding:0;margin:0}";
extraCSS += "p {font-family: '" + name + "';}";
extraCSS += "p {font-size: 40px;line-height:60px;padding:0 10px;margin:0;}";
cssCode += extraCSS;
let src = "data:text/html;charset=utf-8,<!DOCTYPE HTML><head><base></base></head><style></style><p contenteditable>Abc</p>";
iframe.addEventListener("load", function onload() {
iframe.removeEventListener("load", onload, true);
let doc = iframe.contentWindow.document;
// We could have done that earlier, but we want to avoid any URL-encoding
// nightmare.
doc.querySelector("base").href = base;
doc.querySelector("style").textContent = cssCode;
}, true);
iframe.src = src;
},
/**
* Select the <body> to show all the fonts included in the document.
*/
showAll: function FI_showAll() {
if (!this.isActive() ||
!this.inspector.selection.isConnected() ||
!this.inspector.selection.isElementNode()) {
return;
}
let node = this.inspector.selection.node;
let contentDocument = node.ownerDocument;
let root = contentDocument.documentElement;
if (contentDocument.body) {
root = contentDocument.body;
}
this.inspector.selection.setNode(root, "fontinspector");
},
}
window.setPanel = function(panel) {
window.fontInspector = new FontInspector(panel, window);
}
window.onunload = function() {
window.fontInspector.destroy();
}

View File

@ -0,0 +1,41 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE html [
<!ENTITY % fontinspectorDTD SYSTEM "chrome://browser/locale/devtools/font-inspector.dtd" >
%fontinspectorDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&title;</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="font-inspector.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/devtools/common.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/devtools/font-inspector.css" type="text/css"/>
</head>
<body class="devtools-monospace" role="application">
<script type="application/javascript;version=1.8" src="font-inspector.js"></script>
<div id="root">
<ul id="all-fonts"></ul>
<button id="showall" onclick="fontInspector.showAll()">&showAllFonts;</button>
</div>
<div id="template" style="display:none">
<section class="font">
<iframe sandbox="allow-same-origin" class="font-preview"></iframe>
<div class="font-info">
<h1 class="font-name"></h1>
<span class="font-is-local">&system;</span>
<span class="font-is-remote">&remote;</span>
<p class="font-format-url">
<input readonly="readonly" class="font-url"></input>
(<span class="font-format"></span>)
</p>
<p class="font-css">&usedAs; "<span class="font-css-name"></span>"</p>
<pre class="font-css-code"></pre>
</div>
</section>
</div>
</body>
</html>

View File

@ -0,0 +1,6 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
TEST_DIRS += ['test']

View File

@ -0,0 +1,22 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_BROWSER_FILES = \
browser_fontinspector.js \
browser_fontinspector.html \
browser_font.woff \
$(NULL)
libs:: $(_BROWSER_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)

Binary file not shown.

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<style>
@font-face {
font-family: bar;
src: url(bad/font/name.ttf), url(browser_font.woff) format("woff");
}
body{
font-family:Arial;
}
div {
font-family:Arial;
font-family:bar;
}
</style>
<body>
BODY
<div>DIV</div>
</body>

View File

@ -0,0 +1,89 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let tempScope = {};
Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
let TargetFactory = tempScope.TargetFactory;
function test() {
waitForExplicitFinish();
let doc;
let node;
let view;
let inspector;
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
doc = content.document;
waitForFocus(setupTest, content);
}, true);
content.location = "http://mochi.test:8888/browser/browser/devtools/fontinspector/test/browser_fontinspector.html";
function setupTest() {
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
openFontInspector(toolbox.getCurrentPanel());
});
}
function openFontInspector(aInspector) {
inspector = aInspector;
info("Inspector open");
inspector.selection.setNode(doc.body);
inspector.sidebar.select("fontinspector");
inspector.sidebar.once("fontinspector-ready", viewReady);
}
function viewReady() {
info("Font Inspector ready");
view = inspector.sidebar.getWindowForTab("fontinspector");
ok(!!view.fontInspector, "Font inspector document is alive.");
let d = view.document;
let s = d.querySelectorAll("#all-fonts > section");
is(s.length, 2, "Found 2 fonts");
is(s[0].querySelector(".font-name").textContent,
"DeLarge Bold", "font 0: Right font name");
ok(s[0].classList.contains("is-remote"),
"font 0: is remote");
is(s[0].querySelector(".font-url").value,
"http://mochi.test:8888/browser/browser/devtools/fontinspector/test/browser_font.woff",
"font 0: right url");
is(s[0].querySelector(".font-format").textContent,
"woff", "font 0: right font format");
is(s[0].querySelector(".font-css-name").textContent,
"bar", "font 0: right css name");
let font1Name = s[1].querySelector(".font-name").textContent;
let font1CssName = s[1].querySelector(".font-css-name").textContent;
// On Linux test machines, the Arial font doesn't exist.
// The fallback is "Liberation Sans"
ok((font1Name == "Arial") || (font1Name == "Liberation Sans"),
"font 1: Right font name");
ok(s[1].classList.contains("is-local"), "font 1: is local");
ok((font1CssName == "Arial") || (font1CssName == "Liberation Sans"),
"Arial", "font 1: right css name");
executeSoon(function() {
gDevTools.once("toolbox-destroyed", finishUp);
inspector._toolbox.destroy();
});
}
function finishUp() {
gBrowser.removeCurrentTab();
finish();
}
}

View File

@ -0,0 +1,5 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

View File

@ -219,6 +219,12 @@ InspectorPanel.prototype = {
"chrome://browser/content/devtools/csshtmltree.xul",
"computedview" == defaultTab);
if (Services.prefs.getBoolPref("devtools.fontinspector.enabled")) {
this.sidebar.addTab("fontinspector",
"chrome://browser/content/devtools/fontinspector/font-inspector.xhtml",
"fontinspector" == defaultTab);
}
this.sidebar.addTab("layoutview",
"chrome://browser/content/devtools/layoutview/view.xhtml",
"layoutview" == defaultTab);

View File

@ -19,6 +19,9 @@ browser.jar:
content/browser/devtools/layoutview/view.js (layoutview/view.js)
content/browser/devtools/layoutview/view.xhtml (layoutview/view.xhtml)
content/browser/devtools/layoutview/view.css (layoutview/view.css)
content/browser/devtools/fontinspector/font-inspector.js (fontinspector/font-inspector.js)
content/browser/devtools/fontinspector/font-inspector.xhtml (fontinspector/font-inspector.xhtml)
content/browser/devtools/fontinspector/font-inspector.css (fontinspector/font-inspector.css)
content/browser/orion.js (sourceeditor/orion/orion.js)
* content/browser/source-editor-overlay.xul (sourceeditor/source-editor-overlay.xul)
content/browser/debugger.xul (debugger/debugger.xul)

View File

@ -104,7 +104,7 @@ LayoutView.prototype = {
this.browser.removeEventListener("MozAfterPaint", this.update, true);
}
if (this.inspector.highlighter) {
this.inspector.highlighter.on("locked", this.onHighlighterLocked);
this.inspector.highlighter.off("locked", this.onHighlighterLocked);
}
this.sizeHeadingLabel = null;
this.sizeLabel = null;

View File

@ -19,4 +19,5 @@ DIRS += [
'responsivedesign',
'framework',
'profiler',
'fontinspector',
]

View File

@ -0,0 +1,12 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- LOCALIZATION NOTE : FILE This file contains the Font Inspector strings.
- The Font Inspector is the panel accessible in the Inspector sidebar. -->
<!ENTITY title "Fonts">
<!ENTITY showAllFonts "See all the fonts used in the page">
<!ENTITY usedAs "Used as: ">
<!ENTITY system "system">
<!ENTITY remote "remote">

View File

@ -47,6 +47,7 @@
locale/browser/devtools/inspector.dtd (%chrome/browser/devtools/inspector.dtd)
locale/browser/devtools/connection-screen.dtd (%chrome/browser/devtools/connection-screen.dtd)
locale/browser/devtools/connection-screen.properties (%chrome/browser/devtools/connection-screen.properties)
locale/browser/devtools/font-inspector.dtd (%chrome/browser/devtools/font-inspector.dtd)
locale/browser/newTab.dtd (%chrome/browser/newTab.dtd)
locale/browser/newTab.properties (%chrome/browser/newTab.properties)
locale/browser/openLocation.dtd (%chrome/browser/openLocation.dtd)

View File

@ -0,0 +1,79 @@
* {
-moz-box-sizing: border-box;
}
body {
background: #F9F9F9;
margin: 0;
padding-bottom: 20px;
}
#all-fonts {
padding: 0 5px;
margin: 0;
}
#showall {
border-radius: 0;
border: 1px solid black;
margin: 3px;
cursor: pointer;
position: fixed;
bottom: 0;
right: 0;
}
.font {
border-bottom: 1px solid #DDD;
padding: 10px 5px;
font-size: 0;
}
.font:last-of-type {
border-bottom: 0;
}
.font:nth-child(even) {
background: #F4F4F4;
}
.font-preview {
height: 60px;
width: 100%;
border: 0;
display: block;
}
.font-info {
font-size: 1rem;
display: block;
}
.font-name {
display: inline;
}
.font-css-code {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
background: white;
padding: 5px;
border: 1px dotted #CCC;
}
.font-is-local,
.font-is-remote,
.font-format-url,
.font-css {
color: #999
}
.font-url {
border: 1px solid #CCC;
color: #888;
}
.font-url:focus {
color: black;
}

View File

@ -190,6 +190,7 @@ browser.jar:
skin/classic/browser/devtools/tool-profiler.png (devtools/tool-profiler.png)
skin/classic/browser/devtools/close.png (devtools/close.png)
skin/classic/browser/devtools/undock.png (devtools/undock.png)
skin/classic/browser/devtools/font-inspector.css (devtools/font-inspector.css)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-16-throbber.png
skin/classic/browser/sync-16.png

View File

@ -0,0 +1,79 @@
* {
-moz-box-sizing: border-box;
}
body {
background: #F9F9F9;
margin: 0;
padding-bottom: 20px;
}
#all-fonts {
padding: 0 5px;
margin: 0;
}
#showall {
border-radius: 0;
border: 1px solid black;
margin: 3px;
cursor: pointer;
position: fixed;
bottom: 0;
right: 0;
}
.font {
border-bottom: 1px solid #DDD;
padding: 10px 5px;
font-size: 0;
}
.font:last-of-type {
border-bottom: 0;
}
.font:nth-child(even) {
background: #F4F4F4;
}
.font-preview {
height: 60px;
width: 100%;
border: 0;
display: block;
}
.font-info {
font-size: 1rem;
display: block;
}
.font-name {
display: inline;
}
.font-css-code {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
background: white;
padding: 5px;
border: 1px dotted #CCC;
}
.font-is-local,
.font-is-remote,
.font-format-url,
.font-css {
color: #999
}
.font-url {
border: 1px solid #CCC;
color: #888;
}
.font-url:focus {
color: black;
}

View File

@ -271,6 +271,7 @@ browser.jar:
skin/classic/browser/devtools/tool-profiler.png (devtools/tool-profiler.png)
skin/classic/browser/devtools/close.png (devtools/close.png)
skin/classic/browser/devtools/undock.png (devtools/undock.png)
skin/classic/browser/devtools/font-inspector.css (devtools/font-inspector.css)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-throbber.png
skin/classic/browser/sync-16.png

View File

@ -0,0 +1,79 @@
* {
-moz-box-sizing: border-box;
}
body {
background: #F9F9F9;
margin: 0;
padding-bottom: 20px;
}
#all-fonts {
padding: 0 5px;
margin: 0;
}
#showall {
border-radius: 0;
border: 1px solid black;
margin: 3px;
cursor: pointer;
position: fixed;
bottom: 0;
right: 0;
}
.font {
border-bottom: 1px solid #DDD;
padding: 10px 5px;
font-size: 0;
}
.font:last-of-type {
border-bottom: 0;
}
.font:nth-child(even) {
background: #F4F4F4;
}
.font-preview {
height: 60px;
width: 100%;
border: 0;
display: block;
}
.font-info {
font-size: 1rem;
display: block;
}
.font-name {
display: inline;
}
.font-css-code {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
background: white;
padding: 5px;
border: 1px dotted #CCC;
}
.font-is-local,
.font-is-remote,
.font-format-url,
.font-css {
color: #999
}
.font-url {
border: 1px solid #CCC;
color: #888;
}
.font-url:focus {
color: black;
}

View File

@ -217,6 +217,7 @@ browser.jar:
skin/classic/browser/devtools/tool-profiler.png (devtools/tool-profiler.png)
skin/classic/browser/devtools/close.png (devtools/close.png)
skin/classic/browser/devtools/undock.png (devtools/undock.png)
skin/classic/browser/devtools/font-inspector.css (devtools/font-inspector.css)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-throbber.png
skin/classic/browser/sync-16.png
@ -445,6 +446,7 @@ browser.jar:
skin/classic/aero/browser/devtools/tool-profiler.png (devtools/tool-profiler.png)
skin/classic/aero/browser/devtools/close.png (devtools/close.png)
skin/classic/aero/browser/devtools/undock.png (devtools/undock.png)
skin/classic/aero/browser/devtools/font-inspector.css (devtools/font-inspector.css)
#ifdef MOZ_SERVICES_SYNC
skin/classic/aero/browser/sync-throbber.png
skin/classic/aero/browser/sync-16.png