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