[PATCH 6/7] mshtml: Implement inline attributes parsing for createElement.

Gabriel Ivăncescu gabrielopcode at gmail.com
Mon Oct 25 08:30:30 CDT 2021


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 at 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;
 
-- 
2.31.1




More information about the wine-devel mailing list