comctl32/listview: Free ID array when removing all items.
[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
31 #include "mshtml_private.h"
32 #include "htmlevent.h"
33
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
37
38 enum {
39     MUTATION_COMMENT,
40     MUTATION_IFRAME,
41     MUTATION_SCRIPT
42 };
43
44 #define IE_MAJOR_VERSION 7
45 #define IE_MINOR_VERSION 0
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_Init(&nsstr, buf);
170     heap_free(buf);
171
172     /* FIXME: Find better way to insert HTML to document. */
173     nsres = nsIDOMHTMLDocument_Write(doc->nsdoc, &nsstr);
174     nsAString_Finish(&nsstr);
175     if(NS_FAILED(nsres)) {
176         ERR("Write failed: %08x\n", nsres);
177         return FALSE;
178     }
179
180     return TRUE;
181 }
182
183 static void add_script_runner(HTMLDocumentNode *This)
184 {
185     nsIDOMNSDocument *nsdoc;
186     nsresult nsres;
187
188     nsres = nsIDOMHTMLDocument_QueryInterface(This->nsdoc, &IID_nsIDOMNSDocument, (void**)&nsdoc);
189     if(NS_FAILED(nsres)) {
190         ERR("Could not get nsIDOMNSDocument: %08x\n", nsres);
191         return;
192     }
193
194     nsIDOMNSDocument_WineAddScriptRunner(nsdoc, NSRUNNABLE(This));
195     nsIDOMNSDocument_Release(nsdoc);
196 }
197
198 #define NSRUNNABLE_THIS(iface) DEFINE_THIS(HTMLDocumentNode, IRunnable, iface)
199
200 static nsresult NSAPI nsRunnable_QueryInterface(nsIRunnable *iface,
201         nsIIDRef riid, nsQIResult result)
202 {
203     HTMLDocumentNode *This = NSRUNNABLE_THIS(iface);
204
205     if(IsEqualGUID(riid, &IID_nsISupports)) {
206         TRACE("(%p)->(IID_nsISupports %p)\n", This, result);
207         *result = NSRUNNABLE(This);
208     }else if(IsEqualGUID(riid, &IID_nsIRunnable)) {
209         TRACE("(%p)->(IID_nsIRunnable %p)\n", This, result);
210         *result = NSRUNNABLE(This);
211     }else {
212         *result = NULL;
213         WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), result);
214         return NS_NOINTERFACE;
215     }
216
217     nsISupports_AddRef((nsISupports*)*result);
218     return NS_OK;
219 }
220
221 static nsrefcnt NSAPI nsRunnable_AddRef(nsIRunnable *iface)
222 {
223     HTMLDocumentNode *This = NSRUNNABLE_THIS(iface);
224     return htmldoc_addref(&This->basedoc);
225 }
226
227 static nsrefcnt NSAPI nsRunnable_Release(nsIRunnable *iface)
228 {
229     HTMLDocumentNode *This = NSRUNNABLE_THIS(iface);
230     return htmldoc_release(&This->basedoc);
231 }
232
233 static void pop_mutation_queue(HTMLDocumentNode *doc)
234 {
235     mutation_queue_t *tmp = doc->mutation_queue;
236
237     if(!tmp)
238         return;
239
240     doc->mutation_queue = tmp->next;
241     if(!tmp->next)
242         doc->mutation_queue_tail = NULL;
243
244     nsISupports_Release(tmp->nsiface);
245     heap_free(tmp);
246 }
247
248 static nsresult init_iframe_window(HTMLDocumentNode *doc, nsISupports *nsunk)
249 {
250     nsIDOMHTMLIFrameElement *nsiframe;
251     nsIDOMWindow *nswindow;
252     nsIDOMDocument *nsdoc;
253     nsresult nsres;
254
255     nsres = nsISupports_QueryInterface(nsunk, &IID_nsIDOMHTMLIFrameElement, (void**)&nsiframe);
256     if(NS_FAILED(nsres)) {
257         ERR("Could not get nsIDOMHTMLIFrameElement: %08x\n", nsres);
258         return nsres;
259     }
260
261     nsres = nsIDOMHTMLIFrameElement_GetContentDocument(nsiframe, &nsdoc);
262     nsIDOMHTMLIFrameElement_Release(nsiframe);
263     if(NS_FAILED(nsres) || !nsdoc) {
264         ERR("GetContentDocument failed: %08x\n", nsres);
265         return nsres;
266     }
267
268     nswindow = get_nsdoc_window(nsdoc);
269     nsIDOMDocument_Release(nsdoc);
270     if(!nswindow)
271         return NS_ERROR_FAILURE;
272
273     if(!nswindow_to_window(nswindow)) {
274         HTMLWindow *window;
275         HRESULT hres;
276
277         hres = HTMLWindow_Create(doc->basedoc.doc_obj, nswindow, doc->basedoc.window, &window);
278         if(SUCCEEDED(hres))
279             IHTMLWindow2_Release(HTMLWINDOW2(window));
280     }
281
282     nsIDOMWindow_Release(nswindow);
283     return NS_OK;
284 }
285
286 static nsresult NSAPI nsRunnable_Run(nsIRunnable *iface)
287 {
288     HTMLDocumentNode *This = NSRUNNABLE_THIS(iface);
289     nsresult nsres;
290
291     TRACE("(%p)\n", This);
292
293     while(This->mutation_queue) {
294         switch(This->mutation_queue->type) {
295         case MUTATION_COMMENT: {
296             nsIDOMComment *nscomment;
297             nsAString comment_str;
298             BOOL remove_comment = FALSE;
299
300             nsres = nsISupports_QueryInterface(This->mutation_queue->nsiface, &IID_nsIDOMComment, (void**)&nscomment);
301             if(NS_FAILED(nsres)) {
302                 ERR("Could not get nsIDOMComment iface:%08x\n", nsres);
303                 return NS_OK;
304             }
305
306             nsAString_Init(&comment_str, NULL);
307             nsres = nsIDOMComment_GetData(nscomment, &comment_str);
308             if(NS_SUCCEEDED(nsres)) {
309                 const PRUnichar *comment;
310
311                 nsAString_GetData(&comment_str, &comment);
312                 remove_comment = handle_insert_comment(This, comment);
313             }
314
315             nsAString_Finish(&comment_str);
316
317             if(remove_comment) {
318                 nsIDOMNode *nsparent, *tmp;
319                 nsAString magic_str;
320
321                 static const PRUnichar remove_comment_magicW[] =
322                     {'#','!','w','i','n','e', 'r','e','m','o','v','e','!','#',0};
323
324                 nsAString_Init(&magic_str, remove_comment_magicW);
325                 nsres = nsIDOMComment_SetData(nscomment, &magic_str);
326                 nsAString_Finish(&magic_str);
327                 if(NS_FAILED(nsres))
328                     ERR("SetData failed: %08x\n", nsres);
329
330                 nsIDOMComment_GetParentNode(nscomment, &nsparent);
331                 if(nsparent) {
332                     nsIDOMNode_RemoveChild(nsparent, (nsIDOMNode*)nscomment, &tmp);
333                     nsIDOMNode_Release(nsparent);
334                     nsIDOMNode_Release(tmp);
335                 }
336             }
337
338             nsIDOMComment_Release(nscomment);
339             break;
340         }
341
342         case MUTATION_IFRAME:
343             return init_iframe_window(This, This->mutation_queue->nsiface);
344
345         case MUTATION_SCRIPT: {
346             nsIDOMHTMLScriptElement *nsscript;
347
348             nsres = nsISupports_QueryInterface(This->mutation_queue->nsiface, &IID_nsIDOMHTMLScriptElement,
349                                                (void**)&nsscript);
350             if(NS_FAILED(nsres)) {
351                 ERR("Could not get nsIDOMHTMLScriptElement: %08x\n", nsres);
352                 break;
353             }
354
355             doc_insert_script(This->basedoc.window, nsscript);
356             nsIDOMHTMLScriptElement_Release(nsscript);
357             break;
358         }
359
360         default:
361             ERR("invalid type %d\n", This->mutation_queue->type);
362         }
363
364         pop_mutation_queue(This);
365     }
366
367     return S_OK;
368 }
369
370 #undef NSRUNNABLE_THIS
371
372 static const nsIRunnableVtbl nsRunnableVtbl = {
373     nsRunnable_QueryInterface,
374     nsRunnable_AddRef,
375     nsRunnable_Release,
376     nsRunnable_Run
377 };
378
379 #define NSDOCOBS_THIS(iface) DEFINE_THIS(HTMLDocumentNode, IDocumentObserver, iface)
380
381 static nsresult NSAPI nsDocumentObserver_QueryInterface(nsIDocumentObserver *iface,
382         nsIIDRef riid, nsQIResult result)
383 {
384     HTMLDocumentNode *This = NSDOCOBS_THIS(iface);
385
386     if(IsEqualGUID(&IID_nsISupports, riid)) {
387         TRACE("(%p)->(IID_nsISupports, %p)\n", This, result);
388         *result = NSDOCOBS(This);
389     }else if(IsEqualGUID(&IID_nsIMutationObserver, riid)) {
390         TRACE("(%p)->(IID_nsIMutationObserver %p)\n", This, result);
391         *result = NSDOCOBS(This);
392     }else if(IsEqualGUID(&IID_nsIDocumentObserver, riid)) {
393         TRACE("(%p)->(IID_nsIDocumentObserver %p)\n", This, result);
394         *result = NSDOCOBS(This);
395     }else {
396         *result = NULL;
397         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), result);
398         return NS_NOINTERFACE;
399     }
400
401     htmldoc_addref(&This->basedoc);
402     return NS_OK;
403 }
404
405 static nsrefcnt NSAPI nsDocumentObserver_AddRef(nsIDocumentObserver *iface)
406 {
407     HTMLDocumentNode *This = NSDOCOBS_THIS(iface);
408     return htmldoc_addref(&This->basedoc);
409 }
410
411 static nsrefcnt NSAPI nsDocumentObserver_Release(nsIDocumentObserver *iface)
412 {
413     HTMLDocumentNode *This = NSDOCOBS_THIS(iface);
414     return htmldoc_release(&This->basedoc);
415 }
416
417 static void NSAPI nsDocumentObserver_CharacterDataWillChange(nsIDocumentObserver *iface,
418         nsIDocument *aDocument, nsIContent *aContent, void /*CharacterDataChangeInfo*/ *aInfo)
419 {
420 }
421
422 static void NSAPI nsDocumentObserver_CharacterDataChanged(nsIDocumentObserver *iface,
423         nsIDocument *aDocument, nsIContent *aContent, void /*CharacterDataChangeInfo*/ *aInfo)
424 {
425 }
426
427 static void NSAPI nsDocumentObserver_AttributeWillChange(nsIDocumentObserver *iface, nsIDocument *aDocument,
428         nsIContent *aContent, PRInt32 aNameSpaceID, nsIAtom *aAttribute, PRInt32 aModType)
429 {
430 }
431
432 static void NSAPI nsDocumentObserver_AttributeChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
433         nsIContent *aContent, PRInt32 aNameSpaceID, nsIAtom *aAttribute, PRInt32 aModType, PRUint32 aStateMask)
434 {
435 }
436
437 static void NSAPI nsDocumentObserver_ContentAppended(nsIDocumentObserver *iface, nsIDocument *aDocument,
438         nsIContent *aContainer, PRInt32 aNewIndexInContainer)
439 {
440 }
441
442 static void NSAPI nsDocumentObserver_ContentInserted(nsIDocumentObserver *iface, nsIDocument *aDocument,
443         nsIContent *aContainer, nsIContent *aChild, PRInt32 aIndexInContainer)
444 {
445 }
446
447 static void NSAPI nsDocumentObserver_ContentRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
448         nsIContent *aContainer, nsIContent *aChild, PRInt32 aIndexInContainer)
449 {
450 }
451
452 static void NSAPI nsDocumentObserver_NodeWillBeDestroyed(nsIDocumentObserver *iface, const nsINode *aNode)
453 {
454 }
455
456 static void NSAPI nsDocumentObserver_ParentChainChanged(nsIDocumentObserver *iface, nsIContent *aContent)
457 {
458 }
459
460 static void NSAPI nsDocumentObserver_BeginUpdate(nsIDocumentObserver *iface, nsIDocument *aDocument,
461         nsUpdateType aUpdateType)
462 {
463 }
464
465 static void NSAPI nsDocumentObserver_EndUpdate(nsIDocumentObserver *iface, nsIDocument *aDocument,
466         nsUpdateType aUpdateType)
467 {
468 }
469
470 static void NSAPI nsDocumentObserver_BeginLoad(nsIDocumentObserver *iface, nsIDocument *aDocument)
471 {
472 }
473
474 static void NSAPI nsDocumentObserver_EndLoad(nsIDocumentObserver *iface, nsIDocument *aDocument)
475 {
476     HTMLDocumentNode *This = NSDOCOBS_THIS(iface);
477
478     TRACE("\n");
479
480     This->content_ready = TRUE;
481
482     if(This == This->basedoc.doc_obj->basedoc.doc_node) {
483         task_t *task;
484         task = heap_alloc(sizeof(task_t));
485
486         task->doc = &This->basedoc.doc_obj->basedoc;
487         task->task_id = TASK_PARSECOMPLETE;
488         task->next = NULL;
489
490         /*
491          * This should be done in the worker thread that parses HTML,
492          * but we don't have such thread (Gecko parses HTML for us).
493          */
494         push_task(task);
495     }
496 }
497
498 static void NSAPI nsDocumentObserver_ContentStatesChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
499         nsIContent *aContent1, nsIContent *aContent2, PRInt32 aStateMask)
500 {
501 }
502
503 static void NSAPI nsDocumentObserver_StyleSheetAdded(nsIDocumentObserver *iface, nsIDocument *aDocument,
504         nsIStyleSheet *aStyleSheet, PRBool aDocumentSheet)
505 {
506 }
507
508 static void NSAPI nsDocumentObserver_StyleSheetRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
509         nsIStyleSheet *aStyleSheet, PRBool aDocumentSheet)
510 {
511 }
512
513 static void NSAPI nsDocumentObserver_StyleSheetApplicableStateChanged(nsIDocumentObserver *iface,
514         nsIDocument *aDocument, nsIStyleSheet *aStyleSheet, PRBool aApplicable)
515 {
516 }
517
518 static void NSAPI nsDocumentObserver_StyleRuleChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
519         nsIStyleSheet *aStyleSheet, nsIStyleRule *aOldStyleRule, nsIStyleSheet *aNewStyleRule)
520 {
521 }
522
523 static void NSAPI nsDocumentObserver_StyleRuleAdded(nsIDocumentObserver *iface, nsIDocument *aDocument,
524         nsIStyleSheet *aStyleSheet, nsIStyleRule *aStyleRule)
525 {
526 }
527
528 static void NSAPI nsDocumentObserver_StyleRuleRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
529         nsIStyleSheet *aStyleSheet, nsIStyleRule *aStyleRule)
530 {
531 }
532
533 static void push_mutation_queue(HTMLDocumentNode *doc, DWORD type, nsISupports *nsiface)
534 {
535     mutation_queue_t *elem;
536
537     elem = heap_alloc(sizeof(mutation_queue_t));
538     if(!elem)
539         return;
540
541     elem->next = NULL;
542     elem->type = type;
543     elem->nsiface = nsiface;
544     nsISupports_AddRef(nsiface);
545
546     if(doc->mutation_queue_tail)
547         doc->mutation_queue_tail = doc->mutation_queue_tail->next = elem;
548     else
549         doc->mutation_queue = doc->mutation_queue_tail = elem;
550 }
551
552 static void NSAPI nsDocumentObserver_BindToDocument(nsIDocumentObserver *iface, nsIDocument *aDocument,
553         nsIContent *aContent)
554 {
555     HTMLDocumentNode *This = NSDOCOBS_THIS(iface);
556     nsIDOMHTMLIFrameElement *nsiframe;
557     nsIDOMComment *nscomment;
558     nsIDOMElement *nselem;
559     nsresult nsres;
560
561     TRACE("(%p)\n", This);
562
563     nsres = nsISupports_QueryInterface(aContent, &IID_nsIDOMElement, (void**)&nselem);
564     if(NS_SUCCEEDED(nsres)) {
565         check_event_attr(This, nselem);
566         nsIDOMElement_Release(nselem);
567     }
568
569     nsres = nsISupports_QueryInterface(aContent, &IID_nsIDOMComment, (void**)&nscomment);
570     if(NS_SUCCEEDED(nsres)) {
571         TRACE("comment node\n");
572
573         push_mutation_queue(This, MUTATION_COMMENT, (nsISupports*)nscomment);
574         nsIDOMComment_Release(nscomment);
575         add_script_runner(This);
576     }
577
578     nsres = nsISupports_QueryInterface(aContent, &IID_nsIDOMHTMLIFrameElement, (void**)&nsiframe);
579     if(NS_SUCCEEDED(nsres)) {
580         push_mutation_queue(This, MUTATION_IFRAME, (nsISupports*)nsiframe);
581         nsIDOMHTMLIFrameElement_Release(nsiframe);
582         add_script_runner(This);
583     }
584 }
585
586 static void NSAPI nsDocumentObserver_DoneAddingChildren(nsIDocumentObserver *iface, nsIContent *aContent,
587         PRBool aHaveNotified)
588 {
589     HTMLDocumentNode *This = NSDOCOBS_THIS(iface);
590     nsIDOMHTMLScriptElement *nsscript;
591     nsresult nsres;
592
593     TRACE("(%p)->(%p %x)\n", This, aContent, aHaveNotified);
594
595     nsres = nsISupports_QueryInterface(aContent, &IID_nsIDOMHTMLScriptElement, (void**)&nsscript);
596     if(NS_SUCCEEDED(nsres)) {
597         push_mutation_queue(This, MUTATION_SCRIPT, (nsISupports*)nsscript);
598         nsIDOMHTMLScriptElement_Release(nsscript);
599         add_script_runner(This);
600     }
601 }
602
603 #undef NSMUTATIONOBS_THIS
604
605 static const nsIDocumentObserverVtbl nsDocumentObserverVtbl = {
606     nsDocumentObserver_QueryInterface,
607     nsDocumentObserver_AddRef,
608     nsDocumentObserver_Release,
609     nsDocumentObserver_CharacterDataWillChange,
610     nsDocumentObserver_CharacterDataChanged,
611     nsDocumentObserver_AttributeWillChange,
612     nsDocumentObserver_AttributeChanged,
613     nsDocumentObserver_ContentAppended,
614     nsDocumentObserver_ContentInserted,
615     nsDocumentObserver_ContentRemoved,
616     nsDocumentObserver_NodeWillBeDestroyed,
617     nsDocumentObserver_ParentChainChanged,
618     nsDocumentObserver_BeginUpdate,
619     nsDocumentObserver_EndUpdate,
620     nsDocumentObserver_BeginLoad,
621     nsDocumentObserver_EndLoad,
622     nsDocumentObserver_ContentStatesChanged,
623     nsDocumentObserver_StyleSheetAdded,
624     nsDocumentObserver_StyleSheetRemoved,
625     nsDocumentObserver_StyleSheetApplicableStateChanged,
626     nsDocumentObserver_StyleRuleChanged,
627     nsDocumentObserver_StyleRuleAdded,
628     nsDocumentObserver_StyleRuleRemoved,
629     nsDocumentObserver_BindToDocument,
630     nsDocumentObserver_DoneAddingChildren
631 };
632
633 void init_mutation(HTMLDocumentNode *doc)
634 {
635     nsIDOMNSDocument *nsdoc;
636     nsresult nsres;
637
638     doc->lpIDocumentObserverVtbl  = &nsDocumentObserverVtbl;
639     doc->lpIRunnableVtbl          = &nsRunnableVtbl;
640
641     nsres = nsIDOMHTMLDocument_QueryInterface(doc->nsdoc, &IID_nsIDOMNSDocument, (void**)&nsdoc);
642     if(NS_FAILED(nsres)) {
643         ERR("Could not get nsIDOMNSDocument: %08x\n", nsres);
644         return;
645     }
646
647     nsIDOMNSDocument_WineAddObserver(nsdoc, NSDOCOBS(doc));
648     nsIDOMNSDocument_Release(nsdoc);
649 }
650
651 void release_mutation(HTMLDocumentNode *doc)
652 {
653     nsIDOMNSDocument *nsdoc;
654     nsresult nsres;
655
656     nsres = nsIDOMHTMLDocument_QueryInterface(doc->nsdoc, &IID_nsIDOMNSDocument, (void**)&nsdoc);
657     if(NS_FAILED(nsres)) {
658         ERR("Could not get nsIDOMNSDocument: %08x\n", nsres);
659         return;
660     }
661
662     nsIDOMNSDocument_WineRemoveObserver(nsdoc, NSDOCOBS(doc));
663     nsIDOMNSDocument_Release(nsdoc);
664 }