mshtml: Added support for text/vbscript script type.
[wine] / dlls / mshtml / editor.c
1 /*
2  * Copyright 2006-2007 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 <stdarg.h>
20 #include <stdio.h>
21
22 #define COBJMACROS
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "ole2.h"
28 #include "mshtmcid.h"
29
30 #include "wine/debug.h"
31
32 #include "mshtml_private.h"
33 #include "resource.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
36
37 #define NSCMD_ALIGN        "cmd_align"
38 #define NSCMD_BEGINLINE    "cmd_beginLine"
39 #define NSCMD_BOLD         "cmd_bold"
40 #define NSCMD_CHARNEXT     "cmd_charNext"
41 #define NSCMD_CHARPREVIOUS "cmd_charPrevious"
42 #define NSCMD_COPY         "cmd_copy"
43 #define NSCMD_CUT          "cmd_cut"
44 #define NSCMD_DELETECHARFORWARD   "cmd_deleteCharForward"
45 #define NSCMD_DELETEWORDFORWARD   "cmd_deleteWordForward"
46 #define NSCMD_ENDLINE      "cmd_endLine"
47 #define NSCMD_FONTCOLOR    "cmd_fontColor"
48 #define NSCMD_FONTFACE     "cmd_fontFace"
49 #define NSCMD_INDENT       "cmd_indent"
50 #define NSCMD_INSERTHR     "cmd_insertHR"
51 #define NSCMD_INSERTLINKNOUI    "cmd_insertLinkNoUI"
52 #define NSCMD_ITALIC       "cmd_italic"
53 #define NSCMD_LINENEXT     "cmd_lineNext"
54 #define NSCMD_LINEPREVIOUS "cmd_linePrevious"
55 #define NSCMD_MOVEBOTTOM   "cmd_moveBottom"
56 #define NSCMD_MOVEPAGEDOWN "cmd_movePageDown"
57 #define NSCMD_MOVEPAGEUP   "cmd_movePageUp"
58 #define NSCMD_MOVETOP      "cmd_moveTop"
59 #define NSCMD_OL           "cmd_ol"
60 #define NSCMD_OUTDENT      "cmd_outdent"
61 #define NSCMD_PASTE        "cmd_paste"
62 #define NSCMD_SELECTALL           "cmd_selectAll"
63 #define NSCMD_SELECTBEGINLINE     "cmd_selectBeginLine"
64 #define NSCMD_SELECTBOTTOM        "cmd_selectBottom"
65 #define NSCMD_SELECTCHARNEXT      "cmd_selectCharNext"
66 #define NSCMD_SELECTCHARPREVIOUS  "cmd_selectCharPrevious"
67 #define NSCMD_SELECTENDLINE       "cmd_selectEndLine"
68 #define NSCMD_SELECTLINENEXT      "cmd_selectLineNext"
69 #define NSCMD_SELECTLINEPREVIOUS  "cmd_selectLinePrevious"
70 #define NSCMD_SELECTPAGEDOWN      "cmd_selectPageDown"
71 #define NSCMD_SELECTPAGEUP        "cmd_selectPageUp"
72 #define NSCMD_SELECTTOP           "cmd_selectTop"
73 #define NSCMD_SELECTWORDNEXT      "cmd_selectWordNext"
74 #define NSCMD_SELECTWORDPREVIOUS  "cmd_selectWordPrevious"
75 #define NSCMD_UL           "cmd_ul"
76 #define NSCMD_UNDERLINE    "cmd_underline"
77 #define NSCMD_WORDNEXT     "cmd_wordNext"
78 #define NSCMD_WORDPREVIOUS "cmd_wordPrevious"
79
80 #define NSSTATE_ATTRIBUTE "state_attribute"
81 #define NSSTATE_ALL       "state_all"
82
83 #define NSALIGN_CENTER "center"
84 #define NSALIGN_LEFT   "left"
85 #define NSALIGN_RIGHT  "right"
86
87 #define DOM_VK_LEFT     VK_LEFT
88 #define DOM_VK_UP       VK_UP
89 #define DOM_VK_RIGHT    VK_RIGHT
90 #define DOM_VK_DOWN     VK_DOWN
91 #define DOM_VK_DELETE   VK_DELETE
92 #define DOM_VK_HOME     VK_HOME
93 #define DOM_VK_END      VK_END
94
95 static const WCHAR fontW[] = {'f','o','n','t',0};
96 static const WCHAR sizeW[] = {'s','i','z','e',0};
97
98 void set_dirty(HTMLDocument *This, VARIANT_BOOL dirty)
99 {
100     nsresult nsres;
101
102     if(This->doc_obj->usermode != EDITMODE || !This->doc_obj->nscontainer || !This->doc_obj->nscontainer->editor)
103         return;
104
105     if(dirty) {
106         nsres = nsIEditor_IncrementModificationCount(This->doc_obj->nscontainer->editor, 1);
107         if(NS_FAILED(nsres))
108             ERR("IncrementModificationCount failed: %08x\n", nsres);
109     }else {
110         nsres = nsIEditor_ResetModificationCount(This->doc_obj->nscontainer->editor);
111         if(NS_FAILED(nsres))
112             ERR("ResetModificationCount failed: %08x\n", nsres);
113     }
114 }
115
116 static void do_ns_editor_command(NSContainer *This, const char *cmd)
117 {
118     nsresult nsres;
119
120     if(!This->editor_controller)
121         return;
122
123     nsres = nsIController_DoCommand(This->editor_controller, cmd);
124     if(NS_FAILED(nsres))
125         ERR("DoCommand(%s) failed: %08x\n", debugstr_a(cmd), nsres);
126 }
127
128 static nsresult get_ns_command_state(NSContainer *This, const char *cmd, nsICommandParams *nsparam)
129 {
130     nsICommandManager *cmdmgr;
131     nsresult nsres;
132
133     nsres = get_nsinterface((nsISupports*)This->webbrowser, &IID_nsICommandManager, (void**)&cmdmgr);
134     if(NS_FAILED(nsres)) {
135         ERR("Could not get nsICommandManager: %08x\n", nsres);
136         return nsres;
137     }
138
139     nsres = nsICommandManager_GetCommandState(cmdmgr, cmd, This->doc->basedoc.window->nswindow, nsparam);
140     if(NS_FAILED(nsres))
141         ERR("GetCommandState(%s) failed: %08x\n", debugstr_a(cmd), nsres);
142
143     nsICommandManager_Release(cmdmgr);
144     return nsres;
145 }
146
147 static DWORD query_ns_edit_status(HTMLDocument *This, const char *nscmd)
148 {
149     nsICommandParams *nsparam;
150     PRBool b = FALSE;
151
152     if(This->doc_obj->usermode != EDITMODE || This->window->readystate < READYSTATE_INTERACTIVE)
153         return OLECMDF_SUPPORTED;
154
155     if(This->doc_obj->nscontainer && nscmd) {
156         nsparam = create_nscommand_params();
157         get_ns_command_state(This->doc_obj->nscontainer, nscmd, nsparam);
158
159         nsICommandParams_GetBooleanValue(nsparam, NSSTATE_ALL, &b);
160
161         nsICommandParams_Release(nsparam);
162     }
163
164     return OLECMDF_SUPPORTED | OLECMDF_ENABLED | (b ? OLECMDF_LATCHED : 0);
165 }
166
167 static void set_ns_align(HTMLDocument *This, const char *align_str)
168 {
169     nsICommandParams *nsparam;
170
171     if(!This->doc_obj->nscontainer)
172         return;
173
174     nsparam = create_nscommand_params();
175     nsICommandParams_SetCStringValue(nsparam, NSSTATE_ATTRIBUTE, align_str);
176
177     do_ns_command(This, NSCMD_ALIGN, nsparam);
178
179     nsICommandParams_Release(nsparam);
180 }
181
182 static DWORD query_align_status(HTMLDocument *This, const WCHAR *align)
183 {
184     DWORD ret = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
185     nsIDOMNSHTMLDocument *nsdoc;
186     nsAString justify_str;
187     PRBool b;
188     nsresult nsres;
189
190     if(This->doc_obj->usermode != EDITMODE || This->window->readystate < READYSTATE_INTERACTIVE)
191         return OLECMDF_SUPPORTED;
192
193
194     nsres = nsIDOMHTMLDocument_QueryInterface(This->doc_node->nsdoc, &IID_nsIDOMNSHTMLDocument,
195             (void**)&nsdoc);
196     if(NS_FAILED(nsres)) {
197         ERR("Could not get nsIDOMNSHTMLDocument iface: %08x\n", nsres);
198         return 0;
199     }
200
201     nsAString_Init(&justify_str, align);
202     nsres = nsIDOMNSHTMLDocument_QueryCommandState(nsdoc, &justify_str, &b);
203     nsAString_Finish(&justify_str);
204     if(NS_SUCCEEDED(nsres) && b)
205         ret |= OLECMDF_LATCHED;
206
207     nsIDOMNSHTMLDocument_Release(nsdoc);
208     return ret;
209 }
210
211
212 static nsISelection *get_ns_selection(HTMLDocument *This)
213 {
214     nsISelection *nsselection = NULL;
215     nsresult nsres;
216
217     nsres = nsIDOMWindow_GetSelection(This->window->nswindow, &nsselection);
218     if(NS_FAILED(nsres))
219         ERR("GetSelection failed %08x\n", nsres);
220
221     return nsselection;
222
223 }
224
225 static void remove_child_attr(nsIDOMElement *elem, LPCWSTR tag, nsAString *attr_str)
226 {
227     PRBool has_children;
228     PRUint32 child_cnt, i;
229     nsIDOMNode *child_node;
230     nsIDOMNodeList *node_list;
231     PRUint16 node_type;
232
233     nsIDOMElement_HasChildNodes(elem, &has_children);
234     if(!has_children)
235         return;
236
237     nsIDOMElement_GetChildNodes(elem, &node_list);
238     nsIDOMNodeList_GetLength(node_list, &child_cnt);
239
240     for(i=0; i<child_cnt; i++) {
241         nsIDOMNodeList_Item(node_list, i, &child_node);
242
243         nsIDOMNode_GetNodeType(child_node, &node_type);
244         if(node_type == ELEMENT_NODE) {
245             nsIDOMElement *child_elem;
246             nsAString tag_str;
247             const PRUnichar *ctag;
248
249             nsIDOMNode_QueryInterface(child_node, &IID_nsIDOMElement, (void**)&child_elem);
250
251             nsAString_Init(&tag_str, NULL);
252             nsIDOMElement_GetTagName(child_elem, &tag_str);
253             nsAString_GetData(&tag_str, &ctag);
254
255             if(!strcmpiW(ctag, tag))
256                 /* FIXME: remove node if there are no more attributes */
257                 nsIDOMElement_RemoveAttribute(child_elem, attr_str);
258
259             nsAString_Finish(&tag_str);
260
261             remove_child_attr(child_elem, tag, attr_str);
262
263             nsIDOMNode_Release(child_elem);
264         }
265
266         nsIDOMNode_Release(child_node);
267     }
268
269     nsIDOMNodeList_Release(node_list);
270 }
271
272 static void get_font_size(HTMLDocument *This, WCHAR *ret)
273 {
274     nsISelection *nsselection = get_ns_selection(This);
275     nsIDOMElement *elem = NULL;
276     nsIDOMNode *node = NULL, *tmp_node;
277     nsAString tag_str;
278     LPCWSTR tag;
279     PRUint16 node_type;
280     nsresult nsres;
281
282     *ret = 0;
283
284     if(!nsselection)
285         return;
286
287     nsISelection_GetFocusNode(nsselection, &node);
288     nsISelection_Release(nsselection);
289
290     while(node) {
291         nsres = nsIDOMNode_GetNodeType(node, &node_type);
292         if(NS_FAILED(nsres) || node_type == DOCUMENT_NODE)
293             break;
294
295         if(node_type == ELEMENT_NODE) {
296             nsIDOMNode_QueryInterface(node, &IID_nsIDOMElement, (void**)&elem);
297
298             nsAString_Init(&tag_str, NULL);
299             nsIDOMElement_GetTagName(elem, &tag_str);
300             nsAString_GetData(&tag_str, &tag);
301
302             if(!strcmpiW(tag, fontW)) {
303                 nsAString size_str, val_str;
304                 LPCWSTR val;
305
306                 TRACE("found font tag %p\n", elem);
307
308                 nsAString_InitDepend(&size_str, sizeW);
309                 nsAString_Init(&val_str, NULL);
310
311                 nsIDOMElement_GetAttribute(elem, &size_str, &val_str);
312                 nsAString_GetData(&val_str, &val);
313
314                 if(*val) {
315                     TRACE("found size %s\n", debugstr_w(val));
316                     strcpyW(ret, val);
317                 }
318
319                 nsAString_Finish(&size_str);
320                 nsAString_Finish(&val_str);
321             }
322
323             nsAString_Finish(&tag_str);
324
325             nsIDOMElement_Release(elem);
326         }
327
328         if(*ret)
329             break;
330
331         tmp_node = node;
332         nsIDOMNode_GetParentNode(node, &node);
333         nsIDOMNode_Release(tmp_node);
334     }
335
336     if(node)
337         nsIDOMNode_Release(node);
338 }
339
340 static void set_font_size(HTMLDocument *This, LPCWSTR size)
341 {
342     nsISelection *nsselection;
343     PRBool collapsed;
344     nsIDOMHTMLElement *elem;
345     nsIDOMRange *range;
346     PRInt32 range_cnt = 0;
347     nsAString size_str;
348     nsAString val_str;
349
350     if(!This->doc_node->nsdoc) {
351         WARN("NULL nsdoc\n");
352         return;
353     }
354
355     nsselection = get_ns_selection(This);
356     if(!nsselection)
357         return;
358
359     nsISelection_GetRangeCount(nsselection, &range_cnt);
360     if(range_cnt != 1) {
361         FIXME("range_cnt %d not supprted\n", range_cnt);
362         if(!range_cnt) {
363             nsISelection_Release(nsselection);
364             return;
365         }
366     }
367
368     create_nselem(This->doc_node, fontW, &elem);
369
370     nsAString_InitDepend(&size_str, sizeW);
371     nsAString_InitDepend(&val_str, size);
372
373     nsIDOMElement_SetAttribute(elem, &size_str, &val_str);
374     nsAString_Finish(&val_str);
375
376     nsISelection_GetRangeAt(nsselection, 0, &range);
377     nsISelection_GetIsCollapsed(nsselection, &collapsed);
378     nsISelection_RemoveAllRanges(nsselection);
379
380     nsIDOMRange_SurroundContents(range, (nsIDOMNode*)elem);
381
382     if(collapsed) {
383         nsISelection_Collapse(nsselection, (nsIDOMNode*)elem, 0);
384     }else {
385         /* Remove all size attributes from the range */
386         remove_child_attr((nsIDOMElement*)elem, fontW, &size_str);
387         nsISelection_SelectAllChildren(nsselection, (nsIDOMNode*)elem);
388     }
389
390     nsISelection_Release(nsselection);
391     nsIDOMRange_Release(range);
392     nsIDOMElement_Release(elem);
393
394     nsAString_Finish(&size_str);
395
396     set_dirty(This, VARIANT_TRUE);
397 }
398
399 static void handle_arrow_key(HTMLDocument *This, nsIDOMKeyEvent *event, const char * const cmds[4])
400 {
401     int i=0;
402     PRBool b;
403
404     nsIDOMKeyEvent_GetCtrlKey(event, &b);
405     if(b)
406         i |= 1;
407
408     nsIDOMKeyEvent_GetShiftKey(event, &b);
409     if(b)
410         i |= 2;
411
412     if(cmds[i])
413         do_ns_editor_command(This->doc_obj->nscontainer, cmds[i]);
414
415     nsIDOMKeyEvent_PreventDefault(event);
416 }
417
418 void handle_edit_event(HTMLDocument *This, nsIDOMEvent *event)
419 {
420     nsIDOMKeyEvent *key_event;
421     PRUint32 code;
422
423     nsIDOMEvent_QueryInterface(event, &IID_nsIDOMKeyEvent, (void**)&key_event);
424
425     nsIDOMKeyEvent_GetKeyCode(key_event, &code);
426
427     switch(code) {
428     case DOM_VK_LEFT: {
429         static const char * const cmds[] = {
430             NSCMD_CHARPREVIOUS,
431             NSCMD_WORDPREVIOUS,
432             NSCMD_SELECTCHARPREVIOUS,
433             NSCMD_SELECTWORDPREVIOUS
434         };
435
436         TRACE("left\n");
437         handle_arrow_key(This, key_event, cmds);
438         break;
439     }
440     case DOM_VK_RIGHT: {
441         static const char * const cmds[] = {
442             NSCMD_CHARNEXT,
443             NSCMD_WORDNEXT,
444             NSCMD_SELECTCHARNEXT,
445             NSCMD_SELECTWORDNEXT
446         };
447
448         TRACE("right\n");
449         handle_arrow_key(This, key_event, cmds);
450         break;
451     }
452     case DOM_VK_UP: {
453         static const char * const cmds[] = {
454             NSCMD_LINEPREVIOUS,
455             NSCMD_MOVEPAGEUP,
456             NSCMD_SELECTLINEPREVIOUS,
457             NSCMD_SELECTPAGEUP
458         };
459
460         TRACE("up\n");
461         handle_arrow_key(This, key_event, cmds);
462         break;
463     }
464     case DOM_VK_DOWN: {
465         static const char * const cmds[] = {
466             NSCMD_LINENEXT,
467             NSCMD_MOVEPAGEDOWN,
468             NSCMD_SELECTLINENEXT,
469             NSCMD_SELECTPAGEDOWN
470         };
471
472         TRACE("down\n");
473         handle_arrow_key(This, key_event, cmds);
474         break;
475     }
476     case DOM_VK_DELETE: {
477         static const char * const cmds[] = {
478             NSCMD_DELETECHARFORWARD,
479             NSCMD_DELETEWORDFORWARD,
480             NULL, NULL
481         };
482
483         TRACE("delete\n");
484         handle_arrow_key(This, key_event, cmds);
485         break;
486     }
487     case DOM_VK_HOME: {
488         static const char * const cmds[] = {
489             NSCMD_BEGINLINE,
490             NSCMD_MOVETOP,
491             NSCMD_SELECTBEGINLINE,
492             NSCMD_SELECTTOP
493         };
494
495         TRACE("home\n");
496         handle_arrow_key(This, key_event, cmds);
497         break;
498     }
499     case DOM_VK_END: {
500         static const char * const cmds[] = {
501             NSCMD_ENDLINE,
502             NSCMD_MOVEBOTTOM,
503             NSCMD_SELECTENDLINE,
504             NSCMD_SELECTBOTTOM
505         };
506
507         TRACE("end\n");
508         handle_arrow_key(This, key_event, cmds);
509         break;
510     }
511     }
512
513     nsIDOMKeyEvent_Release(key_event);
514 }
515
516 void handle_edit_load(HTMLDocument *This)
517 {
518     get_editor_controller(This->doc_obj->nscontainer);
519 }
520
521 static void set_ns_fontname(HTMLDocument *This, const char *fontname)
522 {
523     nsICommandParams *nsparam = create_nscommand_params();
524
525     nsICommandParams_SetCStringValue(nsparam, NSSTATE_ATTRIBUTE, fontname);
526     do_ns_command(This, NSCMD_FONTFACE, nsparam);
527     nsICommandParams_Release(nsparam);
528 }
529
530 static HRESULT exec_delete(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
531 {
532     TRACE("(%p)->(%p %p)\n", This, in, out);
533
534     if(This->doc_obj->nscontainer)
535         do_ns_editor_command(This->doc_obj->nscontainer, NSCMD_DELETECHARFORWARD);
536
537     update_doc(This, UPDATE_UI);
538     return S_OK;
539 }
540
541 static HRESULT exec_fontname(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
542 {
543     TRACE("(%p)->(%p %p)\n", This, in, out);
544
545     if(!This->doc_obj->nscontainer) {
546         update_doc(This, UPDATE_UI);
547         return E_FAIL;
548     }
549
550     if(in) {
551         char *stra;
552
553         if(V_VT(in) != VT_BSTR) {
554             FIXME("Unsupported vt=%d\n", V_VT(out));
555             return E_INVALIDARG;
556         }
557
558         TRACE("%s\n", debugstr_w(V_BSTR(in)));
559
560         stra = heap_strdupWtoA(V_BSTR(in));
561         set_ns_fontname(This, stra);
562         heap_free(stra);
563
564         update_doc(This, UPDATE_UI);
565     }
566
567     if(out) {
568         nsICommandParams *nsparam;
569         LPWSTR strw;
570         char *stra;
571         DWORD len;
572         nsresult nsres;
573
574         V_VT(out) = VT_BSTR;
575         V_BSTR(out) = NULL;
576
577         nsparam = create_nscommand_params();
578
579         nsres = get_ns_command_state(This->doc_obj->nscontainer, NSCMD_FONTFACE, nsparam);
580         if(NS_FAILED(nsres))
581             return S_OK;
582
583         nsICommandParams_GetCStringValue(nsparam, NSSTATE_ATTRIBUTE, &stra);
584         nsICommandParams_Release(nsparam);
585
586         len = MultiByteToWideChar(CP_ACP, 0, stra, -1, NULL, 0);
587         strw = heap_alloc(len*sizeof(WCHAR));
588         MultiByteToWideChar(CP_ACP, 0, stra, -1, strw, len);
589         nsfree(stra);
590
591         V_BSTR(out) = SysAllocString(strw);
592         heap_free(strw);
593     }
594
595     return S_OK;
596 }
597
598 static HRESULT exec_forecolor(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
599 {
600     TRACE("(%p)->(%p %p)\n", This, in, out);
601
602     if(in) {
603         if(V_VT(in) == VT_I4) {
604             nsICommandParams *nsparam = create_nscommand_params();
605             char color_str[10];
606
607             sprintf(color_str, "#%02x%02x%02x",
608                     V_I4(in)&0xff, (V_I4(in)>>8)&0xff, (V_I4(in)>>16)&0xff);
609
610             nsICommandParams_SetCStringValue(nsparam, NSSTATE_ATTRIBUTE, color_str);
611             do_ns_command(This, NSCMD_FONTCOLOR, nsparam);
612
613             nsICommandParams_Release(nsparam);
614         }else {
615             FIXME("unsupported in vt=%d\n", V_VT(in));
616         }
617
618         update_doc(This, UPDATE_UI);
619     }
620
621     if(out) {
622         FIXME("unsupported out\n");
623         return E_NOTIMPL;
624     }
625
626     return S_OK;
627 }
628
629 static HRESULT exec_fontsize(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
630 {
631     TRACE("(%p)->(%p %p)\n", This, in, out);
632
633     if(out) {
634         WCHAR val[10] = {0};
635
636         get_font_size(This, val);
637         V_VT(out) = VT_I4;
638         V_I4(out) = strtolW(val, NULL, 10);
639     }
640
641     if(in) {
642         switch(V_VT(in)) {
643         case VT_I4: {
644             WCHAR size[10];
645             static const WCHAR format[] = {'%','d',0};
646             wsprintfW(size, format, V_I4(in));
647             set_font_size(This, size);
648             break;
649         }
650         case VT_BSTR:
651             set_font_size(This, V_BSTR(in));
652             break;
653         default:
654             FIXME("unsupported vt %d\n", V_VT(in));
655         }
656
657         update_doc(This, UPDATE_UI);
658     }
659
660     return S_OK;
661 }
662
663 static HRESULT exec_font(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
664 {
665
666     FIXME("(%p)->(%p %p)\n", This, in, out);
667     return E_NOTIMPL;
668 }
669
670 static HRESULT exec_selectall(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
671 {
672     TRACE("(%p)\n", This);
673
674     if(in || out)
675         FIXME("unsupported args\n");
676
677     if(This->doc_obj->nscontainer)
678         do_ns_command(This, NSCMD_SELECTALL, NULL);
679
680     update_doc(This, UPDATE_UI);
681     return S_OK;
682 }
683
684 static HRESULT exec_bold(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
685 {
686     TRACE("(%p)\n", This);
687
688     if(in || out)
689         FIXME("unsupported args\n");
690
691     if(This->doc_obj->nscontainer)
692         do_ns_command(This, NSCMD_BOLD, NULL);
693
694     update_doc(This, UPDATE_UI);
695     return S_OK;
696 }
697
698 static HRESULT exec_italic(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
699 {
700     TRACE("(%p)\n", This);
701
702     if(in || out)
703         FIXME("unsupported args\n");
704
705     if(This->doc_obj->nscontainer)
706         do_ns_command(This, NSCMD_ITALIC, NULL);
707
708     update_doc(This, UPDATE_UI);
709     return S_OK;
710 }
711
712 static HRESULT query_justify(HTMLDocument *This, OLECMD *cmd)
713 {
714     static const PRUnichar justifycenterW[] = {'j','u','s','t','i','f','y','c','e','n','t','e','r',0};
715     static const PRUnichar justifyrightW[] = {'j','u','s','t','i','f','y','r','i','g','h','t',0};
716
717     switch(cmd->cmdID) {
718     case IDM_JUSTIFYCENTER:
719         TRACE("(%p) IDM_JUSTIFYCENTER\n", This);
720         cmd->cmdf = query_align_status(This, justifycenterW);
721         break;
722     case IDM_JUSTIFYLEFT:
723         TRACE("(%p) IDM_JUSTIFYLEFT\n", This);
724         /* FIXME: We should set OLECMDF_LATCHED only if it's set explicitly. */
725         if(This->doc_obj->usermode != EDITMODE || This->window->readystate < READYSTATE_INTERACTIVE)
726             cmd->cmdf = OLECMDF_SUPPORTED;
727         else
728             cmd->cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
729         break;
730     case IDM_JUSTIFYRIGHT:
731         TRACE("(%p) IDM_JUSTIFYRIGHT\n", This);
732         cmd->cmdf = query_align_status(This, justifyrightW);
733         break;
734     }
735
736     return S_OK;
737 }
738
739 static HRESULT exec_justifycenter(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
740 {
741     TRACE("(%p)\n", This);
742
743     if(in || out)
744         FIXME("unsupported args\n");
745
746     set_ns_align(This, NSALIGN_CENTER);
747     update_doc(This, UPDATE_UI);
748     return S_OK;
749 }
750
751 static HRESULT exec_justifyleft(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
752 {
753     TRACE("(%p)\n", This);
754
755     if(in || out)
756         FIXME("unsupported args\n");
757
758     set_ns_align(This, NSALIGN_LEFT);
759     update_doc(This, UPDATE_UI);
760     return S_OK;
761 }
762
763 static HRESULT exec_justifyright(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
764 {
765     TRACE("(%p)\n", This);
766
767     if(in || out)
768         FIXME("unsupported args\n");
769
770     set_ns_align(This, NSALIGN_RIGHT);
771     update_doc(This, UPDATE_UI);
772     return S_OK;
773 }
774
775 static HRESULT exec_underline(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
776 {
777     TRACE("(%p)\n", This);
778
779     if(in || out)
780         FIXME("unsupported args\n");
781
782     do_ns_command(This, NSCMD_UNDERLINE, NULL);
783     update_doc(This, UPDATE_UI);
784     return S_OK;
785 }
786
787 static HRESULT exec_horizontalline(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
788 {
789     TRACE("(%p)\n", This);
790
791     if(in || out)
792         FIXME("unsupported args\n");
793
794     do_ns_command(This, NSCMD_INSERTHR, NULL);
795     update_doc(This, UPDATE_UI);
796     return S_OK;
797 }
798
799 static HRESULT exec_orderlist(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
800 {
801     TRACE("(%p)\n", This);
802
803     if(in || out)
804         FIXME("unsupported args\n");
805
806     do_ns_command(This, NSCMD_OL, NULL);
807     update_doc(This, UPDATE_UI);
808     return S_OK;
809 }
810
811 static HRESULT exec_unorderlist(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
812 {
813     TRACE("(%p)\n", This);
814
815     if(in || out)
816         FIXME("unsupported args\n");
817
818     do_ns_command(This, NSCMD_UL, NULL);
819     update_doc(This, UPDATE_UI);
820     return S_OK;
821 }
822
823 static HRESULT exec_indent(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
824 {
825     TRACE("(%p)\n", This);
826
827     if(in || out)
828         FIXME("unsupported args\n");
829
830     do_ns_command(This, NSCMD_INDENT, NULL);
831     update_doc(This, UPDATE_UI);
832     return S_OK;
833 }
834
835 static HRESULT exec_outdent(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
836 {
837     TRACE("(%p)\n", This);
838
839     if(in || out)
840         FIXME("unsupported args\n");
841
842     do_ns_command(This, NSCMD_OUTDENT, NULL);
843     update_doc(This, UPDATE_UI);
844     return S_OK;
845 }
846
847 static HRESULT exec_composesettings(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
848 {
849     WCHAR *ptr;
850
851     if(out || !in || V_VT(in) != VT_BSTR) {
852         WARN("invalid arg\n");
853         return E_INVALIDARG;
854     }
855
856     TRACE("(%p)->(%x %s)\n", This, cmdexecopt, debugstr_w(V_BSTR(in)));
857
858     update_doc(This, UPDATE_UI);
859
860     ptr = V_BSTR(in);
861     if(*ptr == '1')
862         exec_bold(This, cmdexecopt, NULL, NULL);
863     ptr = strchrW(ptr, ',');
864     if(!ptr)
865         return S_OK;
866
867     if(*++ptr == '1')
868         exec_italic(This, cmdexecopt, NULL, NULL);
869     ptr = strchrW(ptr, ',');
870     if(!ptr)
871         return S_OK;
872
873     if(*++ptr == '1')
874         exec_underline(This, cmdexecopt, NULL, NULL);
875     ptr = strchrW(ptr, ',');
876     if(!ptr)
877         return S_OK;
878
879     if(isdigitW(*++ptr)) {
880         VARIANT v;
881
882         V_VT(&v) = VT_I4;
883         V_I4(&v) = *ptr-'0';
884
885         exec_fontsize(This, cmdexecopt, &v, NULL);
886     }
887     ptr = strchrW(ptr, ',');
888     if(!ptr)
889         return S_OK;
890
891     if(*++ptr != ',')
892         FIXME("set font color\n");
893     ptr = strchrW(ptr, ',');
894     if(!ptr)
895         return S_OK;
896
897     if(*++ptr != ',')
898         FIXME("set background color\n");
899     ptr = strchrW(ptr, ',');
900     if(!ptr)
901         return S_OK;
902
903     ptr++;
904     if(*ptr) {
905         VARIANT v;
906
907         V_VT(&v) = VT_BSTR;
908         V_BSTR(&v) = SysAllocString(ptr);
909
910         exec_fontname(This, cmdexecopt, &v, NULL);
911
912         SysFreeString(V_BSTR(&v));
913     }
914
915     return S_OK;
916 }
917
918 HRESULT editor_exec_copy(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
919 {
920     update_doc(This, UPDATE_UI);
921
922     if(!This->doc_obj->nscontainer)
923         return E_FAIL;
924
925     do_ns_editor_command(This->doc_obj->nscontainer, NSCMD_COPY);
926     return S_OK;
927 }
928
929 HRESULT editor_exec_cut(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
930 {
931     update_doc(This, UPDATE_UI);
932
933     if(!This->doc_obj->nscontainer)
934         return E_FAIL;
935
936     do_ns_editor_command(This->doc_obj->nscontainer, NSCMD_CUT);
937     return S_OK;
938 }
939
940 HRESULT editor_exec_paste(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
941 {
942     update_doc(This, UPDATE_UI);
943
944     if(!This->doc_obj->nscontainer)
945         return E_FAIL;
946
947     do_ns_editor_command(This->doc_obj->nscontainer, NSCMD_PASTE);
948     return S_OK;
949 }
950
951 static HRESULT exec_setdirty(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
952 {
953     TRACE("(%p)->(%08x %p %p)\n", This, cmdexecopt, in, out);
954
955     if(!in)
956         return S_OK;
957
958     if(V_VT(in) == VT_BOOL)
959         set_dirty(This, V_BOOL(in));
960     else
961         FIXME("unsupported vt=%d\n", V_VT(in));
962
963     return S_OK;
964 }
965
966 static HRESULT query_edit_status(HTMLDocument *This, OLECMD *cmd)
967 {
968     switch(cmd->cmdID) {
969     case IDM_DELETE:
970         TRACE("CGID_MSHTML: IDM_DELETE\n");
971         cmd->cmdf = query_ns_edit_status(This, NULL);
972         break;
973     case IDM_FONTNAME:
974         TRACE("CGID_MSHTML: IDM_FONTNAME\n");
975         cmd->cmdf = query_ns_edit_status(This, NULL);
976         break;
977     case IDM_FONTSIZE:
978         TRACE("CGID_MSHTML: IDM_FONTSIZE\n");
979         cmd->cmdf = query_ns_edit_status(This, NULL);
980         break;
981     case IDM_BOLD:
982         TRACE("CGID_MSHTML: IDM_BOLD\n");
983         cmd->cmdf = query_ns_edit_status(This, NSCMD_BOLD);
984         break;
985     case IDM_FORECOLOR:
986         TRACE("CGID_MSHTML: IDM_FORECOLOR\n");
987         cmd->cmdf = query_ns_edit_status(This, NULL);
988         break;
989     case IDM_ITALIC:
990         TRACE("CGID_MSHTML: IDM_ITALIC\n");
991         cmd->cmdf = query_ns_edit_status(This, NSCMD_ITALIC);
992         break;
993     case IDM_UNDERLINE:
994         TRACE("CGID_MSHTML: IDM_UNDERLINE\n");
995         cmd->cmdf = query_ns_edit_status(This, NSCMD_UNDERLINE);
996         break;
997     case IDM_HORIZONTALLINE:
998         TRACE("CGID_MSHTML: IDM_HORIZONTALLINE\n");
999         cmd->cmdf = query_ns_edit_status(This, NULL);
1000         break;
1001     case IDM_ORDERLIST:
1002         TRACE("CGID_MSHTML: IDM_ORDERLIST\n");
1003         cmd->cmdf = query_ns_edit_status(This, NSCMD_OL);
1004         break;
1005     case IDM_UNORDERLIST:
1006         TRACE("CGID_MSHTML: IDM_HORIZONTALLINE\n");
1007         cmd->cmdf = query_ns_edit_status(This, NSCMD_UL);
1008         break;
1009     case IDM_INDENT:
1010         TRACE("CGID_MSHTML: IDM_INDENT\n");
1011         cmd->cmdf = query_ns_edit_status(This, NULL);
1012         break;
1013     case IDM_OUTDENT:
1014         TRACE("CGID_MSHTML: IDM_OUTDENT\n");
1015         cmd->cmdf = query_ns_edit_status(This, NULL);
1016         break;
1017     case IDM_HYPERLINK:
1018         TRACE("CGID_MSHTML: IDM_HYPERLINK\n");
1019         cmd->cmdf = query_ns_edit_status(This, NULL);
1020         break;
1021     }
1022
1023     return S_OK;
1024 }
1025
1026 static INT_PTR CALLBACK hyperlink_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
1027 {
1028     static const WCHAR wszOther[] = {'(','o','t','h','e','r',')',0};
1029
1030     switch (msg)
1031     {
1032         case WM_INITDIALOG:
1033         {
1034             static const WCHAR wszFile[] = {'f','i','l','e',':',0};
1035             static const WCHAR wszFtp[] = {'f','t','p',':',0};
1036             static const WCHAR wszHttp[] = {'h','t','t','p',':',0};
1037             static const WCHAR wszHttps[] = {'h','t','t','p','s',':',0};
1038             static const WCHAR wszMailto[] = {'m','a','i','l','t','o',':',0};
1039             static const WCHAR wszNews[] = {'n','e','w','s',':',0};
1040             INT def_idx;
1041             HWND hwndCB = GetDlgItem(hwnd, IDC_TYPE);
1042             HWND hwndURL = GetDlgItem(hwnd, IDC_URL);
1043             INT len;
1044
1045             SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
1046
1047             SendMessageW(hwndCB, CB_INSERTSTRING, -1, (LPARAM)wszOther);
1048             SendMessageW(hwndCB, CB_INSERTSTRING, -1, (LPARAM)wszFile);
1049             SendMessageW(hwndCB, CB_INSERTSTRING, -1, (LPARAM)wszFtp);
1050             def_idx = SendMessageW(hwndCB, CB_INSERTSTRING, -1, (LPARAM)wszHttp);
1051             SendMessageW(hwndCB, CB_INSERTSTRING, -1, (LPARAM)wszHttps);
1052             SendMessageW(hwndCB, CB_INSERTSTRING, -1, (LPARAM)wszMailto);
1053             SendMessageW(hwndCB, CB_INSERTSTRING, -1, (LPARAM)wszNews);
1054             SendMessageW(hwndCB, CB_SETCURSEL, def_idx, 0);
1055
1056             /* force the updating of the URL edit box */
1057             SendMessageW(hwnd, WM_COMMAND, MAKEWPARAM(IDC_TYPE, CBN_SELCHANGE), (LPARAM)hwndCB);
1058
1059             SetFocus(hwndURL);
1060             len = SendMessageW(hwndURL, WM_GETTEXTLENGTH, 0, 0);
1061             SendMessageW(hwndURL, EM_SETSEL, 0, len);
1062
1063             return FALSE;
1064         }
1065         case WM_COMMAND:
1066             switch (wparam)
1067             {
1068                 case MAKEWPARAM(IDCANCEL, BN_CLICKED):
1069                     EndDialog(hwnd, wparam);
1070                     return TRUE;
1071                 case MAKEWPARAM(IDOK, BN_CLICKED):
1072                 {
1073                     BSTR *url = (BSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
1074                     HWND hwndURL = GetDlgItem(hwnd, IDC_URL);
1075                     INT len = GetWindowTextLengthW(hwndURL);
1076                     *url = SysAllocStringLen(NULL, len + 1);
1077                     GetWindowTextW(hwndURL, *url, len + 1);
1078                     EndDialog(hwnd, wparam);
1079                     return TRUE;
1080                 }
1081                 case MAKEWPARAM(IDC_TYPE, CBN_SELCHANGE):
1082                 {
1083                     HWND hwndURL = GetDlgItem(hwnd, IDC_URL);
1084                     INT item;
1085                     INT len;
1086                     LPWSTR type;
1087                     LPWSTR url;
1088                     LPWSTR p;
1089                     static const WCHAR wszSlashSlash[] = {'/','/'};
1090
1091                     /* get string of currently selected hyperlink type */
1092                     item = SendMessageW((HWND)lparam, CB_GETCURSEL, 0, 0);
1093                     len = SendMessageW((HWND)lparam, CB_GETLBTEXTLEN, item, 0);
1094                     type = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1095                     SendMessageW((HWND)lparam, CB_GETLBTEXT, item, (LPARAM)type);
1096
1097                     if (!strcmpW(type, wszOther))
1098                         *type = '\0';
1099
1100                     /* get current URL */
1101                     len = GetWindowTextLengthW(hwndURL);
1102                     url = HeapAlloc(GetProcessHeap(), 0, (len + strlenW(type) + 3) * sizeof(WCHAR));
1103                     GetWindowTextW(hwndURL, url, len + 1);
1104
1105                     /* strip off old protocol */
1106                     p = strchrW(url, ':');
1107                     if (p && p[1] == '/' && p[2] == '/')
1108                         p += 3;
1109                     if (!p) p = url;
1110                     memmove(url + (*type != '\0' ? strlenW(type) + 2 : 0), p, (len + 1 - (p - url)) * sizeof(WCHAR));
1111
1112                     /* add new protocol */
1113                     if (*type != '\0')
1114                     {
1115                         memcpy(url, type, strlenW(type) * sizeof(WCHAR));
1116                         memcpy(url + strlenW(type), wszSlashSlash, sizeof(wszSlashSlash));
1117                     }
1118
1119                     SetWindowTextW(hwndURL, url);
1120
1121                     HeapFree(GetProcessHeap(), 0, url);
1122                     HeapFree(GetProcessHeap(), 0, type);
1123                     return TRUE;
1124                 }
1125             }
1126             return FALSE;
1127         case WM_CLOSE:
1128             EndDialog(hwnd, IDCANCEL);
1129             return TRUE;
1130         default:
1131             return FALSE;
1132     }
1133 }
1134
1135 static HRESULT exec_hyperlink(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
1136 {
1137     nsAString href_str, ns_url;
1138     nsIHTMLEditor *html_editor;
1139     nsIDOMHTMLElement *anchor_elem;
1140     PRBool insert_link_at_caret;
1141     nsISelection *nsselection;
1142     BSTR url = NULL;
1143     INT ret;
1144     HRESULT hres = E_FAIL;
1145
1146     static const WCHAR aW[] = {'a',0};
1147     static const WCHAR hrefW[] = {'h','r','e','f',0};
1148
1149     TRACE("%p, 0x%x, %p, %p\n", This, cmdexecopt, in, out);
1150
1151     if (cmdexecopt == OLECMDEXECOPT_DONTPROMPTUSER)
1152     {
1153         if (!in || V_VT(in) != VT_BSTR)
1154         {
1155             WARN("invalid arg\n");
1156             return E_INVALIDARG;
1157         }
1158         url = V_BSTR(in);
1159     }
1160     else
1161     {
1162         ret = DialogBoxParamW(hInst, MAKEINTRESOURCEW(IDD_HYPERLINK), NULL /* FIXME */, hyperlink_dlgproc, (LPARAM)&url);
1163         if (ret != IDOK)
1164             return OLECMDERR_E_CANCELED;
1165     }
1166
1167     if(!This->doc_node->nsdoc) {
1168         WARN("NULL nsdoc\n");
1169         return E_UNEXPECTED;
1170     }
1171
1172     nsselection = get_ns_selection(This);
1173     if (!nsselection)
1174         return E_FAIL;
1175
1176     /* create an element for the link */
1177     create_nselem(This->doc_node, aW, &anchor_elem);
1178
1179     nsAString_InitDepend(&href_str, hrefW);
1180     nsAString_InitDepend(&ns_url, url);
1181     nsIDOMElement_SetAttribute(anchor_elem, &href_str, &ns_url);
1182     nsAString_Finish(&href_str);
1183
1184     nsISelection_GetIsCollapsed(nsselection, &insert_link_at_caret);
1185
1186     /* create an element with text of URL */
1187     if (insert_link_at_caret) {
1188         nsIDOMNode *text_node, *unused_node;
1189
1190         nsIDOMDocument_CreateTextNode(This->doc_node->nsdoc, &ns_url, (nsIDOMText **)&text_node);
1191
1192         /* wrap the <a> tags around the text element */
1193         nsIDOMElement_AppendChild(anchor_elem, text_node, &unused_node);
1194         nsIDOMNode_Release(text_node);
1195         nsIDOMNode_Release(unused_node);
1196     }
1197
1198     nsAString_Finish(&ns_url);
1199
1200     nsIEditor_QueryInterface(This->doc_obj->nscontainer->editor, &IID_nsIHTMLEditor, (void **)&html_editor);
1201     if (html_editor) {
1202         nsresult nsres;
1203
1204         if (insert_link_at_caret) {
1205             /* add them to the document at the caret position */
1206             nsres = nsIHTMLEditor_InsertElementAtSelection(html_editor, (nsIDOMElement*)anchor_elem, FALSE);
1207             nsISelection_SelectAllChildren(nsselection, (nsIDOMNode*)anchor_elem);
1208         }else /* add them around the selection using the magic provided to us by nsIHTMLEditor */
1209             nsres = nsIHTMLEditor_InsertLinkAroundSelection(html_editor, (nsIDOMElement*)anchor_elem);
1210
1211         nsIHTMLEditor_Release(html_editor);
1212         hres = NS_SUCCEEDED(nsres) ? S_OK : E_FAIL;
1213     }
1214
1215     nsISelection_Release(nsselection);
1216     nsIDOMElement_Release(anchor_elem);
1217
1218     if (cmdexecopt != OLECMDEXECOPT_DONTPROMPTUSER)
1219         SysFreeString(url);
1220
1221     TRACE("-- 0x%08x\n", hres);
1222     return hres;
1223 }
1224
1225 static HRESULT query_selall_status(HTMLDocument *This, OLECMD *cmd)
1226 {
1227     TRACE("(%p)->(%p)\n", This, cmd);
1228
1229     cmd->cmdf = OLECMDF_SUPPORTED|OLECMDF_ENABLED;
1230     return S_OK;
1231 }
1232
1233 const cmdtable_t editmode_cmds[] = {
1234     {IDM_DELETE,          query_edit_status,    exec_delete},
1235     {IDM_FONTNAME,        query_edit_status,    exec_fontname},
1236     {IDM_FONTSIZE,        query_edit_status,    exec_fontsize},
1237     {IDM_SELECTALL,       query_selall_status , exec_selectall},
1238     {IDM_FORECOLOR,       query_edit_status,    exec_forecolor},
1239     {IDM_BOLD,            query_edit_status,    exec_bold},
1240     {IDM_ITALIC,          query_edit_status,    exec_italic},
1241     {IDM_JUSTIFYCENTER,   query_justify,        exec_justifycenter},
1242     {IDM_JUSTIFYRIGHT,    query_justify,        exec_justifyright},
1243     {IDM_JUSTIFYLEFT,     query_justify,        exec_justifyleft},
1244     {IDM_FONT,            NULL,                 exec_font},
1245     {IDM_UNDERLINE,       query_edit_status,    exec_underline},
1246     {IDM_HORIZONTALLINE,  query_edit_status,    exec_horizontalline},
1247     {IDM_ORDERLIST,       query_edit_status,    exec_orderlist},
1248     {IDM_UNORDERLIST,     query_edit_status,    exec_unorderlist},
1249     {IDM_INDENT,          query_edit_status,    exec_indent},
1250     {IDM_OUTDENT,         query_edit_status,    exec_outdent},
1251     {IDM_COMPOSESETTINGS, NULL,                 exec_composesettings},
1252     {IDM_HYPERLINK,       query_edit_status,    exec_hyperlink},
1253     {IDM_SETDIRTY,        NULL,                 exec_setdirty},
1254     {0,NULL,NULL}
1255 };
1256
1257 void init_editor(HTMLDocument *This)
1258 {
1259     update_doc(This, UPDATE_UI);
1260
1261     set_ns_fontname(This, "Times New Roman");
1262 }
1263
1264 HRESULT editor_is_dirty(HTMLDocument *This)
1265 {
1266     PRBool modified;
1267
1268     if(!This->doc_obj->nscontainer || !This->doc_obj->nscontainer->editor)
1269         return S_FALSE;
1270
1271     nsIEditor_GetDocumentModified(This->doc_obj->nscontainer->editor, &modified);
1272
1273     return modified ? S_OK : S_FALSE;
1274 }