tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 185d41ebb5abbbc7d7d3ea4c78e1f6951d72a335
parent bad4051d3ab19b35c8c4402b5a6cb57f066f8b00
Author: mcarare <48995920+mcarare@users.noreply.github.com>
Date:   Tue,  2 Dec 2025 07:51:56 +0000

Bug 1986995 - Generate the Public Suffix List asset at build time. r=android-reviewers,nalexander,ahochheiden

This patch removes the manually uploaded Public Suffix List (PSL) asset from the source tree and replaces it with a Gradle task that generates it at build time.

The new `generatePslAsset` task reads from the canonical PSL source file in `netwerk/dns/effective_tld_names.dat` and creates the binary asset required by the `lib-publicsuffixlist` component. This ensures the PSL is always up-to-date with the netwerk version and removes the need to manually update and check in the asset file.

The custom `PublicSuffixListPlugin` is refactored into a self-contained, cacheable Gradle task for better performance and maintainability.

Differential Revision: https://phabricator.services.mozilla.com/D272237

Diffstat:
Mmobile/android/android-components/components/lib/publicsuffixlist/build.gradle | 17++++++++---------
Dmobile/android/android-components/components/lib/publicsuffixlist/src/main/assets/publicsuffixes | 0
Mmobile/android/android-components/plugins/publicsuffixlist/build.gradle | 4+++-
Mmobile/android/android-components/plugins/publicsuffixlist/src/main/java/PublicSuffixListPlugin.kt | 176+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
4 files changed, 111 insertions(+), 86 deletions(-)

diff --git a/mobile/android/android-components/components/lib/publicsuffixlist/build.gradle b/mobile/android/android-components/components/lib/publicsuffixlist/build.gradle @@ -3,21 +3,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ plugins { + id 'com.android.library' + id 'kotlin-android' id 'mozac.PublicSuffixListPlugin' } -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'mozac.PublicSuffixListPlugin' +apply from: '../../../common-config.gradle' +apply from: '../../../publish.gradle' android { - buildFeatures { - viewBinding = true - } - namespace = 'mozilla.components.lib.publicsuffixlist' } +publicSuffixList { + sourceFile = file("${gradle.mozconfig.topsrcdir}/netwerk/dns/effective_tld_names.dat") +} + dependencies { implementation libs.androidx.annotation implementation libs.kotlinx.coroutines @@ -30,6 +31,4 @@ dependencies { testImplementation libs.robolectric } -apply from: '../../../common-config.gradle' -apply from: '../../../publish.gradle' ext.configurePublish(config.componentsGroupId, project.name, project.ext.description) diff --git a/mobile/android/android-components/components/lib/publicsuffixlist/src/main/assets/publicsuffixes b/mobile/android/android-components/components/lib/publicsuffixlist/src/main/assets/publicsuffixes Binary files differ. diff --git a/mobile/android/android-components/plugins/publicsuffixlist/build.gradle b/mobile/android/android-components/plugins/publicsuffixlist/build.gradle @@ -22,11 +22,13 @@ repositories { dependencies { implementation libs.okhttp implementation libs.okio + + compileOnly libs.android.gradle.plugin } gradlePlugin { plugins.register("mozac.PublicSuffixListPlugin") { id = "mozac.PublicSuffixListPlugin" - implementationClass = "PublicSuffixListPlugin" + implementationClass = "mozilla.components.gradle.plugins.PublicSuffixListPlugin" } } diff --git a/mobile/android/android-components/plugins/publicsuffixlist/src/main/java/PublicSuffixListPlugin.kt b/mobile/android/android-components/plugins/publicsuffixlist/src/main/java/PublicSuffixListPlugin.kt @@ -2,108 +2,110 @@ * 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/. */ -import okhttp3.OkHttpClient -import okhttp3.Request +package mozilla.components.gradle.plugins + +import com.android.build.api.variant.AndroidComponentsExtension +import com.android.build.gradle.LibraryPlugin +import okio.Buffer import okio.ByteString import okio.ByteString.Companion.encodeUtf8 -import okio.buffer -import okio.sink +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.InvalidUserDataException import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction import java.io.File import java.util.TreeSet /** - * Gradle plugin to update the public suffix list used by the `lib-publicsuffixlist` component. - * - * Base on PublicSuffixListGenerator from OkHttp: - * https://github.com/square/okhttp/blob/master/okhttp/src/test/java/okhttp3/internal/publicsuffix/PublicSuffixListGenerator.java + * A self-contained, configuration-cache-compatible Gradle task to generate the Public Suffix List asset. */ -class PublicSuffixListPlugin : Plugin<Project> { - override fun apply(project: Project) { - project.tasks.register("updatePSL") { - doLast { - val filename = project.projectDir.absolutePath + "/src/main/assets/publicsuffixes" - updatePublicSuffixList(filename) - } - } - } - - private fun updatePublicSuffixList(destination: String) { - val list = fetchPublicSuffixList() - writeListToDisk(destination, list) - } - - private fun writeListToDisk(destination: String, data: PublicSuffixListData) { - val fileSink = File(destination).sink() - - fileSink.buffer().use { sink -> - sink.writeInt(data.totalRuleBytes) +abstract class GeneratePslAssetTask : DefaultTask() { - for (domain in data.sortedRules) { - sink.write(domain).writeByte('\n'.code) - } + @get:InputFile + abstract val sourceFile: RegularFileProperty - sink.writeInt(data.totalExceptionRuleBytes) + @get:OutputDirectory + abstract val outputDir: DirectoryProperty - for (domain in data.sortedExceptionRules) { - sink.write(domain).writeByte('\n'.code) - } + @TaskAction + fun generate() { + val source = sourceFile.get().asFile + if (!source.exists()) { + throw GradleException("Public Suffix List source file not found: ${source.absolutePath}") } - } - - private fun fetchPublicSuffixList(): PublicSuffixListData { - val client = OkHttpClient.Builder().build() - - val request = Request.Builder() - .url("https://publicsuffix.org/list/public_suffix_list.dat") - .build() - client.newCall(request).execute().use { response -> - val source = response.body!!.source() + val startTime = System.currentTimeMillis() + logger.info("PublicSuffixList> Executing generatePslAsset: Reading Public Suffix List from ${source.path}") - val data = PublicSuffixListData() + val listData = parsePublicSuffixList(source) + val newContent = buildBinaryContent(listData) + val destination = outputDir.file("publicsuffixes").get().asFile - while (!source.exhausted()) { - val line = source.readUtf8LineStrict() + logger.info("PublicSuffixList> Writing new Public Suffix List asset...") + destination.parentFile.mkdirs() + destination.writeBytes(newContent.toByteArray()) - if (line.trim { it <= ' ' }.isEmpty() || line.startsWith("//")) { - continue - } - - if (line.contains(WILDCARD_CHAR)) { - assertWildcardRule(line) - } + val duration = System.currentTimeMillis() - startTime + logger.info("PublicSuffixList> Public Suffix List asset generation complete in ${duration}ms.") + } - var rule = line.encodeUtf8() + private fun buildBinaryContent(data: PublicSuffixListData): ByteString { + val buffer = Buffer() + buffer.writeInt(data.totalRuleBytes) + for (domain in data.sortedRules) { + buffer.write(domain).writeByte('\n'.code) + } + buffer.writeInt(data.totalExceptionRuleBytes) + for (domain in data.sortedExceptionRules) { + buffer.write(domain).writeByte('\n'.code) + } + return buffer.readByteString() + } - if (rule.startsWith(EXCEPTION_RULE_MARKER)) { - rule = rule.substring(1) - // We use '\n' for end of value. - data.totalExceptionRuleBytes += rule.size + 1 - data.sortedExceptionRules.add(rule) - } else { - data.totalRuleBytes += rule.size + 1 // We use '\n' for end of value. - data.sortedRules.add(rule) + private fun parsePublicSuffixList(sourceFile: File): PublicSuffixListData { + val data = PublicSuffixListData() + + sourceFile.useLines { lines -> + lines.filter { it.isNotBlank() && !it.startsWith("//") } + .forEach { line -> + if (line.contains(WILDCARD_CHAR)) { + assertWildcardRule(line) + } + + var rule = line.encodeUtf8() + if (rule.startsWith(EXCEPTION_RULE_MARKER)) { + rule = rule.substring(1) + // We use '\n' for end of value. + data.sortedExceptionRules.add(rule) + data.totalExceptionRuleBytes += rule.size + 1 + } else { + data.sortedRules.add(rule) + // We use '\n' for end of value. + data.totalRuleBytes += rule.size + 1 + } } - } - - return data } + + return data } - @Suppress("TooGenericExceptionThrown", "ThrowsCount") + @Suppress("ThrowsCount") private fun assertWildcardRule(rule: String) { - if (rule.indexOf(WILDCARD_CHAR) != 0) { - throw RuntimeException("Wildcard is not not in leftmost position") + if (!rule.startsWith(WILDCARD_CHAR)) { + throw InvalidUserDataException("Wildcard is not in leftmost position") } - - if (rule.indexOf(WILDCARD_CHAR, 1) != -1) { - throw RuntimeException("Rule contains multiple wildcards") + if (rule.lastIndexOf(WILDCARD_CHAR) > 0) { + throw InvalidUserDataException("Rule contains multiple wildcards") } if (rule.length == 1) { - throw RuntimeException("Rule wildcards the first level") + throw InvalidUserDataException("Rule wildcards the first level") } } @@ -113,10 +115,32 @@ class PublicSuffixListPlugin : Plugin<Project> { } } -data class PublicSuffixListData( +abstract class PublicSuffixListExtension { + @get:InputFile + abstract val sourceFile: RegularFileProperty +} + +class PublicSuffixListPlugin : Plugin<Project> { + override fun apply(project: Project) { + project.plugins.withType<LibraryPlugin>().configureEach { + val extension = project.extensions.create("publicSuffixList", PublicSuffixListExtension::class.java) + + val generateTaskProvider = project.tasks.register("generatePslAsset", GeneratePslAssetTask::class.java) { + outputDir.set(project.layout.buildDirectory.dir("generated/assets/publicsuffixlist")) + sourceFile.set(extension.sourceFile) + } + + val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) + androidComponents.onVariants(androidComponents.selector().all()) { variant -> + variant.sources.assets?.addGeneratedSourceDirectory(generateTaskProvider, GeneratePslAssetTask::outputDir) + } + } + } +} + +private data class PublicSuffixListData( var totalRuleBytes: Int = 0, var totalExceptionRuleBytes: Int = 0, - val sortedRules: TreeSet<ByteString> = TreeSet(), val sortedExceptionRules: TreeSet<ByteString> = TreeSet(), )