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"
32 #include "htmlevent.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(mshtml
);
36 /* Native defaults to 5 million chars per origin */
37 enum { MAX_QUOTA
= 5000000 };
41 IHTMLStorage IHTMLStorage_iface
;
44 HTMLInnerWindow
*window
;
45 struct session_map_entry
*session_storage
;
50 struct session_map_entry
{
51 struct wine_rb_entry entry
;
52 struct wine_rb_tree data_map
;
53 struct list data_list
; /* for key() */
61 int session_storage_map_cmp(const void *key
, const struct wine_rb_entry
*entry
)
63 struct session_map_entry
*p
= WINE_RB_ENTRY_VALUE(entry
, struct session_map_entry
, entry
);
64 UINT len
= SysStringLen((BSTR
)key
);
66 return (len
!= p
->origin_len
) ? (len
- p
->origin_len
) : memcmp(key
, p
->origin
, len
* sizeof(WCHAR
));
71 struct wine_rb_entry entry
;
72 struct list list_entry
;
77 static int session_entry_cmp(const void *key
, const struct wine_rb_entry
*entry
)
79 struct session_entry
*data
= WINE_RB_ENTRY_VALUE(entry
, struct session_entry
, entry
);
80 return wcscmp(key
, data
->key
);
83 static struct session_map_entry
*grab_session_map_entry(BSTR origin
)
85 struct session_map_entry
*entry
;
86 struct wine_rb_entry
*rb_entry
;
87 thread_data_t
*thread_data
;
90 thread_data
= get_thread_data(TRUE
);
94 rb_entry
= wine_rb_get(&thread_data
->session_storage_map
, origin
);
96 entry
= WINE_RB_ENTRY_VALUE(rb_entry
, struct session_map_entry
, entry
);
101 origin_len
= SysStringLen(origin
);
102 entry
= malloc(FIELD_OFFSET(struct session_map_entry
, origin
[origin_len
]));
105 wine_rb_init(&entry
->data_map
, session_entry_cmp
);
106 list_init(&entry
->data_list
);
108 entry
->quota
= MAX_QUOTA
;
110 entry
->origin_len
= origin_len
;
111 memcpy(entry
->origin
, origin
, origin_len
* sizeof(WCHAR
));
113 wine_rb_put(&thread_data
->session_storage_map
, origin
, &entry
->entry
);
117 static void release_session_map_entry(struct session_map_entry
*entry
)
119 if(!entry
|| --entry
->ref
|| entry
->num_keys
)
122 wine_rb_remove(&get_thread_data(FALSE
)->session_storage_map
, &entry
->entry
);
126 static HRESULT
get_session_entry(struct session_map_entry
*entry
, const WCHAR
*name
, BOOL create
, struct session_entry
**ret
)
128 const WCHAR
*key
= name
? name
: L
"";
129 struct wine_rb_entry
*rb_entry
;
130 struct session_entry
*data
;
133 rb_entry
= wine_rb_get(&entry
->data_map
, key
);
135 *ret
= WINE_RB_ENTRY_VALUE(rb_entry
, struct session_entry
, entry
);
144 key_len
= wcslen(key
);
145 if(entry
->quota
< key_len
)
146 return E_OUTOFMEMORY
; /* native returns this when quota is exceeded */
147 if(!(data
= malloc(FIELD_OFFSET(struct session_entry
, key
[key_len
+ 1]))))
148 return E_OUTOFMEMORY
;
150 memcpy(data
->key
, key
, (key_len
+ 1) * sizeof(WCHAR
));
152 entry
->quota
-= key_len
;
154 list_add_tail(&entry
->data_list
, &data
->list_entry
);
155 wine_rb_put(&entry
->data_map
, key
, &data
->entry
);
160 static void clear_session_storage(struct session_map_entry
*entry
)
162 struct session_entry
*iter
, *iter2
;
164 LIST_FOR_EACH_ENTRY_SAFE(iter
, iter2
, &entry
->data_list
, struct session_entry
, list_entry
) {
165 SysFreeString(iter
->value
);
168 wine_rb_destroy(&entry
->data_map
, NULL
, NULL
);
169 list_init(&entry
->data_list
);
170 entry
->quota
= MAX_QUOTA
;
174 void destroy_session_storage(thread_data_t
*thread_data
)
176 struct session_map_entry
*iter
, *iter2
;
178 WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter
, iter2
, &thread_data
->session_storage_map
, struct session_map_entry
, entry
) {
179 clear_session_storage(iter
);
184 static void release_props(HTMLStorage
*This
)
186 BSTR
*prop
= This
->props
, *end
= prop
+ This
->num_props
;
188 SysFreeString(*prop
);
194 static inline HTMLStorage
*impl_from_IHTMLStorage(IHTMLStorage
*iface
)
196 return CONTAINING_RECORD(iface
, HTMLStorage
, IHTMLStorage_iface
);
199 static HRESULT
build_session_origin(IUri
*,BSTR
,BSTR
*);
201 struct storage_event_task
{
206 static void storage_event_proc(event_task_t
*_task
)
208 struct storage_event_task
*task
= (struct storage_event_task
*)_task
;
209 HTMLInnerWindow
*window
= task
->header
.window
;
210 DOMEvent
*event
= task
->event
;
211 VARIANT_BOOL cancelled
;
213 if(event
->event_id
== EVENTID_STORAGE
&& dispex_compat_mode(&window
->event_target
.dispex
) >= COMPAT_MODE_IE9
) {
214 dispatch_event(&window
->event_target
, event
);
216 fire_event(&window
->doc
->node
, L
"onstorage", NULL
, &cancelled
);
217 }else if(window
->doc
) {
218 dispatch_event(&window
->doc
->node
.event_target
, event
);
222 static void storage_event_destr(event_task_t
*_task
)
224 struct storage_event_task
*task
= (struct storage_event_task
*)_task
;
225 IDOMEvent_Release(&task
->event
->IDOMEvent_iface
);
228 struct send_storage_event_ctx
{
229 HTMLInnerWindow
*skip_window
;
238 static HRESULT
push_storage_event_task(struct send_storage_event_ctx
*ctx
, HTMLInnerWindow
*window
, BOOL commit
)
240 struct storage_event_task
*task
;
244 hres
= create_storage_event(window
->doc
, ctx
->key
, ctx
->old_value
, ctx
->new_value
, ctx
->url
, commit
, &event
);
248 if(!(task
= malloc(sizeof(*task
)))) {
249 IDOMEvent_Release(&event
->IDOMEvent_iface
);
250 return E_OUTOFMEMORY
;
254 return push_event_task(&task
->header
, window
, storage_event_proc
, storage_event_destr
, window
->task_magic
);
257 static HRESULT
send_storage_event_impl(struct send_storage_event_ctx
*ctx
, HTMLInnerWindow
*window
)
259 HTMLOuterWindow
*child
;
269 LIST_FOR_EACH_ENTRY(child
, &window
->children
, HTMLOuterWindow
, sibling_entry
) {
270 hres
= send_storage_event_impl(ctx
, child
->base
.inner_window
);
275 if(window
== ctx
->skip_window
)
278 /* Try it quick from session storage first, if available */
279 if(window
->session_storage
) {
280 HTMLStorage
*storage
= impl_from_IHTMLStorage(window
->session_storage
);
281 origin
= storage
->session_storage
->origin
;
282 origin_len
= ctx
->skip_window
? wcslen(origin
) : storage
->session_storage
->origin_len
;
284 }else if(!window
->base
.outer_window
->uri
) {
287 hres
= IUri_GetHost(window
->base
.outer_window
->uri
, &bstr
);
297 hres
= build_session_origin(window
->base
.outer_window
->uri
, tmp
, &bstr
);
306 origin_len
= SysStringLen(bstr
);
309 matches
= (origin_len
== ctx
->origin_len
&& !memcmp(origin
, ctx
->origin
, origin_len
* sizeof(WCHAR
)));
312 return matches
? push_storage_event_task(ctx
, window
, FALSE
) : S_OK
;
315 /* Takes ownership of old_value */
316 static HRESULT
send_storage_event(HTMLStorage
*storage
, BSTR key
, BSTR old_value
, BSTR new_value
)
318 HTMLInnerWindow
*window
= storage
->window
;
319 struct send_storage_event_ctx ctx
;
320 HTMLOuterWindow
*top_window
;
321 BSTR hostname
= NULL
;
327 if(window
->base
.outer_window
->uri_nofrag
) {
328 hres
= IUri_GetDisplayUri(window
->base
.outer_window
->uri_nofrag
, &ctx
.url
);
333 get_top_window(window
->base
.outer_window
, &top_window
);
335 ctx
.old_value
= old_value
;
336 ctx
.new_value
= new_value
;
337 if(!storage
->filename
) {
338 ctx
.origin
= storage
->session_storage
->origin
;
339 ctx
.origin_len
= storage
->session_storage
->origin_len
;
340 ctx
.skip_window
= NULL
;
342 hres
= IUri_GetHost(window
->base
.outer_window
->uri
, &hostname
);
346 ctx
.origin
= hostname
;
347 ctx
.origin_len
= SysStringLen(hostname
);
348 ctx
.skip_window
= top_window
->base
.inner_window
; /* localStorage on native skips top window */
350 hres
= send_storage_event_impl(&ctx
, top_window
->base
.inner_window
);
352 if(ctx
.skip_window
&& hres
== S_OK
)
353 hres
= push_storage_event_task(&ctx
, window
, TRUE
);
356 SysFreeString(ctx
.url
);
357 SysFreeString(hostname
);
358 SysFreeString(old_value
);
359 return FAILED(hres
) ? hres
: S_OK
;
362 static HRESULT WINAPI
HTMLStorage_QueryInterface(IHTMLStorage
*iface
, REFIID riid
, void **ppv
)
364 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
365 return IDispatchEx_QueryInterface(&This
->dispex
.IDispatchEx_iface
, riid
, ppv
);
368 static ULONG WINAPI
HTMLStorage_AddRef(IHTMLStorage
*iface
)
370 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
371 return IDispatchEx_AddRef(&This
->dispex
.IDispatchEx_iface
);
374 static ULONG WINAPI
HTMLStorage_Release(IHTMLStorage
*iface
)
376 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
377 return IDispatchEx_Release(&This
->dispex
.IDispatchEx_iface
);
380 static HRESULT WINAPI
HTMLStorage_GetTypeInfoCount(IHTMLStorage
*iface
, UINT
*pctinfo
)
382 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
383 return IDispatchEx_GetTypeInfoCount(&This
->dispex
.IDispatchEx_iface
, pctinfo
);
386 static HRESULT WINAPI
HTMLStorage_GetTypeInfo(IHTMLStorage
*iface
, UINT iTInfo
,
387 LCID lcid
, ITypeInfo
**ppTInfo
)
389 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
391 return IDispatchEx_GetTypeInfo(&This
->dispex
.IDispatchEx_iface
, iTInfo
, lcid
, ppTInfo
);
394 static HRESULT WINAPI
HTMLStorage_GetIDsOfNames(IHTMLStorage
*iface
, REFIID riid
, LPOLESTR
*rgszNames
, UINT cNames
,
395 LCID lcid
, DISPID
*rgDispId
)
397 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
399 return IDispatchEx_GetIDsOfNames(&This
->dispex
.IDispatchEx_iface
, riid
, rgszNames
, cNames
,
403 static HRESULT WINAPI
HTMLStorage_Invoke(IHTMLStorage
*iface
, DISPID dispIdMember
, REFIID riid
, LCID lcid
,
404 WORD wFlags
, DISPPARAMS
*pDispParams
, VARIANT
*pVarResult
, EXCEPINFO
*pExcepInfo
, UINT
*puArgErr
)
406 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
408 return IDispatchEx_Invoke(&This
->dispex
.IDispatchEx_iface
, dispIdMember
, riid
, lcid
, wFlags
,
409 pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
412 static BOOL
create_path(const WCHAR
*path
)
418 new_path
= malloc((wcslen(path
) + 1) * sizeof(WCHAR
));
421 wcscpy(new_path
, path
);
423 while((len
= wcslen(new_path
)) && new_path
[len
- 1] == '\\')
424 new_path
[len
- 1] = 0;
426 while(!CreateDirectoryW(new_path
, NULL
)) {
428 DWORD error
= GetLastError();
429 if(error
== ERROR_ALREADY_EXISTS
) break;
430 if(error
!= ERROR_PATH_NOT_FOUND
) {
434 slash
= wcsrchr(new_path
, '\\');
439 len
= slash
- new_path
;
441 if(!create_path(new_path
)) {
445 new_path
[len
] = '\\';
451 static HRESULT
open_document(const WCHAR
*filename
, IXMLDOMDocument
**ret
)
453 IXMLDOMDocument
*doc
= NULL
;
454 HRESULT hres
= E_OUTOFMEMORY
;
457 VARIANT_BOOL success
;
459 path
= wcsdup(filename
);
461 return E_OUTOFMEMORY
;
463 *(ptr
= wcsrchr(path
, '\\')) = 0;
464 if(!create_path(path
))
467 if(GetFileAttributesW(filename
) == INVALID_FILE_ATTRIBUTES
) {
469 HANDLE file
= CreateFileW(filename
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, 0, NULL
);
470 if(file
== INVALID_HANDLE_VALUE
) {
471 hres
= HRESULT_FROM_WIN32(GetLastError());
474 if(!WriteFile(file
, "<root/>", sizeof("<root/>") - 1, &count
, NULL
)) {
476 hres
= HRESULT_FROM_WIN32(GetLastError());
482 hres
= CoCreateInstance(&CLSID_DOMDocument
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IXMLDOMDocument
, (void**)&doc
);
486 V_VT(&var
) = VT_BSTR
;
487 V_BSTR(&var
) = SysAllocString(filename
);
489 hres
= E_OUTOFMEMORY
;
493 hres
= IXMLDOMDocument_load(doc
, var
, &success
);
494 if(hres
== S_FALSE
|| success
== VARIANT_FALSE
)
497 SysFreeString(V_BSTR(&var
));
504 IXMLDOMDocument_Release(doc
);
509 static HRESULT
get_root_node(IXMLDOMDocument
*doc
, IXMLDOMNode
**root
)
514 str
= SysAllocString(L
"root");
516 return E_OUTOFMEMORY
;
518 hres
= IXMLDOMDocument_selectSingleNode(doc
, str
, root
);
523 static HRESULT
get_node_list(const WCHAR
*filename
, IXMLDOMNodeList
**node_list
)
525 IXMLDOMDocument
*doc
;
530 hres
= open_document(filename
, &doc
);
534 hres
= get_root_node(doc
, &root
);
535 IXMLDOMDocument_Release(doc
);
539 if(!(query
= SysAllocString(L
"item")))
540 hres
= E_OUTOFMEMORY
;
542 hres
= IXMLDOMNode_selectNodes(root
, query
, node_list
);
543 SysFreeString(query
);
545 IXMLDOMNode_Release(root
);
549 static HRESULT WINAPI
HTMLStorage_get_length(IHTMLStorage
*iface
, LONG
*p
)
551 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
552 IXMLDOMNodeList
*node_list
;
555 TRACE("(%p)->(%p)\n", This
, p
);
557 if(!This
->filename
) {
558 *p
= This
->session_storage
->num_keys
;
562 WaitForSingleObject(This
->mutex
, INFINITE
);
563 hres
= get_node_list(This
->filename
, &node_list
);
564 if(SUCCEEDED(hres
)) {
565 hres
= IXMLDOMNodeList_get_length(node_list
, p
);
566 IXMLDOMNodeList_Release(node_list
);
568 ReleaseMutex(This
->mutex
);
573 static HRESULT WINAPI
HTMLStorage_get_remainingSpace(IHTMLStorage
*iface
, LONG
*p
)
575 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
577 TRACE("(%p)->(%p)\n", This
, p
);
579 if(!This
->filename
) {
580 *p
= This
->session_storage
->quota
;
584 FIXME("local storage not supported\n");
588 static HRESULT
get_key(const WCHAR
*filename
, LONG index
, BSTR
*ret
)
590 IXMLDOMNodeList
*node_list
;
591 IXMLDOMElement
*elem
;
596 hres
= get_node_list(filename
, &node_list
);
600 hres
= IXMLDOMNodeList_get_item(node_list
, index
, &node
);
601 IXMLDOMNodeList_Release(node_list
);
603 return FAILED(hres
) ? hres
: E_INVALIDARG
;
605 hres
= IXMLDOMNode_QueryInterface(node
, &IID_IXMLDOMElement
, (void**)&elem
);
606 IXMLDOMNode_Release(node
);
610 hres
= IXMLDOMElement_getAttribute(elem
, (BSTR
)L
"name", &key
);
611 IXMLDOMElement_Release(elem
);
615 if(V_VT(&key
) != VT_BSTR
) {
616 FIXME("non-string key %s\n", debugstr_variant(&key
));
625 static HRESULT WINAPI
HTMLStorage_key(IHTMLStorage
*iface
, LONG lIndex
, BSTR
*p
)
627 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
628 struct session_entry
*session_entry
;
631 TRACE("(%p)->(%ld %p)\n", This
, lIndex
, p
);
633 if(!This
->filename
) {
634 struct list
*entry
= &This
->session_storage
->data_list
;
637 if(lIndex
>= This
->session_storage
->num_keys
)
640 do entry
= entry
->next
; while(i
++ < lIndex
);
641 session_entry
= LIST_ENTRY(entry
, struct session_entry
, list_entry
);
643 *p
= SysAllocString(session_entry
->key
);
644 return *p
? S_OK
: E_OUTOFMEMORY
;
647 WaitForSingleObject(This
->mutex
, INFINITE
);
648 hres
= get_key(This
->filename
, lIndex
, p
);
649 ReleaseMutex(This
->mutex
);
654 static BSTR
build_query(const WCHAR
*key
)
656 static const WCHAR fmt
[] = L
"item[@name='%s']";
657 const WCHAR
*str
= key
? key
: L
"";
658 UINT len
= ARRAY_SIZE(fmt
) + wcslen(str
);
659 BSTR ret
= SysAllocStringLen(NULL
, len
);
661 if(ret
) swprintf(ret
, len
, fmt
, str
);
665 static HRESULT
get_item(const WCHAR
*filename
, BSTR key
, VARIANT
*value
)
667 IXMLDOMDocument
*doc
;
669 IXMLDOMNode
*root
= NULL
, *node
= NULL
;
670 IXMLDOMElement
*elem
= NULL
;
673 hres
= open_document(filename
, &doc
);
677 hres
= get_root_node(doc
, &root
);
681 query
= build_query(key
);
683 hres
= E_OUTOFMEMORY
;
687 hres
= IXMLDOMNode_selectSingleNode(root
, query
, &node
);
689 hres
= IXMLDOMNode_QueryInterface(node
, &IID_IXMLDOMElement
, (void**)&elem
);
693 hres
= IXMLDOMElement_getAttribute(elem
, (BSTR
)L
"value", value
);
695 V_VT(value
) = VT_NULL
;
700 SysFreeString(query
);
702 IXMLDOMNode_Release(root
);
704 IXMLDOMNode_Release(node
);
706 IXMLDOMElement_Release(elem
);
707 IXMLDOMDocument_Release(doc
);
711 static HRESULT WINAPI
HTMLStorage_getItem(IHTMLStorage
*iface
, BSTR bstrKey
, VARIANT
*value
)
713 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
714 struct session_entry
*session_entry
;
717 TRACE("(%p)->(%s %p)\n", This
, debugstr_w(bstrKey
), value
);
722 if(!This
->filename
) {
723 hres
= get_session_entry(This
->session_storage
, bstrKey
, FALSE
, &session_entry
);
724 if(SUCCEEDED(hres
)) {
725 if(!session_entry
|| !session_entry
->value
)
726 V_VT(value
) = VT_NULL
;
728 V_VT(value
) = VT_BSTR
;
729 V_BSTR(value
) = SysAllocStringLen(session_entry
->value
, SysStringLen(session_entry
->value
));
730 hres
= V_BSTR(value
) ? S_OK
: E_OUTOFMEMORY
;
736 WaitForSingleObject(This
->mutex
, INFINITE
);
737 hres
= get_item(This
->filename
, bstrKey
, value
);
738 ReleaseMutex(This
->mutex
);
743 static HRESULT
set_attribute(IXMLDOMElement
*elem
, const WCHAR
*name
, BSTR value
)
749 str
= SysAllocString(name
);
751 return E_OUTOFMEMORY
;
752 V_VT(&var
) = VT_BSTR
;
753 V_BSTR(&var
) = value
;
755 hres
= IXMLDOMElement_setAttribute(elem
, str
, var
);
760 static HRESULT
save_document(IXMLDOMDocument
*doc
, const WCHAR
*filename
)
765 V_VT(&var
) = VT_BSTR
;
766 V_BSTR(&var
) = SysAllocString(filename
);
768 return E_OUTOFMEMORY
;
770 hres
= IXMLDOMDocument_save(doc
, var
);
771 SysFreeString(V_BSTR(&var
));
775 static HRESULT
set_item(const WCHAR
*filename
, BSTR key
, BSTR value
, BSTR
*old_value
)
777 IXMLDOMDocument
*doc
;
778 IXMLDOMNode
*root
= NULL
, *node
= NULL
;
779 IXMLDOMElement
*elem
= NULL
;
784 hres
= open_document(filename
, &doc
);
788 hres
= get_root_node(doc
, &root
);
792 query
= build_query(key
);
794 hres
= E_OUTOFMEMORY
;
798 hres
= IXMLDOMNode_selectSingleNode(root
, query
, &node
);
802 hres
= IXMLDOMNode_QueryInterface(node
, &IID_IXMLDOMElement
, (void**)&elem
);
806 hres
= IXMLDOMElement_getAttribute(elem
, (BSTR
)L
"value", &old
);
808 if(V_VT(&old
) == VT_BSTR
)
809 *old_value
= V_BSTR(&old
);
814 hres
= set_attribute(elem
, L
"value", value
);
818 BSTR str
= SysAllocString(L
"item");
819 hres
= IXMLDOMDocument_createElement(doc
, str
, &elem
);
824 hres
= set_attribute(elem
, L
"name", key
);
828 hres
= set_attribute(elem
, L
"value", value
);
832 hres
= IXMLDOMNode_appendChild(root
, (IXMLDOMNode
*)elem
, NULL
);
837 hres
= save_document(doc
, filename
);
840 SysFreeString(query
);
842 IXMLDOMNode_Release(root
);
844 IXMLDOMNode_Release(node
);
846 IXMLDOMElement_Release(elem
);
847 IXMLDOMDocument_Release(doc
);
849 SysFreeString(*old_value
);
853 static HRESULT WINAPI
HTMLStorage_setItem(IHTMLStorage
*iface
, BSTR bstrKey
, BSTR bstrValue
)
855 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
856 struct session_entry
*session_entry
;
860 TRACE("(%p)->(%s %s)\n", This
, debugstr_w(bstrKey
), debugstr_w(bstrValue
));
862 if(!This
->filename
) {
863 UINT value_len
= bstrValue
? wcslen(bstrValue
) : 0;
864 BSTR value
= SysAllocStringLen(bstrValue
, value_len
);
866 return E_OUTOFMEMORY
;
868 hres
= get_session_entry(This
->session_storage
, bstrKey
, TRUE
, &session_entry
);
870 SysFreeString(value
);
872 UINT old_len
= SysStringLen(session_entry
->value
);
873 if(old_len
< value_len
&& This
->session_storage
->quota
< value_len
- old_len
) {
874 SysFreeString(value
);
875 return E_OUTOFMEMORY
; /* native returns this when quota is exceeded */
877 This
->session_storage
->quota
-= value_len
- old_len
;
878 old_value
= session_entry
->value
;
879 session_entry
->value
= value
;
881 hres
= send_storage_event(This
, bstrKey
, old_value
, value
);
886 WaitForSingleObject(This
->mutex
, INFINITE
);
887 hres
= set_item(This
->filename
, bstrKey
, bstrValue
, &old_value
);
888 ReleaseMutex(This
->mutex
);
891 hres
= send_storage_event(This
, bstrKey
, old_value
, bstrValue
);
895 static HRESULT
remove_item(const WCHAR
*filename
, BSTR key
, BSTR
*old_value
, BOOL
*changed
)
897 IXMLDOMDocument
*doc
;
898 IXMLDOMNode
*root
= NULL
, *node
= NULL
;
904 hres
= open_document(filename
, &doc
);
908 hres
= get_root_node(doc
, &root
);
912 query
= build_query(key
);
914 hres
= E_OUTOFMEMORY
;
918 hres
= IXMLDOMNode_selectSingleNode(root
, query
, &node
);
920 IXMLDOMElement
*elem
;
923 hres
= IXMLDOMNode_QueryInterface(node
, &IID_IXMLDOMElement
, (void**)&elem
);
925 hres
= IXMLDOMElement_getAttribute(elem
, (BSTR
)L
"value", &old
);
927 if(V_VT(&old
) == VT_BSTR
)
928 *old_value
= V_BSTR(&old
);
932 IXMLDOMElement_Release(elem
);
936 hres
= IXMLDOMNode_removeChild(root
, node
, NULL
);
941 hres
= save_document(doc
, filename
);
944 SysFreeString(query
);
946 IXMLDOMNode_Release(root
);
948 IXMLDOMNode_Release(node
);
949 IXMLDOMDocument_Release(doc
);
950 if(hres
!= S_OK
|| !changed
)
951 SysFreeString(*old_value
);
955 static HRESULT WINAPI
HTMLStorage_removeItem(IHTMLStorage
*iface
, BSTR bstrKey
)
957 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
958 struct session_entry
*session_entry
;
963 TRACE("(%p)->(%s)\n", This
, debugstr_w(bstrKey
));
965 if(!This
->filename
) {
966 hres
= get_session_entry(This
->session_storage
, bstrKey
, FALSE
, &session_entry
);
967 if(SUCCEEDED(hres
) && session_entry
) {
968 This
->session_storage
->quota
+= wcslen(session_entry
->key
) + SysStringLen(session_entry
->value
);
969 This
->session_storage
->num_keys
--;
970 list_remove(&session_entry
->list_entry
);
971 wine_rb_remove(&This
->session_storage
->data_map
, &session_entry
->entry
);
972 old_value
= session_entry
->value
;
975 hres
= send_storage_event(This
, bstrKey
, old_value
, NULL
);
980 WaitForSingleObject(This
->mutex
, INFINITE
);
981 hres
= remove_item(This
->filename
, bstrKey
, &old_value
, &changed
);
982 ReleaseMutex(This
->mutex
);
984 if(hres
== S_OK
&& changed
)
985 hres
= send_storage_event(This
, bstrKey
, old_value
, NULL
);
989 static HRESULT WINAPI
HTMLStorage_clear(IHTMLStorage
*iface
)
991 HTMLStorage
*This
= impl_from_IHTMLStorage(iface
);
994 if(!This
->filename
) {
995 UINT num_keys
= This
->session_storage
->num_keys
;
996 clear_session_storage(This
->session_storage
);
997 return num_keys
? send_storage_event(This
, NULL
, NULL
, NULL
) : S_OK
;
1000 WaitForSingleObject(This
->mutex
, INFINITE
);
1001 if(!DeleteFileW(This
->filename
)) {
1002 DWORD error
= GetLastError();
1003 if(error
!= ERROR_FILE_NOT_FOUND
&& error
!= ERROR_PATH_NOT_FOUND
)
1004 hres
= HRESULT_FROM_WIN32(error
);
1006 ReleaseMutex(This
->mutex
);
1008 hres
= send_storage_event(This
, NULL
, NULL
, NULL
);
1012 static const IHTMLStorageVtbl HTMLStorageVtbl
= {
1013 HTMLStorage_QueryInterface
,
1015 HTMLStorage_Release
,
1016 HTMLStorage_GetTypeInfoCount
,
1017 HTMLStorage_GetTypeInfo
,
1018 HTMLStorage_GetIDsOfNames
,
1020 HTMLStorage_get_length
,
1021 HTMLStorage_get_remainingSpace
,
1023 HTMLStorage_getItem
,
1024 HTMLStorage_setItem
,
1025 HTMLStorage_removeItem
,
1029 static inline HTMLStorage
*impl_from_DispatchEx(DispatchEx
*iface
)
1031 return CONTAINING_RECORD(iface
, HTMLStorage
, dispex
);
1034 static void *HTMLStorage_query_interface(DispatchEx
*dispex
, REFIID riid
)
1036 HTMLStorage
*This
= impl_from_DispatchEx(dispex
);
1038 if(IsEqualGUID(&IID_IHTMLStorage
, riid
))
1039 return &This
->IHTMLStorage_iface
;
1044 static void HTMLStorage_destructor(DispatchEx
*dispex
)
1046 HTMLStorage
*This
= impl_from_DispatchEx(dispex
);
1047 release_session_map_entry(This
->session_storage
);
1048 free(This
->filename
);
1049 CloseHandle(This
->mutex
);
1050 release_props(This
);
1054 static HRESULT
check_item(HTMLStorage
*This
, const WCHAR
*key
)
1056 struct session_entry
*session_entry
;
1057 IXMLDOMNode
*root
, *node
;
1058 IXMLDOMDocument
*doc
;
1062 if(!This
->filename
) {
1063 hres
= get_session_entry(This
->session_storage
, key
, FALSE
, &session_entry
);
1065 hres
= (session_entry
&& session_entry
->value
) ? S_OK
: S_FALSE
;
1069 WaitForSingleObject(This
->mutex
, INFINITE
);
1071 hres
= open_document(This
->filename
, &doc
);
1073 hres
= get_root_node(doc
, &root
);
1074 IXMLDOMDocument_Release(doc
);
1076 if(!(query
= build_query(key
)))
1077 hres
= E_OUTOFMEMORY
;
1079 hres
= IXMLDOMNode_selectSingleNode(root
, query
, &node
);
1080 SysFreeString(query
);
1082 IXMLDOMNode_Release(node
);
1084 IXMLDOMNode_Release(root
);
1088 ReleaseMutex(This
->mutex
);
1093 static HRESULT
get_prop(HTMLStorage
*This
, const WCHAR
*name
, DISPID
*dispid
)
1095 UINT name_len
= wcslen(name
);
1096 BSTR p
, *prop
, *end
;
1098 for(prop
= This
->props
, end
= prop
+ This
->num_props
; prop
!= end
; prop
++) {
1099 if(SysStringLen(*prop
) == name_len
&& !memcmp(*prop
, name
, name_len
* sizeof(WCHAR
))) {
1100 *dispid
= MSHTML_DISPID_CUSTOM_MIN
+ (prop
- This
->props
);
1105 if(is_power_of_2(This
->num_props
)) {
1106 BSTR
*new_props
= realloc(This
->props
, max(This
->num_props
* 2, 1) * sizeof(*This
->props
));
1108 return E_OUTOFMEMORY
;
1109 This
->props
= new_props
;
1112 if(!(p
= SysAllocStringLen(name
, name_len
)))
1113 return E_OUTOFMEMORY
;
1115 This
->props
[This
->num_props
] = p
;
1116 *dispid
= MSHTML_DISPID_CUSTOM_MIN
+ This
->num_props
++;
1120 static HRESULT
HTMLStorage_get_dispid(DispatchEx
*dispex
, BSTR name
, DWORD flags
, DISPID
*dispid
)
1122 HTMLStorage
*This
= impl_from_DispatchEx(dispex
);
1125 if(flags
& fdexNameCaseInsensitive
)
1126 FIXME("case insensitive not supported\n");
1128 if(!(flags
& fdexNameEnsure
)) {
1129 hres
= check_item(This
, name
);
1131 return FAILED(hres
) ? hres
: DISP_E_UNKNOWNNAME
;
1134 return get_prop(This
, name
, dispid
);
1137 static HRESULT
HTMLStorage_get_name(DispatchEx
*dispex
, DISPID id
, BSTR
*name
)
1139 HTMLStorage
*This
= impl_from_DispatchEx(dispex
);
1140 DWORD idx
= id
- MSHTML_DISPID_CUSTOM_MIN
;
1142 if(idx
>= This
->num_props
)
1143 return DISP_E_MEMBERNOTFOUND
;
1145 return (*name
= SysAllocString(This
->props
[idx
])) ? S_OK
: E_OUTOFMEMORY
;
1148 static HRESULT
HTMLStorage_invoke(DispatchEx
*dispex
, DISPID id
, LCID lcid
, WORD flags
, DISPPARAMS
*params
,
1149 VARIANT
*res
, EXCEPINFO
*ei
, IServiceProvider
*caller
)
1151 HTMLStorage
*This
= impl_from_DispatchEx(dispex
);
1152 DWORD idx
= id
- MSHTML_DISPID_CUSTOM_MIN
;
1156 if(idx
>= This
->num_props
)
1157 return DISP_E_MEMBERNOTFOUND
;
1160 case DISPATCH_PROPERTYGET
:
1161 hres
= HTMLStorage_getItem(&This
->IHTMLStorage_iface
, This
->props
[idx
], res
);
1164 if(V_VT(res
) == VT_NULL
)
1165 return DISP_E_MEMBERNOTFOUND
;
1167 case DISPATCH_PROPERTYPUTREF
:
1168 case DISPATCH_PROPERTYPUT
:
1169 if(params
->cArgs
!= 1 || (params
->cNamedArgs
&& params
->rgdispidNamedArgs
[0] != DISPID_PROPERTYPUT
)) {
1170 FIXME("unimplemented args %u %u\n", params
->cArgs
, params
->cNamedArgs
);
1174 bstr
= V_BSTR(params
->rgvarg
);
1175 if(V_VT(params
->rgvarg
) != VT_BSTR
) {
1177 hres
= change_type(&var
, params
->rgvarg
, VT_BSTR
, caller
);
1180 bstr
= V_BSTR(&var
);
1183 hres
= HTMLStorage_setItem(&This
->IHTMLStorage_iface
, This
->props
[idx
], bstr
);
1185 if(V_VT(params
->rgvarg
) != VT_BSTR
)
1186 SysFreeString(bstr
);
1190 FIXME("unimplemented flags %x\n", flags
);
1197 static HRESULT
HTMLStorage_delete(DispatchEx
*dispex
, DISPID id
)
1199 HTMLStorage
*This
= impl_from_DispatchEx(dispex
);
1200 DWORD idx
= id
- MSHTML_DISPID_CUSTOM_MIN
;
1202 if(idx
>= This
->num_props
)
1203 return DISP_E_MEMBERNOTFOUND
;
1205 if(dispex_compat_mode(dispex
) < COMPAT_MODE_IE8
)
1206 return MSHTML_E_INVALID_ACTION
;
1208 return HTMLStorage_removeItem(&This
->IHTMLStorage_iface
, This
->props
[idx
]);
1211 static HRESULT
HTMLStorage_next_dispid(DispatchEx
*dispex
, DISPID id
, DISPID
*pid
)
1213 DWORD idx
= (id
== DISPID_STARTENUM
) ? 0 : id
- MSHTML_DISPID_CUSTOM_MIN
+ 1;
1214 HTMLStorage
*This
= impl_from_DispatchEx(dispex
);
1218 if(idx
> MSHTML_CUSTOM_DISPID_CNT
)
1221 while(idx
< This
->num_props
) {
1222 hres
= check_item(This
, This
->props
[idx
]);
1224 *pid
= idx
+ MSHTML_DISPID_CUSTOM_MIN
;
1232 /* Populate possibly missing DISPIDs */
1233 if(!This
->filename
) {
1234 struct session_entry
*session_entry
;
1236 LIST_FOR_EACH_ENTRY(session_entry
, &This
->session_storage
->data_list
, struct session_entry
, list_entry
) {
1237 hres
= get_prop(This
, session_entry
->key
, &tmp
);
1242 IXMLDOMNodeList
*node_list
;
1243 IXMLDOMElement
*elem
;
1249 hres
= get_node_list(This
->filename
, &node_list
);
1254 hres
= IXMLDOMNodeList_get_item(node_list
, index
++, &node
);
1256 IXMLDOMNodeList_Release(node_list
);
1262 hres
= IXMLDOMNode_QueryInterface(node
, &IID_IXMLDOMElement
, (void**)&elem
);
1263 IXMLDOMNode_Release(node
);
1267 hres
= IXMLDOMElement_getAttribute(elem
, (BSTR
)L
"name", &key
);
1268 IXMLDOMElement_Release(elem
);
1272 IXMLDOMNodeList_Release(node_list
);
1276 if(V_VT(&key
) != VT_BSTR
) {
1277 FIXME("non-string key %s\n", debugstr_variant(&key
));
1282 hres
= get_prop(This
, V_BSTR(&key
), &tmp
);
1283 SysFreeString(V_BSTR(&key
));
1285 IXMLDOMNodeList_Release(node_list
);
1291 if(idx
>= This
->num_props
)
1294 *pid
= idx
+ MSHTML_DISPID_CUSTOM_MIN
;
1298 static const dispex_static_data_vtbl_t HTMLStorage_dispex_vtbl
= {
1299 .query_interface
= HTMLStorage_query_interface
,
1300 .destructor
= HTMLStorage_destructor
,
1301 .get_dispid
= HTMLStorage_get_dispid
,
1302 .get_name
= HTMLStorage_get_name
,
1303 .invoke
= HTMLStorage_invoke
,
1304 .delete = HTMLStorage_delete
,
1305 .next_dispid
= HTMLStorage_next_dispid
,
1308 static const tid_t HTMLStorage_iface_tids
[] = {
1312 static dispex_static_data_t HTMLStorage_dispex
= {
1314 &HTMLStorage_dispex_vtbl
,
1316 HTMLStorage_iface_tids
1319 static HRESULT
build_session_origin(IUri
*uri
, BSTR hostname
, BSTR
*ret
)
1321 UINT host_len
, scheme_len
;
1322 BSTR scheme
, origin
;
1325 hres
= IUri_GetSchemeName(uri
, &scheme
);
1329 SysFreeString(scheme
);
1333 /* Since it's only used for lookup, we can apply transformations to
1334 keep the lookup itself simple and fast. First, we convert `https`
1335 to `http` because they are equal for lookup. Next, we place the
1336 scheme after the hostname, separated by NUL, to compare the host
1337 first, since it tends to differ more often. Lastly, we lowercase
1338 the whole thing since lookup must be case-insensitive. */
1339 scheme_len
= SysStringLen(scheme
);
1340 host_len
= SysStringLen(hostname
);
1342 if(scheme_len
== 5 && !wcsicmp(scheme
, L
"https"))
1345 origin
= SysAllocStringLen(NULL
, host_len
+ 1 + scheme_len
);
1348 memcpy(p
, hostname
, host_len
* sizeof(WCHAR
));
1350 *p
= ' '; /* for wcslwr */
1351 memcpy(p
+ 1, scheme
, scheme_len
* sizeof(WCHAR
));
1352 p
[1 + scheme_len
] = '\0';
1356 SysFreeString(scheme
);
1359 return E_OUTOFMEMORY
;
1365 static WCHAR
*build_filename(BSTR hostname
)
1367 static const WCHAR store
[] = L
"\\Microsoft\\Internet Explorer\\DOMStore\\";
1368 WCHAR path
[MAX_PATH
], *ret
;
1371 if(!SHGetSpecialFolderPathW(NULL
, path
, CSIDL_LOCAL_APPDATA
, TRUE
)) {
1372 ERR("Can't get folder path %lu\n", GetLastError());
1377 if(len
+ ARRAY_SIZE(store
) > ARRAY_SIZE(path
)) {
1378 ERR("Path too long\n");
1381 memcpy(path
+ len
, store
, sizeof(store
));
1383 len
+= ARRAY_SIZE(store
);
1384 ret
= malloc((len
+ wcslen(hostname
) + ARRAY_SIZE(L
".xml")) * sizeof(WCHAR
));
1390 wcscat(ret
, hostname
);
1391 wcscat(ret
, L
".xml");
1396 static WCHAR
*build_mutexname(const WCHAR
*filename
)
1399 ret
= wcsdup(filename
);
1402 for(ptr
= ret
; *ptr
; ptr
++)
1403 if(*ptr
== '\\') *ptr
= '_';
1407 HRESULT
create_html_storage(HTMLInnerWindow
*window
, BOOL local
, IHTMLStorage
**p
)
1409 IUri
*uri
= window
->base
.outer_window
->uri
;
1410 BSTR origin
, hostname
= NULL
;
1411 HTMLStorage
*storage
;
1417 hres
= IUri_GetHost(uri
, &hostname
);
1419 SysFreeString(hostname
);
1423 storage
= calloc(1, sizeof(*storage
));
1425 SysFreeString(hostname
);
1426 return E_OUTOFMEMORY
;
1431 storage
->filename
= build_filename(hostname
);
1432 SysFreeString(hostname
);
1433 if(!storage
->filename
) {
1435 return E_OUTOFMEMORY
;
1437 mutexname
= build_mutexname(storage
->filename
);
1439 free(storage
->filename
);
1441 return E_OUTOFMEMORY
;
1443 storage
->mutex
= CreateMutexW(NULL
, FALSE
, mutexname
);
1445 if(!storage
->mutex
) {
1446 free(storage
->filename
);
1448 return HRESULT_FROM_WIN32(GetLastError());
1451 hres
= build_session_origin(uri
, hostname
, &origin
);
1452 SysFreeString(hostname
);
1457 storage
->session_storage
= grab_session_map_entry(origin
);
1458 SysFreeString(origin
);
1459 if(!storage
->session_storage
) {
1461 return E_OUTOFMEMORY
;
1465 storage
->IHTMLStorage_iface
.lpVtbl
= &HTMLStorageVtbl
;
1466 storage
->window
= window
;
1468 init_dispatch(&storage
->dispex
, &HTMLStorage_dispex
, dispex_compat_mode(&window
->event_target
.dispex
));
1470 *p
= &storage
->IHTMLStorage_iface
;
1474 void detach_html_storage(IHTMLStorage
*iface
)
1476 HTMLStorage
*storage
= impl_from_IHTMLStorage(iface
);
1477 storage
->window
= NULL
;