tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Format.sys.mjs (4957B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
      6 
      7 const lazy = {};
      8 
      9 ChromeUtils.defineESModuleGetters(lazy, {
     10  Log: "chrome://remote/content/shared/Log.sys.mjs",
     11 });
     12 
     13 ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
     14 
     15 XPCOMUtils.defineLazyPreferenceGetter(
     16  lazy,
     17  "truncateLog",
     18  "remote.log.truncate",
     19  false
     20 );
     21 
     22 const ELEMENT_NODE = 1;
     23 const MAX_STRING_LENGTH = 250;
     24 
     25 /**
     26 * Pretty-print values passed to template strings.
     27 *
     28 * Usage::
     29 *
     30 *     let bool = {value: true};
     31 *     pprint`Expected boolean, got ${bool}`;
     32 *     => 'Expected boolean, got [object Object] {"value": true}'
     33 *
     34 *     let htmlElement = document.querySelector("input#foo");
     35 *     pprint`Expected element ${htmlElement}`;
     36 *     => 'Expected element <input id="foo" class="bar baz" type="input">'
     37 *
     38 *     pprint`Current window: ${window}`;
     39 *     => '[object Window https://www.mozilla.org/]'
     40 */
     41 export function pprint(ss, ...values) {
     42  function pretty(val) {
     43    let proto = Object.prototype.toString.call(val);
     44    if (
     45      typeof val == "object" &&
     46      val !== null &&
     47      "nodeType" in val &&
     48      val.nodeType === ELEMENT_NODE
     49    ) {
     50      return prettyElement(val);
     51    } else if (["[object Window]", "[object ChromeWindow]"].includes(proto)) {
     52      return prettyWindowGlobal(val);
     53    } else if (proto == "[object Attr]") {
     54      return prettyAttr(val);
     55    }
     56    return prettyObject(val);
     57  }
     58 
     59  function prettyElement(el) {
     60    let attrs = ["id", "class", "href", "name", "src", "type"];
     61 
     62    let idents = "";
     63    for (let attr of attrs) {
     64      if (el.hasAttribute(attr)) {
     65        idents += ` ${attr}="${el.getAttribute(attr)}"`;
     66      }
     67    }
     68 
     69    return `<${el.localName}${idents}>`;
     70  }
     71 
     72  function prettyWindowGlobal(win) {
     73    let proto = Object.prototype.toString.call(win);
     74    return `[${proto.substring(1, proto.length - 1)} ${win.location}]`;
     75  }
     76 
     77  function prettyAttr(obj) {
     78    return `[object Attr ${obj.name}="${obj.value}"]`;
     79  }
     80 
     81  function prettyObject(obj) {
     82    let proto = Object.prototype.toString.call(obj);
     83    let s = "";
     84    try {
     85      s = JSON.stringify(obj);
     86    } catch (e) {
     87      if (e instanceof TypeError) {
     88        s = `<${e.message}>`;
     89      } else {
     90        throw e;
     91      }
     92    }
     93    return `${proto} ${s}`;
     94  }
     95 
     96  let res = [];
     97  for (let i = 0; i < ss.length; i++) {
     98    res.push(ss[i]);
     99    if (i < values.length) {
    100      let s;
    101      try {
    102        s = pretty(values[i]);
    103      } catch (e) {
    104        lazy.logger.warn("Problem pretty printing:", e);
    105        s = typeof values[i];
    106      }
    107      res.push(s);
    108    }
    109  }
    110  return res.join("");
    111 }
    112 
    113 /**
    114 * Template literal that truncates string values in arbitrary objects.
    115 *
    116 * Given any object, the template will walk the object and truncate
    117 * any strings it comes across to a reasonable limit.  This is suitable
    118 * when you have arbitrary data and data integrity is not important.
    119 *
    120 * The strings are truncated in the middle so that the beginning and
    121 * the end is preserved.  This will make a long, truncated string look
    122 * like "X <...> Y", where X and Y are half the number of characters
    123 * of the maximum string length from either side of the string.
    124 *
    125 *
    126 * Usage::
    127 *
    128 *     truncate`Hello ${"x".repeat(260)}!`;
    129 *     // Hello xxx ... xxx!
    130 *
    131 * Functions named `toJSON` or `toString` on objects will be called.
    132 */
    133 export function truncate(strings, ...values) {
    134  function walk(obj) {
    135    const typ = Object.prototype.toString.call(obj);
    136 
    137    switch (typ) {
    138      case "[object Undefined]":
    139      case "[object Null]":
    140      case "[object Boolean]":
    141      case "[object Number]":
    142        return obj;
    143 
    144      case "[object String]":
    145        if (lazy.truncateLog && obj.length > MAX_STRING_LENGTH) {
    146          let s1 = obj.substring(0, MAX_STRING_LENGTH / 2);
    147          let s2 = obj.substring(obj.length - MAX_STRING_LENGTH / 2);
    148          return `${s1} ... ${s2}`;
    149        }
    150        return obj;
    151 
    152      case "[object Array]":
    153        return obj.map(walk);
    154 
    155      // arbitrary object
    156      default: {
    157        if (
    158          Object.getOwnPropertyNames(obj).includes("toString") &&
    159          typeof obj.toString == "function"
    160        ) {
    161          return walk(obj.toString());
    162        }
    163 
    164        let rv = {};
    165        for (let prop in obj) {
    166          rv[prop] = walk(obj[prop]);
    167        }
    168        return rv;
    169      }
    170    }
    171  }
    172 
    173  let res = [];
    174  for (let i = 0; i < strings.length; ++i) {
    175    res.push(strings[i]);
    176    if (i < values.length) {
    177      let obj = walk(values[i]);
    178      let t = Object.prototype.toString.call(obj);
    179      if (t == "[object Array]" || t == "[object Object]") {
    180        res.push(JSON.stringify(obj));
    181      } else {
    182        res.push(obj);
    183      }
    184    }
    185  }
    186  return res.join("");
    187 }