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() {