Bug 1826136 [wpt PR 39338] - Update wpt metadata, a=testonly
[gecko.git] / dom / events / JSEventHandler.cpp
blob8304ec658276a319731213bd8632569913533268
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, file;
120 EventOrString msgOrEvent;
121 Optional<nsAString> fileName;
122 Optional<uint32_t> lineNumber;
123 Optional<uint32_t> columnNumber;
124 Optional<JS::Handle<JS::Value>> error;
126 NS_ENSURE_TRUE(aEvent, NS_ERROR_UNEXPECTED);
127 ErrorEvent* scriptEvent = aEvent->AsErrorEvent();
128 if (scriptEvent) {
129 scriptEvent->GetMessage(errorMsg);
130 msgOrEvent.SetAsString().ShareOrDependUpon(errorMsg);
132 scriptEvent->GetFilename(file);
133 fileName = &file;
135 lineNumber.Construct();
136 lineNumber.Value() = scriptEvent->Lineno();
138 columnNumber.Construct();
139 columnNumber.Value() = scriptEvent->Colno();
141 error.Construct(RootingCx());
142 scriptEvent->GetError(&error.Value());
143 } else {
144 msgOrEvent.SetAsEvent() = aEvent;
147 RefPtr<OnErrorEventHandlerNonNull> handler =
148 mTypedHandler.OnErrorEventHandler();
149 ErrorResult rv;
150 JS::Rooted<JS::Value> retval(RootingCx());
151 handler->Call(target, msgOrEvent, fileName, lineNumber, columnNumber, error,
152 &retval, rv);
153 if (rv.Failed()) {
154 return rv.StealNSResult();
157 if (retval.isBoolean() && retval.toBoolean() == bool(scriptEvent)) {
158 aEvent->PreventDefaultInternal(isChromeHandler);
160 return NS_OK;
163 if (mTypedHandler.Type() == TypedEventHandler::eOnBeforeUnload) {
164 MOZ_ASSERT(mEventName == nsGkAtoms::onbeforeunload);
166 RefPtr<OnBeforeUnloadEventHandlerNonNull> handler =
167 mTypedHandler.OnBeforeUnloadEventHandler();
168 ErrorResult rv;
169 nsString retval;
170 handler->Call(target, *aEvent, retval, rv);
171 if (rv.Failed()) {
172 return rv.StealNSResult();
175 BeforeUnloadEvent* beforeUnload = aEvent->AsBeforeUnloadEvent();
176 NS_ENSURE_STATE(beforeUnload);
178 if (!DOMStringIsNull(retval)) {
179 aEvent->PreventDefaultInternal(isChromeHandler);
181 nsAutoString text;
182 beforeUnload->GetReturnValue(text);
184 // Set the text in the beforeUnload event as long as it wasn't
185 // already set (through event.returnValue, which takes
186 // precedence over a value returned from a JS function in IE)
187 if (text.IsEmpty()) {
188 beforeUnload->SetReturnValue(retval);
192 return NS_OK;
195 MOZ_ASSERT(mTypedHandler.Type() == TypedEventHandler::eNormal);
196 ErrorResult rv;
197 RefPtr<EventHandlerNonNull> handler = mTypedHandler.NormalEventHandler();
198 JS::Rooted<JS::Value> retval(RootingCx());
199 handler->Call(target, *aEvent, &retval, rv);
200 if (rv.Failed()) {
201 return rv.StealNSResult();
204 // If the handler returned false, then prevent default.
205 if (retval.isBoolean() && !retval.toBoolean()) {
206 aEvent->PreventDefaultInternal(isChromeHandler);
209 return NS_OK;
212 } // namespace mozilla
214 using namespace mozilla;
217 * Factory functions
220 nsresult NS_NewJSEventHandler(mozilla::dom::EventTarget* aTarget,
221 nsAtom* aEventType,
222 const TypedEventHandler& aTypedHandler,
223 JSEventHandler** aReturn) {
224 NS_ENSURE_ARG(aEventType || !NS_IsMainThread());
225 JSEventHandler* it = new JSEventHandler(aTarget, aEventType, aTypedHandler);
226 NS_ADDREF(*aReturn = it);
228 return NS_OK;