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 | 37 ++++++++++++++++++----- 5 files changed, 102 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..65d76c11d89 100644 --- a/dlls/mshtml/tests/events.c +++ b/dlls/mshtml/tests/events.c @@ -2002,6 +2002,29 @@ static void pump_msgs(BOOL *b) } }
+#define pump_msgs_timeout(a,b) pump_msgs_timeout_(a,b,__LINE__) +static void pump_msgs_timeout_(BOOL *b, DWORD timeout, unsigned line) +{ + DWORD end = GetTickCount() + timeout, t; + MSG msg; + + while(!*b && end - (t = GetTickCount()) <= timeout) { + UINT_PTR timer = SetTimer(NULL, 0, end - t, NULL); + BOOL ret = GetMessageW(&msg, NULL, 0, 0); + + KillTimer(NULL, timer); + if(ret <= 0 || (msg.message == WM_TIMER && msg.wParam == timer)) + break; + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + while(PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + ok_(__FILE__,line)(*b, "timed out\n"); +} + static IOleCommandTarget cmdtarget, cmdtarget_stub;
static HRESULT WINAPI cmdtarget_QueryInterface(IOleCommandTarget *iface, REFIID riid, void **ppv) @@ -3498,19 +3521,19 @@ 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; - + /* Wine can be flaky here depending on driver and compositor (on X11 wait_for_withdrawn_state can time out randomly) */ SET_EXPECT(visibilitychange); ShowWindow(container_hwnd, SW_MINIMIZE); - pump_msgs(expect); - todo_wine + flaky + pump_msgs_timeout(&called_visibilitychange, 3000); + flaky CHECK_CALLED(visibilitychange);
SET_EXPECT(visibilitychange); ShowWindow(container_hwnd, SW_RESTORE); - pump_msgs(expect); - todo_wine + flaky + pump_msgs_timeout(&called_visibilitychange, 3000); + flaky CHECK_CALLED(visibilitychange); }