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