managing-extensions.rst (9283B)
1 GeckoView Extension Managing API 2 ================================ 3 4 Agi Sferro <agi@sferro.dev> 5 6 November 19th, 2019 7 8 Introduction 9 ------------ 10 11 This document describes the API for installing, uninstalling and updating 12 Extensions with GeckoView. 13 14 Installing an extension provides the extension the ability to run at startup 15 time, especially useful for e.g. extensions that intercept network requests, 16 like an ad-blocker or a proxy extension. It also provides additional security 17 from third-party extensions like signature checking and prompting the user for 18 permissions. 19 20 For this version of the API we will assume that the extension store is backed 21 by ``addons.mozilla.org``, and so are the signatures. Running a third-party 22 extension store is something we might consider in the future but explicitly not 23 in scope for this document. 24 25 API 26 --- 27 28 The embedder will be able to install, uninstall, enable, disable and update 29 extensions using the similarly-named APIs. 30 31 Installing 32 ^^^^^^^^^^ 33 34 Gecko will download the extension pointed by the URI provided in install, parse 35 the manifest and signature and provide an ``onInstallPrompt`` callback with the 36 list of permissions requested by the extension and some information about the 37 extension. 38 39 The embedder will be able to install bundled first-party extensions using 40 ``installBuiltIn``. This method will only accept URIs that start with 41 ``resource://`` and will give additional privileges like being able to use app 42 messaging and not needing a signature. 43 44 Each permission will have a machine readable name that the embedder will use to 45 produce user-facing internationalized strings. E.g. “bookmarks” gives access to 46 bookmarks, “sessions” gives access to recently closed sessions. The full list 47 of permissions that are currently shown to the UI in Firefox Desktop is 48 available at: ``toolkit/global/extensionPermissions.ftl`` 49 50 ``WebExtension.MetaData`` properties expected to be set to absolute moz-extension urls 51 (e.g. ``baseUrl`` and ``optionsPageUrl``) are not available yet right after installing 52 a new extension. Once the extension has been fully started, the delegate method 53 ``WebExtensionController.AddonManagerDelegate.onReady`` will be providing to the 54 embedder app a new instance of the `MetaData` object where ``baseUrl`` is expected 55 to be set to a ``"moz-extension://..."`` url (and ``optionsPageUrl`` as well if an 56 options page was declared in the extension manifest.json file). 57 58 Updating 59 ^^^^^^^^ 60 61 To update an extension, the embedder will be able to call update which will 62 check if any update is available (using the update_url provided by the 63 extension, or addons.mozilla.org if no update_url has been provided). The 64 embedder will receive a GeckoResult that will provide the updated extension 65 object. This result can also be used to know when the update process is 66 complete, e.g. the embedder could use it to display a persistent notification 67 to the user to avoid having the app be killed while updates are in process. 68 69 If the updated extension needs additional permissions, ``GeckoView`` will call 70 ``onUpdatePrompt``. 71 72 Until this callback is resolved (i.e. the embedder’s returned ``GeckoResult`` 73 is completed), the old addon will be running, only when the prompt is resolved 74 and the update is applied the new version of the addon starts running and the 75 ``GeckoResult`` returned from update is resolved. 76 77 This callback will provide both the current ``WebExtension`` object and the 78 updated WebExtension object so that the embedder can show appropriate 79 information to the user, e.g. the app might decide to remember whether the user 80 denied the request for a certain version and only prompt the user once the 81 version string changes. 82 83 As a side effect of updating, Gecko will check its internal blocklist and might 84 disable extensions that are incompatible with the current version of Gecko or 85 deemed unsafe. The resulting ``WebExtension`` object will reflect that by 86 having isEnabled set to false. The embedder will be able to inspect the reason 87 why the extension was disabled using ``metaData.blockedReason``. 88 89 Gecko will not update any extension or blocklist state without the embedder’s 90 input. 91 92 Enabling and Disabling 93 ^^^^^^^^^^^^^^^^^^^^^^ 94 95 Embedders will be able to enable and disabling extension using the homonymous 96 APIs. Calling enable on an extension might not actually enable it if the 97 extension has been added to the Gecko blocklist. Embedders can check the value 98 of ``metaData.blockedReason`` to display to the user whether the extension can 99 actually be enabled or not. The returned WebExtension object will reflect the 100 updated enablement state in isEnabled. 101 102 Listing 103 ^^^^^^^ 104 105 The embedder is expected to keep a collection of all available extensions using 106 the result of install and update. To retrieve the extensions that are already 107 installed the embedder will be able to use ``listInstalled`` which will 108 asynchronously retrieve the full list of extensions. We recommend calling 109 ``listInstalled`` every time the user is presented with the extension manager 110 UI to ensure all information is up to date. 111 112 Outline 113 ^^^^^^^ 114 115 .. code:: java 116 117 public class WebExtensionController { 118 // Start the process of installing an extension, 119 // the embedder will either get the installed extension 120 // or an error 121 GeckoResult<WebExtension> install(String uri); 122 123 // Install a built-in WebExtension with privileged 124 // permissions, uri must be resource:// 125 // Privileged WebExtensions have access to experiments 126 // (i.e. they can run chrome code), don’t need signatures 127 // and have access to native messaging to the app 128 GeckoResult<WebExtension> installBuiltIn(String uri) 129 130 GeckoResult<Void> uninstall(WebExtension extension); 131 132 GeckoResult<WebExtension> enable(WebExtension extension); 133 134 GeckoResult<WebExtension> disable(WebExtension extension); 135 136 GeckoResult<List<WebExtension>> listInstalled(); 137 138 // Checks for updates. This method returns a GeckoResult that is 139 // resolved either with the updated WebExtension object or null 140 // if the extension does not have pending updates. 141 GeckoResult<WebExtension> update(WebExtension extension); 142 143 public interface PromptDelegate { 144 GeckoResult<AllowOrDeny> onInstallPrompt(WebExtension extension); 145 146 GeckoResult<AllowOrDeny> onUpdatePrompt( 147 WebExtension currentlyInstalled, 148 WebExtension updatedExtension, 149 List<String> newPermissions); 150 151 // Called when the extension calls browser.permission.request 152 GeckoResult<AllowOrDeny> onOptionalPrompt( 153 WebExtension extension, 154 List<String> optionalPermissions); 155 } 156 157 void setPromptDelegate(PromptDelegate promptDelegate); 158 } 159 160 As part of this document, we will add a ``MetaData`` field to WebExtension 161 which will contain all the information known about the extension. Note: we will 162 rename ``ActionIcon`` to Icon to represent its generic use as the 163 ``WebExtension`` icon class. 164 165 .. code:: java 166 167 public class WebExtension { 168 // Renamed from ActionIcon 169 static class Icon {} 170 171 final MetaData metadata; 172 final boolean isBuiltIn; 173 174 final boolean isEnabled; 175 176 public static class SignedStateFlags { 177 final static int UNKNOWN; 178 final static int PRELIMINARY; 179 final static int SIGNED; 180 final static int SYSTEM; 181 final static int PRIVILEGED; 182 } 183 184 // See nsIBlocklistService.idl 185 public static class BlockedReason { 186 final static int NOT_BLOCKED; 187 final static int SOFTBLOCKED; 188 final static int BLOCKED; 189 final static int OUTDATED; 190 final static int VULNERABLE_UPDATE_AVAILABLE; 191 final static int VULNERABLE_NO_UPDATE; 192 } 193 194 public class MetaData { 195 final Icon icon; 196 final String[] permissions; 197 final String[] origins; 198 final String name; 199 final String description; 200 final String version; 201 final String creatorName; 202 final String creatorUrl; 203 final String homepageUrl; 204 final String baseUrl; 205 final String optionsPageUrl; 206 final boolean openOptionsPageInTab; 207 final boolean isRecommended; 208 final @BlockedReason int blockedReason; 209 final @SignedState int signedState; 210 // more if needed 211 } 212 } 213 214 Implementation Details 215 ^^^^^^^^^^^^^^^^^^^^^^ 216 217 We will use ``AddonManager`` as a backend for ``WebExtensionController`` and 218 delegate the prompt to the app using ``PromptDelegate``. We will also merge 219 ``WebExtensionController`` and ``WebExtensionEventDispatcher`` for ease of 220 implementation. 221 222 Existing APIs 223 ^^^^^^^^^^^^^ 224 225 Some APIs today return a ``WebExtension`` object that might have not been 226 fetched yet by ``listInstalled``. In these cases, GeckoView will return a stub 227 ``WebExtension`` object in which the metadata field will be null to avoid 228 waiting for a addon list call. To ensure that the metadata field is populated, 229 the embedder will need to call ``listInstalled`` at least once during the app 230 startup. 231 232 Deprecation Path 233 ^^^^^^^^^^^^^^^^ 234 235 The existing ``registerWebExtension`` and ``unregisterWebExtension`` APIs will 236 be deprecated by ``installBuiltIn`` and ``uninstall``. We will remove the above 237 APIs 6 releases after the implementation of ``installBuiltIn`` lands and mark 238 it as deprecated in the API.