mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1769763: Part 2 - Add debug names to StructuredCloneHolder objects for about:memory. r=mccr8,devtools-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D162251
This commit is contained in:
parent
020bf49219
commit
f9100d622c
@ -74,7 +74,11 @@ ChildDebuggerTransport.prototype = {
|
|||||||
*/
|
*/
|
||||||
_canBeSerialized(object) {
|
_canBeSerialized(object) {
|
||||||
try {
|
try {
|
||||||
const holder = new StructuredCloneHolder(object);
|
const holder = new StructuredCloneHolder(
|
||||||
|
"ChildDebuggerTransport._canBeSerialized",
|
||||||
|
null,
|
||||||
|
object
|
||||||
|
);
|
||||||
holder.deserialize(this);
|
holder.deserialize(this);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -12,7 +12,9 @@ add_task(async function test_BrowsingContext_structured_clone() {
|
|||||||
|
|
||||||
let { browsingContext } = frame;
|
let { browsingContext } = frame;
|
||||||
|
|
||||||
let sch = new StructuredCloneHolder({ browsingContext });
|
let sch = new StructuredCloneHolder("debug name", "<anonymized> debug name", {
|
||||||
|
browsingContext,
|
||||||
|
});
|
||||||
|
|
||||||
let deserialize = () => sch.deserialize({}, true);
|
let deserialize = () => sch.deserialize({}, true);
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "mozilla/dom/BlobImpl.h"
|
#include "mozilla/dom/BlobImpl.h"
|
||||||
#include "mozilla/dom/StructuredCloneHolderBinding.h"
|
#include "mozilla/dom/StructuredCloneHolderBinding.h"
|
||||||
#include "mozilla/dom/StructuredCloneTags.h"
|
#include "mozilla/dom/StructuredCloneTags.h"
|
||||||
|
#include "nsPrintfCString.h"
|
||||||
#include "xpcpublic.h"
|
#include "xpcpublic.h"
|
||||||
|
|
||||||
namespace mozilla::dom {
|
namespace mozilla::dom {
|
||||||
@ -37,12 +38,16 @@ StructuredCloneBlob::~StructuredCloneBlob() {
|
|||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
already_AddRefed<StructuredCloneBlob> StructuredCloneBlob::Constructor(
|
already_AddRefed<StructuredCloneBlob> StructuredCloneBlob::Constructor(
|
||||||
GlobalObject& aGlobal, JS::Handle<JS::Value> aValue,
|
GlobalObject& aGlobal, const nsACString& aName,
|
||||||
|
const nsACString& aAnonymizedName, JS::Handle<JS::Value> aValue,
|
||||||
JS::Handle<JSObject*> aTargetGlobal, ErrorResult& aRv) {
|
JS::Handle<JSObject*> aTargetGlobal, ErrorResult& aRv) {
|
||||||
JSContext* cx = aGlobal.Context();
|
JSContext* cx = aGlobal.Context();
|
||||||
|
|
||||||
RefPtr<StructuredCloneBlob> holder = StructuredCloneBlob::Create();
|
RefPtr<StructuredCloneBlob> holder = StructuredCloneBlob::Create();
|
||||||
|
|
||||||
|
holder->mName = aName;
|
||||||
|
holder->mAnonymizedName = aAnonymizedName.IsVoid() ? aName : aAnonymizedName;
|
||||||
|
|
||||||
Maybe<JSAutoRealm> ar;
|
Maybe<JSAutoRealm> ar;
|
||||||
JS::Rooted<JS::Value> value(cx, aValue);
|
JS::Rooted<JS::Value> value(cx, aValue);
|
||||||
|
|
||||||
@ -129,6 +134,14 @@ JSObject* StructuredCloneBlob::ReadStructuredClone(
|
|||||||
{
|
{
|
||||||
RefPtr<StructuredCloneBlob> holder = StructuredCloneBlob::Create();
|
RefPtr<StructuredCloneBlob> holder = StructuredCloneBlob::Create();
|
||||||
|
|
||||||
|
if (!StructuredCloneHolder::ReadCString(aReader, holder->mName)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StructuredCloneHolder::ReadCString(aReader, holder->mAnonymizedName)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (!holder->mHolder->ReadStructuredCloneInternal(aCx, aReader, aHolder) ||
|
if (!holder->mHolder->ReadStructuredCloneInternal(aCx, aReader, aHolder) ||
|
||||||
!holder->WrapObject(aCx, nullptr, &obj)) {
|
!holder->WrapObject(aCx, nullptr, &obj)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -186,6 +199,13 @@ bool StructuredCloneBlob::WriteStructuredClone(JSContext* aCx,
|
|||||||
if (mHolder.isNothing()) {
|
if (mHolder.isNothing()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_STRUCTURED_CLONE_HOLDER, 0) ||
|
||||||
|
!StructuredCloneHolder::WriteCString(aWriter, mName) ||
|
||||||
|
!StructuredCloneHolder::WriteCString(aWriter, mAnonymizedName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return mHolder->WriteStructuredClone(aCx, aWriter, aHolder);
|
return mHolder->WriteStructuredClone(aCx, aWriter, aHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,8 +213,7 @@ bool StructuredCloneBlob::Holder::WriteStructuredClone(
|
|||||||
JSContext* aCx, JSStructuredCloneWriter* aWriter,
|
JSContext* aCx, JSStructuredCloneWriter* aWriter,
|
||||||
StructuredCloneHolder* aHolder) {
|
StructuredCloneHolder* aHolder) {
|
||||||
auto& data = mBuffer->data();
|
auto& data = mBuffer->data();
|
||||||
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_STRUCTURED_CLONE_HOLDER, 0) ||
|
if (!JS_WriteUint32Pair(aWriter, data.Size(), JS_STRUCTURED_CLONE_VERSION) ||
|
||||||
!JS_WriteUint32Pair(aWriter, data.Size(), JS_STRUCTURED_CLONE_VERSION) ||
|
|
||||||
!JS_WriteUint32Pair(aWriter, aHolder->BlobImpls().Length(),
|
!JS_WriteUint32Pair(aWriter, aHolder->BlobImpls().Length(),
|
||||||
BlobImpls().Length())) {
|
BlobImpls().Length())) {
|
||||||
return false;
|
return false;
|
||||||
@ -221,9 +240,12 @@ StructuredCloneBlob::CollectReports(nsIHandleReportCallback* aHandleReport,
|
|||||||
size += mHolder->SizeOfExcludingThis(MallocSizeOf);
|
size += mHolder->SizeOfExcludingThis(MallocSizeOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_COLLECT_REPORT("explicit/dom/structured-clone-holder", KIND_HEAP,
|
aHandleReport->Callback(
|
||||||
UNITS_BYTES, size,
|
""_ns,
|
||||||
"Memory used by StructuredCloneHolder DOM objects.");
|
nsPrintfCString("explicit/dom/structured-clone-holder/%s",
|
||||||
|
aAnonymize ? mAnonymizedName.get() : mName.get()),
|
||||||
|
KIND_HEAP, UNITS_BYTES, size,
|
||||||
|
"Memory used by StructuredCloneHolder DOM objects."_ns, aData);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,8 @@ class StructuredCloneBlob final : public nsIMemoryReporter {
|
|||||||
StructuredCloneHolder* aHolder);
|
StructuredCloneHolder* aHolder);
|
||||||
|
|
||||||
static already_AddRefed<StructuredCloneBlob> Constructor(
|
static already_AddRefed<StructuredCloneBlob> Constructor(
|
||||||
GlobalObject& aGlobal, JS::Handle<JS::Value> aValue,
|
GlobalObject& aGlobal, const nsACString& aName,
|
||||||
|
const nsACString& aAnonymizedName, JS::Handle<JS::Value> aValue,
|
||||||
JS::Handle<JSObject*> aTargetGlobal, ErrorResult& aRv);
|
JS::Handle<JSObject*> aTargetGlobal, ErrorResult& aRv);
|
||||||
|
|
||||||
void Deserialize(JSContext* aCx, JS::Handle<JSObject*> aTargetScope,
|
void Deserialize(JSContext* aCx, JS::Handle<JSObject*> aTargetScope,
|
||||||
@ -71,6 +72,8 @@ class StructuredCloneBlob final : public nsIMemoryReporter {
|
|||||||
StructuredCloneHolder* aHolder);
|
StructuredCloneHolder* aHolder);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nsCString mName;
|
||||||
|
nsCString mAnonymizedName;
|
||||||
Maybe<Holder> mHolder;
|
Maybe<Holder> mHolder;
|
||||||
|
|
||||||
static already_AddRefed<StructuredCloneBlob> Create() {
|
static already_AddRefed<StructuredCloneBlob> Create() {
|
||||||
|
@ -12,7 +12,7 @@ add_task(async function test_structuredCloneHolder() {
|
|||||||
|
|
||||||
const obj = { foo: [{ bar: "baz" }] };
|
const obj = { foo: [{ bar: "baz" }] };
|
||||||
|
|
||||||
let holder = new StructuredCloneHolder(obj);
|
let holder = new StructuredCloneHolder("", "", obj);
|
||||||
|
|
||||||
// Test same-compartment deserialization
|
// Test same-compartment deserialization
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ add_task(async function test_structuredCloneHolder() {
|
|||||||
// Test non-object-value round-trip.
|
// Test non-object-value round-trip.
|
||||||
|
|
||||||
equal(
|
equal(
|
||||||
new StructuredCloneHolder("foo").deserialize(global),
|
new StructuredCloneHolder("", "", "foo").deserialize(global),
|
||||||
"foo",
|
"foo",
|
||||||
"Round-tripping non-object values works as expected"
|
"Round-tripping non-object values works as expected"
|
||||||
);
|
);
|
||||||
@ -128,7 +128,7 @@ add_task(async function test_structuredCloneHolder_xray() {
|
|||||||
let holder;
|
let holder;
|
||||||
Cu.exportFunction(
|
Cu.exportFunction(
|
||||||
function serialize(val) {
|
function serialize(val) {
|
||||||
holder = new StructuredCloneHolder(val, sandbox1);
|
holder = new StructuredCloneHolder("", "", val, sandbox1);
|
||||||
},
|
},
|
||||||
sandbox1,
|
sandbox1,
|
||||||
{ defineAs: "serialize" }
|
{ defineAs: "serialize" }
|
||||||
|
@ -16,9 +16,17 @@ interface StructuredCloneHolder {
|
|||||||
*
|
*
|
||||||
* The serialization happens in the compartment of the given global or, if no
|
* The serialization happens in the compartment of the given global or, if no
|
||||||
* global is provided, the compartment of the data value.
|
* global is provided, the compartment of the data value.
|
||||||
|
*
|
||||||
|
* The name argument is added to the path of the object in
|
||||||
|
* memory reports, to make it easier to determine the source of leaks. In
|
||||||
|
* anonymized memory reports, the anonymized name is used instead. If
|
||||||
|
* anonymizedName is null, name is used in anonymized reports as well.
|
||||||
|
* Anonymized names should not contain any potentially private information,
|
||||||
|
* such as web URLs or user-provided data.
|
||||||
*/
|
*/
|
||||||
[Throws]
|
[Throws]
|
||||||
constructor(any data, optional object? global = null);
|
constructor(UTF8String name, UTF8String? anonymizedName,
|
||||||
|
any data, optional object? global = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserializes the structured clone data in the scope of the given global,
|
* Deserializes the structured clone data in the scope of the given global,
|
||||||
|
@ -1457,7 +1457,11 @@ export class RTCPeerConnection {
|
|||||||
// Exceptions thrown by c++ code do not propagate. In most cases, that's
|
// Exceptions thrown by c++ code do not propagate. In most cases, that's
|
||||||
// fine because we're using Promises, which can be copied. But this is
|
// fine because we're using Promises, which can be copied. But this is
|
||||||
// not promise-based, so we have to do this sketchy stuff.
|
// not promise-based, so we have to do this sketchy stuff.
|
||||||
const holder = new StructuredCloneHolder(new ClonedErrorHolder(e));
|
const holder = new StructuredCloneHolder(
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
new ClonedErrorHolder(e)
|
||||||
|
);
|
||||||
throw holder.deserialize(this._win);
|
throw holder.deserialize(this._win);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ add_task(async function test_unhandled_dom_exception() {
|
|||||||
let messages = await getSandboxMessages(
|
let messages = await getSandboxMessages(
|
||||||
sandbox,
|
sandbox,
|
||||||
`new Promise(() => {
|
`new Promise(() => {
|
||||||
new StructuredCloneHolder(() => {});
|
new StructuredCloneHolder("", "", () => {});
|
||||||
});`
|
});`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -171,7 +171,11 @@ class EmbedderPort {
|
|||||||
|
|
||||||
switch (aEvent) {
|
switch (aEvent) {
|
||||||
case "GeckoView:WebExtension:PortMessageFromApp": {
|
case "GeckoView:WebExtension:PortMessageFromApp": {
|
||||||
const holder = new StructuredCloneHolder(aData.message);
|
const holder = new StructuredCloneHolder(
|
||||||
|
"GeckoView:WebExtension:PortMessageFromApp",
|
||||||
|
null,
|
||||||
|
aData.message
|
||||||
|
);
|
||||||
this.messenger.sendPortMessage(this.id, holder);
|
this.messenger.sendPortMessage(this.id, holder);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -613,9 +613,11 @@ export class SpecialPowersChild extends JSWindowActorChild {
|
|||||||
let name = aMessage.json.name;
|
let name = aMessage.json.name;
|
||||||
let message = aMessage.json.message;
|
let message = aMessage.json.message;
|
||||||
if (this.contentWindow) {
|
if (this.contentWindow) {
|
||||||
message = new StructuredCloneHolder(message).deserialize(
|
message = new StructuredCloneHolder(
|
||||||
this.contentWindow
|
`SpecialPowers/receiveMessage/${name}`,
|
||||||
);
|
null,
|
||||||
|
message
|
||||||
|
).deserialize(this.contentWindow);
|
||||||
}
|
}
|
||||||
// Ignore message from other chrome script
|
// Ignore message from other chrome script
|
||||||
if (messageId != id) {
|
if (messageId != id) {
|
||||||
|
@ -243,11 +243,11 @@ class MessageEvent extends SimpleEventAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function holdMessage(data, native = null) {
|
function holdMessage(name, anonymizedName, data, native = null) {
|
||||||
if (native && AppConstants.platform !== "android") {
|
if (native && AppConstants.platform !== "android") {
|
||||||
data = lazy.NativeApp.encodeMessage(native.context, data);
|
data = lazy.NativeApp.encodeMessage(native.context, data);
|
||||||
}
|
}
|
||||||
return new StructuredCloneHolder(data);
|
return new StructuredCloneHolder(name, anonymizedName, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements the runtime.Port extension API object.
|
// Implements the runtime.Port extension API object.
|
||||||
@ -263,7 +263,10 @@ class Port {
|
|||||||
this.context = context;
|
this.context = context;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
this.holdMessage = native ? data => holdMessage(data, this) : holdMessage;
|
this.holdMessage = native
|
||||||
|
? (name, anonymizedName, data) =>
|
||||||
|
holdMessage(name, anonymizedName, data, this)
|
||||||
|
: holdMessage;
|
||||||
this.conduit = context.openConduit(this, {
|
this.conduit = context.openConduit(this, {
|
||||||
portId,
|
portId,
|
||||||
native,
|
native,
|
||||||
@ -307,7 +310,13 @@ class Port {
|
|||||||
|
|
||||||
sendPortMessage(json) {
|
sendPortMessage(json) {
|
||||||
if (this.conduit.actor) {
|
if (this.conduit.actor) {
|
||||||
return this.conduit.sendPortMessage({ holder: this.holdMessage(json) });
|
return this.conduit.sendPortMessage({
|
||||||
|
holder: this.holdMessage(
|
||||||
|
`Port/${this.context.extension.id}/sendPortMessage/${this.name}`,
|
||||||
|
`Port/${this.context.extension.id}/sendPortMessage/<anonymized>`,
|
||||||
|
json
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
throw new this.context.Error("Attempt to postMessage on disconnected port");
|
throw new this.context.Error("Attempt to postMessage on disconnected port");
|
||||||
}
|
}
|
||||||
@ -342,14 +351,23 @@ class Messenger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sendNativeMessage(nativeApp, json) {
|
sendNativeMessage(nativeApp, json) {
|
||||||
let holder = holdMessage(json, this);
|
let holder = holdMessage(
|
||||||
|
`Messenger/${this.context.extension.id}/sendNativeMessage/${nativeApp}`,
|
||||||
|
null,
|
||||||
|
json,
|
||||||
|
this
|
||||||
|
);
|
||||||
return this.conduit.queryNativeMessage({ nativeApp, holder });
|
return this.conduit.queryNativeMessage({ nativeApp, holder });
|
||||||
}
|
}
|
||||||
|
|
||||||
sendRuntimeMessage({ extensionId, message, callback, ...args }) {
|
sendRuntimeMessage({ extensionId, message, callback, ...args }) {
|
||||||
let response = this.conduit.queryRuntimeMessage({
|
let response = this.conduit.queryRuntimeMessage({
|
||||||
extensionId: extensionId || this.context.extension.id,
|
extensionId: extensionId || this.context.extension.id,
|
||||||
holder: holdMessage(message),
|
holder: holdMessage(
|
||||||
|
`Messenger/${this.context.extension.id}/sendRuntimeMessage`,
|
||||||
|
null,
|
||||||
|
message
|
||||||
|
),
|
||||||
...args,
|
...args,
|
||||||
});
|
});
|
||||||
// If |response| is a rejected promise, the value will be sanitized by
|
// If |response| is a rejected promise, the value will be sanitized by
|
||||||
@ -836,7 +854,12 @@ class ChildAPIManager {
|
|||||||
: fire()
|
: fire()
|
||||||
).then(result => {
|
).then(result => {
|
||||||
if (result !== undefined) {
|
if (result !== undefined) {
|
||||||
return new StructuredCloneHolder(result, this.context.cloneScope);
|
return new StructuredCloneHolder(
|
||||||
|
`ChildAPIManager/${this.context.extension.id}/${data.path}`,
|
||||||
|
null,
|
||||||
|
result,
|
||||||
|
this.context.cloneScope
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
@ -1507,7 +1507,7 @@ class SchemaAPIManager extends EventEmitter {
|
|||||||
|
|
||||||
this._modulesJSONLoaded = true;
|
this._modulesJSONLoaded = true;
|
||||||
|
|
||||||
return new StructuredCloneHolder({
|
return new StructuredCloneHolder("SchemaAPIManager/initModuleJSON", null, {
|
||||||
modules: this.modules,
|
modules: this.modules,
|
||||||
modulePaths: this.modulePaths,
|
modulePaths: this.modulePaths,
|
||||||
manifestKeys: this.manifestKeys,
|
manifestKeys: this.manifestKeys,
|
||||||
|
@ -1168,7 +1168,11 @@ ParentAPIManager = {
|
|||||||
result => {
|
result => {
|
||||||
result = result instanceof SpreadArgs ? [...result] : [result];
|
result = result instanceof SpreadArgs ? [...result] : [result];
|
||||||
|
|
||||||
let holder = new StructuredCloneHolder(result);
|
let holder = new StructuredCloneHolder(
|
||||||
|
`ExtensionParent/${context.extension.id}/recvAPICall/${data.path}`,
|
||||||
|
null,
|
||||||
|
result
|
||||||
|
);
|
||||||
|
|
||||||
reply({ result: holder });
|
reply({ result: holder });
|
||||||
},
|
},
|
||||||
@ -1213,7 +1217,11 @@ ParentAPIManager = {
|
|||||||
path: data.path,
|
path: data.path,
|
||||||
urgentSend,
|
urgentSend,
|
||||||
get args() {
|
get args() {
|
||||||
return new StructuredCloneHolder(listenerArgs);
|
return new StructuredCloneHolder(
|
||||||
|
`ExtensionParent/${context.extension.id}/recvAddListener/${data.path}`,
|
||||||
|
null,
|
||||||
|
listenerArgs
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
context.trackRunListenerPromise(runListenerPromise);
|
context.trackRunListenerPromise(runListenerPromise);
|
||||||
|
@ -80,13 +80,19 @@ class SerializeableMap extends Map {
|
|||||||
* sending a storage value across a message manager, before cloning it
|
* sending a storage value across a message manager, before cloning it
|
||||||
* into an extension scope.
|
* into an extension scope.
|
||||||
*
|
*
|
||||||
|
* @param {string} name
|
||||||
|
* A debugging name for the value, which will appear in the
|
||||||
|
* StructuredCloneHolder's about:memory path.
|
||||||
|
* @param {string?} anonymizedName
|
||||||
|
* An anonymized version of `name`, to be used in anonymized memory
|
||||||
|
* reports. If `null`, then `name` will be used instead.
|
||||||
* @param {StructuredCloneHolder|*} value
|
* @param {StructuredCloneHolder|*} value
|
||||||
* A value to serialize.
|
* A value to serialize.
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
function serialize(value) {
|
function serialize(name, anonymizedName, value) {
|
||||||
if (value && typeof value === "object" && !isStructuredCloneHolder(value)) {
|
if (value && typeof value === "object" && !isStructuredCloneHolder(value)) {
|
||||||
return new StructuredCloneHolder(value);
|
return new StructuredCloneHolder(name, anonymizedName, value);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -219,8 +225,16 @@ var ExtensionStorage = {
|
|||||||
for (let prop in items) {
|
for (let prop in items) {
|
||||||
let item = items[prop];
|
let item = items[prop];
|
||||||
changes[prop] = {
|
changes[prop] = {
|
||||||
oldValue: serialize(jsonFile.data.get(prop)),
|
oldValue: serialize(
|
||||||
newValue: serialize(item),
|
`set/${extensionId}/old/${prop}`,
|
||||||
|
`set/${extensionId}/old/<anonymized>`,
|
||||||
|
jsonFile.data.get(prop)
|
||||||
|
),
|
||||||
|
newValue: serialize(
|
||||||
|
`set/${extensionId}/new/${prop}`,
|
||||||
|
`set/${extensionId}/new/<anonymized>`,
|
||||||
|
item
|
||||||
|
),
|
||||||
};
|
};
|
||||||
jsonFile.data.set(prop, item);
|
jsonFile.data.set(prop, item);
|
||||||
}
|
}
|
||||||
@ -249,7 +263,13 @@ var ExtensionStorage = {
|
|||||||
|
|
||||||
for (let prop of [].concat(items)) {
|
for (let prop of [].concat(items)) {
|
||||||
if (jsonFile.data.has(prop)) {
|
if (jsonFile.data.has(prop)) {
|
||||||
changes[prop] = { oldValue: serialize(jsonFile.data.get(prop)) };
|
changes[prop] = {
|
||||||
|
oldValue: serialize(
|
||||||
|
`remove/${extensionId}/${prop}`,
|
||||||
|
`remove/${extensionId}/<anonymized>`,
|
||||||
|
jsonFile.data.get(prop)
|
||||||
|
),
|
||||||
|
};
|
||||||
jsonFile.data.delete(prop);
|
jsonFile.data.delete(prop);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
@ -282,7 +302,13 @@ var ExtensionStorage = {
|
|||||||
|
|
||||||
for (let [prop, oldValue] of jsonFile.data.entries()) {
|
for (let [prop, oldValue] of jsonFile.data.entries()) {
|
||||||
if (shouldNotifyListeners) {
|
if (shouldNotifyListeners) {
|
||||||
changes[prop] = { oldValue: serialize(oldValue) };
|
changes[prop] = {
|
||||||
|
oldValue: serialize(
|
||||||
|
`clear/${extensionId}/${prop}`,
|
||||||
|
`clear/${extensionId}/<anonymized>`,
|
||||||
|
oldValue
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonFile.data.delete(prop);
|
jsonFile.data.delete(prop);
|
||||||
@ -319,17 +345,21 @@ var ExtensionStorage = {
|
|||||||
*/
|
*/
|
||||||
async get(extensionId, keys) {
|
async get(extensionId, keys) {
|
||||||
let jsonFile = await this.getFile(extensionId);
|
let jsonFile = await this.getFile(extensionId);
|
||||||
return this._filterProperties(jsonFile.data, keys);
|
return this._filterProperties(extensionId, jsonFile.data, keys);
|
||||||
},
|
},
|
||||||
|
|
||||||
async _filterProperties(data, keys) {
|
async _filterProperties(extensionId, data, keys) {
|
||||||
let result = {};
|
let result = {};
|
||||||
if (keys === null) {
|
if (keys === null) {
|
||||||
Object.assign(result, data.toJSON());
|
Object.assign(result, data.toJSON());
|
||||||
} else if (typeof keys == "object" && !Array.isArray(keys)) {
|
} else if (typeof keys == "object" && !Array.isArray(keys)) {
|
||||||
for (let prop in keys) {
|
for (let prop in keys) {
|
||||||
if (data.has(prop)) {
|
if (data.has(prop)) {
|
||||||
result[prop] = serialize(data.get(prop));
|
result[prop] = serialize(
|
||||||
|
`filterProperties/${extensionId}/${prop}`,
|
||||||
|
`filterProperties/${extensionId}/<anonymized>`,
|
||||||
|
data.get(prop)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
result[prop] = keys[prop];
|
result[prop] = keys[prop];
|
||||||
}
|
}
|
||||||
@ -337,7 +367,11 @@ var ExtensionStorage = {
|
|||||||
} else {
|
} else {
|
||||||
for (let prop of [].concat(keys)) {
|
for (let prop of [].concat(keys)) {
|
||||||
if (data.has(prop)) {
|
if (data.has(prop)) {
|
||||||
result[prop] = serialize(data.get(prop));
|
result[prop] = serialize(
|
||||||
|
`filterProperties/${extensionId}/${prop}`,
|
||||||
|
`filterProperties/${extensionId}/<anonymized>`,
|
||||||
|
data.get(prop)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -407,7 +441,12 @@ var ExtensionStorage = {
|
|||||||
let result = {};
|
let result = {};
|
||||||
for (let [key, value] of Object.entries(items)) {
|
for (let [key, value] of Object.entries(items)) {
|
||||||
try {
|
try {
|
||||||
result[key] = new StructuredCloneHolder(value, context.cloneScope);
|
result[key] = new StructuredCloneHolder(
|
||||||
|
`serializeForContext/${context.extension.id}`,
|
||||||
|
null,
|
||||||
|
value,
|
||||||
|
context.cloneScope
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ExtensionError(String(e));
|
throw new ExtensionError(String(e));
|
||||||
}
|
}
|
||||||
|
@ -236,6 +236,10 @@ class ExtensionStorageLocalIDB extends IndexedDB {
|
|||||||
);
|
);
|
||||||
const transactionCompleted = transaction.promiseComplete();
|
const transactionCompleted = transaction.promiseComplete();
|
||||||
|
|
||||||
|
if (!serialize) {
|
||||||
|
serialize = (name, anonymizedName, value) => value;
|
||||||
|
}
|
||||||
|
|
||||||
for (let key of Object.keys(items)) {
|
for (let key of Object.keys(items)) {
|
||||||
try {
|
try {
|
||||||
let oldValue = await objectStore.get(key);
|
let oldValue = await objectStore.get(key);
|
||||||
@ -243,8 +247,9 @@ class ExtensionStorageLocalIDB extends IndexedDB {
|
|||||||
await objectStore.put(items[key], key);
|
await objectStore.put(items[key], key);
|
||||||
|
|
||||||
changes[key] = {
|
changes[key] = {
|
||||||
oldValue: oldValue && serialize ? serialize(oldValue) : oldValue,
|
oldValue:
|
||||||
newValue: serialize ? serialize(items[key]) : items[key],
|
oldValue && serialize(`old/${key}`, `old/<anonymized>`, oldValue),
|
||||||
|
newValue: serialize(`new/${key}`, `new/<anonymized>`, items[key]),
|
||||||
};
|
};
|
||||||
changed = true;
|
changed = true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -701,6 +706,8 @@ ExtensionStorageIDB = {
|
|||||||
// Serialize the nsIPrincipal object into a StructuredCloneHolder related to the privileged
|
// Serialize the nsIPrincipal object into a StructuredCloneHolder related to the privileged
|
||||||
// js global, ready to be sent to the child processes.
|
// js global, ready to be sent to the child processes.
|
||||||
const serializedPrincipal = new StructuredCloneHolder(
|
const serializedPrincipal = new StructuredCloneHolder(
|
||||||
|
"ExtensionStorageIDB/selectBackend/serializedPrincipal",
|
||||||
|
null,
|
||||||
storagePrincipal,
|
storagePrincipal,
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
|
@ -138,7 +138,14 @@ var NativeApp = class extends EventEmitter {
|
|||||||
onConnect(portId, port) {
|
onConnect(portId, port) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
this.on("message", (_, message) => {
|
this.on("message", (_, message) => {
|
||||||
port.sendPortMessage(portId, new StructuredCloneHolder(message));
|
port.sendPortMessage(
|
||||||
|
portId,
|
||||||
|
new StructuredCloneHolder(
|
||||||
|
`NativeMessaging/onConnect/${this.name}`,
|
||||||
|
null,
|
||||||
|
message
|
||||||
|
)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
this.once("disconnect", (_, error) => {
|
this.once("disconnect", (_, error) => {
|
||||||
port.sendPortDisconnect(portId, error && new ClonedErrorHolder(error));
|
port.sendPortDisconnect(portId, error && new ClonedErrorHolder(error));
|
||||||
|
@ -136,7 +136,7 @@ function blobbify(json) {
|
|||||||
// blobbifying.
|
// blobbifying.
|
||||||
json = stripDescriptions(json);
|
json = stripDescriptions(json);
|
||||||
|
|
||||||
return new StructuredCloneHolder(json);
|
return new StructuredCloneHolder("Schemas/blobbify", null, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function readJSONAndBlobbify(url) {
|
async function readJSONAndBlobbify(url) {
|
||||||
|
@ -109,13 +109,21 @@ this.storage = class extends ExtensionAPI {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
set(items) {
|
set(items) {
|
||||||
|
function serialize(name, anonymizedName, value) {
|
||||||
|
return ExtensionStorage.serialize(
|
||||||
|
`set/${context.extension.id}/${name}`,
|
||||||
|
`set/${context.extension.id}/${anonymizedName}`,
|
||||||
|
value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return measureOp(
|
return measureOp(
|
||||||
ExtensionTelemetry.storageLocalSetIDB,
|
ExtensionTelemetry.storageLocalSetIDB,
|
||||||
context.extension,
|
context.extension,
|
||||||
async () => {
|
async () => {
|
||||||
const db = await getDB();
|
const db = await getDB();
|
||||||
const changes = await db.set(items, {
|
const changes = await db.set(items, {
|
||||||
serialize: ExtensionStorage.serialize,
|
serialize,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (changes) {
|
if (changes) {
|
||||||
|
@ -259,7 +259,7 @@ this.test = class extends ExtensionAPI {
|
|||||||
// throw if needed.
|
// throw if needed.
|
||||||
v = ChromeUtils.waiveXrays(v);
|
v = ChromeUtils.waiveXrays(v);
|
||||||
}
|
}
|
||||||
new StructuredCloneHolder(v, globalThis);
|
new StructuredCloneHolder("test.assertEq", null, v, globalThis);
|
||||||
}
|
}
|
||||||
// When WebIDL bindings are used, the objects are already cloned
|
// When WebIDL bindings are used, the objects are already cloned
|
||||||
// structurally, so we don't need to check again.
|
// structurally, so we don't need to check again.
|
||||||
|
@ -337,7 +337,7 @@ this.storage = class extends ExtensionAPIPersistent {
|
|||||||
message: "Managed storage manifest not found",
|
message: "Managed storage manifest not found",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return ExtensionStorage._filterProperties(data, keys);
|
return ExtensionStorage._filterProperties(extension.id, data, keys);
|
||||||
},
|
},
|
||||||
// managed storage is currently initialized once.
|
// managed storage is currently initialized once.
|
||||||
onChanged: ignoreEvent(context, "storage.managed.onChanged"),
|
onChanged: ignoreEvent(context, "storage.managed.onChanged"),
|
||||||
|
@ -460,7 +460,7 @@ while True:
|
|||||||
});
|
});
|
||||||
|
|
||||||
let buffer = NativeApp.encodeMessage(mockContext, MSG);
|
let buffer = NativeApp.encodeMessage(mockContext, MSG);
|
||||||
app.send(new StructuredCloneHolder(buffer));
|
app.send(new StructuredCloneHolder("", null, buffer));
|
||||||
await recvPromise;
|
await recvPromise;
|
||||||
|
|
||||||
app._cleanup();
|
app._cleanup();
|
||||||
|
Loading…
Reference in New Issue
Block a user