Coder Social home page Coder Social logo

Comments (6)

Dylancyclone avatar Dylancyclone commented on September 26, 2024 7

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:

image

Investigating this issue, we found the root cause to be the JavaScript injection in Bridge.java. We notice that if WebViewFeature.DOCUMENT_START_SCRIPTis 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:

// 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);

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.

rfe-css avatar rfe-css commented on September 26, 2024 2

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:

image

i also recommend using https://www.npmjs.com/package/patch-package for all these patches until it is being resolved.

from capacitor.

danbritt avatar danbritt commented on September 26, 2024

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.

Vadinci avatar Vadinci commented on September 26, 2024

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.

jarredhawkins avatar jarredhawkins commented on September 26, 2024

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.

Cow258 avatar Cow258 commented on September 26, 2024

I got same issue too.

image
image

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.