|
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_*/ |