2018-03-28 09:24:41 +03:00
|
|
|
/* 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 { Cu, Ci } = require("chrome");
|
2020-02-17 13:28:24 +03:00
|
|
|
const { DevToolsServer } = require("devtools/server/devtools-server");
|
2018-03-28 09:24:41 +03:00
|
|
|
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
|
|
|
loader.lazyRequireGetter(
|
|
|
|
this,
|
|
|
|
"ObjectUtils",
|
|
|
|
"devtools/server/actors/object/utils"
|
|
|
|
);
|
|
|
|
loader.lazyRequireGetter(
|
|
|
|
this,
|
|
|
|
"PropertyIterators",
|
|
|
|
"devtools/server/actors/object/property-iterator"
|
|
|
|
);
|
|
|
|
|
|
|
|
// Number of items to preview in objects, arrays, maps, sets, lists,
|
|
|
|
// collections, etc.
|
|
|
|
const OBJECT_PREVIEW_MAX_ITEMS = 10;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 raw JS object after calling Debugger.Object.unsafeDereference(). This
|
|
|
|
* argument is only provided if the object is safe for reading properties and
|
|
|
|
* executing methods. See DevToolsUtils.isSafeJSObject().
|
|
|
|
*
|
|
|
|
* Functions must return false if they cannot provide preview
|
|
|
|
* information for the debugger object, or true otherwise.
|
|
|
|
*/
|
|
|
|
const previewers = {
|
|
|
|
String: [
|
|
|
|
function(objectActor, grip, rawObj) {
|
|
|
|
return wrappedPrimitivePreviewer(
|
|
|
|
"String",
|
|
|
|
String,
|
|
|
|
objectActor,
|
|
|
|
grip,
|
|
|
|
rawObj
|
|
|
|
);
|
|
|
|
},
|
|
|
|
],
|
2019-07-05 12:26:00 +03:00
|
|
|
|
2018-03-28 09:24:41 +03:00
|
|
|
Boolean: [
|
|
|
|
function(objectActor, grip, rawObj) {
|
|
|
|
return wrappedPrimitivePreviewer(
|
|
|
|
"Boolean",
|
|
|
|
Boolean,
|
|
|
|
objectActor,
|
|
|
|
grip,
|
|
|
|
rawObj
|
|
|
|
);
|
|
|
|
},
|
|
|
|
],
|
2019-07-05 12:26:00 +03:00
|
|
|
|
2018-03-28 09:24:41 +03:00
|
|
|
Number: [
|
|
|
|
function(objectActor, grip, rawObj) {
|
|
|
|
return wrappedPrimitivePreviewer(
|
|
|
|
"Number",
|
|
|
|
Number,
|
|
|
|
objectActor,
|
|
|
|
grip,
|
|
|
|
rawObj
|
|
|
|
);
|
|
|
|
},
|
|
|
|
],
|
2019-07-05 12:26:00 +03:00
|
|
|
|
2018-03-28 09:24:41 +03:00
|
|
|
Symbol: [
|
|
|
|
function(objectActor, grip, rawObj) {
|
|
|
|
return wrappedPrimitivePreviewer(
|
|
|
|
"Symbol",
|
|
|
|
Symbol,
|
|
|
|
objectActor,
|
|
|
|
grip,
|
|
|
|
rawObj
|
|
|
|
);
|
|
|
|
},
|
|
|
|
],
|
2019-07-05 12:26:00 +03:00
|
|
|
|
2018-03-28 09:24:41 +03:00
|
|
|
Function: [
|
|
|
|
function({ obj, hooks }, grip) {
|
|
|
|
if (obj.name) {
|
|
|
|
grip.name = obj.name;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 (
|
|
|
|
userDisplayName &&
|
|
|
|
typeof userDisplayName.value == "string" &&
|
|
|
|
userDisplayName.value
|
|
|
|
) {
|
|
|
|
grip.userDisplayName = hooks.createValueGrip(userDisplayName.value);
|
|
|
|
}
|
|
|
|
|
2019-12-09 15:35:04 +03:00
|
|
|
grip.isAsync = obj.isAsyncFunction;
|
|
|
|
grip.isGenerator = obj.isGeneratorFunction;
|
|
|
|
|
Bug 1577798 - Don't create a new Debugger instance in WebConsoleActor. r=ochameau,jlast.
Previously, when creating a new WebConsoleActor, we were
also creating a new Debugger instance.
The problem was that we would never call `enable` on it,
which is what allows to create debuggee for all existing
globals, and for the ones that will be created after.
This means that we had multiple workaround to get a
global debuggee, and in the case of function, would add
a global as a debuggee only to get its script location.
This patch remove the creation of the new Debugger,
and retrieve the parent actor Debugger instance instead.
This shuffles things around a bit, are the Debugger instance
was created by the ThreadActor, but is now by the target
actor (BrowsingContext, ContentProcess, ...).
We can then remove the now unused `getGlobalDebugObject`
hooks we were using in the Function previewer, as we
get a proper Debugger object, with the script location.
Differential Revision: https://phabricator.services.mozilla.com/D44480
--HG--
extra : moz-landing-system : lando
2019-09-16 15:12:22 +03:00
|
|
|
if (obj.script) {
|
2019-09-07 00:19:30 +03:00
|
|
|
grip.location = {
|
Bug 1577798 - Don't create a new Debugger instance in WebConsoleActor. r=ochameau,jlast.
Previously, when creating a new WebConsoleActor, we were
also creating a new Debugger instance.
The problem was that we would never call `enable` on it,
which is what allows to create debuggee for all existing
globals, and for the ones that will be created after.
This means that we had multiple workaround to get a
global debuggee, and in the case of function, would add
a global as a debuggee only to get its script location.
This patch remove the creation of the new Debugger,
and retrieve the parent actor Debugger instance instead.
This shuffles things around a bit, are the Debugger instance
was created by the ThreadActor, but is now by the target
actor (BrowsingContext, ContentProcess, ...).
We can then remove the now unused `getGlobalDebugObject`
hooks we were using in the Function previewer, as we
get a proper Debugger object, with the script location.
Differential Revision: https://phabricator.services.mozilla.com/D44480
--HG--
extra : moz-landing-system : lando
2019-09-16 15:12:22 +03:00
|
|
|
url: obj.script.url,
|
|
|
|
line: obj.script.startLine,
|
2019-12-09 15:35:04 +03:00
|
|
|
column: obj.script.startColumn,
|
2019-09-07 00:19:30 +03:00
|
|
|
};
|
2018-03-28 09:24:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
],
|
|
|
|
|
|
|
|
RegExp: [
|
|
|
|
function({ obj, hooks }, grip) {
|
2019-09-17 01:48:15 +03:00
|
|
|
const str = DevToolsUtils.callPropertyOnObject(obj, "toString");
|
|
|
|
if (typeof str != "string") {
|
|
|
|
return false;
|
|
|
|
}
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
grip.displayString = hooks.createValueGrip(str);
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
],
|
|
|
|
|
|
|
|
Date: [
|
|
|
|
function({ obj, hooks }, grip) {
|
2019-09-17 01:48:15 +03:00
|
|
|
const time = DevToolsUtils.callPropertyOnObject(obj, "getTime");
|
2018-03-28 09:24:41 +03:00
|
|
|
if (typeof time != "number") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
grip.preview = {
|
|
|
|
timestamp: hooks.createValueGrip(time),
|
|
|
|
};
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
],
|
|
|
|
|
|
|
|
Array: [
|
|
|
|
function({ obj, hooks }, grip) {
|
2018-06-01 13:36:09 +03:00
|
|
|
const length = ObjectUtils.getArrayLength(obj);
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
grip.preview = {
|
|
|
|
kind: "ArrayLike",
|
|
|
|
length: length,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (hooks.getGripDepth() > 1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
const raw = obj.unsafeDereference();
|
|
|
|
const items = (grip.preview.items = []);
|
2019-07-05 12:26:00 +03:00
|
|
|
|
2018-03-28 09:24:41 +03:00
|
|
|
for (let i = 0; i < length; ++i) {
|
2019-03-03 20:05:07 +03:00
|
|
|
if (raw && !isWorker) {
|
2018-07-24 17:53:30 +03:00
|
|
|
// 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(raw), i);
|
|
|
|
if (desc && !desc.get && !desc.set) {
|
|
|
|
let value = Cu.unwaiveXrays(desc.value);
|
|
|
|
value = ObjectUtils.makeDebuggeeValueIfNeeded(obj, value);
|
|
|
|
items.push(hooks.createValueGrip(value));
|
2020-01-21 16:16:04 +03:00
|
|
|
} else if (!desc) {
|
|
|
|
items.push(null);
|
2019-07-05 12:26:00 +03:00
|
|
|
} else {
|
2019-07-18 15:04:42 +03:00
|
|
|
items.push(hooks.createValueGrip(undefined));
|
2019-07-05 12:26:00 +03:00
|
|
|
}
|
2020-01-21 16:16:04 +03:00
|
|
|
} else if (raw && !Object.getOwnPropertyDescriptor(raw, i)) {
|
|
|
|
items.push(null);
|
2018-07-24 17:53:30 +03:00
|
|
|
} else {
|
2020-02-11 00:03:28 +03:00
|
|
|
// Workers do not have access to Cu.
|
2018-07-24 17:53:30 +03:00
|
|
|
const value = DevToolsUtils.getProperty(obj, i);
|
|
|
|
items.push(hooks.createValueGrip(value));
|
|
|
|
}
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
|
|
|
|
break;
|
2019-07-05 12:26:00 +03:00
|
|
|
}
|
2018-03-28 09:24:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
2019-07-05 12:26:00 +03:00
|
|
|
],
|
2018-03-28 09:24:41 +03:00
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
Set: [
|
|
|
|
function(objectActor, grip) {
|
2019-09-17 01:48:15 +03:00
|
|
|
const size = DevToolsUtils.getProperty(objectActor.obj, "size");
|
|
|
|
if (typeof size != "number") {
|
|
|
|
return false;
|
|
|
|
}
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
grip.preview = {
|
|
|
|
kind: "ArrayLike",
|
|
|
|
length: size,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Avoid recursive object grips.
|
|
|
|
if (objectActor.hooks.getGripDepth() > 1) {
|
2018-06-01 13:36:09 +03:00
|
|
|
return true;
|
2019-07-05 12:26:00 +03:00
|
|
|
}
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
const items = (grip.preview.items = []);
|
2019-12-09 15:35:04 +03:00
|
|
|
for (const item of PropertyIterators.enumSetEntries(
|
|
|
|
objectActor,
|
|
|
|
/* forPreview */ true
|
|
|
|
)) {
|
2018-03-28 09:24:41 +03:00
|
|
|
items.push(item);
|
2018-10-19 15:55:39 +03:00
|
|
|
if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
|
2018-03-28 09:24:41 +03:00
|
|
|
break;
|
|
|
|
}
|
2019-07-05 12:26:00 +03:00
|
|
|
}
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
2019-07-05 12:26:00 +03:00
|
|
|
],
|
2018-03-28 09:24:41 +03:00
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
WeakSet: [
|
|
|
|
function(objectActor, grip) {
|
2019-12-09 15:35:04 +03:00
|
|
|
const enumEntries = PropertyIterators.enumWeakSetEntries(
|
|
|
|
objectActor,
|
|
|
|
/* forPreview */ true
|
|
|
|
);
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
grip.preview = {
|
|
|
|
kind: "ArrayLike",
|
2018-10-19 15:55:39 +03:00
|
|
|
length: enumEntries.size,
|
2018-03-28 09:24:41 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
// Avoid recursive object grips.
|
2018-06-01 13:36:09 +03:00
|
|
|
if (objectActor.hooks.getGripDepth() > 1) {
|
2018-03-28 09:24:41 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const items = (grip.preview.items = []);
|
|
|
|
for (const item of enumEntries) {
|
|
|
|
items.push(item);
|
|
|
|
if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
|
|
|
|
break;
|
|
|
|
}
|
2019-07-05 12:26:00 +03:00
|
|
|
}
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
2019-07-05 12:26:00 +03:00
|
|
|
],
|
2018-03-28 09:24:41 +03:00
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
Map: [
|
|
|
|
function(objectActor, grip) {
|
2019-09-17 01:48:15 +03:00
|
|
|
const size = DevToolsUtils.getProperty(objectActor.obj, "size");
|
|
|
|
if (typeof size != "number") {
|
|
|
|
return false;
|
|
|
|
}
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
grip.preview = {
|
|
|
|
kind: "MapLike",
|
|
|
|
size: size,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (objectActor.hooks.getGripDepth() > 1) {
|
2018-06-01 13:36:09 +03:00
|
|
|
return true;
|
2019-07-05 12:26:00 +03:00
|
|
|
}
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
const entries = (grip.preview.entries = []);
|
2019-12-09 15:35:04 +03:00
|
|
|
for (const entry of PropertyIterators.enumMapEntries(
|
|
|
|
objectActor,
|
|
|
|
/* forPreview */ true
|
|
|
|
)) {
|
2018-03-28 09:24:41 +03:00
|
|
|
entries.push(entry);
|
|
|
|
if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
|
|
|
|
break;
|
|
|
|
}
|
2019-07-05 12:26:00 +03:00
|
|
|
}
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
2019-07-05 12:26:00 +03:00
|
|
|
],
|
2018-03-28 09:24:41 +03:00
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
WeakMap: [
|
|
|
|
function(objectActor, grip) {
|
2019-12-09 15:35:04 +03:00
|
|
|
const enumEntries = PropertyIterators.enumWeakMapEntries(
|
|
|
|
objectActor,
|
|
|
|
/* forPreview */ true
|
|
|
|
);
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
grip.preview = {
|
|
|
|
kind: "MapLike",
|
2018-10-19 15:55:39 +03:00
|
|
|
size: enumEntries.size,
|
2018-03-28 09:24:41 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
if (objectActor.hooks.getGripDepth() > 1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
const entries = (grip.preview.entries = []);
|
|
|
|
for (const entry of enumEntries) {
|
2018-03-28 09:24:41 +03:00
|
|
|
entries.push(entry);
|
|
|
|
if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
|
|
|
|
break;
|
2019-07-05 12:26:00 +03:00
|
|
|
}
|
2018-03-28 09:24:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
2019-07-05 12:26:00 +03:00
|
|
|
],
|
2018-03-28 09:24:41 +03:00
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
DOMStringMap: [
|
|
|
|
function({ obj, hooks }, grip, rawObj) {
|
2018-03-28 09:24:41 +03:00
|
|
|
if (!rawObj) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const keys = obj.getOwnPropertyNames();
|
|
|
|
grip.preview = {
|
|
|
|
kind: "MapLike",
|
|
|
|
size: keys.length,
|
|
|
|
};
|
|
|
|
|
2019-05-14 09:46:30 +03:00
|
|
|
if (hooks.getGripDepth() > 1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-28 09:24:41 +03:00
|
|
|
const entries = (grip.preview.entries = []);
|
|
|
|
for (const key of keys) {
|
|
|
|
const value = ObjectUtils.makeDebuggeeValueIfNeeded(obj, rawObj[key]);
|
|
|
|
entries.push([key, hooks.createValueGrip(value)]);
|
|
|
|
if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
|
2018-06-01 13:36:09 +03:00
|
|
|
break;
|
2019-07-05 12:26:00 +03:00
|
|
|
}
|
|
|
|
}
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
return true;
|
2019-07-05 12:26:00 +03:00
|
|
|
},
|
|
|
|
],
|
|
|
|
|
2018-03-28 09:24:41 +03:00
|
|
|
Proxy: [
|
|
|
|
function({ obj, hooks }, grip, rawObj) {
|
|
|
|
// Only preview top-level proxies, avoiding recursion. Otherwise, since both the
|
2018-10-19 15:55:39 +03:00
|
|
|
// target and handler can also be proxies, we could get an exponential behavior.
|
|
|
|
if (hooks.getGripDepth() > 1) {
|
2018-03-28 09:24:41 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The `isProxy` getter of the debuggee object only detects proxies without
|
|
|
|
// security wrappers. If false, the target and handler are not available.
|
2018-06-01 13:36:09 +03:00
|
|
|
const hasTargetAndHandler = obj.isProxy;
|
2019-07-05 12:26:00 +03:00
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
grip.preview = {
|
2018-03-28 09:24:41 +03:00
|
|
|
kind: "Object",
|
|
|
|
ownProperties: Object.create(null),
|
2018-10-19 15:55:39 +03:00
|
|
|
ownPropertiesLength: 2 * hasTargetAndHandler,
|
2019-07-05 12:26:00 +03:00
|
|
|
};
|
|
|
|
|
2018-03-28 09:24:41 +03:00
|
|
|
if (hasTargetAndHandler) {
|
2019-05-14 09:46:30 +03:00
|
|
|
Object.assign(grip.preview.ownProperties, {
|
|
|
|
"<target>": { value: hooks.createValueGrip(obj.proxyTarget) },
|
|
|
|
"<handler>": { value: hooks.createValueGrip(obj.proxyHandler) },
|
|
|
|
});
|
2018-03-28 09:24:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generic previewer for classes wrapping primitives, like String,
|
|
|
|
* Number and Boolean.
|
|
|
|
*
|
|
|
|
* @param string className
|
|
|
|
* Class name to expect.
|
|
|
|
* @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
|
|
|
|
* @return Booolean true if the object was handled, false otherwise
|
|
|
|
*/
|
|
|
|
function wrappedPrimitivePreviewer(
|
|
|
|
className,
|
|
|
|
classObj,
|
|
|
|
objectActor,
|
|
|
|
grip,
|
|
|
|
rawObj
|
|
|
|
) {
|
2018-06-01 13:36:09 +03:00
|
|
|
const { obj, hooks } = objectActor;
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
let v = null;
|
|
|
|
try {
|
|
|
|
v = classObj.prototype.valueOf.call(rawObj);
|
|
|
|
} catch (ex) {
|
|
|
|
// valueOf() can throw if the raw JS object is "misbehaved".
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v === null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
const canHandle = GenericObject(
|
|
|
|
objectActor,
|
|
|
|
grip,
|
|
|
|
rawObj,
|
|
|
|
className === "String"
|
|
|
|
);
|
2018-03-28 09:24:41 +03:00
|
|
|
if (!canHandle) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
grip.preview.wrappedValue = hooks.createValueGrip(
|
|
|
|
ObjectUtils.makeDebuggeeValueIfNeeded(obj, v)
|
|
|
|
);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function GenericObject(
|
|
|
|
objectActor,
|
|
|
|
grip,
|
|
|
|
rawObj,
|
|
|
|
specialStringBehavior = false
|
|
|
|
) {
|
2018-06-01 13:36:09 +03:00
|
|
|
const { obj, hooks } = objectActor;
|
2018-03-28 09:24:41 +03:00
|
|
|
if (grip.preview || grip.displayString || hooks.getGripDepth() > 1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
const preview = (grip.preview = {
|
2018-03-28 09:24:41 +03:00
|
|
|
kind: "Object",
|
|
|
|
ownProperties: Object.create(null),
|
|
|
|
ownSymbols: [],
|
|
|
|
});
|
|
|
|
|
2019-12-13 16:27:02 +03:00
|
|
|
const names = ObjectUtils.getPropNamesFromObject(obj, rawObj);
|
|
|
|
const symbols = ObjectUtils.getSafeOwnPropertySymbols(obj);
|
|
|
|
|
2018-03-28 09:24:41 +03:00
|
|
|
preview.ownPropertiesLength = names.length;
|
|
|
|
preview.ownSymbolsLength = symbols.length;
|
|
|
|
|
2019-12-13 16:27:02 +03:00
|
|
|
let length,
|
|
|
|
i = 0;
|
2018-03-28 09:24:41 +03:00
|
|
|
if (specialStringBehavior) {
|
|
|
|
length = DevToolsUtils.getProperty(obj, "length");
|
|
|
|
if (typeof length != "number") {
|
|
|
|
specialStringBehavior = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
for (const name of names) {
|
2018-03-28 09:24:41 +03:00
|
|
|
if (specialStringBehavior && /^[0-9]+$/.test(name)) {
|
2018-06-01 13:36:09 +03:00
|
|
|
const num = parseInt(name, 10);
|
2018-03-28 09:24:41 +03:00
|
|
|
if (num.toString() === name && num >= 0 && num < length) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
const desc = objectActor._propertyDescriptor(name, true);
|
2018-03-28 09:24:41 +03:00
|
|
|
if (!desc) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
preview.ownProperties[name] = desc;
|
|
|
|
if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
for (const symbol of symbols) {
|
|
|
|
const descriptor = objectActor._propertyDescriptor(symbol, true);
|
2018-03-28 09:24:41 +03:00
|
|
|
if (!descriptor) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
preview.ownSymbols.push(
|
|
|
|
Object.assign(
|
|
|
|
{
|
2018-10-19 15:55:39 +03:00
|
|
|
descriptor,
|
2018-03-28 09:24:41 +03:00
|
|
|
},
|
|
|
|
hooks.createValueGrip(symbol)
|
2019-07-05 12:26:00 +03:00
|
|
|
)
|
2018-03-28 09:24:41 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-11 00:03:28 +03:00
|
|
|
if (i < OBJECT_PREVIEW_MAX_ITEMS) {
|
2018-03-28 09:24:41 +03:00
|
|
|
preview.safeGetterValues = objectActor._findSafeGetterValues(
|
|
|
|
Object.keys(preview.ownProperties),
|
|
|
|
OBJECT_PREVIEW_MAX_ITEMS - i
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Preview functions that do not rely on the object class.
|
|
|
|
previewers.Object = [
|
|
|
|
function TypedArray({ obj, hooks }, grip) {
|
|
|
|
if (!ObjectUtils.isTypedArray(obj)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
grip.preview = {
|
|
|
|
kind: "ArrayLike",
|
|
|
|
length: ObjectUtils.getArrayLength(obj),
|
|
|
|
};
|
|
|
|
|
|
|
|
if (hooks.getGripDepth() > 1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-12-09 15:35:04 +03:00
|
|
|
const previewLength = Math.min(
|
|
|
|
OBJECT_PREVIEW_MAX_ITEMS,
|
|
|
|
grip.preview.length
|
|
|
|
);
|
2019-08-30 20:06:05 +03:00
|
|
|
grip.preview.items = [];
|
|
|
|
for (let i = 0; i < previewLength; i++) {
|
|
|
|
const desc = obj.getOwnPropertyDescriptor(i);
|
|
|
|
if (!desc) {
|
|
|
|
break;
|
2018-07-24 17:34:23 +03:00
|
|
|
}
|
2019-08-30 20:06:05 +03:00
|
|
|
grip.preview.items.push(desc.value);
|
2018-03-28 09:24:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
function Error({ obj, hooks }, grip) {
|
|
|
|
switch (obj.class) {
|
|
|
|
case "Error":
|
|
|
|
case "EvalError":
|
|
|
|
case "RangeError":
|
|
|
|
case "ReferenceError":
|
|
|
|
case "SyntaxError":
|
|
|
|
case "TypeError":
|
|
|
|
case "URIError":
|
2019-09-17 01:48:15 +03:00
|
|
|
const name = DevToolsUtils.getProperty(obj, "name");
|
|
|
|
const msg = DevToolsUtils.getProperty(obj, "message");
|
|
|
|
const stack = DevToolsUtils.getProperty(obj, "stack");
|
|
|
|
const fileName = DevToolsUtils.getProperty(obj, "fileName");
|
|
|
|
const lineNumber = DevToolsUtils.getProperty(obj, "lineNumber");
|
|
|
|
const columnNumber = DevToolsUtils.getProperty(obj, "columnNumber");
|
|
|
|
grip.preview = {
|
|
|
|
kind: "Error",
|
|
|
|
name: hooks.createValueGrip(name),
|
|
|
|
message: hooks.createValueGrip(msg),
|
|
|
|
stack: hooks.createValueGrip(stack),
|
|
|
|
fileName: hooks.createValueGrip(fileName),
|
|
|
|
lineNumber: hooks.createValueGrip(lineNumber),
|
|
|
|
columnNumber: hooks.createValueGrip(columnNumber),
|
|
|
|
};
|
2018-03-28 09:24:41 +03:00
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
function CSSMediaRule({ obj, hooks }, grip, rawObj) {
|
|
|
|
if (isWorker || !rawObj || obj.class != "CSSMediaRule") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
grip.preview = {
|
|
|
|
kind: "ObjectWithText",
|
|
|
|
text: hooks.createValueGrip(rawObj.conditionText),
|
|
|
|
};
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
function CSSStyleRule({ obj, hooks }, grip, rawObj) {
|
|
|
|
if (isWorker || !rawObj || obj.class != "CSSStyleRule") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
grip.preview = {
|
|
|
|
kind: "ObjectWithText",
|
|
|
|
text: hooks.createValueGrip(rawObj.selectorText),
|
|
|
|
};
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
function ObjectWithURL({ obj, hooks }, grip, rawObj) {
|
|
|
|
if (
|
|
|
|
isWorker ||
|
|
|
|
!rawObj ||
|
|
|
|
!(
|
|
|
|
obj.class == "CSSImportRule" ||
|
|
|
|
obj.class == "CSSStyleSheet" ||
|
|
|
|
obj.class == "Location" ||
|
|
|
|
rawObj instanceof Ci.nsIDOMWindow
|
2019-07-05 12:26:00 +03:00
|
|
|
)
|
2018-03-28 09:24:41 +03:00
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
let url;
|
|
|
|
if (rawObj instanceof Ci.nsIDOMWindow && rawObj.location) {
|
|
|
|
url = rawObj.location.href;
|
|
|
|
} else if (rawObj.href) {
|
|
|
|
url = rawObj.href;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
grip.preview = {
|
|
|
|
kind: "ObjectWithURL",
|
|
|
|
url: hooks.createValueGrip(url),
|
|
|
|
};
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
function ArrayLike({ obj, hooks }, grip, rawObj) {
|
|
|
|
if (
|
|
|
|
isWorker ||
|
|
|
|
!rawObj ||
|
|
|
|
(obj.class != "DOMStringList" &&
|
|
|
|
obj.class != "DOMTokenList" &&
|
|
|
|
obj.class != "CSSRuleList" &&
|
|
|
|
obj.class != "MediaList" &&
|
|
|
|
obj.class != "StyleSheetList" &&
|
|
|
|
obj.class != "NamedNodeMap" &&
|
|
|
|
obj.class != "FileList" &&
|
|
|
|
obj.class != "NodeList")
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof rawObj.length != "number") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
grip.preview = {
|
|
|
|
kind: "ArrayLike",
|
|
|
|
length: rawObj.length,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (hooks.getGripDepth() > 1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
const items = (grip.preview.items = []);
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
for (
|
|
|
|
let i = 0;
|
|
|
|
i < rawObj.length && items.length < OBJECT_PREVIEW_MAX_ITEMS;
|
|
|
|
i++
|
|
|
|
) {
|
2018-06-01 13:36:09 +03:00
|
|
|
const value = ObjectUtils.makeDebuggeeValueIfNeeded(obj, rawObj[i]);
|
2018-03-28 09:24:41 +03:00
|
|
|
items.push(hooks.createValueGrip(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
function CSSStyleDeclaration({ obj, hooks }, grip, rawObj) {
|
|
|
|
if (
|
|
|
|
isWorker ||
|
|
|
|
!rawObj ||
|
|
|
|
(obj.class != "CSSStyleDeclaration" && obj.class != "CSS2Properties")
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
grip.preview = {
|
|
|
|
kind: "MapLike",
|
|
|
|
size: rawObj.length,
|
|
|
|
};
|
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
const entries = (grip.preview.entries = []);
|
2018-03-28 09:24:41 +03:00
|
|
|
|
|
|
|
for (let i = 0; i < OBJECT_PREVIEW_MAX_ITEMS && i < rawObj.length; i++) {
|
2018-06-01 13:36:09 +03:00
|
|
|
const prop = rawObj[i];
|
|
|
|
const value = rawObj.getPropertyValue(prop);
|
2018-03-28 09:24:41 +03:00
|
|
|
entries.push([prop, hooks.createValueGrip(value)]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
function DOMNode({ obj, hooks }, grip, rawObj) {
|
|
|
|
if (
|
|
|
|
isWorker ||
|
|
|
|
obj.class == "Object" ||
|
|
|
|
!rawObj ||
|
2018-05-30 05:58:50 +03:00
|
|
|
!Node.isInstance(rawObj)
|
|
|
|
) {
|
2018-03-28 09:24:41 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
const preview = (grip.preview = {
|
2018-03-28 09:24:41 +03:00
|
|
|
kind: "DOMNode",
|
|
|
|
nodeType: rawObj.nodeType,
|
|
|
|
nodeName: rawObj.nodeName,
|
|
|
|
isConnected: rawObj.isConnected === true,
|
|
|
|
});
|
|
|
|
|
2018-05-29 20:39:00 +03:00
|
|
|
if (rawObj.nodeType == rawObj.DOCUMENT_NODE && rawObj.location) {
|
2018-03-28 09:24:41 +03:00
|
|
|
preview.location = hooks.createValueGrip(rawObj.location.href);
|
2018-04-18 04:13:03 +03:00
|
|
|
} else if (obj.class == "DocumentFragment") {
|
2018-03-28 09:24:41 +03:00
|
|
|
preview.childNodesLength = rawObj.childNodes.length;
|
|
|
|
|
|
|
|
if (hooks.getGripDepth() < 2) {
|
|
|
|
preview.childNodes = [];
|
2018-06-01 13:36:09 +03:00
|
|
|
for (const node of rawObj.childNodes) {
|
|
|
|
const actor = hooks.createValueGrip(obj.makeDebuggeeValue(node));
|
2018-03-28 09:24:41 +03:00
|
|
|
preview.childNodes.push(actor);
|
|
|
|
if (preview.childNodes.length == OBJECT_PREVIEW_MAX_ITEMS) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-04-27 06:37:34 +03:00
|
|
|
} else if (Element.isInstance(rawObj)) {
|
2018-03-28 09:24:41 +03:00
|
|
|
// 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 (rawObj.namespaceURI == "http://www.w3.org/1999/xhtml") {
|
|
|
|
preview.nodeName = preview.nodeName.toLowerCase();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add preview for DOM element attributes.
|
|
|
|
preview.attributes = {};
|
|
|
|
preview.attributesLength = rawObj.attributes.length;
|
2018-06-01 13:36:09 +03:00
|
|
|
for (const attr of rawObj.attributes) {
|
2018-03-28 09:24:41 +03:00
|
|
|
preview.attributes[attr.nodeName] = hooks.createValueGrip(attr.value);
|
|
|
|
}
|
|
|
|
} else if (obj.class == "Attr") {
|
|
|
|
preview.value = hooks.createValueGrip(rawObj.value);
|
|
|
|
} else if (
|
|
|
|
obj.class == "Text" ||
|
|
|
|
obj.class == "CDATASection" ||
|
|
|
|
obj.class == "Comment"
|
|
|
|
) {
|
|
|
|
preview.textContent = hooks.createValueGrip(rawObj.textContent);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
function DOMEvent({ obj, hooks }, grip, rawObj) {
|
2018-04-20 19:55:31 +03:00
|
|
|
if (isWorker || !rawObj || !Event.isInstance(rawObj)) {
|
2018-03-28 09:24:41 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
const preview = (grip.preview = {
|
2018-03-28 09:24:41 +03:00
|
|
|
kind: "DOMEvent",
|
|
|
|
type: rawObj.type,
|
|
|
|
properties: Object.create(null),
|
|
|
|
});
|
|
|
|
|
|
|
|
if (hooks.getGripDepth() < 2) {
|
2018-06-01 13:36:09 +03:00
|
|
|
const target = obj.makeDebuggeeValue(rawObj.target);
|
2018-03-28 09:24:41 +03:00
|
|
|
preview.target = hooks.createValueGrip(target);
|
|
|
|
}
|
|
|
|
|
2019-12-13 16:27:02 +03:00
|
|
|
if (obj.class == "KeyboardEvent") {
|
2018-03-28 09:24:41 +03:00
|
|
|
preview.eventKind = "key";
|
2019-12-13 16:27:02 +03:00
|
|
|
preview.modifiers = ObjectUtils.getModifiersForEvent(rawObj);
|
2018-03-28 09:24:41 +03:00
|
|
|
}
|
|
|
|
|
2019-12-13 16:27:02 +03:00
|
|
|
const props = ObjectUtils.getPropsForEvent(obj.class);
|
|
|
|
|
2018-03-28 09:24:41 +03:00
|
|
|
// Add event-specific properties.
|
2018-06-01 13:36:09 +03:00
|
|
|
for (const prop of props) {
|
2018-03-28 09:24:41 +03:00
|
|
|
let value = rawObj[prop];
|
2019-12-13 16:27:02 +03:00
|
|
|
if (ObjectUtils.isObjectOrFunction(value)) {
|
2018-03-28 09:24:41 +03:00
|
|
|
// Skip properties pointing to objects.
|
|
|
|
if (hooks.getGripDepth() > 1) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
value = obj.makeDebuggeeValue(value);
|
|
|
|
}
|
|
|
|
preview.properties[prop] = hooks.createValueGrip(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add any properties we find on the event object.
|
|
|
|
if (!props.length) {
|
|
|
|
let i = 0;
|
2018-06-01 13:36:09 +03:00
|
|
|
for (const prop in rawObj) {
|
2018-03-28 09:24:41 +03:00
|
|
|
let value = rawObj[prop];
|
|
|
|
if (
|
|
|
|
prop == "target" ||
|
|
|
|
prop == "type" ||
|
|
|
|
value === null ||
|
|
|
|
typeof value == "function"
|
|
|
|
) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (value && typeof value == "object") {
|
|
|
|
if (hooks.getGripDepth() > 1) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
value = obj.makeDebuggeeValue(value);
|
|
|
|
}
|
|
|
|
preview.properties[prop] = hooks.createValueGrip(value);
|
|
|
|
if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
function DOMException({ obj, hooks }, grip, rawObj) {
|
2018-04-07 06:27:56 +03:00
|
|
|
if (isWorker || !rawObj || obj.class !== "DOMException") {
|
2018-03-28 09:24:41 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
grip.preview = {
|
|
|
|
kind: "DOMException",
|
|
|
|
name: hooks.createValueGrip(rawObj.name),
|
|
|
|
message: hooks.createValueGrip(rawObj.message),
|
|
|
|
code: hooks.createValueGrip(rawObj.code),
|
|
|
|
result: hooks.createValueGrip(rawObj.result),
|
|
|
|
filename: hooks.createValueGrip(rawObj.filename),
|
|
|
|
lineNumber: hooks.createValueGrip(rawObj.lineNumber),
|
|
|
|
columnNumber: hooks.createValueGrip(rawObj.columnNumber),
|
|
|
|
};
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
function Object(objectActor, grip, rawObj) {
|
|
|
|
return GenericObject(
|
|
|
|
objectActor,
|
|
|
|
grip,
|
|
|
|
rawObj,
|
|
|
|
/* specialStringBehavior = */ false
|
|
|
|
);
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
module.exports = previewers;
|