Unverified Commit 34fcee15 authored by Alex Catarineu's avatar Alex Catarineu Committed by boklm
Browse files

Modify Add-on support

Bug 40030: Install HTTPS Everywhere and NoScript addons on startup

HTTPS Everywhere is installed as a builtin extension and NoScript as
a regular AMO addon. To avoid unnecessary I/O we only install NoScript
the first time, and rely on the browser addon updating mechanism for
keeping up with new versions. This is the same behaviour that was
implemented in the Fennec-based Tor Browser, where it was installed
as a "distribution addon", which also only occurred once.

Bug 40062: HTTPS Everywhere is not shown as installed

Also 40070: Consider storing the list of recommended addons

This implements our own AddonsProvider, which loads the list of
available addons from assets instead of fetching it from an
endpoint. In this list, we replace https-everywhere by
our https-everywhere-eff, so that the EFF one is shown as installed
in the addons list and the AMO one is not displayed.

Also, we hide the uninstall button for builtin addons.

Bug 40058: Hide option for disallowing addon in private mode
parent 4ca15eeb
Loading
Loading
Loading
Loading
+3266 −0

File added.

Preview size limit exceeded, changes collapsed.

+5 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import kotlinx.coroutines.launch
import mozilla.components.feature.addons.Addon
import mozilla.components.feature.addons.AddonManagerException
import mozilla.components.feature.addons.ui.translateName
import mozilla.components.support.webextensions.WebExtensionSupport.installedExtensions
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
@@ -115,7 +116,7 @@ class InstalledAddonDetailsFragment : Fragment() {
                        runIfFragmentIsAttached {
                            this.addon = it
                            switch.isClickable = true
                            privateBrowsingSwitch.isVisible = it.isEnabled()
                            privateBrowsingSwitch.isVisible = false
                            privateBrowsingSwitch.isChecked = it.isAllowedInPrivateBrowsing()
                            switch.setText(R.string.mozac_feature_addons_enabled)
                            view.settings.isVisible = shouldSettingsBeVisible()
@@ -244,7 +245,7 @@ class InstalledAddonDetailsFragment : Fragment() {
    private fun bindAllowInPrivateBrowsingSwitch(view: View) {
        val switch = view.allow_in_private_browsing_switch
        switch.isChecked = addon.isAllowedInPrivateBrowsing()
        switch.isVisible = addon.isEnabled()
        switch.isVisible = false
        switch.setOnCheckedChangeListener { v, isChecked ->
            val addonManager = v.context.components.addonManager
            switch.isClickable = false
@@ -270,6 +271,8 @@ class InstalledAddonDetailsFragment : Fragment() {
        }
    }
    private fun bindRemoveButton(view: View) {
        val isBuiltin = installedExtensions[addon.id]?.isBuiltIn() ?: false
        view.remove_add_on.isVisible = !isBuiltin
        view.remove_add_on.setOnClickListener {
            setAllInteractiveViewsClickable(view, false)
            requireContext().components.addonManager.uninstallAddon(
+1 −27
Original line number Diff line number Diff line
@@ -10,7 +10,6 @@ import android.content.Intent
import androidx.core.net.toUri
import com.google.android.play.core.review.ReviewManagerFactory
import mozilla.components.feature.addons.AddonManager
import mozilla.components.feature.addons.amo.AddonCollectionProvider
import mozilla.components.feature.addons.migration.DefaultSupportedAddonsChecker
import mozilla.components.feature.addons.migration.SupportedAddonsChecker
import mozilla.components.feature.addons.update.AddonUpdater
@@ -95,32 +94,7 @@ class Components(private val context: Context) {
    }

    val addonCollectionProvider by lazyMonitored {
        // Check if we have a customized (overridden) AMO collection (only supported in Nightly)
        if (Config.channel.isNightlyOrDebug && context.settings().amoCollectionOverrideConfigured()) {
            AddonCollectionProvider(
                context,
                core.client,
                collectionUser = context.settings().overrideAmoUser,
                collectionName = context.settings().overrideAmoCollection
            )
        }
        // Use build config otherwise
        else if (!BuildConfig.AMO_COLLECTION_USER.isNullOrEmpty() &&
            !BuildConfig.AMO_COLLECTION_NAME.isNullOrEmpty()
        ) {
            AddonCollectionProvider(
                context,
                core.client,
                serverURL = BuildConfig.AMO_SERVER_URL,
                collectionUser = BuildConfig.AMO_COLLECTION_USER,
                collectionName = BuildConfig.AMO_COLLECTION_NAME,
                maxCacheAgeInMinutes = AMO_COLLECTION_MAX_CACHE_AGE
            )
        }
        // Fall back to defaults
        else {
            AddonCollectionProvider(context, core.client, maxCacheAgeInMinutes = AMO_COLLECTION_MAX_CACHE_AGE)
        }
        TorAddonCollectionProvider(context, core.client)
    }

    val appStartupTelemetry by lazyMonitored { AppStartupTelemetry(analytics.metrics) }
+2 −0
Original line number Diff line number Diff line
@@ -137,6 +137,8 @@ class Core(
                WebCompatFeature.install(it)
                WebCompatReporterFeature.install(it, "fenix")
            }

            TorBrowserFeatures.install(context, it)
        }
    }

+64 −0
Original line number Diff line number Diff line
/* 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/. */

// Copyright (c) 2020, The Tor Project, Inc.

package org.mozilla.fenix.components

import android.content.Context
import android.graphics.Bitmap
import kotlinx.coroutines.withContext
import mozilla.components.concept.fetch.Client
import mozilla.components.feature.addons.Addon
import kotlinx.coroutines.Dispatchers
import mozilla.components.feature.addons.amo.AddonCollectionProvider
import java.io.IOException

internal const val COLLECTION_NAME = "tor_browser_collection"
internal const val ALLOWED_ADDONS_PATH = "allowed_addons.json"
internal const val MAX_CACHE_AGE = 1000L * 365L * 24L * 60L // 1000 years

class TorAddonCollectionProvider(
    private val context: Context,
    client: Client
) : AddonCollectionProvider(
    context, client, serverURL = "",
    collectionName = COLLECTION_NAME,
    maxCacheAgeInMinutes = MAX_CACHE_AGE
) {
    private var isCacheLoaded = false

    @Throws(IOException::class)
    override suspend fun getAvailableAddons(
        allowCache: Boolean,
        readTimeoutInSeconds: Long?,
        language: String?
    ): List<Addon> {
        ensureCache(language)
        return super.getAvailableAddons(true, readTimeoutInSeconds, language)
    }

    @Throws(IOException::class)
    override suspend fun getAddonIconBitmap(addon: Addon): Bitmap? {
        // super.getAddonIconBitmap does not look at the cache, so calling
        // ensureCache here is not helpful. In addition, now the cache depends
        // on a language, and that isn't available right now.
        // ensureCache(language)
        return super.getAddonIconBitmap(addon)
    }

    @Throws(IOException::class)
    private suspend fun ensureCache(language: String?) {
        if (isCacheLoaded) {
            return
        }
        return withContext(Dispatchers.IO) {
            val data = context.assets.open(ALLOWED_ADDONS_PATH).bufferedReader().use {
                it.readText()
            }
            writeToDiskCache(data, language)
            isCacheLoaded = true
        }
    }
}
Loading