From: Gabriel Ivăncescu gabrielopcode@gmail.com
In addition to increasing it during task processing itself, we also have to do it from any wine-gecko callbacks that can lead to a message loop (calling into external code).
Note that native still sends a synchronous readyState notification during SuperNavigate (which we don't yet).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/mshtml_private.h | 3 +- dlls/mshtml/mutation.c | 7 +++ dlls/mshtml/navigate.c | 20 +++++-- dlls/mshtml/nsevents.c | 7 +++ dlls/mshtml/nsio.c | 17 +++++- dlls/mshtml/olecmd.c | 11 +++- dlls/mshtml/task.c | 18 ++++-- dlls/mshtml/tests/events.c | 103 ++++++++++++++++++++++++++++++++++- dlls/mshtml/xmlhttprequest.c | 2 +- 9 files changed, 171 insertions(+), 17 deletions(-)
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 582cc109d31..e49b2f70245 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1493,12 +1493,13 @@ typedef struct { struct list *pending_xhr_events_tail; struct wine_rb_tree session_storage_map; void *blocking_xhr; + unsigned tasks_locked; BOOL timer_blocked; } thread_data_t;
thread_data_t *get_thread_data(BOOL); HWND get_thread_hwnd(void); -void unblock_tasks_and_timers(thread_data_t*); +void unblock_tasks_and_timers(thread_data_t*,BOOL); int session_storage_map_cmp(const void*,const struct wine_rb_entry*); void destroy_session_storage(thread_data_t*);
diff --git a/dlls/mshtml/mutation.c b/dlls/mshtml/mutation.c index d8c0cb50b10..0aa44d2b3a6 100644 --- a/dlls/mshtml/mutation.c +++ b/dlls/mshtml/mutation.c @@ -293,11 +293,16 @@ static nsresult run_end_load(HTMLDocumentNode *This, nsISupports *arg1, nsISuppo { HTMLDocumentObj *doc_obj = This->doc_obj; HTMLInnerWindow *window = This->window; + thread_data_t *thread_data;
TRACE("(%p)\n", This);
if(!doc_obj) return NS_OK; + + if(!(thread_data = get_thread_data(FALSE))) + return NS_ERROR_OUT_OF_MEMORY; + thread_data->tasks_locked++; IHTMLWindow2_AddRef(&window->base.IHTMLWindow2_iface);
if(This == doc_obj->doc_node) { @@ -317,6 +322,8 @@ static nsresult run_end_load(HTMLDocumentNode *This, nsISupports *arg1, nsISuppo set_ready_state(window->base.outer_window, READYSTATE_INTERACTIVE); } IHTMLWindow2_Release(&window->base.IHTMLWindow2_iface); + if(!--thread_data->tasks_locked) + unblock_tasks_and_timers(thread_data, TRUE); return NS_OK; }
diff --git a/dlls/mshtml/navigate.c b/dlls/mshtml/navigate.c index 90ca22ea8b1..36e18c5cd8a 100644 --- a/dlls/mshtml/navigate.c +++ b/dlls/mshtml/navigate.c @@ -1435,6 +1435,7 @@ static HRESULT async_stop_request(nsChannelBSC *This)
static void handle_navigation_error(nsChannelBSC *This, DWORD result) { + thread_data_t *thread_data = get_thread_data(FALSE); HTMLOuterWindow *outer_window; HTMLDocumentObj *doc; BOOL is_error_url; @@ -1446,27 +1447,28 @@ static void handle_navigation_error(nsChannelBSC *This, DWORD result) HRESULT hres;
if(!This->is_doc_channel || !This->bsc.window || !This->bsc.window->base.outer_window - || !This->bsc.window->base.outer_window->browser) + || !This->bsc.window->base.outer_window->browser || !thread_data) return;
outer_window = This->bsc.window->base.outer_window; doc = outer_window->browser->doc; if(!doc->doc_object_service || !doc->client) return; + thread_data->tasks_locked++;
hres = IDocObjectService_IsErrorUrl(doc->doc_object_service, outer_window->url, &is_error_url); if(FAILED(hres) || is_error_url) - return; + goto done;
if(!doc->client_cmdtrg) - return; + goto done;
bound.lLbound = 0; bound.cElements = 8; sa = SafeArrayCreate(VT_VARIANT, 1, &bound); if(!sa) - return; + goto done;
ind = 0; V_VT(&var) = VT_I4; @@ -1518,6 +1520,10 @@ static void handle_navigation_error(nsChannelBSC *This, DWORD result)
SysFreeString(unk); SafeArrayDestroy(sa); + +done: + if(!--thread_data->tasks_locked) + unblock_tasks_and_timers(thread_data, TRUE); }
static HRESULT nsChannelBSC_stop_binding(BSCallback *bsc, HRESULT result) @@ -1636,6 +1642,7 @@ static IUri *get_moniker_uri(IMoniker *mon)
static void handle_extern_mime_navigation(nsChannelBSC *This) { + thread_data_t *thread_data = get_thread_data(FALSE); IWebBrowserPriv2IE9 *webbrowser_priv; IOleCommandTarget *cmdtrg; HTMLDocumentObj *doc_obj; @@ -1644,9 +1651,10 @@ static void handle_extern_mime_navigation(nsChannelBSC *This) VARIANT flags; HRESULT hres;
- if(!This->bsc.window || !This->bsc.window->base.outer_window || !This->bsc.window->base.outer_window->browser) + if(!This->bsc.window || !This->bsc.window->base.outer_window || !This->bsc.window->base.outer_window->browser || !thread_data) return;
+ thread_data->tasks_locked++; doc_obj = This->bsc.window->base.outer_window->browser->doc; IUnknown_AddRef(doc_obj->outer_unk);
@@ -1701,6 +1709,8 @@ static void handle_extern_mime_navigation(nsChannelBSC *This) IUri_Release(uri);
done: + if(!--thread_data->tasks_locked) + unblock_tasks_and_timers(thread_data, TRUE); IUnknown_Release(doc_obj->outer_unk); }
diff --git a/dlls/mshtml/nsevents.c b/dlls/mshtml/nsevents.c index 0604fb997c7..ba6ce056347 100644 --- a/dlls/mshtml/nsevents.c +++ b/dlls/mshtml/nsevents.c @@ -308,12 +308,15 @@ static void handle_docobj_load(HTMLDocumentObj *doc)
static nsresult handle_load(HTMLDocumentNode *doc, nsIDOMEvent *event) { + thread_data_t *thread_data = get_thread_data(FALSE); HTMLDocumentObj *doc_obj = NULL; DOMEvent *load_event; HRESULT hres;
TRACE("(%p)\n", doc);
+ if(!thread_data) + return NS_ERROR_OUT_OF_MEMORY; if(!doc->window || !doc->window->base.outer_window) return NS_ERROR_FAILURE; if(doc->doc_obj && doc->doc_obj->doc_node == doc) { @@ -321,6 +324,7 @@ static nsresult handle_load(HTMLDocumentNode *doc, nsIDOMEvent *event) IUnknown_AddRef(doc_obj->outer_unk); } connect_scripts(doc->window); + thread_data->tasks_locked++;
if(doc_obj) handle_docobj_load(doc_obj); @@ -344,6 +348,9 @@ static nsresult handle_load(HTMLDocumentNode *doc, nsIDOMEvent *event) IUnknown_Release(doc_obj->outer_unk); }
+ if(!--thread_data->tasks_locked) + unblock_tasks_and_timers(thread_data, TRUE); + doc->window->load_event_start_time = get_time_stamp();
if(doc->dom_document) { diff --git a/dlls/mshtml/nsio.c b/dlls/mshtml/nsio.c index e42319dec13..d074ee10dbc 100644 --- a/dlls/mshtml/nsio.c +++ b/dlls/mshtml/nsio.c @@ -245,6 +245,7 @@ static BOOL exec_shldocvw_67(HTMLDocumentObj *doc, BSTR url) static nsresult before_async_open(nsChannel *channel, GeckoBrowser *container, BOOL *cancel) { HTMLDocumentObj *doc = container->doc; + thread_data_t *thread_data; BSTR display_uri; HRESULT hres;
@@ -256,6 +257,11 @@ static nsresult before_async_open(nsChannel *channel, GeckoBrowser *container, B hres = IUri_GetDisplayUri(channel->uri->uri, &display_uri); if(FAILED(hres)) return NS_ERROR_FAILURE; + + if(!(thread_data = get_thread_data(FALSE))) + return NS_ERROR_OUT_OF_MEMORY; + + thread_data->tasks_locked++; IUnknown_AddRef(doc->outer_unk);
if(doc->hostui) { @@ -284,6 +290,8 @@ static nsresult before_async_open(nsChannel *channel, GeckoBrowser *container, B *cancel = TRUE;
done: + if(!--thread_data->tasks_locked) + unblock_tasks_and_timers(thread_data, TRUE); IUnknown_Release(doc->outer_unk); return NS_OK; } @@ -986,6 +994,7 @@ static void start_binding_task_destr(task_t *_task) static nsresult async_open(nsChannel *This, HTMLOuterWindow *window, BOOL is_doc_channel, UINT32 load_type, nsIStreamListener *listener, nsISupports *context) { + thread_data_t *thread_data; nsChannelBSC *bscallback; IMoniker *mon = NULL; HRESULT hres; @@ -996,8 +1005,14 @@ static nsresult async_open(nsChannel *This, HTMLOuterWindow *window, BOOL is_doc return NS_ERROR_UNEXPECTED; }
- if(is_doc_channel) + if(is_doc_channel) { + if(!(thread_data = get_thread_data(FALSE))) + return NS_ERROR_OUT_OF_MEMORY; + thread_data->tasks_locked++; set_current_mon(window, mon, BINDING_NAVIGATED); + if(!--thread_data->tasks_locked) + unblock_tasks_and_timers(thread_data, TRUE); + }
hres = create_channelbsc(mon, NULL, NULL, 0, is_doc_channel, &bscallback); IMoniker_Release(mon); diff --git a/dlls/mshtml/olecmd.c b/dlls/mshtml/olecmd.c index 650c44c9d2b..6836de491ad 100644 --- a/dlls/mshtml/olecmd.c +++ b/dlls/mshtml/olecmd.c @@ -1023,12 +1023,17 @@ static const IOleCommandTargetVtbl DocObjOleCommandTargetVtbl = {
void show_context_menu(HTMLDocumentObj *This, DWORD dwID, POINT *ppt, IDispatch *elem) { + thread_data_t *thread_data = get_thread_data(FALSE); HMENU menu_res, menu; DWORD cmdid;
+ if(!thread_data) + return; + thread_data->tasks_locked++; + if(This->hostui && S_OK == IDocHostUIHandler_ShowContextMenu(This->hostui, dwID, ppt, (IUnknown*)&This->IOleCommandTarget_iface, elem)) - return; + goto done;
menu_res = LoadMenuW(get_shdoclc(), MAKEINTRESOURCEW(IDR_BROWSE_CONTEXT_MENU)); menu = GetSubMenu(menu_res, dwID); @@ -1040,6 +1045,10 @@ void show_context_menu(HTMLDocumentObj *This, DWORD dwID, POINT *ppt, IDispatch if(cmdid) IOleCommandTarget_Exec(&This->IOleCommandTarget_iface, &CGID_MSHTML, cmdid, 0, NULL, NULL); + +done: + if(!--thread_data->tasks_locked) + unblock_tasks_and_timers(thread_data, TRUE); }
void HTMLDocumentNode_OleCmd_Init(HTMLDocumentNode *This) diff --git a/dlls/mshtml/task.c b/dlls/mshtml/task.c index 9387a217109..0e3b45f9152 100644 --- a/dlls/mshtml/task.c +++ b/dlls/mshtml/task.c @@ -139,7 +139,7 @@ void remove_target_tasks(LONG target) release_task_timer(thread_data->thread_hwnd, timer); }
- if(!list_empty(&thread_data->timer_list)) { + if(!list_empty(&thread_data->timer_list) && !thread_data->tasks_locked && !thread_data->blocking_xhr) { DWORD tc = GetTickCount();
timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry); @@ -223,7 +223,7 @@ HRESULT set_task_timer(HTMLInnerWindow *window, LONG msec, enum timer_type timer IDispatch_AddRef(disp); timer->disp = disp;
- if(queue_timer(thread_data, timer)) + if(queue_timer(thread_data, timer) && !thread_data->tasks_locked && !thread_data->blocking_xhr) SetTimer(thread_data->thread_hwnd, TIMER_ID, msec, NULL);
*id = timer->id; @@ -321,7 +321,7 @@ static LRESULT process_timer(void) thread_data = get_thread_data(FALSE); assert(thread_data != NULL);
- if(list_empty(&thread_data->timer_list) || thread_data->blocking_xhr) { + if(list_empty(&thread_data->timer_list) || thread_data->tasks_locked || thread_data->blocking_xhr) { if(!list_empty(&thread_data->timer_list)) thread_data->timer_blocked = TRUE; KillTimer(thread_data->thread_hwnd, TIMER_ID); @@ -358,7 +358,7 @@ static LRESULT process_timer(void) call_timer_disp(disp, timer_type);
IDispatch_Release(disp); - }while(!list_empty(&thread_data->timer_list) && !thread_data->blocking_xhr); + }while(!list_empty(&thread_data->timer_list) && !thread_data->tasks_locked && !thread_data->blocking_xhr);
if(!list_empty(&thread_data->timer_list)) thread_data->timer_blocked = TRUE; @@ -379,10 +379,16 @@ static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPa while(1) { struct list *head = list_head(&thread_data->task_list);
+ if(thread_data->tasks_locked) + break; + if(head) { task_t *task = LIST_ENTRY(head, task_t, entry); list_remove(&task->entry); + thread_data->tasks_locked++; task->proc(task); + if(!--thread_data->tasks_locked) + unblock_timers(thread_data); task->destr(task); free(task); continue; @@ -494,9 +500,9 @@ ULONGLONG get_time_stamp(void) return (((ULONGLONG)time.dwHighDateTime << 32) + time.dwLowDateTime) / 10000 - time_epoch; }
-void unblock_tasks_and_timers(thread_data_t *thread_data) +void unblock_tasks_and_timers(thread_data_t *thread_data, BOOL include_task_list) { - if(!list_empty(&thread_data->event_task_list)) + if(!list_empty(&thread_data->event_task_list) || (include_task_list && !list_empty(&thread_data->task_list))) PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0);
unblock_timers(thread_data); diff --git a/dlls/mshtml/tests/events.c b/dlls/mshtml/tests/events.c index 2cf63f06dd3..ef89733380e 100644 --- a/dlls/mshtml/tests/events.c +++ b/dlls/mshtml/tests/events.c @@ -133,6 +133,7 @@ static IOleDocumentView *view; static BOOL is_ie9plus; static int document_mode; static unsigned in_fire_event; +static DWORD main_thread_id;
typedef struct { LONG x; @@ -6196,8 +6197,45 @@ static HRESULT QueryInterface(REFIID riid, void **ppv) }
static IHTMLDocument2 *notif_doc; +static unsigned in_nav_notif_test, nav_notif_test_depth; static BOOL doc_complete;
+static void nav_notif_test(void) +{ + IHTMLPrivateWindow *priv_window; + IHTMLWindow2 *window; + BSTR bstr, bstr2; + HRESULT hres; + VARIANT v; + + in_nav_notif_test++; + nav_notif_test_depth++; + + hres = IHTMLDocument2_get_parentWindow(notif_doc, &window); + ok(hres == S_OK, "get_parentWindow failed: %08lx\n", hres); + ok(window != NULL, "window == NULL\n"); + + V_VT(&v) = VT_EMPTY; + bstr = SysAllocString(L"about:blank"); + bstr2 = SysAllocString(L""); + hres = IHTMLWindow2_QueryInterface(window, &IID_IHTMLPrivateWindow, (void**)&priv_window); + ok(hres == S_OK, "Could not get IHTMLPrivateWindow) interface: %08lx\n", hres); + hres = IHTMLPrivateWindow_SuperNavigate(priv_window, bstr, bstr2, NULL, NULL, &v, &v, 0); + ok(hres == S_OK, "SuperNavigate failed: %08lx\n", hres); + IHTMLPrivateWindow_Release(priv_window); + IHTMLWindow2_Release(window); + SysFreeString(bstr2); + SysFreeString(bstr); + + ok(nav_notif_test_depth == 1, "nav_notif_test_depth = %u\n", nav_notif_test_depth); + pump_msgs(NULL); + pump_msgs(NULL); + pump_msgs(NULL); + ok(nav_notif_test_depth == 1, "nav_notif_test_depth = %u\n", nav_notif_test_depth); + ok(!doc_complete, "doc_complete = TRUE\n"); + nav_notif_test_depth--; +} + static HRESULT WINAPI PropertyNotifySink_QueryInterface(IPropertyNotifySink *iface, REFIID riid, void**ppv) { @@ -6222,9 +6260,16 @@ static ULONG WINAPI PropertyNotifySink_Release(IPropertyNotifySink *iface)
static HRESULT WINAPI PropertyNotifySink_OnChanged(IPropertyNotifySink *iface, DISPID dispID) { - if(dispID == DISPID_READYSTATE){ - BSTR state; + ok(GetCurrentThreadId() == main_thread_id, "OnChanged called on different thread\n"); + + if(dispID == DISPID_READYSTATE) { HRESULT hres; + BSTR state; + + if(in_nav_notif_test == 11) + nav_notif_test(); + + ok(nav_notif_test_depth < 2, "nav_notif_test_depth = %u\n", nav_notif_test_depth);
hres = IHTMLDocument2_get_readyState(notif_doc, &state); ok(hres == S_OK, "get_readyState failed: %08lx\n", hres); @@ -6235,6 +6280,13 @@ static HRESULT WINAPI PropertyNotifySink_OnChanged(IPropertyNotifySink *iface, D SysFreeString(state); }
+ if(dispID == 1005) { + ok(!nav_notif_test_depth, "nav_notif_test_depth = %u\n", nav_notif_test_depth); + + if(in_nav_notif_test == 1) + nav_notif_test(); + } + return S_OK; }
@@ -7445,6 +7497,51 @@ static void test_sync_xhr_events(const char *doc_str) IHTMLDocument2_Release(doc[1]); }
+static void test_navigation_during_notif(void) +{ + IPersistMoniker *persist; + IHTMLDocument2 *doc; + IMoniker *mon; + HRESULT hres; + unsigned i; + BSTR url; + MSG msg; + + for(i = 0; i < 2; i++) { + if(!(doc = create_document())) + return; + + notif_doc = doc; + doc_complete = FALSE; + set_client_site(doc, TRUE); + do_advise((IUnknown*)doc, &IID_IPropertyNotifySink, (IUnknown*)&PropertyNotifySink); + + url = SysAllocString(L"about:setting"); + hres = CreateURLMoniker(NULL, url, &mon); + SysFreeString(url); + ok(hres == S_OK, "CreateUrlMoniker failed: %08lx\n", hres); + + hres = IHTMLDocument2_QueryInterface(doc, &IID_IPersistMoniker, (void**)&persist); + ok(hres == S_OK, "Could not get IPersistMoniker iface: %08lx\n", hres); + + hres = IPersistMoniker_Load(persist, FALSE, mon, NULL, 0); + ok(hres == S_OK, "Load failed: %08lx\n", hres); + IPersistMoniker_Release(persist); + IMoniker_Release(mon); + + in_nav_notif_test = i*10 + 1; + while(in_nav_notif_test != i*10 + 2 && !doc_complete && GetMessageA(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + ok(!nav_notif_test_depth, "nav_notif_test_depth = %u\n", nav_notif_test_depth); + in_nav_notif_test = 0; + + set_client_site(doc, FALSE); + IHTMLDocument2_Release(doc); + } +} + static BOOL check_ie(void) { IHTMLDocument2 *doc; @@ -7475,6 +7572,7 @@ static BOOL check_ie(void) START_TEST(events) { CoInitialize(NULL); + main_thread_id = GetCurrentThreadId();
if(check_ie()) { container_hwnd = create_container_window(); @@ -7516,6 +7614,7 @@ START_TEST(events) test_storage_events(empty_doc_ie9_str); test_sync_xhr_events(empty_doc_ie9_str); } + test_navigation_during_notif();
/* Test this last since it doesn't close the view properly. */ test_document_close(); diff --git a/dlls/mshtml/xmlhttprequest.c b/dlls/mshtml/xmlhttprequest.c index fffc9fab51b..f7eff984d87 100644 --- a/dlls/mshtml/xmlhttprequest.c +++ b/dlls/mshtml/xmlhttprequest.c @@ -306,7 +306,7 @@ static nsresult sync_xhr_send(HTMLXMLHttpRequest *xhr, nsIVariant *nsbody) window->base.outer_window->readystate_locked--;
if(!--window->blocking_depth) - unblock_tasks_and_timers(thread_data); + unblock_tasks_and_timers(thread_data, FALSE);
/* Process any pending events now since they were part of the blocked send() above */ synthesize_pending_events(xhr);