wxWebView run JavaScript code in iframes? Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
sommi
In need of some credit
In need of some credit
Posts: 5
Joined: Sat Aug 27, 2022 9:58 pm

wxWebView run JavaScript code in iframes?

Post by sommi »

Hello,

im using wxWebView with Edge backend. Is it possible to execute javascript code and getting elements inside an iframe?
I can do this with EnableAccessToDevTools(), switch context and write code in console.
With RunScriptAsync() i get error message: TypeError: Cannot read properties of undefined (reading 'children')
(im using wxWidgets 3.2.2.1)

Greets
OS: Windows 11
Compiler: mingw-w64-i686
wxWidgets: 3.2.2.1
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxWebView run JavaScript code in iframes?

Post by doublemax »

That should be possible. I guess you're executing the JS code too early, but without seeing code, i can't tell more.
Use the source, Luke!
sommi
In need of some credit
In need of some credit
Posts: 5
Joined: Sat Aug 27, 2022 9:58 pm

Re: wxWebView run JavaScript code in iframes?

Post by sommi »

Hey, thank you for fast reply!

Here is my main frame code:

Code: Select all

WebViewFrame::WebViewFrame(wxWindow* parent,wxWindowID id)
{
    ...
    ...
    WebView = wxWebView::New(this, wxID_ANY);
    WebView -> LoadURL("https://www.example.com/en/smtng/");
    BoxSizer1 -> Add(WebView, 1, wxALL|wxEXPAND);

    WebView -> EnableAccessToDevTools();

    WebView -> Bind(wxEVT_WEBVIEW_LOADED, &WebViewFrame::Loaded, this);
    WebView -> Bind(wxEVT_WEBVIEW_SCRIPT_RESULT, &WebViewFrame::ScriptHandler, this);
}
Here are my Handlers:

Code: Select all

void WebViewFrame::ScriptHandler(wxWebViewEvent& event){
    std::cout << event.GetString() << "\nError: " << event.IsError() << std::endl;
}

void WebViewFrame::Loaded(wxWebViewEvent& event){
    std::cout << "Document loaded\n";
}
Here im running RunScriptAsync() from another dialog when i press a button:

Code: Select all

void Dia::OnButton1Click(wxCommandEvent& event){
    main -> WebView -> RunScriptAsync("document.getElementsByClassName(\"btn\")[0].children[0].children[0].innerHTML", data);
}
I cant show the website URL unfortunately as its for my work, but it looks something like this:
efrsw5hfdtfhdfgew.png
efrsw5hfdtfhdfgew.png (12.54 KiB) Viewed 622 times

The website is completly loaded when i try to run the script.
Inside dev tools i can just switch the context to example-iframe.com and run my JS code from console.

To be honest I dont have much knowledge about web development, i hope my question is not too stupid :?

Best regards
OS: Windows 11
Compiler: mingw-w64-i686
wxWidgets: 3.2.2.1
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxWebView run JavaScript code in iframes?

Post by doublemax »

I don't know too much about web dev either, but Google lets me believe that you can't access elements in an iframe directly that way.

Try this:
https://www.w3schools.com/howto/howto_j ... iframe.asp
Use the source, Luke!
sommi
In need of some credit
In need of some credit
Posts: 5
Joined: Sat Aug 27, 2022 9:58 pm

Re: wxWebView run JavaScript code in iframes?

Post by sommi »

Im getting Uncaught DOMException: Blocked a frame with origin "https://example.com" from accessing a cross-origin frame.
probably because i cant switch to right execution context!

But i found something in Win32 api:
https://learn.microsoft.com/en-us/micro ... cutescript
A WebView2 app can run any JavaScript in a frame, by using ExecuteScript.

In order for script to be run in an iframe, an execution context must be created. An execution context is created after the ContentLoading event, that's why if ExecuteScript is called before the ContentLoading event is raised, the script will not be run and the string null will be returned.

For information about the ContentLoading event, see Navigation events for WebView2 apps, which is valid for frames as well as webpages.
It should be possible to call ICoreWebView2Frame2::ExecuteScript and run JS code inside the right context.
https://learn.microsoft.com/en-us/micro ... cutescript

Code: Select all

    wil::com_ptr<ICoreWebView2_4> webview2_4 = m_webView.try_query<ICoreWebView2_4>();
    if (webview2_4)
    {
        CHECK_FAILURE(webview2_4->add_FrameCreated(
            Callback<ICoreWebView2FrameCreatedEventHandler>(
                [](ICoreWebView2* sender, ICoreWebView2FrameCreatedEventArgs* args) -> HRESULT {
                    wil::com_ptr<ICoreWebView2Frame> webviewFrame;
                    CHECK_FAILURE(args->get_Frame(&webviewFrame));
                    wil::com_ptr<ICoreWebView2Frame2> frame2 =
                        webviewFrame.try_query<ICoreWebView2Frame2>();
                    if (frame2)
                    {
                        frame2->add_DOMContentLoaded(
                            Callback<ICoreWebView2FrameDOMContentLoadedEventHandler>(
                                [](ICoreWebView2Frame* frame,
                                   ICoreWebView2DOMContentLoadedEventArgs* args) -> HRESULT {
                                    wil::com_ptr<ICoreWebView2Frame2> frame2;
                                    frame->QueryInterface(IID_PPV_ARGS(&frame2));
                                    frame2->ExecuteScript(
                                        LR"~(
                                        let content = document.createElement("h2");
                                        content.style.color = 'blue';
                                        content.textContent = "This text was added to the iframe by the host app";
                                        document.body.appendChild(content);
                                        )~",
                                        Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
                                            [](HRESULT error, PCWSTR result) -> HRESULT {
                                                // Handle ExecuteScript error and result here if needed
                                                // or pass nullptr as callback parametr otherwise.
                                                return S_OK;
                                            })
                                            .Get());
                                    return S_OK;
                                })
                                .Get(),
                            NULL);
                    }
                    return S_OK;
                })
                .Get(),
            &m_frameCreatedToken));
    }
This code looks terrible, but i think i have to switch to native windows api unfortunately.
Maybe wxDevs can push something like this in next major update :D

best regards

Edit: actually it works with win api
OS: Windows 11
Compiler: mingw-w64-i686
wxWidgets: 3.2.2.1
Post Reply