WebView2

Post your working scripts, libraries and tools.
User avatar
thqby
Posts: 560
Joined: 16 Apr 2021, 11:18
Contact:

WebView2

16 Oct 2021, 09:31

The Microsoft Edge WebView2 control enables you to host web content in your application using Microsoft Edge (Chromium) as the rendering engine. For more information, see Overview of Microsoft Edge WebView2 and Getting Started with WebView2.

The WebView2 Runtime is built into Win10(latest version) and Win11 and can be easily used in AHK.

Download on GitHub

You can download the latest version of WebView2Loader.dll from nuget.org, which is a nuget package that you can change its file suffix to .zip and open it.

v2.0 changes
  • The Asynchronous method will have the Async suffix, such as ExecuteScriptAsync, and return the Promise after the call.
  • The add_event method accepts an ahk callable object with two minimum parameters, and it has a method named event, which returns the object and cancels the registration event after the object is destructed.
limitation
  • can't wait for the asynchronous method to return the result in the event callback of webview2.
    Refer to threading model.
Last edited by thqby on 13 Sep 2024, 22:52, edited 8 times in total.
User avatar
thqby
Posts: 560
Joined: 16 Apr 2021, 11:18
Contact:

Re: 2.0-beta.1 - WebView2

18 Oct 2021, 07:00

Example1: AddHostObjectToEdge, Open with multiple windows

Code: Select all

#Include <WebView2\WebView2>

main := Gui()
main.OnEvent('Close', (*) => (wvc := wv := 0))
main.Show(Format('w{} h{}', A_ScreenWidth * 0.6, A_ScreenHeight * 0.6))

wvc := WebView2.CreateControllerAsync(main.Hwnd).await2()
wv := wvc.CoreWebView2
wv.Navigate('https://autohotkey.com')
wv.AddHostObjectToScript('ahk', {str:'str from ahk',func:MsgBox})
wv.OpenDevToolsWindow()
Run code in Edge DevTools

Code: Select all

obj = await window.chrome.webview.hostObjects.ahk;
obj.func('call from edge\n' + (await obj.str));
obj = window.chrome.webview.hostObjects.sync.ahk;
obj.func('call from edge\n' + obj.str);
Example2: Open with only one Tab

Code: Select all

#Include <WebView2\WebView2>

main := Gui()
main.OnEvent('Close', (*) => ExitApp())
main.Show(Format('w{} h{}', A_ScreenWidth * 0.6, A_ScreenHeight * 0.6))

wvc := WebView2.CreateControllerAsync(main.Hwnd).await2()
wv := wvc.CoreWebView2
nwr := wv.NewWindowRequested(NewWindowRequestedHandler)
wv.Navigate('https://autohotkey.com')

NewWindowRequestedHandler(wv2, arg) {
	deferral := arg.GetDeferral()
	arg.NewWindow := wv2
	deferral.Complete()
}
Example3: Open with multiple Tabs in a window and Auto resize

Code: Select all

#Include <WebView2\WebView2>

main := Gui('+Resize'), main.MarginX := main.MarginY := 0
main.OnEvent('Close', _exit_)
main.OnEvent('Size', gui_size)
tab := main.AddTab2(Format('w{} h{}', A_ScreenWidth * 0.6, A_ScreenHeight * 0.6), ['tab1'])
tab.UseTab(1), tabs := []
tabs.Push(ctl := main.AddText('x0 y25 w' (A_ScreenWidth * 0.6) ' h' (A_ScreenHeight * 0.6)))
tab.UseTab()
main.Show()
ctl.wvc := wvc := WebView2.CreateControllerAsync(ctl.Hwnd).await2()
wv := wvc.CoreWebView2
ctl.nwr := wv.NewWindowRequested(NewWindowRequestedHandler)
wv.Navigate('https://autohotkey.com')

gui_size(GuiObj, MinMax, Width, Height) {
	if (MinMax != -1) {
		tab.Move(, , Width, Height)
		for t in tabs {
			t.move(, , Width, Height - 23)
			try t.wvc.Fill()
		}
	}
}

NewWindowRequestedHandler(wv2, arg) {
	deferral := arg.GetDeferral()
	tab.Add(['tab' (i := tabs.Length + 1)])
	tab.UseTab(i), tab.Choose(i)
	main.GetClientPos(, , &w, &h)
	tabs.Push(ctl := main.AddText('x0 y25 w' w ' h' (h - 25)))
	tab.UseTab()
	wv2.Environment.CreateCoreWebView2ControllerAsync(ctl.Hwnd).then(ControllerCompleted)
	ControllerCompleted(wvc) {
		ctl.wvc := wvc
		arg.NewWindow := wv := wvc.CoreWebView2
		ctl.nwr := wv.NewWindowRequested(NewWindowRequestedHandler)
		deferral.Complete()
	}
}

_exit_(*) {
	for t in tabs
		t.wvc := t.nwr := 0
	ExitApp()
}
Example4: PrintToPdf and wait for asynchronous event

Code: Select all

#Include <WebView2\WebView2>

main := Gui()
main.Show('w800 h600')
wvc := WebView2.CreateControllerAsync(main.Hwnd).await2()
wv := wvc.CoreWebView2
wv.Navigate('https://autohotkey.com')
MsgBox('Wait for loading to complete')
PrintToPdf(wv, A_ScriptDir '\11.pdf')

PrintToPdf(wv, path) {
	set := wv.Environment.CreatePrintSettings()
	set.Orientation := WebView2.PRINT_ORIENTATION.LANDSCAPE
	waitting := true, t := A_TickCount
	try {
		wv.PrintToPdfAsync(A_ScriptDir '\11.pdf', set).await2(5000)
		Run(A_ScriptDir '\11.pdf')
		MsgBox('PrintToPdf complete')
	} catch TimeoutError
		MsgBox('PrintToPdf timeout')
}
Example5: Modify web resource request

Code: Select all

#Include <WebView2\WebView2>

main := Gui()
main.Show('w800 h600')
wvc := WebView2.CreateControllerAsync(main.Hwnd).await2()
wv := wvc.CoreWebView2
wv.AddWebResourceRequestedFilter('https://autohotkey.com/*', 0)
wv.add_WebResourceRequested(handler)
wv.Navigate('https://autohotkey.com')
handler(wv, args) {
	StrPut(s := 'hello ahk<br>url: ' args.Request.Uri, buf := Buffer(StrPut(s, 'utf-8') - 1), 'utf-8')
	stream := WebView2.CreateMemStream(buf)
	args.Response := wv.Environment.CreateWebResourceResponse(stream, 200, 'OK', 'content-type: text/html; charset=utf-8')
}
Last edited by thqby on 08 Oct 2024, 01:54, edited 8 times in total.
tuzi
Posts: 223
Joined: 27 Apr 2016, 23:40

Re: 2.0-beta.1 - WebView2

18 Oct 2021, 08:42

collecting! :bravo: :bravo: :bravo: :bravo: :bravo:
Tre4shunter
Posts: 139
Joined: 26 Jan 2016, 16:05

Re: 2.0-beta.1 - WebView2

19 Oct 2021, 17:12

@thqby,

This is amazing work! It works great - and im so excited to start building with it!

Any plans to implement:
https://docs.microsoft.com/en-us/dotnet ... ntSettings_

I would be forever in your debt if you could make that possible!

-tre4
User avatar
thqby
Posts: 560
Joined: 16 Apr 2021, 11:18
Contact:

Re: 2.0-beta.1 - WebView2

19 Oct 2021, 22:34

ICoreWebView2Experimental7::PrintToPdf method is implemented in this version 1.0.1018-prerelease.
It is intended to be updated after the official release.
20170201225639
Posts: 144
Joined: 01 Feb 2017, 22:57

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 00:42

Very nice.
Does WebView2 have any current limitations as compared with the IE webbrowser control?
User avatar
thqby
Posts: 560
Joined: 16 Apr 2021, 11:18
Contact:

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 06:05

You need to install dependencies, such as edge dev or edge runtime, to support systems above win7.
In win11, runtime is built-in.
In latest win10, runtime may be pre installed by other software. Office 365 began using webview2 to render some gui.
IE webview may not support some modern web technologies, while webview2 does not.
User avatar
kczx3
Posts: 1677
Joined: 06 Oct 2015, 21:39

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 07:55

Have you figured out working with AHK arrays in JavaScript via AddHostObjectToScript?
Tre4shunter
Posts: 139
Joined: 26 Jan 2016, 16:05

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 08:24

@kczx3 -

What do you mean by that? Im able to send arrays from JS -> ahk and vice versa using this lib(as JSON strings)
User avatar
kczx3
Posts: 1677
Joined: 06 Oct 2015, 21:39

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 08:43

Sending via JSON is unrelated to sharing of host objects via the AddHostObjectToScript method. JSON is serialized and reconstructed instead of sending an AHK array directly.
Tre4shunter
Posts: 139
Joined: 26 Jan 2016, 16:05

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 08:57

Understood. What would the major benefits be of accessing the ahk arrays directly? Performance? I suppose for my use cases serializing the data is more than sufficient.
User avatar
kczx3
Posts: 1677
Joined: 06 Oct 2015, 21:39

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 09:00

I think one benefit would being able to modify the AHK array from the JS side since its sent ByRef.
20170201225639
Posts: 144
Joined: 01 Feb 2017, 22:57

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 10:25

thqby wrote:
20 Oct 2021, 06:05
You need to install dependencies, such as edge dev or edge runtime, to support systems above win7.
In win11, runtime is built-in.
In latest win10, runtime may be pre installed by other software. Office 365 began using webview2 to render some gui.
IE webview may not support some modern web technologies, while webview2 does not.
Thanks for the explanation.
20170201225639
Posts: 144
Joined: 01 Feb 2017, 22:57

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 10:39

Tre4shunter wrote:
20 Oct 2021, 08:57
Understood. What would the major benefits be of accessing the ahk arrays directly? Performance? I suppose for my use cases serializing the data is more than sufficient.
kczx3 wrote:
20 Oct 2021, 09:00
I think one benefit would being able to modify the AHK array from the JS side since its sent ByRef.
I've tried to get this to work with IE browser control as I needed to reflect some data back into the DOM, motivated by the prospect of increased performance. In the end, it turns out that in my case at least, direct DOM manipulation is more performant when done directly from AHK than from JS, even setting aside the time needed to stringify in AHK and to parse in JS. The exact same set of createElement / appendChild / setAttribute operations take less time to complete in AHK than in JS

Also, FWIW, when I use ObjRegisterActive to share an array between 2 ahk scripts, hoping script A can pass the array to script B and say "do some computation on this so I can be freed to do something else", I found that there's actually a decrease in overall performance, because apparently each time script B accesses an item from the array this is forwarded as a call back to script A, so script A is not actually "freed to do something else". I wonder if that would also be the case if JS accesses the shared array?
User avatar
kczx3
Posts: 1677
Joined: 06 Oct 2015, 21:39

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 11:11

Unless you specifically access the data from the host synchronously, everything from the JS side is asynchronous. The host script obviously has to handle the property get/set or method call though. At some level, the host isn't entirely "freed".
20170201225639
Posts: 144
Joined: 01 Feb 2017, 22:57

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 11:40

It looks like one limitation of Webview2 compared to the IE control is .... there's no (native) DOM manipulation any more.

https://github.com/MicrosoftEdge/WebView2Feedback/issues/176
Oops, no DOM access anymore? (Last time I used this was with the IE-based control. I only come back to this when there's a proper HTML engine behind this, what WebView2 seems to be.) I need to disable JavaScript on the content for security reasons. How can I edit and manipulate the HTML content then? Is this impossible? If I wanted to use JavaScript to manipulate the HTML on the page, I'd be using Electron or another framework that lets me write my application in JavaScript altogether.
Last edited by 20170201225639 on 20 Oct 2021, 12:00, edited 1 time in total.
20170201225639
Posts: 144
Joined: 01 Feb 2017, 22:57

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 11:51

kczx3 wrote:
20 Oct 2021, 11:11
Unless you specifically access the data from the host synchronously, everything from the JS side is asynchronous. The host script obviously has to handle the property get/set or method call though. At some level, the host isn't entirely "freed".
Thanks. Yes, the JS side computation on the array items is asychronous, it's the accessing of the items that isn't. I do wonder: is there a way, theoretically, to free the host from having to handle get operation for built-in data structures like arrays?

I can see how for a custom object the host script must handle the get calls, because the client script doesn't know how otherwise. But if both host and client are AHK scripts they should both understand arrays from the get-go.

(I wonder about this, because otherwise the performance costs involved in repeated get calls during loops would mean there's a lot less use cases for object sharing between scritps at least for me personally)
Tre4shunter
Posts: 139
Joined: 26 Jan 2016, 16:05

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 12:20

I modifed the Neutron Bootstrap Example just as an exercise to see if i could 'replicate' how it worked. This is what i came up with.

Please correct anything i did grossly incorrect/inneficiently as i am still very green with the whole Webview2 and ahkV2 syntaxes. It does work though.

ah2

Code: Select all


Persistent
#SingleInstance
#Include "<JSON>"
#Include WebView2.ahk
  
main := Gui('+Resize'), main.MarginX := main.MarginY := 0
main.Title := "Webview2 Test"
main.OnEvent('Size', gui_size)

main.OnEvent('Close', (*) => (wvc := wv := 0))
main.Show(Format('w{} h{}', A_ScreenWidth * 0.6, A_ScreenHeight * 0.6))

wvc := WebView2.create(main.Hwnd, , 0, "", A_ScriptDir "\Runtime")
main.GetClientPos(,,&w,&h)
ctl := main.AddText('x0 y25 w' w ' h' (h - 25))

wv := wvc.CoreWebView2

wv.Navigate('file:///' A_ScriptDir '\bootstrap.html')

boundbtns := ButtonsFromHTML.Bind()
boundform := FormFromHTML.Bind()
wv.AddHostObjectToScript('btnsToAHK', {Func:boundbtns})
wv.AddHostObjectToScript('formdataToAHK', {Func:boundform})

ButtonsFromHTML(p*){
    msgbox('You clicked: ' p[1])
}
FormFromHTML(p*){
	rVal := p[1]
	items := JxonDecode(&rVal)
	for k,v in items
	{
		if(v)
			msgbox(k "`n" v)
	}
	msgbox('Now lets send some data From AHK to JS.')
	
	testArr:=['one','two','three']
    wv.ExecuteScript("alert(" JxonEncode(testArr) ")",myHandler(wv,arg:=0))   
    return 0
    myHandler(wv,arg){
        return(0)
    }
}

gui_size(GuiObj, MinMax, Width, Height) {
	if (MinMax != -1) {
		try ctl.Move(, , Width, Height - 23)
        try wvc.Fill()
	}
}

_exit_(*) {
	ExitApp()
}


html

Code: Select all

<!DOCTYPE html>
<html>

<head>

  <!--
    The IE Compatibility flag, while not always necessary, makes sure your page
    will always load in IE11+ rendering mode instead of being limited to IE7
    features and behaviors. While IE11 is still relatively old as a rendering
    engine, it supports the most fundamental parts needed for modern webpages to
    function correctly.

    Neutron will do its best to enable IE11+ rendering mode regardless of this
    tag, but in current releases pages will not render correctly when compiled
    unless this tag is present. Because of that and other edge cases, any page
    you write to load in Neutron should contain this meta tag.
  -->
  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <link href="bootstrap.min.css" rel="stylesheet">
  <script src="jquery.min.js"></script>
  <script src="bootstrap.min.js"></script>

  <style>
    html,
    body {
      width: 100%;
      height: 100%;
    }

    .title-btn {
      padding: 0.35em 1.0em;
      cursor: pointer;
      vertical-align: bottom;
      font-family: Webdings;
      font-size: 11pt;
    }

    .title-btn:hover {
      background: rgba(0, 0, 0, .2);
    }

    .title-btn-close:hover {
      background: #dc3545;
    }
  </style>
</head>

<body class="d-flex flex-column">

  <!-- Title Bar 
  <header>
    <div class="d-flex align-items-stretch bg-dark text-white">
      <span class="flex-grow-1 px-2 py-1" onmousedown="neutron.DragTitleBar()">AutoHotkey</span>
      <span class="title-btn" onclick="neutron.Minimize()">0</span>
      <span class="title-btn" onclick="neutron.Maximize()">1</span>
      <span class="title-btn title-btn-close" onclick="neutron.Close()">r</span>
    </div>
  </header>
  End Title Bar -->

  <!-- Window Contents -->
  <div class="flex-grow-1 d-flex">

    <!-- Sidebar -->
    <div class="nav flex-column nav-pills bg-dark text-light" id="v-pills-tab" role="tablist"
      aria-orientation="vertical">
      <a class="nav-link text-light active" id="v-pills-home-tab" data-toggle="pill" href="#v-pills-home" role="tab"
        aria-controls="v-pills-home" aria-selected="true">Home</a>
      <a class="nav-link text-light" id="v-pills-buttons-tab" data-toggle="pill" href="#v-pills-buttons" role="tab"
        aria-controls="v-pills-buttons" aria-selected="false">Buttons</a>
      <a class="nav-link text-light" id="v-pills-forms-tab" data-toggle="pill" href="#v-pills-forms" role="tab"
        aria-controls="v-pills-forms" aria-selected="false">Forms</a>
      <a class="nav-link text-light" id="v-pills-settings-tab" data-toggle="pill" href="#v-pills-settings" role="tab"
        aria-controls="v-pills-settings" aria-selected="false">Settings</a>
    </div>
    <!-- End Sidebar -->

    <!-- Page Content -->
    <div class="flex-grow-1 overflow-auto">
      <div class="tab-content container p-2" id="v-pills-tabContent">

        <!-- v-pills-home -->
        <div class="tab-pane fade show active" id="v-pills-home" role="tabpanel" aria-labelledby="v-pills-home-tab">
          <h1>Welcome to Neutron</h1>
          <p>
            Neutron provides a powerful set of tools for build HTML-based user interfaces with AutoHotkey. It leverages
            the Trident engine, known for its use in Internet Explorer, because of its deep integration with the
            Microsoft Windows operating system and its wide availability across systems.
          </p>
          <p>
            This example is designed to show how you can use third party frameworks like Bootstrap to build advanced
            user interfaces, while still keeping all the code local. This script can be compiled and still function
            fine without the need to extract any files to a temporary directory.
          </p>
          <p>
            As this example is more advanced, it assumes a stronger familiarity with the technology and may gloss over
            some parts more than other examples. If you're just getting started it may be helpful to work with some of
            the other example scripts first.
          </p>
        </div>
        <!-- End v-pills-home -->

        <!-- v-pills-buttons -->
        <div class="tab-pane fade" id="v-pills-buttons" role="tabpanel" aria-labelledby="v-pills-buttons-tab">
          <h1>Buttons!</h1>

          <h2>Colored Buttons:</h2>
          <div class="btns_to_trigger_ahk">
            <button type="button" class="btn btn-primary">Primary</button>
            <button type="button" class="btn btn-secondary">Secondary</button>
            <button type="button" class="btn btn-success">Success</button>
            <button type="button" class="btn btn-danger">Danger</button>
            <button type="button" class="btn btn-warning">Warning</button>
            <button type="button" class="btn btn-info">Info</button>
            <button type="button" class="btn btn-light">Light</button>
            <button type="button" class="btn btn-dark">Dark</button>
            <button type="button" class="btn btn-link">Link</button>
  
            <h2>Outline Buttons:</h2>
            <button type="button" class="btn btn-outline-primary">Primary</button>
            <button type="button" class="btn btn-outline-secondary">Secondary</button>
            <button type="button" class="btn btn-outline-success">Success</button>
            <button type="button" class="btn btn-outline-danger">Danger</button>
            <button type="button" class="btn btn-outline-warning">Warning</button>
            <button type="button" class="btn btn-outline-info">Info</button>
            <button type="button" class="btn btn-outline-light">Light</button>
            <button type="button" class="btn btn-outline-dark">Dark</button>
            <button type="button" class="btn btn-outline-link">Link</button>
  
            <h2>Block Buttons:</h2>
            <button type="button" class="btn btn-block btn-primary">Block level
              button</button>
            <button type="button" class="btn btn-block btn-outline-primary">Block level
              button</button>
          </div>

        </div>
        <!-- End v-pills-buttons -->

        <!-- v-pills-forms -->
        <div class="tab-pane fade" id="v-pills-forms" role="tabpanel" aria-labelledby="v-pills-forms-tab">
          <h1>Forms!</h1>
          <form>
            <div class="form-row">
              <div class="form-group col-md-6">
                <label for="inputEmail">Email</label>
                <input type="email" class="form-control" id="inputEmail" placeholder="Email" required>
              </div>
              <div class="form-group col-md-6">
                <label for="inputPassword">Password</label>
                <input type="password" class="form-control" id="inputPassword" placeholder="Password" required>
              </div>
            </div>
            <div class="form-group">
              <label for="inputAddress">Address</label>
              <input type="text" class="form-control" id="inputAddress" placeholder="1234 Main St">
            </div>
            <div class="form-group">
              <label for="inputAddress2">Address 2</label>
              <input type="text" class="form-control" id="inputAddress2" placeholder="Apartment, studio, or floor">
            </div>
            <div class="form-row">
              <div class="form-group col-md-6">
                <label for="inputCity">City</label>
                <input type="text" class="form-control" id="inputCity">
              </div>
              <div class="form-group col-md-4">
                <label for="inputState">State</label>
                <select id="inputState" class="form-control">
                  <option selected>Choose...</option>
                  <option>...</option>
                </select>
              </div>
              <div class="form-group col-md-2">
                <label for="inputZip">Zip</label>
                <input type="text" class="form-control" id="inputZip">
              </div>
            </div>
            <div class="form-group">
              <div class="form-check">
                <input class="form-check-input" type="checkbox" id="gridCheck">
                <label class="form-check-label" for="gridCheck">
                  Check me out
                </label>
              </div>
            </div>
            <button class="btn btn-primary">Sign in</button>
          </form>
        </div>
        <!-- End v-pills-forms -->

        <!-- v-pills-settings -->
        <div class="tab-pane fade" id="v-pills-settings" role="tabpanel" aria-labelledby="v-pills-settings-tab">
          Settings
        </div>
        <!-- End v-pills-settings -->

      </div>
    </div>
    <!-- End Page Contents -->

  </div>
  <!-- End Window Contents -->

</body>

<script>
    var btns = document.querySelectorAll('.btns_to_trigger_ahk button')
    for(i=0;i<btns.length;i++){
      btns[i].addEventListener('click',() => getBtns(this,event), false)
    }
    async function getBtns(t,e){
       obj1 = await window.chrome.webview.hostObjects.btnsToAHK;
       obj1.func(e.target.innerText);
    }
</script>

<script>
  document.querySelector('#v-pills-forms > form').addEventListener("submit", () => getForm(this,event), true)
  
  async function getForm(t,e){
    e.preventDefault(); // before the code
    var FormData = Object()
    var els = document.querySelector('form').elements;
    for(i=0;i<els.length;i++){
      var name = els[i].name
			if (name !== '')
        FormData[els[i].name] = els[i].value
      else
        FormData[els[i].id] = els[i].value
    }

    obj = await window.chrome.webview.hostObjects.formdataToAHK;
    obj.func(JSON.stringify(FormData));
  }
  
</script>

</html>
can grab the other files, css/js etc from the neutron bootstrap github. These are the only things i modified.

Thanks!
User avatar
kczx3
Posts: 1677
Joined: 06 Oct 2015, 21:39

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 14:35

Why are you calling bind on the functions but not... binding anything?
Tre4shunter
Posts: 139
Joined: 26 Jan 2016, 16:05

Re: 2.0-beta.1 - WebView2

20 Oct 2021, 14:42

well, because like i said....i really am just fumbling my way through this. Its the only way i could get it to work :lol:

Return to “Scripts and Functions (v2)”

Who is online

Users browsing this forum: No registered users and 39 guests