1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
19 * Portions created by the Initial Developer are Copyright (C) 2004
20 * the Initial Developer. All Rights Reserved.
23 * Olli Pettay <Olli.Pettay@helsinki.fi> (original author)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsIDOMMutationEvent.h"
40 #include "nsXMLEventsManager.h"
41 #include "nsGkAtoms.h"
42 #include "nsIDOMElement.h"
43 #include "nsIDOMDocument.h"
44 #include "nsIDOMEventTarget.h"
45 #include "nsNetUtil.h"
47 #include "nsIDOMEventListener.h"
48 #include "nsINameSpaceManager.h"
49 #include "nsINodeInfo.h"
51 PRBool
nsXMLEventsListener::InitXMLEventsListener(nsIDocument
* aDocument
,
52 nsXMLEventsManager
* aManager
,
53 nsIContent
* aContent
)
57 if (aContent
->GetDocument() != aDocument
)
59 if (aContent
->NodeInfo()->Equals(nsGkAtoms::listener
,
60 kNameSpaceID_XMLEvents
))
61 nameSpaceID
= kNameSpaceID_None
;
63 nameSpaceID
= kNameSpaceID_XMLEvents
;
64 nsAutoString eventType
;
65 aContent
->GetAttr(nameSpaceID
, nsGkAtoms::event
, eventType
);
66 if (eventType
.IsEmpty())
68 nsAutoString handlerURIStr
;
69 PRBool hasHandlerURI
= PR_FALSE
;
70 nsCOMPtr
<nsIContent
> handler
;
71 nsAutoString observerID
;
72 nsAutoString targetIdref
;
74 if (aContent
->GetAttr(nameSpaceID
, nsGkAtoms::handler
, handlerURIStr
)) {
75 hasHandlerURI
= PR_TRUE
;
76 nsCAutoString handlerRef
;
77 nsCOMPtr
<nsIURI
> handlerURI
;
78 PRBool equals
= PR_FALSE
;
79 nsIURI
*docURI
= aDocument
->GetDocumentURI();
80 nsIURI
*baseURI
= aDocument
->GetBaseURI();
81 rv
= NS_NewURI( getter_AddRefs(handlerURI
), handlerURIStr
, nsnull
, baseURI
);
82 if (NS_SUCCEEDED(rv
)) {
83 nsCOMPtr
<nsIURL
> handlerURL(do_QueryInterface(handlerURI
));
85 handlerURL
->GetRef(handlerRef
);
86 handlerURL
->SetRef(EmptyCString());
87 //We support only XML Events Basic.
88 docURI
->Equals(handlerURL
, &equals
);
90 nsCOMPtr
<nsIDOMDocument
> doc(do_QueryInterface(aDocument
));
92 nsCOMPtr
<nsIDOMElement
> domhandler
;
93 doc
->GetElementById(NS_ConvertUTF8toUTF16(handlerRef
),
94 getter_AddRefs(domhandler
));
95 handler
= do_QueryInterface(domhandler
);
106 aContent
->GetAttr(nameSpaceID
, nsGkAtoms::target
, targetIdref
);
109 aContent
->GetAttr(nameSpaceID
, nsGkAtoms::observer
, observerID
);
112 aContent
->AttrValueIs(nameSpaceID
, nsGkAtoms::phase
,
113 nsGkAtoms::capture
, eCaseMatters
);
115 PRBool stopPropagation
=
116 aContent
->AttrValueIs(nameSpaceID
, nsGkAtoms::propagate
,
117 nsGkAtoms::stop
, eCaseMatters
);
119 PRBool cancelDefault
=
120 aContent
->AttrValueIs(nameSpaceID
, nsGkAtoms::defaultAction
,
121 nsGkAtoms::cancel
, eCaseMatters
);
123 nsCOMPtr
<nsIContent
> observer
;
125 if (!hasHandlerURI
) //Parent should be the observer
126 observer
= aContent
->GetParent();
127 else //We have the handler, so this is the observer
130 else if (!observerID
.IsEmpty()) {
131 nsCOMPtr
<nsIDOMDocument
> doc(do_QueryInterface(aDocument
));
133 nsCOMPtr
<nsIDOMElement
> el
;
134 doc
->GetElementById(observerID
, getter_AddRefs(el
));
135 observer
= do_QueryInterface(el
);
138 nsCOMPtr
<nsIDOMEventTarget
> eventObserver
;
140 eventObserver
= do_QueryInterface(observer
);
142 nsXMLEventsListener
* eli
= new nsXMLEventsListener(aManager
,
152 nsresult rv
= eventObserver
->AddEventListener(eventType
, eli
, capture
);
153 if (NS_SUCCEEDED(rv
)) {
154 aManager
->RemoveXMLEventsContent(aContent
);
155 aManager
->RemoveListener(aContent
);
156 aManager
->AddListener(aContent
, eli
);
166 nsXMLEventsListener::nsXMLEventsListener(nsXMLEventsManager
* aManager
,
167 nsIContent
* aElement
,
168 nsIContent
* aObserver
,
169 nsIContent
* aHandler
,
170 const nsAString
& aEvent
,
172 PRBool aStopPropagation
,
173 PRBool aCancelDefault
,
174 const nsAString
& aTarget
)
175 : mManager(aManager
),
177 mObserver(aObserver
),
181 mStopPropagation(aStopPropagation
),
182 mCancelDefault(aCancelDefault
)
184 if (!aTarget
.IsEmpty())
185 mTarget
= do_GetAtom(aTarget
);
188 nsXMLEventsListener::~nsXMLEventsListener()
192 void nsXMLEventsListener::Unregister()
194 nsCOMPtr
<nsIDOMEventTarget
> target
= do_QueryInterface(mObserver
);
196 target
->RemoveEventListener(mEvent
, this, mPhase
);
202 void nsXMLEventsListener::SetIncomplete()
205 mManager
->AddXMLEventsContent(mElement
);
209 PRBool
nsXMLEventsListener::ObserverEquals(nsIContent
* aTarget
)
211 return aTarget
== mObserver
;
214 PRBool
nsXMLEventsListener::HandlerEquals(nsIContent
* aTarget
)
216 return aTarget
== mHandler
;
219 NS_IMPL_ISUPPORTS1(nsXMLEventsListener
, nsIDOMEventListener
)
221 nsXMLEventsListener::HandleEvent(nsIDOMEvent
* aEvent
)
224 return NS_ERROR_INVALID_ARG
;
225 PRBool targetMatched
= PR_TRUE
;
226 nsCOMPtr
<nsIDOMEvent
> event(aEvent
);
228 targetMatched
= PR_FALSE
;
229 nsCOMPtr
<nsIDOMEventTarget
> target
;
230 aEvent
->GetTarget(getter_AddRefs(target
));
231 nsCOMPtr
<nsIContent
> targetEl(do_QueryInterface(target
));
232 if (targetEl
&& targetEl
->GetID() == mTarget
)
233 targetMatched
= PR_TRUE
;
237 nsCOMPtr
<nsIDOMEventListener
> handler(do_QueryInterface(mHandler
));
239 nsresult rv
= handler
->HandleEvent(event
);
240 if (NS_SUCCEEDED(rv
)) {
241 if (mStopPropagation
)
242 event
->StopPropagation();
244 event
->PreventDefault();
252 //XMLEventsManager / DocumentObserver
254 PR_STATIC_CALLBACK(PLDHashOperator
) EnumAndUnregisterListener(nsISupports
* aContent
,
255 nsCOMPtr
<nsXMLEventsListener
> & aListener
,
259 aListener
->Unregister();
260 return PL_DHASH_NEXT
;
263 PR_STATIC_CALLBACK(PLDHashOperator
) EnumAndSetIncomplete(nsISupports
* aContent
,
264 nsCOMPtr
<nsXMLEventsListener
> & aListener
,
267 if (aListener
&& aData
) {
268 nsCOMPtr
<nsIContent
> content
= static_cast<nsIContent
*>(aData
);
270 if (aListener
->ObserverEquals(content
) || aListener
->HandlerEquals(content
)) {
271 aListener
->SetIncomplete();
272 return PL_DHASH_REMOVE
;
276 return PL_DHASH_NEXT
;
279 nsXMLEventsManager::nsXMLEventsManager()
283 nsXMLEventsManager::~nsXMLEventsManager()
287 NS_IMPL_ISUPPORTS2(nsXMLEventsManager
, nsIDocumentObserver
, nsIMutationObserver
)
289 void nsXMLEventsManager::AddXMLEventsContent(nsIContent
* aContent
)
291 mIncomplete
.RemoveObject(aContent
);
292 mIncomplete
.AppendObject(aContent
);
295 void nsXMLEventsManager::RemoveXMLEventsContent(nsIContent
* aContent
)
297 mIncomplete
.RemoveObject(aContent
);
300 void nsXMLEventsManager::AddListener(nsIContent
* aContent
,
301 nsXMLEventsListener
* aListener
)
303 mListeners
.Put(aContent
, aListener
);
306 PRBool
nsXMLEventsManager::RemoveListener(nsIContent
* aContent
)
308 nsCOMPtr
<nsXMLEventsListener
> listener
;
309 mListeners
.Get(aContent
, getter_AddRefs(listener
));
311 listener
->Unregister();
312 mListeners
.Remove(aContent
);
318 void nsXMLEventsManager::AddListeners(nsIDocument
* aDocument
)
321 for (int i
= 0; i
< mIncomplete
.Count(); ++i
) {
322 cur
= mIncomplete
[i
];
323 //If this succeeds, the object will be removed from mIncomplete
324 if (nsXMLEventsListener::InitXMLEventsListener(aDocument
, this, cur
) == PR_TRUE
)
330 nsXMLEventsManager::BeginUpdate(nsIDocument
* aDocument
, nsUpdateType aUpdateType
) {}
332 nsXMLEventsManager::EndUpdate(nsIDocument
* aDocument
, nsUpdateType aUpdateType
) {}
334 nsXMLEventsManager::NodeWillBeDestroyed(const nsINode
* aNode
)
337 mListeners
.Enumerate(EnumAndUnregisterListener
, this);
342 nsXMLEventsManager::BeginLoad(nsIDocument
* aDocument
) {}
345 nsXMLEventsManager::EndLoad(nsIDocument
* aDocument
)
347 AddListeners(aDocument
);
349 NS_IMPL_NSIDOCUMENTOBSERVER_STATE_STUB(nsXMLEventsManager
)
351 nsXMLEventsManager::CharacterDataWillChange(nsIDocument
* aDocument
,
352 nsIContent
* aContent
,
353 CharacterDataChangeInfo
* aInfo
) {}
355 nsXMLEventsManager::CharacterDataChanged(nsIDocument
* aDocument
,
356 nsIContent
* aContent
,
357 CharacterDataChangeInfo
* aInfo
) {}
359 nsXMLEventsManager::AttributeChanged(nsIDocument
* aDocument
,
360 nsIContent
* aContent
,
361 PRInt32 aNameSpaceID
,
366 if (aNameSpaceID
== kNameSpaceID_XMLEvents
&&
367 (aAttribute
== nsGkAtoms::event
||
368 aAttribute
== nsGkAtoms::handler
||
369 aAttribute
== nsGkAtoms::target
||
370 aAttribute
== nsGkAtoms::observer
||
371 aAttribute
== nsGkAtoms::phase
||
372 aAttribute
== nsGkAtoms::propagate
)) {
373 RemoveListener(aContent
);
374 AddXMLEventsContent(aContent
);
375 nsXMLEventsListener::InitXMLEventsListener(aDocument
, this, aContent
);
378 if (aContent
->NodeInfo()->Equals(nsGkAtoms::listener
,
379 kNameSpaceID_XMLEvents
)) {
380 RemoveListener(aContent
);
381 AddXMLEventsContent(aContent
);
382 nsXMLEventsListener::InitXMLEventsListener(aDocument
, this, aContent
);
384 else if (aContent
->GetIDAttributeName() == aAttribute
) {
385 if (aModType
== nsIDOMMutationEvent::REMOVAL
)
386 mListeners
.Enumerate(EnumAndSetIncomplete
, aContent
);
387 else if (aModType
== nsIDOMMutationEvent::MODIFICATION
) {
388 //Remove possible listener
389 mListeners
.Enumerate(EnumAndSetIncomplete
, aContent
);
391 AddListeners(aDocument
);
394 //If we are adding the ID attribute, we must check whether we can
396 AddListeners(aDocument
);
403 nsXMLEventsManager::ContentAppended(nsIDocument
* aDocument
,
404 nsIContent
* aContainer
,
405 PRInt32 aNewIndexInContainer
)
407 AddListeners(aDocument
);
411 nsXMLEventsManager::ContentInserted(nsIDocument
* aDocument
,
412 nsIContent
* aContainer
,
414 PRInt32 aIndexInContainer
)
416 AddListeners(aDocument
);
420 nsXMLEventsManager::ContentRemoved(nsIDocument
* aDocument
,
421 nsIContent
* aContainer
,
423 PRInt32 aIndexInContainer
)
425 if (!aChild
|| !aChild
->IsNodeOfType(nsINode::eELEMENT
))
427 //Note, we can't use IDs here, the observer may not always have an ID.
428 //And to remember: the same observer can be referenced by many
431 //If the content was an XML Events observer or handler
432 mListeners
.Enumerate(EnumAndSetIncomplete
, aChild
);
434 //If the content was an XML Events attributes container
435 if (RemoveListener(aChild
)) {
436 //for aContainer.appendChild(aContainer.removeChild(aChild));
437 AddXMLEventsContent(aChild
);
440 PRUint32 count
= aChild
->GetChildCount();
441 for (PRUint32 i
= 0; i
< count
; ++i
) {
442 ContentRemoved(aDocument
, aChild
, aChild
->GetChildAt(i
), i
);
447 nsXMLEventsManager::ParentChainChanged(nsIContent
*aContent
)
451 NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsXMLEventsManager
)