1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/public/nsReferencedElement.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,186 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=78: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef NSREFERENCEDELEMENT_H_ 1.11 +#define NSREFERENCEDELEMENT_H_ 1.12 + 1.13 +#include "mozilla/Attributes.h" 1.14 +#include "mozilla/dom/Element.h" 1.15 +#include "nsIAtom.h" 1.16 +#include "nsIDocument.h" 1.17 +#include "nsThreadUtils.h" 1.18 +#include "nsAutoPtr.h" 1.19 + 1.20 +class nsIURI; 1.21 +class nsCycleCollectionCallback; 1.22 + 1.23 +/** 1.24 + * Class to track what element is referenced by a given ID. 1.25 + * 1.26 + * To use it, call Reset() to set it up to watch a given URI. Call get() 1.27 + * anytime to determine the referenced element (which may be null if 1.28 + * the element isn't found). When the element changes, ElementChanged 1.29 + * will be called, so subclass this class if you want to receive that 1.30 + * notification. ElementChanged runs at safe-for-script time, i.e. outside 1.31 + * of the content update. Call Unlink() if you want to stop watching 1.32 + * for changes (get() will then return null). 1.33 + * 1.34 + * By default this is a single-shot tracker --- i.e., when ElementChanged 1.35 + * fires, we will automatically stop tracking. get() will continue to return 1.36 + * the changed-to element. 1.37 + * Override IsPersistent to return true if you want to keep tracking after 1.38 + * the first change. 1.39 + */ 1.40 +class nsReferencedElement { 1.41 +public: 1.42 + typedef mozilla::dom::Element Element; 1.43 + 1.44 + nsReferencedElement() {} 1.45 + ~nsReferencedElement() { 1.46 + Unlink(); 1.47 + } 1.48 + 1.49 + /** 1.50 + * Find which element, if any, is referenced. 1.51 + */ 1.52 + Element* get() { return mElement; } 1.53 + 1.54 + /** 1.55 + * Set up the reference. This can be called multiple times to 1.56 + * change which reference is being tracked, but these changes 1.57 + * do not trigger ElementChanged. 1.58 + * @param aFrom the source element for context 1.59 + * @param aURI the URI containing a hash-reference to the element 1.60 + * @param aWatch if false, then we do not set up the notifications to track 1.61 + * changes, so ElementChanged won't fire and get() will always return the same 1.62 + * value, the current element for the ID. 1.63 + * @param aReferenceImage whether the ID references image elements which are 1.64 + * subject to the document's mozSetImageElement overriding mechanism. 1.65 + */ 1.66 + void Reset(nsIContent* aFrom, nsIURI* aURI, bool aWatch = true, 1.67 + bool aReferenceImage = false); 1.68 + 1.69 + /** 1.70 + * A variation on Reset() to set up a reference that consists of the ID of 1.71 + * an element in the same document as aFrom. 1.72 + * @param aFrom the source element for context 1.73 + * @param aID the ID of the element 1.74 + * @param aWatch if false, then we do not set up the notifications to track 1.75 + * changes, so ElementChanged won't fire and get() will always return the same 1.76 + * value, the current element for the ID. 1.77 + */ 1.78 + void ResetWithID(nsIContent* aFrom, const nsString& aID, 1.79 + bool aWatch = true); 1.80 + 1.81 + /** 1.82 + * Clears the reference. ElementChanged is not triggered. get() will return 1.83 + * null. 1.84 + */ 1.85 + void Unlink(); 1.86 + 1.87 + void Traverse(nsCycleCollectionTraversalCallback* aCB); 1.88 + 1.89 +protected: 1.90 + /** 1.91 + * Override this to be notified of element changes. Don't forget 1.92 + * to call this superclass method to change mElement. This is called 1.93 + * at script-runnable time. 1.94 + */ 1.95 + virtual void ElementChanged(Element* aFrom, Element* aTo) { 1.96 + mElement = aTo; 1.97 + } 1.98 + 1.99 + /** 1.100 + * Override this to convert from a single-shot notification to 1.101 + * a persistent notification. 1.102 + */ 1.103 + virtual bool IsPersistent() { return false; } 1.104 + 1.105 + /** 1.106 + * Set ourselves up with our new document. Note that aDocument might be 1.107 + * null. Either aWatch must be false or aRef must be empty. 1.108 + */ 1.109 + void HaveNewDocument(nsIDocument* aDocument, bool aWatch, 1.110 + const nsString& aRef); 1.111 + 1.112 +private: 1.113 + static bool Observe(Element* aOldElement, 1.114 + Element* aNewElement, void* aData); 1.115 + 1.116 + class Notification : public nsISupports { 1.117 + public: 1.118 + virtual void SetTo(Element* aTo) = 0; 1.119 + virtual void Clear() { mTarget = nullptr; } 1.120 + virtual ~Notification() {} 1.121 + protected: 1.122 + Notification(nsReferencedElement* aTarget) 1.123 + : mTarget(aTarget) 1.124 + { 1.125 + NS_PRECONDITION(aTarget, "Must have a target"); 1.126 + } 1.127 + nsReferencedElement* mTarget; 1.128 + }; 1.129 + 1.130 + class ChangeNotification : public nsRunnable, 1.131 + public Notification 1.132 + { 1.133 + public: 1.134 + ChangeNotification(nsReferencedElement* aTarget, 1.135 + Element* aFrom, Element* aTo) 1.136 + : Notification(aTarget), mFrom(aFrom), mTo(aTo) 1.137 + {} 1.138 + virtual ~ChangeNotification() {} 1.139 + 1.140 + NS_DECL_ISUPPORTS_INHERITED 1.141 + NS_IMETHOD Run() MOZ_OVERRIDE { 1.142 + if (mTarget) { 1.143 + mTarget->mPendingNotification = nullptr; 1.144 + mTarget->ElementChanged(mFrom, mTo); 1.145 + } 1.146 + return NS_OK; 1.147 + } 1.148 + virtual void SetTo(Element* aTo) { mTo = aTo; } 1.149 + virtual void Clear() 1.150 + { 1.151 + Notification::Clear(); mFrom = nullptr; mTo = nullptr; 1.152 + } 1.153 + protected: 1.154 + nsRefPtr<Element> mFrom; 1.155 + nsRefPtr<Element> mTo; 1.156 + }; 1.157 + friend class ChangeNotification; 1.158 + 1.159 + class DocumentLoadNotification : public Notification, 1.160 + public nsIObserver 1.161 + { 1.162 + public: 1.163 + DocumentLoadNotification(nsReferencedElement* aTarget, 1.164 + const nsString& aRef) : 1.165 + Notification(aTarget) 1.166 + { 1.167 + if (!mTarget->IsPersistent()) { 1.168 + mRef = aRef; 1.169 + } 1.170 + } 1.171 + virtual ~DocumentLoadNotification() {} 1.172 + 1.173 + NS_DECL_ISUPPORTS 1.174 + NS_DECL_NSIOBSERVER 1.175 + private: 1.176 + virtual void SetTo(Element* aTo) { } 1.177 + 1.178 + nsString mRef; 1.179 + }; 1.180 + friend class DocumentLoadNotification; 1.181 + 1.182 + nsCOMPtr<nsIAtom> mWatchID; 1.183 + nsCOMPtr<nsIDocument> mWatchDocument; 1.184 + nsRefPtr<Element> mElement; 1.185 + nsRefPtr<Notification> mPendingNotification; 1.186 + bool mReferencingImage; 1.187 +}; 1.188 + 1.189 +#endif /*NSREFERENCEDELEMENT_H_*/