Backed out changeset f594e6f00208 (bug 1940883) for causing crashes in bug 1941164.
[gecko.git] / dom / events / JSEventHandler.cpp
blob33cc08d4baef6691441bce1da31e82f2a89bea15
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 #include "nsJSUtils.h"
7 #include "nsString.h"
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"
24 namespace mozilla {
26 using namespace dom;
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.
32 HoldJSObjects(this);
35 JSEventHandler::~JSEventHandler() {
36 NS_ASSERTION(!mTarget, "Should have called Disconnect()!");
37 DropJSObjects(this);
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) {
47 nsAutoCString name;
48 name.AppendLiteral("JSEventHandler handlerName=");
49 name.Append(
50 NS_ConvertUTF16toUTF8(nsDependentAtomString(tmp->mEventName)).get());
51 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name.get());
52 } else {
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()) {
60 return true;
62 // If we have a target, it is the one which has tmp as onfoo handler.
63 if (tmp->mTarget) {
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)
89 NS_INTERFACE_MAP_END
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 =
110 isMainThread
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);
119 nsString errorMsg;
120 nsCString file;
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();
129 if (scriptEvent) {
130 scriptEvent->GetMessage(errorMsg);
131 msgOrEvent.SetAsString().ShareOrDependUpon(errorMsg);
133 scriptEvent->GetFilename(file);
134 fileName = &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());
144 } else {
145 msgOrEvent.SetAsEvent() = aEvent;
148 RefPtr<OnErrorEventHandlerNonNull> handler =
149 mTypedHandler.OnErrorEventHandler();
150 ErrorResult rv;
151 JS::Rooted<JS::Value> retval(RootingCx());
152 handler->Call(target, msgOrEvent, fileName, lineNumber, columnNumber, error,
153 &retval, rv);
154 if (rv.Failed()) {
155 return rv.StealNSResult();
158 if (retval.isBoolean() && retval.toBoolean() == bool(scriptEvent)) {
159 aEvent->PreventDefaultInternal(isChromeHandler);
161 return NS_OK;
164 if (mTypedHandler.Type() == TypedEventHandler::eOnBeforeUnload) {
165 MOZ_ASSERT(mEventName == nsGkAtoms::onbeforeunload);
167 RefPtr<OnBeforeUnloadEventHandlerNonNull> handler =
168 mTypedHandler.OnBeforeUnloadEventHandler();
169 ErrorResult rv;
170 nsString retval;
171 handler->Call(target, *aEvent, retval, rv);
172 if (rv.Failed()) {
173 return rv.StealNSResult();
176 BeforeUnloadEvent* beforeUnload = aEvent->AsBeforeUnloadEvent();
177 NS_ENSURE_STATE(beforeUnload);
179 if (!DOMStringIsNull(retval)) {
180 aEvent->PreventDefaultInternal(isChromeHandler);
182 nsAutoString text;
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);
193 return NS_OK;
196 MOZ_ASSERT(mTypedHandler.Type() == TypedEventHandler::eNormal);
197 ErrorResult rv;
198 RefPtr<EventHandlerNonNull> handler = mTypedHandler.NormalEventHandler();
199 JS::Rooted<JS::Value> retval(RootingCx());
200 handler->Call(target, *aEvent, &retval, rv);
201 if (rv.Failed()) {
202 return rv.StealNSResult();
205 // If the handler returned false, then prevent default.
206 if (retval.isBoolean() && !retval.toBoolean()) {
207 aEvent->PreventDefaultInternal(isChromeHandler);
210 return NS_OK;
213 } // namespace mozilla
215 using namespace mozilla;
218 * Factory functions
221 nsresult NS_NewJSEventHandler(mozilla::dom::EventTarget* aTarget,
222 nsAtom* aEventType,
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);
229 return NS_OK;