WebBrowserComponent:How to run javascript function in webpag

Hi all,

I’m a new juce developer, I added a WebBrowserComponent in my Windows Juce project and loaded html files. I want my app run a javascript function in html file and get the return value. This function is similar to IOS [UIWebView stringByEvaluatingJavaScriptFromString] function, but I don’t know how Juce can support this? Anyone who can give me some suggestion? Thanks

1 Like

I’m also interested on this, I created a WebBrowserComponent to show a pdf and I’d like to trigger with two juce buttons the funcs “next page” “previous page”

use Choc own webview
https://github.com/Tracktion/choc/blob/main/gui/choc_WebView.h

1 Like

wow really thank you!

Sorry But how to add it as child in juce component?

@Ayra You can put it inside JUCE’s HWNDComponent (Windows) / NSViewComponent (Mac) / UIViewComponent (iOS).
And then call setHWND(webView.getViewHandle()) on Windows, and setView(webView.getViewHandle()) on Mac and iOS.

It’s a native view inside a JUCE component hierarchy, so things like transparency and z-layering won’t work. But you would have those limitations with WebBrowserComponent, too.

1 Like

Really thank you :heart::pray:

1 Like

Ok, all works, really thankyou! only last thing … I’d like to pass, as said before, javascript (the only thing i want to do are get num of pages of pdf, get height and width of pdf and set current page of a pdf loaded in), how can I pass variables to javascript and get values in return? I see that I need to call bind and after call evaluateJavascript, I’m on the right road? I don’t really understand how it works… here the code I wrote up to now…

On constructor:

    webView.bind ("getNumOfPages", [this] (const choc::value::ValueView& args) -> choc::value::Value
    {
       //what to do here ??
        return {};
    });
    
    webView.bind ("getWidthOfPDF", [this] (const choc::value::ValueView& args) -> choc::value::Value
    {
       //what to do here ??
        return {};
    });
    
    webView.bind ("getHeightOfPDF", [this] (const choc::value::ValueView& args) -> choc::value::Value
    {
       //what to do here ??
        return {};
    });
    
    webView.bind ("setCurrentPage", [this] (const choc::value::ValueView& args) -> choc::value::Value
    {
        //what to do here ??
        return {};
    });

and an example of func:

void PdfViewer::setCurrentPage(int newPageNumber)
{

    //is it right this ? 
    webView.evaluateJavascript(R"xxx(

        <script>
              setCurrentPage(newPageNumber);
        </script>

        
    )xxx");
}
 auto json = choc::value::createObject("notify",
                                        "type", "unknownError",
                                        "error", 23);
  std::string script = fmt::format("notify({})", choc::json::toString(json));
  webview->evaluateJavascript(script);

and in your bind
checks the args using the method on ValueView like toString or getInt64

This is a little more complicated. In your JavaScript, instead of returning a value, you need to call back to a C++ function that you’ve bound using WebView’s bind. Pass the value you want to return to C++ as a parameter to that function. Then in C++, your C++ lambda gets called, and the choc::value::ValueView args is the “returned” value from JavaScript.

Note that this will be asynchronous :neutral_face: Meaning: When your call to evaluateJavascript() returns, your C++ lambda (from bind()) hasn’t yet been called, so the “return” value isn’t there yet. Adjust your code to be async and continue inside your lambda, once that gets called.

1 Like

Unfortunately my problems start before this …

if I use navigate(“”) I cannot have the structure of document to call from javascript functions on it …
while if I use for example pdf is not shown in view :

webView.setHTML (R"xxx(
      <!DOCTYPE html> <html>
        <head> <title>Page Title</title> </head>
        <body>
         <object data=)xxx" + newPath.toStdString() + "R"xxx( type="application/pdf" width="100%" height="100%">
         </object>
        </body>
      </html>
    )xxx");

I never imagined that opening a PDF and changing the pages of this by code would be so complicated…

The WebView probably can’t access local files like that, because that would be “cross-origin”. (Google it if you need more info :slightly_smiling_face: )

Instead, set the WebView config’s fetchResource callback. Now whenever your HTML loads a file (e.g. .css, .js or .pdf), this C++ callback gets called. So your C++ code needs to return the data (in your case, the PDF as bytes) as well as the MIME type such as application/pdf

You can set a breakpoint in your fetchResource callback, to check that it’s being called for the PDF.

1 Like