From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/dispex.c | 10 ++++ dlls/mshtml/htmldoc.c | 1 + dlls/mshtml/htmlelem.c | 1 + dlls/mshtml/htmlstorage.c | 88 +++++++++++++++++++++++++++++++ dlls/mshtml/htmlwindow.c | 1 + dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/tests/documentmode.js | 28 +++++++++- 7 files changed, 129 insertions(+), 1 deletion(-)
diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index 357549698fc..923896dfb54 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -1879,6 +1879,9 @@ static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, if(!ensure_real_info(This)) return E_OUTOFMEMORY;
+ if(is_custom_dispid(id)) + goto custom; + if(is_dynamic_dispid(id)) { DWORD idx = id - DISPID_DYNPROP_0;
@@ -1905,6 +1908,13 @@ static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, func++; }
+custom: + if(This->info->desc->vtbl && This->info->desc->vtbl->next_dispid) { + hres = This->info->desc->vtbl->next_dispid(This, id, pid); + if(hres != S_FALSE) + return hres; + } + if(get_dynamic_data(This) && This->dynamic_data->prop_cnt) return next_dynamic_id(This, 0, pid);
diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c index 2537a316500..fe743e666c1 100644 --- a/dlls/mshtml/htmldoc.c +++ b/dlls/mshtml/htmldoc.c @@ -5935,6 +5935,7 @@ static const event_target_vtbl_t HTMLDocumentNode_event_target_vtbl = { HTMLDocumentNode_get_name, HTMLDocumentNode_invoke, NULL, + NULL, HTMLDocumentNode_get_compat_mode, NULL }, diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index d3704ccbb5c..856f55da6c7 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -7212,6 +7212,7 @@ static event_target_vtbl_t HTMLElement_event_target_vtbl = { HTMLElement_invoke, NULL, NULL, + NULL, HTMLElement_populate_props }, HTMLElement_get_gecko_target, diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index 12ad4443e93..5d56037ff04 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -1010,12 +1010,100 @@ static HRESULT HTMLStorage_delete(DispatchEx *dispex, DISPID id) return HTMLStorage_removeItem(&This->IHTMLStorage_iface, This->props[idx]); }
+static HRESULT HTMLStorage_next_dispid(DispatchEx *dispex, DISPID id, DISPID *pid) +{ + DWORD idx = (id == DISPID_STARTENUM) ? 0 : id - MSHTML_DISPID_CUSTOM_MIN + 1; + HTMLStorage *This = impl_from_DispatchEx(dispex); + HRESULT hres; + DISPID tmp; + + if(idx > MSHTML_CUSTOM_DISPID_CNT) + return S_FALSE; + + while(idx < This->num_props) { + hres = check_item(This, This->props[idx]); + if(hres == S_OK) { + *pid = idx + MSHTML_DISPID_CUSTOM_MIN; + return S_OK; + } + if(FAILED(hres)) + return hres; + idx++; + } + + /* Populate possibly missing DISPIDs */ + if(!This->filename) { + struct session_entry *session_entry; + + LIST_FOR_EACH_ENTRY(session_entry, &This->session_storage->data_list, struct session_entry, list_entry) { + hres = get_prop(This, session_entry->key, &tmp); + if(FAILED(hres)) + return hres; + } + }else { + IXMLDOMNodeList *node_list; + IXMLDOMElement *elem; + IXMLDOMNode *node; + LONG index = 0; + HRESULT hres; + VARIANT key; + + hres = get_node_list(This->filename, &node_list); + if(FAILED(hres)) + return hres; + + for(;;) { + hres = IXMLDOMNodeList_get_item(node_list, index++, &node); + if(hres != S_OK) { + IXMLDOMNodeList_Release(node_list); + if(FAILED(hres)) + return hres; + break; + } + + hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem); + IXMLDOMNode_Release(node); + if(hres != S_OK) + continue; + + hres = IXMLDOMElement_getAttribute(elem, (BSTR)L"name", &key); + IXMLDOMElement_Release(elem); + if(hres != S_OK) { + if(SUCCEEDED(hres)) + continue; + IXMLDOMNodeList_Release(node_list); + return hres; + } + + if(V_VT(&key) != VT_BSTR) { + FIXME("non-string key %s\n", debugstr_variant(&key)); + VariantClear(&key); + continue; + } + + hres = get_prop(This, V_BSTR(&key), &tmp); + SysFreeString(V_BSTR(&key)); + if(FAILED(hres)) { + IXMLDOMNodeList_Release(node_list); + return hres; + } + } + } + + if(idx >= This->num_props) + return S_FALSE; + + *pid = idx + MSHTML_DISPID_CUSTOM_MIN; + return S_OK; +} + static const dispex_static_data_vtbl_t HTMLStorage_dispex_vtbl = { NULL, HTMLStorage_get_dispid, HTMLStorage_get_name, HTMLStorage_invoke, HTMLStorage_delete, + HTMLStorage_next_dispid, NULL };
diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index 6be61ba3f4f..00eef4d846a 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -3967,6 +3967,7 @@ static const event_target_vtbl_t HTMLWindow_event_target_vtbl = { HTMLWindow_get_name, HTMLWindow_invoke, NULL, + NULL, HTMLWindow_get_compat_mode, NULL }, diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 101dd63d9aa..85f34bb4a9f 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -336,6 +336,7 @@ typedef struct { HRESULT (*get_name)(DispatchEx*,DISPID,BSTR*); HRESULT (*invoke)(DispatchEx*,DISPID,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,IServiceProvider*); HRESULT (*delete)(DispatchEx*,DISPID); + HRESULT (*next_dispid)(DispatchEx*,DISPID,DISPID*); compat_mode_t (*get_compat_mode)(DispatchEx*); HRESULT (*populate_props)(DispatchEx*); } dispex_static_data_vtbl_t; diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 24adb08005c..8cc1da534e6 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1202,7 +1202,10 @@ sync_test("map_obj", function() { });
sync_test("storage", function() { - var v = document.documentMode, r; + var v = document.documentMode, i, r, list; + + sessionStorage["add-at-end"] = 0; + sessionStorage.removeItem("add-at-end");
sessionStorage.setItem("foobar", "1234"); ok("foobar" in sessionStorage, "foobar not in sessionStorage"); @@ -1211,6 +1214,29 @@ sync_test("storage", function() { sessionStorage.barfoo = 4321; r = sessionStorage.getItem("barfoo"); ok(r === "4321", "sessionStorage.barfoo = " + r); + sessionStorage.setItem("abcd", "blah"); + sessionStorage.dcba = "test"; + + // Order isn't consistent, but changes are reflected during the enumeration. + // Elements that were already traversed in DISPID (even if removed before + // the enumeration) are not enumerated, even if re-added during the enum. + i = 0; list = [ "foobar", "barfoo", "abcd", "dcba" ]; + for(r in sessionStorage) { + for(var j = 0; j < list.length; j++) + if(r === list[j]) + break; + ok(j < list.length, "got '" + r + "' enumerating"); + list.splice(j, 1); + if(i === 1) { + sessionStorage.removeItem(list[0]); + sessionStorage.setItem("new", "new"); + list.splice(0, 1, "new"); + } + if(!list.length) + sessionStorage.setItem("add-at-end", "0"); + i++; + } + ok(i === 4, "enum did " + i + " iterations");
try { delete sessionStorage.foobar;