Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 :
16 : class nsIURI;
17 :
18 : /**
19 : * Class to track what element is referenced by a given ID.
20 : *
21 : * To use it, call Reset() to set it up to watch a given URI. Call get()
22 : * anytime to determine the referenced element (which may be null if
23 : * the element isn't found). When the element changes, ElementChanged
24 : * will be called, so subclass this class if you want to receive that
25 : * notification. ElementChanged runs at safe-for-script time, i.e. outside
26 : * of the content update. Call Unlink() if you want to stop watching
27 : * for changes (get() will then return null).
28 : *
29 : * By default this is a single-shot tracker --- i.e., when ElementChanged
30 : * fires, we will automatically stop tracking. get() will continue to return
31 : * the changed-to element.
32 : * Override IsPersistent to return true if you want to keep tracking after
33 : * the first change.
34 : */
35 : class nsReferencedElement {
36 : public:
37 : typedef mozilla::dom::Element Element;
38 :
39 32 : nsReferencedElement()
40 32 : : mReferencingImage(false)
41 32 : {}
42 0 : ~nsReferencedElement() {
43 0 : Unlink();
44 0 : }
45 :
46 : /**
47 : * Find which element, if any, is referenced.
48 : */
49 69 : 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 0 : virtual void ElementChanged(Element* aFrom, Element* aTo) {
93 0 : mElement = aTo;
94 0 : }
95 :
96 : /**
97 : * Override this to convert from a single-shot notification to
98 : * a persistent notification.
99 : */
100 0 : 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 0 : virtual void Clear() { mTarget = nullptr; }
117 0 : virtual ~Notification() {}
118 : protected:
119 0 : explicit Notification(nsReferencedElement* aTarget)
120 0 : : mTarget(aTarget)
121 : {
122 0 : NS_PRECONDITION(aTarget, "Must have a target");
123 0 : }
124 : nsReferencedElement* mTarget;
125 : };
126 :
127 : class ChangeNotification : public mozilla::Runnable,
128 : public Notification
129 : {
130 : public:
131 0 : ChangeNotification(nsReferencedElement* aTarget,
132 : Element* aFrom,
133 : Element* aTo)
134 0 : : mozilla::Runnable("nsReferencedElement::ChangeNotification")
135 : , Notification(aTarget)
136 : , mFrom(aFrom)
137 0 : , mTo(aTo)
138 0 : {}
139 :
140 : NS_DECL_ISUPPORTS_INHERITED
141 0 : NS_IMETHOD Run() override {
142 0 : if (mTarget) {
143 0 : mTarget->mPendingNotification = nullptr;
144 0 : mTarget->ElementChanged(mFrom, mTo);
145 : }
146 0 : return NS_OK;
147 : }
148 0 : virtual void SetTo(Element* aTo) override { mTo = aTo; }
149 0 : virtual void Clear() override
150 : {
151 0 : Notification::Clear(); mFrom = nullptr; mTo = nullptr;
152 0 : }
153 : protected:
154 0 : virtual ~ChangeNotification() {}
155 :
156 : RefPtr<Element> mFrom;
157 : RefPtr<Element> mTo;
158 : };
159 : friend class ChangeNotification;
160 :
161 : class DocumentLoadNotification : public Notification,
162 : public nsIObserver
163 : {
164 : public:
165 0 : DocumentLoadNotification(nsReferencedElement* aTarget,
166 0 : const nsString& aRef) :
167 0 : Notification(aTarget)
168 : {
169 0 : if (!mTarget->IsPersistent()) {
170 0 : mRef = aRef;
171 : }
172 0 : }
173 :
174 : NS_DECL_ISUPPORTS
175 : NS_DECL_NSIOBSERVER
176 : private:
177 0 : virtual ~DocumentLoadNotification() {}
178 :
179 0 : virtual void SetTo(Element* aTo) override { }
180 :
181 : nsString mRef;
182 : };
183 : friend class DocumentLoadNotification;
184 :
185 : nsCOMPtr<nsIAtom> mWatchID;
186 : nsCOMPtr<nsIDocument> mWatchDocument;
187 : RefPtr<Element> mElement;
188 : RefPtr<Notification> mPendingNotification;
189 : bool mReferencingImage;
190 : };
191 :
192 : inline void
193 0 : ImplCycleCollectionUnlink(nsReferencedElement& aField)
194 : {
195 0 : aField.Unlink();
196 0 : }
197 :
198 : inline void
199 0 : ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
200 : nsReferencedElement& aField,
201 : const char* aName,
202 : uint32_t aFlags = 0)
203 : {
204 0 : aField.Traverse(&aCallback);
205 0 : }
206 :
207 : #endif /*NSREFERENCEDELEMENT_H_*/
|