comdlg32: Use unicode APIs to react to the change printer combo.
[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_SCRIPT
41 };
42
43 void set_mutation_observer(NSContainer *nscontainer, nsIDOMHTMLDocument *nshtmldoc)
44 {
45     nsIDOMNSDocument *nsdoc;
46     nsresult nsres;
47
48     nsres = nsIDOMHTMLDocument_QueryInterface(nshtmldoc, &IID_nsIDOMNSDocument, (void**)&nsdoc);
49     if(NS_FAILED(nsres)) {
50         ERR("Could not get nsIDOMNSDocument: %08x\n", nsres);
51         return;
52     }
53
54     nsIDOMNSDocument_WineAddObserver(nsdoc, NSDOCOBS(nscontainer));
55     nsIDOMNSDocument_Release(nsdoc);
56 }
57
58 #define IE_MAJOR_VERSION 7
59 #define IE_MINOR_VERSION 0
60
61 static BOOL handle_insert_comment(HTMLDocument *doc, const PRUnichar *comment)
62 {
63     DWORD len;
64     int majorv = 0, minorv = 0;
65     const PRUnichar *ptr, *end;
66     nsAString nsstr;
67     PRUnichar *buf;
68     nsresult nsres;
69
70     enum {
71         CMP_EQ,
72         CMP_LT,
73         CMP_LTE,
74         CMP_GT,
75         CMP_GTE
76     } cmpt = CMP_EQ;
77
78     static const PRUnichar endifW[] = {'<','!','[','e','n','d','i','f',']'};
79
80     if(comment[0] != '[' || comment[1] != 'i' || comment[2] != 'f')
81         return FALSE;
82
83     ptr = comment+3;
84     while(isspaceW(*ptr))
85         ptr++;
86
87     if(ptr[0] == 'l' && ptr[1] == 't') {
88         ptr += 2;
89         if(*ptr == 'e') {
90             cmpt = CMP_LTE;
91             ptr++;
92         }else {
93             cmpt = CMP_LT;
94         }
95     }else if(ptr[0] == 'g' && ptr[1] == 't') {
96         ptr += 2;
97         if(*ptr == 'e') {
98             cmpt = CMP_GTE;
99             ptr++;
100         }else {
101             cmpt = CMP_GT;
102         }
103     }
104
105     if(!isspaceW(*ptr++))
106         return FALSE;
107     while(isspaceW(*ptr))
108         ptr++;
109
110     if(ptr[0] != 'I' || ptr[1] != 'E')
111         return FALSE;
112
113     ptr +=2;
114     if(!isspaceW(*ptr++))
115         return FALSE;
116     while(isspaceW(*ptr))
117         ptr++;
118
119     if(!isdigitW(*ptr))
120         return FALSE;
121     while(isdigitW(*ptr))
122         majorv = majorv*10 + (*ptr++ - '0');
123
124     if(*ptr == '.') {
125         ptr++;
126         if(!isdigitW(*ptr))
127             return FALSE;
128         while(isdigitW(*ptr))
129             minorv = minorv*10 + (*ptr++ - '0');
130     }
131
132     while(isspaceW(*ptr))
133         ptr++;
134     if(ptr[0] != ']' || ptr[1] != '>')
135         return FALSE;
136     ptr += 2;
137
138     len = strlenW(ptr);
139     if(len < sizeof(endifW)/sizeof(WCHAR))
140         return FALSE;
141
142     end = ptr + len-sizeof(endifW)/sizeof(WCHAR);
143     if(memcmp(end, endifW, sizeof(endifW)))
144         return FALSE;
145
146     switch(cmpt) {
147     case CMP_EQ:
148         if(majorv == IE_MAJOR_VERSION && minorv == IE_MINOR_VERSION)
149             break;
150         return FALSE;
151     case CMP_LT:
152         if(majorv > IE_MAJOR_VERSION)
153             break;
154         if(majorv == IE_MAJOR_VERSION && minorv > IE_MINOR_VERSION)
155             break;
156         return FALSE;
157     case CMP_LTE:
158         if(majorv > IE_MAJOR_VERSION)
159             break;
160         if(majorv == IE_MAJOR_VERSION && minorv >= IE_MINOR_VERSION)
161             break;
162         return FALSE;
163     case CMP_GT:
164         if(majorv < IE_MAJOR_VERSION)
165             break;
166         if(majorv == IE_MAJOR_VERSION && minorv < IE_MINOR_VERSION)
167             break;
168         return FALSE;
169     case CMP_GTE:
170         if(majorv < IE_MAJOR_VERSION)
171             break;
172         if(majorv == IE_MAJOR_VERSION && minorv <= IE_MINOR_VERSION)
173             break;
174         return FALSE;
175     }
176
177     buf = heap_alloc((end-ptr+1)*sizeof(WCHAR));
178     if(!buf)
179         return FALSE;
180
181     memcpy(buf, ptr, (end-ptr)*sizeof(WCHAR));
182     buf[end-ptr] = 0;
183     nsAString_Init(&nsstr, buf);
184     heap_free(buf);
185
186     /* FIXME: Find better way to insert HTML to document. */
187     nsres = nsIDOMHTMLDocument_Write(doc->nsdoc, &nsstr);
188     nsAString_Finish(&nsstr);
189     if(NS_FAILED(nsres)) {
190         ERR("Write failed: %08x\n", nsres);
191         return FALSE;
192     }
193
194     return TRUE;
195 }
196
197 static void add_script_runner(NSContainer *This)
198 {
199     nsIDOMNSDocument *nsdoc;
200     nsresult nsres;
201
202     nsres = nsIDOMHTMLDocument_QueryInterface(This->doc->nsdoc, &IID_nsIDOMNSDocument, (void**)&nsdoc);
203     if(NS_FAILED(nsres)) {
204         ERR("Could not get nsIDOMNSDocument: %08x\n", nsres);
205         return;
206     }
207
208     nsIDOMNSDocument_WineAddScriptRunner(nsdoc, NSRUNNABLE(This));
209     nsIDOMNSDocument_Release(nsdoc);
210 }
211
212 #define NSRUNNABLE_THIS(iface) DEFINE_THIS(NSContainer, Runnable, iface)
213
214 static nsresult NSAPI nsRunnable_QueryInterface(nsIRunnable *iface,
215         nsIIDRef riid, nsQIResult result)
216 {
217     NSContainer *This = NSRUNNABLE_THIS(iface);
218
219     if(IsEqualGUID(riid, &IID_nsISupports)) {
220         TRACE("(%p)->(IID_nsISupports %p)\n", This, result);
221         *result = NSRUNNABLE(This);
222     }else if(IsEqualGUID(riid, &IID_nsIRunnable)) {
223         TRACE("(%p)->(IID_nsIRunnable %p)\n", This, result);
224         *result = NSRUNNABLE(This);
225     }else {
226         *result = NULL;
227         WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), result);
228         return NS_NOINTERFACE;
229     }
230
231     nsISupports_AddRef((nsISupports*)*result);
232     return NS_OK;
233 }
234
235 static nsrefcnt NSAPI nsRunnable_AddRef(nsIRunnable *iface)
236 {
237     NSContainer *This = NSRUNNABLE_THIS(iface);
238     return nsIWebBrowserChrome_AddRef(NSWBCHROME(This));
239 }
240
241 static nsrefcnt NSAPI nsRunnable_Release(nsIRunnable *iface)
242 {
243     NSContainer *This = NSRUNNABLE_THIS(iface);
244     return nsIWebBrowserChrome_Release(NSWBCHROME(This));
245 }
246
247 static void pop_mutation_queue(NSContainer *nscontainer)
248 {
249     mutation_queue_t *tmp = nscontainer->mutation_queue;
250
251     if(!tmp)
252         return;
253
254     nscontainer->mutation_queue = tmp->next;
255     if(!tmp->next)
256         nscontainer->mutation_queue_tail = NULL;
257
258     nsISupports_Release(tmp->nsiface);
259     heap_free(tmp);
260 }
261
262 static nsresult NSAPI nsRunnable_Run(nsIRunnable *iface)
263 {
264     NSContainer *This = NSRUNNABLE_THIS(iface);
265     nsresult nsres;
266
267     TRACE("(%p)\n", This);
268
269     while(This->mutation_queue) {
270         switch(This->mutation_queue->type) {
271         case MUTATION_COMMENT: {
272             nsIDOMComment *nscomment;
273             nsAString comment_str;
274             BOOL remove_comment = FALSE;
275
276             nsres = nsISupports_QueryInterface(This->mutation_queue->nsiface, &IID_nsIDOMComment, (void**)&nscomment);
277             if(NS_FAILED(nsres)) {
278                 ERR("Could not get nsIDOMComment iface:%08x\n", nsres);
279                 return NS_OK;
280             }
281
282             nsAString_Init(&comment_str, NULL);
283             nsres = nsIDOMComment_GetData(nscomment, &comment_str);
284             if(NS_SUCCEEDED(nsres)) {
285                 const PRUnichar *comment;
286
287                 nsAString_GetData(&comment_str, &comment);
288                 remove_comment = handle_insert_comment(This->doc, comment);
289             }
290
291             nsAString_Finish(&comment_str);
292
293             if(remove_comment) {
294                 nsIDOMNode *nsparent, *tmp;
295                 nsAString magic_str;
296
297                 static const PRUnichar remove_comment_magicW[] =
298                     {'#','!','w','i','n','e', 'r','e','m','o','v','e','!','#',0};
299
300                 nsAString_Init(&magic_str, remove_comment_magicW);
301                 nsres = nsIDOMComment_SetData(nscomment, &magic_str);
302                 nsAString_Finish(&magic_str);
303                 if(NS_FAILED(nsres))
304                     ERR("SetData failed: %08x\n", nsres);
305
306                 nsIDOMComment_GetParentNode(nscomment, &nsparent);
307                 if(nsparent) {
308                     nsIDOMNode_RemoveChild(nsparent, (nsIDOMNode*)nscomment, &tmp);
309                     nsIDOMNode_Release(nsparent);
310                     nsIDOMNode_Release(tmp);
311                 }
312             }
313
314             nsIDOMComment_Release(nscomment);
315             break;
316         }
317
318         case MUTATION_SCRIPT: {
319             nsIDOMHTMLScriptElement *nsscript;
320
321             nsres = nsISupports_QueryInterface(This->mutation_queue->nsiface, &IID_nsIDOMHTMLScriptElement,
322                                                (void**)&nsscript);
323             if(NS_FAILED(nsres)) {
324                 ERR("Could not get nsIDOMHTMLScriptElement: %08x\n", nsres);
325                 break;
326             }
327
328             doc_insert_script(This->doc, nsscript);
329             nsIDOMHTMLScriptElement_Release(nsscript);
330             break;
331         }
332
333         default:
334             ERR("invalid type %d\n", This->mutation_queue->type);
335         }
336
337         pop_mutation_queue(This);
338     }
339
340     return S_OK;
341 }
342
343 #undef NSRUNNABLE_THIS
344
345 static const nsIRunnableVtbl nsRunnableVtbl = {
346     nsRunnable_QueryInterface,
347     nsRunnable_AddRef,
348     nsRunnable_Release,
349     nsRunnable_Run
350 };
351
352 #define NSDOCOBS_THIS(iface) DEFINE_THIS(NSContainer, DocumentObserver, iface)
353
354 static nsresult NSAPI nsDocumentObserver_QueryInterface(nsIDocumentObserver *iface,
355         nsIIDRef riid, nsQIResult result)
356 {
357     NSContainer *This = NSDOCOBS_THIS(iface);
358
359     if(IsEqualGUID(&IID_nsISupports, riid)) {
360         TRACE("(%p)->(IID_nsISupports, %p)\n", This, result);
361         *result = NSWBCHROME(This);
362     }else if(IsEqualGUID(&IID_nsIMutationObserver, riid)) {
363         TRACE("(%p)->(IID_nsIMutationObserver %p)\n", This, result);
364         *result = NSDOCOBS(This);
365     }else if(IsEqualGUID(&IID_nsIDocumentObserver, riid)) {
366         TRACE("(%p)->(IID_nsIDocumentObserver %p)\n", This, result);
367         *result = NSDOCOBS(This);
368     }else {
369         *result = NULL;
370         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), result);
371         return NS_NOINTERFACE;
372     }
373
374     nsIWebBrowserChrome_AddRef(NSWBCHROME(This));
375     return NS_OK;
376 }
377
378 static nsrefcnt NSAPI nsDocumentObserver_AddRef(nsIDocumentObserver *iface)
379 {
380     NSContainer *This = NSDOCOBS_THIS(iface);
381     return nsIWebBrowserChrome_AddRef(NSWBCHROME(This));
382 }
383
384 static nsrefcnt NSAPI nsDocumentObserver_Release(nsIDocumentObserver *iface)
385 {
386     NSContainer *This = NSDOCOBS_THIS(iface);
387     return nsIWebBrowserChrome_Release(NSWBCHROME(This));
388 }
389
390 static void NSAPI nsDocumentObserver_CharacterDataWillChange(nsIDocumentObserver *iface,
391         nsIDocument *aDocument, nsIContent *aContent, void /*CharacterDataChangeInfo*/ *aInfo)
392 {
393 }
394
395 static void NSAPI nsDocumentObserver_CharacterDataChanged(nsIDocumentObserver *iface,
396         nsIDocument *aDocument, nsIContent *aContent, void /*CharacterDataChangeInfo*/ *aInfo)
397 {
398 }
399
400 static void NSAPI nsDocumentObserver_AttributeChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
401         nsIContent *aContent, PRInt32 aNameSpaceID, nsIAtom *aAttribute, PRInt32 aModType, PRUint32 aStateMask)
402 {
403 }
404
405 static void NSAPI nsDocumentObserver_ContentAppended(nsIDocumentObserver *iface, nsIDocument *aDocument,
406         nsIContent *aContainer, PRInt32 aNewIndexInContainer)
407 {
408 }
409
410 static void NSAPI nsDocumentObserver_ContentInserted(nsIDocumentObserver *iface, nsIDocument *aDocument,
411         nsIContent *aContainer, nsIContent *aChild, PRInt32 aIndexInContainer)
412 {
413 }
414
415 static void NSAPI nsDocumentObserver_ContentRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
416         nsIContent *aContainer, nsIContent *aChild, PRInt32 aIndexInContainer)
417 {
418 }
419
420 static void NSAPI nsDocumentObserver_NodeWillBeDestroyed(nsIDocumentObserver *iface, const nsINode *aNode)
421 {
422 }
423
424 static void NSAPI nsDocumentObserver_ParentChainChanged(nsIDocumentObserver *iface, nsIContent *aContent)
425 {
426 }
427
428 static void NSAPI nsDocumentObserver_BeginUpdate(nsIDocumentObserver *iface, nsIDocument *aDocument,
429         nsUpdateType aUpdateType)
430 {
431 }
432
433 static void NSAPI nsDocumentObserver_EndUpdate(nsIDocumentObserver *iface, nsIDocument *aDocument,
434         nsUpdateType aUpdateType)
435 {
436 }
437
438 static void NSAPI nsDocumentObserver_BeginLoad(nsIDocumentObserver *iface, nsIDocument *aDocument)
439 {
440 }
441
442 static void NSAPI nsDocumentObserver_EndLoad(nsIDocumentObserver *iface, nsIDocument *aDocument)
443 {
444 }
445
446 static void NSAPI nsDocumentObserver_ContentStatesChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
447         nsIContent *aContent1, nsIContent *aContent2, PRInt32 aStateMask)
448 {
449 }
450
451 static void NSAPI nsDocumentObserver_StyleSheetAdded(nsIDocumentObserver *iface, nsIDocument *aDocument,
452         nsIStyleSheet *aStyleSheet, PRBool aDocumentSheet)
453 {
454 }
455
456 static void NSAPI nsDocumentObserver_StyleSheetRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
457         nsIStyleSheet *aStyleSheet, PRBool aDocumentSheet)
458 {
459 }
460
461 static void NSAPI nsDocumentObserver_StyleSheetApplicableStateChanged(nsIDocumentObserver *iface,
462         nsIDocument *aDocument, nsIStyleSheet *aStyleSheet, PRBool aApplicable)
463 {
464 }
465
466 static void NSAPI nsDocumentObserver_StyleRuleChanged(nsIDocumentObserver *iface, nsIDocument *aDocument,
467         nsIStyleSheet *aStyleSheet, nsIStyleRule *aOldStyleRule, nsIStyleSheet *aNewStyleRule)
468 {
469 }
470
471 static void NSAPI nsDocumentObserver_StyleRuleAdded(nsIDocumentObserver *iface, nsIDocument *aDocument,
472         nsIStyleSheet *aStyleSheet, nsIStyleRule *aStyleRule)
473 {
474 }
475
476 static void NSAPI nsDocumentObserver_StyleRuleRemoved(nsIDocumentObserver *iface, nsIDocument *aDocument,
477         nsIStyleSheet *aStyleSheet, nsIStyleRule *aStyleRule)
478 {
479 }
480
481 static void push_mutation_queue(NSContainer *nscontainer, DWORD type, nsISupports *nsiface)
482 {
483     mutation_queue_t *elem;
484
485     elem = heap_alloc(sizeof(mutation_queue_t));
486     if(!elem)
487         return;
488
489     elem->next = NULL;
490     elem->type = type;
491     elem->nsiface = nsiface;
492     nsISupports_AddRef(nsiface);
493
494     if(nscontainer->mutation_queue_tail)
495         nscontainer->mutation_queue_tail = nscontainer->mutation_queue_tail->next = elem;
496     else
497         nscontainer->mutation_queue = nscontainer->mutation_queue_tail = elem;
498 }
499
500 static void NSAPI nsDocumentObserver_BindToDocument(nsIDocumentObserver *iface, nsIDocument *aDocument,
501         nsIContent *aContent)
502 {
503     NSContainer *This = NSDOCOBS_THIS(iface);
504     nsIDOMComment *nscomment;
505     nsIDOMElement *nselem;
506     nsresult nsres;
507
508     TRACE("(%p)\n", This);
509
510     nsres = nsISupports_QueryInterface(aContent, &IID_nsIDOMElement, (void**)&nselem);
511     if(NS_SUCCEEDED(nsres)) {
512         check_event_attr(This->doc, nselem);
513         nsIDOMElement_Release(nselem);
514     }
515
516     nsres = nsISupports_QueryInterface(aContent, &IID_nsIDOMComment, (void**)&nscomment);
517     if(NS_SUCCEEDED(nsres)) {
518         TRACE("comment node\n");
519
520         push_mutation_queue(This, MUTATION_COMMENT, (nsISupports*)nscomment);
521         nsIDOMComment_Release(nscomment);
522         add_script_runner(This);
523     }
524 }
525
526 static void NSAPI nsDocumentObserver_DoneAddingChildren(nsIDocumentObserver *iface, nsIContent *aContent,
527         PRBool aHaveNotified)
528 {
529     NSContainer *This = NSDOCOBS_THIS(iface);
530     nsIDOMHTMLScriptElement *nsscript;
531     nsresult nsres;
532
533     TRACE("(%p)->(%p %x)\n", This, aContent, aHaveNotified);
534
535     nsres = nsISupports_QueryInterface(aContent, &IID_nsIDOMHTMLScriptElement, (void**)&nsscript);
536     if(NS_SUCCEEDED(nsres)) {
537         push_mutation_queue(This, MUTATION_SCRIPT, (nsISupports*)nsscript);
538         nsIDOMHTMLScriptElement_Release(nsscript);
539         add_script_runner(This);
540     }
541 }
542
543 #undef NSMUTATIONOBS_THIS
544
545 static const nsIDocumentObserverVtbl nsDocumentObserverVtbl = {
546     nsDocumentObserver_QueryInterface,
547     nsDocumentObserver_AddRef,
548     nsDocumentObserver_Release,
549     nsDocumentObserver_CharacterDataWillChange,
550     nsDocumentObserver_CharacterDataChanged,
551     nsDocumentObserver_AttributeChanged,
552     nsDocumentObserver_ContentAppended,
553     nsDocumentObserver_ContentInserted,
554     nsDocumentObserver_ContentRemoved,
555     nsDocumentObserver_NodeWillBeDestroyed,
556     nsDocumentObserver_ParentChainChanged,
557     nsDocumentObserver_BeginUpdate,
558     nsDocumentObserver_EndUpdate,
559     nsDocumentObserver_BeginLoad,
560     nsDocumentObserver_EndLoad,
561     nsDocumentObserver_ContentStatesChanged,
562     nsDocumentObserver_StyleSheetAdded,
563     nsDocumentObserver_StyleSheetRemoved,
564     nsDocumentObserver_StyleSheetApplicableStateChanged,
565     nsDocumentObserver_StyleRuleChanged,
566     nsDocumentObserver_StyleRuleAdded,
567     nsDocumentObserver_StyleRuleRemoved,
568     nsDocumentObserver_BindToDocument,
569     nsDocumentObserver_DoneAddingChildren
570 };
571
572 void init_mutation(NSContainer *nscontainer)
573 {
574     nscontainer->lpDocumentObserverVtbl  = &nsDocumentObserverVtbl;
575     nscontainer->lpRunnableVtbl          = &nsRunnableVtbl;
576 }