Bug 1457711 - Catch errors thrown by console's property previewer; r=nchevobbe

MozReview-Commit-ID: LKsYn5gSn58
This commit is contained in:
Oriol Brufau
2018-04-28 22:29:43 +02:00
parent e7a99f8281
commit d55e29ac45
2 changed files with 83 additions and 14 deletions

View File

@@ -393,6 +393,9 @@ function getMatchedPropsImpl(obj, match, {chainIterator, getProperties}) {
let iter = chainIterator(obj); let iter = chainIterator(obj);
for (obj of iter) { for (obj of iter) {
let props = getProperties(obj); let props = getProperties(obj);
if (!props) {
continue;
}
numProps += props.length; numProps += props.length;
// If there are too many properties to event attempt autocompletion, // If there are too many properties to event attempt autocompletion,
@@ -459,12 +462,22 @@ var JSObjectSupport = {
chainIterator: function* (obj) { chainIterator: function* (obj) {
while (obj) { while (obj) {
yield obj; yield obj;
try {
obj = Object.getPrototypeOf(obj); obj = Object.getPrototypeOf(obj);
} catch (error) {
// The above can throw e.g. for some proxy objects.
return;
}
} }
}, },
getProperties: function(obj) { getProperties: function(obj) {
try {
return Object.getOwnPropertyNames(obj); return Object.getOwnPropertyNames(obj);
} catch (error) {
// The above can throw e.g. for some proxy objects.
return null;
}
}, },
getProperty: function() { getProperty: function() {
@@ -477,12 +490,22 @@ var DebuggerObjectSupport = {
chainIterator: function* (obj) { chainIterator: function* (obj) {
while (obj) { while (obj) {
yield obj; yield obj;
try {
obj = obj.proto; obj = obj.proto;
} catch (error) {
// The above can throw e.g. for some proxy objects.
return;
}
} }
}, },
getProperties: function(obj) { getProperties: function(obj) {
try {
return obj.getOwnPropertyNames(); return obj.getOwnPropertyNames();
} catch (error) {
// The above can throw e.g. for some proxy objects.
return null;
}
}, },
getProperty: function(obj, name, rootObj) { getProperty: function(obj, name, rootObj) {

View File

@@ -23,7 +23,7 @@ function evaluateJS(input, options = {}) {
}); });
} }
function autocompletePromise(str, cursor, frameActor) { function autocompletePromise(str, cursor = str.length, frameActor) {
return new Promise(resolve => { return new Promise(resolve => {
gState.client.autocomplete(str, cursor, resolve, frameActor); gState.client.autocomplete(str, cursor, resolve, frameActor);
}); });
@@ -34,13 +34,13 @@ function autocompletePromise(str, cursor, frameActor) {
let runningInTab = true; let runningInTab = true;
function startTest({worker}) { function startTest({worker}) {
if (worker) { if (worker) {
attachConsoleToWorker(["PageError"], onAttach); attachConsoleToWorker(["PageError"], onAttach.bind(null, true));
} else { } else {
attachConsoleToTab(["PageError"], onAttach); attachConsoleToTab(["PageError"], onAttach.bind(null, false));
} }
}; };
let onAttach = async function (aState, response) { let onAttach = async function (isWorker, aState, response) {
gState = aState; gState = aState;
let longStrLength = DebuggerServer.LONG_STRING_LENGTH; let longStrLength = DebuggerServer.LONG_STRING_LENGTH;
@@ -71,20 +71,32 @@ let onAttach = async function (aState, response) {
for (let i = 0; i < ${MAX_AUTOCOMPLETIONS * 2}; i++) { for (let i = 0; i < ${MAX_AUTOCOMPLETIONS * 2}; i++) {
window.largeObject2['a' + i] = i; window.largeObject2['a' + i] = i;
} }
window.proxy1 = new Proxy({foo: 1}, {
getPrototypeOf() { throw new Error() }
});
window.proxy2 = new Proxy(Object.create(Object.create(null, {foo:{}})), {
ownKeys() { throw new Error() }
});
`; `;
await evaluateJS(script); await evaluateJS(script);
let tests = [doAutocomplete1, doAutocomplete2, doAutocomplete3, let tests = [doAutocomplete1, doAutocomplete2, doAutocomplete3,
doAutocomplete4, doAutocompleteLarge1, doAutocomplete4, doAutocompleteLarge1,
doAutocompleteLarge2]; doAutocompleteLarge2, doAutocompleteProxyThrowsPrototype,
doAutocompleteProxyThrowsOwnKeys];
if (!isWorker) {
// `Cu` is not defined in workers, then we can't test `Cu.Sandbox`
tests.push(doAutocompleteSandbox);
}
runTests(tests, testEnd); runTests(tests, testEnd);
}; };
async function doAutocomplete1() { async function doAutocomplete1() {
info("test autocomplete for 'window.foo'"); info("test autocomplete for 'window.foo'");
let response = await autocompletePromise("window.foo", 10); let response = await autocompletePromise("window.foo");
let matches = response.matches; let matches = response.matches;
is(response.matchProp, "foo", "matchProp"); is(response.matchProp, "foo", "matchProp");
@@ -96,7 +108,7 @@ async function doAutocomplete1() {
async function doAutocomplete2() { async function doAutocomplete2() {
info("test autocomplete for 'window.foobarObject.'"); info("test autocomplete for 'window.foobarObject.'");
let response = await autocompletePromise("window.foobarObject.", 20); let response = await autocompletePromise("window.foobarObject.");
let matches = response.matches; let matches = response.matches;
ok(!response.matchProp, "matchProp"); ok(!response.matchProp, "matchProp");
@@ -124,7 +136,7 @@ async function doAutocomplete3() {
async function doAutocomplete4() { async function doAutocomplete4() {
// Check that completion requests can have no suggestions. // Check that completion requests can have no suggestions.
info("test autocomplete for 'dump(window.foobarObject.)'"); info("test autocomplete for 'dump(window.foobarObject.)'");
let response = await autocompletePromise("dump(window.foobarObject.)", 26); let response = await autocompletePromise("dump(window.foobarObject.)");
ok(!response.matchProp, "matchProp"); ok(!response.matchProp, "matchProp");
is(response.matches.length, 0, "matches.length"); is(response.matches.length, 0, "matches.length");
@@ -135,7 +147,7 @@ async function doAutocompleteLarge1() {
// Check that completion requests with too large objects will // Check that completion requests with too large objects will
// have no suggestions. // have no suggestions.
info("test autocomplete for 'window.largeObject1.'"); info("test autocomplete for 'window.largeObject1.'");
let response = await autocompletePromise("window.largeObject1.", 20); let response = await autocompletePromise("window.largeObject1.");
ok(!response.matchProp, "matchProp"); ok(!response.matchProp, "matchProp");
info (response.matches.join("|")); info (response.matches.join("|"));
is(response.matches.length, 0, "Bailed out with too many properties"); is(response.matches.length, 0, "Bailed out with too many properties");
@@ -147,13 +159,47 @@ async function doAutocompleteLarge2() {
// Check that completion requests with pretty large objects will // Check that completion requests with pretty large objects will
// have MAX_AUTOCOMPLETIONS suggestions // have MAX_AUTOCOMPLETIONS suggestions
info("test autocomplete for 'window.largeObject2.'"); info("test autocomplete for 'window.largeObject2.'");
let response = await autocompletePromise("window.largeObject2.", 20); let response = await autocompletePromise("window.largeObject2.");
ok(!response.matchProp, "matchProp"); ok(!response.matchProp, "matchProp");
is(response.matches.length, MAX_AUTOCOMPLETIONS, "matches.length is MAX_AUTOCOMPLETIONS"); is(response.matches.length, MAX_AUTOCOMPLETIONS, "matches.length is MAX_AUTOCOMPLETIONS");
nextTest(); nextTest();
} }
async function doAutocompleteProxyThrowsPrototype() {
// Check that completion provides own properties even if [[GetPrototypeOf]] throws.
info("test autocomplete for 'window.proxy1.'");
let response = await autocompletePromise("window.proxy1.");
ok(!response.matchProp, "matchProp");
is(response.matches.length, 1, "matches.length");
checkObject(response.matches, ["foo"]);
nextTest();
}
async function doAutocompleteProxyThrowsOwnKeys() {
// Check that completion provides inherited properties even if [[OwnPropertyKeys]] throws.
info("test autocomplete for 'window.proxy2.'");
let response = await autocompletePromise("window.proxy2.");
ok(!response.matchProp, "matchProp");
is(response.matches.length, 1, "matches.length");
checkObject(response.matches, ["foo"]);
nextTest();
}
async function doAutocompleteSandbox() {
// Check that completion provides inherited properties even if [[OwnPropertyKeys]] throws.
info("test autocomplete for 'Cu.Sandbox.'");
let response = await autocompletePromise("Cu.Sandbox.");
ok(!response.matchProp, "matchProp");
let keys = Object.getOwnPropertyNames(Object.prototype).sort();
is(response.matches.length, keys.length, "matches.length");
checkObject(response.matches, keys);
nextTest();
}
function testEnd() function testEnd()
{ {
// If this is the first run, reload the page and do it again // If this is the first run, reload the page and do it again