v2: Fix test failure.
Signed-off-by: Hans Leidekker hans@codeweavers.com --- dlls/mshtml/htmlstorage.c | 245 ++++++++++++++++++++++++++++++++++- dlls/mshtml/htmlwindow.c | 4 +- dlls/mshtml/mshtml_private.h | 2 +- 3 files changed, 243 insertions(+), 8 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index 95f7ae5cd16..fe1d6a21b88 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -24,6 +24,7 @@ #include "winbase.h" #include "winuser.h" #include "ole2.h" +#include "shlobj.h"
#include "wine/debug.h"
@@ -35,6 +36,7 @@ typedef struct { DispatchEx dispex; IHTMLStorage IHTMLStorage_iface; LONG ref; + HTMLInnerWindow *window; } HTMLStorage;
static inline HTMLStorage *impl_from_IHTMLStorage(IHTMLStorage *iface) @@ -121,6 +123,151 @@ static HRESULT WINAPI HTMLStorage_Invoke(IHTMLStorage *iface, DISPID dispIdMembe pDispParams, pVarResult, pExcepInfo, puArgErr); }
+static BOOL create_path(const WCHAR *path) +{ + BOOL ret = TRUE; + WCHAR *new_path; + int len; + + new_path = malloc((wcslen(path) + 1) * sizeof(WCHAR)); + if(!new_path) + return FALSE; + wcscpy(new_path, path); + + while((len = wcslen(new_path)) && new_path[len - 1] == '\') + new_path[len - 1] = 0; + + while(!CreateDirectoryW(new_path, NULL)) { + WCHAR *slash; + DWORD error = GetLastError(); + if(error == ERROR_ALREADY_EXISTS) break; + if(error != ERROR_PATH_NOT_FOUND) { + ret = FALSE; + break; + } + slash = wcsrchr(new_path, '\'); + if(!slash) { + ret = FALSE; + break; + } + len = slash - new_path; + new_path[len] = 0; + if(!create_path(new_path)) { + ret = FALSE; + break; + } + new_path[len] = '\'; + } + free(new_path); + return ret; +} + +static WCHAR *build_filename(const WCHAR *hostname) +{ + static const WCHAR store[] = L"\Microsoft\Internet Explorer\DOMStore\"; + WCHAR path[MAX_PATH], *ret; + int len; + + if(!SHGetSpecialFolderPathW(NULL, path, CSIDL_LOCAL_APPDATA, TRUE)) { + ERR("Can't get folder path %lu\n", GetLastError()); + return NULL; + } + + len = wcslen(path); + if(len + ARRAY_SIZE(store) > ARRAY_SIZE(path)) { + ERR("Path too long\n"); + return NULL; + } + memcpy(path + len, store, sizeof(store)); + + len += ARRAY_SIZE(store); + ret = malloc((len + wcslen(hostname) + ARRAY_SIZE(L".xml")) * sizeof(WCHAR)); + if(!ret) + return NULL; + + wcscpy(ret, path); + wcscat(ret, hostname); + wcscat(ret, L".xml"); + return ret; +} + +struct storage { + WCHAR *filename; + IXMLDOMDocument *doc; +}; + +static void close_storage(struct storage *storage) +{ + free(storage->filename); + if(storage->doc) + IXMLDOMDocument_Release(storage->doc); + free(storage); +} + +static HRESULT open_storage(const WCHAR *hostname, struct storage **ret) +{ + struct storage *storage; + HRESULT hres = E_OUTOFMEMORY; + VARIANT var; + VARIANT_BOOL success; + WCHAR *ptr; + + storage = calloc(1, sizeof(*storage)); + if(!storage) + return E_OUTOFMEMORY; + + storage->filename = build_filename(hostname); + if(!storage->filename) + goto done; + + *(ptr = wcsrchr(storage->filename, '\')) = 0; + if(!create_path(storage->filename)) { + hres = E_FAIL; + goto done; + } + *ptr = '\'; + + if(GetFileAttributesW(storage->filename) == INVALID_FILE_ATTRIBUTES) { + DWORD count; + HANDLE file = CreateFileW(storage->filename, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); + if(file == INVALID_HANDLE_VALUE) { + hres = HRESULT_FROM_WIN32(GetLastError()); + goto done; + } + if(!WriteFile(file, "<root/>", sizeof("<root/>") - 1, &count, NULL)) { + CloseHandle(file); + hres = HRESULT_FROM_WIN32(GetLastError()); + goto done; + } + CloseHandle(file); + } + + hres = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void**)&storage->doc); + if(hres != S_OK) + goto done; + + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = SysAllocString(storage->filename); + if(!V_BSTR(&var)) { + hres = E_OUTOFMEMORY; + goto done; + } + + hres = IXMLDOMDocument_load(storage->doc, var, &success); + if(hres == S_FALSE || success == VARIANT_FALSE) + hres = E_FAIL; + + SysFreeString(V_BSTR(&var)); + +done: + if(hres != S_OK) + close_storage(storage); + else + *ret = storage; + + return hres; +} + static HRESULT WINAPI HTMLStorage_get_length(IHTMLStorage *iface, LONG *p) { HTMLStorage *This = impl_from_IHTMLStorage(iface); @@ -142,14 +289,101 @@ static HRESULT WINAPI HTMLStorage_key(IHTMLStorage *iface, LONG lIndex, BSTR *p) return E_NOTIMPL; }
-static HRESULT WINAPI HTMLStorage_getItem(IHTMLStorage *iface, BSTR bstrKey, VARIANT *p) +static BSTR build_query(const WCHAR *key) +{ + static const WCHAR fmt[] = L"item[@name='%s']"; + const WCHAR *str = key ? key : L""; + UINT len = ARRAY_SIZE(fmt) + wcslen(str); + BSTR ret = SysAllocStringLen(NULL, len); + + if(ret) swprintf(ret, len, fmt, str); + 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(BSTR hostname, BSTR key, VARIANT *value) +{ + struct storage *storage; + BSTR query = NULL; + IXMLDOMNode *root = NULL, *node = NULL; + IXMLDOMElement *elem = NULL; + HRESULT hres; + + hres = open_storage(hostname, &storage); + if(hres != S_OK) + return hres; + + hres = get_root_node(storage->doc, &root); + if(hres != S_OK) + goto done; + + query = build_query(key); + if(!query) { + hres = E_OUTOFMEMORY; + goto done; + } + + hres = IXMLDOMNode_selectSingleNode(root, query, &node); + if(hres == S_OK) { + hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem); + if(hres != S_OK) + goto done; + + hres = IXMLDOMElement_getAttribute(elem, (BSTR)L"value", value); + }else { + V_VT(value) = VT_NULL; + hres = S_OK; + } + +done: + SysFreeString(query); + if(root) + IXMLDOMNode_Release(root); + if(node) + IXMLDOMNode_Release(node); + if(elem) + IXMLDOMElement_Release(elem); + close_storage(storage); + return hres; +} + +static HRESULT WINAPI HTMLStorage_getItem(IHTMLStorage *iface, BSTR bstrKey, VARIANT *value) { HTMLStorage *This = impl_from_IHTMLStorage(iface); + BSTR hostname; + HRESULT hres;
- FIXME("(%p)->(%s %p)\n", This, debugstr_w(bstrKey), p); + TRACE("(%p)->(%s %p)\n", This, debugstr_w(bstrKey), value);
- V_VT(p) = VT_NULL; - return S_OK; + if(!value) + return E_POINTER; + + if(!This->window) { + FIXME("session storage not supported\n"); + V_VT(value) = VT_NULL; + return S_OK; + } + + hres = IUri_GetHost(This->window->base.outer_window->uri, &hostname); + if(hres != S_OK) + return hres; + + hres = get_item(hostname, bstrKey, value); + SysFreeString(hostname); + return hres; }
static HRESULT WINAPI HTMLStorage_setItem(IHTMLStorage *iface, BSTR bstrKey, BSTR bstrValue) @@ -201,7 +435,7 @@ static dispex_static_data_t HTMLStorage_dispex = { HTMLStorage_iface_tids };
-HRESULT create_html_storage(compat_mode_t compat_mode, IHTMLStorage **p) +HRESULT create_html_storage(compat_mode_t compat_mode, HTMLInnerWindow *window, IHTMLStorage **p) { HTMLStorage *storage;
@@ -211,6 +445,7 @@ HRESULT create_html_storage(compat_mode_t compat_mode, IHTMLStorage **p)
storage->IHTMLStorage_iface.lpVtbl = &HTMLStorageVtbl; storage->ref = 1; + storage->window = window; init_dispatch(&storage->dispex, (IUnknown*)&storage->IHTMLStorage_iface, &HTMLStorage_dispex, compat_mode);
*p = &storage->IHTMLStorage_iface; diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index ecd530db7e2..57388ae64a2 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -2143,7 +2143,7 @@ static HRESULT WINAPI HTMLWindow6_get_sessionStorage(IHTMLWindow6 *iface, IHTMLS HRESULT hres;
hres = create_html_storage(dispex_compat_mode(&This->inner_window->event_target.dispex), - &This->inner_window->session_storage); + NULL, &This->inner_window->session_storage); if(FAILED(hres)) return hres; } @@ -2163,7 +2163,7 @@ static HRESULT WINAPI HTMLWindow6_get_localStorage(IHTMLWindow6 *iface, IHTMLSto HRESULT hres;
hres = create_html_storage(dispex_compat_mode(&This->inner_window->event_target.dispex), - &This->inner_window->local_storage); + This->inner_window, &This->inner_window->local_storage); if(FAILED(hres)) return hres; } diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index a94f1be1969..0fed06b8aae 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -941,7 +941,7 @@ HRESULT create_namespace_collection(compat_mode_t,IHTMLNamespaceCollection**) DE HRESULT create_dom_implementation(HTMLDocumentNode*,IHTMLDOMImplementation**) DECLSPEC_HIDDEN; void detach_dom_implementation(IHTMLDOMImplementation*) DECLSPEC_HIDDEN;
-HRESULT create_html_storage(compat_mode_t,IHTMLStorage**) DECLSPEC_HIDDEN; +HRESULT create_html_storage(compat_mode_t,HTMLInnerWindow*,IHTMLStorage**) DECLSPEC_HIDDEN;
void HTMLDocument_Persist_Init(HTMLDocument*) DECLSPEC_HIDDEN; void HTMLDocument_OleCmd_Init(HTMLDocument*) DECLSPEC_HIDDEN;
Hi Hans,
On 5/23/22 10:01, Hans Leidekker wrote:
@@ -201,7 +435,7 @@ static dispex_static_data_t HTMLStorage_dispex = { HTMLStorage_iface_tids };
-HRESULT create_html_storage(compat_mode_t compat_mode, IHTMLStorage **p) +HRESULT create_html_storage(compat_mode_t compat_mode, HTMLInnerWindow *window, IHTMLStorage **p) { HTMLStorage *storage;
@@ -211,6 +445,7 @@ HRESULT create_html_storage(compat_mode_t compat_mode, IHTMLStorage **p)
storage->IHTMLStorage_iface.lpVtbl = &HTMLStorageVtbl; storage->ref = 1;
- storage->window = window;
This does not seem right, HTMLStorage object may potentially live longer than window object. To make it a proper weak reference, you'd need to invalidate the reference in window object destructor. However, note that host of inner window does not change (if outer window's URL changes, a new inner window is created), so it would be probably more convenient to just compute file path in create_html_store and store just that.
Thanks,
Jacek