mshtml: Added IHTMLTxtRange::isEqual implementation.
[wine] / dlls / mshtml / txtrange.c
1 /*
2  * Copyright 2006 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 typedef struct {
40     const IHTMLTxtRangeVtbl *lpHTMLTxtRangeVtbl;
41
42     LONG ref;
43
44     nsIDOMRange *nsrange;
45     HTMLDocument *doc;
46
47     struct list entry;
48 } HTMLTxtRange;
49
50 #define HTMLTXTRANGE(x)  ((IHTMLTxtRange*)  &(x)->lpHTMLTxtRangeVtbl)
51
52 static HTMLTxtRange *get_range_object(HTMLDocument *doc, IHTMLTxtRange *iface)
53 {
54     HTMLTxtRange *iter;
55
56     LIST_FOR_EACH_ENTRY(iter, &doc->range_list, HTMLTxtRange, entry) {
57         if(HTMLTXTRANGE(iter) == iface)
58             return iter;
59     }
60
61     ERR("Could not find range in document\n");
62     return NULL;
63 }
64
65 static int string_to_nscmptype(LPCWSTR str)
66 {
67     static const WCHAR seW[] = {'S','t','a','r','t','T','o','E','n','d',0};
68     static const WCHAR ssW[] = {'S','t','a','r','t','T','o','S','t','a','r','t',0};
69     static const WCHAR esW[] = {'E','n','d','T','o','S','t','a','r','t',0};
70     static const WCHAR eeW[] = {'E','n','d','T','o','E','n','d',0};
71
72     if(!strcmpiW(str, seW))  return NS_START_TO_END;
73     if(!strcmpiW(str, ssW))  return NS_START_TO_START;
74     if(!strcmpiW(str, esW))  return NS_END_TO_START;
75     if(!strcmpiW(str, eeW))  return NS_END_TO_END;
76
77     return -1;
78 }
79
80 static PRUint16 get_node_type(nsIDOMNode *node)
81 {
82     PRUint16 type = 0xfff;
83
84     if(node)
85         nsIDOMNode_GetNodeType(node, &type);
86
87     return type;
88 }
89
90 #define HTMLTXTRANGE_THIS(iface) DEFINE_THIS(HTMLTxtRange, HTMLTxtRange, iface)
91
92 static HRESULT WINAPI HTMLTxtRange_QueryInterface(IHTMLTxtRange *iface, REFIID riid, void **ppv)
93 {
94     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
95
96     *ppv = NULL;
97
98     if(IsEqualGUID(&IID_IUnknown, riid)) {
99         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
100         *ppv = HTMLTXTRANGE(This);
101     }else if(IsEqualGUID(&IID_IDispatch, riid)) {
102         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
103         *ppv = HTMLTXTRANGE(This);
104     }else if(IsEqualGUID(&IID_IHTMLTxtRange, riid)) {
105         TRACE("(%p)->(IID_IHTMLTxtRange %p)\n", This, ppv);
106         *ppv = HTMLTXTRANGE(This);
107     }
108
109     if(*ppv) {
110         IUnknown_AddRef((IUnknown*)*ppv);
111         return S_OK;
112     }
113
114     WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
115     return E_NOINTERFACE;
116 }
117
118 static ULONG WINAPI HTMLTxtRange_AddRef(IHTMLTxtRange *iface)
119 {
120     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
121     LONG ref = InterlockedIncrement(&This->ref);
122
123     TRACE("(%p) ref=%d\n", This, ref);
124
125     return ref;
126 }
127
128 static ULONG WINAPI HTMLTxtRange_Release(IHTMLTxtRange *iface)
129 {
130     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
131     LONG ref = InterlockedDecrement(&This->ref);
132
133     TRACE("(%p) ref=%d\n", This, ref);
134
135     if(!ref) {
136         if(This->nsrange)
137             nsISelection_Release(This->nsrange);
138         if(This->doc)
139             list_remove(&This->entry);
140         mshtml_free(This);
141     }
142
143     return ref;
144 }
145
146 static HRESULT WINAPI HTMLTxtRange_GetTypeInfoCount(IHTMLTxtRange *iface, UINT *pctinfo)
147 {
148     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
149     FIXME("(%p)->(%p)\n", This, pctinfo);
150     return E_NOTIMPL;
151 }
152
153 static HRESULT WINAPI HTMLTxtRange_GetTypeInfo(IHTMLTxtRange *iface, UINT iTInfo,
154                                                LCID lcid, ITypeInfo **ppTInfo)
155 {
156     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
157     FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
158     return E_NOTIMPL;
159 }
160
161 static HRESULT WINAPI HTMLTxtRange_GetIDsOfNames(IHTMLTxtRange *iface, REFIID riid,
162                                                  LPOLESTR *rgszNames, UINT cNames,
163                                                  LCID lcid, DISPID *rgDispId)
164 {
165     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
166     FIXME("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
167           lcid, rgDispId);
168     return E_NOTIMPL;
169 }
170
171 static HRESULT WINAPI HTMLTxtRange_Invoke(IHTMLTxtRange *iface, DISPID dispIdMember,
172                             REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
173                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
174 {
175     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
176     FIXME("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
177           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
178     return E_NOTIMPL;
179 }
180
181 static HRESULT WINAPI HTMLTxtRange_get_htmlText(IHTMLTxtRange *iface, BSTR *p)
182 {
183     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
184
185     TRACE("(%p)->(%p)\n", This, p);
186
187     *p = NULL;
188
189     if(This->nsrange) {
190         nsIDOMDocumentFragment *fragment;
191         nsresult nsres;
192
193         nsres = nsIDOMRange_CloneContents(This->nsrange, &fragment);
194         if(NS_SUCCEEDED(nsres)) {
195             const PRUnichar *nstext;
196             nsAString nsstr;
197
198             nsAString_Init(&nsstr, NULL);
199             nsnode_to_nsstring((nsIDOMNode*)fragment, &nsstr);
200             nsIDOMDocumentFragment_Release(fragment);
201
202             nsAString_GetData(&nsstr, &nstext, NULL);
203             *p = SysAllocString(nstext);
204
205             nsAString_Finish(&nsstr);
206         }
207     }
208
209     if(!*p) {
210         const WCHAR emptyW[] = {0};
211         *p = SysAllocString(emptyW);
212     }
213
214     TRACE("return %s\n", debugstr_w(*p));
215     return S_OK;
216 }
217
218 static HRESULT WINAPI HTMLTxtRange_put_text(IHTMLTxtRange *iface, BSTR v)
219 {
220     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
221     nsIDOMDocument *nsdoc;
222     nsIDOMText *text_node;
223     nsAString text_str;
224     nsresult nsres;
225
226     TRACE("(%p)->(%s)\n", This, debugstr_w(v));
227
228     if(!This->doc)
229         return MSHTML_E_NODOC;
230
231     nsres = nsIWebNavigation_GetDocument(This->doc->nscontainer->navigation, &nsdoc);
232     if(NS_FAILED(nsres)) {
233         ERR("GetDocument failed: %08x\n", nsres);
234         return S_OK;
235     }
236
237     nsAString_Init(&text_str, v);
238     nsres = nsIDOMDocument_CreateTextNode(nsdoc, &text_str, &text_node);
239     nsAString_Finish(&text_str);
240     if(NS_FAILED(nsres)) {
241         ERR("CreateTextNode failed: %08x\n", nsres);
242         return S_OK;
243     }
244     nsres = nsIDOMRange_DeleteContents(This->nsrange);
245     if(NS_FAILED(nsres))
246         ERR("DeleteContents failed: %08x\n", nsres);
247
248     nsres = nsIDOMRange_InsertNode(This->nsrange, (nsIDOMNode*)text_node);
249     if(NS_FAILED(nsres))
250         ERR("InsertNode failed: %08x\n", nsres);
251
252     return S_OK;
253 }
254
255 static HRESULT WINAPI HTMLTxtRange_get_text(IHTMLTxtRange *iface, BSTR *p)
256 {
257     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
258
259     TRACE("(%p)->(%p)\n", This, p);
260
261     *p = NULL;
262
263     if(This->nsrange) {
264         nsAString text_str;
265         nsresult nsres;
266
267         nsAString_Init(&text_str, NULL);
268
269         nsres = nsIDOMRange_ToString(This->nsrange, &text_str);
270         if(NS_SUCCEEDED(nsres)) {
271             const PRUnichar *nstext;
272
273             nsAString_GetData(&text_str, &nstext, NULL);
274             *p = SysAllocString(nstext);
275         }else {
276             ERR("ToString failed: %08x\n", nsres);
277         }
278
279         nsAString_Finish(&text_str);
280     }
281
282     if(!*p) {
283         static const WCHAR empty[] = {0};
284         *p = SysAllocString(empty);
285     }
286
287     return S_OK;
288 }
289
290 static HRESULT WINAPI HTMLTxtRange_parentElement(IHTMLTxtRange *iface, IHTMLElement **parent)
291 {
292     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
293     nsIDOMNode *nsnode, *tmp;
294     HTMLDOMNode *node;
295     HRESULT hres;
296
297     TRACE("(%p)->(%p)\n", This, parent);
298
299     nsIDOMRange_GetCommonAncestorContainer(This->nsrange, &nsnode);
300     while(nsnode && get_node_type(nsnode) != ELEMENT_NODE) {
301         nsIDOMNode_GetParentNode(nsnode, &tmp);
302         nsIDOMNode_Release(nsnode);
303         nsnode = tmp;
304     }
305
306     if(!nsnode) {
307         *parent = NULL;
308         return S_OK;
309     }
310
311     node = get_node(This->doc, nsnode);
312     nsIDOMNode_Release(nsnode);
313
314     hres = IHTMLDOMNode_QueryInterface(HTMLDOMNODE(node), &IID_IHTMLElement, (void**)parent);
315
316     IHTMLDOMNode_Release(HTMLDOMNODE(node));
317     return hres;
318 }
319
320 static HRESULT WINAPI HTMLTxtRange_duplicate(IHTMLTxtRange *iface, IHTMLTxtRange **Duplicate)
321 {
322     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
323     nsIDOMRange *nsrange = NULL;
324
325     TRACE("(%p)->(%p)\n", This, Duplicate);
326
327     nsIDOMRange_CloneRange(This->nsrange, &nsrange);
328     *Duplicate = HTMLTxtRange_Create(This->doc, nsrange);
329     nsIDOMRange_Release(nsrange);
330
331     return S_OK;
332 }
333
334 static HRESULT WINAPI HTMLTxtRange_inRange(IHTMLTxtRange *iface, IHTMLTxtRange *Range,
335         VARIANT_BOOL *InRange)
336 {
337     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
338     HTMLTxtRange *src_range;
339     PRInt16 nsret = 0;
340     nsresult nsres;
341
342     TRACE("(%p)->(%p %p)\n", This, Range, InRange);
343
344     *InRange = VARIANT_FALSE;
345
346     src_range = get_range_object(This->doc, Range);
347     if(!src_range)
348         return E_FAIL;
349
350     nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, NS_START_TO_START,
351             src_range->nsrange, &nsret);
352     if(NS_SUCCEEDED(nsres) && nsret <= 0) {
353         nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, NS_END_TO_END,
354                 src_range->nsrange, &nsret);
355         if(NS_SUCCEEDED(nsres) && nsret >= 0)
356             *InRange = VARIANT_TRUE;
357     }
358
359     if(NS_FAILED(nsres))
360         ERR("CompareBoundaryPoints failed: %08x\n", nsres);
361
362     return S_OK;
363 }
364
365 static HRESULT WINAPI HTMLTxtRange_isEqual(IHTMLTxtRange *iface, IHTMLTxtRange *Range,
366         VARIANT_BOOL *IsEqual)
367 {
368     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
369     HTMLTxtRange *src_range;
370     PRInt16 nsret = 0;
371     nsresult nsres;
372
373     TRACE("(%p)->(%p %p)\n", This, Range, IsEqual);
374
375     *IsEqual = VARIANT_FALSE;
376
377     src_range = get_range_object(This->doc, Range);
378     if(!src_range)
379         return E_FAIL;
380
381     nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, NS_START_TO_START,
382             src_range->nsrange, &nsret);
383     if(NS_SUCCEEDED(nsres) && !nsret) {
384         nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, NS_END_TO_END,
385                 src_range->nsrange, &nsret);
386         if(NS_SUCCEEDED(nsres) && !nsret)
387             *IsEqual = VARIANT_TRUE;
388     }
389
390     if(NS_FAILED(nsres))
391         ERR("CompareBoundaryPoints failed: %08x\n", nsres);
392
393     return S_OK;
394 }
395
396 static HRESULT WINAPI HTMLTxtRange_scrollIntoView(IHTMLTxtRange *iface, VARIANT_BOOL fStart)
397 {
398     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
399     FIXME("(%p)->(%x)\n", This, fStart);
400     return E_NOTIMPL;
401 }
402
403 static HRESULT WINAPI HTMLTxtRange_collapse(IHTMLTxtRange *iface, VARIANT_BOOL Start)
404 {
405     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
406
407     TRACE("(%p)->(%x)\n", This, Start);
408
409     nsIDOMRange_Collapse(This->nsrange, Start != VARIANT_FALSE);
410     return S_OK;
411 }
412
413 static HRESULT WINAPI HTMLTxtRange_expand(IHTMLTxtRange *iface, BSTR Unit, VARIANT_BOOL *Success)
414 {
415     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
416     FIXME("(%p)->(%s %p)\n", This, debugstr_w(Unit), Success);
417     return E_NOTIMPL;
418 }
419
420 static HRESULT WINAPI HTMLTxtRange_move(IHTMLTxtRange *iface, BSTR Unit,
421         long Count, long *ActualCount)
422 {
423     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
424     FIXME("(%p)->(%s %ld %p)\n", This, debugstr_w(Unit), Count, ActualCount);
425     return E_NOTIMPL;
426 }
427
428 static HRESULT WINAPI HTMLTxtRange_moveStart(IHTMLTxtRange *iface, BSTR Unit,
429         long Count, long *ActualCount)
430 {
431     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
432     FIXME("(%p)->(%s %ld %p)\n", This, debugstr_w(Unit), Count, ActualCount);
433     return E_NOTIMPL;
434 }
435
436 static HRESULT WINAPI HTMLTxtRange_moveEnd(IHTMLTxtRange *iface, BSTR Unit,
437         long Count, long *ActualCount)
438 {
439     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
440     FIXME("(%p)->(%s %ld %p)\n", This, debugstr_w(Unit), Count, ActualCount);
441     return E_NOTIMPL;
442 }
443
444 static HRESULT WINAPI HTMLTxtRange_select(IHTMLTxtRange *iface)
445 {
446     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
447
448     TRACE("(%p)\n", This);
449
450     if(This->doc->nscontainer) {
451         nsIDOMWindow *dom_window = NULL;
452         nsISelection *nsselection;
453
454         nsIWebBrowser_GetContentDOMWindow(This->doc->nscontainer->webbrowser, &dom_window);
455         nsIDOMWindow_GetSelection(dom_window, &nsselection);
456         nsIDOMWindow_Release(dom_window);
457
458         nsISelection_RemoveAllRanges(nsselection);
459         nsISelection_AddRange(nsselection, This->nsrange);
460
461         nsISelection_Release(nsselection);
462     }
463
464     return S_OK;
465 }
466
467 static HRESULT WINAPI HTMLTxtRange_pasteHTML(IHTMLTxtRange *iface, BSTR html)
468 {
469     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
470     FIXME("(%p)->(%s)\n", This, debugstr_w(html));
471     return E_NOTIMPL;
472 }
473
474 static HRESULT WINAPI HTMLTxtRange_moveToElementText(IHTMLTxtRange *iface, IHTMLElement *element)
475 {
476     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
477     FIXME("(%p)->(%p)\n", This, element);
478     return E_NOTIMPL;
479 }
480
481 static HRESULT WINAPI HTMLTxtRange_setEndPoint(IHTMLTxtRange *iface, BSTR how,
482         IHTMLTxtRange *SourceRange)
483 {
484     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
485     FIXME("(%p)->(%s %p)\n", This, debugstr_w(how), SourceRange);
486     return E_NOTIMPL;
487 }
488
489 static HRESULT WINAPI HTMLTxtRange_compareEndPoints(IHTMLTxtRange *iface, BSTR how,
490         IHTMLTxtRange *SourceRange, long *ret)
491 {
492     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
493     HTMLTxtRange *src_range;
494     PRInt16 nsret = 0;
495     int nscmpt;
496     nsresult nsres;
497
498     TRACE("(%p)->(%s %p %p)\n", This, debugstr_w(how), SourceRange, ret);
499
500     nscmpt = string_to_nscmptype(how);
501     if(nscmpt == -1)
502         return E_INVALIDARG;
503
504     src_range = get_range_object(This->doc, SourceRange);
505     if(!src_range)
506         return E_FAIL;
507
508     nsres = nsIDOMRange_CompareBoundaryPoints(This->nsrange, nscmpt, src_range->nsrange, &nsret);
509     if(NS_FAILED(nsres))
510         ERR("CompareBoundaryPoints failed: %08x\n", nsres);
511
512     *ret = nsret;
513     return S_OK;
514 }
515
516 static HRESULT WINAPI HTMLTxtRange_findText(IHTMLTxtRange *iface, BSTR String,
517         long count, long Flags, VARIANT_BOOL *Success)
518 {
519     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
520     FIXME("(%p)->(%s %ld %08lx %p)\n", This, debugstr_w(String), count, Flags, Success);
521     return E_NOTIMPL;
522 }
523
524 static HRESULT WINAPI HTMLTxtRange_moveToPoint(IHTMLTxtRange *iface, long x, long y)
525 {
526     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
527     FIXME("(%p)->(%ld %ld)\n", This, x, y);
528     return E_NOTIMPL;
529 }
530
531 static HRESULT WINAPI HTMLTxtRange_getBookmark(IHTMLTxtRange *iface, BSTR *Bookmark)
532 {
533     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
534     FIXME("(%p)->(%p)\n", This, Bookmark);
535     return E_NOTIMPL;
536 }
537
538 static HRESULT WINAPI HTMLTxtRange_moveToBookmark(IHTMLTxtRange *iface, BSTR Bookmark,
539         VARIANT_BOOL *Success)
540 {
541     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
542     FIXME("(%p)->(%s %p)\n", This, debugstr_w(Bookmark), Success);
543     return E_NOTIMPL;
544 }
545
546 static HRESULT WINAPI HTMLTxtRange_queryCommandSupported(IHTMLTxtRange *iface, BSTR cmdID,
547         VARIANT_BOOL *pfRet)
548 {
549     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
550     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
551     return E_NOTIMPL;
552 }
553
554 static HRESULT WINAPI HTMLTxtRange_queryCommandEnabled(IHTMLTxtRange *iface, BSTR cmdID,
555         VARIANT_BOOL *pfRet)
556 {
557     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
558     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
559     return E_NOTIMPL;
560 }
561
562 static HRESULT WINAPI HTMLTxtRange_queryCommandState(IHTMLTxtRange *iface, BSTR cmdID,
563         VARIANT_BOOL *pfRet)
564 {
565     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
566     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
567     return E_NOTIMPL;
568 }
569
570 static HRESULT WINAPI HTMLTxtRange_queryCommandIndeterm(IHTMLTxtRange *iface, BSTR cmdID,
571         VARIANT_BOOL *pfRet)
572 {
573     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
574     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
575     return E_NOTIMPL;
576 }
577
578 static HRESULT WINAPI HTMLTxtRange_queryCommandText(IHTMLTxtRange *iface, BSTR cmdID,
579         BSTR *pcmdText)
580 {
581     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
582     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pcmdText);
583     return E_NOTIMPL;
584 }
585
586 static HRESULT WINAPI HTMLTxtRange_queryCommandValue(IHTMLTxtRange *iface, BSTR cmdID,
587         VARIANT *pcmdValue)
588 {
589     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
590     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pcmdValue);
591     return E_NOTIMPL;
592 }
593
594 static HRESULT WINAPI HTMLTxtRange_execCommand(IHTMLTxtRange *iface, BSTR cmdID,
595         VARIANT_BOOL showUI, VARIANT value, VARIANT_BOOL *pfRet)
596 {
597     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
598     FIXME("(%p)->(%s %x v %p)\n", This, debugstr_w(cmdID), showUI, pfRet);
599     return E_NOTIMPL;
600 }
601
602 static HRESULT WINAPI HTMLTxtRange_execCommandShowHelp(IHTMLTxtRange *iface, BSTR cmdID,
603         VARIANT_BOOL *pfRet)
604 {
605     HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface);
606     FIXME("(%p)->(%s %p)\n", This, debugstr_w(cmdID), pfRet);
607     return E_NOTIMPL;
608 }
609
610 #undef HTMLTXTRANGE_THIS
611
612 static const IHTMLTxtRangeVtbl HTMLTxtRangeVtbl = {
613     HTMLTxtRange_QueryInterface,
614     HTMLTxtRange_AddRef,
615     HTMLTxtRange_Release,
616     HTMLTxtRange_GetTypeInfoCount,
617     HTMLTxtRange_GetTypeInfo,
618     HTMLTxtRange_GetIDsOfNames,
619     HTMLTxtRange_Invoke,
620     HTMLTxtRange_get_htmlText,
621     HTMLTxtRange_put_text,
622     HTMLTxtRange_get_text,
623     HTMLTxtRange_parentElement,
624     HTMLTxtRange_duplicate,
625     HTMLTxtRange_inRange,
626     HTMLTxtRange_isEqual,
627     HTMLTxtRange_scrollIntoView,
628     HTMLTxtRange_collapse,
629     HTMLTxtRange_expand,
630     HTMLTxtRange_move,
631     HTMLTxtRange_moveStart,
632     HTMLTxtRange_moveEnd,
633     HTMLTxtRange_select,
634     HTMLTxtRange_pasteHTML,
635     HTMLTxtRange_moveToElementText,
636     HTMLTxtRange_setEndPoint,
637     HTMLTxtRange_compareEndPoints,
638     HTMLTxtRange_findText,
639     HTMLTxtRange_moveToPoint,
640     HTMLTxtRange_getBookmark,
641     HTMLTxtRange_moveToBookmark,
642     HTMLTxtRange_queryCommandSupported,
643     HTMLTxtRange_queryCommandEnabled,
644     HTMLTxtRange_queryCommandState,
645     HTMLTxtRange_queryCommandIndeterm,
646     HTMLTxtRange_queryCommandText,
647     HTMLTxtRange_queryCommandValue,
648     HTMLTxtRange_execCommand,
649     HTMLTxtRange_execCommandShowHelp
650 };
651
652 IHTMLTxtRange *HTMLTxtRange_Create(HTMLDocument *doc, nsIDOMRange *nsrange)
653 {
654     HTMLTxtRange *ret = mshtml_alloc(sizeof(HTMLTxtRange));
655
656     ret->lpHTMLTxtRangeVtbl = &HTMLTxtRangeVtbl;
657     ret->ref = 1;
658
659     if(nsrange)
660         nsIDOMRange_AddRef(nsrange);
661     ret->nsrange = nsrange;
662
663     ret->doc = doc;
664     list_add_head(&doc->range_list, &ret->entry);
665
666     return HTMLTXTRANGE(ret);
667 }
668
669 void detach_ranges(HTMLDocument *This)
670 {
671     HTMLTxtRange *iter;
672
673     LIST_FOR_EACH_ENTRY(iter, &This->range_list, HTMLTxtRange, entry) {
674         iter->doc = NULL;
675     }
676 }