wined3d: Default to GLSL. This is safe because we now have proper ps2.0/vs2.0 detection.
[wine] / dlls / mshtml / txtrange.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 "config.h"
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "winnls.h"
30 #include "ole2.h"
31
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34
35 #include "mshtml_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
38
39 static const WCHAR brW[] = {'b','r',0};
40
41 typedef struct {
42     const IHTMLTxtRangeVtbl *lpHTMLTxtRangeVtbl;
43
44     LONG ref;
45
46     nsIDOMRange *nsrange;
47     HTMLDocument *doc;
48
49     struct list entry;
50 } HTMLTxtRange;
51
52 #define HTMLTXTRANGE(x)  ((IHTMLTxtRange*)  &(x)->lpHTMLTxtRangeVtbl)
53
54 typedef struct {
55     WCHAR *buf;
56     DWORD len;
57     DWORD size;
58 } wstrbuf_t;
59
60 typedef struct {
61     PRUint16 type;
62     nsIDOMNode *node;
63     PRUint32 off;
64     nsAString str;
65     const PRUnichar *p;
66 } dompos_t;
67
68 typedef enum {
69     RU_UNKNOWN,
70     RU_CHAR,
71     RU_WORD,
72     RU_SENTENCE,
73     RU_TEXTEDIT
74 } range_unit_t;
75
76 static HTMLTxtRange *get_range_object(HTMLDocument *doc, IHTMLTxtRange *iface)
77 {
78     HTMLTxtRange *iter;
79
80     LIST_FOR_EACH_ENTRY(iter, &doc->range_list, HTMLTxtRange, entry) {
81         if(HTMLTXTRANGE(iter) == iface)
82             return iter;
83     }
84
85     ERR("Could not find range in document\n");
86     return NULL;
87 }
88
89 static range_unit_t string_to_unit(LPCWSTR str)
90 {
91     static const WCHAR characterW[] =
92         {'c','h','a','r','a','c','t','e','r',0};
93     static const WCHAR wordW[] =
94         {'w','o','r','d',0};
95     static const WCHAR sentenceW[] =
96         {'s','e','n','t','e','n','c','e',0};
97     static const WCHAR texteditW[] =
98         {'t','e','x','t','e','d','i','t',0};
99
100     if(!strcmpiW(str, characterW))  return RU_CHAR;
101     if(!strcmpiW(str, wordW))       return RU_WORD;
102     if(!strcmpiW(str, sentenceW))   return RU_SENTENCE;
103     if(!strcmpiW(str, texteditW))   return RU_TEXTEDIT;
104
105     return RU_UNKNOWN;
106 }
107
108 static int string_to_nscmptype(LPCWSTR str)
109 {
110     static const WCHAR seW[] = {'S','t','a','r','t','T','o','E','n','d',0};
111     static const WCHAR ssW[] = {'S','t','a','r','t','T','o','S','t','a','r','t',0};
112     static const WCHAR esW[] = {'E','n','d','T','o','S','t','a','r','t',0};
113     static const WCHAR eeW[] = {'E','n','d','T','o','E','n','d',0};
114
115     if(!strcmpiW(str, seW))  return NS_START_TO_END;
116     if(!strcmpiW(str, ssW))  return NS_START_TO_START;
117     if(!strcmpiW(str, esW))  return NS_END_TO_START;
118     if(!strcmpiW(str, eeW))  return NS_END_TO_END;
119
120     return -1;
121 }
122
123 static PRUint16 get_node_type(nsIDOMNode *node)
124 {
125     PRUint16 type = 0xfff;
126
127     if(node)
128         nsIDOMNode_GetNodeType(node, &type);
129
130     return type;
131 }
132
133 static BOOL is_br_node(nsIDOMNode *node)
134 {
135     nsIDOMElement *elem;
136     nsAString tag_str;
137     const PRUnichar *tag;
138     BOOL ret = FALSE;
139     nsresult nsres;
140
141     nsres = nsIDOMNode_QueryInterface(node, &IID_nsIDOMElement, (void**)&elem);
142     if(NS_FAILED(nsres))
143         return FALSE;
144
145     nsAString_Init(&tag_str, NULL);
146     nsIDOMElement_GetTagName(elem, &tag_str);
147     nsIDOMElement_Release(elem);
148     nsAString_GetData(&tag_str, &tag, 0);
149
150     if(!strcmpiW(tag, brW))
151         ret = TRUE;
152
153     nsAString_Finish(&tag_str);
154
155     return ret;
156 }
157
158 static inline void wstrbuf_init(wstrbuf_t *buf)
159 {
160     buf->len = 0;
161     buf->size = 16;
162     buf->buf = mshtml_alloc(buf->size * sizeof(WCHAR));
163     *buf->buf = 0;
164 }
165
166 static inline void wstrbuf_finish(wstrbuf_t *buf)
167 {
168     mshtml_free(buf->buf);
169 }
170
171 static void wstrbuf_append_len(wstrbuf_t *buf, LPCWSTR str, int len)
172 {
173     if(buf->len+len >= buf->size) {
174         buf->size = 2*buf->len+len;
175         buf->buf = mshtml_realloc(buf->buf, buf->size * sizeof(WCHAR));
176     }
177
178     memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
179     buf->len += len;
180     buf->buf[buf->len] = 0;
181 }
182
183 static inline void wstrbuf_append(wstrbuf_t *buf, LPCWSTR str)
184 {
185     wstrbuf_append_len(buf, str, strlenW(str));
186 }
187
188 static void wstrbuf_append_nodetxt(wstrbuf_t *buf, LPCWSTR str, int len)
189 {
190     const WCHAR *s = str;
191     WCHAR *d;
192
193     if(buf->len+len >= buf->size) {
194         buf->size = 2*buf->len+len;
195         buf->buf = mshtml_realloc(buf->buf, buf->size * sizeof(WCHAR));
196     }
197
198     if(buf->len && isspaceW(buf->buf[buf->len-1])) {
199         while(s < str+len && isspaceW(*s))
200             s++;
201     }
202
203     d = buf->buf+buf->len;
204     while(s < str+len) {
205         if(isspaceW(*s)) {
206             *d++ = ' ';
207             s++;
208             while(s < str+len && isspaceW(*s))
209                 s++;
210         }else {
211             *d++ = *s++;
212         }
213     }
214
215     buf->len = d - buf->buf;
216     *d = 0;
217 }
218
219 static void wstrbuf_append_node(wstrbuf_t *buf, nsIDOMNode *node)
220 {
221
222     switch(get_node_type(node)) {
223     case TEXT_NODE: {
224         nsIDOMText *nstext;
225         nsAString data_str;
226         const PRUnichar *data;
227
228         nsIDOMNode_QueryInterface(node, &IID_nsIDOMText, (void**)&nstext);
229
230         nsAString_Init(&data_str, NULL);
231         nsIDOMText_GetData(nstext, &data_str);
232         nsAString_GetData(&data_str, &data, NULL);
233         wstrbuf_append_nodetxt(buf, data, strlenW(data));
234         nsAString_Finish(&data_str);
235
236        nsIDOMText_Release(nstext);
237
238         break;
239     }
240     case ELEMENT_NODE:
241         if(is_br_node(node)) {
242             static const WCHAR endlW[] = {'\r','\n'};
243             wstrbuf_append_len(buf, endlW, 2);
244         }
245     }
246 }
247
248 static BOOL fill_nodestr(dompos_t *pos)
249 {
250     nsIDOMText *text;
251     nsresult nsres;
252
253     if(pos->type != TEXT_NODE)
254         return FALSE;
255
256     nsres = nsIDOMNode_QueryInterface(pos->node, &IID_nsIDOMText, (void**)&text);
257     if(NS_FAILED(nsres))
258         return FALSE;
259
260     nsAString_Init(&pos->str, NULL);
261     nsIDOMText_GetData(text, &pos->str);
262     nsIDOMText_Release(text);
263     nsAString_GetData(&pos->str, &pos->p, NULL);
264
265     return TRUE;
266 }
267
268 static nsIDOMNode *next_node(nsIDOMNode *iter)
269 {
270     nsIDOMNode *ret, *tmp;
271     nsresult nsres;
272
273     if(!iter)
274         return NULL;
275
276     nsres = nsIDOMNode_GetFirstChild(iter, &ret);
277     if(NS_SUCCEEDED(nsres) && ret)
278         return ret;
279
280     nsIDOMNode_AddRef(iter);
281
282     do {
283         nsres = nsIDOMNode_GetNextSibling(iter, &ret);
284         if(NS_SUCCEEDED(nsres) && ret) {
285             nsIDOMNode_Release(iter);
286             return ret;
287         }
288
289         nsres = nsIDOMNode_GetParentNode(iter, &tmp);
290         nsIDOMNode_Release(iter);
291         iter = tmp;
292     }while(NS_SUCCEEDED(nsres) && iter);
293
294     return NULL;
295 }
296
297 static nsIDOMNode *prev_node(HTMLTxtRange *This, nsIDOMNode *iter)
298 {
299     nsIDOMNode *ret, *tmp;
300     nsresult nsres;
301
302     if(!iter) {
303         nsIDOMHTMLDocument *nshtmldoc;
304         nsIDOMHTMLElement *nselem;
305         nsIDOMDocument *nsdoc;
306
307         nsIWebNavigation_GetDocument(This->doc->nscontainer->navigation, &nsdoc);
308         nsIDOMDocument_QueryInterface(nsdoc, &IID_nsIDOMHTMLDocument, (void**)&nshtmldoc);
309         nsIDOMDocument_Release(nsdoc);
310         nsIDOMHTMLDocument_GetBody(nshtmldoc, &nselem);
311         nsIDOMHTMLDocument_Release(nshtmldoc);
312
313         nsIDOMElement_GetLastChild(nselem, &tmp);
314         if(!tmp)
315             return (nsIDOMNode*)nselem;
316
317         while(tmp) {
318             ret = tmp;
319             nsIDOMNode_GetLastChild(ret, &tmp);
320         }
321
322         nsIDOMElement_Release(nselem);
323
324         return ret;
325     }
326
327     nsres = nsIDOMNode_GetLastChild(iter, &ret);
328     if(NS_SUCCEEDED(nsres) && ret)
329         return ret;
330
331     nsIDOMNode_AddRef(iter);
332
333     do {
334         nsres = nsIDOMNode_GetPreviousSibling(iter, &ret);
335         if(NS_SUCCEEDED(nsres) && ret) {
336             nsIDOMNode_Release(iter);
337             return ret;
338         }
339
340         nsres = nsIDOMNode_GetParentNode(iter, &tmp);
341         nsIDOMNode_Release(iter);
342         iter = tmp;
343     }while(NS_SUCCEEDED(nsres) && iter);
344
345     return NULL;
346 }
347
348 static nsIDOMNode *get_child_node(nsIDOMNode *node, PRUint32 off)
349 {
350     nsIDOMNodeList *node_list;
351     nsIDOMNode *ret = NULL;
352
353     nsIDOMNode_GetChildNodes(node, &node_list);
354     nsIDOMNodeList_Item(node_list, off, &ret);
355     nsIDOMNodeList_Release(node_list);
356
357     return ret;
358 }
359
360 static void get_cur_pos(HTMLTxtRange *This, BOOL start, dompos_t *pos)
361 {
362     nsIDOMNode *node;
363     PRInt32 off;
364
365     pos->p = NULL;
366
367     if(!start) {
368         PRBool collapsed;
369         nsIDOMRange_GetCollapsed(This->nsrange, &collapsed);
370         start = collapsed;
371     }
372
373     if(start) {
374         nsIDOMRange_GetStartContainer(This->nsrange, &node);
375         nsIDOMRange_GetStartOffset(This->nsrange, &off);
376     }else {
377         nsIDOMRange_GetEndContainer(This->nsrange, &node);
378         nsIDOMRange_GetEndOffset(This->nsrange, &off);
379     }
380
381     pos->type = get_node_type(node);
382     if(pos->type == ELEMENT_NODE) {
383         if(start) {
384             pos->node = get_child_node(node, off);
385             pos->off = 0;
386         }else {
387             pos->node = off ? get_child_node(node, off-1) : prev_node(This, node);
388             pos->off = -1;
389         }
390
391         pos->type = get_node_type(pos->node);
392         nsIDOMNode_Release(node);
393     }else if(start) {
394         pos->node = node;
395         pos->off = off;
396     }else if(off) {
397         pos->node = node;
398         pos->off = off-1;
399     }else {
400         pos->node = prev_node(This, node);
401         pos->off = -1;
402         nsIDOMNode_Release(node);
403     }
404
405     if(pos->type == TEXT_NODE)
406         fill_nodestr(pos);
407 }
408
409 static void set_range_pos(HTMLTxtRange *This, BOOL start, dompos_t *pos)
410 {
411     nsresult nsres;
412
413     if(start) {
414         if(pos->type == TEXT_NODE)
415             nsres = nsIDOMRange_SetStart(This->nsrange, pos->node, pos->off);
416         else
417             nsres = nsIDOMRange_SetStartBefore(This->nsrange, pos->node);
418     }else {
419         if(pos->type == TEXT_NODE)
420             nsres = nsIDOMRange_SetEnd(This->nsrange, pos->node, pos->off+1);
421         else
422             nsres = nsIDOMRange_SetEndAfter(This->nsrange, pos->node);
423     }
424
425     if(NS_FAILED(nsres))
426         ERR("failed: %p %08x\n", pos->node, nsres);
427 }
428
429 static inline void dompos_release(dompos_t *pos)
430 {
431     if(pos->node)
432         nsIDOMNode_Release(pos->node);
433
434     if(pos->p)
435         nsAString_Finish(&pos->str);
436 }
437
438 static inline void dompos_addref(dompos_t *pos)
439 {
440     if(pos->node)
441         nsIDOMNode_AddRef(pos->node);
442
443     if(pos->type == TEXT_NODE)
444         fill_nodestr(pos);
445 }
446
447 static inline BOOL dompos_cmp(const dompos_t *pos1, const dompos_t *pos2)
448 {
449     return pos1->node == pos2->node && pos1->off == pos2->off;
450 }
451
452 static void range_to_string(HTMLTxtRange *This, wstrbuf_t *buf)
453 {
454     nsIDOMNode *iter, *tmp;
455     dompos_t start_pos, end_pos;
456     PRBool collapsed;
457
458     nsIDOMRange_GetCollapsed(This->nsrange, &collapsed);
459     if(collapsed) {
460         wstrbuf_finish(buf);
461         buf->buf = NULL;
462         buf->size = 0;
463         return;
464     }
465
466     get_cur_pos(This, FALSE, &end_pos);
467     get_cur_pos(This, TRUE, &start_pos);
468
469     if(start_pos.type == TEXT_NODE) {
470         if(start_pos.node == end_pos.node) {
471             wstrbuf_append_nodetxt(buf, start_pos.p+start_pos.off, end_pos.off-start_pos.off+1);
472             iter = start_pos.node;
473             nsIDOMNode_AddRef(iter);
474         }else {
475             wstrbuf_append_nodetxt(buf, start_pos.p+start_pos.off, strlenW(start_pos.p+start_pos.off));
476             iter = next_node(start_pos.node);
477         }
478     }else {
479         iter = start_pos.node;
480         nsIDOMNode_AddRef(iter);
481     }
482
483     while(iter != end_pos.node) {
484         wstrbuf_append_node(buf, iter);
485         tmp = next_node(iter);
486         nsIDOMNode_Release(iter);
487         iter = tmp;
488     }
489
490     nsIDOMNode_AddRef(end_pos.node);
491
492     if(start_pos.node != end_pos.node && !is_br_node(end_pos.node))
493         wstrbuf_append_nodetxt(buf, end_pos.p, end_pos.off+1);
494
495     nsIDOMNode_Release(iter);
496     dompos_release(&start_pos);
497     dompos_release(&end_pos);
498
499     if(buf->len) {
500         WCHAR *p;
501
502         for(p = buf->buf+buf->len-1; p >= buf->buf && isspaceW(*p); p--);
503
504         p = strchrW(p, '\r');
505         if(p)
506             *p = 0;
507     }
508 }
509
510 static WCHAR get_pos_char(const dompos_t *pos)
511 {
512     switch(pos->type) {
513     case TEXT_NODE:
514         return pos->p[pos->off];
515     case ELEMENT_NODE:
516         if(is_br_node(pos->node))
517             return '\n';
518     }
519
520     return 0;
521 }
522
523 static WCHAR next_char(const dompos_t *pos, dompos_t *new_pos)
524 {
525     nsIDOMNode *iter, *tmp;
526     dompos_t last_space, tmp_pos;
527     const WCHAR *p;
528     WCHAR cspace = 0;
529
530     if(pos->type == TEXT_NODE && pos->off != -1 && pos->p[pos->off]) {
531         p = pos->p+pos->off+1;
532         if(*p && isspaceW(*p)) {
533             cspace = ' ';
534             while(p[1] && isspaceW(p[1]))
535                 p++;
536         }
537
538         if(*p) {
539             *new_pos = *pos;
540             new_pos->off = p - pos->p;
541             dompos_addref(new_pos);
542
543             return *p;
544         }else {
545             last_space = *pos;
546             last_space.off = p - pos->p;
547             dompos_addref(&last_space);
548         }
549     }
550
551     iter = next_node(pos->node);
552
553     while(iter) {
554         switch(get_node_type(iter)) {
555         case TEXT_NODE:
556             tmp_pos.node = iter;
557             tmp_pos.type = TEXT_NODE;
558             dompos_addref(&tmp_pos);
559
560             p = tmp_pos.p;
561
562             if(!*p) {
563                 dompos_release(&tmp_pos);
564                 break;
565             }else if(isspaceW(*p)) {
566                 if(cspace)
567                     dompos_release(&last_space);
568                 else
569                     cspace = ' ';
570
571                 while(p[1] && isspaceW(p[1]))
572                       p++;
573
574                 tmp_pos.off = p-tmp_pos.p;
575
576                 if(!p[1]) {
577                     last_space = tmp_pos;
578                     break;
579                 }
580
581                 *new_pos = tmp_pos;
582                 nsIDOMNode_Release(iter);
583                 return cspace;
584             }else if(cspace) {
585                 *new_pos = last_space;
586                 dompos_release(&tmp_pos);
587                 nsIDOMNode_Release(iter);
588
589                 return cspace;
590             }else if(*p) {
591                 tmp_pos.off = 0;
592                 *new_pos = tmp_pos;
593             }
594
595             nsIDOMNode_Release(iter);
596             return *p;
597
598         case ELEMENT_NODE:
599             if(!is_br_node(iter))
600                 break;
601
602             if(cspace)
603                 dompos_release(&last_space);
604             cspace = '\n';
605
606             nsIDOMNode_AddRef(iter);
607             last_space.node = iter;
608             last_space.type = ELEMENT_NODE;
609             last_space.off = 0;
610             last_space.p = NULL;
611         }
612
613         tmp = iter;
614         iter = next_node(iter);
615         nsIDOMNode_Release(tmp);
616     }
617
618     if(cspace) {
619         *new_pos = last_space;
620     }else {
621         *new_pos = *pos;
622         dompos_addref(new_pos);
623     }
624
625     return cspace;
626 }
627
628 static WCHAR prev_char(HTMLTxtRange *This, const dompos_t *pos, dompos_t *new_pos)
629 {
630     nsIDOMNode *iter, *tmp;
631     const WCHAR *p;
632     BOOL skip_space = FALSE;
633
634     if(pos->type == TEXT_NODE && isspaceW(pos->p[pos->off]))
635         skip_space = TRUE;
636
637     if(pos->type == TEXT_NODE && pos->off) {
638         p = pos->p+pos->off-1;
639
640         if(skip_space) {
641             while(p >= pos->p && isspace(*p))
642                 p--;
643         }
644
645         if(p >= pos->p) {
646             *new_pos = *pos;
647             new_pos->off = p-pos->p;
648             dompos_addref(new_pos);
649             return new_pos->p[new_pos->off];
650         }
651     }
652
653     iter = prev_node(This, pos->node);
654
655     while(iter) {
656         switch(get_node_type(iter)) {
657         case TEXT_NODE: {
658             dompos_t tmp_pos;
659
660             tmp_pos.node = iter;
661             tmp_pos.type = TEXT_NODE;
662             dompos_addref(&tmp_pos);
663
664             p = tmp_pos.p + strlenW(tmp_pos.p)-1;
665
666             if(skip_space) {
667                 while(p >= tmp_pos.p && isspaceW(*p))
668                     p--;
669             }
670
671             if(p < tmp_pos.p) {
672                 dompos_release(&tmp_pos);
673                 break;
674             }
675
676             tmp_pos.off = p-tmp_pos.p;
677             *new_pos = tmp_pos;
678             nsIDOMNode_Release(iter);
679             return *p;
680         }
681
682         case ELEMENT_NODE:
683             if(!is_br_node(iter))
684                 break;
685
686             if(skip_space) {
687                 skip_space = FALSE;
688                 break;
689             }
690
691             new_pos->node = iter;
692             new_pos->type = ELEMENT_NODE;
693             new_pos->off = 0;
694             new_pos->p = NULL;
695             return '\n';
696         }
697
698         tmp = iter;
699         iter = prev_node(This, iter);
700         nsIDOMNode_Release(tmp);
701     }
702
703     *new_pos = *pos;
704     dompos_addref(new_pos);
705     return 0;
706 }
707
708 static long move_next_chars(long cnt, const dompos_t *pos, BOOL col, const dompos_t *bound_pos,
709         BOOL *bounded, dompos_t *new_pos)
710 {
711     dompos_t iter, tmp;
712     long ret = 0;
713     WCHAR c;
714
715     if(bounded)
716         *bounded = FALSE;
717
718     if(col)
719         ret++;
720
721     if(ret >= cnt) {
722         *new_pos = *pos;
723         dompos_addref(new_pos);
724         return ret;
725     }
726
727     c = next_char(pos, &iter);
728     ret++;
729
730     while(ret < cnt) {
731         tmp = iter;
732         c = next_char(&tmp, &iter);
733         dompos_release(&tmp);
734         if(!c)
735             break;
736
737         ret++;
738         if(bound_pos && dompos_cmp(&tmp, bound_pos)) {
739             *bounded = TRUE;
740             ret++;
741         }
742     }
743
744     *new_pos = iter;
745     return ret;
746 }
747
748 static long move_prev_chars(HTMLTxtRange *This, long cnt, const dompos_t *pos, BOOL end,
749         const dompos_t *bound_pos, BOOL *bounded, dompos_t *new_pos)
750 {
751     dompos_t iter, tmp;
752     long ret = 0;
753     WCHAR c;
754
755     if(bounded)
756         *bounded = FALSE;
757
758     c = prev_char(This, pos, &iter);
759     if(c)
760         ret++;
761
762     while(c && ret < cnt) {
763         tmp = iter;
764         c = prev_char(This, &tmp, &iter);
765         dompos_release(&tmp);
766         if(!c) {
767             if(end)
768                 ret++;
769             break;
770         }
771
772         ret++;
773
774         if(bound_pos && dompos_cmp(&iter, bound_pos))
775             *bounded = TRUE;
776     }
777
778     *new_pos = iter;
779     return ret;
780 }
781
782 static BOOL find_next_space(const dompos_t *pos, dompos_t *ret)
783 {
784     dompos_t iter, tmp;
785     WCHAR c;
786
787     c = next_char(pos, &iter);
788     if(!c) {
789         *ret = iter;
790         return FALSE;
791     }
792
793     while(c && !isspaceW(c)) {
794         tmp = iter;
795         c = next_char(&tmp, &iter);
796         dompos_release(&tmp);
797     }
798
799     *ret = iter;
800     return TRUE;
801 }
802
803 static long find_prev_space(HTMLTxtRange *This, const dompos_t *pos, BOOL first_space, dompos_t *ret)
804 {
805     dompos_t iter, tmp;
806     WCHAR c;
807
808     c = prev_char(This, pos, &iter);
809     if(!c || (first_space && isspaceW(c))) {
810         *ret = iter;
811         return FALSE;
812     }
813
814     while(1) {
815         tmp = iter;
816         c = prev_char(This, &tmp, &iter);
817         if(!c || isspaceW(c)) {
818             dompos_release(&iter);
819             break;
820         }
821         dompos_release(&tmp);
822     }
823
824     *ret = tmp;
825     return TRUE;
826 }
827
828 static long move_next_words(long cnt, const dompos_t *pos, dompos_t *new_pos)
829 {
830     dompos_t iter, tmp;
831     long ret = 0;
832
833     iter = *pos;
834     dompos_addref(&iter);
835
836     while(ret < cnt) {
837         if(!find_next_space(&iter, &tmp))
838             break;
839
840         ret++;
841         dompos_release(&iter);
842         iter = tmp;
843     }
844
845     *new_pos = iter;
846     return ret;
847 }
848
849 static long move_prev_words(HTMLTxtRange *This, long cnt, const dompos_t *pos, dompos_t *new_pos)
850 {
851     dompos_t iter, tmp;
852     long ret = 0;
853
854     iter = *pos;
855     dompos_addref(&iter);
856
857     while(ret < cnt) {
858         if(!find_prev_space(This, &iter, FALSE, &tmp))
859             break;
860
861         dompos_release(&iter);
862         iter = tmp;
863         ret++;
864     }
865
866     *new_pos = iter;
867     return ret;
868 }
869
870 #define HTMLTXTRANGE_THIS(iface) DEFINE_THIS(HTMLTxtRange, HTMLTxtRange, iface)
871
872 static HRESULT WINAPI HTMLTxtRange_QueryInterface(IHTMLTxtRange *iface, REFIID riid, void **ppv)
873 {
874     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
875
876     *ppv = NULL;
877
878     if(IsEqualGUID(&IID_IUnknown, riid)) {
879         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
880         *ppv = HTMLTXTRANGE(This);
881     }else if(IsEqualGUID(&IID_IDispatch, riid)) {
882         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
883         *ppv = HTMLTXTRANGE(This);
884     }else if(IsEqualGUID(&IID_IHTMLTxtRange, riid)) {
885         TRACE("(%p)->(IID_IHTMLTxtRange %p)\n", This, ppv);
886         *ppv = HTMLTXTRANGE(This);
887     }
888
889     if(*ppv) {
890         IUnknown_AddRef((IUnknown*)*ppv);
891         return S_OK;
892     }
893
894     WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
895     return E_NOINTERFACE;
896 }
897
898 static ULONG WINAPI HTMLTxtRange_AddRef(IHTMLTxtRange *iface)
899 {
900     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
901     LONG ref = InterlockedIncrement(&This->ref);
902
903     TRACE("(%p) ref=%d\n", This, ref);
904
905     return ref;
906 }
907
908 static ULONG WINAPI HTMLTxtRange_Release(IHTMLTxtRange *iface)
909 {
910     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
911     LONG ref = InterlockedDecrement(&This->ref);
912
913     TRACE("(%p) ref=%d\n", This, ref);
914
915     if(!ref) {
916         if(This->nsrange)
917             nsISelection_Release(This->nsrange);
918         if(This->doc)
919             list_remove(&This->entry);
920         mshtml_free(This);
921     }
922
923     return ref;
924 }
925
926 static HRESULT WINAPI HTMLTxtRange_GetTypeInfoCount(IHTMLTxtRange *iface, UINT *pctinfo)
927 {
928     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
929     FIXME("(%p)->(%p)\n", This, pctinfo);
930     return E_NOTIMPL;
931 }
932
933 static HRESULT WINAPI HTMLTxtRange_GetTypeInfo(IHTMLTxtRange *iface, UINT iTInfo,
934                                                LCID lcid, ITypeInfo **ppTInfo)
935 {
936     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
937     FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
938     return E_NOTIMPL;
939 }
940
941 static HRESULT WINAPI HTMLTxtRange_GetIDsOfNames(IHTMLTxtRange *iface, REFIID riid,
942                                                  LPOLESTR *rgszNames, UINT cNames,
943                                                  LCID lcid, DISPID *rgDispId)
944 {
945     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
946     FIXME("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
947           lcid, rgDispId);
948     return E_NOTIMPL;
949 }
950
951 static HRESULT WINAPI HTMLTxtRange_Invoke(IHTMLTxtRange *iface, DISPID dispIdMember,
952                             REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
953                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
954 {
955     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
956     FIXME("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
957           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
958     return E_NOTIMPL;
959 }
960
961 static HRESULT WINAPI HTMLTxtRange_get_htmlText(IHTMLTxtRange *iface, BSTR *p)
962 {
963     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
964
965     TRACE("(%p)->(%p)\n", This, p);
966
967     *p = NULL;
968
969     if(This->nsrange) {
970         nsIDOMDocumentFragment *fragment;
971         nsresult nsres;
972
973         nsres = nsIDOMRange_CloneContents(This->nsrange, &fragment);
974         if(NS_SUCCEEDED(nsres)) {
975             const PRUnichar *nstext;
976             nsAString nsstr;
977
978             nsAString_Init(&nsstr, NULL);
979             nsnode_to_nsstring((nsIDOMNode*)fragment, &nsstr);
980             nsIDOMDocumentFragment_Release(fragment);
981
982             nsAString_GetData(&nsstr, &nstext, NULL);
983             *p = SysAllocString(nstext);
984
985             nsAString_Finish(&nsstr);
986         }
987     }
988
989     if(!*p) {
990         const WCHAR emptyW[] = {0};
991         *p = SysAllocString(emptyW);
992     }
993
994     TRACE("return %s\n", debugstr_w(*p));
995     return S_OK;
996 }
997
998 static HRESULT WINAPI HTMLTxtRange_put_text(IHTMLTxtRange *iface, BSTR v)
999 {
1000     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1001     nsIDOMDocument *nsdoc;
1002     nsIDOMText *text_node;
1003     nsAString text_str;
1004     nsresult nsres;
1005
1006     TRACE("(%p)->(%s)\n", This, debugstr_w(v));
1007
1008     if(!This->doc)
1009         return MSHTML_E_NODOC;
1010
1011     nsres = nsIWebNavigation_GetDocument(This->doc->nscontainer->navigation, &nsdoc);
1012     if(NS_FAILED(nsres)) {
1013         ERR("GetDocument failed: %08x\n", nsres);
1014         return S_OK;
1015     }
1016
1017     nsAString_Init(&text_str, v);
1018     nsres = nsIDOMDocument_CreateTextNode(nsdoc, &text_str, &text_node);
1019     nsIDOMDocument_Release(nsdoc);
1020     nsAString_Finish(&text_str);
1021     if(NS_FAILED(nsres)) {
1022         ERR("CreateTextNode failed: %08x\n", nsres);
1023         return S_OK;
1024     }
1025     nsres = nsIDOMRange_DeleteContents(This->nsrange);
1026     if(NS_FAILED(nsres))
1027         ERR("DeleteContents failed: %08x\n", nsres);
1028
1029     nsres = nsIDOMRange_InsertNode(This->nsrange, (nsIDOMNode*)text_node);
1030     if(NS_FAILED(nsres))
1031         ERR("InsertNode failed: %08x\n", nsres);
1032
1033     nsres = nsIDOMRange_SetEndAfter(This->nsrange, (nsIDOMNode*)text_node);
1034     if(NS_FAILED(nsres))
1035         ERR("SetEndAfter failed: %08x\n", nsres);
1036
1037     return IHTMLTxtRange_collapse(HTMLTXTRANGE(This), VARIANT_FALSE);
1038 }
1039
1040 static HRESULT WINAPI HTMLTxtRange_get_text(IHTMLTxtRange *iface, BSTR *p)
1041 {
1042     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1043     wstrbuf_t buf;
1044
1045     TRACE("(%p)->(%p)\n", This, p);
1046
1047     *p = NULL;
1048     if(!This->nsrange)
1049         return S_OK;
1050
1051     wstrbuf_init(&buf);
1052     range_to_string(This, &buf);
1053     if(buf.buf)
1054         *p = SysAllocString(buf.buf);
1055     wstrbuf_finish(&buf);
1056
1057     TRACE("ret %s\n", debugstr_w(*p));
1058     return S_OK;
1059 }
1060
1061 static HRESULT WINAPI HTMLTxtRange_parentElement(IHTMLTxtRange *iface, IHTMLElement **parent)
1062 {
1063     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1064     nsIDOMNode *nsnode, *tmp;
1065     HTMLDOMNode *node;
1066
1067     TRACE("(%p)->(%p)\n", This, parent);
1068
1069     nsIDOMRange_GetCommonAncestorContainer(This->nsrange, &nsnode);
1070     while(nsnode && get_node_type(nsnode) != ELEMENT_NODE) {
1071         nsIDOMNode_GetParentNode(nsnode, &tmp);
1072         nsIDOMNode_Release(nsnode);
1073         nsnode = tmp;
1074     }
1075
1076     if(!nsnode) {
1077         *parent = NULL;
1078         return S_OK;
1079     }
1080
1081     node = get_node(This->doc, nsnode);
1082     nsIDOMNode_Release(nsnode);
1083
1084     return IHTMLDOMNode_QueryInterface(HTMLDOMNODE(node), &IID_IHTMLElement, (void**)parent);
1085 }
1086
1087 static HRESULT WINAPI HTMLTxtRange_duplicate(IHTMLTxtRange *iface, IHTMLTxtRange **Duplicate)
1088 {
1089     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1090     nsIDOMRange *nsrange = NULL;
1091
1092     TRACE("(%p)->(%p)\n", This, Duplicate);
1093
1094     nsIDOMRange_CloneRange(This->nsrange, &nsrange);
1095     *Duplicate = HTMLTxtRange_Create(This->doc, nsrange);
1096     nsIDOMRange_Release(nsrange);
1097
1098     return S_OK;
1099 }
1100
1101 static HRESULT WINAPI HTMLTxtRange_inRange(IHTMLTxtRange *iface, IHTMLTxtRange *Range,
1102         VARIANT_BOOL *InRange)
1103 {
1104     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1105     HTMLTxtRange *src_range;
1106     PRInt16 nsret = 0;
1107     nsresult nsres;
1108
1109     TRACE("(%p)->(%p %p)\n", This, Range, InRange);
1110
1111     *InRange = VARIANT_FALSE;
1112
1113     src_range = get_range_object(This->doc, Range);
1114     if(!src_range)
1115         return E_FAIL;
1116
1117     nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, NS_START_TO_START,
1118             src_range->nsrange, &nsret);
1119     if(NS_SUCCEEDED(nsres) && nsret <= 0) {
1120         nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, NS_END_TO_END,
1121                 src_range->nsrange, &nsret);
1122         if(NS_SUCCEEDED(nsres) && nsret >= 0)
1123             *InRange = VARIANT_TRUE;
1124     }
1125
1126     if(NS_FAILED(nsres))
1127         ERR("CompareBoundaryPoints failed: %08x\n", nsres);
1128
1129     return S_OK;
1130 }
1131
1132 static HRESULT WINAPI HTMLTxtRange_isEqual(IHTMLTxtRange *iface, IHTMLTxtRange *Range,
1133         VARIANT_BOOL *IsEqual)
1134 {
1135     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1136     HTMLTxtRange *src_range;
1137     PRInt16 nsret = 0;
1138     nsresult nsres;
1139
1140     TRACE("(%p)->(%p %p)\n", This, Range, IsEqual);
1141
1142     *IsEqual = VARIANT_FALSE;
1143
1144     src_range = get_range_object(This->doc, Range);
1145     if(!src_range)
1146         return E_FAIL;
1147
1148     nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, NS_START_TO_START,
1149             src_range->nsrange, &nsret);
1150     if(NS_SUCCEEDED(nsres) && !nsret) {
1151         nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, NS_END_TO_END,
1152                 src_range->nsrange, &nsret);
1153         if(NS_SUCCEEDED(nsres) && !nsret)
1154             *IsEqual = VARIANT_TRUE;
1155     }
1156
1157     if(NS_FAILED(nsres))
1158         ERR("CompareBoundaryPoints failed: %08x\n", nsres);
1159
1160     return S_OK;
1161 }
1162
1163 static HRESULT WINAPI HTMLTxtRange_scrollIntoView(IHTMLTxtRange *iface, VARIANT_BOOL fStart)
1164 {
1165     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1166     FIXME("(%p)->(%x)\n", This, fStart);
1167     return E_NOTIMPL;
1168 }
1169
1170 static HRESULT WINAPI HTMLTxtRange_collapse(IHTMLTxtRange *iface, VARIANT_BOOL Start)
1171 {
1172     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1173
1174     TRACE("(%p)->(%x)\n", This, Start);
1175
1176     nsIDOMRange_Collapse(This->nsrange, Start != VARIANT_FALSE);
1177     return S_OK;
1178 }
1179
1180 static HRESULT WINAPI HTMLTxtRange_expand(IHTMLTxtRange *iface, BSTR Unit, VARIANT_BOOL *Success)
1181 {
1182     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1183     range_unit_t unit;
1184
1185     TRACE("(%p)->(%s %p)\n", This, debugstr_w(Unit), Success);
1186
1187     unit = string_to_unit(Unit);
1188     if(unit == RU_UNKNOWN)
1189         return E_INVALIDARG;
1190
1191     *Success = VARIANT_FALSE;
1192
1193     switch(unit) {
1194     case RU_WORD: {
1195         dompos_t end_pos, start_pos, new_pos;
1196
1197         get_cur_pos(This, TRUE, &start_pos);
1198         get_cur_pos(This, FALSE, &end_pos);
1199
1200         if(!isspaceW(get_pos_char(&end_pos))) {
1201             if(find_next_space(&end_pos, &new_pos)) {
1202                 set_range_pos(This, FALSE, &new_pos);
1203                 *Success = VARIANT_TRUE;
1204             }
1205             dompos_release(&new_pos);
1206         }
1207
1208         if(find_prev_space(This, &start_pos, TRUE, &new_pos)) {
1209             set_range_pos(This, TRUE, &new_pos);
1210             *Success = VARIANT_TRUE;
1211         }
1212
1213         dompos_release(&new_pos);
1214         dompos_release(&end_pos);
1215
1216         break;
1217     }
1218
1219     case RU_TEXTEDIT: {
1220         nsIDOMDocument *nsdoc;
1221         nsIDOMHTMLDocument *nshtmldoc;
1222         nsIDOMHTMLElement *nsbody = NULL;
1223         nsresult nsres;
1224
1225         nsres = nsIWebNavigation_GetDocument(This->doc->nscontainer->navigation, &nsdoc);
1226         if(NS_FAILED(nsres) || !nsdoc) {
1227             ERR("GetDocument failed: %08x\n", nsres);
1228             break;
1229         }
1230
1231         nsIDOMDocument_QueryInterface(nsdoc, &IID_nsIDOMHTMLDocument, (void**)&nshtmldoc);
1232         nsIDOMDocument_Release(nsdoc);
1233
1234         nsres = nsIDOMHTMLDocument_GetBody(nshtmldoc, &nsbody);
1235         nsIDOMHTMLDocument_Release(nshtmldoc);
1236         if(NS_FAILED(nsres) || !nsbody) {
1237             ERR("Could not get body: %08x\n", nsres);
1238             break;
1239         }
1240
1241         nsres = nsIDOMRange_SelectNodeContents(This->nsrange, (nsIDOMNode*)nsbody);
1242         nsIDOMHTMLElement_Release(nsbody);
1243         if(NS_FAILED(nsres)) {
1244             ERR("Collapse failed: %08x\n", nsres);
1245             break;
1246         }
1247
1248         *Success = VARIANT_TRUE;
1249         break;
1250     }
1251
1252     default:
1253         FIXME("Unimplemented unit %s\n", debugstr_w(Unit));
1254     }
1255
1256     return S_OK;
1257 }
1258
1259 static HRESULT WINAPI HTMLTxtRange_move(IHTMLTxtRange *iface, BSTR Unit,
1260         long Count, long *ActualCount)
1261 {
1262     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1263     range_unit_t unit;
1264
1265     TRACE("(%p)->(%s %ld %p)\n", This, debugstr_w(Unit), Count, ActualCount);
1266
1267     unit = string_to_unit(Unit);
1268     if(unit == RU_UNKNOWN)
1269         return E_INVALIDARG;
1270
1271     IHTMLTxtRange_collapse(HTMLTXTRANGE(This), TRUE);
1272
1273     if(!Count) {
1274         *ActualCount = 0;
1275         return S_OK;
1276     }
1277
1278     switch(unit) {
1279     case RU_CHAR: {
1280         dompos_t cur_pos, new_pos;
1281
1282         get_cur_pos(This, TRUE, &cur_pos);
1283
1284         if(Count > 0) {
1285             *ActualCount = move_next_chars(Count, &cur_pos, TRUE, NULL, NULL, &new_pos);
1286             set_range_pos(This, FALSE, &new_pos);
1287             IHTMLTxtRange_collapse(HTMLTXTRANGE(This), FALSE);
1288             dompos_release(&new_pos);
1289         }else {
1290             *ActualCount = -move_prev_chars(This, -Count, &cur_pos, FALSE, NULL, NULL, &new_pos);
1291             set_range_pos(This, TRUE, &new_pos);
1292             IHTMLTxtRange_collapse(HTMLTXTRANGE(This), TRUE);
1293             dompos_release(&new_pos);
1294         }
1295
1296         dompos_release(&cur_pos);
1297         break;
1298     }
1299
1300     case RU_WORD: {
1301         dompos_t cur_pos, new_pos;
1302
1303         get_cur_pos(This, TRUE, &cur_pos);
1304
1305         if(Count > 0) {
1306             *ActualCount = move_next_words(Count, &cur_pos, &new_pos);
1307             set_range_pos(This, FALSE, &new_pos);
1308             IHTMLTxtRange_collapse(HTMLTXTRANGE(This), FALSE);
1309             dompos_release(&new_pos);
1310         }else {
1311             *ActualCount = -move_prev_words(This, -Count, &cur_pos, &new_pos);
1312             set_range_pos(This, TRUE, &new_pos);
1313             IHTMLTxtRange_collapse(HTMLTXTRANGE(This), TRUE);
1314             dompos_release(&new_pos);
1315         }
1316
1317         dompos_release(&cur_pos);
1318         break;
1319     }
1320
1321     default:
1322         FIXME("unimplemented unit %s\n", debugstr_w(Unit));
1323     }
1324
1325     TRACE("ret %ld\n", *ActualCount);
1326     return S_OK;
1327 }
1328
1329 static HRESULT WINAPI HTMLTxtRange_moveStart(IHTMLTxtRange *iface, BSTR Unit,
1330         long Count, long *ActualCount)
1331 {
1332     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1333     range_unit_t unit;
1334
1335     TRACE("(%p)->(%s %ld %p)\n", This, debugstr_w(Unit), Count, ActualCount);
1336
1337     unit = string_to_unit(Unit);
1338     if(unit == RU_UNKNOWN)
1339         return E_INVALIDARG;
1340
1341     if(!Count) {
1342         *ActualCount = 0;
1343         return S_OK;
1344     }
1345
1346     switch(unit) {
1347     case RU_CHAR: {
1348         dompos_t start_pos, end_pos, new_pos;
1349         PRBool collapsed;
1350
1351         get_cur_pos(This, TRUE, &start_pos);
1352         get_cur_pos(This, FALSE, &end_pos);
1353         nsIDOMRange_GetCollapsed(This->nsrange, &collapsed);
1354
1355         if(Count > 0) {
1356             BOOL bounded;
1357
1358             *ActualCount = move_next_chars(Count, &start_pos, collapsed, &end_pos, &bounded, &new_pos);
1359             set_range_pos(This, !bounded, &new_pos);
1360             if(bounded)
1361                 IHTMLTxtRange_collapse(HTMLTXTRANGE(This), FALSE);
1362         }else {
1363             *ActualCount = -move_prev_chars(This, -Count, &start_pos, FALSE, NULL, NULL, &new_pos);
1364             set_range_pos(This, TRUE, &new_pos);
1365         }
1366
1367         dompos_release(&start_pos);
1368         dompos_release(&end_pos);
1369         dompos_release(&new_pos);
1370         break;
1371     }
1372
1373     default:
1374         FIXME("unimplemented unit %s\n", debugstr_w(Unit));
1375     }
1376
1377     return S_OK;
1378 }
1379
1380 static HRESULT WINAPI HTMLTxtRange_moveEnd(IHTMLTxtRange *iface, BSTR Unit,
1381         long Count, long *ActualCount)
1382 {
1383     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1384     range_unit_t unit;
1385
1386     TRACE("(%p)->(%s %ld %p)\n", This, debugstr_w(Unit), Count, ActualCount);
1387
1388     unit = string_to_unit(Unit);
1389     if(unit == RU_UNKNOWN)
1390         return E_INVALIDARG;
1391
1392     if(!Count) {
1393         *ActualCount = 0;
1394         return S_OK;
1395     }
1396
1397     switch(unit) {
1398     case RU_CHAR: {
1399         dompos_t start_pos, end_pos, new_pos;
1400         PRBool collapsed;
1401
1402         get_cur_pos(This, TRUE, &start_pos);
1403         get_cur_pos(This, FALSE, &end_pos);
1404         nsIDOMRange_GetCollapsed(This->nsrange, &collapsed);
1405
1406         if(Count > 0) {
1407             *ActualCount = move_next_chars(Count, &end_pos, collapsed, NULL, NULL, &new_pos);
1408             set_range_pos(This, FALSE, &new_pos);
1409         }else {
1410             BOOL bounded;
1411
1412             *ActualCount = -move_prev_chars(This, -Count, &end_pos, TRUE, &start_pos, &bounded, &new_pos);
1413             set_range_pos(This, bounded, &new_pos);
1414             if(bounded)
1415                 IHTMLTxtRange_collapse(HTMLTXTRANGE(This), TRUE);
1416         }
1417
1418         dompos_release(&start_pos);
1419         dompos_release(&end_pos);
1420         dompos_release(&new_pos);
1421         break;
1422     }
1423
1424     default:
1425         FIXME("unimplemented unit %s\n", debugstr_w(Unit));
1426     }
1427
1428     return S_OK;
1429 }
1430
1431 static HRESULT WINAPI HTMLTxtRange_select(IHTMLTxtRange *iface)
1432 {
1433     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1434
1435     TRACE("(%p)\n", This);
1436
1437     if(This->doc->nscontainer) {
1438         nsIDOMWindow *dom_window = NULL;
1439         nsISelection *nsselection;
1440
1441         nsIWebBrowser_GetContentDOMWindow(This->doc->nscontainer->webbrowser, &dom_window);
1442         nsIDOMWindow_GetSelection(dom_window, &nsselection);
1443         nsIDOMWindow_Release(dom_window);
1444
1445         nsISelection_RemoveAllRanges(nsselection);
1446         nsISelection_AddRange(nsselection, This->nsrange);
1447
1448         nsISelection_Release(nsselection);
1449     }
1450
1451     return S_OK;
1452 }
1453
1454 static HRESULT WINAPI HTMLTxtRange_pasteHTML(IHTMLTxtRange *iface, BSTR html)
1455 {
1456     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1457     FIXME("(%p)->(%s)\n", This, debugstr_w(html));
1458     return E_NOTIMPL;
1459 }
1460
1461 static HRESULT WINAPI HTMLTxtRange_moveToElementText(IHTMLTxtRange *iface, IHTMLElement *element)
1462 {
1463     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1464     FIXME("(%p)->(%p)\n", This, element);
1465     return E_NOTIMPL;
1466 }
1467
1468 static HRESULT WINAPI HTMLTxtRange_setEndPoint(IHTMLTxtRange *iface, BSTR how,
1469         IHTMLTxtRange *SourceRange)
1470 {
1471     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1472     FIXME("(%p)->(%s %p)\n", This, debugstr_w(how), SourceRange);
1473     return E_NOTIMPL;
1474 }
1475
1476 static HRESULT WINAPI HTMLTxtRange_compareEndPoints(IHTMLTxtRange *iface, BSTR how,
1477         IHTMLTxtRange *SourceRange, long *ret)
1478 {
1479     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1480     HTMLTxtRange *src_range;
1481     PRInt16 nsret = 0;
1482     int nscmpt;
1483     nsresult nsres;
1484
1485     TRACE("(%p)->(%s %p %p)\n", This, debugstr_w(how), SourceRange, ret);
1486
1487     nscmpt = string_to_nscmptype(how);
1488     if(nscmpt == -1)
1489         return E_INVALIDARG;
1490
1491     src_range = get_range_object(This->doc, SourceRange);
1492     if(!src_range)
1493         return E_FAIL;
1494
1495     nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, nscmpt, src_range->nsrange, &nsret);
1496     if(NS_FAILED(nsres))
1497         ERR("CompareBoundaryPoints failed: %08x\n", nsres);
1498
1499     *ret = nsret;
1500     return S_OK;
1501 }
1502
1503 static HRESULT WINAPI HTMLTxtRange_findText(IHTMLTxtRange *iface, BSTR String,
1504         long count, long Flags, VARIANT_BOOL *Success)
1505 {
1506     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1507     FIXME("(%p)->(%s %ld %08lx %p)\n", This, debugstr_w(String), count, Flags, Success);
1508     return E_NOTIMPL;
1509 }
1510
1511 static HRESULT WINAPI HTMLTxtRange_moveToPoint(IHTMLTxtRange *iface, long x, long y)
1512 {
1513     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1514     FIXME("(%p)->(%ld %ld)\n", This, x, y);
1515     return E_NOTIMPL;
1516 }
1517
1518 static HRESULT WINAPI HTMLTxtRange_getBookmark(IHTMLTxtRange *iface, BSTR *Bookmark)
1519 {
1520     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1521     FIXME("(%p)->(%p)\n", This, Bookmark);
1522     return E_NOTIMPL;
1523 }
1524
1525 static HRESULT WINAPI HTMLTxtRange_moveToBookmark(IHTMLTxtRange *iface, BSTR Bookmark,
1526         VARIANT_BOOL *Success)
1527 {
1528     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1529     FIXME("(%p)->(%s %p)\n", This, debugstr_w(Bookmark), Success);
1530     return E_NOTIMPL;
1531 }
1532
1533 static HRESULT WINAPI HTMLTxtRange_queryCommandSupported(IHTMLTxtRange *iface, BSTR cmdID,
1534         VARIANT_BOOL *pfRet)
1535 {
1536     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1537     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
1538     return E_NOTIMPL;
1539 }
1540
1541 static HRESULT WINAPI HTMLTxtRange_queryCommandEnabled(IHTMLTxtRange *iface, BSTR cmdID,
1542         VARIANT_BOOL *pfRet)
1543 {
1544     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1545     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
1546     return E_NOTIMPL;
1547 }
1548
1549 static HRESULT WINAPI HTMLTxtRange_queryCommandState(IHTMLTxtRange *iface, BSTR cmdID,
1550         VARIANT_BOOL *pfRet)
1551 {
1552     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1553     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
1554     return E_NOTIMPL;
1555 }
1556
1557 static HRESULT WINAPI HTMLTxtRange_queryCommandIndeterm(IHTMLTxtRange *iface, BSTR cmdID,
1558         VARIANT_BOOL *pfRet)
1559 {
1560     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1561     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
1562     return E_NOTIMPL;
1563 }
1564
1565 static HRESULT WINAPI HTMLTxtRange_queryCommandText(IHTMLTxtRange *iface, BSTR cmdID,
1566         BSTR *pcmdText)
1567 {
1568     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1569     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pcmdText);
1570     return E_NOTIMPL;
1571 }
1572
1573 static HRESULT WINAPI HTMLTxtRange_queryCommandValue(IHTMLTxtRange *iface, BSTR cmdID,
1574         VARIANT *pcmdValue)
1575 {
1576     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1577     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pcmdValue);
1578     return E_NOTIMPL;
1579 }
1580
1581 static HRESULT WINAPI HTMLTxtRange_execCommand(IHTMLTxtRange *iface, BSTR cmdID,
1582         VARIANT_BOOL showUI, VARIANT value, VARIANT_BOOL *pfRet)
1583 {
1584     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1585     FIXME("(%p)->(%s %x v %p)\n", This, debugstr_w(cmdID), showUI, pfRet);
1586     return E_NOTIMPL;
1587 }
1588
1589 static HRESULT WINAPI HTMLTxtRange_execCommandShowHelp(IHTMLTxtRange *iface, BSTR cmdID,
1590         VARIANT_BOOL *pfRet)
1591 {
1592     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
1593     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
1594     return E_NOTIMPL;
1595 }
1596
1597 #undef HTMLTXTRANGE_THIS
1598
1599 static const IHTMLTxtRangeVtbl HTMLTxtRangeVtbl = {
1600     HTMLTxtRange_QueryInterface,
1601     HTMLTxtRange_AddRef,
1602     HTMLTxtRange_Release,
1603     HTMLTxtRange_GetTypeInfoCount,
1604     HTMLTxtRange_GetTypeInfo,
1605     HTMLTxtRange_GetIDsOfNames,
1606     HTMLTxtRange_Invoke,
1607     HTMLTxtRange_get_htmlText,
1608     HTMLTxtRange_put_text,
1609     HTMLTxtRange_get_text,
1610     HTMLTxtRange_parentElement,
1611     HTMLTxtRange_duplicate,
1612     HTMLTxtRange_inRange,
1613     HTMLTxtRange_isEqual,
1614     HTMLTxtRange_scrollIntoView,
1615     HTMLTxtRange_collapse,
1616     HTMLTxtRange_expand,
1617     HTMLTxtRange_move,
1618     HTMLTxtRange_moveStart,
1619     HTMLTxtRange_moveEnd,
1620     HTMLTxtRange_select,
1621     HTMLTxtRange_pasteHTML,
1622     HTMLTxtRange_moveToElementText,
1623     HTMLTxtRange_setEndPoint,
1624     HTMLTxtRange_compareEndPoints,
1625     HTMLTxtRange_findText,
1626     HTMLTxtRange_moveToPoint,
1627     HTMLTxtRange_getBookmark,
1628     HTMLTxtRange_moveToBookmark,
1629     HTMLTxtRange_queryCommandSupported,
1630     HTMLTxtRange_queryCommandEnabled,
1631     HTMLTxtRange_queryCommandState,
1632     HTMLTxtRange_queryCommandIndeterm,
1633     HTMLTxtRange_queryCommandText,
1634     HTMLTxtRange_queryCommandValue,
1635     HTMLTxtRange_execCommand,
1636     HTMLTxtRange_execCommandShowHelp
1637 };
1638
1639 IHTMLTxtRange *HTMLTxtRange_Create(HTMLDocument *doc, nsIDOMRange *nsrange)
1640 {
1641     HTMLTxtRange *ret = mshtml_alloc(sizeof(HTMLTxtRange));
1642
1643     ret->lpHTMLTxtRangeVtbl = &HTMLTxtRangeVtbl;
1644     ret->ref = 1;
1645
1646     if(nsrange)
1647         nsIDOMRange_AddRef(nsrange);
1648     ret->nsrange = nsrange;
1649
1650     ret->doc = doc;
1651     list_add_head(&doc->range_list, &ret->entry);
1652
1653     return HTMLTXTRANGE(ret);
1654 }
1655
1656 void detach_ranges(HTMLDocument *This)
1657 {
1658     HTMLTxtRange *iter;
1659
1660     LIST_FOR_EACH_ENTRY(iter, &This->range_list, HTMLTxtRange, entry) {
1661         iter->doc = NULL;
1662     }
1663 }