|
const { Class } = require('sdk/core/heritage'); |
|
const { defer } = require('sdk/core/promise'); |
|
|
|
const { Cu } = require("chrome"); |
|
const { ObjectClient } = Cu.import('resource://gre/modules/devtools/dbg-client.jsm', {}); |
|
|
|
const SPECIAL_TYPES = Set(["null", "undefined", "Infinity", "-Infinity", "NaN", "-0"]); |
|
const valueFromSpecialType = type => { |
|
if (SPECIAL_TYPES.has(type)) { |
|
return eval(type) |
|
} else { |
|
throw new Error("Invalid grip type: " + type); |
|
} |
|
}; |
|
|
|
/* |
|
* Wraps a property descriptor so that its `value` field is either a primitive |
|
* or an Obj. |
|
*/ |
|
const Descriptor = (debuggerClient, descriptor) => { |
|
let value; |
|
if (descriptor.value.actor) { |
|
// Its value is an object grip. Wrap it as an Obj. |
|
// |
|
// TODO: consider other clients for Array, Functions, etc. |
|
let grip = descriptor.value; |
|
value = Obj(debuggerClient, grip); |
|
} else if (descriptor.value.type) { |
|
// It has a type field which names its value. |
|
// Evalate this type field to the value. |
|
value = valueFromSpecialType(descriptor.value.type); |
|
} else { |
|
// Its value is a primitive. Return the property as is. |
|
value = descriptor.value; |
|
} |
|
|
|
return { |
|
configurable: descriptor.configurable, |
|
enumerable: descriptor.enumerable, |
|
writable: descriptor.writable, |
|
value: value |
|
}; |
|
}; |
|
|
|
/* |
|
* Wraps an ObjectClient so that its interface is restricted and promise-based. |
|
*/ |
|
const Obj = Class({ |
|
initialize: function initialize(debuggerClient, grip) { |
|
this.grip = grip; |
|
this.debuggerClient = debuggerClient; |
|
this.client = new ObjectClient(debuggerClient, grip); |
|
}, |
|
|
|
/* |
|
* Resolves to a property descriptor with the following fields: |
|
* - configurable (boolean) |
|
* - enumerable (boolean) |
|
* - writable (boolean) |
|
* - value (primitive | Obj) |
|
* or rejects if the Obj has no value for this key. |
|
*/ |
|
descriptor: function descriptor(key) { |
|
let deferred = defer(); |
|
|
|
this.client.getProperty(key, ({descriptor}) => { |
|
if (descriptor) { |
|
// The descriptor will have a value field which will be |
|
// a grip (not necessarily for an object). |
|
// Dress it up correctly. |
|
deferred.resolve(Descriptor(this.debuggerClient, descriptor)); |
|
} else { |
|
deferred.reject(); |
|
} |
|
}); |
|
|
|
return deferred.promise; |
|
}, |
|
|
|
/* |
|
* Resolve to an Obj for this Obj's prototype, |
|
* or rejects if this Obj has no prototype. |
|
*/ |
|
prototype: function prototype() { |
|
let deferred = defer(); |
|
|
|
this.client.getPrototype(({prototype}) => { |
|
if (prototype) { |
|
// The prototpe will be an object grip. |
|
// Dress it up as an Obj. |
|
deferred.resolve(Obj(this.debuggerClient, prototype)); |
|
} else { |
|
deferred.reject(); |
|
} |
|
}); |
|
|
|
return deferred.promise; |
|
}, |
|
|
|
/* |
|
* Returns an array of the names of keys for this Obj's own properties. |
|
*/ |
|
keys: function keys() { |
|
let deferred = defer(); |
|
|
|
this.client.getOwnPropertyNames(({ownPropertyNames}) => { |
|
deferred.resolve(ownPropertyNames); |
|
}); |
|
|
|
return deferred.promise; |
|
} |
|
}); |
|
exports.Object = Obj; |
|
|
|
/* |
|
* Creates a client.Object from a grip and its debuggerClient. |
|
*/ |
|
const fromGrip = (debuggerClient, grip) => Obj(debuggerClient, grip); |
|
exports.fromGrip = fromGrip; |
|
|
|
/* |
|
* Creates a client.Object from an existing ObjectClient. |
|
*/ |
|
const fromClient = objectClient => Obj(objectClient._client, objectClient._grip); |
|
exports.fromClient = fromClien |
I like this. What about other clients like e.g. LongStringClient that supports additional API (i.e. substring). Or clients that doesn't exist now e.g. FunctionClient that could also support additional API (e.g. getArguments). What do you think is better, having a hierarchy of clients or rather just one that is generic?
Honza