Bug 866231: Have the remote protocol profiler actor copy notification values and break cycles before sending them to the client. r=dcamp

This commit is contained in:
Jim Blandy 2013-04-30 15:00:29 -07:00
parent 4fd4d6ce30
commit a31f97d54e

View File

@ -99,35 +99,40 @@ ProfilerActor.prototype = {
return { unregistered: unregistered }
},
observe: makeInfallible(function(aSubject, aTopic, aData) {
function unWrapper(obj) {
if (obj && typeof obj == "object" && ("wrappedJSObject" in obj)) {
obj = obj.wrappedJSObject;
if (("wrappedJSObject" in obj) && (obj.wrappedJSObject == obj)) {
/* If the object defines wrappedJSObject as itself, which is the
* typical idiom for wrapped JS objects, JSON.stringify won't be
* able to work because the object is cyclic.
* But removing the wrappedJSObject property will break aSubject
* for possible other observers of the same topic, so we need
* to restore wrappedJSObject afterwards */
delete obj.wrappedJSObject;
return { unwrapped: obj,
fixup: function() {
this.unwrapped.wrappedJSObject = this.unwrapped;
}
}
}
/*
* this.conn.send can only transmit acyclic values. However, it is
* idiomatic for wrapped JS objects like aSubject (and possibly aData?)
* to have a 'wrappedJSObject' property pointing to themselves.
*
* this.conn.send also assumes that it can retain the object it is
* passed to be handled on later event ticks; and that it's okay to
* freeze it. Since we don't really know what aSubject and aData are,
* we need to pass this.conn.send a copy of them, not the originals.
*
* We break the cycle and make the copy by JSON.stringifying those
* values with a replacer that omits properties known to introduce
* cycles, and then JSON.parsing the result. This spends processor
* time, but it's simple.
*/
function cycleBreaker(key, value) {
if (key === 'wrappedJSObject') {
return undefined;
}
return { unwrapped: obj, fixup: function() { } }
return value;
}
var subject = unWrapper(aSubject);
var data = unWrapper(aData);
/*
* If these values are objects with a non-null 'wrappedJSObject'
* property, use its value. Otherwise, use the value unchanged.
*/
aSubject = (aSubject && aSubject.wrappedJSObject) || aSubject;
aData = (aData && aData.wrappedJSObject) || aData;
this.conn.send({ from: this.actorID,
type: "eventNotification",
event: aTopic,
subject: subject.unwrapped,
data: data.unwrapped });
data.fixup();
subject.fixup();
subject: JSON.parse(JSON.stringify(aSubject, cycleBreaker)),
data: JSON.parse(JSON.stringify(aData, cycleBreaker)) });
}, "ProfilerActor.prototype.observe"),
};