content/base/public/nsReferencedElement.h

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:3a676fce571e
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 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef NSREFERENCEDELEMENT_H_
8 #define NSREFERENCEDELEMENT_H_
9
10 #include "mozilla/Attributes.h"
11 #include "mozilla/dom/Element.h"
12 #include "nsIAtom.h"
13 #include "nsIDocument.h"
14 #include "nsThreadUtils.h"
15 #include "nsAutoPtr.h"
16
17 class nsIURI;
18 class nsCycleCollectionCallback;
19
20 /**
21 * Class to track what element is referenced by a given ID.
22 *
23 * To use it, call Reset() to set it up to watch a given URI. Call get()
24 * anytime to determine the referenced element (which may be null if
25 * the element isn't found). When the element changes, ElementChanged
26 * will be called, so subclass this class if you want to receive that
27 * notification. ElementChanged runs at safe-for-script time, i.e. outside
28 * of the content update. Call Unlink() if you want to stop watching
29 * for changes (get() will then return null).
30 *
31 * By default this is a single-shot tracker --- i.e., when ElementChanged
32 * fires, we will automatically stop tracking. get() will continue to return
33 * the changed-to element.
34 * Override IsPersistent to return true if you want to keep tracking after
35 * the first change.
36 */
37 class nsReferencedElement {
38 public:
39 typedef mozilla::dom::Element Element;
40
41 nsReferencedElement() {}
42 ~nsReferencedElement() {
43 Unlink();
44 }
45
46 /**
47 * Find which element, if any, is referenced.
48 */
49 Element* get() { return mElement; }
50
51 /**
52 * Set up the reference. This can be called multiple times to
53 * change which reference is being tracked, but these changes
54 * do not trigger ElementChanged.
55 * @param aFrom the source element for context
56 * @param aURI the URI containing a hash-reference to the element
57 * @param aWatch if false, then we do not set up the notifications to track
58 * changes, so ElementChanged won't fire and get() will always return the same
59 * value, the current element for the ID.
60 * @param aReferenceImage whether the ID references image elements which are
61 * subject to the document's mozSetImageElement overriding mechanism.
62 */
63 void Reset(nsIContent* aFrom, nsIURI* aURI, bool aWatch = true,
64 bool aReferenceImage = false);
65
66 /**
67 * A variation on Reset() to set up a reference that consists of the ID of
68 * an element in the same document as aFrom.
69 * @param aFrom the source element for context
70 * @param aID the ID of the element
71 * @param aWatch if false, then we do not set up the notifications to track
72 * changes, so ElementChanged won't fire and get() will always return the same
73 * value, the current element for the ID.
74 */
75 void ResetWithID(nsIContent* aFrom, const nsString& aID,
76 bool aWatch = true);
77
78 /**
79 * Clears the reference. ElementChanged is not triggered. get() will return
80 * null.
81 */
82 void Unlink();
83
84 void Traverse(nsCycleCollectionTraversalCallback* aCB);
85
86 protected:
87 /**
88 * Override this to be notified of element changes. Don't forget
89 * to call this superclass method to change mElement. This is called
90 * at script-runnable time.
91 */
92 virtual void ElementChanged(Element* aFrom, Element* aTo) {
93 mElement = aTo;
94 }
95
96 /**
97 * Override this to convert from a single-shot notification to
98 * a persistent notification.
99 */
100 virtual bool IsPersistent() { return false; }
101
102 /**
103 * Set ourselves up with our new document. Note that aDocument might be
104 * null. Either aWatch must be false or aRef must be empty.
105 */
106 void HaveNewDocument(nsIDocument* aDocument, bool aWatch,
107 const nsString& aRef);
108
109 private:
110 static bool Observe(Element* aOldElement,
111 Element* aNewElement, void* aData);
112
113 class Notification : public nsISupports {
114 public:
115 virtual void SetTo(Element* aTo) = 0;
116 virtual void Clear() { mTarget = nullptr; }
117 virtual ~Notification() {}
118 protected:
119 Notification(nsReferencedElement* aTarget)
120 : mTarget(aTarget)
121 {
122 NS_PRECONDITION(aTarget, "Must have a target");
123 }
124 nsReferencedElement* mTarget;
125 };
126
127 class ChangeNotification : public nsRunnable,
128 public Notification
129 {
130 public:
131 ChangeNotification(nsReferencedElement* aTarget,
132 Element* aFrom, Element* aTo)
133 : Notification(aTarget), mFrom(aFrom), mTo(aTo)
134 {}
135 virtual ~ChangeNotification() {}
136
137 NS_DECL_ISUPPORTS_INHERITED
138 NS_IMETHOD Run() MOZ_OVERRIDE {
139 if (mTarget) {
140 mTarget->mPendingNotification = nullptr;
141 mTarget->ElementChanged(mFrom, mTo);
142 }
143 return NS_OK;
144 }
145 virtual void SetTo(Element* aTo) { mTo = aTo; }
146 virtual void Clear()
147 {
148 Notification::Clear(); mFrom = nullptr; mTo = nullptr;
149 }
150 protected:
151 nsRefPtr<Element> mFrom;
152 nsRefPtr<Element> mTo;
153 };
154 friend class ChangeNotification;
155
156 class DocumentLoadNotification : public Notification,
157 public nsIObserver
158 {
159 public:
160 DocumentLoadNotification(nsReferencedElement* aTarget,
161 const nsString& aRef) :
162 Notification(aTarget)
163 {
164 if (!mTarget->IsPersistent()) {
165 mRef = aRef;
166 }
167 }
168 virtual ~DocumentLoadNotification() {}
169
170 NS_DECL_ISUPPORTS
171 NS_DECL_NSIOBSERVER
172 private:
173 virtual void SetTo(Element* aTo) { }
174
175 nsString mRef;
176 };
177 friend class DocumentLoadNotification;
178
179 nsCOMPtr<nsIAtom> mWatchID;
180 nsCOMPtr<nsIDocument> mWatchDocument;
181 nsRefPtr<Element> mElement;
182 nsRefPtr<Notification> mPendingNotification;
183 bool mReferencingImage;
184 };
185
186 #endif /*NSREFERENCEDELEMENT_H_*/

mercurial