LCOV - code coverage report
Current view: top level - dom/events - JSEventHandler.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 43 127 33.9 %
Date: 2017-07-14 16:53:18 Functions: 9 15 60.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13