Bug 1619766 - [1.2] Add WebExtension openOptionsPage support. r=agi,geckoview-reviewers,robwu

Differential Revision: https://phabricator.services.mozilla.com/D77901
This commit is contained in:
Eugen Sawin 2020-06-05 17:21:24 +00:00
Родитель d02396bd67
Коммит 60b2d3d9e9
6 изменённых файлов: 114 добавлений и 12 удалений

Просмотреть файл

@ -1,5 +1,17 @@
/* 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 { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
mobileWindowTracker: "resource://gre/modules/GeckoViewWebExtension.jsm",
});
// This function is pretty tightly tied to Extension.jsm.
// Its job is to fill in the |tab| property of the sender.
const getSender = (extension, target, sender) => {
@ -37,9 +49,35 @@ extensions.on("page-shutdown", (type, context) => {
});
/* eslint-enable mozilla/balanced-listeners */
global.openOptionsPage = extension => {
// TODO: Bug 1619766
return Promise.reject({ message: "Not implemented yet." });
global.openOptionsPage = async extension => {
const { options_ui } = extension.manifest;
const extensionId = extension.id;
if (options_ui.open_in_tab) {
// Delegate new tab creation and open the options page in the new tab.
const tab = await GeckoViewTabBridge.createNewTab({
extensionId,
createProperties: {
url: options_ui.page,
active: true,
},
});
const { browser } = tab;
const flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
browser.loadURI(options_ui.page, {
flags,
triggeringPrincipal: extension.principal,
});
const newWindow = browser.ownerGlobal;
mobileWindowTracker.setTabActive(newWindow, true);
return;
}
// Delegate option page handling to the app.
return GeckoViewTabBridge.openOptionsPage(extensionId);
};
extensions.registerModules({

Просмотреть файл

@ -1635,6 +1635,7 @@ package org.mozilla.geckoview {
public static interface WebExtension.TabDelegate {
method @UiThread @Nullable default public GeckoResult<GeckoSession> onNewTab(@NonNull WebExtension, @NonNull WebExtension.CreateTabDetails);
method @UiThread default public void onOpenOptionsPage(@NonNull WebExtension);
}
public static class WebExtension.UpdateTabDetails {

Просмотреть файл

@ -630,6 +630,18 @@ public class WebExtension {
@NonNull CreateTabDetails createDetails) {
return null;
}
/**
* Called when runtime.openOptionsPage is invoked with
* options_ui.open_in_tab = false.
* In this case, GeckoView delegates options page handling to the app.
* With options_ui.open_in_tab = true, {@link #onNewTab} is called
* instead.
*
* @param source An instance of {@link WebExtension}.
*/
@UiThread
default void onOpenOptionsPage(@NonNull WebExtension source) {}
}
/**
@ -842,7 +854,8 @@ public class WebExtension {
this,
"GeckoView:WebExtension:NewTab",
"GeckoView:WebExtension:UpdateTab",
"GeckoView:WebExtension:CloseTab"
"GeckoView:WebExtension:CloseTab",
"GeckoView:WebExtension:OpenOptionsPage"
);
mTabDelegateRegistered = true;
}
@ -874,7 +887,8 @@ public class WebExtension {
this,
"GeckoView:WebExtension:NewTab",
"GeckoView:WebExtension:UpdateTab",
"GeckoView:WebExtension:CloseTab"
"GeckoView:WebExtension:CloseTab",
"GeckoView:WebExtension:OpenOptionsPage"
);
mTabDelegateRegistered = true;
}
@ -901,7 +915,8 @@ public class WebExtension {
this,
"GeckoView:WebExtension:NewTab",
"GeckoView:WebExtension:UpdateTab",
"GeckoView:WebExtension:CloseTab"
"GeckoView:WebExtension:CloseTab",
"GeckoView:WebExtension:OpenOptionsPage"
);
mTabDelegateRegistered = true;
}

Просмотреть файл

@ -832,6 +832,8 @@ public class WebExtensionController {
final EventCallback callback, final GeckoSession session) {
final Message message = new Message(event, bundle, callback, session);
Log.d(LOGTAG, "handleMessage " + event);
if ("GeckoView:WebExtension:Disconnect".equals(event)) {
disconnect(bundle.getLong("portId", -1), callback);
return;
@ -881,6 +883,9 @@ public class WebExtensionController {
} else if ("GeckoView:PageAction:OpenPopup".equals(event)) {
openPopup(message, extension, WebExtension.Action.TYPE_PAGE_ACTION);
return;
} else if ("GeckoView:WebExtension:OpenOptionsPage".equals(event)) {
openOptionsPage(message, extension);
return;
}
final String nativeApp = bundle.getString("nativeApp");
@ -986,6 +991,22 @@ public class WebExtensionController {
});
}
/* package */ void openOptionsPage(
final Message message,
final WebExtension extension) {
final GeckoBundle bundle = message.bundle;
final WebExtension.TabDelegate delegate =
mListener.getTabDelegate(extension);
if (delegate != null) {
delegate.onOpenOptionsPage(extension);
} else {
// TODO: Save as pending?
}
message.callback.sendSuccess(null);
}
/* package */ void newTab(final Message message, final WebExtension extension) {
newTab(message, null, extension);
}

Просмотреть файл

@ -13,6 +13,13 @@ exclude: true
⚠️ breaking change and deprecation notices
## v79
- Added `runtime.openOptionsPage` support. For `options_ui.open_in_new_tab` ==
`false`, [`TabDelegate.onOpenOptionsPage`][79.1] is called.
([bug 1618058]({{bugzilla}}1619766))
[79.1]: {{javadoc_uri}}/WebExtension.TabDelegate.html#onOpenOptionsPage-org.mozilla.geckoview.WebExtension-
## v78
- Added [`WebExtensionController.installBuiltIn`][78.1] that allows installing an
extension that is bundled with the APK. This method is meant as a replacement
@ -712,4 +719,4 @@ exclude: true
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
[65.25]: {{javadoc_uri}}/GeckoResult.html
[api-version]: f334c3c72b512156da2d96b2d2e7bbf4392c0b0e
[api-version]: c75d02b7653f49b0d301d95a50f6d559dca1c48c

Просмотреть файл

@ -45,7 +45,7 @@ const GeckoViewTabBridge = {
/**
* Converts windowId to tabId as in GeckoView every browser window has exactly one tab.
*
* @param {windowId} number outerWindowId
* @param {number} windowId outerWindowId
*
* @returns {number} tabId
*/
@ -56,7 +56,7 @@ const GeckoViewTabBridge = {
/**
* Converts tabId to windowId.
*
* @param {windowId} number
* @param {number} tabId
*
* @returns {number}
* outerWindowId of browser window to which the tab belongs.
@ -65,14 +65,32 @@ const GeckoViewTabBridge = {
return tabId - TAB_ID_BASE;
},
/**
* Delegates openOptionsPage handling to the app.
*
* @param {number} extensionId
* The ID of the extension requesting the options menu.
*
* @returns {Promise<Void>}
* A promise resolved after successful handling.
*/
async openOptionsPage(extensionId) {
debug`openOptionsPage for extensionId ${extensionId}`;
return EventDispatcher.instance.sendRequestForResult({
type: "GeckoView:WebExtension:OpenOptionsPage",
extensionId,
});
},
/**
* Request the GeckoView App to create a new tab (GeckoSession).
*
* @param {object} options
* @param {string} options.url The url to load in the newly created tab
* @param {nsIPrincipal} options.triggeringPrincipal
* @param {boolean} [options.disallowInheritPrincipal]
* @param {string} options.extensionId
* The ID of the extension that requested a new tab.
* @param {object} options.createProperties
* The properties for the new tab, see tabs.create reference for details.
*
* @returns {Promise<Tab>}
* A promise resolved to the newly created tab.
@ -80,6 +98,8 @@ const GeckoViewTabBridge = {
* Throws an error if the GeckoView app doesn't support tabs.create or fails to handle the request.
*/
async createNewTab({ extensionId, createProperties } = {}) {
debug`createNewTab`;
const sessionId = await EventDispatcher.instance.sendRequestForResult({
type: "GeckoView:WebExtension:NewTab",
extensionId,