Comments (6)
So it looks like there are two parts to this issue: redirecting to a local address, and capacitor not injecting the JS bridge into external pages. Again, both of these are only relevant to Android, the iOS side works perfectly
For the first issue, if we try to navigate to a computer on the local network (http, through ip address or [computername].local
) we would get the issue described above where the platform would be seen as web
and the plugins would use the web version or fail entirely. If we instead redirect to a remotely hosted server (https), we get a slightly different issue.
If we add our remote server to the server.allowNavigation
config and redirect to it using the steps above, the platform is reported as android
correctly, but all plugins are completely broken:
Investigating this issue, we found the root cause to be the JavaScript injection in Bridge.java. We notice that if WebViewFeature.DOCUMENT_START_SCRIPT
is supported, the javascript is only injected into the base URL (if it is NOT supported (i.e by commenting the if guard out), the injector always works). If the addDocumentStartJavaScript()
function were changed to inject on all allowedOriginRules
(the base URL and everything in the server.allowNavigation
setting) instead of just the base URL, all plugins work again.
Here's the diff:
Original:
capacitor/android/capacitor/src/main/java/com/getcapacitor/Bridge.java
Lines 254 to 269 in f8264cc
New:
// Start the local web server
JSInjector injector = getJSInjector();
if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
WebViewCompat.addDocumentStartJavaScript(webView, injector.getScriptString(), allowedOriginRules);
injector = null;
}
localServer = new WebViewLocalServer(context, this, injector, authorities, html5mode);
I am new to capacitor and have no idea if this would have any side effects, but this seems to fix our issue completely and bring parity with the iOS version.
So to summarize:
The original issue still exists while redirecting to a computer on a local network (or just over http, I don't have the capability to test that right now)
There also appears to be a bug when redirecting to a remote server (again, maybe just over https) caused by the native bridge not injecting capacitor's Javascript on remote pages. A fix we found was to make the change above, but I'd rather ask if it is the correct approach before creating a pull request.
from capacitor.
In our case the issue was, that the script did not get injected on allowNavigation pages because it is not provided to the addDocumentStartJavaScript function:
i also recommend using https://www.npmjs.com/package/patch-package for all these patches until it is being resolved.
from capacitor.
I'm experiencing this as well. I use the allowNavigation to switch between different stages of the app, and this prevents that workflow. Thank you for all of the digging you did, the detailed explanation, and workaround!
from capacitor.
We are experiencing this issue as well, and I cannot thank you enough for having found this workaround 🙇 I can confirm that changing the addDocumentStartJavaScript
call to just use allowedOriginRules
fixes the issues for https requests on local networks. Using the fallback jsInjector
does not however.
I have hacked together a quick "patch" that we call in the MainActivity, to avoid editing the Bridge.Java file directly (as it gets messy with version control). Here it is in case anyone wants to use it (call it after onCreate
and before loading any other pages):
class MainActivity : BridgeActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/**
* ...
*/
patchJSInjection();
}
/**
* ...
*/
private fun patchJSInjection() {
try {
if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
val getJsInjector = bridge::class.java.declaredMethods.single { it.name == "getJSInjector" };
getJsInjector.isAccessible = true;
val injector = getJsInjector.invoke(bridge);
val getScriptString = injector::class.java.declaredMethods.single { it.name === "getScriptString" };
val scriptString = getScriptString.invoke(injector) as String;
val allowedOrigins: MutableSet<String> = mutableSetOf();
// Add origins that the Capacitor JS Bridge should be injected into
allowedOrigins.add("https://www.foo.bar");
WebViewCompat.addDocumentStartJavaScript(bridge.webView, scriptString, allowedOrigins)
}
}catch (e: Exception) {
Log.e("Error", e.message ?: "");
}
}
}
from capacitor.
I have hacked together a quick "patch" that we call in the MainActivity, to avoid editing the Bridge.Java file directly (as it gets messy with version control). Here it is in case anyone wants to use it (call it after
onCreate
and before loading any other pages):
@Vadinci huge thanks for that snippet. I hacked together similar in java:
private void patchJSInjection() {
try {
if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
var getJsInjector = Arrays.stream(bridge.getClass().getDeclaredMethods())
.filter(method -> method.getName().equals("getJSInjector"))
.findFirst()
.get();
getJsInjector.setAccessible(true);
var injector = getJsInjector.invoke(bridge);
var getScriptString = Arrays.stream(injector.getClass().getDeclaredMethods())
.filter(method -> method.getName().equals("getScriptString"))
.findFirst()
.get();
var scriptString = (String) getScriptString.invoke(injector);
var allowedOrigins = Arrays.stream(bridge.getConfig().getAllowNavigation())
.filter(str -> str.contains("yourdomain.com") || str.contains("otherdomain.com"))
.filter(str -> str.contains("https://"))
// WebViewCompat likes things formatted particularly, trim trailing /*
.map(str -> str.replaceAll("/\\*$", ""))
.collect(Collectors.toSet());
Logger.info("patchJSInjection", "Injecting custom rules " + allowedOrigins);
WebViewCompat.addDocumentStartJavaScript(bridge.getWebView(), scriptString, allowedOrigins);
}
} catch (Exception e) {
Logger.error( e.getMessage(), e);
}
}
from capacitor.
I got same issue too.
To resolve this issue, I apply a patch to Bridge.java
after it has been built
const xfs = require('fs/promises')
const { glob } = require('glob')
module.exports = async () => {
const originPatten = `\
// Start the local web server
JSInjector injector = getJSInjector();
if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
String allowedOrigin = appUrl;
Uri appUri = Uri.parse(appUrl);
if (appUri.getPath() != null) {
if (appUri.getPath().equals("/")) {
allowedOrigin = appUrl.substring(0, appUrl.length() - 1);
} else {
allowedOrigin = appUri.toString().replace(appUri.getPath(), "");
}
}
WebViewCompat.addDocumentStartJavaScript(webView, injector.getScriptString(), Collections.singleton(allowedOrigin));
injector = null;
}
localServer = new WebViewLocalServer(context, this, injector, authorities, html5mode);`.split('\n')
const replacePatten = `\
// Patched local web server
JSInjector injector = getJSInjector();
if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
WebViewCompat.addDocumentStartJavaScript(webView, injector.getScriptString(), allowedOriginRules);
injector = null;
}
localServer = new WebViewLocalServer(context, this, injector, authorities, html5mode);`.split('\n')
let modified = false
for (const path of await glob('node_modules/.pnpm/**/Bridge.java')) {
const source = await xfs.readFile(path, 'utf8')
const lines = source.split('\n')
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
if (line.includes('// Start the local web server')) {
if (lines.slice(i, i + originPatten.length).join('\n') !== originPatten.join('\n')) {
console.log('Skipped: Pattern not matched')
continue
}
console.log('Modified: Pattern matched')
lines.splice(i, originPatten.length, ...replacePatten)
modified = true
break
}
}
if (modified) {
await xfs.writeFile(path, lines.join('\n'), 'utf8')
break
}
}
}
from capacitor.
Related Issues (20)
- [Bug]: iOS - Network (?) problems following an iOS update HOT 6
- [Bug]: iOS build fails with ‘Command line “app-store” is deprecated’ HOT 8
- [Bug]: `CapacitorException.code` should be of type string
- [Feature]: add method `getDouble` to Config HOT 2
- [Feature]: Bun monorepo support HOT 1
- [Bug]: Failed to load the ts file of m3u8 video after enabling CapacitorHttp for capacitor HOT 3
- [Bug]: The build is NOT signed ERR_SUBPROCESS_COMMAND_NOT_FOUND HOT 1
- [Bug]: @capacitor-community/barcode-scanner does not show live-camera on Android, Web is ok HOT 2
- [Bug]: HTML5 Audio pauses as soon as it is played - Real IOS Device only HOT 4
- [Bug]: App does not extend to the status bar on Android and safe-area-inset-* do not work. HOT 1
- [Bug]: Dark Mode Does Not Switch Automatically on Android in a Freshly Generated Ionic App HOT 1
- parallélism for capacitor and Android and iOS HOT 2
- bug: Execution failed for task ':capacitor-android:compileDebugJavaWithJavac' / ':capacitor-android:androidJdkImage' HOT 1
- [Bug]: 3xx status code response through error when using it with fetch API
- [Bug]: Android build not including capacitor plugins HOT 2
- [Bug]: Marker tintColor, tittle and snippet not rendering HOT 3
- [Bug]: `npx cap sync ios` does not restart app on iOS 18 real device with Xcode 16 if app is open
- [Feature]: PMTiles Byte Range Request Support
- [Bug]: Fatal Crash on Launch: `SwiftUICore` Library Not Loaded When Using Capacitor 6 with Xcode 16 on iOS 17 and Below
- [Bug]: Serious error executing plugin Background Runner HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from capacitor.