For the first commit, note that as of now, there's a lot of tests that will fail on native if mshtml_test.exe is renamed to iexplore.exe, even with *all* the FeatureControl values for `iexplore.exe` removed, which leads me to believe there's a lot of other places where native mshtml hardcodes behavior based on the app name being `iexplore.exe`.
For the purposes of testing this, please skip all other tests and execute just the test_strict_mode() test, for example (which *should* fail on native if the executable is renamed to `iexplore.exe` instead).
I've actually literally searched the *entire* registry on a temporary VM for `iexplore` and removed all values with it, and it still showed such different behavior after the fact, so it must be hardcoded into mshtml.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
For documents exposing a <!DOCTYPE> node but no specific X-UA-Compatible, mshtml defaults to IE7 compat mode, unless the app is Internet Explorer and is in Internet URL Zone. This checking for the `iexplore.exe` app name seems hardcoded into mshtml; the FeatureControl registry keys do not affect this directly, and none are set by default for iexplore.exe that would affect this, anyway.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/mutation.c | 23 ++++++++++++++++++++++- dlls/mshtml/tests/dom.c | 3 +++ dlls/mshtml/tests/script.c | 23 +++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/dlls/mshtml/mutation.c b/dlls/mshtml/mutation.c index 3355139ad31..79918278b3d 100644 --- a/dlls/mshtml/mutation.c +++ b/dlls/mshtml/mutation.c @@ -27,6 +27,7 @@ #include "ole2.h" #include "shlguid.h" #include "wininet.h" +#include "winternl.h"
#include "mshtml_private.h" #include "htmlscript.h" @@ -52,6 +53,20 @@ static const IID NS_ICONTENTUTILS_CID =
static nsIContentUtils *content_utils;
+static BOOL is_iexplore(void) +{ + static volatile char cache = -1; + BOOL ret = cache; + if(ret == -1) { + const WCHAR *p, *name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; + if((p = wcsrchr(name, '/'))) name = p + 1; + if((p = wcsrchr(name, '\'))) name = p + 1; + ret = !wcsicmp(name, L"iexplore.exe"); + cache = ret; + } + return ret; +} + static PRUnichar *handle_insert_comment(HTMLDocumentNode *doc, const PRUnichar *comment) { unsigned majorv = 0, minorv = 0, compat_version; @@ -823,7 +838,13 @@ static void NSAPI nsDocumentObserver_BindToDocument(nsIDocumentObserver *iface,
TRACE("doctype node\n");
- if(This->window && This->window->base.outer_window) { + /* Native mshtml hardcodes special behavior for iexplore.exe here. The feature control registry + keys under HKLM or HKCU\Software\Microsoft\Internet Explorer\Main\FeatureControl are not used + in this case (neither in Wow6432Node), although FEATURE_BROWSER_EMULATION does override this, + but it is not set by default on native, and the behavior is still different. This was tested + by removing all iexplore.exe values from any FeatureControl subkeys, and renaming the test + executable to iexplore.exe, which changed its default compat mode in such cases. */ + if(This->window && This->window->base.outer_window && is_iexplore()) { HTMLOuterWindow *window = This->window->base.outer_window; DWORD zone; HRESULT hres; diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index 8f78c8da17f..cd4daa84e0a 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -11449,6 +11449,9 @@ static void test_quirks_mode(void) expected_document_mode = 5; run_domtest("<html><body></body></html>", test_document_mode);
+ expected_document_mode = 7; + run_domtest("<!DOCTYPE html>\n<html></html>", test_document_mode); + if(!is_ie9plus) return;
diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index a92d18fb454..660f62759c9 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -3734,6 +3734,28 @@ static void run_script_as_http_with_mode(const char *script, const char *opt, co run_from_path(L"/index.html", opt ? opt : script); }
+static void test_strict_mode(void) +{ + sprintf(index_html_data, + "<!DOCTYPE html>\n" + "<html>\n" + " <head>\n" + " <script src="winetest.js" type="text/javascript"></script>\n" + " <script type="text/javascript">\n" + " function test() {\n" + " ok(document.documentMode === 7, 'document mode = ' + document.documentMode);\n" + " next_test();\n" + " }\n" + " var tests = [ test ];\n" + " </script>\n" + " </head>\n" + " <body onload="run_tests();">\n" + " </body>\n" + "</html>\n"); + + run_from_path(L"/index.html", "test_strict_mode"); +} + static void init_protocol_handler(void) { IInternetSession *internet_session; @@ -3767,6 +3789,7 @@ static void run_js_tests(void)
init_protocol_handler();
+ test_strict_mode(); run_script_as_http_with_mode("xhr.js", NULL, "9"); run_script_as_http_with_mode("xhr.js", NULL, "10"); run_script_as_http_with_mode("xhr.js", NULL, "11");
From: Gabriel Ivăncescu gabrielopcode@gmail.com
There can be multiple compat modes defined, separated by commas, but also surrounded by whitespace. The highest mode in the list is picked as the document mode, with 'edge' being the highest mode available.
It stops as soon as an invalid mode is found in the list and returns whatever highest mode was found until then.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/main.c | 2 +- dlls/mshtml/mshtml_private.h | 3 ++- dlls/mshtml/mutation.c | 37 ++++++++++++++++++++++++------------ dlls/mshtml/tests/dom.c | 37 +++++++++++++++++++++++++++--------- 4 files changed, 56 insertions(+), 23 deletions(-)
diff --git a/dlls/mshtml/main.c b/dlls/mshtml/main.c index bbaed1f1967..1a64bdb9b7d 100644 --- a/dlls/mshtml/main.c +++ b/dlls/mshtml/main.c @@ -154,7 +154,7 @@ static BOOL read_compat_mode(HKEY key, compat_mode_t *r) if(status != ERROR_SUCCESS || type != REG_SZ) return FALSE;
- return parse_compat_version(version, r); + return parse_compat_version(version, r) != NULL; }
static BOOL WINAPI load_compat_settings(INIT_ONCE *once, void *param, void **context) diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index d8b4ddef249..2e30210db2e 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -297,6 +297,7 @@ PRIVATE_TID_LIST } tid_t;
typedef enum { + COMPAT_MODE_INVALID = -1, COMPAT_MODE_QUIRKS, COMPAT_MODE_IE5, COMPAT_MODE_IE7, @@ -1253,7 +1254,7 @@ HRESULT set_task_timer(HTMLInnerWindow*,LONG,enum timer_type,IDispatch*,LONG*) D HRESULT clear_task_timer(HTMLInnerWindow*,DWORD) DECLSPEC_HIDDEN; HRESULT clear_animation_timer(HTMLInnerWindow*,DWORD) DECLSPEC_HIDDEN;
-BOOL parse_compat_version(const WCHAR*,compat_mode_t*) DECLSPEC_HIDDEN; +const WCHAR *parse_compat_version(const WCHAR*,compat_mode_t*) DECLSPEC_HIDDEN;
const char *debugstr_mshtml_guid(const GUID*) DECLSPEC_HIDDEN;
diff --git a/dlls/mshtml/mutation.c b/dlls/mshtml/mutation.c index 79918278b3d..02b511e309e 100644 --- a/dlls/mshtml/mutation.c +++ b/dlls/mshtml/mutation.c @@ -412,15 +412,20 @@ static void set_document_mode(HTMLDocumentNode *doc, compat_mode_t document_mode lock_document_mode(doc); }
-BOOL parse_compat_version(const WCHAR *version_string, compat_mode_t *r) +static BOOL is_ua_compatible_delimiter(WCHAR c) +{ + return !c || c == ';' || c == ',' || iswspace(c); +} + +const WCHAR *parse_compat_version(const WCHAR *version_string, compat_mode_t *r) { DWORD version = 0; const WCHAR *p;
for(p = version_string; '0' <= *p && *p <= '9'; p++) version = version * 10 + *p-'0'; - if((*p && *p != ';') || p == version_string) - return FALSE; + if(!is_ua_compatible_delimiter(*p) || p == version_string) + return NULL;
switch(version){ case 5: @@ -442,13 +447,14 @@ BOOL parse_compat_version(const WCHAR *version_string, compat_mode_t *r) default: *r = version < 5 ? COMPAT_MODE_QUIRKS : COMPAT_MODE_IE11; } - return TRUE; + return p; }
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'}; + compat_mode_t mode = COMPAT_MODE_INVALID;
TRACE("%s\n", debugstr_w(p));
@@ -456,15 +462,22 @@ static BOOL parse_ua_compatible(const WCHAR *p, compat_mode_t *r) return FALSE; p += 3;
- if(!wcsnicmp(p, edgeW, ARRAY_SIZE(edgeW))) { - p += ARRAY_SIZE(edgeW); - if(*p && *p != ';') - return FALSE; - *r = COMPAT_MODE_IE11; - return TRUE; - } + do { + while(iswspace(*p)) p++; + if(!wcsnicmp(p, edgeW, ARRAY_SIZE(edgeW))) { + p += ARRAY_SIZE(edgeW); + if(is_ua_compatible_delimiter(*p)) + mode = COMPAT_MODE_IE11; + break; + }else if(!(p = parse_compat_version(p, r))) + break; + if(mode < *r) + mode = *r; + while(iswspace(*p)) p++; + } while(*p++ == ',');
- return parse_compat_version(p, r); + *r = mode; + return mode != COMPAT_MODE_INVALID; }
void process_document_response_headers(HTMLDocumentNode *doc, IBinding *binding) diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c index cd4daa84e0a..d25c7ac0df8 100644 --- a/dlls/mshtml/tests/dom.c +++ b/dlls/mshtml/tests/dom.c @@ -11441,6 +11441,26 @@ static void test_document_mode(IHTMLDocument2 *doc2)
static void test_quirks_mode(void) { + static const struct { + const char *str; + unsigned expected_mode; + } tests[] = { + { "9", 9 }, + { " \t9 ", 9 }, + { " 5 , 8 , 7", 8 }, + { " 8 , 7 , 5", 8 }, + { " 5 , 5 , 7", 7 }, + { " 5 , 9 , 7", 9 }, + { " 5, 7,9", 9 }, + { " 5, 7;9", 7 }, + { " 5, edge,8", 11 }, + { " 5, foo,8", 5 }, + { " 5, 8,foo", 8 }, + { " 5, ,,7", 5 }, + { " 5, , ,7", 5 }, + }; + unsigned i; + run_domtest("<html></html>", check_quirks_mode); run_domtest("<!DOCTYPE html>\n<html></html>", check_strict_mode); run_domtest("<!-- comment --><!DOCTYPE html>\n<html></html>", check_quirks_mode); @@ -11455,15 +11475,14 @@ static void test_quirks_mode(void) if(!is_ie9plus) return;
- expected_document_mode = 9; - run_domtest("<!DOCTYPE html>\n" - "<html>" - " <head>" - " <meta http-equiv="x-ua-compatible" content="IE=9" />" - " </head>" - " <body>" - " </body>" - "</html>", test_document_mode); + for(i = 0; i < ARRAY_SIZE(tests); i++) { + char buf[128]; + expected_document_mode = tests[i].expected_mode; + sprintf(buf, "<!DOCTYPE html>\n<html><head>" + " <meta http-equiv="x-ua-compatible" content="IE=%s" />" + "</head><body></body></html>", tests[i].str); + run_domtest(buf, test_document_mode); + }
expected_document_mode = 8; run_domtest("<!DOCTYPE html>\n"
This merge request was approved by Jacek Caban.