xref: /reactos/dll/win32/mshtml/nsevents.c (revision 5100859e)
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 }
449