From c25dfb79651acc0f2f72a5d5fc3d23da86421aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= Date: Mon, 31 Oct 2022 18:27:37 +0200 Subject: [PATCH] mshtml: Implement document.mimeType. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gabriel Ivăncescu --- dlls/mshtml/htmldoc.c | 23 ++++++- dlls/mshtml/main.c | 103 ++++++++++++++++++++++++++++++ dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/tests/Makefile.in | 2 +- dlls/mshtml/tests/documentmode.js | 1 + dlls/mshtml/tests/htmldoc.c | 100 +++++++++++++++++++++++++++++ dlls/mshtml/tests/script.c | 98 ++++++++++++++++++++++++++++ 7 files changed, 325 insertions(+), 3 deletions(-) diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c index e3ef57ecfee..e7722f9c602 100644 --- a/dlls/mshtml/htmldoc.c +++ b/dlls/mshtml/htmldoc.c @@ -1245,8 +1245,27 @@ static HRESULT WINAPI HTMLDocument_get_defaultCharset(IHTMLDocument2 *iface, BST static HRESULT WINAPI HTMLDocument_get_mimeType(IHTMLDocument2 *iface, BSTR *p) { HTMLDocumentNode *This = impl_from_IHTMLDocument2(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + const PRUnichar *content_type; + nsAString nsstr; + nsresult nsres; + HRESULT hres; + + TRACE("(%p)->(%p)\n", This, p); + + *p = NULL; + + if(This->window && This->window->base.outer_window->readystate == READYSTATE_UNINITIALIZED) + return (*p = SysAllocString(L"")) ? S_OK : E_FAIL; + + nsAString_InitDepend(&nsstr, NULL); + nsres = nsIDOMHTMLDocument_GetContentType(This->nsdoc, &nsstr); + if(NS_FAILED(nsres)) + return map_nsresult(nsres); + + nsAString_GetData(&nsstr, &content_type); + hres = get_mime_type_display_name(content_type, p); + nsAString_Finish(&nsstr); + return hres; } static HRESULT WINAPI HTMLDocument_get_fileSize(IHTMLDocument2 *iface, BSTR *p) diff --git a/dlls/mshtml/main.c b/dlls/mshtml/main.c index ae14137bc54..af3e9fc30e9 100644 --- a/dlls/mshtml/main.c +++ b/dlls/mshtml/main.c @@ -126,6 +126,109 @@ BSTR charset_string_from_cp(UINT cp) return SysAllocString(info.wszWebCharset); } +HRESULT get_mime_type_display_name(const WCHAR *content_type, BSTR *ret) +{ + /* undocumented */ + extern BOOL WINAPI GetMIMETypeSubKeyW(LPCWSTR,LPWSTR,DWORD); + + WCHAR buffer[128], ext[128], *str, *progid; + DWORD type, len; + HKEY key = NULL; + LSTATUS status; + HRESULT hres; + CLSID clsid; + + str = buffer; + if(!GetMIMETypeSubKeyW(content_type, buffer, ARRAY_SIZE(buffer))) { + len = wcslen(content_type) + 32; + for(;;) { + if(!(str = heap_alloc(len * sizeof(WCHAR)))) + return E_OUTOFMEMORY; + if(GetMIMETypeSubKeyW(content_type, str, len)) + break; + heap_free(str); + len *= 2; + } + } + + status = RegOpenKeyExW(HKEY_CLASSES_ROOT, str, 0, KEY_QUERY_VALUE, &key); + if(str != buffer) + heap_free(str); + if(status != ERROR_SUCCESS) + goto fail; + + len = sizeof(ext); + status = RegQueryValueExW(key, L"Extension", NULL, &type, (BYTE*)ext, &len); + if(status != ERROR_SUCCESS || type != REG_SZ) { + len = sizeof(buffer); + status = RegQueryValueExW(key, L"CLSID", NULL, &type, (BYTE*)buffer, &len); + if(status != ERROR_SUCCESS || type != REG_SZ || CLSIDFromString(buffer, &clsid) != S_OK) + goto fail; + + hres = ProgIDFromCLSID(&clsid, &progid); + if(hres == E_OUTOFMEMORY) { + RegCloseKey(key); + return hres; + } + if(hres != S_OK) + goto fail; + }else { + progid = ext; + } + + len = ARRAY_SIZE(buffer); + str = buffer; + for(;;) { + hres = AssocQueryStringW(ASSOCF_NOTRUNCATE, ASSOCSTR_FRIENDLYDOCNAME, progid, NULL, str, &len); + if(hres == S_OK && len) + break; + if(str != buffer) + heap_free(str); + if(hres != E_POINTER) { + if(progid != ext) { + CoTaskMemFree(progid); + goto fail; + } + + /* Try from CLSID */ + len = sizeof(buffer); + status = RegQueryValueExW(key, L"CLSID", NULL, &type, (BYTE*)buffer, &len); + if(status != ERROR_SUCCESS || type != REG_SZ || CLSIDFromString(buffer, &clsid) != S_OK) + goto fail; + + hres = ProgIDFromCLSID(&clsid, &progid); + if(hres == E_OUTOFMEMORY) { + RegCloseKey(key); + return hres; + } + if(hres != S_OK) + goto fail; + + len = ARRAY_SIZE(buffer); + str = buffer; + continue; + } + str = heap_alloc(len * sizeof(WCHAR)); + } + if(progid != ext) + CoTaskMemFree(progid); + RegCloseKey(key); + + *ret = SysAllocString(str); + if(str != buffer) + heap_free(str); + return *ret ? S_OK : E_OUTOFMEMORY; + +fail: + RegCloseKey(key); + + WARN("Did not find MIME in database for %s\n", debugstr_w(content_type)); + + /* native seems to return "File" when it doesn't know the content type */ + *ret = SysAllocString(L"File"); + return *ret ? S_OK : E_OUTOFMEMORY; +} + IInternetSecurityManager *get_security_manager(void) { if(!security_manager) { diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 49393c977cc..c403b4efc54 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1455,6 +1455,7 @@ extern void *call_thiscall_func; 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; +HRESULT get_mime_type_display_name(const WCHAR*,BSTR*) DECLSPEC_HIDDEN; HINSTANCE get_shdoclc(void) DECLSPEC_HIDDEN; void set_statustext(HTMLDocumentObj*,INT,LPCWSTR) DECLSPEC_HIDDEN; IInternetSecurityManager *get_security_manager(void) DECLSPEC_HIDDEN; diff --git a/dlls/mshtml/tests/Makefile.in b/dlls/mshtml/tests/Makefile.in index fac78316986..ce258d13ada 100644 --- a/dlls/mshtml/tests/Makefile.in +++ b/dlls/mshtml/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = mshtml.dll -IMPORTS = ole32 oleaut32 wininet user32 urlmon gdi32 advapi32 +IMPORTS = ole32 oleaut32 shlwapi wininet user32 urlmon gdi32 advapi32 C_SRCS = \ activex.c \ dom.c \ diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index a9ff38d5781..a0f170c73b4 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -311,6 +311,7 @@ sync_test("doc_props", function() { } var v = document.documentMode; + ok(document.mimeType === external.getExpectedMimeType("text/html"), "mimeType = " + document.mimeType); test_exposed("onstorage", v < 9); test_exposed("textContent", v >= 9); diff --git a/dlls/mshtml/tests/htmldoc.c b/dlls/mshtml/tests/htmldoc.c index 7764f256ab0..49c0ddfe5c4 100644 --- a/dlls/mshtml/tests/htmldoc.c +++ b/dlls/mshtml/tests/htmldoc.c @@ -27,6 +27,7 @@ #include "winbase.h" #include "initguid.h" #include "ole2.h" +#include "shlwapi.h" #include "mshtml.h" #include "docobj.h" #include "docobjectservice.h" @@ -449,6 +450,84 @@ static void _test_current_url(unsigned line, IUnknown *unk, const WCHAR *exurl) IHTMLDocument2_Release(doc); } +static BSTR get_mime_type_display_name(const WCHAR *content_type) +{ + WCHAR buffer[128], ext[128], *str, *progid; + HKEY key, type_key = NULL; + DWORD type, len; + LSTATUS status; + HRESULT hres; + CLSID clsid; + BSTR ret; + + status = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"MIME\\Database\\Content Type", 0, KEY_READ, &key); + if(status != ERROR_SUCCESS) + goto fail; + + status = RegOpenKeyExW(key, content_type, 0, KEY_QUERY_VALUE, &type_key); + RegCloseKey(key); + if(status != ERROR_SUCCESS) + goto fail; + + len = sizeof(ext); + status = RegQueryValueExW(type_key, L"Extension", NULL, &type, (BYTE*)ext, &len); + if(status != ERROR_SUCCESS || type != REG_SZ) { + len = sizeof(buffer); + status = RegQueryValueExW(type_key, L"CLSID", NULL, &type, (BYTE*)buffer, &len); + + if(status != ERROR_SUCCESS || type != REG_SZ || CLSIDFromString(buffer, &clsid) != S_OK || + ProgIDFromCLSID(&clsid, &progid) != S_OK) + goto fail; + }else { + /* For some reason w1064v1809 testbot VM uses .htm here, despite .html being set in the database */ + if(!wcscmp(ext, L".html")) + wcscpy(ext, L".htm"); + progid = ext; + } + + len = ARRAY_SIZE(buffer); + str = buffer; + for(;;) { + hres = AssocQueryStringW(ASSOCF_NOTRUNCATE, ASSOCSTR_FRIENDLYDOCNAME, progid, NULL, str, &len); + if(hres == S_OK && len) + break; + if(str != buffer) + free(str); + if(hres != E_POINTER) { + if(progid != ext) { + CoTaskMemFree(progid); + goto fail; + } + + /* Try from CLSID */ + len = sizeof(buffer); + status = RegQueryValueExW(type_key, L"CLSID", NULL, &type, (BYTE*)buffer, &len); + + if(status != ERROR_SUCCESS || type != REG_SZ || CLSIDFromString(buffer, &clsid) != S_OK || + ProgIDFromCLSID(&clsid, &progid) != S_OK) + goto fail; + + len = ARRAY_SIZE(buffer); + str = buffer; + continue; + } + str = malloc(len * sizeof(WCHAR)); + } + if(progid != ext) + CoTaskMemFree(progid); + RegCloseKey(type_key); + + ret = SysAllocString(str); + if(str != buffer) + free(str); + return ret; + +fail: + RegCloseKey(type_key); + trace("Did not find MIME in database for %s\n", debugstr_w(content_type)); + return SysAllocString(L"File"); +} + DEFINE_GUID(IID_External_unk,0x30510406,0x98B5,0x11CF,0xBB,0x82,0x00,0xAA,0x00,0xBD,0xCE,0x0B); static HRESULT WINAPI External_QueryInterface(IDispatch *iface, REFIID riid, void **ppv) @@ -7680,6 +7759,25 @@ static void test_QueryInterface(IHTMLDocument2 *htmldoc) IUnknown_Release(qi); } +static void test_mimeType(IHTMLDocument2 *doc, const WCHAR *content_type) +{ + BSTR mime_type = (BSTR)0xdeadbeef; + HRESULT hres; + + hres = IHTMLDocument2_get_mimeType(doc, &mime_type); + if(content_type) { + BSTR display_name = get_mime_type_display_name(content_type); + ok(hres == S_OK, "get_mimeType returned %08lx\n", hres); + ok(!wcscmp(mime_type, display_name), "mime type = %s, expected %s\n", + debugstr_w(mime_type), debugstr_w(display_name)); + SysFreeString(display_name); + }else { + ok(hres == S_OK || broken(hres == E_FAIL), "get_mimeType returned %08lx\n", hres); + ok(!mime_type || !mime_type[0], "mime type = %s\n", debugstr_w(mime_type)); + } + SysFreeString(mime_type); +} + static void init_test(enum load_state_t ls) { doc_unk = NULL; doc_hwnd = last_hwnd = NULL; @@ -7733,6 +7831,7 @@ static void test_HTMLDocument(BOOL do_load, BOOL mime) test_GetCurMoniker((IUnknown*)doc, &Moniker, NULL, FALSE); test_elem_from_point(doc); } + test_mimeType(doc, do_load ? L"text/html" : NULL); test_MSHTML_QueryStatus(doc, OLECMDF_SUPPORTED); test_OleCommandTarget_fail(doc); @@ -7844,6 +7943,7 @@ static void test_MHTMLDocument(void) set_custom_uihandler(doc, &CustomDocHostUIHandler); test_GetCurMoniker((IUnknown*)doc, NULL, L"mhtml:winetest:doc", FALSE); test_download(0); + test_mimeType(doc, L"text/html"); test_exec_onunload(doc); test_UIDeactivate(); diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index 6762cd985d0..e71f178e51a 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -26,6 +26,7 @@ #include "windef.h" #include "winbase.h" #include "ole2.h" +#include "shlwapi.h" #include "wininet.h" #include "docobj.h" #include "dispex.h" @@ -157,6 +158,7 @@ DEFINE_EXPECT(GetTypeInfo); #define DISPID_EXTERNAL_IS_ENGLISH 0x300009 #define DISPID_EXTERNAL_LIST_SEP 0x30000A #define DISPID_EXTERNAL_TEST_VARS 0x30000B +#define DISPID_EXTERNAL_GETMIMETYPE 0x30000C static const GUID CLSID_TestScript = {0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x07,0x46}}; @@ -200,6 +202,84 @@ static BOOL init_key(const char *key_name, const char *def_value, BOOL init) return res == ERROR_SUCCESS; } +static BSTR get_mime_type_display_name(const WCHAR *content_type) +{ + WCHAR buffer[128], ext[128], *str, *progid; + HKEY key, type_key = NULL; + DWORD type, len; + LSTATUS status; + HRESULT hres; + CLSID clsid; + BSTR ret; + + status = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"MIME\\Database\\Content Type", 0, KEY_READ, &key); + if(status != ERROR_SUCCESS) + goto fail; + + status = RegOpenKeyExW(key, content_type, 0, KEY_QUERY_VALUE, &type_key); + RegCloseKey(key); + if(status != ERROR_SUCCESS) + goto fail; + + len = sizeof(ext); + status = RegQueryValueExW(type_key, L"Extension", NULL, &type, (BYTE*)ext, &len); + if(status != ERROR_SUCCESS || type != REG_SZ) { + len = sizeof(buffer); + status = RegQueryValueExW(type_key, L"CLSID", NULL, &type, (BYTE*)buffer, &len); + + if(status != ERROR_SUCCESS || type != REG_SZ || CLSIDFromString(buffer, &clsid) != S_OK || + ProgIDFromCLSID(&clsid, &progid) != S_OK) + goto fail; + }else { + /* For some reason w1064v1809 testbot VM uses .htm here, despite .html being set in the database */ + if(!wcscmp(ext, L".html")) + wcscpy(ext, L".htm"); + progid = ext; + } + + len = ARRAY_SIZE(buffer); + str = buffer; + for(;;) { + hres = AssocQueryStringW(ASSOCF_NOTRUNCATE, ASSOCSTR_FRIENDLYDOCNAME, progid, NULL, str, &len); + if(hres == S_OK && len) + break; + if(str != buffer) + free(str); + if(hres != E_POINTER) { + if(progid != ext) { + CoTaskMemFree(progid); + goto fail; + } + + /* Try from CLSID */ + len = sizeof(buffer); + status = RegQueryValueExW(type_key, L"CLSID", NULL, &type, (BYTE*)buffer, &len); + + if(status != ERROR_SUCCESS || type != REG_SZ || CLSIDFromString(buffer, &clsid) != S_OK || + ProgIDFromCLSID(&clsid, &progid) != S_OK) + goto fail; + + len = ARRAY_SIZE(buffer); + str = buffer; + continue; + } + str = malloc(len * sizeof(WCHAR)); + } + if(progid != ext) + CoTaskMemFree(progid); + RegCloseKey(type_key); + + ret = SysAllocString(str); + if(str != buffer) + free(str); + return ret; + +fail: + RegCloseKey(type_key); + trace("Did not find MIME in database for %s\n", debugstr_w(content_type)); + return SysAllocString(L"File"); +} + static void test_script_vars(unsigned argc, VARIANTARG *argv) { static const WCHAR *const jsobj_names[] = { L"abc", L"foO", L"bar", L"TostRing", L"hasownpropERty" }; @@ -715,6 +795,10 @@ static HRESULT WINAPI externalDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, *pid = DISPID_EXTERNAL_TEST_VARS; return S_OK; } + if(!lstrcmpW(bstrName, L"getExpectedMimeType")) { + *pid = DISPID_EXTERNAL_GETMIMETYPE; + return S_OK; + } ok(0, "unexpected name %s\n", wine_dbgstr_w(bstrName)); return DISP_E_UNKNOWNNAME; @@ -948,6 +1032,20 @@ static HRESULT WINAPI externalDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID test_script_vars(pdp->cArgs, pdp->rgvarg); return S_OK; + case DISPID_EXTERNAL_GETMIMETYPE: + ok(pdp != NULL, "pdp == NULL\n"); + ok(pdp->rgvarg != NULL, "rgvarg == NULL\n"); + ok(V_VT(pdp->rgvarg) == VT_BSTR, "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(pvarRes != NULL, "pvarRes == NULL\n"); + ok(V_VT(pvarRes) == VT_EMPTY, "V_VT(pvarRes) = %d\n", V_VT(pvarRes)); + ok(pei != NULL, "pei == NULL\n"); + V_BSTR(pvarRes) = get_mime_type_display_name(V_BSTR(pdp->rgvarg)); + V_VT(pvarRes) = V_BSTR(pvarRes) ? VT_BSTR : VT_NULL; + return S_OK; + default: ok(0, "unexpected call\n"); return E_NOTIMPL;