From 5b8321c937654c3fc1161b1c88087372b775358a Mon Sep 17 00:00:00 2001 From: NathanFallet Date: Mon, 10 Nov 2025 17:52:22 +0100 Subject: [PATCH 1/2] draft fix for extensions --- .../kotlin/dev/kdriver/core/browser/Config.kt | 5 +++-- .../kdriver/core/browser/DefaultBrowser.kt | 22 +++++++++++-------- settings.gradle.kts | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/core/src/commonMain/kotlin/dev/kdriver/core/browser/Config.kt b/core/src/commonMain/kotlin/dev/kdriver/core/browser/Config.kt index c994d59dd..883a1f115 100644 --- a/core/src/commonMain/kotlin/dev/kdriver/core/browser/Config.kt +++ b/core/src/commonMain/kotlin/dev/kdriver/core/browser/Config.kt @@ -76,9 +76,10 @@ class Config( "--disable-renderer-backgrounding", "--disable-background-networking", "--disable-dev-shm-usage", - "--disable-features=IsolateOrigins,DisableLoadExtensionCommandLineSwitch,site-per-process", + "--disable-features=IsolateOrigins,site-per-process", "--disable-session-crashed-bubble", - "--disable-search-engine-choice-screen" + "--disable-search-engine-choice-screen", + "--enable-unsafe-extension-debugging" ) val browserArgs: List diff --git a/core/src/commonMain/kotlin/dev/kdriver/core/browser/DefaultBrowser.kt b/core/src/commonMain/kotlin/dev/kdriver/core/browser/DefaultBrowser.kt index 0e9a5346f..3011d6dd8 100644 --- a/core/src/commonMain/kotlin/dev/kdriver/core/browser/DefaultBrowser.kt +++ b/core/src/commonMain/kotlin/dev/kdriver/core/browser/DefaultBrowser.kt @@ -1,9 +1,6 @@ package dev.kdriver.core.browser -import dev.kdriver.cdp.domain.Target -import dev.kdriver.cdp.domain.browser -import dev.kdriver.cdp.domain.page -import dev.kdriver.cdp.domain.target +import dev.kdriver.cdp.domain.* import dev.kdriver.core.connection.Connection import dev.kdriver.core.connection.DefaultConnection import dev.kdriver.core.connection.OwnedConnection @@ -151,11 +148,6 @@ open class DefaultBrowser( config.port = freePort() } - // handle extensions if any - config.extensions.takeIf { it.isNotEmpty() }?.let { - config.addArgument("--load-extension=${it.joinToString(",")}") - } - config.lang?.let { config.addArgument("--lang=$it") } @@ -220,6 +212,18 @@ open class DefaultBrowser( connection.target.setDiscoverTargets(discover = true) } + if (config.extensions.isNotEmpty()) { + logger.info("Loading ${config.extensions.size} extension(s) using CDP") + config.extensions.forEach { extensionPath -> + try { + val result = connection.extensions.loadUnpacked(extensionPath.toString()) + logger.info("Successfully loaded extension from $extensionPath with ID: ${result.id}") + } catch (e: Exception) { + logger.warn("Failed to load extension from $extensionPath: ${e.message}") + } + } + } + updateTargets() return this } diff --git a/settings.gradle.kts b/settings.gradle.kts index 02dc479fc..d84259e7d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,7 +23,7 @@ dependencyResolutionManagement { plugin("maven", "com.vanniktech.maven.publish").version("0.30.0") // Kaccelero - version("kaccelero", "0.6.7") + version("kaccelero", "0.6.8") library("kaccelero-core", "dev.kaccelero", "core").versionRef("kaccelero") // Ktor From 835cf242f70e9d5936cb39526201cc032b010a31 Mon Sep 17 00:00:00 2001 From: NathanFallet Date: Mon, 10 Nov 2025 18:17:07 +0100 Subject: [PATCH 2/2] drafting a test case --- .../kdriver/core/browser/DefaultBrowser.kt | 4 ++- .../kotlin/dev/kdriver/core/Extensions.kt | 8 +++++ .../dev/kdriver/core/browser/BrowserTest.kt | 36 +++++++++++++++++++ .../jvmTest/resources/extension/content.js | 20 +++++++++++ .../jvmTest/resources/extension/manifest.json | 22 ++++++++++++ 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 core/src/jvmTest/resources/extension/content.js create mode 100644 core/src/jvmTest/resources/extension/manifest.json diff --git a/core/src/commonMain/kotlin/dev/kdriver/core/browser/DefaultBrowser.kt b/core/src/commonMain/kotlin/dev/kdriver/core/browser/DefaultBrowser.kt index 3011d6dd8..8063cfa03 100644 --- a/core/src/commonMain/kotlin/dev/kdriver/core/browser/DefaultBrowser.kt +++ b/core/src/commonMain/kotlin/dev/kdriver/core/browser/DefaultBrowser.kt @@ -219,7 +219,9 @@ open class DefaultBrowser( val result = connection.extensions.loadUnpacked(extensionPath.toString()) logger.info("Successfully loaded extension from $extensionPath with ID: ${result.id}") } catch (e: Exception) { - logger.warn("Failed to load extension from $extensionPath: ${e.message}") + logger.error("Failed to load extension from $extensionPath: ${e.message}") + e.printStackTrace() + throw e } } } diff --git a/core/src/jvmTest/kotlin/dev/kdriver/core/Extensions.kt b/core/src/jvmTest/kotlin/dev/kdriver/core/Extensions.kt index 746ab25d9..c13d4f42f 100644 --- a/core/src/jvmTest/kotlin/dev/kdriver/core/Extensions.kt +++ b/core/src/jvmTest/kotlin/dev/kdriver/core/Extensions.kt @@ -1,8 +1,16 @@ package dev.kdriver.core +import kotlinx.io.files.Path +import java.io.File +import java.net.URI + fun sampleFile(name: String): String { val resource = checkNotNull(Thread.currentThread().contextClassLoader.getResource(name)) { "Resource '$name' not found" } return resource.toURI().toString() } + +fun samplePath(name: String): Path { + return Path(File(URI(sampleFile(name))).absolutePath) +} diff --git a/core/src/jvmTest/kotlin/dev/kdriver/core/browser/BrowserTest.kt b/core/src/jvmTest/kotlin/dev/kdriver/core/browser/BrowserTest.kt index 319768cff..b8a15bcd2 100644 --- a/core/src/jvmTest/kotlin/dev/kdriver/core/browser/BrowserTest.kt +++ b/core/src/jvmTest/kotlin/dev/kdriver/core/browser/BrowserTest.kt @@ -1,6 +1,8 @@ package dev.kdriver.core.browser +import dev.kdriver.core.extensions.addExtension import dev.kdriver.core.sampleFile +import dev.kdriver.core.samplePath import dev.kdriver.core.tab.ReadyState import kotlinx.coroutines.runBlocking import kotlin.test.Test @@ -151,4 +153,38 @@ class BrowserTest { browser.stop() } + @Test + fun testExtensionLoading() = runBlocking { + val config = Config(headless = true, sandbox = false) + config.addExtension(samplePath("extension")) + + assertTrue(config.extensions.isNotEmpty()) + assertEquals(1, config.extensions.size) + + val browser = createBrowser(this, config) + assertNotNull(browser) + assertNotNull(browser.connection) + + // Navigate to a test page + val tab = browser.get("https://example.com") + tab.waitForReadyState(ReadyState.COMPLETE) + + // Wait for extension to inject content + tab.wait(5000) + + // Verify the extension marker element exists + val markerElement = tab.select("#kdriver-extension-marker", timeout = 10000) + assertNotNull( + markerElement, + "Extension marker element should exist, indicating the extension loaded successfully" + ) + assertEquals("KDriver Extension Active", markerElement.text) + + // Verify the data attribute + val dataAttr = markerElement["data-extension-loaded"] + assertEquals("true", dataAttr) + + browser.stop() + } + } diff --git a/core/src/jvmTest/resources/extension/content.js b/core/src/jvmTest/resources/extension/content.js new file mode 100644 index 000000000..2afb1e3e8 --- /dev/null +++ b/core/src/jvmTest/resources/extension/content.js @@ -0,0 +1,20 @@ +// KDriver test extension content script +// This script adds a marker element to the page to verify the extension was loaded + +(function () { + // Create a marker div that we can detect in tests + const marker = document.createElement('div'); + marker.id = 'kdriver-extension-marker'; + marker.setAttribute('data-extension-loaded', 'true'); + marker.style.display = 'none'; + marker.textContent = 'KDriver Extension Active'; + + // Insert the marker into the document + if (document.body) { + document.body.appendChild(marker); + } else { + document.addEventListener('DOMContentLoaded', function () { + document.body.appendChild(marker); + }); + } +})(); diff --git a/core/src/jvmTest/resources/extension/manifest.json b/core/src/jvmTest/resources/extension/manifest.json new file mode 100644 index 000000000..8dc54bbc5 --- /dev/null +++ b/core/src/jvmTest/resources/extension/manifest.json @@ -0,0 +1,22 @@ +{ + "manifest_version": 3, + "name": "KDriver Test Extension", + "version": "1.0", + "description": "Test extension for verifying extension loading", + "content_scripts": [ + { + "matches": [ + "" + ], + "js": [ + "content.js" + ], + "run_at": "document_end", + "match_about_blank": true, + "all_frames": true + } + ], + "host_permissions": [ + "" + ] +}