tor-browser

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

PublicSuffixListPlugin.kt (5539B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 package mozilla.components.gradle.plugins
      6 
      7 import com.android.build.api.variant.AndroidComponentsExtension
      8 import com.android.build.gradle.LibraryPlugin
      9 import okio.Buffer
     10 import okio.ByteString
     11 import okio.ByteString.Companion.encodeUtf8
     12 import org.gradle.api.DefaultTask
     13 import org.gradle.api.GradleException
     14 import org.gradle.api.InvalidUserDataException
     15 import org.gradle.api.Plugin
     16 import org.gradle.api.Project
     17 import org.gradle.api.file.DirectoryProperty
     18 import org.gradle.api.file.RegularFileProperty
     19 import org.gradle.api.tasks.InputFile
     20 import org.gradle.api.tasks.OutputDirectory
     21 import org.gradle.api.tasks.TaskAction
     22 import org.gradle.kotlin.dsl.withType
     23 import java.io.File
     24 import java.util.TreeSet
     25 
     26 /**
     27 * A self-contained, configuration-cache-compatible Gradle task to generate the Public Suffix List asset.
     28 */
     29 abstract class GeneratePslAssetTask : DefaultTask() {
     30 
     31    @get:InputFile
     32    abstract val sourceFile: RegularFileProperty
     33 
     34    @get:OutputDirectory
     35    abstract val outputDir: DirectoryProperty
     36 
     37    @TaskAction
     38    fun generate() {
     39        val source = sourceFile.get().asFile
     40        if (!source.exists()) {
     41            throw GradleException("Public Suffix List source file not found: ${source.absolutePath}")
     42        }
     43 
     44        val startTime = System.currentTimeMillis()
     45        logger.info("PublicSuffixList> Executing generatePslAsset: Reading Public Suffix List from ${source.path}")
     46 
     47        val listData = parsePublicSuffixList(source)
     48        val newContent = buildBinaryContent(listData)
     49        val destination = outputDir.file("publicsuffixes").get().asFile
     50 
     51        logger.info("PublicSuffixList> Writing new Public Suffix List asset...")
     52        destination.parentFile.mkdirs()
     53        destination.writeBytes(newContent.toByteArray())
     54 
     55        val duration = System.currentTimeMillis() - startTime
     56        logger.info("PublicSuffixList> Public Suffix List asset generation complete in ${duration}ms.")
     57    }
     58 
     59    private fun buildBinaryContent(data: PublicSuffixListData): ByteString {
     60        val buffer = Buffer()
     61        buffer.writeInt(data.totalRuleBytes)
     62        for (domain in data.sortedRules) {
     63            buffer.write(domain).writeByte('\n'.code)
     64        }
     65        buffer.writeInt(data.totalExceptionRuleBytes)
     66        for (domain in data.sortedExceptionRules) {
     67            buffer.write(domain).writeByte('\n'.code)
     68        }
     69        return buffer.readByteString()
     70    }
     71 
     72    private fun parsePublicSuffixList(sourceFile: File): PublicSuffixListData {
     73        val data = PublicSuffixListData()
     74 
     75        sourceFile.useLines { lines ->
     76            lines.filter { it.isNotBlank() && !it.startsWith("//") }
     77                .forEach { line ->
     78                    if (line.contains(WILDCARD_CHAR)) {
     79                        assertWildcardRule(line)
     80                    }
     81 
     82                    var rule = line.encodeUtf8()
     83                    if (rule.startsWith(EXCEPTION_RULE_MARKER)) {
     84                        rule = rule.substring(1)
     85                        // We use '\n' for end of value.
     86                        data.sortedExceptionRules.add(rule)
     87                        data.totalExceptionRuleBytes += rule.size + 1
     88                    } else {
     89                        data.sortedRules.add(rule)
     90                        // We use '\n' for end of value.
     91                        data.totalRuleBytes += rule.size + 1
     92                    }
     93                }
     94        }
     95 
     96        return data
     97    }
     98 
     99    @Suppress("ThrowsCount")
    100    private fun assertWildcardRule(rule: String) {
    101        if (!rule.startsWith(WILDCARD_CHAR)) {
    102            throw InvalidUserDataException("Wildcard is not in leftmost position")
    103        }
    104        if (rule.lastIndexOf(WILDCARD_CHAR) > 0) {
    105            throw InvalidUserDataException("Rule contains multiple wildcards")
    106        }
    107 
    108        if (rule.length == 1) {
    109            throw InvalidUserDataException("Rule wildcards the first level")
    110        }
    111    }
    112 
    113    companion object {
    114        private const val WILDCARD_CHAR = "*"
    115        private val EXCEPTION_RULE_MARKER = "!".encodeUtf8()
    116    }
    117 }
    118 
    119 abstract class PublicSuffixListExtension {
    120    @get:InputFile
    121    abstract val sourceFile: RegularFileProperty
    122 }
    123 
    124 class PublicSuffixListPlugin : Plugin<Project> {
    125    override fun apply(project: Project) {
    126        project.plugins.withType<LibraryPlugin>().configureEach {
    127            val extension = project.extensions.create("publicSuffixList", PublicSuffixListExtension::class.java)
    128 
    129            val generateTaskProvider = project.tasks.register("generatePslAsset", GeneratePslAssetTask::class.java) {
    130                outputDir.set(project.layout.buildDirectory.dir("generated/assets/publicsuffixlist"))
    131                sourceFile.set(extension.sourceFile)
    132            }
    133 
    134            val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
    135            androidComponents.onVariants(androidComponents.selector().all()) { variant ->
    136                variant.sources.assets?.addGeneratedSourceDirectory(generateTaskProvider, GeneratePslAssetTask::outputDir)
    137            }
    138        }
    139    }
    140 }
    141 
    142 private data class PublicSuffixListData(
    143    var totalRuleBytes: Int = 0,
    144    var totalExceptionRuleBytes: Int = 0,
    145    val sortedRules: TreeSet<ByteString> = TreeSet(),
    146    val sortedExceptionRules: TreeSet<ByteString> = TreeSet(),
    147 )