diff --git a/dlls/xmllite/reader.c b/dlls/xmllite/reader.c
index 9193d2f5e29..ec2a071bb78 100644
--- a/dlls/xmllite/reader.c
+++ b/dlls/xmllite/reader.c
@@ -53,7 +53,8 @@ typedef enum
XmlReadInState_DTD_Misc,
XmlReadInState_Element,
XmlReadInState_Content,
- XmlReadInState_MiscEnd
+ XmlReadInState_MiscEnd, /* optional Misc at the end of a document */
+ XmlReadInState_Eof
} XmlReaderInternalState;
/* This state denotes where parsing was interrupted by input problem.
@@ -65,7 +66,8 @@ typedef enum
XmlReadResumeState_PIBody,
XmlReadResumeState_CDATA,
XmlReadResumeState_Comment,
- XmlReadResumeState_STag
+ XmlReadResumeState_STag,
+ XmlReadResumeState_CharData
} XmlReaderResumeState;
/* saved pointer index to resume from particular input position */
@@ -73,7 +75,7 @@ typedef enum
{
XmlReadResume_Name, /* PITarget, name for NCName, prefix for QName */
XmlReadResume_Local, /* local for QName */
- XmlReadResume_Body, /* PI body, comment text, CDATA text */
+ XmlReadResume_Body, /* PI body, comment text, CDATA text, CharData text */
XmlReadResume_Last
} XmlReaderResume;
@@ -1788,6 +1790,10 @@ static HRESULT reader_parse_endtag(xmlreader *reader)
reader_pop_element(reader);
+ /* It was a root element, the rest is expected as Misc */
+ if (list_empty(&reader->elements))
+ reader->instate = XmlReadInState_MiscEnd;
+
reader->nodetype = XmlNodeType_EndElement;
reader_set_strvalue(reader, StringValue_LocalName, &local);
reader_set_strvalue(reader, StringValue_QualifiedName, &qname);
@@ -1867,8 +1873,48 @@ static HRESULT reader_parse_reference(xmlreader *reader)
/* [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*) */
static HRESULT reader_parse_chardata(xmlreader *reader)
{
- FIXME("CharData not supported\n");
- return E_NOTIMPL;
+ WCHAR *start, *ptr;
+
+ if (reader->resume[XmlReadResume_Body])
+ {
+ start = reader->resume[XmlReadResume_Body];
+ ptr = reader_get_cur(reader);
+ }
+ else
+ {
+ reader_shrink(reader);
+ ptr = start = reader_get_cur(reader);
+ /* There's no text */
+ if (!*ptr || *ptr == '<') return S_OK;
+ reader->nodetype = XmlNodeType_Text;
+ reader->resume[XmlReadResume_Body] = start;
+ reader->resumestate = XmlReadResumeState_CharData;
+ reader_set_strvalue(reader, StringValue_LocalName, &strval_empty);
+ reader_set_strvalue(reader, StringValue_QualifiedName, &strval_empty);
+ reader_set_strvalue(reader, StringValue_Value, NULL);
+ }
+
+ while (*ptr)
+ {
+ /* CDATA closing sequence ']]>' is not allowed */
+ if (ptr[0] == ']' && ptr[1] == ']' && ptr[2] == '>')
+ return WC_E_CDSECTEND;
+
+ /* Found next markup part */
+ if (ptr[0] == '<')
+ {
+ strval value;
+
+ reader_init_strvalue(start, ptr-start, &value);
+ reader_set_strvalue(reader, StringValue_Value, &value);
+ return S_OK;
+ }
+
+ reader_skipn(reader, 1);
+ ptr++;
+ }
+
+ return S_OK;
}
/* [43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)* */
@@ -1889,6 +1935,8 @@ static HRESULT reader_parse_content(xmlreader *reader)
case XmlReadResumeState_PIBody:
case XmlReadResumeState_PITarget:
return reader_parse_pi(reader);
+ case XmlReadResumeState_CharData:
+ return reader_parse_chardata(reader);
default:
ERR("unknown resume state %d\n", reader->resumestate);
}
@@ -1986,6 +2034,15 @@ static HRESULT reader_parse_nextnode(xmlreader *reader)
return reader_parse_element(reader);
case XmlReadInState_Content:
return reader_parse_content(reader);
+ case XmlReadInState_MiscEnd:
+ hr = reader_parse_misc(reader);
+ if (FAILED(hr)) return hr;
+
+ if (hr == S_FALSE)
+ reader->instate = XmlReadInState_Eof;
+ return hr;
+ case XmlReadInState_Eof:
+ return S_FALSE;
default:
FIXME("internal state %d not handled\n", reader->instate);
return E_NOTIMPL;
diff --git a/dlls/xmllite/tests/reader.c b/dlls/xmllite/tests/reader.c
index 46c6eddb396..c66671209c9 100644
--- a/dlls/xmllite/tests/reader.c
+++ b/dlls/xmllite/tests/reader.c
@@ -1418,6 +1418,106 @@ static void test_read_cdata(void)
IXmlReader_Release(reader);
}
+static struct test_entry text_tests[] = {
+ { "simple text", "", "simple text", S_OK },
+ { "text ]]> text", "", "", WC_E_CDSECTEND },
+ { NULL }
+};
+
+static void test_read_text(void)
+{
+ struct test_entry *test = text_tests;
+ IXmlReader *reader;
+ HRESULT hr;
+
+ hr = pCreateXmlReader(&IID_IXmlReader, (void**)&reader, NULL);
+ ok(hr == S_OK, "S_OK, got %08x\n", hr);
+
+ while (test->xml)
+ {
+ XmlNodeType type;
+ IStream *stream;
+
+ stream = create_stream_on_data(test->xml, strlen(test->xml)+1);
+ hr = IXmlReader_SetInput(reader, (IUnknown*)stream);
+ ok(hr == S_OK, "got %08x\n", hr);
+
+ type = XmlNodeType_None;
+ hr = IXmlReader_Read(reader, &type);
+
+ /* read one more to get to CDATA */
+ if (type == XmlNodeType_Element)
+ {
+ type = XmlNodeType_None;
+ hr = IXmlReader_Read(reader, &type);
+ }
+
+ if (test->hr_broken)
+ ok(hr == test->hr || broken(hr == test->hr_broken), "got %08x for %s\n", hr, test->xml);
+ else
+ ok(hr == test->hr, "got %08x for %s\n", hr, test->xml);
+ if (hr == S_OK)
+ {
+ const WCHAR *str;
+ WCHAR *str_exp;
+ UINT len;
+
+ ok(type == XmlNodeType_Text, "got %d for %s\n", type, test->xml);
+
+ str_exp = a2w(test->name);
+
+ len = 1;
+ str = NULL;
+ hr = IXmlReader_GetLocalName(reader, &str, &len);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ ok(len == strlen(test->name), "got %u\n", len);
+ ok(!lstrcmpW(str, str_exp), "got %s\n", wine_dbgstr_w(str));
+
+ str = NULL;
+ hr = IXmlReader_GetLocalName(reader, &str, NULL);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ ok(!lstrcmpW(str, str_exp), "got %s\n", wine_dbgstr_w(str));
+
+ free_str(str_exp);
+
+ len = 1;
+ str = NULL;
+ hr = IXmlReader_GetQualifiedName(reader, &str, &len);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+ ok(len == strlen(test->name), "got %u\n", len);
+ str_exp = a2w(test->name);
+ ok(!lstrcmpW(str, str_exp), "got %s\n", wine_dbgstr_w(str));
+ free_str(str_exp);
+
+ /* value */
+ len = 1;
+ str = NULL;
+ hr = IXmlReader_GetValue(reader, &str, &len);
+ ok(hr == S_OK, "got 0x%08x\n", hr);
+
+ str_exp = a2w(test->value);
+ if (test->todo)
+ {
+ todo_wine {
+ ok(len == strlen(test->value), "got %u\n", len);
+ ok(!lstrcmpW(str, str_exp), "got %s\n", wine_dbgstr_w(str));
+ }
+ }
+ else
+ {
+ ok(len == strlen(test->value), "got %u\n", len);
+ ok(!lstrcmpW(str, str_exp), "got %s\n", wine_dbgstr_w(str));
+ }
+ free_str(str_exp);
+ }
+
+ IStream_Release(stream);
+ test++;
+ }
+
+ IXmlReader_Release(reader);
+}
+
START_TEST(reader)
{
HRESULT r;
@@ -1439,6 +1539,7 @@ START_TEST(reader)
test_read_pi();
test_read_dtd();
test_read_element();
+ test_read_text();
test_read_full();
test_read_pending();
test_readvaluechunk();