tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

JSEventHandler.cpp (7571B)


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