|
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 |
Honza, I agree that it makes sense to have a few other sorts of clients. Adding a function client and a LongString client shouldn't be too difficult. I'll look into this.