diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /remote/Format.jsm | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'remote/Format.jsm')
-rw-r--r-- | remote/Format.jsm | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/remote/Format.jsm b/remote/Format.jsm new file mode 100644 index 0000000000..0b571fc660 --- /dev/null +++ b/remote/Format.jsm @@ -0,0 +1,184 @@ +/* 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"; + +var EXPORTED_SYMBOLS = ["pprint", "truncate"]; + +const { Log } = ChromeUtils.import("chrome://remote/content/Log.jsm"); + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +); + +XPCOMUtils.defineLazyGetter(this, "log", Log.get); + +const ELEMENT_NODE = 1; +const MAX_STRING_LENGTH = 250; +const PREF_TRUNCATE = "remote.log.truncate"; + +/** + * Pretty-print values passed to template strings. + * + * Usage:: + * + * let bool = {value: true}; + * pprint`Expected boolean, got ${bool}`; + * => 'Expected boolean, got [object Object] {"value": true}' + * + * let htmlElement = document.querySelector("input#foo"); + * pprint`Expected element ${htmlElement}`; + * => 'Expected element <input id="foo" class="bar baz" type="input">' + * + * pprint`Current window: ${window}`; + * => '[object Window https://www.mozilla.org/]' + */ +function pprint(ss, ...values) { + function pretty(val) { + let proto = Object.prototype.toString.call(val); + if ( + typeof val == "object" && + val !== null && + "nodeType" in val && + val.nodeType === ELEMENT_NODE + ) { + return prettyElement(val); + } else if (["[object Window]", "[object ChromeWindow]"].includes(proto)) { + return prettyWindowGlobal(val); + } else if (proto == "[object Attr]") { + return prettyAttr(val); + } + return prettyObject(val); + } + + function prettyElement(el) { + let attrs = ["id", "class", "href", "name", "src", "type"]; + + let idents = ""; + for (let attr of attrs) { + if (el.hasAttribute(attr)) { + idents += ` ${attr}="${el.getAttribute(attr)}"`; + } + } + + return `<${el.localName}${idents}>`; + } + + function prettyWindowGlobal(win) { + let proto = Object.prototype.toString.call(win); + return `[${proto.substring(1, proto.length - 1)} ${win.location}]`; + } + + function prettyAttr(obj) { + return `[object Attr ${obj.name}="${obj.value}"]`; + } + + function prettyObject(obj) { + let proto = Object.prototype.toString.call(obj); + let s = ""; + try { + s = JSON.stringify(obj); + } catch (e) { + if (e instanceof TypeError) { + s = `<${e.message}>`; + } else { + throw e; + } + } + return `${proto} ${s}`; + } + + let res = []; + for (let i = 0; i < ss.length; i++) { + res.push(ss[i]); + if (i < values.length) { + let s; + try { + s = pretty(values[i]); + } catch (e) { + log.warn("Problem pretty printing:", e); + s = typeof values[i]; + } + res.push(s); + } + } + return res.join(""); +} + +/** + * Template literal that truncates string values in arbitrary objects. + * + * Given any object, the template will walk the object and truncate + * any strings it comes across to a reasonable limit. This is suitable + * when you have arbitrary data and data integrity is not important. + * + * The strings are truncated in the middle so that the beginning and + * the end is preserved. This will make a long, truncated string look + * like "X <...> Y", where X and Y are half the number of characters + * of the maximum string length from either side of the string. + * + * + * Usage:: + * + * truncate`Hello ${"x".repeat(260)}!`; + * // Hello xxx ... xxx! + * + * Functions named `toJSON` or `toString` on objects will be called. + */ +function truncate(strings, ...values) { + const truncateLog = Services.prefs.getBoolPref(PREF_TRUNCATE, false); + function walk(obj) { + const typ = Object.prototype.toString.call(obj); + + switch (typ) { + case "[object Undefined]": + case "[object Null]": + case "[object Boolean]": + case "[object Number]": + return obj; + + case "[object String]": + if (truncateLog && obj.length > MAX_STRING_LENGTH) { + let s1 = obj.substring(0, MAX_STRING_LENGTH / 2); + let s2 = obj.substring(obj.length - MAX_STRING_LENGTH / 2); + return `${s1} ... ${s2}`; + } + return obj; + + case "[object Array]": + return obj.map(walk); + + // arbitrary object + default: + if ( + Object.getOwnPropertyNames(obj).includes("toString") && + typeof obj.toString == "function" + ) { + return walk(obj.toString()); + } + + let rv = {}; + for (let prop in obj) { + rv[prop] = walk(obj[prop]); + } + return rv; + } + } + + let res = []; + for (let i = 0; i < strings.length; ++i) { + res.push(strings[i]); + if (i < values.length) { + let obj = walk(values[i]); + let t = Object.prototype.toString.call(obj); + if (t == "[object Array]" || t == "[object Object]") { + res.push(JSON.stringify(obj)); + } else { + res.push(obj); + } + } + } + return res.join(""); +} |