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
4 : *
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/
9 : *
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
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Olli Pettay.
19 : * Portions created by the Initial Developer are Copyright (C) 2004
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Olli Pettay <Olli.Pettay@helsinki.fi> (original author)
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
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"
46 : #include "nsIURL.h"
47 : #include "nsIDOMEventListener.h"
48 : #include "nsINameSpaceManager.h"
49 : #include "nsINodeInfo.h"
50 : #include "mozilla/dom/Element.h"
51 :
52 : using namespace mozilla::dom;
53 :
54 0 : bool nsXMLEventsListener::InitXMLEventsListener(nsIDocument * aDocument,
55 : nsXMLEventsManager * aManager,
56 : nsIContent * aContent)
57 : {
58 : nsresult rv;
59 : PRInt32 nameSpaceID;
60 0 : if (aContent->GetDocument() != aDocument)
61 0 : return false;
62 0 : if (aContent->NodeInfo()->Equals(nsGkAtoms::listener,
63 0 : kNameSpaceID_XMLEvents))
64 0 : nameSpaceID = kNameSpaceID_None;
65 : else
66 0 : nameSpaceID = kNameSpaceID_XMLEvents;
67 0 : nsAutoString eventType;
68 0 : aContent->GetAttr(nameSpaceID, nsGkAtoms::event, eventType);
69 0 : if (eventType.IsEmpty())
70 0 : return false;
71 0 : nsAutoString handlerURIStr;
72 0 : bool hasHandlerURI = false;
73 0 : nsIContent *handler = nsnull;
74 0 : nsAutoString observerID;
75 0 : nsAutoString targetIdref;
76 :
77 0 : if (aContent->GetAttr(nameSpaceID, nsGkAtoms::handler, handlerURIStr)) {
78 0 : hasHandlerURI = true;
79 0 : nsCAutoString handlerRef;
80 0 : nsCOMPtr<nsIURI> handlerURI;
81 0 : bool equals = false;
82 0 : nsIURI *docURI = aDocument->GetDocumentURI();
83 0 : nsIURI *baseURI = aDocument->GetDocBaseURI();
84 0 : rv = NS_NewURI( getter_AddRefs(handlerURI), handlerURIStr, nsnull, baseURI);
85 0 : if (NS_SUCCEEDED(rv)) {
86 0 : handlerURI->GetRef(handlerRef);
87 : // We support only XML Events Basic.
88 0 : rv = docURI->EqualsExceptRef(handlerURI, &equals);
89 0 : if (NS_SUCCEEDED(rv) && equals) {
90 0 : handler = aDocument->GetElementById(NS_ConvertUTF8toUTF16(handlerRef));
91 : }
92 : }
93 : }
94 : else
95 0 : handler = aContent;
96 0 : if (!handler)
97 0 : return false;
98 :
99 0 : aContent->GetAttr(nameSpaceID, nsGkAtoms::target, targetIdref);
100 :
101 : bool hasObserver =
102 0 : aContent->GetAttr(nameSpaceID, nsGkAtoms::observer, observerID);
103 :
104 : bool capture =
105 : aContent->AttrValueIs(nameSpaceID, nsGkAtoms::phase,
106 0 : nsGkAtoms::capture, eCaseMatters);
107 :
108 : bool stopPropagation =
109 : aContent->AttrValueIs(nameSpaceID, nsGkAtoms::propagate,
110 0 : nsGkAtoms::stop, eCaseMatters);
111 :
112 : bool cancelDefault =
113 : aContent->AttrValueIs(nameSpaceID, nsGkAtoms::defaultAction,
114 0 : nsGkAtoms::cancel, eCaseMatters);
115 :
116 0 : nsIContent *observer = nsnull;
117 0 : if (!hasObserver) {
118 0 : if (!hasHandlerURI) //Parent should be the observer
119 0 : observer = aContent->GetParent();
120 : else //We have the handler, so this is the observer
121 0 : observer = aContent;
122 : }
123 0 : else if (!observerID.IsEmpty()) {
124 0 : observer = aDocument->GetElementById(observerID);
125 : }
126 0 : nsCOMPtr<nsIDOMEventTarget> eventObserver(do_QueryInterface(observer));
127 0 : if (eventObserver) {
128 : nsXMLEventsListener * eli = new nsXMLEventsListener(aManager,
129 : aContent,
130 : observer,
131 : handler,
132 : eventType,
133 : capture,
134 : stopPropagation,
135 : cancelDefault,
136 0 : targetIdref);
137 0 : if (eli) {
138 0 : nsresult rv = eventObserver->AddEventListener(eventType, eli, capture);
139 0 : if (NS_SUCCEEDED(rv)) {
140 0 : aManager->RemoveXMLEventsContent(aContent);
141 0 : aManager->RemoveListener(aContent);
142 0 : aManager->AddListener(aContent, eli);
143 0 : return true;
144 : }
145 : else
146 0 : delete eli;
147 : }
148 : }
149 0 : return false;
150 : }
151 :
152 0 : nsXMLEventsListener::nsXMLEventsListener(nsXMLEventsManager * aManager,
153 : nsIContent * aElement,
154 : nsIContent * aObserver,
155 : nsIContent * aHandler,
156 : const nsAString& aEvent,
157 : bool aPhase,
158 : bool aStopPropagation,
159 : bool aCancelDefault,
160 : const nsAString& aTarget)
161 : : mManager(aManager),
162 : mElement(aElement),
163 : mObserver(aObserver),
164 : mHandler(aHandler),
165 : mEvent(aEvent),
166 : mPhase(aPhase),
167 : mStopPropagation(aStopPropagation),
168 0 : mCancelDefault(aCancelDefault)
169 : {
170 0 : if (!aTarget.IsEmpty())
171 0 : mTarget = do_GetAtom(aTarget);
172 0 : }
173 :
174 0 : nsXMLEventsListener::~nsXMLEventsListener()
175 : {
176 0 : }
177 :
178 0 : void nsXMLEventsListener::Unregister()
179 : {
180 0 : nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mObserver);
181 0 : if (target) {
182 0 : target->RemoveEventListener(mEvent, this, mPhase);
183 : }
184 0 : mObserver = nsnull;
185 0 : mHandler = nsnull;
186 0 : }
187 :
188 0 : void nsXMLEventsListener::SetIncomplete()
189 : {
190 0 : Unregister();
191 0 : mManager->AddXMLEventsContent(mElement);
192 0 : mElement = nsnull;
193 0 : }
194 :
195 0 : bool nsXMLEventsListener::ObserverEquals(nsIContent * aTarget)
196 : {
197 0 : return aTarget == mObserver;
198 : }
199 :
200 0 : bool nsXMLEventsListener::HandlerEquals(nsIContent * aTarget)
201 : {
202 0 : return aTarget == mHandler;
203 : }
204 :
205 0 : NS_IMPL_ISUPPORTS1(nsXMLEventsListener, nsIDOMEventListener)
206 : NS_IMETHODIMP
207 0 : nsXMLEventsListener::HandleEvent(nsIDOMEvent* aEvent)
208 : {
209 0 : if (!aEvent)
210 0 : return NS_ERROR_INVALID_ARG;
211 0 : bool targetMatched = true;
212 0 : nsCOMPtr<nsIDOMEvent> event(aEvent);
213 0 : if (mTarget) {
214 0 : targetMatched = false;
215 0 : nsCOMPtr<nsIDOMEventTarget> target;
216 0 : aEvent->GetTarget(getter_AddRefs(target));
217 0 : nsCOMPtr<nsIContent> targetEl(do_QueryInterface(target));
218 0 : if (targetEl && targetEl->GetID() == mTarget)
219 0 : targetMatched = true;
220 : }
221 0 : if (!targetMatched)
222 0 : return NS_OK;
223 0 : nsCOMPtr<nsIDOMEventListener> handler(do_QueryInterface(mHandler));
224 0 : if (handler) {
225 0 : nsresult rv = handler->HandleEvent(event);
226 0 : if (NS_SUCCEEDED(rv)) {
227 0 : if (mStopPropagation)
228 0 : event->StopPropagation();
229 0 : if (mCancelDefault)
230 0 : event->PreventDefault();
231 : }
232 0 : return rv;
233 : }
234 0 : return NS_OK;
235 : }
236 :
237 :
238 : //XMLEventsManager / DocumentObserver
239 :
240 0 : static PLDHashOperator EnumAndUnregisterListener(nsISupports * aContent,
241 : nsCOMPtr<nsXMLEventsListener> & aListener,
242 : void * aData)
243 : {
244 0 : if (aListener)
245 0 : aListener->Unregister();
246 0 : return PL_DHASH_NEXT;
247 : }
248 :
249 0 : static PLDHashOperator EnumAndSetIncomplete(nsISupports * aContent,
250 : nsCOMPtr<nsXMLEventsListener> & aListener,
251 : void * aData)
252 : {
253 0 : if (aListener && aData) {
254 0 : nsCOMPtr<nsIContent> content = static_cast<nsIContent *>(aData);
255 0 : if (content) {
256 0 : if (aListener->ObserverEquals(content) || aListener->HandlerEquals(content)) {
257 0 : aListener->SetIncomplete();
258 0 : return PL_DHASH_REMOVE;
259 : }
260 : }
261 : }
262 0 : return PL_DHASH_NEXT;
263 : }
264 :
265 0 : nsXMLEventsManager::nsXMLEventsManager()
266 : {
267 0 : mListeners.Init();
268 0 : }
269 0 : nsXMLEventsManager::~nsXMLEventsManager()
270 : {
271 0 : }
272 :
273 0 : NS_IMPL_ISUPPORTS2(nsXMLEventsManager, nsIDocumentObserver, nsIMutationObserver)
274 :
275 0 : void nsXMLEventsManager::AddXMLEventsContent(nsIContent * aContent)
276 : {
277 0 : mIncomplete.RemoveObject(aContent);
278 0 : mIncomplete.AppendObject(aContent);
279 0 : }
280 :
281 0 : void nsXMLEventsManager::RemoveXMLEventsContent(nsIContent * aContent)
282 : {
283 0 : mIncomplete.RemoveObject(aContent);
284 0 : }
285 :
286 0 : void nsXMLEventsManager::AddListener(nsIContent * aContent,
287 : nsXMLEventsListener * aListener)
288 : {
289 0 : mListeners.Put(aContent, aListener);
290 0 : }
291 :
292 0 : bool nsXMLEventsManager::RemoveListener(nsIContent * aContent)
293 : {
294 0 : nsCOMPtr<nsXMLEventsListener> listener;
295 0 : mListeners.Get(aContent, getter_AddRefs(listener));
296 0 : if (listener) {
297 0 : listener->Unregister();
298 0 : mListeners.Remove(aContent);
299 0 : return true;
300 : }
301 0 : return false;
302 : }
303 :
304 0 : void nsXMLEventsManager::AddListeners(nsIDocument* aDocument)
305 : {
306 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
307 :
308 : nsIContent *cur;
309 0 : for (int i = 0; i < mIncomplete.Count(); ++i) {
310 0 : cur = mIncomplete[i];
311 : //If this succeeds, the object will be removed from mIncomplete
312 0 : if (nsXMLEventsListener::InitXMLEventsListener(aDocument, this, cur))
313 0 : --i;
314 : }
315 0 : }
316 :
317 : void
318 0 : nsXMLEventsManager::NodeWillBeDestroyed(const nsINode* aNode)
319 : {
320 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
321 0 : mIncomplete.Clear();
322 0 : mListeners.Enumerate(EnumAndUnregisterListener, this);
323 0 : mListeners.Clear();
324 0 : }
325 :
326 : void
327 0 : nsXMLEventsManager::EndLoad(nsIDocument* aDocument)
328 : {
329 0 : AddListeners(aDocument);
330 0 : }
331 :
332 : void
333 0 : nsXMLEventsManager::AttributeChanged(nsIDocument* aDocument,
334 : Element* aElement,
335 : PRInt32 aNameSpaceID,
336 : nsIAtom* aAttribute,
337 : PRInt32 aModType)
338 : {
339 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
340 :
341 0 : if (aNameSpaceID == kNameSpaceID_XMLEvents &&
342 : (aAttribute == nsGkAtoms::event ||
343 : aAttribute == nsGkAtoms::handler ||
344 : aAttribute == nsGkAtoms::target ||
345 : aAttribute == nsGkAtoms::observer ||
346 : aAttribute == nsGkAtoms::phase ||
347 : aAttribute == nsGkAtoms::propagate)) {
348 0 : RemoveListener(aElement);
349 0 : AddXMLEventsContent(aElement);
350 0 : nsXMLEventsListener::InitXMLEventsListener(aDocument, this, aElement);
351 : }
352 : else {
353 0 : if (aElement->NodeInfo()->Equals(nsGkAtoms::listener,
354 0 : kNameSpaceID_XMLEvents)) {
355 0 : RemoveListener(aElement);
356 0 : AddXMLEventsContent(aElement);
357 0 : nsXMLEventsListener::InitXMLEventsListener(aDocument, this, aElement);
358 : }
359 0 : else if (aElement->GetIDAttributeName() == aAttribute) {
360 0 : if (aModType == nsIDOMMutationEvent::REMOVAL)
361 0 : mListeners.Enumerate(EnumAndSetIncomplete, aElement);
362 0 : else if (aModType == nsIDOMMutationEvent::MODIFICATION) {
363 : //Remove possible listener
364 0 : mListeners.Enumerate(EnumAndSetIncomplete, aElement);
365 : //Add new listeners
366 0 : AddListeners(aDocument);
367 : }
368 : else {
369 : //If we are adding the ID attribute, we must check whether we can
370 : //add new listeners
371 0 : AddListeners(aDocument);
372 : }
373 : }
374 : }
375 0 : }
376 :
377 : void
378 0 : nsXMLEventsManager::ContentAppended(nsIDocument* aDocument,
379 : nsIContent* aContainer,
380 : nsIContent* aFirstNewContent,
381 : PRInt32 aNewIndexInContainer)
382 : {
383 0 : AddListeners(aDocument);
384 0 : }
385 :
386 : void
387 0 : nsXMLEventsManager::ContentInserted(nsIDocument* aDocument,
388 : nsIContent* aContainer,
389 : nsIContent* aChild,
390 : PRInt32 aIndexInContainer)
391 : {
392 0 : AddListeners(aDocument);
393 0 : }
394 :
395 : void
396 0 : nsXMLEventsManager::ContentRemoved(nsIDocument* aDocument,
397 : nsIContent* aContainer,
398 : nsIContent* aChild,
399 : PRInt32 aIndexInContainer,
400 : nsIContent* aPreviousSibling)
401 : {
402 0 : if (!aChild || !aChild->IsElement())
403 0 : return;
404 : //Note, we can't use IDs here, the observer may not always have an ID.
405 : //And to remember: the same observer can be referenced by many
406 : //XMLEventsListeners
407 :
408 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
409 :
410 : //If the content was an XML Events observer or handler
411 0 : mListeners.Enumerate(EnumAndSetIncomplete, aChild);
412 :
413 : //If the content was an XML Events attributes container
414 0 : if (RemoveListener(aChild)) {
415 : //for aContainer.appendChild(aContainer.removeChild(aChild));
416 0 : AddXMLEventsContent(aChild);
417 : }
418 :
419 0 : PRUint32 count = aChild->GetChildCount();
420 0 : for (PRUint32 i = 0; i < count; ++i) {
421 0 : ContentRemoved(aDocument, aChild, aChild->GetChildAt(i), i, aChild->GetPreviousSibling());
422 : }
423 : }
|