-- v3: mshtml: Implement targetOrigin for postMessage. mshtml: Semi-implement non-string primitive data for postMessage.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
IE10 and up actually pass cloned data without converting them to strings, using the Structured Clone Algorithm. Implement support for the basic variant types for now.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlevent.c | 46 ++++++++++++++---- dlls/mshtml/htmlevent.h | 2 +- dlls/mshtml/htmlwindow.c | 71 +++++++++++++++++++++++++++- dlls/mshtml/mshtml_private_iface.idl | 2 + dlls/mshtml/tests/documentmode.js | 8 +++- 5 files changed, 117 insertions(+), 12 deletions(-)
diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index 24c107487af..149611174f0 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -2314,7 +2314,7 @@ static void DOMCustomEvent_destroy(DOMEvent *event) typedef struct { DOMEvent event; IDOMMessageEvent IDOMMessageEvent_iface; - WCHAR *data; + VARIANT data; } DOMMessageEvent;
static inline DOMMessageEvent *impl_from_IDOMMessageEvent(IDOMMessageEvent *iface) @@ -2376,7 +2376,26 @@ static HRESULT WINAPI DOMMessageEvent_get_data(IDOMMessageEvent *iface, BSTR *p)
TRACE("(%p)->(%p)\n", This, p);
- return (*p = SysAllocString(This->data)) ? S_OK : E_OUTOFMEMORY; + if(V_VT(&This->data) != VT_BSTR) { + FIXME("non-string data\n"); + return E_NOTIMPL; + } + + return (*p = SysAllocString(V_BSTR(&This->data))) ? S_OK : E_OUTOFMEMORY; +} + +static HRESULT DOMMessageEvent_get_data_hook(DispatchEx *dispex, WORD flags, DISPPARAMS *dp, VARIANT *res, + EXCEPINFO *ei, IServiceProvider *caller) +{ + DOMMessageEvent *This = CONTAINING_RECORD(dispex, DOMMessageEvent, event.dispex); + + if(!(flags & DISPATCH_PROPERTYGET) || !res) + return S_FALSE; + + TRACE("(%p)->(%p)\n", This, res); + + V_VT(res) = VT_EMPTY; + return VariantCopy(res, &This->data); }
static HRESULT WINAPI DOMMessageEvent_get_origin(IDOMMessageEvent *iface, BSTR *p) @@ -2433,7 +2452,16 @@ static void *DOMMessageEvent_query_interface(DOMEvent *event, REFIID riid) static void DOMMessageEvent_destroy(DOMEvent *event) { DOMMessageEvent *message_event = DOMMessageEvent_from_DOMEvent(event); - heap_free(message_event->data); + VariantClear(&message_event->data); +} + +static void DOMMessageEvent_init_dispex_info(dispex_data_t *info, compat_mode_t compat_mode) +{ + static const dispex_hook_t hooks[] = { + {DISPID_IDOMMESSAGEEVENT_DATA, DOMMessageEvent_get_data_hook}, + {DISPID_UNKNOWN} + }; + dispex_info_add_interface(info, IDOMMessageEvent_tid, compat_mode >= COMPAT_MODE_IE10 ? hooks : NULL); }
typedef struct { @@ -2646,7 +2674,6 @@ static dispex_static_data_t DOMCustomEvent_dispex = {
static const tid_t DOMMessageEvent_iface_tids[] = { IDOMEvent_tid, - IDOMMessageEvent_tid, 0 };
@@ -2654,7 +2681,8 @@ dispex_static_data_t DOMMessageEvent_dispex = { L"MessageEvent", NULL, DispDOMMessageEvent_tid, - DOMMessageEvent_iface_tids + DOMMessageEvent_iface_tids, + DOMMessageEvent_init_dispex_info };
static const tid_t DOMProgressEvent_iface_tids[] = { @@ -2878,7 +2906,7 @@ HRESULT create_document_event(HTMLDocumentNode *doc, eventid_t event_id, DOMEven return S_OK; }
-HRESULT create_message_event(HTMLDocumentNode *doc, BSTR data, DOMEvent **ret) +HRESULT create_message_event(HTMLDocumentNode *doc, VARIANT *data, DOMEvent **ret) { DOMMessageEvent *message_event; DOMEvent *event; @@ -2889,9 +2917,11 @@ HRESULT create_message_event(HTMLDocumentNode *doc, BSTR data, DOMEvent **ret) return hres; message_event = DOMMessageEvent_from_DOMEvent(event);
- if(!(message_event->data = heap_strdupW(data))) { + V_VT(&message_event->data) = VT_EMPTY; + hres = VariantCopy(&message_event->data, data); + if(FAILED(hres)) { IDOMEvent_Release(&event->IDOMEvent_iface); - return E_OUTOFMEMORY; + return hres; }
*ret = event; diff --git a/dlls/mshtml/htmlevent.h b/dlls/mshtml/htmlevent.h index d0c72d87a5d..ed11e660789 100644 --- a/dlls/mshtml/htmlevent.h +++ b/dlls/mshtml/htmlevent.h @@ -111,7 +111,7 @@ void dispatch_event(EventTarget*,DOMEvent*) DECLSPEC_HIDDEN; HRESULT create_document_event(HTMLDocumentNode*,eventid_t,DOMEvent**) DECLSPEC_HIDDEN; 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*,BSTR,DOMEvent**) DECLSPEC_HIDDEN; +HRESULT create_message_event(HTMLDocumentNode*,VARIANT*,DOMEvent**) DECLSPEC_HIDDEN;
void init_nsevents(HTMLDocumentNode*) DECLSPEC_HIDDEN; void release_nsevents(HTMLDocumentNode*) DECLSPEC_HIDDEN; diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index 64e6ae25849..8bc30ba395c 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -2218,6 +2218,7 @@ static HRESULT WINAPI HTMLWindow6_postMessage(IHTMLWindow6 *iface, BSTR msg, VAR HTMLWindow *This = impl_from_IHTMLWindow6(iface); DOMEvent *event; HRESULT hres; + VARIANT var;
FIXME("(%p)->(%s %s) semi-stub\n", This, debugstr_w(msg), debugstr_variant(&targetOrigin));
@@ -2226,7 +2227,9 @@ static HRESULT WINAPI HTMLWindow6_postMessage(IHTMLWindow6 *iface, BSTR msg, VAR return E_FAIL; }
- hres = create_message_event(This->inner_window->doc, msg, &event); + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = msg; + hres = create_message_event(This->inner_window->doc, &var, &event); if(FAILED(hres)) return hres;
@@ -3175,6 +3178,71 @@ static HRESULT WINAPI window_private_matchMedia(IWineHTMLWindowPrivate *iface, B return create_media_query_list(This, media_query, media_query_list); }
+static HRESULT WINAPI window_private_postMessage(IWineHTMLWindowPrivate *iface, VARIANT msg, BSTR targetOrigin, VARIANT transfer) +{ + HTMLWindow *This = impl_from_IWineHTMLWindowPrivateVtbl(iface); + HTMLInnerWindow *window = This->inner_window; + struct post_message_task *task; + DOMEvent *event; + HRESULT hres; + + TRACE("iface %p, msg %s, targetOrigin %s, transfer %s\n", iface, debugstr_variant(&msg), + debugstr_w(targetOrigin), debugstr_variant(&transfer)); + + if(V_VT(&transfer) != VT_EMPTY) + FIXME("transfer not implemented, ignoring\n"); + + switch(V_VT(&msg)) { + case VT_EMPTY: + case VT_NULL: + case VT_VOID: + case VT_I1: + case VT_I2: + case VT_I4: + case VT_I8: + case VT_UI1: + case VT_UI2: + case VT_UI4: + case VT_UI8: + case VT_INT: + case VT_UINT: + case VT_R4: + case VT_R8: + case VT_BOOL: + case VT_BSTR: + case VT_CY: + case VT_DATE: + case VT_DECIMAL: + case VT_HRESULT: + break; + case VT_ERROR: + V_VT(&msg) = VT_EMPTY; + break; + default: + FIXME("Unsupported vt %d\n", V_VT(&msg)); + return E_NOTIMPL; + } + + if(!window->doc) { + FIXME("No document\n"); + return E_FAIL; + } + + hres = create_message_event(window->doc, &msg, &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, post_message_proc, post_message_destr, window->task_magic); +} + static HRESULT WINAPI window_private_get_console(IWineHTMLWindowPrivate *iface, IDispatch **console) { HTMLWindow *This = impl_from_IWineHTMLWindowPrivateVtbl(iface); @@ -3202,6 +3270,7 @@ static const IWineHTMLWindowPrivateVtbl WineHTMLWindowPrivateVtbl = { window_private_cancelAnimationFrame, window_private_get_console, window_private_matchMedia, + window_private_postMessage, };
static inline HTMLWindow *impl_from_IWineHTMLWindowCompatPrivateVtbl(IWineHTMLWindowCompatPrivate *iface) diff --git a/dlls/mshtml/mshtml_private_iface.idl b/dlls/mshtml/mshtml_private_iface.idl index a17960e493b..fc023473cc9 100644 --- a/dlls/mshtml/mshtml_private_iface.idl +++ b/dlls/mshtml/mshtml_private_iface.idl @@ -112,6 +112,8 @@ interface IWineHTMLWindowPrivate : IDispatch HRESULT console([retval, out] IDispatch **console); [id(53)] HRESULT matchMedia([in] BSTR media_query, [retval, out] IDispatch **media_query_list); + [id(54)] + HRESULT postMessage([in] VARIANT msg, [in] BSTR targetOrigin, [in, optional] VARIANT transfer); }
[ diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 76fa6bacac8..0ec4785450e 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -2078,10 +2078,14 @@ sync_test("__defineSetter__", function() { async_test("postMessage", function() { var v = document.documentMode; var onmessage_called = false; - window.onmessage = function() { + window.onmessage = function(e) { onmessage_called = true; + if(v < 9) + ok(e === undefined, "e = " + e); + else + ok(e.data === (v < 10 ? "10" : 10), "e.data = " + e.data); next_test(); } - window.postMessage("test", "*"); + window.postMessage(10, "*"); ok(onmessage_called == (v < 9 ? true : false), "onmessage not called"); });
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlwindow.c | 74 ++++++++++++++++++++++++++++++- dlls/mshtml/tests/documentmode.js | 53 ++++++++++++++++++++-- dlls/mshtml/tests/events.js | 2 +- 3 files changed, 124 insertions(+), 5 deletions(-)
diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index 8bc30ba395c..7d55d53cfe8 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -25,6 +25,7 @@ #include "winuser.h" #include "ole2.h" #include "mshtmdid.h" +#include "wininet.h" #include "shlguid.h" #include "shobjidl.h" #include "exdispid.h" @@ -2213,6 +2214,66 @@ static void post_message_destr(task_t *_task) IHTMLWindow2_Release(&task->window->base.IHTMLWindow2_iface); }
+static HRESULT check_target_origin(HTMLInnerWindow *window, const WCHAR *target_origin) +{ + IUri *uri, *target; + DWORD port, port2; + BSTR bstr, bstr2; + HRESULT hres; + + if(!wcscmp(target_origin, L"*")) + return S_OK; + + hres = create_uri(target_origin, Uri_CREATE_NOFRAG | Uri_CREATE_NO_DECODE_EXTRA_INFO, &target); + if(FAILED(hres)) + return hres; + + if(!(uri = window->base.outer_window->uri)) { + FIXME("window with no URI\n"); + hres = S_FALSE; + goto done; + } + + hres = IUri_GetSchemeName(uri, &bstr); + if(FAILED(hres)) + goto done; + hres = IUri_GetSchemeName(target, &bstr2); + if(SUCCEEDED(hres)) { + hres = !wcsicmp(bstr, bstr2) ? S_OK : S_FALSE; + SysFreeString(bstr2); + } + SysFreeString(bstr); + if(hres != S_OK) + goto done; + + hres = IUri_GetHost(uri, &bstr); + if(FAILED(hres)) + goto done; + hres = IUri_GetHost(target, &bstr2); + if(SUCCEEDED(hres)) { + hres = !wcsicmp(bstr, bstr2) ? S_OK : S_FALSE; + SysFreeString(bstr2); + } + SysFreeString(bstr); + if(hres != S_OK) + goto done; + + /* Legacy modes ignore port */ + if(dispex_compat_mode(&window->event_target.dispex) < COMPAT_MODE_IE9) + goto done; + + hres = IUri_GetPort(uri, &port); + if(FAILED(hres)) + goto done; + hres = IUri_GetPort(target, &port2); + if(SUCCEEDED(hres)) + hres = (port == port2) ? S_OK : S_FALSE; + +done: + IUri_Release(target); + return hres; +} + static HRESULT WINAPI HTMLWindow6_postMessage(IHTMLWindow6 *iface, BSTR msg, VARIANT targetOrigin) { HTMLWindow *This = impl_from_IHTMLWindow6(iface); @@ -2220,7 +2281,14 @@ static HRESULT WINAPI HTMLWindow6_postMessage(IHTMLWindow6 *iface, BSTR msg, VAR HRESULT hres; VARIANT var;
- FIXME("(%p)->(%s %s) semi-stub\n", This, debugstr_w(msg), debugstr_variant(&targetOrigin)); + TRACE("(%p)->(%s %s)\n", This, debugstr_w(msg), debugstr_variant(&targetOrigin)); + + if(V_VT(&targetOrigin) != VT_BSTR) + return E_INVALIDARG; + + hres = check_target_origin(This->inner_window, V_BSTR(&targetOrigin)); + if(hres != S_OK) + return SUCCEEDED(hres) ? S_OK : hres;
if(!This->inner_window->doc) { FIXME("No document\n"); @@ -3192,6 +3260,10 @@ static HRESULT WINAPI window_private_postMessage(IWineHTMLWindowPrivate *iface, if(V_VT(&transfer) != VT_EMPTY) FIXME("transfer not implemented, ignoring\n");
+ hres = check_target_origin(window, targetOrigin); + if(hres != S_OK) + return SUCCEEDED(hres) ? S_OK : hres; + switch(V_VT(&msg)) { case VT_EMPTY: case VT_NULL: diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 0ec4785450e..5b746598dba 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -2082,10 +2082,57 @@ async_test("postMessage", function() { onmessage_called = true; if(v < 9) ok(e === undefined, "e = " + e); - else + else { ok(e.data === (v < 10 ? "10" : 10), "e.data = " + e.data); - next_test(); + next_test(); + } + } + + var invalid = [ + v < 10 ? { toString: function() { return "http://winetest.example.org"; } } : null, + (function() { return "http://winetest.example.org"; }), + "winetest.example.org", + "example.org", + undefined + ]; + for(var i = 0; i < invalid.length; i++) { + try { + window.postMessage("invalid " + i, invalid[i]); + ok(false, "expected exception with targetOrigin " + invalid[i]); + }catch(ex) { + var n = ex.number >>> 0; + todo_wine_if(v >= 10). + ok(n === (v < 10 ? 0x80070057 : 0), "postMessage with targetOrigin " + invalid[i] + " threw " + n); + if(v >= 10) + todo_wine. + ok(ex.name === "SyntaxError", "postMessage with targetOrigin " + invalid[i] + " threw " + ex.name); + } } - window.postMessage(10, "*"); + try { + window.postMessage("invalid empty", ""); + ok(false, "expected exception with empty targetOrigin"); + }catch(ex) { + var n = ex.number >>> 0; + ok(n === 0x80070057, "postMessage with empty targetOrigin threw " + n); + } + + window.postMessage("wrong port", "http://winetest.example.org:1234"); + ok(onmessage_called == (v < 9 ? true : false), "onmessage not called with wrong port"); + onmessage_called = false; + + var not_sent = [ + "http://winetest.example.com", + "ftp://winetest.example.org", + "http://wine.example.org", + "http://example.org" + ]; + for(var i = 0; i < not_sent.length; i++) { + window.postMessage("not_sent " + i, not_sent[i]); + ok(onmessage_called == false, "onmessage called with targetOrigin " + not_sent[i]); + onmessage_called = false; + } + + window.postMessage(10, (v < 10 ? "*" : { toString: function() { return "*"; } })); ok(onmessage_called == (v < 9 ? true : false), "onmessage not called"); + if(v < 9) next_test(); }); diff --git a/dlls/mshtml/tests/events.js b/dlls/mshtml/tests/events.js index e67bf07d210..997e583ad20 100644 --- a/dlls/mshtml/tests/events.js +++ b/dlls/mshtml/tests/events.js @@ -818,6 +818,6 @@ async_test("message event", function() { next_test(); });
- window.postMessage("test", "http://winetest.example.org"); + window.postMessage("test", "httP://wineTest.example.org"); ok(listener_called == false, "listener already called"); });
Jacek Caban (@jacek) commented about dlls/mshtml/htmlwindow.c:
return E_FAIL; }
- hres = create_message_event(This->inner_window->doc, msg, &event);
- V_VT(&var) = VT_BSTR;
- V_BSTR(&var) = msg;
- hres = create_message_event(This->inner_window->doc, &var, &event);
You could just forward the call to IWineHTMLWindowPrivate here to avoid duplication and simplify the next patch.