dom/events/JSEventHandler.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; 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/CycleCollectedJSRuntime.h"
    22 #include "mozilla/HoldDropJSObjects.h"
    23 #include "mozilla/JSEventHandler.h"
    24 #include "mozilla/Likely.h"
    25 #include "mozilla/dom/ErrorEvent.h"
    26 #include "mozilla/dom/UnionTypes.h"
    28 namespace mozilla {
    30 using namespace dom;
    32 JSEventHandler::JSEventHandler(nsISupports* aTarget,
    33                                nsIAtom* aType,
    34                                const TypedEventHandler& aTypedHandler)
    35   : mEventName(aType)
    36   , mTypedHandler(aTypedHandler)
    37 {
    38   nsCOMPtr<nsISupports> base = do_QueryInterface(aTarget);
    39   mTarget = base.get();
    40   // Note, we call HoldJSObjects to get CanSkip called before CC.
    41   HoldJSObjects(this);
    42 }
    44 JSEventHandler::~JSEventHandler()
    45 {
    46   NS_ASSERTION(!mTarget, "Should have called Disconnect()!");
    47   DropJSObjects(this);
    48 }
    50 NS_IMPL_CYCLE_COLLECTION_CLASS(JSEventHandler)
    52 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JSEventHandler)
    53   tmp->mTypedHandler.ForgetHandler();
    54 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(JSEventHandler)
    56   if (MOZ_UNLIKELY(cb.WantDebugInfo()) && tmp->mEventName) {
    57     nsAutoCString name;
    58     name.AppendLiteral("JSEventHandler handlerName=");
    59     name.Append(
    60       NS_ConvertUTF16toUTF8(nsDependentAtomString(tmp->mEventName)).get());
    61     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name.get());
    62   } else {
    63     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(JSEventHandler, tmp->mRefCnt.get())
    64   }
    65   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mTypedHandler.Ptr())
    66   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
    67 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    69 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(JSEventHandler)
    70   if (tmp->IsBlackForCC()) {
    71     return true;
    72   }
    73   // If we have a target, it is the one which has tmp as onfoo handler.
    74   if (tmp->mTarget) {
    75     nsXPCOMCycleCollectionParticipant* cp = nullptr;
    76     CallQueryInterface(tmp->mTarget, &cp);
    77     nsISupports* canonical = nullptr;
    78     tmp->mTarget->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
    79                                  reinterpret_cast<void**>(&canonical));
    80     // Usually CanSkip ends up unmarking the event listeners of mTarget,
    81     // so tmp may become black.
    82     if (cp && canonical && cp->CanSkip(canonical, true)) {
    83       return tmp->IsBlackForCC();
    84     }
    85   }
    86 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
    88 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(JSEventHandler)
    89   return tmp->IsBlackForCC();
    90 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
    92 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(JSEventHandler)
    93   return tmp->IsBlackForCC();
    94 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
    96 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSEventHandler)
    97   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
    98   NS_INTERFACE_MAP_ENTRY(nsISupports)
    99   NS_INTERFACE_MAP_ENTRY(JSEventHandler)
   100 NS_INTERFACE_MAP_END
   102 NS_IMPL_CYCLE_COLLECTING_ADDREF(JSEventHandler)
   103 NS_IMPL_CYCLE_COLLECTING_RELEASE(JSEventHandler)
   105 bool
   106 JSEventHandler::IsBlackForCC()
   107 {
   108   // We can claim to be black if all the things we reference are
   109   // effectively black already.
   110   return !mTypedHandler.HasEventHandler() ||
   111          !mTypedHandler.Ptr()->HasGrayCallable();
   112 }
   114 nsresult
   115 JSEventHandler::HandleEvent(nsIDOMEvent* aEvent)
   116 {
   117   nsCOMPtr<EventTarget> target = do_QueryInterface(mTarget);
   118   if (!target || !mTypedHandler.HasEventHandler() ||
   119       !GetTypedEventHandler().Ptr()->CallbackPreserveColor()) {
   120     return NS_ERROR_FAILURE;
   121   }
   123   Event* event = aEvent->InternalDOMEvent();
   124   bool isMainThread = event->IsMainThreadEvent();
   125   bool isChromeHandler =
   126     isMainThread ?
   127       nsContentUtils::GetObjectPrincipal(
   128         GetTypedEventHandler().Ptr()->CallbackPreserveColor()) ==
   129         nsContentUtils::GetSystemPrincipal() :
   130       mozilla::dom::workers::IsCurrentThreadRunningChromeWorker();
   132   if (mTypedHandler.Type() == TypedEventHandler::eOnError) {
   133     MOZ_ASSERT_IF(mEventName, mEventName == nsGkAtoms::onerror);
   135     nsString errorMsg, file;
   136     EventOrString msgOrEvent;
   137     Optional<nsAString> fileName;
   138     Optional<uint32_t> lineNumber;
   139     Optional<uint32_t> columnNumber;
   140     Optional<JS::Handle<JS::Value>> error;
   142     NS_ENSURE_TRUE(aEvent, NS_ERROR_UNEXPECTED);
   143     ErrorEvent* scriptEvent = aEvent->InternalDOMEvent()->AsErrorEvent();
   144     if (scriptEvent) {
   145       scriptEvent->GetMessage(errorMsg);
   146       msgOrEvent.SetAsString().SetData(errorMsg.Data(), errorMsg.Length());
   148       scriptEvent->GetFilename(file);
   149       fileName = &file;
   151       lineNumber.Construct();
   152       lineNumber.Value() = scriptEvent->Lineno();
   154       columnNumber.Construct();
   155       columnNumber.Value() = scriptEvent->Colno();
   157       ThreadsafeAutoJSContext cx;
   158       error.Construct(cx);
   159       scriptEvent->GetError(cx, &error.Value());
   160     } else {
   161       msgOrEvent.SetAsEvent() = aEvent->InternalDOMEvent();
   162     }
   164     nsRefPtr<OnErrorEventHandlerNonNull> handler =
   165       mTypedHandler.OnErrorEventHandler();
   166     ErrorResult rv;
   167     bool handled = handler->Call(mTarget, msgOrEvent, fileName, lineNumber,
   168                                  columnNumber, error, rv);
   169     if (rv.Failed()) {
   170       return rv.ErrorCode();
   171     }
   173     if (handled) {
   174       event->PreventDefaultInternal(isChromeHandler);
   175     }
   176     return NS_OK;
   177   }
   179   if (mTypedHandler.Type() == TypedEventHandler::eOnBeforeUnload) {
   180     MOZ_ASSERT(mEventName == nsGkAtoms::onbeforeunload);
   182     nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handler =
   183       mTypedHandler.OnBeforeUnloadEventHandler();
   184     ErrorResult rv;
   185     nsString retval;
   186     handler->Call(mTarget, *(aEvent->InternalDOMEvent()), retval, rv);
   187     if (rv.Failed()) {
   188       return rv.ErrorCode();
   189     }
   191     nsCOMPtr<nsIDOMBeforeUnloadEvent> beforeUnload = do_QueryInterface(aEvent);
   192     NS_ENSURE_STATE(beforeUnload);
   194     if (!DOMStringIsNull(retval)) {
   195       event->PreventDefaultInternal(isChromeHandler);
   197       nsAutoString text;
   198       beforeUnload->GetReturnValue(text);
   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       if (text.IsEmpty()) {
   204         beforeUnload->SetReturnValue(retval);
   205       }
   206     }
   208     return NS_OK;
   209   }
   211   MOZ_ASSERT(mTypedHandler.Type() == TypedEventHandler::eNormal);
   212   ErrorResult rv;
   213   nsRefPtr<EventHandlerNonNull> handler = mTypedHandler.NormalEventHandler();
   214   JS::Rooted<JS::Value> retval(CycleCollectedJSRuntime::Get()->Runtime());
   215   handler->Call(mTarget, *(aEvent->InternalDOMEvent()), &retval, rv);
   216   if (rv.Failed()) {
   217     return rv.ErrorCode();
   218   }
   220   // If the handler returned false and its sense is not reversed,
   221   // or the handler returned true and its sense is reversed from
   222   // the usual (false means cancel), then prevent default.
   223   if (retval.isBoolean() &&
   224       retval.toBoolean() == (mEventName == nsGkAtoms::onerror ||
   225                              mEventName == nsGkAtoms::onmouseover)) {
   226     event->PreventDefaultInternal(isChromeHandler);
   227   }
   229   return NS_OK;
   230 }
   232 } // namespace mozilla
   234 using namespace mozilla;
   236 /*
   237  * Factory functions
   238  */
   240 nsresult
   241 NS_NewJSEventHandler(nsISupports* aTarget,
   242                      nsIAtom* aEventType,
   243                      const TypedEventHandler& aTypedHandler,
   244                      JSEventHandler** aReturn)
   245 {
   246   NS_ENSURE_ARG(aEventType || !NS_IsMainThread());
   247   JSEventHandler* it =
   248     new JSEventHandler(aTarget, aEventType, aTypedHandler);
   249   NS_ADDREF(*aReturn = it);
   251   return NS_OK;
   252 }

mercurial