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