GCC Code Coverage Report


Directory: src/gate/
File: src/gate/ui/webviews.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 0 6 0.0%
Functions: 0 3 0.0%
Branches: 0 0 -%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/ui/webviews.h"
30 #include "gate/results.h"
31
32 #if defined(GATE_UI_WINAPI) && !defined(GATE_SYS_WIN16)
33
34 #include "gate/platform/windows/win32com.h"
35 #include "gate/ui/gateui_winapi.h"
36 #include "gate/platforms.h"
37
38 #define GATE_UI_WEBVIEW_ATLWEBBROWSER_IMPL 1
39
40 #if defined(GATE_WIN32_UNICODE) && !defined(GATE_SYS_WINCE)
41 /* edge webview2 support is only available on WinNT unicode-platforms */
42 # define GATE_UI_WEBVIEW_EDGEWV2_IMPL 1
43 #endif
44
45
46 #include "gate/libraries.h"
47
48 #ifdef GATE_UI_WEBVIEW_ATLWEBBROWSER_IMPL
49 # include "gate/ui/webviews_win32.h"
50 #endif
51
52 #ifdef GATE_UI_WEBVIEW_EDGEWV2_IMPL
53 # include "gate/ui/webviews_msedgewv2.h"
54 #endif
55
56 typedef struct gate_ui_webview_impl_class
57 {
58 gate_ui_webview_t* webview;
59 HWND hwnd;
60
61 #ifdef GATE_UI_WEBVIEW_ATLWEBBROWSER_IMPL
62 /* atl/webbrowser stuff: */
63 IWebBrowser* webbrowser;
64 IWebBrowser2* webbrowser2;
65 IDispatch* webbrowser_events;
66 #endif
67
68 #ifdef GATE_UI_WEBVIEW_EDGEWV2_IMPL
69 /* webview2 stuff: */
70 ICoreWebView2Controller* wv2_controller;
71 ICoreWebView2* wv2;
72 IUnknown* wv2_event_handlers;
73 #endif
74
75 } gate_ui_webview_impl_t;
76
77 static IID const gate_IID_IUnknown = { 0x00000000, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
78 static IID const gate_IID_IDispatch = { 0x00020400, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
79
80 static gate_bool_t webview_windowproc_events(void* hwnd, gate_ui_ctrl_t* ctrl, gate_uint32_t msg, gate_uintptr_t wParam, gate_intptr_t lParam, gate_intptr_t* lresult);
81
82 static gate_result_t webview_destroy_impl(gate_ui_ctrl_t* ctrl);
83
84 static gate_ui_winapi_dispatchers_t global_webview_dispatchers =
85 {
86 &webview_destroy_impl,
87 &gate_ui_winapi_is_enabled,
88 &gate_ui_winapi_is_visible,
89 &gate_ui_winapi_is_focused,
90 &gate_ui_winapi_get_position,
91 &gate_ui_winapi_get_size,
92 &gate_ui_winapi_get_children,
93 &gate_ui_winapi_get_text_length,
94 &gate_ui_winapi_get_text,
95 &gate_ui_winapi_get_state,
96 &gate_ui_winapi_set_enabled,
97 &gate_ui_winapi_set_visible,
98 &gate_ui_winapi_set_focus,
99 &gate_ui_winapi_set_position,
100 &gate_ui_winapi_set_text,
101 &gate_ui_winapi_set_state,
102 &gate_ui_winapi_refresh
103 };
104
105
106
107 #ifdef GATE_UI_WEBVIEW_ATLWEBBROWSER_IMPL
108 /****************************/
109 /* */
110 /* ATL / webbrowser code: */
111 /* */
112 /****************************/
113
114 typedef struct gate_ui_winapi_atl_code
115 {
116 BOOL(__stdcall* WinInit)();
117 HRESULT(STDMETHODCALLTYPE* GetControl)(HWND hwnd, IUnknown** ptr);
118 HRESULT(STDMETHODCALLTYPE* GetHost)(HWND hwnd, IUnknown** ptr);
119 } gate_ui_winapi_atl_code_t;
120
121
122 static gate_library_t atl_lib = NULL;
123 static gate_bool_t volatile atl_code_loaded = false;
124 static gate_ui_winapi_atl_code_t atl_code = GATE_INIT_EMPTY;
125
126 static gate_result_t load_atl_code()
127 {
128 #if defined(GATE_SYS_WINCE)
129 static gate_string_t const atl_lib_name = { "atlce300.dll", 12, NULL };
130 #else
131 static gate_string_t const atl_lib_name = { "atl.dll", 7, NULL };
132 #endif
133 gate_result_t ret = GATE_RESULT_FAILED;
134 if (!atl_code_loaded)
135 {
136 gate_win32_com_init();
137 do
138 {
139 if (atl_lib == NULL)
140 {
141 ret = gate_library_open(&atl_lib_name, &atl_lib, 0);
142 GATE_BREAK_IF_FAILED(ret);
143 }
144
145 ret = gate_library_get_function_name(atl_lib, "AtlAxWinInit", &atl_code.WinInit);
146 GATE_BREAK_IF_FAILED(ret);
147
148 ret = gate_library_get_function_name(atl_lib, "AtlAxGetControl", &atl_code.GetControl);
149 GATE_BREAK_IF_FAILED(ret);
150
151 ret = gate_library_get_function_name(atl_lib, "AtlAxGetHost", &atl_code.GetHost);
152 GATE_BREAK_IF_FAILED(ret);
153
154 if (atl_code.WinInit())
155 {
156 atl_code_loaded = true;
157 ret = GATE_RESULT_OK;
158 }
159 } while (0);
160 }
161 else
162 {
163 ret = GATE_RESULT_OK;
164 }
165 return ret;
166 }
167
168
169 static gate_result_t get_control_interface(HWND hwnd, GUID const* interface_id, void** pptr_interface)
170 {
171 gate_result_t ret = GATE_RESULT_FAILED;
172 IUnknown* punk = NULL;
173 IUnknown* ptr_interface = NULL;
174 HRESULT hr;
175 do
176 {
177 hr = atl_code.GetControl(hwnd, &punk);
178 if (FAILED(hr))
179 {
180 ret = GATE_RESULT_NOTAVAILABLE;
181 break;
182 }
183 if (!punk)
184 {
185 ret = GATE_RESULT_NOTAVAILABLE;
186 break;
187 }
188 if (!interface_id)
189 {
190 ptr_interface = punk;
191 punk->lpVtbl->AddRef(punk);
192 }
193 else
194 {
195 hr = punk->lpVtbl->QueryInterface(punk, interface_id, (void**)&ptr_interface);
196 if (FAILED(hr))
197 {
198 ret = GATE_RESULT_INCORRECTTYPE;
199 break;
200 }
201 }
202 if (pptr_interface)
203 {
204 *pptr_interface = (void*)ptr_interface;
205 }
206 else
207 {
208 ptr_interface->lpVtbl->Release(ptr_interface);
209 }
210 ret = GATE_RESULT_OK;
211 } while (0);
212
213 if (punk)
214 {
215 punk->lpVtbl->Release(punk);
216 }
217
218 return ret;
219 }
220
221 static void* get_control_interface_ptr(HWND hwnd, GUID const* interface_id)
222 {
223 void* ptr_interface = NULL;
224 gate_result_t result = get_control_interface(hwnd, interface_id, &ptr_interface);
225 if (GATE_FAILED(result))
226 {
227 ptr_interface = NULL;
228 }
229 return ptr_interface;
230 }
231
232
233
234 typedef struct webbrowser_events_class
235 {
236 IDispatch dispatch;
237
238 gate_atomic_int_t ref_counter;
239 gate_ui_webview_t* webview;
240 } webbrowser_events_t;
241
242
243
244 static HRESULT STDMETHODCALLTYPE webbrowser_events_QueryInterface(IDispatch* This, REFIID riid, void** ppvObject)
245 {
246 webbrowser_events_t* self = (webbrowser_events_t*)This;
247
248 if (!ppvObject || !riid || !self)
249 {
250 return E_POINTER;
251 }
252
253 if (IsEqualGUID(riid, &gate_IID_IDispatch) || IsEqualGUID(riid, &gate_IID_IUnknown)
254 || IsEqualGUID(riid, &IID_DWebBrowserEvents2) || IsEqualGUID(riid, &IID_DWebBrowserEvents))
255 {
256 *ppvObject = self;
257 gate_atomic_int_inc(&self->ref_counter);
258 return S_OK;
259 }
260 return E_NOINTERFACE;
261 }
262 static ULONG STDMETHODCALLTYPE webbrowser_events_AddRef(IDispatch* This)
263 {
264 webbrowser_events_t* self = (webbrowser_events_t*)This;
265 return (ULONG)gate_atomic_int_inc(&self->ref_counter);
266 }
267 static ULONG STDMETHODCALLTYPE webbrowser_events_Release(IDispatch* This)
268 {
269 webbrowser_events_t* self = (webbrowser_events_t*)This;
270 ULONG cnt = (ULONG)gate_atomic_int_dec(&self->ref_counter);
271 if (cnt == 0)
272 {
273 if (self->webview)
274 {
275 self->webview = NULL;
276 }
277 gate_mem_dealloc(self);
278 }
279 return cnt;
280 }
281
282 static HRESULT STDMETHODCALLTYPE webbrowser_events_GetTypeInfoCount(IDispatch* This, UINT* pctinfo)
283 {
284 if (pctinfo)
285 {
286 *pctinfo = 0;
287 }
288 return S_OK;
289 }
290
291 static HRESULT STDMETHODCALLTYPE webbrowser_events_GetTypeInfo(IDispatch* This, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
292 {
293 return DISP_E_BADINDEX;
294 }
295
296 static gate_bool_t wstr_equal(wchar_t const* a, wchar_t const* b)
297 {
298 if (!a || !b)
299 {
300 return false;
301 }
302 for (; *a == *b; ++a, ++b)
303 {
304 if (*a == 0)
305 {
306 return true;
307 }
308 }
309 return false;
310 }
311
312 struct dispid_map
313 {
314 DISPID id;
315 wchar_t const* name;
316 };
317
318 static struct dispid_map const dispid_webbrowser_map[] =
319 {
320 { DISPID_BEFORENAVIGATE, L"" },
321 { DISPID_NAVIGATECOMPLETE, L"" },
322 { DISPID_STATUSTEXTCHANGE, L"" },
323 { DISPID_QUIT, L"" },
324 { DISPID_DOWNLOADCOMPLETE, L"" },
325 { DISPID_COMMANDSTATECHANGE, L"" },
326 { DISPID_DOWNLOADBEGIN, L"" },
327 { DISPID_NEWWINDOW, L"" },
328 { DISPID_PROGRESSCHANGE, L"" },
329 { DISPID_WINDOWMOVE, L"" },
330 { DISPID_WINDOWRESIZE, L"" },
331 { DISPID_WINDOWACTIVATE, L"" },
332 { DISPID_PROPERTYCHANGE, L"" },
333 { DISPID_TITLECHANGE, L"" },
334 { DISPID_TITLEICONCHANGE, L"" }
335 };
336 static gate_size_t const dispid_webbrowser_map_count = sizeof(dispid_webbrowser_map) / sizeof(dispid_webbrowser_map[0]);
337
338
339 static void webbrowser_disable_script_errors(gate_ui_webview_impl_t* impl)
340 {
341 if (impl->webbrowser2)
342 {
343 VARIANT vin;
344 VARIANT vout;
345 HRESULT hr;
346 VARIANT_BOOL vb = FALSE;
347 vin.vt = VT_BOOL;
348 vin.boolVal = vb;
349 vout.vt = VT_EMPTY;
350 hr = impl->webbrowser2->lpVtbl->ExecWB(impl->webbrowser2, OLECMDID_SHOWSCRIPTERROR, OLECMDEXECOPT_DONTPROMPTUSER, &vin, &vout);
351 impl->webbrowser2->lpVtbl->put_Silent(impl->webbrowser2, TRUE);
352 Sleep(0);
353 }
354 }
355
356
357 static HRESULT STDMETHODCALLTYPE webbrowser_events_GetIDsOfNames(IDispatch* This, REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
358 {
359 UINT ndx;
360 gate_size_t ndx_map;
361
362 for (ndx = 0; ndx != cNames; ++ndx)
363 {
364 rgDispId[ndx] = 0;
365 for (ndx_map = 0; ndx_map != dispid_webbrowser_map_count; ++ndx_map)
366 {
367 if (wstr_equal(rgszNames[ndx], dispid_webbrowser_map[ndx_map].name))
368 {
369 rgDispId[ndx] = dispid_webbrowser_map[ndx_map].id;
370 break;
371 }
372 }
373 }
374 return S_OK;
375 }
376
377 static HRESULT STDMETHODCALLTYPE webbrowser_events_Invoke(IDispatch* This, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
378 DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr)
379 {
380 webbrowser_events_t* self = (webbrowser_events_t*)This;
381 gate_string_t text = GATE_STRING_INIT_EMPTY;
382 gate_bool_t b = false;
383 gate_int32_t n1 = 0, n2 = 0;
384 gate_ui_webview_impl_t* impl = NULL;
385
386 do
387 {
388 if (!self || !self->webview)
389 {
390 break;
391 }
392 impl = (gate_ui_webview_impl_t*)GATE_UI_WINAPI_GET_CTRL_PARAM(&self->webview->ctrl);
393
394 switch (dispIdMember)
395 {
396 case DISPID_BEFORENAVIGATE:
397 {
398 webbrowser_disable_script_errors(impl);
399 if (self->webview->on_navigate_start)
400 {
401 VARIANTARG* a0 = &pDispParams->rgvarg[0];
402 VARIANTARG* a1 = &pDispParams->rgvarg[1];
403 VARIANTARG* a2 = &pDispParams->rgvarg[2];
404 VARIANTARG* a3 = &pDispParams->rgvarg[3];
405 VARIANTARG* a4 = &pDispParams->rgvarg[4];
406 VARIANTARG* a5 = &pDispParams->rgvarg[5];
407 self->webview->on_navigate_start(&self->webview->ctrl, &text, &b);
408 }
409 break;
410 }
411 case DISPID_PROGRESSCHANGE:
412 {
413 if (self->webview->on_navigate_progress)
414 {
415 self->webview->on_navigate_progress(&self->webview->ctrl, n1, n2);
416 }
417 break;
418 }
419 case DISPID_NAVIGATECOMPLETE:
420 case DISPID_NAVIGATECOMPLETE2:
421 {
422 webbrowser_disable_script_errors(impl);
423 if (self->webview->on_navigate_complete)
424 {
425 if (pDispParams->cArgs >= 1)
426 {
427 if (pDispParams->rgvarg[0].vt == VT_BSTR)
428 {
429 gate_win32_bstr_to_string(pDispParams->rgvarg[0].bstrVal, &text);
430 }
431 else if ((pDispParams->rgvarg[0].vt == (VT_VARIANT | VT_BYREF)) && (pDispParams->rgvarg[0].pvarVal))
432 {
433 if (pDispParams->rgvarg[0].pvarVal->vt == VT_BSTR)
434 {
435 gate_win32_bstr_to_string(pDispParams->rgvarg[0].pvarVal->bstrVal, &text);
436 }
437 }
438 }
439 self->webview->on_navigate_complete(&self->webview->ctrl, &text);
440 }
441 break;
442 }
443 case DISPID_COMMANDSTATECHANGE:
444 {
445 if (self->webview->on_internal_state_changed)
446 {
447 self->webview->on_internal_state_changed(&self->webview->ctrl, 0);
448 }
449 break;
450 }
451 case DISPID_TITLECHANGE:
452 {
453 if (self->webview->on_title_text_changed)
454 {
455 if ((pDispParams->cArgs >= 1) && (pDispParams->rgvarg[0].vt == VT_BSTR))
456 {
457 gate_win32_bstr_to_string(pDispParams->rgvarg[0].bstrVal, &text);
458 }
459 self->webview->on_title_text_changed(&self->webview->ctrl, &text);
460 }
461 break;
462 }
463 case DISPID_STATUSTEXTCHANGE:
464 {
465 if (self->webview->on_status_text_changed)
466 {
467 if ((pDispParams->cArgs >= 1) && (pDispParams->rgvarg[0].vt == VT_BSTR))
468 {
469 gate_win32_bstr_to_string(pDispParams->rgvarg[0].bstrVal, &text);
470 }
471 self->webview->on_status_text_changed(&self->webview->ctrl, &text);
472 }
473 break;
474 }
475 case DISPID_NAVIGATEERROR:
476 {
477 Sleep(0);
478 break;
479 }
480 case DISPID_NEWWINDOW2:
481 case DISPID_NEWWINDOW:
482 {
483 if (self->webview->on_new_window)
484 {
485 gate_ui_webview_t* ptr_newview = NULL;
486 self->webview->on_new_window(&self->webview->ctrl, &ptr_newview);
487 if (ptr_newview && (pDispParams->cArgs >= 2))
488 {
489 gate_ui_webview_impl_t* other_impl = (gate_ui_webview_impl_t*)GATE_UI_WINAPI_GET_CTRL_PARAM(&ptr_newview->ctrl);
490 VARIANTARG* a0 = &pDispParams->rgvarg[0];
491 //*a0->pboolVal = TRUE;
492 VARIANTARG* a1 = &pDispParams->rgvarg[1];
493 if (other_impl && other_impl->webbrowser2 && (a1->vt == (VT_DISPATCH | VT_BYREF)))
494 {
495 IDispatch* pdisp = NULL;
496 other_impl->webbrowser2->lpVtbl->put_RegisterAsBrowser(other_impl->webbrowser2, TRUE);
497 other_impl->webbrowser2->lpVtbl->QueryInterface(other_impl->webbrowser2, &gate_IID_IDispatch, (void**)&pdisp);
498 *a1->ppdispVal = pdisp;
499 }
500 Sleep(0);
501 }
502 }
503 break;
504 }
505 case DISPID_QUIT:
506 case DISPID_DOWNLOADCOMPLETE:
507 case DISPID_DOWNLOADBEGIN:
508 case DISPID_WINDOWMOVE:
509 case DISPID_WINDOWRESIZE:
510 case DISPID_WINDOWACTIVATE:
511 case DISPID_PROPERTYCHANGE:
512 case DISPID_TITLEICONCHANGE:
513 break;
514 }
515
516 } while (0);
517
518 gate_string_release(&text);
519
520
521 return S_OK;
522 }
523
524
525 static IDispatchVtbl webview_disp_vtbl =
526 {
527 &webbrowser_events_QueryInterface,
528 &webbrowser_events_AddRef,
529 &webbrowser_events_Release,
530
531 &webbrowser_events_GetTypeInfoCount,
532 &webbrowser_events_GetTypeInfo,
533 &webbrowser_events_GetIDsOfNames,
534 &webbrowser_events_Invoke
535 };
536
537
538
539
540 static DWORD register_webbrowser_event_dispatcher(IUnknown* target, IID const* event_iid, IDispatch* event_handler)
541 {
542 DWORD ret = 0;
543 HRESULT hr;
544 IConnectionPointContainer* ptr_container = NULL;
545 IConnectionPoint* ptr_point = NULL;
546 DWORD cookie = 0;
547
548 do
549 {
550 hr = target->lpVtbl->QueryInterface(target, &IID_IConnectionPointContainer, (void**)&ptr_container);
551 if (FAILED(hr))
552 {
553 break;
554 }
555 hr = ptr_container->lpVtbl->FindConnectionPoint(ptr_container, event_iid, &ptr_point);
556 if (FAILED(hr))
557 {
558 break;
559 }
560 hr = ptr_point->lpVtbl->Advise(ptr_point, (IUnknown*)event_handler, &cookie);
561 if (FAILED(hr))
562 {
563 break;
564 }
565 ret = cookie;
566 } while (0);
567
568 if (ptr_point)
569 {
570 ptr_point->lpVtbl->Release(ptr_point);
571 }
572 if (ptr_container)
573 {
574 ptr_container->lpVtbl->Release(ptr_container);
575 }
576 return ret;
577 }
578
579
580 static gate_ui_webview_impl_t* create_webbrowser_impl(gate_ui_webview_t* webview)
581 {
582 HWND hwnd_ctrl = NULL;
583 gate_ui_webview_impl_t* impl = NULL;
584 gate_ui_host_t* host = NULL;
585 RECT rect;
586 DWORD event_cookie = 0;
587 #if defined(GATE_SYS_WINCE)
588 static LPCTSTR browser_id = TEXT("Microsoft.PIEDocView");
589 #else
590 static LPCTSTR browser_id = TEXT("SHDocVwCtl.WebBrowser");
591 #endif
592 webbrowser_events_t* webbrowser_events;
593
594 do
595 {
596 hwnd_ctrl = GATE_UI_WINAPI_GET_HWND(&webview->ctrl);
597 host = GATE_UI_WINAPI_GET_HOST(&webview->ctrl);
598 impl = (gate_ui_webview_impl_t*)gate_mem_alloc(sizeof(gate_ui_webview_impl_t));
599 if (!impl)
600 {
601 break;
602 }
603
604 gate_mem_clear(impl, sizeof(gate_ui_webview_impl_t));
605 impl->webview = webview;
606 GetClientRect(hwnd_ctrl, &rect);
607 impl->hwnd = CreateWindow(TEXT("AtlAxWin"), browser_id,
608 WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL,
609 0, 0, rect.right, rect.bottom,
610 hwnd_ctrl, NULL,
611 (HINSTANCE)GATE_UI_WINAPI_GET_HOST_APPHANDLE(host), NULL);
612 if (impl->hwnd == NULL)
613 {
614 gate_mem_dealloc(impl);
615 impl = NULL;
616 break;
617 }
618
619 impl->webbrowser = (IWebBrowser*)get_control_interface_ptr(impl->hwnd, &IID_IWebBrowser);
620 impl->webbrowser2 = (IWebBrowser2*)get_control_interface_ptr(impl->hwnd, &IID_IWebBrowser2);
621
622 webbrowser_events = (webbrowser_events_t*)gate_mem_alloc(sizeof(webbrowser_events_t));
623 if (NULL != webbrowser_events)
624 {
625 gate_mem_clear(webbrowser_events, sizeof(webbrowser_events_t));
626 webbrowser_events->dispatch.lpVtbl = &webview_disp_vtbl;
627 gate_atomic_int_init(&webbrowser_events->ref_counter, 1);
628 webbrowser_events->webview = webview;
629
630 event_cookie = register_webbrowser_event_dispatcher((IUnknown*)impl->webbrowser, &IID_DWebBrowserEvents2, (IDispatch*)webbrowser_events);
631 if (0 == event_cookie)
632 {
633 event_cookie = register_webbrowser_event_dispatcher((IUnknown*)impl->webbrowser, &IID_DWebBrowserEvents, (IDispatch*)webbrowser_events);
634 }
635 impl->webbrowser_events = (IDispatch*)webbrowser_events;
636 }
637 } while (0);
638
639 return impl;
640 }
641
642 static gate_result_t gate_ui_webview_create_activex(gate_ui_webview_t* webview, gate_ui_ctrl_t* parent,
643 gate_ui_position_t const* position, gate_uint32_t flags, void* userparam)
644 {
645 static gate_string_t active_x_progid = { "SHDocVwCtl.WebBrowser", 21, NULL };
646 //static gate_string_t active_x_progid = { "Shell.Explorer.2", 16, NULL };
647 //static gate_string_t active_x_progid = { "MsRDP.MsRDP.2", 13, NULL };
648 gate_result_t ret = GATE_RESULT_FAILED;
649 gate_uint32_t styles, exstyles;
650 gate_ui_host_t* host;
651 HWND hwnd_ctrl = NULL;
652 HWND hwnd_parent = NULL;
653 gate_ui_webview_impl_t* impl = NULL;
654
655 do
656 {
657 ret = load_atl_code();
658 GATE_BREAK_IF_FAILED(ret);
659
660 if (parent == NULL)
661 {
662 ret = GATE_RESULT_NULLPOINTER;
663 break;
664 }
665 host = GATE_UI_WINAPI_GET_HOST(parent);
666 hwnd_parent = GATE_UI_WINAPI_GET_HWND(parent);
667
668 gate_mem_clear(webview, sizeof(gate_ui_webview_t));
669
670 exstyles = 0;
671 #if !defined(GATE_SYS_WINCE)
672 exstyles |= WS_EX_CONTROLPARENT;
673 #endif
674 styles = WS_GROUP | WS_CHILD | WS_TABSTOP | WS_CLIPCHILDREN;
675 if (!GATE_FLAG_ENABLED(flags, GATE_UI_FLAG_ENABLED)) styles |= WS_DISABLED;
676 if (GATE_FLAG_ENABLED(flags, GATE_UI_FLAG_VISIBLE)) styles |= WS_VISIBLE;
677
678 ret = gate_ui_winapi_create(&webview->ctrl, host, hwnd_parent, NULL, position, styles, exstyles, NULL, userparam, false);
679 if (GATE_SUCCEEDED(ret))
680 {
681 GATE_UI_WINAPI_SET_DISPATCHER(&webview->ctrl, &global_webview_dispatchers);
682 hwnd_ctrl = GATE_UI_WINAPI_GET_HWND(&webview->ctrl);
683 impl = create_webbrowser_impl(webview);
684 if (impl == NULL)
685 {
686 gate_ui_winapi_destroy(&webview->ctrl);
687 ret = GATE_RESULT_OUTOFMEMORY;
688 break;
689 }
690 GATE_UI_WINAPI_SET_CTRL_PARAM(&webview->ctrl, impl);
691
692 gate_ui_winapi_register_event(host, (void*)hwnd_ctrl, WM_SIZE, &webview_windowproc_events, &webview->ctrl);
693 }
694 } while (0);
695 return ret;
696 }
697
698 static gate_result_t webbrowser_navigate_to(gate_ui_webview_impl_t* impl, gate_string_t const* address)
699 {
700 gate_result_t ret = GATE_RESULT_FAILED;
701 BSTR bstr_address = NULL;
702 VARIANT var_addr;
703 VARIANT var_empty = GATE_INIT_EMPTY;
704 HRESULT hr;
705
706 do
707 {
708 if (!impl->webbrowser && !impl->webbrowser2)
709 {
710 ret = GATE_RESULT_NOTAVAILABLE;
711 break;
712 }
713 gate_win32_variant_init(&var_empty);
714 bstr_address = gate_win32_bstr_create(address);
715
716 if (impl->webbrowser2)
717 {
718 webbrowser_disable_script_errors(impl);
719
720 var_addr.vt = VT_BSTR;
721 var_addr.bstrVal = bstr_address;
722 hr = impl->webbrowser2->lpVtbl->Navigate2(impl->webbrowser2, &var_addr, NULL, NULL, NULL, NULL);
723 }
724 else
725 {
726 hr = impl->webbrowser->lpVtbl->Navigate(impl->webbrowser, bstr_address, NULL, NULL, NULL, NULL);
727 }
728
729 if (FAILED(hr))
730 {
731 ret = GATE_RESULT_FAILED;
732 break;
733 }
734 ret = GATE_RESULT_OK;
735 } while (0);
736
737 gate_win32_bstr_release(bstr_address);
738 return ret;
739 }
740
741 static gate_result_t webbrowser_execute(gate_ui_webview_impl_t* impl, gate_enumint_t command)
742 {
743 HRESULT hr;
744
745 switch (command)
746 {
747 case GATE_UI_WEBVIEW_COMMAND_STOP:
748 {
749 if (impl->webbrowser2)
750 {
751 hr = impl->webbrowser2->lpVtbl->Stop(impl->webbrowser2);
752 }
753 else
754 {
755 hr = impl->webbrowser->lpVtbl->Stop(impl->webbrowser);
756 }
757 return SUCCEEDED(hr) ? GATE_RESULT_OK : GATE_RESULT_FAILED;
758 }
759 case GATE_UI_WEBVIEW_COMMAND_REFRESH:
760 {
761 if (impl->webbrowser2)
762 {
763 hr = impl->webbrowser2->lpVtbl->Refresh(impl->webbrowser2);
764 }
765 else
766 {
767 hr = impl->webbrowser->lpVtbl->Refresh(impl->webbrowser);
768 }
769 return SUCCEEDED(hr) ? GATE_RESULT_OK : GATE_RESULT_FAILED;
770 }
771 case GATE_UI_WEBVIEW_COMMAND_GOBACK:
772 {
773 if (impl->webbrowser2)
774 {
775 hr = impl->webbrowser2->lpVtbl->GoBack(impl->webbrowser2);
776 }
777 else
778 {
779 hr = impl->webbrowser->lpVtbl->GoBack(impl->webbrowser);
780 }
781 return SUCCEEDED(hr) ? GATE_RESULT_OK : GATE_RESULT_FAILED;
782 }
783 case GATE_UI_WEBVIEW_COMMAND_GONEXT:
784 {
785 if (impl->webbrowser2)
786 {
787 hr = impl->webbrowser2->lpVtbl->GoForward(impl->webbrowser2);
788 }
789 else
790 {
791 hr = impl->webbrowser->lpVtbl->GoForward(impl->webbrowser);
792 }
793 return SUCCEEDED(hr) ? GATE_RESULT_OK : GATE_RESULT_FAILED;
794 }
795 case GATE_UI_WEBVIEW_COMMAND_GOHOME:
796 {
797 if (impl->webbrowser2)
798 {
799 hr = impl->webbrowser2->lpVtbl->GoHome(impl->webbrowser2);
800 }
801 else
802 {
803 hr = impl->webbrowser->lpVtbl->GoHome(impl->webbrowser);
804 }
805 return SUCCEEDED(hr) ? GATE_RESULT_OK : GATE_RESULT_FAILED;
806 }
807 }
808
809 return GATE_RESULT_NOTSUPPORTED;
810 }
811 #endif /* GATE_UI_WEBVIEW_ATLWEBBROWSER_IMPL */
812
813
814
815
816
817 #ifdef GATE_UI_WEBVIEW_EDGEWV2_IMPL
818
819 /**********************************/
820 /* */
821 /* EDGE WebView2 implementation */
822 /* */
823 /**********************************/
824
825 #ifndef PM_QS_SENDMESSAGE
826 #define PM_QS_SENDMESSAGE (QS_SENDMESSAGE << 16)
827 #endif
828
829 #ifndef PM_QS_POSTMESSAGE
830 #define PM_QS_POSTMESSAGE ((QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER) << 16)
831 #endif
832
833 static gate_result_t await_event_and_process_messages(HANDLE waitable, DWORD timeout_ms)
834 {
835 gate_win32_userapi_t const* const userapi = gate_win32_userapi();
836 MSG msg;
837 BOOL b;
838 DWORD result;
839
840 result = WaitForSingleObject(waitable, 0);
841 if (result == WAIT_OBJECT_0)
842 {
843 return GATE_RESULT_OK;
844 }
845
846 for (;;)
847 {
848 gate_mem_clear(&msg, sizeof(msg));
849 b = userapi->UserPeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
850 if (b == TRUE)
851 {
852 /* handle user message */
853 userapi->UserDispatchMessage(&msg);
854 continue;
855 }
856
857 /* wait for user message or even completion */
858 result = userapi->UserMsgWaitForMultipleObjects(1, &waitable, FALSE, timeout_ms, QS_ALLINPUT);
859 if (result == WAIT_TIMEOUT)
860 {
861 return GATE_RESULT_TIMEOUT;
862 }
863 if (result == WAIT_OBJECT_0)
864 {
865 return GATE_RESULT_OK;
866 }
867 if (result != (WAIT_OBJECT_0 + 1))
868 {
869 /* unknown error/state -> cancel loop */
870 break;
871 }
872 }
873 return GATE_RESULT_FAILED;
874 }
875
876
877
878 static gate_result_t wv2_event_init(HANDLE* ptr_waitable)
879 {
880 if (*ptr_waitable == NULL)
881 {
882 *ptr_waitable = CreateSemaphore(NULL, 0, 1, NULL);
883 if (*ptr_waitable == NULL)
884 {
885 return GATE_RESULT_OUTOFRESOURCES;
886 }
887 return GATE_RESULT_OK;
888 }
889 else
890 {
891 //return await_event_and_process_messages(*ptr_waitable, INFINITE);
892 return GATE_RESULT_OK;
893 }
894 }
895 static void wv2_event_notify(HANDLE waitable)
896 {
897 ReleaseSemaphore(waitable, 1, NULL);
898 }
899 static gate_result_t wv2_event_await(HANDLE waitable)
900 {
901 return await_event_and_process_messages(waitable, INFINITE);
902 }
903
904
905 typedef struct wv2_env_completed_class
906 {
907 ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl* lpVtbl;
908
909 ICoreWebView2Environment* env;
910 HANDLE semaphore;
911 } wv2_env_completed_t;
912
913 static HRESULT STDMETHODCALLTYPE wv2_env_completed_QueryInterface(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This, REFIID riid, void** ppvObject)
914 {
915 wv2_env_completed_t* self = (wv2_env_completed_t*)This;
916 if (ppvObject)
917 {
918 *ppvObject = self;
919 }
920 return S_OK;
921 }
922 static ULONG STDMETHODCALLTYPE wv2_env_completed_AddRef(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This)
923 {
924 wv2_env_completed_t* self = (wv2_env_completed_t*)This;
925 return 2;
926 }
927 static ULONG STDMETHODCALLTYPE wv2_env_completed_Release(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This)
928 {
929 wv2_env_completed_t* self = (wv2_env_completed_t*)This;
930 return 1;
931 }
932
933 static HRESULT STDMETHODCALLTYPE wv2_env_completed_Invoke(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* This, HRESULT errorCode, ICoreWebView2Environment* createdEnvironment)
934 {
935 wv2_env_completed_t* self = (wv2_env_completed_t*)This;
936 if (createdEnvironment)
937 {
938 self->env = createdEnvironment;
939 createdEnvironment->lpVtbl->AddRef(createdEnvironment);
940 }
941 wv2_event_notify(self->semaphore);
942 return S_OK;
943 }
944
945 static ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl wv2_env_completed_vtbl = {
946 &wv2_env_completed_QueryInterface,
947 &wv2_env_completed_AddRef,
948 &wv2_env_completed_Release,
949 &wv2_env_completed_Invoke
950 };
951
952 static wv2_env_completed_t wv2_env_completed = {
953 &wv2_env_completed_vtbl,
954 NULL,
955 NULL
956 };
957
958
959
960
961 typedef struct wv2_ctrl_completed_class
962 {
963 ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl* lpVtbl;
964
965 ICoreWebView2Controller* ctrl;
966 HANDLE semaphore;
967 } wv2_ctrl_completed_t;
968
969 static HRESULT STDMETHODCALLTYPE wv2_ctrl_completed_QueryInterface(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This, REFIID riid, void** ppvObject)
970 {
971 wv2_ctrl_completed_t* self = (wv2_ctrl_completed_t*)This;
972 if (ppvObject)
973 {
974 *ppvObject = self;
975 }
976 return S_OK;
977 }
978 static ULONG STDMETHODCALLTYPE wv2_ctrl_completed_AddRef(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This)
979 {
980 wv2_ctrl_completed_t* self = (wv2_ctrl_completed_t*)This;
981 return 2;
982 }
983 static ULONG STDMETHODCALLTYPE wv2_ctrl_completed_Release(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This)
984 {
985 wv2_ctrl_completed_t* self = (wv2_ctrl_completed_t*)This;
986 return 1;
987 }
988
989 static HRESULT STDMETHODCALLTYPE wv2_ctrl_completed_Invoke(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* This, HRESULT errorCode, ICoreWebView2Controller* createdController)
990 {
991 wv2_ctrl_completed_t* self = (wv2_ctrl_completed_t*)This;
992 if (createdController)
993 {
994 self->ctrl = createdController;
995 createdController->lpVtbl->AddRef(createdController);
996 }
997 wv2_event_notify(self->semaphore);
998 return S_OK;
999 }
1000
1001 static ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl wv2_ctrl_completed_vtbl = {
1002 &wv2_ctrl_completed_QueryInterface,
1003 &wv2_ctrl_completed_AddRef,
1004 &wv2_ctrl_completed_Release,
1005 &wv2_ctrl_completed_Invoke
1006 };
1007
1008 static wv2_ctrl_completed_t wv2_ctrl_completed = {
1009 &wv2_ctrl_completed_vtbl,
1010 NULL
1011 };
1012
1013
1014 typedef struct wv2_event_handlers_class
1015 {
1016 IUnknownVtbl* vtblUnkown;
1017
1018 ICoreWebView2NewWindowRequestedEventHandler NewWindowRequested;
1019 EventRegistrationToken tokenNewWindowRequested;
1020
1021 ICoreWebView2NavigationStartingEventHandler NavigationStarting;
1022 EventRegistrationToken tokenNavigationStarting;
1023
1024 ICoreWebView2NavigationCompletedEventHandler NavigationCompleted;
1025 EventRegistrationToken tokenNavigationCompleted;
1026
1027 gate_atomic_int_t ref_counter;
1028 gate_ui_webview_impl_t* impl;
1029
1030 } wv2_event_handlers_t;
1031
1032
1033 static void wv2_event_handlers_attach_events(wv2_event_handlers_t* handlers)
1034 {
1035 HRESULT hr;
1036 ICoreWebView2* wv2 = handlers->impl->wv2;
1037
1038 hr = wv2->lpVtbl->add_NewWindowRequested(wv2, &handlers->NewWindowRequested, &handlers->tokenNewWindowRequested);
1039 hr = wv2->lpVtbl->add_NavigationStarting(wv2, &handlers->NavigationStarting, &handlers->tokenNavigationStarting);
1040 hr = wv2->lpVtbl->add_NavigationCompleted(wv2, &handlers->NavigationCompleted, &handlers->tokenNavigationCompleted);
1041 }
1042
1043
1044 static void wv2_event_handlers_detach_events(wv2_event_handlers_t* handlers)
1045 {
1046 ICoreWebView2* wv2 = NULL;
1047
1048 if (!handlers || !handlers->impl || !handlers->impl->wv2) return;
1049 wv2 = handlers->impl->wv2;
1050
1051 if (handlers->tokenNewWindowRequested.value)
1052 {
1053 wv2->lpVtbl->remove_NewWindowRequested(wv2, handlers->tokenNewWindowRequested);
1054 handlers->tokenNewWindowRequested.value = 0;
1055 }
1056
1057 if (handlers->tokenNavigationStarting.value)
1058 {
1059 wv2->lpVtbl->remove_NavigationStarting(wv2, handlers->tokenNavigationStarting);
1060 handlers->tokenNavigationStarting.value = 0;
1061 }
1062
1063 if (handlers->tokenNavigationCompleted.value)
1064 {
1065 wv2->lpVtbl->remove_NavigationCompleted(wv2, handlers->tokenNavigationCompleted);
1066 handlers->tokenNavigationCompleted.value = 0;
1067 }
1068 }
1069
1070
1071
1072 static ULONG STDMETHODCALLTYPE wv2_event_handlers_AddRef(IUnknown* This)
1073 {
1074 wv2_event_handlers_t* handlers = (wv2_event_handlers_t*)This;
1075 return (ULONG)gate_atomic_int_inc(&handlers->ref_counter);
1076 }
1077
1078 static ULONG STDMETHODCALLTYPE wv2_event_handlers_Release(IUnknown* This)
1079 {
1080 wv2_event_handlers_t* handlers = (wv2_event_handlers_t*)This;
1081 ULONG cnt = (ULONG)gate_atomic_int_dec(&handlers->ref_counter);
1082 if (cnt == 0)
1083 {
1084 wv2_event_handlers_detach_events(handlers);
1085 gate_mem_dealloc(handlers);
1086 }
1087 return cnt;
1088 }
1089 static HRESULT STDMETHODCALLTYPE wv2_event_handlers_QueryInterface(IUnknown* This, REFIID riid, void** ppvObject)
1090 {
1091 wv2_event_handlers_t* handlers = (wv2_event_handlers_t*)This;
1092 if (!ppvObject || !riid || !This)
1093 {
1094 return E_INVALIDARG;
1095 }
1096
1097 if (IsEqualGUID(riid, &gate_IID_IUnknown))
1098 {
1099 *ppvObject = This;
1100 wv2_event_handlers_AddRef(This);
1101 return S_OK;
1102 }
1103 if (IsEqualGUID(riid, &IID_ICoreWebView2NewWindowRequestedEventHandler))
1104 {
1105 *ppvObject = &handlers->NewWindowRequested;
1106 wv2_event_handlers_AddRef(This);
1107 return S_OK;
1108 }
1109 if (IsEqualGUID(riid, &IID_ICoreWebView2NavigationStartingEventHandler))
1110 {
1111 *ppvObject = &handlers->NavigationStarting;
1112 wv2_event_handlers_AddRef(This);
1113 return S_OK;
1114 }
1115 if (IsEqualGUID(riid, &IID_ICoreWebView2NavigationCompletedEventHandler))
1116 {
1117 *ppvObject = &handlers->NavigationCompleted;
1118 wv2_event_handlers_AddRef(This);
1119 return S_OK;
1120 }
1121
1122 return E_NOTIMPL;
1123 }
1124
1125 static IUnknownVtbl IUnknown_wv2_event_handlers_vtbl = {
1126 &wv2_event_handlers_QueryInterface,
1127 &wv2_event_handlers_AddRef,
1128 &wv2_event_handlers_Release
1129 };
1130
1131 #define CREATE_WV2_EVENT_HANDLER_VTBL(event_name, iface_type, sender_type, args_type) \
1132 static HRESULT STDMETHODCALLTYPE event_name ## _QueryInterface ( iface_type * This, REFIID riid, void** ppvObject) \
1133 { \
1134 char* const this_ptr = (char*)This; \
1135 static size_t const obj_pos = offsetof(wv2_event_handlers_t, event_name); \
1136 IUnknown* unk_ptr = (IUnknown*)(this_ptr - obj_pos); \
1137 return wv2_event_handlers_QueryInterface(unk_ptr, riid, ppvObject); \
1138 } \
1139 static ULONG STDMETHODCALLTYPE event_name ## _AddRef ( iface_type * This) \
1140 { \
1141 char* const this_ptr = (char*)This; \
1142 static size_t const obj_pos = offsetof(wv2_event_handlers_t, event_name); \
1143 IUnknown* unk_ptr = (IUnknown*)(this_ptr - obj_pos); \
1144 return wv2_event_handlers_AddRef(unk_ptr); \
1145 } \
1146 static ULONG STDMETHODCALLTYPE event_name ## _Release( iface_type * This) \
1147 { \
1148 char* const this_ptr = (char*)This; \
1149 static size_t const obj_pos = offsetof(wv2_event_handlers_t, event_name); \
1150 IUnknown* unk_ptr = (IUnknown*)(this_ptr - obj_pos); \
1151 return wv2_event_handlers_Release(unk_ptr); \
1152 } \
1153 static HRESULT STDMETHODCALLTYPE event_name ## _Invoke( iface_type * This, sender_type sender, args_type args) \
1154 { \
1155 char* const this_ptr = (char*)This; \
1156 static size_t const obj_pos = offsetof(wv2_event_handlers_t, event_name); \
1157 wv2_event_handlers_t* evt_handlers_ptr = (wv2_event_handlers_t*)(this_ptr - obj_pos); \
1158 return event_name ## _wv2_callback (evt_handlers_ptr, sender, args); \
1159 } \
1160 static iface_type ## Vtbl event_name ## _wv2_event_handlers_vtbl = { \
1161 & event_name ## _QueryInterface, \
1162 & event_name ## _AddRef, \
1163 & event_name ## _Release, \
1164 & event_name ## _Invoke \
1165 }
1166
1167
1168
1169 typedef struct NewWindowRequested_task_class
1170 {
1171 GATE_INTERFACE_VTBL(gate_runnable) const* vtbl;
1172
1173 gate_atomic_int_t ref_counter;
1174 ICoreWebView2NewWindowRequestedEventArgs* args;
1175 ICoreWebView2Deferral* deferral;
1176 gate_ui_webview_t* webview;
1177 } NewWindowRequested_task_t;
1178
1179 static char const* NewWindowRequested_task_get_interface_name(void* ptr)
1180 {
1181 return GATE_INTERFACE_NAME_RUNNABLE;
1182 }
1183 static void NewWindowRequested_task_release(void* ptr)
1184 {
1185 NewWindowRequested_task_t* self = (NewWindowRequested_task_t*)ptr;
1186 if (0 == gate_atomic_int_dec(&self->ref_counter))
1187 {
1188 self->deferral->lpVtbl->Release(self->deferral);
1189 self->args->lpVtbl->Release(self->args);
1190 }
1191 }
1192 static int NewWindowRequested_task_retain(void* ptr)
1193 {
1194 NewWindowRequested_task_t* self = (NewWindowRequested_task_t*)ptr;
1195 return (int)gate_atomic_int_inc(&self->ref_counter);
1196 }
1197 static gate_result_t NewWindowRequested_task_run(void* ptr)
1198 {
1199 NewWindowRequested_task_t* self = (NewWindowRequested_task_t*)ptr;
1200 gate_ui_webview_t* ptr_new_webview = NULL;
1201
1202 self->webview->on_new_window(&self->webview->ctrl, &ptr_new_webview);
1203 if (ptr_new_webview)
1204 {
1205 gate_ui_webview_impl_t* impl = (gate_ui_webview_impl_t*)GATE_UI_WINAPI_GET_CTRL_PARAM(&ptr_new_webview->ctrl);
1206 self->args->lpVtbl->put_Handled(self->args, TRUE);
1207 self->args->lpVtbl->put_NewWindow(self->args, impl->wv2);
1208 }
1209 self->deferral->lpVtbl->Complete(self->deferral);
1210 return GATE_RESULT_OK;
1211 }
1212 GATE_INTERFACE_VTBL(gate_runnable) const NewWindowRequested_task_vtbl = {
1213 &NewWindowRequested_task_get_interface_name,
1214 &NewWindowRequested_task_release,
1215 &NewWindowRequested_task_retain,
1216 &NewWindowRequested_task_run
1217 };
1218
1219 static HRESULT NewWindowRequested_wv2_callback(wv2_event_handlers_t* handlers, ICoreWebView2* sender, ICoreWebView2NewWindowRequestedEventArgs* args)
1220 {
1221 gate_ui_webview_t* ptr_new_webview = NULL;
1222 ICoreWebView2Deferral* deferral = NULL;
1223 gate_ui_host_t* host = NULL;
1224 NewWindowRequested_task_t* task = NULL;
1225 HRESULT hr;
1226
1227 if (handlers && handlers->impl && handlers->impl->webview && handlers->impl->webview->on_new_window)
1228 {
1229 host = GATE_UI_WINAPI_GET_HOST(&handlers->impl->webview->ctrl);
1230 // gate_ui_host_execute(host, gate_runnable_create)
1231 hr = args->lpVtbl->GetDeferral(args, &deferral);
1232 if (SUCCEEDED(hr))
1233 {
1234 NewWindowRequested_task_t* task = gate_mem_alloc(sizeof(NewWindowRequested_task_t));
1235 if (!task)
1236 {
1237 deferral->lpVtbl->Release(deferral);
1238 }
1239 else
1240 {
1241 gate_mem_clear(task, sizeof(NewWindowRequested_task_t));
1242 task->vtbl = &NewWindowRequested_task_vtbl;
1243 gate_atomic_int_init(&task->ref_counter, 1);
1244 task->args = args;
1245 task->deferral = deferral;
1246 task->args->lpVtbl->AddRef(task->args);
1247 task->deferral->lpVtbl->AddRef(task->deferral);
1248 task->webview = handlers->impl->webview;
1249 gate_ui_winapi_host_post_runner(host, (gate_runnable_t*)task, true, false);
1250 task = NULL;
1251 }
1252 }
1253 }
1254 if (task)
1255 {
1256 gate_object_release(task);
1257 }
1258 return S_OK;
1259 }
1260 CREATE_WV2_EVENT_HANDLER_VTBL(NewWindowRequested, ICoreWebView2NewWindowRequestedEventHandler, ICoreWebView2*, ICoreWebView2NewWindowRequestedEventArgs*);
1261
1262
1263 static HRESULT NavigationStarting_wv2_callback(wv2_event_handlers_t* handlers, ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args)
1264 {
1265 return S_OK;
1266 }
1267 CREATE_WV2_EVENT_HANDLER_VTBL(NavigationStarting, ICoreWebView2NavigationStartingEventHandler, ICoreWebView2*, ICoreWebView2NavigationStartingEventArgs*);
1268
1269
1270 static HRESULT NavigationCompleted_wv2_callback(wv2_event_handlers_t* handlers, ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args)
1271 {
1272 LPWSTR wstrUrl = NULL;
1273 HRESULT hr;
1274 gate_string_t url = GATE_STRING_INIT_EMPTY;
1275
1276 if (handlers && handlers->impl && handlers->impl->webview && handlers->impl->webview->on_navigate_complete)
1277 {
1278 hr = sender->lpVtbl->get_Source(sender, &wstrUrl);
1279 if (SUCCEEDED(hr))
1280 {
1281 gate_string_create_utf16(&url, wstrUrl, gate_str16_length(wstrUrl));
1282 }
1283 handlers->impl->webview->on_navigate_complete(&handlers->impl->webview->ctrl, &url);
1284 }
1285 gate_string_release(&url);
1286 if (wstrUrl)
1287 {
1288 // CoTaskMemFree needs to free the URL
1289 gate_win32_com_dealloc(wstrUrl);
1290 }
1291 return S_OK;
1292 }
1293 CREATE_WV2_EVENT_HANDLER_VTBL(NavigationCompleted, ICoreWebView2NavigationCompletedEventHandler, ICoreWebView2*, ICoreWebView2NavigationCompletedEventArgs*);
1294
1295
1296
1297
1298 static wv2_event_handlers_t* create_webview2_event_handlers(gate_ui_webview_impl_t* impl)
1299 {
1300 wv2_event_handlers_t* handlers = NULL;
1301 do
1302 {
1303 handlers = (wv2_event_handlers_t*)gate_mem_alloc(sizeof(wv2_event_handlers_t));
1304 if (NULL == handlers)
1305 {
1306 break;
1307 }
1308 gate_mem_clear(handlers, sizeof(wv2_event_handlers_t));
1309 gate_atomic_int_init(&handlers->ref_counter, 1);
1310
1311 handlers->impl = impl;
1312 handlers->vtblUnkown = &IUnknown_wv2_event_handlers_vtbl;
1313 handlers->NewWindowRequested.lpVtbl = &NewWindowRequested_wv2_event_handlers_vtbl;
1314 handlers->NavigationStarting.lpVtbl = &NavigationStarting_wv2_event_handlers_vtbl;
1315 handlers->NavigationCompleted.lpVtbl = &NavigationCompleted_wv2_event_handlers_vtbl;
1316
1317 wv2_event_handlers_attach_events(handlers);
1318 } while (0);
1319 return handlers;
1320
1321 }
1322
1323 static gate_ui_webview_impl_t* create_webview2_impl(gate_ui_webview_t* webview, ICoreWebView2Controller* controller)
1324 {
1325 gate_ui_webview_impl_t* impl = NULL;
1326 wv2_event_handlers_t* handlers = NULL;
1327 HRESULT hr;
1328
1329 do
1330 {
1331 impl = (gate_ui_webview_impl_t*)gate_mem_alloc(sizeof(gate_ui_webview_impl_t));
1332 if (!impl)
1333 {
1334 break;
1335 }
1336
1337 gate_mem_clear(impl, sizeof(gate_ui_webview_impl_t));
1338 impl->webview = webview;
1339 impl->wv2_controller = controller;
1340 hr = impl->wv2_controller->lpVtbl->get_CoreWebView2(impl->wv2_controller, &impl->wv2);
1341 if (FAILED(hr))
1342 {
1343 gate_mem_dealloc(impl);
1344 impl = NULL;
1345 break;
1346 }
1347 handlers = create_webview2_event_handlers(impl);
1348 if (!handlers)
1349 {
1350 gate_mem_dealloc(impl);
1351 impl = NULL;
1352 break;
1353 }
1354 impl->wv2_event_handlers = (IUnknown*)handlers;
1355
1356 hr = impl->wv2_controller->lpVtbl->put_IsVisible(impl->wv2_controller, TRUE);
1357
1358 } while (0);
1359
1360 return impl;
1361 }
1362
1363
1364 static gate_result_t gate_ui_webview_create_wv2(gate_ui_webview_t* webview, gate_ui_ctrl_t* parent,
1365 gate_ui_position_t const* position, gate_uint32_t flags, void* userparam)
1366 {
1367 gate_result_t ret;
1368 HRESULT hr;
1369 HWND hwnd_parent = NULL;
1370 HWND hwnd_ctrl = NULL;
1371 gate_ui_host_t* host = NULL;
1372 gate_uint32_t exstyles = 0;
1373 gate_uint32_t styles = 0;
1374 gate_ui_webview_impl_t* impl = NULL;
1375 ICoreWebView2Controller* controller = NULL;
1376 static ICoreWebView2Environment* global_wv2_environment = NULL;
1377
1378 host = GATE_UI_WINAPI_GET_HOST(parent);
1379 hwnd_parent = GATE_UI_WINAPI_GET_HWND(parent);
1380
1381 do
1382 {
1383 ret = gate_win32_com_init();
1384
1385 ret = load_webview2();
1386 GATE_BREAK_IF_FAILED(ret);
1387
1388 if (global_wv2_environment == NULL)
1389 {
1390 ret = wv2_event_init(&wv2_env_completed.semaphore);
1391 GATE_BREAK_IF_FAILED(ret);
1392
1393 wv2_env_completed.env = NULL;
1394 hr = webview2_loader.CreateCoreWebView2EnvironmentWithOptions(NULL, NULL, NULL, (ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler*)&wv2_env_completed);
1395
1396 wv2_event_await(wv2_env_completed.semaphore);
1397 if (!wv2_env_completed.env)
1398 {
1399 break;
1400 }
1401 global_wv2_environment = wv2_env_completed.env;
1402 }
1403
1404 exstyles = 0;
1405 #if !defined(GATE_SYS_WINCE)
1406 exstyles |= WS_EX_CONTROLPARENT;
1407 #endif
1408 styles = WS_GROUP | WS_CHILD | WS_TABSTOP | WS_CLIPCHILDREN;
1409 if (!GATE_FLAG_ENABLED(flags, GATE_UI_FLAG_ENABLED)) styles |= WS_DISABLED;
1410 if (GATE_FLAG_ENABLED(flags, GATE_UI_FLAG_VISIBLE)) styles |= WS_VISIBLE;
1411
1412 ret = gate_ui_winapi_create(&webview->ctrl, host, hwnd_parent, NULL, position, styles, exstyles, NULL, userparam, false);
1413 GATE_BREAK_IF_FAILED(ret);
1414
1415 GATE_UI_WINAPI_SET_DISPATCHER(&webview->ctrl, &global_webview_dispatchers);
1416 hwnd_ctrl = GATE_UI_WINAPI_GET_HWND(&webview->ctrl);
1417
1418 ret = wv2_event_init(&wv2_ctrl_completed.semaphore);
1419 GATE_BREAK_IF_FAILED(ret);
1420
1421 wv2_ctrl_completed.ctrl = NULL;
1422 hr = global_wv2_environment->lpVtbl->CreateCoreWebView2Controller(global_wv2_environment, hwnd_ctrl, (ICoreWebView2CreateCoreWebView2ControllerCompletedHandler*)&wv2_ctrl_completed);
1423
1424 ret = wv2_event_await(wv2_ctrl_completed.semaphore);
1425 if (!wv2_ctrl_completed.ctrl)
1426 {
1427 ret = GATE_RESULT_NOTAVAILABLE;
1428 break;
1429 }
1430 controller = wv2_ctrl_completed.ctrl;
1431 wv2_ctrl_completed.ctrl = NULL;
1432
1433 impl = create_webview2_impl(webview, controller);
1434 if (impl == NULL)
1435 {
1436 ret = GATE_RESULT_OUTOFMEMORY;
1437 break;
1438 }
1439 GATE_UI_WINAPI_SET_CTRL_PARAM(&webview->ctrl, impl);
1440
1441 gate_ui_winapi_register_event(host, (void*)hwnd_ctrl, WM_SIZE, &webview_windowproc_events, &webview->ctrl);
1442
1443 ret = GATE_RESULT_OK;
1444 } while (0);
1445
1446 if (GATE_FAILED(ret))
1447 {
1448 if (controller)
1449 {
1450 controller->lpVtbl->Release(controller);
1451 }
1452 if (hwnd_ctrl)
1453 {
1454 gate_ui_winapi_destroy(&webview->ctrl);
1455 }
1456 if (impl != NULL)
1457 {
1458 }
1459 }
1460
1461 return ret;
1462 }
1463
1464 static gate_result_t wv2_navigate_to(gate_ui_webview_impl_t* impl, gate_string_t const* address)
1465 {
1466 gate_result_t ret = GATE_RESULT_FAILED;
1467 gate_cstrbuffer16_t buffer = GATE_INIT_EMPTY;
1468 HRESULT hr;
1469
1470 do
1471 {
1472 if (NULL == gate_cstrbuffer16_create_string(&buffer, address))
1473 {
1474 ret = GATE_RESULT_OUTOFMEMORY;
1475 break;
1476 }
1477 hr = impl->wv2->lpVtbl->Navigate(impl->wv2, gate_cstrbuffer16_get(&buffer));
1478 if (FAILED(hr))
1479 {
1480 ret = GATE_RESULT_FAILED;
1481 break;
1482 }
1483
1484 ret = GATE_RESULT_OK;
1485 } while (0);
1486
1487 gate_cstrbuffer16_destroy(&buffer);
1488 return ret;
1489 }
1490
1491 static gate_result_t wv2_execute(gate_ui_webview_impl_t* impl, gate_enumint_t command)
1492 {
1493 HRESULT hr;
1494
1495 switch (command)
1496 {
1497 case GATE_UI_WEBVIEW_COMMAND_STOP:
1498 {
1499 hr = impl->wv2->lpVtbl->Stop(impl->wv2);
1500 return SUCCEEDED(hr) ? GATE_RESULT_OK : GATE_RESULT_FAILED;
1501 }
1502 case GATE_UI_WEBVIEW_COMMAND_REFRESH:
1503 {
1504 hr = impl->wv2->lpVtbl->Reload(impl->wv2);
1505 return SUCCEEDED(hr) ? GATE_RESULT_OK : GATE_RESULT_FAILED;
1506 }
1507 case GATE_UI_WEBVIEW_COMMAND_GOHOME:
1508 {
1509 break;
1510 }
1511 case GATE_UI_WEBVIEW_COMMAND_GOBACK:
1512 {
1513 hr = impl->wv2->lpVtbl->GoBack(impl->wv2);
1514 return SUCCEEDED(hr) ? GATE_RESULT_OK : GATE_RESULT_FAILED;
1515 }
1516 case GATE_UI_WEBVIEW_COMMAND_GONEXT:
1517 {
1518 hr = impl->wv2->lpVtbl->GoForward(impl->wv2);
1519 return SUCCEEDED(hr) ? GATE_RESULT_OK : GATE_RESULT_FAILED;
1520 }
1521 }
1522 return GATE_RESULT_NOTSUPPORTED;
1523 }
1524 #endif /* GATE_UI_WEBVIEW_EDGEWV2_IMPL */
1525
1526
1527
1528
1529
1530 /*******************************/
1531 /* */
1532 /* GATE webview control code: */
1533 /* */
1534 /*******************************/
1535
1536
1537 static gate_result_t webview_destroy_impl(gate_ui_ctrl_t* ctrl)
1538 {
1539 gate_ui_webview_impl_t* impl = (gate_ui_webview_impl_t*)GATE_UI_WINAPI_GET_CTRL_PARAM(ctrl);
1540
1541 GATE_UI_WINAPI_SET_CTRL_PARAM(ctrl, NULL);
1542
1543 if (impl != NULL)
1544 {
1545 #ifdef GATE_UI_WEBVIEW_EDGEWV2_IMPL
1546
1547 if (impl->wv2_event_handlers)
1548 {
1549 wv2_event_handlers_t* wv2_handlers = (wv2_event_handlers_t*)impl->wv2_event_handlers;
1550 wv2_event_handlers_detach_events(wv2_handlers);
1551 impl->wv2_event_handlers->lpVtbl->Release(impl->wv2_event_handlers);
1552 impl->wv2_event_handlers = NULL;
1553 }
1554 if (impl->wv2)
1555 {
1556 impl->wv2->lpVtbl->Release(impl->wv2);
1557 impl->wv2 = NULL;
1558 }
1559 if (impl->wv2_controller)
1560 {
1561 impl->wv2_controller->lpVtbl->Close(impl->wv2_controller);
1562 impl->wv2_controller->lpVtbl->Release(impl->wv2_controller);
1563 impl->wv2_controller = NULL;
1564 }
1565 #endif
1566
1567 #ifdef GATE_UI_WEBVIEW_ATLWEBBROWSER_IMPL
1568 if (impl->webbrowser2)
1569 {
1570 impl->webbrowser2->lpVtbl->Release(impl->webbrowser2);
1571 impl->webbrowser2 = NULL;
1572 }
1573
1574 if (impl->webbrowser)
1575 {
1576 impl->webbrowser->lpVtbl->Release(impl->webbrowser);
1577 impl->webbrowser = NULL;
1578 }
1579 if (impl->webbrowser_events)
1580 {
1581 impl->webbrowser_events->lpVtbl->Release(impl->webbrowser_events);
1582 impl->webbrowser_events = NULL;
1583 }
1584 #endif
1585
1586 impl->webview = NULL;
1587 if (impl->hwnd)
1588 {
1589 DestroyWindow(impl->hwnd);
1590 impl->hwnd = NULL;
1591 }
1592 gate_mem_dealloc(impl);
1593 }
1594 return gate_ui_winapi_destroy(ctrl);
1595 }
1596
1597
1598 static gate_bool_t webview_windowproc_events(void* hwnd, gate_ui_ctrl_t* ctrl, gate_uint32_t msg, gate_uintptr_t wParam, gate_intptr_t lParam, gate_intptr_t* lresult)
1599 {
1600 HWND hwndctrl;
1601 gate_ui_webview_impl_t* impl = NULL;
1602 RECT rect;
1603
1604 if (!ctrl)
1605 {
1606 return false;
1607 }
1608
1609 hwndctrl = GATE_UI_WINAPI_GET_HWND(ctrl);
1610 impl = (gate_ui_webview_impl_t*)GATE_UI_WINAPI_GET_CTRL_PARAM(ctrl);
1611 if (!hwndctrl || !impl || (hwndctrl != hwnd))
1612 {
1613 return false;
1614 }
1615
1616 switch (msg)
1617 {
1618 case WM_SIZE:
1619 {
1620 GetClientRect(hwndctrl, &rect);
1621
1622 #ifdef GATE_UI_WEBVIEW_EDGEWV2_IMPL
1623 if (impl->wv2_controller)
1624 {
1625 impl->wv2_controller->lpVtbl->put_Bounds(impl->wv2_controller, rect);
1626 }
1627 #endif
1628
1629 if (impl->hwnd)
1630 {
1631 SetWindowPos(impl->hwnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
1632 SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
1633 *lresult = 0;
1634 return true;
1635 }
1636 break;
1637 }
1638 }
1639 return false;
1640 }
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652 gate_result_t gate_ui_webview_create(gate_ui_webview_t* webview, gate_ui_ctrl_t* parent,
1653 gate_ui_position_t const* position, gate_uint32_t flags, void* userparam)
1654 {
1655 gate_result_t ret = GATE_RESULT_FAILED;
1656 do
1657 {
1658
1659 #ifdef GATE_UI_WEBVIEW_EDGEWV2_IMPL
1660 ret = gate_ui_webview_create_wv2(webview, parent, position, flags, userparam);
1661 if (GATE_SUCCEEDED(ret))
1662 {
1663 break;
1664 }
1665 #endif
1666
1667 #ifdef GATE_UI_WEBVIEW_ATLWEBBROWSER_IMPL
1668 ret = gate_ui_webview_create_activex(webview, parent, position, flags, userparam);
1669 if (GATE_SUCCEEDED(ret))
1670 {
1671 break;
1672 }
1673 #endif
1674
1675 } while (0);
1676 return ret;
1677 }
1678
1679
1680
1681
1682 gate_result_t gate_ui_webview_navigate_to(gate_ui_webview_t* webview, gate_string_t const* address)
1683 {
1684 gate_result_t ret = GATE_RESULT_FAILED;
1685 gate_ui_webview_impl_t* impl = NULL;
1686
1687 if (webview)
1688 {
1689 impl = (gate_ui_webview_impl_t*)GATE_UI_WINAPI_GET_CTRL_PARAM(&webview->ctrl);
1690 }
1691
1692 do
1693 {
1694 if (!impl)
1695 {
1696 ret = GATE_RESULT_NOTAVAILABLE;
1697 break;
1698 }
1699
1700 #ifdef GATE_UI_WEBVIEW_EDGEWV2_IMPL
1701 if (impl->wv2)
1702 {
1703 ret = wv2_navigate_to(impl, address);
1704 break;
1705 }
1706 #endif
1707
1708 #ifdef GATE_UI_WEBVIEW_ATLWEBBROWSER_IMPL
1709 if (impl->webbrowser || impl->webbrowser2)
1710 {
1711 ret = webbrowser_navigate_to(impl, address);
1712 break;
1713 }
1714 #endif
1715
1716 } while (0);
1717
1718 return ret;
1719 }
1720
1721
1722
1723
1724 gate_result_t gate_ui_webview_execute(gate_ui_webview_t* webview, gate_enumint_t command)
1725 {
1726 gate_result_t ret = GATE_RESULT_FAILED;
1727 gate_ui_webview_impl_t* impl = NULL;
1728
1729 do
1730 {
1731 if (webview)
1732 {
1733 impl = (gate_ui_webview_impl_t*)GATE_UI_WINAPI_GET_CTRL_PARAM(&webview->ctrl);
1734 }
1735 if (!impl)
1736 {
1737 ret = GATE_RESULT_NOTAVAILABLE;
1738 break;
1739 }
1740
1741 #ifdef GATE_UI_WEBVIEW_EDGEWV2_IMPL
1742 if (impl->wv2)
1743 {
1744 ret = wv2_execute(impl, command);
1745 break;
1746 }
1747 #endif
1748
1749 #ifdef GATE_UI_WEBVIEW_ATLWEBBROWSER_IMPL
1750 if (impl->webbrowser || impl->webbrowser2)
1751 {
1752 ret = webbrowser_execute(impl, command);
1753 break;
1754 }
1755 #endif
1756
1757 } while (0);
1758
1759 return ret;
1760 }
1761
1762
1763 #endif
1764
1765 #if defined(GATE_UI_GTK)
1766
1767 gate_result_t gate_ui_webview_create(gate_ui_webview_t* webview, gate_ui_ctrl_t* parent,
1768 gate_ui_position_t const* position, gate_uint32_t flags, void* userparam)
1769 {
1770 return GATE_RESULT_NOTIMPLEMENTED;
1771 }
1772
1773 gate_result_t gate_ui_webview_navigate_to(gate_ui_webview_t* webview, gate_string_t const* address)
1774 {
1775 return GATE_RESULT_NOTIMPLEMENTED;
1776 }
1777
1778 gate_result_t gate_ui_webview_execute(gate_ui_webview_t* webview, gate_enumint_t command)
1779 {
1780 return GATE_RESULT_NOTIMPLEMENTED;
1781 }
1782
1783
1784 #endif /* GATE_UI_GTK */
1785
1786
1787
1788 #if defined(GATE_UI_MOTIF)
1789
1790 gate_result_t gate_ui_webview_create(gate_ui_webview_t* webview, gate_ui_ctrl_t* parent,
1791 gate_ui_position_t const* position, gate_uint32_t flags, void* userparam)
1792 {
1793 return GATE_RESULT_NOTIMPLEMENTED;
1794 }
1795
1796 gate_result_t gate_ui_webview_navigate_to(gate_ui_webview_t* webview, gate_string_t const* address)
1797 {
1798 return GATE_RESULT_NOTIMPLEMENTED;
1799 }
1800
1801 gate_result_t gate_ui_webview_execute(gate_ui_webview_t* webview, gate_enumint_t command)
1802 {
1803 return GATE_RESULT_NOTIMPLEMENTED;
1804 }
1805
1806 #endif /* GATE_UI_MOTIF */
1807