ucrtbase: Store exception record in ExceptionInformation[6] during unwinding.
[wine.git] / dlls / mshtml / mutation.c
blob290174e4785a866c88acbfc6b6a36a6f528c5780
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 = malloc((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->dom_document, (nsIDOMNode*)nscomment, replace_html);
215 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->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->window->load_flags & BINDING_REFRESH))
287 IDocObjectService_FireNavigateComplete2(doc->doc_object_service, &doc->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 HTMLDocumentObj *doc_obj = This->doc_obj;
295 HTMLInnerWindow *window = This->window;
297 TRACE("(%p)\n", This);
299 if(!doc_obj)
300 return NS_OK;
301 IHTMLWindow2_AddRef(&window->base.IHTMLWindow2_iface);
303 if(This == doc_obj->doc_node) {
305 * This should be done in the worker thread that parses HTML,
306 * but we don't have such thread (Gecko parses HTML for us).
308 IUnknown_AddRef(doc_obj->outer_unk);
309 parse_complete(doc_obj);
310 IUnknown_Release(doc_obj->outer_unk);
313 bind_event_scripts(This);
315 if(This->window == window) {
316 window->dom_interactive_time = get_time_stamp();
317 set_ready_state(This->window->base.outer_window, READYSTATE_INTERACTIVE);
319 IHTMLWindow2_Release(&window->base.IHTMLWindow2_iface);
320 return NS_OK;
323 static nsresult run_insert_script(HTMLDocumentNode *doc, nsISupports *script_iface, nsISupports *parser_iface)
325 nsIDOMHTMLScriptElement *nsscript;
326 HTMLScriptElement *script_elem;
327 nsIParser *nsparser = NULL;
328 script_queue_entry_t *iter;
329 HTMLInnerWindow *window;
330 nsresult nsres;
331 HRESULT hres;
333 TRACE("(%p)->(%p)\n", doc, script_iface);
335 window = doc->window;
336 if(!window)
337 return NS_OK;
339 nsres = nsISupports_QueryInterface(script_iface, &IID_nsIDOMHTMLScriptElement, (void**)&nsscript);
340 if(NS_FAILED(nsres)) {
341 ERR("Could not get nsIDOMHTMLScriptElement: %08lx\n", nsres);
342 return nsres;
345 if(parser_iface) {
346 nsres = nsISupports_QueryInterface(parser_iface, &IID_nsIParser, (void**)&nsparser);
347 if(NS_FAILED(nsres)) {
348 ERR("Could not get nsIParser iface: %08lx\n", nsres);
349 nsparser = NULL;
353 hres = script_elem_from_nsscript(nsscript, &script_elem);
354 nsIDOMHTMLScriptElement_Release(nsscript);
355 if(FAILED(hres)) {
356 if(nsparser)
357 nsIParser_Release(nsparser);
358 return NS_ERROR_FAILURE;
361 if(nsparser) {
362 nsIParser_BeginEvaluatingParserInsertedScript(nsparser);
363 window->parser_callback_cnt++;
366 IHTMLWindow2_AddRef(&window->base.IHTMLWindow2_iface);
368 doc_insert_script(window, script_elem, TRUE);
370 while(!list_empty(&window->script_queue)) {
371 iter = LIST_ENTRY(list_head(&window->script_queue), script_queue_entry_t, entry);
372 list_remove(&iter->entry);
373 if(!iter->script->parsed)
374 doc_insert_script(window, iter->script, TRUE);
375 IHTMLScriptElement_Release(&iter->script->IHTMLScriptElement_iface);
376 free(iter);
379 if(nsparser) {
380 window->parser_callback_cnt--;
381 nsIParser_EndEvaluatingParserInsertedScript(nsparser);
382 nsIParser_Release(nsparser);
385 IHTMLWindow2_Release(&window->base.IHTMLWindow2_iface);
386 IHTMLScriptElement_Release(&script_elem->IHTMLScriptElement_iface);
388 return NS_OK;
391 DWORD get_compat_mode_version(compat_mode_t compat_mode)
393 switch(compat_mode) {
394 case COMPAT_MODE_QUIRKS:
395 case COMPAT_MODE_IE5:
396 case COMPAT_MODE_IE7:
397 return 7;
398 case COMPAT_MODE_IE8:
399 return 8;
400 case COMPAT_MODE_IE9:
401 return 9;
402 case COMPAT_MODE_IE10:
403 return 10;
404 case COMPAT_MODE_IE11:
405 return 11;
406 DEFAULT_UNREACHABLE;
408 return 0;
412 * We may change document mode only in early stage of document lifetime.
413 * Later attempts will not have an effect.
415 compat_mode_t lock_document_mode(HTMLDocumentNode *doc)
417 TRACE("%p: %d\n", doc, doc->document_mode);
419 if(!doc->document_mode_locked) {
420 doc->document_mode_locked = TRUE;
422 if(doc->html_document)
423 nsIDOMHTMLDocument_SetIECompatMode(doc->html_document, get_compat_mode_version(doc->document_mode));
426 return doc->document_mode;
429 static void set_document_mode(HTMLDocumentNode *doc, compat_mode_t document_mode, BOOL lock)
431 compat_mode_t max_compat_mode;
433 if(doc->document_mode_locked) {
434 WARN("attempting to set document mode %d on locked document %p\n", document_mode, doc);
435 return;
438 TRACE("%p: %d\n", doc, document_mode);
440 max_compat_mode = doc->window && doc->window->base.outer_window
441 ? get_max_compat_mode(doc->window->base.outer_window->uri)
442 : COMPAT_MODE_IE11;
443 if(max_compat_mode < document_mode) {
444 WARN("Tried to set compat mode %u higher than maximal configured %u\n",
445 document_mode, max_compat_mode);
446 document_mode = max_compat_mode;
449 doc->document_mode = document_mode;
450 if(lock)
451 lock_document_mode(doc);
454 static BOOL is_ua_compatible_delimiter(WCHAR c)
456 return !c || c == ';' || c == ',' || iswspace(c);
459 const WCHAR *parse_compat_version(const WCHAR *version_string, compat_mode_t *r)
461 DWORD version = 0;
462 const WCHAR *p;
464 for(p = version_string; '0' <= *p && *p <= '9'; p++)
465 version = version * 10 + *p-'0';
466 if(!is_ua_compatible_delimiter(*p) || p == version_string)
467 return NULL;
469 switch(version){
470 case 5:
471 case 6:
472 *r = COMPAT_MODE_IE5;
473 break;
474 case 7:
475 *r = COMPAT_MODE_IE7;
476 break;
477 case 8:
478 *r = COMPAT_MODE_IE8;
479 break;
480 case 9:
481 *r = COMPAT_MODE_IE9;
482 break;
483 case 10:
484 *r = COMPAT_MODE_IE10;
485 break;
486 default:
487 *r = version < 5 ? COMPAT_MODE_QUIRKS : COMPAT_MODE_IE11;
489 return p;
492 static BOOL parse_ua_compatible(const WCHAR *p, compat_mode_t *r)
494 static const WCHAR ie_eqW[] = {'I','E','='};
495 static const WCHAR edgeW[] = {'e','d','g','e'};
496 compat_mode_t mode = COMPAT_MODE_INVALID;
498 TRACE("%s\n", debugstr_w(p));
500 if(wcsnicmp(ie_eqW, p, ARRAY_SIZE(ie_eqW)))
501 return FALSE;
502 p += 3;
504 do {
505 while(iswspace(*p)) p++;
506 if(!wcsnicmp(p, edgeW, ARRAY_SIZE(edgeW))) {
507 p += ARRAY_SIZE(edgeW);
508 if(is_ua_compatible_delimiter(*p))
509 mode = COMPAT_MODE_IE11;
510 break;
511 }else if(!(p = parse_compat_version(p, r)))
512 break;
513 if(mode < *r)
514 mode = *r;
515 while(iswspace(*p)) p++;
516 } while(*p++ == ',');
518 *r = mode;
519 return mode != COMPAT_MODE_INVALID;
522 void process_document_response_headers(HTMLDocumentNode *doc, IBinding *binding)
524 IWinInetHttpInfo *http_info;
525 char buf[1024];
526 DWORD size;
527 HRESULT hres;
529 hres = IBinding_QueryInterface(binding, &IID_IWinInetHttpInfo, (void**)&http_info);
530 if(FAILED(hres)) {
531 TRACE("No IWinInetHttpInfo\n");
532 return;
535 size = sizeof(buf);
536 strcpy(buf, "X-UA-Compatible");
537 hres = IWinInetHttpInfo_QueryInfo(http_info, HTTP_QUERY_CUSTOM, buf, &size, NULL, NULL);
538 if(hres == S_OK && size) {
539 compat_mode_t document_mode;
540 WCHAR *header;
542 TRACE("size %lu\n", size);
544 header = strdupAtoW(buf);
545 if(header && parse_ua_compatible(header, &document_mode)) {
546 TRACE("setting document mode %d\n", document_mode);
547 set_document_mode(doc, document_mode, FALSE);
549 free(header);
552 IWinInetHttpInfo_Release(http_info);
555 static void process_meta_element(HTMLDocumentNode *doc, nsIDOMHTMLMetaElement *meta_element)
557 nsAString http_equiv_str, content_str;
558 nsresult nsres;
560 nsAString_Init(&http_equiv_str, NULL);
561 nsAString_Init(&content_str, NULL);
562 nsres = nsIDOMHTMLMetaElement_GetHttpEquiv(meta_element, &http_equiv_str);
563 if(NS_SUCCEEDED(nsres))
564 nsres = nsIDOMHTMLMetaElement_GetContent(meta_element, &content_str);
566 if(NS_SUCCEEDED(nsres)) {
567 const PRUnichar *http_equiv, *content;
569 nsAString_GetData(&http_equiv_str, &http_equiv);
570 nsAString_GetData(&content_str, &content);
572 TRACE("%s: %s\n", debugstr_w(http_equiv), debugstr_w(content));
574 if(!wcsicmp(http_equiv, L"x-ua-compatible")) {
575 compat_mode_t document_mode;
576 if(parse_ua_compatible(content, &document_mode))
577 set_document_mode(doc, document_mode, TRUE);
578 else
579 FIXME("Unsupported document mode %s\n", debugstr_w(content));
583 nsAString_Finish(&http_equiv_str);
584 nsAString_Finish(&content_str);
587 typedef struct nsRunnable nsRunnable;
589 typedef nsresult (*runnable_proc_t)(HTMLDocumentNode*,nsISupports*,nsISupports*);
591 struct nsRunnable {
592 nsIRunnable nsIRunnable_iface;
594 LONG ref;
596 runnable_proc_t proc;
598 HTMLDocumentNode *doc;
599 nsISupports *arg1;
600 nsISupports *arg2;
603 static inline nsRunnable *impl_from_nsIRunnable(nsIRunnable *iface)
605 return CONTAINING_RECORD(iface, nsRunnable, nsIRunnable_iface);
608 static nsresult NSAPI nsRunnable_QueryInterface(nsIRunnable *iface,
609 nsIIDRef riid, void **result)
611 nsRunnable *This = impl_from_nsIRunnable(iface);
613 if(IsEqualGUID(riid, &IID_nsISupports)) {
614 TRACE("(%p)->(IID_nsISupports %p)\n", This, result);
615 *result = &This->nsIRunnable_iface;
616 }else if(IsEqualGUID(riid, &IID_nsIRunnable)) {
617 TRACE("(%p)->(IID_nsIRunnable %p)\n", This, result);
618 *result = &This->nsIRunnable_iface;
619 }else {
620 *result = NULL;
621 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), result);
622 return NS_NOINTERFACE;
625 nsISupports_AddRef((nsISupports*)*result);
626 return NS_OK;
629 static nsrefcnt NSAPI nsRunnable_AddRef(nsIRunnable *iface)
631 nsRunnable *This = impl_from_nsIRunnable(iface);
632 LONG ref = InterlockedIncrement(&This->ref);
634 TRACE("(%p) ref=%ld\n", This, ref);
636 return ref;
639 static nsrefcnt NSAPI nsRunnable_Release(nsIRunnable *iface)
641 nsRunnable *This = impl_from_nsIRunnable(iface);
642 LONG ref = InterlockedDecrement(&This->ref);
644 TRACE("(%p) ref=%ld\n", This, ref);
646 if(!ref) {
647 IHTMLDOMNode_Release(&This->doc->node.IHTMLDOMNode_iface);
648 if(This->arg1)
649 nsISupports_Release(This->arg1);
650 if(This->arg2)
651 nsISupports_Release(This->arg2);
652 free(This);
655 return ref;
658 static nsresult NSAPI nsRunnable_Run(nsIRunnable *iface)
660 nsRunnable *This = impl_from_nsIRunnable(iface);
662 return This->proc(This->doc, This->arg1, This->arg2);
665 static const nsIRunnableVtbl nsRunnableVtbl = {
666 nsRunnable_QueryInterface,
667 nsRunnable_AddRef,
668 nsRunnable_Release,
669 nsRunnable_Run
672 static void add_script_runner(HTMLDocumentNode *This, runnable_proc_t proc, nsISupports *arg1, nsISupports *arg2)
674 nsRunnable *runnable;
676 runnable = calloc(1, sizeof(*runnable));
677 if(!runnable)
678 return;
680 runnable->nsIRunnable_iface.lpVtbl = &nsRunnableVtbl;
681 runnable->ref = 1;
683 IHTMLDOMNode_AddRef(&This->node.IHTMLDOMNode_iface);
684 runnable->doc = This;
685 runnable->proc = proc;
687 if(arg1)
688 nsISupports_AddRef(arg1);
689 runnable->arg1 = arg1;
691 if(arg2)
692 nsISupports_AddRef(arg2);
693 runnable->arg2 = arg2;
695 nsIContentUtils_AddScriptRunner(content_utils, &runnable->nsIRunnable_iface);
697 nsIRunnable_Release(&runnable->nsIRunnable_iface);
700 static inline HTMLDocumentNode *impl_from_nsIDocumentObserver(nsIDocumentObserver *iface)
702 return CONTAINING_RECORD(iface, HTMLDocumentNode, nsIDocumentObserver_iface);
705 static nsresult NSAPI nsDocumentObserver_QueryInterface(nsIDocumentObserver *iface,
706 nsIIDRef riid, void **result)
708 HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
710 if(IsEqualGUID(&IID_nsISupports, riid)) {
711 TRACE("(%p)->(IID_nsISupports, %p)\n", This, result);
712 *result = &This->nsIDocumentObserver_iface;
713 }else if(IsEqualGUID(&IID_nsIMutationObserver, riid)) {
714 TRACE("(%p)->(IID_nsIMutationObserver %p)\n", This, result);
715 *result = &This->nsIDocumentObserver_iface;
716 }else if(IsEqualGUID(&IID_nsIDocumentObserver, riid)) {
717 TRACE("(%p)->(IID_nsIDocumentObserver %p)\n", This, result);
718 *result = &This->nsIDocumentObserver_iface;
719 }else {
720 *result = NULL;
721 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), result);
722 return NS_NOINTERFACE;
725 IHTMLDOMNode_AddRef(&This->node.IHTMLDOMNode_iface);
726 return NS_OK;
729 static nsrefcnt NSAPI nsDocumentObserver_AddRef(nsIDocumentObserver *iface)
731 HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
732 return IHTMLDOMNode_AddRef(&This->node.IHTMLDOMNode_iface);
735 static nsrefcnt NSAPI nsDocumentObserver_Release(nsIDocumentObserver *iface)
737 HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
738 return IHTMLDOMNode_Release(&This->node.IHTMLDOMNode_iface);
741 static void NSAPI nsDocumentObserver_CharacterDataWillChange(nsIDocumentObserver *iface,
742 nsIDocument *aDocument, nsIContent *aContent, void /*CharacterDataChangeInfo*/ *aInfo)
746 static void NSAPI nsDocumentObserver_CharacterDataChanged(nsIDocumentObserver *iface,
747 nsIDocument *aDocument, nsIContent *aContent, void /*CharacterDataChangeInfo*/ *aInfo)
751 static void NSAPI nsDocumentObserver_AttributeWillChange(nsIDocumentObserver *iface, nsIDocument *aDocument,
752 void *aElement, LONG aNameSpaceID, nsIAtom *aAttribute, LONG aModType, const nsAttrValue *aNewValue)
756 static void NSAPI nsDocumentObserver_AttributeChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
757 void *aElement, LONG aNameSpaceID, nsIAtom *aAttribute, LONG aModType, const nsAttrValue *aOldValue)
761 static void NSAPI nsDocumentObserver_NativeAnonymousChildListChange(nsIDocumentObserver *iface, nsIDocument *aDocument,
762 nsIContent *aContent, cpp_bool aIsRemove)
766 static void NSAPI nsDocumentObserver_AttributeSetToCurrentValue(nsIDocumentObserver *iface, nsIDocument *aDocument,
767 void *aElement, LONG aNameSpaceID, nsIAtom *aAttribute)
771 static void NSAPI nsDocumentObserver_ContentAppended(nsIDocumentObserver *iface, nsIDocument *aDocument,
772 nsIContent *aContainer, nsIContent *aFirstNewContent, LONG aNewIndexInContainer)
776 static void NSAPI nsDocumentObserver_ContentInserted(nsIDocumentObserver *iface, nsIDocument *aDocument,
777 nsIContent *aContainer, nsIContent *aChild, LONG aIndexInContainer)
781 static void NSAPI nsDocumentObserver_ContentRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
782 nsIContent *aContainer, nsIContent *aChild, LONG aIndexInContainer,
783 nsIContent *aProviousSibling)
787 static void NSAPI nsDocumentObserver_NodeWillBeDestroyed(nsIDocumentObserver *iface, const nsINode *aNode)
791 static void NSAPI nsDocumentObserver_ParentChainChanged(nsIDocumentObserver *iface, nsIContent *aContent)
795 static void NSAPI nsDocumentObserver_BeginUpdate(nsIDocumentObserver *iface, nsIDocument *aDocument,
796 nsUpdateType aUpdateType)
800 static void NSAPI nsDocumentObserver_EndUpdate(nsIDocumentObserver *iface, nsIDocument *aDocument,
801 nsUpdateType aUpdateType)
805 static void NSAPI nsDocumentObserver_BeginLoad(nsIDocumentObserver *iface, nsIDocument *aDocument)
809 static void NSAPI nsDocumentObserver_EndLoad(nsIDocumentObserver *iface, nsIDocument *aDocument)
811 HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
813 TRACE("(%p)\n", This);
815 if(This->skip_mutation_notif)
816 return;
818 This->content_ready = TRUE;
819 add_script_runner(This, run_end_load, NULL, NULL);
822 static void NSAPI nsDocumentObserver_ContentStatesChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
823 nsIContent *aContent, EventStates aStateMask)
827 static void NSAPI nsDocumentObserver_DocumentStatesChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
828 EventStates aStateMask)
832 static void NSAPI nsDocumentObserver_StyleSheetAdded(nsIDocumentObserver *iface, mozilla_StyleSheetHandle aStyleSheet,
833 cpp_bool aDocumentSheet)
837 static void NSAPI nsDocumentObserver_StyleSheetRemoved(nsIDocumentObserver *iface, mozilla_StyleSheetHandle aStyleSheet,
838 cpp_bool aDocumentSheet)
842 static void NSAPI nsDocumentObserver_StyleSheetApplicableStateChanged(nsIDocumentObserver *iface,
843 mozilla_StyleSheetHandle aStyleSheet)
847 static void NSAPI nsDocumentObserver_StyleRuleChanged(nsIDocumentObserver *iface, mozilla_StyleSheetHandle aStyleSheet)
851 static void NSAPI nsDocumentObserver_StyleRuleAdded(nsIDocumentObserver *iface, mozilla_StyleSheetHandle aStyleSheet)
855 static void NSAPI nsDocumentObserver_StyleRuleRemoved(nsIDocumentObserver *iface, mozilla_StyleSheetHandle aStyleSheet)
859 static void NSAPI nsDocumentObserver_BindToDocument(nsIDocumentObserver *iface, nsIDocument *aDocument,
860 nsIContent *aContent)
862 HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
863 nsIDOMHTMLIFrameElement *nsiframe;
864 nsIDOMHTMLFrameElement *nsframe;
865 nsIDOMHTMLScriptElement *nsscript;
866 nsIDOMHTMLMetaElement *nsmeta;
867 nsIDOMElement *nselem;
868 nsIDOMComment *nscomment;
869 nsresult nsres;
871 TRACE("(%p)->(%p %p)\n", This, aDocument, aContent);
873 if(This->document_mode < COMPAT_MODE_IE10) {
874 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMComment, (void**)&nscomment);
875 if(NS_SUCCEEDED(nsres)) {
876 TRACE("comment node\n");
878 add_script_runner(This, run_insert_comment, (nsISupports*)nscomment, NULL);
879 nsIDOMComment_Release(nscomment);
880 return;
884 if(This->document_mode == COMPAT_MODE_QUIRKS) {
885 nsIDOMDocumentType *nsdoctype;
887 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMDocumentType, (void**)&nsdoctype);
888 if(NS_SUCCEEDED(nsres)) {
889 compat_mode_t mode = COMPAT_MODE_IE7;
891 TRACE("doctype node\n");
893 /* Native mshtml hardcodes special behavior for iexplore.exe here. The feature control registry
894 keys under HKLM or HKCU\Software\Microsoft\Internet Explorer\Main\FeatureControl are not used
895 in this case (neither in Wow6432Node), although FEATURE_BROWSER_EMULATION does override this,
896 but it is not set by default on native, and the behavior is still different. This was tested
897 by removing all iexplore.exe values from any FeatureControl subkeys, and renaming the test
898 executable to iexplore.exe, which changed its default compat mode in such cases. */
899 if(This->window && This->window->base.outer_window && is_iexplore()) {
900 HTMLOuterWindow *window = This->window->base.outer_window;
901 DWORD zone;
902 HRESULT hres;
904 /* Internet URL zone is treated differently and defaults to the latest supported mode. */
905 hres = IInternetSecurityManager_MapUrlToZone(get_security_manager(), window->url, &zone, 0);
906 if(SUCCEEDED(hres) && zone == URLZONE_INTERNET)
907 mode = COMPAT_MODE_IE11;
910 set_document_mode(This, mode, FALSE);
911 nsIDOMDocumentType_Release(nsdoctype);
915 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMElement, (void**)&nselem);
916 if(NS_FAILED(nsres))
917 return;
919 check_event_attr(This, nselem);
920 nsIDOMElement_Release(nselem);
922 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLIFrameElement, (void**)&nsiframe);
923 if(NS_SUCCEEDED(nsres)) {
924 TRACE("iframe node\n");
926 add_script_runner(This, run_bind_to_tree, (nsISupports*)nsiframe, NULL);
927 nsIDOMHTMLIFrameElement_Release(nsiframe);
928 return;
931 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLFrameElement, (void**)&nsframe);
932 if(NS_SUCCEEDED(nsres)) {
933 TRACE("frame node\n");
935 add_script_runner(This, run_bind_to_tree, (nsISupports*)nsframe, NULL);
936 nsIDOMHTMLFrameElement_Release(nsframe);
937 return;
940 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLScriptElement, (void**)&nsscript);
941 if(NS_SUCCEEDED(nsres)) {
942 TRACE("script element\n");
944 add_script_runner(This, run_bind_to_tree, (nsISupports*)nsscript, NULL);
945 nsIDOMHTMLScriptElement_Release(nsscript);
946 return;
949 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLMetaElement, (void**)&nsmeta);
950 if(NS_SUCCEEDED(nsres)) {
951 process_meta_element(This, nsmeta);
952 nsIDOMHTMLMetaElement_Release(nsmeta);
956 static void NSAPI nsDocumentObserver_AttemptToExecuteScript(nsIDocumentObserver *iface, nsIContent *aContent,
957 nsIParser *aParser, cpp_bool *aBlock)
959 HTMLDocumentNode *This = impl_from_nsIDocumentObserver(iface);
960 nsIDOMHTMLScriptElement *nsscript;
961 nsresult nsres;
963 TRACE("(%p)->(%p %p %p)\n", This, aContent, aParser, aBlock);
965 nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMHTMLScriptElement, (void**)&nsscript);
966 if(NS_SUCCEEDED(nsres)) {
967 TRACE("script node\n");
969 lock_document_mode(This);
970 add_script_runner(This, run_insert_script, (nsISupports*)nsscript, (nsISupports*)aParser);
971 nsIDOMHTMLScriptElement_Release(nsscript);
975 static const nsIDocumentObserverVtbl nsDocumentObserverVtbl = {
976 nsDocumentObserver_QueryInterface,
977 nsDocumentObserver_AddRef,
978 nsDocumentObserver_Release,
979 nsDocumentObserver_CharacterDataWillChange,
980 nsDocumentObserver_CharacterDataChanged,
981 nsDocumentObserver_AttributeWillChange,
982 nsDocumentObserver_AttributeChanged,
983 nsDocumentObserver_NativeAnonymousChildListChange,
984 nsDocumentObserver_AttributeSetToCurrentValue,
985 nsDocumentObserver_ContentAppended,
986 nsDocumentObserver_ContentInserted,
987 nsDocumentObserver_ContentRemoved,
988 nsDocumentObserver_NodeWillBeDestroyed,
989 nsDocumentObserver_ParentChainChanged,
990 nsDocumentObserver_BeginUpdate,
991 nsDocumentObserver_EndUpdate,
992 nsDocumentObserver_BeginLoad,
993 nsDocumentObserver_EndLoad,
994 nsDocumentObserver_ContentStatesChanged,
995 nsDocumentObserver_DocumentStatesChanged,
996 nsDocumentObserver_StyleSheetAdded,
997 nsDocumentObserver_StyleSheetRemoved,
998 nsDocumentObserver_StyleSheetApplicableStateChanged,
999 nsDocumentObserver_StyleRuleChanged,
1000 nsDocumentObserver_StyleRuleAdded,
1001 nsDocumentObserver_StyleRuleRemoved,
1002 nsDocumentObserver_BindToDocument,
1003 nsDocumentObserver_AttemptToExecuteScript
1006 void init_document_mutation(HTMLDocumentNode *doc)
1008 nsIDocument *nsdoc;
1009 nsresult nsres;
1011 doc->nsIDocumentObserver_iface.lpVtbl = &nsDocumentObserverVtbl;
1013 nsres = nsIDOMDocument_QueryInterface(doc->dom_document, &IID_nsIDocument, (void**)&nsdoc);
1014 if(NS_FAILED(nsres)) {
1015 ERR("Could not get nsIDocument: %08lx\n", nsres);
1016 return;
1019 nsIContentUtils_AddDocumentObserver(content_utils, nsdoc, &doc->nsIDocumentObserver_iface);
1020 nsIDocument_Release(nsdoc);
1023 void release_document_mutation(HTMLDocumentNode *doc)
1025 nsIDocument *nsdoc;
1026 nsresult nsres;
1028 nsres = nsIDOMDocument_QueryInterface(doc->dom_document, &IID_nsIDocument, (void**)&nsdoc);
1029 if(NS_FAILED(nsres)) {
1030 ERR("Could not get nsIDocument: %08lx\n", nsres);
1031 return;
1034 nsIContentUtils_RemoveDocumentObserver(content_utils, nsdoc, &doc->nsIDocumentObserver_iface);
1035 nsIDocument_Release(nsdoc);
1038 JSContext *get_context_from_document(nsIDOMDocument *nsdoc)
1040 nsIDocument *doc;
1041 JSContext *ctx;
1042 nsresult nsres;
1044 nsres = nsIDOMDocument_QueryInterface(nsdoc, &IID_nsIDocument, (void**)&doc);
1045 assert(nsres == NS_OK);
1047 ctx = nsIContentUtils_GetContextFromDocument(content_utils, doc);
1048 nsIDocument_Release(doc);
1050 TRACE("ret %p\n", ctx);
1051 return ctx;
1054 void init_mutation(nsIComponentManager *component_manager)
1056 nsIFactory *factory;
1057 nsresult nsres;
1059 if(!component_manager) {
1060 if(content_utils) {
1061 nsIContentUtils_Release(content_utils);
1062 content_utils = NULL;
1064 return;
1067 nsres = nsIComponentManager_GetClassObject(component_manager, &NS_ICONTENTUTILS_CID,
1068 &IID_nsIFactory, (void**)&factory);
1069 if(NS_FAILED(nsres)) {
1070 ERR("Could not create nsIContentUtils service: %08lx\n", nsres);
1071 return;
1074 nsres = nsIFactory_CreateInstance(factory, NULL, &IID_nsIContentUtils, (void**)&content_utils);
1075 nsIFactory_Release(factory);
1076 if(NS_FAILED(nsres))
1077 ERR("Could not create nsIContentUtils instance: %08lx\n", nsres);
1080 struct mutation_observer {
1081 IWineMSHTMLMutationObserver IWineMSHTMLMutationObserver_iface;
1083 DispatchEx dispex;
1084 IDispatch *callback;
1087 static inline struct mutation_observer *impl_from_IWineMSHTMLMutationObserver(IWineMSHTMLMutationObserver *iface)
1089 return CONTAINING_RECORD(iface, struct mutation_observer, IWineMSHTMLMutationObserver_iface);
1092 DISPEX_IDISPATCH_IMPL(MutationObserver, IWineMSHTMLMutationObserver,
1093 impl_from_IWineMSHTMLMutationObserver(iface)->dispex)
1095 static HRESULT WINAPI MutationObserver_disconnect(IWineMSHTMLMutationObserver *iface)
1097 struct mutation_observer *This = impl_from_IWineMSHTMLMutationObserver(iface);
1099 FIXME("(%p), stub\n", This);
1101 return S_OK;
1104 static HRESULT WINAPI MutationObserver_observe(IWineMSHTMLMutationObserver *iface, IHTMLDOMNode *target,
1105 IDispatch *options)
1107 struct mutation_observer *This = impl_from_IWineMSHTMLMutationObserver(iface);
1109 FIXME("(%p)->(%p %p), stub\n", This, target, options);
1111 return S_OK;
1114 static HRESULT WINAPI MutationObserver_takeRecords(IWineMSHTMLMutationObserver *iface, IDispatch **ret)
1116 struct mutation_observer *This = impl_from_IWineMSHTMLMutationObserver(iface);
1118 FIXME("(%p)->(%p), stub\n", This, ret);
1120 return E_NOTIMPL;
1123 static const IWineMSHTMLMutationObserverVtbl WineMSHTMLMutationObserverVtbl = {
1124 MutationObserver_QueryInterface,
1125 MutationObserver_AddRef,
1126 MutationObserver_Release,
1127 MutationObserver_GetTypeInfoCount,
1128 MutationObserver_GetTypeInfo,
1129 MutationObserver_GetIDsOfNames,
1130 MutationObserver_Invoke,
1131 MutationObserver_disconnect,
1132 MutationObserver_observe,
1133 MutationObserver_takeRecords
1136 static inline struct mutation_observer *mutation_observer_from_DispatchEx(DispatchEx *iface)
1138 return CONTAINING_RECORD(iface, struct mutation_observer, dispex);
1141 static void *mutation_observer_query_interface(DispatchEx *dispex, REFIID riid)
1143 struct mutation_observer *This = mutation_observer_from_DispatchEx(dispex);
1145 if(IsEqualGUID(&IID_IWineMSHTMLMutationObserver, riid))
1146 return &This->IWineMSHTMLMutationObserver_iface;
1148 return NULL;
1151 static void mutation_observer_traverse(DispatchEx *dispex, nsCycleCollectionTraversalCallback *cb)
1153 struct mutation_observer *This = mutation_observer_from_DispatchEx(dispex);
1154 if(This->callback)
1155 note_cc_edge((nsISupports*)This->callback, "callback", cb);
1158 static void mutation_observer_unlink(DispatchEx *dispex)
1160 struct mutation_observer *This = mutation_observer_from_DispatchEx(dispex);
1161 unlink_ref(&This->callback);
1164 static void mutation_observer_destructor(DispatchEx *dispex)
1166 struct mutation_observer *This = mutation_observer_from_DispatchEx(dispex);
1167 free(This);
1170 static const dispex_static_data_vtbl_t mutation_observer_dispex_vtbl = {
1171 .query_interface = mutation_observer_query_interface,
1172 .destructor = mutation_observer_destructor,
1173 .traverse = mutation_observer_traverse,
1174 .unlink = mutation_observer_unlink
1177 static const tid_t mutation_observer_iface_tids[] = {
1178 IWineMSHTMLMutationObserver_tid,
1181 static dispex_static_data_t mutation_observer_dispex = {
1182 "MutationObserver",
1183 &mutation_observer_dispex_vtbl,
1184 IWineMSHTMLMutationObserver_tid,
1185 mutation_observer_iface_tids
1188 static HRESULT create_mutation_observer(compat_mode_t compat_mode, IDispatch *callback,
1189 IWineMSHTMLMutationObserver **ret)
1191 struct mutation_observer *obj;
1193 TRACE("(compat_mode = %d, callback = %p, ret = %p)\n", compat_mode, callback, ret);
1195 obj = calloc(1, sizeof(*obj));
1196 if(!obj)
1198 ERR("No memory.\n");
1199 return E_OUTOFMEMORY;
1202 obj->IWineMSHTMLMutationObserver_iface.lpVtbl = &WineMSHTMLMutationObserverVtbl;
1203 init_dispatch(&obj->dispex, &mutation_observer_dispex, compat_mode);
1205 IDispatch_AddRef(callback);
1206 obj->callback = callback;
1207 *ret = &obj->IWineMSHTMLMutationObserver_iface;
1208 return S_OK;
1211 struct mutation_observer_ctor {
1212 DispatchEx dispex;
1215 static inline struct mutation_observer_ctor *mutation_observer_ctor_from_DispatchEx(DispatchEx *iface)
1217 return CONTAINING_RECORD(iface, struct mutation_observer_ctor, dispex);
1220 static void mutation_observer_ctor_destructor(DispatchEx *dispex)
1222 struct mutation_observer_ctor *This = mutation_observer_ctor_from_DispatchEx(dispex);
1223 free(This);
1226 static HRESULT mutation_observer_ctor_value(DispatchEx *dispex, LCID lcid,
1227 WORD flags, DISPPARAMS *params, VARIANT *res, EXCEPINFO *ei,
1228 IServiceProvider *caller)
1230 struct mutation_observer_ctor *This = mutation_observer_ctor_from_DispatchEx(dispex);
1231 VARIANT *callback;
1232 IWineMSHTMLMutationObserver *mutation_observer;
1233 HRESULT hres;
1234 int argc = params->cArgs - params->cNamedArgs;
1236 TRACE("(%p)->(%lx %x %p %p %p %p)\n", This, lcid, flags, params, res, ei, caller);
1238 switch (flags) {
1239 case DISPATCH_METHOD | DISPATCH_PROPERTYGET:
1240 if (!res)
1241 return E_INVALIDARG;
1242 case DISPATCH_CONSTRUCT:
1243 case DISPATCH_METHOD:
1244 break;
1245 default:
1246 FIXME("flags %x is not supported\n", flags);
1247 return E_NOTIMPL;
1250 if (argc < 1)
1251 return E_UNEXPECTED;
1253 callback = params->rgvarg + (params->cArgs - 1);
1254 if (V_VT(callback) != VT_DISPATCH) {
1255 FIXME("Should return TypeMismatchError\n");
1256 return E_FAIL;
1259 if (!res)
1260 return S_OK;
1262 hres = create_mutation_observer(dispex_compat_mode(&This->dispex), V_DISPATCH(callback),
1263 &mutation_observer);
1264 if (FAILED(hres))
1265 return hres;
1267 V_VT(res) = VT_DISPATCH;
1268 V_DISPATCH(res) = (IDispatch*)mutation_observer;
1270 return S_OK;
1273 static dispex_static_data_vtbl_t mutation_observer_ctor_dispex_vtbl = {
1274 .destructor = mutation_observer_ctor_destructor,
1275 .value = mutation_observer_ctor_value
1278 static const tid_t mutation_observer_ctor_iface_tids[] = {
1282 static dispex_static_data_t mutation_observer_ctor_dispex = {
1283 "Function",
1284 &mutation_observer_ctor_dispex_vtbl,
1285 NULL_tid,
1286 mutation_observer_ctor_iface_tids
1289 HRESULT create_mutation_observer_ctor(compat_mode_t compat_mode, IDispatch **ret)
1291 struct mutation_observer_ctor *obj;
1293 TRACE("(compat_mode = %d, ret = %p)\n", compat_mode, ret);
1295 obj = calloc(1, sizeof(*obj));
1296 if(!obj)
1298 ERR("No memory.\n");
1299 return E_OUTOFMEMORY;
1302 init_dispatch(&obj->dispex, &mutation_observer_ctor_dispex, compat_mode);
1304 *ret = (IDispatch *)&obj->dispex.IWineJSDispatchHost_iface;
1305 return S_OK;