Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/dom/IDTracker.h
Line
Count
Source (jump to first uncovered line)
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 mozilla_dom_IDTracker_h_
8
#define mozilla_dom_IDTracker_h_
9
10
#include "mozilla/Attributes.h"
11
#include "mozilla/dom/Element.h"
12
#include "mozilla/dom/ShadowRoot.h"
13
#include "nsAtom.h"
14
#include "nsIDocument.h"
15
#include "nsThreadUtils.h"
16
17
class nsIURI;
18
19
namespace mozilla {
20
namespace dom {
21
22
/**
23
 * Class to track what element is referenced by a given ID.
24
 *
25
 * To use it, call Reset() to set it up to watch a given URI. Call get()
26
 * anytime to determine the referenced element (which may be null if
27
 * the element isn't found). When the element changes, ElementChanged
28
 * will be called, so subclass this class if you want to receive that
29
 * notification. ElementChanged runs at safe-for-script time, i.e. outside
30
 * of the content update. Call Unlink() if you want to stop watching
31
 * for changes (get() will then return null).
32
 *
33
 * By default this is a single-shot tracker --- i.e., when ElementChanged
34
 * fires, we will automatically stop tracking. get() will continue to return
35
 * the changed-to element.
36
 * Override IsPersistent to return true if you want to keep tracking after
37
 * the first change.
38
 */
39
class IDTracker {
40
public:
41
  typedef mozilla::dom::Element Element;
42
43
0
  IDTracker() = default;
44
45
  ~IDTracker()
46
0
  {
47
0
    Unlink();
48
0
  }
49
50
  /**
51
   * Find which element, if any, is referenced.
52
   */
53
0
  Element* get() { return mElement; }
54
55
  /**
56
   * Set up the reference. This can be called multiple times to
57
   * change which reference is being tracked, but these changes
58
   * do not trigger ElementChanged.
59
   * @param aFrom the source element for context
60
   * @param aURI the URI containing a hash-reference to the element
61
   * @param aReferrer the referrer URI for loading external resource
62
   * @param aReferrerPolicy the referrer policy for loading external resource
63
   * @param aWatch if false, then we do not set up the notifications to track
64
   * changes, so ElementChanged won't fire and get() will always return the same
65
   * value, the current element for the ID.
66
   * @param aReferenceImage whether the ID references image elements which are
67
   * subject to the document's mozSetImageElement overriding mechanism.
68
   */
69
  void Reset(nsIContent* aFrom, nsIURI* aURI, nsIURI* aReferrer,
70
             uint32_t aReferrerPolicy, bool aWatch = true,
71
             bool aReferenceImage = false);
72
73
  /**
74
   * A variation on Reset() to set up a reference that consists of the ID of
75
   * an element in the same document as aFrom.
76
   * @param aFrom the source element for context
77
   * @param aID the ID of the element
78
   * @param aWatch if false, then we do not set up the notifications to track
79
   * changes, so ElementChanged won't fire and get() will always return the same
80
   * value, the current element for the ID.
81
   */
82
  void ResetWithID(Element& aFrom, nsAtom* aID, bool aWatch = true);
83
84
  /**
85
   * Clears the reference. ElementChanged is not triggered. get() will return
86
   * null.
87
   */
88
  void Unlink();
89
90
  void Traverse(nsCycleCollectionTraversalCallback* aCB);
91
92
protected:
93
  /**
94
   * Override this to be notified of element changes. Don't forget
95
   * to call this superclass method to change mElement. This is called
96
   * at script-runnable time.
97
   */
98
  virtual void ElementChanged(Element* aFrom, Element* aTo)
99
0
  {
100
0
    mElement = aTo;
101
0
  }
102
103
  /**
104
   * Override this to convert from a single-shot notification to
105
   * a persistent notification.
106
   */
107
0
  virtual bool IsPersistent() { return false; }
108
109
  /**
110
   * Set ourselves up with our new document.  Note that aDocument might be
111
   * null.  Either aWatch must be false or aRef must be empty.
112
   */
113
  void HaveNewDocumentOrShadowRoot(DocumentOrShadowRoot*,
114
                                   bool aWatch,
115
                                   const nsString& aRef);
116
117
private:
118
  static bool Observe(Element* aOldElement,
119
                        Element* aNewElement, void* aData);
120
121
  class Notification : public nsISupports {
122
  public:
123
    virtual void SetTo(Element* aTo) = 0;
124
    virtual void Clear() { mTarget = nullptr; }
125
    virtual ~Notification() {}
126
  protected:
127
    explicit Notification(IDTracker* aTarget)
128
      : mTarget(aTarget)
129
    {
130
      MOZ_ASSERT(aTarget, "Must have a target");
131
    }
132
    IDTracker* mTarget;
133
  };
134
135
  class ChangeNotification : public mozilla::Runnable,
136
                             public Notification
137
  {
138
  public:
139
    ChangeNotification(IDTracker* aTarget,
140
                       Element* aFrom,
141
                       Element* aTo)
142
      : mozilla::Runnable("IDTracker::ChangeNotification")
143
      , Notification(aTarget)
144
      , mFrom(aFrom)
145
      , mTo(aTo)
146
    {}
147
148
    // We need to actually declare all of nsISupports, because
149
    // Notification inherits from it but doesn't declare it.
150
    NS_DECL_ISUPPORTS_INHERITED
151
    NS_IMETHOD Run() override {
152
      if (mTarget) {
153
        mTarget->mPendingNotification = nullptr;
154
        mTarget->ElementChanged(mFrom, mTo);
155
      }
156
      return NS_OK;
157
    }
158
    virtual void SetTo(Element* aTo) override { mTo = aTo; }
159
    virtual void Clear() override
160
    {
161
      Notification::Clear(); mFrom = nullptr; mTo = nullptr;
162
    }
163
  protected:
164
    virtual ~ChangeNotification() {}
165
166
    RefPtr<Element> mFrom;
167
    RefPtr<Element> mTo;
168
  };
169
  friend class ChangeNotification;
170
171
  class DocumentLoadNotification : public Notification,
172
                                   public nsIObserver
173
  {
174
  public:
175
    DocumentLoadNotification(IDTracker* aTarget, const nsString& aRef)
176
      : Notification(aTarget)
177
    {
178
      if (!mTarget->IsPersistent()) {
179
        mRef = aRef;
180
      }
181
    }
182
183
    NS_DECL_ISUPPORTS
184
    NS_DECL_NSIOBSERVER
185
  private:
186
    virtual ~DocumentLoadNotification() {}
187
188
    virtual void SetTo(Element* aTo) override { }
189
190
    nsString mRef;
191
  };
192
  friend class DocumentLoadNotification;
193
194
  DocumentOrShadowRoot* GetWatchDocOrShadowRoot() const
195
  {
196
    if (!mWatchDocumentOrShadowRoot) {
197
      return nullptr;
198
    }
199
    MOZ_ASSERT(mWatchDocumentOrShadowRoot->IsDocument() ||
200
               mWatchDocumentOrShadowRoot->IsShadowRoot());
201
    if (ShadowRoot* shadow = ShadowRoot::FromNode(*mWatchDocumentOrShadowRoot)) {
202
      return shadow;
203
    }
204
    return mWatchDocumentOrShadowRoot->AsDocument();
205
  }
206
207
  RefPtr<nsAtom> mWatchID;
208
  nsCOMPtr<nsINode> mWatchDocumentOrShadowRoot; // Always a `DocumentOrShadowRoot`.
209
  RefPtr<Element> mElement;
210
  RefPtr<Notification> mPendingNotification;
211
  bool mReferencingImage = false;
212
};
213
214
inline void
215
ImplCycleCollectionUnlink(IDTracker& aField)
216
0
{
217
0
  aField.Unlink();
218
0
}
219
220
inline void
221
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
222
                            IDTracker& aField,
223
                            const char* aName,
224
                            uint32_t aFlags = 0)
225
0
{
226
0
  aField.Traverse(&aCallback);
227
0
}
228
229
} // namespace dom
230
} // namespace mozilla
231
232
#endif /* mozilla_dom_IDTracker_h_ */