SpiderEye icon indicating copy to clipboard operation
SpiderEye copied to clipboard

Make ExecuteScriptAsync visible from WebviewBridge

Open TamaBaka opened this issue 3 years ago • 1 comments

There is an interesting segment of code in SpiderEye.Bridge.WebviewBridge:

        public async Task InvokeAsync(string id, object data)
        {
            string script = GetInvokeScript(id, data);
            string? resultJson = await Application.Invoke(() => Webview.ExecuteScriptAsync(script));
            ResolveEventResult(id, resultJson);
        }

I'm actually using webview for automation instead of creating an interactable custom webpage so I found the ExecuteScriptAsync feature useful on the Windows Webview. Except I was trying to port my code to a Raspberry Pi and Webview currently doesn't work on Linux.

Given the code segment above, I think it might be possible to reproduce that behavior here if we could directly inject a custom javascript string instead of searching for a pre-registered function. I.e. skip the GetInvokeScript step and the ResolveEventResult step and just return resultJson directly.

Is this not done because the intent of the bridge was to work with well-defined endpoints? Or is it because calling ExecuteScriptAsync directly is inherently unstable and creates all sorts of unpredictable side effects? Did I happen to miss a function that actually does what I'm requesting already?

Bear in mind that I don't control the endpoint, so I can't insert custom code into the destination webpage. And I'm not desperate enough to encapsulate the endpoint into an iFrame just so I can embed tools in the parent container to manipulate whatever's in the iFrame.

That being said, here's an example of my code when I directly used Webview2.

                        var script = $@"
                                document.querySelector(""input[name='USER']"").value = ""{ kvp[0] }"";
                                document.querySelector(""input[name='PASSWORD']"").value = ""{ kvp[1] }"";
                                document.querySelector(""#logon_button"").click();
                        ";

                        await signInView.ExecuteScriptAsync(script);

Set a user, set a password, then click a specific button. Wait for page loaded event to execute further actions.

TamaBaka avatar Jun 23 '22 03:06 TamaBaka

Oh neat, it worked. Just need to clone the code, add the following to WebviewBridge

        public async Task<string?> ExecuteScriptAsync(string script)
        {
            return await Application.Invoke(() => Webview.ExecuteScriptAsync(script));
        }

Then add a definition to IWebviewBridge

Task<string?> ExecuteScriptAsync(string script);

Recompile and use the updated SpiderEye.Core library and now you can use window.Bridge.ExecuteScriptAsync

Tested the below simple case on Windows and Linux.

               window.LoadUrl("https://www.google.com");
              
              window.EnableDevTools = true;
              
                var stage = "1";        // simple state machine to prevent an infinite pageloaded event loop

                window.PageLoaded += async (s, e) =>
                {
                    if (stage == "1")
                        await window.Bridge.ExecuteScriptAsync($@"
                            console.log(""Hello World"");
                            document.querySelectorAll('input[title=""Search""]')[0].value = ""Hello World"";
                            document.querySelectorAll('input[value=""Google Search""]')[0].click();");

                    stage = "2";

                };

Stability of this change is unknown but it works on the simple stuff.

First time I've ever had to create a nullable string though. That's just weird when string is nullable already.

TamaBaka avatar Jun 24 '22 03:06 TamaBaka