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

Gabriel Ivăncescu gabrielopcode at gmail.com
Fri Apr 8 09:35:51 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>
---

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
-- 
2.34.1




More information about the wine-devel mailing list