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)