sync with trunk r46493
[reactos.git] / dll / win32 / mshtml / nsevents.c
index d0f5079..c491424 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Jacek Caban for CodeWeavers
+ * Copyright 2007-2008 Jacek Caban for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #include "winbase.h"
 #include "winuser.h"
 #include "ole2.h"
+#include "mshtmcid.h"
+#include "shlguid.h"
 
 #include "wine/debug.h"
 #include "wine/unicode.h"
 
 #include "mshtml_private.h"
+#include "htmlevent.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
 
+typedef struct {
+    const nsIDOMEventListenerVtbl      *lpDOMEventListenerVtbl;
+    nsDocumentEventListener *This;
+} nsEventListener;
+
+struct nsDocumentEventListener {
+    nsEventListener blur_listener;
+    nsEventListener focus_listener;
+    nsEventListener keypress_listener;
+    nsEventListener load_listener;
+    nsEventListener htmlevent_listener;
+
+    LONG ref;
+
+    HTMLDocumentNode *doc;
+};
+
+static LONG release_listener(nsDocumentEventListener *This)
+{
+    LONG ref = InterlockedDecrement(&This->ref);
+
+    TRACE("(%p) ref=%d\n", This, ref);
+
+    if(!ref)
+        heap_free(This);
+
+    return ref;
+}
+
 #define NSEVENTLIST_THIS(iface) DEFINE_THIS(nsEventListener, DOMEventListener, iface)
 
 static nsresult NSAPI nsDOMEventListener_QueryInterface(nsIDOMEventListener *iface,
@@ -62,37 +94,44 @@ static nsresult NSAPI nsDOMEventListener_QueryInterface(nsIDOMEventListener *ifa
 
 static nsrefcnt NSAPI nsDOMEventListener_AddRef(nsIDOMEventListener *iface)
 {
-    NSContainer *This = NSEVENTLIST_THIS(iface)->This;
-    return nsIWebBrowserChrome_AddRef(NSWBCHROME(This));
+    nsDocumentEventListener *This = NSEVENTLIST_THIS(iface)->This;
+    LONG ref = InterlockedIncrement(&This->ref);
+
+    TRACE("(%p) ref=%d\n", This, ref);
+
+    return ref;
 }
 
 static nsrefcnt NSAPI nsDOMEventListener_Release(nsIDOMEventListener *iface)
 {
-    NSContainer *This = NSEVENTLIST_THIS(iface)->This;
-    return nsIWebBrowserChrome_Release(NSWBCHROME(This));
+    nsDocumentEventListener *This = NSEVENTLIST_THIS(iface)->This;
+
+    return release_listener(This);
 }
 
-static BOOL is_doc_child_focus(NSContainer *This)
+static BOOL is_doc_child_focus(HTMLDocumentObj *doc)
 {
     HWND hwnd;
 
-    if(!This->doc)
-        return FALSE;
-
-    for(hwnd = GetFocus(); hwnd && hwnd != This->doc->hwnd; hwnd = GetParent(hwnd));
+    for(hwnd = GetFocus(); hwnd && hwnd != doc->hwnd; hwnd = GetParent(hwnd));
 
-    return hwnd == This->doc->hwnd;
+    return hwnd != NULL;
 }
 
 static nsresult NSAPI handle_blur(nsIDOMEventListener *iface, nsIDOMEvent *event)
 {
-    NSContainer *This = NSEVENTLIST_THIS(iface)->This;
+    HTMLDocumentNode *doc = NSEVENTLIST_THIS(iface)->This->doc;
+    HTMLDocumentObj *doc_obj;
 
-    TRACE("(%p)\n", This);
+    TRACE("(%p)\n", doc);
 
-    if(!This->reset_focus && This->doc && This->doc->focus && !is_doc_child_focus(This)) {
-        This->doc->focus = FALSE;
-        notif_focus(This->doc);
+    if(!doc || !doc->basedoc.doc_obj)
+        return NS_ERROR_FAILURE;
+    doc_obj = doc->basedoc.doc_obj;
+
+    if(!doc_obj->nscontainer->reset_focus && doc_obj->focus && !is_doc_child_focus(doc_obj)) {
+        doc_obj->focus = FALSE;
+        notif_focus(doc_obj);
     }
 
     return NS_OK;
@@ -100,13 +139,18 @@ static nsresult NSAPI handle_blur(nsIDOMEventListener *iface, nsIDOMEvent *event
 
 static nsresult NSAPI handle_focus(nsIDOMEventListener *iface, nsIDOMEvent *event)
 {
-    NSContainer *This = NSEVENTLIST_THIS(iface)->This;
+    HTMLDocumentNode *doc = NSEVENTLIST_THIS(iface)->This->doc;
+    HTMLDocumentObj *doc_obj;
+
+    TRACE("(%p)\n", doc);
 
-    TRACE("(%p)\n", This);
+    if(!doc)
+        return NS_ERROR_FAILURE;
+    doc_obj = doc->basedoc.doc_obj;
 
-    if(!This->reset_focus && This->doc && !This->doc->focus) {
-        This->doc->focus = TRUE;
-        notif_focus(This->doc);
+    if(!doc_obj->nscontainer->reset_focus && !doc_obj->focus) {
+        doc_obj->focus = TRUE;
+        notif_focus(doc_obj);
     }
 
     return NS_OK;
@@ -115,105 +159,111 @@ static nsresult NSAPI handle_focus(nsIDOMEventListener *iface, nsIDOMEvent *even
 static nsresult NSAPI handle_keypress(nsIDOMEventListener *iface,
         nsIDOMEvent *event)
 {
-    NSContainer *This = NSEVENTLIST_THIS(iface)->This;
+    HTMLDocumentNode *doc = NSEVENTLIST_THIS(iface)->This->doc;
+    HTMLDocumentObj *doc_obj;
 
-    TRACE("(%p)->(%p)\n", This, event);
+    if(!doc)
+        return NS_ERROR_FAILURE;
+    doc_obj = doc->basedoc.doc_obj;
 
-    update_doc(This->doc, UPDATE_UI);
-    if(This->doc->usermode == EDITMODE)
-        handle_edit_event(This->doc, event);
+    TRACE("(%p)->(%p)\n", doc, event);
+
+    update_doc(&doc_obj->basedoc, UPDATE_UI);
+    if(doc_obj->usermode == EDITMODE)
+        handle_edit_event(&doc_obj->basedoc, event);
 
     return NS_OK;
 }
 
-static nsresult NSAPI handle_load(nsIDOMEventListener *iface, nsIDOMEvent *event)
+static void handle_docobj_load(HTMLDocumentObj *doc)
 {
-    NSContainer *This = NSEVENTLIST_THIS(iface)->This;
-    nsIDOMHTMLDocument *nshtmldoc;
-    nsIDOMHTMLElement *nsbody = NULL;
-    nsIDOMDocument *nsdoc;
-    task_t *task;
-
-    TRACE("(%p)\n", This);
+    IOleCommandTarget *olecmd = NULL;
+    HRESULT hres;
 
-    if(!This->doc)
-        return NS_OK;
+    if(!doc->client)
+        return;
 
-    connect_scripts(This->doc);
-    setup_nswindow(This->doc->window);
+    hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
+    if(SUCCEEDED(hres)) {
+        if(doc->download_state) {
+            VARIANT state, progress;
 
-    if(This->editor_controller) {
-        nsIController_Release(This->editor_controller);
-        This->editor_controller = NULL;
-    }
+            V_VT(&progress) = VT_I4;
+            V_I4(&progress) = 0;
+            IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETPROGRESSPOS,
+                    OLECMDEXECOPT_DONTPROMPTUSER, &progress, NULL);
 
-    if(This->doc->usermode == EDITMODE)
-        handle_edit_load(This->doc);
+            V_VT(&state) = VT_I4;
+            V_I4(&state) = 0;
+            IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_SETDOWNLOADSTATE,
+                    OLECMDEXECOPT_DONTPROMPTUSER, &state, NULL);
+        }
 
-    task = heap_alloc(sizeof(task_t));
+        IOleCommandTarget_Exec(olecmd, &CGID_ShellDocView, 103, 0, NULL, NULL);
+        IOleCommandTarget_Exec(olecmd, &CGID_MSHTML, IDM_PARSECOMPLETE, 0, NULL, NULL);
+        IOleCommandTarget_Exec(olecmd, NULL, OLECMDID_HTTPEQUIV_DONE, 0, NULL, NULL);
 
-    task->doc = This->doc;
-    task->task_id = TASK_PARSECOMPLETE;
-    task->next = NULL;
+        IOleCommandTarget_Release(olecmd);
+    }
+    doc->download_state = 0;
+}
 
-    /*
-     * This should be done in the worker thread that parses HTML,
-     * but we don't have such thread (Gecko parses HTML for us).
-     */
-    push_task(task);
+static nsresult NSAPI handle_load(nsIDOMEventListener *iface, nsIDOMEvent *event)
+{
+    HTMLDocumentNode *doc = NSEVENTLIST_THIS(iface)->This->doc;
+    HTMLDocumentObj *doc_obj;
+    nsIDOMHTMLElement *nsbody = NULL;
 
+    TRACE("(%p)\n", doc);
 
-    nsIWebNavigation_GetDocument(This->navigation, &nsdoc);
-    nsIDOMDocument_QueryInterface(nsdoc, &IID_nsIDOMHTMLDocument, (void**)&nshtmldoc);
-    nsIDOMDocument_Release(nsdoc);
+    if(!doc || !doc->basedoc.window)
+        return NS_ERROR_FAILURE;
+    doc_obj = doc->basedoc.doc_obj;
 
-    nsIDOMHTMLDocument_GetBody(nshtmldoc, &nsbody);
-    nsIDOMHTMLDocument_Release(nshtmldoc);
+    connect_scripts(doc->basedoc.window);
 
-    if(nsbody) {
-        fire_event(This->doc, EVENTID_LOAD, (nsIDOMNode*)nsbody);
-        nsIDOMHTMLElement_Release(nsbody);
+    if(doc_obj->nscontainer->editor_controller) {
+        nsIController_Release(doc_obj->nscontainer->editor_controller);
+        doc_obj->nscontainer->editor_controller = NULL;
     }
 
-    return NS_OK;
-}
+    if(doc_obj->usermode == EDITMODE)
+        handle_edit_load(&doc_obj->basedoc);
 
-static nsresult NSAPI handle_node_insert(nsIDOMEventListener *iface, nsIDOMEvent *event)
-{
-    NSContainer *This = NSEVENTLIST_THIS(iface)->This;
-    nsIDOMHTMLScriptElement *script;
-    nsIDOMEventTarget *target;
-    nsIDOMElement *elem;
-    nsresult nsres;
+    if(doc == doc_obj->basedoc.doc_node)
+        handle_docobj_load(doc_obj);
 
-    TRACE("(%p %p)\n", This, event);
+    set_ready_state(doc->basedoc.window, READYSTATE_COMPLETE);
 
-    nsres = nsIDOMEvent_GetTarget(event, &target);
-    if(NS_FAILED(nsres)) {
-        ERR("GetTarget failed: %08x\n", nsres);
-        return NS_OK;
-    }
+    if(doc == doc_obj->basedoc.doc_node) {
+        if(doc_obj->view_sink)
+            IAdviseSink_OnViewChange(doc_obj->view_sink, DVASPECT_CONTENT, -1);
 
-    nsres = nsIDOMEventTarget_QueryInterface(target, &IID_nsIDOMElement, (void**)&elem);
-    nsIDOMEventTarget_Release(target);
-    if(NS_FAILED(nsres))
-        return NS_OK;
+        if(doc_obj->frame) {
+            static const WCHAR wszDone[] = {'D','o','n','e',0};
+            IOleInPlaceFrame_SetStatusText(doc_obj->frame, wszDone);
+        }
+
+        update_title(doc_obj);
+    }
 
-    nsres = nsIDOMElement_QueryInterface(elem, &IID_nsIDOMHTMLScriptElement, (void**)&script);
-    if(SUCCEEDED(nsres)) {
-        doc_insert_script(This->doc, script);
-        nsIDOMHTMLScriptElement_Release(script);
+    if(!doc->nsdoc) {
+        ERR("NULL nsdoc\n");
+        return NS_ERROR_FAILURE;
     }
 
-    check_event_attr(This->doc, elem);
+    nsIDOMHTMLDocument_GetBody(doc->nsdoc, &nsbody);
+    if(nsbody) {
+        fire_event(doc, EVENTID_LOAD, TRUE, (nsIDOMNode*)nsbody, event);
+        nsIDOMHTMLElement_Release(nsbody);
+    }
 
-    nsIDOMNode_Release(elem);
     return NS_OK;
 }
 
 static nsresult NSAPI handle_htmlevent(nsIDOMEventListener *iface, nsIDOMEvent *event)
 {
-    NSContainer *This = NSEVENTLIST_THIS(iface)->This;
+    HTMLDocumentNode *doc = NSEVENTLIST_THIS(iface)->This->doc;
     const PRUnichar *type;
     nsIDOMEventTarget *event_target;
     nsIDOMNode *nsnode;
@@ -221,6 +271,8 @@ static nsresult NSAPI handle_htmlevent(nsIDOMEventListener *iface, nsIDOMEvent *
     eventid_t eid;
     nsresult nsres;
 
+    TRACE("\n");
+
     nsAString_Init(&type_str, NULL);
     nsIDOMEvent_GetType(event, &type_str);
     nsAString_GetData(&type_str, &type);
@@ -240,7 +292,7 @@ static nsresult NSAPI handle_htmlevent(nsIDOMEventListener *iface, nsIDOMEvent *
         return NS_OK;
     }
 
-    fire_event(This->doc, eid, nsnode);
+    fire_event(doc, eid, TRUE, nsnode, event);
 
     nsIDOMNode_Release(nsnode);
 
@@ -261,7 +313,6 @@ static const nsIDOMEventListenerVtbl blur_vtbl =      EVENTLISTENER_VTBL(handle_
 static const nsIDOMEventListenerVtbl focus_vtbl =     EVENTLISTENER_VTBL(handle_focus);
 static const nsIDOMEventListenerVtbl keypress_vtbl =  EVENTLISTENER_VTBL(handle_keypress);
 static const nsIDOMEventListenerVtbl load_vtbl =      EVENTLISTENER_VTBL(handle_load);
-static const nsIDOMEventListenerVtbl node_insert_vtbl = EVENTLISTENER_VTBL(handle_node_insert);
 static const nsIDOMEventListenerVtbl htmlevent_vtbl = EVENTLISTENER_VTBL(handle_htmlevent);
 
 static void init_event(nsIDOMEventTarget *target, const PRUnichar *type,
@@ -270,7 +321,7 @@ static void init_event(nsIDOMEventTarget *target, const PRUnichar *type,
     nsAString type_str;
     nsresult nsres;
 
-    nsAString_Init(&type_str, type);
+    nsAString_InitDepend(&type_str, type);
     nsres = nsIDOMEventTarget_AddEventListener(target, &type_str, listener, capture);
     nsAString_Finish(&type_str);
     if(NS_FAILED(nsres))
@@ -278,39 +329,43 @@ static void init_event(nsIDOMEventTarget *target, const PRUnichar *type,
 
 }
 
-static void init_listener(nsEventListener *This, NSContainer *container,
+static void init_listener(nsEventListener *This, nsDocumentEventListener *listener,
         const nsIDOMEventListenerVtbl *vtbl)
 {
     This->lpDOMEventListenerVtbl = vtbl;
-    This->This = container;
+    This->This = listener;
 }
 
-void add_nsevent_listener(NSContainer *container, LPCWSTR type)
+void add_nsevent_listener(HTMLDocumentNode *doc, nsIDOMNode *nsnode, LPCWSTR type)
 {
-    nsIDOMWindow *dom_window;
     nsIDOMEventTarget *target;
     nsresult nsres;
 
-    nsres = nsIWebBrowser_GetContentDOMWindow(container->webbrowser, &dom_window);
-    if(NS_FAILED(nsres)) {
-        ERR("GetContentDOMWindow failed: %08x\n", nsres);
-        return;
-    }
-
-    nsres = nsIDOMWindow_QueryInterface(dom_window, &IID_nsIDOMEventTarget, (void**)&target);
-    nsIDOMWindow_Release(dom_window);
+    if(nsnode)
+        nsres = nsIDOMNode_QueryInterface(nsnode, &IID_nsIDOMEventTarget, (void**)&target);
+    else
+        nsres = nsIDOMWindow_QueryInterface(doc->basedoc.window->nswindow, &IID_nsIDOMEventTarget, (void**)&target);
     if(NS_FAILED(nsres)) {
         ERR("Could not get nsIDOMEventTarget interface: %08x\n", nsres);
         return;
     }
 
-    init_event(target, type, NSEVENTLIST(&container->htmlevent_listener), TRUE);
+    init_event(target, type, NSEVENTLIST(&doc->nsevent_listener->htmlevent_listener), TRUE);
     nsIDOMEventTarget_Release(target);
 }
 
-void init_nsevents(NSContainer *This)
+void release_nsevents(HTMLDocumentNode *doc)
 {
-    nsIDOMWindow *dom_window;
+    if(doc->nsevent_listener) {
+        doc->nsevent_listener->doc = NULL;
+        release_listener(doc->nsevent_listener);
+        doc->nsevent_listener = NULL;
+    }
+}
+
+void init_nsevents(HTMLDocumentNode *doc)
+{
+    nsDocumentEventListener *listener;
     nsIDOMEventTarget *target;
     nsresult nsres;
 
@@ -318,34 +373,32 @@ void init_nsevents(NSContainer *This)
     static const PRUnichar wsz_focus[]     = {'f','o','c','u','s',0};
     static const PRUnichar wsz_keypress[]  = {'k','e','y','p','r','e','s','s',0};
     static const PRUnichar wsz_load[]      = {'l','o','a','d',0};
-    static const PRUnichar DOMNodeInsertedW[] =
-        {'D','O','M','N','o','d','e','I','n','s','e','r','t','e','d',0};
-
-    init_listener(&This->blur_listener,        This, &blur_vtbl);
-    init_listener(&This->focus_listener,       This, &focus_vtbl);
-    init_listener(&This->keypress_listener,    This, &keypress_vtbl);
-    init_listener(&This->load_listener,        This, &load_vtbl);
-    init_listener(&This->node_insert_listener, This, &node_insert_vtbl);
-    init_listener(&This->htmlevent_listener,   This, &htmlevent_vtbl);
 
-    nsres = nsIWebBrowser_GetContentDOMWindow(This->webbrowser, &dom_window);
-    if(NS_FAILED(nsres)) {
-        ERR("GetContentDOMWindow failed: %08x\n", nsres);
+    listener = heap_alloc(sizeof(nsDocumentEventListener));
+    if(!listener)
         return;
-    }
 
-    nsres = nsIDOMWindow_QueryInterface(dom_window, &IID_nsIDOMEventTarget, (void**)&target);
-    nsIDOMWindow_Release(dom_window);
+    listener->ref = 1;
+    listener->doc = doc;
+
+    init_listener(&listener->blur_listener,        listener, &blur_vtbl);
+    init_listener(&listener->focus_listener,       listener, &focus_vtbl);
+    init_listener(&listener->keypress_listener,    listener, &keypress_vtbl);
+    init_listener(&listener->load_listener,        listener, &load_vtbl);
+    init_listener(&listener->htmlevent_listener,   listener, &htmlevent_vtbl);
+
+    doc->nsevent_listener = listener;
+
+    nsres = nsIDOMWindow_QueryInterface(doc->basedoc.window->nswindow, &IID_nsIDOMEventTarget, (void**)&target);
     if(NS_FAILED(nsres)) {
         ERR("Could not get nsIDOMEventTarget interface: %08x\n", nsres);
         return;
     }
 
-    init_event(target, wsz_blur,       NSEVENTLIST(&This->blur_listener),        TRUE);
-    init_event(target, wsz_focus,      NSEVENTLIST(&This->focus_listener),       TRUE);
-    init_event(target, wsz_keypress,   NSEVENTLIST(&This->keypress_listener),    FALSE);
-    init_event(target, wsz_load,       NSEVENTLIST(&This->load_listener),        TRUE);
-    init_event(target, DOMNodeInsertedW,NSEVENTLIST(&This->node_insert_listener),TRUE);
+    init_event(target, wsz_blur,       NSEVENTLIST(&listener->blur_listener),        TRUE);
+    init_event(target, wsz_focus,      NSEVENTLIST(&listener->focus_listener),       TRUE);
+    init_event(target, wsz_keypress,   NSEVENTLIST(&listener->keypress_listener),    FALSE);
+    init_event(target, wsz_load,       NSEVENTLIST(&listener->load_listener),        TRUE);
 
     nsIDOMEventTarget_Release(target);
 }