This uses the undocumented MapBrowserEmulationModeToUserAgent and only the first field of the unknown struct, but it is enough for this purpose. It is important for some apps (e.g. FFXIV Launcher) which expects it to work like this.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
I've exhaustively tried other options (and flags) for ObtainUserAgentString, but none of them gave any special result on native. This is not fixable on urlmon side for ObtainUserAgentString API, see next patch (which would break the mshtml tests by itself, that's why it is after the fix).
dlls/mshtml/omnavigator.c | 42 +++++++++++++++------------------- dlls/mshtml/tests/dom.c | 6 +++++ dlls/mshtml/tests/rsrc.rc | 3 +++ dlls/mshtml/tests/script.c | 32 ++++++++++++++++++++++++++ dlls/mshtml/tests/useragent.js | 29 +++++++++++++++++++++++ dlls/urlmon/urlmon.spec | 2 +- 6 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 dlls/mshtml/tests/useragent.js
diff --git a/dlls/mshtml/omnavigator.c b/dlls/mshtml/omnavigator.c index 0a2a6bb..844fa63 100644 --- a/dlls/mshtml/omnavigator.c +++ b/dlls/mshtml/omnavigator.c @@ -1182,57 +1182,51 @@ static unsigned int get_ua_version(OmNavigator *navigator) return 0; }
+/* undocumented, added in IE8 */ +extern HRESULT WINAPI MapBrowserEmulationModeToUserAgent(const void*,WCHAR**); + static HRESULT WINAPI OmNavigator_get_appVersion(IOmNavigator *iface, BSTR *p) { OmNavigator *This = impl_from_IOmNavigator(iface); - - char user_agent[512]; - DWORD size; + DWORD len, version = get_ua_version(This); + WCHAR *user_agent; HRESULT hres; const unsigned skip_prefix = 8; /* strlen("Mozilla/") */
TRACE("(%p)->(%p)\n", This, p);
- size = sizeof(user_agent); - hres = ObtainUserAgentString(get_ua_version(This), user_agent, &size); + hres = MapBrowserEmulationModeToUserAgent(&version, &user_agent); if(FAILED(hres)) return hres; + len = wcslen(user_agent);
- if(size <= skip_prefix) { + if(len <= skip_prefix) { + CoTaskMemFree(user_agent); *p = NULL; return S_OK; }
- size = MultiByteToWideChar(CP_ACP, 0, user_agent + skip_prefix, -1, NULL, 0); - *p = SysAllocStringLen(NULL, size-1); - if(!*p) - return E_OUTOFMEMORY; - - MultiByteToWideChar(CP_ACP, 0, user_agent + skip_prefix, -1, *p, size); - return S_OK; + *p = SysAllocStringLen(user_agent + skip_prefix, len - skip_prefix); + CoTaskMemFree(user_agent); + return *p ? S_OK : E_OUTOFMEMORY; }
static HRESULT WINAPI OmNavigator_get_userAgent(IOmNavigator *iface, BSTR *p) { OmNavigator *This = impl_from_IOmNavigator(iface); - char user_agent[512]; - DWORD size; + DWORD version = get_ua_version(This); + WCHAR *user_agent; HRESULT hres;
TRACE("(%p)->(%p)\n", This, p);
- size = sizeof(user_agent); - hres = ObtainUserAgentString(get_ua_version(This), user_agent, &size); + hres = MapBrowserEmulationModeToUserAgent(&version, &user_agent); if(FAILED(hres)) return hres;
- size = MultiByteToWideChar(CP_ACP, 0, user_agent, -1, NULL, 0); - *p = SysAllocStringLen(NULL, size-1); - if(!*p) - return E_OUTOFMEMORY; - - MultiByteToWideChar(CP_ACP, 0, user_agent, -1, *p, size); - return S_OK; + *p = SysAllocString(user_agent); + CoTaskMemFree(user_agent); + return *p ? S_OK : E_OUTOFMEMORY; }
static HRESULT WINAPI OmNavigator_javaEnabled(IOmNavigator *iface, VARIANT_BOOL *enabled) diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index b66073f..2de34b5 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -6479,6 +6479,12 @@ static void test_navigator(IHTMLDocument2 *doc) ok(!lstrcmpW(bstr, buf+8), "appVersion returned %s, expected "%s"\n", wine_dbgstr_w(bstr), wine_dbgstr_w(buf+8)); SysFreeString(bstr);
+ bstr = NULL; + hres = IOmNavigator_get_userAgent(navigator, &bstr); + ok(hres == S_OK, "get_userAgent failed: %08lx\n", hres); + ok(!lstrcmpW(bstr, buf), "userAgent returned %s, expected "%s"\n", wine_dbgstr_w(bstr), wine_dbgstr_w(buf)); + SysFreeString(bstr); + hres = UrlMkSetSessionOption(URLMON_OPTION_USERAGENT, buf, lstrlenW(buf), 0); ok(hres == S_OK, "UrlMkSetSessionOption failed: %08lx\n", hres);
diff --git a/dlls/mshtml/tests/rsrc.rc b/dlls/mshtml/tests/rsrc.rc index 61e6c94..2b9a9ba 100644 --- a/dlls/mshtml/tests/rsrc.rc +++ b/dlls/mshtml/tests/rsrc.rc @@ -64,6 +64,9 @@ events.js HTML "events.js" /* @makedep: documentmode.js */ documentmode.js HTML "documentmode.js"
+/* @makedep: useragent.js */ +useragent.js HTML "useragent.js" + /* @makedep: blank.html */ blank.html HTML "blank.html"
diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index de42e89..05cdad1 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -151,6 +151,7 @@ DEFINE_EXPECT(GetTypeInfo); #define DISPID_EXTERNAL_BROKEN 0x300004 #define DISPID_EXTERNAL_WIN_SKIP 0x300005 #define DISPID_EXTERNAL_WRITESTREAM 0x300006 +#define DISPID_EXTERNAL_SET_UA 0x300007
static const GUID CLSID_TestScript = {0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x07,0x46}}; @@ -589,6 +590,10 @@ static HRESULT WINAPI externalDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, *pid = DISPID_EXTERNAL_WRITESTREAM; return S_OK; } + if(!lstrcmpW(bstrName, L"setUA")) { + *pid = DISPID_EXTERNAL_SET_UA; + return S_OK; + }
ok(0, "unexpected name %s\n", wine_dbgstr_w(bstrName)); return DISP_E_UNKNOWNNAME; @@ -716,6 +721,22 @@ static HRESULT WINAPI externalDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID stream_write(V_BSTR(pdp->rgvarg+1), V_BSTR(pdp->rgvarg)); return S_OK;
+ case DISPID_EXTERNAL_SET_UA: { + char buf[128]; + DWORD size; + + ok(pdp != NULL, "pdp == NULL\n"); + ok(pdp->rgvarg != NULL, "rgvarg == NULL\n"); + ok(V_VT(pdp->rgvarg) == VT_BSTR, "V_VT(rgvarg) = %d\n", V_VT(pdp->rgvarg)); + ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n"); + ok(pdp->cArgs == 1, "cArgs = %d\n", pdp->cArgs); + ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs); + ok(pei != NULL, "pei == NULL\n"); + + size = WideCharToMultiByte(CP_ACP, 0, V_BSTR(pdp->rgvarg), -1, buf, sizeof(buf), NULL, NULL); + return UrlMkSetSessionOption(URLMON_OPTION_USERAGENT, buf, size, 0); + } + default: ok(0, "unexpected call\n"); return E_NOTIMPL; @@ -3624,6 +3645,17 @@ static void run_js_tests(void) run_script_as_http_with_mode("documentmode.js", "11", "edge;123");
run_script_as_http_with_mode("asyncscriptload.js", NULL, "9"); + + /* Run these last since they mess with the process-wide user agent */ + run_script_as_http_with_mode("useragent.js", "0", NULL); + run_script_as_http_with_mode("useragent.js", "5", "5"); + run_script_as_http_with_mode("useragent.js", "5", "6"); + run_script_as_http_with_mode("useragent.js", "7", "7"); + run_script_as_http_with_mode("useragent.js", "8", "8"); + run_script_as_http_with_mode("useragent.js", "9", "9"); + run_script_as_http_with_mode("useragent.js", "10", "10;abc"); + run_script_as_http_with_mode("useragent.js", "11", "11"); + run_script_as_http_with_mode("useragent.js", "11", "edge;123"); }
static BOOL init_registry(BOOL init) diff --git a/dlls/mshtml/tests/useragent.js b/dlls/mshtml/tests/useragent.js new file mode 100644 index 0000000..f36869d --- /dev/null +++ b/dlls/mshtml/tests/useragent.js @@ -0,0 +1,29 @@ +/* + * Copyright 2022 Gabriel Ivăncescu for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +var tests = []; + +sync_test("user_agent", function() { + var ua = "1234567890xxxABC"; + + if(document.documentMode === 5) + window.external.setUA(ua); + + ok(navigator.userAgent === ua, "userAgent = " + navigator.userAgent); + ok(navigator.appVersion === "90xxxABC", "appVersion = " + navigator.appVersion); +}); diff --git a/dlls/urlmon/urlmon.spec b/dlls/urlmon/urlmon.spec index 2fda69e..8725c8c 100644 --- a/dlls/urlmon/urlmon.spec +++ b/dlls/urlmon/urlmon.spec @@ -110,6 +110,6 @@ 410 stdcall @(long long) LogSqmBits 423 stdcall @(long long long long) LogSqmUXCommandOffsetInternal 444 stdcall @(long long long) MapUriToBrowserEmulationState -445 stdcall @(ptr ptr) MapBrowserEmulationModeToUserAgent +445 stdcall -noname MapBrowserEmulationModeToUserAgent(ptr ptr) 446 stdcall @(long) CoInternetGetBrowserProfile 455 stdcall @() FlushUrlmonZonesCache