Search code examples
iosjavafxwebviewgluongluon-mobile

JavaFX WebView - How to capture the URL loading?


I am currently developing an application with GluonHQ (JavaFXPorts) where I am using the WebView to load some Internet pages. When I am loading the https://signin.ebay.com/ page I see the following messages on the console

[WebEngine] I can handle this protocol for https://signin.ebay.com/
ES2ResourceFactory: Prism - createStockShader: AlphaTexture_RadialGradient.frag
[WebEngine] I can handle this protocol for https://www.ebay.com/ws/
[WebEngine] I can't handle this protocol for about:blank
[WebEngine] I can handle this protocol for https://www.ebay.com/t_n.html?org_id=usllpic0&session_id=f5b8d48d1670a9cce3431193fffe9431&suppressFlash=true
[WebEngine] I can't handle this protocol for about:blank
2018-12-28 18:49:53 Watchdog[2021:302159] WF: _userSettingsForUser mobile: {
    filterBlacklist =     (
    );
    filterWhitelist =     (
    );
    restrictWeb = 1;
    useContentFilter = 0;
    useContentFilterOverrides = 0;
    whitelistEnabled = 0;
}
2018-12-28 18:49:53 Watchdog[2021:302159] WF: _WebFilterIsActive returning: NO
2018-12-28 18:49:55 Watchdog[2021:302159] CoreAnimation: [EAGLContext renderbufferStorage:fromDrawable:] was called from a non-main thread in an implicit transaction! Note that this may be unsafe without an explicit CATransaction or a call to [CATransaction flush].
[WebEngine] I can handle this protocol for https://src.ebay-us.com/fp/top_fp.html;CIS3SID=08561C52B9A266EE68861D27BCE99A74?org_id=usllpic0&session_id=f5b8d48d1670a9cce3431193fffe9431&nonce=b6190f233a143165

It seems that the app calls the load of other sites as well, but I cannot find a way of capturing them. I have tried also to get the location when the state is changed

webEngine.getLoadWorker().stateProperty().addListener((ov, oldState, newState) -> {
    System.out.println("State(" + newState + "): " + webEngine.getLocation());
}

but without any success. It seems that the location is null until the site is first time loaded, and then original URL is returned, i.e. https://signin.ebay.com. I believe that the extra "protocols" appearing are part of AJAX. Anyone has an idea on how to implement a listener to capture those?

[WebEngine] I can handle this protocol for https://signin.ebay.com/
State(SCHEDULED): null
State(RUNNING):   null
ES2ResourceFactory: Prism - createStockShader: AlphaTexture_RadialGradient.frag
[WebEngine] I can handle this protocol for https://www.ebay.com/ws/
[WebEngine] I can't handle this protocol for about:blank
State(SCHEDULED): null
State(RUNNING):   null
State(SUCCEEDED): null
[WebEngine] I can handle this protocol for https://www.ebay.com/t_n.html?org_id=usllpic0&session_id=f5c7ab7f1670a9ccaac210b2ffe8709e&suppressFlash=true
State(SCHEDULED): https://signin.ebay.com
State(RUNNING):   https://signin.ebay.com
[WebEngine] I can't handle this protocol for about:blank
State(SCHEDULED): https://signin.ebay.com
State(RUNNING):   https://signin.ebay.com
State(SUCCEEDED): https://signin.ebay.com
2018-12-28 19:06:05 Watchdog[2050:306357] WF: _userSettingsForUser mobile: {
    filterBlacklist =     (
    );
    filterWhitelist =     (
    );
    restrictWeb = 1;
    useContentFilter = 0;
        useContentFilterOverrides = 0;
        whitelistEnabled = 0;
    }
2018-12-28 19:06:05 Watchdog[2050:306357] WF: _WebFilterIsActive returning: NO
2018-12-28 19:06:09 Watchdog[2050:306357] CoreAnimation: [EAGLContext renderbufferStorage:fromDrawable:] was called from a non-main thread in an implicit transaction! Note that this may be unsafe without an explicit CATransaction or a call to [CATransaction flush].
[WebEngine] I can handle this protocol for https://src.ebay-us.com/fp/top_fp.html;CIS3SID=D553152B77DCBE35641E606BDAB6AFDA?org_id=usllpic0&session_id=f5c7ab7f1670a9ccaac210b2ffe8709e&nonce=f3e7bb25c25eb5c1
State(SCHEDULED): https://signin.ebay.com
State(RUNNING):   https://signin.ebay.com
State(SUCCEEDED): https://signin.ebay.com

Solution

  • As a follow-up of this similar question, where it was stated that:

    With JavaFXPorts, on mobile (both Android and iOS) the WebView control is not the JavaFX built-in control but the native control

    You can see this method in the native implementation:

    - (BOOL)webView:(UIWebView *)wv shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
        NSString *url = [[request URL] absoluteString];
        JNIEnv *env = [self getJNIEnv];
        if (env != NULL) {
            jstring jUrl = createJString(env, url);
            (*env)->CallVoidMethod(env, jObject, jmidHasAppProtocolHandler, jUrl);
        ...
    }
    

    that goes into the JavaFX WebEngine callback:

    /**
     * check if there is a handler registered for dealing with this protocol.
     */
    boolean hasAppProtocolHandler(String url) {
        boolean answer = false;
        try {
            URL u = newURL(null, url);
            System.out.println ("[WebEngine] I can handle this protocol for "+url);
            u.openConnection();
            // location.set(url);
            answer = true;
        }
        // catch (MalformedURLException e) {
        catch (Exception e) {
            System.out.println ("[WebEngine] I can't handle this protocol for "+url);
            // no handler known for this protocol
        }
        return answer;
    }
    

    This explains the console printouts: when the URL can be opened, or when there is an exception. All the different results seem to be related to how the native browser handles the URL loading, but you get just an echo in the JavaFX layer.

    About using WebEngine::getLocation, note that only when the native browser finishes loading an Url, the notification is sent back and the location is set:

    void notifyLoadFinished(String loc, String content) {
        synchronized (loadedLock) {
            this.pageContent = "<html>"+content+"</html>";
            loaded = true;
            updateProgress(1.0);
            updateState(Worker.State.SUCCEEDED);
            location.set(loc); // <--- location from native browser is set
            document.invalidate(true);
            if (pageListener != null) {
                pageListener.onLoadFinished();
            }
        }
    }
    

    I'd suggest you have a look at the JavaFX code for WebView and WebEngine for the iOS platform, to see what can and can't be done.

    For instance, the script engine could work, so you might be able to execute some script that will report some information back.