diff --git a/dlls/mshtml/txtrange.c b/dlls/mshtml/txtrange.c index 2821e020922..99248e7d760 100644 --- a/dlls/mshtml/txtrange.c +++ b/dlls/mshtml/txtrange.c @@ -36,6 +36,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(mshtml); +static const WCHAR brW[] = {'b','r',0}; + typedef struct { const IHTMLTxtRangeVtbl *lpHTMLTxtRangeVtbl; @@ -49,6 +51,20 @@ typedef struct { #define HTMLTXTRANGE(x) ((IHTMLTxtRange*) &(x)->lpHTMLTxtRangeVtbl) +typedef struct { + WCHAR *buf; + DWORD len; + DWORD size; +} wstrbuf_t; + +typedef struct { + PRUint16 type; + nsIDOMNode *node; + PRUint32 off; + nsAString str; + const PRUnichar *p; +} dompos_t; + static HTMLTxtRange *get_range_object(HTMLDocument *doc, IHTMLTxtRange *iface) { HTMLTxtRange *iter; @@ -87,6 +103,308 @@ static PRUint16 get_node_type(nsIDOMNode *node) return type; } +static BOOL is_br_node(nsIDOMNode *node) +{ + nsIDOMElement *elem; + nsAString tag_str; + const PRUnichar *tag; + BOOL ret = FALSE; + nsresult nsres; + + nsres = nsIDOMNode_QueryInterface(node, &IID_nsIDOMElement, (void**)&elem); + if(NS_FAILED(nsres)) + return FALSE; + + nsAString_Init(&tag_str, NULL); + nsIDOMElement_GetTagName(elem, &tag_str); + nsIDOMElement_Release(elem); + nsAString_GetData(&tag_str, &tag, 0); + + if(!strcmpiW(tag, brW)) + ret = TRUE; + + nsAString_Finish(&tag_str); + + return ret; +} + +static void inline wstrbuf_init(wstrbuf_t *buf) +{ + buf->len = 0; + buf->size = 16; + buf->buf = mshtml_alloc(buf->size * sizeof(WCHAR)); + *buf->buf = 0; +} + +static void inline wstrbuf_finish(wstrbuf_t *buf) +{ + mshtml_free(buf->buf); +} + +static void wstrbuf_append_len(wstrbuf_t *buf, LPCWSTR str, int len) +{ + if(buf->len+len >= buf->size) { + buf->size = 2*buf->len+len; + buf->buf = mshtml_realloc(buf->buf, buf->size * sizeof(WCHAR)); + } + + memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR)); + buf->len += len; + buf->buf[buf->len] = 0; +} + +static void inline wstrbuf_append(wstrbuf_t *buf, LPCWSTR str) +{ + wstrbuf_append_len(buf, str, strlenW(str)); +} + +static void wstrbuf_append_node(wstrbuf_t *buf, nsIDOMNode *node) +{ + + switch(get_node_type(node)) { + case TEXT_NODE: { + nsIDOMText *nstext; + nsAString data_str; + const PRUnichar *data; + + nsIDOMNode_QueryInterface(node, &IID_nsIDOMText, (void**)&nstext); + + nsAString_Init(&data_str, NULL); + nsIDOMText_GetData(nstext, &data_str); + nsAString_GetData(&data_str, &data, NULL); + wstrbuf_append(buf, data); + nsAString_Finish(&data_str); + + nsIDOMText_Release(nstext); + + break; + } + case ELEMENT_NODE: + if(is_br_node(node)) { + static const WCHAR endlW[] = {'\r','\n'}; + wstrbuf_append_len(buf, endlW, 2); + } + } +} + +static BOOL fill_nodestr(dompos_t *pos) +{ + nsIDOMText *text; + nsresult nsres; + + if(pos->type != TEXT_NODE) + return FALSE; + + nsres = nsIDOMNode_QueryInterface(pos->node, &IID_nsIDOMText, (void**)&text); + if(NS_FAILED(nsres)) + return FALSE; + + nsAString_Init(&pos->str, NULL); + nsIDOMText_GetData(text, &pos->str); + nsIDOMText_Release(text); + nsAString_GetData(&pos->str, &pos->p, NULL); + + return TRUE; +} + +static nsIDOMNode *next_node(nsIDOMNode *iter) +{ + nsIDOMNode *ret, *tmp; + nsresult nsres; + + if(!iter) + return NULL; + + nsres = nsIDOMNode_GetFirstChild(iter, &ret); + if(NS_SUCCEEDED(nsres) && ret) + return ret; + + nsIDOMNode_AddRef(iter); + + do { + nsres = nsIDOMNode_GetNextSibling(iter, &ret); + if(NS_SUCCEEDED(nsres) && ret) { + nsIDOMNode_Release(iter); + return ret; + } + + nsres = nsIDOMNode_GetParentNode(iter, &tmp); + nsIDOMNode_Release(iter); + iter = tmp; + }while(NS_SUCCEEDED(nsres) && iter); + + return NULL; +} + +static nsIDOMNode *prev_node(HTMLTxtRange *This, nsIDOMNode *iter) +{ + nsIDOMNode *ret, *tmp; + nsresult nsres; + + if(!iter) { + nsIDOMHTMLDocument *nshtmldoc; + nsIDOMHTMLElement *nselem; + nsIDOMDocument *nsdoc; + + nsIWebNavigation_GetDocument(This->doc->nscontainer->navigation, &nsdoc); + nsIDOMDocument_QueryInterface(nsdoc, &IID_nsIDOMHTMLDocument, (void**)&nshtmldoc); + nsIDOMDocument_Release(nsdoc); + nsIDOMHTMLDocument_GetBody(nshtmldoc, &nselem); + nsIDOMHTMLDocument_Release(nshtmldoc); + + nsIDOMElement_GetLastChild(nselem, &tmp); + if(!tmp) + return (nsIDOMNode*)nselem; + + while(tmp) { + ret = tmp; + nsIDOMNode_GetLastChild(ret, &tmp); + } + + nsIDOMElement_Release(nselem); + + return ret; + } + + nsres = nsIDOMNode_GetLastChild(iter, &ret); + if(NS_SUCCEEDED(nsres) && ret) + return ret; + + nsIDOMNode_AddRef(iter); + + do { + nsres = nsIDOMNode_GetPreviousSibling(iter, &ret); + if(NS_SUCCEEDED(nsres) && ret) { + nsIDOMNode_Release(iter); + return ret; + } + + nsres = nsIDOMNode_GetParentNode(iter, &tmp); + nsIDOMNode_Release(iter); + iter = tmp; + }while(NS_SUCCEEDED(nsres) && iter); + + return NULL; +} + +static nsIDOMNode *get_child_node(nsIDOMNode *node, PRUint32 off) +{ + nsIDOMNodeList *node_list; + nsIDOMNode *ret = NULL; + + nsIDOMNode_GetChildNodes(node, &node_list); + nsIDOMNodeList_Item(node_list, off, &ret); + nsIDOMNodeList_Release(node_list); + + return ret; +} + +static void get_cur_pos(HTMLTxtRange *This, BOOL start, dompos_t *pos) +{ + nsIDOMNode *node; + PRInt32 off; + + pos->p = NULL; + + if(!start) { + PRBool collapsed; + nsIDOMRange_GetCollapsed(This->nsrange, &collapsed); + start = collapsed; + } + + if(start) { + nsIDOMRange_GetStartContainer(This->nsrange, &node); + nsIDOMRange_GetStartOffset(This->nsrange, &off); + }else { + nsIDOMRange_GetEndContainer(This->nsrange, &node); + nsIDOMRange_GetEndOffset(This->nsrange, &off); + } + + pos->type = get_node_type(node); + if(pos->type == ELEMENT_NODE) { + if(start) { + pos->node = get_child_node(node, off); + pos->off = 0; + }else { + pos->node = off ? get_child_node(node, off-1) : prev_node(This, node); + pos->off = -1; + } + + pos->type = get_node_type(pos->node); + nsIDOMNode_Release(node); + }else if(start) { + pos->node = node; + pos->off = off; + }else if(off) { + pos->node = node; + pos->off = off-1; + }else { + pos->node = prev_node(This, node); + pos->off = -1; + nsIDOMNode_Release(node); + } + + if(pos->type == TEXT_NODE) + fill_nodestr(pos); +} + +static void inline dompos_release(dompos_t *pos) +{ + if(pos->node) + nsIDOMNode_Release(pos->node); + + if(pos->p) + nsAString_Finish(&pos->str); +} + +static void range_to_string(HTMLTxtRange *This, wstrbuf_t *buf) +{ + nsIDOMNode *iter, *tmp; + dompos_t start_pos, end_pos; + PRBool collapsed; + + nsIDOMRange_GetCollapsed(This->nsrange, &collapsed); + if(collapsed) { + wstrbuf_finish(buf); + buf->buf = NULL; + buf->size = 0; + return; + } + + get_cur_pos(This, FALSE, &end_pos); + get_cur_pos(This, TRUE, &start_pos); + + if(start_pos.type == TEXT_NODE) { + if(start_pos.node == end_pos.node) { + wstrbuf_append_len(buf, start_pos.p+start_pos.off, end_pos.off-start_pos.off+1); + iter = start_pos.node; + nsIDOMNode_AddRef(iter); + }else { + wstrbuf_append(buf, start_pos.p+start_pos.off); + iter = next_node(start_pos.node); + } + }else { + iter = start_pos.node; + nsIDOMNode_AddRef(iter); + } + + while(iter != end_pos.node) { + wstrbuf_append_node(buf, iter); + tmp = next_node(iter); + nsIDOMNode_Release(iter); + iter = tmp; + } + + nsIDOMNode_AddRef(end_pos.node); + + if(start_pos.node != end_pos.node && !is_br_node(end_pos.node)) + wstrbuf_append_len(buf, end_pos.p, end_pos.off+1); + + nsIDOMNode_Release(iter); + dompos_release(&start_pos); + dompos_release(&end_pos); +} + #define HTMLTXTRANGE_THIS(iface) DEFINE_THIS(HTMLTxtRange, HTMLTxtRange, iface) static HRESULT WINAPI HTMLTxtRange_QueryInterface(IHTMLTxtRange *iface, REFIID riid, void **ppv) @@ -255,35 +573,19 @@ static HRESULT WINAPI HTMLTxtRange_put_text(IHTMLTxtRange *iface, BSTR v) static HRESULT WINAPI HTMLTxtRange_get_text(IHTMLTxtRange *iface, BSTR *p) { HTMLTxtRange *This = HTMLTXTRANGE_THIS(iface); + wstrbuf_t buf; TRACE("(%p)->(%p)\n", This, p); - *p = NULL; - - if(This->nsrange) { - nsAString text_str; - nsresult nsres; - - nsAString_Init(&text_str, NULL); - - nsres = nsIDOMRange_ToString(This->nsrange, &text_str); - if(NS_SUCCEEDED(nsres)) { - const PRUnichar *nstext; - - nsAString_GetData(&text_str, &nstext, NULL); - *p = SysAllocString(nstext); - }else { - ERR("ToString failed: %08x\n", nsres); - } - - nsAString_Finish(&text_str); - } - - if(!*p) { - static const WCHAR empty[] = {0}; - *p = SysAllocString(empty); - } + wstrbuf_init(&buf); + range_to_string(This, &buf); + if(buf.buf) + *p = SysAllocString(buf.buf); + else + *p = NULL; + wstrbuf_finish(&buf); + TRACE("ret %s\n", debugstr_w(*p)); return S_OK; }