This should implement error page navigation similar to native, which is unfortunately mostly undocumented.
Briefly, native uses a local resource in some DLL (the exact one varies by Windows version and locale, so it's not reliable to test) which has a bunch of html error pages for various errors (404 is a different resource than 405, etc). In Windows XP this is in `shdoclc.dll` (which is where I placed it as well, because we already use it in Wine), in Windows 10 it's somewhere in `system32\en-US\ieframe.dll` (or something like that), for US locale at least.
To keep it simple for now, this only adds one error page called `ERROR.HTM` though, which is generic and displays the error code (given via a query). That said, it should still probably be localized somehow, but I've no idea how to localize html resources…
The error page is navigated with URL of the form `res://C:\windows\system32.../NAME.HTM#original_url`, and some of the notifications are special cased to return the original_url from the fragment (on native as well, tests included).
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Seems to be doing a replace and skipping updating travel log/history.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlwindow.c | 9 +- dlls/mshtml/tests/htmldoc.c | 206 +++++++++++++++++++++++++++++++++++- 2 files changed, 212 insertions(+), 3 deletions(-)
diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index 398fef9bde7..2034ae8487a 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -2816,6 +2816,7 @@ static HRESULT WINAPI HTMLPrivateWindow_SuperNavigate(IHTMLPrivateWindow *iface, BSTR arg4, VARIANT *post_data_var, VARIANT *headers_var, ULONG flags) { HTMLWindow *This = impl_from_IHTMLPrivateWindow(iface); + DWORD binding_flags = BINDING_NAVIGATED|BINDING_NOFRAG; HTMLOuterWindow *window = This->outer_window; OLECHAR *translated_url = NULL; DWORD post_data_size = 0; @@ -2827,6 +2828,9 @@ static HRESULT WINAPI HTMLPrivateWindow_SuperNavigate(IHTMLPrivateWindow *iface, TRACE("(%p)->(%s %s %s %s %s %s %lx)\n", This, debugstr_w(url), debugstr_w(arg2), debugstr_w(arg3), debugstr_w(arg4), debugstr_variant(post_data_var), debugstr_variant(headers_var), flags);
+ if(flags & ~2) + FIXME("unimplemented flags %lx\n", flags & ~2); + if(!window || !window->browser) return E_UNEXPECTED;
@@ -2855,7 +2859,10 @@ static HRESULT WINAPI HTMLPrivateWindow_SuperNavigate(IHTMLPrivateWindow *iface, headers = V_BSTR(headers_var); }
- hres = super_navigate(window, uri, BINDING_NAVIGATED|BINDING_NOFRAG, headers, post_data, post_data_size); + if(flags & 2) + binding_flags |= BINDING_REPLACE; + + hres = super_navigate(window, uri, binding_flags, headers, post_data, post_data_size); IUri_Release(uri); if(post_data) SafeArrayUnaccessData(V_ARRAY(post_data_var)); diff --git a/dlls/mshtml/tests/htmldoc.c b/dlls/mshtml/tests/htmldoc.c index 28aa06b0001..9874540e973 100644 --- a/dlls/mshtml/tests/htmldoc.c +++ b/dlls/mshtml/tests/htmldoc.c @@ -226,6 +226,7 @@ static BOOL support_wbapp, allow_new_window, no_travellog; static BOOL report_mime; static BOOL testing_submit; static BOOL resetting_document; +static BOOL is_error_url; static BOOL is_mhtml; static int stream_read, protocol_read; static IStream *history_stream; @@ -1202,6 +1203,7 @@ static HRESULT WINAPI PropertyNotifySink_OnChanged(IPropertyNotifySink *iface, D case 3000031: case 3000032: case 3000033: + case 3000034: /* TODO */ return S_OK; } @@ -3698,7 +3700,8 @@ static HRESULT WINAPI DocObjectService_FireBeforeNavigate2(IDocObjectService *i if(!testing_submit) { ok(!pPostData, "pPostData = %p\n", pPostData); ok(!cbPostData, "cbPostData = %ld\n", cbPostData); - ok(!lpszHeaders || !lstrcmpW(lpszHeaders, L"Referer: http://test.winehq.org/tests/winehq_snapshot/%5Cr%5Cn"), + ok(!lpszHeaders || !lstrcmpW(lpszHeaders, L"Referer: http://test.winehq.org/tests/winehq_snapshot/%5Cr%5Cn") || + !lstrcmpW(lpszHeaders, L"Referer: http://test.winehq.org/tests/hello.html%5Cr%5Cn"), "lpszHeaders = %s\n", wine_dbgstr_w(lpszHeaders)); }else { ok(cbPostData == 9, "cbPostData = %ld\n", cbPostData); @@ -3800,7 +3803,7 @@ static HRESULT WINAPI DocObjectService_IsErrorUrl( BOOL *pfIsError) { CHECK_EXPECT2(IsErrorUrl); - *pfIsError = FALSE; + *pfIsError = is_error_url; return S_OK; }
@@ -6485,6 +6488,204 @@ static void test_reload(IHTMLDocument2 *doc) test_download(DWL_VERBDONE|DWL_HTTP|DWL_ONREADY_LOADING|DWL_REFRESH|DWL_EX_GETHOSTINFO); }
+static void test_super_navigate(IHTMLDocument2 *doc) +{ + IHTMLElementCollection *elem_col; + IHTMLPrivateWindow *priv_window; + IHTMLDocument2 *doc2; + IHTMLDocument3 *doc3; + IHTMLWindow2 *window; + HRESULT hres; + VARIANT var; + BSTR str; + + trace("SuperNavigate...\n"); + + hres = IHTMLDocument2_get_parentWindow(doc, &window); + ok(hres == S_OK, "get_parentWindow failed: %08lx\n", hres); + + hres = IHTMLWindow2_QueryInterface(window, &IID_IHTMLPrivateWindow, (void**)&priv_window); + ok(hres == S_OK, "QueryInterface(IID_IHTMLPrivateWindow) failed: %08lx\n", hres); + + prev_url = nav_url; + nav_serv_url = nav_url = L"http://test.winehq.org/tests/hello.html"; + + readystate_set_loading = TRUE; + SET_EXPECT(TranslateUrl); + SET_EXPECT(FireBeforeNavigate2); + SET_EXPECT(Exec_ShellDocView_67); + SET_EXPECT(Invoke_AMBIENT_SILENT); + SET_EXPECT(Invoke_AMBIENT_OFFLINEIFNOTCONNECTED); + SET_EXPECT(Exec_ShellDocView_63); + SET_EXPECT(Exec_ShellDocView_84); + + str = SysAllocString(L"http://test.winehq.org/tests/hello.html"); + V_VT(&var) = VT_EMPTY; + hres = IHTMLPrivateWindow_SuperNavigate(priv_window, str, NULL, NULL, NULL, &var, &var, 1); + ok(hres == S_OK, "SuperNavigate failed: %08lx\n", hres); + SysFreeString(str); + + CHECK_CALLED(TranslateUrl); + todo_wine CHECK_CALLED(FireBeforeNavigate2); + CLEAR_CALLED(Exec_ShellDocView_67); /* Not called by IE11 */ + CHECK_CALLED(Invoke_AMBIENT_SILENT); + CHECK_CALLED(Invoke_AMBIENT_OFFLINEIFNOTCONNECTED); + CHECK_CALLED(Exec_ShellDocView_63); + CHECK_CALLED_BROKEN(Exec_ShellDocView_84); + + test_download(DWL_VERBDONE | DWL_ONREADY_LOADING | DWL_EXPECT_HISTUPDATE | DWL_EX_GETHOSTINFO); + + hres = IHTMLPrivateWindow_GetAddressBarUrl(priv_window, &str); + ok(hres == S_OK, "GetAddressBarUrl failed: %08lx\n", hres); + ok(!wcscmp(str, L"http://test.winehq.org/tests/hello.html"), "unexpected address bar url: %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + prev_url = nav_url; + nav_serv_url = nav_url = L"about:blank"; + + readystate_set_loading = TRUE; + SET_EXPECT(TranslateUrl); + SET_EXPECT(Exec_ShellDocView_67); + SET_EXPECT(Invoke_AMBIENT_SILENT); + SET_EXPECT(Invoke_AMBIENT_OFFLINEIFNOTCONNECTED); + SET_EXPECT(Exec_ShellDocView_63); + SET_EXPECT(Exec_ShellDocView_84); + + str = SysAllocString(L"about:blank"); + V_VT(&var) = VT_EMPTY; + hres = IHTMLPrivateWindow_SuperNavigate(priv_window, str, NULL, NULL, NULL, &var, &var, 4); + ok(hres == S_OK, "SuperNavigate failed: %08lx\n", hres); + SysFreeString(str); + + CHECK_CALLED(TranslateUrl); + CLEAR_CALLED(Exec_ShellDocView_67); /* Not called by IE11 */ + CHECK_CALLED(Invoke_AMBIENT_SILENT); + CHECK_CALLED(Invoke_AMBIENT_OFFLINEIFNOTCONNECTED); + CHECK_CALLED(Exec_ShellDocView_63); + CHECK_CALLED_BROKEN(Exec_ShellDocView_84); + + test_download(DWL_VERBDONE | DWL_ONREADY_LOADING | DWL_EXPECT_HISTUPDATE | DWL_EX_GETHOSTINFO); + + hres = IHTMLPrivateWindow_GetAddressBarUrl(priv_window, &str); + ok(hres == S_OK, "GetAddressBarUrl failed: %08lx\n", hres); + ok(!wcscmp(str, L"about:blank"), "unexpected address bar url: %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + prev_url = nav_url; + nav_serv_url = nav_url = L"http://test.winehq.org/tests/hello.html"; + + readystate_set_loading = TRUE; + SET_EXPECT(TranslateUrl); + SET_EXPECT(FireBeforeNavigate2); + SET_EXPECT(Exec_ShellDocView_67); + SET_EXPECT(Invoke_AMBIENT_SILENT); + SET_EXPECT(Invoke_AMBIENT_OFFLINEIFNOTCONNECTED); + SET_EXPECT(Exec_ShellDocView_63); + SET_EXPECT(Exec_ShellDocView_84); + + str = SysAllocString(L"http://test.winehq.org/tests/hello.html"); + hres = IHTMLPrivateWindow_SuperNavigate(priv_window, str, NULL, NULL, NULL, &var, &var, 2 | 1); + ok(hres == S_OK, "SuperNavigate failed: %08lx\n", hres); + SysFreeString(str); + + CHECK_CALLED(TranslateUrl); + todo_wine CHECK_CALLED(FireBeforeNavigate2); + CLEAR_CALLED(Exec_ShellDocView_67); /* Not called by IE11 */ + CHECK_CALLED(Invoke_AMBIENT_SILENT); + CHECK_CALLED(Invoke_AMBIENT_OFFLINEIFNOTCONNECTED); + CHECK_CALLED(Exec_ShellDocView_63); + CHECK_CALLED_BROKEN(Exec_ShellDocView_84); + + test_download(DWL_VERBDONE | DWL_ONREADY_LOADING | DWL_EX_GETHOSTINFO); + + hres = IHTMLPrivateWindow_GetAddressBarUrl(priv_window, &str); + ok(hres == S_OK, "GetAddressBarUrl failed: %08lx\n", hres); + ok(!wcscmp(str, L"http://test.winehq.org/tests/hello.html"), "unexpected address bar url: %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + hres = IHTMLWindow2_get_document(window, &doc2); + ok(hres == S_OK, "get_document failed: %08lx\n", hres); + + hres = IHTMLDocument2_QueryInterface(doc2, &IID_IHTMLDocument3, (void**)&doc3); + ok(hres == S_OK, "QueryInterface(IID_IHTMLDocument3) failed: %08lx\n", hres); + IHTMLDocument2_Release(doc2); + + str = SysAllocString(L"H1"); + hres = IHTMLDocument3_getElementsByTagName(doc3, str, &elem_col); + ok(hres == S_OK, "getElementsByTagName failed: %08lx\n", hres); + ok(elem_col != NULL, "elem_col = NULL\n"); + IHTMLElementCollection_Release(elem_col); + IHTMLDocument3_Release(doc3); + SysFreeString(str); + + prev_url = nav_url; + nav_serv_url = nav_url = L"about:blank"; + + readystate_set_loading = TRUE; + SET_EXPECT(TranslateUrl); + SET_EXPECT(Exec_ShellDocView_67); + SET_EXPECT(Invoke_AMBIENT_SILENT); + SET_EXPECT(Invoke_AMBIENT_OFFLINEIFNOTCONNECTED); + SET_EXPECT(Exec_ShellDocView_63); + SET_EXPECT(Exec_ShellDocView_84); + + str = SysAllocString(L"about:blank"); + V_VT(&var) = VT_EMPTY; + hres = IHTMLPrivateWindow_SuperNavigate(priv_window, str, NULL, NULL, NULL, &var, &var, 2 | 4); + ok(hres == S_OK, "SuperNavigate failed: %08lx\n", hres); + SysFreeString(str); + + CHECK_CALLED(TranslateUrl); + CLEAR_CALLED(Exec_ShellDocView_67); /* Not called by IE11 */ + CHECK_CALLED(Invoke_AMBIENT_SILENT); + CHECK_CALLED(Invoke_AMBIENT_OFFLINEIFNOTCONNECTED); + CHECK_CALLED(Exec_ShellDocView_63); + CHECK_CALLED_BROKEN(Exec_ShellDocView_84); + + test_download(DWL_VERBDONE | DWL_ONREADY_LOADING | DWL_EX_GETHOSTINFO); + + hres = IHTMLPrivateWindow_GetAddressBarUrl(priv_window, &str); + ok(hres == S_OK, "GetAddressBarUrl failed: %08lx\n", hres); + ok(!wcscmp(str, L"about:blank"), "unexpected address bar url: %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + prev_url = nav_url; + nav_serv_url = nav_url = L"http://test.winehq.org/tests/hello.html"; + is_error_url = TRUE; + + readystate_set_loading = TRUE; + SET_EXPECT(TranslateUrl); + SET_EXPECT(Exec_ShellDocView_67); + SET_EXPECT(Invoke_AMBIENT_SILENT); + SET_EXPECT(Invoke_AMBIENT_OFFLINEIFNOTCONNECTED); + SET_EXPECT(Exec_ShellDocView_63); + SET_EXPECT(Exec_ShellDocView_84); + + str = SysAllocString(L"http://test.winehq.org/tests/hello.html"); + V_VT(&var) = VT_EMPTY; + hres = IHTMLPrivateWindow_SuperNavigate(priv_window, str, NULL, NULL, NULL, &var, &var, 2); + ok(hres == S_OK, "SuperNavigate failed: %08lx\n", hres); + SysFreeString(str); + + CHECK_CALLED(TranslateUrl); + CLEAR_CALLED(Exec_ShellDocView_67); /* Not called by IE11 */ + CHECK_CALLED(Invoke_AMBIENT_SILENT); + CHECK_CALLED(Invoke_AMBIENT_OFFLINEIFNOTCONNECTED); + CHECK_CALLED(Exec_ShellDocView_63); + CHECK_CALLED_BROKEN(Exec_ShellDocView_84); + + test_download(DWL_VERBDONE | DWL_ONREADY_LOADING | DWL_EX_GETHOSTINFO); + + hres = IHTMLPrivateWindow_GetAddressBarUrl(priv_window, &str); + ok(hres == S_OK, "GetAddressBarUrl failed: %08lx\n", hres); + ok(!wcscmp(str, L"http://test.winehq.org/tests/hello.html"), "unexpected address bar url: %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + is_error_url = FALSE; + + IHTMLPrivateWindow_Release(priv_window); + IHTMLWindow2_Release(window); +} + static void test_open_window(IHTMLDocument2 *doc, BOOL do_block) { IHTMLWindow2 *window, *new_window; @@ -8318,6 +8519,7 @@ static void test_HTMLDocument_http(BOOL with_wbapp) if(!support_wbapp) /* FIXME */ test_open_window(doc, FALSE); if(support_wbapp) { + test_super_navigate(doc); test_put_href(doc, FALSE, NULL, L"http://test.winehq.org/tests/file.winetest", FALSE, FALSE, DWL_EXTERNAL); test_window_close(doc); }
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/shdoclc/ERROR.HTM | 13 +++++++++++++ dlls/shdoclc/shdoclc.rc | 3 +++ 2 files changed, 16 insertions(+) create mode 100644 dlls/shdoclc/ERROR.HTM
diff --git a/dlls/shdoclc/ERROR.HTM b/dlls/shdoclc/ERROR.HTM new file mode 100644 index 00000000000..e74efa15608 --- /dev/null +++ b/dlls/shdoclc/ERROR.HTM @@ -0,0 +1,13 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <title>Page Load Error</title> +</head> +<body onload="javascript: if(window.location.search) document.getElementById('err').innerHTML = unescape(window.location.search.substr(1))"> +<div style="margin-left:45px; width:720px;"> + <h2>The page cannot be loaded because of an error</h2> + <p>Error code: <b id="err">Unknown</b></p> +</div> +</body> +</html> diff --git a/dlls/shdoclc/shdoclc.rc b/dlls/shdoclc/shdoclc.rc index 57b03e152d4..a350438e0fb 100644 --- a/dlls/shdoclc/shdoclc.rc +++ b/dlls/shdoclc/shdoclc.rc @@ -241,3 +241,6 @@ IDR_BROWSE_CONTEXT_MENU MENU MENUITEM "Scroll Right", IDM_SCROLL_RIGHT } } + +/* @makedep: ERROR.HTM */ +ERROR.HTM HTML "ERROR.HTM"
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Tested in next patch. --- dlls/ieframe/iehtmlwnd.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/dlls/ieframe/iehtmlwnd.c b/dlls/ieframe/iehtmlwnd.c index 44e58a2cdff..de211fc784f 100644 --- a/dlls/ieframe/iehtmlwnd.c +++ b/dlls/ieframe/iehtmlwnd.c @@ -22,6 +22,25 @@
WINE_DEFAULT_DEBUG_CHANNEL(ieframe);
+HRESULT get_window(DocHost *doc_host, IHTMLWindow2 **ret) +{ + IHTMLDocument2 *doc_obj; + HRESULT hres; + + if(!doc_host->document) { + *ret = NULL; + return S_OK; + } + + hres = IUnknown_QueryInterface(doc_host->document, &IID_IHTMLDocument2, (void**)&doc_obj); + if(FAILED(hres)) + return hres; + + hres = IHTMLDocument2_get_parentWindow(doc_obj, ret); + IHTMLDocument2_Release(doc_obj); + return hres; +} + static inline IEHTMLWindow *impl_from_IHTMLWindow2(IHTMLWindow2 *iface) { return CONTAINING_RECORD(iface, IEHTMLWindow, IHTMLWindow2_iface); @@ -432,8 +451,22 @@ static HRESULT WINAPI IEHTMLWindow2_get_onscroll(IHTMLWindow2 *iface, VARIANT *p static HRESULT WINAPI IEHTMLWindow2_get_document(IHTMLWindow2 *iface, IHTMLDocument2 **p) { IEHTMLWindow *This = impl_from_IHTMLWindow2(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + IHTMLWindow2 *window; + HRESULT hres; + + TRACE("(%p)->(%p)\n", This, p); + + hres = get_window(This->doc_host, &window); + if(FAILED(hres)) + return hres; + + if(!window) + *p = NULL; + else { + hres = IHTMLWindow2_get_document(window, p); + IHTMLWindow2_Release(window); + } + return hres; }
static HRESULT WINAPI IEHTMLWindow2_get_event(IHTMLWindow2 *iface, IHTMLEventObj **p)
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/ieframe/ieframe.h | 1 + dlls/ieframe/navigate.c | 50 +++++++++++- dlls/ieframe/tests/ie.c | 175 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 218 insertions(+), 8 deletions(-)
diff --git a/dlls/ieframe/ieframe.h b/dlls/ieframe/ieframe.h index b5194f3b5fe..b03fe6e8e83 100644 --- a/dlls/ieframe/ieframe.h +++ b/dlls/ieframe/ieframe.h @@ -277,6 +277,7 @@ HRESULT go_home(DocHost*); HRESULT go_back(DocHost*); HRESULT go_forward(DocHost*); HRESULT refresh_document(DocHost*,const VARIANT*); +HRESULT get_window(DocHost*,IHTMLWindow2**); HRESULT get_location_url(DocHost*,BSTR*); HRESULT set_dochost_url(DocHost*,const WCHAR*); void handle_navigation_error(DocHost*,HRESULT,BSTR,IHTMLWindow2*); diff --git a/dlls/ieframe/navigate.c b/dlls/ieframe/navigate.c index 488ac62fbdf..67f72d53ee0 100644 --- a/dlls/ieframe/navigate.c +++ b/dlls/ieframe/navigate.c @@ -306,12 +306,13 @@ static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, return S_OK; }
-void handle_navigation_error(DocHost* doc_host, HRESULT hres, BSTR url, IHTMLWindow2 *win2) +void handle_navigation_error(DocHost* doc_host, HRESULT status_code, BSTR url, IHTMLWindow2 *win2) { VARIANT var_status_code, var_frame_name, var_url; DISPPARAMS dispparams; VARIANTARG params[5]; VARIANT_BOOL cancel = VARIANT_FALSE; + HRESULT hres;
dispparams.cArgs = 5; dispparams.cNamedArgs = 0; @@ -324,7 +325,7 @@ void handle_navigation_error(DocHost* doc_host, HRESULT hres, BSTR url, IHTMLWin V_VT(params+1) = VT_VARIANT|VT_BYREF; V_VARIANTREF(params+1) = &var_status_code; V_VT(&var_status_code) = VT_I4; - V_I4(&var_status_code) = hres; + V_I4(&var_status_code) = status_code;
V_VT(params+2) = VT_VARIANT|VT_BYREF; V_VARIANTREF(params+2) = &var_frame_name; @@ -347,8 +348,49 @@ void handle_navigation_error(DocHost* doc_host, HRESULT hres, BSTR url, IHTMLWin call_sink(doc_host->cps.wbe2, DISPID_NAVIGATEERROR, &dispparams); SysFreeString(V_BSTR(&var_frame_name));
- if(!cancel) - FIXME("Navigate to error page\n"); + if(!cancel) { + IHTMLPrivateWindow *priv_window; + IHTMLWindow2 *tmp; + + if(win2) + hres = IHTMLWindow2_QueryInterface(win2, &IID_IHTMLPrivateWindow, (void**)&priv_window); + else { + hres = get_window(doc_host, &tmp); + if(SUCCEEDED(hres)) { + if(!tmp) + hres = E_UNEXPECTED; + else { + hres = IHTMLWindow2_QueryInterface(tmp, &IID_IHTMLPrivateWindow, (void**)&priv_window); + IHTMLWindow2_Release(tmp); + } + } + } + if(SUCCEEDED(hres)) { + /* Error page navigation URL is a local resource (varies on native, also depending on error), + * with the fragment being the original URL of the page that failed to load. We add a query + * with the error code so the generic error page can display the actual error code there. */ + static const WCHAR shdoclcW[13] = L"\shdoclc.dll/"; + WCHAR buf[INTERNET_MAX_URL_LENGTH * 2]; + BSTR nav_url; + UINT len; + + memcpy(buf, L"res://", 6 * sizeof(WCHAR)); + len = 6 + GetSystemDirectoryW(buf + 6, ARRAY_SIZE(buf) - 6 - ARRAY_SIZE(shdoclcW)); + memcpy(buf + len, shdoclcW, sizeof(shdoclcW)); + len += ARRAY_SIZE(shdoclcW); + + if(SUCCEEDED(status_code)) + swprintf(buf + len, ARRAY_SIZE(buf) - len, L"ERROR.HTM?HTTP %u#%s", status_code, url); + else + swprintf(buf + len, ARRAY_SIZE(buf) - len, L"ERROR.HTM?0x%08x#%s", status_code, url); + + if((nav_url = SysAllocString(buf))) { + IHTMLPrivateWindow_SuperNavigate(priv_window, nav_url, NULL, NULL, NULL, NULL, NULL, 2); + SysFreeString(nav_url); + } + IHTMLPrivateWindow_Release(priv_window); + } + } }
static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface, diff --git a/dlls/ieframe/tests/ie.c b/dlls/ieframe/tests/ie.c index f5f565aa7fd..e6c222dd669 100644 --- a/dlls/ieframe/tests/ie.c +++ b/dlls/ieframe/tests/ie.c @@ -55,9 +55,72 @@ expect_ ## func = called_ ## func = FALSE; \ }while(0)
+DEFINE_EXPECT(Invoke_BEFORENAVIGATE2); DEFINE_EXPECT(Invoke_NAVIGATECOMPLETE2); +DEFINE_EXPECT(Invoke_NAVIGATEERROR); +DEFINE_EXPECT(Invoke_DOCUMENTCOMPLETE);
-static BOOL navigate_complete; +static BOOL navigate_complete, navigation_timed_out, broken_error_page_nav; +static const WCHAR *navigate_url; + +static BSTR get_window_url(IDispatch *webbrowser) +{ + IHTMLPrivateWindow *priv_window; + IHTMLLocation *location; + IHTMLWindow2 *window; + IServiceProvider *sp; + IHTMLDocument2 *doc; + HRESULT hres; + BSTR url; + + hres = IDispatch_QueryInterface(webbrowser, &IID_IServiceProvider, (void**)&sp); + ok(hres == S_OK, "QueryInterface(IServiceProvider) failed: %08lx\n", hres); + + hres = IServiceProvider_QueryService(sp, &SID_SHTMLWindow, &IID_IHTMLWindow2, (void**)&window); + ok(hres == S_OK, "Could not get SHTMLWindow service: %08lx\n", hres); + ok(window != NULL, "window = NULL\n"); + IServiceProvider_Release(sp); + + hres = IHTMLWindow2_QueryInterface(window, &IID_IHTMLPrivateWindow, (void**)&priv_window); + ok(hres == E_NOINTERFACE, "QueryInterface(IID_IHTMLPrivateWindow) returned: %08lx\n", hres); + + hres = IHTMLWindow2_get_document(window, &doc); + ok(hres == S_OK, "get_document failed: %08lx\n", hres); + ok(doc != NULL, "doc = NULL\n"); + IHTMLWindow2_Release(window); + + hres = IHTMLDocument2_get_parentWindow(doc, &window); + ok(hres == S_OK, "get_parentWindow failed: %08lx\n", hres); + ok(window != NULL, "window = NULL\n"); + IHTMLDocument2_Release(doc); + + hres = IHTMLWindow2_get_location(window, &location); + ok(hres == S_OK, "get_location failed: %08lx\n", hres); + ok(location != NULL, "location = NULL\n"); + IHTMLWindow2_Release(window); + + hres = IHTMLLocation_get_href(location, &url); + ok(hres == S_OK, "get_href failed: %08lx\n", hres); + ok(url != NULL, "url = NULL\n"); + IHTMLLocation_Release(location); + + return url; +} + +#define test_url(a) _test_url(__LINE__,a) +static void _test_url(unsigned line, const WCHAR *url) +{ + /* If error page, it actually returns the error page's resource URL, followed by #, followed by the original URL. + Since the error page's location varies on native, and depends on the error itself, just check for res:// here. */ + if(called_Invoke_NAVIGATEERROR) { + ok_(__FILE__,line)(!wcsncmp(url, L"res://", ARRAY_SIZE(L"res://")-1), "url is not a local resource: %s\n", wine_dbgstr_w(url)); + url = wcschr(url, '#'); + ok_(__FILE__,line)(url != NULL, "url has no fragment: %s\n", wine_dbgstr_w(url)); + ok_(__FILE__,line)(!wcscmp(url + 1, navigate_url), "url after fragment = %s\n", wine_dbgstr_w(url + 1)); + }else { + ok_(__FILE__,line)(!wcscmp(url, navigate_url), "url = %s\n", wine_dbgstr_w(url)); + } +}
static HRESULT WINAPI Dispatch_QueryInterface(IDispatch *iface, REFIID riid, void **ppv) { @@ -104,10 +167,77 @@ static HRESULT WINAPI Dispatch_Invoke(IDispatch *iface, DISPID dispIdMember, REF LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { + VARIANT *arg; + BSTR url; + switch(dispIdMember) { + case DISPID_BEFORENAVIGATE2: + CHECK_EXPECT(Invoke_BEFORENAVIGATE2); + + arg = pDispParams->rgvarg + 5; + ok(V_VT(arg) == (VT_BYREF | VT_VARIANT), "VT = %d\n", V_VT(arg)); + ok(V_VT(V_VARIANTREF(arg)) == VT_BSTR, "VT = %d\n", V_VT(V_VARIANTREF(arg))); + + /* w8adm is broken and will set error page to about:blank */ + if(expect_Invoke_NAVIGATEERROR && !wcscmp(V_BSTR(V_VARIANTREF(arg)), L"about:blank")) { + win_skip("Broken error page navigation, skipping tests...\n"); + broken_error_page_nav = TRUE; + return S_OK; + } + + test_url(V_BSTR(V_VARIANTREF(arg))); + return S_OK; case DISPID_NAVIGATECOMPLETE2: CHECK_EXPECT(Invoke_NAVIGATECOMPLETE2); + + if(broken_error_page_nav) + return S_OK; + + arg = pDispParams->rgvarg; + ok(V_VT(arg) == (VT_BYREF | VT_VARIANT), "VT = %d\n", V_VT(arg)); + ok(V_VT(V_VARIANTREF(arg)) == VT_BSTR, "VT = %d\n", V_VT(V_VARIANTREF(arg))); + todo_wine_if(called_Invoke_NAVIGATEERROR) + ok(!wcscmp(V_BSTR(V_VARIANTREF(arg)), navigate_url), "url = %s\n", wine_dbgstr_w(V_BSTR(V_VARIANTREF(arg)))); + + arg = pDispParams->rgvarg + 1; + ok(V_VT(arg) == VT_DISPATCH, "VT = %d\n", V_VT(arg)); + ok(V_DISPATCH(arg) != NULL, "V_DISPATCH = NULL\n"); + url = get_window_url(V_DISPATCH(arg)); + test_url(url); + SysFreeString(url); + return S_OK; + case DISPID_NAVIGATEERROR: + CHECK_EXPECT(Invoke_NAVIGATEERROR); + ok(!called_Invoke_NAVIGATECOMPLETE2, "NAVIGATECOMPLETE2 called before NAVIGATEERROR\n"); + + arg = pDispParams->rgvarg; + ok(V_VT(arg) == (VT_BYREF | VT_BOOL), "VT = %d\n", V_VT(arg)); + ok(*V_BOOLREF(arg) == VARIANT_FALSE, "cancel = %#x\n", *V_BOOLREF(arg)); + + arg = pDispParams->rgvarg + 3; + ok(V_VT(arg) == (VT_BYREF | VT_VARIANT), "VT = %d\n", V_VT(arg)); + ok(V_VT(V_VARIANTREF(arg)) == VT_BSTR, "VT = %d\n", V_VT(V_VARIANTREF(arg))); + ok(!wcscmp(V_BSTR(V_VARIANTREF(arg)), navigate_url), "url = %s\n", wine_dbgstr_w(V_BSTR(V_VARIANTREF(arg)))); + return S_OK; + case DISPID_DOCUMENTCOMPLETE: + CHECK_EXPECT(Invoke_DOCUMENTCOMPLETE); navigate_complete = TRUE; + + if(broken_error_page_nav) + return S_OK; + + arg = pDispParams->rgvarg; + ok(V_VT(arg) == (VT_BYREF | VT_VARIANT), "VT = %d\n", V_VT(arg)); + ok(V_VT(V_VARIANTREF(arg)) == VT_BSTR, "VT = %d\n", V_VT(V_VARIANTREF(arg))); + todo_wine_if(called_Invoke_NAVIGATEERROR) + ok(!wcscmp(V_BSTR(V_VARIANTREF(arg)), navigate_url), "url = %s\n", wine_dbgstr_w(V_BSTR(V_VARIANTREF(arg)))); + + arg = pDispParams->rgvarg + 1; + ok(V_VT(arg) == VT_DISPATCH, "VT = %d\n", V_VT(arg)); + ok(V_DISPATCH(arg) != NULL, "V_DISPATCH = NULL\n"); + url = get_window_url(V_DISPATCH(arg)); + test_url(url); + SysFreeString(url); return S_OK; }
@@ -141,7 +271,8 @@ static void advise_cp(IUnknown *unk, BOOL init)
hres = IConnectionPointContainer_FindConnectionPoint(container, &DIID_DWebBrowserEvents2, &point); IConnectionPointContainer_Release(container); - ok(hres == S_OK, "FindConnectionPoint failed: %08lx\n", hres); + if(!navigation_timed_out) + ok(hres == S_OK, "FindConnectionPoint failed: %08lx\n", hres); if(FAILED(hres)) return;
@@ -211,13 +342,33 @@ static void test_window(IWebBrowser2 *wb) ok(!strcmp(buf, "IEFrame"), "Unexpected class name %s\n", buf); }
-static void test_navigate(IWebBrowser2 *wb, const WCHAR *url) +static void CALLBACK navigate_timeout(HWND hwnd, UINT msg, UINT_PTR timer, DWORD time) +{ + win_skip("Navigation timed out, skipping tests...\n"); + called_Invoke_BEFORENAVIGATE2 = TRUE; + called_Invoke_NAVIGATECOMPLETE2 = TRUE; + called_Invoke_DOCUMENTCOMPLETE = TRUE; + if(expect_Invoke_NAVIGATEERROR) { + CHECK_EXPECT(Invoke_NAVIGATEERROR); + broken_error_page_nav = FALSE; + } + navigation_timed_out = TRUE; + navigate_complete = TRUE; +} + +static void test_navigate(IWebBrowser2 *wb, const WCHAR *url, DWORD timeout) { VARIANT urlv, emptyv; + UINT_PTR timer = 0; MSG msg; HRESULT hres;
+ SET_EXPECT(Invoke_BEFORENAVIGATE2); SET_EXPECT(Invoke_NAVIGATECOMPLETE2); + SET_EXPECT(Invoke_DOCUMENTCOMPLETE); + navigation_timed_out = FALSE; + navigate_complete = FALSE; + navigate_url = url;
V_VT(&urlv) = VT_BSTR; V_BSTR(&urlv) = SysAllocString(url); @@ -226,12 +377,24 @@ static void test_navigate(IWebBrowser2 *wb, const WCHAR *url) ok(hres == S_OK, "Navigate2 failed: %08lx\n", hres); SysFreeString(V_BSTR(&urlv));
+ if(timeout) + timer = SetTimer(NULL, 0, timeout, navigate_timeout); + while(!navigate_complete && GetMessageW(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); }
+ if(timer) + KillTimer(NULL, timer); + + CHECK_CALLED(Invoke_BEFORENAVIGATE2); CHECK_CALLED(Invoke_NAVIGATECOMPLETE2); + CHECK_CALLED(Invoke_DOCUMENTCOMPLETE); + if(broken_error_page_nav) { + CHECK_EXPECT(Invoke_NAVIGATEERROR); + broken_error_page_nav = FALSE; + } }
static void test_busy(IWebBrowser2 *wb) @@ -272,7 +435,11 @@ static void test_InternetExplorer(void) test_visible(wb); test_html_window(wb); test_window(wb); - test_navigate(wb, L"http://test.winehq.org/tests/hello.html"); + test_navigate(wb, L"http://test.winehq.org/tests/hello.html", 0); + + SET_EXPECT(Invoke_NAVIGATEERROR); + test_navigate(wb, L"http://localhost/#frag?query=foo&wine=bar", 6000); + CHECK_CALLED(Invoke_NAVIGATEERROR);
advise_cp(unk, FALSE);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/ieframe/ieframe.h | 1 + dlls/ieframe/iexplore.c | 4 ++++ dlls/ieframe/shellbrowser.c | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+)
diff --git a/dlls/ieframe/ieframe.h b/dlls/ieframe/ieframe.h index b03fe6e8e83..602bb1c5d36 100644 --- a/dlls/ieframe/ieframe.h +++ b/dlls/ieframe/ieframe.h @@ -281,6 +281,7 @@ HRESULT get_window(DocHost*,IHTMLWindow2**); HRESULT get_location_url(DocHost*,BSTR*); HRESULT set_dochost_url(DocHost*,const WCHAR*); void handle_navigation_error(DocHost*,HRESULT,BSTR,IHTMLWindow2*); +const WCHAR *error_url_frag(const WCHAR*); HRESULT dochost_object_available(DocHost*,IUnknown*); void set_doc_state(DocHost*,READYSTATE); void activate_document(DocHost*); diff --git a/dlls/ieframe/iexplore.c b/dlls/ieframe/iexplore.c index acf7a18e3f3..43ecf5ee3f1 100644 --- a/dlls/ieframe/iexplore.c +++ b/dlls/ieframe/iexplore.c @@ -783,6 +783,10 @@ static void DocHostContainer_on_command_state_change(DocHost *iface, LONG comman static void DocHostContainer_set_url(DocHost* iface, const WCHAR *url) { InternetExplorer *This = impl_from_DocHost(iface); + const WCHAR *orig_url = error_url_frag(url); + + if(orig_url) + url = orig_url;
This->nohome = FALSE; SendMessageW(This->frame_hwnd, WM_UPDATEADDRBAR, 0, (LPARAM)url); diff --git a/dlls/ieframe/shellbrowser.c b/dlls/ieframe/shellbrowser.c index a5666fd761b..d3f8c6f8ddb 100644 --- a/dlls/ieframe/shellbrowser.c +++ b/dlls/ieframe/shellbrowser.c @@ -29,6 +29,24 @@
WINE_DEFAULT_DEBUG_CHANNEL(ieframe);
+const WCHAR *error_url_frag(const WCHAR *url) +{ + if(!wcsncmp(url, L"res://", ARRAY_SIZE(L"res://")-1)) { + WCHAR buf[MAX_PATH]; + UINT len = GetSystemDirectoryW(buf, ARRAY_SIZE(buf)); + + if(len && !wcsncmp(url + ARRAY_SIZE(L"res://")-1, buf, len)) { + len += ARRAY_SIZE(L"res://")-1; + if(!wcsncmp(url + len, L"\shdoclc.dll/ERROR.HTM", ARRAY_SIZE(L"\shdoclc.dll/ERROR.HTM")-1)) { + len += ARRAY_SIZE(L"\shdoclc.dll/ERROR.HTM")-1; + url = wcschr(url + len, '#'); + return url ? url + 1 : NULL; + } + } + } + return NULL; +} + static inline ShellBrowser *impl_from_IShellBrowser(IShellBrowser *iface) { return CONTAINING_RECORD(iface, ShellBrowser, IShellBrowser_iface);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/ieframe/shellbrowser.c | 8 ++++++++ dlls/ieframe/tests/ie.c | 1 - 2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/dlls/ieframe/shellbrowser.c b/dlls/ieframe/shellbrowser.c index d3f8c6f8ddb..58ff851fb0b 100644 --- a/dlls/ieframe/shellbrowser.c +++ b/dlls/ieframe/shellbrowser.c @@ -758,6 +758,7 @@ static HRESULT WINAPI DocObjectService_FireNavigateComplete2( ShellBrowser *This = impl_from_IDocObjectService(iface); DocHost *doc_host = This->doc_host; IHTMLPrivateWindow *priv_window; + const WCHAR *orig_url; VARIANTARG params[2]; DISPPARAMS dp = {params, NULL, 2, 0}; VARIANT url_var; @@ -786,6 +787,13 @@ static HRESULT WINAPI DocObjectService_FireNavigateComplete2( TRACE("got URL %s\n", debugstr_w(url)); set_dochost_url(This->doc_host, url);
+ orig_url = error_url_frag(url); + if(orig_url) { + BSTR tmp = SysAllocString(orig_url); + SysFreeString(url); + url = tmp; + } + V_VT(params) = (VT_BYREF|VT_VARIANT); V_VARIANTREF(params) = &url_var;
diff --git a/dlls/ieframe/tests/ie.c b/dlls/ieframe/tests/ie.c index e6c222dd669..571a5b2d837 100644 --- a/dlls/ieframe/tests/ie.c +++ b/dlls/ieframe/tests/ie.c @@ -196,7 +196,6 @@ static HRESULT WINAPI Dispatch_Invoke(IDispatch *iface, DISPID dispIdMember, REF arg = pDispParams->rgvarg; ok(V_VT(arg) == (VT_BYREF | VT_VARIANT), "VT = %d\n", V_VT(arg)); ok(V_VT(V_VARIANTREF(arg)) == VT_BSTR, "VT = %d\n", V_VT(V_VARIANTREF(arg))); - todo_wine_if(called_Invoke_NAVIGATEERROR) ok(!wcscmp(V_BSTR(V_VARIANTREF(arg)), navigate_url), "url = %s\n", wine_dbgstr_w(V_BSTR(V_VARIANTREF(arg))));
arg = pDispParams->rgvarg + 1;
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/ieframe/shellbrowser.c | 8 ++++++++ dlls/ieframe/tests/ie.c | 1 - 2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/dlls/ieframe/shellbrowser.c b/dlls/ieframe/shellbrowser.c index 58ff851fb0b..ca229c0ac8b 100644 --- a/dlls/ieframe/shellbrowser.c +++ b/dlls/ieframe/shellbrowser.c @@ -839,6 +839,7 @@ static HRESULT WINAPI DocObjectService_FireDocumentComplete( { ShellBrowser *This = impl_from_IDocObjectService(iface); IHTMLPrivateWindow *priv_window; + const WCHAR *orig_url; VARIANTARG params[2]; DISPPARAMS dp = {params, NULL, 2, 0}; VARIANT url_var; @@ -858,6 +859,13 @@ static HRESULT WINAPI DocObjectService_FireDocumentComplete(
TRACE("got URL %s\n", debugstr_w(url));
+ orig_url = error_url_frag(url); + if(orig_url) { + BSTR tmp = SysAllocString(orig_url); + SysFreeString(url); + url = tmp; + } + V_VT(params) = (VT_BYREF|VT_VARIANT); V_VARIANTREF(params) = &url_var;
diff --git a/dlls/ieframe/tests/ie.c b/dlls/ieframe/tests/ie.c index 571a5b2d837..8d5c570f22b 100644 --- a/dlls/ieframe/tests/ie.c +++ b/dlls/ieframe/tests/ie.c @@ -228,7 +228,6 @@ static HRESULT WINAPI Dispatch_Invoke(IDispatch *iface, DISPID dispIdMember, REF arg = pDispParams->rgvarg; ok(V_VT(arg) == (VT_BYREF | VT_VARIANT), "VT = %d\n", V_VT(arg)); ok(V_VT(V_VARIANTREF(arg)) == VT_BSTR, "VT = %d\n", V_VT(V_VARIANTREF(arg))); - todo_wine_if(called_Invoke_NAVIGATEERROR) ok(!wcscmp(V_BSTR(V_VARIANTREF(arg)), navigate_url), "url = %s\n", wine_dbgstr_w(V_BSTR(V_VARIANTREF(arg))));
arg = pDispParams->rgvarg + 1;
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Needed to prevent an infinite loop when the error page itself fails to load (mshtml bails out early).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/ieframe/shellbrowser.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/dlls/ieframe/shellbrowser.c b/dlls/ieframe/shellbrowser.c index ca229c0ac8b..e2c4c451741 100644 --- a/dlls/ieframe/shellbrowser.c +++ b/dlls/ieframe/shellbrowser.c @@ -930,9 +930,10 @@ static HRESULT WINAPI DocObjectService_IsErrorUrl( BOOL *pfIsError) { ShellBrowser *This = impl_from_IDocObjectService(iface); - FIXME("%p %s %p\n", This, debugstr_w(lpszUrl), pfIsError);
- *pfIsError = FALSE; + TRACE("(%p)->(%s %p)\n", This, debugstr_w(lpszUrl), pfIsError); + + *pfIsError = !!error_url_frag(lpszUrl); return S_OK; }
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=138303
Your paranoid android.
=== w10pro64_en_AE_u8 (32 bit report) ===
mshtml: htmldoc.c:352: Test failed: expected Exec_SETTITLE
Jacek Caban (@jacek) commented about dlls/ieframe/navigate.c:
hres = get_window(doc_host, &tmp);
if(SUCCEEDED(hres)) {
if(!tmp)
hres = E_UNEXPECTED;
else {
hres = IHTMLWindow2_QueryInterface(tmp, &IID_IHTMLPrivateWindow, (void**)&priv_window);
IHTMLWindow2_Release(tmp);
}
}
}
if(SUCCEEDED(hres)) {
/* Error page navigation URL is a local resource (varies on native, also depending on error),
* with the fragment being the original URL of the page that failed to load. We add a query
* with the error code so the generic error page can display the actual error code there. */
static const WCHAR shdoclcW[13] = L"\\shdoclc.dll/";
WCHAR buf[INTERNET_MAX_URL_LENGTH * 2];
Please avoid using INTERNET_MAX_URL_LENGTH, restrictions on URL length were relaxed after its introduction and its remaining usage in IE components are likely wrong. In this case you dynamically allocate URL anyway.
BTW, there is IUriBuilder which is a cleaner way to construct URLs without worrying about corner cases, but I guess it's simple enough that it doesn't matter much in this case.
Jacek Caban (@jacek) commented about dlls/ieframe/tests/ie.c:
ok(!strcmp(buf, "IEFrame"), "Unexpected class name %s\n", buf);
}
-static void test_navigate(IWebBrowser2 *wb, const WCHAR *url) +static void CALLBACK navigate_timeout(HWND hwnd, UINT msg, UINT_PTR timer, DWORD time) +{
- win_skip("Navigation timed out, skipping tests...\n");
This timeout is weird, do you know why do we hit it? Would using something like test.winehq.org:1234 instead of localhost avoid the problem?
On Thu Oct 5 10:43:38 2023 +0000, Jacek Caban wrote:
This timeout is weird, do you know why do we hit it? Would using something like test.winehq.org:1234 instead of localhost avoid the problem?
No idea why it happens only on a couple testbot machines (it doesn't happen on my VM either). I'll try experiment a bit but can't promise if it will fix it, since I've no idea what causes it.