diff --git a/dlls/mshtml/txtrange.c b/dlls/mshtml/txtrange.c
index 0dd3aaeb82c..c6fba3ee564 100644
--- a/dlls/mshtml/txtrange.c
+++ b/dlls/mshtml/txtrange.c
@@ -55,6 +55,12 @@ typedef struct {
DWORD size;
} wstrbuf_t;
+typedef struct {
+ UINT16 type;
+ nsIDOMNode *node;
+ UINT32 off;
+} rangepoint_t;
+
typedef struct {
UINT16 type;
nsIDOMNode *node;
@@ -128,6 +134,136 @@ static UINT16 get_node_type(nsIDOMNode *node)
return type;
}
+static void get_text_node_data(nsIDOMNode *node, nsAString *nsstr, const PRUnichar **str)
+{
+ nsIDOMText *nstext;
+ nsresult nsres;
+
+ nsres = nsIDOMNode_QueryInterface(node, &IID_nsIDOMText, (void**)&nstext);
+ assert(nsres == NS_OK);
+
+ nsAString_Init(nsstr, NULL);
+ nsres = nsIDOMText_GetData(nstext, nsstr);
+ nsIDOMText_Release(nstext);
+ if(NS_FAILED(nsres))
+ ERR("GetData failed: %08x\n", nsres);
+
+ nsAString_GetData(nsstr, str);
+}
+
+static nsIDOMNode *get_child_node(nsIDOMNode *node, UINT32 off)
+{
+ nsIDOMNodeList *node_list;
+ nsIDOMNode *ret = NULL;
+
+ nsIDOMNode_GetChildNodes(node, &node_list);
+ nsIDOMNodeList_Item(node_list, off, &ret);
+ nsIDOMNodeList_Release(node_list);
+
+ return ret;
+}
+
+/* This is very inefficient, but there is no faster way to compute index in
+ * child node list using public API. Gecko has internal nsINode::IndexOf
+ * function that we could consider exporting and use instead. */
+static int get_child_index(nsIDOMNode *parent, nsIDOMNode *child)
+{
+ nsIDOMNodeList *node_list;
+ nsIDOMNode *node;
+ int ret = 0;
+ nsresult nsres;
+
+ nsres = nsIDOMNode_GetChildNodes(parent, &node_list);
+ assert(nsres == NS_OK);
+
+ while(1) {
+ nsres = nsIDOMNodeList_Item(node_list, ret, &node);
+ assert(nsres == NS_OK && node);
+ if(node == child) {
+ nsIDOMNode_Release(node);
+ break;
+ }
+ nsIDOMNode_Release(node);
+ ret++;
+ }
+
+ nsIDOMNodeList_Release(node_list);
+ return ret;
+}
+
+static void init_rangepoint(rangepoint_t *rangepoint, nsIDOMNode *node, UINT32 off)
+{
+ nsIDOMNode_AddRef(node);
+
+ rangepoint->type = get_node_type(node);
+ rangepoint->node = node;
+ rangepoint->off = off;
+}
+
+static inline void free_rangepoint(rangepoint_t *rangepoint)
+{
+ nsIDOMNode_Release(rangepoint->node);
+}
+
+static inline BOOL rangepoint_cmp(const rangepoint_t *point1, const rangepoint_t *point2)
+{
+ return point1->node == point2->node && point1->off == point2->off;
+}
+
+static BOOL rangepoint_next_node(rangepoint_t *iter)
+{
+ nsIDOMNode *node;
+ UINT32 off;
+ nsresult nsres;
+
+ /* Try to move to the child node. */
+ node = get_child_node(iter->node, iter->off);
+ if(node) {
+ free_rangepoint(iter);
+ init_rangepoint(iter, node, 0);
+ nsIDOMNode_Release(node);
+ return TRUE;
+ }
+
+ /* There are no more children in the node. Move to parent. */
+ nsres = nsIDOMNode_GetParentNode(iter->node, &node);
+ assert(nsres == NS_OK);
+ if(!node)
+ return FALSE;
+
+ off = get_child_index(node, iter->node)+1;
+ free_rangepoint(iter);
+ init_rangepoint(iter, node, off);
+ nsIDOMNode_Release(node);
+ return TRUE;
+}
+
+static void get_start_point(HTMLTxtRange *This, rangepoint_t *ret)
+{
+ nsIDOMNode *node;
+ LONG off;
+
+ nsIDOMRange_GetStartContainer(This->nsrange, &node);
+ nsIDOMRange_GetStartOffset(This->nsrange, &off);
+
+ init_rangepoint(ret, node, off);
+
+ nsIDOMNode_Release(node);
+}
+
+static void get_end_point(HTMLTxtRange *This, rangepoint_t *ret)
+{
+ nsIDOMNode *node;
+ LONG off;
+
+ nsIDOMRange_GetEndContainer(This->nsrange, &node);
+ nsIDOMRange_GetEndOffset(This->nsrange, &off);
+
+ init_rangepoint(ret, node, off);
+
+ nsIDOMNode_Release(node);
+}
+
static BOOL is_elem_tag(nsIDOMNode *node, LPCWSTR istag)
{
nsIDOMElement *elem;
@@ -236,7 +372,7 @@ static void wstrbuf_append_nodetxt(wstrbuf_t *buf, LPCWSTR str, int len)
*d = 0;
}
-static void wstrbuf_append_node(wstrbuf_t *buf, nsIDOMNode *node)
+static void wstrbuf_append_node(wstrbuf_t *buf, nsIDOMNode *node, BOOL ignore_text)
{
switch(get_node_type(node)) {
@@ -245,6 +381,9 @@ static void wstrbuf_append_node(wstrbuf_t *buf, nsIDOMNode *node)
nsAString data_str;
const PRUnichar *data;
+ if(ignore_text)
+ break;
+
nsIDOMNode_QueryInterface(node, &IID_nsIDOMText, (void**)&nstext);
nsAString_Init(&data_str, NULL);
@@ -272,7 +411,7 @@ static void wstrbuf_append_node_rec(wstrbuf_t *buf, nsIDOMNode *node)
{
nsIDOMNode *iter, *tmp;
- wstrbuf_append_node(buf, node);
+ wstrbuf_append_node(buf, node, FALSE);
nsIDOMNode_GetFirstChild(node, &iter);
while(iter) {
@@ -379,18 +518,6 @@ static nsIDOMNode *prev_node(HTMLTxtRange *This, nsIDOMNode *iter)
return NULL;
}
-static nsIDOMNode *get_child_node(nsIDOMNode *node, UINT32 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;
@@ -485,8 +612,7 @@ static inline BOOL dompos_cmp(const dompos_t *pos1, const dompos_t *pos2)
static void range_to_string(HTMLTxtRange *This, wstrbuf_t *buf)
{
- nsIDOMNode *iter, *tmp;
- dompos_t start_pos, end_pos;
+ rangepoint_t end_pos, iter;
cpp_bool collapsed;
nsIDOMRange_GetCollapsed(This->nsrange, &collapsed);
@@ -497,42 +623,42 @@ static void range_to_string(HTMLTxtRange *This, wstrbuf_t *buf)
return;
}
- get_cur_pos(This, FALSE, &end_pos);
- get_cur_pos(This, TRUE, &start_pos);
+ get_end_point(This, &end_pos);
+ get_start_point(This, &iter);
- if(start_pos.type == TEXT_NODE) {
- if(start_pos.node == end_pos.node) {
- wstrbuf_append_nodetxt(buf, start_pos.p+start_pos.off, end_pos.off-start_pos.off+1);
- iter = start_pos.node;
- nsIDOMNode_AddRef(iter);
+ do {
+ if(iter.type == TEXT_NODE) {
+ const PRUnichar *str;
+ nsAString nsstr;
+
+ get_text_node_data(iter.node, &nsstr, &str);
+
+ if(iter.node == end_pos.node) {
+ wstrbuf_append_nodetxt(buf, str+iter.off, end_pos.off-iter.off);
+ nsAString_Finish(&nsstr);
+ break;
+ }
+
+ wstrbuf_append_nodetxt(buf, str+iter.off, strlenW(str+iter.off));
+ nsAString_Finish(&nsstr);
}else {
- wstrbuf_append_nodetxt(buf, start_pos.p+start_pos.off, strlenW(start_pos.p+start_pos.off));
- iter = next_node(start_pos.node);
+ nsIDOMNode *node;
+
+ node = get_child_node(iter.node, iter.off);
+ if(node) {
+ wstrbuf_append_node(buf, node, TRUE);
+ nsIDOMNode_Release(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;
- }
+ if(!rangepoint_next_node(&iter)) {
+ ERR("End of document?\n");
+ break;
+ }
+ }while(!rangepoint_cmp(&iter, &end_pos));
- nsIDOMNode_AddRef(end_pos.node);
-
- if(start_pos.node != end_pos.node) {
- if(end_pos.type == TEXT_NODE)
- wstrbuf_append_nodetxt(buf, end_pos.p, end_pos.off+1);
- else
- wstrbuf_append_node(buf, end_pos.node);
- }
-
- nsIDOMNode_Release(iter);
- dompos_release(&start_pos);
- dompos_release(&end_pos);
+ free_rangepoint(&iter);
+ free_rangepoint(&end_pos);
if(buf->len) {
WCHAR *p;