web-view icon indicating copy to clipboard operation
web-view copied to clipboard

`external` becoming undefined after changing pages

Open MGlolenstine opened this issue 5 years ago • 9 comments

Whenever I get redirected browsing the page I'd like (having to login via Google Auth), the external becomes undefined and I'm unable to send data back to Rust.

I'm trying to fetch cookies before and after login, so that I can load them next time and have user pre-logged.

This is the output I have:

arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
arg: GPS=1; PREF=volume=100
CONSOLE ERROR Unrecognized Content-Security-Policy directive 'worker-src'.

https://examplewebsite.com/:1:9: CONSOLE ERROR ReferenceError: Can't find variable: external
https://examplewebsite.com/:1:9: CONSOLE ERROR ReferenceError: Can't find variable: external
https://examplewebsite.com/:1:9: CONSOLE ERROR ReferenceError: Can't find variable: external
https://examplewebsite.com/:1:9: CONSOLE ERROR ReferenceError: Can't find variable: external
https://examplewebsite.com/:1:9: CONSOLE ERROR ReferenceError: Can't find variable: external
https://examplewebsite.com/:1:9: CONSOLE ERROR ReferenceError: Can't find variable: external
https://examplewebsite.com/:1:9: CONSOLE ERROR ReferenceError: Can't find variable: external
https://examplewebsite.com/:1:9: CONSOLE ERROR ReferenceError: Can't find variable: external

The first arg: lines are printing cookies once a second before login. CONSOLE ERROR and a line after that are printed when I open the Google Auth page and after that is the same code that should be outputting arg: , but it's throwing errors, as the external cannot be found.

What am I missing?

This is the full code, replace examplewebsite with any website that uses Google Auth:

use web_view::*;
use std::thread;
use std::time::Duration;
fn main() {
    let webview = WebViewBuilder::new()
        .title("WebView Test")
        .content(Content::Url("https://examplewebsite.com"))
        .size(800, 600)
        .resizable(true)
        .debug(true)
        .user_data(())
        .invoke_handler(|_webview, arg| {
            println!("arg: {}", arg);
            if arg.contains("__Secure"){
                println!("Logged in! {}", arg);
                Ok(())
            }else{
                Ok(())
            }
        })
        .build()
        .unwrap();
    let handle = webview.handle();
    thread::spawn(move || loop {
        {
            handle
                .dispatch(move |webview| {
                    webview.eval("external.invoke(document.cookie);").unwrap();
                    Ok(())
                })
                .unwrap();
        }
        thread::sleep(Duration::from_secs(1));
    });
    webview.run().unwrap();
}

I'm using GTK backend on VoidLinux

MGlolenstine avatar Aug 09 '20 09:08 MGlolenstine

Fixed it by putting

window.external={invoke:function(x){window.webkit.messageHandlers.external.postMessage(x);}};

before any calls to external. It doesn't hurt if there is external there and it works if there is no external assigned.

Explanation: Apparently it's set once and never again. So if we navigate to another website, we'd have to recreate the webview. Instead, we can add that external into the website by running the above JS code to insert it. It'd probably be better to only inject it once the website is changed, but I'm unsure on how to detect that, so, for now, spam is the solution.

MGlolenstine avatar Aug 09 '20 14:08 MGlolenstine

Well I dont think its possible to persist js run through "eval" b/w webpages, You might have to implement Iframes for that. Its the similar case of the commands run in browser console isnt persistent if you navigate elsewhere.

Blakeinstein avatar Aug 13 '20 07:08 Blakeinstein

Well I dont think its possible to persist js run through "eval" b/w webpages, You might have to implement Iframes for that. Its the similar case of the commands run in browser console isnt persistent if you navigate elsewhere.

That's indeed correct, but one would assume that the external would stay between page navigations, as someone might want to use more than just one page in their application. I thought that I was missing reinject_js for reinjecting external, but if that's not the intended way then I can just close this issue.

MGlolenstine avatar Aug 13 '20 07:08 MGlolenstine

I cant test this yet, but could you try window.external.notify(msg) instead?

Blakeinstein avatar Aug 13 '20 07:08 Blakeinstein

I cant test this yet, but could you try window.external.notify(msg) instead?

The problem here is that external is still "undefined". It doesn't matter if it's in the window or not. window.external has been deprecated. I have no idea how much impact this has on the issue, but if I don't set it manually like the above, the external just isn't there. All I do is inject it like the WebView::build() does.

MGlolenstine avatar Aug 13 '20 07:08 MGlolenstine

Ah well this might be a webkit only issue then, external.notify would work on ie/edge.

Blakeinstein avatar Aug 13 '20 07:08 Blakeinstein

Ah well this might be a webkit only issue then, external.notify would work on ie/edge.

Might be. I can check it later, after I'm off-work.

MGlolenstine avatar Aug 13 '20 08:08 MGlolenstine

Hi, I think I am observing that notify does not actually work either, hinting that external is not defined in Edge either.

I'm trying both notify and invoke

addEventListener('DOMContentLoaded', () => {
  window.external.notify({cmd: 'load'});
})

In the edge webview and no message seem to be going to the rust side (I'm validating this by printing something in the invoke_handler callable on the rust side in the Load command branch).

When running the js app directly from Edge instead of within the webview, I get an Uncaught TypeError: window.external.notify is not a function, hence I presume that it is the same problem from within the webview.

Zvax avatar Aug 14 '20 22:08 Zvax

Well the reason this happens is during initialization, webview uses "eval" to add the external.invoke function. upon navigation that is cleared out. We could bind this to an event if the underlying api's have said events, if you do find out anything, put it here.

Blakeinstein avatar Aug 15 '20 06:08 Blakeinstein