browser/devtools/fontinspector/font-inspector.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/devtools/fontinspector/font-inspector.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,219 @@
     1.4 +/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +"use strict";
    1.11 +
    1.12 +const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
    1.13 +const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
    1.14 +
    1.15 +function FontInspector(inspector, window)
    1.16 +{
    1.17 +  this.inspector = inspector;
    1.18 +  this.chromeDoc = window.document;
    1.19 +  this.init();
    1.20 +}
    1.21 +
    1.22 +FontInspector.prototype = {
    1.23 +  init: function FI_init() {
    1.24 +    this.update = this.update.bind(this);
    1.25 +    this.onNewNode = this.onNewNode.bind(this);
    1.26 +    this.inspector.selection.on("new-node", this.onNewNode);
    1.27 +    this.inspector.sidebar.on("fontinspector-selected", this.onNewNode);
    1.28 +    this.showAll = this.showAll.bind(this);
    1.29 +    this.showAllButton = this.chromeDoc.getElementById("showall");
    1.30 +    this.showAllButton.addEventListener("click", this.showAll);
    1.31 +    this.update();
    1.32 +  },
    1.33 +
    1.34 +  /**
    1.35 +   * Is the fontinspector visible in the sidebar?
    1.36 +   */
    1.37 +  isActive: function FI_isActive() {
    1.38 +    return this.inspector.sidebar &&
    1.39 +           this.inspector.sidebar.getCurrentTabID() == "fontinspector";
    1.40 +  },
    1.41 +
    1.42 +  /**
    1.43 +   * Remove listeners.
    1.44 +   */
    1.45 +  destroy: function FI_destroy() {
    1.46 +    this.chromeDoc = null;
    1.47 +    this.inspector.sidebar.off("layoutview-selected", this.onNewNode);
    1.48 +    this.inspector.selection.off("new-node", this.onNewNode);
    1.49 +    this.showAllButton.removeEventListener("click", this.showAll);
    1.50 +  },
    1.51 +
    1.52 +  /**
    1.53 +   * Selection 'new-node' event handler.
    1.54 +   */
    1.55 +  onNewNode: function FI_onNewNode() {
    1.56 +    if (this.isActive() &&
    1.57 +        this.inspector.selection.isLocal() &&
    1.58 +        this.inspector.selection.isConnected() &&
    1.59 +        this.inspector.selection.isElementNode()) {
    1.60 +      this.undim();
    1.61 +      this.update();
    1.62 +    } else {
    1.63 +      this.dim();
    1.64 +    }
    1.65 +  },
    1.66 +
    1.67 +  /**
    1.68 +   * Hide the font list. No node are selected.
    1.69 +   */
    1.70 +  dim: function FI_dim() {
    1.71 +    this.chromeDoc.body.classList.add("dim");
    1.72 +    this.chromeDoc.querySelector("#all-fonts").innerHTML = "";
    1.73 +  },
    1.74 +
    1.75 +  /**
    1.76 +   * Show the font list. A node is selected.
    1.77 +   */
    1.78 +  undim: function FI_undim() {
    1.79 +    this.chromeDoc.body.classList.remove("dim");
    1.80 +  },
    1.81 +
    1.82 +  /**
    1.83 +   * Retrieve all the font related info we have for the selected
    1.84 +   * node and display them.
    1.85 +   */
    1.86 +  update: function FI_update() {
    1.87 +    if (!this.isActive() ||
    1.88 +        !this.inspector.selection.isConnected() ||
    1.89 +        !this.inspector.selection.isElementNode() ||
    1.90 +        this.chromeDoc.body.classList.contains("dim")) {
    1.91 +      return;
    1.92 +    }
    1.93 +
    1.94 +    let node = this.inspector.selection.node;
    1.95 +    let contentDocument = node.ownerDocument;
    1.96 +
    1.97 +    // We don't get fonts for a node, but for a range
    1.98 +    let rng = contentDocument.createRange();
    1.99 +    rng.selectNode(node);
   1.100 +    let fonts = DOMUtils.getUsedFontFaces(rng);
   1.101 +    let fontsArray = [];
   1.102 +    for (let i = 0; i < fonts.length; i++) {
   1.103 +      fontsArray.push(fonts.item(i));
   1.104 +    }
   1.105 +    fontsArray = fontsArray.sort(function(a, b) {
   1.106 +      return a.srcIndex < b.srcIndex;
   1.107 +    });
   1.108 +    this.chromeDoc.querySelector("#all-fonts").innerHTML = "";
   1.109 +    for (let f of fontsArray) {
   1.110 +      this.render(f, contentDocument);
   1.111 +    }
   1.112 +  },
   1.113 +
   1.114 +  /**
   1.115 +   * Display the information of one font.
   1.116 +   */
   1.117 +  render: function FI_render(font, document) {
   1.118 +    let s = this.chromeDoc.querySelector("#template > section");
   1.119 +    s = s.cloneNode(true);
   1.120 +
   1.121 +    s.querySelector(".font-name").textContent = font.name;
   1.122 +    s.querySelector(".font-css-name").textContent = font.CSSFamilyName;
   1.123 +    s.querySelector(".font-format").textContent = font.format;
   1.124 +
   1.125 +    if (font.srcIndex == -1) {
   1.126 +      s.classList.add("is-local");
   1.127 +    } else {
   1.128 +      s.classList.add("is-remote");
   1.129 +    }
   1.130 +
   1.131 +    s.querySelector(".font-url").value = font.URI;
   1.132 +
   1.133 +    let iframe = s.querySelector(".font-preview");
   1.134 +    if (font.rule) {
   1.135 +      // This is the @font-face{…} code.
   1.136 +      let cssText = font.rule.style.parentRule.cssText;
   1.137 +
   1.138 +      s.classList.add("has-code");
   1.139 +      s.querySelector(".font-css-code").textContent = cssText;
   1.140 +
   1.141 +      // We guess the base URL of the stylesheet to make
   1.142 +      // sure the font will be accessible in the preview.
   1.143 +      // If the font-face is in an inline <style>, we get
   1.144 +      // the location of the page.
   1.145 +      let origin = font.rule.style.parentRule.parentStyleSheet.href;
   1.146 +      if (!origin) { // Inline stylesheet
   1.147 +        origin = document.location.href;
   1.148 +      }
   1.149 +      // We remove the last part of the URL to get a correct base.
   1.150 +      let base = origin.replace(/\/[^\/]*$/,"/")
   1.151 +
   1.152 +      // From all this information, we build a preview.
   1.153 +      this.buildPreview(iframe, font.CSSFamilyName, cssText, base);
   1.154 +    } else {
   1.155 +      this.buildPreview(iframe, font.CSSFamilyName, "", "");
   1.156 +    }
   1.157 +
   1.158 +    this.chromeDoc.querySelector("#all-fonts").appendChild(s);
   1.159 +  },
   1.160 +
   1.161 +  /**
   1.162 +   * Show a preview of the font in an iframe.
   1.163 +   */
   1.164 +  buildPreview: function FI_buildPreview(iframe, name, cssCode, base) {
   1.165 +    /* The HTML code of the preview is:
   1.166 +     *   <!DOCTYPE HTML>
   1.167 +     *   <head>
   1.168 +     *    <base href="{base}"></base>
   1.169 +     *   </head>
   1.170 +     *   <style>
   1.171 +     *   p {font-family: {name};}
   1.172 +     *   * {font-size: 40px;line-height:60px;padding:0 10px;margin:0};
   1.173 +     *   </style>
   1.174 +     *   <p contenteditable spellcheck='false'>Abc</p>
   1.175 +     */
   1.176 +    let extraCSS = "* {padding:0;margin:0}";
   1.177 +    extraCSS += ".theme-dark {color: white}";
   1.178 +    extraCSS += "p {font-size: 40px;line-height:60px;padding:0 10px;margin:0;}";
   1.179 +    cssCode += extraCSS;
   1.180 +    let src = "data:text/html;charset=utf-8,<!DOCTYPE HTML><head><base></base></head><style></style><p contenteditable spellcheck='false'>Abc</p>";
   1.181 +    iframe.addEventListener("load", function onload() {
   1.182 +      iframe.removeEventListener("load", onload, true);
   1.183 +      let doc = iframe.contentWindow.document;
   1.184 +      // We could have done that earlier, but we want to avoid any URL-encoding
   1.185 +      // nightmare.
   1.186 +      doc.querySelector("base").href = base;
   1.187 +      doc.querySelector("style").textContent = cssCode;
   1.188 +      doc.querySelector("p").style.fontFamily = name;
   1.189 +      // Forward theme
   1.190 +      doc.documentElement.className = document.documentElement.className;
   1.191 +    }, true);
   1.192 +    iframe.src = src;
   1.193 +  },
   1.194 +
   1.195 +  /**
   1.196 +   * Select the <body> to show all the fonts included in the document.
   1.197 +   */
   1.198 +  showAll: function FI_showAll() {
   1.199 +    if (!this.isActive() ||
   1.200 +        !this.inspector.selection.isConnected() ||
   1.201 +        !this.inspector.selection.isElementNode()) {
   1.202 +      return;
   1.203 +    }
   1.204 +
   1.205 +    // Select the body node to show all fonts
   1.206 +    let walker = this.inspector.walker;
   1.207 +
   1.208 +    walker.getRootNode().then(root => walker.querySelector(root, "body")).then(body => {
   1.209 +      this.inspector.selection.setNodeFront(body, "fontinspector");
   1.210 +    });
   1.211 +  },
   1.212 +}
   1.213 +
   1.214 +window.setPanel = function(panel) {
   1.215 +  window.fontInspector = new FontInspector(panel, window);
   1.216 +}
   1.217 +
   1.218 +window.onunload = function() {
   1.219 +  if (window.fontInspector) {
   1.220 +    window.fontInspector.destroy();
   1.221 +  }
   1.222 +}

mercurial