mshtml: Use a hook to implement postMessage.

We need the caller ServiceProvider to obtain the source.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com>
This commit is contained in:
Gabriel Ivăncescu 2024-02-05 16:15:22 +02:00 committed by Alexandre Julliard
parent 7c7544aba1
commit 22214ec357
2 changed files with 211 additions and 164 deletions

View file

@ -2234,6 +2234,87 @@ static HRESULT WINAPI HTMLWindow6_get_maxConnectionsPerServer(IHTMLWindow6 *ifac
return E_NOTIMPL;
}
static HRESULT check_target_origin(HTMLInnerWindow *window, const WCHAR *target_origin)
{
BOOL no_port = FALSE;
IUri *uri, *target;
DWORD port, port2;
BSTR bstr, bstr2;
HRESULT hres;
if(!target_origin)
return E_INVALIDARG;
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;
}
bstr = NULL;
hres = IUri_GetSchemeName(uri, &bstr);
if(hres != S_OK) {
SysFreeString(bstr);
goto done;
}
hres = IUri_GetSchemeName(target, &bstr2);
if(SUCCEEDED(hres)) {
if(hres == S_OK && wcsicmp(bstr, bstr2))
hres = S_FALSE;
SysFreeString(bstr2);
}
SysFreeString(bstr);
if(hres != S_OK)
goto done;
bstr = NULL;
hres = IUri_GetHost(uri, &bstr);
if(hres != S_OK) {
SysFreeString(bstr);
goto done;
}
hres = IUri_GetHost(target, &bstr2);
if(SUCCEEDED(hres)) {
if(hres == S_OK && wcsicmp(bstr, bstr2))
hres = 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(hres != S_OK) {
if(FAILED(hres))
goto done;
no_port = TRUE; /* some protocols don't have ports (e.g. res) */
}
hres = IUri_GetPort(target, &port2);
if(hres != S_OK) {
if(FAILED(hres))
goto done;
if(no_port)
hres = S_OK;
}else if(no_port || port != port2) {
hres = S_FALSE;
}
done:
IUri_Release(target);
return hres;
}
struct post_message_task {
event_task_t header;
DOMEvent *event;
@ -2251,6 +2332,77 @@ static void post_message_destr(event_task_t *_task)
IDOMEvent_Release(&task->event->IDOMEvent_iface);
}
static HRESULT post_message(HTMLInnerWindow *window, VARIANT msg, BSTR targetOrigin, VARIANT transfer,
IServiceProvider *caller, compat_mode_t compat_mode)
{
DOMEvent *event;
HRESULT hres;
if(V_VT(&transfer) != VT_EMPTY && V_VT(&transfer) != VT_ERROR)
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:
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(compat_mode >= COMPAT_MODE_IE9) {
struct post_message_task *task;
if(!(task = malloc(sizeof(*task)))) {
IDOMEvent_Release(&event->IDOMEvent_iface);
return E_OUTOFMEMORY;
}
/* Because message events can be sent to different windows, they get blocked by any context */
task->header.thread_blocked = TRUE;
task->event = event;
return push_event_task(&task->header, window, post_message_proc, post_message_destr, window->task_magic);
}
dispatch_event(&window->event_target, event);
IDOMEvent_Release(&event->IDOMEvent_iface);
return S_OK;
}
static HRESULT WINAPI HTMLWindow6_postMessage(IHTMLWindow6 *iface, BSTR msg, VARIANT targetOrigin)
{
HTMLWindow *This = impl_from_IHTMLWindow6(iface);
@ -2264,7 +2416,8 @@ static HRESULT WINAPI HTMLWindow6_postMessage(IHTMLWindow6 *iface, BSTR msg, VAR
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = msg;
V_VT(&transfer) = VT_EMPTY;
return IWineHTMLWindowPrivate_postMessage(&This->IWineHTMLWindowPrivate_iface, var, V_BSTR(&targetOrigin), transfer);
return post_message(This->inner_window, var, V_BSTR(&targetOrigin), transfer, NULL,
dispex_compat_mode(&This->inner_window->event_target.dispex));
}
static HRESULT WINAPI HTMLWindow6_toStaticHTML(IHTMLWindow6 *iface, BSTR bstrHTML, BSTR *pbstrStaticHTML)
@ -3197,162 +3350,6 @@ static HRESULT WINAPI window_private_matchMedia(IWineHTMLWindowPrivate *iface, B
return create_media_query_list(This, media_query, media_query_list);
}
static HRESULT check_target_origin(HTMLInnerWindow *window, const WCHAR *target_origin)
{
BOOL no_port = FALSE;
IUri *uri, *target;
DWORD port, port2;
BSTR bstr, bstr2;
HRESULT hres;
if(!target_origin)
return E_INVALIDARG;
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;
}
bstr = NULL;
hres = IUri_GetSchemeName(uri, &bstr);
if(hres != S_OK) {
SysFreeString(bstr);
goto done;
}
hres = IUri_GetSchemeName(target, &bstr2);
if(SUCCEEDED(hres)) {
if(hres == S_OK && wcsicmp(bstr, bstr2))
hres = S_FALSE;
SysFreeString(bstr2);
}
SysFreeString(bstr);
if(hres != S_OK)
goto done;
bstr = NULL;
hres = IUri_GetHost(uri, &bstr);
if(hres != S_OK) {
SysFreeString(bstr);
goto done;
}
hres = IUri_GetHost(target, &bstr2);
if(SUCCEEDED(hres)) {
if(hres == S_OK && wcsicmp(bstr, bstr2))
hres = 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(hres != S_OK) {
if(FAILED(hres))
goto done;
no_port = TRUE; /* some protocols don't have ports (e.g. res) */
}
hres = IUri_GetPort(target, &port2);
if(hres != S_OK) {
if(FAILED(hres))
goto done;
if(no_port)
hres = S_OK;
}else if(no_port || port != port2) {
hres = S_FALSE;
}
done:
IUri_Release(target);
return hres;
}
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;
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 && V_VT(&transfer) != VT_ERROR)
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:
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(dispex_compat_mode(&window->event_target.dispex) >= COMPAT_MODE_IE9) {
struct post_message_task *task;
if(!(task = malloc(sizeof(*task)))) {
IDOMEvent_Release(&event->IDOMEvent_iface);
return E_OUTOFMEMORY;
}
/* Because message events can be sent to different windows, they get blocked by any context */
task->header.thread_blocked = TRUE;
task->event = event;
return push_event_task(&task->header, window, post_message_proc, post_message_destr, window->task_magic);
}
dispatch_event(&window->event_target, event);
IDOMEvent_Release(&event->IDOMEvent_iface);
return S_OK;
}
static HRESULT WINAPI window_private_get_console(IWineHTMLWindowPrivate *iface, IDispatch **console)
{
HTMLWindow *This = impl_from_IWineHTMLWindowPrivateVtbl(iface);
@ -3401,7 +3398,6 @@ static const IWineHTMLWindowPrivateVtbl WineHTMLWindowPrivateVtbl = {
window_private_cancelAnimationFrame,
window_private_get_console,
window_private_matchMedia,
window_private_postMessage,
window_private_get_MutationObserver
};
@ -4219,6 +4215,57 @@ static HRESULT IHTMLWindow3_setTimeout_hook(DispatchEx *dispex, WORD flags, DISP
return dispex_call_builtin(dispex, DISPID_IHTMLWINDOW3_SETTIMEOUT, &new_dp, res, ei, caller);
}
static HRESULT IHTMLWindow6_postMessage_hook(DispatchEx *dispex, WORD flags, DISPPARAMS *dp, VARIANT *res,
EXCEPINFO *ei, IServiceProvider *caller)
{
HTMLInnerWindow *This = impl_from_DispatchEx(dispex);
BSTR targetOrigin, converted_msg = NULL;
VARIANT msg, transfer, converted;
compat_mode_t compat_mode;
HRESULT hres;
if(!(flags & DISPATCH_METHOD) || dp->cArgs < 2 || dp->cNamedArgs)
return S_FALSE;
compat_mode = dispex_compat_mode(&This->event_target.dispex);
msg = dp->rgvarg[dp->cArgs - 1];
V_VT(&transfer) = VT_EMPTY;
if(compat_mode >= COMPAT_MODE_IE10 && dp->cArgs > 2)
transfer = dp->rgvarg[dp->cArgs - 3];
TRACE("(%p)->(msg %s, targetOrigin %s, transfer %s)\n", This, debugstr_variant(&msg),
debugstr_variant(&dp->rgvarg[dp->cArgs - 2]), debugstr_variant(&transfer));
if(compat_mode < COMPAT_MODE_IE10 && V_VT(&msg) != VT_BSTR) {
hres = change_type(&msg, &dp->rgvarg[dp->cArgs - 1], VT_BSTR, caller);
if(FAILED(hres))
return hres;
converted_msg = V_BSTR(&msg);
}
if(V_VT(&dp->rgvarg[dp->cArgs - 2]) == VT_BSTR) {
targetOrigin = V_BSTR(&dp->rgvarg[dp->cArgs - 2]);
V_BSTR(&converted) = NULL;
}else {
if(compat_mode < COMPAT_MODE_IE10) {
SysFreeString(converted_msg);
return E_INVALIDARG;
}
hres = change_type(&converted, &dp->rgvarg[dp->cArgs - 2], VT_BSTR, caller);
if(FAILED(hres)) {
SysFreeString(converted_msg);
return hres;
}
targetOrigin = V_BSTR(&converted);
}
hres = post_message(This, msg, targetOrigin, transfer, caller, compat_mode);
SysFreeString(V_BSTR(&converted));
SysFreeString(converted_msg);
return hres;
}
static void HTMLWindow_init_dispex_info(dispex_data_t *info, compat_mode_t compat_mode)
{
static const dispex_hook_t window2_hooks[] = {
@ -4244,6 +4291,10 @@ static void HTMLWindow_init_dispex_info(dispex_data_t *info, compat_mode_t compa
{DISPID_IHTMLWINDOW4_CREATEPOPUP, NULL},
{DISPID_UNKNOWN}
};
static const dispex_hook_t window6_hooks[] = {
{DISPID_IHTMLWINDOW6_POSTMESSAGE, IHTMLWindow6_postMessage_hook},
{DISPID_UNKNOWN}
};
/* Hide props not available in IE10 */
static const dispex_hook_t private_ie10_hooks[] = {
@ -4259,6 +4310,7 @@ static void HTMLWindow_init_dispex_info(dispex_data_t *info, compat_mode_t compa
dispex_info_add_interface(info, IWineHTMLWindowPrivate_tid,
compat_mode >= COMPAT_MODE_IE11 ? NULL : private_ie10_hooks);
dispex_info_add_interface(info, IHTMLWindow6_tid, window6_hooks);
dispex_info_add_interface(info, IHTMLWindow5_tid, NULL);
dispex_info_add_interface(info, IHTMLWindow4_tid, compat_mode >= COMPAT_MODE_IE11 ? window4_ie11_hooks : NULL);
dispex_info_add_interface(info, IHTMLWindow3_tid, compat_mode >= COMPAT_MODE_IE11 ? window3_ie11_hooks : window3_hooks);
@ -4290,10 +4342,7 @@ static const event_target_vtbl_t HTMLWindow_event_target_vtbl = {
.set_current_event = HTMLWindow_set_current_event
};
static const tid_t HTMLWindow_iface_tids[] = {
IHTMLWindow6_tid,
0
};
static const tid_t HTMLWindow_iface_tids[] = { 0 };
static dispex_static_data_t HTMLWindow_dispex = {
"Window",

View file

@ -131,8 +131,6 @@ 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);
[propget, id(DISPID_IWINEHTMLWINDOWPRIVATE_MUTATIONOBSERVER)]
HRESULT MutationObserver([retval, out] IDispatch **observer_ctor);
}