This implements sending StorageEvents to the same window/doc from where the storage change happened, which seems to be a thing on native IE. For some reason I was unable to have them actually sent to other windows, so that's not implemented since I've no idea how that even gets triggered to test it, and if it does it will probably be in a follow up MR, even though that's *supposed* to be the use of these events���
localStorage sends for `onstoragecommit` on the doc that did the change, while sessionStorage sends for `onstorage` on the window and doc that did the change, depending on compat mode. Although the event sent to the document's `onstorage` seems to be a legacy/old-style event (as tests show).
Tests are on the last patch since events have to be sent properly (else it hangs).
-- v3: mshtml/tests: Add tests for StorageEvents. mshtml: Send StorageEvents when removing an existing item. mshtml: Send StorageEvents when setting an item. mshtml: Implement StorageEvent and send it when clearing the storage. mshtml: Move the MessageEvent construction to a helper. include: Add IDOMStorageEvent and DispDOMStorageEvent interfaces.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- include/mshtmdid.h | 8 +++++ include/mshtml.idl | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+)
diff --git a/include/mshtmdid.h b/include/mshtmdid.h index f56fc4d6f83..4e25ad8036b 100644 --- a/include/mshtmdid.h +++ b/include/mshtmdid.h @@ -4716,6 +4716,14 @@ #define DISPID_IDOMPROGRESSEVENT_TOTAL DISPID_DOMPROGRESSEVENT+3 #define DISPID_IDOMPROGRESSEVENT_INITPROGRESSEVENT DISPID_DOMPROGRESSEVENT+4
+/* IDOMStorageEvent */ +#define DISPID_IDOMSTORAGEEVENT_KEY DISPID_DOMSTORAGEEVENT+1 +#define DISPID_IDOMSTORAGEEVENT_OLDVALUE DISPID_DOMSTORAGEEVENT+2 +#define DISPID_IDOMSTORAGEEVENT_NEWVALUE DISPID_DOMSTORAGEEVENT+3 +#define DISPID_IDOMSTORAGEEVENT_URL DISPID_DOMSTORAGEEVENT+4 +#define DISPID_IDOMSTORAGEEVENT_STORAGEAREA DISPID_DOMSTORAGEEVENT+5 +#define DISPID_IDOMSTORAGEEVENT_INITSTORAGEEVENT DISPID_DOMSTORAGEEVENT+6 + /* IHTMLControlElement */ #define DISPID_IHTMLCONTROLELEMENT_TABINDEX STDPROPID_XOBJ_TABINDEX #define DISPID_IHTMLCONTROLELEMENT_FOCUS (DISPID_SITE+0) diff --git a/include/mshtml.idl b/include/mshtml.idl index 6caa0c8ee5a..aa243897405 100644 --- a/include/mshtml.idl +++ b/include/mshtml.idl @@ -28661,6 +28661,84 @@ methods: [in] ULONGLONG totalArg); };
+/***************************************************************************** + * IDOMStorageEvent interface + */ +[ + odl, + oleautomation, + dual, + uuid(30510722-98b5-11cf-bb82-00aa00bdce0b) +] +interface IDOMStorageEvent : IDispatch +{ + [propget, id(DISPID_IDOMSTORAGEEVENT_KEY)] + HRESULT key([out, retval] BSTR *p); + + [propget, id(DISPID_IDOMSTORAGEEVENT_OLDVALUE)] + HRESULT oldValue([out, retval] BSTR *p); + + [propget, id(DISPID_IDOMSTORAGEEVENT_NEWVALUE)] + HRESULT newValue([out, retval] BSTR *p); + + [propget, id(DISPID_IDOMSTORAGEEVENT_URL)] + HRESULT url([out, retval] BSTR *p); + + [propget, id(DISPID_IDOMSTORAGEEVENT_STORAGEAREA)] + HRESULT storageArea([out, retval] IHTMLStorage **p); + + [id(DISPID_IDOMSTORAGEEVENT_INITSTORAGEEVENT)] + HRESULT initStorageEvent( + [in] BSTR eventType, + [in] VARIANT_BOOL canBubble, + [in] VARIANT_BOOL cancelable, + [in] BSTR keyArg, + [in] BSTR oldValueArg, + [in] BSTR newValueArg, + [in] BSTR urlArg, + [in] IHTMLStorage *storageAreaArg); +}; + +/***************************************************************************** + * DispDOMStorageEvent dispinterface + */ +[ + hidden, + uuid(30590093-98b5-11cf-bb82-00aa00bdce0b) +] +dispinterface DispDOMStorageEvent +{ +properties: +methods: + WINE_IDOMEVENT_DISPINTERFACE_DECL; + + [propget, id(DISPID_IDOMSTORAGEEVENT_KEY)] + BSTR key(); + + [propget, id(DISPID_IDOMSTORAGEEVENT_OLDVALUE)] + BSTR oldValue(); + + [propget, id(DISPID_IDOMSTORAGEEVENT_NEWVALUE)] + BSTR newValue(); + + [propget, id(DISPID_IDOMSTORAGEEVENT_URL)] + BSTR url(); + + [propget, id(DISPID_IDOMSTORAGEEVENT_STORAGEAREA)] + IHTMLStorage *storageArea(); + + [id(DISPID_IDOMSTORAGEEVENT_INITSTORAGEEVENT)] + void initStorageEvent( + [in] BSTR eventType, + [in] VARIANT_BOOL canBubble, + [in] VARIANT_BOOL cancelable, + [in] BSTR keyArg, + [in] BSTR oldValueArg, + [in] BSTR newValueArg, + [in] BSTR urlArg, + [in] IHTMLStorage *storageAreaArg); +}; + /***************************************************************************** * IHTMLNamespaceCollection interface */
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlevent.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index 149611174f0..a0b024fd65f 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -2790,6 +2790,15 @@ static DOMEvent *progress_event_ctor(void *iface, nsIDOMEvent *nsevent, eventid_ return &progress_event->event; }
+static DOMEvent *message_event_ctor(nsIDOMEvent *nsevent, eventid_t event_id, compat_mode_t compat_mode) +{ + DOMMessageEvent *message_event = event_ctor(sizeof(DOMMessageEvent), &DOMMessageEvent_dispex, + DOMMessageEvent_query_interface, DOMMessageEvent_destroy, nsevent, event_id, compat_mode); + if(!message_event) return NULL; + message_event->IDOMMessageEvent_iface.lpVtbl = &DOMMessageEventVtbl; + return &message_event->event; +} + static DOMEvent *alloc_event(nsIDOMEvent *nsevent, compat_mode_t compat_mode, eventid_t event_id) { static const struct { @@ -2805,14 +2814,8 @@ static DOMEvent *alloc_event(nsIDOMEvent *nsevent, compat_mode_t compat_mode, ev DOMEvent *event; unsigned i;
- if(event_id == EVENTID_MESSAGE) { - DOMMessageEvent *message_event = event_ctor(sizeof(DOMMessageEvent), &DOMMessageEvent_dispex, - DOMMessageEvent_query_interface, DOMMessageEvent_destroy, nsevent, event_id, compat_mode); - if(!message_event) - return NULL; - message_event->IDOMMessageEvent_iface.lpVtbl = &DOMMessageEventVtbl; - return &message_event->event; - } + if(event_id == EVENTID_MESSAGE) + return message_event_ctor(nsevent, event_id, compat_mode);
for(i = 0; i < ARRAY_SIZE(types_table); i++) { void *iface;
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmldoc.c | 24 ++-- dlls/mshtml/htmlevent.c | 225 ++++++++++++++++++++++++++++++++++- dlls/mshtml/htmlevent.h | 3 + dlls/mshtml/htmlstorage.c | 65 +++++++++- dlls/mshtml/htmlwindow.c | 2 +- dlls/mshtml/mshtml_private.h | 2 + 6 files changed, 309 insertions(+), 12 deletions(-)
diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c index fe743e666c1..413a3a4054e 100644 --- a/dlls/mshtml/htmldoc.c +++ b/dlls/mshtml/htmldoc.c @@ -3423,30 +3423,38 @@ static HRESULT WINAPI HTMLDocument6_get_onstorage(IHTMLDocument6 *iface, VARIANT *p) { HTMLDocument *This = impl_from_IHTMLDocument6(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + + TRACE("(%p)->(%p)\n", This, p); + + return get_doc_event(This, EVENTID_STORAGE, p); }
static HRESULT WINAPI HTMLDocument6_put_onstorage(IHTMLDocument6 *iface, VARIANT v) { HTMLDocument *This = impl_from_IHTMLDocument6(iface); - FIXME("(%p)->(%s)\n", This, debugstr_variant(&v)); - return E_NOTIMPL; + + TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); + + return set_doc_event(This, EVENTID_STORAGE, &v); }
static HRESULT WINAPI HTMLDocument6_get_onstoragecommit(IHTMLDocument6 *iface, VARIANT *p) { HTMLDocument *This = impl_from_IHTMLDocument6(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + + TRACE("(%p)->(%p)\n", This, p); + + return get_doc_event(This, EVENTID_STORAGECOMMIT, p); }
static HRESULT WINAPI HTMLDocument6_put_onstoragecommit(IHTMLDocument6 *iface, VARIANT v) { HTMLDocument *This = impl_from_IHTMLDocument6(iface); - FIXME("(%p)->(%s)\n", This, debugstr_variant(&v)); - return E_NOTIMPL; + + TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); + + return set_doc_event(This, EVENTID_STORAGECOMMIT, &v); }
static HRESULT WINAPI HTMLDocument6_getElementById(IHTMLDocument6 *iface, diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index a0b024fd65f..5502a1d041d 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -191,6 +191,10 @@ static const event_info_t event_info[] = { EVENT_FIXME}, {L"selectstart", EVENT_TYPE_EVENT, DISPID_EVMETH_ONSELECTSTART, EVENT_FIXME | EVENT_BUBBLES | EVENT_CANCELABLE}, + {L"storage", EVENT_TYPE_EVENT, DISPID_EVMETH_ONSTORAGE, + 0}, + {L"storagecommit", EVENT_TYPE_EVENT, DISPID_EVMETH_ONSTORAGECOMMIT, + 0}, {L"submit", EVENT_TYPE_EVENT, DISPID_EVMETH_ONSUBMIT, EVENT_DEFAULTLISTENER | EVENT_HASDEFAULTHANDLERS | EVENT_BUBBLES | EVENT_CANCELABLE}, {L"timeout", EVENT_TYPE_PROGRESS, DISPID_EVPROP_TIMEOUT, @@ -2606,6 +2610,164 @@ static void DOMProgressEvent_destroy(DOMEvent *event) nsIDOMProgressEvent_Release(This->nsevent); }
+typedef struct { + DOMEvent event; + IDOMStorageEvent IDOMStorageEvent_iface; + BSTR key; + BSTR old_value; + BSTR new_value; +} DOMStorageEvent; + +static inline DOMStorageEvent *impl_from_IDOMStorageEvent(IDOMStorageEvent *iface) +{ + return CONTAINING_RECORD(iface, DOMStorageEvent, IDOMStorageEvent_iface); +} + +static HRESULT WINAPI DOMStorageEvent_QueryInterface(IDOMStorageEvent *iface, REFIID riid, void **ppv) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + return IDOMEvent_QueryInterface(&This->event.IDOMEvent_iface, riid, ppv); +} + +static ULONG WINAPI DOMStorageEvent_AddRef(IDOMStorageEvent *iface) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + return IDOMEvent_AddRef(&This->event.IDOMEvent_iface); +} + +static ULONG WINAPI DOMStorageEvent_Release(IDOMStorageEvent *iface) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + return IDOMEvent_Release(&This->event.IDOMEvent_iface); +} + +static HRESULT WINAPI DOMStorageEvent_GetTypeInfoCount(IDOMStorageEvent *iface, UINT *pctinfo) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + return IDispatchEx_GetTypeInfoCount(&This->event.dispex.IDispatchEx_iface, pctinfo); +} + +static HRESULT WINAPI DOMStorageEvent_GetTypeInfo(IDOMStorageEvent *iface, UINT iTInfo, + LCID lcid, ITypeInfo **ppTInfo) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + return IDispatchEx_GetTypeInfo(&This->event.dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo); +} + +static HRESULT WINAPI DOMStorageEvent_GetIDsOfNames(IDOMStorageEvent *iface, REFIID riid, + LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + return IDispatchEx_GetIDsOfNames(&This->event.dispex.IDispatchEx_iface, riid, rgszNames, cNames, + lcid, rgDispId); +} + +static HRESULT WINAPI DOMStorageEvent_Invoke(IDOMStorageEvent *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + return IDispatchEx_Invoke(&This->event.dispex.IDispatchEx_iface, dispIdMember, riid, lcid, + wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); +} + +static HRESULT WINAPI DOMStorageEvent_get_key(IDOMStorageEvent *iface, BSTR *p) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + + TRACE("(%p)->(%p)\n", This, p); + + if(This->key) + return (*p = SysAllocStringLen(This->key, SysStringLen(This->key))) ? S_OK : E_OUTOFMEMORY; + *p = NULL; + return S_OK; +} + +static HRESULT WINAPI DOMStorageEvent_get_oldValue(IDOMStorageEvent *iface, BSTR *p) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + + TRACE("(%p)->(%p)\n", This, p); + + if(This->old_value) + return (*p = SysAllocStringLen(This->old_value, SysStringLen(This->old_value))) ? S_OK : E_OUTOFMEMORY; + *p = NULL; + return S_OK; +} + +static HRESULT WINAPI DOMStorageEvent_get_newValue(IDOMStorageEvent *iface, BSTR *p) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + + TRACE("(%p)->(%p)\n", This, p); + + if(This->new_value) + return (*p = SysAllocStringLen(This->new_value, SysStringLen(This->new_value))) ? S_OK : E_OUTOFMEMORY; + *p = NULL; + return S_OK; +} + +static HRESULT WINAPI DOMStorageEvent_get_url(IDOMStorageEvent *iface, BSTR *p) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + FIXME("(%p)->(%p)\n", This, p); + return E_NOTIMPL; +} + +static HRESULT WINAPI DOMStorageEvent_get_storageArea(IDOMStorageEvent *iface, IHTMLStorage **p) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + FIXME("(%p)->(%p)\n", This, p); + return E_NOTIMPL; +} + +static HRESULT WINAPI DOMStorageEvent_initStorageEvent(IDOMStorageEvent *iface, BSTR type, VARIANT_BOOL can_bubble, + VARIANT_BOOL cancelable, BSTR keyArg, BSTR oldValueArg, + BSTR newValueArg, BSTR urlArg, IHTMLStorage *storageAreaArg) +{ + DOMStorageEvent *This = impl_from_IDOMStorageEvent(iface); + FIXME("(%p)->(%s %x %x %s %s %s %s %p)\n", This, debugstr_w(type), can_bubble, cancelable, + debugstr_w(keyArg), debugstr_w(oldValueArg), debugstr_w(newValueArg), debugstr_w(urlArg), storageAreaArg); + return E_NOTIMPL; +} + +static const IDOMStorageEventVtbl DOMStorageEventVtbl = { + DOMStorageEvent_QueryInterface, + DOMStorageEvent_AddRef, + DOMStorageEvent_Release, + DOMStorageEvent_GetTypeInfoCount, + DOMStorageEvent_GetTypeInfo, + DOMStorageEvent_GetIDsOfNames, + DOMStorageEvent_Invoke, + DOMStorageEvent_get_key, + DOMStorageEvent_get_oldValue, + DOMStorageEvent_get_newValue, + DOMStorageEvent_get_url, + DOMStorageEvent_get_storageArea, + DOMStorageEvent_initStorageEvent +}; + +static DOMStorageEvent *DOMStorageEvent_from_DOMEvent(DOMEvent *event) +{ + return CONTAINING_RECORD(event, DOMStorageEvent, event); +} + +static void *DOMStorageEvent_query_interface(DOMEvent *event, REFIID riid) +{ + DOMStorageEvent *storage_event = DOMStorageEvent_from_DOMEvent(event); + if(IsEqualGUID(&IID_IDOMStorageEvent, riid)) + return &storage_event->IDOMStorageEvent_iface; + return NULL; +} + +static void DOMStorageEvent_destroy(DOMEvent *event) +{ + DOMStorageEvent *storage_event = DOMStorageEvent_from_DOMEvent(event); + SysFreeString(storage_event->key); + SysFreeString(storage_event->old_value); + SysFreeString(storage_event->new_value); +} + static const tid_t DOMEvent_iface_tids[] = { IDOMEvent_tid, 0 @@ -2698,6 +2860,19 @@ dispex_static_data_t DOMProgressEvent_dispex = { DOMProgressEvent_iface_tids };
+static const tid_t DOMStorageEvent_iface_tids[] = { + IDOMEvent_tid, + IDOMStorageEvent_tid, + 0 +}; + +dispex_static_data_t DOMStorageEvent_dispex = { + L"StorageEvent", + NULL, + DispDOMStorageEvent_tid, + DOMStorageEvent_iface_tids +}; + static void *event_ctor(unsigned size, dispex_static_data_t *dispex_data, void *(*query_interface)(DOMEvent*,REFIID), void (*destroy)(DOMEvent*), nsIDOMEvent *nsevent, eventid_t event_id, compat_mode_t compat_mode) { @@ -2799,6 +2974,15 @@ static DOMEvent *message_event_ctor(nsIDOMEvent *nsevent, eventid_t event_id, co return &message_event->event; }
+static DOMEvent *storage_event_ctor(nsIDOMEvent *nsevent, eventid_t event_id, compat_mode_t compat_mode) +{ + DOMStorageEvent *storage_event = event_ctor(sizeof(DOMStorageEvent), &DOMStorageEvent_dispex, + DOMStorageEvent_query_interface, DOMStorageEvent_destroy, nsevent, event_id, compat_mode); + if(!storage_event) return NULL; + storage_event->IDOMStorageEvent_iface.lpVtbl = &DOMStorageEventVtbl; + return &storage_event->event; +} + static DOMEvent *alloc_event(nsIDOMEvent *nsevent, compat_mode_t compat_mode, eventid_t event_id) { static const struct { @@ -2814,8 +2998,12 @@ static DOMEvent *alloc_event(nsIDOMEvent *nsevent, compat_mode_t compat_mode, ev DOMEvent *event; unsigned i;
- if(event_id == EVENTID_MESSAGE) - return message_event_ctor(nsevent, event_id, compat_mode); + switch(event_id) { + case EVENTID_MESSAGE: return message_event_ctor(nsevent, event_id, compat_mode); + case EVENTID_STORAGECOMMIT: + case EVENTID_STORAGE: return storage_event_ctor(nsevent, event_id, compat_mode); + default: break; + }
for(i = 0; i < ARRAY_SIZE(types_table); i++) { void *iface; @@ -2931,6 +3119,39 @@ HRESULT create_message_event(HTMLDocumentNode *doc, VARIANT *data, DOMEvent **re return S_OK; }
+/* Takes ownership of old_value, do not free it on caller side! */ +HRESULT create_storage_event(HTMLDocumentNode *doc, BSTR key, BSTR old_value, BSTR new_value, + BOOL commit, DOMEvent **ret) +{ + DOMStorageEvent *storage_event; + DOMEvent *event; + HRESULT hres; + + hres = create_document_event(doc, commit ? EVENTID_STORAGECOMMIT : EVENTID_STORAGE, &event); + if(FAILED(hres)) { + SysFreeString(old_value); + return hres; + } + storage_event = DOMStorageEvent_from_DOMEvent(event); + + if(commit) + SysFreeString(old_value); + else { + storage_event->old_value = old_value; + if(key && !(storage_event->key = SysAllocString(key))) + goto fail; + if(new_value && !(storage_event->new_value = SysAllocString(new_value))) + goto fail; + } + + *ret = event; + return S_OK; + +fail: + IDOMEvent_Release(&event->IDOMEvent_iface); + return E_OUTOFMEMORY; +} + static HRESULT call_disp_func(IDispatch *disp, DISPPARAMS *dp, VARIANT *retv) { IDispatchEx *dispex; diff --git a/dlls/mshtml/htmlevent.h b/dlls/mshtml/htmlevent.h index ed11e660789..ee0c7831009 100644 --- a/dlls/mshtml/htmlevent.h +++ b/dlls/mshtml/htmlevent.h @@ -58,6 +58,8 @@ typedef enum { EVENTID_SCROLL, EVENTID_SELECTIONCHANGE, EVENTID_SELECTSTART, + EVENTID_STORAGE, + EVENTID_STORAGECOMMIT, EVENTID_SUBMIT, EVENTID_TIMEOUT, EVENTID_UNLOAD, @@ -112,6 +114,7 @@ HRESULT create_document_event(HTMLDocumentNode*,eventid_t,DOMEvent**) DECLSPEC_H HRESULT create_document_event_str(HTMLDocumentNode*,const WCHAR*,IDOMEvent**) DECLSPEC_HIDDEN; HRESULT create_event_from_nsevent(nsIDOMEvent*,compat_mode_t,DOMEvent**) DECLSPEC_HIDDEN; HRESULT create_message_event(HTMLDocumentNode*,VARIANT*,DOMEvent**) DECLSPEC_HIDDEN; +HRESULT create_storage_event(HTMLDocumentNode*,BSTR,BSTR,BSTR,BOOL,DOMEvent**) DECLSPEC_HIDDEN;
void init_nsevents(HTMLDocumentNode*) DECLSPEC_HIDDEN; void release_nsevents(HTMLDocumentNode*) DECLSPEC_HIDDEN; diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index 5d56037ff04..7956fb35367 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -29,6 +29,7 @@ #include "wine/debug.h"
#include "mshtml_private.h" +#include "htmlevent.h"
WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
@@ -41,6 +42,7 @@ typedef struct { LONG ref; unsigned num_props; BSTR *props; + HTMLInnerWindow *window; struct session_map_entry *session_storage; WCHAR *filename; HANDLE mutex; @@ -190,6 +192,60 @@ static void release_props(HTMLStorage *This) heap_free(This->props); }
+struct storage_event_task { + task_t header; + HTMLInnerWindow *window; + DOMEvent *event; +}; + +static void storage_event_proc(task_t *_task) +{ + struct storage_event_task *task = (struct storage_event_task*)_task; + HTMLInnerWindow *window = task->window; + DOMEvent *event = task->event; + VARIANT_BOOL cancelled; + + if(event->event_id == EVENTID_STORAGE) { + if(dispex_compat_mode(&window->event_target.dispex) >= COMPAT_MODE_IE9) + dispatch_event(&window->event_target, event); + if(window->doc) + fire_event(&window->doc->node, L"onstorage", NULL, &cancelled); + }else if(window->doc) { + dispatch_event(&window->doc->node.event_target, event); + } +} + +static void storage_event_destr(task_t *_task) +{ + struct storage_event_task *task = (struct storage_event_task*)_task; + IDOMEvent_Release(&task->event->IDOMEvent_iface); + IHTMLWindow2_Release(&task->window->base.IHTMLWindow2_iface); +} + +/* Takes ownership of old_value */ +static HRESULT send_storage_event(HTMLStorage *storage, BSTR key, BSTR old_value, BSTR new_value) +{ + HTMLInnerWindow *window = storage->window; + BOOL local = !!storage->filename; + struct storage_event_task *task; + DOMEvent *event; + HRESULT hres; + + hres = create_storage_event(window->doc, key, old_value, new_value, local, &event); + if(FAILED(hres)) + return hres; + + if(!(task = heap_alloc(sizeof(*task)))) { + IDOMEvent_Release(&event->IDOMEvent_iface); + return E_OUTOFMEMORY; + } + + task->event = event; + task->window = window; + IHTMLWindow2_AddRef(&task->window->base.IHTMLWindow2_iface); + return push_task(&task->header, storage_event_proc, storage_event_destr, window->task_magic); +} + static inline HTMLStorage *impl_from_IHTMLStorage(IHTMLStorage *iface) { return CONTAINING_RECORD(iface, HTMLStorage, IHTMLStorage_iface); @@ -235,6 +291,7 @@ static ULONG WINAPI HTMLStorage_Release(IHTMLStorage *iface) TRACE("(%p) ref=%ld\n", This, ref);
if(!ref) { + IHTMLWindow2_Release(&This->window->base.IHTMLWindow2_iface); release_session_map_entry(This->session_storage); release_dispex(&This->dispex); heap_free(This->filename); @@ -817,8 +874,9 @@ static HRESULT WINAPI HTMLStorage_clear(IHTMLStorage *iface) HRESULT hres = S_OK;
if(!This->filename) { + UINT num_keys = This->session_storage->num_keys; clear_session_storage(This->session_storage); - return S_OK; + return num_keys ? send_storage_event(This, NULL, NULL, NULL) : S_OK; }
WaitForSingleObject(This->mutex, INFINITE); @@ -828,6 +886,8 @@ static HRESULT WINAPI HTMLStorage_clear(IHTMLStorage *iface) hres = HRESULT_FROM_WIN32(error); } ReleaseMutex(This->mutex); + if(hres == S_OK) + hres = send_storage_event(This, NULL, NULL, NULL); return hres; }
@@ -1266,6 +1326,9 @@ HRESULT create_html_storage(HTMLInnerWindow *window, BOOL local, IHTMLStorage **
storage->IHTMLStorage_iface.lpVtbl = &HTMLStorageVtbl; storage->ref = 1; + storage->window = window; + IHTMLWindow2_AddRef(&window->base.IHTMLWindow2_iface); + init_dispatch(&storage->dispex, (IUnknown*)&storage->IHTMLStorage_iface, &HTMLStorage_dispex, dispex_compat_mode(&window->event_target.dispex));
diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index 00eef4d846a..d9941677c47 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -2613,7 +2613,7 @@ HTMLWINDOW7_ONEVENT_PROPERTY_STUB(seeked) HTMLWINDOW7_ONEVENT_PROPERTY_STUB(seeking) HTMLWINDOW7_ONEVENT_PROPERTY_STUB(select) HTMLWINDOW7_ONEVENT_PROPERTY_STUB(stalled) -HTMLWINDOW7_ONEVENT_PROPERTY_STUB(storage) +HTMLWINDOW7_ONEVENT_PROPERTY_IMPL(storage, EVENTID_STORAGE) HTMLWINDOW7_ONEVENT_PROPERTY_IMPL(submit, EVENTID_SUBMIT) HTMLWINDOW7_ONEVENT_PROPERTY_STUB(suspend) HTMLWINDOW7_ONEVENT_PROPERTY_STUB(timeupdate) diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 85f34bb4a9f..59a830f484a 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -91,6 +91,7 @@ typedef struct EventTarget EventTarget; XDIID(DispDOMMessageEvent) \ XDIID(DispDOMMouseEvent) \ XDIID(DispDOMProgressEvent) \ + XDIID(DispDOMStorageEvent) \ XDIID(DispDOMUIEvent) \ XDIID(DispDOMDocumentType) \ XDIID(DispHTMLAnchorElement) \ @@ -154,6 +155,7 @@ typedef struct EventTarget EventTarget; XIID(IDOMMessageEvent) \ XIID(IDOMMouseEvent) \ XIID(IDOMProgressEvent) \ + XIID(IDOMStorageEvent) \ XIID(IDOMUIEvent) \ XIID(IDOMDocumentType) \ XIID(IDocumentEvent) \
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstorage.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index 7956fb35367..fa1e773bf71 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -698,7 +698,7 @@ static HRESULT save_document(IXMLDOMDocument *doc, const WCHAR *filename) return hres; }
-static HRESULT set_item(const WCHAR *filename, BSTR key, BSTR value) +static HRESULT set_item(const WCHAR *filename, BSTR key, BSTR value, BSTR *old_value) { IXMLDOMDocument *doc; IXMLDOMNode *root = NULL, *node = NULL; @@ -706,6 +706,7 @@ static HRESULT set_item(const WCHAR *filename, BSTR key, BSTR value) BSTR query = NULL; HRESULT hres;
+ *old_value = NULL; hres = open_document(filename, &doc); if(hres != S_OK) return hres; @@ -722,10 +723,20 @@ static HRESULT set_item(const WCHAR *filename, BSTR key, BSTR value)
hres = IXMLDOMNode_selectSingleNode(root, query, &node); if(hres == S_OK) { + VARIANT old; + hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem); if(hres != S_OK) goto done;
+ hres = IXMLDOMElement_getAttribute(elem, (BSTR)L"value", &old); + if(hres == S_OK) { + if(V_VT(&old) == VT_BSTR) + *old_value = V_BSTR(&old); + else + VariantClear(&old); + } + hres = set_attribute(elem, L"value", value); if(hres != S_OK) goto done; @@ -760,6 +771,8 @@ done: if(elem) IXMLDOMElement_Release(elem); IXMLDOMDocument_Release(doc); + if(hres != S_OK) + SysFreeString(*old_value); return hres; }
@@ -767,6 +780,7 @@ static HRESULT WINAPI HTMLStorage_setItem(IHTMLStorage *iface, BSTR bstrKey, BST { HTMLStorage *This = impl_from_IHTMLStorage(iface); struct session_entry *session_entry; + BSTR old_value; HRESULT hres;
TRACE("(%p)->(%s %s)\n", This, debugstr_w(bstrKey), debugstr_w(bstrValue)); @@ -787,16 +801,20 @@ static HRESULT WINAPI HTMLStorage_setItem(IHTMLStorage *iface, BSTR bstrKey, BST return E_OUTOFMEMORY; /* native returns this when quota is exceeded */ } This->session_storage->quota -= value_len - old_len; - SysFreeString(session_entry->value); + old_value = session_entry->value; session_entry->value = value; + + hres = send_storage_event(This, bstrKey, old_value, value); } return hres; }
WaitForSingleObject(This->mutex, INFINITE); - hres = set_item(This->filename, bstrKey, bstrValue); + hres = set_item(This->filename, bstrKey, bstrValue, &old_value); ReleaseMutex(This->mutex);
+ if(hres == S_OK) + hres = send_storage_event(This, bstrKey, old_value, bstrValue); return hres; }
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstorage.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index fa1e773bf71..2b8aa0a295f 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -818,13 +818,15 @@ static HRESULT WINAPI HTMLStorage_setItem(IHTMLStorage *iface, BSTR bstrKey, BST return hres; }
-static HRESULT remove_item(const WCHAR *filename, BSTR key) +static HRESULT remove_item(const WCHAR *filename, BSTR key, BSTR *old_value, BOOL *changed) { IXMLDOMDocument *doc; IXMLDOMNode *root = NULL, *node = NULL; BSTR query = NULL; HRESULT hres;
+ *old_value = NULL; + *changed = FALSE; hres = open_document(filename, &doc); if(hres != S_OK) return hres; @@ -841,6 +843,22 @@ static HRESULT remove_item(const WCHAR *filename, BSTR key)
hres = IXMLDOMNode_selectSingleNode(root, query, &node); if(hres == S_OK) { + IXMLDOMElement *elem; + VARIANT old; + + hres = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&elem); + if(hres == S_OK) { + hres = IXMLDOMElement_getAttribute(elem, (BSTR)L"value", &old); + if(hres == S_OK) { + if(V_VT(&old) == VT_BSTR) + *old_value = V_BSTR(&old); + else + VariantClear(&old); + } + IXMLDOMElement_Release(elem); + *changed = TRUE; + } + hres = IXMLDOMNode_removeChild(root, node, NULL); if(hres != S_OK) goto done; @@ -855,6 +873,8 @@ done: if(node) IXMLDOMNode_Release(node); IXMLDOMDocument_Release(doc); + if(hres != S_OK || !changed) + SysFreeString(*old_value); return hres; }
@@ -862,7 +882,9 @@ static HRESULT WINAPI HTMLStorage_removeItem(IHTMLStorage *iface, BSTR bstrKey) { HTMLStorage *This = impl_from_IHTMLStorage(iface); struct session_entry *session_entry; + BSTR old_value; HRESULT hres; + BOOL changed;
TRACE("(%p)->(%s)\n", This, debugstr_w(bstrKey));
@@ -873,16 +895,20 @@ static HRESULT WINAPI HTMLStorage_removeItem(IHTMLStorage *iface, BSTR bstrKey) This->session_storage->num_keys--; list_remove(&session_entry->list_entry); wine_rb_remove(&This->session_storage->data_map, &session_entry->entry); - SysFreeString(session_entry->value); + old_value = session_entry->value; heap_free(session_entry); + + hres = send_storage_event(This, bstrKey, old_value, NULL); } return hres; }
WaitForSingleObject(This->mutex, INFINITE); - hres = remove_item(This->filename, bstrKey); + hres = remove_item(This->filename, bstrKey, &old_value, &changed); ReleaseMutex(This->mutex);
+ if(hres == S_OK && changed) + hres = send_storage_event(This, bstrKey, old_value, NULL); return hres; }
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/tests/script.c | 478 ++++++++++++++++++++++++++++++++++++- 1 file changed, 471 insertions(+), 7 deletions(-)
diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index 14e3a148239..80f34a6606e 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -62,7 +62,7 @@ const GUID GUID_CUSTOM_CONFIRMOBJECTSAFETY = #endif
#define DEFINE_EXPECT(func) \ - static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + static BOOL expect_ ## func, called_ ## func
#define SET_EXPECT(func) \ do { called_ ## func = FALSE; expect_ ## func = TRUE; } while(0) @@ -137,6 +137,9 @@ DEFINE_EXPECT(QS_IActiveScriptSite); DEFINE_EXPECT(QS_GetCaller); DEFINE_EXPECT(ChangeType); DEFINE_EXPECT(GetTypeInfo); +DEFINE_EXPECT(doc_onstorage[2]); +DEFINE_EXPECT(doc_onstoragecommit[2]); +DEFINE_EXPECT(window_onstorage[2]);
#define TESTSCRIPT_CLSID "{178fc163-f585-4e24-9c13-4bb7faf80746}" #define TESTACTIVEX_CLSID "{178fc163-f585-4e24-9c13-4bb7faf80646}" @@ -3737,7 +3740,7 @@ static void test_simple_script(void) CHECK_CALLED(Close); }
-static void run_from_moniker(IMoniker *mon) +static void run_from_moniker(IMoniker *mon, IHTMLDocument2 **ret_doc) { IPersistMoniker *persist; IHTMLDocument2 *doc; @@ -3768,6 +3771,11 @@ static void run_from_moniker(IMoniker *mon)
CHECK_CALLED(external_success);
+ if(ret_doc) { + *ret_doc = doc; + return; + } + free_registered_streams(); set_client_site(doc, FALSE); IHTMLDocument2_Release(doc); @@ -3788,12 +3796,12 @@ static void run_js_script(const char *test_name)
hres = CreateURLMoniker(NULL, url, &mon); ok(hres == S_OK, "CreateURLMoniker failed: %08lx\n", hres); - run_from_moniker(mon); + run_from_moniker(mon, NULL);
IMoniker_Release(mon); }
-static void run_from_path(const WCHAR *path, const char *opt) +static void run_from_path(const WCHAR *path, const char *opt, IHTMLDocument2 **doc) { WCHAR buf[255] = L"http://winetest.example.org"; IMoniker *mon; @@ -3808,11 +3816,462 @@ static void run_from_path(const WCHAR *path, const char *opt) SysFreeString(url); ok(hres == S_OK, "CreateUrlMoniker failed: %08lx\n", hres);
- run_from_moniker(mon); + run_from_moniker(mon, doc);
IMoniker_Release(mon); }
+static unsigned onstorage_expect_line, onstorage_expect_document_mode; +static const WCHAR *onstorage_expect_key, *onstorage_expect_old_value, *onstorage_expect_new_value; + +#define check_local_onstorage(a,b,c,d) _check_onstorage(a,b,c,d,TRUE,document_mode,__LINE__) +#define check_session_onstorage(a,b,c,d) _check_onstorage(a,b,c,d,FALSE,document_mode,__LINE__) +static void _check_onstorage(unsigned idx, const WCHAR *key, const WCHAR *old_value, + const WCHAR *new_value, BOOL local, unsigned document_mode, unsigned line) +{ + BOOL *const *expect, *const *called; + MSG msg; + + onstorage_expect_line = line; + onstorage_expect_document_mode = document_mode; + onstorage_expect_key = key; + onstorage_expect_old_value = old_value; + onstorage_expect_new_value = new_value; + + if(local) { + static BOOL *const _expect[] = { expect_doc_onstoragecommit, NULL }; + static BOOL *const _called[] = { called_doc_onstoragecommit, NULL }; + expect = _expect, called = _called; + }else { + if(document_mode < 9) { + static BOOL *const _expect[] = { expect_doc_onstorage, NULL }; + static BOOL *const _called[] = { called_doc_onstorage, NULL }; + expect = _expect, called = _called; + }else { + static BOOL *const _expect[] = { expect_window_onstorage, expect_doc_onstorage, NULL }; + static BOOL *const _called[] = { called_window_onstorage, called_doc_onstorage, NULL }; + expect = _expect, called = _called; + } + } + + (*expect)[idx] = TRUE, (*called)[idx] = FALSE; + do { + while(!(*called)[idx] && GetMessageW(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } while(*++expect && *++called); + CLEAR_CALLED(doc_onstorage[0]); + CLEAR_CALLED(doc_onstoragecommit[0]); + CLEAR_CALLED(window_onstorage[0]); + CLEAR_CALLED(doc_onstorage[1]); + CLEAR_CALLED(doc_onstoragecommit[1]); + CLEAR_CALLED(window_onstorage[1]); +} + +static void test_storage_event(DISPPARAMS *params, BOOL commit) +{ + const WCHAR *expect_key = onstorage_expect_key, *expect_old_value = onstorage_expect_old_value, *expect_new_value = onstorage_expect_new_value; + unsigned line = onstorage_expect_line; + BOOL doc_onstorage = TRUE; + IHTMLEventObj *event_obj; + IDOMStorageEvent *event; + IHTMLWindow2 *window; + IDispatchEx *dispex; + HRESULT hres; + unsigned i; + DISPID id; + BSTR bstr; + + if(onstorage_expect_document_mode < 9) { + ok_(__FILE__,line)(params->cArgs == 1, "cArgs = %u\n", params->cArgs); + ok_(__FILE__,line)(V_VT(¶ms->rgvarg[0]) == VT_DISPATCH, "V_VT(this) = %d\n", V_VT(¶ms->rgvarg[0])); + return; + } + + if(commit) { + expect_key = NULL; + expect_old_value = NULL; + expect_new_value = NULL; + doc_onstorage = FALSE; + } + + ok_(__FILE__,line)(params->cArgs == 2, "cArgs = %u\n", params->cArgs); + ok_(__FILE__,line)(V_VT(¶ms->rgvarg[1]) == VT_DISPATCH, "V_VT(event) = %d\n", V_VT(¶ms->rgvarg[1])); + hres = IDispatch_QueryInterface(V_DISPATCH(¶ms->rgvarg[1]), &IID_IDispatchEx, (void**)&dispex); + ok_(__FILE__,line)(hres == S_OK, "Could not get IDispatchEx: %08lx\n", hres); + + hres = IDispatch_QueryInterface(V_DISPATCH(¶ms->rgvarg[0]), &IID_IHTMLWindow2, (void**)&window); + if(hres == S_OK) { + doc_onstorage = FALSE; + IHTMLWindow2_Release(window); + } + + hres = IDispatchEx_QueryInterface(dispex, &IID_IDOMStorageEvent, (void**)&event); + if(doc_onstorage) { + static const WCHAR *props[] = { L"key", L"oldValue", L"newValue", L"storageArea" }; + ok_(__FILE__,line)(hres != S_OK, "Got IDOMStorageEvent in document.onstorage handler\n"); + + hres = IDispatchEx_QueryInterface(dispex, &IID_IHTMLEventObj, (void**)&event_obj); + ok_(__FILE__,line)(hres == S_OK, "Could not get IHTMLEventObj: %08lx\n", hres); + IHTMLEventObj_Release(event_obj); + + for(i = 0; i < ARRAY_SIZE(props); i++) { + bstr = SysAllocString(props[i]); + hres = IDispatchEx_GetDispID(dispex, bstr, 0, &id); + ok_(__FILE__,line)(hres == DISP_E_UNKNOWNNAME, "GetDispID(%s) failed: %08lx\n", wine_dbgstr_w(bstr), hres); + SysFreeString(bstr); + } + + IDispatchEx_Release(dispex); + return; + } + + ok_(__FILE__,line)(hres == S_OK, "Could not get IDOMStorageEvent: %08lx\n", hres); + IDispatchEx_Release(dispex); + + hres = IDOMStorageEvent_get_key(event, &bstr); + ok_(__FILE__,line)(hres == S_OK, "get_key failed: %08lx\n", hres); + ok_(__FILE__,line)((!bstr || !expect_key) ? (bstr == expect_key) : !wcscmp(bstr, expect_key), + "key = %s\n", wine_dbgstr_w(bstr)); + SysFreeString(bstr); + + hres = IDOMStorageEvent_get_oldValue(event, &bstr); + ok_(__FILE__,line)(hres == S_OK, "get_oldValue failed: %08lx\n", hres); + ok_(__FILE__,line)((!bstr || !expect_old_value) ? (bstr == expect_old_value) : !wcscmp(bstr, expect_old_value), + "oldValue = %s\n", wine_dbgstr_w(bstr)); + SysFreeString(bstr); + + hres = IDOMStorageEvent_get_newValue(event, &bstr); + ok_(__FILE__,line)(hres == S_OK, "get_newValue failed: %08lx\n", hres); + ok_(__FILE__,line)((!bstr || !expect_new_value) ? (bstr == expect_new_value) : !wcscmp(bstr, expect_new_value), + "newValue = %s\n", wine_dbgstr_w(bstr)); + SysFreeString(bstr); + + IDOMStorageEvent_Release(event); +} + +static HRESULT WINAPI doc1_onstorage_func(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + test_storage_event(pdp, FALSE); + CHECK_EXPECT(doc_onstorage[0]); + return S_OK; +} + +static const IDispatchExVtbl doc1_onstorage_vtbl = { + DispatchEx_QueryInterface, + DispatchEx_AddRef, + DispatchEx_Release, + DispatchEx_GetTypeInfoCount, + DispatchEx_GetTypeInfo, + DispatchEx_GetIDsOfNames, + DispatchEx_Invoke, + DispatchEx_GetDispID, + doc1_onstorage_func, + DispatchEx_DeleteMemberByName, + DispatchEx_DeleteMemberByDispID, + DispatchEx_GetMemberProperties, + DispatchEx_GetMemberName, + DispatchEx_GetNextDispID, + DispatchEx_GetNameSpaceParent +}; + +static HRESULT WINAPI doc1_onstoragecommit_func(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + test_storage_event(pdp, TRUE); + CHECK_EXPECT(doc_onstoragecommit[0]); + return S_OK; +} + +static const IDispatchExVtbl doc1_onstoragecommit_vtbl = { + DispatchEx_QueryInterface, + DispatchEx_AddRef, + DispatchEx_Release, + DispatchEx_GetTypeInfoCount, + DispatchEx_GetTypeInfo, + DispatchEx_GetIDsOfNames, + DispatchEx_Invoke, + DispatchEx_GetDispID, + doc1_onstoragecommit_func, + DispatchEx_DeleteMemberByName, + DispatchEx_DeleteMemberByDispID, + DispatchEx_GetMemberProperties, + DispatchEx_GetMemberName, + DispatchEx_GetNextDispID, + DispatchEx_GetNameSpaceParent +}; + +static HRESULT WINAPI window1_onstorage_func(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + test_storage_event(pdp, FALSE); + CHECK_EXPECT(window_onstorage[0]); + SET_EXPECT(doc_onstorage[0]); + return S_OK; +} + +static const IDispatchExVtbl window1_onstorage_vtbl = { + DispatchEx_QueryInterface, + DispatchEx_AddRef, + DispatchEx_Release, + DispatchEx_GetTypeInfoCount, + DispatchEx_GetTypeInfo, + DispatchEx_GetIDsOfNames, + DispatchEx_Invoke, + DispatchEx_GetDispID, + window1_onstorage_func, + DispatchEx_DeleteMemberByName, + DispatchEx_DeleteMemberByDispID, + DispatchEx_GetMemberProperties, + DispatchEx_GetMemberName, + DispatchEx_GetNextDispID, + DispatchEx_GetNameSpaceParent +}; + +static HRESULT WINAPI doc2_onstorage_func(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + test_storage_event(pdp, FALSE); + CHECK_EXPECT(doc_onstorage[1]); + return S_OK; +} + +static const IDispatchExVtbl doc2_onstorage_vtbl = { + DispatchEx_QueryInterface, + DispatchEx_AddRef, + DispatchEx_Release, + DispatchEx_GetTypeInfoCount, + DispatchEx_GetTypeInfo, + DispatchEx_GetIDsOfNames, + DispatchEx_Invoke, + DispatchEx_GetDispID, + doc2_onstorage_func, + DispatchEx_DeleteMemberByName, + DispatchEx_DeleteMemberByDispID, + DispatchEx_GetMemberProperties, + DispatchEx_GetMemberName, + DispatchEx_GetNextDispID, + DispatchEx_GetNameSpaceParent +}; + +static HRESULT WINAPI doc2_onstoragecommit_func(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + test_storage_event(pdp, TRUE); + CHECK_EXPECT(doc_onstoragecommit[1]); + return S_OK; +} + +static const IDispatchExVtbl doc2_onstoragecommit_vtbl = { + DispatchEx_QueryInterface, + DispatchEx_AddRef, + DispatchEx_Release, + DispatchEx_GetTypeInfoCount, + DispatchEx_GetTypeInfo, + DispatchEx_GetIDsOfNames, + DispatchEx_Invoke, + DispatchEx_GetDispID, + doc2_onstoragecommit_func, + DispatchEx_DeleteMemberByName, + DispatchEx_DeleteMemberByDispID, + DispatchEx_GetMemberProperties, + DispatchEx_GetMemberName, + DispatchEx_GetNextDispID, + DispatchEx_GetNameSpaceParent +}; + +static HRESULT WINAPI window2_onstorage_func(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + test_storage_event(pdp, FALSE); + CHECK_EXPECT(window_onstorage[1]); + SET_EXPECT(doc_onstorage[1]); + return S_OK; +} + +static const IDispatchExVtbl window2_onstorage_vtbl = { + DispatchEx_QueryInterface, + DispatchEx_AddRef, + DispatchEx_Release, + DispatchEx_GetTypeInfoCount, + DispatchEx_GetTypeInfo, + DispatchEx_GetIDsOfNames, + DispatchEx_Invoke, + DispatchEx_GetDispID, + window2_onstorage_func, + DispatchEx_DeleteMemberByName, + DispatchEx_DeleteMemberByDispID, + DispatchEx_GetMemberProperties, + DispatchEx_GetMemberName, + DispatchEx_GetNextDispID, + DispatchEx_GetNameSpaceParent +}; + +static void test_storage_events(unsigned document_mode) +{ + static struct { + IDispatchEx onstorage; + IDispatchEx onstoragecommit; + IDispatchEx window_onstorage; + } doc_onstorage_handlers[] = { + { { &doc1_onstorage_vtbl }, { &doc1_onstoragecommit_vtbl }, { &window1_onstorage_vtbl } }, + { { &doc2_onstorage_vtbl }, { &doc2_onstoragecommit_vtbl }, { &window2_onstorage_vtbl } }, + }; + IHTMLStorage *session_storage[2], *local_storage[2]; + IHTMLDocument2 *doc[2]; + BSTR key, value; + HRESULT hres; + VARIANT var; + unsigned i; + + trace("Running test_storage_events in %u mode...\n", document_mode); + + for(i = 0; i < ARRAY_SIZE(doc); i++) { + sprintf(index_html_data, + "<!DOCTYPE html>\n" + "<html>\n" + " <head>\n" + " <meta http-equiv="x-ua-compatible" content="Ie=%u">\n" + " <script src="winetest.js" type="text/javascript"></script>\n" + " <script type="text/javascript">var tests = [ function() { next_test() } ];</script>\n" + " </head>\n" + " <body onload="run_tests();">\n" + " </body>\n" + "</html>\n", document_mode); + + run_from_path(L"/index.html", "test_storage_events", &doc[i]); + } + + for(i = 0; i < ARRAY_SIZE(doc); i++) { + IHTMLWindow6 *window6; + IHTMLWindow7 *window7; + IHTMLWindow2 *window; + IHTMLDocument6 *doc6; + + hres = IHTMLDocument2_get_parentWindow(doc[i], &window); + ok(hres == S_OK, "get_parentWindow[%u] failed: %08lx\n", i, hres); + ok(window != NULL, "window[%u] == NULL\n", i); + + hres = IHTMLWindow2_QueryInterface(window, &IID_IHTMLWindow6, (void**)&window6); + ok(hres == S_OK, "Could not get IHTMLWindow6: %08lx\n", hres); + IHTMLWindow2_Release(window); + + hres = IHTMLWindow6_get_sessionStorage(window6, &session_storage[i]); + ok(hres == S_OK, "get_sessionStorage[%u] failed: %08lx\n", i, hres); + ok(session_storage[i] != NULL, "session_storage[%u] == NULL\n", i); + hres = IHTMLWindow6_get_localStorage(window6, &local_storage[i]); + ok(hres == S_OK, "get_localStorage[%u] failed: %08lx\n", i, hres); + ok(local_storage[i] != NULL, "local_storage[%u] == NULL\n", i); + if(i == 0) { + MSG msg; + IHTMLStorage_clear(local_storage[0]); + while(PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + + hres = IHTMLDocument2_QueryInterface(doc[i], &IID_IHTMLDocument6, (void**)&doc6); + if(SUCCEEDED(hres)) { + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = (IDispatch*)&doc_onstorage_handlers[i].onstorage; + hres = IHTMLDocument6_put_onstorage(doc6, var); + ok(hres == S_OK, "put_onstorage[%u] failed: %08lx\n", i, hres); + + V_DISPATCH(&var) = (IDispatch*)&doc_onstorage_handlers[i].onstoragecommit; + hres = IHTMLDocument6_put_onstoragecommit(doc6, var); + ok(hres == S_OK, "put_onstoragecommit[%u] failed: %08lx\n", i, hres); + IHTMLDocument6_Release(doc6); + } + + hres = IHTMLWindow6_QueryInterface(window6, &IID_IHTMLWindow7, (void**)&window7); + IHTMLWindow6_Release(window6); + if(SUCCEEDED(hres)) { + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = (IDispatch*)&doc_onstorage_handlers[i].window_onstorage; + hres = IHTMLWindow7_put_onstorage(window7, var); + ok(hres == S_OK, "put_onstorage[%u] failed: %08lx\n", i, hres); + IHTMLWindow7_Release(window7); + } + } + + /* local storage */ + key = SysAllocString(L"test"); + hres = IHTMLStorage_removeItem(local_storage[0], key); + ok(hres == S_OK, "removeItem failed: %08lx\n", hres); + + value = SysAllocString(L"WINE"); + hres = IHTMLStorage_setItem(local_storage[0], key, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(value); + check_local_onstorage(0, L"test", NULL, L"WINE"); + + value = SysAllocString(L"wine"); + hres = IHTMLStorage_setItem(local_storage[1], key, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(value); + check_local_onstorage(1, L"test", L"WINE", L"wine"); + + hres = IHTMLStorage_removeItem(local_storage[0], key); + ok(hres == S_OK, "removeItem failed: %08lx\n", hres); + SysFreeString(key); + check_local_onstorage(0, L"test", L"wine", NULL); + + key = SysAllocString(L"winetest"); + value = SysAllocString(L"WineTest"); + hres = IHTMLStorage_setItem(local_storage[1], key, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(value); + SysFreeString(key); + check_local_onstorage(1, L"winetest", NULL, L"WineTest"); + + hres = IHTMLStorage_clear(local_storage[0]); + ok(hres == S_OK, "clear failed: %08lx\n", hres); + check_local_onstorage(0, NULL, NULL, NULL); + + /* session storage */ + key = SysAllocString(L"foobar"); + hres = IHTMLStorage_removeItem(session_storage[0], key); + ok(hres == S_OK, "removeItem failed: %08lx\n", hres); + + value = SysAllocString(L"BarFoo"); + hres = IHTMLStorage_setItem(session_storage[0], key, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(value); + check_session_onstorage(0, L"foobar", NULL, L"BarFoo"); + + value = SysAllocString(L"barfoo"); + hres = IHTMLStorage_setItem(session_storage[1], key, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(value); + check_session_onstorage(1, L"foobar", L"BarFoo", L"barfoo"); + + hres = IHTMLStorage_removeItem(session_storage[0], key); + ok(hres == S_OK, "removeItem failed: %08lx\n", hres); + SysFreeString(key); + check_session_onstorage(0, L"foobar", L"barfoo", NULL); + + key = SysAllocString(L"winetest"); + value = SysAllocString(L"WineTest"); + hres = IHTMLStorage_setItem(session_storage[1], key, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(value); + SysFreeString(key); + check_session_onstorage(1, L"winetest", NULL, L"WineTest"); + + hres = IHTMLStorage_clear(session_storage[0]); + ok(hres == S_OK, "clear failed: %08lx\n", hres); + check_session_onstorage(0, NULL, NULL, NULL); + + free_registered_streams(); + set_client_site(doc[0], FALSE); + set_client_site(doc[1], FALSE); + IHTMLDocument2_Release(doc[0]); + IHTMLDocument2_Release(doc[1]); +} + static void run_script_as_http_with_mode(const char *script, const char *opt, const char *document_mode) { trace("Running %s script in %s mode...\n", script, document_mode ? document_mode : "quirks"); @@ -3834,7 +4293,7 @@ static void run_script_as_http_with_mode(const char *script, const char *opt, co document_mode ? "">" : "", script);
- run_from_path(L"/index.html", opt ? opt : script); + run_from_path(L"/index.html", opt ? opt : script, NULL); }
static void test_strict_mode(void) @@ -3856,7 +4315,7 @@ static void test_strict_mode(void) " </body>\n" "</html>\n");
- run_from_path(L"/index.html", "test_strict_mode"); + run_from_path(L"/index.html", "test_strict_mode", NULL); }
static void init_protocol_handler(void) @@ -3893,6 +4352,11 @@ static void run_js_tests(void) init_protocol_handler();
test_strict_mode(); + test_storage_events(0); + test_storage_events(7); + test_storage_events(8); + test_storage_events(9); + test_storage_events(11); run_script_as_http_with_mode("xhr.js", NULL, "9"); run_script_as_http_with_mode("xhr.js", NULL, "10"); run_script_as_http_with_mode("xhr.js", NULL, "11");
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=122803
Your paranoid android.
=== w7u_el (32 bit report) ===
mshtml: script.c:3984: Test failed: unexpected call doc_onstoragecommit[0]
I removed the (semi-hacky) tests in `misc.c` since they seemed to not be reliable anyway, no idea why since I can't reproduce it at all.
Jacek Caban (@jacek) commented about dlls/mshtml/htmlstorage.c:
storage->IHTMLStorage_iface.lpVtbl = &HTMLStorageVtbl; storage->ref = 1;
- storage->window = window;
- IHTMLWindow2_AddRef(&window->base.IHTMLWindow2_iface);
This creates a circular dependency.
Jacek Caban (@jacek) commented about dlls/mshtml/tests/script.c:
- }
- (*expect)[idx] = TRUE, (*called)[idx] = FALSE;
- do {
while(!(*called)[idx] && GetMessageW(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
- } while(*++expect && *++called);
- CLEAR_CALLED(doc_onstorage[0]);
- CLEAR_CALLED(doc_onstoragecommit[0]);
- CLEAR_CALLED(window_onstorage[0]);
- CLEAR_CALLED(doc_onstorage[1]);
- CLEAR_CALLED(doc_onstoragecommit[1]);
- CLEAR_CALLED(window_onstorage[1]);
+}
Please find a way to avoid things like that, I'm sure it's possible a cleaner solution. If you want event tests in C, there are tools for that in events.c.
Jacek Caban (@jacek) commented about dlls/mshtml/tests/script.c:
- trace("Running test_storage_events in %u mode...\n", document_mode);
- for(i = 0; i < ARRAY_SIZE(doc); i++) {
sprintf(index_html_data,
"<!DOCTYPE html>\n"
"<html>\n"
" <head>\n"
" <meta http-equiv=\"x-ua-compatible\" content=\"Ie=%u\">\n"
" <script src=\"winetest.js\" type=\"text/javascript\"></script>\n"
" <script type=\"text/javascript\">var tests = [ function() { next_test() } ];</script>\n"
" </head>\n"
" <body onload=\"run_tests();\">\n"
" </body>\n"
"</html>\n", document_mode);
run_from_path(L"/index.html", "test_storage_events", &doc[i]);
Is there any need for the test array and using run_from_path()? Also, is there anything interesting in testing more than two modes?
On Wed Sep 7 19:06:18 2022 +0000, Jacek Caban wrote:
Please find a way to avoid things like that, I'm sure it's possible a cleaner solution. If you want event tests in C, there are tools for that in events.c.
Which part specifically do you mean? The array / iteration itself? I mean, it's not like it's necessary, I thought it's simpler this way because it's way easier to extend it for a specific sequence.
I don't really know how to do it otherwise without *hardcoding* them with bunch of ifs.
On Wed Sep 7 19:06:19 2022 +0000, Jacek Caban wrote:
Is there any need for the test array and using run_from_path()? Also, is there anything interesting in testing more than two modes?
Well, we need both compat mode and a valid origin for the storage. The dom.c tests won't work since they have no origin. The misc.c tests won't work since we have no compat mode.
It's not exactly interesting per se testing more than 2 modes, but the "cutoff" in behavior seems to be IE8/IE9, and most of the other 2-mode tests are something like quirks + IE9, which doesn't provide this test. I can have it test only IE8 and IE9, if you prefer it that way.
On Wed Sep 7 19:33:37 2022 +0000, Gabriel Iv��ncescu wrote:
Well, we need both compat mode and a valid origin for the storage. The dom.c tests won't work since they have no origin. The misc.c tests won't work since we have no compat mode. It's not exactly interesting per se testing more than 2 modes, but the "cutoff" in behavior seems to be IE8/IE9, and most of the other 2-mode tests are something like quirks + IE9, which doesn't provide this test. I can have it test only IE8 and IE9, if you prefer it that way.
BTW sorry, I forgot to mention. I'm using an array here because I'm trying to test two windows/documents with same origin. This is to test that the events are *not* sent on native between them, for whatever reason. I initially expected them to be sent, though.
From my understanding this is the point of the "standards" of these events (they aren't even supposed to be sent to same Window), but clearly IE thinks otherwise.
On Wed Sep 7 19:35:23 2022 +0000, Gabriel Iv��ncescu wrote:
BTW sorry, I forgot to mention. I'm using an array here because I'm trying to test two windows/documents with same origin. This is to test that the events are *not* sent on native between them, for whatever reason. I initially expected them to be sent, though. From my understanding this is the point of the "standards" of these events (they aren't even supposed to be sent to same Window), but clearly IE thinks otherwise.
We can add origin to add a valid origin events.c. (We could also have those tests in JavaScript, but C is fine as well).
If it's all to test that events are not sent, then I'd say it's better to drop those parts. A comment in the code could document it as well.
For compat mode, it seems that the most interesting one those days is IE11. For additional test, just pick any <IE9 that you think is interesting in this case.
Thinking some more about it, I suspect that it's more likely that those events are supported across `<iframe>` documents. It may be fine to skip it, but if you want to test it, using `<iframe>` would be more convincing.
EDIT: gitlab doesn't like `<iframe>` in comments.
On Wed Sep 7 22:58:01 2022 +0000, Jacek Caban wrote:
We can add origin to add a valid origin events.c. (We could also have those tests in JavaScript, but C is fine as well). If it's all to test that events are not sent, then I'd say it's better to drop those parts. A comment in the code could document it as well. For compat mode, it seems that the most interesting one those days is IE11. For additional test, just pick any <IE9 that you think is interesting in this case.
*(Gitlab's reply via email seems a bit borked right now, so I apologize if the messages will be repeated if it solves itself, but here it is)*
Wait, I'm struggling a bit, how do you add a (fake) origin without a custom pluggable protocol? (which is in script.c)
I don't mind testing it from JS, and I'll do that with iframe, but I wanted to test parallel docs with same origin, where events are not sent between them.
On Thu Sep 8 16:21:40 2022 +0000, Gabriel Iv��ncescu wrote:
*(Gitlab's reply via email seems a bit borked right now, so I apologize if the messages will be repeated if it solves itself, but here it is)* Wait, I'm struggling a bit, how do you add a (fake) origin without a custom pluggable protocol? (which is in script.c) I don't mind testing it from JS, and I'll do that with iframe, but I wanted to test parallel docs with same origin, where events are not sent between them.
We could add pluggable protocol there. For the main document, it's possible to just expose protocol handler from moniker's bind context (but registering the handler globally may be more reliable).
Ok, so I've got almost all of it working now. The C tests moved to events.c with a simplified pluggable protocol works fine. I added JS iframe tests (which has a few quirks) but I also implemented it now and it works fine.
There's only one problem. But for context, first, please look at the `storage_event_proc` in the current patchset. Namely, the IE9+ part.
So in the C code in events.c, in IE9+ modes, it first sends an `onstorage` event to the window, and then an `onstorage` event to the doc, but the one on the doc is a "legacy" event.
But in JS code, in IE9+ modes, it **only** sends an `onstorage` event to the window, and nothing to the doc.
Do you have some ideas as to what could cause this? I guess I could mark this as todo somehow (although it's an expected event, so need to figure out how to wait for it...), but maybe there's a known issue or something like that? How can I even detect if it's JS code or not, though.
On Mon Sep 12 10:22:07 2022 +0000, Gabriel Iv��ncescu wrote:
Ok, so I've got almost all of it working now. The C tests moved to events.c with a simplified pluggable protocol works fine. I added JS iframe tests (which has a few quirks) but I also implemented it now and it works fine. There's only one problem. But for context, first, please look at the `storage_event_proc` in the current patchset. Namely, the IE9+ part. So in the C code in events.c, in IE9+ modes, it first sends an `onstorage` event to the window, and then an `onstorage` event to the doc, but the one on the doc is a "legacy" event. But in JS code, in IE9+ modes, it **only** sends an `onstorage` event to the window, and nothing to the doc. Do you have some ideas as to what could cause this? I guess I could mark this as todo somehow (although it's an expected event, so need to figure out how to wait for it...), but maybe there's a known issue or something like that? How can I even detect if it's JS code or not, though.
I think that document.onstorage is a non-standard thing that was removed in IE9+ compatibility modes, see: https://testbot.winehq.org/JobDetails.pl?Key=123067