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