This should implement all of sessionStorage and most of the missing localStorage (except for the space quota for the latter). Some other common parts are still missing and pending (using props to directly access items in the underlying storage, and StorageEvents, which will come later).
On native, sessionStorage seems to be per-thread, and based on a specific origin, so it's implemented that way using a rbtree for origins in the thread local storage. The diff below (applied after all of the patches) should show that, which works on native as expected, but it's not in the actual commits because it crashes wine-gecko due to known multi-threading issues ([bug 37906](https://bugs.winehq.org/show_bug.cgi?id=37906)).
```diff diff --git a/dlls/mshtml/tests/misc.c b/dlls/mshtml/tests/misc.c index c2c8370..e358e9c 100644 --- a/dlls/mshtml/tests/misc.c +++ b/dlls/mshtml/tests/misc.c @@ -193,6 +193,32 @@ static HRESULT get_sessionstorage(IHTMLDocument2 *doc, IHTMLStorage **storage) return hres; }
+static DWORD WINAPI test_HTMLStorage_thread(void *data) +{ + IHTMLStorage *storage; + IHTMLDocument2 *doc; + BSTR key = data; + HRESULT hres; + VARIANT var; + + CoInitialize(NULL); + + doc = create_doc_from_url(L"http://www.codeweavers.com/"); + hres = get_sessionstorage(doc, &storage); + ok(hres == S_OK, "got %08lx\n", hres); + + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_NULL, "got %d\n", V_VT(&var)); + + IHTMLStorage_Release(storage); + IHTMLDocument2_Release(doc); + + CoUninitialize(); + return 0; +} + static void test_HTMLStorage(void) { IHTMLDocument2 *doc, *doc2; @@ -200,7 +226,9 @@ static void test_HTMLStorage(void) LONG space, length, lval; VARIANT var; BSTR key, value; + HANDLE thread; HRESULT hres; + DWORD ret;
doc = create_doc_from_url(L"http://www.codeweavers.com/"); doc2 = create_doc_from_url(L"http://www.codeweavers.com/"); @@ -607,6 +635,12 @@ static void test_HTMLStorage(void) ok(hres == S_OK, "get_remainingSpace failed %08lx\n", hres); ok(lval == space, "remainingSpace = %ld\n", lval);
+ /* Different thread */ + thread = CreateThread(NULL, 0, test_HTMLStorage_thread, key, 0, NULL); + ret = WaitForSingleObject(thread, INFINITE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %08lx\n", ret); + CloseHandle(thread); + hres = IHTMLStorage_clear(storage); ok(hres == S_OK, "clear failed %08lx\n", hres);
```
There's another rbtree for the actual storage on a given origin, which contains key/value pairs, with keys stored inline because they do not change.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstorage.c | 190 ++++++++++++++++++++++++++++++++++- dlls/mshtml/main.c | 1 + dlls/mshtml/mshtml_private.h | 11 ++ dlls/mshtml/task.c | 9 ++ dlls/mshtml/tests/misc.c | 68 ++++++++++++- 5 files changed, 274 insertions(+), 5 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index ae63c2b2f53..73df6be73e3 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -36,10 +36,122 @@ typedef struct { DispatchEx dispex; IHTMLStorage IHTMLStorage_iface; LONG ref; + struct session_storage_map_entry *session_storage; WCHAR *filename; HANDLE mutex; } HTMLStorage;
+struct session_entry +{ + struct wine_rb_entry entry; + BSTR value; + UINT key_len; + WCHAR key[1]; +}; + +static int session_entry_cmp(const void *key, const struct wine_rb_entry *entry) +{ + struct session_entry *data = WINE_RB_ENTRY_VALUE(entry, struct session_entry, entry); + UINT key_len = SysStringLen((BSTR)key); + + return (key_len != data->key_len) ? (key_len - data->key_len) : memcmp(key, data->key, key_len * sizeof(WCHAR)); +} + +static struct session_storage_map_entry *grab_session_map_entry(BSTR origin) +{ + struct session_storage_map_entry *entry; + struct wine_rb_entry *rb_entry; + thread_data_t *thread_data; + UINT origin_len; + + thread_data = get_thread_data(TRUE); + if(!thread_data) + return NULL; + + rb_entry = wine_rb_get(&thread_data->session_storage_map, origin); + if(rb_entry) { + entry = WINE_RB_ENTRY_VALUE(rb_entry, struct session_storage_map_entry, entry); + entry->ref++; + return entry; + } + + origin_len = SysStringLen(origin); + entry = heap_alloc(FIELD_OFFSET(struct session_storage_map_entry, origin[origin_len])); + if(!entry) + return NULL; + wine_rb_init(&entry->data_map, session_entry_cmp); + entry->ref = 1; + entry->num_keys = 0; + entry->origin_len = origin_len; + memcpy(entry->origin, origin, origin_len * sizeof(WCHAR)); + + wine_rb_put(&thread_data->session_storage_map, origin, &entry->entry); + return entry; +} + +static void release_session_map_entry(struct session_storage_map_entry *entry) +{ + thread_data_t *thread_data; + + if(!entry || --entry->ref || entry->num_keys || !(thread_data = get_thread_data(FALSE))) + return; + + wine_rb_remove(&thread_data->session_storage_map, &entry->entry); + heap_free(entry); +} + +static HRESULT get_session_entry(struct session_storage_map_entry *entry, BSTR key, BOOL create, struct session_entry **ret) +{ + struct wine_rb_entry *rb_entry; + struct session_entry *data; + UINT key_len; + + rb_entry = wine_rb_get(&entry->data_map, key); + if(rb_entry) { + *ret = WINE_RB_ENTRY_VALUE(rb_entry, struct session_entry, entry); + return S_OK; + } + + if(!create) { + *ret = NULL; + return S_OK; + } + + key_len = SysStringLen(key); + if(!(data = heap_alloc(FIELD_OFFSET(struct session_entry, key[key_len])))) + return E_OUTOFMEMORY; + data->value = NULL; + data->key_len = key_len; + memcpy(data->key, key, key_len * sizeof(WCHAR)); + + entry->num_keys++; + wine_rb_put(&entry->data_map, key, &data->entry); + *ret = data; + return S_OK; +} + +static void clear_session_storage(struct session_storage_map_entry *entry) +{ + struct session_entry *iter, *iter2; + + WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter, iter2, &entry->data_map, struct session_entry, entry) { + SysFreeString(iter->value); + heap_free(iter); + } + wine_rb_destroy(&entry->data_map, NULL, NULL); + entry->num_keys = 0; +} + +void destroy_session_storage(thread_data_t *thread_data) +{ + struct session_storage_map_entry *iter, *iter2; + + WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter, iter2, &thread_data->session_storage_map, struct session_storage_map_entry, entry) { + clear_session_storage(iter); + heap_free(iter); + } +} + static inline HTMLStorage *impl_from_IHTMLStorage(IHTMLStorage *iface) { return CONTAINING_RECORD(iface, HTMLStorage, IHTMLStorage_iface); @@ -85,6 +197,7 @@ static ULONG WINAPI HTMLStorage_Release(IHTMLStorage *iface) TRACE("(%p) ref=%ld\n", This, ref);
if(!ref) { + release_session_map_entry(This->session_storage); release_dispex(&This->dispex); heap_free(This->filename); CloseHandle(This->mutex); @@ -438,13 +551,24 @@ done: static HRESULT WINAPI HTMLStorage_setItem(IHTMLStorage *iface, BSTR bstrKey, BSTR bstrValue) { HTMLStorage *This = impl_from_IHTMLStorage(iface); + struct session_entry *session_entry; HRESULT hres;
TRACE("(%p)->(%s %s)\n", This, debugstr_w(bstrKey), debugstr_w(bstrValue));
if(!This->filename) { - FIXME("session storage not supported\n"); - return E_NOTIMPL; + BSTR value = SysAllocStringLen(bstrValue, SysStringLen(bstrValue)); + if(!value) + return E_OUTOFMEMORY; + + hres = get_session_entry(This->session_storage, bstrKey, TRUE, &session_entry); + if(FAILED(hres)) + SysFreeString(value); + else { + SysFreeString(session_entry->value); + session_entry->value = value; + } + return hres; }
WaitForSingleObject(This->mutex, INFINITE); @@ -548,6 +672,52 @@ static dispex_static_data_t HTMLStorage_dispex = { HTMLStorage_iface_tids };
+static HRESULT build_session_origin(IUri *uri, BSTR hostname, BSTR *ret) +{ + UINT host_len, scheme_len; + BSTR scheme, origin; + HRESULT hres; + + hres = IUri_GetSchemeName(uri, &scheme); + if(FAILED(hres)) + return hres; + if(hres != S_OK) { + SysFreeString(scheme); + scheme = NULL; + } + + /* Since it's only used for lookup, we can apply transformations to + keep the lookup itself simple and fast. First, we convert `https` + to `http` because they are equal for lookup. Next, we place the + scheme after the hostname, separated by NUL, to compare the host + first, since it tends to differ more often. Lastly, we lowercase + the whole thing since lookup must be case-insensitive. */ + scheme_len = SysStringLen(scheme); + host_len = SysStringLen(hostname); + + if(scheme_len == 5 && !wcsicmp(scheme, L"https")) + scheme_len--; + + origin = SysAllocStringLen(NULL, host_len + 1 + scheme_len); + if(origin) { + WCHAR *p = origin; + memcpy(p, hostname, host_len * sizeof(WCHAR)); + p += host_len; + *p = ' '; /* for wcslwr */ + memcpy(p + 1, scheme, scheme_len * sizeof(WCHAR)); + p[1 + scheme_len] = '\0'; + _wcslwr(origin); + *p = '\0'; + } + SysFreeString(scheme); + + if(!origin) + return E_OUTOFMEMORY; + + *ret = origin; + return S_OK; +} + static WCHAR *build_filename(BSTR hostname) { static const WCHAR store[] = L"\Microsoft\Internet Explorer\DOMStore\"; @@ -593,8 +763,8 @@ static WCHAR *build_mutexname(const WCHAR *filename) HRESULT create_html_storage(HTMLInnerWindow *window, BOOL local, IHTMLStorage **p) { IUri *uri = window->base.outer_window->uri; + BSTR origin, hostname = NULL; HTMLStorage *storage; - BSTR hostname = NULL; HRESULT hres;
if(!uri) @@ -633,8 +803,20 @@ HRESULT create_html_storage(HTMLInnerWindow *window, BOOL local, IHTMLStorage ** heap_free(storage); return HRESULT_FROM_WIN32(GetLastError()); } - }else + }else { + hres = build_session_origin(uri, hostname, &origin); SysFreeString(hostname); + if(hres != S_OK) { + heap_free(storage); + return hres; + } + storage->session_storage = grab_session_map_entry(origin); + SysFreeString(origin); + if(!storage->session_storage) { + heap_free(storage); + return E_OUTOFMEMORY; + } + }
storage->IHTMLStorage_iface.lpVtbl = &HTMLStorageVtbl; storage->ref = 1; diff --git a/dlls/mshtml/main.c b/dlls/mshtml/main.c index 26c689ea016..ae14137bc54 100644 --- a/dlls/mshtml/main.c +++ b/dlls/mshtml/main.c @@ -256,6 +256,7 @@ static void thread_detach(void) if(thread_data->thread_hwnd) DestroyWindow(thread_data->thread_hwnd);
+ destroy_session_storage(thread_data); heap_free(thread_data); }
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 23217ea3308..3a6555128e1 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1238,10 +1238,21 @@ typedef struct { HWND thread_hwnd; struct list task_list; struct list timer_list; + struct wine_rb_tree session_storage_map; } thread_data_t;
+struct session_storage_map_entry { + struct wine_rb_entry entry; + struct wine_rb_tree data_map; + UINT ref; + UINT num_keys; + UINT origin_len; + WCHAR origin[1]; +}; + thread_data_t *get_thread_data(BOOL) DECLSPEC_HIDDEN; HWND get_thread_hwnd(void) DECLSPEC_HIDDEN; +void destroy_session_storage(thread_data_t*) DECLSPEC_HIDDEN;
LONG get_task_target_magic(void) DECLSPEC_HIDDEN; HRESULT push_task(task_t*,task_proc_t,task_proc_t,LONG) DECLSPEC_HIDDEN; diff --git a/dlls/mshtml/task.c b/dlls/mshtml/task.c index 1bb761e1f0f..96889a17030 100644 --- a/dlls/mshtml/task.c +++ b/dlls/mshtml/task.c @@ -388,6 +388,14 @@ HWND get_thread_hwnd(void) return thread_data->thread_hwnd; }
+static int session_storage_map_cmp(const void *key, const struct wine_rb_entry *entry) +{ + struct session_storage_map_entry *p = WINE_RB_ENTRY_VALUE(entry, struct session_storage_map_entry, entry); + UINT len = SysStringLen((BSTR)key); + + return (len != p->origin_len) ? (len - p->origin_len) : memcmp(key, p->origin, len * sizeof(WCHAR)); +} + thread_data_t *get_thread_data(BOOL create) { thread_data_t *thread_data; @@ -416,6 +424,7 @@ thread_data_t *get_thread_data(BOOL create) TlsSetValue(mshtml_tls, thread_data); list_init(&thread_data->task_list); list_init(&thread_data->timer_list); + wine_rb_init(&thread_data->session_storage_map, session_storage_map_cmp); }
return thread_data; diff --git a/dlls/mshtml/tests/misc.c b/dlls/mshtml/tests/misc.c index 72692c4915b..045e93a9cf6 100644 --- a/dlls/mshtml/tests/misc.c +++ b/dlls/mshtml/tests/misc.c @@ -168,6 +168,31 @@ static HRESULT get_localstorage(IHTMLDocument2 *doc, IHTMLStorage **storage) return hres; }
+static HRESULT get_sessionstorage(IHTMLDocument2 *doc, IHTMLStorage **storage) +{ + IHTMLWindow2 *window; + IHTMLWindow6 *window6; + HRESULT hres; + + hres = IHTMLDocument2_get_parentWindow(doc, &window); + ok(hres == S_OK, "get_parentWindow failed: %08lx\n", hres); + ok(window != NULL, "window == NULL\n"); + + hres = IHTMLWindow2_QueryInterface(window, &IID_IHTMLWindow6, (void**)&window6); + IHTMLWindow2_Release(window); + if(FAILED(hres)) { + win_skip("IHTMLWindow6 not supported\n"); + return hres; + } + + hres = IHTMLWindow6_get_sessionStorage(window6, storage); + ok(hres == S_OK, "get_sessionStorage failed: %08lx\n", hres); + ok(*storage != NULL, "*storage == NULL\n"); + + IHTMLWindow6_Release(window6); + return hres; +} + static void test_HTMLStorage(void) { IHTMLDocument2 *doc, *doc2; @@ -291,8 +316,49 @@ static void test_HTMLStorage(void)
IHTMLStorage_Release(storage); IHTMLStorage_Release(storage2); - IHTMLDocument2_Release(doc); + + /* Session storage on same docs */ + hres = get_sessionstorage(doc, &storage); + ok(hres == S_OK, "got %08lx\n", hres); + + hres = get_sessionstorage(doc2, &storage2); + ok(hres == S_OK, "got %08lx\n", hres); + + key = SysAllocString(L"undefined"); + value = SysAllocString(L"null"); + hres = IHTMLStorage_setItem(storage, key, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(value); + SysFreeString(key); + + value = SysAllocString(L"asdf"); + hres = IHTMLStorage_setItem(storage, NULL, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(value); + + key = SysAllocString(L"null-value"); + hres = IHTMLStorage_setItem(storage, key, NULL); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(key); + + key = SysAllocString(L"aaaa"); + value = SysAllocString(L"bbbb"); + hres = IHTMLStorage_setItem(storage2, key, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(value); + SysFreeString(key); + + key = SysAllocString(L"foo"); + value = SysAllocString(L"bar"); + hres = IHTMLStorage_setItem(storage, key, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(value); + SysFreeString(key); + + IHTMLStorage_Release(storage2); + IHTMLStorage_Release(storage); IHTMLDocument2_Release(doc2); + IHTMLDocument2_Release(doc); }
START_TEST(misc)
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstorage.c | 14 ++++- dlls/mshtml/tests/misc.c | 126 +++++++++++++++++++++++++++++++++++++- 2 files changed, 135 insertions(+), 5 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index 73df6be73e3..6c4e3075e07 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -431,6 +431,7 @@ done: static HRESULT WINAPI HTMLStorage_getItem(IHTMLStorage *iface, BSTR bstrKey, VARIANT *value) { HTMLStorage *This = impl_from_IHTMLStorage(iface); + struct session_entry *session_entry; HRESULT hres;
TRACE("(%p)->(%s %p)\n", This, debugstr_w(bstrKey), value); @@ -439,9 +440,16 @@ static HRESULT WINAPI HTMLStorage_getItem(IHTMLStorage *iface, BSTR bstrKey, VAR return E_POINTER;
if(!This->filename) { - FIXME("session storage not supported\n"); - V_VT(value) = VT_NULL; - return S_OK; + hres = get_session_entry(This->session_storage, bstrKey, FALSE, &session_entry); + if(SUCCEEDED(hres)) { + if(!session_entry || !session_entry->value) + V_VT(value) = VT_NULL; + else { + V_VT(value) = VT_BSTR; + V_BSTR(value) = SysAllocStringLen(session_entry->value, SysStringLen(session_entry->value)); + } + } + return hres; }
WaitForSingleObject(This->mutex, INFINITE); diff --git a/dlls/mshtml/tests/misc.c b/dlls/mshtml/tests/misc.c index 045e93a9cf6..02ef7451b88 100644 --- a/dlls/mshtml/tests/misc.c +++ b/dlls/mshtml/tests/misc.c @@ -324,11 +324,32 @@ static void test_HTMLStorage(void) hres = get_sessionstorage(doc2, &storage2); ok(hres == S_OK, "got %08lx\n", hres);
+ key = SysAllocString(L""); + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_NULL, "got %d\n", V_VT(&var)); + SysFreeString(key); + key = SysAllocString(L"undefined"); value = SysAllocString(L"null"); hres = IHTMLStorage_setItem(storage, key, value); ok(hres == S_OK, "setItem failed: %08lx\n", hres); SysFreeString(value); + + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); + ok(!wcscmp(V_BSTR(&var), L"null"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + VariantClear(&var); + + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage2, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); + ok(!wcscmp(V_BSTR(&var), L"null"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + VariantClear(&var); SysFreeString(key);
value = SysAllocString(L"asdf"); @@ -336,9 +357,32 @@ static void test_HTMLStorage(void) ok(hres == S_OK, "setItem failed: %08lx\n", hres); SysFreeString(value);
+ V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage2, NULL, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); + ok(!wcscmp(V_BSTR(&var), L"asdf"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + VariantClear(&var); + + V_VT(&var) = 0xdead; + key = SysAllocString(L""); + hres = IHTMLStorage_getItem(storage2, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); + ok(!wcscmp(V_BSTR(&var), L"asdf"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + VariantClear(&var); + SysFreeString(key); + key = SysAllocString(L"null-value"); hres = IHTMLStorage_setItem(storage, key, NULL); ok(hres == S_OK, "setItem failed: %08lx\n", hres); + + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage2, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); + ok(!wcscmp(V_BSTR(&var), L""), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + VariantClear(&var); SysFreeString(key);
key = SysAllocString(L"aaaa"); @@ -346,19 +390,97 @@ static void test_HTMLStorage(void) hres = IHTMLStorage_setItem(storage2, key, value); ok(hres == S_OK, "setItem failed: %08lx\n", hres); SysFreeString(value); + + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); + ok(!wcscmp(V_BSTR(&var), L"bbbb"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + VariantClear(&var); SysFreeString(key);
key = SysAllocString(L"foo"); value = SysAllocString(L"bar"); hres = IHTMLStorage_setItem(storage, key, value); ok(hres == S_OK, "setItem failed: %08lx\n", hres); - SysFreeString(value); - SysFreeString(key); + + IHTMLStorage_Release(storage2); + IHTMLDocument2_Release(doc2); + + /* Case insensitive domain */ + doc2 = create_doc_from_url(L"htTp://www.CodeWeavers.com/"); + hres = get_sessionstorage(doc2, &storage2); + ok(hres == S_OK, "got %08lx\n", hres); + + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage2, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); + ok(!wcscmp(V_BSTR(&var), L"bar"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + VariantClear(&var); + + IHTMLStorage_Release(storage2); + IHTMLDocument2_Release(doc2); + + /* Different schemes */ + doc2 = create_doc_from_url(L"https://www.codeweavers.com/"); + hres = get_sessionstorage(doc2, &storage2); + ok(hres == S_OK, "got %08lx\n", hres); + + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage2, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); + ok(!wcscmp(V_BSTR(&var), L"bar"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + VariantClear(&var); + + IHTMLStorage_Release(storage2); + IHTMLDocument2_Release(doc2); + + doc2 = create_doc_from_url(L"ftp://www.codeweavers.com/"); + hres = get_sessionstorage(doc2, &storage2); + ok(hres == S_OK, "got %08lx\n", hres); + + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage2, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_NULL, "got %d\n", V_VT(&var)); + + hres = IHTMLStorage_setItem(storage2, key, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage2, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); + ok(!wcscmp(V_BSTR(&var), L"bar"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + VariantClear(&var); + + IHTMLStorage_Release(storage2); + IHTMLDocument2_Release(doc2); + + /* Different domain */ + doc2 = create_doc_from_url(L"https://www.winehq.org/"); + hres = get_sessionstorage(doc2, &storage2); + ok(hres == S_OK, "got %08lx\n", hres); + + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); + ok(!wcscmp(V_BSTR(&var), L"bar"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + VariantClear(&var); + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage2, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_NULL, "got %d\n", V_VT(&var));
IHTMLStorage_Release(storage2); IHTMLStorage_Release(storage); IHTMLDocument2_Release(doc2); IHTMLDocument2_Release(doc); + SysFreeString(value); + SysFreeString(key); }
START_TEST(misc)
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstorage.c | 11 +++++++++-- dlls/mshtml/tests/misc.c | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index 6c4e3075e07..a39cb7a8898 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -629,13 +629,20 @@ done: static HRESULT WINAPI HTMLStorage_removeItem(IHTMLStorage *iface, BSTR bstrKey) { HTMLStorage *This = impl_from_IHTMLStorage(iface); + struct session_entry *session_entry; HRESULT hres;
TRACE("(%p)->(%s)\n", This, debugstr_w(bstrKey));
if(!This->filename) { - FIXME("session storage not supported\n"); - return E_NOTIMPL; + hres = get_session_entry(This->session_storage, bstrKey, FALSE, &session_entry); + if(SUCCEEDED(hres) && session_entry) { + This->session_storage->num_keys--; + wine_rb_remove(&This->session_storage->data_map, &session_entry->entry); + SysFreeString(session_entry->value); + heap_free(session_entry); + } + return hres; }
WaitForSingleObject(This->mutex, INFINITE); diff --git a/dlls/mshtml/tests/misc.c b/dlls/mshtml/tests/misc.c index 02ef7451b88..1a60ce4f70d 100644 --- a/dlls/mshtml/tests/misc.c +++ b/dlls/mshtml/tests/misc.c @@ -332,6 +332,9 @@ static void test_HTMLStorage(void) SysFreeString(key);
key = SysAllocString(L"undefined"); + hres = IHTMLStorage_removeItem(storage, key); + ok(hres == S_OK, "removeItem failed: %08lx\n", hres); + value = SysAllocString(L"null"); hres = IHTMLStorage_setItem(storage, key, value); ok(hres == S_OK, "setItem failed: %08lx\n", hres); @@ -383,6 +386,11 @@ static void test_HTMLStorage(void) ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); ok(!wcscmp(V_BSTR(&var), L""), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); VariantClear(&var); + + hres = IHTMLStorage_removeItem(storage, NULL); + ok(hres == S_OK, "removeItem failed: %08lx\n", hres); + hres = IHTMLStorage_removeItem(storage2, key); + ok(hres == S_OK, "removeItem failed: %08lx\n", hres); SysFreeString(key);
key = SysAllocString(L"aaaa"); @@ -397,6 +405,14 @@ static void test_HTMLStorage(void) ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); ok(!wcscmp(V_BSTR(&var), L"bbbb"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); VariantClear(&var); + + hres = IHTMLStorage_removeItem(storage2, key); + ok(hres == S_OK, "removeItem failed: %08lx\n", hres); + + V_VT(&var) = 0xdead; + hres = IHTMLStorage_getItem(storage, key, &var); + ok(hres == S_OK, "getItem failed: %08lx\n", hres); + ok(V_VT(&var) == VT_NULL, "got %d\n", V_VT(&var)); SysFreeString(key);
key = SysAllocString(L"foo");
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstorage.c | 27 +++++++++++++++++++++++++-- dlls/mshtml/tests/misc.c | 9 +++++++++ 2 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index a39cb7a8898..a4862d45f70 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -655,8 +655,31 @@ static HRESULT WINAPI HTMLStorage_removeItem(IHTMLStorage *iface, BSTR bstrKey) static HRESULT WINAPI HTMLStorage_clear(IHTMLStorage *iface) { HTMLStorage *This = impl_from_IHTMLStorage(iface); - FIXME("(%p)->()\n", This); - return E_NOTIMPL; + HRESULT hres = S_OK; + HANDLE file; + DWORD count; + + if(!This->filename) { + clear_session_storage(This->session_storage); + return S_OK; + } + + WaitForSingleObject(This->mutex, INFINITE); + + /* Clear the file, if it exists */ + file = CreateFileW(This->filename, GENERIC_WRITE, 0, NULL, TRUNCATE_EXISTING, 0, NULL); + if(file == INVALID_HANDLE_VALUE) { + DWORD error = GetLastError(); + if(error != ERROR_FILE_NOT_FOUND) + hres = HRESULT_FROM_WIN32(error); + }else { + if(!WriteFile(file, "<root/>", sizeof("<root/>") - 1, &count, NULL)) + hres = HRESULT_FROM_WIN32(GetLastError()); + CloseHandle(file); + } + + ReleaseMutex(This->mutex); + return hres; }
static const IHTMLStorageVtbl HTMLStorageVtbl = { diff --git a/dlls/mshtml/tests/misc.c b/dlls/mshtml/tests/misc.c index 1a60ce4f70d..c2a8c87f473 100644 --- a/dlls/mshtml/tests/misc.c +++ b/dlls/mshtml/tests/misc.c @@ -415,6 +415,9 @@ static void test_HTMLStorage(void) ok(V_VT(&var) == VT_NULL, "got %d\n", V_VT(&var)); SysFreeString(key);
+ hres = IHTMLStorage_clear(storage2); + ok(hres == S_OK, "clear failed %08lx\n", hres); + key = SysAllocString(L"foo"); value = SysAllocString(L"bar"); hres = IHTMLStorage_setItem(storage, key, value); @@ -472,6 +475,9 @@ static void test_HTMLStorage(void) ok(!wcscmp(V_BSTR(&var), L"bar"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); VariantClear(&var);
+ hres = IHTMLStorage_clear(storage2); + ok(hres == S_OK, "clear failed %08lx\n", hres); + IHTMLStorage_Release(storage2); IHTMLDocument2_Release(doc2);
@@ -491,6 +497,9 @@ static void test_HTMLStorage(void) ok(hres == S_OK, "getItem failed: %08lx\n", hres); ok(V_VT(&var) == VT_NULL, "got %d\n", V_VT(&var));
+ hres = IHTMLStorage_clear(storage); + ok(hres == S_OK, "clear failed %08lx\n", hres); + IHTMLStorage_Release(storage2); IHTMLStorage_Release(storage); IHTMLDocument2_Release(doc2);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstorage.c | 27 +++++++++++++++++++++++++-- dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/tests/misc.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index a4862d45f70..610a5d0d4b3 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -44,6 +44,7 @@ typedef struct { struct session_entry { struct wine_rb_entry entry; + struct list list_entry; BSTR value; UINT key_len; WCHAR key[1]; @@ -80,6 +81,7 @@ static struct session_storage_map_entry *grab_session_map_entry(BSTR origin) if(!entry) return NULL; wine_rb_init(&entry->data_map, session_entry_cmp); + list_init(&entry->data_list); entry->ref = 1; entry->num_keys = 0; entry->origin_len = origin_len; @@ -125,6 +127,7 @@ static HRESULT get_session_entry(struct session_storage_map_entry *entry, BSTR k memcpy(data->key, key, key_len * sizeof(WCHAR));
entry->num_keys++; + list_add_tail(&entry->data_list, &data->list_entry); wine_rb_put(&entry->data_map, key, &data->entry); *ret = data; return S_OK; @@ -134,11 +137,12 @@ static void clear_session_storage(struct session_storage_map_entry *entry) { struct session_entry *iter, *iter2;
- WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter, iter2, &entry->data_map, struct session_entry, entry) { + LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &entry->data_list, struct session_entry, list_entry) { SysFreeString(iter->value); heap_free(iter); } wine_rb_destroy(&entry->data_map, NULL, NULL); + list_init(&entry->data_list); entry->num_keys = 0; }
@@ -353,7 +357,25 @@ static HRESULT WINAPI HTMLStorage_get_remainingSpace(IHTMLStorage *iface, LONG * static HRESULT WINAPI HTMLStorage_key(IHTMLStorage *iface, LONG lIndex, BSTR *p) { HTMLStorage *This = impl_from_IHTMLStorage(iface); - FIXME("(%p)->(%ld %p)\n", This, lIndex, p); + struct session_entry *session_entry; + + TRACE("(%p)->(%ld %p)\n", This, lIndex, p); + + if(!This->filename) { + struct list *entry = &This->session_storage->data_list; + unsigned i = 0; + + if(lIndex >= This->session_storage->num_keys) + return E_INVALIDARG; + + do entry = entry->next; while(i++ < lIndex); + session_entry = LIST_ENTRY(entry, struct session_entry, list_entry); + + *p = SysAllocStringLen(session_entry->key, session_entry->key_len); + return *p ? S_OK : E_OUTOFMEMORY; + } + + FIXME("local storage not supported\n"); return E_NOTIMPL; }
@@ -638,6 +660,7 @@ static HRESULT WINAPI HTMLStorage_removeItem(IHTMLStorage *iface, BSTR bstrKey) hres = get_session_entry(This->session_storage, bstrKey, FALSE, &session_entry); if(SUCCEEDED(hres) && session_entry) { This->session_storage->num_keys--; + list_remove(&session_entry->list_entry); wine_rb_remove(&This->session_storage->data_map, &session_entry->entry); SysFreeString(session_entry->value); heap_free(session_entry); diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 3a6555128e1..cf3e769b0e8 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1244,6 +1244,7 @@ typedef struct { struct session_storage_map_entry { struct wine_rb_entry entry; struct wine_rb_tree data_map; + struct list data_list; /* for key() */ UINT ref; UINT num_keys; UINT origin_len; diff --git a/dlls/mshtml/tests/misc.c b/dlls/mshtml/tests/misc.c index c2a8c87f473..660c191ac99 100644 --- a/dlls/mshtml/tests/misc.c +++ b/dlls/mshtml/tests/misc.c @@ -405,11 +405,38 @@ static void test_HTMLStorage(void) ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); ok(!wcscmp(V_BSTR(&var), L"bbbb"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); VariantClear(&var); + SysFreeString(key); + + hres = IHTMLStorage_key(storage, 0, &key); + ok(hres == S_OK, "key failed %08lx\n", hres); + ok(!wcscmp(key, L"undefined"), "key(0) = %s\n", wine_dbgstr_w(key)); + SysFreeString(key);
+ hres = IHTMLStorage_key(storage, 1, &key); + ok(hres == S_OK, "key failed %08lx\n", hres); + ok(!wcscmp(key, L"aaaa"), "key(0) = %s\n", wine_dbgstr_w(key)); + SysFreeString(key); + + hres = IHTMLStorage_key(storage, 2, &key); + ok(hres == E_INVALIDARG, "key failed %08lx\n", hres); + hres = IHTMLStorage_key(storage, -1, &key); + ok(hres == E_INVALIDARG, "key failed %08lx\n", hres); + + key = SysAllocString(L"undefined"); hres = IHTMLStorage_removeItem(storage2, key); ok(hres == S_OK, "removeItem failed: %08lx\n", hres); + SysFreeString(key); + + hres = IHTMLStorage_key(storage, 0, &key); + ok(hres == S_OK, "key failed %08lx\n", hres); + ok(!wcscmp(key, L"aaaa"), "key(0) = %s\n", wine_dbgstr_w(key)); + SysFreeString(key); + + hres = IHTMLStorage_key(storage, 1, &key); + ok(hres == E_INVALIDARG, "key failed %08lx\n", hres);
V_VT(&var) = 0xdead; + key = SysAllocString(L"undefined"); hres = IHTMLStorage_getItem(storage, key, &var); ok(hres == S_OK, "getItem failed: %08lx\n", hres); ok(V_VT(&var) == VT_NULL, "got %d\n", V_VT(&var)); @@ -418,6 +445,9 @@ static void test_HTMLStorage(void) hres = IHTMLStorage_clear(storage2); ok(hres == S_OK, "clear failed %08lx\n", hres);
+ hres = IHTMLStorage_key(storage, 0, &key); + ok(hres == E_INVALIDARG, "key failed %08lx\n", hres); + key = SysAllocString(L"foo"); value = SysAllocString(L"bar"); hres = IHTMLStorage_setItem(storage, key, value);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstorage.c | 99 ++++++++++++++++++++++++++++++++------- dlls/mshtml/tests/misc.c | 13 +++++ 2 files changed, 96 insertions(+), 16 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index 610a5d0d4b3..f1c5801764d 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -340,6 +340,46 @@ done: return hres; }
+static HRESULT get_root_node(IXMLDOMDocument *doc, IXMLDOMNode **root) +{ + HRESULT hres; + BSTR str; + + str = SysAllocString(L"root"); + if(!str) + return E_OUTOFMEMORY; + + hres = IXMLDOMDocument_selectSingleNode(doc, str, root); + SysFreeString(str); + return hres; +} + +static HRESULT get_node_list(const WCHAR *filename, IXMLDOMNodeList **node_list) +{ + IXMLDOMDocument *doc; + IXMLDOMNode *root; + HRESULT hres; + BSTR query; + + hres = open_document(filename, &doc); + if(hres != S_OK) + return hres; + + hres = get_root_node(doc, &root); + IXMLDOMDocument_Release(doc); + if(hres != S_OK) + return hres; + + if(!(query = SysAllocString(L"item"))) + hres = E_OUTOFMEMORY; + else { + hres = IXMLDOMNode_selectNodes(root, query, node_list); + SysFreeString(query); + } + IXMLDOMNode_Release(root); + return hres; +} + static HRESULT WINAPI HTMLStorage_get_length(IHTMLStorage *iface, LONG *p) { HTMLStorage *This = impl_from_IHTMLStorage(iface); @@ -354,10 +394,48 @@ static HRESULT WINAPI HTMLStorage_get_remainingSpace(IHTMLStorage *iface, LONG * return E_NOTIMPL; }
+static HRESULT get_key(const WCHAR *filename, LONG index, BSTR *ret) +{ + IXMLDOMNodeList *node_list; + IXMLDOMElement *elem; + IXMLDOMNode *node; + HRESULT hres; + VARIANT key; + + hres = get_node_list(filename, &node_list); + if(FAILED(hres)) + return hres; + + hres = IXMLDOMNodeList_get_item(node_list, index, &node); + IXMLDOMNodeList_Release(node_list); + if(hres != S_OK) + return FAILED(hres) ? hres : E_INVALIDARG; + + hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem); + IXMLDOMNode_Release(node); + if(hres != S_OK) + return E_INVALIDARG; + + hres = IXMLDOMElement_getAttribute(elem, (BSTR)L"name", &key); + IXMLDOMElement_Release(elem); + if(FAILED(hres)) + return hres; + + if(V_VT(&key) != VT_BSTR) { + FIXME("non-string key %s\n", debugstr_variant(&key)); + VariantClear(&key); + return E_NOTIMPL; + } + + *ret = V_BSTR(&key); + return S_OK; +} + static HRESULT WINAPI HTMLStorage_key(IHTMLStorage *iface, LONG lIndex, BSTR *p) { HTMLStorage *This = impl_from_IHTMLStorage(iface); struct session_entry *session_entry; + HRESULT hres;
TRACE("(%p)->(%ld %p)\n", This, lIndex, p);
@@ -375,8 +453,11 @@ static HRESULT WINAPI HTMLStorage_key(IHTMLStorage *iface, LONG lIndex, BSTR *p) return *p ? S_OK : E_OUTOFMEMORY; }
- FIXME("local storage not supported\n"); - return E_NOTIMPL; + WaitForSingleObject(This->mutex, INFINITE); + hres = get_key(This->filename, lIndex, p); + ReleaseMutex(This->mutex); + + return hres; }
static BSTR build_query(const WCHAR *key) @@ -390,20 +471,6 @@ static BSTR build_query(const WCHAR *key) return ret; }
-static HRESULT get_root_node(IXMLDOMDocument *doc, IXMLDOMNode **root) -{ - HRESULT hres; - BSTR str; - - str = SysAllocString(L"root"); - if(!str) - return E_OUTOFMEMORY; - - hres = IXMLDOMDocument_selectSingleNode(doc, str, root); - SysFreeString(str); - return hres; -} - static HRESULT get_item(const WCHAR *filename, BSTR key, VARIANT *value) { IXMLDOMDocument *doc; diff --git a/dlls/mshtml/tests/misc.c b/dlls/mshtml/tests/misc.c index 660c191ac99..d898b04fffa 100644 --- a/dlls/mshtml/tests/misc.c +++ b/dlls/mshtml/tests/misc.c @@ -229,6 +229,19 @@ static void test_HTMLStorage(void) ok(hres == S_OK, "getItem failed: %08lx\n", hres); ok(V_VT(&var) == VT_BSTR, "got %d\n", V_VT(&var)); if (V_VT(&var) == VT_BSTR) ok(!wcscmp(V_BSTR(&var), L"null"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + SysFreeString(key); + + hres = IHTMLStorage_key(storage, 0, &key); + ok(hres == S_OK, "key failed %08lx\n", hres); + ok(!wcscmp(key, L"undefined"), "key(0) = %s\n", wine_dbgstr_w(key)); + SysFreeString(key); + + hres = IHTMLStorage_key(storage, 1, &key); + ok(hres == E_INVALIDARG, "key failed %08lx\n", hres); + hres = IHTMLStorage_key(storage, -1, &key); + ok(hres == E_INVALIDARG, "key failed %08lx\n", hres); + + key = SysAllocString(L"undefined"); hres = IHTMLStorage_removeItem(storage, key); ok(hres == S_OK, "removeItem failed: %08lx\n", hres); SysFreeString(key);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
`length` for localStorage is not updated immediately on native, which suggests some caching is involved for a period of time (probably for performance reasons to avoid reading the file everytime the prop is read), or it doesn't block like the rest since the write is asynchronous, making it unreliable to test, which is why the tests for it are basic.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstorage.c | 21 +++++++++++++++++++-- dlls/mshtml/tests/misc.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index f1c5801764d..9b0beff8f9f 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -383,8 +383,25 @@ static HRESULT get_node_list(const WCHAR *filename, IXMLDOMNodeList **node_list) static HRESULT WINAPI HTMLStorage_get_length(IHTMLStorage *iface, LONG *p) { HTMLStorage *This = impl_from_IHTMLStorage(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + IXMLDOMNodeList *node_list; + HRESULT hres; + + TRACE("(%p)->(%p)\n", This, p); + + if(!This->filename) { + *p = This->session_storage->num_keys; + return S_OK; + } + + WaitForSingleObject(This->mutex, INFINITE); + hres = get_node_list(This->filename, &node_list); + if(SUCCEEDED(hres)) { + hres = IXMLDOMNodeList_get_length(node_list, p); + IXMLDOMNodeList_Release(node_list); + } + ReleaseMutex(This->mutex); + + return hres; }
static HRESULT WINAPI HTMLStorage_get_remainingSpace(IHTMLStorage *iface, LONG *p) diff --git a/dlls/mshtml/tests/misc.c b/dlls/mshtml/tests/misc.c index d898b04fffa..d1bdd2d544e 100644 --- a/dlls/mshtml/tests/misc.c +++ b/dlls/mshtml/tests/misc.c @@ -197,6 +197,7 @@ static void test_HTMLStorage(void) { IHTMLDocument2 *doc, *doc2; IHTMLStorage *storage, *storage2; + LONG length, lval; VARIANT var; BSTR key, value; HRESULT hres; @@ -220,6 +221,10 @@ static void test_HTMLStorage(void) hres = IHTMLStorage_removeItem(storage, key); ok(hres == S_OK, "removeItem failed: %08lx\n", hres);
+ hres = IHTMLStorage_get_length(storage, &length); + ok(hres == S_OK, "get_length failed %08lx\n", hres); + ok(length >= 0, "length = %ld\n", lval); + value = SysAllocString(L"null"); hres = IHTMLStorage_setItem(storage, key, value); ok(hres == S_OK, "setItem failed: %08lx\n", hres); @@ -337,6 +342,10 @@ static void test_HTMLStorage(void) hres = get_sessionstorage(doc2, &storage2); ok(hres == S_OK, "got %08lx\n", hres);
+ hres = IHTMLStorage_get_length(storage, &lval); + ok(hres == S_OK, "get_length failed %08lx\n", hres); + ok(lval == 0, "length = %ld\n", lval); + key = SysAllocString(L""); V_VT(&var) = 0xdead; hres = IHTMLStorage_getItem(storage, key, &var); @@ -353,6 +362,13 @@ static void test_HTMLStorage(void) ok(hres == S_OK, "setItem failed: %08lx\n", hres); SysFreeString(value);
+ hres = IHTMLStorage_get_length(storage, &lval); + ok(hres == S_OK, "get_length failed %08lx\n", hres); + ok(lval == 1, "length = %ld\n", lval); + hres = IHTMLStorage_get_length(storage2, &lval); + ok(hres == S_OK, "get_length failed %08lx\n", hres); + ok(lval == 1, "length = %ld\n", lval); + V_VT(&var) = 0xdead; hres = IHTMLStorage_getItem(storage, key, &var); ok(hres == S_OK, "getItem failed: %08lx\n", hres); @@ -420,6 +436,13 @@ static void test_HTMLStorage(void) VariantClear(&var); SysFreeString(key);
+ hres = IHTMLStorage_get_length(storage, &lval); + ok(hres == S_OK, "get_length failed %08lx\n", hres); + ok(lval == 2, "length = %ld\n", lval); + hres = IHTMLStorage_get_length(storage2, &lval); + ok(hres == S_OK, "get_length failed %08lx\n", hres); + ok(lval == 2, "length = %ld\n", lval); + hres = IHTMLStorage_key(storage, 0, &key); ok(hres == S_OK, "key failed %08lx\n", hres); ok(!wcscmp(key, L"undefined"), "key(0) = %s\n", wine_dbgstr_w(key)); @@ -458,6 +481,13 @@ static void test_HTMLStorage(void) hres = IHTMLStorage_clear(storage2); ok(hres == S_OK, "clear failed %08lx\n", hres);
+ hres = IHTMLStorage_get_length(storage, &lval); + ok(hres == S_OK, "get_length failed %08lx\n", hres); + ok(lval == 0, "length = %ld\n", lval); + hres = IHTMLStorage_get_length(storage2, &lval); + ok(hres == S_OK, "get_length failed %08lx\n", hres); + ok(lval == 0, "length = %ld\n", lval); + hres = IHTMLStorage_key(storage, 0, &key); ok(hres == E_INVALIDARG, "key failed %08lx\n", hres);
From: Gabriel Ivăncescu gabrielopcode@gmail.com
And a character quota that matches native.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstorage.c | 28 ++++++++++++++++++++++++-- dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/tests/misc.c | 39 +++++++++++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 3 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index 9b0beff8f9f..2b529498a64 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -32,6 +32,9 @@
WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
+/* Native defaults to 5 million chars per origin */ +enum { MAX_QUOTA = 5000000 }; + typedef struct { DispatchEx dispex; IHTMLStorage IHTMLStorage_iface; @@ -83,6 +86,7 @@ static struct session_storage_map_entry *grab_session_map_entry(BSTR origin) wine_rb_init(&entry->data_map, session_entry_cmp); list_init(&entry->data_list); entry->ref = 1; + entry->quota = MAX_QUOTA; entry->num_keys = 0; entry->origin_len = origin_len; memcpy(entry->origin, origin, origin_len * sizeof(WCHAR)); @@ -120,12 +124,15 @@ static HRESULT get_session_entry(struct session_storage_map_entry *entry, BSTR k }
key_len = SysStringLen(key); + if(entry->quota < key_len) + return E_OUTOFMEMORY; /* native returns this when quota is exceeded */ if(!(data = heap_alloc(FIELD_OFFSET(struct session_entry, key[key_len])))) return E_OUTOFMEMORY; data->value = NULL; data->key_len = key_len; memcpy(data->key, key, key_len * sizeof(WCHAR));
+ entry->quota -= key_len; entry->num_keys++; list_add_tail(&entry->data_list, &data->list_entry); wine_rb_put(&entry->data_map, key, &data->entry); @@ -143,6 +150,7 @@ static void clear_session_storage(struct session_storage_map_entry *entry) } wine_rb_destroy(&entry->data_map, NULL, NULL); list_init(&entry->data_list); + entry->quota = MAX_QUOTA; entry->num_keys = 0; }
@@ -407,7 +415,15 @@ static HRESULT WINAPI HTMLStorage_get_length(IHTMLStorage *iface, LONG *p) static HRESULT WINAPI HTMLStorage_get_remainingSpace(IHTMLStorage *iface, LONG *p) { HTMLStorage *This = impl_from_IHTMLStorage(iface); - FIXME("(%p)->(%p)\n", This, p); + + TRACE("(%p)->(%p)\n", This, p); + + if(!This->filename) { + *p = This->session_storage->quota; + return S_OK; + } + + FIXME("local storage not supported\n"); return E_NOTIMPL; }
@@ -671,7 +687,8 @@ static HRESULT WINAPI HTMLStorage_setItem(IHTMLStorage *iface, BSTR bstrKey, BST TRACE("(%p)->(%s %s)\n", This, debugstr_w(bstrKey), debugstr_w(bstrValue));
if(!This->filename) { - BSTR value = SysAllocStringLen(bstrValue, SysStringLen(bstrValue)); + UINT value_len = SysStringLen(bstrValue); + BSTR value = SysAllocStringLen(bstrValue, value_len); if(!value) return E_OUTOFMEMORY;
@@ -679,6 +696,12 @@ static HRESULT WINAPI HTMLStorage_setItem(IHTMLStorage *iface, BSTR bstrKey, BST if(FAILED(hres)) SysFreeString(value); else { + UINT old_len = SysStringLen(session_entry->value); + if(old_len < value_len && This->session_storage->quota < value_len - old_len) { + SysFreeString(value); + return E_OUTOFMEMORY; /* native returns this when quota is exceeded */ + } + This->session_storage->quota -= value_len - old_len; SysFreeString(session_entry->value); session_entry->value = value; } @@ -743,6 +766,7 @@ static HRESULT WINAPI HTMLStorage_removeItem(IHTMLStorage *iface, BSTR bstrKey) if(!This->filename) { hres = get_session_entry(This->session_storage, bstrKey, FALSE, &session_entry); if(SUCCEEDED(hres) && session_entry) { + This->session_storage->quota += session_entry->key_len + SysStringLen(session_entry->value); This->session_storage->num_keys--; list_remove(&session_entry->list_entry); wine_rb_remove(&This->session_storage->data_map, &session_entry->entry); diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index cf3e769b0e8..0f353fadf31 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1246,6 +1246,7 @@ struct session_storage_map_entry { struct wine_rb_tree data_map; struct list data_list; /* for key() */ UINT ref; + UINT quota; UINT num_keys; UINT origin_len; WCHAR origin[1]; diff --git a/dlls/mshtml/tests/misc.c b/dlls/mshtml/tests/misc.c index d1bdd2d544e..c2c8370cc4b 100644 --- a/dlls/mshtml/tests/misc.c +++ b/dlls/mshtml/tests/misc.c @@ -197,7 +197,7 @@ static void test_HTMLStorage(void) { IHTMLDocument2 *doc, *doc2; IHTMLStorage *storage, *storage2; - LONG length, lval; + LONG space, length, lval; VARIANT var; BSTR key, value; HRESULT hres; @@ -346,6 +346,10 @@ static void test_HTMLStorage(void) ok(hres == S_OK, "get_length failed %08lx\n", hres); ok(lval == 0, "length = %ld\n", lval);
+ hres = IHTMLStorage_get_remainingSpace(storage, &space); + ok(hres == S_OK, "get_remainingSpace failed %08lx\n", hres); + ok(space >= 5000000, "remainingSpace = %ld\n", space); + key = SysAllocString(L""); V_VT(&var) = 0xdead; hres = IHTMLStorage_getItem(storage, key, &var); @@ -369,6 +373,13 @@ static void test_HTMLStorage(void) ok(hres == S_OK, "get_length failed %08lx\n", hres); ok(lval == 1, "length = %ld\n", lval);
+ hres = IHTMLStorage_get_remainingSpace(storage, &lval); + ok(hres == S_OK, "get_remainingSpace failed %08lx\n", hres); + ok(lval == space - 13, "remainingSpace = %ld\n", lval); + hres = IHTMLStorage_get_remainingSpace(storage2, &lval); + ok(hres == S_OK, "get_remainingSpace failed %08lx\n", hres); + ok(lval == space - 13, "remainingSpace = %ld\n", lval); + V_VT(&var) = 0xdead; hres = IHTMLStorage_getItem(storage, key, &var); ok(hres == S_OK, "getItem failed: %08lx\n", hres); @@ -443,6 +454,13 @@ static void test_HTMLStorage(void) ok(hres == S_OK, "get_length failed %08lx\n", hres); ok(lval == 2, "length = %ld\n", lval);
+ hres = IHTMLStorage_get_remainingSpace(storage, &lval); + ok(hres == S_OK, "get_remainingSpace failed %08lx\n", hres); + ok(lval == space - 21, "remainingSpace = %ld\n", lval); + hres = IHTMLStorage_get_remainingSpace(storage2, &lval); + ok(hres == S_OK, "get_remainingSpace failed %08lx\n", hres); + ok(lval == space - 21, "remainingSpace = %ld\n", lval); + hres = IHTMLStorage_key(storage, 0, &key); ok(hres == S_OK, "key failed %08lx\n", hres); ok(!wcscmp(key, L"undefined"), "key(0) = %s\n", wine_dbgstr_w(key)); @@ -488,6 +506,13 @@ static void test_HTMLStorage(void) ok(hres == S_OK, "get_length failed %08lx\n", hres); ok(lval == 0, "length = %ld\n", lval);
+ hres = IHTMLStorage_get_remainingSpace(storage, &lval); + ok(hres == S_OK, "get_remainingSpace failed %08lx\n", hres); + ok(lval == space, "remainingSpace = %ld\n", lval); + hres = IHTMLStorage_get_remainingSpace(storage2, &lval); + ok(hres == S_OK, "get_remainingSpace failed %08lx\n", hres); + ok(lval == space, "remainingSpace = %ld\n", lval); + hres = IHTMLStorage_key(storage, 0, &key); ok(hres == E_INVALIDARG, "key failed %08lx\n", hres);
@@ -526,6 +551,10 @@ static void test_HTMLStorage(void) ok(!wcscmp(V_BSTR(&var), L"bar"), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); VariantClear(&var);
+ hres = IHTMLStorage_get_remainingSpace(storage, &lval); + ok(hres == S_OK, "get_remainingSpace failed %08lx\n", hres); + ok(lval == space - 6, "remainingSpace = %ld\n", lval); + IHTMLStorage_Release(storage2); IHTMLDocument2_Release(doc2);
@@ -538,6 +567,10 @@ static void test_HTMLStorage(void) ok(hres == S_OK, "getItem failed: %08lx\n", hres); ok(V_VT(&var) == VT_NULL, "got %d\n", V_VT(&var));
+ hres = IHTMLStorage_get_remainingSpace(storage2, &lval); + ok(hres == S_OK, "get_remainingSpace failed %08lx\n", hres); + ok(lval == space, "remainingSpace = %ld\n", lval); + hres = IHTMLStorage_setItem(storage2, key, value); ok(hres == S_OK, "setItem failed: %08lx\n", hres);
@@ -570,6 +603,10 @@ static void test_HTMLStorage(void) ok(hres == S_OK, "getItem failed: %08lx\n", hres); ok(V_VT(&var) == VT_NULL, "got %d\n", V_VT(&var));
+ hres = IHTMLStorage_get_remainingSpace(storage2, &lval); + ok(hres == S_OK, "get_remainingSpace failed %08lx\n", hres); + ok(lval == space, "remainingSpace = %ld\n", lval); + hres = IHTMLStorage_clear(storage); ok(hres == S_OK, "clear failed %08lx\n", hres);
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=121835
Your paranoid android.
=== build (build log) ===
error: patch failed: dlls/mshtml/tests/misc.c:200 Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/mshtml/tests/misc.c:200 Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/mshtml/tests/misc.c:200 Task: Patch failed to apply
Jacek Caban (@jacek) commented about dlls/mshtml/mshtml_private.h:
HWND thread_hwnd; struct list task_list; struct list timer_list;
- struct wine_rb_tree session_storage_map;
} thread_data_t;
+struct session_storage_map_entry {
- struct wine_rb_entry entry;
- struct wine_rb_tree data_map;
- UINT ref;
- UINT num_keys;
- UINT origin_len;
- WCHAR origin[1];
+};
I think it would be better to keep it private to htmlstorage.c. You may just expose session_storage_map_cmp from there, for example.
Jacek Caban (@jacek) commented about dlls/mshtml/htmlstorage.c:
return NULL;
- wine_rb_init(&entry->data_map, session_entry_cmp);
- entry->ref = 1;
- entry->num_keys = 0;
- entry->origin_len = origin_len;
- memcpy(entry->origin, origin, origin_len * sizeof(WCHAR));
- wine_rb_put(&thread_data->session_storage_map, origin, &entry->entry);
- return entry;
+}
+static void release_session_map_entry(struct session_storage_map_entry *entry) +{
- thread_data_t *thread_data;
- if(!entry || --entry->ref || entry->num_keys || !(thread_data = get_thread_data(FALSE)))
This ref counting seems suspicions. You may, for example, end up clearing session entry when navigating between pages on the same origin (when previous page would already release HTML storage object before the new page has a chance to create it). Shouldn't session data be more persistent in general? I guess it could be queried for as long as the thread is alive.
The entry->num_keys check should keep it alive even with zero refcount, to preserve the data. I did it this way to remove spurious empty origins, in case there's a lot of navigation going on, and then there would be a lot of entries left behind until the thread shuts down for no reason.
So it only gets removed when:
* No storage objects hold a ref to it, and * There is no data stored for that origin.
Since an empty data for an origin is the same as it not having an entry at all, so we can safely remove it (as long as no objects hold a ref to it).
On Thu Aug 25 17:09:11 2022 +0000, Gabriel Iv��ncescu wrote:
The entry->num_keys check should keep it alive even with zero refcount, to preserve the data. I did it this way to remove spurious empty origins, in case there's a lot of navigation going on, and then there would be a lot of entries left behind until the thread shuts down for no reason. So it only gets removed when:
- No storage objects hold a ref to it, and
- There is no data stored for that origin.
Since an empty data for an origin is the same as it not having an entry at all, so we can safely remove it (as long as no objects hold a ref to it).
Ah, right, that's fine. (While we're at this, you don't need get_thread_data() error handling here; you know that it exists if session_storage_map_entry exists).