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