fune/toolkit/actors/TestWindowChild.jsm
Kris Maglione e01b754b29 Bug 1672263: Use ThrowInvalidStateError with useful messages in JSWindowActorChild. r=nika
Currently, when JS callers try to access certain JSWindowActors before an
actor is initialized, or after it's destroyed, we throw a generic
`NS_ERROR_INVALID_STATE_ERROR` exception without any useful information about
the failure.

This patch changes that to use `ThrowInvalidStateError`, with a message
including the property name, the actor name, and details about whether the
error occurred because the actor has not been initialized or because it has
already been destroyed.

Differential Revision: https://phabricator.services.mozilla.com/D96628
2020-11-10 22:04:49 +00:00

104 lines
2.5 KiB
JavaScript

/* vim: set ts=2 sw=2 sts=2 et tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var EXPORTED_SYMBOLS = ["TestWindowChild"];
var docShellThunks = new Map();
class TestWindowChild extends JSWindowActorChild {
constructor() {
super();
this.sawActorCreated = false;
try {
void this.contentWindow;
} catch (e) {
this.uninitializedGetterError = e;
}
}
actorCreated() {
this.sawActorCreated = true;
}
receiveMessage(aMessage) {
switch (aMessage.name) {
case "toChild":
aMessage.data.toChild = true;
this.sendAsyncMessage("toParent", aMessage.data);
break;
case "asyncAdd":
let { a, b } = aMessage.data;
return new Promise(resolve => {
resolve({ result: a + b });
});
case "error":
return Promise.reject(new SyntaxError(aMessage.data.message));
case "exception":
return Promise.reject(
Components.Exception(aMessage.data.message, aMessage.data.result)
);
case "done":
this.done(aMessage.data);
break;
case "noncloneReply":
// Return a value which is non-cloneable, like a WindowProxy.
return this.contentWindow;
case "storeActor":
docShellThunks.set(this.docShell, this);
break;
case "checkActor": {
let actor = docShellThunks.get(this.docShell);
docShellThunks.delete(this.docShell);
let contentWindow;
let error;
try {
contentWindow = actor.contentWindow;
} catch (e) {
error = e;
}
if (error) {
return {
status: "error",
errorType: error.name,
};
}
return {
status: "success",
valueIsNull: contentWindow === null,
};
}
}
return undefined;
}
handleEvent(aEvent) {
this.sendAsyncMessage("event", { type: aEvent.type });
}
observe(subject, topic, data) {
switch (topic) {
case "audio-playback":
this.done({ subject, topic, data });
break;
default:
this.lastObserved = { subject, topic, data };
break;
}
}
show() {
return "TestWindowChild";
}
didDestroy() {
Services.obs.notifyObservers(this, "test-js-window-actor-diddestroy", true);
}
}