[tbb-commits] [Git][tpo/applications/tor-browser][tor-browser-128.2.0esr-14.0-1] fixup! [android] Modify add-on support
ma1 (@ma1)
git at gitlab.torproject.org
Wed Sep 11 20:02:22 UTC 2024
ma1 pushed to branch tor-browser-128.2.0esr-14.0-1 at The Tor Project / Applications / Tor Browser
Commits:
4eb9b64f by hackademix at 2024-09-11T21:59:25+02:00
fixup! [android] Modify add-on support
Bug 43097: Use default (non-builtin) extension installation method which works with xpi files.
- - - - -
6 changed files:
- mobile/android/android-components/components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/webextension/GeckoWebExtension.kt
- mobile/android/android-components/components/concept/engine/src/main/java/mozilla/components/concept/engine/webextension/WebExtension.kt
- mobile/android/android-components/components/support/webextensions/src/main/java/mozilla/components/support/webextensions/WebExtensionSupport.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/TorBrowserFeatures.kt
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionController.java
Changes:
=====================================
mobile/android/android-components/components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/webextension/GeckoWebExtension.kt
=====================================
@@ -393,6 +393,7 @@ class GeckoWebExtension(
override fun isAllowedInPrivateBrowsing(): Boolean {
return isBuiltIn() || nativeExtension.metaData.allowedInPrivateBrowsing
+ || isBundled()
}
override suspend fun loadIcon(size: Int): Bitmap? {
=====================================
mobile/android/android-components/components/concept/engine/src/main/java/mozilla/components/concept/engine/webextension/WebExtension.kt
=====================================
@@ -164,6 +164,14 @@ abstract class WebExtension(
*/
open fun isBuiltIn(): Boolean = Uri.parse(url).scheme == "resource"
+ /**
+ * Checks whether or not this extension is bundled with this browser,
+ * but otherwise behaves as an unprivileged (non built-in) extension,
+ * except it cannot be disabled or uninstalled from the UI (e.g.
+ * NoScript in the Tor Browser).
+ */
+ open fun isBundled(): Boolean = id == "{73a6fe31-595d-460b-a920-fcc0f8843232}"
+
/**
* Checks whether or not this extension is enabled.
*/
=====================================
mobile/android/android-components/components/support/webextensions/src/main/java/mozilla/components/support/webextensions/WebExtensionSupport.kt
=====================================
@@ -234,6 +234,7 @@ object WebExtensionSupport {
// when the add-on has already been installed, we don't need to show anything
// either.
val shouldDispatchAction = !installedExtensions.containsKey(extension.id) && !extension.isBuiltIn()
+ && !extension.isBundled()
registerInstalledExtension(store, extension)
if (shouldDispatchAction) {
store.dispatch(
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt
=====================================
@@ -44,6 +44,8 @@ class InstalledAddonDetailsFragment : Fragment() {
private var _binding: FragmentInstalledAddOnDetailsBinding? = null
+ private var isBundledAddon = false;
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -51,6 +53,7 @@ class InstalledAddonDetailsFragment : Fragment() {
): View {
if (!::addon.isInitialized) {
addon = AddonDetailsFragmentArgs.fromBundle(requireNotNull(arguments)).addon
+ isBundledAddon = installedExtensions[addon.id]?.isBundled() ?: false
}
setBindingAndBindUI(
@@ -148,6 +151,7 @@ class InstalledAddonDetailsFragment : Fragment() {
// When the ad-on is blocklisted or not correctly signed, we do not want to enable the toggle switch
// because users shouldn't be able to re-enable an add-on in this state.
if (
+ isBundledAddon ||
addon.isDisabledAsBlocklisted() ||
addon.isDisabledAsNotCorrectlySigned() ||
addon.isDisabledAsIncompatible()
@@ -303,6 +307,7 @@ class InstalledAddonDetailsFragment : Fragment() {
}
private fun bindReportButton() {
+ binding.reportAddOn.isVisible = !isBundledAddon
binding.reportAddOn.setOnClickListener {
val shouldCreatePrivateSession = (activity as HomeActivity).browsingModeManager.mode.isPrivate
@@ -367,8 +372,7 @@ class InstalledAddonDetailsFragment : Fragment() {
}
private fun bindRemoveButton() {
- val isBuiltin = installedExtensions[addon.id]?.isBuiltIn() ?: false
- binding.removeAddOn.isVisible = !isBuiltin
+ binding.removeAddOn.isVisible = !isBundledAddon
binding.removeAddOn.setOnClickListener {
setAllInteractiveViewsClickable(binding, false)
requireContext().components.addonManager.uninstallAddon(
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/TorBrowserFeatures.kt
=====================================
@@ -6,12 +6,14 @@
package org.mozilla.fenix.components
+import android.os.StrictMode
import android.content.Context
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import java.io.IOException
import mozilla.components.concept.engine.webextension.WebExtension
import mozilla.components.concept.engine.webextension.WebExtensionRuntime
import mozilla.components.support.webextensions.WebExtensionSupport
@@ -25,14 +27,39 @@ object TorBrowserFeatures {
private const val NOSCRIPT_ID = "{73a6fe31-595d-460b-a920-fcc0f8843232}"
private fun installNoScript(
+ context: Context,
runtime: WebExtensionRuntime,
onSuccess: ((WebExtension) -> Unit),
onError: ((Throwable) -> Unit)
) {
+ /**
+ * Copy the xpi from assets to cacheDir, we do not care if the file is later deleted.
+ */
+ val xpiName = "$NOSCRIPT_ID.xpi"
+ val addonPath = context.cacheDir.resolve(xpiName)
+ val policy = StrictMode.getThreadPolicy()
+ try {
+ context.assets.open("extensions/$xpiName")
+ .use { inStream ->
+ // we don't want penaltyDeath() on disk write
+ StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.LAX)
- runtime.installBuiltInWebExtension(
- id = NOSCRIPT_ID,
- url = "resource://android/assets/extensions/" + NOSCRIPT_ID + ".xpi",
+ addonPath.outputStream().use { outStream ->
+ inStream.copyTo(outStream)
+ }
+ }
+ } catch (throwable: IOException) {
+ onError(throwable)
+ return
+ } finally {
+ StrictMode.setThreadPolicy(policy)
+ }
+
+ /**
+ * Install with a file:// URI pointing to the temp location where the addon was copied to.
+ */
+ runtime.installWebExtension(
+ url = addonPath.toURI().toString(),
onSuccess = { extension ->
runtime.setAllowedInPrivateBrowsing(
extension,
@@ -95,6 +122,7 @@ object TorBrowserFeatures {
*/
if (!settings.noscriptInstalled) {
installNoScript(
+ context,
runtime,
onSuccess = {
settings.noscriptInstalled = true
=====================================
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionController.java
=====================================
@@ -1166,6 +1166,27 @@ public class WebExtensionController {
});
}
+ private boolean isBundledExtension(final String extensionId) {
+ return "{73a6fe31-595d-460b-a920-fcc0f8843232}".equals(extensionId);
+ }
+
+ private boolean promptBypass(final WebExtension extension, final EventCallback callback) {
+ // allow bundled extensions, e.g. NoScript, to be installed with no prompt
+ if (isBundledExtension(extension.id)) {
+ callback.resolveTo(
+ GeckoResult.allow().map(
+ allowOrDeny -> {
+ final GeckoBundle response = new GeckoBundle(1);
+ response.putBoolean("allow", true);
+ return response;
+ }
+ )
+ );
+ return true;
+ }
+ return false;
+ }
+
private void installPrompt(final GeckoBundle message, final EventCallback callback) {
final GeckoBundle extensionBundle = message.getBundle("extension");
if (extensionBundle == null
@@ -1181,6 +1202,10 @@ public class WebExtensionController {
final WebExtension extension = new WebExtension(mDelegateControllerProvider, extensionBundle);
+ if (promptBypass(extension, callback)) {
+ return;
+ }
+
if (mPromptDelegate == null) {
Log.e(
LOGTAG, "Tried to install extension " + extension.id + " but no delegate is registered");
@@ -1220,6 +1245,10 @@ public class WebExtensionController {
final WebExtension currentExtension =
new WebExtension(mDelegateControllerProvider, currentBundle);
+ if (promptBypass(currentExtension, callback)) {
+ return;
+ }
+
final WebExtension updatedExtension =
new WebExtension(mDelegateControllerProvider, updatedBundle);
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/4eb9b64fb15c92baa3f7aded1a8a5d1959e7eaed
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/4eb9b64fb15c92baa3f7aded1a8a5d1959e7eaed
You're receiving this email because of your account on gitlab.torproject.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.torproject.org/pipermail/tbb-commits/attachments/20240911/a4661ba1/attachment-0001.htm>
More information about the tbb-commits
mailing list