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 mozilla_DOMEventTargetHelper_h_
8 : #define mozilla_DOMEventTargetHelper_h_
9 :
10 : #include "nsCOMPtr.h"
11 : #include "nsGkAtoms.h"
12 : #include "nsCycleCollectionParticipant.h"
13 : #include "nsPIDOMWindow.h"
14 : #include "nsIScriptGlobalObject.h"
15 : #include "nsIScriptContext.h"
16 : #include "nsIWeakReferenceUtils.h"
17 : #include "MainThreadUtils.h"
18 : #include "mozilla/Attributes.h"
19 : #include "mozilla/EventListenerManager.h"
20 : #include "mozilla/dom/EventTarget.h"
21 :
22 : struct JSCompartment;
23 : class nsIDocument;
24 :
25 : namespace mozilla {
26 :
27 : class ErrorResult;
28 :
29 : #define NS_DOMEVENTTARGETHELPER_IID \
30 : { 0xa28385c6, 0x9451, 0x4d7e, \
31 : { 0xa3, 0xdd, 0xf4, 0xb6, 0x87, 0x2f, 0xa4, 0x76 } }
32 :
33 : class DOMEventTargetHelper : public dom::EventTarget
34 : {
35 : public:
36 13 : DOMEventTargetHelper()
37 13 : : mParentObject(nullptr)
38 : , mOwnerWindow(nullptr)
39 : , mHasOrHasHadOwnerWindow(false)
40 13 : , mIsKeptAlive(false)
41 : {
42 13 : }
43 9 : explicit DOMEventTargetHelper(nsPIDOMWindowInner* aWindow)
44 9 : : mParentObject(nullptr)
45 : , mOwnerWindow(nullptr)
46 : , mHasOrHasHadOwnerWindow(false)
47 9 : , mIsKeptAlive(false)
48 : {
49 9 : BindToOwner(aWindow);
50 9 : }
51 2 : explicit DOMEventTargetHelper(nsIGlobalObject* aGlobalObject)
52 2 : : mParentObject(nullptr)
53 : , mOwnerWindow(nullptr)
54 : , mHasOrHasHadOwnerWindow(false)
55 2 : , mIsKeptAlive(false)
56 : {
57 2 : BindToOwner(aGlobalObject);
58 2 : }
59 3 : explicit DOMEventTargetHelper(DOMEventTargetHelper* aOther)
60 3 : : mParentObject(nullptr)
61 : , mOwnerWindow(nullptr)
62 : , mHasOrHasHadOwnerWindow(false)
63 3 : , mIsKeptAlive(false)
64 : {
65 3 : BindToOwner(aOther);
66 3 : }
67 :
68 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
69 6638 : NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(DOMEventTargetHelper)
70 :
71 : NS_DECL_NSIDOMEVENTTARGET
72 :
73 : virtual EventListenerManager* GetExistingListenerManager() const override;
74 : virtual EventListenerManager* GetOrCreateListenerManager() override;
75 :
76 : using dom::EventTarget::RemoveEventListener;
77 : virtual void AddEventListener(const nsAString& aType,
78 : dom::EventListener* aListener,
79 : const dom::AddEventListenerOptionsOrBoolean& aOptions,
80 : const dom::Nullable<bool>& aWantsUntrusted,
81 : ErrorResult& aRv) override;
82 :
83 : NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOMEVENTTARGETHELPER_IID)
84 :
85 0 : void GetParentObject(nsIScriptGlobalObject **aParentObject)
86 : {
87 0 : if (mParentObject) {
88 0 : CallQueryInterface(mParentObject, aParentObject);
89 : } else {
90 0 : *aParentObject = nullptr;
91 : }
92 0 : }
93 :
94 0 : static DOMEventTargetHelper* FromSupports(nsISupports* aSupports)
95 : {
96 0 : dom::EventTarget* target = static_cast<dom::EventTarget*>(aSupports);
97 : #ifdef DEBUG
98 : {
99 0 : nsCOMPtr<dom::EventTarget> target_qi = do_QueryInterface(aSupports);
100 :
101 : // If this assertion fires the QI implementation for the object in
102 : // question doesn't use the EventTarget pointer as the
103 : // nsISupports pointer. That must be fixed, or we'll crash...
104 0 : NS_ASSERTION(target_qi == target, "Uh, fix QI!");
105 : }
106 : #endif
107 :
108 0 : return static_cast<DOMEventTargetHelper*>(target);
109 : }
110 :
111 14 : bool HasListenersFor(const nsAString& aType)
112 : {
113 14 : return mListenerManager && mListenerManager->HasListenersFor(aType);
114 : }
115 :
116 6 : bool HasListenersFor(nsIAtom* aTypeWithOn)
117 : {
118 6 : return mListenerManager && mListenerManager->HasListenersFor(aTypeWithOn);
119 : }
120 :
121 : nsresult SetEventHandler(nsIAtom* aType,
122 : JSContext* aCx,
123 : const JS::Value& aValue);
124 : using dom::EventTarget::SetEventHandler;
125 : void GetEventHandler(nsIAtom* aType,
126 : JSContext* aCx,
127 : JS::Value* aValue);
128 : using dom::EventTarget::GetEventHandler;
129 0 : virtual nsPIDOMWindowOuter* GetOwnerGlobalForBindings() override
130 : {
131 0 : return nsPIDOMWindowOuter::GetFromCurrentInner(GetOwner());
132 : }
133 :
134 296 : nsresult CheckInnerWindowCorrectness() const
135 : {
136 296 : NS_ENSURE_STATE(!mHasOrHasHadOwnerWindow || mOwnerWindow);
137 296 : if (mOwnerWindow && !mOwnerWindow->IsCurrentInnerWindow()) {
138 0 : return NS_ERROR_FAILURE;
139 : }
140 296 : return NS_OK;
141 : }
142 :
143 218 : nsPIDOMWindowInner* GetOwner() const { return mOwnerWindow; }
144 : // Like GetOwner, but only returns non-null if the window being returned is
145 : // current (in the "current document" sense of the HTML spec).
146 : nsPIDOMWindowInner* GetWindowIfCurrent() const;
147 : // Returns the document associated with this event target, if that document is
148 : // the current document of its browsing context. Will return null otherwise.
149 : nsIDocument* GetDocumentIfCurrent() const;
150 : void BindToOwner(nsIGlobalObject* aOwner);
151 : void BindToOwner(nsPIDOMWindowInner* aOwner);
152 : void BindToOwner(DOMEventTargetHelper* aOther);
153 : virtual void DisconnectFromOwner();
154 48 : nsIGlobalObject* GetParentObject() const
155 : {
156 48 : return GetOwnerGlobal();
157 : }
158 52 : virtual nsIGlobalObject* GetOwnerGlobal() const override
159 : {
160 104 : nsCOMPtr<nsIGlobalObject> parentObject = do_QueryReferent(mParentObject);
161 104 : return parentObject;
162 : }
163 8 : bool HasOrHasHadOwner() { return mHasOrHasHadOwnerWindow; }
164 :
165 : virtual void EventListenerAdded(nsIAtom* aType) override;
166 : virtual void EventListenerAdded(const nsAString& aType) override;
167 :
168 : virtual void EventListenerRemoved(nsIAtom* aType) override;
169 : virtual void EventListenerRemoved(const nsAString& aType) override;
170 :
171 172 : virtual void EventListenerWasAdded(const nsAString& aType,
172 : ErrorResult& aRv,
173 172 : JSCompartment* aCompartment = nullptr) {}
174 24 : virtual void EventListenerWasRemoved(const nsAString& aType,
175 : ErrorResult& aRv,
176 24 : JSCompartment* aCompartment = nullptr) {}
177 :
178 : // Dispatch a trusted, non-cancellable and non-bubbling event to |this|.
179 : nsresult DispatchTrustedEvent(const nsAString& aEventName);
180 : protected:
181 : virtual ~DOMEventTargetHelper();
182 :
183 : nsresult WantsUntrusted(bool* aRetVal);
184 :
185 : void MaybeUpdateKeepAlive();
186 : void MaybeDontKeepAlive();
187 :
188 : // If this method returns true your object is kept alive until it returns
189 : // false. You can use this method instead using
190 : // NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN macro.
191 0 : virtual bool IsCertainlyAliveForCC() const
192 : {
193 0 : return mIsKeptAlive;
194 : }
195 :
196 : RefPtr<EventListenerManager> mListenerManager;
197 : // Make |event| trusted and dispatch |aEvent| to |this|.
198 : nsresult DispatchTrustedEvent(nsIDOMEvent* aEvent);
199 :
200 0 : virtual void LastRelease() {}
201 :
202 : void KeepAliveIfHasListenersFor(const nsAString& aType);
203 : void KeepAliveIfHasListenersFor(nsIAtom* aType);
204 :
205 : void IgnoreKeepAliveIfHasListenersFor(const nsAString& aType);
206 : void IgnoreKeepAliveIfHasListenersFor(nsIAtom* aType);
207 :
208 : private:
209 : // Inner window or sandbox.
210 : nsWeakPtr mParentObject;
211 : // mParentObject pre QI-ed and cached (inner window)
212 : // (it is needed for off main thread access)
213 : // It is obtained in BindToOwner and reset in DisconnectFromOwner.
214 : nsPIDOMWindowInner* MOZ_NON_OWNING_REF mOwnerWindow;
215 : bool mHasOrHasHadOwnerWindow;
216 :
217 27 : struct {
218 : nsTArray<nsString> mStrings;
219 : nsTArray<nsCOMPtr<nsIAtom>> mAtoms;
220 : } mKeepingAliveTypes;
221 :
222 : bool mIsKeptAlive;
223 : };
224 :
225 : NS_DEFINE_STATIC_IID_ACCESSOR(DOMEventTargetHelper,
226 : NS_DOMEVENTTARGETHELPER_IID)
227 :
228 : } // namespace mozilla
229 :
230 : // XPIDL event handlers
231 : #define NS_IMPL_EVENT_HANDLER(_class, _event) \
232 : NS_IMETHODIMP _class::GetOn##_event(JSContext* aCx, \
233 : JS::MutableHandle<JS::Value> aValue) \
234 : { \
235 : GetEventHandler(nsGkAtoms::on##_event, aCx, aValue.address()); \
236 : return NS_OK; \
237 : } \
238 : NS_IMETHODIMP _class::SetOn##_event(JSContext* aCx, \
239 : JS::Handle<JS::Value> aValue) \
240 : { \
241 : return SetEventHandler(nsGkAtoms::on##_event, aCx, aValue); \
242 : }
243 :
244 : #define NS_IMPL_FORWARD_EVENT_HANDLER(_class, _event, _baseclass) \
245 : NS_IMETHODIMP _class::GetOn##_event(JSContext* aCx, \
246 : JS::MutableHandle<JS::Value> aValue) \
247 : { \
248 : return _baseclass::GetOn##_event(aCx, aValue); \
249 : } \
250 : NS_IMETHODIMP _class::SetOn##_event(JSContext* aCx, \
251 : JS::Handle<JS::Value> aValue) \
252 : { \
253 : return _baseclass::SetOn##_event(aCx, aValue); \
254 : }
255 :
256 : // WebIDL event handlers
257 : #define IMPL_EVENT_HANDLER(_event) \
258 : inline mozilla::dom::EventHandlerNonNull* GetOn##_event() \
259 : { \
260 : if (NS_IsMainThread()) { \
261 : return GetEventHandler(nsGkAtoms::on##_event, EmptyString()); \
262 : } \
263 : return GetEventHandler(nullptr, NS_LITERAL_STRING(#_event)); \
264 : } \
265 : inline void SetOn##_event(mozilla::dom::EventHandlerNonNull* aCallback) \
266 : { \
267 : if (NS_IsMainThread()) { \
268 : SetEventHandler(nsGkAtoms::on##_event, EmptyString(), aCallback); \
269 : } else { \
270 : SetEventHandler(nullptr, NS_LITERAL_STRING(#_event), aCallback); \
271 : } \
272 : }
273 :
274 : /* Use this macro to declare functions that forward the behavior of this
275 : * interface to another object.
276 : * This macro doesn't forward GetEventTargetParent because sometimes subclasses
277 : * want to override it.
278 : */
279 : #define NS_FORWARD_NSIDOMEVENTTARGET_NOGETEVENTTARGETPARENT(_to) \
280 : NS_IMETHOD AddEventListener(const nsAString & type, nsIDOMEventListener *listener, bool useCapture, bool wantsUntrusted, uint8_t _argc) { \
281 : return _to AddEventListener(type, listener, useCapture, wantsUntrusted, _argc); \
282 : } \
283 : NS_IMETHOD AddSystemEventListener(const nsAString & type, nsIDOMEventListener *listener, bool aUseCapture, bool aWantsUntrusted, uint8_t _argc) { \
284 : return _to AddSystemEventListener(type, listener, aUseCapture, aWantsUntrusted, _argc); \
285 : } \
286 : NS_IMETHOD RemoveEventListener(const nsAString & type, nsIDOMEventListener *listener, bool useCapture) { \
287 : return _to RemoveEventListener(type, listener, useCapture); \
288 : } \
289 : NS_IMETHOD RemoveSystemEventListener(const nsAString & type, nsIDOMEventListener *listener, bool aUseCapture) { \
290 : return _to RemoveSystemEventListener(type, listener, aUseCapture); \
291 : } \
292 : NS_IMETHOD DispatchEvent(nsIDOMEvent *evt, bool *_retval) { \
293 : return _to DispatchEvent(evt, _retval); \
294 : } \
295 : virtual mozilla::dom::EventTarget* GetTargetForDOMEvent() { \
296 : return _to GetTargetForDOMEvent(); \
297 : } \
298 : virtual mozilla::dom::EventTarget* GetTargetForEventTargetChain() { \
299 : return _to GetTargetForEventTargetChain(); \
300 : } \
301 : virtual nsresult WillHandleEvent( \
302 : mozilla::EventChainPostVisitor & aVisitor) { \
303 : return _to WillHandleEvent(aVisitor); \
304 : } \
305 : virtual nsresult PostHandleEvent( \
306 : mozilla::EventChainPostVisitor & aVisitor) { \
307 : return _to PostHandleEvent(aVisitor); \
308 : } \
309 : virtual nsresult DispatchDOMEvent(mozilla::WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent, nsPresContext* aPresContext, nsEventStatus* aEventStatus) { \
310 : return _to DispatchDOMEvent(aEvent, aDOMEvent, aPresContext, aEventStatus); \
311 : } \
312 : virtual mozilla::EventListenerManager* GetOrCreateListenerManager() { \
313 : return _to GetOrCreateListenerManager(); \
314 : } \
315 : virtual mozilla::EventListenerManager* GetExistingListenerManager() const { \
316 : return _to GetExistingListenerManager(); \
317 : } \
318 : virtual nsIScriptContext * GetContextForEventHandlers(nsresult *aRv) { \
319 : return _to GetContextForEventHandlers(aRv); \
320 : }
321 :
322 : #define NS_REALLY_FORWARD_NSIDOMEVENTTARGET(_class) \
323 : using _class::AddEventListener; \
324 : using _class::RemoveEventListener; \
325 : NS_FORWARD_NSIDOMEVENTTARGET(_class::) \
326 : virtual mozilla::EventListenerManager* \
327 : GetOrCreateListenerManager() override { \
328 : return _class::GetOrCreateListenerManager(); \
329 : } \
330 : virtual mozilla::EventListenerManager* \
331 : GetExistingListenerManager() const override { \
332 : return _class::GetExistingListenerManager(); \
333 : }
334 :
335 : #endif // mozilla_DOMEventTargetHelper_h_
|