[tor-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/tor-commits/attachments/20240911/f42a5191/attachment-0001.htm>


More information about the tor-commits mailing list