kernel32: Add a structure to store all the information about an executable.
[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             init_iframe_window(This, This->mutation_queue->nsiface);
344             break;
345
346         case MUTATION_SCRIPT: {
347             nsIDOMHTMLScriptElement *nsscript;
348
349             nsres = nsISupports_QueryInterface(This->mutation_queue->nsiface, &IID_nsIDOMHTMLScriptElement,
350                                                (void**)&nsscript);
351             if(NS_FAILED(nsres)) {
352                 ERR("Could not get nsIDOMHTMLScriptElement: %08x\n", nsres);
353                 break;
354             }
355
356             doc_insert_script(This->basedoc.window, nsscript);
357             nsIDOMHTMLScriptElement_Release(nsscript);
358             break;
359         }
360
361         default:
362             ERR("invalid type %d\n", This->mutation_queue->type);
363         }
364
365         pop_mutation_queue(This);
366     }
367
368     return S_OK;
369 }
370
371 #undef NSRUNNABLE_THIS
372
373 static const nsIRunnableVtbl nsRunnableVtbl = {
374     nsRunnable_QueryInterface,
375     nsRunnable_AddRef,
376     nsRunnable_Release,
377     nsRunnable_Run
378 };
379
380 #define NSDOCOBS_THIS(iface) DEFINE_THIS(HTMLDocumentNode, IDocumentObserver, iface)
381
382 static nsresult NSAPI nsDocumentObserver_QueryInterface(nsIDocumentObserver *iface,
383         nsIIDRef riid, nsQIResult result)
384 {
385     HTMLDocumentNode *This = NSDOCOBS_THIS(iface);
386
387     if(IsEqualGUID(&IID_nsISupports, riid)) {
388         TRACE("(%p)->(IID_nsISupports, %p)\n", This, result);
389         *result = NSDOCOBS(This);
390     }else if(IsEqualGUID(&IID_nsIMutationObserver, riid)) {
391         TRACE("(%p)->(IID_nsIMutationObserver %p)\n", This, result);
392         *result = NSDOCOBS(This);
393     }else if(IsEqualGUID(&IID_nsIDocumentObserver, riid)) {
394         TRACE("(%p)->(IID_nsIDocumentObserver %p)\n", This, result);
395         *result = NSDOCOBS(This);
396     }else {
397         *result = NULL;
398         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), result);
399         return NS_NOINTERFACE;
400     }
401
402     htmldoc_addref(&This->basedoc);
403     return NS_OK;
404 }
405
406 static nsrefcnt NSAPI nsDocumentObserver_AddRef(nsIDocumentObserver *iface)
407 {
408     HTMLDocumentNode *This = NSDOCOBS_THIS(iface);
409     return htmldoc_addref(&This->basedoc);
410 }
411
412 static nsrefcnt NSAPI nsDocumentObserver_Release(nsIDocumentObserver *iface)
413 {
414     HTMLDocumentNode *This = NSDOCOBS_THIS(iface);
415     return htmldoc_release(&This->basedoc);
416 }
417
418 static void NSAPI nsDocumentObserver_CharacterDataWillChange(nsIDocumentObserver *iface,
419         nsIDocument *aDocument, nsIContent *aContent, void /*CharacterDataChangeInfo*/ *aInfo)
420 {
421 }
422
423 static void NSAPI nsDocumentObserver_CharacterDataChanged(nsIDocumentObserver *iface,
424         nsIDocument *aDocument, nsIContent *aContent, void /*CharacterDataChangeInfo*/ *aInfo)
425 {
426 }
427
428 static void NSAPI nsDocumentObserver_AttributeWillChange(nsIDocumentObserver *iface, nsIDocument *aDocument,
429         nsIContent *aContent, PRInt32 aNameSpaceID, nsIAtom *aAttribute, PRInt32 aModType)
430 {
431 }
432
433 static void NSAPI nsDocumentObserver_AttributeChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
434         nsIContent *aContent, PRInt32 aNameSpaceID, nsIAtom *aAttribute, PRInt32 aModType, PRUint32 aStateMask)
435 {
436 }
437
438 static void NSAPI nsDocumentObserver_ContentAppended(nsIDocumentObserver *iface, nsIDocument *aDocument,
439         nsIContent *aContainer, PRInt32 aNewIndexInContainer)
440 {
441 }
442
443 static void NSAPI nsDocumentObserver_ContentInserted(nsIDocumentObserver *iface, nsIDocument *aDocument,
444         nsIContent *aContainer, nsIContent *aChild, PRInt32 aIndexInContainer)
445 {
446 }
447
448 static void NSAPI nsDocumentObserver_ContentRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
449         nsIContent *aContainer, nsIContent *aChild, PRInt32 aIndexInContainer)
450 {
451 }
452
453 static void NSAPI nsDocumentObserver_NodeWillBeDestroyed(nsIDocumentObserver *iface, const nsINode *aNode)
454 {
455 }
456
457 static void NSAPI nsDocumentObserver_ParentChainChanged(nsIDocumentObserver *iface, nsIContent *aContent)
458 {
459 }
460
461 static void NSAPI nsDocumentObserver_BeginUpdate(nsIDocumentObserver *iface, nsIDocument *aDocument,
462         nsUpdateType aUpdateType)
463 {
464 }
465
466 static void NSAPI nsDocumentObserver_EndUpdate(nsIDocumentObserver *iface, nsIDocument *aDocument,
467         nsUpdateType aUpdateType)
468 {
469 }
470
471 static void NSAPI nsDocumentObserver_BeginLoad(nsIDocumentObserver *iface, nsIDocument *aDocument)
472 {
473 }
474
475 static void parse_complete_proc(task_t *_task)
476 {
477     docobj_task_t *task = (docobj_task_t*)_task;
478     parse_complete(task->doc);
479 }
480
481 static void NSAPI nsDocumentObserver_EndLoad(nsIDocumentObserver *iface, nsIDocument *aDocument)
482 {
483     HTMLDocumentNode *This = NSDOCOBS_THIS(iface);
484
485     TRACE("\n");
486
487     This->content_ready = TRUE;
488
489     if(This == This->basedoc.doc_obj->basedoc.doc_node) {
490         docobj_task_t *task;
491
492         task = heap_alloc(sizeof(docobj_task_t));
493         if(!task)
494             return;
495
496         task->doc = This->basedoc.doc_obj;
497
498         /*
499          * This should be done in the worker thread that parses HTML,
500          * but we don't have such thread (Gecko parses HTML for us).
501          */
502         push_task(&task->header, &parse_complete_proc, This->basedoc.doc_obj->basedoc.task_magic);
503     }
504 }
505
506 static void NSAPI nsDocumentObserver_ContentStatesChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
507         nsIContent *aContent1, nsIContent *aContent2, PRInt32 aStateMask)
508 {
509 }
510
511 static void NSAPI nsDocumentObserver_StyleSheetAdded(nsIDocumentObserver *iface, nsIDocument *aDocument,
512         nsIStyleSheet *aStyleSheet, PRBool aDocumentSheet)
513 {
514 }
515
516 static void NSAPI nsDocumentObserver_StyleSheetRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
517         nsIStyleSheet *aStyleSheet, PRBool aDocumentSheet)
518 {
519 }
520
521 static void NSAPI nsDocumentObserver_StyleSheetApplicableStateChanged(nsIDocumentObserver *iface,
522         nsIDocument *aDocument, nsIStyleSheet *aStyleSheet, PRBool aApplicable)
523 {
524 }
525
526 static void NSAPI nsDocumentObserver_StyleRuleChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
527         nsIStyleSheet *aStyleSheet, nsIStyleRule *aOldStyleRule, nsIStyleSheet *aNewStyleRule)
528 {
529 }
530
531 static void NSAPI nsDocumentObserver_StyleRuleAdded(nsIDocumentObserver *iface, nsIDocument *aDocument,
532         nsIStyleSheet *aStyleSheet, nsIStyleRule *aStyleRule)
533 {
534 }
535
536 static void NSAPI nsDocumentObserver_StyleRuleRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
537         nsIStyleSheet *aStyleSheet, nsIStyleRule *aStyleRule)
538 {
539 }
540
541 static void push_mutation_queue(HTMLDocumentNode *doc, DWORD type, nsISupports *nsiface)
542 {
543     mutation_queue_t *elem;
544
545     elem = heap_alloc(sizeof(mutation_queue_t));
546     if(!elem)
547         return;
548
549     elem->next = NULL;
550     elem->type = type;
551     elem->nsiface = nsiface;
552     nsISupports_AddRef(nsiface);
553
554     if(doc->mutation_queue_tail)
555         doc->mutation_queue_tail = doc->mutation_queue_tail->next = elem;
556     else
557         doc->mutation_queue = doc->mutation_queue_tail = elem;
558 }
559
560 static void NSAPI nsDocumentObserver_BindToDocument(nsIDocumentObserver *iface, nsIDocument *aDocument,
561         nsIContent *aContent)
562 {
563     HTMLDocumentNode *This = NSDOCOBS_THIS(iface);
564     nsIDOMHTMLIFrameElement *nsiframe;
565     nsIDOMComment *nscomment;
566     nsIDOMElement *nselem;
567     nsresult nsres;
568
569     TRACE("(%p)\n", This);
570
571     nsres = nsISupports_QueryInterface(aContent, &IID_nsIDOMElement, (void**)&nselem);
572     if(NS_SUCCEEDED(nsres)) {
573         check_event_attr(This, nselem);
574         nsIDOMElement_Release(nselem);
575     }
576
577     nsres = nsISupports_QueryInterface(aContent, &IID_nsIDOMComment, (void**)&nscomment);
578     if(NS_SUCCEEDED(nsres)) {
579         TRACE("comment node\n");
580
581         push_mutation_queue(This, MUTATION_COMMENT, (nsISupports*)nscomment);
582         nsIDOMComment_Release(nscomment);
583         add_script_runner(This);
584     }
585
586     nsres = nsISupports_QueryInterface(aContent, &IID_nsIDOMHTMLIFrameElement, (void**)&nsiframe);
587     if(NS_SUCCEEDED(nsres)) {
588         TRACE("iframe node\n");
589
590         push_mutation_queue(This, MUTATION_IFRAME, (nsISupports*)nsiframe);
591         nsIDOMHTMLIFrameElement_Release(nsiframe);
592         add_script_runner(This);
593     }
594 }
595
596 static void NSAPI nsDocumentObserver_DoneAddingChildren(nsIDocumentObserver *iface, nsIContent *aContent,
597         PRBool aHaveNotified)
598 {
599     HTMLDocumentNode *This = NSDOCOBS_THIS(iface);
600     nsIDOMHTMLScriptElement *nsscript;
601     nsresult nsres;
602
603     TRACE("(%p)->(%p %x)\n", This, aContent, aHaveNotified);
604
605     nsres = nsISupports_QueryInterface(aContent, &IID_nsIDOMHTMLScriptElement, (void**)&nsscript);
606     if(NS_SUCCEEDED(nsres)) {
607         TRACE("script node\n");
608
609         push_mutation_queue(This, MUTATION_SCRIPT, (nsISupports*)nsscript);
610         nsIDOMHTMLScriptElement_Release(nsscript);
611         add_script_runner(This);
612     }
613 }
614
615 #undef NSMUTATIONOBS_THIS
616
617 static const nsIDocumentObserverVtbl nsDocumentObserverVtbl = {
618     nsDocumentObserver_QueryInterface,
619     nsDocumentObserver_AddRef,
620     nsDocumentObserver_Release,
621     nsDocumentObserver_CharacterDataWillChange,
622     nsDocumentObserver_CharacterDataChanged,
623     nsDocumentObserver_AttributeWillChange,
624     nsDocumentObserver_AttributeChanged,
625     nsDocumentObserver_ContentAppended,
626     nsDocumentObserver_ContentInserted,
627     nsDocumentObserver_ContentRemoved,
628     nsDocumentObserver_NodeWillBeDestroyed,
629     nsDocumentObserver_ParentChainChanged,
630     nsDocumentObserver_BeginUpdate,
631     nsDocumentObserver_EndUpdate,
632     nsDocumentObserver_BeginLoad,
633     nsDocumentObserver_EndLoad,
634     nsDocumentObserver_ContentStatesChanged,
635     nsDocumentObserver_StyleSheetAdded,
636     nsDocumentObserver_StyleSheetRemoved,
637     nsDocumentObserver_StyleSheetApplicableStateChanged,
638     nsDocumentObserver_StyleRuleChanged,
639     nsDocumentObserver_StyleRuleAdded,
640     nsDocumentObserver_StyleRuleRemoved,
641     nsDocumentObserver_BindToDocument,
642     nsDocumentObserver_DoneAddingChildren
643 };
644
645 void init_mutation(HTMLDocumentNode *doc)
646 {
647     nsIDOMNSDocument *nsdoc;
648     nsresult nsres;
649
650     doc->lpIDocumentObserverVtbl  = &nsDocumentObserverVtbl;
651     doc->lpIRunnableVtbl          = &nsRunnableVtbl;
652
653     nsres = nsIDOMHTMLDocument_QueryInterface(doc->nsdoc, &IID_nsIDOMNSDocument, (void**)&nsdoc);
654     if(NS_FAILED(nsres)) {
655         ERR("Could not get nsIDOMNSDocument: %08x\n", nsres);
656         return;
657     }
658
659     nsIDOMNSDocument_WineAddObserver(nsdoc, NSDOCOBS(doc));
660     nsIDOMNSDocument_Release(nsdoc);
661 }
662
663 void release_mutation(HTMLDocumentNode *doc)
664 {
665     nsIDOMNSDocument *nsdoc;
666     nsresult nsres;
667
668     nsres = nsIDOMHTMLDocument_QueryInterface(doc->nsdoc, &IID_nsIDOMNSDocument, (void**)&nsdoc);
669     if(NS_FAILED(nsres)) {
670         ERR("Could not get nsIDOMNSDocument: %08x\n", nsres);
671         return;
672     }
673
674     nsIDOMNSDocument_WineRemoveObserver(nsdoc, NSDOCOBS(doc));
675     nsIDOMNSDocument_Release(nsdoc);
676 }