|  | /* | 
|  | * Copyright 2007 Jacek Caban for CodeWeavers | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | #define COBJMACROS | 
|  | #define CONST_VTABLE | 
|  |  | 
|  | #include <wine/test.h> | 
|  | #include <stdarg.h> | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "ole2.h" | 
|  | #include "mshtml.h" | 
|  | #include "docobj.h" | 
|  |  | 
|  | static const char doc_str1[] = "<html><body>test</body></html>"; | 
|  |  | 
|  | static const char *dbgstr_w(LPCWSTR str) | 
|  | { | 
|  | static char buf[512]; | 
|  | WideCharToMultiByte(CP_ACP, 0, str, -1, buf, sizeof(buf), NULL, NULL); | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | static int strcmp_wa(LPCWSTR strw, const char *stra) | 
|  | { | 
|  | WCHAR buf[512]; | 
|  | MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, sizeof(buf)/sizeof(WCHAR)); | 
|  | return lstrcmpW(strw, buf); | 
|  | } | 
|  |  | 
|  | static IHTMLDocument2 *create_document(void) | 
|  | { | 
|  | IHTMLDocument2 *doc; | 
|  | HRESULT hres; | 
|  |  | 
|  | hres = CoCreateInstance(&CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER, | 
|  | &IID_IHTMLDocument2, (void**)&doc); | 
|  | ok(hres == S_OK, "CoCreateInstance failed: %08x\n", hres); | 
|  |  | 
|  | return doc; | 
|  | } | 
|  |  | 
|  | static void test_node_name(IUnknown *unk, const char *exname) | 
|  | { | 
|  | IHTMLDOMNode *node; | 
|  | BSTR name; | 
|  | HRESULT hres; | 
|  |  | 
|  | hres = IUnknown_QueryInterface(unk, &IID_IHTMLDOMNode, (void**)&node); | 
|  | ok(hres == S_OK, "QueryInterface(IID_IHTMLNode) failed: %08x\n", hres); | 
|  |  | 
|  | hres = IHTMLDOMNode_get_nodeName(node, &name); | 
|  | IHTMLDOMNode_Release(node); | 
|  | ok(hres == S_OK, "get_nodeName failed: %08x\n", hres); | 
|  | ok(!strcmp_wa(name, exname), "got name: %s, expected HTML\n", dbgstr_w(name)); | 
|  |  | 
|  | SysFreeString(name); | 
|  | } | 
|  |  | 
|  | static void test_doc_elem(IHTMLDocument2 *doc) | 
|  | { | 
|  | IHTMLElement *elem; | 
|  | IHTMLDocument3 *doc3; | 
|  | HRESULT hres; | 
|  |  | 
|  | hres = IHTMLDocument2_QueryInterface(doc, &IID_IHTMLDocument3, (void**)&doc3); | 
|  | ok(hres == S_OK, "QueryInterface(IID_IHTMLDocument3) failed: %08x\n", hres); | 
|  |  | 
|  | hres = IHTMLDocument3_get_documentElement(doc3, &elem); | 
|  | IHTMLDocument3_Release(doc3); | 
|  | ok(hres == S_OK, "get_documentElement failed: %08x\n", hres); | 
|  |  | 
|  | test_node_name((IUnknown*)elem, "HTML"); | 
|  |  | 
|  | IHTMLElement_Release(elem); | 
|  | } | 
|  |  | 
|  | static IHTMLDocument2 *notif_doc; | 
|  | static BOOL doc_complete; | 
|  |  | 
|  | static HRESULT WINAPI PropertyNotifySink_QueryInterface(IPropertyNotifySink *iface, | 
|  | REFIID riid, void**ppv) | 
|  | { | 
|  | if(IsEqualGUID(&IID_IPropertyNotifySink, riid)) { | 
|  | *ppv = iface; | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | ok(0, "unexpected call\n"); | 
|  | return E_NOINTERFACE; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI PropertyNotifySink_AddRef(IPropertyNotifySink *iface) | 
|  | { | 
|  | return 2; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI PropertyNotifySink_Release(IPropertyNotifySink *iface) | 
|  | { | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PropertyNotifySink_OnChanged(IPropertyNotifySink *iface, DISPID dispID) | 
|  | { | 
|  | if(dispID == DISPID_READYSTATE){ | 
|  | BSTR state; | 
|  | HRESULT hres; | 
|  |  | 
|  | static const WCHAR completeW[] = {'c','o','m','p','l','e','t','e',0}; | 
|  |  | 
|  | hres = IHTMLDocument2_get_readyState(notif_doc, &state); | 
|  | ok(hres == S_OK, "get_readyState failed: %08x\n", hres); | 
|  |  | 
|  | if(!lstrcmpW(state, completeW)) | 
|  | doc_complete = TRUE; | 
|  |  | 
|  | SysFreeString(state); | 
|  | } | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PropertyNotifySink_OnRequestEdit(IPropertyNotifySink *iface, DISPID dispID) | 
|  | { | 
|  | ok(0, "unexpected call\n"); | 
|  | return E_NOTIMPL; | 
|  | } | 
|  |  | 
|  | static IPropertyNotifySinkVtbl PropertyNotifySinkVtbl = { | 
|  | PropertyNotifySink_QueryInterface, | 
|  | PropertyNotifySink_AddRef, | 
|  | PropertyNotifySink_Release, | 
|  | PropertyNotifySink_OnChanged, | 
|  | PropertyNotifySink_OnRequestEdit | 
|  | }; | 
|  |  | 
|  | static IPropertyNotifySink PropertyNotifySink = { &PropertyNotifySinkVtbl }; | 
|  |  | 
|  | static IHTMLDocument2 *create_doc_with_string(const char *str) | 
|  | { | 
|  | IPersistStreamInit *init; | 
|  | IStream *stream; | 
|  | IHTMLDocument2 *doc; | 
|  | HGLOBAL mem; | 
|  | SIZE_T len; | 
|  |  | 
|  | notif_doc = doc = create_document(); | 
|  | if(!doc) | 
|  | return NULL; | 
|  |  | 
|  | doc_complete = FALSE; | 
|  | len = strlen(str); | 
|  | mem = GlobalAlloc(0, len); | 
|  | memcpy(mem, str, len); | 
|  | CreateStreamOnHGlobal(mem, TRUE, &stream); | 
|  |  | 
|  | IHTMLDocument2_QueryInterface(doc, &IID_IPersistStreamInit, (void**)&init); | 
|  |  | 
|  | IPersistStreamInit_Load(init, stream); | 
|  | IPersistStreamInit_Release(init); | 
|  | IStream_Release(stream); | 
|  |  | 
|  | return doc; | 
|  | } | 
|  |  | 
|  | static void do_advise(IUnknown *unk, REFIID riid, IUnknown *unk_advise) | 
|  | { | 
|  | IConnectionPointContainer *container; | 
|  | IConnectionPoint *cp; | 
|  | DWORD cookie; | 
|  | HRESULT hres; | 
|  |  | 
|  | hres = IUnknown_QueryInterface(unk, &IID_IConnectionPointContainer, (void**)&container); | 
|  | ok(hres == S_OK, "QueryInterface(IID_IConnectionPointContainer) failed: %08x\n", hres); | 
|  |  | 
|  | hres = IConnectionPointContainer_FindConnectionPoint(container, riid, &cp); | 
|  | IConnectionPointContainer_Release(container); | 
|  | ok(hres == S_OK, "FindConnectionPoint failed: %08x\n", hres); | 
|  |  | 
|  | hres = IConnectionPoint_Advise(cp, unk_advise, &cookie); | 
|  | IConnectionPoint_Release(cp); | 
|  | ok(hres == S_OK, "Advise failed: %08x\n", hres); | 
|  | } | 
|  |  | 
|  | typedef void (*domtest_t)(IHTMLDocument2*); | 
|  |  | 
|  | static void run_domtest(const char *str, domtest_t test) | 
|  | { | 
|  | IHTMLDocument2 *doc; | 
|  | ULONG ref; | 
|  | MSG msg; | 
|  |  | 
|  | doc = create_doc_with_string(str); | 
|  | do_advise((IUnknown*)doc, &IID_IPropertyNotifySink, (IUnknown*)&PropertyNotifySink); | 
|  |  | 
|  | while(!doc_complete && GetMessage(&msg, NULL, 0, 0)) { | 
|  | TranslateMessage(&msg); | 
|  | DispatchMessage(&msg); | 
|  | } | 
|  |  | 
|  | test(doc); | 
|  |  | 
|  | ref = IHTMLDocument2_Release(doc); | 
|  | ok(!ref, "ref = %d\n", ref); | 
|  | } | 
|  |  | 
|  | static void gecko_installer_workaround(BOOL disable) | 
|  | { | 
|  | HKEY hkey; | 
|  | DWORD res; | 
|  |  | 
|  | static BOOL has_url = FALSE; | 
|  | static char url[2048]; | 
|  |  | 
|  | if(!disable && !has_url) | 
|  | return; | 
|  |  | 
|  | res = RegOpenKey(HKEY_CURRENT_USER, "Software\\Wine\\MSHTML", &hkey); | 
|  | if(res != ERROR_SUCCESS) | 
|  | return; | 
|  |  | 
|  | if(disable) { | 
|  | DWORD type, size = sizeof(url); | 
|  |  | 
|  | res = RegQueryValueEx(hkey, "GeckoUrl", NULL, &type, (PVOID)url, &size); | 
|  | if(res == ERROR_SUCCESS && type == REG_SZ) | 
|  | has_url = TRUE; | 
|  |  | 
|  | RegDeleteValue(hkey, "GeckoUrl"); | 
|  | }else { | 
|  | RegSetValueEx(hkey, "GeckoUrl", 0, REG_SZ, (PVOID)url, lstrlenA(url)+1); | 
|  | } | 
|  |  | 
|  | RegCloseKey(hkey); | 
|  | } | 
|  |  | 
|  | START_TEST(dom) | 
|  | { | 
|  | gecko_installer_workaround(TRUE); | 
|  | CoInitialize(NULL); | 
|  |  | 
|  | run_domtest(doc_str1, test_doc_elem); | 
|  |  | 
|  | CoUninitialize(); | 
|  | gecko_installer_workaround(FALSE); | 
|  | } |