This is implemented by creating a dummy HTML element and parsing the inline tag there, under a dummy tag name, so that it's filled with the parsed attributes. After that, its attributes are copied into a proper element with the proper tag name.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlelem.c | 154 +++++++++++++++++++++++++++++- dlls/mshtml/tests/documentmode.js | 69 +++++++++++++ 2 files changed, 222 insertions(+), 1 deletion(-)
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index de69e2f..d5463f5 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -355,6 +355,154 @@ static inline HTMLElement *impl_from_IHTMLElement(IHTMLElement *iface) return CONTAINING_RECORD(iface, HTMLElement, IHTMLElement_iface); }
+static HRESULT create_nselem_with_elem_attrs(HTMLDocumentNode *doc, const WCHAR *tag, nsIDOMElement *elem_with_attr, nsIDOMElement **ret) +{ + nsIDOMMozNamedAttrMap *attrs; + nsAString name_str, val_str; + nsresult nsres, nsres2; + nsIDOMElement *elem; + nsIDOMAttr *attr; + UINT32 i, length; + + nsAString_InitDepend(&name_str, tag); + nsres = nsIDOMHTMLDocument_CreateElement(doc->nsdoc, &name_str, &elem); + nsAString_Finish(&name_str); + if(NS_FAILED(nsres)) + return map_nsresult(nsres); + + nsres = nsIDOMElement_GetAttributes(elem_with_attr, &attrs); + if(NS_FAILED(nsres)) + goto fail; + + nsres = nsIDOMMozNamedAttrMap_GetLength(attrs, &length); + if(NS_FAILED(nsres)) { + nsIDOMMozNamedAttrMap_Release(attrs); + goto fail; + } + + nsAString_Init(&name_str, NULL); + nsAString_Init(&val_str, NULL); + for(i = 0; i < length; i++) { + nsres = nsIDOMMozNamedAttrMap_Item(attrs, i, &attr); + if(NS_FAILED(nsres)) + continue; + + nsres = nsIDOMAttr_GetNodeName(attr, &name_str); + nsres2 = nsIDOMAttr_GetNodeValue(attr, &val_str); + nsIDOMAttr_Release(attr); + if(NS_FAILED(nsres) || NS_FAILED(nsres2)) + continue; + + nsres = nsIDOMElement_SetAttribute(elem, &name_str, &val_str); + } + nsAString_Finish(&name_str); + nsAString_Finish(&val_str); + + nsIDOMMozNamedAttrMap_Release(attrs); + *ret = elem; + return S_OK; + +fail: + nsIDOMElement_Release(elem); + return E_FAIL; +} + +static HRESULT create_nselem_parse(HTMLDocumentNode *doc, const WCHAR *tag, nsIDOMElement **ret) +{ + static const WCHAR prefix[15] = L"<BODY><DUMMYTAG"; + static const WCHAR suffix[] = L"/></BODY>"; + WCHAR *p = wcschr(tag + 1, '>'); + UINT32 i, len, name_len, length; + nsIDOMHTMLCollection *nscol; + nsIDOMHTMLElement *nshtml; + HRESULT hres = E_FAIL; + nsresult nsres; + nsAString str; + + if(!p || p[1] || wcschr(tag + 1, '<')) + return E_FAIL; + if(!doc->nsdoc) { + WARN("NULL nsdoc\n"); + return E_UNEXPECTED; + } + if(p - tag >= 0x40000000) + return E_OUTOFMEMORY; + + /* Ignore the > or /> end token */ + len = p - tag - (p[-1] == '/'); + + /* Get the tag name using HTML whitespace rules */ + name_len = len - 1; + for(i = 1; i < len; i++) { + if((tag[i] >= 0x09 && tag[i] <= 0x0d) || tag[i] == ' ') { + name_len = i - 1; + break; + } + } + if(!name_len) + return E_FAIL; + len -= name_len + 1; + + /* Create a temporary html element and parse it there */ + nsAString_InitDepend(&str, L"HTML"); + nsres = nsIDOMHTMLDocument_CreateElement(doc->nsdoc, &str, (nsIDOMElement**)&nshtml); + nsAString_Finish(&str); + if(NS_FAILED(nsres)) + return map_nsresult(nsres); + + length = ARRAY_SIZE(prefix) + ARRAY_SIZE(suffix) + len; + if(!(p = heap_alloc(length * sizeof(WCHAR)))) { + hres = E_OUTOFMEMORY; + goto fail; + } + memcpy(p, prefix, sizeof(prefix)); + memcpy(p + ARRAY_SIZE(prefix), tag + 1 + name_len, len * sizeof(WCHAR)); + memcpy(p + ARRAY_SIZE(prefix) + len, suffix, sizeof(suffix)); + + nsAString_InitDepend(&str, p); + nsres = nsIDOMHTMLElement_SetInnerHTML(nshtml, &str); + nsAString_Finish(&str); + heap_free(p); + if(NS_FAILED(nsres)) { + hres = map_nsresult(nsres); + goto fail; + } + + /* Get the dummy element and copy its attributes into the proper element */ + nsAString_InitDepend(&str, L"DUMMYTAG"); + nsres = nsIDOMHTMLElement_GetElementsByTagName(nshtml, &str, &nscol); + nsAString_Finish(&str); + if(NS_FAILED(nsres)) + goto fail; + + length = 0; + nsres = nsIDOMHTMLCollection_GetLength(nscol, &length); + if(NS_SUCCEEDED(nsres) && length == 1) { + nsIDOMElement *nselem; + nsIDOMNode *nsnode; + + nsIDOMHTMLCollection_Item(nscol, 0, &nsnode); + nsres = nsIDOMNode_QueryInterface(nsnode, &IID_nsIDOMElement, (void**)&nselem); + nsIDOMNode_Release(nsnode); + if(NS_SUCCEEDED(nsres)) { + if(!(p = heap_alloc((name_len + 1) * sizeof(WCHAR)))) + hres = E_OUTOFMEMORY; + else { + memcpy(p, tag + 1, name_len * sizeof(WCHAR)); + p[name_len] = '\0'; + hres = create_nselem_with_elem_attrs(doc, p, nselem, ret); + heap_free(p); + } + nsIDOMElement_Release(nselem); + } + } + nsIDOMHTMLCollection_Release(nscol); + +fail: + nsIDOMHTMLElement_Release(nshtml); + return hres; +} + HRESULT create_nselem(HTMLDocumentNode *doc, const WCHAR *tag, nsIDOMElement **ret) { nsAString tag_str; @@ -385,7 +533,11 @@ HRESULT create_element(HTMLDocumentNode *doc, const WCHAR *tag, HTMLElement **re if(!doc->nsdoc) doc = doc->node.doc;
- hres = create_nselem(doc, tag, &nselem); + /* IE8 and below allow creating elements with attributes, such as <div class="a"> */ + if(tag[0] == '<' && dispex_compat_mode(&doc->node.event_target.dispex) <= COMPAT_MODE_IE8) + hres = create_nselem_parse(doc, tag, &nselem); + else + hres = create_nselem(doc, tag, &nselem); if(FAILED(hres)) return hres;
diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 02a5f5f..7e85af6 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -488,6 +488,75 @@ sync_test("style_props", function() { } });
+sync_test("createElement_inline_props", function() { + var v = document.documentMode, e, s; + + if(v < 9) { + s = document.createElement("<div>").tagName; + ok(s === "DIV", "<div>.tagName returned " + s); + s = document.createElement("<div >").tagName; + ok(s === "DIV", "<div >.tagName returned " + s); + s = document.createElement("<div/>").tagName; + ok(s === "DIV", "<div/>.tagName returned " + s); + e = 0; + try { + document.createElement("<div"); + }catch(ex) { + e = ex.number; + } + ok(e === 0x4005 - 0x80000000, "<div e = " + e); + e = 0; + try { + document.createElement("<div test=1"); + }catch(ex) { + e = ex.number; + } + ok(e === 0x4005 - 0x80000000, "<div test=1 e = " + e); + + e = document.createElement("<div test='a"' abcd=""b"">"); + ok(e.tagName === "DIV", "<div test="a" abcd="b">.tagName returned " + e.tagName); + todo_wine_if(v == 8). + ok(e.test === "a"", "<div test='a"' abcd=""b"">.test returned " + e.test); + todo_wine_if(v == 8). + ok(e.abcd === ""b"", "<div test='a"' abcd=""b"">.abcd returned " + e.abcd); + + e = document.createElement("<html test='<'>"); + ok(e.tagName === "HTML", "<html test='<'>.tagName returned " + e.tagName); + todo_wine_if(v == 8). + ok(e.test === "<", "<html test='<'>.test returned " + e.test); + + e = document.createElement("<body test='>'>"); + ok(e.tagName === "BODY", "<body test='>'>.tagName returned " + e.tagName); + todo_wine_if(v == 8). + ok(e.test === ">", "<body test='>'>.test returned " + e.test); + }else { + s = ""; + e = 0; + try { + document.createElement("<div>"); + }catch(ex) { + s = ex.toString(); + e = ex.number; + } + todo_wine. + ok(e === undefined, "<div> e = " + e); + todo_wine. + ok(s === "InvalidCharacterError", "<div> s = " + s); + s = ""; + e = 0; + try { + document.createElement("<div test="a">"); + }catch(ex) { + s = ex.toString(); + e = ex.number; + } + todo_wine. + ok(e === undefined, "<div test="a"> e = " + e); + todo_wine. + ok(s === "InvalidCharacterError", "<div test="a"> s = " + s); + } +}); + sync_test("JS objs", function() { var g = window;