1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=78: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is Mozilla.org.
19 : * Portions created by the Initial Developer are Copyright (C) 2008
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Robert O'Callahan <robert@ocallahan.org>
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 : #ifndef NSREFERENCEDELEMENT_H_
40 : #define NSREFERENCEDELEMENT_H_
41 :
42 : #include "mozilla/dom/Element.h"
43 : #include "nsIAtom.h"
44 : #include "nsIDocument.h"
45 : #include "nsThreadUtils.h"
46 : #include "nsAutoPtr.h"
47 :
48 : class nsIURI;
49 : class nsCycleCollectionCallback;
50 :
51 : /**
52 : * Class to track what element is referenced by a given ID.
53 : *
54 : * To use it, call Reset() to set it up to watch a given URI. Call get()
55 : * anytime to determine the referenced element (which may be null if
56 : * the element isn't found). When the element changes, ElementChanged
57 : * will be called, so subclass this class if you want to receive that
58 : * notification. ElementChanged runs at safe-for-script time, i.e. outside
59 : * of the content update. Call Unlink() if you want to stop watching
60 : * for changes (get() will then return null).
61 : *
62 : * By default this is a single-shot tracker --- i.e., when ElementChanged
63 : * fires, we will automatically stop tracking. get() will continue to return
64 : * the changed-to element.
65 : * Override IsPersistent to return true if you want to keep tracking after
66 : * the first change.
67 : */
68 : class nsReferencedElement {
69 : public:
70 : typedef mozilla::dom::Element Element;
71 :
72 0 : nsReferencedElement() {}
73 0 : ~nsReferencedElement() {
74 0 : Unlink();
75 0 : }
76 :
77 : /**
78 : * Find which element, if any, is referenced.
79 : */
80 0 : Element* get() { return mElement; }
81 :
82 : /**
83 : * Set up the reference. This can be called multiple times to
84 : * change which reference is being tracked, but these changes
85 : * do not trigger ElementChanged.
86 : * @param aFrom the source element for context
87 : * @param aURI the URI containing a hash-reference to the element
88 : * @param aWatch if false, then we do not set up the notifications to track
89 : * changes, so ElementChanged won't fire and get() will always return the same
90 : * value, the current element for the ID.
91 : * @param aReferenceImage whether the ID references image elements which are
92 : * subject to the document's mozSetImageElement overriding mechanism.
93 : */
94 : void Reset(nsIContent* aFrom, nsIURI* aURI, bool aWatch = true,
95 : bool aReferenceImage = false);
96 :
97 : /**
98 : * A variation on Reset() to set up a reference that consists of the ID of
99 : * an element in the same document as aFrom.
100 : * @param aFrom the source element for context
101 : * @param aID the ID of the element
102 : * @param aWatch if false, then we do not set up the notifications to track
103 : * changes, so ElementChanged won't fire and get() will always return the same
104 : * value, the current element for the ID.
105 : */
106 : void ResetWithID(nsIContent* aFrom, const nsString& aID,
107 : bool aWatch = true);
108 :
109 : /**
110 : * Clears the reference. ElementChanged is not triggered. get() will return
111 : * null.
112 : */
113 : void Unlink();
114 :
115 : void Traverse(nsCycleCollectionTraversalCallback* aCB);
116 :
117 : protected:
118 : /**
119 : * Override this to be notified of element changes. Don't forget
120 : * to call this superclass method to change mElement. This is called
121 : * at script-runnable time.
122 : */
123 0 : virtual void ElementChanged(Element* aFrom, Element* aTo) {
124 0 : mElement = aTo;
125 0 : }
126 :
127 : /**
128 : * Override this to convert from a single-shot notification to
129 : * a persistent notification.
130 : */
131 0 : virtual bool IsPersistent() { return false; }
132 :
133 : /**
134 : * Set ourselves up with our new document. Note that aDocument might be
135 : * null. Either aWatch must be false or aRef must be empty.
136 : */
137 : void HaveNewDocument(nsIDocument* aDocument, bool aWatch,
138 : const nsString& aRef);
139 :
140 : private:
141 : static bool Observe(Element* aOldElement,
142 : Element* aNewElement, void* aData);
143 :
144 : class Notification : public nsISupports {
145 : public:
146 : virtual void SetTo(Element* aTo) = 0;
147 0 : virtual void Clear() { mTarget = nsnull; }
148 0 : virtual ~Notification() {}
149 : protected:
150 0 : Notification(nsReferencedElement* aTarget)
151 0 : : mTarget(aTarget)
152 : {
153 0 : NS_PRECONDITION(aTarget, "Must have a target");
154 0 : }
155 : nsReferencedElement* mTarget;
156 : };
157 :
158 : class ChangeNotification : public nsRunnable,
159 : public Notification
160 : {
161 : public:
162 0 : ChangeNotification(nsReferencedElement* aTarget,
163 : Element* aFrom, Element* aTo)
164 0 : : Notification(aTarget), mFrom(aFrom), mTo(aTo)
165 0 : {}
166 0 : virtual ~ChangeNotification() {}
167 :
168 : NS_DECL_ISUPPORTS_INHERITED
169 0 : NS_IMETHOD Run() {
170 0 : if (mTarget) {
171 0 : mTarget->mPendingNotification = nsnull;
172 0 : mTarget->ElementChanged(mFrom, mTo);
173 : }
174 0 : return NS_OK;
175 : }
176 0 : virtual void SetTo(Element* aTo) { mTo = aTo; }
177 0 : virtual void Clear()
178 : {
179 0 : Notification::Clear(); mFrom = nsnull; mTo = nsnull;
180 0 : }
181 : protected:
182 : nsRefPtr<Element> mFrom;
183 : nsRefPtr<Element> mTo;
184 : };
185 : friend class ChangeNotification;
186 :
187 : class DocumentLoadNotification : public Notification,
188 : public nsIObserver
189 : {
190 : public:
191 0 : DocumentLoadNotification(nsReferencedElement* aTarget,
192 : const nsString& aRef) :
193 0 : Notification(aTarget)
194 : {
195 0 : if (!mTarget->IsPersistent()) {
196 0 : mRef = aRef;
197 : }
198 0 : }
199 0 : virtual ~DocumentLoadNotification() {}
200 :
201 : NS_DECL_ISUPPORTS
202 : NS_DECL_NSIOBSERVER
203 : private:
204 0 : virtual void SetTo(Element* aTo) { }
205 :
206 : nsString mRef;
207 : };
208 : friend class DocumentLoadNotification;
209 :
210 : nsCOMPtr<nsIAtom> mWatchID;
211 : nsCOMPtr<nsIDocument> mWatchDocument;
212 : nsRefPtr<Element> mElement;
213 : nsRefPtr<Notification> mPendingNotification;
214 : bool mReferencingImage;
215 : };
216 :
217 : #endif /*NSREFERENCEDELEMENT_H_*/
|