mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 08:12:05 +00:00
Bug 1682668 - Improve error messages when TabDelegate is not present. r=owlish
Differential Revision: https://phabricator.services.mozilla.com/D99851
This commit is contained in:
parent
d07e5d1640
commit
83036590b9
@ -8,6 +8,12 @@ const APIS = {
|
||||
AddHistogram({ id, value }) {
|
||||
browser.test.addHistogram(id, value);
|
||||
},
|
||||
Eval({ code }) {
|
||||
// eslint-disable-next-line no-eval
|
||||
return eval(`(async () => {
|
||||
${code}
|
||||
})()`);
|
||||
},
|
||||
SetScalar({ id, value }) {
|
||||
browser.test.setScalar(id, value);
|
||||
},
|
||||
@ -59,10 +65,14 @@ browser.runtime.onConnect.addListener(contentPort => {
|
||||
|
||||
function apiCall(message, impl) {
|
||||
const { id, args } = message;
|
||||
sendResponse(id, impl(args));
|
||||
try {
|
||||
sendResponse(id, impl(args));
|
||||
} catch (error) {
|
||||
sendResponse(id, Promise.reject(error));
|
||||
}
|
||||
}
|
||||
|
||||
function sendResponse(id, response, exception) {
|
||||
function sendResponse(id, response) {
|
||||
Promise.resolve(response).then(
|
||||
value => sendSyncResponse(id, value),
|
||||
reason => sendSyncResponse(id, null, reason)
|
||||
|
@ -15,6 +15,7 @@
|
||||
"run_at": "document_start"
|
||||
}
|
||||
],
|
||||
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self';",
|
||||
"background": {
|
||||
"scripts": ["background.js"]
|
||||
},
|
||||
@ -29,6 +30,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"options_ui": {
|
||||
"page": "dummy.html"
|
||||
},
|
||||
"permissions": [
|
||||
"geckoViewAddons",
|
||||
"nativeMessaging",
|
||||
|
@ -1281,6 +1281,46 @@ class GeckoSessionTestRuleTest : BaseSessionTest(noErrorCollector = true) {
|
||||
sessionRule.session.waitForPageStop()
|
||||
}
|
||||
|
||||
@Test fun evaluateExtensionJS() {
|
||||
assertThat("JS string result should be correct",
|
||||
sessionRule.evaluateExtensionJS("return 'foo';") as String, equalTo("foo"))
|
||||
|
||||
assertThat("JS number result should be correct",
|
||||
sessionRule.evaluateExtensionJS("return 1+1;") as Double, equalTo(2.0))
|
||||
|
||||
assertThat("JS boolean result should be correct",
|
||||
sessionRule.evaluateExtensionJS("return !0;") as Boolean, equalTo(true))
|
||||
|
||||
val expected = JSONObject("{bar:42,baz:true,foo:'bar'}")
|
||||
val actual = sessionRule.evaluateExtensionJS("return {foo:'bar',bar:42,baz:true};") as JSONObject
|
||||
for (key in expected.keys()) {
|
||||
assertThat("JS object result should be correct",
|
||||
actual.get(key), equalTo(expected.get(key)))
|
||||
}
|
||||
|
||||
assertThat("JS array result should be correct",
|
||||
sessionRule.evaluateExtensionJS("return [1,2,3];") as JSONArray,
|
||||
equalTo(JSONArray("[1,2,3]")))
|
||||
|
||||
assertThat("Can access extension APIS",
|
||||
sessionRule.evaluateExtensionJS("return !!browser.runtime;") as Boolean,
|
||||
equalTo(true))
|
||||
|
||||
assertThat("Can access extension APIS",
|
||||
sessionRule.evaluateExtensionJS("""
|
||||
return true;
|
||||
// Comments at the end are allowed""".trimIndent()) as Boolean,
|
||||
equalTo(true))
|
||||
|
||||
try {
|
||||
sessionRule.evaluateExtensionJS("test({ what")
|
||||
assertThat("Should fail", true, equalTo(false))
|
||||
} catch (e: RejectedPromiseException) {
|
||||
assertThat("Syntax errors are reported",
|
||||
e.message, containsString("SyntaxError"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun evaluateJS() {
|
||||
sessionRule.session.loadTestPath(HELLO_HTML_PATH);
|
||||
sessionRule.session.waitForPageStop();
|
||||
|
@ -21,6 +21,7 @@ import org.mozilla.geckoview.WebExtensionController.EnableSource
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.Setting
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.RejectedPromiseException
|
||||
import org.mozilla.geckoview.test.util.Callbacks
|
||||
import org.mozilla.geckoview.test.util.RuntimeCreator
|
||||
import org.mozilla.geckoview.test.util.UiThreadUtils
|
||||
@ -139,6 +140,42 @@ class WebExtensionTest : BaseSessionTest() {
|
||||
equalTo(blocklistDisabled))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun noDelegateErrorMessage() {
|
||||
try {
|
||||
sessionRule.evaluateExtensionJS("""
|
||||
const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
await browser.tabs.update(tab.id, { url: "www.google.com" });
|
||||
""")
|
||||
assertThat("tabs.update should not succeed", true, equalTo(false))
|
||||
} catch (ex: RejectedPromiseException) {
|
||||
assertThat("Error message matches", ex.message,
|
||||
equalTo("Error: tabs.update is not supported"))
|
||||
}
|
||||
|
||||
try {
|
||||
sessionRule.evaluateExtensionJS("""
|
||||
const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
await browser.tabs.remove(tab.id);
|
||||
""")
|
||||
assertThat("tabs.remove should not succeed", true, equalTo(false))
|
||||
} catch (ex: RejectedPromiseException) {
|
||||
assertThat("Error message matches", ex.message,
|
||||
equalTo("Error: tabs.remove is not supported"))
|
||||
}
|
||||
|
||||
try {
|
||||
sessionRule.evaluateExtensionJS("""
|
||||
await browser.runtime.openOptionsPage();
|
||||
""")
|
||||
assertThat("runtime.openOptionsPage should not succeed",
|
||||
true, equalTo(false))
|
||||
} catch (ex: RejectedPromiseException) {
|
||||
assertThat("Error message matches", ex.message,
|
||||
equalTo("Error: runtime.openOptionsPage is not supported"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun enableDisable() {
|
||||
mainSession.loadUri("example.com")
|
||||
|
@ -1888,6 +1888,12 @@ public class GeckoSessionTestRule implements TestRule {
|
||||
return new ExtensionPromise(UUID.randomUUID(), session, js);
|
||||
}
|
||||
|
||||
public Object evaluateExtensionJS(final @NonNull String js) {
|
||||
return webExtensionApiCall("Eval", args -> {
|
||||
args.put("code", js);
|
||||
});
|
||||
}
|
||||
|
||||
public Object evaluateJS(final @NonNull GeckoSession session, final @NonNull String js) {
|
||||
// Let's make sure we have the port already
|
||||
UiThreadUtils.waitForCondition(() -> mPorts.containsKey(session), mTimeoutMillis);
|
||||
|
@ -1075,7 +1075,7 @@ public class WebExtensionController {
|
||||
if (delegate != null) {
|
||||
delegate.onOpenOptionsPage(extension);
|
||||
} else {
|
||||
// TODO: Save as pending?
|
||||
message.callback.sendError("runtime.openOptionsPage is not supported");
|
||||
}
|
||||
|
||||
message.callback.sendSuccess(null);
|
||||
@ -1102,10 +1102,9 @@ public class WebExtensionController {
|
||||
return;
|
||||
}
|
||||
|
||||
result.accept(session -> {
|
||||
message.callback.resolveTo(result.map(session -> {
|
||||
if (session == null) {
|
||||
message.callback.sendSuccess(null);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (session.isOpen()) {
|
||||
@ -1114,8 +1113,8 @@ public class WebExtensionController {
|
||||
|
||||
session.open(mListener.runtime);
|
||||
|
||||
message.callback.sendSuccess(session.getId());
|
||||
});
|
||||
return session.getId();
|
||||
}));
|
||||
}
|
||||
|
||||
/* package */ void updateTab(final Message message, final WebExtension extension) {
|
||||
@ -1124,19 +1123,21 @@ public class WebExtensionController {
|
||||
final EventCallback callback = message.callback;
|
||||
|
||||
if (delegate == null) {
|
||||
callback.sendError(null);
|
||||
callback.sendError("tabs.update is not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
delegate.onUpdateTab(extension, message.session,
|
||||
new WebExtension.UpdateTabDetails(message.bundle.getBundle("updateProperties")))
|
||||
.accept(value -> {
|
||||
final WebExtension.UpdateTabDetails details =
|
||||
new WebExtension.UpdateTabDetails(message.bundle.getBundle("updateProperties"));
|
||||
callback.resolveTo(delegate
|
||||
.onUpdateTab(extension, message.session, details)
|
||||
.map(value -> {
|
||||
if (value == AllowOrDeny.ALLOW) {
|
||||
callback.sendSuccess(null);
|
||||
return null;
|
||||
} else {
|
||||
callback.sendError(null);
|
||||
throw new Exception("tabs.update is not supported");
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
/* package */ void closeTab(final Message message,
|
||||
@ -1151,13 +1152,13 @@ public class WebExtensionController {
|
||||
result = GeckoResult.fromValue(AllowOrDeny.DENY);
|
||||
}
|
||||
|
||||
result.accept(value -> {
|
||||
message.callback.resolveTo(result.map(value -> {
|
||||
if (value == AllowOrDeny.ALLOW) {
|
||||
message.callback.sendSuccess(null);
|
||||
return null;
|
||||
} else {
|
||||
message.callback.sendError(null);
|
||||
throw new Exception("tabs.remove is not supported");
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1325,7 +1326,7 @@ public class WebExtensionController {
|
||||
try {
|
||||
content = message.bundle.toJSONObject().get("data");
|
||||
} catch (JSONException ex) {
|
||||
callback.sendError(ex);
|
||||
callback.sendError(ex.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,11 @@ const { GeckoViewModule } = ChromeUtils.import(
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
const { ExtensionUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/ExtensionUtils.jsm"
|
||||
);
|
||||
|
||||
const { ExtensionError } = ExtensionUtils;
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
EventDispatcher: "resource://gre/modules/Messaging.jsm",
|
||||
@ -75,10 +80,17 @@ const GeckoViewTabBridge = {
|
||||
async openOptionsPage(extensionId) {
|
||||
debug`openOptionsPage for extensionId ${extensionId}`;
|
||||
|
||||
return EventDispatcher.instance.sendRequestForResult({
|
||||
type: "GeckoView:WebExtension:OpenOptionsPage",
|
||||
extensionId,
|
||||
});
|
||||
try {
|
||||
await EventDispatcher.instance.sendRequestForResult({
|
||||
type: "GeckoView:WebExtension:OpenOptionsPage",
|
||||
extensionId,
|
||||
});
|
||||
} catch (errorMessage) {
|
||||
// The error message coming from GeckoView is about :OpenOptionsPage not
|
||||
// being registered so we need to have one that's extension friendly
|
||||
// here.
|
||||
throw new ExtensionError("runtime.openOptionsPage is not supported");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -98,14 +110,21 @@ const GeckoViewTabBridge = {
|
||||
async createNewTab({ extensionId, createProperties } = {}) {
|
||||
debug`createNewTab`;
|
||||
|
||||
const sessionId = await EventDispatcher.instance.sendRequestForResult({
|
||||
type: "GeckoView:WebExtension:NewTab",
|
||||
extensionId,
|
||||
createProperties,
|
||||
});
|
||||
let sessionId;
|
||||
try {
|
||||
sessionId = await EventDispatcher.instance.sendRequestForResult({
|
||||
type: "GeckoView:WebExtension:NewTab",
|
||||
extensionId,
|
||||
createProperties,
|
||||
});
|
||||
} catch (errorMessage) {
|
||||
// The error message coming from GeckoView is about :NewTab not being
|
||||
// registered so we need to have one that's extension friendly here.
|
||||
throw new ExtensionError("tabs.create is not supported");
|
||||
}
|
||||
|
||||
if (!sessionId) {
|
||||
throw new Error("Cannot create new tab");
|
||||
throw new ExtensionError("Cannot create new tab");
|
||||
}
|
||||
|
||||
const window = await new Promise(resolve => {
|
||||
@ -143,18 +162,26 @@ const GeckoViewTabBridge = {
|
||||
* Throws an error if the GeckoView app doesn't allow extension to close tab.
|
||||
*/
|
||||
async closeTab({ window, extensionId } = {}) {
|
||||
await window.WindowEventDispatcher.sendRequestForResult({
|
||||
type: "GeckoView:WebExtension:CloseTab",
|
||||
extensionId,
|
||||
});
|
||||
try {
|
||||
await window.WindowEventDispatcher.sendRequestForResult({
|
||||
type: "GeckoView:WebExtension:CloseTab",
|
||||
extensionId,
|
||||
});
|
||||
} catch (errorMessage) {
|
||||
throw new ExtensionError(errorMessage);
|
||||
}
|
||||
},
|
||||
|
||||
async updateTab({ window, extensionId, updateProperties } = {}) {
|
||||
await window.WindowEventDispatcher.sendRequestForResult({
|
||||
type: "GeckoView:WebExtension:UpdateTab",
|
||||
extensionId,
|
||||
updateProperties,
|
||||
});
|
||||
try {
|
||||
await window.WindowEventDispatcher.sendRequestForResult({
|
||||
type: "GeckoView:WebExtension:UpdateTab",
|
||||
extensionId,
|
||||
updateProperties,
|
||||
});
|
||||
} catch (errorMessage) {
|
||||
throw new ExtensionError(errorMessage);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -95,14 +95,6 @@ class ExtensionActionHelper {
|
||||
return window.WindowEventDispatcher;
|
||||
}
|
||||
|
||||
sendRequestForResult(aTabId, aData) {
|
||||
return this.eventDispatcherFor(aTabId).sendRequestForResult({
|
||||
...aData,
|
||||
aTabId,
|
||||
extensionId: this.extension.id,
|
||||
});
|
||||
}
|
||||
|
||||
sendRequest(aTabId, aData) {
|
||||
return this.eventDispatcherFor(aTabId).sendRequest({
|
||||
...aData,
|
||||
|
Loading…
Reference in New Issue
Block a user