wrc: Use ARRAY_SIZE instead of open coding it.
[wine.git] / dlls / mshtml / mutation.c
blob0f1a083e616dd3eb41832b2a321163666eb5a859
1 /*
2 * Copyright 2008 Jacek Caban for CodeWeavers
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.
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.
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
19 #include <stdarg.h>
21 #define COBJMACROS
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "winreg.h"
27 #include "ole2.h"
28 #include "shlguid.h"
29 #include "wininet.h"
30 #include "winternl.h"
32 #include "mshtml_private.h"
33 #include "htmlscript.h"
34 #include "htmlevent.h"
35 #include "binding.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
41 const compat_mode_info_t compat_mode_info[] = {
42 { 5, 7 }, /* DOCMODE_QUIRKS */
43 { 5, 5 }, /* DOCMODE_IE5 */
44 { 7, 7 }, /* DOCMODE_IE7 */
45 { 8, 8 }, /* DOCMODE_IE8 */
46 { 9, 9 }, /* DOCMODE_IE8 */
47 { 10, 10 }, /* DOCMODE_IE10 */
48 { 11, 11 } /* DOCMODE_IE11 */
51 static const IID NS_ICONTENTUTILS_CID =
52 {0x762C4AE7,0xB923,0x422F,{0xB9,0x7E,0xB9,0xBF,0xC1,0xEF,0x7B,0xF0}};
54 static nsIContentUtils *content_utils;
56 static BOOL is_iexplore(void)
58 static volatile char cache = -1;
59 BOOL ret = cache;
60 if(ret == -1) {
61 const WCHAR *p, *name = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer;
62 if((p = wcsrchr(name, '/'))) name = p + 1;
63 if((p = wcsrchr(name, '\\'))) name = p + 1;
64 ret = !wcsicmp(name, L"iexplore.exe");
65 cache = ret;
67 return ret;
70 static PRUnichar *handle_insert_comment(HTMLDocumentNode *doc, const PRUnichar *comment)
72 unsigned majorv = 0, minorv = 0, compat_version;
73 const PRUnichar *ptr, *end;
74 PRUnichar *buf;
75 DWORD len;
77 enum {
78 CMP_EQ,
79 CMP_LT,
80 CMP_LTE,
81 CMP_GT,
82 CMP_GTE
83 } cmpt = CMP_EQ;
85 static const PRUnichar endifW[] = {'<','!','[','e','n','d','i','f',']'};
87 if(comment[0] != '[' || comment[1] != 'i' || comment[2] != 'f')
88 return NULL;
90 ptr = comment+3;
91 while(iswspace(*ptr))
92 ptr++;
94 if(ptr[0] == 'l' && ptr[1] == 't') {
95 ptr += 2;
96 if(*ptr == 'e') {
97 cmpt = CMP_LTE;
98 ptr++;
99 }else {
100 cmpt = CMP_LT;
102 }else if(ptr[0] == 'g' && ptr[1] == 't') {
103 ptr += 2;
104 if(*ptr == 'e') {
105 cmpt = CMP_GTE;
106 ptr++;
107 }else {
108 cmpt = CMP_GT;
112 if(!iswspace(*ptr++))
113 return NULL;
114 while(iswspace(*ptr))
115 ptr++;
117 if(ptr[0] != 'I' || ptr[1] != 'E')
118 return NULL;
120 ptr +=2;
121 if(!iswspace(*ptr++))
122 return NULL;
123 while(iswspace(*ptr))
124 ptr++;
126 if(!is_digit(*ptr))
127 return NULL;
128 while(is_digit(*ptr))
129 majorv = majorv*10 + (*ptr++ - '0');
131 if(*ptr == '.') {
132 ptr++;
133 if(!is_digit(*ptr))
134 return NULL;
135 while(is_digit(*ptr))
136 minorv = minorv*10 + (*ptr++ - '0');
139 while(iswspace(*ptr))
140 ptr++;
141 if(ptr[0] != ']' || ptr[1] != '>')
142 return NULL;
143 ptr += 2;
145 len = lstrlenW(ptr);
146 if(len < ARRAY_SIZE(endifW))
147 return NULL;
149 end = ptr + len - ARRAY_SIZE(endifW);
150 if(memcmp(end, endifW, sizeof(endifW)))
151 return NULL;
153 compat_version = compat_mode_info[doc->document_mode].ie_version;
155 switch(cmpt) {
156 case CMP_EQ:
157 if(compat_version == majorv && !minorv)
158 break;
159 return NULL;
160 case CMP_LT:
161 if(compat_version < majorv || (compat_version == majorv && minorv))
162 break;
163 return NULL;
164 case CMP_LTE:
165 if(compat_version <= majorv)
166 break;
167 return NULL;
168 case CMP_GT:
169 if(compat_version > majorv)
170 break;
171 return NULL;
172 case CMP_GTE:
173 if(compat_version >= majorv || (compat_version == majorv && !minorv))
174 break;
175 return NULL;
178 buf = heap_alloc((end-ptr+1)*sizeof(WCHAR));
179 if(!buf)
180 return NULL;
182 memcpy(buf, ptr, (end-ptr)*sizeof(WCHAR));
183 buf[end-ptr] = 0;
185 return buf;
188 static nsresult run_insert_comment(HTMLDocumentNode *doc, nsISupports *comment_iface, nsISupports *arg2)
190 const PRUnichar *comment;
191 nsIDOMComment *nscomment;
192 PRUnichar *replace_html;
193 nsAString comment_str;
194 nsresult nsres;
196 nsres = nsISupports_QueryInterface(comment_iface, &IID_nsIDOMComment, (void**)&nscomment);
197 if(NS_FAILED(nsres)) {
198 ERR("Could not get nsIDOMComment iface:%08lx\n", nsres);
199 return nsres;
202 nsAString_Init(&comment_str, NULL);
203 nsres = nsIDOMComment_GetData(nscomment, &comment_str);
204 if(NS_FAILED(nsres))
205 return nsres;
207 nsAString_GetData(&comment_str, &comment);
208 replace_html = handle_insert_comment(doc, comment);
209 nsAString_Finish(&comment_str);
211 if(replace_html) {
212 HRESULT hres;
214 hres = replace_node_by_html(doc->nsdoc, (nsIDOMNode*)nscomment, replace_html);
215 heap_free(replace_html);
216 if(FAILED(hres))
217 nsres = NS_ERROR_FAILURE;
221 nsIDOMComment_Release(nscomment);
222 return nsres;
225 static nsresult run_bind_to_tree(HTMLDocumentNode *doc, nsISupports *nsiface, nsISupports *arg2)
227 nsIDOMNode *nsnode;
228 HTMLDOMNode *node;
229 nsresult nsres;
230 HRESULT hres;
232 TRACE("(%p)->(%p)\n", doc, nsiface);
234 nsres = nsISupports_QueryInterface(nsiface, &IID_nsIDOMNode, (void**)&nsnode);
235 if(NS_FAILED(nsres))
236 return nsres;
238 hres = get_node(nsnode, TRUE, &node);
239 nsIDOMNode_Release(nsnode);
240 if(FAILED(hres)) {
241 ERR("Could not get node\n");
242 return nsres;
245 if(node->vtbl->bind_to_tree)
246 node->vtbl->bind_to_tree(node);
248 node_release(node);
249 return nsres;
252 /* Calls undocumented 69 cmd of CGID_Explorer */
253 static void call_explorer_69(HTMLDocumentObj *doc)
255 IOleCommandTarget *olecmd;
256 VARIANT var;
257 HRESULT hres;
259 if(!doc->client)
260 return;
262 hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
263 if(FAILED(hres))
264 return;
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");
273 static void parse_complete(HTMLDocumentObj *doc)
275 TRACE("(%p)\n", doc);
277 if(doc->nscontainer->usermode == EDITMODE)
278 init_editor(doc->basedoc.doc_node);
280 call_explorer_69(doc);
281 if(doc->view_sink)
282 IAdviseSink_OnViewChange(doc->view_sink, DVASPECT_CONTENT, -1);
283 call_property_onchanged(&doc->cp_container, 1005);
284 call_explorer_69(doc);
286 if(doc->webbrowser && doc->nscontainer->usermode != EDITMODE && !(doc->basedoc.window->load_flags & BINDING_REFRESH))
287 IDocObjectService_FireNavigateComplete2(doc->doc_object_service, &doc->basedoc.window->base.IHTMLWindow2_iface, 0);
289 /* FIXME: IE7 calls EnableModelless(TRUE), EnableModelless(FALSE) and sets interactive state here */
292 static nsresult run_end_load(HTMLDocumentNode *This, nsISupports *arg1, nsISupports *arg2)
294 TRACE("(%p)\n", This);
296 if(!This->basedoc.doc_obj)
297 return NS_OK;
299 if(This == This->basedoc.doc_obj->basedoc.doc_node) {
301 * This should be done in the worker thread that parses HTML,
302 * but we don't have such thread (Gecko parses HTML for us).
304 parse_complete(This->basedoc.doc_obj);
307 bind_event_scripts(This);
308 set_ready_state(This->basedoc.window, READYSTATE_INTERACTIVE);
309 return NS_OK;
312 static nsresult run_insert_script(HTMLDocumentNode *doc, nsISupports *script_iface, nsISupports *parser_iface)
314 nsIDOMHTMLScriptElement *nsscript;
315 HTMLScriptElement *script_elem;
316 nsIParser *nsparser = NULL;
317 script_queue_entry_t *iter;
318 HTMLInnerWindow *window;
319 nsresult nsres;
320 HRESULT hres;
322 TRACE("(%p)->(%p)\n", doc, script_iface);
324 window = doc->window;
325 if(!window)
326 return NS_OK;
328 nsres = nsISupports_QueryInterface(script_iface, &IID_nsIDOMHTMLScriptElement, (void**)&nsscript);
329 if(NS_FAILED(nsres)) {
330 ERR("Could not get nsIDOMHTMLScriptElement: %08lx\n", nsres);
331 return nsres;
334 if(parser_iface) {
335 nsres = nsISupports_QueryInterface(parser_iface, &IID_nsIParser, (void**)&nsparser);
336 if(NS_FAILED(nsres)) {
337 ERR("Could not get nsIParser iface: %08lx\n", nsres);
338 nsparser = NULL;
342 hres = script_elem_from_nsscript(nsscript, &script_elem);
343 nsIDOMHTMLScriptElement_Release(nsscript);
344 if(FAILED(hres))
345 return NS_ERROR_FAILURE;
347 if(nsparser) {
348 nsIParser_BeginEvaluatingParserInsertedScript(nsparser);
349 window->parser_callback_cnt++;
352 IHTMLWindow2_AddRef(&window->base.IHTMLWindow2_iface);
354 doc_insert_script(window, script_elem, TRUE);
356 while(!list_empty(&window->script_queue)) {
357 iter = LIST_ENTRY(list_head(&window->script_queue), script_queue_entry_t, entry);
358 list_remove(&iter->entry);
359 if(!iter->script->parsed)
360 doc_insert_script(window, iter->script, TRUE);
361 IHTMLScriptElement_Release(&iter->script->IHTMLScriptElement_iface);
362 heap_free(iter);
365 IHTMLWindow2_Release(&window->base.IHTMLWindow2_iface);
367 if(nsparser) {
368 window->parser_callback_cnt--;
369 nsIParser_EndEvaluatingParserInsertedScript(nsparser);
370 nsIParser_Release(nsparser);
373 IHTMLScriptElement_Release(&script_elem->IHTMLScriptElement_iface);
375 return NS_OK;
379 * We may change document mode only in early stage of document lifetime.
380 * Later attempts will not have an effect.
382 compat_mode_t lock_document_mode(HTMLDocumentNode *doc)
384 TRACE("%p: %d\n", doc, doc->document_mode);
386 doc->document_mode_locked = TRUE;
387 return doc->document_mode;
390 static void set_document_mode(HTMLDocumentNode *doc, compat_mode_t document_mode, BOOL lock)
392 compat_mode_t max_compat_mode;
394 if(doc->document_mode_locked) {
395 WARN("attempting to set document mode %d on locked document %p\n", document_mode, doc);
396 return;
399 TRACE("%p: %d\n", doc, document_mode);
401 max_compat_mode = doc->window && doc->window->base.outer_window
402 ? get_max_compat_mode(doc->window->base.outer_window->uri)
403 : COMPAT_MODE_IE11;
404 if(max_compat_mode < document_mode) {
405 WARN("Tried to set compat mode %u higher than maximal configured %u\n",
406 document_mode, max_compat_mode);
407 document_mode = max_compat_mode;
410 doc->document_mode = document_mode;
411 if(lock)
412 lock_document_mode(doc);
415 static BOOL is_ua_compatible_delimiter(WCHAR c)
417 return !c || c == ';' || c == ',' || iswspace(c);
420 const WCHAR *parse_compat_version(const WCHAR *version_string, compat_mode_t *r)
422 DWORD version = 0;
423 const WCHAR *p;
425 for(p = version_string; '0' <= *p && *p <= '9'; p++)
426 version = version * 10 + *p-'0';
427 if(!is_ua_compatible_delimiter(*p) || p == version_string)
428 return NULL;
430 switch(version){
431 case 5:
432 case 6:
433 *r = COMPAT_MODE_IE5;
434 break;
435 case 7:
436 *r = COMPAT_MODE_IE7;
437 break;
438 case 8:
439 *r = COMPAT_MODE_IE8;
440 break;
441 case 9:
442 *r = COMPAT_MODE_IE9;
443 break;
444 case 10:
445 *r = COMPAT_MODE_IE10;
446 break;
447 default:
448 *r = version < 5 ? COMPAT_MODE_QUIRKS : COMPAT_MODE_IE11;
450 return p;
453 static BOOL parse_ua_compatible(const WCHAR *p, compat_mode_t *r)
455 static const WCHAR ie_eqW[] = {'I','E','='};
456 static const WCHAR edgeW[] = {'e','d','g','e'};
457 compat_mode_t mode = COMPAT_MODE_INVALID;
459 TRACE("%s\n", debugstr_w(p));
461 if(wcsnicmp(ie_eqW, p, ARRAY_SIZE(ie_eqW)))
462 return FALSE;
463 p += 3;
465 do {
466 while(iswspace(*p)) p++;
467 if(!wcsnicmp(p, edgeW, ARRAY_SIZE(edgeW))) {
468 p += ARRAY_SIZE(edgeW);
469 if(is_ua_compatible_delimiter(*p))
470 mode = COMPAT_MODE_IE11;
471 break;
472 }else if(!(p = parse_compat_version(p, r)))
473 break;
474 if(mode < *r)
475 mode = *r;
476 while(iswspace(*p)) p++;
477 } while(*p++ == ',');
479 *r = mode;
480 return mode != COMPAT_MODE_INVALID;
483 void process_document_response_headers(HTMLDocumentNode *doc, IBinding *binding)
485 IWinInetHttpInfo *http_info;
486 char buf[1024];
487 DWORD size;
488 HRESULT hres;
490 hres = IBinding_QueryInterface(binding, &IID_IWinInetHttpInfo, (void**)&http_info);
491 if(FAILED(hres)) {
492 TRACE("No IWinInetHttpInfo\n");
493 return;
496 size = sizeof(buf);
497 strcpy(buf, "X-UA-Compatible");
498 hres = IWinInetHttpInfo_QueryInfo(http_info, HTTP_QUERY_CUSTOM, buf, &size, NULL, NULL);
499 if(hres == S_OK && size) {
500 compat_mode_t document_mode;
501 WCHAR *header;
503 TRACE("size %lu\n", size);
505 header = heap_strdupAtoW(buf);
506 if(header && parse_ua_compatible(header, &document_mode)) {
507 TRACE("setting document mode %d\n", document_mode);
508 set_document_mode(doc, document_mode, FALSE);
510 heap_free(header);
513 IWinInetHttpInfo_Release(http_info);
516 static void process_meta_element(HTMLDocumentNode *doc, nsIDOMHTMLMetaElement *meta_element)
518 nsAString http_equiv_str, content_str;
519 nsresult nsres;
521 nsAString_Init(&http_equiv_str, NULL);
522 nsAString_Init(&content_str, NULL);
523 nsres = nsIDOMHTMLMetaElement_GetHttpEquiv(meta_element, &http_equiv_str);
524 if(NS_SUCCEEDED(nsres))
525 nsres = nsIDOMHTMLMetaElement_GetContent(meta_element, &content_str);
527 if(NS_SUCCEEDED(nsres)) {
528 const PRUnichar *http_equiv, *content;
530 nsAString_GetData(&http_equiv_str, &http_equiv);
531 nsAString_GetData(&content_str, &content);
533 TRACE("%s: %s\n", debugstr_w(http_equiv), debugstr_w(content));
535 if(!wcsicmp(http_equiv, L"x-ua-compatible")) {
536 compat_mode_t document_mode;
537 if(parse_ua_compatible(content, &document_mode))
538 set_document_mode(doc, document_mode, TRUE);
539 else
540 FIXME("Unsupported document mode %s\n", debugstr_w(content));
544 nsAString_Finish(&http_equiv_str);
545 nsAString_Finish(&content_str);
548 typedef struct nsRunnable nsRunnable;
550 typedef nsresult (*runnable_proc_t)(HTMLDocumentNode*,nsISupports*,nsISupports*);
552 struct nsRunnable {
553 nsIRunnable nsIRunnable_iface;
555 LONG ref;
557 runnable_proc_t proc;
559 HTMLDocumentNode *doc;
560 nsISupports *arg1;
561 nsISupports *arg2;
564 static inline nsRunnable *impl_from_nsIRunnable(nsIRunnable *iface)
566 return CONTAINING_RECORD(iface, nsRunnable, nsIRunnable_iface);
569 static nsresult NSAPI nsRunnable_QueryInterface(nsIRunnable *iface,
570 nsIIDRef riid, void **result)
572 nsRunnable *This = impl_from_nsIRunnable(iface);
574 if(IsEqualGUID(riid, &IID_nsISupports)) {
575 TRACE("(%p)->(IID_nsISupports %p)\n", This, result);
576 *result = &This->nsIRunnable_iface;
577 }else if(IsEqualGUID(riid, &IID_nsIRunnable)) {
578 TRACE("(%p)->(IID_nsIRunnable %p)\n", This, result);
579 *result = &This->nsIRunnable_iface;
580 }else {
581 *result = NULL;
582 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), result);
583 return NS_NOINTERFACE;
586 nsISupports_AddRef((nsISupports*)*result);
587 return NS_OK;
590 static nsrefcnt NSAPI nsRunnable_AddRef(nsIRunnable *iface)
592 nsRunnable *This = impl_from_nsIRunnable(iface);
593 LONG ref = InterlockedIncrement(&This->ref);
595 TRACE("(%p) ref=%ld\n", This, ref);
597 return ref;
600 static nsrefcnt NSAPI nsRunnable_Release(nsIRunnable *iface)
602 nsRunnable *This = impl_from_nsIRunnable(iface);
603 LONG ref = InterlockedDecrement(&This->ref);
605 TRACE("(%p) ref=%ld\n", This, ref);
607 if(!ref) {
608 htmldoc_release(&This->doc->basedoc);
609 if(This->arg1)
610 nsISupports_Release(This->arg1);
611 if(This->arg2)
612 nsISupports_Release(This->arg2);
613 heap_free(This);
616 return ref;
619 static nsresult NSAPI nsRunnable_Run(nsIRunnable *iface)
621 nsRunnable *This = impl_from_nsIRunnable(iface);
623 return This->proc(This->doc, This->arg1, This->arg2);
626 static const nsIRunnableVtbl nsRunnableVtbl = {
627 nsRunnable_QueryInterface,
628 nsRunnable_AddRef,
629 nsRunnable_Release,
630 nsRunnable_Run
633 static void add_script_runner(HTMLDocumentNode *This, runnable_proc_t proc, nsISupports *arg1, nsISupports *arg2)
635 nsRunnable *runnable;
637 runnable = heap_alloc_zero(sizeof(*runnable));
638 if(!runnable)
639 return;
641 runnable->nsIRunnable_iface.lpVtbl = &nsRunnableVtbl;
642 runnable->ref = 1;
644 htmldoc_addref(&This->basedoc);
645 runnable->doc = This;
646 runnable->proc = proc;
648 if(arg1)
649 nsISupports_AddRef(arg1);
650 runnable->arg1 = arg1;
652 if(arg2)
653 nsISupports_AddRef(arg2);
654 runnable->arg2 = arg2;
656 nsIContentUtils_AddScriptRunner(content_utils, &runnable->nsIRunnable_iface);
658 nsIRunnable_Release(&runnable->nsIRunnable_iface);
661 static inline HTMLDocumentNode *impl_from_nsIDocumentObserver(nsIDocumentObserver *iface)
663 return CONTAINING_RECORD(iface, HTMLDocumentNode, nsIDocumentObserver_iface);
666 static nsresult NSAPI nsDocumentObserver_QueryInterface(nsIDocumentObserver *iface,
667 nsIIDRef riid, void **result)
669 HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
671 if(IsEqualGUID(&IID_nsISupports, riid)) {
672 TRACE("(%p)->(IID_nsISupports, %p)\n", This, result);
673 *result = &This->nsIDocumentObserver_iface;
674 }else if(IsEqualGUID(&IID_nsIMutationObserver, riid)) {
675 TRACE("(%p)->(IID_nsIMutationObserver %p)\n", This, result);
676 *result = &This->nsIDocumentObserver_iface;
677 }else if(IsEqualGUID(&IID_nsIDocumentObserver, riid)) {
678 TRACE("(%p)->(IID_nsIDocumentObserver %p)\n", This, result);
679 *result = &This->nsIDocumentObserver_iface;
680 }else {
681 *result = NULL;
682 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), result);
683 return NS_NOINTERFACE;
686 htmldoc_addref(&This->basedoc);
687 return NS_OK;
690 static nsrefcnt NSAPI nsDocumentObserver_AddRef(nsIDocumentObserver *iface)
692 HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
693 return htmldoc_addref(&This->basedoc);
696 static nsrefcnt NSAPI nsDocumentObserver_Release(nsIDocumentObserver *iface)
698 HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
699 return htmldoc_release(&This->basedoc);
702 static void NSAPI nsDocumentObserver_CharacterDataWillChange(nsIDocumentObserver *iface,
703 nsIDocument *aDocument, nsIContent *aContent, void /*CharacterDataChangeInfo*/ *aInfo)
707 static void NSAPI nsDocumentObserver_CharacterDataChanged(nsIDocumentObserver *iface,
708 nsIDocument *aDocument, nsIContent *aContent, void /*CharacterDataChangeInfo*/ *aInfo)
712 static void NSAPI nsDocumentObserver_AttributeWillChange(nsIDocumentObserver *iface, nsIDocument *aDocument,
713 void *aElement, LONG aNameSpaceID, nsIAtom *aAttribute, LONG aModType, const nsAttrValue *aNewValue)
717 static void NSAPI nsDocumentObserver_AttributeChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
718 void *aElement, LONG aNameSpaceID, nsIAtom *aAttribute, LONG aModType, const nsAttrValue *aOldValue)
722 static void NSAPI nsDocumentObserver_NativeAnonymousChildListChange(nsIDocumentObserver *iface, nsIDocument *aDocument,
723 nsIContent *aContent, cpp_bool aIsRemove)
727 static void NSAPI nsDocumentObserver_AttributeSetToCurrentValue(nsIDocumentObserver *iface, nsIDocument *aDocument,
728 void *aElement, LONG aNameSpaceID, nsIAtom *aAttribute)
732 static void NSAPI nsDocumentObserver_ContentAppended(nsIDocumentObserver *iface, nsIDocument *aDocument,
733 nsIContent *aContainer, nsIContent *aFirstNewContent, LONG aNewIndexInContainer)
737 static void NSAPI nsDocumentObserver_ContentInserted(nsIDocumentObserver *iface, nsIDocument *aDocument,
738 nsIContent *aContainer, nsIContent *aChild, LONG aIndexInContainer)
742 static void NSAPI nsDocumentObserver_ContentRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
743 nsIContent *aContainer, nsIContent *aChild, LONG aIndexInContainer,
744 nsIContent *aProviousSibling)
748 static void NSAPI nsDocumentObserver_NodeWillBeDestroyed(nsIDocumentObserver *iface, const nsINode *aNode)
752 static void NSAPI nsDocumentObserver_ParentChainChanged(nsIDocumentObserver *iface, nsIContent *aContent)
756 static void NSAPI nsDocumentObserver_BeginUpdate(nsIDocumentObserver *iface, nsIDocument *aDocument,
757 nsUpdateType aUpdateType)
761 static void NSAPI nsDocumentObserver_EndUpdate(nsIDocumentObserver *iface, nsIDocument *aDocument,
762 nsUpdateType aUpdateType)
766 static void NSAPI nsDocumentObserver_BeginLoad(nsIDocumentObserver *iface, nsIDocument *aDocument)
770 static void NSAPI nsDocumentObserver_EndLoad(nsIDocumentObserver *iface, nsIDocument *aDocument)
772 HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
774 TRACE("(%p)\n", This);
776 if(This->skip_mutation_notif)
777 return;
779 This->content_ready = TRUE;
780 add_script_runner(This, run_end_load, NULL, NULL);
783 static void NSAPI nsDocumentObserver_ContentStatesChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
784 nsIContent *aContent, EventStates aStateMask)
788 static void NSAPI nsDocumentObserver_DocumentStatesChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
789 EventStates aStateMask)
793 static void NSAPI nsDocumentObserver_StyleSheetAdded(nsIDocumentObserver *iface, mozilla_StyleSheetHandle aStyleSheet,
794 cpp_bool aDocumentSheet)
798 static void NSAPI nsDocumentObserver_StyleSheetRemoved(nsIDocumentObserver *iface, mozilla_StyleSheetHandle aStyleSheet,
799 cpp_bool aDocumentSheet)
803 static void NSAPI nsDocumentObserver_StyleSheetApplicableStateChanged(nsIDocumentObserver *iface,
804 mozilla_StyleSheetHandle aStyleSheet)
808 static void NSAPI nsDocumentObserver_StyleRuleChanged(nsIDocumentObserver *iface, mozilla_StyleSheetHandle aStyleSheet)
812 static void NSAPI nsDocumentObserver_StyleRuleAdded(nsIDocumentObserver *iface, mozilla_StyleSheetHandle aStyleSheet)
816 static void NSAPI nsDocumentObserver_StyleRuleRemoved(nsIDocumentObserver *iface, mozilla_StyleSheetHandle aStyleSheet)
820 static void NSAPI nsDocumentObserver_BindToDocument(nsIDocumentObserver *iface, nsIDocument *aDocument,
821 nsIContent *aContent)
823 HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
824 nsIDOMHTMLIFrameElement *nsiframe;
825 nsIDOMHTMLFrameElement *nsframe;
826 nsIDOMHTMLScriptElement *nsscript;
827 nsIDOMHTMLMetaElement *nsmeta;
828 nsIDOMElement *nselem;
829 nsIDOMComment *nscomment;
830 nsresult nsres;
832 TRACE("(%p)->(%p %p)\n", This, aDocument, aContent);
834 if(This->document_mode < COMPAT_MODE_IE10) {
835 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMComment, (void**)&nscomment);
836 if(NS_SUCCEEDED(nsres)) {
837 TRACE("comment node\n");
839 add_script_runner(This, run_insert_comment, (nsISupports*)nscomment, NULL);
840 nsIDOMComment_Release(nscomment);
841 return;
845 if(This->document_mode == COMPAT_MODE_QUIRKS) {
846 nsIDOMDocumentType *nsdoctype;
848 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMDocumentType, (void**)&nsdoctype);
849 if(NS_SUCCEEDED(nsres)) {
850 compat_mode_t mode = COMPAT_MODE_IE7;
852 TRACE("doctype node\n");
854 /* Native mshtml hardcodes special behavior for iexplore.exe here. The feature control registry
855 keys under HKLM or HKCU\Software\Microsoft\Internet Explorer\Main\FeatureControl are not used
856 in this case (neither in Wow6432Node), although FEATURE_BROWSER_EMULATION does override this,
857 but it is not set by default on native, and the behavior is still different. This was tested
858 by removing all iexplore.exe values from any FeatureControl subkeys, and renaming the test
859 executable to iexplore.exe, which changed its default compat mode in such cases. */
860 if(This->window && This->window->base.outer_window && is_iexplore()) {
861 HTMLOuterWindow *window = This->window->base.outer_window;
862 DWORD zone;
863 HRESULT hres;
865 /* Internet URL zone is treated differently and defaults to the latest supported mode. */
866 hres = IInternetSecurityManager_MapUrlToZone(get_security_manager(), window->url, &zone, 0);
867 if(SUCCEEDED(hres) && zone == URLZONE_INTERNET)
868 mode = COMPAT_MODE_IE11;
871 set_document_mode(This, mode, FALSE);
872 nsIDOMDocumentType_Release(nsdoctype);
876 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMElement, (void**)&nselem);
877 if(NS_FAILED(nsres))
878 return;
880 check_event_attr(This, nselem);
881 nsIDOMElement_Release(nselem);
883 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLIFrameElement, (void**)&nsiframe);
884 if(NS_SUCCEEDED(nsres)) {
885 TRACE("iframe node\n");
887 add_script_runner(This, run_bind_to_tree, (nsISupports*)nsiframe, NULL);
888 nsIDOMHTMLIFrameElement_Release(nsiframe);
889 return;
892 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLFrameElement, (void**)&nsframe);
893 if(NS_SUCCEEDED(nsres)) {
894 TRACE("frame node\n");
896 add_script_runner(This, run_bind_to_tree, (nsISupports*)nsframe, NULL);
897 nsIDOMHTMLFrameElement_Release(nsframe);
898 return;
901 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLScriptElement, (void**)&nsscript);
902 if(NS_SUCCEEDED(nsres)) {
903 TRACE("script element\n");
905 add_script_runner(This, run_bind_to_tree, (nsISupports*)nsscript, NULL);
906 nsIDOMHTMLScriptElement_Release(nsscript);
907 return;
910 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLMetaElement, (void**)&nsmeta);
911 if(NS_SUCCEEDED(nsres)) {
912 process_meta_element(This, nsmeta);
913 nsIDOMHTMLMetaElement_Release(nsmeta);
917 static void NSAPI nsDocumentObserver_AttemptToExecuteScript(nsIDocumentObserver *iface, nsIContent *aContent,
918 nsIParser *aParser, cpp_bool *aBlock)
920 HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
921 nsIDOMHTMLScriptElement *nsscript;
922 nsresult nsres;
924 TRACE("(%p)->(%p %p %p)\n", This, aContent, aParser, aBlock);
926 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLScriptElement, (void**)&nsscript);
927 if(NS_SUCCEEDED(nsres)) {
928 TRACE("script node\n");
930 lock_document_mode(This);
931 add_script_runner(This, run_insert_script, (nsISupports*)nsscript, (nsISupports*)aParser);
932 nsIDOMHTMLScriptElement_Release(nsscript);
936 static const nsIDocumentObserverVtbl nsDocumentObserverVtbl = {
937 nsDocumentObserver_QueryInterface,
938 nsDocumentObserver_AddRef,
939 nsDocumentObserver_Release,
940 nsDocumentObserver_CharacterDataWillChange,
941 nsDocumentObserver_CharacterDataChanged,
942 nsDocumentObserver_AttributeWillChange,
943 nsDocumentObserver_AttributeChanged,
944 nsDocumentObserver_NativeAnonymousChildListChange,
945 nsDocumentObserver_AttributeSetToCurrentValue,
946 nsDocumentObserver_ContentAppended,
947 nsDocumentObserver_ContentInserted,
948 nsDocumentObserver_ContentRemoved,
949 nsDocumentObserver_NodeWillBeDestroyed,
950 nsDocumentObserver_ParentChainChanged,
951 nsDocumentObserver_BeginUpdate,
952 nsDocumentObserver_EndUpdate,
953 nsDocumentObserver_BeginLoad,
954 nsDocumentObserver_EndLoad,
955 nsDocumentObserver_ContentStatesChanged,
956 nsDocumentObserver_DocumentStatesChanged,
957 nsDocumentObserver_StyleSheetAdded,
958 nsDocumentObserver_StyleSheetRemoved,
959 nsDocumentObserver_StyleSheetApplicableStateChanged,
960 nsDocumentObserver_StyleRuleChanged,
961 nsDocumentObserver_StyleRuleAdded,
962 nsDocumentObserver_StyleRuleRemoved,
963 nsDocumentObserver_BindToDocument,
964 nsDocumentObserver_AttemptToExecuteScript
967 void init_document_mutation(HTMLDocumentNode *doc)
969 nsIDocument *nsdoc;
970 nsresult nsres;
972 doc->nsIDocumentObserver_iface.lpVtbl = &nsDocumentObserverVtbl;
974 nsres = nsIDOMHTMLDocument_QueryInterface(doc->nsdoc, &IID_nsIDocument, (void**)&nsdoc);
975 if(NS_FAILED(nsres)) {
976 ERR("Could not get nsIDocument: %08lx\n", nsres);
977 return;
980 nsIContentUtils_AddDocumentObserver(content_utils, nsdoc, &doc->nsIDocumentObserver_iface);
981 nsIDocument_Release(nsdoc);
984 void release_document_mutation(HTMLDocumentNode *doc)
986 nsIDocument *nsdoc;
987 nsresult nsres;
989 nsres = nsIDOMHTMLDocument_QueryInterface(doc->nsdoc, &IID_nsIDocument, (void**)&nsdoc);
990 if(NS_FAILED(nsres)) {
991 ERR("Could not get nsIDocument: %08lx\n", nsres);
992 return;
995 nsIContentUtils_RemoveDocumentObserver(content_utils, nsdoc, &doc->nsIDocumentObserver_iface);
996 nsIDocument_Release(nsdoc);
999 JSContext *get_context_from_document(nsIDOMHTMLDocument *nsdoc)
1001 nsIDocument *doc;
1002 JSContext *ctx;
1003 nsresult nsres;
1005 nsres = nsIDOMHTMLDocument_QueryInterface(nsdoc, &IID_nsIDocument, (void**)&doc);
1006 assert(nsres == NS_OK);
1008 ctx = nsIContentUtils_GetContextFromDocument(content_utils, doc);
1009 nsIDocument_Release(doc);
1011 TRACE("ret %p\n", ctx);
1012 return ctx;
1015 void init_mutation(nsIComponentManager *component_manager)
1017 nsIFactory *factory;
1018 nsresult nsres;
1020 if(!component_manager) {
1021 if(content_utils) {
1022 nsIContentUtils_Release(content_utils);
1023 content_utils = NULL;
1025 return;
1028 nsres = nsIComponentManager_GetClassObject(component_manager, &NS_ICONTENTUTILS_CID,
1029 &IID_nsIFactory, (void**)&factory);
1030 if(NS_FAILED(nsres)) {
1031 ERR("Could not create nsIContentUtils service: %08lx\n", nsres);
1032 return;
1035 nsres = nsIFactory_CreateInstance(factory, NULL, &IID_nsIContentUtils, (void**)&content_utils);
1036 nsIFactory_Release(factory);
1037 if(NS_FAILED(nsres))
1038 ERR("Could not create nsIContentUtils instance: %08lx\n", nsres);