mshtml: Added IHTMLDocument4::focus implementation.
[wine] / dlls / mshtml / script.c
1 /*
2  * Copyright 2008 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "ole2.h"
29 #include "activscp.h"
30 #include "activdbg.h"
31 #include "objsafe.h"
32
33 #include "wine/debug.h"
34
35 #include "mshtml_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
38
39 static const WCHAR windowW[] = {'w','i','n','d','o','w',0};
40 static const WCHAR emptyW[] = {0};
41
42 static const CLSID CLSID_JScript =
43     {0xf414c260,0x6ac0,0x11cf,{0xb6,0xd1,0x00,0xaa,0x00,0xbb,0xbb,0x58}};
44
45 typedef struct {
46     const IActiveScriptSiteVtbl               *lpIActiveScriptSiteVtbl;
47     const IActiveScriptSiteInterruptPollVtbl  *lpIActiveScriptSiteInterruptPollVtbl;
48     const IActiveScriptSiteWindowVtbl         *lpIActiveScriptSiteWindowVtbl;
49     const IActiveScriptSiteDebug32Vtbl        *lpIActiveScriptSiteDebug32Vtbl;
50
51     LONG ref;
52
53     IActiveScript *script;
54     IActiveScriptParse *parse;
55     IActiveScriptParseProcedure *parse_proc;
56
57     SCRIPTSTATE script_state;
58
59     HTMLDocument *doc;
60
61     GUID guid;
62     struct list entry;
63 } ScriptHost;
64
65 #define ACTSCPSITE(x)  ((IActiveScriptSite*)               &(x)->lpIActiveScriptSiteVtbl)
66 #define ACTSCPPOLL(x)  ((IActiveScriptSiteInterruptPoll*)  &(x)->lpIActiveScriptSiteInterruptPollVtbl)
67 #define ACTSCPWIN(x)   ((IActiveScriptSiteWindow*)         &(x)->lpIActiveScriptSiteWindowVtbl)
68 #define ACTSCPDBG32(x) ((IActiveScriptSiteDebug32*)        &(x)->lpIActiveScriptSiteDebug32Vtbl)
69
70 static BOOL init_script_engine(ScriptHost *script_host)
71 {
72     IActiveScriptProperty *property;
73     IObjectSafety *safety;
74     SCRIPTSTATE state;
75     DWORD supported_opts=0, enabled_opts=0;
76     HRESULT hres;
77
78     hres = IActiveScript_QueryInterface(script_host->script, &IID_IActiveScriptParse, (void**)&script_host->parse);
79     if(FAILED(hres)) {
80         WARN("Could not get IActiveScriptHost: %08x\n", hres);
81         return FALSE;
82     }
83
84     hres = IActiveScript_QueryInterface(script_host->script, &IID_IObjectSafety, (void**)&safety);
85     if(FAILED(hres)) {
86         FIXME("Could not get IObjectSafety: %08x\n", hres);
87         return FALSE;
88     }
89
90     hres = IObjectSafety_GetInterfaceSafetyOptions(safety, &IID_IActiveScriptParse, &supported_opts, &enabled_opts);
91     if(FAILED(hres)) {
92         FIXME("GetInterfaceSafetyOptions failed: %08x\n", hres);
93     }else if(!(supported_opts & INTERFACE_USES_DISPEX)) {
94         FIXME("INTERFACE_USES_DISPEX is not supported\n");
95     }else {
96         hres = IObjectSafety_SetInterfaceSafetyOptions(safety, &IID_IActiveScriptParse,
97                 INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACE_USES_DISPEX|INTERFACE_USES_SECURITY_MANAGER,
98                 INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACE_USES_DISPEX|INTERFACE_USES_SECURITY_MANAGER);
99         if(FAILED(hres))
100             FIXME("SetInterfaceSafetyOptions failed: %08x\n", hres);
101     }
102
103     IObjectSafety_Release(safety);
104     if(FAILED(hres))
105         return FALSE;
106
107     hres = IActiveScript_QueryInterface(script_host->script, &IID_IActiveScriptProperty, (void**)&property);
108     if(SUCCEEDED(hres)) {
109         VARIANT var;
110
111         V_VT(&var) = VT_BOOL;
112         V_BOOL(&var) = VARIANT_TRUE;
113         hres = IActiveScriptProperty_SetProperty(property, SCRIPTPROP_HACK_TRIDENTEVENTSINK, NULL, &var);
114         if(FAILED(hres))
115             WARN("SetProperty failed: %08x\n", hres);
116
117         IActiveScriptProperty_Release(property);
118     }else {
119         WARN("Could not get IActiveScriptProperty: %08x\n", hres);
120     }
121
122     hres = IActiveScriptParse_InitNew(script_host->parse);
123     if(FAILED(hres)) {
124         WARN("InitNew failed: %08x\n", hres);
125         return FALSE;
126     }
127
128     hres = IActiveScript_SetScriptSite(script_host->script, ACTSCPSITE(script_host));
129     if(FAILED(hres)) {
130         WARN("SetScriptSite failed: %08x\n", hres);
131         IActiveScript_Close(script_host->script);
132         return FALSE;
133     }
134
135     hres = IActiveScript_GetScriptState(script_host->script, &state);
136     if(FAILED(hres))
137         WARN("GetScriptState failed: %08x\n", hres);
138     else if(state != SCRIPTSTATE_INITIALIZED)
139         FIXME("state = %x\n", state);
140
141     hres = IActiveScript_SetScriptState(script_host->script, SCRIPTSTATE_STARTED);
142     if(FAILED(hres)) {
143         WARN("Starting script failed: %08x\n", hres);
144         return FALSE;
145     }
146
147     hres = IActiveScript_AddNamedItem(script_host->script, windowW,
148             SCRIPTITEM_ISVISIBLE|SCRIPTITEM_ISSOURCE|SCRIPTITEM_GLOBALMEMBERS);
149     if(FAILED(hres))
150        WARN("AddNamedItem failed: %08x\n", hres);
151
152     hres = IActiveScript_QueryInterface(script_host->script, &IID_IActiveScriptParseProcedure2,
153                                         (void**)&script_host->parse_proc);
154     if(FAILED(hres)) {
155         /* FIXME: QI for IActiveScriptParseProcedure */
156         WARN("Could not get IActiveScriptParseProcedure iface: %08x\n", hres);
157     }
158
159     return TRUE;
160 }
161
162 static void release_script_engine(ScriptHost *This)
163 {
164     if(!This->script)
165         return;
166
167     switch(This->script_state) {
168     case SCRIPTSTATE_CONNECTED:
169         IActiveScript_SetScriptState(This->script, SCRIPTSTATE_DISCONNECTED);
170
171     case SCRIPTSTATE_STARTED:
172     case SCRIPTSTATE_DISCONNECTED:
173     case SCRIPTSTATE_INITIALIZED:
174         IActiveScript_Close(This->script);
175
176     default:
177         if(This->parse_proc) {
178             IActiveScriptParseProcedure_Release(This->parse_proc);
179             This->parse_proc = NULL;
180         }
181
182         if(This->parse) {
183             IActiveScriptParse_Release(This->parse);
184             This->parse = NULL;
185         }
186     }
187
188     IActiveScript_Release(This->script);
189     This->script = NULL;
190     This->script_state = SCRIPTSTATE_UNINITIALIZED;
191 }
192
193 void connect_scripts(HTMLDocument *doc)
194 {
195     ScriptHost *iter;
196
197     LIST_FOR_EACH_ENTRY(iter, &doc->script_hosts, ScriptHost, entry) {
198         if(iter->script_state == SCRIPTSTATE_STARTED)
199             IActiveScript_SetScriptState(iter->script, SCRIPTSTATE_CONNECTED);
200     }
201 }
202
203 #define ACTSCPSITE_THIS(iface) DEFINE_THIS(ScriptHost, IActiveScriptSite, iface)
204
205 static HRESULT WINAPI ActiveScriptSite_QueryInterface(IActiveScriptSite *iface, REFIID riid, void **ppv)
206 {
207     ScriptHost *This = ACTSCPSITE_THIS(iface);
208
209     *ppv = NULL;
210
211     if(IsEqualGUID(&IID_IUnknown, riid)) {
212         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
213         *ppv = ACTSCPSITE(This);
214     }else if(IsEqualGUID(&IID_IActiveScriptSite, riid)) {
215         TRACE("(%p)->(IID_IActiveScriptSite %p)\n", This, ppv);
216         *ppv = ACTSCPSITE(This);
217     }else if(IsEqualGUID(&IID_IActiveScriptSiteInterruptPoll, riid)) {
218         TRACE("(%p)->(IID_IActiveScriptSiteInterruprtPoll %p)\n", This, ppv);
219         *ppv = ACTSCPPOLL(This);
220     }else if(IsEqualGUID(&IID_IActiveScriptSiteWindow, riid)) {
221         TRACE("(%p)->(IID_IActiveScriptSiteWindow %p)\n", This, ppv);
222         *ppv = ACTSCPWIN(This);
223     }else if(IsEqualGUID(&IID_IActiveScriptSiteDebug32, riid)) {
224         TRACE("(%p)->(IID_IActiveScriptSiteDebug32 %p)\n", This, ppv);
225         *ppv = ACTSCPDBG32(This);
226     }else {
227         FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
228         return E_NOINTERFACE;
229     }
230
231     IUnknown_AddRef((IUnknown*)*ppv);
232     return S_OK;
233 }
234
235 static ULONG WINAPI ActiveScriptSite_AddRef(IActiveScriptSite *iface)
236 {
237     ScriptHost *This = ACTSCPSITE_THIS(iface);
238     LONG ref = InterlockedIncrement(&This->ref);
239
240     TRACE("(%p) ref=%d\n", This, ref);
241
242     return ref;
243 }
244
245 static ULONG WINAPI ActiveScriptSite_Release(IActiveScriptSite *iface)
246 {
247     ScriptHost *This = ACTSCPSITE_THIS(iface);
248     LONG ref = InterlockedDecrement(&This->ref);
249
250     TRACE("(%p) ref=%d\n", This, ref);
251
252     if(!ref) {
253         release_script_engine(This);
254         if(This->doc)
255             list_remove(&This->entry);
256         heap_free(This);
257     }
258
259     return ref;
260 }
261
262 static HRESULT WINAPI ActiveScriptSite_GetLCID(IActiveScriptSite *iface, LCID *plcid)
263 {
264     ScriptHost *This = ACTSCPSITE_THIS(iface);
265
266     TRACE("(%p)->(%p)\n", This, plcid);
267
268     *plcid = GetUserDefaultLCID();
269     return S_OK;
270 }
271
272 static HRESULT WINAPI ActiveScriptSite_GetItemInfo(IActiveScriptSite *iface, LPCOLESTR pstrName,
273         DWORD dwReturnMask, IUnknown **ppiunkItem, ITypeInfo **ppti)
274 {
275     ScriptHost *This = ACTSCPSITE_THIS(iface);
276
277     TRACE("(%p)->(%s %x %p %p)\n", This, debugstr_w(pstrName), dwReturnMask, ppiunkItem, ppti);
278
279     if(dwReturnMask != SCRIPTINFO_IUNKNOWN) {
280         FIXME("Unsupported mask %x\n", dwReturnMask);
281         return E_NOTIMPL;
282     }
283
284     *ppiunkItem = NULL;
285
286     if(strcmpW(pstrName, windowW))
287         return DISP_E_MEMBERNOTFOUND;
288
289     if(!This->doc)
290         return E_FAIL;
291
292     /* FIXME: Return proxy object */
293     *ppiunkItem = (IUnknown*)HTMLWINDOW2(This->doc->window);
294     IUnknown_AddRef(*ppiunkItem);
295
296     return S_OK;
297 }
298
299 static HRESULT WINAPI ActiveScriptSite_GetDocVersionString(IActiveScriptSite *iface, BSTR *pbstrVersion)
300 {
301     ScriptHost *This = ACTSCPSITE_THIS(iface);
302     FIXME("(%p)->(%p)\n", This, pbstrVersion);
303     return E_NOTIMPL;
304 }
305
306 static HRESULT WINAPI ActiveScriptSite_OnScriptTerminate(IActiveScriptSite *iface,
307         const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo)
308 {
309     ScriptHost *This = ACTSCPSITE_THIS(iface);
310     FIXME("(%p)->(%p %p)\n", This, pvarResult, pexcepinfo);
311     return E_NOTIMPL;
312 }
313
314 static HRESULT WINAPI ActiveScriptSite_OnStateChange(IActiveScriptSite *iface, SCRIPTSTATE ssScriptState)
315 {
316     ScriptHost *This = ACTSCPSITE_THIS(iface);
317
318     TRACE("(%p)->(%x)\n", This, ssScriptState);
319
320     This->script_state = ssScriptState;
321     return S_OK;
322 }
323
324 static HRESULT WINAPI ActiveScriptSite_OnScriptError(IActiveScriptSite *iface, IActiveScriptError *pscripterror)
325 {
326     ScriptHost *This = ACTSCPSITE_THIS(iface);
327     FIXME("(%p)->(%p)\n", This, pscripterror);
328     return E_NOTIMPL;
329 }
330
331 static HRESULT WINAPI ActiveScriptSite_OnEnterScript(IActiveScriptSite *iface)
332 {
333     ScriptHost *This = ACTSCPSITE_THIS(iface);
334
335     TRACE("(%p)->()\n", This);
336
337     return S_OK;
338 }
339
340 static HRESULT WINAPI ActiveScriptSite_OnLeaveScript(IActiveScriptSite *iface)
341 {
342     ScriptHost *This = ACTSCPSITE_THIS(iface);
343
344     TRACE("(%p)->()\n", This);
345
346     return S_OK;
347 }
348
349 #undef ACTSCPSITE_THIS
350
351 static const IActiveScriptSiteVtbl ActiveScriptSiteVtbl = {
352     ActiveScriptSite_QueryInterface,
353     ActiveScriptSite_AddRef,
354     ActiveScriptSite_Release,
355     ActiveScriptSite_GetLCID,
356     ActiveScriptSite_GetItemInfo,
357     ActiveScriptSite_GetDocVersionString,
358     ActiveScriptSite_OnScriptTerminate,
359     ActiveScriptSite_OnStateChange,
360     ActiveScriptSite_OnScriptError,
361     ActiveScriptSite_OnEnterScript,
362     ActiveScriptSite_OnLeaveScript
363 };
364
365 #define ACTSCPPOLL_THIS(iface) DEFINE_THIS(ScriptHost, IActiveScriptSiteInterruptPoll, iface)
366
367 static HRESULT WINAPI ActiveScriptSiteInterruptPoll_QueryInterface(IActiveScriptSiteInterruptPoll *iface,
368         REFIID riid, void **ppv)
369 {
370     ScriptHost *This = ACTSCPPOLL_THIS(iface);
371     return IActiveScriptSite_QueryInterface(ACTSCPSITE(This), riid, ppv);
372 }
373
374 static ULONG WINAPI ActiveScriptSiteInterruptPoll_AddRef(IActiveScriptSiteInterruptPoll *iface)
375 {
376     ScriptHost *This = ACTSCPPOLL_THIS(iface);
377     return IActiveScriptSite_AddRef(ACTSCPSITE(This));
378 }
379
380 static ULONG WINAPI ActiveScriptSiteInterruptPoll_Release(IActiveScriptSiteInterruptPoll *iface)
381 {
382     ScriptHost *This = ACTSCPPOLL_THIS(iface);
383     return IActiveScriptSite_Release(ACTSCPSITE(This));
384 }
385
386 static HRESULT WINAPI ActiveScriptSiteInterruptPoll_QueryContinue(IActiveScriptSiteInterruptPoll *iface)
387 {
388     ScriptHost *This = ACTSCPPOLL_THIS(iface);
389
390     TRACE("(%p)\n", This);
391
392     return S_OK;
393 }
394
395 #undef ACTSCPPOLL_THIS
396
397 static const IActiveScriptSiteInterruptPollVtbl ActiveScriptSiteInterruptPollVtbl = {
398     ActiveScriptSiteInterruptPoll_QueryInterface,
399     ActiveScriptSiteInterruptPoll_AddRef,
400     ActiveScriptSiteInterruptPoll_Release,
401     ActiveScriptSiteInterruptPoll_QueryContinue
402 };
403
404 #define ACTSCPWIN_THIS(iface) DEFINE_THIS(ScriptHost, IActiveScriptSiteWindow, iface)
405
406 static HRESULT WINAPI ActiveScriptSiteWindow_QueryInterface(IActiveScriptSiteWindow *iface,
407         REFIID riid, void **ppv)
408 {
409     ScriptHost *This = ACTSCPWIN_THIS(iface);
410     return IActiveScriptSite_QueryInterface(ACTSCPSITE(This), riid, ppv);
411 }
412
413 static ULONG WINAPI ActiveScriptSiteWindow_AddRef(IActiveScriptSiteWindow *iface)
414 {
415     ScriptHost *This = ACTSCPWIN_THIS(iface);
416     return IActiveScriptSite_AddRef(ACTSCPSITE(This));
417 }
418
419 static ULONG WINAPI ActiveScriptSiteWindow_Release(IActiveScriptSiteWindow *iface)
420 {
421     ScriptHost *This = ACTSCPWIN_THIS(iface);
422     return IActiveScriptSite_Release(ACTSCPSITE(This));
423 }
424
425 static HRESULT WINAPI ActiveScriptSiteWindow_GetWindow(IActiveScriptSiteWindow *iface, HWND *phwnd)
426 {
427     ScriptHost *This = ACTSCPWIN_THIS(iface);
428     FIXME("(%p)->(%p)\n", This, phwnd);
429     return E_NOTIMPL;
430 }
431
432 static HRESULT WINAPI ActiveScriptSiteWindow_EnableModeless(IActiveScriptSiteWindow *iface, BOOL fEnable)
433 {
434     ScriptHost *This = ACTSCPWIN_THIS(iface);
435     FIXME("(%p)->(%x)\n", This, fEnable);
436     return E_NOTIMPL;
437 }
438
439 #undef ACTSCPWIN_THIS
440
441 static const IActiveScriptSiteWindowVtbl ActiveScriptSiteWindowVtbl = {
442     ActiveScriptSiteWindow_QueryInterface,
443     ActiveScriptSiteWindow_AddRef,
444     ActiveScriptSiteWindow_Release,
445     ActiveScriptSiteWindow_GetWindow,
446     ActiveScriptSiteWindow_EnableModeless
447 };
448
449 #define ACTSCPDBG32_THIS(iface) DEFINE_THIS(ScriptHost, IActiveScriptSiteDebug32, iface)
450
451 static HRESULT WINAPI ActiveScriptSiteDebug32_QueryInterface(IActiveScriptSiteDebug32 *iface,
452         REFIID riid, void **ppv)
453 {
454     ScriptHost *This = ACTSCPDBG32_THIS(iface);
455     return IActiveScriptSite_QueryInterface(ACTSCPSITE(This), riid, ppv);
456 }
457
458 static ULONG WINAPI ActiveScriptSiteDebug32_AddRef(IActiveScriptSiteDebug32 *iface)
459 {
460     ScriptHost *This = ACTSCPDBG32_THIS(iface);
461     return IActiveScriptSite_AddRef(ACTSCPSITE(This));
462 }
463
464 static ULONG WINAPI ActiveScriptSiteDebug32_Release(IActiveScriptSiteDebug32 *iface)
465 {
466     ScriptHost *This = ACTSCPDBG32_THIS(iface);
467     return IActiveScriptSite_Release(ACTSCPSITE(This));
468 }
469
470 static HRESULT WINAPI ActiveScriptSiteDebug32_GetDocumentContextFromPosition(IActiveScriptSiteDebug32 *iface,
471             DWORD dwSourceContext, ULONG uCharacterOffset, ULONG uNumChars, IDebugDocumentContext **ppsc)
472 {
473     ScriptHost *This = ACTSCPDBG32_THIS(iface);
474     FIXME("(%p)->(%x %u %u %p)\n", This, dwSourceContext, uCharacterOffset, uNumChars, ppsc);
475     return E_NOTIMPL;
476 }
477
478 static HRESULT WINAPI ActiveScriptSiteDebug32_GetApplication(IActiveScriptSiteDebug32 *iface, IDebugApplication32 **ppda)
479 {
480     ScriptHost *This = ACTSCPDBG32_THIS(iface);
481     FIXME("(%p)->(%p)\n", This, ppda);
482     return E_NOTIMPL;
483 }
484
485 static HRESULT WINAPI ActiveScriptSiteDebug32_GetRootApplicationNode(IActiveScriptSiteDebug32 *iface,
486             IDebugApplicationNode **ppdanRoot)
487 {
488     ScriptHost *This = ACTSCPDBG32_THIS(iface);
489     FIXME("(%p)->(%p)\n", This, ppdanRoot);
490     return E_NOTIMPL;
491 }
492
493 static HRESULT WINAPI ActiveScriptSiteDebug32_OnScriptErrorDebug(IActiveScriptSiteDebug32 *iface,
494             IActiveScriptErrorDebug *pErrorDebug, BOOL *pfEnterDebugger, BOOL *pfCallOnScriptErrorWhenContinuing)
495 {
496     ScriptHost *This = ACTSCPDBG32_THIS(iface);
497     FIXME("(%p)->(%p %p %p)\n", This, pErrorDebug, pfEnterDebugger, pfCallOnScriptErrorWhenContinuing);
498     return E_NOTIMPL;
499 }
500
501 #undef ACTSCPDBG32_THIS
502
503 static const IActiveScriptSiteDebug32Vtbl ActiveScriptSiteDebug32Vtbl = {
504     ActiveScriptSiteDebug32_QueryInterface,
505     ActiveScriptSiteDebug32_AddRef,
506     ActiveScriptSiteDebug32_Release,
507     ActiveScriptSiteDebug32_GetDocumentContextFromPosition,
508     ActiveScriptSiteDebug32_GetApplication,
509     ActiveScriptSiteDebug32_GetRootApplicationNode,
510     ActiveScriptSiteDebug32_OnScriptErrorDebug
511 };
512
513 static ScriptHost *create_script_host(HTMLDocument *doc, const GUID *guid)
514 {
515     ScriptHost *ret;
516     HRESULT hres;
517
518     ret = heap_alloc_zero(sizeof(*ret));
519     ret->lpIActiveScriptSiteVtbl               = &ActiveScriptSiteVtbl;
520     ret->lpIActiveScriptSiteInterruptPollVtbl  = &ActiveScriptSiteInterruptPollVtbl;
521     ret->lpIActiveScriptSiteWindowVtbl         = &ActiveScriptSiteWindowVtbl;
522     ret->lpIActiveScriptSiteDebug32Vtbl        = &ActiveScriptSiteDebug32Vtbl;
523     ret->ref = 1;
524     ret->doc = doc;
525     ret->script_state = SCRIPTSTATE_UNINITIALIZED;
526
527     ret->guid = *guid;
528     list_add_tail(&doc->script_hosts, &ret->entry);
529
530     hres = CoCreateInstance(&ret->guid, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
531             &IID_IActiveScript, (void**)&ret->script);
532     if(FAILED(hres))
533         WARN("Could not load script engine: %08x\n", hres);
534     else if(!init_script_engine(ret))
535         release_script_engine(ret);
536
537     return ret;
538 }
539
540 static void parse_text(ScriptHost *script_host, LPCWSTR text)
541 {
542     EXCEPINFO excepinfo;
543     VARIANT var;
544     HRESULT hres;
545
546     static const WCHAR script_endW[] = {'<','/','S','C','R','I','P','T','>',0};
547
548     TRACE("%s\n", debugstr_w(text));
549
550     VariantInit(&var);
551     memset(&excepinfo, 0, sizeof(excepinfo));
552     hres = IActiveScriptParse_ParseScriptText(script_host->parse, text, windowW, NULL, script_endW,
553                                               0, 0, SCRIPTTEXT_ISVISIBLE|SCRIPTTEXT_HOSTMANAGESSOURCE,
554                                               &var, &excepinfo);
555     if(FAILED(hres))
556         WARN("ParseScriptText failed: %08x\n", hres);
557
558 }
559
560 static void parse_extern_script(ScriptHost *script_host, LPCWSTR src)
561 {
562     IMoniker *mon;
563     char *buf;
564     WCHAR *text;
565     DWORD len, size=0;
566     HRESULT hres;
567
568     static const WCHAR wine_schemaW[] = {'w','i','n','e',':'};
569
570     if(strlenW(src) > sizeof(wine_schemaW)/sizeof(WCHAR) && !memcmp(src, wine_schemaW, sizeof(wine_schemaW)))
571         src += sizeof(wine_schemaW)/sizeof(WCHAR);
572
573     hres = CreateURLMoniker(NULL, src, &mon);
574     if(FAILED(hres))
575         return;
576
577     hres = bind_mon_to_buffer(script_host->doc, mon, (void**)&buf, &size);
578     IMoniker_Release(mon);
579     if(FAILED(hres))
580         return;
581
582     len = MultiByteToWideChar(CP_ACP, 0, buf, size, NULL, 0);
583     text = heap_alloc((len+1)*sizeof(WCHAR));
584     MultiByteToWideChar(CP_ACP, 0, buf, size, text, len);
585     heap_free(buf);
586     text[len] = 0;
587
588     parse_text(script_host, text);
589
590     heap_free(text);
591 }
592
593 static void parse_inline_script(ScriptHost *script_host, nsIDOMHTMLScriptElement *nsscript)
594 {
595     const PRUnichar *text;
596     nsAString text_str;
597     nsresult nsres;
598
599     nsAString_Init(&text_str, NULL);
600
601     nsres = nsIDOMHTMLScriptElement_GetText(nsscript, &text_str);
602
603     if(NS_SUCCEEDED(nsres)) {
604         nsAString_GetData(&text_str, &text);
605         parse_text(script_host, text);
606     }else {
607         ERR("GetText failed: %08x\n", nsres);
608     }
609
610     nsAString_Finish(&text_str);
611 }
612
613 static void parse_script_elem(ScriptHost *script_host, nsIDOMHTMLScriptElement *nsscript)
614 {
615     const PRUnichar *src;
616     nsAString src_str;
617     nsresult nsres;
618
619     nsAString_Init(&src_str, NULL);
620
621     nsres = nsIDOMHTMLScriptElement_GetSrc(nsscript, &src_str);
622     nsAString_GetData(&src_str, &src);
623
624     if(NS_FAILED(nsres))
625         ERR("GetSrc failed: %08x\n", nsres);
626     else if(*src)
627         parse_extern_script(script_host, src);
628     else
629         parse_inline_script(script_host, nsscript);
630
631     nsAString_Finish(&src_str);
632 }
633
634 static BOOL get_guid_from_type(LPCWSTR type, GUID *guid)
635 {
636     const WCHAR text_javascriptW[] =
637         {'t','e','x','t','/','j','a','v','a','s','c','r','i','p','t',0};
638
639     /* FIXME: Handle more types */
640     if(!strcmpW(type, text_javascriptW)) {
641         *guid = CLSID_JScript;
642     }else {
643         FIXME("Unknown type %s\n", debugstr_w(type));
644         return FALSE;
645     }
646
647     return TRUE;
648 }
649
650 static BOOL get_guid_from_language(LPCWSTR type, GUID *guid)
651 {
652     HRESULT hres;
653
654     hres = CLSIDFromProgID(type, guid);
655     if(FAILED(hres))
656         return FALSE;
657
658     /* FIXME: Check CATID_ActiveScriptParse */
659
660     return TRUE;
661 }
662
663 static BOOL get_script_guid(nsIDOMHTMLScriptElement *nsscript, GUID *guid)
664 {
665     nsAString attr_str, val_str;
666     BOOL ret = FALSE;
667     nsresult nsres;
668
669     static const PRUnichar languageW[] = {'l','a','n','g','u','a','g','e',0};
670
671     nsAString_Init(&val_str, NULL);
672
673     nsres = nsIDOMHTMLScriptElement_GetType(nsscript, &val_str);
674     if(NS_SUCCEEDED(nsres)) {
675         const PRUnichar *type;
676
677         nsAString_GetData(&val_str, &type);
678         if(*type) {
679             ret = get_guid_from_type(type, guid);
680             nsAString_Finish(&val_str);
681             return ret;
682         }
683     }else {
684         ERR("GetType failed: %08x\n", nsres);
685     }
686
687     nsAString_Init(&attr_str, languageW);
688
689     nsres = nsIDOMHTMLScriptElement_GetAttribute(nsscript, &attr_str, &val_str);
690     if(NS_SUCCEEDED(nsres)) {
691         const PRUnichar *language;
692
693         nsAString_GetData(&val_str, &language);
694
695         if(*language) {
696             ret = get_guid_from_language(language, guid);
697         }else {
698             *guid = CLSID_JScript;
699             ret = TRUE;
700         }
701     }else {
702         ERR("GetAttribute(language) failed: %08x\n", nsres);
703     }
704
705     nsAString_Finish(&attr_str);
706     nsAString_Finish(&val_str);
707
708     return ret;
709 }
710
711 static ScriptHost *get_script_host(HTMLDocument *doc, const GUID *guid)
712 {
713     ScriptHost *iter;
714
715     if(IsEqualGUID(&CLSID_JScript, &guid) && doc->scriptmode != SCRIPTMODE_ACTIVESCRIPT) {
716         TRACE("Ignoring JScript\n");
717         return NULL;
718     }
719
720     LIST_FOR_EACH_ENTRY(iter, &doc->script_hosts, ScriptHost, entry) {
721         if(IsEqualGUID(guid, &iter->guid))
722             return iter;
723     }
724
725     return create_script_host(doc, guid);
726 }
727
728 void doc_insert_script(HTMLDocument *doc, nsIDOMHTMLScriptElement *nsscript)
729 {
730     ScriptHost *script_host;
731     GUID guid;
732
733     if(!get_script_guid(nsscript, &guid)) {
734         WARN("Could not find script GUID\n");
735         return;
736     }
737
738     script_host = get_script_host(doc, &guid);
739     if(!script_host)
740         return;
741
742     if(script_host->parse)
743         parse_script_elem(script_host, nsscript);
744 }
745
746 IDispatch *script_parse_event(HTMLDocument *doc, LPCWSTR text)
747 {
748     ScriptHost *script_host;
749     GUID guid = CLSID_JScript;
750     const WCHAR *ptr;
751     IDispatch *disp;
752     HRESULT hres;
753
754     static const WCHAR delimiterW[] = {'\"',0};
755
756     for(ptr = text; isalnumW(*ptr); ptr++);
757     if(*ptr == ':') {
758         LPWSTR language;
759         BOOL b;
760
761         language = heap_alloc((ptr-text+1)*sizeof(WCHAR));
762         memcpy(language, text, (ptr-text)*sizeof(WCHAR));
763         language[ptr-text] = 0;
764
765         b = get_guid_from_language(language, &guid);
766
767         heap_free(language);
768
769         if(!b) {
770             WARN("Could not find language\n");
771             return NULL;
772         }
773
774         ptr++;
775     }else {
776         ptr = text;
777     }
778
779     script_host = get_script_host(doc, &guid);
780     if(!script_host || !script_host->parse_proc)
781         return NULL;
782
783     hres = IActiveScriptParseProcedure_ParseProcedureText(script_host->parse_proc, ptr, NULL, emptyW,
784             NULL, NULL, delimiterW, 0 /* FIXME */, 0,
785             SCRIPTPROC_HOSTMANAGESSOURCE|SCRIPTPROC_IMPLICIT_THIS|SCRIPTPROC_IMPLICIT_PARENTS, &disp);
786     if(FAILED(hres)) {
787         WARN("ParseProcedureText failed: %08x\n", hres);
788         return NULL;
789     }
790
791     TRACE("ret %p\n", disp);
792     return disp;
793 }
794
795 static BOOL is_jscript_available(void)
796 {
797     static BOOL available, checked;
798
799     if(!checked) {
800         IUnknown *unk;
801         HRESULT hres = CoGetClassObject(&CLSID_JScript, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void**)&unk);
802
803         if(SUCCEEDED(hres)) {
804             available = TRUE;
805             IUnknown_Release(unk);
806         }else {
807             available = FALSE;
808         }
809         checked = TRUE;
810     }
811
812     return available;
813 }
814
815 void set_script_mode(HTMLDocument *doc, SCRIPTMODE mode)
816 {
817     nsIWebBrowserSetup *setup;
818     nsresult nsres;
819
820     if(mode == SCRIPTMODE_ACTIVESCRIPT && !is_jscript_available()) {
821         TRACE("jscript.dll not available\n");
822         doc->scriptmode = SCRIPTMODE_GECKO;
823         return;
824     }
825
826     doc->scriptmode = mode;
827
828     if(!doc->nscontainer || !doc->nscontainer->webbrowser)
829         return;
830
831     nsres = nsIWebBrowser_QueryInterface(doc->nscontainer->webbrowser,
832             &IID_nsIWebBrowserSetup, (void**)&setup);
833     if(NS_SUCCEEDED(nsres)) {
834         nsres = nsIWebBrowserSetup_SetProperty(setup, SETUP_ALLOW_JAVASCRIPT,
835                 doc->scriptmode == SCRIPTMODE_GECKO);
836         nsIWebBrowserSetup_Release(setup);
837     }
838
839     if(NS_FAILED(nsres))
840         ERR("JavaScript setup failed: %08x\n", nsres);
841 }
842
843 void release_script_hosts(HTMLDocument *doc)
844 {
845     ScriptHost *iter;
846
847     while(!list_empty(&doc->script_hosts)) {
848         iter = LIST_ENTRY(list_head(&doc->script_hosts), ScriptHost, entry);
849
850         release_script_engine(iter);
851         list_remove(&iter->entry);
852         iter->doc = NULL;
853         IActiveScript_Release(ACTSCPSITE(iter));
854     }
855 }