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).
-- v6: mshtml: Send StorageEvents to iframe windows properly. mshtml: Don't expose document.onstorage for IE9+ modes. mshtml/tests: Add tests for StorageEvents with parallel documents. 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/mshtml: 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 | 217 ++++++++++++++++++++++++++++++++++- dlls/mshtml/htmlevent.h | 3 + dlls/mshtml/htmlstorage.c | 74 +++++++++++- dlls/mshtml/htmlwindow.c | 10 +- dlls/mshtml/mshtml_private.h | 3 + 6 files changed, 317 insertions(+), 14 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..7d13ac5b3fb 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,31 @@ HRESULT create_message_event(HTMLDocumentNode *doc, VARIANT *data, DOMEvent **re return S_OK; }
+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)) + return hres; + storage_event = DOMStorageEvent_from_DOMEvent(event); + + if(!commit) { + if((key && !(storage_event->key = SysAllocString(key))) || + (old_value && !(storage_event->old_value = SysAllocString(old_value))) || + (new_value && !(storage_event->new_value = SysAllocString(new_value)))) { + IDOMEvent_Release(&event->IDOMEvent_iface); + return E_OUTOFMEMORY; + } + } + + *ret = event; + return S_OK; +} + 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..67149e0c950 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; @@ -195,6 +197,65 @@ static inline HTMLStorage *impl_from_IHTMLStorage(IHTMLStorage *iface) return CONTAINING_RECORD(iface, HTMLStorage, IHTMLStorage_iface); }
+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 && 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; + + if(!window) { + SysFreeString(old_value); + return S_OK; + } + + hres = create_storage_event(window->doc, key, old_value, new_value, local, &event); + SysFreeString(old_value); + 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 HRESULT WINAPI HTMLStorage_QueryInterface(IHTMLStorage *iface, REFIID riid, void **ppv) { HTMLStorage *This = impl_from_IHTMLStorage(iface); @@ -817,8 +878,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 +890,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,9 +1330,17 @@ HRESULT create_html_storage(HTMLInnerWindow *window, BOOL local, IHTMLStorage **
storage->IHTMLStorage_iface.lpVtbl = &HTMLStorageVtbl; storage->ref = 1; + storage->window = window; + init_dispatch(&storage->dispex, (IUnknown*)&storage->IHTMLStorage_iface, &HTMLStorage_dispex, dispex_compat_mode(&window->event_target.dispex));
*p = &storage->IHTMLStorage_iface; return S_OK; } + +void detach_html_storage(IHTMLStorage *iface) +{ + HTMLStorage *storage = impl_from_IHTMLStorage(iface); + storage->window = NULL; +} diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index 00eef4d846a..ba631c9a7ef 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -299,10 +299,14 @@ static void release_inner_window(HTMLInnerWindow *This)
if(This->navigator) IOmNavigator_Release(This->navigator); - if(This->session_storage) + if(This->session_storage) { + detach_html_storage(This->session_storage); IHTMLStorage_Release(This->session_storage); - if(This->local_storage) + } + if(This->local_storage) { + detach_html_storage(This->local_storage); IHTMLStorage_Release(This->local_storage); + }
if(This->mon) IMoniker_Release(This->mon); @@ -2613,7 +2617,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..18104afc10a 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) \ @@ -956,6 +958,7 @@ HRESULT create_dom_implementation(HTMLDocumentNode*,IHTMLDOMImplementation**) DE void detach_dom_implementation(IHTMLDOMImplementation*) DECLSPEC_HIDDEN;
HRESULT create_html_storage(HTMLInnerWindow*,BOOL,IHTMLStorage**) DECLSPEC_HIDDEN; +void detach_html_storage(IHTMLStorage*) DECLSPEC_HIDDEN;
void HTMLDocument_Persist_Init(HTMLDocument*) DECLSPEC_HIDDEN; void HTMLDocument_OleCmd_Init(HTMLDocument*) DECLSPEC_HIDDEN;
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 67149e0c950..1145fbc42b1 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -702,7 +702,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; @@ -710,6 +710,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; @@ -726,10 +727,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; @@ -764,6 +775,8 @@ done: if(elem) IXMLDOMElement_Release(elem); IXMLDOMDocument_Release(doc); + if(hres != S_OK) + SysFreeString(*old_value); return hres; }
@@ -771,6 +784,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)); @@ -791,16 +805,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 1145fbc42b1..751afd6155d 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -822,13 +822,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; @@ -845,6 +847,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; @@ -859,6 +877,8 @@ done: if(node) IXMLDOMNode_Release(node); IXMLDOMDocument_Release(doc); + if(hres != S_OK || !changed) + SysFreeString(*old_value); return hres; }
@@ -866,7 +886,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));
@@ -877,16 +899,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/events.c | 717 +++++++++++++++++++++++++++++++++++++ 1 file changed, 717 insertions(+)
diff --git a/dlls/mshtml/tests/events.c b/dlls/mshtml/tests/events.c index 87151987f16..ff64db7130b 100644 --- a/dlls/mshtml/tests/events.c +++ b/dlls/mshtml/tests/events.c @@ -57,6 +57,9 @@ expect_ ## func = called_ ## func = FALSE; \ }while(0)
+#define CLEAR_CALLED(func) \ + expect_ ## func = called_ ## func = FALSE + DEFINE_EXPECT(document_onclick); DEFINE_EXPECT(body_onclick); DEFINE_EXPECT(doc_onclick_attached); @@ -90,6 +93,12 @@ DEFINE_EXPECT(submit_onclick_attached_check_cancel); DEFINE_EXPECT(submit_onclick_setret); DEFINE_EXPECT(elem2_cp_onclick); DEFINE_EXPECT(iframe_onload); +DEFINE_EXPECT(doc1_onstorage); +DEFINE_EXPECT(doc1_onstoragecommit); +DEFINE_EXPECT(window1_onstorage); +DEFINE_EXPECT(doc2_onstorage); +DEFINE_EXPECT(doc2_onstoragecommit); +DEFINE_EXPECT(window2_onstorage);
static HWND container_hwnd = NULL; static IHTMLWindow2 *window; @@ -2840,6 +2849,145 @@ static void test_create_event(IHTMLDocument2 *doc) IDocumentEvent_Release(doc_event); }
+static unsigned onstorage_expect_line; +static const WCHAR *onstorage_expect_key, *onstorage_expect_old_value, *onstorage_expect_new_value; + +#define set_onstorage_expect(a,b,c) _set_onstorage_expect(__LINE__,a,b,c) +static void _set_onstorage_expect(unsigned line, const WCHAR *key, const WCHAR *old_value, const WCHAR *new_value) +{ + onstorage_expect_line = line; + onstorage_expect_key = key; + onstorage_expect_old_value = old_value; + onstorage_expect_new_value = new_value; +} + +static void test_storage_event(DISPPARAMS *params, BOOL doc_onstorage) +{ + 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; + IHTMLEventObj *event_obj; + IDOMStorageEvent *event; + IDispatchEx *dispex; + HRESULT hres; + unsigned i; + DISPID id; + BSTR bstr; + + if(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; + } + + 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 = 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(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(doc1_onstorage); + test_storage_event(pdp, TRUE); + return S_OK; +} + +EVENT_HANDLER_FUNC_OBJ(doc1_onstorage); + +static HRESULT WINAPI doc1_onstoragecommit(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(doc1_onstoragecommit); + test_storage_event(pdp, FALSE); + return S_OK; +} + +EVENT_HANDLER_FUNC_OBJ(doc1_onstoragecommit); + +static HRESULT WINAPI window1_onstorage(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(window1_onstorage); + test_storage_event(pdp, FALSE); + return S_OK; +} + +EVENT_HANDLER_FUNC_OBJ(window1_onstorage); + +static HRESULT WINAPI doc2_onstorage(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(doc2_onstorage); + test_storage_event(pdp, TRUE); + return S_OK; +} + +EVENT_HANDLER_FUNC_OBJ(doc2_onstorage); + +static HRESULT WINAPI doc2_onstoragecommit(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(doc2_onstoragecommit); + test_storage_event(pdp, FALSE); + return S_OK; +} + +EVENT_HANDLER_FUNC_OBJ(doc2_onstoragecommit); + +static HRESULT WINAPI window2_onstorage(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(window2_onstorage); + test_storage_event(pdp, FALSE); + return S_OK; +} + +EVENT_HANDLER_FUNC_OBJ(window2_onstorage); + static HRESULT QueryInterface(REFIID,void**);
static HRESULT WINAPI InPlaceFrame_QueryInterface(IOleInPlaceFrame *iface, REFIID riid, void **ppv) @@ -3267,14 +3415,327 @@ static const IPropertyNotifySinkVtbl PropertyNotifySinkVtbl = {
static IPropertyNotifySink PropertyNotifySink = { &PropertyNotifySinkVtbl };
+typedef struct { + IInternetProtocolEx IInternetProtocolEx_iface; + IWinInetHttpInfo IWinInetHttpInfo_iface; + + LONG ref; + + IInternetProtocolSink *sink; + IUri *uri; + + ULONG size; + const char *data; + const char *ptr; +} ProtocolHandler; + +static DWORD WINAPI async_switch_proc(void *arg) +{ + PROTOCOLDATA protocol_data = { PI_FORCE_ASYNC }; + ProtocolHandler *protocol_handler = arg; + IInternetProtocolSink_Switch(protocol_handler->sink, &protocol_data); + return 0; +} + +static inline ProtocolHandler *impl_from_IInternetProtocolEx(IInternetProtocolEx *iface) +{ + return CONTAINING_RECORD(iface, ProtocolHandler, IInternetProtocolEx_iface); +} + +static HRESULT WINAPI Protocol_QueryInterface(IInternetProtocolEx *iface, REFIID riid, void **ppv) +{ + ProtocolHandler *This = impl_from_IInternetProtocolEx(iface); + + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetProtocol, riid) || IsEqualGUID(&IID_IInternetProtocolEx, riid)) { + *ppv = &This->IInternetProtocolEx_iface; + }else if(IsEqualGUID(&IID_IWinInetInfo, riid) || IsEqualGUID(&IID_IWinInetHttpInfo, riid)) { + *ppv = &This->IWinInetHttpInfo_iface; + }else { + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI Protocol_AddRef(IInternetProtocolEx *iface) +{ + ProtocolHandler *This = impl_from_IInternetProtocolEx(iface); + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI Protocol_Release(IInternetProtocolEx *iface) +{ + ProtocolHandler *This = impl_from_IInternetProtocolEx(iface); + LONG ref = InterlockedDecrement(&This->ref); + + if(!ref) { + if(This->sink) + IInternetProtocolSink_Release(This->sink); + if(This->uri) + IUri_Release(This->uri); + free(This); + } + + return ref; +} + +static HRESULT WINAPI Protocol_Start(IInternetProtocolEx *iface, LPCWSTR szUrl, IInternetProtocolSink *pOIProtSink, + IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE_PTR dwReserved) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA *pProtocolData) +{ + ProtocolHandler *This = impl_from_IInternetProtocolEx(iface); + IServiceProvider *service_provider; + IHttpNegotiate *http_negotiate; + WCHAR *addl_headers = NULL; + HRESULT hres; + BSTR bstr; + + hres = IInternetProtocolSink_QueryInterface(This->sink, &IID_IServiceProvider, (void**)&service_provider); + ok(hres == S_OK, "Could not get IServiceProvider iface: %08lx\n", hres); + + hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate, &IID_IHttpNegotiate, (void**)&http_negotiate); + IServiceProvider_Release(service_provider); + ok(hres == S_OK, "Could not get IHttpNegotiate interface: %08lx\n", hres); + + hres = IUri_GetDisplayUri(This->uri, &bstr); + ok(hres == S_OK, "Failed to get display uri: %08lx\n", hres); + hres = IHttpNegotiate_BeginningTransaction(http_negotiate, bstr, L"", 0, &addl_headers); + ok(hres == S_OK, "BeginningTransaction failed: %08lx\n", hres); + CoTaskMemFree(addl_headers); + SysFreeString(bstr); + + bstr = SysAllocString(L"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"); + hres = IHttpNegotiate_OnResponse(http_negotiate, 200, bstr, NULL, NULL); + ok(hres == S_OK, "OnResponse failed: %08lx\n", hres); + IHttpNegotiate_Release(http_negotiate); + SysFreeString(bstr); + + hres = IInternetProtocolSink_ReportData(This->sink, BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION, + This->size, This->size); + ok(hres == S_OK, "ReportData failed: %08lx\n", hres); + + hres = IInternetProtocolSink_ReportResult(This->sink, S_OK, 0, NULL); + ok(hres == S_OK, "ReportResult failed: %08lx\n", hres); + + IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface); + return S_OK; +} + +static HRESULT WINAPI Protocol_Abort(IInternetProtocolEx *iface, HRESULT hrReason, DWORD dwOptions) +{ + trace("Abort(%08lx %lx)\n", hrReason, dwOptions); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_Terminate(IInternetProtocolEx *iface, DWORD dwOptions) +{ + return S_OK; +} + +static HRESULT WINAPI Protocol_Suspend(IInternetProtocolEx *iface) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_Resume(IInternetProtocolEx *iface) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_Read(IInternetProtocolEx *iface, void *pv, ULONG cb, ULONG *pcbRead) +{ + ProtocolHandler *This = impl_from_IInternetProtocolEx(iface); + ULONG read; + + read = This->size - (This->ptr - This->data); + if(read > cb) + read = cb; + + if(read) { + memcpy(pv, This->ptr, read); + This->ptr += read; + } + + *pcbRead = read; + return (This->ptr != This->data + This->size) ? S_OK : S_FALSE; +} + +static HRESULT WINAPI Protocol_Seek(IInternetProtocolEx *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, + ULARGE_INTEGER *plibNewPosition) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_LockRequest(IInternetProtocolEx *iface, DWORD dwOptions) +{ + return S_OK; +} + +static HRESULT WINAPI Protocol_UnlockRequest(IInternetProtocolEx *iface) +{ + return S_OK; +} + +static const char *protocol_doc_str; + +static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri, IInternetProtocolSink *pOIProtSink, + IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE *dwReserved) +{ + ProtocolHandler *This = impl_from_IInternetProtocolEx(iface); + + This->data = protocol_doc_str; + This->size = strlen(This->data); + + IInternetProtocolSink_AddRef(This->sink = pOIProtSink); + IUri_AddRef(This->uri = uri); + This->ptr = This->data; + + IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface); + QueueUserWorkItem(async_switch_proc, This, 0); + return E_PENDING; +} + +static const IInternetProtocolExVtbl ProtocolExVtbl = { + Protocol_QueryInterface, + Protocol_AddRef, + Protocol_Release, + Protocol_Start, + Protocol_Continue, + Protocol_Abort, + Protocol_Terminate, + Protocol_Suspend, + Protocol_Resume, + Protocol_Read, + Protocol_Seek, + Protocol_LockRequest, + Protocol_UnlockRequest, + ProtocolEx_StartEx +}; + +static inline ProtocolHandler *impl_from_IWinInetHttpInfo(IWinInetHttpInfo *iface) +{ + return CONTAINING_RECORD(iface, ProtocolHandler, IWinInetHttpInfo_iface); +} + +static HRESULT WINAPI HttpInfo_QueryInterface(IWinInetHttpInfo *iface, REFIID riid, void **ppv) +{ + ProtocolHandler *This = impl_from_IWinInetHttpInfo(iface); + return IInternetProtocolEx_QueryInterface(&This->IInternetProtocolEx_iface, riid, ppv); +} + +static ULONG WINAPI HttpInfo_AddRef(IWinInetHttpInfo *iface) +{ + ProtocolHandler *This = impl_from_IWinInetHttpInfo(iface); + return IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface); +} + +static ULONG WINAPI HttpInfo_Release(IWinInetHttpInfo *iface) +{ + ProtocolHandler *This = impl_from_IWinInetHttpInfo(iface); + return IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface); +} + +static HRESULT WINAPI HttpInfo_QueryOption(IWinInetHttpInfo *iface, DWORD dwOption, void *pBuffer, DWORD *pcbBuffer) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI HttpInfo_QueryInfo(IWinInetHttpInfo *iface, DWORD dwOption, void *pBuffer, DWORD *pcbBuffer, + DWORD *pdwFlags, DWORD *pdwReserved) +{ + return E_NOTIMPL; +} + +static const IWinInetHttpInfoVtbl WinInetHttpInfoVtbl = { + HttpInfo_QueryInterface, + HttpInfo_AddRef, + HttpInfo_Release, + HttpInfo_QueryOption, + HttpInfo_QueryInfo +}; + +static HRESULT WINAPI ProtocolCF_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IClassFactory, riid)) { + *ppv = iface; + return S_OK; + } + + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI ProtocolCF_AddRef(IClassFactory *iface) +{ + return 2; +} + +static ULONG WINAPI ProtocolCF_Release(IClassFactory *iface) +{ + return 1; +} + +static HRESULT WINAPI ProtocolCF_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv) +{ + ProtocolHandler *protocol; + HRESULT hres; + + protocol = calloc(1, sizeof(*protocol)); + protocol->IInternetProtocolEx_iface.lpVtbl = &ProtocolExVtbl; + protocol->IWinInetHttpInfo_iface.lpVtbl = &WinInetHttpInfoVtbl; + protocol->ref = 1; + + hres = IInternetProtocolEx_QueryInterface(&protocol->IInternetProtocolEx_iface, riid, ppv); + IInternetProtocolEx_Release(&protocol->IInternetProtocolEx_iface); + return hres; +} + +static HRESULT WINAPI ProtocolCF_LockServer(IClassFactory *iface, BOOL dolock) +{ + ok(0, "unexpected call\n"); + return S_OK; +} + +static const IClassFactoryVtbl ProtocolCFVtbl = { + ProtocolCF_QueryInterface, + ProtocolCF_AddRef, + ProtocolCF_Release, + ProtocolCF_CreateInstance, + ProtocolCF_LockServer +}; + +static IClassFactory protocol_cf = { &ProtocolCFVtbl }; + static void doc_load_string(IHTMLDocument2 *doc, const char *str) { + IInternetSession *internet_session; IPersistStreamInit *init; IStream *stream; HRESULT hres; HGLOBAL mem; SIZE_T len;
+ if(protocol_doc_str) { + hres = CoInternetGetSession(0, &internet_session, 0); + ok(hres == S_OK, "CoInternetGetSession failed: %08lx\n", hres); + + hres = IInternetSession_UnregisterNameSpace(internet_session, &protocol_cf, L"http"); + ok(hres == S_OK, "RegisterNameSpace failed: %08lx\n", hres); + + IInternetSession_Release(internet_session); + protocol_doc_str = NULL; + } + notif_doc = doc;
doc_complete = FALSE; @@ -3358,6 +3819,57 @@ static IHTMLDocument2 *create_document(void) return SUCCEEDED(hres) ? doc : NULL; }
+static IHTMLDocument2 *create_document_with_origin(const char *str) +{ + IInternetSession *internet_session; + IPersistMoniker *persist; + IHTMLDocument2 *doc; + IMoniker *mon; + HRESULT hres; + BSTR url; + MSG msg; + + doc = create_document(); + if(!doc) + return NULL; + + if(!protocol_doc_str) { + hres = CoInternetGetSession(0, &internet_session, 0); + ok(hres == S_OK, "CoInternetGetSession failed: %08lx\n", hres); + + hres = IInternetSession_RegisterNameSpace(internet_session, &protocol_cf, &CLSID_HttpProtocol, L"http", 0, NULL, 0); + ok(hres == S_OK, "RegisterNameSpace failed: %08lx\n", hres); + + IInternetSession_Release(internet_session); + } + protocol_doc_str = str; + + notif_doc = doc; + doc_complete = FALSE; + set_client_site(doc, TRUE); + do_advise((IUnknown*)doc, &IID_IPropertyNotifySink, (IUnknown*)&PropertyNotifySink); + + url = SysAllocString(L"http://winetest.example.org"); + hres = CreateURLMoniker(NULL, url, &mon); + SysFreeString(url); + ok(hres == S_OK, "CreateUrlMoniker failed: %08lx\n", hres); + + hres = IHTMLDocument2_QueryInterface(doc, &IID_IPersistMoniker, (void**)&persist); + ok(hres == S_OK, "Could not get IPersistMoniker iface: %08lx\n", hres); + + hres = IPersistMoniker_Load(persist, FALSE, mon, NULL, 0); + ok(hres == S_OK, "Load failed: %08lx\n", hres); + IPersistMoniker_Release(persist); + IMoniker_Release(mon); + + while(!doc_complete && GetMessageA(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + + return doc; +} +
typedef void (*testfunc_t)(IHTMLDocument2*);
@@ -3469,6 +3981,208 @@ static void test_empty_document(void) IHTMLDocument2_Release(doc); }
+static void test_storage_events(const char *doc_str) +{ + static struct { + IDispatchEx *onstorage; + IDispatchEx *onstoragecommit; + IDispatchEx *window_onstorage; + } doc_onstorage_handlers[] = { + { &doc1_onstorage_obj, &doc1_onstoragecommit_obj, &window1_onstorage_obj }, + { &doc2_onstorage_obj, &doc2_onstoragecommit_obj, &window2_onstorage_obj }, + }; + IHTMLStorage *session_storage[2], *local_storage[2]; + IHTMLDocument2 *doc[2]; + IHTMLDocument6 *doc6; + BSTR key, value; + HRESULT hres; + VARIANT var; + LONG length; + unsigned i; + + for(i = 0; i < ARRAY_SIZE(doc); i++) + doc[i] = create_document_with_origin(doc_str); + + document_mode = 0; + for(i = 0; i < ARRAY_SIZE(doc); i++) { + IHTMLWindow6 *window6; + IHTMLWindow7 *window7; + IHTMLWindow2 *window; + + 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); + + hres = IHTMLDocument2_QueryInterface(doc[i], &IID_IHTMLDocument6, (void**)&doc6); + if(SUCCEEDED(hres)) { + if(i == 0) { + hres = IHTMLDocument6_get_documentMode(doc6, &var); + ok(hres == S_OK, "get_documentMode failed: %08lx\n", hres); + ok(V_VT(&var) == VT_R4, "V_VT(documentMode) = %u\n", V_VT(&var)); + document_mode = V_R4(&var); + } + 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 */ + for(;;) { + hres = IHTMLStorage_get_length(local_storage[0], &length); + ok(hres == S_OK, "get_length failed %08lx\n", hres); + if(length == 0) break; + + hres = IHTMLStorage_clear(local_storage[0]); + ok(hres == S_OK, "clear failed: %08lx\n", hres); + SET_EXPECT(doc1_onstoragecommit); + set_onstorage_expect(NULL, NULL, NULL); + pump_msgs(&called_doc1_onstoragecommit); + CHECK_CALLED(doc1_onstoragecommit); + } + + 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); + SET_EXPECT(doc1_onstoragecommit); + set_onstorage_expect(NULL, NULL, NULL); + pump_msgs(&called_doc1_onstoragecommit); + CHECK_CALLED(doc1_onstoragecommit); + + value = SysAllocString(L"wine"); + hres = IHTMLStorage_setItem(local_storage[1], key, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(value); + SET_EXPECT(doc1_onstoragecommit); /* Native sometimes calls this here for some reason */ + SET_EXPECT(doc2_onstoragecommit); + set_onstorage_expect(NULL, NULL, NULL); + pump_msgs(&called_doc2_onstoragecommit); + CHECK_CALLED(doc2_onstoragecommit); + CLEAR_CALLED(doc1_onstoragecommit); + + hres = IHTMLStorage_removeItem(local_storage[0], key); + ok(hres == S_OK, "removeItem failed: %08lx\n", hres); + SysFreeString(key); + SET_EXPECT(doc1_onstoragecommit); + set_onstorage_expect(NULL, NULL, NULL); + pump_msgs(&called_doc1_onstoragecommit); + CHECK_CALLED(doc1_onstoragecommit); + + 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); + SET_EXPECT(doc2_onstoragecommit); + set_onstorage_expect(NULL, NULL, NULL); + pump_msgs(&called_doc2_onstoragecommit); + CHECK_CALLED(doc2_onstoragecommit); + + hres = IHTMLStorage_clear(local_storage[0]); + ok(hres == S_OK, "clear failed: %08lx\n", hres); + SET_EXPECT(doc1_onstoragecommit); + set_onstorage_expect(NULL, NULL, NULL); + pump_msgs(&called_doc1_onstoragecommit); + CHECK_CALLED(doc1_onstoragecommit); + + /* 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); + if(document_mode >= 9) SET_EXPECT(window1_onstorage); + SET_EXPECT(doc1_onstorage); + set_onstorage_expect(L"foobar", NULL, L"BarFoo"); + pump_msgs(&called_doc1_onstorage); + CHECK_CALLED(doc1_onstorage); + if(document_mode >= 9) CHECK_CALLED(window1_onstorage); + + value = SysAllocString(L"barfoo"); + hres = IHTMLStorage_setItem(session_storage[1], key, value); + ok(hres == S_OK, "setItem failed: %08lx\n", hres); + SysFreeString(value); + if(document_mode >= 9) SET_EXPECT(window2_onstorage); + SET_EXPECT(doc2_onstorage); + set_onstorage_expect(L"foobar", L"BarFoo", L"barfoo"); + pump_msgs(&called_doc2_onstorage); + CHECK_CALLED(doc2_onstorage); + if(document_mode >= 9) CHECK_CALLED(window2_onstorage); + + hres = IHTMLStorage_removeItem(session_storage[0], key); + ok(hres == S_OK, "removeItem failed: %08lx\n", hres); + SysFreeString(key); + if(document_mode >= 9) SET_EXPECT(window1_onstorage); + SET_EXPECT(doc1_onstorage); + set_onstorage_expect(L"foobar", L"barfoo", NULL); + pump_msgs(&called_doc1_onstorage); + CHECK_CALLED(doc1_onstorage); + if(document_mode >= 9) CHECK_CALLED(window1_onstorage); + + 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); + if(document_mode >= 9) SET_EXPECT(window2_onstorage); + SET_EXPECT(doc2_onstorage); + set_onstorage_expect(L"winetest", NULL, L"WineTest"); + pump_msgs(&called_doc2_onstorage); + CHECK_CALLED(doc2_onstorage); + if(document_mode >= 9) CHECK_CALLED(window2_onstorage); + + hres = IHTMLStorage_clear(session_storage[0]); + ok(hres == S_OK, "clear failed: %08lx\n", hres); + if(document_mode >= 9) SET_EXPECT(window1_onstorage); + SET_EXPECT(doc1_onstorage); + set_onstorage_expect(NULL, NULL, NULL); + pump_msgs(&called_doc1_onstorage); + CHECK_CALLED(doc1_onstorage); + if(document_mode >= 9) CHECK_CALLED(window1_onstorage); + + set_client_site(doc[0], FALSE); + set_client_site(doc[1], FALSE); + IHTMLDocument2_Release(doc[0]); + IHTMLDocument2_Release(doc[1]); +} + static BOOL check_ie(void) { IHTMLDocument2 *doc; @@ -3521,6 +4235,9 @@ START_TEST(events) run_test(empty_doc_ie9_str, test_create_event);
test_empty_document(); + test_storage_events(empty_doc_str); + if(is_ie9plus) + test_storage_events(empty_doc_ie9_str);
DestroyWindow(container_hwnd); }else {
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmldoc.c | 6 +++++- dlls/mshtml/tests/documentmode.js | 1 + 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c index 413a3a4054e..aa3efe27c68 100644 --- a/dlls/mshtml/htmldoc.c +++ b/dlls/mshtml/htmldoc.c @@ -5978,6 +5978,10 @@ static void HTMLDocumentNode_init_dispex_info(dispex_data_t *info, compat_mode_t {DISPID_IHTMLDOCUMENT2_LOCATION, HTMLDocumentNode_location_hook}, {DISPID_UNKNOWN} }; + static const dispex_hook_t document6_ie9_hooks[] = { + {DISPID_IHTMLDOCUMENT6_ONSTORAGE}, + {DISPID_UNKNOWN} + };
HTMLDOMNode_init_dispex_info(info, mode);
@@ -5992,7 +5996,7 @@ static void HTMLDocumentNode_init_dispex_info(dispex_data_t *info, compat_mode_t dispex_info_add_interface(info, IHTMLDocument3_tid, NULL); dispex_info_add_interface(info, IHTMLDocument6_tid, NULL); }else { - dispex_info_add_interface(info, IHTMLDocument6_tid, NULL); + dispex_info_add_interface(info, IHTMLDocument6_tid, mode >= COMPAT_MODE_IE9 ? document6_ie9_hooks : NULL); dispex_info_add_interface(info, IHTMLDocument3_tid, NULL); } dispex_info_add_interface(info, IHTMLDocument2_tid, document2_hooks); diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 8cc1da534e6..91d5e38c59a 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -312,6 +312,7 @@ sync_test("doc_props", function() {
var v = document.documentMode;
+ test_exposed("onstorage", v < 9); test_exposed("textContent", v >= 9); test_exposed("prefix", v >= 9); test_exposed("defaultView", v >= 9);
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 tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=123208
Your paranoid android.
=== w10pro64 (testbot log) ===
WineRunTask.pl:error: The previous 1 run(s) terminated abnormally
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlstorage.c | 120 ++++++++++++++++++++++++++--- dlls/mshtml/tests/documentmode.js | 124 ++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+), 11 deletions(-)
diff --git a/dlls/mshtml/htmlstorage.c b/dlls/mshtml/htmlstorage.c index 751afd6155d..f411c6cb122 100644 --- a/dlls/mshtml/htmlstorage.c +++ b/dlls/mshtml/htmlstorage.c @@ -197,6 +197,8 @@ static inline HTMLStorage *impl_from_IHTMLStorage(IHTMLStorage *iface) return CONTAINING_RECORD(iface, HTMLStorage, IHTMLStorage_iface); }
+static HRESULT build_session_origin(IUri*,BSTR,BSTR*); + struct storage_event_task { task_t header; HTMLInnerWindow *window; @@ -226,22 +228,22 @@ static void storage_event_destr(task_t *_task) 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) +struct send_storage_event_ctx { + HTMLInnerWindow *skip_window; + const WCHAR *origin; + UINT origin_len; + BSTR key; + BSTR old_value; + BSTR new_value; +}; + +static HRESULT push_storage_event_task(struct send_storage_event_ctx *ctx, HTMLInnerWindow *window, BOOL commit) { - HTMLInnerWindow *window = storage->window; - BOOL local = !!storage->filename; struct storage_event_task *task; DOMEvent *event; HRESULT hres;
- if(!window) { - SysFreeString(old_value); - return S_OK; - } - - hres = create_storage_event(window->doc, key, old_value, new_value, local, &event); - SysFreeString(old_value); + hres = create_storage_event(window->doc, ctx->key, ctx->old_value, ctx->new_value, commit, &event); if(FAILED(hres)) return hres;
@@ -256,6 +258,102 @@ static HRESULT send_storage_event(HTMLStorage *storage, BSTR key, BSTR old_value return push_task(&task->header, storage_event_proc, storage_event_destr, window->task_magic); }
+static HRESULT send_storage_event_impl(struct send_storage_event_ctx *ctx, HTMLInnerWindow *window) +{ + HTMLOuterWindow *child; + const WCHAR *origin; + UINT origin_len; + BOOL matches; + HRESULT hres; + BSTR bstr; + + if(!window) + return S_OK; + + LIST_FOR_EACH_ENTRY(child, &window->children, HTMLOuterWindow, sibling_entry) { + hres = send_storage_event_impl(ctx, child->base.inner_window); + if(FAILED(hres)) + return hres; + } + + if(window == ctx->skip_window) + return S_OK; + + /* Try it quick from session storage first, if available */ + if(window->session_storage) { + HTMLStorage *storage = impl_from_IHTMLStorage(window->session_storage); + origin = storage->session_storage->origin; + origin_len = ctx->skip_window ? wcslen(origin) : storage->session_storage->origin_len; + bstr = NULL; + }else { + hres = IUri_GetHost(window->base.outer_window->uri, &bstr); + if(hres != S_OK) { + if(SUCCEEDED(hres)) + SysFreeString(bstr); + return S_OK; + } + if(ctx->skip_window) + _wcslwr(bstr); + else { + BSTR tmp = bstr; + hres = build_session_origin(window->base.outer_window->uri, tmp, &bstr); + SysFreeString(tmp); + if(hres != S_OK) { + if(SUCCEEDED(hres)) + SysFreeString(bstr); + return S_OK; + } + } + origin = bstr; + origin_len = SysStringLen(bstr); + } + + matches = (origin_len == ctx->origin_len && !memcmp(origin, ctx->origin, origin_len * sizeof(WCHAR))); + SysFreeString(bstr); + + return matches ? push_storage_event_task(ctx, window, FALSE) : S_OK; +} + +/* Takes ownership of old_value */ +static HRESULT send_storage_event(HTMLStorage *storage, BSTR key, BSTR old_value, BSTR new_value) +{ + HTMLInnerWindow *window = storage->window; + struct send_storage_event_ctx ctx; + HTMLOuterWindow *top_window; + BSTR hostname = NULL; + HRESULT hres = S_OK; + + if(!window) + goto done; + get_top_window(window->base.outer_window, &top_window); + + ctx.key = key; + ctx.old_value = old_value; + ctx.new_value = new_value; + if(!storage->filename) { + ctx.origin = storage->session_storage->origin; + ctx.origin_len = storage->session_storage->origin_len; + ctx.skip_window = NULL; + }else { + hres = IUri_GetHost(window->base.outer_window->uri, &hostname); + if(hres != S_OK) + goto done; + _wcslwr(hostname); + ctx.origin = hostname; + ctx.origin_len = SysStringLen(hostname); + ctx.skip_window = top_window->base.inner_window; /* localStorage on native skips top window */ + } + hres = send_storage_event_impl(&ctx, top_window->base.inner_window); + + if(ctx.skip_window && hres == S_OK) + hres = push_storage_event_task(&ctx, window, TRUE); + +done: + SysFreeString(hostname); + SysFreeString(old_value); + return hres; +} + static HRESULT WINAPI HTMLStorage_QueryInterface(IHTMLStorage *iface, REFIID riid, void **ppv) { HTMLStorage *This = impl_from_IHTMLStorage(iface); diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 91d5e38c59a..9532f529332 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1253,6 +1253,130 @@ sync_test("storage", function() { sessionStorage.clear(); });
+async_test("storage events", function() { + var iframe = document.createElement("iframe"), iframe2 = document.createElement("iframe"); + var local = false, storage, storage2, v = document.documentMode, i = 0; + + var tests = [ + function() { + expect(); + storage.removeItem("foobar"); + }, + function() { + expect(0, "foobar", "", "test"); + storage.setItem("foobar", "test"); + }, + function() { + expect(1, "foobar", "test", "TEST", true); + storage2.setItem("foobar", "TEST"); + }, + function() { + expect(0, "foobar", "TEST", ""); + storage.removeItem("foobar"); + }, + function() { + expect(1, "winetest", "", "WineTest"); + storage2.setItem("winetest", "WineTest"); + }, + function() { + expect(0, "", "", ""); + storage.clear(); + } + ]; + + function next() { + if(++i < tests.length) + tests[i](); + else if(local) + next_test(); + else { + // w10pro64 testbot VM throws WININET_E_INTERNAL_ERROR for some reason + storage = null, storage2 = null; + try { + storage = window.localStorage, storage2 = iframe.contentWindow.localStorage; + }catch(e) { + ok(e.number === 0x72ee4 - 0x80000000, "localStorage threw " + e.number + ": " + e); + } + if(!storage || !storage2) { + win_skip("localStorage is buggy and not available, skipping"); + next_test(); + return; + } + i = 0, local = true; + + if(!storage.length) + setTimeout(function() { tests[0](); }); + else { + // Get rid of any entries first, since native doesn't update immediately + var w = [ window, iframe.contentWindow ]; + for(var j = 0; j < w.length; j++) + w[j].onstorage = w[j].document.onstorage = w[j].document.onstoragecommit = null; + document.onstoragecommit = function() { + if(!storage.length) + setTimeout(function() { tests[0](); }); + else + storage.clear(); + }; + storage.clear(); + } + } + } + + function test_event(e, key, oldValue, newValue) { + if(v < 9) { + ok(e === undefined, "event not undefined in legacy mode: " + e); + return; + } + var s = Object.prototype.toString.call(e); + todo_wine. + ok(s === "[object StorageEvent]", "Object.toString = " + s); + ok(e.key === key, "key = " + e.key + ", expected " + key); + ok(e.oldValue === oldValue, "oldValue = " + e.oldValue + ", expected " + oldValue); + ok(e.newValue === newValue, "newValue = " + e.newValue + ", expected " + newValue); + } + + function expect(idx, key, oldValue, newValue, quirk) { + var window2 = iframe.contentWindow, document2 = window2.document; + window.onstorage = function() { ok(false, "window.onstorage called"); }; + document.onstorage = function() { ok(false, "doc.onstorage called"); }; + document.onstoragecommit = function() { ok(false, "doc.onstoragecommit called"); }; + window2.onstorage = function() { ok(false, "iframe window.onstorage called"); }; + document2.onstorage = function() { ok(false, "iframe doc.onstorage called"); }; + document2.onstoragecommit = function() { ok(false, "iframe doc.onstoragecommit called"); }; + + if(idx === undefined) { + setTimeout(function() { next(); }); + }else { + // Native sometimes calls this for some reason + if(local && quirk) document.onstoragecommit = null; + + (v < 9 ? document2 : window2)["onstorage"] = function(e) { + (local && idx ? document2 : (local || v < 9 ? document : window))[local ? "onstoragecommit" : "onstorage"] = function(e) { + test_event(e, local ? "" : key, local ? "" : oldValue, local ? "" : newValue); + next(); + } + test_event(e, key, oldValue, newValue); + } + } + } + + iframe.onload = function() { + iframe2.onload = function() { + var w = iframe2.contentWindow; + w.onstorage = function() { ok(false, "about:blank window.onstorage called"); }; + w.document.onstorage = function() { ok(false, "about:blank document.onstorage called"); }; + w.document.onstoragecommit = function() { ok(false, "about:blank document.onstoragecommit called"); }; + + storage = window.sessionStorage, storage2 = iframe.contentWindow.sessionStorage; + tests[0](); + }; + iframe2.src = "about:blank"; + document.body.appendChild(iframe2); + }; + iframe.src = "blank.html"; + document.body.appendChild(iframe); +}); + sync_test("elem_attr", function() { var v = document.documentMode; var elem = document.createElement("div"), r;
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 tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=123209
Your paranoid android.
=== w10pro64_zh_CN (64 bit report) ===
mshtml: htmldoc.c:350: Test failed: expected Exec_SETTITLE htmldoc.c:2859: Test failed: unexpected call Exec_SETTITLE
This merge request was approved by Jacek Caban.