diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index f4413ba2a3a..716a306ee6b 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -39,6 +39,8 @@ typedef struct { DispatchEx dispex; IHTMLStorage IHTMLStorage_iface; LONG ref; + unsigned num_props; + BSTR *props; struct session_map_entry *session_storage; WCHAR *filename; HANDLE mutex; @@ -178,6 +180,16 @@ void destroy_session_storage(thread_data_t *thread_data) } } +static void release_props(HTMLStorage *This) +{ + BSTR *prop = This->props, *end = prop + This->num_props; + while(prop != end) { + SysFreeString(*prop); + prop++; + } + heap_free(This->props); +} + static inline HTMLStorage *impl_from_IHTMLStorage(IHTMLStorage *iface) { return CONTAINING_RECORD(iface, HTMLStorage, IHTMLStorage_iface); @@ -227,6 +239,7 @@ static ULONG WINAPI HTMLStorage_Release(IHTMLStorage *iface) release_dispex(&This->dispex); heap_free(This->filename); CloseHandle(This->mutex); + release_props(This); heap_free(This); } @@ -835,13 +848,157 @@ static const IHTMLStorageVtbl HTMLStorageVtbl = { HTMLStorage_clear }; +static inline HTMLStorage *impl_from_DispatchEx(DispatchEx *iface) +{ + return CONTAINING_RECORD(iface, HTMLStorage, dispex); +} + +static HRESULT check_item(HTMLStorage *This, const WCHAR *key) +{ + struct session_entry *session_entry; + IXMLDOMNode *root, *node; + IXMLDOMDocument *doc; + HRESULT hres; + BSTR query; + + if(!This->filename) { + hres = get_session_entry(This->session_storage, key, FALSE, &session_entry); + if(SUCCEEDED(hres)) + hres = (session_entry && session_entry->value) ? S_OK : S_FALSE; + return hres; + } + + WaitForSingleObject(This->mutex, INFINITE); + + hres = open_document(This->filename, &doc); + if(hres == S_OK) { + hres = get_root_node(doc, &root); + IXMLDOMDocument_Release(doc); + if(hres == S_OK) { + if(!(query = build_query(key))) + hres = E_OUTOFMEMORY; + else { + hres = IXMLDOMNode_selectSingleNode(root, query, &node); + SysFreeString(query); + if(hres == S_OK) + IXMLDOMNode_Release(node); + } + IXMLDOMNode_Release(root); + } + } + + ReleaseMutex(This->mutex); + + return hres; +} + +static HRESULT get_prop(HTMLStorage *This, const WCHAR *name, DISPID *dispid) +{ + UINT name_len = wcslen(name); + BSTR p, *prop, *end; + + for(prop = This->props, end = prop + This->num_props; prop != end; prop++) { + if(SysStringLen(*prop) == name_len && !memcmp(*prop, name, name_len * sizeof(WCHAR))) { + *dispid = MSHTML_DISPID_CUSTOM_MIN + (prop - This->props); + return S_OK; + } + } + + if(is_power_of_2(This->num_props)) { + BSTR *new_props = heap_realloc(This->props, max(This->num_props * 2 * sizeof(BSTR*), 1)); + if(!new_props) + return E_OUTOFMEMORY; + This->props = new_props; + } + + if(!(p = SysAllocStringLen(name, name_len))) + return E_OUTOFMEMORY; + + This->props[This->num_props] = p; + *dispid = MSHTML_DISPID_CUSTOM_MIN + This->num_props++; + return S_OK; +} + +static HRESULT HTMLStorage_get_dispid(DispatchEx *dispex, BSTR name, DWORD flags, DISPID *dispid) +{ + HTMLStorage *This = impl_from_DispatchEx(dispex); + HRESULT hres; + + if(flags & fdexNameCaseInsensitive) + FIXME("case insensitive not supported\n"); + + if(!(flags & fdexNameEnsure)) { + hres = check_item(This, name); + if(hres != S_OK) + return FAILED(hres) ? hres : DISP_E_UNKNOWNNAME; + } + + return get_prop(This, name, dispid); +} + +static HRESULT HTMLStorage_invoke(DispatchEx *dispex, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params, + VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) +{ + HTMLStorage *This = impl_from_DispatchEx(dispex); + DWORD idx = id - MSHTML_DISPID_CUSTOM_MIN; + HRESULT hres; + BSTR bstr; + + if(idx >= This->num_props) + return DISP_E_MEMBERNOTFOUND; + + switch(flags) { + case DISPATCH_PROPERTYGET: + hres = HTMLStorage_getItem(&This->IHTMLStorage_iface, This->props[idx], res); + if(FAILED(hres)) + return hres; + if(V_VT(res) == VT_NULL) + return DISP_E_MEMBERNOTFOUND; + break; + case DISPATCH_PROPERTYPUTREF: + case DISPATCH_PROPERTYPUT: + if(params->cArgs != 1 || (params->cNamedArgs && params->rgdispidNamedArgs[0] != DISPID_PROPERTYPUT)) { + FIXME("unimplemented args %u %u\n", params->cArgs, params->cNamedArgs); + return E_NOTIMPL; + } + + bstr = V_BSTR(params->rgvarg); + if(V_VT(params->rgvarg) != VT_BSTR) { + VARIANT var; + hres = change_type(&var, params->rgvarg, VT_BSTR, caller); + if(FAILED(hres)) + return hres; + bstr = V_BSTR(&var); + } + + hres = HTMLStorage_setItem(&This->IHTMLStorage_iface, This->props[idx], bstr); + + if(V_VT(params->rgvarg) != VT_BSTR) + SysFreeString(bstr); + return hres; + + default: + FIXME("unimplemented flags %x\n", flags); + return E_NOTIMPL; + } + + return S_OK; +} + +static const dispex_static_data_vtbl_t HTMLStorage_dispex_vtbl = { + NULL, + HTMLStorage_get_dispid, + HTMLStorage_invoke, + NULL +}; + static const tid_t HTMLStorage_iface_tids[] = { IHTMLStorage_tid, 0 }; static dispex_static_data_t HTMLStorage_dispex = { L"Storage", - NULL, + &HTMLStorage_dispex_vtbl, IHTMLStorage_tid, HTMLStorage_iface_tids }; diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index cbaa3f15232..aee57456c3a 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1423,6 +1423,11 @@ static inline BOOL is_digit(WCHAR c) return '0' <= c && c <= '9'; } +static inline BOOL is_power_of_2(unsigned x) +{ + return !(x & (x - 1)); +} + #ifdef __i386__ extern void *call_thiscall_func; #endif diff --git a/dlls/mshtml/tests/dom.js b/dlls/mshtml/tests/dom.js index fcbca0f4b2f..e82424e5c55 100644 --- a/dlls/mshtml/tests/dom.js +++ b/dlls/mshtml/tests/dom.js @@ -473,7 +473,27 @@ sync_test("storage", function() { "typeof(window.localStorage) = " + typeof(window.localStorage)); var item = sessionStorage.getItem("nonexisting"); - ok(item === null, "item = " + item); + ok(item === null, "'nonexisting' item = " + item); + item = sessionStorage["nonexisting"]; + ok(item === undefined, "[nonexisting] item = " + item); + ok(!("nonexisting" in sessionStorage), "nonexisting in sessionStorage"); + + sessionStorage.setItem("foobar", 42); + ok("foobar" in sessionStorage, "foobar not in sessionStorage"); + item = sessionStorage.getItem("foobar"); + ok(item === "42", "'foobar' item = " + item); + item = sessionStorage["foobar"]; + ok(item === "42", "[foobar] item = " + item); + sessionStorage.removeItem("foobar"); + item = sessionStorage["foobar"]; + ok(item === undefined, "[foobar] item after removal = " + item); + + sessionStorage["barfoo"] = true; + ok("barfoo" in sessionStorage, "barfoo not in sessionStorage"); + item = sessionStorage["barfoo"]; + ok(item === "true", "[barfoo] item = " + item); + item = sessionStorage.getItem("barfoo"); + ok(item === "true", "'barfoo' item = " + item); }); async_test("animation", function() {