Commit 80311e41 authored by Alex Catarineu's avatar Alex Catarineu Committed by Matthew Finkel
Browse files

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.
parent 4c051716
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -126,6 +126,8 @@ class Core(
            if (Config.channel.isNightlyOrDebug || Config.channel.isBeta) {
                WebCompatReporterFeature.install(it, "fenix")
            }

            TorBrowserFeatures.install(context, it)
        }
    }

+109 −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 mozilla.components.concept.engine.webextension.WebExtension
import mozilla.components.concept.engine.webextension.WebExtensionRuntime
import mozilla.components.support.base.log.logger.Logger
import org.mozilla.fenix.ext.settings
import java.io.IOException

object TorBrowserFeatures {
    private val logger = Logger("torbrowser-features")

    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 addonPath =
            context.cacheDir.resolve("{73a6fe31-595d-460b-a920-fcc0f8843232}.xpi")
        try {
            context.assets.open("extensions/{73a6fe31-595d-460b-a920-fcc0f8843232}.xpi")
                .use { inStream ->
                    addonPath.outputStream().use { outStream ->
                        inStream.copyTo(outStream)
                    }
                }
        } catch (throwable: IOException) {
            onError(throwable)
            return
        }

        /**
         * Install with a file:// URI pointing to the temp location where the addon was copied to.
         */
        runtime.installWebExtension(
            id = "{73a6fe31-595d-460b-a920-fcc0f8843232}",
            url = addonPath.toURI().toString(),
            onSuccess = { extension ->
                runtime.setAllowedInPrivateBrowsing(
                    extension,
                    true,
                    onSuccess,
                    onError
                )
            },
            onError = { _, throwable -> onError(throwable) })
    }

    private fun installHTTPSEverywhere(
        runtime: WebExtensionRuntime,
        onSuccess: ((WebExtension) -> Unit),
        onError: ((Throwable) -> Unit)
    ) {
        runtime.installWebExtension(
            id = "https-everywhere-eff@eff.org",
            url = "resource://android/assets/extensions/https-everywhere/",
            onSuccess = onSuccess,
            onError = { _, throwable -> onError(throwable) }
        )
    }

    fun install(context: Context, runtime: WebExtensionRuntime) {
        /**
         * Install HTTPS Everywhere as a builtin addon, with a resource://android/ URI.
         * No signatures will be checked/required and there will be no automatic
         * extension updates. It's ok to always try to install, since for builtin extensions it will
         * be checked internally whether it is necessary to install or not: it will only be done
         * if this is the first time or if it's a newer version.
         */
        installHTTPSEverywhere(
            runtime,
            onSuccess = {
                logger.debug("HTTPS Everywhere extension was installed successfully")
            },
            onError = { throwable ->
                logger.error("Could not install HTTPS Everywhere extension", throwable)
            }
        )

        /**
         *  Install NoScript as a user WebExtension if we have not already done so.
         *  AMO signature is checked, and AMO automatic updates will work. The extension should
         *  behave as if the user had installed it manually.
         */
        if (!context.settings().noscriptInstalled) {
            installNoScript(
                context,
                runtime,
                onSuccess = {
                    context.settings().noscriptInstalled = true
                    logger.debug("NoScript extension was installed successfully")
                },
                onError = { throwable ->
                    logger.error("Could not install NoScript extension", throwable)
                }
            )
        }
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -1000,4 +1000,9 @@ class Settings(private val appContext: Context) : PreferencesHolder {
        appContext.getPreferenceKey(R.string.pref_key_swipe_toolbar_switch_tabs),
        default = true
    )

    var noscriptInstalled by booleanPreference(
        appContext.getPreferenceKey(R.string.pref_key_noscript_installed),
        default = false
    )
}
+2 −0
Original line number Diff line number Diff line
@@ -237,4 +237,6 @@
    <string name="pref_key_camera_permissions_needed" translatable="false">pref_key_camera_permissions_needed</string>

    <string name="pref_key_return_to_browser" translatable="false">pref_key_return_to_browser</string>

    <string name="pref_key_noscript_installed" translatable="false">pref_key_noscript_installed</string>
</resources>