mshtml: Expose IEventTarget on HTMLDocumentObj and forward it to the doc node.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com>
This commit is contained in:
Gabriel Ivăncescu 2022-11-14 19:53:26 +02:00 committed by Alexandre Julliard
parent 7359226ad1
commit cab7ce5935
6 changed files with 275 additions and 17 deletions

View file

@ -677,6 +677,7 @@ struct HTMLDocumentObj {
IObjectSafety IObjectSafety_iface;
IServiceProvider IServiceProvider_iface;
ITargetContainer ITargetContainer_iface;
IEventTarget IEventTarget_iface;
IWindowForBindingUI IWindowForBindingUI_iface;

View file

@ -3238,6 +3238,52 @@ static const IDocumentRangeVtbl DocObjDocumentRangeVtbl = {
DocObjDocumentRange_createRange
};
/**********************************************************
* IEventTarget implementation
*/
static inline HTMLDocumentObj *impl_from_IEventTarget(IEventTarget *iface)
{
return CONTAINING_RECORD(iface, HTMLDocumentObj, IEventTarget_iface);
}
HTMLDOCUMENTOBJ_IDISPATCH_METHODS(EventTarget)
static HRESULT WINAPI DocObjEventTarget_addEventListener(IEventTarget *iface, BSTR type, IDispatch *listener,
VARIANT_BOOL capture)
{
HTMLDocumentObj *This = impl_from_IEventTarget(iface);
return IEventTarget_addEventListener(&This->doc_node->node.event_target.IEventTarget_iface, type, listener, capture);
}
static HRESULT WINAPI DocObjEventTarget_removeEventListener(IEventTarget *iface, BSTR type, IDispatch *listener,
VARIANT_BOOL capture)
{
HTMLDocumentObj *This = impl_from_IEventTarget(iface);
return IEventTarget_removeEventListener(&This->doc_node->node.event_target.IEventTarget_iface, type, listener, capture);
}
static HRESULT WINAPI DocObjEventTarget_dispatchEvent(IEventTarget *iface, IDOMEvent *event_iface, VARIANT_BOOL *result)
{
HTMLDocumentObj *This = impl_from_IEventTarget(iface);
return IEventTarget_dispatchEvent(&This->doc_node->node.event_target.IEventTarget_iface, event_iface, result);
}
static const IEventTargetVtbl DocObjEventTargetVtbl = {
DocObjEventTarget_QueryInterface,
DocObjEventTarget_AddRef,
DocObjEventTarget_Release,
DocObjEventTarget_GetTypeInfoCount,
DocObjEventTarget_GetTypeInfo,
DocObjEventTarget_GetIDsOfNames,
DocObjEventTarget_Invoke,
DocObjEventTarget_addEventListener,
DocObjEventTarget_removeEventListener,
DocObjEventTarget_dispatchEvent
};
static inline HTMLDocumentObj *impl_from_IUnknown(IUnknown *iface)
{
return CONTAINING_RECORD(iface, HTMLDocumentObj, IUnknown_inner);
@ -3339,6 +3385,14 @@ static HRESULT WINAPI HTMLDocumentObj_QueryInterface(IUnknown *iface, REFIID rii
*ppv = &This->ITargetContainer_iface;
}else if(IsEqualGUID(&IID_IConnectionPointContainer, riid)) {
*ppv = &This->cp_container.IConnectionPointContainer_iface;
}else if(IsEqualGUID(&IID_IEventTarget, riid)) {
/* IEventTarget is conditionally exposed. This breaks COM rules when
it changes its compat mode, but it is how native works (see tests). */
if(!This->doc_node || dispex_compat_mode(&This->doc_node->node.event_target.dispex) < COMPAT_MODE_IE9) {
*ppv = NULL;
return E_NOINTERFACE;
}
*ppv = &This->IEventTarget_iface;
}else if(IsEqualGUID(&CLSID_CMarkup, riid)) {
FIXME("(%p)->(CLSID_CMarkup %p)\n", This, ppv);
*ppv = NULL;
@ -3734,6 +3788,7 @@ static HRESULT create_document_object(BOOL is_mhtml, IUnknown *outer, REFIID rii
doc->IMarkupContainer_iface.lpVtbl = &DocObjMarkupContainerVtbl;
doc->IDisplayServices_iface.lpVtbl = &DocObjDisplayServicesVtbl;
doc->IDocumentRange_iface.lpVtbl = &DocObjDocumentRangeVtbl;
doc->IEventTarget_iface.lpVtbl = &DocObjEventTargetVtbl;
doc->outer_unk = outer ? outer : &doc->IUnknown_inner;

View file

@ -0,0 +1,3 @@
<html>
<script type="text/javascript">document.prop = 137;</script>
</html>

View file

@ -0,0 +1,4 @@
<html><head>
<meta http-equiv="x-ua-compatible" content="IE=9" />
<script type="text/javascript">document.prop = 137;</script>
</head></html>

View file

@ -31,6 +31,7 @@
#include "mshtmhst.h"
#include "docobj.h"
#include "hlink.h"
#include "wininet.h"
#include "shdeprecated.h"
#include "dispex.h"
@ -62,6 +63,7 @@
#define CLEAR_CALLED(func) \
expect_ ## func = called_ ## func = FALSE
DEFINE_EXPECT(docobj_onclick);
DEFINE_EXPECT(document_onclick);
DEFINE_EXPECT(body_onclick);
DEFINE_EXPECT(doc_onclick_attached);
@ -146,6 +148,8 @@ static const char input_doc_str[] =
static const char iframe_doc_str[] =
"<html><body><iframe id=\"ifr\">Testing</iframe></body></html>";
static void navigate(IHTMLDocument2*,const WCHAR*);
static BOOL iface_cmp(IUnknown *iface1, IUnknown *iface2)
{
IUnknown *unk1, *unk2;
@ -967,6 +971,16 @@ static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown
}; \
static IDispatchEx event ## _obj = { &event ## FuncVtbl };
static HRESULT WINAPI docobj_onclick(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
{
CHECK_EXPECT(docobj_onclick);
test_event_args(document_mode < 9 ? &DIID_DispHTMLDocument : NULL, id, wFlags, pdp, pvarRes, pei, pspCaller);
return S_OK;
}
EVENT_HANDLER_FUNC_OBJ(docobj_onclick);
static HRESULT WINAPI document_onclick(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
{
@ -2731,6 +2745,71 @@ static void test_iframe_connections(IHTMLDocument2 *doc)
IHTMLDocument2_Release(iframes_doc);
}
static void test_doc_obj(IHTMLDocument2 *doc)
{
int orig_doc_mode = document_mode;
IEventTarget *event_target;
IHTMLElement *body;
HRESULT hres;
BSTR bstr;
event_target = (void*)0xdeadbeef;
hres = IHTMLDocument2_QueryInterface(doc, &IID_IEventTarget, (void**)&event_target);
if(document_mode < 9) {
ok(hres == E_NOINTERFACE, "hres = %08lx, expected E_NOINTERFACE\n", hres);
ok(!event_target, "event_target != NULL\n");
}else {
IHTMLDocument2 *tmp;
ok(hres == S_OK, "hres = %08lx, expected S_OK\n", hres);
ok(!!event_target, "event_target = NULL\n");
bstr = SysAllocString(L"click");
IEventTarget_addEventListener(event_target, bstr, (IDispatch*)&docobj_onclick_obj, TRUE);
ok(hres == S_OK, "addEventListener failed: %08lx\n", hres);
SysFreeString(bstr);
hres = IEventTarget_QueryInterface(event_target, &IID_IHTMLDocument2, (void**)&tmp);
ok(hres == S_OK, "Could not get IHTMLDocument2: %08lx\n", hres);
IEventTarget_Release(event_target);
ok(doc == tmp, "IHTMLDocument2 from IEventTarget not same as original\n");
IHTMLDocument2_Release(tmp);
body = doc_get_body(doc);
SET_EXPECT(docobj_onclick);
hres = IHTMLElement_click(body);
ok(hres == S_OK, "click failed: %08lx\n", hres);
IHTMLElement_Release(body);
pump_msgs(&called_docobj_onclick);
CHECK_CALLED(docobj_onclick);
}
/* Navigate to a different document mode page, checking using the same doc obj.
Test that it breaks COM rules, since IEventTarget is conditionally exposed.
All the events registered on the old doc node are also removed. */
navigate(doc, document_mode < 9 ? L"doc_with_prop_ie9.html" : L"doc_with_prop.html");
ok(document_mode == (orig_doc_mode < 9 ? 9 : 5), "new document_mode = %d\n", document_mode);
event_target = (void*)0xdeadbeef;
hres = IHTMLDocument2_QueryInterface(doc, &IID_IEventTarget, (void**)&event_target);
if(document_mode < 9) {
ok(hres == E_NOINTERFACE, "hres = %08lx, expected E_NOINTERFACE\n", hres);
ok(!event_target, "event_target != NULL\n");
body = doc_get_body(doc);
hres = IHTMLElement_click(body);
ok(hres == S_OK, "click failed: %08lx\n", hres);
IHTMLElement_Release(body);
pump_msgs(NULL);
}else {
ok(hres == S_OK, "hres = %08lx, expected S_OK\n", hres);
ok(!!event_target, "event_target = NULL\n");
IEventTarget_Release(event_target);
}
}
static void test_create_event(IHTMLDocument2 *doc)
{
IDOMKeyboardEvent *keyboard_event;
@ -3325,9 +3404,11 @@ static HRESULT WINAPI DocumentSite_ActivateMe(IOleDocumentSite *iface, IOleDocum
hres = IOleDocumentView_QueryInterface(pViewToActivate, &IID_IOleDocument, (void**)&document);
ok(hres == S_OK, "could not get IOleDocument: %08lx\n", hres);
hres = IOleDocument_CreateView(document, &InPlaceSite, NULL, 0, &view);
if(!view) {
hres = IOleDocument_CreateView(document, &InPlaceSite, NULL, 0, &view);
ok(hres == S_OK, "CreateView failed: %08lx\n", hres);
}
IOleDocument_Release(document);
ok(hres == S_OK, "CreateView failed: %08lx\n", hres);
hres = IOleDocumentView_SetInPlaceSite(view, &InPlaceSite);
ok(hres == S_OK, "SetInPlaceSite failed: %08lx\n", hres);
@ -4689,24 +4770,12 @@ 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;
@ -4724,6 +4793,51 @@ static void doc_load_string(IHTMLDocument2 *doc, const char *str)
IStream_Release(stream);
}
static void doc_load_res(IHTMLDocument2 *doc, const WCHAR *file)
{
static const WCHAR res[] = { 'r','e','s',':','/','/' };
WCHAR url[INTERNET_MAX_URL_LENGTH];
IPersistMoniker *persist;
IHlinkTarget *hlink;
IBindCtx *bind;
IMoniker *mon;
HRESULT hres;
DWORD len;
BSTR bstr;
wcscpy(url, SZ_HTML_CLIENTSITE_OBJECTPARAM);
CreateBindCtx(0, &bind);
IBindCtx_RegisterObjectParam(bind, url, (IUnknown*)&ClientSite);
notif_doc = doc;
doc_complete = FALSE;
memcpy(url, res, sizeof(res));
len = 6 + GetModuleFileNameW(NULL, url + ARRAY_SIZE(res), ARRAY_SIZE(url) - ARRAY_SIZE(res) - 1);
url[len++] = '/';
lstrcpynW(url + len, file, ARRAY_SIZE(url) - len);
bstr = SysAllocString(url);
hres = CreateURLMoniker(NULL, bstr, &mon);
SysFreeString(bstr);
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, bind, 0x12);
ok(hres == S_OK, "Load failed: %08lx\n", hres);
IPersistMoniker_Release(persist);
IBindCtx_Release(bind);
IMoniker_Release(mon);
hres = IHTMLDocument2_QueryInterface(doc, &IID_IHlinkTarget, (void**)&hlink);
ok(hres == S_OK, "Could not get IHlinkTarget iface: %08lx\n", hres);
hres = IHlinkTarget_Navigate(hlink, 0, NULL);
ok(hres == S_OK, "Navigate failed: %08lx\n", hres);
IHlinkTarget_Release(hlink);
}
static void do_advise(IUnknown *unk, REFIID riid, IUnknown *unk_advise)
{
IConnectionPointContainer *container;
@ -4776,6 +4890,51 @@ static void set_client_site(IHTMLDocument2 *doc, BOOL set)
IOleObject_Release(oleobj);
}
static void navigate(IHTMLDocument2 *doc, const WCHAR *url)
{
IHTMLLocation *location;
IHTMLDocument6 *doc6;
HRESULT hres;
VARIANT res;
BSTR bstr;
MSG msg;
location = NULL;
hres = IHTMLDocument2_get_location(doc, &location);
ok(hres == S_OK, "get_location failed: %08lx\n", hres);
ok(location != NULL, "location == NULL\n");
doc_complete = FALSE;
bstr = SysAllocString(url);
hres = IHTMLLocation_replace(location, bstr);
ok(hres == S_OK, "replace failed: %08lx\n", hres);
IHTMLLocation_Release(location);
SysFreeString(bstr);
while(!doc_complete && GetMessageW(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
hres = IHTMLDocument2_QueryInterface(doc, &IID_IHTMLDocument6, (void**)&doc6);
if(SUCCEEDED(hres)) {
hres = IHTMLDocument6_get_documentMode(doc6, &res);
ok(hres == S_OK, "get_documentMode failed: %08lx\n", hres);
ok(V_VT(&res) == VT_R4, "V_VT(documentMode) = %u\n", V_VT(&res));
document_mode = V_R4(&res);
IHTMLDocument6_Release(doc6);
}else {
document_mode = 0;
}
if(window)
IHTMLWindow2_Release(window);
hres = IHTMLDocument2_get_parentWindow(doc, &window);
ok(hres == S_OK, "get_parentWindow failed: %08lx\n", hres);
ok(window != NULL, "window == NULL\n");
}
static IHTMLDocument2 *create_document(void)
{
IHTMLDocument2 *doc;
@ -4844,8 +5003,9 @@ static IHTMLDocument2 *create_document_with_origin(const char *str)
typedef void (*testfunc_t)(IHTMLDocument2*);
static void run_test(const char *str, testfunc_t test)
static void run_test_impl(const char *str, const WCHAR *res, testfunc_t test)
{
IInternetSession *internet_session;
IHTMLDocument2 *doc;
IHTMLElement *body = NULL;
MSG msg;
@ -4855,7 +5015,23 @@ static void run_test(const char *str, testfunc_t test)
if (!doc)
return;
set_client_site(doc, TRUE);
doc_load_string(doc, str);
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;
}
if(res)
doc_load_res(doc, res);
else
doc_load_string(doc, str);
do_advise((IUnknown*)doc, &IID_IPropertyNotifySink, (IUnknown*)&PropertyNotifySink);
while(!doc_complete && GetMessageA(&msg, NULL, 0, 0)) {
@ -4899,6 +5075,16 @@ static void run_test(const char *str, testfunc_t test)
IHTMLDocument2_Release(doc);
}
static void run_test(const char *str, testfunc_t test)
{
return run_test_impl(str, NULL, test);
}
static void run_test_from_res(const WCHAR *res, testfunc_t test)
{
return run_test_impl(NULL, res, test);
}
static LRESULT WINAPI wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProcA(hwnd, msg, wParam, lParam);
@ -5216,8 +5402,11 @@ START_TEST(events)
run_test(empty_doc_str, test_submit);
run_test(empty_doc_ie9_str, test_submit);
run_test(iframe_doc_str, test_iframe_connections);
if(is_ie9plus)
if(is_ie9plus) {
run_test_from_res(L"doc_with_prop.html", test_doc_obj);
run_test_from_res(L"doc_with_prop_ie9.html", test_doc_obj);
run_test(empty_doc_ie9_str, test_create_event);
}
test_empty_document();
test_storage_events(empty_doc_str);

View file

@ -73,6 +73,12 @@ blank2.html HTML "blank.html"
/* @makedep: blank.html */
123 HTML "blank.html"
/* @makedep: doc_with_prop.html */
doc_with_prop.html HTML "doc_with_prop.html"
/* @makedep: doc_with_prop_ie9.html */
doc_with_prop_ie9.html HTML "doc_with_prop_ie9.html"
/* For res: protocol test: */
/* @makedep: jstest.html */