also looking at this
fix(android): jsQuote takes Any?, because three call sites are not strings
#164
2 files
+18
-5
| @@ -170,15 +170,20 @@ class CraftBridge( | ||
| 170 | 170 | * Quote a value for interpolation into JavaScript. |
| 171 | 171 | * |
| 172 | 172 | * The result carries its own quotes, so write `f($payload)` — leaving the |
| 173 | * surrounding `'...'` in place would quote it twice. A Kotlin null renders as | |
| 174 | * the string "null", which is what plain interpolation produced before; whether | |
| 175 | * that should instead be a JS null is a separate question from escaping. | |
| 173 | * surrounding `'...'` in place would quote it twice. | |
| 176 | 174 | * |
| 177 | 175 | * Without this, a value containing an apostrophe ends the JS string early, the |
| 178 | 176 | * script fails to parse, and `evaluateJavascript` runs nothing at all — so a |
| 179 | 177 | * hand-built promise with no timeout never settles. |
| 178 | * | |
| 179 | * `Any?` rather than `String?`, because these call sites replaced string | |
| 180 | * interpolation and interpolation accepts anything: `contactId` is the `Long` | |
| 181 | * from `ContentUris.parseId`, `downloadId` the `Long` from | |
| 182 | * `DownloadManager.enqueue`, and `errString` the `CharSequence` a | |
| 183 | * `BiometricPrompt` error arrives as. `Any?.toString()` is exactly what `$x` | |
| 184 | * did, null included — it renders as the string "null", the way it always has. | |
| 180 | 185 | */ |
| 181 | private fun jsQuote(value: String?): String = JSONObject.quote(value ?: "null") | |
| 186 | private fun jsQuote(value: Any?): String = JSONObject.quote(value.toString()) | |
| 182 | 187 | |
| 183 | 188 | fun injectBridge() { |
| 184 | 189 | // Before the script, so a native callback arriving while the page is |
| @@ -143,6 +143,14 @@ test "the templates that answer the page are actually in the scanned set" { | ||
| 143 | 143 | |
| 144 | 144 | // And the fix is present rather than the sites merely being deleted. |
| 145 | 145 | const bridge = sourceOf("CraftBridge.kt.template").?; |
| 146 | try testing.expect(std.mem.indexOf(u8, bridge, "private fun jsQuote(") != null); | |
| 147 | 146 | try testing.expect(std.mem.count(u8, bridge, "jsQuote(") >= 50); |
| 147 | ||
| 148 | // `Any?`, not `String?`. The call sites replaced string interpolation, | |
| 149 | // which accepts anything — `contactId` is a Long, `downloadId` is a Long, | |
| 150 | // `errString` is a CharSequence — and narrowing the parameter back makes | |
| 151 | // those three sites stop compiling. Nothing in CI compiles Kotlin, so this | |
| 152 | // is the only thing that would say so. See #163. | |
| 153 | try testing.expect( | |
| 154 | std.mem.indexOf(u8, bridge, "private fun jsQuote(value: Any?): String") != null, | |
| 155 | ); | |
| 148 | 156 | } |