oleaut32: Add a test for loading/saving an empty picture.
[wine] / dlls / mshtml / mutation.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 "winreg.h"
29 #include "ole2.h"
30 #include "shlguid.h"
31
32 #include "mshtml_private.h"
33 #include "htmlscript.h"
34 #include "htmlevent.h"
35 #include "binding.h"
36
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
40
41 #define IE_MAJOR_VERSION 7
42 #define IE_MINOR_VERSION 0
43
44 static const IID NS_ICONTENTUTILS_CID =
45     {0x762C4AE7,0xB923,0x422F,{0xB9,0x7E,0xB9,0xBF,0xC1,0xEF,0x7B,0xF0}};
46
47 static nsIContentUtils *content_utils;
48
49 static PRUnichar *handle_insert_comment(HTMLDocumentNode *doc, const PRUnichar *comment)
50 {
51     int majorv = 0, minorv = 0;
52     const PRUnichar *ptr, *end;
53     PRUnichar *buf;
54     DWORD len;
55
56     enum {
57         CMP_EQ,
58         CMP_LT,
59         CMP_LTE,
60         CMP_GT,
61         CMP_GTE
62     } cmpt = CMP_EQ;
63
64     static const PRUnichar endifW[] = {'<','!','[','e','n','d','i','f',']'};
65
66     if(comment[0] != '[' || comment[1] != 'i' || comment[2] != 'f')
67         return NULL;
68
69     ptr = comment+3;
70     while(isspaceW(*ptr))
71         ptr++;
72
73     if(ptr[0] == 'l' && ptr[1] == 't') {
74         ptr += 2;
75         if(*ptr == 'e') {
76             cmpt = CMP_LTE;
77             ptr++;
78         }else {
79             cmpt = CMP_LT;
80         }
81     }else if(ptr[0] == 'g' && ptr[1] == 't') {
82         ptr += 2;
83         if(*ptr == 'e') {
84             cmpt = CMP_GTE;
85             ptr++;
86         }else {
87             cmpt = CMP_GT;
88         }
89     }
90
91     if(!isspaceW(*ptr++))
92         return NULL;
93     while(isspaceW(*ptr))
94         ptr++;
95
96     if(ptr[0] != 'I' || ptr[1] != 'E')
97         return NULL;
98
99     ptr +=2;
100     if(!isspaceW(*ptr++))
101         return NULL;
102     while(isspaceW(*ptr))
103         ptr++;
104
105     if(!isdigitW(*ptr))
106         return NULL;
107     while(isdigitW(*ptr))
108         majorv = majorv*10 + (*ptr++ - '0');
109
110     if(*ptr == '.') {
111         ptr++;
112         if(!isdigitW(*ptr))
113             return NULL;
114         while(isdigitW(*ptr))
115             minorv = minorv*10 + (*ptr++ - '0');
116     }
117
118     while(isspaceW(*ptr))
119         ptr++;
120     if(ptr[0] != ']' || ptr[1] != '>')
121         return NULL;
122     ptr += 2;
123
124     len = strlenW(ptr);
125     if(len < sizeof(endifW)/sizeof(WCHAR))
126         return NULL;
127
128     end = ptr + len-sizeof(endifW)/sizeof(WCHAR);
129     if(memcmp(end, endifW, sizeof(endifW)))
130         return NULL;
131
132     switch(cmpt) {
133     case CMP_EQ:
134         if(majorv == IE_MAJOR_VERSION && minorv == IE_MINOR_VERSION)
135             break;
136         return NULL;
137     case CMP_LT:
138         if(majorv > IE_MAJOR_VERSION)
139             break;
140         if(majorv == IE_MAJOR_VERSION && minorv > IE_MINOR_VERSION)
141             break;
142         return NULL;
143     case CMP_LTE:
144         if(majorv > IE_MAJOR_VERSION)
145             break;
146         if(majorv == IE_MAJOR_VERSION && minorv >= IE_MINOR_VERSION)
147             break;
148         return NULL;
149     case CMP_GT:
150         if(majorv < IE_MAJOR_VERSION)
151             break;
152         if(majorv == IE_MAJOR_VERSION && minorv < IE_MINOR_VERSION)
153             break;
154         return NULL;
155     case CMP_GTE:
156         if(majorv < IE_MAJOR_VERSION)
157             break;
158         if(majorv == IE_MAJOR_VERSION && minorv <= IE_MINOR_VERSION)
159             break;
160         return NULL;
161     }
162
163     buf = heap_alloc((end-ptr+1)*sizeof(WCHAR));
164     if(!buf)
165         return NULL;
166
167     memcpy(buf, ptr, (end-ptr)*sizeof(WCHAR));
168     buf[end-ptr] = 0;
169
170     return buf;
171 }
172
173 static nsresult run_insert_comment(HTMLDocumentNode *doc, nsISupports *comment_iface, nsISupports *arg2)
174 {
175     const PRUnichar *comment;
176     nsIDOMComment *nscomment;
177     PRUnichar *replace_html;
178     nsAString comment_str;
179     nsresult nsres;
180
181     nsres = nsISupports_QueryInterface(comment_iface, &IID_nsIDOMComment, (void**)&nscomment);
182     if(NS_FAILED(nsres)) {
183         ERR("Could not get nsIDOMComment iface:%08x\n", nsres);
184         return nsres;
185     }
186
187     nsAString_Init(&comment_str, NULL);
188     nsres = nsIDOMComment_GetData(nscomment, &comment_str);
189     if(NS_FAILED(nsres))
190         return nsres;
191
192     nsAString_GetData(&comment_str, &comment);
193     replace_html = handle_insert_comment(doc, comment);
194     nsAString_Finish(&comment_str);
195
196     if(replace_html) {
197         HRESULT hres;
198
199         hres = replace_node_by_html(doc->nsdoc, (nsIDOMNode*)nscomment, replace_html);
200         heap_free(replace_html);
201         if(FAILED(hres))
202             nsres = NS_ERROR_FAILURE;
203     }
204
205
206     nsIDOMComment_Release(nscomment);
207     return nsres;
208 }
209
210 static nsresult run_bind_to_tree(HTMLDocumentNode *doc, nsISupports *nsiface, nsISupports *arg2)
211 {
212     nsIDOMNode *nsnode;
213     HTMLDOMNode *node;
214     nsresult nsres;
215     HRESULT hres;
216
217     TRACE("(%p)->(%p)\n", doc, nsiface);
218
219     nsres = nsISupports_QueryInterface(nsiface, &IID_nsIDOMNode, (void**)&nsnode);
220     if(NS_FAILED(nsres))
221         return nsres;
222
223     hres = get_node(doc, nsnode, TRUE, &node);
224     nsIDOMNode_Release(nsnode);
225     if(FAILED(hres)) {
226         ERR("Could not get node\n");
227         return nsres;
228     }
229
230     if(node->vtbl->bind_to_tree)
231         node->vtbl->bind_to_tree(node);
232
233     node_release(node);
234     return nsres;
235 }
236
237 /* Calls undocumented 69 cmd of CGID_Explorer */
238 static void call_explorer_69(HTMLDocumentObj *doc)
239 {
240     IOleCommandTarget *olecmd;
241     VARIANT var;
242     HRESULT hres;
243
244     if(!doc->client)
245         return;
246
247     hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
248     if(FAILED(hres))
249         return;
250
251     VariantInit(&var);
252     hres = IOleCommandTarget_Exec(olecmd, &CGID_Explorer, 69, 0, NULL, &var);
253     IOleCommandTarget_Release(olecmd);
254     if(SUCCEEDED(hres) && V_VT(&var) != VT_NULL)
255         FIXME("handle result\n");
256 }
257
258 static void parse_complete(HTMLDocumentObj *doc)
259 {
260     TRACE("(%p)\n", doc);
261
262     if(doc->usermode == EDITMODE)
263         init_editor(&doc->basedoc);
264
265     call_explorer_69(doc);
266     if(doc->view_sink)
267         IAdviseSink_OnViewChange(doc->view_sink, DVASPECT_CONTENT, -1);
268     call_property_onchanged(&doc->basedoc.cp_propnotif, 1005);
269     call_explorer_69(doc);
270
271     if(doc->is_webbrowser && doc->usermode != EDITMODE && !(doc->basedoc.window->load_flags & BINDING_REFRESH))
272         IDocObjectService_FireNavigateComplete2(doc->doc_object_service, &doc->basedoc.window->base.IHTMLWindow2_iface, 0);
273
274     /* FIXME: IE7 calls EnableModelless(TRUE), EnableModelless(FALSE) and sets interactive state here */
275 }
276
277 static nsresult run_end_load(HTMLDocumentNode *This, nsISupports *arg1, nsISupports *arg2)
278 {
279     TRACE("(%p)\n", This);
280
281     if(!This->basedoc.doc_obj)
282         return NS_OK;
283
284     if(This == This->basedoc.doc_obj->basedoc.doc_node) {
285         /*
286          * This should be done in the worker thread that parses HTML,
287          * but we don't have such thread (Gecko parses HTML for us).
288          */
289         parse_complete(This->basedoc.doc_obj);
290     }
291
292     bind_event_scripts(This);
293     set_ready_state(This->basedoc.window, READYSTATE_INTERACTIVE);
294     return NS_OK;
295 }
296
297 static nsresult run_insert_script(HTMLDocumentNode *doc, nsISupports *script_iface, nsISupports *parser_iface)
298 {
299     nsIDOMHTMLScriptElement *nsscript;
300     HTMLScriptElement *script_elem;
301     nsIParser *nsparser = NULL;
302     script_queue_entry_t *iter;
303     HTMLInnerWindow *window;
304     nsresult nsres;
305     HRESULT hres;
306
307     TRACE("(%p)->(%p)\n", doc, script_iface);
308
309     window = doc->window;
310     if(!window)
311         return NS_OK;
312
313     nsres = nsISupports_QueryInterface(script_iface, &IID_nsIDOMHTMLScriptElement, (void**)&nsscript);
314     if(NS_FAILED(nsres)) {
315         ERR("Could not get nsIDOMHTMLScriptElement: %08x\n", nsres);
316         return nsres;
317     }
318
319     if(parser_iface) {
320         nsres = nsISupports_QueryInterface(parser_iface, &IID_nsIParser, (void**)&nsparser);
321         if(NS_FAILED(nsres)) {
322             ERR("Could not get nsIParser iface: %08x\n", nsres);
323             nsparser = NULL;
324         }
325     }
326
327     hres = script_elem_from_nsscript(doc, nsscript, &script_elem);
328     nsIDOMHTMLScriptElement_Release(nsscript);
329     if(FAILED(hres))
330         return NS_ERROR_FAILURE;
331
332     if(nsparser) {
333         nsIParser_BeginEvaluatingParserInsertedScript(nsparser);
334         window->parser_callback_cnt++;
335     }
336
337     IHTMLWindow2_AddRef(&window->base.IHTMLWindow2_iface);
338
339     doc_insert_script(window, script_elem);
340
341     while(!list_empty(&window->script_queue)) {
342         iter = LIST_ENTRY(list_head(&window->script_queue), script_queue_entry_t, entry);
343         list_remove(&iter->entry);
344         doc_insert_script(window, iter->script);
345         IHTMLScriptElement_Release(&iter->script->IHTMLScriptElement_iface);
346         heap_free(iter);
347     }
348
349     IHTMLWindow2_Release(&window->base.IHTMLWindow2_iface);
350
351     if(nsparser) {
352         window->parser_callback_cnt--;
353         nsIParser_EndEvaluatingParserInsertedScript(nsparser);
354         nsIParser_Release(nsparser);
355     }
356
357     IHTMLScriptElement_Release(&script_elem->IHTMLScriptElement_iface);
358
359     return NS_OK;
360 }
361
362 typedef struct nsRunnable nsRunnable;
363
364 typedef nsresult (*runnable_proc_t)(HTMLDocumentNode*,nsISupports*,nsISupports*);
365
366 struct nsRunnable {
367     nsIRunnable  nsIRunnable_iface;
368
369     LONG ref;
370
371     runnable_proc_t proc;
372
373     HTMLDocumentNode *doc;
374     nsISupports *arg1;
375     nsISupports *arg2;
376 };
377
378 static inline nsRunnable *impl_from_nsIRunnable(nsIRunnable *iface)
379 {
380     return CONTAINING_RECORD(iface, nsRunnable, nsIRunnable_iface);
381 }
382
383 static nsresult NSAPI nsRunnable_QueryInterface(nsIRunnable *iface,
384         nsIIDRef riid, void **result)
385 {
386     nsRunnable *This = impl_from_nsIRunnable(iface);
387
388     if(IsEqualGUID(riid, &IID_nsISupports)) {
389         TRACE("(%p)->(IID_nsISupports %p)\n", This, result);
390         *result = &This->nsIRunnable_iface;
391     }else if(IsEqualGUID(riid, &IID_nsIRunnable)) {
392         TRACE("(%p)->(IID_nsIRunnable %p)\n", This, result);
393         *result = &This->nsIRunnable_iface;
394     }else {
395         *result = NULL;
396         WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), result);
397         return NS_NOINTERFACE;
398     }
399
400     nsISupports_AddRef((nsISupports*)*result);
401     return NS_OK;
402 }
403
404 static nsrefcnt NSAPI nsRunnable_AddRef(nsIRunnable *iface)
405 {
406     nsRunnable *This = impl_from_nsIRunnable(iface);
407     LONG ref = InterlockedIncrement(&This->ref);
408
409     TRACE("(%p) ref=%d\n", This, ref);
410
411     return ref;
412 }
413
414 static nsrefcnt NSAPI nsRunnable_Release(nsIRunnable *iface)
415 {
416     nsRunnable *This = impl_from_nsIRunnable(iface);
417     LONG ref = InterlockedDecrement(&This->ref);
418
419     TRACE("(%p) ref=%d\n", This, ref);
420
421     if(!ref) {
422         htmldoc_release(&This->doc->basedoc);
423         if(This->arg1)
424             nsISupports_Release(This->arg1);
425         if(This->arg2)
426             nsISupports_Release(This->arg2);
427         heap_free(This);
428     }
429
430     return ref;
431 }
432
433 static nsresult NSAPI nsRunnable_Run(nsIRunnable *iface)
434 {
435     nsRunnable *This = impl_from_nsIRunnable(iface);
436
437     return This->proc(This->doc, This->arg1, This->arg2);
438 }
439
440 static const nsIRunnableVtbl nsRunnableVtbl = {
441     nsRunnable_QueryInterface,
442     nsRunnable_AddRef,
443     nsRunnable_Release,
444     nsRunnable_Run
445 };
446
447 static void add_script_runner(HTMLDocumentNode *This, runnable_proc_t proc, nsISupports *arg1, nsISupports *arg2)
448 {
449     nsRunnable *runnable;
450
451     runnable = heap_alloc_zero(sizeof(*runnable));
452     if(!runnable)
453         return;
454
455     runnable->nsIRunnable_iface.lpVtbl = &nsRunnableVtbl;
456     runnable->ref = 1;
457
458     htmldoc_addref(&This->basedoc);
459     runnable->doc = This;
460     runnable->proc = proc;
461
462     if(arg1)
463         nsISupports_AddRef(arg1);
464     runnable->arg1 = arg1;
465
466     if(arg2)
467         nsISupports_AddRef(arg2);
468     runnable->arg2 = arg2;
469
470     nsIContentUtils_AddScriptRunner(content_utils, &runnable->nsIRunnable_iface);
471
472     nsIRunnable_Release(&runnable->nsIRunnable_iface);
473 }
474
475 static inline HTMLDocumentNode *impl_from_nsIDocumentObserver(nsIDocumentObserver *iface)
476 {
477     return CONTAINING_RECORD(iface, HTMLDocumentNode, nsIDocumentObserver_iface);
478 }
479
480 static nsresult NSAPI nsDocumentObserver_QueryInterface(nsIDocumentObserver *iface,
481         nsIIDRef riid, void **result)
482 {
483     HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
484
485     if(IsEqualGUID(&IID_nsISupports, riid)) {
486         TRACE("(%p)->(IID_nsISupports, %p)\n", This, result);
487         *result = &This->nsIDocumentObserver_iface;
488     }else if(IsEqualGUID(&IID_nsIMutationObserver, riid)) {
489         TRACE("(%p)->(IID_nsIMutationObserver %p)\n", This, result);
490         *result = &This->nsIDocumentObserver_iface;
491     }else if(IsEqualGUID(&IID_nsIDocumentObserver, riid)) {
492         TRACE("(%p)->(IID_nsIDocumentObserver %p)\n", This, result);
493         *result = &This->nsIDocumentObserver_iface;
494     }else {
495         *result = NULL;
496         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), result);
497         return NS_NOINTERFACE;
498     }
499
500     htmldoc_addref(&This->basedoc);
501     return NS_OK;
502 }
503
504 static nsrefcnt NSAPI nsDocumentObserver_AddRef(nsIDocumentObserver *iface)
505 {
506     HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
507     return htmldoc_addref(&This->basedoc);
508 }
509
510 static nsrefcnt NSAPI nsDocumentObserver_Release(nsIDocumentObserver *iface)
511 {
512     HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
513     return htmldoc_release(&This->basedoc);
514 }
515
516 static void NSAPI nsDocumentObserver_CharacterDataWillChange(nsIDocumentObserver *iface,
517         nsIDocument *aDocument, nsIContent *aContent, void /*CharacterDataChangeInfo*/ *aInfo)
518 {
519 }
520
521 static void NSAPI nsDocumentObserver_CharacterDataChanged(nsIDocumentObserver *iface,
522         nsIDocument *aDocument, nsIContent *aContent, void /*CharacterDataChangeInfo*/ *aInfo)
523 {
524 }
525
526 static void NSAPI nsDocumentObserver_AttributeWillChange(nsIDocumentObserver *iface, nsIDocument *aDocument,
527         nsIContent *aContent, PRInt32 aNameSpaceID, nsIAtom *aAttribute, PRInt32 aModType)
528 {
529 }
530
531 static void NSAPI nsDocumentObserver_AttributeChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
532         nsIContent *aContent, PRInt32 aNameSpaceID, nsIAtom *aAttribute, PRInt32 aModType)
533 {
534 }
535
536 static void NSAPI nsDocumentObserver_ContentAppended(nsIDocumentObserver *iface, nsIDocument *aDocument,
537         nsIContent *aContainer, nsIContent *aFirstNewContent, PRInt32 aNewIndexInContainer)
538 {
539 }
540
541 static void NSAPI nsDocumentObserver_ContentInserted(nsIDocumentObserver *iface, nsIDocument *aDocument,
542         nsIContent *aContainer, nsIContent *aChild, PRInt32 aIndexInContainer)
543 {
544 }
545
546 static void NSAPI nsDocumentObserver_ContentRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
547         nsIContent *aContainer, nsIContent *aChild, PRInt32 aIndexInContainer,
548         nsIContent *aProviousSibling)
549 {
550 }
551
552 static void NSAPI nsDocumentObserver_NodeWillBeDestroyed(nsIDocumentObserver *iface, const nsINode *aNode)
553 {
554 }
555
556 static void NSAPI nsDocumentObserver_ParentChainChanged(nsIDocumentObserver *iface, nsIContent *aContent)
557 {
558 }
559
560 static void NSAPI nsDocumentObserver_BeginUpdate(nsIDocumentObserver *iface, nsIDocument *aDocument,
561         nsUpdateType aUpdateType)
562 {
563 }
564
565 static void NSAPI nsDocumentObserver_EndUpdate(nsIDocumentObserver *iface, nsIDocument *aDocument,
566         nsUpdateType aUpdateType)
567 {
568 }
569
570 static void NSAPI nsDocumentObserver_BeginLoad(nsIDocumentObserver *iface, nsIDocument *aDocument)
571 {
572 }
573
574 static void NSAPI nsDocumentObserver_EndLoad(nsIDocumentObserver *iface, nsIDocument *aDocument)
575 {
576     HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
577
578     TRACE("(%p)\n", This);
579
580     if(This->skip_mutation_notif)
581         return;
582
583     This->content_ready = TRUE;
584     add_script_runner(This, run_end_load, NULL, NULL);
585 }
586
587 static void NSAPI nsDocumentObserver_ContentStatesChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
588         nsIContent *aContent, nsEventStates *aStateMask)
589 {
590 }
591
592 static void NSAPI nsDocumentObserver_DocumentStatesChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
593         nsEventStates *aStateMask)
594 {
595 }
596
597 static void NSAPI nsDocumentObserver_StyleSheetAdded(nsIDocumentObserver *iface, nsIDocument *aDocument,
598         nsIStyleSheet *aStyleSheet, cpp_bool aDocumentSheet)
599 {
600 }
601
602 static void NSAPI nsDocumentObserver_StyleSheetRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
603         nsIStyleSheet *aStyleSheet, cpp_bool aDocumentSheet)
604 {
605 }
606
607 static void NSAPI nsDocumentObserver_StyleSheetApplicableStateChanged(nsIDocumentObserver *iface,
608         nsIDocument *aDocument, nsIStyleSheet *aStyleSheet, cpp_bool aApplicable)
609 {
610 }
611
612 static void NSAPI nsDocumentObserver_StyleRuleChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
613         nsIStyleSheet *aStyleSheet, nsIStyleRule *aOldStyleRule, nsIStyleSheet *aNewStyleRule)
614 {
615 }
616
617 static void NSAPI nsDocumentObserver_StyleRuleAdded(nsIDocumentObserver *iface, nsIDocument *aDocument,
618         nsIStyleSheet *aStyleSheet, nsIStyleRule *aStyleRule)
619 {
620 }
621
622 static void NSAPI nsDocumentObserver_StyleRuleRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
623         nsIStyleSheet *aStyleSheet, nsIStyleRule *aStyleRule)
624 {
625 }
626
627 static void NSAPI nsDocumentObserver_BindToDocument(nsIDocumentObserver *iface, nsIDocument *aDocument,
628         nsIContent *aContent)
629 {
630     HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
631     nsIDOMHTMLIFrameElement *nsiframe;
632     nsIDOMHTMLFrameElement *nsframe;
633     nsIDOMHTMLScriptElement *nsscript;
634     nsIDOMComment *nscomment;
635     nsIDOMElement *nselem;
636     nsresult nsres;
637
638     TRACE("(%p)\n", This);
639
640     nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMElement, (void**)&nselem);
641     if(NS_SUCCEEDED(nsres)) {
642         check_event_attr(This, nselem);
643         nsIDOMElement_Release(nselem);
644     }
645
646     nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMComment, (void**)&nscomment);
647     if(NS_SUCCEEDED(nsres)) {
648         TRACE("comment node\n");
649
650         add_script_runner(This, run_insert_comment, (nsISupports*)nscomment, NULL);
651         nsIDOMComment_Release(nscomment);
652         return;
653     }
654
655     nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLIFrameElement, (void**)&nsiframe);
656     if(NS_SUCCEEDED(nsres)) {
657         TRACE("iframe node\n");
658
659         add_script_runner(This, run_bind_to_tree, (nsISupports*)nsiframe, NULL);
660         nsIDOMHTMLIFrameElement_Release(nsiframe);
661         return;
662     }
663
664     nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLFrameElement, (void**)&nsframe);
665     if(NS_SUCCEEDED(nsres)) {
666         TRACE("frame node\n");
667
668         add_script_runner(This, run_bind_to_tree, (nsISupports*)nsframe, NULL);
669         nsIDOMHTMLFrameElement_Release(nsframe);
670         return;
671     }
672
673     nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLScriptElement, (void**)&nsscript);
674     if(NS_SUCCEEDED(nsres)) {
675         HTMLScriptElement *script_elem;
676         HRESULT hres;
677
678         TRACE("script element\n");
679
680         hres = script_elem_from_nsscript(This, nsscript, &script_elem);
681         nsIDOMHTMLScriptElement_Release(nsscript);
682         if(FAILED(hres))
683             return;
684
685         if(script_elem->parse_on_bind)
686             add_script_runner(This, run_insert_script, (nsISupports*)nsscript, NULL);
687
688         IHTMLScriptElement_Release(&script_elem->IHTMLScriptElement_iface);
689     }
690 }
691
692 static void NSAPI nsDocumentObserver_AttemptToExecuteScript(nsIDocumentObserver *iface, nsIContent *aContent,
693         nsIParser *aParser, cpp_bool *aBlock)
694 {
695     HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
696     nsIDOMHTMLScriptElement *nsscript;
697     nsresult nsres;
698
699     TRACE("(%p)->(%p %p %p)\n", This, aContent, aParser, aBlock);
700
701     nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLScriptElement, (void**)&nsscript);
702     if(NS_SUCCEEDED(nsres)) {
703         TRACE("script node\n");
704
705         add_script_runner(This, run_insert_script, (nsISupports*)nsscript, (nsISupports*)aParser);
706         nsIDOMHTMLScriptElement_Release(nsscript);
707     }
708 }
709
710 static const nsIDocumentObserverVtbl nsDocumentObserverVtbl = {
711     nsDocumentObserver_QueryInterface,
712     nsDocumentObserver_AddRef,
713     nsDocumentObserver_Release,
714     nsDocumentObserver_CharacterDataWillChange,
715     nsDocumentObserver_CharacterDataChanged,
716     nsDocumentObserver_AttributeWillChange,
717     nsDocumentObserver_AttributeChanged,
718     nsDocumentObserver_ContentAppended,
719     nsDocumentObserver_ContentInserted,
720     nsDocumentObserver_ContentRemoved,
721     nsDocumentObserver_NodeWillBeDestroyed,
722     nsDocumentObserver_ParentChainChanged,
723     nsDocumentObserver_BeginUpdate,
724     nsDocumentObserver_EndUpdate,
725     nsDocumentObserver_BeginLoad,
726     nsDocumentObserver_EndLoad,
727     nsDocumentObserver_ContentStatesChanged,
728     nsDocumentObserver_DocumentStatesChanged,
729     nsDocumentObserver_StyleSheetAdded,
730     nsDocumentObserver_StyleSheetRemoved,
731     nsDocumentObserver_StyleSheetApplicableStateChanged,
732     nsDocumentObserver_StyleRuleChanged,
733     nsDocumentObserver_StyleRuleAdded,
734     nsDocumentObserver_StyleRuleRemoved,
735     nsDocumentObserver_BindToDocument,
736     nsDocumentObserver_AttemptToExecuteScript
737 };
738
739 void init_document_mutation(HTMLDocumentNode *doc)
740 {
741     nsIDocument *nsdoc;
742     nsresult nsres;
743
744     doc->nsIDocumentObserver_iface.lpVtbl = &nsDocumentObserverVtbl;
745
746     nsres = nsIDOMHTMLDocument_QueryInterface(doc->nsdoc, &IID_nsIDocument, (void**)&nsdoc);
747     if(NS_FAILED(nsres)) {
748         ERR("Could not get nsIDocument: %08x\n", nsres);
749         return;
750     }
751
752     nsIContentUtils_AddDocumentObserver(content_utils, nsdoc, &doc->nsIDocumentObserver_iface);
753     nsIDocument_Release(nsdoc);
754 }
755
756 void release_document_mutation(HTMLDocumentNode *doc)
757 {
758     nsIDocument *nsdoc;
759     nsresult nsres;
760
761     nsres = nsIDOMHTMLDocument_QueryInterface(doc->nsdoc, &IID_nsIDocument, (void**)&nsdoc);
762     if(NS_FAILED(nsres)) {
763         ERR("Could not get nsIDocument: %08x\n", nsres);
764         return;
765     }
766
767     nsIContentUtils_RemoveDocumentObserver(content_utils, nsdoc, &doc->nsIDocumentObserver_iface);
768     nsIDocument_Release(nsdoc);
769 }
770
771 void init_mutation(nsIComponentManager *component_manager)
772 {
773     nsIFactory *factory;
774     nsresult nsres;
775
776     if(!component_manager) {
777         if(content_utils) {
778             nsIContentUtils_Release(content_utils);
779             content_utils = NULL;
780         }
781         return;
782     }
783
784     nsres = nsIComponentManager_GetClassObject(component_manager, &NS_ICONTENTUTILS_CID,
785             &IID_nsIFactory, (void**)&factory);
786     if(NS_FAILED(nsres)) {
787         ERR("Could not create nsIContentUtils service: %08x\n", nsres);
788         return;
789     }
790
791     nsres = nsIFactory_CreateInstance(factory, NULL, &IID_nsIContentUtils, (void**)&content_utils);
792     nsIFactory_Release(factory);
793     if(NS_FAILED(nsres))
794         ERR("Could not create nsIContentUtils instance: %08x\n", nsres);
795 }