JSEventHandler.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /* -*- Mode: C++; tab-width: 8; 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/. */
  5. #include "nsJSUtils.h"
  6. #include "nsString.h"
  7. #include "nsIServiceManager.h"
  8. #include "nsIScriptSecurityManager.h"
  9. #include "nsIScriptContext.h"
  10. #include "nsIScriptGlobalObject.h"
  11. #include "nsIXPConnect.h"
  12. #include "nsIMutableArray.h"
  13. #include "nsVariant.h"
  14. #include "nsIDOMBeforeUnloadEvent.h"
  15. #include "nsGkAtoms.h"
  16. #include "xpcpublic.h"
  17. #include "nsJSEnvironment.h"
  18. #include "nsDOMJSUtils.h"
  19. #include "WorkerPrivate.h"
  20. #include "mozilla/ContentEvents.h"
  21. #include "mozilla/CycleCollectedJSContext.h"
  22. #include "mozilla/HoldDropJSObjects.h"
  23. #include "mozilla/JSEventHandler.h"
  24. #include "mozilla/Likely.h"
  25. #include "mozilla/dom/ErrorEvent.h"
  26. namespace mozilla {
  27. using namespace dom;
  28. JSEventHandler::JSEventHandler(nsISupports* aTarget,
  29. nsIAtom* aType,
  30. const TypedEventHandler& aTypedHandler)
  31. : mEventName(aType)
  32. , mTypedHandler(aTypedHandler)
  33. {
  34. nsCOMPtr<nsISupports> base = do_QueryInterface(aTarget);
  35. mTarget = base.get();
  36. // Note, we call HoldJSObjects to get CanSkip called before CC.
  37. HoldJSObjects(this);
  38. }
  39. JSEventHandler::~JSEventHandler()
  40. {
  41. NS_ASSERTION(!mTarget, "Should have called Disconnect()!");
  42. DropJSObjects(this);
  43. }
  44. NS_IMPL_CYCLE_COLLECTION_CLASS(JSEventHandler)
  45. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JSEventHandler)
  46. tmp->mTypedHandler.ForgetHandler();
  47. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  48. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(JSEventHandler)
  49. if (MOZ_UNLIKELY(cb.WantDebugInfo()) && tmp->mEventName) {
  50. nsAutoCString name;
  51. name.AppendLiteral("JSEventHandler handlerName=");
  52. name.Append(
  53. NS_ConvertUTF16toUTF8(nsDependentAtomString(tmp->mEventName)).get());
  54. cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name.get());
  55. } else {
  56. NS_IMPL_CYCLE_COLLECTION_DESCRIBE(JSEventHandler, tmp->mRefCnt.get())
  57. }
  58. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mTypedHandler.Ptr())
  59. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  60. NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(JSEventHandler)
  61. if (tmp->IsBlackForCC()) {
  62. return true;
  63. }
  64. // If we have a target, it is the one which has tmp as onfoo handler.
  65. if (tmp->mTarget) {
  66. nsXPCOMCycleCollectionParticipant* cp = nullptr;
  67. CallQueryInterface(tmp->mTarget, &cp);
  68. nsISupports* canonical = nullptr;
  69. tmp->mTarget->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
  70. reinterpret_cast<void**>(&canonical));
  71. // Usually CanSkip ends up unmarking the event listeners of mTarget,
  72. // so tmp may become black.
  73. if (cp && canonical && cp->CanSkip(canonical, true)) {
  74. return tmp->IsBlackForCC();
  75. }
  76. }
  77. NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
  78. NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(JSEventHandler)
  79. return tmp->IsBlackForCC();
  80. 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
  84. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSEventHandler)
  85. NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
  86. NS_INTERFACE_MAP_ENTRY(nsISupports)
  87. NS_INTERFACE_MAP_ENTRY(JSEventHandler)
  88. NS_INTERFACE_MAP_END
  89. NS_IMPL_CYCLE_COLLECTING_ADDREF(JSEventHandler)
  90. NS_IMPL_CYCLE_COLLECTING_RELEASE(JSEventHandler)
  91. bool
  92. JSEventHandler::IsBlackForCC()
  93. {
  94. // We can claim to be black if all the things we reference are
  95. // effectively black already.
  96. return !mTypedHandler.HasEventHandler() ||
  97. !mTypedHandler.Ptr()->HasGrayCallable();
  98. }
  99. nsresult
  100. JSEventHandler::HandleEvent(nsIDOMEvent* aEvent)
  101. {
  102. nsCOMPtr<EventTarget> target = do_QueryInterface(mTarget);
  103. if (!target || !mTypedHandler.HasEventHandler() ||
  104. !GetTypedEventHandler().Ptr()->CallbackPreserveColor()) {
  105. return NS_ERROR_FAILURE;
  106. }
  107. Event* event = aEvent->InternalDOMEvent();
  108. bool isMainThread = event->IsMainThreadEvent();
  109. bool isChromeHandler =
  110. isMainThread ?
  111. nsContentUtils::ObjectPrincipal(
  112. GetTypedEventHandler().Ptr()->CallbackPreserveColor()) ==
  113. nsContentUtils::GetSystemPrincipal() :
  114. mozilla::dom::workers::IsCurrentThreadRunningChromeWorker();
  115. if (mTypedHandler.Type() == TypedEventHandler::eOnError) {
  116. MOZ_ASSERT_IF(mEventName, mEventName == nsGkAtoms::onerror);
  117. nsString errorMsg, file;
  118. EventOrString msgOrEvent;
  119. Optional<nsAString> fileName;
  120. Optional<uint32_t> lineNumber;
  121. Optional<uint32_t> columnNumber;
  122. Optional<JS::Handle<JS::Value>> error;
  123. NS_ENSURE_TRUE(aEvent, NS_ERROR_UNEXPECTED);
  124. ErrorEvent* scriptEvent = aEvent->InternalDOMEvent()->AsErrorEvent();
  125. if (scriptEvent) {
  126. scriptEvent->GetMessage(errorMsg);
  127. msgOrEvent.SetAsString().ShareOrDependUpon(errorMsg);
  128. scriptEvent->GetFilename(file);
  129. fileName = &file;
  130. lineNumber.Construct();
  131. lineNumber.Value() = scriptEvent->Lineno();
  132. columnNumber.Construct();
  133. columnNumber.Value() = scriptEvent->Colno();
  134. error.Construct(RootingCx());
  135. scriptEvent->GetError(&error.Value());
  136. } else {
  137. msgOrEvent.SetAsEvent() = aEvent->InternalDOMEvent();
  138. }
  139. RefPtr<OnErrorEventHandlerNonNull> handler =
  140. mTypedHandler.OnErrorEventHandler();
  141. ErrorResult rv;
  142. bool handled = handler->Call(mTarget, msgOrEvent, fileName, lineNumber,
  143. columnNumber, error, rv);
  144. if (rv.Failed()) {
  145. return rv.StealNSResult();
  146. }
  147. if (handled) {
  148. event->PreventDefaultInternal(isChromeHandler);
  149. }
  150. return NS_OK;
  151. }
  152. if (mTypedHandler.Type() == TypedEventHandler::eOnBeforeUnload) {
  153. MOZ_ASSERT(mEventName == nsGkAtoms::onbeforeunload);
  154. RefPtr<OnBeforeUnloadEventHandlerNonNull> handler =
  155. mTypedHandler.OnBeforeUnloadEventHandler();
  156. ErrorResult rv;
  157. nsString retval;
  158. handler->Call(mTarget, *(aEvent->InternalDOMEvent()), retval, rv);
  159. if (rv.Failed()) {
  160. return rv.StealNSResult();
  161. }
  162. nsCOMPtr<nsIDOMBeforeUnloadEvent> beforeUnload = do_QueryInterface(aEvent);
  163. NS_ENSURE_STATE(beforeUnload);
  164. if (!DOMStringIsNull(retval)) {
  165. event->PreventDefaultInternal(isChromeHandler);
  166. nsAutoString text;
  167. beforeUnload->GetReturnValue(text);
  168. // Set the text in the beforeUnload event as long as it wasn't
  169. // already set (through event.returnValue, which takes
  170. // precedence over a value returned from a JS function in IE)
  171. if (text.IsEmpty()) {
  172. beforeUnload->SetReturnValue(retval);
  173. }
  174. }
  175. return NS_OK;
  176. }
  177. MOZ_ASSERT(mTypedHandler.Type() == TypedEventHandler::eNormal);
  178. ErrorResult rv;
  179. RefPtr<EventHandlerNonNull> handler = mTypedHandler.NormalEventHandler();
  180. JS::Rooted<JS::Value> retval(RootingCx());
  181. handler->Call(mTarget, *(aEvent->InternalDOMEvent()), &retval, rv);
  182. if (rv.Failed()) {
  183. return rv.StealNSResult();
  184. }
  185. // If the handler returned false and its sense is not reversed,
  186. // or the handler returned true and its sense is reversed from
  187. // the usual (false means cancel), then prevent default.
  188. if (retval.isBoolean() &&
  189. retval.toBoolean() == (mEventName == nsGkAtoms::onerror ||
  190. mEventName == nsGkAtoms::onmouseover)) {
  191. event->PreventDefaultInternal(isChromeHandler);
  192. }
  193. return NS_OK;
  194. }
  195. } // namespace mozilla
  196. using namespace mozilla;
  197. /*
  198. * Factory functions
  199. */
  200. nsresult
  201. NS_NewJSEventHandler(nsISupports* aTarget,
  202. nsIAtom* aEventType,
  203. const TypedEventHandler& aTypedHandler,
  204. JSEventHandler** aReturn)
  205. {
  206. NS_ENSURE_ARG(aEventType || !NS_IsMainThread());
  207. JSEventHandler* it =
  208. new JSEventHandler(aTarget, aEventType, aTypedHandler);
  209. NS_ADDREF(*aReturn = it);
  210. return NS_OK;
  211. }