-- v5: mshtml/tests: Accept rare return value from ReportResult on native. mshtml: Don't process tasks recursively. mshtml: Remember if timer was blocked. mshtml: Implement EmulateIE* modes for X-UA-Compatible. mshtml: Don't return default ports from location.host in IE10+ modes. mshtml: Avoid calling remove_target_tasks needlessly.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
It's already called by abort_window_bindings.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlwindow.c | 1 - dlls/mshtml/oleobj.c | 2 -- dlls/mshtml/persist.c | 2 -- 3 files changed, 5 deletions(-)
diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index 30ad3e0fd0d..ccb593724d4 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -135,7 +135,6 @@ static void detach_inner_window(HTMLInnerWindow *window) dispex_props_unlink(&outer_window->location->dispex);
abort_window_bindings(window); - remove_target_tasks(window->task_magic); release_script_hosts(window); unlink_ref(&window->jscript); window->base.outer_window = NULL; diff --git a/dlls/mshtml/oleobj.c b/dlls/mshtml/oleobj.c index 2374ce0ea81..7dffdbeeec2 100644 --- a/dlls/mshtml/oleobj.c +++ b/dlls/mshtml/oleobj.c @@ -3441,8 +3441,6 @@ static void set_window_uninitialized(HTMLOuterWindow *window) if(SUCCEEDED(hres)) { channelbsc->bsc.bindf = 0; /* synchronous binding */
- if(window->base.inner_window->doc) - remove_target_tasks(window->base.inner_window->task_magic); abort_window_bindings(window->base.inner_window); window->base.inner_window->doc->unload_sent = TRUE;
diff --git a/dlls/mshtml/persist.c b/dlls/mshtml/persist.c index bea2ab65750..5d55dac6826 100644 --- a/dlls/mshtml/persist.c +++ b/dlls/mshtml/persist.c @@ -381,8 +381,6 @@ HRESULT set_moniker(HTMLOuterWindow *window, IMoniker *mon, IUri *nav_uri, IBind hres = create_channelbsc(mon, NULL, NULL, 0, TRUE, &bscallback);
if(SUCCEEDED(hres)) { - if(window->base.inner_window->doc) - remove_target_tasks(window->base.inner_window->task_magic); abort_window_bindings(window->base.inner_window);
hres = load_nsuri(window, nsuri, NULL, bscallback, LOAD_FLAGS_BYPASS_CACHE);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmllocation.c | 16 ++++++++++++++++ dlls/mshtml/tests/documentmode.js | 3 +++ 2 files changed, 19 insertions(+)
diff --git a/dlls/mshtml/htmllocation.c b/dlls/mshtml/htmllocation.c index 5c7e66c40df..b176b56280c 100644 --- a/dlls/mshtml/htmllocation.c +++ b/dlls/mshtml/htmllocation.c @@ -225,12 +225,28 @@ static HRESULT WINAPI HTMLLocation_get_host(IHTMLLocation *iface, BSTR *p) HTMLLocation *This = impl_from_IHTMLLocation(iface); URL_COMPONENTSW url = {sizeof(URL_COMPONENTSW)}; HRESULT hres; + IUri *uri;
TRACE("(%p)->(%p)\n", This, p);
if(!p) return E_POINTER;
+ if(dispex_compat_mode(&This->window->base.inner_window->event_target.dispex) >= COMPAT_MODE_IE10) { + if(!(uri = get_uri(This))) { + *p = NULL; + return S_OK; + } + + hres = IUri_GetAuthority(uri, p); + if(hres == S_OK || FAILED(hres)) + return hres; + + SysFreeString(*p); + *p = NULL; + return S_OK; + } + url.dwHostNameLength = 1; hres = get_url_components(This, &url); if(FAILED(hres)) diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 97941bb81c2..7fdf510ddd7 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1184,8 +1184,11 @@ async_test("script_load", function() { });
sync_test("location", function() { + var v = document.documentMode; document.body.innerHTML = '<a name="testanchor">test</a>';
+ ok(location.host === "winetest.example.org" + (v < 10 ? ":80" : ""), "location.host = " + location.host); + ok(location.hash === "", "initial location.hash = " + location.hash); location.hash = "TestAnchor"; ok(location.hash === "#TestAnchor", "location.hash after set to TestAnchor = " + location.hash);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Despite what MSDN says it appears that EmulateIE10 and EmulateIE11 don't care about DOCTYPE validity at all (same as IE=10 and IE=11).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/mutation.c | 63 +++++++++++++++++++++++++----------- dlls/mshtml/tests/dom.c | 22 +++++++++++++ 3 files changed, 68 insertions(+), 18 deletions(-)
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index e63a05f4314..d97a206b215 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1128,6 +1128,7 @@ struct HTMLDocumentNode {
nsIDOMDocument *dom_document; nsIDOMHTMLDocument *html_document; + unsigned int emulate_mode : 1; unsigned int content_ready : 1; unsigned int unload_sent : 1;
diff --git a/dlls/mshtml/mutation.c b/dlls/mshtml/mutation.c index 3c1a25646db..d8c0cb50b10 100644 --- a/dlls/mshtml/mutation.c +++ b/dlls/mshtml/mutation.c @@ -414,19 +414,28 @@ DWORD get_compat_mode_version(compat_mode_t compat_mode) */ compat_mode_t lock_document_mode(HTMLDocumentNode *doc) { - TRACE("%p: %d\n", doc, doc->document_mode); - if(!doc->document_mode_locked) { doc->document_mode_locked = TRUE;
+ if(doc->emulate_mode && doc->document_mode < COMPAT_MODE_IE10) { + nsIDOMDocumentType *nsdoctype; + + if(NS_SUCCEEDED(nsIDOMDocument_GetDoctype(doc->dom_document, &nsdoctype)) && nsdoctype) + nsIDOMDocumentType_Release(nsdoctype); + else + doc->document_mode = COMPAT_MODE_QUIRKS; + } + if(doc->html_document) nsIDOMHTMLDocument_SetIECompatMode(doc->html_document, get_compat_mode_version(doc->document_mode)); }
+ TRACE("%p: %d\n", doc, doc->document_mode); + return doc->document_mode; }
-static void set_document_mode(HTMLDocumentNode *doc, compat_mode_t document_mode, BOOL lock) +static void set_document_mode(HTMLDocumentNode *doc, compat_mode_t document_mode, BOOL emulate_mode, BOOL lock) { compat_mode_t max_compat_mode;
@@ -447,6 +456,7 @@ static void set_document_mode(HTMLDocumentNode *doc, compat_mode_t document_mode }
doc->document_mode = document_mode; + doc->emulate_mode = emulate_mode; if(lock) lock_document_mode(doc); } @@ -489,34 +499,44 @@ const WCHAR *parse_compat_version(const WCHAR *version_string, compat_mode_t *r) return p; }
-static BOOL parse_ua_compatible(const WCHAR *p, compat_mode_t *r) +static compat_mode_t parse_ua_compatible(const WCHAR *p, BOOL *emulate_mode) { + static const WCHAR emulateIEW[] = {'E','m','u','l','a','t','e','I','E'}; static const WCHAR ie_eqW[] = {'I','E','='}; static const WCHAR edgeW[] = {'e','d','g','e'}; - compat_mode_t mode = COMPAT_MODE_INVALID; + compat_mode_t parsed_mode, mode = COMPAT_MODE_INVALID; + *emulate_mode = FALSE;
TRACE("%s\n", debugstr_w(p));
if(wcsnicmp(ie_eqW, p, ARRAY_SIZE(ie_eqW))) - return FALSE; + return mode; p += 3;
do { + BOOL is_emulate = FALSE; + while(iswspace(*p)) p++; if(!wcsnicmp(p, edgeW, ARRAY_SIZE(edgeW))) { p += ARRAY_SIZE(edgeW); if(is_ua_compatible_delimiter(*p)) mode = COMPAT_MODE_IE11; break; - }else if(!(p = parse_compat_version(p, r))) + } + if(!wcsnicmp(p, emulateIEW, ARRAY_SIZE(emulateIEW))) { + p += ARRAY_SIZE(emulateIEW); + is_emulate = TRUE; + } + if(!(p = parse_compat_version(p, &parsed_mode))) break; - if(mode < *r) - mode = *r; + if(mode < parsed_mode) { + mode = parsed_mode; + *emulate_mode = is_emulate; + } while(iswspace(*p)) p++; } while(*p++ == ',');
- *r = mode; - return mode != COMPAT_MODE_INVALID; + return mode; }
void process_document_response_headers(HTMLDocumentNode *doc, IBinding *binding) @@ -537,14 +557,19 @@ void process_document_response_headers(HTMLDocumentNode *doc, IBinding *binding) hres = IWinInetHttpInfo_QueryInfo(http_info, HTTP_QUERY_CUSTOM, buf, &size, NULL, NULL); if(hres == S_OK && size) { compat_mode_t document_mode; + BOOL emulate_mode; WCHAR *header;
TRACE("size %lu\n", size);
header = strdupAtoW(buf); - if(header && parse_ua_compatible(header, &document_mode)) { - TRACE("setting document mode %d\n", document_mode); - set_document_mode(doc, document_mode, FALSE); + if(header) { + document_mode = parse_ua_compatible(header, &emulate_mode); + + if(document_mode != COMPAT_MODE_INVALID) { + TRACE("setting document mode %d\n", document_mode); + set_document_mode(doc, document_mode, emulate_mode, FALSE); + } } free(header); } @@ -572,9 +597,11 @@ static void process_meta_element(HTMLDocumentNode *doc, nsIDOMHTMLMetaElement *m TRACE("%s: %s\n", debugstr_w(http_equiv), debugstr_w(content));
if(!wcsicmp(http_equiv, L"x-ua-compatible")) { - compat_mode_t document_mode; - if(parse_ua_compatible(content, &document_mode)) - set_document_mode(doc, document_mode, TRUE); + BOOL emulate_mode; + compat_mode_t document_mode = parse_ua_compatible(content, &emulate_mode); + + if(document_mode != COMPAT_MODE_INVALID) + set_document_mode(doc, document_mode, emulate_mode, TRUE); else FIXME("Unsupported document mode %s\n", debugstr_w(content)); } @@ -907,7 +934,7 @@ static void NSAPI nsDocumentObserver_BindToDocument(nsIDocumentObserver *iface, mode = COMPAT_MODE_IE11; }
- set_document_mode(This, mode, FALSE); + set_document_mode(This, mode, FALSE, FALSE); nsIDOMDocumentType_Release(nsdoctype); } } diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index 8383f167cdd..0ff11a74d01 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -11836,6 +11836,10 @@ static void test_quirks_mode(void) " <meta http-equiv="x-ua-compatible" content="IE=%s" />" "</head><body></body></html>", tests[i].str); run_domtest(buf, test_document_mode); + sprintf(buf, "<html><head>" + " <meta http-equiv="x-ua-compatible" content="IE=%s" />" + "</head><body></body></html>", tests[i].str); + run_domtest(buf, test_document_mode); }
expected_document_mode = 8; @@ -11848,6 +11852,24 @@ static void test_quirks_mode(void) " <body>" " </body>" "</html>", test_document_mode); + + for(i = 7; i <= 11; i++) { + char buf[128]; + expected_document_mode = i; + sprintf(buf, "<!DOCTYPE html>\n<html><head>" + " <meta http-equiv="x-ua-compatible" content="IE=EmulateIE%u" />" + "</head><body></body></html>", i); + run_domtest(buf, test_document_mode); + expected_document_mode = i < 10 ? 5 : i; + sprintf(buf, "<html><head>" + " <meta http-equiv="x-ua-compatible" content="IE=EmulateIE%u" />" + "</head><body></body></html>", i); + run_domtest(buf, test_document_mode); + sprintf(buf, "<html><head>" + " <meta http-equiv="x-ua-compatible" content="IE=eMulaTeie%u" />" + "</head><body></body></html>", i); + run_domtest(buf, test_document_mode); + } }
static void test_document_mode_lock(void)
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Needed for next patch. --- dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/task.c | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index d97a206b215..582cc109d31 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1493,6 +1493,7 @@ typedef struct { struct list *pending_xhr_events_tail; struct wine_rb_tree session_storage_map; void *blocking_xhr; + BOOL timer_blocked; } thread_data_t;
thread_data_t *get_thread_data(BOOL); diff --git a/dlls/mshtml/task.c b/dlls/mshtml/task.c index 49f76d101a9..9387a217109 100644 --- a/dlls/mshtml/task.c +++ b/dlls/mshtml/task.c @@ -112,6 +112,17 @@ static void release_task_timer(HWND thread_hwnd, task_timer_t *timer) free(timer); }
+static void unblock_timers(thread_data_t *thread_data) +{ + if(thread_data->timer_blocked && !list_empty(&thread_data->timer_list)) { + task_timer_t *timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry); + DWORD tc = GetTickCount(); + + thread_data->timer_blocked = FALSE; + SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time > tc ? timer->time - tc : 0, NULL); + } +} + void remove_target_tasks(LONG target) { thread_data_t *thread_data = get_thread_data(FALSE); @@ -311,6 +322,8 @@ static LRESULT process_timer(void) assert(thread_data != NULL);
if(list_empty(&thread_data->timer_list) || thread_data->blocking_xhr) { + if(!list_empty(&thread_data->timer_list)) + thread_data->timer_blocked = TRUE; KillTimer(thread_data->thread_hwnd, TIMER_ID); return 0; } @@ -347,6 +360,8 @@ static LRESULT process_timer(void) IDispatch_Release(disp); }while(!list_empty(&thread_data->timer_list) && !thread_data->blocking_xhr);
+ if(!list_empty(&thread_data->timer_list)) + thread_data->timer_blocked = TRUE; KillTimer(thread_data->thread_hwnd, TIMER_ID); return 0; } @@ -484,10 +499,5 @@ void unblock_tasks_and_timers(thread_data_t *thread_data) if(!list_empty(&thread_data->event_task_list)) PostMessageW(thread_data->thread_hwnd, WM_PROCESSTASK, 0, 0);
- if(!thread_data->blocking_xhr && !list_empty(&thread_data->timer_list)) { - task_timer_t *timer = LIST_ENTRY(list_head(&thread_data->timer_list), task_timer_t, entry); - DWORD tc = GetTickCount(); - - SetTimer(thread_data->thread_hwnd, TIMER_ID, timer->time > tc ? timer->time - tc : 0, NULL); - } + unblock_timers(thread_data); }
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);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
events.c already has it.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/tests/script.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index e6acf5ae5b1..88831bc66c1 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -4289,7 +4289,7 @@ static void report_data(ProtocolHandler *This) ok(hres == S_OK, "ReportData failed: %08lx\n", hres);
hres = IInternetProtocolSink_ReportResult(This->sink, S_OK, 0, NULL); - ok(hres == S_OK, "ReportResult failed: %08lx\n", hres); + ok(hres == S_OK || broken(hres == 0x80ef0001), "ReportResult failed: %08lx\n", hres); }
typedef struct js_stream_t {
Updated. I had to carefully analyze all the callback paths from gecko to any external code and use them there, because those aren't going through the tasks, so it's not enough.
Why do you need to store an info if a timer was blocked? All you need to know is if there are any timers that need to be processed, for which comparing time stamps would be enough.
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).
I don't think it has to be done in this MR and I'm sure it doesn't need to be a massive single commit. It looks questionable in its current form, but I didn't look deeper into that.
Why do you need to store an info if a timer was blocked? All you need to know is if there are any timers that need to be processed, for which comparing time stamps would be enough.
Well without that we'd get spurious SetTimer calls even if the timer wasn't actually killed, which will happen a lot (on every task almost in common case). Is that fine? I didn't want to mess with the original timer. I wasn't sure if it loses resolution or something.
e.g. timer in 50ms, we process a task that takes 2ms (probably more than usual already), then we SetTimer again on it because list isn't empty, possibly back to 50ms? (depends on tick count granularity).
With the patch the timer was never killed so it won't SetTimer on it.
I don't think it has to be done in this MR and I'm sure it doesn't need to be a massive single commit. It looks questionable in its current form, but I didn't look deeper into that.
I could split it but it is necessary unfortunately. Note that for the tests to pass we already need to do it at least in `run_end_load` since it's one of the tests (the 1005 OnChanged notification).
The reason is that it's called by gecko callback (via AddScriptRunner) and not from our msg loop.