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