Since `EVENT_SYSTEM_MINIMIZESTART` and `EVENT_SYSTEM_MINIMIZEEND` are implemented now (thanks to Esme), this is implementing visibility change events using them. We have to keep track of the browsers to iterate all the documents from the hook.
Note that I tried to refcount the per-thread hook and add it only when necessary (on a visiblity change listener), since it can slow down the entire prefix.
-- v2: mshtml: Implement visibilitychange event.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlevent.c | 57 +++++++++++++++++++++++++++++++++++- dlls/mshtml/mshtml_private.h | 5 ++++ dlls/mshtml/nsembed.c | 13 ++++++-- dlls/mshtml/task.c | 1 + dlls/mshtml/tests/events.c | 9 ++---- 5 files changed, 74 insertions(+), 11 deletions(-)
diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index f83153fc084..130b1668d1e 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -218,7 +218,7 @@ static const event_info_t event_info[] = { {L"unload", EVENT_TYPE_UIEVENT, DISPID_EVMETH_ONUNLOAD, EVENT_BIND_TO_TARGET}, {L"visibilitychange", EVENT_TYPE_EVENT, DISPID_EVPROP_VISIBILITYCHANGE, - EVENT_FIXME | EVENT_BUBBLES}, + EVENT_BUBBLES},
/* EVENTID_LAST special entry */ {NULL, EVENT_TYPE_EVENT, 0, 0} @@ -266,6 +266,49 @@ const WCHAR *get_event_name(eventid_t eid) return event_info[eid].name; }
+static void CALLBACK minimize_event_hook(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD idEventThread, DWORD dwmsEventTime) +{ + thread_data_t *thread_data = get_thread_data(FALSE); + HTMLDocumentNode *doc; + GeckoBrowser *browser; + DOMEvent *dom_event; + HRESULT hres; + + assert(thread_data != NULL); + if(thread_data->minimize_hook != hWinEventHook) + return; + + LIST_FOR_EACH_ENTRY(browser, &thread_data->browsers, GeckoBrowser, entry) { + if(hwnd == GetAncestor(browser->doc->hwnd, GA_ROOT)) { + LIST_FOR_EACH_ENTRY(doc, &browser->document_nodes, HTMLDocumentNode, browser_entry) { + if(doc->document_mode >= COMPAT_MODE_IE10) { + hres = create_document_event(doc, EVENTID_VISIBILITYCHANGE, &dom_event); + if(SUCCEEDED(hres)) { + dispatch_event(&doc->node.event_target, dom_event); + IDOMEvent_Release(&dom_event->IDOMEvent_iface); + } + } + } + } + } +} + +static BOOL install_minimize_event_hook(void) +{ + thread_data_t *thread_data = get_thread_data(TRUE); + + if(!thread_data) + return FALSE; + + if(!thread_data->minimize_hook) { + thread_data->minimize_hook = SetWinEventHook(EVENT_SYSTEM_MINIMIZESTART, EVENT_SYSTEM_MINIMIZEEND, NULL, minimize_event_hook, 0, 0, WINEVENT_OUTOFCONTEXT); + if(!thread_data->minimize_hook) + return FALSE; + } + thread_data->minimize_hook_ref++; + return TRUE; +} + static listener_container_t *get_listener_container(EventTarget *event_target, const WCHAR *type, BOOL alloc) { const event_target_vtbl_t *vtbl; @@ -290,6 +333,12 @@ static listener_container_t *get_listener_container(EventTarget *event_target, c return NULL; memcpy(container->type, type, (type_len + 1) * sizeof(WCHAR)); list_init(&container->listeners); + + if(eid == EVENTID_VISIBILITYCHANGE && !install_minimize_event_hook()) { + free(container); + return NULL; + } + vtbl = dispex_get_vtbl(&event_target->dispex); if (!vtbl->bind_event) FIXME("Unsupported event binding on target %p\n", event_target); @@ -5136,9 +5185,15 @@ void traverse_event_target(EventTarget *event_target, nsCycleCollectionTraversal
void release_event_target(EventTarget *event_target) { + thread_data_t *thread_data = get_thread_data(FALSE); listener_container_t *iter, *iter2;
WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter, iter2, &event_target->handler_map, listener_container_t, entry) { + if(thread_data && !wcscmp(iter->type, L"visibilitychange") && !--thread_data->minimize_hook_ref) { + UnhookWinEvent(thread_data->minimize_hook); + thread_data->minimize_hook = NULL; + thread_data = NULL; + } while(!list_empty(&iter->listeners)) { event_listener_t *listener = LIST_ENTRY(list_head(&iter->listeners), event_listener_t, entry); list_remove(&listener->entry); diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 4b2cffd0f54..c1b51c687fb 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -858,6 +858,8 @@ struct GeckoBrowser {
struct list document_nodes; struct list outer_windows; + + struct list entry; };
typedef struct { @@ -1352,12 +1354,15 @@ typedef struct {
typedef struct { HWND thread_hwnd; + struct list browsers; struct list task_list; struct list event_task_list; struct list timer_list; struct list *pending_xhr_events_tail; struct wine_rb_tree session_storage_map; void *blocking_xhr; + HWINEVENTHOOK minimize_hook; + ULONG minimize_hook_ref; } thread_data_t;
thread_data_t *get_thread_data(BOOL); diff --git a/dlls/mshtml/nsembed.c b/dlls/mshtml/nsembed.c index 1b31e4f7159..53e02bf3eda 100644 --- a/dlls/mshtml/nsembed.c +++ b/dlls/mshtml/nsembed.c @@ -2292,12 +2292,16 @@ static HRESULT init_browser(GeckoBrowser *browser)
HRESULT create_gecko_browser(HTMLDocumentObj *doc, GeckoBrowser **_ret) { + thread_data_t *thread_data; GeckoBrowser *ret; HRESULT hres;
if(!load_gecko()) return CLASS_E_CLASSNOTAVAILABLE;
+ if(!(thread_data = get_thread_data(TRUE))) + return E_OUTOFMEMORY; + ret = calloc(1, sizeof(GeckoBrowser)); if(!ret) return E_OUTOFMEMORY; @@ -2318,10 +2322,12 @@ HRESULT create_gecko_browser(HTMLDocumentObj *doc, GeckoBrowser **_ret) list_init(&ret->outer_windows);
hres = init_browser(ret); - if(SUCCEEDED(hres)) - *_ret = ret; - else + if(FAILED(hres)) nsIWebBrowserChrome_Release(&ret->nsIWebBrowserChrome_iface); + else { + *_ret = ret; + list_add_tail(&thread_data->browsers, &ret->entry); + } return hres; }
@@ -2331,6 +2337,7 @@ void detach_gecko_browser(GeckoBrowser *This)
TRACE("(%p)\n", This);
+ list_remove(&This->entry); This->doc = NULL;
if(This->content_window) { diff --git a/dlls/mshtml/task.c b/dlls/mshtml/task.c index 49f76d101a9..067f2efd119 100644 --- a/dlls/mshtml/task.c +++ b/dlls/mshtml/task.c @@ -458,6 +458,7 @@ thread_data_t *get_thread_data(BOOL create) return NULL;
TlsSetValue(mshtml_tls, thread_data); + list_init(&thread_data->browsers); list_init(&thread_data->task_list); list_init(&thread_data->event_task_list); list_init(&thread_data->timer_list); diff --git a/dlls/mshtml/tests/events.c b/dlls/mshtml/tests/events.c index 9760d6bcb9a..cced0754bde 100644 --- a/dlls/mshtml/tests/events.c +++ b/dlls/mshtml/tests/events.c @@ -3498,19 +3498,14 @@ static void test_visibilitychange(IHTMLDocument2 *doc) ShowWindow(container_hwnd, SW_RESTORE); pump_msgs(NULL); }else { - /* FIXME: currently not implemented in Wine, so we can't wait for it */ - BOOL *expect = broken(1) ? &called_visibilitychange : NULL; - SET_EXPECT(visibilitychange); ShowWindow(container_hwnd, SW_MINIMIZE); - pump_msgs(expect); - todo_wine + pump_msgs(&called_visibilitychange); CHECK_CALLED(visibilitychange);
SET_EXPECT(visibilitychange); ShowWindow(container_hwnd, SW_RESTORE); - pump_msgs(expect); - todo_wine + pump_msgs(&called_visibilitychange); CHECK_CALLED(visibilitychange); }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=146340
Your paranoid android.
=== debian11b (64 bit WoW report) ===
mshtml: events: Timeout
user32: input.c:4305: Test succeeded inside todo block: button_down_hwnd_todo 1: got MSG_TEST_WIN hwnd 0000000000A600E2, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032
The random failure seems to be from winex11 quirk: `fixme:event:wait_for_withdrawn_state window 0x1100ae/7a00005 wait timed out`
Having `wait_for_withdrawn_state` be a no-op fixes it for me but obviously it's there for a reason. Not sure how to relax the tests, this happens because of the SW_SHOW and then SW_HIDE at the top of `test_visibilitychange`, rarely. Retrying until it's actually visible doesn't work, because we can't query X11 compositor from test code (and anyway it wouldn't work with other drivers).
Maybe moving those hide/show after the minimize?