Commit 3d74d78f authored by Nicolas Chevobbe's avatar Nicolas Chevobbe
Browse files

Bug 1835680 - [devtools] Use proxy target when walking up prototype chain for...

Bug 1835680 - [devtools] Use proxy target when walking up prototype chain for autocomplete. r=devtools-reviewers,jdescottes.

In the JSPropertyProvider, we are walking up the prototype to retrieve properties.
For proxies with `getPrototypeOf` trap, this means that we were returning erroneous
values, as the prototype chain of the proxy isn't altered by the trap.
Worse, it could lead to infinite loop if the trap was setting a circular reference.

To avoid this, when dealing with a proxy, we retrieve the prototype of the proxy
target.

Test cases are added to ensure we don't regress this.

Differential Revision: https://phabricator.services.mozilla.com/D179640
parent 4bd6af91
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -714,6 +714,21 @@ var DebuggerObjectSupport = {
    while (obj) {
      yield obj;
      try {
        // There could be transparent security wrappers, unwrap to check if it's a proxy.
        const unwrapped = DevToolsUtils.unwrap(obj);
        if (unwrapped === undefined) {
          // Objects belonging to an invisible-to-debugger compartment can't be unwrapped.
          return;
        }

        if (unwrapped.isProxy) {
          // Proxies might have a `getPrototypeOf` method, which is triggered by `obj.proto`,
          // but this does not impact the actual prototype chain.
          // In such case, we need to use the proxy target prototype.
          // We retrieve proxyTarget from `obj` (and not `unwrapped`) to avoid exposing
          // the unwrapped target.
          obj = unwrapped.proxyTarget;
        }
        obj = obj.proto;
      } catch (error) {
        // The above can throw e.g. for some proxy objects.
+27 −0
Original line number Diff line number Diff line
@@ -72,6 +72,19 @@ function run_test() {
    };
  `;

  const testProxies = `
    var testSelfPrototypeProxy = new Proxy({
      hello: 1
    }, {
      getPrototypeOf: () => testProxy
    });
    var testArrayPrototypeProxy = new Proxy({
      world: 2
    }, {
      getPrototypeOf: () => Array.prototype
    })
  `;

  const sandbox = Cu.Sandbox("http://example.com");
  const dbg = new Debugger();
  const dbgObject = dbg.addDebuggee(sandbox);
@@ -91,6 +104,7 @@ function run_test() {
  Cu.evalInSandbox(testLet, sandbox);
  Cu.evalInSandbox(testGenerators, sandbox);
  Cu.evalInSandbox(testGetters, sandbox);
  Cu.evalInSandbox(testProxies, sandbox);

  info("Running tests with dbgObject");
  runChecks(dbgObject, null, sandbox);
@@ -678,6 +692,19 @@ function runChecks(dbgObject, environment, sandbox) {
  // Test autocompletion on debugger statement does not throw
  results = propertyProvider(`debugger.`);
  Assert.ok(results === null, "Does not complete a debugger keyword");

  // Test autocompletion on Proxies
  // proxy does not get autocompletion result from prototype defined in `getPrototypeOf`
  test_has_no_results(propertyProvider(`testArrayPrototypeProxy.filte`));
  results = propertyProvider(`testArrayPrototypeProxy.`);
  // it does get the own property
  test_has_result(results, `world`);
  // as well as method from the actual proxy target prototype
  test_has_result(results, `hasOwnProperty`);

  results = propertyProvider(`testSelfPrototypeProxy.`);
  test_has_result(results, `hello`);
  test_has_result(results, `hasOwnProperty`);
}

/**