From 2fd2780b79f8bd6e727345930d09ca14e2729c20 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 24 Sep 2026 11:15:32 -0400 Subject: [PATCH 1/3] fix(android): match the dev server by host and port The dev-server navigation check compared hosts only, so any port on the dev server's host loaded in the WebView. That also failed GutenbergViewTest when GUTENBERG_EDITOR_URL pointed at 10.0.2.2. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../org/wordpress/gutenberg/GutenbergView.kt | 15 +++++--- .../wordpress/gutenberg/GutenbergViewTest.kt | 35 +++++++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt b/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt index d269ddb5e..a5b085aaa 100644 --- a/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt +++ b/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt @@ -518,11 +518,8 @@ class GutenbergView : FrameLayout { } // Allow local development server if configured - if (BuildConfig.GUTENBERG_EDITOR_URL.isNotEmpty()) { - val editorUrl = Uri.parse(BuildConfig.GUTENBERG_EDITOR_URL) - if (url.host == editorUrl.host) { - return false - } + if (isDevServerUrl(url, BuildConfig.GUTENBERG_EDITOR_URL)) { + return false } // For all other URLs, open in external browser @@ -1400,6 +1397,14 @@ class GutenbergView : FrameLayout { return if (uri.port != -1 && uri.port != defaultPort) "$host:${uri.port}" else host } + /** + * Whether [url] is on the local development server at [editorUrl]. Compares + * origin authorities so another port on the same host, such as a local + * WordPress site beside the dev server, isn't treated as the dev server. + */ + internal fun isDevServerUrl(url: Uri, editorUrl: String): Boolean = + editorUrl.isNotEmpty() && url.authority == originAuthority(editorUrl) + private const val ASSET_LOADING_TIMEOUT_MS = 5000L /** diff --git a/android/Gutenberg/src/test/java/org/wordpress/gutenberg/GutenbergViewTest.kt b/android/Gutenberg/src/test/java/org/wordpress/gutenberg/GutenbergViewTest.kt index f4c283653..613385b5e 100644 --- a/android/Gutenberg/src/test/java/org/wordpress/gutenberg/GutenbergViewTest.kt +++ b/android/Gutenberg/src/test/java/org/wordpress/gutenberg/GutenbergViewTest.kt @@ -292,6 +292,41 @@ class GutenbergViewTest { assertEquals(null, GutenbergView.originAuthority("not a url")) } + // ===== isDevServerUrl ===== + + @Test + fun `isDevServerUrl matches the dev server's host and port`() { + assertTrue( + GutenbergView.isDevServerUrl( + Uri.parse("http://10.0.2.2:5173/src/index.html"), + "http://10.0.2.2:5173/" + ) + ) + } + + @Test + fun `isDevServerUrl rejects another port on the dev server's host`() { + assertFalse( + GutenbergView.isDevServerUrl(Uri.parse("http://10.0.2.2:8888/"), "http://10.0.2.2:5173/") + ) + assertFalse( + GutenbergView.isDevServerUrl(Uri.parse("http://10.0.2.2/"), "http://10.0.2.2:5173/") + ) + } + + @Test + fun `isDevServerUrl matches a dev server URL written with its default port`() { + // Chromium drops a default port before the URL reaches the WebViewClient. + assertTrue( + GutenbergView.isDevServerUrl(Uri.parse("http://localhost/"), "http://localhost:80/") + ) + } + + @Test + fun `isDevServerUrl rejects every URL when no dev server is configured`() { + assertFalse(GutenbergView.isDevServerUrl(Uri.parse("http://localhost:5173/"), "")) + } + // ===== REST API navigation ===== @Test From a33754f42a6ef3db604635cfb2fe18af74b2d3fd Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Fri, 25 Sep 2026 07:57:33 -0400 Subject: [PATCH 2/3] fix(android): skip the dev server check when its URL has no host A schemeless GUTENBERG_EDITOR_URL has no authority, so it matched host-less URLs like mailto: and loaded them in the WebView instead of externally. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../java/org/wordpress/gutenberg/GutenbergView.kt | 13 +++++++++---- .../org/wordpress/gutenberg/GutenbergViewTest.kt | 7 +++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt b/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt index a5b085aaa..22168f5b2 100644 --- a/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt +++ b/android/Gutenberg/src/main/java/org/wordpress/gutenberg/GutenbergView.kt @@ -1399,11 +1399,16 @@ class GutenbergView : FrameLayout { /** * Whether [url] is on the local development server at [editorUrl]. Compares - * origin authorities so another port on the same host, such as a local - * WordPress site beside the dev server, isn't treated as the dev server. + * host and port so another port on the same host, such as a local WordPress + * site beside the dev server, isn't treated as the dev server. + * + * Returns false when [editorUrl] has no host (unset or missing a scheme), so + * host-less URLs like `mailto:` never match it. */ - internal fun isDevServerUrl(url: Uri, editorUrl: String): Boolean = - editorUrl.isNotEmpty() && url.authority == originAuthority(editorUrl) + internal fun isDevServerUrl(url: Uri, editorUrl: String): Boolean { + val devServerAuthority = originAuthority(editorUrl) ?: return false + return url.authority == devServerAuthority + } private const val ASSET_LOADING_TIMEOUT_MS = 5000L diff --git a/android/Gutenberg/src/test/java/org/wordpress/gutenberg/GutenbergViewTest.kt b/android/Gutenberg/src/test/java/org/wordpress/gutenberg/GutenbergViewTest.kt index 613385b5e..07fe1668c 100644 --- a/android/Gutenberg/src/test/java/org/wordpress/gutenberg/GutenbergViewTest.kt +++ b/android/Gutenberg/src/test/java/org/wordpress/gutenberg/GutenbergViewTest.kt @@ -327,6 +327,13 @@ class GutenbergViewTest { assertFalse(GutenbergView.isDevServerUrl(Uri.parse("http://localhost:5173/"), "")) } + @Test + fun `isDevServerUrl rejects host-less URLs when the dev server URL has no host`() { + // Without a scheme, the dev server URL has no authority, and neither do these. + assertFalse(GutenbergView.isDevServerUrl(Uri.parse("mailto:a@example.com"), "10.0.2.2:5173")) + assertFalse(GutenbergView.isDevServerUrl(Uri.parse("tel:5551234"), "10.0.2.2:5173")) + } + // ===== REST API navigation ===== @Test From 97bbdc306bb2daa34360432127f8dfc3972ae563 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Fri, 25 Sep 2026 09:58:33 -0400 Subject: [PATCH 3/3] fix(android): trim the dev server URL from local.properties Properties keeps trailing whitespace, so a stray space after the port made Uri drop it and the dev server check never match. Co-Authored-By: Claude Opus 5.5 (1M context) --- android/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 454b87067..c693855b1 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -6,7 +6,7 @@ if (localPropertiesFile.exists()) { } ext { - set("gutenbergEditorUrl", localProperties.getProperty("GUTENBERG_EDITOR_URL") ?: "") + set("gutenbergEditorUrl", localProperties.getProperty("GUTENBERG_EDITOR_URL")?.trim() ?: "") } plugins {