mshtml: Implement remainingSpace prop for sessionStorage.
[wine.git] / dlls / mshtml / htmlstorage.c
blobf4413ba2a3a0de09f7b44716d0b0be79f475ee27
1 /*
2 * Copyright 2012 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <stdarg.h>
21 #define COBJMACROS
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "ole2.h"
27 #include "shlobj.h"
29 #include "wine/debug.h"
31 #include "mshtml_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
35 /* Native defaults to 5 million chars per origin */
36 enum { MAX_QUOTA = 5000000 };
38 typedef struct {
39 DispatchEx dispex;
40 IHTMLStorage IHTMLStorage_iface;
41 LONG ref;
42 struct session_map_entry *session_storage;
43 WCHAR *filename;
44 HANDLE mutex;
45 } HTMLStorage;
47 struct session_map_entry {
48 struct wine_rb_entry entry;
49 struct wine_rb_tree data_map;
50 struct list data_list; /* for key() */
51 UINT ref;
52 UINT quota;
53 UINT num_keys;
54 UINT origin_len;
55 WCHAR origin[1];
58 int session_storage_map_cmp(const void *key, const struct wine_rb_entry *entry)
60 struct session_map_entry *p = WINE_RB_ENTRY_VALUE(entry, struct session_map_entry, entry);
61 UINT len = SysStringLen((BSTR)key);
63 return (len != p->origin_len) ? (len - p->origin_len) : memcmp(key, p->origin, len * sizeof(WCHAR));
66 struct session_entry
68 struct wine_rb_entry entry;
69 struct list list_entry;
70 BSTR value;
71 WCHAR key[1];
74 static int session_entry_cmp(const void *key, const struct wine_rb_entry *entry)
76 struct session_entry *data = WINE_RB_ENTRY_VALUE(entry, struct session_entry, entry);
77 return wcscmp(key, data->key);
80 static struct session_map_entry *grab_session_map_entry(BSTR origin)
82 struct session_map_entry *entry;
83 struct wine_rb_entry *rb_entry;
84 thread_data_t *thread_data;
85 UINT origin_len;
87 thread_data = get_thread_data(TRUE);
88 if(!thread_data)
89 return NULL;
91 rb_entry = wine_rb_get(&thread_data->session_storage_map, origin);
92 if(rb_entry) {
93 entry = WINE_RB_ENTRY_VALUE(rb_entry, struct session_map_entry, entry);
94 entry->ref++;
95 return entry;
98 origin_len = SysStringLen(origin);
99 entry = heap_alloc(FIELD_OFFSET(struct session_map_entry, origin[origin_len]));
100 if(!entry)
101 return NULL;
102 wine_rb_init(&entry->data_map, session_entry_cmp);
103 list_init(&entry->data_list);
104 entry->ref = 1;
105 entry->quota = MAX_QUOTA;
106 entry->num_keys = 0;
107 entry->origin_len = origin_len;
108 memcpy(entry->origin, origin, origin_len * sizeof(WCHAR));
110 wine_rb_put(&thread_data->session_storage_map, origin, &entry->entry);
111 return entry;
114 static void release_session_map_entry(struct session_map_entry *entry)
116 if(!entry || --entry->ref || entry->num_keys)
117 return;
119 wine_rb_remove(&get_thread_data(FALSE)->session_storage_map, &entry->entry);
120 heap_free(entry);
123 static HRESULT get_session_entry(struct session_map_entry *entry, const WCHAR *name, BOOL create, struct session_entry **ret)
125 const WCHAR *key = name ? name : L"";
126 struct wine_rb_entry *rb_entry;
127 struct session_entry *data;
128 UINT key_len;
130 rb_entry = wine_rb_get(&entry->data_map, key);
131 if(rb_entry) {
132 *ret = WINE_RB_ENTRY_VALUE(rb_entry, struct session_entry, entry);
133 return S_OK;
136 if(!create) {
137 *ret = NULL;
138 return S_OK;
141 key_len = wcslen(key);
142 if(entry->quota < key_len)
143 return E_OUTOFMEMORY; /* native returns this when quota is exceeded */
144 if(!(data = heap_alloc(FIELD_OFFSET(struct session_entry, key[key_len + 1]))))
145 return E_OUTOFMEMORY;
146 data->value = NULL;
147 memcpy(data->key, key, (key_len + 1) * sizeof(WCHAR));
149 entry->quota -= key_len;
150 entry->num_keys++;
151 list_add_tail(&entry->data_list, &data->list_entry);
152 wine_rb_put(&entry->data_map, key, &data->entry);
153 *ret = data;
154 return S_OK;
157 static void clear_session_storage(struct session_map_entry *entry)
159 struct session_entry *iter, *iter2;
161 LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &entry->data_list, struct session_entry, list_entry) {
162 SysFreeString(iter->value);
163 heap_free(iter);
165 wine_rb_destroy(&entry->data_map, NULL, NULL);
166 list_init(&entry->data_list);
167 entry->quota = MAX_QUOTA;
168 entry->num_keys = 0;
171 void destroy_session_storage(thread_data_t *thread_data)
173 struct session_map_entry *iter, *iter2;
175 WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter, iter2, &thread_data->session_storage_map, struct session_map_entry, entry) {
176 clear_session_storage(iter);
177 heap_free(iter);
181 static inline HTMLStorage *impl_from_IHTMLStorage(IHTMLStorage *iface)
183 return CONTAINING_RECORD(iface, HTMLStorage, IHTMLStorage_iface);
186 static HRESULT WINAPI HTMLStorage_QueryInterface(IHTMLStorage *iface, REFIID riid, void **ppv)
188 HTMLStorage *This = impl_from_IHTMLStorage(iface);
190 TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
192 if(IsEqualGUID(&IID_IUnknown, riid)) {
193 *ppv = &This->IHTMLStorage_iface;
194 }else if(IsEqualGUID(&IID_IHTMLStorage, riid)) {
195 *ppv = &This->IHTMLStorage_iface;
196 }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
197 return *ppv ? S_OK : E_NOINTERFACE;
198 }else {
199 *ppv = NULL;
200 WARN("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
201 return E_NOINTERFACE;
204 IUnknown_AddRef((IUnknown*)*ppv);
205 return S_OK;
208 static ULONG WINAPI HTMLStorage_AddRef(IHTMLStorage *iface)
210 HTMLStorage *This = impl_from_IHTMLStorage(iface);
211 LONG ref = InterlockedIncrement(&This->ref);
213 TRACE("(%p) ref=%ld\n", This, ref);
215 return ref;
218 static ULONG WINAPI HTMLStorage_Release(IHTMLStorage *iface)
220 HTMLStorage *This = impl_from_IHTMLStorage(iface);
221 LONG ref = InterlockedDecrement(&This->ref);
223 TRACE("(%p) ref=%ld\n", This, ref);
225 if(!ref) {
226 release_session_map_entry(This->session_storage);
227 release_dispex(&This->dispex);
228 heap_free(This->filename);
229 CloseHandle(This->mutex);
230 heap_free(This);
233 return ref;
236 static HRESULT WINAPI HTMLStorage_GetTypeInfoCount(IHTMLStorage *iface, UINT *pctinfo)
238 HTMLStorage *This = impl_from_IHTMLStorage(iface);
239 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
242 static HRESULT WINAPI HTMLStorage_GetTypeInfo(IHTMLStorage *iface, UINT iTInfo,
243 LCID lcid, ITypeInfo **ppTInfo)
245 HTMLStorage *This = impl_from_IHTMLStorage(iface);
247 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
250 static HRESULT WINAPI HTMLStorage_GetIDsOfNames(IHTMLStorage *iface, REFIID riid, LPOLESTR *rgszNames, UINT cNames,
251 LCID lcid, DISPID *rgDispId)
253 HTMLStorage *This = impl_from_IHTMLStorage(iface);
255 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid, rgszNames, cNames,
256 lcid, rgDispId);
259 static HRESULT WINAPI HTMLStorage_Invoke(IHTMLStorage *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
260 WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
262 HTMLStorage *This = impl_from_IHTMLStorage(iface);
264 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember, riid, lcid, wFlags,
265 pDispParams, pVarResult, pExcepInfo, puArgErr);
268 static BOOL create_path(const WCHAR *path)
270 BOOL ret = TRUE;
271 WCHAR *new_path;
272 int len;
274 new_path = heap_alloc((wcslen(path) + 1) * sizeof(WCHAR));
275 if(!new_path)
276 return FALSE;
277 wcscpy(new_path, path);
279 while((len = wcslen(new_path)) && new_path[len - 1] == '\\')
280 new_path[len - 1] = 0;
282 while(!CreateDirectoryW(new_path, NULL)) {
283 WCHAR *slash;
284 DWORD error = GetLastError();
285 if(error == ERROR_ALREADY_EXISTS) break;
286 if(error != ERROR_PATH_NOT_FOUND) {
287 ret = FALSE;
288 break;
290 slash = wcsrchr(new_path, '\\');
291 if(!slash) {
292 ret = FALSE;
293 break;
295 len = slash - new_path;
296 new_path[len] = 0;
297 if(!create_path(new_path)) {
298 ret = FALSE;
299 break;
301 new_path[len] = '\\';
303 heap_free(new_path);
304 return ret;
307 static HRESULT open_document(const WCHAR *filename, IXMLDOMDocument **ret)
309 IXMLDOMDocument *doc = NULL;
310 HRESULT hres = E_OUTOFMEMORY;
311 WCHAR *ptr, *path;
312 VARIANT var;
313 VARIANT_BOOL success;
315 path = heap_strdupW(filename);
316 if(!path)
317 return E_OUTOFMEMORY;
319 *(ptr = wcsrchr(path, '\\')) = 0;
320 if(!create_path(path))
321 goto done;
323 if(GetFileAttributesW(filename) == INVALID_FILE_ATTRIBUTES) {
324 DWORD count;
325 HANDLE file = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
326 if(file == INVALID_HANDLE_VALUE) {
327 hres = HRESULT_FROM_WIN32(GetLastError());
328 goto done;
330 if(!WriteFile(file, "<root/>", sizeof("<root/>") - 1, &count, NULL)) {
331 CloseHandle(file);
332 hres = HRESULT_FROM_WIN32(GetLastError());
333 goto done;
335 CloseHandle(file);
338 hres = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void**)&doc);
339 if(hres != S_OK)
340 goto done;
342 V_VT(&var) = VT_BSTR;
343 V_BSTR(&var) = SysAllocString(filename);
344 if(!V_BSTR(&var)) {
345 hres = E_OUTOFMEMORY;
346 goto done;
349 hres = IXMLDOMDocument_load(doc, var, &success);
350 if(hres == S_FALSE || success == VARIANT_FALSE)
351 hres = E_FAIL;
353 SysFreeString(V_BSTR(&var));
355 done:
356 heap_free(path);
357 if(hres == S_OK)
358 *ret = doc;
359 else if(doc)
360 IXMLDOMDocument_Release(doc);
362 return hres;
365 static HRESULT get_root_node(IXMLDOMDocument *doc, IXMLDOMNode **root)
367 HRESULT hres;
368 BSTR str;
370 str = SysAllocString(L"root");
371 if(!str)
372 return E_OUTOFMEMORY;
374 hres = IXMLDOMDocument_selectSingleNode(doc, str, root);
375 SysFreeString(str);
376 return hres;
379 static HRESULT get_node_list(const WCHAR *filename, IXMLDOMNodeList **node_list)
381 IXMLDOMDocument *doc;
382 IXMLDOMNode *root;
383 HRESULT hres;
384 BSTR query;
386 hres = open_document(filename, &doc);
387 if(hres != S_OK)
388 return hres;
390 hres = get_root_node(doc, &root);
391 IXMLDOMDocument_Release(doc);
392 if(hres != S_OK)
393 return hres;
395 if(!(query = SysAllocString(L"item")))
396 hres = E_OUTOFMEMORY;
397 else {
398 hres = IXMLDOMNode_selectNodes(root, query, node_list);
399 SysFreeString(query);
401 IXMLDOMNode_Release(root);
402 return hres;
405 static HRESULT WINAPI HTMLStorage_get_length(IHTMLStorage *iface, LONG *p)
407 HTMLStorage *This = impl_from_IHTMLStorage(iface);
408 IXMLDOMNodeList *node_list;
409 HRESULT hres;
411 TRACE("(%p)->(%p)\n", This, p);
413 if(!This->filename) {
414 *p = This->session_storage->num_keys;
415 return S_OK;
418 WaitForSingleObject(This->mutex, INFINITE);
419 hres = get_node_list(This->filename, &node_list);
420 if(SUCCEEDED(hres)) {
421 hres = IXMLDOMNodeList_get_length(node_list, p);
422 IXMLDOMNodeList_Release(node_list);
424 ReleaseMutex(This->mutex);
426 return hres;
429 static HRESULT WINAPI HTMLStorage_get_remainingSpace(IHTMLStorage *iface, LONG *p)
431 HTMLStorage *This = impl_from_IHTMLStorage(iface);
433 TRACE("(%p)->(%p)\n", This, p);
435 if(!This->filename) {
436 *p = This->session_storage->quota;
437 return S_OK;
440 FIXME("local storage not supported\n");
441 return E_NOTIMPL;
444 static HRESULT get_key(const WCHAR *filename, LONG index, BSTR *ret)
446 IXMLDOMNodeList *node_list;
447 IXMLDOMElement *elem;
448 IXMLDOMNode *node;
449 HRESULT hres;
450 VARIANT key;
452 hres = get_node_list(filename, &node_list);
453 if(FAILED(hres))
454 return hres;
456 hres = IXMLDOMNodeList_get_item(node_list, index, &node);
457 IXMLDOMNodeList_Release(node_list);
458 if(hres != S_OK)
459 return FAILED(hres) ? hres : E_INVALIDARG;
461 hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem);
462 IXMLDOMNode_Release(node);
463 if(hres != S_OK)
464 return E_INVALIDARG;
466 hres = IXMLDOMElement_getAttribute(elem, (BSTR)L"name", &key);
467 IXMLDOMElement_Release(elem);
468 if(FAILED(hres))
469 return hres;
471 if(V_VT(&key) != VT_BSTR) {
472 FIXME("non-string key %s\n", debugstr_variant(&key));
473 VariantClear(&key);
474 return E_NOTIMPL;
477 *ret = V_BSTR(&key);
478 return S_OK;
481 static HRESULT WINAPI HTMLStorage_key(IHTMLStorage *iface, LONG lIndex, BSTR *p)
483 HTMLStorage *This = impl_from_IHTMLStorage(iface);
484 struct session_entry *session_entry;
485 HRESULT hres;
487 TRACE("(%p)->(%ld %p)\n", This, lIndex, p);
489 if(!This->filename) {
490 struct list *entry = &This->session_storage->data_list;
491 unsigned i = 0;
493 if(lIndex >= This->session_storage->num_keys)
494 return E_INVALIDARG;
496 do entry = entry->next; while(i++ < lIndex);
497 session_entry = LIST_ENTRY(entry, struct session_entry, list_entry);
499 *p = SysAllocString(session_entry->key);
500 return *p ? S_OK : E_OUTOFMEMORY;
503 WaitForSingleObject(This->mutex, INFINITE);
504 hres = get_key(This->filename, lIndex, p);
505 ReleaseMutex(This->mutex);
507 return hres;
510 static BSTR build_query(const WCHAR *key)
512 static const WCHAR fmt[] = L"item[@name='%s']";
513 const WCHAR *str = key ? key : L"";
514 UINT len = ARRAY_SIZE(fmt) + wcslen(str);
515 BSTR ret = SysAllocStringLen(NULL, len);
517 if(ret) swprintf(ret, len, fmt, str);
518 return ret;
521 static HRESULT get_item(const WCHAR *filename, BSTR key, VARIANT *value)
523 IXMLDOMDocument *doc;
524 BSTR query = NULL;
525 IXMLDOMNode *root = NULL, *node = NULL;
526 IXMLDOMElement *elem = NULL;
527 HRESULT hres;
529 hres = open_document(filename, &doc);
530 if(hres != S_OK)
531 return hres;
533 hres = get_root_node(doc, &root);
534 if(hres != S_OK)
535 goto done;
537 query = build_query(key);
538 if(!query) {
539 hres = E_OUTOFMEMORY;
540 goto done;
543 hres = IXMLDOMNode_selectSingleNode(root, query, &node);
544 if(hres == S_OK) {
545 hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem);
546 if(hres != S_OK)
547 goto done;
549 hres = IXMLDOMElement_getAttribute(elem, (BSTR)L"value", value);
550 }else {
551 V_VT(value) = VT_NULL;
552 hres = S_OK;
555 done:
556 SysFreeString(query);
557 if(root)
558 IXMLDOMNode_Release(root);
559 if(node)
560 IXMLDOMNode_Release(node);
561 if(elem)
562 IXMLDOMElement_Release(elem);
563 IXMLDOMDocument_Release(doc);
564 return hres;
567 static HRESULT WINAPI HTMLStorage_getItem(IHTMLStorage *iface, BSTR bstrKey, VARIANT *value)
569 HTMLStorage *This = impl_from_IHTMLStorage(iface);
570 struct session_entry *session_entry;
571 HRESULT hres;
573 TRACE("(%p)->(%s %p)\n", This, debugstr_w(bstrKey), value);
575 if(!value)
576 return E_POINTER;
578 if(!This->filename) {
579 hres = get_session_entry(This->session_storage, bstrKey, FALSE, &session_entry);
580 if(SUCCEEDED(hres)) {
581 if(!session_entry || !session_entry->value)
582 V_VT(value) = VT_NULL;
583 else {
584 V_VT(value) = VT_BSTR;
585 V_BSTR(value) = SysAllocStringLen(session_entry->value, SysStringLen(session_entry->value));
586 hres = V_BSTR(value) ? S_OK : E_OUTOFMEMORY;
589 return hres;
592 WaitForSingleObject(This->mutex, INFINITE);
593 hres = get_item(This->filename, bstrKey, value);
594 ReleaseMutex(This->mutex);
596 return hres;
599 static HRESULT set_attribute(IXMLDOMElement *elem, const WCHAR *name, BSTR value)
601 BSTR str;
602 VARIANT var;
603 HRESULT hres;
605 str = SysAllocString(name);
606 if(!str)
607 return E_OUTOFMEMORY;
608 V_VT(&var) = VT_BSTR;
609 V_BSTR(&var) = value;
611 hres = IXMLDOMElement_setAttribute(elem, str, var);
612 SysFreeString(str);
613 return hres;
616 static HRESULT save_document(IXMLDOMDocument *doc, const WCHAR *filename)
618 VARIANT var;
619 HRESULT hres;
621 V_VT(&var) = VT_BSTR;
622 V_BSTR(&var) = SysAllocString(filename);
623 if(!V_BSTR(&var))
624 return E_OUTOFMEMORY;
626 hres = IXMLDOMDocument_save(doc, var);
627 SysFreeString(V_BSTR(&var));
628 return hres;
631 static HRESULT set_item(const WCHAR *filename, BSTR key, BSTR value)
633 IXMLDOMDocument *doc;
634 IXMLDOMNode *root = NULL, *node = NULL;
635 IXMLDOMElement *elem = NULL;
636 BSTR query = NULL;
637 HRESULT hres;
639 hres = open_document(filename, &doc);
640 if(hres != S_OK)
641 return hres;
643 hres = get_root_node(doc, &root);
644 if(hres != S_OK)
645 goto done;
647 query = build_query(key);
648 if(!query) {
649 hres = E_OUTOFMEMORY;
650 goto done;
653 hres = IXMLDOMNode_selectSingleNode(root, query, &node);
654 if(hres == S_OK) {
655 hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem);
656 if(hres != S_OK)
657 goto done;
659 hres = set_attribute(elem, L"value", value);
660 if(hres != S_OK)
661 goto done;
662 }else {
663 BSTR str = SysAllocString(L"item");
664 hres = IXMLDOMDocument_createElement(doc, str, &elem);
665 SysFreeString(str);
666 if(hres != S_OK)
667 goto done;
669 hres = set_attribute(elem, L"name", key);
670 if(hres != S_OK)
671 goto done;
673 hres = set_attribute(elem, L"value", value);
674 if(hres != S_OK)
675 goto done;
677 hres = IXMLDOMNode_appendChild(root, (IXMLDOMNode*)elem, NULL);
678 if(hres != S_OK)
679 goto done;
682 hres = save_document(doc, filename);
684 done:
685 SysFreeString(query);
686 if(root)
687 IXMLDOMNode_Release(root);
688 if(node)
689 IXMLDOMNode_Release(node);
690 if(elem)
691 IXMLDOMElement_Release(elem);
692 IXMLDOMDocument_Release(doc);
693 return hres;
696 static HRESULT WINAPI HTMLStorage_setItem(IHTMLStorage *iface, BSTR bstrKey, BSTR bstrValue)
698 HTMLStorage *This = impl_from_IHTMLStorage(iface);
699 struct session_entry *session_entry;
700 HRESULT hres;
702 TRACE("(%p)->(%s %s)\n", This, debugstr_w(bstrKey), debugstr_w(bstrValue));
704 if(!This->filename) {
705 UINT value_len = bstrValue ? wcslen(bstrValue) : 0;
706 BSTR value = SysAllocStringLen(bstrValue, value_len);
707 if(!value)
708 return E_OUTOFMEMORY;
710 hres = get_session_entry(This->session_storage, bstrKey, TRUE, &session_entry);
711 if(FAILED(hres))
712 SysFreeString(value);
713 else {
714 UINT old_len = SysStringLen(session_entry->value);
715 if(old_len < value_len && This->session_storage->quota < value_len - old_len) {
716 SysFreeString(value);
717 return E_OUTOFMEMORY; /* native returns this when quota is exceeded */
719 This->session_storage->quota -= value_len - old_len;
720 SysFreeString(session_entry->value);
721 session_entry->value = value;
723 return hres;
726 WaitForSingleObject(This->mutex, INFINITE);
727 hres = set_item(This->filename, bstrKey, bstrValue);
728 ReleaseMutex(This->mutex);
730 return hres;
733 static HRESULT remove_item(const WCHAR *filename, BSTR key)
735 IXMLDOMDocument *doc;
736 IXMLDOMNode *root = NULL, *node = NULL;
737 BSTR query = NULL;
738 HRESULT hres;
740 hres = open_document(filename, &doc);
741 if(hres != S_OK)
742 return hres;
744 hres = get_root_node(doc, &root);
745 if(hres != S_OK)
746 goto done;
748 query = build_query(key);
749 if(!query) {
750 hres = E_OUTOFMEMORY;
751 goto done;
754 hres = IXMLDOMNode_selectSingleNode(root, query, &node);
755 if(hres == S_OK) {
756 hres = IXMLDOMNode_removeChild(root, node, NULL);
757 if(hres != S_OK)
758 goto done;
761 hres = save_document(doc, filename);
763 done:
764 SysFreeString(query);
765 if(root)
766 IXMLDOMNode_Release(root);
767 if(node)
768 IXMLDOMNode_Release(node);
769 IXMLDOMDocument_Release(doc);
770 return hres;
773 static HRESULT WINAPI HTMLStorage_removeItem(IHTMLStorage *iface, BSTR bstrKey)
775 HTMLStorage *This = impl_from_IHTMLStorage(iface);
776 struct session_entry *session_entry;
777 HRESULT hres;
779 TRACE("(%p)->(%s)\n", This, debugstr_w(bstrKey));
781 if(!This->filename) {
782 hres = get_session_entry(This->session_storage, bstrKey, FALSE, &session_entry);
783 if(SUCCEEDED(hres) && session_entry) {
784 This->session_storage->quota += wcslen(session_entry->key) + SysStringLen(session_entry->value);
785 This->session_storage->num_keys--;
786 list_remove(&session_entry->list_entry);
787 wine_rb_remove(&This->session_storage->data_map, &session_entry->entry);
788 SysFreeString(session_entry->value);
789 heap_free(session_entry);
791 return hres;
794 WaitForSingleObject(This->mutex, INFINITE);
795 hres = remove_item(This->filename, bstrKey);
796 ReleaseMutex(This->mutex);
798 return hres;
801 static HRESULT WINAPI HTMLStorage_clear(IHTMLStorage *iface)
803 HTMLStorage *This = impl_from_IHTMLStorage(iface);
804 HRESULT hres = S_OK;
806 if(!This->filename) {
807 clear_session_storage(This->session_storage);
808 return S_OK;
811 WaitForSingleObject(This->mutex, INFINITE);
812 if(!DeleteFileW(This->filename)) {
813 DWORD error = GetLastError();
814 if(error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND)
815 hres = HRESULT_FROM_WIN32(error);
817 ReleaseMutex(This->mutex);
818 return hres;
821 static const IHTMLStorageVtbl HTMLStorageVtbl = {
822 HTMLStorage_QueryInterface,
823 HTMLStorage_AddRef,
824 HTMLStorage_Release,
825 HTMLStorage_GetTypeInfoCount,
826 HTMLStorage_GetTypeInfo,
827 HTMLStorage_GetIDsOfNames,
828 HTMLStorage_Invoke,
829 HTMLStorage_get_length,
830 HTMLStorage_get_remainingSpace,
831 HTMLStorage_key,
832 HTMLStorage_getItem,
833 HTMLStorage_setItem,
834 HTMLStorage_removeItem,
835 HTMLStorage_clear
838 static const tid_t HTMLStorage_iface_tids[] = {
839 IHTMLStorage_tid,
842 static dispex_static_data_t HTMLStorage_dispex = {
843 L"Storage",
844 NULL,
845 IHTMLStorage_tid,
846 HTMLStorage_iface_tids
849 static HRESULT build_session_origin(IUri *uri, BSTR hostname, BSTR *ret)
851 UINT host_len, scheme_len;
852 BSTR scheme, origin;
853 HRESULT hres;
855 hres = IUri_GetSchemeName(uri, &scheme);
856 if(FAILED(hres))
857 return hres;
858 if(hres != S_OK) {
859 SysFreeString(scheme);
860 scheme = NULL;
863 /* Since it's only used for lookup, we can apply transformations to
864 keep the lookup itself simple and fast. First, we convert `https`
865 to `http` because they are equal for lookup. Next, we place the
866 scheme after the hostname, separated by NUL, to compare the host
867 first, since it tends to differ more often. Lastly, we lowercase
868 the whole thing since lookup must be case-insensitive. */
869 scheme_len = SysStringLen(scheme);
870 host_len = SysStringLen(hostname);
872 if(scheme_len == 5 && !wcsicmp(scheme, L"https"))
873 scheme_len--;
875 origin = SysAllocStringLen(NULL, host_len + 1 + scheme_len);
876 if(origin) {
877 WCHAR *p = origin;
878 memcpy(p, hostname, host_len * sizeof(WCHAR));
879 p += host_len;
880 *p = ' '; /* for wcslwr */
881 memcpy(p + 1, scheme, scheme_len * sizeof(WCHAR));
882 p[1 + scheme_len] = '\0';
883 _wcslwr(origin);
884 *p = '\0';
886 SysFreeString(scheme);
888 if(!origin)
889 return E_OUTOFMEMORY;
891 *ret = origin;
892 return S_OK;
895 static WCHAR *build_filename(BSTR hostname)
897 static const WCHAR store[] = L"\\Microsoft\\Internet Explorer\\DOMStore\\";
898 WCHAR path[MAX_PATH], *ret;
899 int len;
901 if(!SHGetSpecialFolderPathW(NULL, path, CSIDL_LOCAL_APPDATA, TRUE)) {
902 ERR("Can't get folder path %lu\n", GetLastError());
903 return NULL;
906 len = wcslen(path);
907 if(len + ARRAY_SIZE(store) > ARRAY_SIZE(path)) {
908 ERR("Path too long\n");
909 return NULL;
911 memcpy(path + len, store, sizeof(store));
913 len += ARRAY_SIZE(store);
914 ret = heap_alloc((len + wcslen(hostname) + ARRAY_SIZE(L".xml")) * sizeof(WCHAR));
915 if(!ret) {
916 return NULL;
919 wcscpy(ret, path);
920 wcscat(ret, hostname);
921 wcscat(ret, L".xml");
923 return ret;
926 static WCHAR *build_mutexname(const WCHAR *filename)
928 WCHAR *ret, *ptr;
929 ret = heap_strdupW(filename);
930 if(!ret)
931 return NULL;
932 for(ptr = ret; *ptr; ptr++)
933 if(*ptr == '\\') *ptr = '_';
934 return ret;
937 HRESULT create_html_storage(HTMLInnerWindow *window, BOOL local, IHTMLStorage **p)
939 IUri *uri = window->base.outer_window->uri;
940 BSTR origin, hostname = NULL;
941 HTMLStorage *storage;
942 HRESULT hres;
944 if(!uri)
945 return S_FALSE;
947 hres = IUri_GetHost(uri, &hostname);
948 if(hres != S_OK) {
949 SysFreeString(hostname);
950 return hres;
953 storage = heap_alloc_zero(sizeof(*storage));
954 if(!storage) {
955 SysFreeString(hostname);
956 return E_OUTOFMEMORY;
959 if(local) {
960 WCHAR *mutexname;
961 storage->filename = build_filename(hostname);
962 SysFreeString(hostname);
963 if(!storage->filename) {
964 heap_free(storage);
965 return E_OUTOFMEMORY;
967 mutexname = build_mutexname(storage->filename);
968 if(!mutexname) {
969 heap_free(storage->filename);
970 heap_free(storage);
971 return E_OUTOFMEMORY;
973 storage->mutex = CreateMutexW(NULL, FALSE, mutexname);
974 heap_free(mutexname);
975 if(!storage->mutex) {
976 heap_free(storage->filename);
977 heap_free(storage);
978 return HRESULT_FROM_WIN32(GetLastError());
980 }else {
981 hres = build_session_origin(uri, hostname, &origin);
982 SysFreeString(hostname);
983 if(hres != S_OK) {
984 heap_free(storage);
985 return hres;
987 storage->session_storage = grab_session_map_entry(origin);
988 SysFreeString(origin);
989 if(!storage->session_storage) {
990 heap_free(storage);
991 return E_OUTOFMEMORY;
995 storage->IHTMLStorage_iface.lpVtbl = &HTMLStorageVtbl;
996 storage->ref = 1;
997 init_dispatch(&storage->dispex, (IUnknown*)&storage->IHTMLStorage_iface, &HTMLStorage_dispex,
998 dispex_compat_mode(&window->event_target.dispex));
1000 *p = &storage->IHTMLStorage_iface;
1001 return S_OK;