From 5489529f4e20efeec908af38c2f0d1f2974aa59c Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Tue, 9 Oct 2018 12:22:16 +0200 Subject: [PATCH] mshtml: Support registry configuration for maximal allowed compatibility mode. Signed-off-by: Jacek Caban Signed-off-by: Alexandre Julliard --- dlls/mshtml/main.c | 125 +++++++++++++++++++++++++++++++++++ dlls/mshtml/mshtml_private.h | 3 + dlls/mshtml/mutation.c | 60 +++++++++++------ 3 files changed, 166 insertions(+), 22 deletions(-) diff --git a/dlls/mshtml/main.c b/dlls/mshtml/main.c index 6ce8198aee5..2ba648f4190 100644 --- a/dlls/mshtml/main.c +++ b/dlls/mshtml/main.c @@ -36,6 +36,7 @@ #include "rpcproxy.h" #include "shlguid.h" #include "mlang.h" +#include "wininet.h" #include "wine/debug.h" @@ -53,6 +54,14 @@ DWORD mshtml_tls = TLS_OUT_OF_INDEXES; static HINSTANCE shdoclc = NULL; static WCHAR *status_strings[IDS_STATUS_LAST-IDS_STATUS_FIRST+1]; static IMultiLanguage2 *mlang; +static unsigned global_max_compat_mode = COMPAT_MODE_IE11; +static struct list compat_config = LIST_INIT(compat_config); + +typedef struct { + struct list entry; + compat_mode_t max_compat_mode; + WCHAR host[1]; +} compat_config_t; static BOOL ensure_mlang(void) { @@ -109,6 +118,114 @@ BSTR charset_string_from_cp(UINT cp) return SysAllocString(info.wszWebCharset); } +static BOOL read_compat_mode(HKEY key, compat_mode_t *r) +{ + WCHAR version[32]; + DWORD type, size; + LSTATUS status; + + static const WCHAR max_compat_modeW[] = {'M','a','x','C','o','m','p','a','t','M','o','d','e',0}; + + size = sizeof(version); + status = RegQueryValueExW(key, max_compat_modeW, NULL, &type, (BYTE*)version, &size); + if(status != ERROR_SUCCESS || type != REG_SZ) + return FALSE; + + return parse_compat_version(version, r); +} + +static BOOL WINAPI load_compat_settings(INIT_ONCE *once, void *param, void **context) +{ + WCHAR key_name[INTERNET_MAX_HOST_NAME_LENGTH]; + DWORD index = 0, name_size; + compat_config_t *new_entry; + compat_mode_t max_compat_mode; + HKEY key, host_key; + DWORD res; + + static const WCHAR key_nameW[] = { + 'S','o','f','t','w','a','r','e', + '\\','W','i','n','e', + '\\','M','S','H','T','M','L', + '\\','C','o','m','p','a','t','M','o','d','e',0}; + + /* @@ Wine registry key: HKCU\Software\Wine\MSHTML\CompatMode */ + res = RegOpenKeyW(HKEY_CURRENT_USER, key_nameW, &key); + if(res != ERROR_SUCCESS) + return TRUE; + + if(read_compat_mode(key, &max_compat_mode)) { + TRACE("Setting global max compat mode to %u\n", max_compat_mode); + global_max_compat_mode = max_compat_mode; + } + + while(1) { + res = RegEnumKeyW(key, index, key_name, ARRAY_SIZE(key_name)); + if(res == ERROR_NO_MORE_ITEMS) + break; + index++; + if(res != ERROR_SUCCESS) { + WARN("RegEnumKey failed: %u\n", GetLastError()); + continue; + } + + name_size = strlenW(key_name) + 1; + new_entry = heap_alloc(FIELD_OFFSET(compat_config_t, host[name_size])); + if(!new_entry) + continue; + + new_entry->max_compat_mode = COMPAT_MODE_IE11; + memcpy(new_entry->host, key_name, name_size * sizeof(WCHAR)); + list_add_tail(&compat_config, &new_entry->entry); + + res = RegOpenKeyW(key, key_name, &host_key); + if(res != ERROR_SUCCESS) + continue; + + if(read_compat_mode(host_key, &max_compat_mode)) { + TRACE("Setting max compat mode for %s to %u\n", debugstr_w(key_name), max_compat_mode); + new_entry->max_compat_mode = max_compat_mode; + } + + RegCloseKey(host_key); + } + + RegCloseKey(key); + return TRUE; +} + +compat_mode_t get_max_compat_mode(IUri *uri) +{ + compat_config_t *iter; + size_t len, iter_len; + BSTR host; + HRESULT hres; + + static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; + InitOnceExecuteOnce(&init_once, load_compat_settings, NULL, NULL); + + if(!uri) + return global_max_compat_mode; + hres = IUri_GetHost(uri, &host); + if(FAILED(hres)) + return global_max_compat_mode; + len = SysStringLen(host); + + LIST_FOR_EACH_ENTRY(iter, &compat_config, compat_config_t, entry) { + iter_len = strlenW(iter->host); + /* If configured host starts with '.', we also match subdomains */ + if((len == iter_len || (iter->host[0] == '.' && len > iter_len)) + && !memcmp(host + len - iter_len, iter->host, iter_len * sizeof(WCHAR))) { + TRACE("Found max mode %u\n", iter->max_compat_mode); + return iter->max_compat_mode; + } + } + + SysFreeString(host); + TRACE("Using global max mode %u\n", global_max_compat_mode); + return global_max_compat_mode; +} + static void thread_detach(void) { thread_data_t *thread_data; @@ -132,9 +249,17 @@ static void free_strings(void) static void process_detach(void) { + compat_config_t *config; + close_gecko(); release_typelib(); + while(!list_empty(&compat_config)) { + config = LIST_ENTRY(list_head(&compat_config), compat_config_t, entry); + list_remove(&config->entry); + heap_free(config); + } + if(shdoclc) FreeLibrary(shdoclc); if(mshtml_tls != TLS_OUT_OF_INDEXES) diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index ddc8df53655..89a56bd80e3 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1167,6 +1167,8 @@ void remove_target_tasks(LONG) DECLSPEC_HIDDEN; HRESULT set_task_timer(HTMLInnerWindow*,LONG,BOOL,IDispatch*,LONG*) DECLSPEC_HIDDEN; HRESULT clear_task_timer(HTMLInnerWindow*,DWORD) DECLSPEC_HIDDEN; +BOOL parse_compat_version(const WCHAR*,compat_mode_t*) DECLSPEC_HIDDEN; + const char *debugstr_mshtml_guid(const GUID*) DECLSPEC_HIDDEN; DEFINE_GUID(CLSID_AboutProtocol, 0x3050F406, 0x98B5, 0x11CF, 0xBB,0x82, 0x00,0xAA,0x00,0xBD,0xCE,0x0B); @@ -1337,6 +1339,7 @@ static inline VARIANT_BOOL variant_bool(BOOL b) extern void *call_thiscall_func; #endif +compat_mode_t get_max_compat_mode(IUri*) DECLSPEC_HIDDEN; UINT cp_from_charset_string(BSTR) DECLSPEC_HIDDEN; BSTR charset_string_from_cp(UINT) DECLSPEC_HIDDEN; HINSTANCE get_shdoclc(void) DECLSPEC_HIDDEN; diff --git a/dlls/mshtml/mutation.c b/dlls/mshtml/mutation.c index dd46dd8644a..1a1cfe358cd 100644 --- a/dlls/mshtml/mutation.c +++ b/dlls/mshtml/mutation.c @@ -377,6 +377,8 @@ compat_mode_t lock_document_mode(HTMLDocumentNode *doc) static void set_document_mode(HTMLDocumentNode *doc, compat_mode_t document_mode, BOOL lock) { + compat_mode_t max_compat_mode; + if(doc->document_mode_locked) { WARN("attempting to set document mode %d on locked document %p\n", document_mode, doc); return; @@ -384,35 +386,31 @@ static void set_document_mode(HTMLDocumentNode *doc, compat_mode_t document_mode TRACE("%p: %d\n", doc, document_mode); + max_compat_mode = doc->window && doc->window->base.outer_window + ? get_max_compat_mode(doc->window->base.outer_window->uri) + : COMPAT_MODE_IE11; + if(max_compat_mode < document_mode) { + WARN("Tried to set compat mode %u higher than maximal configured %u\n", + document_mode, max_compat_mode); + document_mode = max_compat_mode; + } + doc->document_mode = document_mode; if(lock) lock_document_mode(doc); } -static BOOL parse_ua_compatible(const WCHAR *p, compat_mode_t *r) +BOOL parse_compat_version(const WCHAR *version_string, compat_mode_t *r) { - int v = 0; + DWORD version = 0; + const WCHAR *p; - static const WCHAR ie_eqW[] = {'I','E','='}; - static const WCHAR edgeW[] = {'e','d','g','e',0}; - - TRACE("%s\n", debugstr_w(p)); - - if(strncmpiW(ie_eqW, p, ARRAY_SIZE(ie_eqW))) - return FALSE; - p += 3; - - if(!strcmpiW(p, edgeW)) { - *r = COMPAT_MODE_IE11; - return TRUE; - } - - while('0' <= *p && *p <= '9') - v = v*10 + *(p++)-'0'; - if(*p || !v) + for(p = version_string; '0' <= *p && *p <= '9'; p++) + version = version * 10 + *p-'0'; + if(*p || p == version_string) return FALSE; - switch(v){ + switch(version){ case 5: case 6: *r = COMPAT_MODE_IE5; @@ -430,10 +428,28 @@ static BOOL parse_ua_compatible(const WCHAR *p, compat_mode_t *r) *r = COMPAT_MODE_IE10; break; default: - *r = v < 5 ? COMPAT_MODE_QUIRKS : COMPAT_MODE_IE11; + *r = version < 5 ? COMPAT_MODE_QUIRKS : COMPAT_MODE_IE11; + } + return TRUE; +} + +static BOOL parse_ua_compatible(const WCHAR *p, compat_mode_t *r) +{ + static const WCHAR ie_eqW[] = {'I','E','='}; + static const WCHAR edgeW[] = {'e','d','g','e',0}; + + TRACE("%s\n", debugstr_w(p)); + + if(strncmpiW(ie_eqW, p, ARRAY_SIZE(ie_eqW))) + return FALSE; + p += 3; + + if(!strcmpiW(p, edgeW)) { + *r = COMPAT_MODE_IE11; + return TRUE; } - return TRUE; + return parse_compat_version(p, r); } void process_document_response_headers(HTMLDocumentNode *doc, IBinding *binding)