Bug 1316266 - Handle grip `safeGetterValues` property in Grip Reps; r=Honza
Some object has properties in the safeGetterValues property, like ArrayBuffer
for example.
We now evaluate this property too when building the Rep. A function was created
to retrieve the property value since in safeGetterValues, the value is a wrapper
object with a getterValue property holding the actual value.
We add some tests to make sure we handle those grips as expected.
MozReview-Commit-ID: JqnTXnnAZpd
--- a/devtools/client/shared/components/reps/grip.js
+++ b/devtools/client/shared/components/reps/grip.js
@@ -64,32 +64,40 @@ define(function (require, exports, modul
let isInterestingProp = this.props.isInterestingProp || ((type, value) => {
return (
type == "boolean" ||
type == "number" ||
(type == "string" && value.length != 0)
);
});
- let ownProperties = object.preview ? object.preview.ownProperties : {};
+ let properties = object.preview
+ ? object.preview.ownProperties
+ : {};
let propertiesLength = object.preview && object.preview.ownPropertiesLength
? object.preview.ownPropertiesLength
: object.ownPropertyLength;
- let indexes = this.getPropIndexes(ownProperties, max, isInterestingProp);
+
+ if (object.preview && object.preview.safeGetterValues) {
+ properties = Object.assign({}, properties, object.preview.safeGetterValues);
+ propertiesLength += Object.keys(object.preview.safeGetterValues).length;
+ }
+
+ let indexes = this.getPropIndexes(properties, max, isInterestingProp);
if (indexes.length < max && indexes.length < propertiesLength) {
// There are not enough props yet. Then add uninteresting props to display them.
indexes = indexes.concat(
- this.getPropIndexes(ownProperties, max - indexes.length, (t, value, name) => {
+ this.getPropIndexes(properties, max - indexes.length, (t, value, name) => {
return !isInterestingProp(t, value, name);
})
);
}
- const truncate = Object.keys(ownProperties).length > max;
- let props = this.getProps(ownProperties, indexes, truncate);
+ const truncate = Object.keys(properties).length > max;
+ let props = this.getProps(properties, indexes, truncate);
if (truncate) {
// There are some undisplayed props. Then display "more...".
let objectLink = this.props.objectLink || span;
props.push(Caption({
object: objectLink({
object: object
}, `${object.ownPropertyLength - max} moreā¦`)
@@ -97,82 +105,98 @@ define(function (require, exports, modul
}
return props;
},
/**
* Get props ordered by index.
*
- * @param {Object} ownProperties Props object.
+ * @param {Object} properties Props object.
* @param {Array} indexes Indexes of props.
* @param {Boolean} truncate true if the grip will be truncated.
* @return {Array} Props.
*/
- getProps: function (ownProperties, indexes, truncate) {
+ getProps: function (properties, indexes, truncate) {
let props = [];
// Make indexes ordered by ascending.
indexes.sort(function (a, b) {
return a - b;
});
indexes.forEach((i) => {
- let name = Object.keys(ownProperties)[i];
- let prop = ownProperties[name];
- let value = prop.value !== undefined ? prop.value : prop;
+ let name = Object.keys(properties)[i];
+ let value = this.getPropValue(properties[name]);
+
props.push(PropRep(Object.assign({}, this.props, {
mode: "tiny",
name: name,
object: value,
equal: ": ",
delim: i !== indexes.length - 1 || truncate ? ", " : "",
defaultRep: Grip
})));
});
return props;
},
/**
* Get the indexes of props in the object.
*
- * @param {Object} ownProperties Props object.
+ * @param {Object} properties Props object.
* @param {Number} max The maximum length of indexes array.
* @param {Function} filter Filter the props you want.
* @return {Array} Indexes of interesting props in the object.
*/
- getPropIndexes: function (ownProperties, max, filter) {
+ getPropIndexes: function (properties, max, filter) {
let indexes = [];
try {
let i = 0;
- for (let name in ownProperties) {
+ for (let name in properties) {
if (indexes.length >= max) {
return indexes;
}
- let prop = ownProperties[name];
- let value = prop.value !== undefined ? prop.value : prop;
-
// Type is specified in grip's "class" field and for primitive
// values use typeof.
+ let value = this.getPropValue(properties[name]);
let type = (value.class || typeof value);
type = type.toLowerCase();
if (filter(type, value, name)) {
indexes.push(i);
}
i++;
}
} catch (err) {
console.error(err);
}
+ return indexes;
+ },
- return indexes;
+ /**
+ * Get the actual value of a property.
+ *
+ * @param {Object} property
+ * @return {Object} Value of the property.
+ */
+ getPropValue: function (property) {
+ let value = property;
+ if (typeof property === "object") {
+ let keys = Object.keys(property);
+ if (keys.includes("value")) {
+ value = property.value;
+ } else if (keys.includes("getterValue")) {
+ value = property.getterValue;
+ }
+ }
+ return value;
},
render: function () {
let object = this.props.object;
let props = this.safePropIterator(object,
(this.props.mode == "long") ? 100 : 3);
let objectLink = this.props.objectLink || span;
--- a/devtools/client/shared/components/test/mochitest/test_reps_grip.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_grip.html
@@ -23,16 +23,18 @@ window.onload = Task.async(function* ()
const componentUnderTest = Grip;
try {
yield testBasic();
yield testBooleanObject();
yield testNumberObject();
yield testStringObject();
yield testProxy();
+ yield testArrayBuffer();
+ yield testSharedArrayBuffer();
// Test property iterator
yield testMaxProps();
yield testMoreThanMaxProps();
yield testUninterestingProps();
yield testNonEnumerableProps();
// Test that properties are rendered as expected by PropRep
@@ -212,16 +214,84 @@ window.onload = Task.async(function* ()
mode: "long",
expectedOutput: defaultOutput,
}
];
testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
}
+ function testArrayBuffer() {
+ // Test object: `new ArrayBuffer(10)`
+ const testName = "testArrayBuffer";
+
+ // Test that correct rep is chosen
+ const gripStub = getGripStub(testName);
+ const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
+ is(renderedRep.type, Grip.rep, `Rep correctly selects ${Grip.rep.displayName}`);
+
+ // Test rendering
+ const defaultOutput = `ArrayBuffer { byteLength: 10 }`;
+
+ const modeTests = [
+ {
+ mode: undefined,
+ expectedOutput: defaultOutput,
+ },
+ {
+ mode: "tiny",
+ expectedOutput: `ArrayBuffer`,
+ },
+ {
+ mode: "short",
+ expectedOutput: defaultOutput,
+ },
+ {
+ mode: "long",
+ expectedOutput: defaultOutput,
+ }
+ ];
+
+ testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
+ }
+
+ function testSharedArrayBuffer() {
+ // Test object: `new SharedArrayBuffer(5)`
+ const testName = "testSharedArrayBuffer";
+
+ // Test that correct rep is chosen
+ const gripStub = getGripStub(testName);
+ const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
+ is(renderedRep.type, Grip.rep, `Rep correctly selects ${Grip.rep.displayName}`);
+
+ // Test rendering
+ const defaultOutput = `SharedArrayBuffer { byteLength: 5 }`;
+
+ const modeTests = [
+ {
+ mode: undefined,
+ expectedOutput: defaultOutput,
+ },
+ {
+ mode: "tiny",
+ expectedOutput: `SharedArrayBuffer`,
+ },
+ {
+ mode: "short",
+ expectedOutput: defaultOutput,
+ },
+ {
+ mode: "long",
+ expectedOutput: defaultOutput,
+ }
+ ];
+
+ testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
+ }
+
function testMaxProps() {
// Test object: `{a: "a", b: "b", c: "c"}`;
const testName = "testMaxProps";
const defaultOutput = `Object { a: "a", b: "b", c: "c" }`;
const modeTests = [
{
@@ -757,15 +827,61 @@ window.onload = Task.async(function* ()
"length": 3
}
}
}
},
"ownPropertiesLength": 2
}
};
+ case "testArrayBuffer":
+ return {
+ "type": "object",
+ "actor": "server1.conn1.child1/obj170",
+ "class": "ArrayBuffer",
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "ownPropertyLength": 0,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {},
+ "ownPropertiesLength": 0,
+ "safeGetterValues": {
+ "byteLength": {
+ "getterValue": 10,
+ "getterPrototypeLevel": 1,
+ "enumerable": false,
+ "writable": true
+ }
+ }
+ }
+ };
+ case "testSharedArrayBuffer":
+ return {
+ "type": "object",
+ "actor": "server1.conn1.child1/obj171",
+ "class": "SharedArrayBuffer",
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "ownPropertyLength": 0,
+ "preview": {
+ "kind": "Object",
+ "ownProperties": {},
+ "ownPropertiesLength": 0,
+ "safeGetterValues": {
+ "byteLength": {
+ "getterValue": 5,
+ "getterPrototypeLevel": 1,
+ "enumerable": false,
+ "writable": true
+ }
+ }
+ }
+ };
}
}
});
</script>
</pre>
</body>
</html>