[PATCH v2 2/6] mshtml: Always return the custom user agent if it has been set.

Gabriel Ivăncescu gabrielopcode at gmail.com
Mon Apr 11 10:58:48 CDT 2022


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 at gmail.com>
---
 dlls/mshtml/omnavigator.c | 67 ++++++++++++++++++++-------------------
 dlls/mshtml/tests/dom.c   | 51 +++++++++++++++++++++++++++++
 dlls/urlmon/urlmon.spec   |  2 +-
 3 files changed, 87 insertions(+), 33 deletions(-)

diff --git a/dlls/mshtml/omnavigator.c b/dlls/mshtml/omnavigator.c
index 0a2a6bb..9962cc2 100644
--- a/dlls/mshtml/omnavigator.c
+++ b/dlls/mshtml/omnavigator.c
@@ -1162,77 +1162,80 @@ static HRESULT WINAPI OmNavigator_get_appName(IOmNavigator *iface, BSTR *p)
     return S_OK;
 }
 
-static unsigned int get_ua_version(OmNavigator *navigator)
+/* undocumented, added in IE8 */
+extern HRESULT WINAPI MapBrowserEmulationModeToUserAgent(const void*,WCHAR**);
+
+/* Retrieves allocated user agent via CoTaskMemAlloc */
+static HRESULT get_user_agent(OmNavigator *navigator, WCHAR **user_agent)
 {
+    DWORD version;
+
     switch(dispex_compat_mode(&navigator->dispex)) {
     case COMPAT_MODE_QUIRKS:
     case COMPAT_MODE_IE5:
     case COMPAT_MODE_IE7:
-        return 7;
+        version = 7;
+        break;
     case COMPAT_MODE_IE8:
-        return 8;
+        version = 8;
+        break;
     case COMPAT_MODE_IE9:
-        return 9;
+        version = 9;
+        break;
     case COMPAT_MODE_IE10:
-        return 10;
+        version = 10;
+        break;
     case COMPAT_MODE_IE11:
-        return 11;
+        version = 11;
+        break;
+    default:
+        assert(0);
+        return E_FAIL;
     }
-    assert(0);
-    return 0;
+    return MapBrowserEmulationModeToUserAgent(&version, user_agent);
 }
 
 static HRESULT WINAPI OmNavigator_get_appVersion(IOmNavigator *iface, BSTR *p)
 {
     OmNavigator *This = impl_from_IOmNavigator(iface);
-
-    char user_agent[512];
-    DWORD size;
+    WCHAR *user_agent;
+    unsigned len;
     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 = get_user_agent(This, &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;
+    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 = get_user_agent(This, &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..4af0e9b 100644
--- a/dlls/mshtml/tests/dom.c
+++ b/dlls/mshtml/tests/dom.c
@@ -6479,6 +6479,11 @@ 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);
 
+    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);
 
@@ -11294,6 +11299,42 @@ static void test_quirks_mode(void)
                 "</html>", test_document_mode);
 }
 
+static void test_custom_user_agent(IHTMLDocument2 *doc)
+{
+    static const WCHAR ua[] = L"1234567890xxxABC";
+    static char ua_ascii[] = "1234567890xxxABC";
+    IOmNavigator *navigator;
+    IHTMLWindow2 *window;
+    HRESULT hres;
+    BSTR bstr;
+
+    /* Set it only first time, to test it doesn't get reset on compat mode change */
+    if(compat_mode == COMPAT_NONE) {
+        hres = UrlMkSetSessionOption(URLMON_OPTION_USERAGENT, ua_ascii, sizeof(ua_ascii), 0);
+        ok(hres == S_OK, "UrlMkSetSessionOption failed: %08lx\n", hres);
+    }
+
+    hres = IHTMLDocument2_get_parentWindow(doc, &window);
+    ok(hres == S_OK, "parentWidnow failed: %08lx\n", hres);
+
+    hres = IHTMLWindow2_get_navigator(window, &navigator);
+    ok(hres == S_OK, "get_navigator failed: %08lx\n", hres);
+    ok(navigator != NULL, "navigator == NULL\n");
+    IHTMLWindow2_Release(window);
+
+    hres = IOmNavigator_get_appVersion(navigator, &bstr);
+    ok(hres == S_OK, "get_appVersion failed: %08lx\n", hres);
+    ok(!lstrcmpW(bstr, ua+8), "appVersion returned %s, expected %s\n", wine_dbgstr_w(bstr), wine_dbgstr_w(ua+8));
+    SysFreeString(bstr);
+
+    hres = IOmNavigator_get_userAgent(navigator, &bstr);
+    ok(hres == S_OK, "get_userAgent failed: %08lx\n", hres);
+    ok(!lstrcmpW(bstr, ua), "userAgent returned %s, expected %s\n", wine_dbgstr_w(bstr), wine_dbgstr_w(ua));
+    SysFreeString(bstr);
+
+    IOmNavigator_Release(navigator);
+}
+
 static void check_ie(void)
 {
     IHTMLDocument2 *doc;
@@ -11356,6 +11397,16 @@ START_TEST(dom)
 
     test_quirks_mode();
 
+    /* Run this last since it messes with the process-wide user agent */
+    if (winetest_interactive || ! is_ie_hardened()) {
+        run_domtest(doc_blank, test_custom_user_agent);
+        if(is_ie9plus) {
+            compat_mode = COMPAT_IE9;
+            run_domtest(doc_blank_ie9, test_custom_user_agent);
+            compat_mode = COMPAT_NONE;
+        }
+    }
+
     DestroyWindow(container_hwnd);
     CoUninitialize();
 }
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
-- 
2.34.1




More information about the wine-devel mailing list