This is necessary for mshtml's navigator userAgent to work properly, since some apps expect it.
The second argument seems to be output pointer that gets allocated (by CoTaskMemAlloc, or something compatible with it), but the first argument seems to be a pointer to an unknown struct, which makes it almost impossible to guess reliably what it does by just inspecting its behavior...
Thankfully, it seems only the first DWORD field is necessary for this. Changing it returns the user agent for that given mode, if the rest is zeros.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/urlmon/session.c | 40 +++++++++++++++++++++++++++ dlls/urlmon/tests/misc.c | 57 +++++++++++++++++++++++++++++++++++++++ dlls/urlmon/urlmon.spec | 2 +- dlls/urlmon/urlmon_main.c | 10 ------- 4 files changed, 98 insertions(+), 11 deletions(-)
diff --git a/dlls/urlmon/session.c b/dlls/urlmon/session.c index 052444d..e5dd26c 100644 --- a/dlls/urlmon/session.c +++ b/dlls/urlmon/session.c @@ -504,6 +504,7 @@ static BOOL get_url_encoding(HKEY root, DWORD *encoding) }
static LPWSTR user_agent; +static BOOL user_agent_set;
static size_t obtain_user_agent(unsigned int version, WCHAR *ret, size_t size) { @@ -710,6 +711,7 @@ HRESULT WINAPI UrlMkSetSessionOption(DWORD dwOption, LPVOID pBuffer, DWORD dwBuf
heap_free(user_agent); user_agent = new_user_agent; + user_agent_set = TRUE; update_user_agent(user_agent);
LeaveCriticalSection(&session_cs); @@ -748,6 +750,44 @@ HRESULT WINAPI ObtainUserAgentString(DWORD option, char *ret, DWORD *ret_size) return hres; }
+/*********************************************************************** + * MapBrowserEmulationModeToUserAgent (URLMON.445) + * Undocumented, added in IE8 + */ +HRESULT WINAPI MapBrowserEmulationModeToUserAgent(const void *arg, WCHAR **ret) +{ + DWORD size, version; + const WCHAR *ua; + WCHAR buf[1024]; + + TRACE("%p %p: semi-stub\n", arg, ret); + + if(user_agent_set) { + /* Native ignores first arg if custom user agent has been set, doesn't crash even if NULL */ + size = (wcslen(user_agent) + 1) * sizeof(WCHAR); + ua = user_agent; + }else { + *ret = NULL; + + /* First arg seems to be a pointer to a structure of unknown size, and crashes + if it's too small (or filled with arbitrary values from the stack). For our + purposes, we only check first field which seems to be the requested version. */ + version = *(DWORD*)arg; + if(version == 5) + version = 7; + if(version < 7 || version > 11) + return E_FAIL; + + size = obtain_user_agent(version, buf, ARRAY_SIZE(buf)) * sizeof(WCHAR); + ua = buf; + } + + if(!(*ret = CoTaskMemAlloc(size))) + return E_OUTOFMEMORY; + memcpy(*ret, ua, size); + return S_OK; +} + void free_session(void) { name_space *ns_iter, *ns_last; diff --git a/dlls/urlmon/tests/misc.c b/dlls/urlmon/tests/misc.c index 0575a43..c67b1d5 100644 --- a/dlls/urlmon/tests/misc.c +++ b/dlls/urlmon/tests/misc.c @@ -83,6 +83,7 @@ static HRESULT (WINAPI *pCompareSecurityIds)(BYTE*,DWORD,BYTE*,DWORD,DWORD); static HRESULT (WINAPI *pCoInternetIsFeatureEnabled)(INTERNETFEATURELIST,DWORD); static HRESULT (WINAPI *pCoInternetSetFeatureEnabled)(INTERNETFEATURELIST,DWORD,BOOL); static HRESULT (WINAPI *pIEInstallScope)(DWORD*); +static HRESULT (WINAPI *pMapBrowserEmulationModeToUserAgent)(const void*,WCHAR**);
static WCHAR *a2co(const char *str) { @@ -2721,6 +2722,59 @@ static void test_bsc_marshaling(void) TerminateThread(thread, 0); }
+static void test_MapBrowserEmulationModeToUserAgent(BOOL custom_ua) +{ + /* Undocumented structure of unknown size, crashes if it's too small (with arbitrary values from stack) */ + struct + { + DWORD version; + char unknown[252]; + } arg; + static char test_str[] = "test"; + const char *custom_ua_msg = ""; + HRESULT hres; + unsigned i; + WCHAR *ua; + + if(!pMapBrowserEmulationModeToUserAgent) { + win_skip("MapBrowserEmulationModeToUserAgent not available\n"); + return; + } + memset(&arg, 0, sizeof(arg)); + + if(custom_ua) { + hres = UrlMkSetSessionOption(URLMON_OPTION_USERAGENT, test_str, sizeof(test_str), 0); + ok(hres == S_OK, "UrlMkSetSessionOption failed: %08lx\n", hres); + custom_ua_msg = " (with custom ua)"; + } + + for(i = 0; i < 12; i++) { + arg.version = i; + ua = (WCHAR*)0xdeadbeef; + hres = pMapBrowserEmulationModeToUserAgent(&arg, &ua); + ok(hres == (i == 5 || i >= 7 || custom_ua ? S_OK : E_FAIL), + "[%u] MapBrowserEmulationModeToUserAgent%s returned %08lx\n", i, custom_ua_msg, hres); + if(hres != S_OK) + ok(ua == NULL, "[%u] ua%s = %p\n", i, custom_ua_msg, ua); + else { + char buf[1024]; + DWORD size = sizeof(buf); + WCHAR *ua2; + + if(custom_ua) + ua2 = a2co(test_str); + else { + hres = pObtainUserAgentString(i, buf, &size); + ok(hres == S_OK, "[%u] ObtainUserAgentString%s failed: %08lx\n", i, custom_ua_msg, hres); + ua2 = a2co(buf); + } + ok(!lstrcmpW(ua, ua2), "[%u] ua%s = %s, expected %s\n", i, custom_ua_msg, wine_dbgstr_w(ua), wine_dbgstr_w(ua2)); + CoTaskMemFree(ua2); + CoTaskMemFree(ua); + } + } +} + START_TEST(misc) { HMODULE hurlmon; @@ -2745,6 +2799,7 @@ START_TEST(misc) pCoInternetIsFeatureEnabled = (void*) GetProcAddress(hurlmon, "CoInternetIsFeatureEnabled"); pCoInternetSetFeatureEnabled = (void*) GetProcAddress(hurlmon, "CoInternetSetFeatureEnabled"); pIEInstallScope = (void*) GetProcAddress(hurlmon, "IEInstallScope"); + pMapBrowserEmulationModeToUserAgent = (void*) GetProcAddress(hurlmon, (const char*)445);
if (!pCoInternetCompareUrl || !pCoInternetGetSecurityUrl || !pCoInternetGetSession || !pCoInternetParseUrl || !pCompareSecurityIds) { @@ -2757,6 +2812,7 @@ START_TEST(misc) if(argc <= 2 || strcmp(argv[2], "internet_features")) { register_protocols();
+ test_MapBrowserEmulationModeToUserAgent(FALSE); test_CreateFormatEnum(); test_RegisterFormatEnumerator(); test_CoInternetParseUrl(); @@ -2773,6 +2829,7 @@ START_TEST(misc) test_MkParseDisplayNameEx(); test_IsValidURL(); test_bsc_marshaling(); + test_MapBrowserEmulationModeToUserAgent(TRUE); }
test_internet_features(); diff --git a/dlls/urlmon/urlmon.spec b/dlls/urlmon/urlmon.spec index ea3cd38..2fda69e 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 @(long long) MapBrowserEmulationModeToUserAgent +445 stdcall @(ptr ptr) MapBrowserEmulationModeToUserAgent 446 stdcall @(long) CoInternetGetBrowserProfile 455 stdcall @() FlushUrlmonZonesCache diff --git a/dlls/urlmon/urlmon_main.c b/dlls/urlmon/urlmon_main.c index b540313..56c2257 100644 --- a/dlls/urlmon/urlmon_main.c +++ b/dlls/urlmon/urlmon_main.c @@ -791,16 +791,6 @@ int WINAPI MapUriToBrowserEmulationState(DWORD unk1, DWORD unk2, DWORD unk3) return 0; }
-/*********************************************************************** - * MapBrowserEmulationModeToUserAgent (URLMON.445) - * Undocumented, added in IE8 - */ -int WINAPI MapBrowserEmulationModeToUserAgent(DWORD unk1, DWORD unk2) -{ - FIXME("stub: %ld %ld\n", unk1, unk2); - return 0; -} - /*********************************************************************** * CoInternetGetBrowserProfile (URLMON.446) * Undocumented, added in IE8