/* 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/. */
// Number of items to preview in objects, arrays, maps, sets, lists, // collections, etc. const OBJECT_PREVIEW_MAX_ITEMS = 10;
const ERROR_CLASSNAMES = new Set([ "Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError", "InternalError", "AggregateError", "CompileError", "DebuggeeWouldRun", "LinkError", "RuntimeError", "Exception", // This related to Components.Exception() "SuppressedError",
]); const ARRAY_LIKE_CLASSNAMES = new Set([ "DOMStringList", "DOMTokenList", "CSSRuleList", "MediaList", "StyleSheetList", "NamedNodeMap", "FileList", "NodeList",
]); const OBJECT_WITH_URL_CLASSNAMES = new Set([ "CSSImportRule", "CSSStyleSheet", "Location",
]);
/** * Functions for adding information to ObjectActor grips for the purpose of * having customized output. This object holds arrays mapped by * Debugger.Object.prototype.class. * * In each array you can add functions that take three * arguments: * - the ObjectActor instance and its hooks to make a preview for, * - the grip object being prepared for the client, * - the depth of the object compared to the top level object, * when we are inspecting nested attributes. * * Functions must return false if they cannot provide preview * information for the debugger object, or true otherwise.
*/ const previewers = {
String: [ function(objectActor, grip, depth) { return wrappedPrimitivePreviewer(
String,
objectActor,
grip,
depth
);
},
],
if (obj.displayName) {
grip.displayName = obj.displayName.substr(0, 500);
}
if (obj.parameterNames) {
grip.parameterNames = obj.parameterNames;
}
// Check if the developer has added a de-facto standard displayName // property for us to use.
let userDisplayName; try {
userDisplayName = obj.getOwnPropertyDescriptor("displayName");
} catch (e) { // The above can throw "permission denied" errors when the debuggee // does not subsume the function's compartment.
}
if (obj.script) { // NOTE: Debugger.Script.prototype.startColumn is 1-based. // Convert to 0-based, while keeping the wasm's column (1) as is. // (bug 1863878) const columnBase = obj.script.format === "wasm" ? 0 : 1;
grip.location = {
url: obj.script.url,
line: obj.script.startLine,
column: obj.script.startColumn - columnBase,
};
}
returntrue;
},
],
RegExp: [ function(objectActor, grip, depth) {
let str; if (isWorker) { // For some reason, the following incantation on the worker thread returns "/undefined/undefined" // str = RegExp.prototype.toString.call(objectActor.obj.unsafeDereference()); // // The following method will throw in case of method being overloaded by the page, // and a more generic previewer will render the object. try {
str = DevToolsUtils.callPropertyOnObject(objectActor.obj, "toString");
} catch(e) { // Ensure displaying something in case of error. // Otherwise this would render an object with an empty label
grip.displayString = "RegExp with overloaded toString";
}
} else { const { RegExp } = objectActor.targetActor.targetGlobal;
str = RegExp.prototype.toString.call(objectActor.safeRawObj);
}
Date: [ function(objectActor, grip, depth) {
let time; if (isWorker) { // Also, targetGlobal is an opaque wrapper, from which we can't access its Date object, // so fallback to the privileged one // // In worker objectActor.safeRawObj is considered unsafe and is null, // so retrieve the objectActor.rawObj object directly from Debugger.Object.unsafeDereference
time = Date.prototype.getTime.call(objectActor.rawObj);
} else { const { Date } = objectActor.targetActor.targetGlobal;
time = Date.prototype.getTime.call(objectActor.safeRawObj);
} if (typeof time != "number") { returnfalse;
}
for (let i = 0; i < length; ++i) { if (rawObj && !isWorker) { // Array Xrays filter out various possibly-unsafe properties (like // functions, and claim that the value is undefined instead. This // is generally the right thing for privileged code accessing untrusted // objects, but quite confusing for Object previews. So we manually // override this protection by waiving Xrays on the array, and re-applying // Xrays on any indexed value props that we pull off of it. const desc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(rawObj), i); if (desc && !desc.get && !desc.set) {
let value = Cu.unwaiveXrays(desc.value);
value = ObjectUtils.makeDebuggeeValueIfNeeded(obj, value);
items.push(objectActor.createValueGrip(value, depth));
} elseif (!desc) {
items.push(null);
} else { const item = {}; if (desc.get) {
let getter = Cu.unwaiveXrays(desc.get);
getter = ObjectUtils.makeDebuggeeValueIfNeeded(obj, getter);
item.get = objectActor.createValueGrip(getter, depth);
} if (desc.set) {
let setter = Cu.unwaiveXrays(desc.set);
setter = ObjectUtils.makeDebuggeeValueIfNeeded(obj, setter);
item.set = objectActor.createValueGrip(setter, depth);
}
items.push(item);
}
} elseif (rawObj && !obj.getOwnPropertyDescriptor(i)) {
items.push(null);
} else { // Workers do not have access to Cu. const value = DevToolsUtils.getProperty(obj, i);
items.push(objectActor.createValueGrip(value, depth));
}
if (items.length == OBJECT_PREVIEW_MAX_ITEMS) { break;
}
}
Proxy: [ function(objectActor, grip, depth) { // Only preview top-level proxies, avoiding recursion. Otherwise, since both the // target and handler can also be proxies, we could get an exponential behavior. if (depth > 1) { returntrue;
}
const { obj } = objectActor;
// The `isProxy` getter of the debuggee object only detects proxies without // security wrappers. If false, the target and handler are not available. const hasTargetAndHandler = obj.isProxy;
const items = (grip.preview.items = []); for (const item of PropertyIterators.enumCustomStateSetEntries(objectActor, depth)) {
items.push(item); if (items.length == OBJECT_PREVIEW_MAX_ITEMS) { break;
}
}
returntrue;
},
],
};
/** * Generic previewer for classes wrapping primitives, like String, * Number and Boolean. * * @param object classObj * The class to expect, eg. String. The valueOf() method of the class is * invoked on the given object. * @param ObjectActor objectActor * The object actor * @param Object grip * The result grip to fill in * @param Number depth * Depth of the object compared to the top level object, * when we are inspecting nested attributes. * @return Booolean true if the object was handled, false otherwise
*/ function wrappedPrimitivePreviewer(
classObj,
objectActor,
grip,
depth
) { const { safeRawObj } = objectActor;
let v = null; try {
v = classObj.prototype.valueOf.call(safeRawObj);
} catch (ex) { // valueOf() can throw if the raw JS object is "misbehaved". returnfalse;
}
/** * @param {ObjectActor} objectActor * @param {Object} grip: The grip built by the objectActor, for which we need to populate * the `preview` property. * @param {Number} depth * Depth of the object compared to the top level object, * when we are inspecting nested attributes. * @returns
*/ function GenericObject(objectActor, grip, depth) { const { obj, safeRawObj } = objectActor; if (grip.preview || grip.displayString || depth > 1) { returnfalse;
}
let length,
i = 0;
let specialStringBehavior = objectActor.className === "String"; if (specialStringBehavior) {
length = DevToolsUtils.getProperty(obj, "length"); if (typeof length != "number") {
specialStringBehavior = false;
}
}
for (const name of names) { if (specialStringBehavior && /^[0-9]+$/.test(name)) { const num = parseInt(name, 10); if (num.toString() === name && num >= 0 && num < length) { continue;
}
}
if (++i == OBJECT_PREVIEW_MAX_ITEMS) { break;
}
}
}
if (i === OBJECT_PREVIEW_MAX_ITEMS) { returntrue;
}
const safeGetterValues = objectActor._findSafeGetterValues(
Object.keys(preview.ownProperties),
depth,
OBJECT_PREVIEW_MAX_ITEMS - i
); if (Object.keys(safeGetterValues).length) {
preview.safeGetterValues = safeGetterValues;
}
returntrue;
}
// Preview functions that do not rely on the object class.
previewers.Object = [ function TypedArray({ obj }, grip, depth) { if (!ObjectUtils.isTypedArray(obj)) { returnfalse;
}
const previewLength = Math.min(
OBJECT_PREVIEW_MAX_ITEMS,
grip.preview.length
);
grip.preview.items = []; for (let i = 0; i < previewLength; i++) { const desc = obj.getOwnPropertyDescriptor(i); if (!desc) { break;
}
grip.preview.items.push(desc.value);
}
returntrue;
},
function Error(objectActor, grip, depth) { if (!ERROR_CLASSNAMES.has(objectActor.className)) { returnfalse;
}
const { obj } = objectActor;
// The name and/or message could be getters, and even if it's unsafe, we do want // to show it to the user (See Bug 1710694). const name = DevToolsUtils.getProperty(obj, "name", true); const msg = DevToolsUtils.getProperty(obj, "message", true); const stack = DevToolsUtils.getProperty(obj, "stack"); const fileName = DevToolsUtils.getProperty(obj, "fileName"); const lineNumber = DevToolsUtils.getProperty(obj, "lineNumber"); const columnNumber = DevToolsUtils.getProperty(obj, "columnNumber");
let url; if (isWindow && safeRawObj.location) { try {
url = safeRawObj.location.href;
} catch(e) { // This can happen when we have a cross-process window. // In such case, let's retrieve the url from the iframe. // For window.top from a remote iframe, there's no way we can't retrieve the URL, // so return a label that help user know what's going on.
url = safeRawObj.browsingContext?.embedderElement?.src || "Restricted";
}
} elseif (safeRawObj.href) {
url = safeRawObj.href;
} else { returnfalse;
}
for (
let i = 0;
i < safeRawObj.length && items.length < OBJECT_PREVIEW_MAX_ITEMS;
i++
) { const value = ObjectUtils.makeDebuggeeValueIfNeeded(objectActor.obj, safeRawObj[i]);
items.push(objectActor.createValueGrip(value, depth));
}
if (depth < 2) {
preview.childNodes = []; for (const node of safeRawObj.childNodes) { const actor = objectActor.createValueGrip(obj.makeDebuggeeValue(node), depth);
preview.childNodes.push(actor); if (preview.childNodes.length == OBJECT_PREVIEW_MAX_ITEMS) { break;
}
}
}
} elseif (Element.isInstance(safeRawObj)) { // For HTML elements (in an HTML document, at least), the nodeName is an // uppercased version of the actual element name. Check for HTML // elements, that is elements in the HTML namespace, and lowercase the // nodeName in that case. if (safeRawObj.namespaceURI == "http://www.w3.org/1999/xhtml") {
preview.nodeName = preview.nodeName.toLowerCase();
}
// Add event-specific properties. for (const prop of props) {
let value = safeRawObj[prop]; if (ObjectUtils.isObjectOrFunction(value)) { // Skip properties pointing to objects. if (depth > 1) { continue;
}
value = obj.makeDebuggeeValue(value);
}
preview.properties[prop] = objectActor.createValueGrip(value, depth);
}
// Add any properties we find on the event object. if (!props.length) {
let i = 0; for (const prop in safeRawObj) {
let value = safeRawObj[prop]; if (
prop == "target" ||
prop == "type" ||
value === null || typeof value == "function"
) { continue;
} if (value && typeof value == "object") { if (depth > 1) { continue;
}
value = obj.makeDebuggeeValue(value);
}
preview.properties[prop] = objectActor.createValueGrip(value, depth); if (++i == OBJECT_PREVIEW_MAX_ITEMS) { break;
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.