--- /dev/null
+/*
+ * Copyright 2008 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 "activscp.h"
+
+#define DEFINE_EXPECT(func) \
+ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
+
+#define SET_EXPECT(func) \
+ do { called_ ## func = FALSE; expect_ ## func = TRUE; } while(0)
+
+#define CHECK_EXPECT2(func) \
+ do { \
+ ok(expect_ ##func, "unexpected call " #func "\n"); \
+ called_ ## func = TRUE; \
+ }while(0)
+
+#define CHECK_EXPECT(func) \
+ do { \
+ CHECK_EXPECT2(func); \
+ expect_ ## func = FALSE; \
+ }while(0)
+
+#define CHECK_CALLED(func) \
+ do { \
+ ok(called_ ## func, "expected " #func "\n"); \
+ expect_ ## func = called_ ## func = FALSE; \
+ }while(0)
+
+#define CHECK_NOT_CALLED(func) \
+ do { \
+ ok(!called_ ## func, "unexpected " #func "\n"); \
+ expect_ ## func = called_ ## func = FALSE; \
+ }while(0)
+
+#define CLEAR_CALLED(func) \
+ expect_ ## func = called_ ## func = FALSE
+
+
+DEFINE_EXPECT(CreateInstance);
+
+
+#define TESTSCRIPT_CLSID "{178fc163-f585-4e24-9c13-4bb7faf80746}"
+
+static const GUID CLSID_TestScript =
+ {0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x07,0x46}};
+
+static IHTMLDocument2 *notif_doc;
+static BOOL doc_complete;
+
+static const char *debugstr_guid(REFIID riid)
+{
+ static char buf[50];
+
+ sprintf(buf, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
+ riid->Data1, riid->Data2, riid->Data3, riid->Data4[0],
+ riid->Data4[1], riid->Data4[2], riid->Data4[3], riid->Data4[4],
+ riid->Data4[5], riid->Data4[6], riid->Data4[7]);
+
+ return buf;
+}
+
+static HRESULT WINAPI PropertyNotifySink_QueryInterface(IPropertyNotifySink *iface,
+ REFIID riid, void**ppv)
+{
+ if(IsEqualGUID(&IID_IPropertyNotifySink, riid)) {
+ *ppv = iface;
+ return S_OK;
+ }
+
+ 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_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 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 IHTMLDocument2 *create_and_load_doc(const char *str)
+{
+ IHTMLDocument2 *doc;
+ IHTMLElement *body = NULL;
+ ULONG ref;
+ MSG msg;
+ HRESULT hres;
+
+ 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);
+ }
+
+ hres = IHTMLDocument2_get_body(doc, &body);
+ ok(hres == S_OK, "get_body failed: %08x\n", hres);
+
+ if(!body) {
+ skip("Could not get document body. Assuming no Gecko installed.\n");
+ ref = IHTMLDocument2_Release(doc);
+ ok(!ref, "ref = %d\n", ref);
+ return NULL;
+ }
+
+ IHTMLElement_Release(body);
+ return doc;
+}
+
+static HRESULT WINAPI ActiveScript_QueryInterface(IActiveScript *iface, REFIID riid, void **ppv)
+{
+ *ppv = NULL;
+
+ if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IActiveScript, riid)) {
+ *ppv = iface;
+ return S_OK;
+ }
+
+ if(IsEqualGUID(&IID_IActiveScriptParse, riid)) {
+ /* TODO */
+ return E_NOINTERFACE;
+ }
+
+ ok(0, "unexpected riid %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ActiveScript_AddRef(IActiveScript *iface)
+{
+ return 2;
+}
+
+static ULONG WINAPI ActiveScript_Release(IActiveScript *iface)
+{
+ return 1;
+}
+
+static HRESULT WINAPI ActiveScript_SetScriptSite(IActiveScript *iface, IActiveScriptSite *pass)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ActiveScript_GetScriptSite(IActiveScript *iface, REFIID riid,
+ void **ppvObject)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ActiveScript_SetScriptState(IActiveScript *iface, SCRIPTSTATE ss)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ActiveScript_GetScriptState(IActiveScript *iface, SCRIPTSTATE *pssState)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ActiveScript_Close(IActiveScript *iface)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ActiveScript_AddNamedItem(IActiveScript *iface,
+ LPCOLESTR pstrName, DWORD dwFlags)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ActiveScript_AddTypeLib(IActiveScript *iface, REFGUID rguidTypeLib,
+ DWORD dwMajor, DWORD dwMinor, DWORD dwFlags)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ActiveScript_GetScriptDispatch(IActiveScript *iface, LPCOLESTR pstrItemName,
+ IDispatch **ppdisp)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ActiveScript_GetCurrentScriptThreadID(IActiveScript *iface,
+ SCRIPTTHREADID *pstridThread)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ActiveScript_GetScriptThreadID(IActiveScript *iface,
+ DWORD dwWin32ThreadId, SCRIPTTHREADID *pstidThread)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ActiveScript_GetScriptThreadState(IActiveScript *iface,
+ SCRIPTTHREADID stidThread, SCRIPTTHREADSTATE *pstsState)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ActiveScript_InterruptScriptThread(IActiveScript *iface,
+ SCRIPTTHREADID stidThread, const EXCEPINFO *pexcepinfo, DWORD dwFlags)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ActiveScript_Clone(IActiveScript *iface, IActiveScript **ppscript)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static const IActiveScriptVtbl ActiveScriptVtbl = {
+ ActiveScript_QueryInterface,
+ ActiveScript_AddRef,
+ ActiveScript_Release,
+ ActiveScript_SetScriptSite,
+ ActiveScript_GetScriptSite,
+ ActiveScript_SetScriptState,
+ ActiveScript_GetScriptState,
+ ActiveScript_Close,
+ ActiveScript_AddNamedItem,
+ ActiveScript_AddTypeLib,
+ ActiveScript_GetScriptDispatch,
+ ActiveScript_GetCurrentScriptThreadID,
+ ActiveScript_GetScriptThreadID,
+ ActiveScript_GetScriptThreadState,
+ ActiveScript_InterruptScriptThread,
+ ActiveScript_Clone
+};
+
+static IActiveScript ActiveScript = { &ActiveScriptVtbl };
+
+static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv)
+{
+ *ppv = NULL;
+
+ if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IClassFactory, riid)) {
+ *ppv = iface;
+ return S_OK;
+ }
+
+ if(IsEqualGUID(&IID_IMarshal, riid))
+ return E_NOINTERFACE;
+ if(IsEqualGUID(&CLSID_IdentityUnmarshal, riid))
+ return E_NOINTERFACE;
+
+ ok(0, "unexpected riid %s\n", debugstr_guid(riid));
+ return E_NOTIMPL;
+}
+
+static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface)
+{
+ return 2;
+}
+
+static ULONG WINAPI ClassFactory_Release(IClassFactory *iface)
+{
+ return 1;
+}
+
+static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv)
+{
+ CHECK_EXPECT(CreateInstance);
+
+ ok(!outer, "outer = %p\n", outer);
+ ok(IsEqualGUID(&IID_IActiveScript, riid), "unexpected riid %s\n", debugstr_guid(riid));
+ *ppv = &ActiveScript;
+ return S_OK;
+}
+
+static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock)
+{
+ ok(0, "unexpected call\n");
+ return S_OK;
+}
+
+static const IClassFactoryVtbl ClassFactoryVtbl = {
+ ClassFactory_QueryInterface,
+ ClassFactory_AddRef,
+ ClassFactory_Release,
+ ClassFactory_CreateInstance,
+ ClassFactory_LockServer
+};
+
+static IClassFactory script_cf = { &ClassFactoryVtbl };
+
+static const char simple_script_str[] =
+ "<html><head></head><body>"
+ "<script language=\"TestScript\">simple script</script>"
+ "</body></html>";
+
+static void test_simple_script(void)
+{
+ IHTMLDocument2 *doc;
+
+ SET_EXPECT(CreateInstance);
+ doc = create_and_load_doc(simple_script_str);
+ CHECK_CALLED(CreateInstance);
+
+ IHTMLDocument2_Release(doc);
+}
+
+static BOOL init_key(const char *key_name, const char *def_value, BOOL init)
+{
+ HKEY hkey;
+ DWORD res;
+
+ if(!init) {
+ RegDeleteKey(HKEY_CLASSES_ROOT, key_name);
+ return TRUE;
+ }
+
+ res = RegCreateKeyA(HKEY_CLASSES_ROOT, key_name, &hkey);
+ if(res != ERROR_SUCCESS)
+ return FALSE;
+
+ if(def_value)
+ res = RegSetValueA(hkey, NULL, REG_SZ, def_value, strlen(def_value));
+
+ RegCloseKey(hkey);
+
+ return res == ERROR_SUCCESS;
+}
+
+static BOOL init_registry(BOOL init)
+{
+ return init_key("TestScript\\CLSID", TESTSCRIPT_CLSID, init)
+ && init_key("CLSID\\"TESTSCRIPT_CLSID"\\Implemented Categories\\{F0B7A1A1-9847-11CF-8F20-00805F2CD064}",
+ NULL, init)
+ && init_key("CLSID\\"TESTSCRIPT_CLSID"\\Implemented Categories\\{F0B7A1A2-9847-11CF-8F20-00805F2CD064}",
+ NULL, init);
+}
+
+static BOOL register_script_engine(void)
+{
+ DWORD regid;
+ HRESULT hres;
+
+ if(!init_registry(TRUE)) {
+ init_registry(FALSE);
+ return FALSE;
+ }
+
+ hres = CoRegisterClassObject(&CLSID_TestScript, (IUnknown *)&script_cf,
+ CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, ®id);
+ ok(hres == S_OK, "Could not register screipt engine: %08x\n", hres);
+
+ return TRUE;
+}
+
+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(script)
+{
+ gecko_installer_workaround(TRUE);
+ CoInitialize(NULL);
+
+ if(register_script_engine()) {
+ test_simple_script();
+ init_registry(FALSE);
+ }else {
+ skip("Could not register TestScript engine\n");
+ }
+
+ CoUninitialize();
+ gecko_installer_workaround(FALSE);
+}