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
29 #include "wine/debug.h"
31 #include "mshtml_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(mshtml
);
37 IHTMLStorage IHTMLStorage_iface
;
39 struct session_map_entry
*session_storage
;
44 struct session_map_entry
{
45 struct wine_rb_entry entry
;
46 struct wine_rb_tree data_map
;
53 int session_storage_map_cmp(const void *key
, const struct wine_rb_entry
*entry
)
55 struct session_map_entry
*p
= WINE_RB_ENTRY_VALUE(entry
, struct session_map_entry
, entry
);
56 UINT len
= SysStringLen((BSTR
)key
);
58 return (len
!= p
->origin_len
) ? (len
- p
->origin_len
) : memcmp(key
, p
->origin
, len
* sizeof(WCHAR
));
63 struct wine_rb_entry entry
;
68 static int session_entry_cmp(const void *key
, const struct wine_rb_entry
*entry
)
70 struct session_entry
*data
= WINE_RB_ENTRY_VALUE(entry
, struct session_entry
, entry
);
71 return wcscmp(key
, data
->key
);
74 static struct session_map_entry
*grab_session_map_entry(BSTR origin
)
76 struct session_map_entry
*entry
;
77 struct wine_rb_entry
*rb_entry
;
78 thread_data_t
*thread_data
;
81 thread_data
= get_thread_data(TRUE
);
85 rb_entry
= wine_rb_get(&thread_data
->session_storage_map
, origin
);
87 entry
= WINE_RB_ENTRY_VALUE(rb_entry
, struct session_map_entry
, entry
);
92 origin_len
= SysStringLen(origin
);
93 entry
= heap_alloc(FIELD_OFFSET(struct session_map_entry
, origin
[origin_len
]));
96 wine_rb_init(&entry
->data_map
, session_entry_cmp
);
99 entry
->origin_len
= origin_len
;
100 memcpy(entry
->origin
, origin
, origin_len
* sizeof(WCHAR
));
102 wine_rb_put(&thread_data
->session_storage_map
, origin
, &entry
->entry
);
106 static void release_session_map_entry(struct session_map_entry
*entry
)
108 if(!entry
|| --entry
->ref
|| entry
->num_keys
)
111 wine_rb_remove(&get_thread_data(FALSE
)->session_storage_map
, &entry
->entry
);
115 static HRESULT
get_session_entry(struct session_map_entry
*entry
, const WCHAR
*name
, BOOL create
, struct session_entry
**ret
)
117 const WCHAR
*key
= name
? name
: L
"";
118 struct wine_rb_entry
*rb_entry
;
119 struct session_entry
*data
;
122 rb_entry
= wine_rb_get(&entry
->data_map
, key
);
124 *ret
= WINE_RB_ENTRY_VALUE(rb_entry
, struct session_entry
, entry
);
133 key_len
= wcslen(key
);
134 if(!(data
= heap_alloc(FIELD_OFFSET(struct session_entry
, key
[key_len
+ 1]))))
135 return E_OUTOFMEMORY
;
137 memcpy(data
->key
, key
, (key_len
+ 1) * sizeof(WCHAR
));
140 wine_rb_put(&entry
->data_map
, key
, &data
->entry
);
145 static void clear_session_storage(struct session_map_entry
*entry
)
147 struct session_entry
*iter
, *iter2
;
149 WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter
, iter2
, &entry
->data_map
, struct session_entry
, entry
) {
150 SysFreeString(iter
->value
);
153 wine_rb_destroy(&entry
->data_map
, NULL
, NULL
);
157 void destroy_session_storage(thread_data_t
*thread_data
)
159 struct session_map_entry
*iter
, *iter2
;
161 WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter
, iter2
, &thread_data
->session_storage_map
, struct session_map_entry
, entry
) {
162 clear_session_storage(iter
);
167 static inline HTMLStorage
*impl_from_IHTMLStorage(IHTMLStorage
*iface
)
169 return CONTAINING_RECORD(iface
, HTMLStorage
, IHTMLStorage_iface
);
172 static HRESULT WINAPI
HTMLStorage_QueryInterface(IHTMLStorage
*iface
, REFIID riid
, void **ppv
)
174 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
176 TRACE("(%p)->(%s %p)\n", This
, debugstr_mshtml_guid(riid
), ppv
);
178 if(IsEqualGUID(&IID_IUnknown
, riid
)) {
179 *ppv
= &This
->IHTMLStorage_iface
;
180 }else if(IsEqualGUID(&IID_IHTMLStorage
, riid
)) {
181 *ppv
= &This
->IHTMLStorage_iface
;
182 }else if(dispex_query_interface(&This
->dispex
, riid
, ppv
)) {
183 return *ppv
? S_OK
: E_NOINTERFACE
;
186 WARN("(%p)->(%s %p)\n", This
, debugstr_mshtml_guid(riid
), ppv
);
187 return E_NOINTERFACE
;
190 IUnknown_AddRef((IUnknown
*)*ppv
);
194 static ULONG WINAPI
HTMLStorage_AddRef(IHTMLStorage
*iface
)
196 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
197 LONG ref
= InterlockedIncrement(&This
->ref
);
199 TRACE("(%p) ref=%ld\n", This
, ref
);
204 static ULONG WINAPI
HTMLStorage_Release(IHTMLStorage
*iface
)
206 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
207 LONG ref
= InterlockedDecrement(&This
->ref
);
209 TRACE("(%p) ref=%ld\n", This
, ref
);
212 release_session_map_entry(This
->session_storage
);
213 release_dispex(&This
->dispex
);
214 heap_free(This
->filename
);
215 CloseHandle(This
->mutex
);
222 static HRESULT WINAPI
HTMLStorage_GetTypeInfoCount(IHTMLStorage
*iface
, UINT
*pctinfo
)
224 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
225 return IDispatchEx_GetTypeInfoCount(&This
->dispex
.IDispatchEx_iface
, pctinfo
);
228 static HRESULT WINAPI
HTMLStorage_GetTypeInfo(IHTMLStorage
*iface
, UINT iTInfo
,
229 LCID lcid
, ITypeInfo
**ppTInfo
)
231 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
233 return IDispatchEx_GetTypeInfo(&This
->dispex
.IDispatchEx_iface
, iTInfo
, lcid
, ppTInfo
);
236 static HRESULT WINAPI
HTMLStorage_GetIDsOfNames(IHTMLStorage
*iface
, REFIID riid
, LPOLESTR
*rgszNames
, UINT cNames
,
237 LCID lcid
, DISPID
*rgDispId
)
239 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
241 return IDispatchEx_GetIDsOfNames(&This
->dispex
.IDispatchEx_iface
, riid
, rgszNames
, cNames
,
245 static HRESULT WINAPI
HTMLStorage_Invoke(IHTMLStorage
*iface
, DISPID dispIdMember
, REFIID riid
, LCID lcid
,
246 WORD wFlags
, DISPPARAMS
*pDispParams
, VARIANT
*pVarResult
, EXCEPINFO
*pExcepInfo
, UINT
*puArgErr
)
248 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
250 return IDispatchEx_Invoke(&This
->dispex
.IDispatchEx_iface
, dispIdMember
, riid
, lcid
, wFlags
,
251 pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
254 static BOOL
create_path(const WCHAR
*path
)
260 new_path
= heap_alloc((wcslen(path
) + 1) * sizeof(WCHAR
));
263 wcscpy(new_path
, path
);
265 while((len
= wcslen(new_path
)) && new_path
[len
- 1] == '\\')
266 new_path
[len
- 1] = 0;
268 while(!CreateDirectoryW(new_path
, NULL
)) {
270 DWORD error
= GetLastError();
271 if(error
== ERROR_ALREADY_EXISTS
) break;
272 if(error
!= ERROR_PATH_NOT_FOUND
) {
276 slash
= wcsrchr(new_path
, '\\');
281 len
= slash
- new_path
;
283 if(!create_path(new_path
)) {
287 new_path
[len
] = '\\';
293 static HRESULT
open_document(const WCHAR
*filename
, IXMLDOMDocument
**ret
)
295 IXMLDOMDocument
*doc
= NULL
;
296 HRESULT hres
= E_OUTOFMEMORY
;
299 VARIANT_BOOL success
;
301 path
= heap_strdupW(filename
);
303 return E_OUTOFMEMORY
;
305 *(ptr
= wcsrchr(path
, '\\')) = 0;
306 if(!create_path(path
))
309 if(GetFileAttributesW(filename
) == INVALID_FILE_ATTRIBUTES
) {
311 HANDLE file
= CreateFileW(filename
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, 0, NULL
);
312 if(file
== INVALID_HANDLE_VALUE
) {
313 hres
= HRESULT_FROM_WIN32(GetLastError());
316 if(!WriteFile(file
, "<root/>", sizeof("<root/>") - 1, &count
, NULL
)) {
318 hres
= HRESULT_FROM_WIN32(GetLastError());
324 hres
= CoCreateInstance(&CLSID_DOMDocument
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IXMLDOMDocument
, (void**)&doc
);
328 V_VT(&var
) = VT_BSTR
;
329 V_BSTR(&var
) = SysAllocString(filename
);
331 hres
= E_OUTOFMEMORY
;
335 hres
= IXMLDOMDocument_load(doc
, var
, &success
);
336 if(hres
== S_FALSE
|| success
== VARIANT_FALSE
)
339 SysFreeString(V_BSTR(&var
));
346 IXMLDOMDocument_Release(doc
);
351 static HRESULT WINAPI
HTMLStorage_get_length(IHTMLStorage
*iface
, LONG
*p
)
353 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
354 FIXME("(%p)->(%p)\n", This
, p
);
358 static HRESULT WINAPI
HTMLStorage_get_remainingSpace(IHTMLStorage
*iface
, LONG
*p
)
360 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
361 FIXME("(%p)->(%p)\n", This
, p
);
365 static HRESULT WINAPI
HTMLStorage_key(IHTMLStorage
*iface
, LONG lIndex
, BSTR
*p
)
367 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
368 FIXME("(%p)->(%ld %p)\n", This
, lIndex
, p
);
372 static BSTR
build_query(const WCHAR
*key
)
374 static const WCHAR fmt
[] = L
"item[@name='%s']";
375 const WCHAR
*str
= key
? key
: L
"";
376 UINT len
= ARRAY_SIZE(fmt
) + wcslen(str
);
377 BSTR ret
= SysAllocStringLen(NULL
, len
);
379 if(ret
) swprintf(ret
, len
, fmt
, str
);
383 static HRESULT
get_root_node(IXMLDOMDocument
*doc
, IXMLDOMNode
**root
)
388 str
= SysAllocString(L
"root");
390 return E_OUTOFMEMORY
;
392 hres
= IXMLDOMDocument_selectSingleNode(doc
, str
, root
);
397 static HRESULT
get_item(const WCHAR
*filename
, BSTR key
, VARIANT
*value
)
399 IXMLDOMDocument
*doc
;
401 IXMLDOMNode
*root
= NULL
, *node
= NULL
;
402 IXMLDOMElement
*elem
= NULL
;
405 hres
= open_document(filename
, &doc
);
409 hres
= get_root_node(doc
, &root
);
413 query
= build_query(key
);
415 hres
= E_OUTOFMEMORY
;
419 hres
= IXMLDOMNode_selectSingleNode(root
, query
, &node
);
421 hres
= IXMLDOMNode_QueryInterface(node
, &IID_IXMLDOMElement
, (void**)&elem
);
425 hres
= IXMLDOMElement_getAttribute(elem
, (BSTR
)L
"value", value
);
427 V_VT(value
) = VT_NULL
;
432 SysFreeString(query
);
434 IXMLDOMNode_Release(root
);
436 IXMLDOMNode_Release(node
);
438 IXMLDOMElement_Release(elem
);
439 IXMLDOMDocument_Release(doc
);
443 static HRESULT WINAPI
HTMLStorage_getItem(IHTMLStorage
*iface
, BSTR bstrKey
, VARIANT
*value
)
445 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
446 struct session_entry
*session_entry
;
449 TRACE("(%p)->(%s %p)\n", This
, debugstr_w(bstrKey
), value
);
454 if(!This
->filename
) {
455 hres
= get_session_entry(This
->session_storage
, bstrKey
, FALSE
, &session_entry
);
456 if(SUCCEEDED(hres
)) {
457 if(!session_entry
|| !session_entry
->value
)
458 V_VT(value
) = VT_NULL
;
460 V_VT(value
) = VT_BSTR
;
461 V_BSTR(value
) = SysAllocStringLen(session_entry
->value
, SysStringLen(session_entry
->value
));
462 hres
= V_BSTR(value
) ? S_OK
: E_OUTOFMEMORY
;
468 WaitForSingleObject(This
->mutex
, INFINITE
);
469 hres
= get_item(This
->filename
, bstrKey
, value
);
470 ReleaseMutex(This
->mutex
);
475 static HRESULT
set_attribute(IXMLDOMElement
*elem
, const WCHAR
*name
, BSTR value
)
481 str
= SysAllocString(name
);
483 return E_OUTOFMEMORY
;
484 V_VT(&var
) = VT_BSTR
;
485 V_BSTR(&var
) = value
;
487 hres
= IXMLDOMElement_setAttribute(elem
, str
, var
);
492 static HRESULT
save_document(IXMLDOMDocument
*doc
, const WCHAR
*filename
)
497 V_VT(&var
) = VT_BSTR
;
498 V_BSTR(&var
) = SysAllocString(filename
);
500 return E_OUTOFMEMORY
;
502 hres
= IXMLDOMDocument_save(doc
, var
);
503 SysFreeString(V_BSTR(&var
));
507 static HRESULT
set_item(const WCHAR
*filename
, BSTR key
, BSTR value
)
509 IXMLDOMDocument
*doc
;
510 IXMLDOMNode
*root
= NULL
, *node
= NULL
;
511 IXMLDOMElement
*elem
= NULL
;
515 hres
= open_document(filename
, &doc
);
519 hres
= get_root_node(doc
, &root
);
523 query
= build_query(key
);
525 hres
= E_OUTOFMEMORY
;
529 hres
= IXMLDOMNode_selectSingleNode(root
, query
, &node
);
531 hres
= IXMLDOMNode_QueryInterface(node
, &IID_IXMLDOMElement
, (void**)&elem
);
535 hres
= set_attribute(elem
, L
"value", value
);
539 BSTR str
= SysAllocString(L
"item");
540 hres
= IXMLDOMDocument_createElement(doc
, str
, &elem
);
545 hres
= set_attribute(elem
, L
"name", key
);
549 hres
= set_attribute(elem
, L
"value", value
);
553 hres
= IXMLDOMNode_appendChild(root
, (IXMLDOMNode
*)elem
, NULL
);
558 hres
= save_document(doc
, filename
);
561 SysFreeString(query
);
563 IXMLDOMNode_Release(root
);
565 IXMLDOMNode_Release(node
);
567 IXMLDOMElement_Release(elem
);
568 IXMLDOMDocument_Release(doc
);
572 static HRESULT WINAPI
HTMLStorage_setItem(IHTMLStorage
*iface
, BSTR bstrKey
, BSTR bstrValue
)
574 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
575 struct session_entry
*session_entry
;
578 TRACE("(%p)->(%s %s)\n", This
, debugstr_w(bstrKey
), debugstr_w(bstrValue
));
580 if(!This
->filename
) {
581 BSTR value
= SysAllocString(bstrValue
? bstrValue
: L
"");
583 return E_OUTOFMEMORY
;
585 hres
= get_session_entry(This
->session_storage
, bstrKey
, TRUE
, &session_entry
);
587 SysFreeString(value
);
589 SysFreeString(session_entry
->value
);
590 session_entry
->value
= value
;
595 WaitForSingleObject(This
->mutex
, INFINITE
);
596 hres
= set_item(This
->filename
, bstrKey
, bstrValue
);
597 ReleaseMutex(This
->mutex
);
602 static HRESULT
remove_item(const WCHAR
*filename
, BSTR key
)
604 IXMLDOMDocument
*doc
;
605 IXMLDOMNode
*root
= NULL
, *node
= NULL
;
609 hres
= open_document(filename
, &doc
);
613 hres
= get_root_node(doc
, &root
);
617 query
= build_query(key
);
619 hres
= E_OUTOFMEMORY
;
623 hres
= IXMLDOMNode_selectSingleNode(root
, query
, &node
);
625 hres
= IXMLDOMNode_removeChild(root
, node
, NULL
);
630 hres
= save_document(doc
, filename
);
633 SysFreeString(query
);
635 IXMLDOMNode_Release(root
);
637 IXMLDOMNode_Release(node
);
638 IXMLDOMDocument_Release(doc
);
642 static HRESULT WINAPI
HTMLStorage_removeItem(IHTMLStorage
*iface
, BSTR bstrKey
)
644 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
647 TRACE("(%p)->(%s)\n", This
, debugstr_w(bstrKey
));
649 if(!This
->filename
) {
650 FIXME("session storage not supported\n");
654 WaitForSingleObject(This
->mutex
, INFINITE
);
655 hres
= remove_item(This
->filename
, bstrKey
);
656 ReleaseMutex(This
->mutex
);
661 static HRESULT WINAPI
HTMLStorage_clear(IHTMLStorage
*iface
)
663 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
664 FIXME("(%p)->()\n", This
);
668 static const IHTMLStorageVtbl HTMLStorageVtbl
= {
669 HTMLStorage_QueryInterface
,
672 HTMLStorage_GetTypeInfoCount
,
673 HTMLStorage_GetTypeInfo
,
674 HTMLStorage_GetIDsOfNames
,
676 HTMLStorage_get_length
,
677 HTMLStorage_get_remainingSpace
,
681 HTMLStorage_removeItem
,
685 static const tid_t HTMLStorage_iface_tids
[] = {
689 static dispex_static_data_t HTMLStorage_dispex
= {
693 HTMLStorage_iface_tids
696 static HRESULT
build_session_origin(IUri
*uri
, BSTR hostname
, BSTR
*ret
)
698 UINT host_len
, scheme_len
;
702 hres
= IUri_GetSchemeName(uri
, &scheme
);
706 SysFreeString(scheme
);
710 /* Since it's only used for lookup, we can apply transformations to
711 keep the lookup itself simple and fast. First, we convert `https`
712 to `http` because they are equal for lookup. Next, we place the
713 scheme after the hostname, separated by NUL, to compare the host
714 first, since it tends to differ more often. Lastly, we lowercase
715 the whole thing since lookup must be case-insensitive. */
716 scheme_len
= SysStringLen(scheme
);
717 host_len
= SysStringLen(hostname
);
719 if(scheme_len
== 5 && !wcsicmp(scheme
, L
"https"))
722 origin
= SysAllocStringLen(NULL
, host_len
+ 1 + scheme_len
);
725 memcpy(p
, hostname
, host_len
* sizeof(WCHAR
));
727 *p
= ' '; /* for wcslwr */
728 memcpy(p
+ 1, scheme
, scheme_len
* sizeof(WCHAR
));
729 p
[1 + scheme_len
] = '\0';
733 SysFreeString(scheme
);
736 return E_OUTOFMEMORY
;
742 static WCHAR
*build_filename(BSTR hostname
)
744 static const WCHAR store
[] = L
"\\Microsoft\\Internet Explorer\\DOMStore\\";
745 WCHAR path
[MAX_PATH
], *ret
;
748 if(!SHGetSpecialFolderPathW(NULL
, path
, CSIDL_LOCAL_APPDATA
, TRUE
)) {
749 ERR("Can't get folder path %lu\n", GetLastError());
754 if(len
+ ARRAY_SIZE(store
) > ARRAY_SIZE(path
)) {
755 ERR("Path too long\n");
758 memcpy(path
+ len
, store
, sizeof(store
));
760 len
+= ARRAY_SIZE(store
);
761 ret
= heap_alloc((len
+ wcslen(hostname
) + ARRAY_SIZE(L
".xml")) * sizeof(WCHAR
));
767 wcscat(ret
, hostname
);
768 wcscat(ret
, L
".xml");
773 static WCHAR
*build_mutexname(const WCHAR
*filename
)
776 ret
= heap_strdupW(filename
);
779 for(ptr
= ret
; *ptr
; ptr
++)
780 if(*ptr
== '\\') *ptr
= '_';
784 HRESULT
create_html_storage(HTMLInnerWindow
*window
, BOOL local
, IHTMLStorage
**p
)
786 IUri
*uri
= window
->base
.outer_window
->uri
;
787 BSTR origin
, hostname
= NULL
;
788 HTMLStorage
*storage
;
794 hres
= IUri_GetHost(uri
, &hostname
);
796 SysFreeString(hostname
);
800 storage
= heap_alloc_zero(sizeof(*storage
));
802 SysFreeString(hostname
);
803 return E_OUTOFMEMORY
;
808 storage
->filename
= build_filename(hostname
);
809 SysFreeString(hostname
);
810 if(!storage
->filename
) {
812 return E_OUTOFMEMORY
;
814 mutexname
= build_mutexname(storage
->filename
);
816 heap_free(storage
->filename
);
818 return E_OUTOFMEMORY
;
820 storage
->mutex
= CreateMutexW(NULL
, FALSE
, mutexname
);
821 heap_free(mutexname
);
822 if(!storage
->mutex
) {
823 heap_free(storage
->filename
);
825 return HRESULT_FROM_WIN32(GetLastError());
828 hres
= build_session_origin(uri
, hostname
, &origin
);
829 SysFreeString(hostname
);
834 storage
->session_storage
= grab_session_map_entry(origin
);
835 SysFreeString(origin
);
836 if(!storage
->session_storage
) {
838 return E_OUTOFMEMORY
;
842 storage
->IHTMLStorage_iface
.lpVtbl
= &HTMLStorageVtbl
;
844 init_dispatch(&storage
->dispex
, (IUnknown
*)&storage
->IHTMLStorage_iface
, &HTMLStorage_dispex
,
845 dispex_compat_mode(&window
->event_target
.dispex
));
847 *p
= &storage
->IHTMLStorage_iface
;