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;