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