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/. */
8 #include "nsIScriptContext.h"
9 #include "nsIScriptGlobalObject.h"
10 #include "nsVariant.h"
11 #include "nsGkAtoms.h"
12 #include "xpcpublic.h"
13 #include "nsJSEnvironment.h"
14 #include "nsDOMJSUtils.h"
15 #include "mozilla/ContentEvents.h"
16 #include "mozilla/CycleCollectedJSContext.h"
17 #include "mozilla/HoldDropJSObjects.h"
18 #include "mozilla/JSEventHandler.h"
19 #include "mozilla/Likely.h"
20 #include "mozilla/dom/BeforeUnloadEvent.h"
21 #include "mozilla/dom/ErrorEvent.h"
22 #include "mozilla/dom/WorkerPrivate.h"
28 JSEventHandler::JSEventHandler(EventTarget
* aTarget
, nsAtom
* aType
,
29 const TypedEventHandler
& aTypedHandler
)
30 : mTarget(aTarget
), mEventName(aType
), mTypedHandler(aTypedHandler
) {
31 // Note, we call HoldJSObjects to get CanSkip called before CC.
35 JSEventHandler::~JSEventHandler() {
36 NS_ASSERTION(!mTarget
, "Should have called Disconnect()!");
40 NS_IMPL_CYCLE_COLLECTION_CLASS(JSEventHandler
)
42 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JSEventHandler
)
43 tmp
->mTypedHandler
.ForgetHandler();
44 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
45 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(JSEventHandler
)
46 if (MOZ_UNLIKELY(cb
.WantDebugInfo()) && tmp
->mEventName
) {
48 name
.AppendLiteral("JSEventHandler handlerName=");
50 NS_ConvertUTF16toUTF8(nsDependentAtomString(tmp
->mEventName
)).get());
51 cb
.DescribeRefCountedNode(tmp
->mRefCnt
.get(), name
.get());
53 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(JSEventHandler
, tmp
->mRefCnt
.get())
55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mTypedHandler
.Ptr())
56 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
58 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(JSEventHandler
)
59 if (tmp
->IsBlackForCC()) {
62 // If we have a target, it is the one which has tmp as onfoo handler.
64 nsXPCOMCycleCollectionParticipant
* cp
= nullptr;
65 CallQueryInterface(tmp
->mTarget
, &cp
);
66 nsISupports
* canonical
= nullptr;
67 tmp
->mTarget
->QueryInterface(NS_GET_IID(nsCycleCollectionISupports
),
68 reinterpret_cast<void**>(&canonical
));
69 // Usually CanSkip ends up unmarking the event listeners of mTarget,
70 // so tmp may become black.
71 if (cp
&& canonical
&& cp
->CanSkip(canonical
, true)) {
72 return tmp
->IsBlackForCC();
75 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
77 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(JSEventHandler
)
78 return tmp
->IsBlackForCC();
79 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
81 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(JSEventHandler
)
82 return tmp
->IsBlackForCC();
83 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
85 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSEventHandler
)
86 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener
)
87 NS_INTERFACE_MAP_ENTRY(nsISupports
)
88 NS_INTERFACE_MAP_ENTRY(JSEventHandler
)
91 NS_IMPL_CYCLE_COLLECTING_ADDREF(JSEventHandler
)
92 NS_IMPL_CYCLE_COLLECTING_RELEASE(JSEventHandler
)
94 bool JSEventHandler::IsBlackForCC() {
95 // We can claim to be black if all the things we reference are
96 // effectively black already.
97 return !mTypedHandler
.HasEventHandler() ||
98 mTypedHandler
.Ptr()->IsBlackForCC();
101 nsresult
JSEventHandler::HandleEvent(Event
* aEvent
) {
102 nsCOMPtr
<EventTarget
> target
= mTarget
;
103 if (!target
|| !mTypedHandler
.HasEventHandler() ||
104 !GetTypedEventHandler().Ptr()->CallbackPreserveColor()) {
105 return NS_ERROR_FAILURE
;
108 bool isMainThread
= aEvent
->IsMainThreadEvent();
109 bool isChromeHandler
=
111 ? nsContentUtils::ObjectPrincipal(
112 GetTypedEventHandler().Ptr()->CallbackGlobalOrNull()) ==
113 nsContentUtils::GetSystemPrincipal()
114 : mozilla::dom::IsCurrentThreadRunningChromeWorker();
116 if (mTypedHandler
.Type() == TypedEventHandler::eOnError
) {
117 MOZ_ASSERT_IF(mEventName
, mEventName
== nsGkAtoms::onerror
);
121 EventOrString msgOrEvent
;
122 Optional
<nsACString
> fileName
;
123 Optional
<uint32_t> lineNumber
;
124 Optional
<uint32_t> columnNumber
;
125 Optional
<JS::Handle
<JS::Value
>> error
;
127 NS_ENSURE_TRUE(aEvent
, NS_ERROR_UNEXPECTED
);
128 ErrorEvent
* scriptEvent
= aEvent
->AsErrorEvent();
130 scriptEvent
->GetMessage(errorMsg
);
131 msgOrEvent
.SetAsString().ShareOrDependUpon(errorMsg
);
133 scriptEvent
->GetFilename(file
);
136 lineNumber
.Construct();
137 lineNumber
.Value() = scriptEvent
->Lineno();
139 columnNumber
.Construct();
140 columnNumber
.Value() = scriptEvent
->Colno();
142 error
.Construct(RootingCx());
143 scriptEvent
->GetError(&error
.Value());
145 msgOrEvent
.SetAsEvent() = aEvent
;
148 RefPtr
<OnErrorEventHandlerNonNull
> handler
=
149 mTypedHandler
.OnErrorEventHandler();
151 JS::Rooted
<JS::Value
> retval(RootingCx());
152 handler
->Call(target
, msgOrEvent
, fileName
, lineNumber
, columnNumber
, error
,
155 return rv
.StealNSResult();
158 if (retval
.isBoolean() && retval
.toBoolean() == bool(scriptEvent
)) {
159 aEvent
->PreventDefaultInternal(isChromeHandler
);
164 if (mTypedHandler
.Type() == TypedEventHandler::eOnBeforeUnload
) {
165 MOZ_ASSERT(mEventName
== nsGkAtoms::onbeforeunload
);
167 RefPtr
<OnBeforeUnloadEventHandlerNonNull
> handler
=
168 mTypedHandler
.OnBeforeUnloadEventHandler();
171 handler
->Call(target
, *aEvent
, retval
, rv
);
173 return rv
.StealNSResult();
176 BeforeUnloadEvent
* beforeUnload
= aEvent
->AsBeforeUnloadEvent();
177 NS_ENSURE_STATE(beforeUnload
);
179 if (!DOMStringIsNull(retval
)) {
180 aEvent
->PreventDefaultInternal(isChromeHandler
);
183 beforeUnload
->GetReturnValue(text
);
185 // Set the text in the beforeUnload event as long as it wasn't
186 // already set (through event.returnValue, which takes
187 // precedence over a value returned from a JS function in IE)
188 if (text
.IsEmpty()) {
189 beforeUnload
->SetReturnValue(retval
);
196 MOZ_ASSERT(mTypedHandler
.Type() == TypedEventHandler::eNormal
);
198 RefPtr
<EventHandlerNonNull
> handler
= mTypedHandler
.NormalEventHandler();
199 JS::Rooted
<JS::Value
> retval(RootingCx());
200 handler
->Call(target
, *aEvent
, &retval
, rv
);
202 return rv
.StealNSResult();
205 // If the handler returned false, then prevent default.
206 if (retval
.isBoolean() && !retval
.toBoolean()) {
207 aEvent
->PreventDefaultInternal(isChromeHandler
);
213 } // namespace mozilla
215 using namespace mozilla
;
221 nsresult
NS_NewJSEventHandler(mozilla::dom::EventTarget
* aTarget
,
223 const TypedEventHandler
& aTypedHandler
,
224 JSEventHandler
** aReturn
) {
225 NS_ENSURE_ARG(aEventType
|| !NS_IsMainThread());
226 JSEventHandler
* it
= new JSEventHandler(aTarget
, aEventType
, aTypedHandler
);
227 NS_ADDREF(*aReturn
= it
);