[MSHTA] Implement MSHTA.exe (#577) CORE-12813
[reactos.git] / dll / win32 / mshtml / nsevents.c
1 /*
2 * Copyright 2007-2008 Jacek Caban for CodeWeavers
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include "mshtml_private.h"
20
21 static const PRUnichar blurW[] = {'b','l','u','r',0};
22 static const PRUnichar focusW[] = {'f','o','c','u','s',0};
23 static const PRUnichar keypressW[] = {'k','e','y','p','r','e','s','s',0};
24 static const PRUnichar loadW[] = {'l','o','a','d',0};
25
26 typedef struct {
27 nsIDOMEventListener nsIDOMEventListener_iface;
28 nsDocumentEventListener *This;
29 } nsEventListener;
30
31 struct nsDocumentEventListener {
32 nsEventListener blur_listener;
33 nsEventListener focus_listener;
34 nsEventListener keypress_listener;
35 nsEventListener load_listener;
36 nsEventListener htmlevent_listener;
37
38 LONG ref;
39
40 HTMLDocumentNode *doc;
41 };
42
43 static LONG release_listener(nsDocumentEventListener *This)
44 {
45 LONG ref = InterlockedDecrement(&This->ref);
46
47 TRACE("(%p) ref=%d\n", This, ref);
48
49 if(!ref)
50 heap_free(This);
51
52 return ref;
53 }
54
55 static inline nsEventListener *impl_from_nsIDOMEventListener(nsIDOMEventListener *iface)
56 {
57 return CONTAINING_RECORD(iface, nsEventListener, nsIDOMEventListener_iface);
58 }
59
60 static nsresult NSAPI nsDOMEventListener_QueryInterface(nsIDOMEventListener *iface,
61 nsIIDRef riid, void **result)
62 {
63 nsEventListener *This = impl_from_nsIDOMEventListener(iface);
64
65 *result = NULL;
66
67 if(IsEqualGUID(&IID_nsISupports, riid)) {
68 TRACE("(%p)->(IID_nsISupports, %p)\n", This, result);
69 *result = &This->nsIDOMEventListener_iface;
70 }else if(IsEqualGUID(&IID_nsIDOMEventListener, riid)) {
71 TRACE("(%p)->(IID_nsIDOMEventListener %p)\n", This, result);
72 *result = &This->nsIDOMEventListener_iface;
73 }
74
75 if(*result) {
76 nsIDOMEventListener_AddRef(&This->nsIDOMEventListener_iface);
77 return NS_OK;
78 }
79
80 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), result);
81 return NS_NOINTERFACE;
82 }
83
84 static nsrefcnt NSAPI nsDOMEventListener_AddRef(nsIDOMEventListener *iface)
85 {
86 nsEventListener *This = impl_from_nsIDOMEventListener(iface);
87 LONG ref = InterlockedIncrement(&This->This->ref);
88
89 TRACE("(%p) ref=%d\n", This->This, ref);
90
91 return ref;
92 }
93
94 static nsrefcnt NSAPI nsDOMEventListener_Release(nsIDOMEventListener *iface)
95 {
96 nsEventListener *This = impl_from_nsIDOMEventListener(iface);
97
98 return release_listener(This->This);
99 }
100
101 static BOOL is_doc_child_focus(NSContainer *nscontainer)
102 {
103 HWND hwnd;
104
105 for(hwnd = GetFocus(); hwnd && hwnd != nscontainer->hwnd; hwnd = GetParent(hwnd));
106
107 return hwnd != NULL;
108 }
109
110 static nsresult NSAPI handle_blur(nsIDOMEventListener *iface, nsIDOMEvent *event)
111 {
112 nsEventListener *This = impl_from_nsIDOMEventListener(iface);
113 HTMLDocumentNode *doc = This->This->doc;
114 HTMLDocumentObj *doc_obj;
115
116 TRACE("(%p)\n", doc);
117
118 if(!doc || !doc->basedoc.doc_obj)
119 return NS_ERROR_FAILURE;
120 doc_obj = doc->basedoc.doc_obj;
121
122 if(doc_obj->focus && !is_doc_child_focus(doc_obj->nscontainer)) {
123 doc_obj->focus = FALSE;
124 notif_focus(doc_obj);
125 }
126
127 return NS_OK;
128 }
129
130 static nsresult NSAPI handle_focus(nsIDOMEventListener *iface, nsIDOMEvent *event)
131 {
132 nsEventListener *This = impl_from_nsIDOMEventListener(iface);
133 HTMLDocumentNode *doc = This->This->doc;
134 HTMLDocumentObj *doc_obj;
135
136 TRACE("(%p)\n", doc);
137
138 if(!doc)
139 return NS_ERROR_FAILURE;
140 doc_obj = doc->basedoc.doc_obj;
141
142 if(!doc_obj->focus) {
143 doc_obj->focus = TRUE;
144 notif_focus(doc_obj);
145 }
146
147 return NS_OK;
148 }
149
150 static nsresult NSAPI handle_keypress(nsIDOMEventListener *iface,
151 nsIDOMEvent *event)
152 {
153 nsEventListener *This = impl_from_nsIDOMEventListener(iface);
154 HTMLDocumentNode *doc = This->This->doc;
155 HTMLDocumentObj *doc_obj;
156
157 if(!doc)
158 return NS_ERROR_FAILURE;
159 doc_obj = doc->basedoc.doc_obj;
160
161 TRACE("(%p)->(%p)\n", doc, event);
162
163 update_doc(&doc_obj->basedoc, UPDATE_UI);
164 if(doc_obj->usermode == EDITMODE)
165 handle_edit_event(&doc_obj->basedoc, event);
166
167 return NS_OK;
168 }
169
170 static void handle_docobj_load(HTMLDocumentObj *doc)
171 {
172 IOleCommandTarget *olecmd = NULL;
173 HRESULT hres;
174
175 if(doc->nscontainer->editor_controller) {
176 nsIController_Release(doc->nscontainer->editor_controller);
177 doc->nscontainer->editor_controller = NULL;
178 }
179
180 if(doc->usermode == EDITMODE)
181 handle_edit_load(&doc->basedoc);
182
183 if(doc->client) {
184 hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
185 if(FAILED(hres))
186 olecmd = NULL;
187 }
188
189 if(doc->download_state) {
190 if(olecmd) {
191 VARIANT progress;
192
193 V_VT(&progress) = VT_I4;
194 V_I4(&progress) = 0;
195 IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETPROGRESSPOS,
196 OLECMDEXECOPT_DONTPROMPTUSER, &progress, NULL);
197 }
198
199 set_download_state(doc, 0);
200 }
201
202 if(olecmd) {
203 IOleCommandTarget_Exec(olecmd, &CGID_ShellDocView, 103, 0, NULL, NULL);
204 IOleCommandTarget_Exec(olecmd, &CGID_MSHTML, IDM_PARSECOMPLETE, 0, NULL, NULL);
205 IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_HTTPEQUIV_DONE, 0, NULL, NULL);
206
207 IOleCommandTarget_Release(olecmd);
208 }
209 }
210
211 static nsresult NSAPI handle_load(nsIDOMEventListener *iface, nsIDOMEvent *event)
212 {
213 nsEventListener *This = impl_from_nsIDOMEventListener(iface);
214 HTMLDocumentNode *doc = This->This->doc;
215 HTMLDocumentObj *doc_obj = NULL;
216 nsresult nsres = NS_OK;
217
218 TRACE("(%p)\n", doc);
219
220 if(!doc || !doc->basedoc.window)
221 return NS_ERROR_FAILURE;
222 if(doc->basedoc.doc_obj && doc->basedoc.doc_obj->basedoc.doc_node == doc)
223 doc_obj = doc->basedoc.doc_obj;
224
225 connect_scripts(doc->window);
226
227 htmldoc_addref(&doc->basedoc);
228
229 if(doc_obj)
230 handle_docobj_load(doc_obj);
231
232 set_ready_state(doc->basedoc.window, READYSTATE_COMPLETE);
233
234 if(doc_obj) {
235 if(doc_obj->view_sink)
236 IAdviseSink_OnViewChange(doc_obj->view_sink, DVASPECT_CONTENT, -1);
237
238 set_statustext(doc_obj, IDS_STATUS_DONE, NULL);
239
240 update_title(doc_obj);
241 }
242
243 if(doc_obj && doc_obj->usermode!=EDITMODE && doc_obj->doc_object_service
244 && !(doc->basedoc.window->load_flags & BINDING_REFRESH))
245 IDocObjectService_FireDocumentComplete(doc_obj->doc_object_service,
246 &doc->basedoc.window->base.IHTMLWindow2_iface, 0);
247
248 if(doc->nsdoc) {
249 nsIDOMHTMLElement *nsbody;
250
251 flush_pending_tasks(doc->basedoc.task_magic);
252
253 nsres = nsIDOMHTMLDocument_GetBody(doc->nsdoc, &nsbody);
254 if(NS_SUCCEEDED(nsres) && nsbody) {
255 fire_event(doc, EVENTID_LOAD, TRUE, (nsIDOMNode*)nsbody, event, (IDispatch*)&doc->window->base.IDispatchEx_iface);
256 nsIDOMHTMLElement_Release(nsbody);
257 }
258 }else {
259 ERR("NULL nsdoc\n");
260 nsres = NS_ERROR_FAILURE;
261 }
262
263 htmldoc_release(&doc->basedoc);
264 return nsres;
265 }
266
267 static nsresult NSAPI handle_htmlevent(nsIDOMEventListener *iface, nsIDOMEvent *event)
268 {
269 nsEventListener *This = impl_from_nsIDOMEventListener(iface);
270 HTMLDocumentNode *doc = This->This->doc;
271 const PRUnichar *type;
272 nsIDOMEventTarget *event_target;
273 nsIDOMNode *nsnode;
274 nsAString type_str;
275 eventid_t eid;
276 nsresult nsres;
277
278 TRACE("%p\n", This->This);
279
280 if(!doc) {
281 WARN("NULL doc\n");
282 return NS_OK;
283 }
284
285 nsAString_Init(&type_str, NULL);
286 nsIDOMEvent_GetType(event, &type_str);
287 nsAString_GetData(&type_str, &type);
288 eid = str_to_eid(type);
289 nsAString_Finish(&type_str);
290
291 nsres = nsIDOMEvent_GetTarget(event, &event_target);
292 if(NS_FAILED(nsres) || !event_target) {
293 ERR("GetEventTarget failed: %08x\n", nsres);
294 return NS_OK;
295 }
296
297 nsres = nsIDOMEventTarget_QueryInterface(event_target, &IID_nsIDOMNode, (void**)&nsnode);
298 nsIDOMEventTarget_Release(event_target);
299 if(NS_FAILED(nsres)) {
300 ERR("Could not get nsIDOMNode: %08x\n", nsres);
301 return NS_OK;
302 }
303
304 fire_event(doc, eid, TRUE, nsnode, event, NULL);
305
306 nsIDOMNode_Release(nsnode);
307
308 return NS_OK;
309 }
310
311 #define EVENTLISTENER_VTBL(handler) \
312 { \
313 nsDOMEventListener_QueryInterface, \
314 nsDOMEventListener_AddRef, \
315 nsDOMEventListener_Release, \
316 handler, \
317 }
318
319 static const nsIDOMEventListenerVtbl blur_vtbl = EVENTLISTENER_VTBL(handle_blur);
320 static const nsIDOMEventListenerVtbl focus_vtbl = EVENTLISTENER_VTBL(handle_focus);
321 static const nsIDOMEventListenerVtbl keypress_vtbl = EVENTLISTENER_VTBL(handle_keypress);
322 static const nsIDOMEventListenerVtbl load_vtbl = EVENTLISTENER_VTBL(handle_load);
323 static const nsIDOMEventListenerVtbl htmlevent_vtbl = EVENTLISTENER_VTBL(handle_htmlevent);
324
325 static void init_event(nsIDOMEventTarget *target, const PRUnichar *type,
326 nsIDOMEventListener *listener, BOOL capture)
327 {
328 nsAString type_str;
329 nsresult nsres;
330
331 nsAString_InitDepend(&type_str, type);
332 nsres = nsIDOMEventTarget_AddEventListener(target, &type_str, listener, capture, FALSE, 1);
333 nsAString_Finish(&type_str);
334 if(NS_FAILED(nsres))
335 ERR("AddEventTarget failed: %08x\n", nsres);
336
337 }
338
339 static void init_listener(nsEventListener *This, nsDocumentEventListener *listener,
340 const nsIDOMEventListenerVtbl *vtbl)
341 {
342 This->nsIDOMEventListener_iface.lpVtbl = vtbl;
343 This->This = listener;
344 }
345
346 void add_nsevent_listener(HTMLDocumentNode *doc, nsIDOMNode *nsnode, LPCWSTR type)
347 {
348 nsIDOMEventTarget *target;
349 nsresult nsres;
350
351 if(nsnode)
352 nsres = nsIDOMNode_QueryInterface(nsnode, &IID_nsIDOMEventTarget, (void**)&target);
353 else
354 nsres = nsIDOMWindow_QueryInterface(doc->basedoc.window->nswindow, &IID_nsIDOMEventTarget, (void**)&target);
355 if(NS_FAILED(nsres)) {
356 ERR("Could not get nsIDOMEventTarget interface: %08x\n", nsres);
357 return;
358 }
359
360 init_event(target, type, &doc->nsevent_listener->htmlevent_listener.nsIDOMEventListener_iface,
361 TRUE);
362 nsIDOMEventTarget_Release(target);
363 }
364
365 static void detach_nslistener(HTMLDocumentNode *doc, const WCHAR *type, nsEventListener *listener, cpp_bool is_capture)
366 {
367 nsIDOMEventTarget *target;
368 nsAString type_str;
369 nsresult nsres;
370
371 if(!doc->basedoc.window)
372 return;
373
374 nsres = nsIDOMWindow_QueryInterface(doc->basedoc.window->nswindow, &IID_nsIDOMEventTarget, (void**)&target);
375 if(NS_FAILED(nsres)) {
376 ERR("Could not get nsIDOMEventTarget interface: %08x\n", nsres);
377 return;
378 }
379
380 nsAString_InitDepend(&type_str, type);
381 nsres = nsIDOMEventTarget_RemoveEventListener(target, &type_str,
382 &listener->nsIDOMEventListener_iface, is_capture);
383 nsAString_Finish(&type_str);
384 nsIDOMEventTarget_Release(target);
385 if(NS_FAILED(nsres))
386 ERR("RemoveEventTarget failed: %08x\n", nsres);
387 }
388
389 void detach_nsevent(HTMLDocumentNode *doc, const WCHAR *type)
390 {
391 detach_nslistener(doc, type, &doc->nsevent_listener->htmlevent_listener, TRUE);
392 }
393
394 void release_nsevents(HTMLDocumentNode *doc)
395 {
396 nsDocumentEventListener *listener = doc->nsevent_listener;
397
398 TRACE("%p %p\n", doc, doc->nsevent_listener);
399
400 if(!listener)
401 return;
402
403 detach_nslistener(doc, blurW, &listener->blur_listener, TRUE);
404 detach_nslistener(doc, focusW, &listener->focus_listener, TRUE);
405 detach_nslistener(doc, keypressW, &listener->keypress_listener, FALSE);
406 detach_nslistener(doc, loadW, &listener->load_listener, TRUE);
407
408 listener->doc = NULL;
409 release_listener(listener);
410 doc->nsevent_listener = NULL;
411 }
412
413 void init_nsevents(HTMLDocumentNode *doc)
414 {
415 nsDocumentEventListener *listener;
416 nsIDOMEventTarget *target;
417 nsresult nsres;
418
419 listener = heap_alloc(sizeof(nsDocumentEventListener));
420 if(!listener)
421 return;
422
423 TRACE("%p %p\n", doc, listener);
424
425 listener->ref = 1;
426 listener->doc = doc;
427
428 init_listener(&listener->blur_listener, listener, &blur_vtbl);
429 init_listener(&listener->focus_listener, listener, &focus_vtbl);
430 init_listener(&listener->keypress_listener, listener, &keypress_vtbl);
431 init_listener(&listener->load_listener, listener, &load_vtbl);
432 init_listener(&listener->htmlevent_listener, listener, &htmlevent_vtbl);
433
434 doc->nsevent_listener = listener;
435
436 nsres = nsIDOMWindow_QueryInterface(doc->basedoc.window->nswindow, &IID_nsIDOMEventTarget, (void**)&target);
437 if(NS_FAILED(nsres)) {
438 ERR("Could not get nsIDOMEventTarget interface: %08x\n", nsres);
439 return;
440 }
441
442 init_event(target, blurW, &listener->blur_listener.nsIDOMEventListener_iface, TRUE);
443 init_event(target, focusW, &listener->focus_listener.nsIDOMEventListener_iface, TRUE);
444 init_event(target, keypressW, &listener->keypress_listener.nsIDOMEventListener_iface, FALSE);
445 init_event(target, loadW, &listener->load_listener.nsIDOMEventListener_iface, TRUE);
446
447 nsIDOMEventTarget_Release(target);
448 }