зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1549782 - Implement Runtime.getProperties. r=remote-protocol-reviewers,ato
Differential Revision: https://phabricator.services.mozilla.com/D30232 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
9420c9eccf
Коммит
d99b8ff7b6
|
@ -72,6 +72,16 @@ class Runtime extends ContentProcessDomain {
|
|||
return context.evaluate(request.expression);
|
||||
}
|
||||
|
||||
getRemoteObject(objectId) {
|
||||
for (const ctx of this.contexts.values()) {
|
||||
const obj = ctx.getRemoteObject(objectId);
|
||||
if (typeof(obj) != "undefined") {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
releaseObject({ objectId }) {
|
||||
let context = null;
|
||||
for (const ctx of this.contexts.values()) {
|
||||
|
@ -121,6 +131,16 @@ class Runtime extends ContentProcessDomain {
|
|||
return context.callFunctionOn(request.functionDeclaration, request.arguments, request.returnByValue, request.awaitPromise, request.objectId);
|
||||
}
|
||||
|
||||
getProperties({ objectId, ownProperties }) {
|
||||
for (const ctx of this.contexts.values()) {
|
||||
const obj = ctx.getRemoteObject(objectId);
|
||||
if (typeof(obj) != "undefined") {
|
||||
return ctx.getProperties({ objectId, ownProperties });
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
get _debugger() {
|
||||
if (this.__debugger) {
|
||||
return this.__debugger;
|
||||
|
|
|
@ -48,6 +48,10 @@ class ExecutionContext {
|
|||
return this._remoteObjects.has(id);
|
||||
}
|
||||
|
||||
getRemoteObject(id) {
|
||||
return this._remoteObjects.get(id);
|
||||
}
|
||||
|
||||
releaseObject(id) {
|
||||
return this._remoteObjects.delete(id);
|
||||
}
|
||||
|
@ -168,6 +172,49 @@ class ExecutionContext {
|
|||
};
|
||||
}
|
||||
|
||||
getProperties({ objectId, ownProperties }) {
|
||||
let obj = this.getRemoteObject(objectId);
|
||||
if (!obj) {
|
||||
throw new Error("Cannot find object with id = " + objectId);
|
||||
}
|
||||
const result = [];
|
||||
const serializeObject = (obj, isOwn) => {
|
||||
for (const propertyName of obj.getOwnPropertyNames()) {
|
||||
const descriptor = obj.getOwnPropertyDescriptor(propertyName);
|
||||
result.push({
|
||||
name: propertyName,
|
||||
|
||||
configurable: descriptor.configurable,
|
||||
enumerable: descriptor.enumerable,
|
||||
writable: descriptor.writable,
|
||||
value: this._toRemoteObject(descriptor.value),
|
||||
get: descriptor.get ? this._toRemoteObject(descriptor.get) : undefined,
|
||||
set: descriptor.set ? this._toRemoteObject(descriptor.set) : undefined,
|
||||
|
||||
isOwn,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// When `ownProperties` is set to true, we only iterate over own properties.
|
||||
// Otherwise, we also iterate over propreties inherited from the prototype chain.
|
||||
serializeObject(obj, true);
|
||||
|
||||
if (!ownProperties) {
|
||||
while (true) {
|
||||
obj = obj.proto;
|
||||
if (!obj) {
|
||||
break;
|
||||
}
|
||||
serializeObject(obj, false);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
result,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a given `Debugger.Object` to a JSON string.
|
||||
*
|
||||
|
|
|
@ -12,11 +12,12 @@ support-files =
|
|||
[browser_page_bringToFront.js]
|
||||
[browser_page_frameNavigated.js]
|
||||
[browser_page_runtime_events.js]
|
||||
[browser_runtime_evaluate.js]
|
||||
[browser_runtime_remote_objects.js]
|
||||
[browser_runtime_callFunctionOn.js]
|
||||
[browser_runtime_evaluate.js]
|
||||
[browser_runtime_executionContext.js]
|
||||
skip-if = os == "mac" || (verify && os == 'win') # bug 1547961
|
||||
[browser_runtime_get_properties.js]
|
||||
[browser_runtime_remote_objects.js]
|
||||
[browser_session.js]
|
||||
[browser_tabs.js]
|
||||
[browser_target.js]
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test the Runtime remote object
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
||||
|
||||
add_task(async function() {
|
||||
const {client} = await setupTestForUri(TEST_URI);
|
||||
|
||||
const firstContext = await testRuntimeEnable(client);
|
||||
const contextId = firstContext.id;
|
||||
|
||||
await testGetOwnSimpleProperties(client, contextId);
|
||||
await testGetCustomProperty(client, contextId);
|
||||
await testGetPrototypeProperties(client, contextId);
|
||||
await testGetGetterSetterProperties(client, contextId);
|
||||
|
||||
await client.close();
|
||||
ok(true, "The client is closed");
|
||||
|
||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
|
||||
await RemoteAgent.close();
|
||||
});
|
||||
|
||||
async function testRuntimeEnable({ Runtime }) {
|
||||
// Enable watching for new execution context
|
||||
await Runtime.enable();
|
||||
ok(true, "Runtime domain has been enabled");
|
||||
|
||||
// Calling Runtime.enable will emit executionContextCreated for the existing contexts
|
||||
const { context } = await Runtime.executionContextCreated();
|
||||
ok(!!context.id, "The execution context has an id");
|
||||
ok(context.auxData.isDefault, "The execution context is the default one");
|
||||
ok(!!context.auxData.frameId, "The execution context has a frame id set");
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
async function testGetOwnSimpleProperties({ Runtime }, contextId) {
|
||||
const { result } = await Runtime.evaluate({ contextId, expression: "({ bool: true, fun() {}, int: 1, object: {}, string: 'foo' })" });
|
||||
is(result.subtype, null, "JS Object have no subtype");
|
||||
is(result.type, "object", "The type is correct");
|
||||
ok(!!result.objectId, "Got an object id");
|
||||
|
||||
const { result: result2 } = await Runtime.getProperties({ objectId: result.objectId, ownProperties: true });
|
||||
is(result2.length, 5, "ownProperties=true allows to iterate only over direct object properties (i.e. ignore prototype)");
|
||||
result2.sort((a, b) => a.name > b.name);
|
||||
is(result2[0].name, "bool");
|
||||
is(result2[0].configurable, true);
|
||||
is(result2[0].enumerable, true);
|
||||
is(result2[0].writable, true);
|
||||
is(result2[0].value.type, "boolean");
|
||||
is(result2[0].value.value, true);
|
||||
is(result2[0].isOwn, true);
|
||||
|
||||
is(result2[1].name, "fun");
|
||||
is(result2[1].configurable, true);
|
||||
is(result2[1].enumerable, true);
|
||||
is(result2[1].writable, true);
|
||||
is(result2[1].value.type, "function");
|
||||
ok(!!result2[1].value.objectId);
|
||||
is(result2[1].isOwn, true);
|
||||
|
||||
is(result2[2].name, "int");
|
||||
is(result2[2].configurable, true);
|
||||
is(result2[2].enumerable, true);
|
||||
is(result2[2].writable, true);
|
||||
is(result2[2].value.type, "number");
|
||||
is(result2[2].value.value, 1);
|
||||
is(result2[2].isOwn, true);
|
||||
|
||||
is(result2[3].name, "object");
|
||||
is(result2[3].configurable, true);
|
||||
is(result2[3].enumerable, true);
|
||||
is(result2[3].writable, true);
|
||||
is(result2[3].value.type, "object");
|
||||
ok(!!result2[3].value.objectId);
|
||||
is(result2[3].isOwn, true);
|
||||
|
||||
is(result2[4].name, "string");
|
||||
is(result2[4].configurable, true);
|
||||
is(result2[4].enumerable, true);
|
||||
is(result2[4].writable, true);
|
||||
is(result2[4].value.type, "string");
|
||||
is(result2[4].value.value, "foo");
|
||||
is(result2[4].isOwn, true);
|
||||
}
|
||||
|
||||
async function testGetPrototypeProperties({ Runtime }, contextId) {
|
||||
const { result } = await Runtime.evaluate({ contextId, expression: "({ foo: 42 })" });
|
||||
is(result.subtype, null, "JS Object have no subtype");
|
||||
is(result.type, "object", "The type is correct");
|
||||
ok(!!result.objectId, "Got an object id");
|
||||
|
||||
const { result: result2 } = await Runtime.getProperties({ objectId: result.objectId, ownProperties: false });
|
||||
ok(result2.length > 1, "We have more properties than just the object one");
|
||||
const foo = result2.find(p => p.name == "foo");
|
||||
ok(foo, "The object property is described");
|
||||
ok(foo.isOwn, "and is reported as 'own' property");
|
||||
|
||||
const toString = result2.find(p => p.name == "toString");
|
||||
ok(toString, "Function from Object's prototype are also described like toString");
|
||||
ok(!toString.isOwn, "but are reported as not being an 'own' property");
|
||||
}
|
||||
|
||||
async function testGetGetterSetterProperties({ Runtime }, contextId) {
|
||||
const { result } = await Runtime.evaluate({ contextId, expression: "({ get prop() { return this.x; }, set prop(v) { this.x = v; } })" });
|
||||
is(result.subtype, null, "JS Object have no subtype");
|
||||
is(result.type, "object", "The type is correct");
|
||||
ok(!!result.objectId, "Got an object id");
|
||||
|
||||
const { result: result2 } = await Runtime.getProperties({ objectId: result.objectId, ownProperties: true });
|
||||
is(result2.length, 1);
|
||||
|
||||
is(result2[0].name, "prop");
|
||||
is(result2[0].configurable, true);
|
||||
is(result2[0].enumerable, true);
|
||||
is(result2[0].writable, undefined, "writable is only set for data properties");
|
||||
|
||||
is(result2[0].get.type, "function");
|
||||
ok(!!result2[0].get.objectId);
|
||||
is(result2[0].set.type, "function");
|
||||
ok(!!result2[0].set.objectId);
|
||||
|
||||
is(result2[0].isOwn, true);
|
||||
|
||||
const { result: result3 } = await Runtime.callFunctionOn({
|
||||
executionContextId: contextId,
|
||||
functionDeclaration: "(set, get) => { set(42); return get(); }",
|
||||
arguments: [{ objectId: result2[0].set.objectId }, { objectId: result2[0].get.objectId }],
|
||||
});
|
||||
is(result3.type, "number", "The type is correct");
|
||||
is(result3.subtype, null, "The subtype is null for numbers");
|
||||
is(result3.value, 42, "The getter returned the value set by the setter");
|
||||
}
|
||||
|
||||
async function testGetCustomProperty({ Runtime }, contextId) {
|
||||
const { result } = await Runtime.evaluate({ contextId, expression: `const obj = {}; Object.defineProperty(obj, "prop", { value: 42 }); obj` });
|
||||
is(result.subtype, null, "JS Object have no subtype");
|
||||
is(result.type, "object", "The type is correct");
|
||||
ok(!!result.objectId, "Got an object id");
|
||||
|
||||
const { result: result2 } = await Runtime.getProperties({ objectId: result.objectId, ownProperties: true });
|
||||
is(result2.length, 1, "We only get the one object's property");
|
||||
is(result2[0].name, "prop");
|
||||
is(result2[0].configurable, false);
|
||||
is(result2[0].enumerable, false);
|
||||
is(result2[0].writable, false);
|
||||
is(result2[0].value.type, "number");
|
||||
is(result2[0].value.value, 42);
|
||||
is(result2[0].isOwn, true);
|
||||
}
|
Загрузка…
Ссылка в новой задаче