1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsContentUtils.h"
7 #include "nsIDocument.h"
9 #include "nsGlobalWindow.h"
10 #include "ScriptSettings.h"
11 #include "mozilla/DOMEventTargetHelper.h"
12 #include "mozilla/EventDispatcher.h"
13 #include "mozilla/EventListenerManager.h"
14 #include "mozilla/Likely.h"
20 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMEventTargetHelper
)
22 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMEventTargetHelper
)
23 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
24 NS_IMPL_CYCLE_COLLECTION_TRACE_END
26 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(DOMEventTargetHelper
)
27 if (MOZ_UNLIKELY(cb
.WantDebugInfo())) {
30 if (tmp
->mOwnerWindow
&& tmp
->mOwnerWindow
->GetExtantDoc()) {
31 tmp
->mOwnerWindow
->GetExtantDoc()->GetDocumentURI(uri
);
33 PR_snprintf(name
, sizeof(name
), "DOMEventTargetHelper %s",
34 NS_ConvertUTF16toUTF8(uri
).get());
35 cb
.DescribeRefCountedNode(tmp
->mRefCnt
.get(), name
);
37 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(DOMEventTargetHelper
, tmp
->mRefCnt
.get())
40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager
)
42 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
44 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMEventTargetHelper
)
45 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
46 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager
)
47 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
49 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(DOMEventTargetHelper
)
51 if (tmp
->mListenerManager
) {
52 tmp
->mListenerManager
->MarkForCC();
56 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
58 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(DOMEventTargetHelper
)
59 return tmp
->IsBlackAndDoesNotNeedTracing(tmp
);
60 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
62 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(DOMEventTargetHelper
)
63 return tmp
->IsBlack();
64 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
66 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMEventTargetHelper
)
67 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
68 NS_INTERFACE_MAP_ENTRY(nsISupports
)
69 NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget
)
70 NS_INTERFACE_MAP_ENTRY(dom::EventTarget
)
71 NS_INTERFACE_MAP_ENTRY(DOMEventTargetHelper
)
74 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMEventTargetHelper
)
75 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(DOMEventTargetHelper
,
78 NS_IMPL_DOMTARGET_DEFAULTS(DOMEventTargetHelper
)
80 DOMEventTargetHelper::~DOMEventTargetHelper()
82 if (nsPIDOMWindow
* owner
= GetOwner()) {
83 static_cast<nsGlobalWindow
*>(owner
)->RemoveEventTargetObject(this);
85 if (mListenerManager
) {
86 mListenerManager
->Disconnect();
92 DOMEventTargetHelper::BindToOwner(nsPIDOMWindow
* aOwner
)
94 MOZ_ASSERT_IF(aOwner
, aOwner
->IsInnerWindow());
95 nsCOMPtr
<nsIGlobalObject
> glob
= do_QueryInterface(aOwner
);
100 DOMEventTargetHelper::BindToOwner(nsIGlobalObject
* aOwner
)
104 static_cast<nsGlobalWindow
*>(mOwnerWindow
)->RemoveEventTargetObject(this);
105 mOwnerWindow
= nullptr;
107 mParentObject
= nullptr;
108 mHasOrHasHadOwnerWindow
= false;
111 mParentObject
= aOwner
;
112 // Let's cache the result of this QI for fast access and off main thread usage
113 mOwnerWindow
= nsCOMPtr
<nsPIDOMWindow
>(do_QueryInterface(aOwner
)).get();
115 MOZ_ASSERT(mOwnerWindow
->IsInnerWindow());
116 mHasOrHasHadOwnerWindow
= true;
117 static_cast<nsGlobalWindow
*>(mOwnerWindow
)->AddEventTargetObject(this);
123 DOMEventTargetHelper::BindToOwner(DOMEventTargetHelper
* aOther
)
126 static_cast<nsGlobalWindow
*>(mOwnerWindow
)->RemoveEventTargetObject(this);
127 mOwnerWindow
= nullptr;
128 mParentObject
= nullptr;
129 mHasOrHasHadOwnerWindow
= false;
132 mHasOrHasHadOwnerWindow
= aOther
->HasOrHasHadOwner();
133 if (aOther
->GetParentObject()) {
134 mParentObject
= aOther
->GetParentObject();
135 // Let's cache the result of this QI for fast access and off main thread usage
136 mOwnerWindow
= nsCOMPtr
<nsPIDOMWindow
>(do_QueryInterface(mParentObject
)).get();
138 MOZ_ASSERT(mOwnerWindow
->IsInnerWindow());
139 mHasOrHasHadOwnerWindow
= true;
140 static_cast<nsGlobalWindow
*>(mOwnerWindow
)->AddEventTargetObject(this);
147 DOMEventTargetHelper::DisconnectFromOwner()
149 mOwnerWindow
= nullptr;
150 mParentObject
= nullptr;
151 // Event listeners can't be handled anymore, so we can release them here.
152 if (mListenerManager
) {
153 mListenerManager
->Disconnect();
154 mListenerManager
= nullptr;
159 DOMEventTargetHelper::RemoveEventListener(const nsAString
& aType
,
160 nsIDOMEventListener
* aListener
,
163 EventListenerManager
* elm
= GetExistingListenerManager();
165 elm
->RemoveEventListener(aType
, aListener
, aUseCapture
);
171 NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(DOMEventTargetHelper
)
174 DOMEventTargetHelper::AddEventListener(const nsAString
& aType
,
175 nsIDOMEventListener
* aListener
,
177 bool aWantsUntrusted
,
178 uint8_t aOptionalArgc
)
180 NS_ASSERTION(!aWantsUntrusted
|| aOptionalArgc
> 1,
181 "Won't check if this is chrome, you want to set "
182 "aWantsUntrusted to false or make the aWantsUntrusted "
183 "explicit by making aOptionalArgc non-zero.");
185 if (aOptionalArgc
< 2) {
186 nsresult rv
= WantsUntrusted(&aWantsUntrusted
);
187 NS_ENSURE_SUCCESS(rv
, rv
);
190 EventListenerManager
* elm
= GetOrCreateListenerManager();
191 NS_ENSURE_STATE(elm
);
192 elm
->AddEventListener(aType
, aListener
, aUseCapture
, aWantsUntrusted
);
197 DOMEventTargetHelper::AddEventListener(const nsAString
& aType
,
198 EventListener
* aListener
,
200 const Nullable
<bool>& aWantsUntrusted
,
204 if (aWantsUntrusted
.IsNull()) {
205 nsresult rv
= WantsUntrusted(&wantsUntrusted
);
211 wantsUntrusted
= aWantsUntrusted
.Value();
214 EventListenerManager
* elm
= GetOrCreateListenerManager();
216 aRv
.Throw(NS_ERROR_UNEXPECTED
);
219 elm
->AddEventListener(aType
, aListener
, aUseCapture
, wantsUntrusted
);
223 DOMEventTargetHelper::AddSystemEventListener(const nsAString
& aType
,
224 nsIDOMEventListener
* aListener
,
226 bool aWantsUntrusted
,
227 uint8_t aOptionalArgc
)
229 NS_ASSERTION(!aWantsUntrusted
|| aOptionalArgc
> 1,
230 "Won't check if this is chrome, you want to set "
231 "aWantsUntrusted to false or make the aWantsUntrusted "
232 "explicit by making aOptionalArgc non-zero.");
234 if (aOptionalArgc
< 2) {
235 nsresult rv
= WantsUntrusted(&aWantsUntrusted
);
236 NS_ENSURE_SUCCESS(rv
, rv
);
239 return NS_AddSystemEventListener(this, aType
, aListener
, aUseCapture
,
244 DOMEventTargetHelper::DispatchEvent(nsIDOMEvent
* aEvent
, bool* aRetVal
)
246 nsEventStatus status
= nsEventStatus_eIgnore
;
248 EventDispatcher::DispatchDOMEvent(this, nullptr, aEvent
, nullptr, &status
);
250 *aRetVal
= (status
!= nsEventStatus_eConsumeNoDefault
);
255 DOMEventTargetHelper::DispatchTrustedEvent(const nsAString
& aEventName
)
257 nsCOMPtr
<nsIDOMEvent
> event
;
258 NS_NewDOMEvent(getter_AddRefs(event
), this, nullptr, nullptr);
259 nsresult rv
= event
->InitEvent(aEventName
, false, false);
260 NS_ENSURE_SUCCESS(rv
, rv
);
262 return DispatchTrustedEvent(event
);
266 DOMEventTargetHelper::DispatchTrustedEvent(nsIDOMEvent
* event
)
268 event
->SetTrusted(true);
271 return DispatchEvent(event
, &dummy
);
275 DOMEventTargetHelper::SetEventHandler(nsIAtom
* aType
,
277 const JS::Value
& aValue
)
279 nsRefPtr
<EventHandlerNonNull
> handler
;
280 JS::Rooted
<JSObject
*> callable(aCx
);
281 if (aValue
.isObject() && JS::IsCallable(callable
= &aValue
.toObject())) {
282 handler
= new EventHandlerNonNull(callable
, dom::GetIncumbentGlobal());
284 SetEventHandler(aType
, EmptyString(), handler
);
289 DOMEventTargetHelper::GetEventHandler(nsIAtom
* aType
,
293 EventHandlerNonNull
* handler
= GetEventHandler(aType
, EmptyString());
295 *aValue
= JS::ObjectValue(*handler
->Callable());
297 *aValue
= JS::NullValue();
302 DOMEventTargetHelper::PreHandleEvent(EventChainPreVisitor
& aVisitor
)
304 aVisitor
.mCanHandle
= true;
305 aVisitor
.mParentTarget
= nullptr;
310 DOMEventTargetHelper::PostHandleEvent(EventChainPostVisitor
& aVisitor
)
316 DOMEventTargetHelper::DispatchDOMEvent(WidgetEvent
* aEvent
,
317 nsIDOMEvent
* aDOMEvent
,
318 nsPresContext
* aPresContext
,
319 nsEventStatus
* aEventStatus
)
321 return EventDispatcher::DispatchDOMEvent(this, aEvent
, aDOMEvent
,
322 aPresContext
, aEventStatus
);
325 EventListenerManager
*
326 DOMEventTargetHelper::GetOrCreateListenerManager()
328 if (!mListenerManager
) {
329 mListenerManager
= new EventListenerManager(this);
332 return mListenerManager
;
335 EventListenerManager
*
336 DOMEventTargetHelper::GetExistingListenerManager() const
338 return mListenerManager
;
342 DOMEventTargetHelper::GetContextForEventHandlers(nsresult
* aRv
)
344 *aRv
= CheckInnerWindowCorrectness();
345 if (NS_FAILED(*aRv
)) {
348 nsPIDOMWindow
* owner
= GetOwner();
349 return owner
? static_cast<nsGlobalWindow
*>(owner
)->GetContextInternal()
354 DOMEventTargetHelper::WantsUntrusted(bool* aRetVal
)
357 nsIScriptContext
* context
= GetContextForEventHandlers(&rv
);
358 NS_ENSURE_SUCCESS(rv
, rv
);
359 nsCOMPtr
<nsIDocument
> doc
=
360 nsContentUtils::GetDocumentFromScriptContext(context
);
361 // We can let listeners on workers to always handle all the events.
362 *aRetVal
= (doc
&& !nsContentUtils::IsChromeDoc(doc
)) || !NS_IsMainThread();
367 DOMEventTargetHelper::EventListenerAdded(nsIAtom
* aType
)
370 EventListenerWasAdded(Substring(nsDependentAtomString(aType
), 2), rv
);
374 DOMEventTargetHelper::EventListenerRemoved(nsIAtom
* aType
)
377 EventListenerWasRemoved(Substring(nsDependentAtomString(aType
), 2), rv
);
380 } // namespace mozilla