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.
-- v3: mshtml: Implement remainingSpace prop for sessionStorage. mshtml: Implement length prop for Storage. mshtml: Implement key() for localStorage. mshtml: Implement key() for sessionStorage. mshtml: Implement clear() for Storage. mshtml: Implement removeItem() for sessionStorage. mshtml: Implement getItem() for sessionStorage. mshtml: Implement setItem() for sessionStorage.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstorage.c | 202 ++++++++++++++++++++++++++++++++++- dlls/mshtml/main.c | 1 + dlls/mshtml/mshtml_private.h | 3 + dlls/mshtml/task.c | 1 + dlls/mshtml/tests/misc.c | 68 +++++++++++- 5 files changed, 270 insertions(+), 5 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index ae63c2b2f53..6e8b2190b1a 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -36,10 +36,134 @@ typedef struct { DispatchEx dispex; IHTMLStorage IHTMLStorage_iface; LONG ref; + struct session_map_entry *session_storage; WCHAR *filename; HANDLE mutex; } HTMLStorage;
+struct session_map_entry { + struct wine_rb_entry entry; + struct wine_rb_tree data_map; + UINT ref; + UINT num_keys; + UINT origin_len; + WCHAR origin[1]; +}; + +int session_storage_map_cmp(const void *key, const struct wine_rb_entry *entry) +{ + struct session_map_entry *p = WINE_RB_ENTRY_VALUE(entry, struct session_map_entry, entry); + UINT len = SysStringLen((BSTR)key); + + return (len != p->origin_len) ? (len - p->origin_len) : memcmp(key, p->origin, len * sizeof(WCHAR)); +} + +struct session_entry +{ + struct wine_rb_entry entry; + BSTR value; + 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); + return wcscmp(key, data->key); +} + +static struct session_map_entry *grab_session_map_entry(BSTR origin) +{ + struct session_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_map_entry, entry); + entry->ref++; + return entry; + } + + origin_len = SysStringLen(origin); + entry = heap_alloc(FIELD_OFFSET(struct session_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_map_entry *entry) +{ + if(!entry || --entry->ref || entry->num_keys) + return; + + wine_rb_remove(&get_thread_data(FALSE)->session_storage_map, &entry->entry); + heap_free(entry); +} + +static HRESULT get_session_entry(struct session_map_entry *entry, const WCHAR *name, BOOL create, struct session_entry **ret) +{ + const WCHAR *key = name ? name : L""; + 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 = wcslen(key); + if(!(data = heap_alloc(FIELD_OFFSET(struct session_entry, key[key_len + 1])))) + return E_OUTOFMEMORY; + data->value = NULL; + memcpy(data->key, key, (key_len + 1) * 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_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_map_entry *iter, *iter2; + + WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter, iter2, &thread_data->session_storage_map, struct session_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 +209,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 +563,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 = SysAllocString(bstrValue ? bstrValue : L""); + 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 +684,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 +775,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 +815,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..cbaa3f15232 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1238,10 +1238,13 @@ typedef struct { HWND thread_hwnd; struct list task_list; struct list timer_list; + struct wine_rb_tree session_storage_map; } thread_data_t;
thread_data_t *get_thread_data(BOOL) DECLSPEC_HIDDEN; HWND get_thread_hwnd(void) DECLSPEC_HIDDEN; +int session_storage_map_cmp(const void*,const struct wine_rb_entry*) 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..28564f7b8d8 100644 --- a/dlls/mshtml/task.c +++ b/dlls/mshtml/task.c @@ -416,6 +416,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 | 15 ++++- dlls/mshtml/tests/misc.c | 126 +++++++++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 5 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index 6e8b2190b1a..d25f8fef6a1 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -443,6 +443,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); @@ -451,9 +452,17 @@ 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)); + hres = V_BSTR(value) ? S_OK : E_OUTOFMEMORY; + } + } + 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 d25f8fef6a1..a7feba6ce69 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -642,13 +642,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 | 17 +++++++++++++++-- dlls/mshtml/tests/misc.c | 9 +++++++++ 2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index a7feba6ce69..e9498306a81 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -668,8 +668,21 @@ 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; + + if(!This->filename) { + clear_session_storage(This->session_storage); + return S_OK; + } + + WaitForSingleObject(This->mutex, INFINITE); + if(!DeleteFileW(This->filename)) { + DWORD error = GetLastError(); + if(error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND) + hres = HRESULT_FROM_WIN32(error); + } + 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 | 28 ++++++++++++++++++++++++++-- dlls/mshtml/tests/misc.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index e9498306a81..24b696bd373 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -44,6 +44,7 @@ typedef struct { struct session_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; @@ -61,6 +62,7 @@ int session_storage_map_cmp(const void *key, const struct wine_rb_entry *entry) struct session_entry { struct wine_rb_entry entry; + struct list list_entry; BSTR value; WCHAR key[1]; }; @@ -94,6 +96,7 @@ static struct session_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; @@ -137,6 +140,7 @@ static HRESULT get_session_entry(struct session_map_entry *entry, const WCHAR *n memcpy(data->key, key, (key_len + 1) * 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; @@ -146,11 +150,12 @@ static void clear_session_storage(struct session_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; }
@@ -365,7 +370,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 = SysAllocString(session_entry->key); + return *p ? S_OK : E_OUTOFMEMORY; + } + + FIXME("local storage not supported\n"); return E_NOTIMPL; }
@@ -651,6 +674,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/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 24b696bd373..e0459c42497 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -353,6 +353,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); @@ -367,10 +407,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);
@@ -388,8 +466,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) @@ -403,20 +484,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 e0459c42497..f1ebb29a1fa 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -396,8 +396,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 | 29 +++++++++++++++++++++++++++-- dlls/mshtml/tests/misc.c | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 3 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index f1ebb29a1fa..f4413ba2a3a 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; @@ -46,6 +49,7 @@ struct session_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]; @@ -98,6 +102,7 @@ static struct session_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)); @@ -134,11 +139,14 @@ static HRESULT get_session_entry(struct session_map_entry *entry, const WCHAR *n }
key_len = wcslen(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 + 1])))) return E_OUTOFMEMORY; data->value = NULL; memcpy(data->key, key, (key_len + 1) * 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); @@ -156,6 +164,7 @@ static void clear_session_storage(struct session_map_entry *entry) } wine_rb_destroy(&entry->data_map, NULL, NULL); list_init(&entry->data_list); + entry->quota = MAX_QUOTA; entry->num_keys = 0; }
@@ -420,7 +429,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; }
@@ -685,7 +702,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 = SysAllocString(bstrValue ? bstrValue : L""); + UINT value_len = bstrValue ? wcslen(bstrValue) : 0; + BSTR value = SysAllocStringLen(bstrValue, value_len); if(!value) return E_OUTOFMEMORY;
@@ -693,6 +711,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; } @@ -757,6 +781,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 += wcslen(session_entry->key) + 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/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=122044
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
This merge request was approved by Jacek Caban.