Back to index

lightning-sunbird  0.9+nobinonly
nsXMLEventsManager.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Olli Pettay.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Olli Pettay <Olli.Pettay@helsinki.fi> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsIDOMMutationEvent.h"
00040 #include "nsXMLEventsManager.h"
00041 #include "nsHTMLAtoms.h"
00042 #include "nsIDOMElement.h"
00043 #include "nsIDOMDocument.h"
00044 #include "nsIDOMEventReceiver.h"
00045 #include "nsIStyledContent.h"
00046 #include "nsNetUtil.h"
00047 #include "nsIURL.h"
00048 #include "nsIDOMEventListener.h"
00049 #include "nsINameSpaceManager.h"
00050 #include "nsINodeInfo.h"
00051 
00052 PRBool nsXMLEventsListener::InitXMLEventsListener(nsIDocument * aDocument,
00053                                                   nsXMLEventsManager * aManager,
00054                                                   nsIContent * aContent)
00055 {
00056   PRInt32 nameSpaceID;
00057   if (aContent->GetDocument() != aDocument)
00058     return PR_FALSE;
00059   if (aContent->GetNodeInfo()->Equals(nsHTMLAtoms::listener, kNameSpaceID_XMLEvents))
00060     nameSpaceID = kNameSpaceID_None;
00061   else
00062     nameSpaceID = kNameSpaceID_XMLEvents;
00063   nsAutoString eventType;
00064   nsresult rv = aContent->GetAttr(nameSpaceID, nsHTMLAtoms::_event, eventType);
00065   if (rv != NS_CONTENT_ATTR_HAS_VALUE)
00066     return PR_FALSE;
00067   nsAutoString handlerURIStr;
00068   PRBool hasHandlerURI = PR_FALSE;
00069   nsCOMPtr<nsIContent> handler;
00070   nsAutoString observerID;
00071   nsAutoString targetIdref;
00072   
00073   if (aContent->GetAttr(nameSpaceID, nsHTMLAtoms::handler, handlerURIStr) != 
00074       NS_CONTENT_ATTR_NOT_THERE) {
00075     hasHandlerURI = PR_TRUE;
00076     nsCAutoString handlerRef;
00077     nsCOMPtr<nsIURI> handlerURI;
00078     PRBool equals = PR_FALSE;
00079     nsIURI *docURI = aDocument->GetDocumentURI();
00080     nsIURI *baseURI = aDocument->GetBaseURI();
00081     rv = NS_NewURI( getter_AddRefs(handlerURI), handlerURIStr, nsnull, baseURI);
00082     if (NS_SUCCEEDED(rv)) {
00083       nsCOMPtr<nsIURL> handlerURL(do_QueryInterface(handlerURI));
00084       if (handlerURL) {
00085         handlerURL->GetRef(handlerRef);
00086         handlerURL->SetRef(EmptyCString());
00087         //We support only XML Events Basic.
00088         docURI->Equals(handlerURL, &equals);
00089         if (equals) {
00090           nsCOMPtr<nsIDOMDocument> doc(do_QueryInterface(aDocument));
00091           if (doc) {
00092             nsCOMPtr<nsIDOMElement> domhandler;
00093             doc->GetElementById(NS_ConvertUTF8toUTF16(handlerRef),
00094                                 getter_AddRefs(domhandler));
00095             handler = do_QueryInterface(domhandler);
00096           }
00097         }
00098       }
00099     }
00100   }
00101   else
00102     handler = aContent;
00103   if (!handler)
00104     return PR_FALSE;
00105 
00106   aContent->GetAttr(nameSpaceID, nsHTMLAtoms::target, targetIdref);
00107 
00108   PRBool hasObserver = 
00109     aContent->GetAttr(nameSpaceID, nsHTMLAtoms::observer, observerID) != 
00110     NS_CONTENT_ATTR_NOT_THERE;
00111 
00112   nsAutoString phase;
00113   PRBool capture =
00114     aContent->GetAttr(nameSpaceID, nsHTMLAtoms::phase, phase) !=
00115     NS_CONTENT_ATTR_NOT_THERE && phase.Equals(NS_LITERAL_STRING("capture"));
00116 
00117   nsAutoString prop;
00118   PRBool stopPropagation = 
00119     aContent->GetAttr(nameSpaceID, nsHTMLAtoms::propagate, prop) !=
00120     NS_CONTENT_ATTR_NOT_THERE && prop.Equals(NS_LITERAL_STRING("stop"));
00121 
00122   nsAutoString cancel;
00123   PRBool cancelDefault = 
00124     aContent->GetAttr(nameSpaceID, nsHTMLAtoms::defaultAction, cancel) !=
00125     NS_CONTENT_ATTR_NOT_THERE && cancel.Equals(NS_LITERAL_STRING("cancel"));
00126 
00127   nsCOMPtr<nsIContent> observer;
00128   if (!hasObserver) {
00129     if (!hasHandlerURI) //Parent should be the observer
00130       observer = aContent->GetParent();
00131     else //We have the handler, so this is the observer
00132       observer = aContent;
00133   }
00134   else if (!observerID.IsEmpty()) {
00135     nsCOMPtr<nsIDOMDocument> doc(do_QueryInterface(aDocument));
00136     if (doc) {
00137       nsCOMPtr<nsIDOMElement> el;
00138       doc->GetElementById(observerID, getter_AddRefs(el));
00139       observer = do_QueryInterface(el);
00140     }
00141   }
00142   nsCOMPtr<nsIDOMEventTarget> eventObserver;
00143   if (observer)
00144     eventObserver = do_QueryInterface(observer);
00145   if (eventObserver) {
00146     nsXMLEventsListener * eli = new nsXMLEventsListener(aManager,
00147                                                         aContent,
00148                                                         observer,
00149                                                         handler,
00150                                                         eventType,
00151                                                         capture,
00152                                                         stopPropagation,
00153                                                         cancelDefault,
00154                                                         targetIdref);
00155     if (eli) {
00156       nsresult rv = eventObserver->AddEventListener(eventType, eli, capture);
00157       if (NS_SUCCEEDED(rv)) {
00158         aManager->RemoveXMLEventsContent(aContent);
00159         aManager->RemoveListener(aContent);
00160         aManager->AddListener(aContent, eli);
00161         return PR_TRUE;
00162       }
00163       else
00164         delete eli;
00165     }
00166   }
00167   return PR_FALSE;
00168 }
00169 
00170 nsXMLEventsListener::nsXMLEventsListener(nsXMLEventsManager * aManager,
00171                                          nsIContent * aElement,
00172                                          nsIContent * aObserver,
00173                                          nsIContent * aHandler,
00174                                          const nsAString& aEvent,
00175                                          PRBool aPhase,
00176                                          PRBool aStopPropagation,
00177                                          PRBool aCancelDefault,
00178                                          const nsAString& aTarget)
00179  : mManager(aManager),
00180    mElement(aElement),
00181    mObserver(aObserver),
00182    mHandler(aHandler),
00183    mEvent(aEvent),
00184    mPhase(aPhase),
00185    mStopPropagation(aStopPropagation),
00186    mCancelDefault(aCancelDefault)
00187 {
00188   if (!aTarget.IsEmpty())
00189     mTarget = do_GetAtom(aTarget);
00190 }
00191 
00192 nsXMLEventsListener::~nsXMLEventsListener()
00193 {
00194 }
00195 
00196 void nsXMLEventsListener::Unregister()
00197 {
00198   nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mObserver);
00199   if (target) {
00200     target->RemoveEventListener(mEvent, this, mPhase);
00201   }
00202   mObserver = nsnull;
00203   mHandler = nsnull;
00204 }
00205 
00206 void nsXMLEventsListener::SetIncomplete()
00207 {
00208   Unregister();
00209   mManager->AddXMLEventsContent(mElement);
00210   mElement = nsnull;
00211 }
00212 
00213 PRBool nsXMLEventsListener::ObserverEquals(nsIContent * aTarget)
00214 {
00215   return aTarget == mObserver;
00216 }
00217 
00218 PRBool nsXMLEventsListener::HandlerEquals(nsIContent * aTarget)
00219 {
00220   return aTarget == mHandler;
00221 }
00222 
00223 NS_IMPL_ISUPPORTS1(nsXMLEventsListener, nsIDOMEventListener)
00224 NS_IMETHODIMP
00225 nsXMLEventsListener::HandleEvent(nsIDOMEvent* aEvent)
00226 {
00227   if (!aEvent) 
00228     return NS_ERROR_INVALID_ARG;
00229   PRBool targetMatched = PR_TRUE;
00230   nsCOMPtr<nsIDOMEvent> event(aEvent);
00231   if (mTarget) {
00232     targetMatched = PR_FALSE;
00233     nsCOMPtr<nsIDOMEventTarget> target;
00234     aEvent->GetTarget(getter_AddRefs(target));
00235     nsCOMPtr<nsIStyledContent> targetEl(do_QueryInterface(target));
00236     if (targetEl && targetEl->GetID() == mTarget) 
00237         targetMatched = PR_TRUE;
00238   }
00239   if (!targetMatched)
00240     return NS_OK;
00241   nsCOMPtr<nsIDOMEventListener> handler(do_QueryInterface(mHandler));
00242   if (handler) {
00243     nsresult rv = handler->HandleEvent(event);
00244     if (NS_SUCCEEDED(rv)) {
00245       if (mStopPropagation)
00246         event->StopPropagation();
00247       if (mCancelDefault)
00248         event->PreventDefault();
00249     }
00250     return rv;
00251   }
00252   return NS_OK;
00253 }
00254 
00255 
00256 //XMLEventsManager / DocumentObserver
00257 
00258 PR_STATIC_CALLBACK(PLDHashOperator) EnumAndUnregisterListener(nsISupports * aContent, 
00259                                                               nsCOMPtr<nsXMLEventsListener> & aListener, 
00260                                                               void * aData)
00261 {
00262   if (aListener)
00263     aListener->Unregister();
00264   return PL_DHASH_NEXT;
00265 }
00266 
00267 PR_STATIC_CALLBACK(PLDHashOperator) EnumAndSetIncomplete(nsISupports * aContent, 
00268                                                          nsCOMPtr<nsXMLEventsListener> & aListener,
00269                                                          void * aData)
00270 {
00271   if (aListener && aData) {
00272     nsCOMPtr<nsIContent> content = NS_STATIC_CAST(nsIContent *, aData);
00273     if (content) { 
00274       if (aListener->ObserverEquals(content) || aListener->HandlerEquals(content)) {
00275         aListener->SetIncomplete();
00276         return PL_DHASH_REMOVE;
00277       }
00278     }
00279   }
00280   return PL_DHASH_NEXT;
00281 }
00282 
00283 nsXMLEventsManager::nsXMLEventsManager()
00284 {
00285   mListeners.Init();
00286 }
00287 nsXMLEventsManager::~nsXMLEventsManager()
00288 {
00289 }
00290 
00291 NS_IMPL_ISUPPORTS1(nsXMLEventsManager, nsIDocumentObserver)
00292 
00293 void nsXMLEventsManager::AddXMLEventsContent(nsIContent * aContent)
00294 {
00295   mIncomplete.RemoveObject(aContent);
00296   mIncomplete.AppendObject(aContent);
00297 }
00298 
00299 void nsXMLEventsManager::RemoveXMLEventsContent(nsIContent * aContent)
00300 {
00301   mIncomplete.RemoveObject(aContent);
00302 }
00303 
00304 void nsXMLEventsManager::AddListener(nsIContent * aContent, 
00305                                      nsXMLEventsListener * aListener)
00306 {
00307   mListeners.Put(aContent, aListener);
00308 }
00309 
00310 PRBool nsXMLEventsManager::RemoveListener(nsIContent * aContent)
00311 {
00312   nsCOMPtr<nsXMLEventsListener> listener;
00313   mListeners.Get(aContent, getter_AddRefs(listener));
00314   if (listener) {
00315     listener->Unregister();
00316     mListeners.Remove(aContent);
00317     return PR_TRUE;
00318   }
00319   return PR_FALSE;
00320 }
00321 
00322 void nsXMLEventsManager::AddListeners(nsIDocument* aDocument)
00323 {
00324   nsIContent *cur;
00325   for (int i = 0; i < mIncomplete.Count(); ++i) {
00326     cur = mIncomplete[i];
00327     //If this succeeds, the object will be removed from mIncomplete
00328     if (nsXMLEventsListener::InitXMLEventsListener(aDocument, this, cur) == PR_TRUE)
00329       --i;
00330   }
00331 }
00332 
00333 void 
00334 nsXMLEventsManager::BeginUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType) {}
00335 void 
00336 nsXMLEventsManager::EndUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType) {}
00337 void 
00338 nsXMLEventsManager::DocumentWillBeDestroyed(nsIDocument* aDocument){
00339   mIncomplete.Clear();
00340   mListeners.Enumerate(EnumAndUnregisterListener, this);
00341   mListeners.Clear();
00342   aDocument->RemoveObserver(this);
00343 }
00344 
00345 void 
00346 nsXMLEventsManager::BeginLoad(nsIDocument* aDocument) {}
00347 
00348 void 
00349 nsXMLEventsManager::EndLoad(nsIDocument* aDocument)
00350 {
00351   AddListeners(aDocument);
00352 }
00353 NS_IMPL_NSIDOCUMENTOBSERVER_REFLOW_STUB(nsXMLEventsManager)
00354 NS_IMPL_NSIDOCUMENTOBSERVER_STATE_STUB(nsXMLEventsManager)
00355 void 
00356 nsXMLEventsManager::CharacterDataChanged(nsIDocument* aDocument,
00357                                          nsIContent* aContent,
00358                                          PRBool aAppend) {}
00359 void
00360 nsXMLEventsManager::AttributeChanged(nsIDocument* aDocument,
00361                                      nsIContent* aContent,
00362                                      PRInt32 aNameSpaceID,
00363                                      nsIAtom* aAttribute,
00364                                      PRInt32 aModType)
00365 {
00366   if (aNameSpaceID == kNameSpaceID_XMLEvents &&
00367       (aAttribute == nsHTMLAtoms::_event ||
00368        aAttribute == nsHTMLAtoms::handler ||
00369        aAttribute == nsHTMLAtoms::target ||
00370        aAttribute == nsHTMLAtoms::observer ||
00371        aAttribute == nsHTMLAtoms::phase ||
00372        aAttribute == nsHTMLAtoms::propagate)) {
00373     RemoveListener(aContent);
00374     AddXMLEventsContent(aContent);
00375     nsXMLEventsListener::InitXMLEventsListener(aDocument, this, aContent);
00376   }
00377   else {
00378     if (aContent->GetNodeInfo()->Equals(nsHTMLAtoms::listener, kNameSpaceID_XMLEvents)) {
00379       RemoveListener(aContent);
00380       AddXMLEventsContent(aContent);
00381       nsXMLEventsListener::InitXMLEventsListener(aDocument, this, aContent);
00382     }
00383     else if (aContent->GetIDAttributeName() == aAttribute) {
00384       if (aModType == nsIDOMMutationEvent::REMOVAL)
00385         mListeners.Enumerate(EnumAndSetIncomplete, aContent);
00386       else if (aModType == nsIDOMMutationEvent::MODIFICATION) {
00387         //Remove possible listener
00388         mListeners.Enumerate(EnumAndSetIncomplete, aContent);
00389         //Add new listeners
00390         AddListeners(aDocument);
00391       }
00392       else {
00393         //If we are adding the ID attribute, we must check whether we can 
00394         //add new listeners
00395         AddListeners(aDocument);
00396       }
00397     }
00398   }
00399 }
00400 
00401 void
00402 nsXMLEventsManager::ContentAppended(nsIDocument* aDocument,
00403                                     nsIContent* aContainer,
00404                                     PRInt32 aNewIndexInContainer)
00405 {
00406   AddListeners(aDocument);
00407 }
00408 
00409 void
00410 nsXMLEventsManager::ContentInserted(nsIDocument* aDocument,
00411                                     nsIContent* aContainer,
00412                                     nsIContent* aChild,
00413                                     PRInt32 aIndexInContainer)
00414 {
00415   AddListeners(aDocument);
00416 }
00417 
00418 void
00419 nsXMLEventsManager::ContentRemoved(nsIDocument* aDocument, 
00420                                    nsIContent* aContainer,
00421                                    nsIContent* aChild,
00422                                    PRInt32 aIndexInContainer)
00423 {
00424   if (!aChild || !aChild->IsContentOfType(nsIContent::eELEMENT))
00425     return;
00426   //Note, we can't use IDs here, the observer may not always have an ID.
00427   //And to remember: the same observer can be referenced by many 
00428   //XMLEventsListeners
00429 
00430   //If the content was an XML Events observer or handler
00431   mListeners.Enumerate(EnumAndSetIncomplete, aChild);
00432 
00433   //If the content was an XML Events attributes container
00434   if (RemoveListener(aChild)) {
00435     //for aContainer.appendChild(aContainer.removeChild(aChild));
00436     AddXMLEventsContent(aChild);
00437   }
00438 
00439   PRUint32 count = aChild->GetChildCount();
00440   for (PRUint32 i = 0; i < count; ++i) {
00441     ContentRemoved(aDocument, aChild, aChild->GetChildAt(i), i);
00442   }
00443 }
00444 
00445 NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsXMLEventsManager)
00446