LCOV - code coverage report
Current view: top level - dom/base - nsJSTimeoutHandler.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 37 159 23.3 %
Date: 2017-07-14 16:53:18 Functions: 15 34 44.1 %
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             : 
       7             : #include <algorithm>
       8             : 
       9             : #include "mozilla/Attributes.h"
      10             : #include "mozilla/Likely.h"
      11             : #include "mozilla/Maybe.h"
      12             : #include "mozilla/dom/FunctionBinding.h"
      13             : #include "nsAXPCNativeCallContext.h"
      14             : #include "nsCOMPtr.h"
      15             : #include "nsContentUtils.h"
      16             : #include "nsError.h"
      17             : #include "nsGlobalWindow.h"
      18             : #include "nsIContentSecurityPolicy.h"
      19             : #include "nsIDocument.h"
      20             : #include "nsIScriptTimeoutHandler.h"
      21             : #include "nsIXPConnect.h"
      22             : #include "nsJSUtils.h"
      23             : #include "WorkerPrivate.h"
      24             : 
      25             : static const char kSetIntervalStr[] = "setInterval";
      26             : static const char kSetTimeoutStr[] = "setTimeout";
      27             : 
      28             : using namespace mozilla;
      29             : using namespace mozilla::dom;
      30             : using namespace mozilla::dom::workers;
      31             : 
      32             : // Our JS nsIScriptTimeoutHandler implementation.
      33             : class nsJSScriptTimeoutHandler final : public nsIScriptTimeoutHandler
      34             : {
      35             : public:
      36             :   // nsISupports
      37             :   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
      38         169 :   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsJSScriptTimeoutHandler)
      39             : 
      40             :   nsJSScriptTimeoutHandler();
      41             :   // This will call SwapElements on aArguments with an empty array.
      42             :   nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindow* aWindow,
      43             :                            Function& aFunction,
      44             :                            nsTArray<JS::Heap<JS::Value>>&& aArguments,
      45             :                            ErrorResult& aError);
      46             :   nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindow* aWindow,
      47             :                            const nsAString& aExpression, bool* aAllowEval,
      48             :                            ErrorResult& aError);
      49             :   nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
      50             :                            Function& aFunction,
      51             :                            nsTArray<JS::Heap<JS::Value>>&& aArguments);
      52             :   nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
      53             :                            const nsAString& aExpression);
      54             : 
      55             :   virtual const nsAString& GetHandlerText() override;
      56             : 
      57           7 :   virtual Function* GetCallback() override
      58             :   {
      59           7 :     return mFunction;
      60             :   }
      61             : 
      62           7 :   virtual const nsTArray<JS::Value>& GetArgs() override
      63             :   {
      64           7 :     return mArgs;
      65             :   }
      66             : 
      67           0 :   virtual nsresult Call() override
      68             :   {
      69           0 :     return NS_OK;
      70             :   }
      71             : 
      72          11 :   virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
      73             :                            uint32_t* aColumn) override
      74             :   {
      75          11 :     *aFileName = mFileName.get();
      76          11 :     *aLineNo = mLineNo;
      77          11 :     *aColumn = mColumn;
      78          11 :   }
      79             : 
      80           0 :   virtual void MarkForCC() override
      81             :   {
      82           0 :     if (mFunction) {
      83           0 :       mFunction->MarkForCC();
      84             :     }
      85           0 :   }
      86             : 
      87             :   void ReleaseJSObjects();
      88             : 
      89             : private:
      90             :   ~nsJSScriptTimeoutHandler();
      91             : 
      92             :   void Init(JSContext* aCx,
      93             :             nsTArray<JS::Heap<JS::Value>>&& aArguments);
      94             :   void Init(JSContext* aCx);
      95             : 
      96             :   // filename, line number and JS language version string of the
      97             :   // caller of setTimeout()
      98             :   nsCString mFileName;
      99             :   uint32_t mLineNo;
     100             :   uint32_t mColumn;
     101             :   nsTArray<JS::Heap<JS::Value>> mArgs;
     102             : 
     103             :   // The expression to evaluate or function to call. If mFunction is non-null
     104             :   // it should be used, else use mExpr.
     105             :   nsString mExpr;
     106             :   RefPtr<Function> mFunction;
     107             : };
     108             : 
     109             : 
     110             : // nsJSScriptTimeoutHandler
     111             : // QueryInterface implementation for nsJSScriptTimeoutHandler
     112             : NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler)
     113             : 
     114           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler)
     115           0 :   tmp->ReleaseJSObjects();
     116           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     117           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSScriptTimeoutHandler)
     118           0 :   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
     119           0 :     nsAutoCString name("nsJSScriptTimeoutHandler");
     120           0 :     if (tmp->mFunction) {
     121           0 :       JSObject* obj = tmp->mFunction->CallablePreserveColor();
     122           0 :       JSFunction* fun = JS_GetObjectFunction(js::UncheckedUnwrap(obj));
     123           0 :       if (fun && JS_GetFunctionId(fun)) {
     124           0 :         JSFlatString *funId = JS_ASSERT_STRING_IS_FLAT(JS_GetFunctionId(fun));
     125           0 :         size_t size = 1 + JS_PutEscapedFlatString(nullptr, 0, funId, 0);
     126           0 :         char *funIdName = new char[size];
     127           0 :         if (funIdName) {
     128           0 :           JS_PutEscapedFlatString(funIdName, size, funId, 0);
     129           0 :           name.AppendLiteral(" [");
     130           0 :           name.Append(funIdName);
     131           0 :           delete[] funIdName;
     132           0 :           name.Append(']');
     133             :         }
     134             :       }
     135             :     } else {
     136           0 :       name.AppendLiteral(" [");
     137           0 :       name.Append(tmp->mFileName);
     138           0 :       name.Append(':');
     139           0 :       name.AppendInt(tmp->mLineNo);
     140           0 :       name.Append(':');
     141           0 :       name.AppendInt(tmp->mColumn);
     142           0 :       name.Append(']');
     143             :     }
     144           0 :     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name.get());
     145             :   }
     146             :   else {
     147           0 :     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSScriptTimeoutHandler,
     148             :                                       tmp->mRefCnt.get())
     149             :   }
     150             : 
     151           0 :   if (tmp->mFunction) {
     152           0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFunction)
     153             :   }
     154           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     155             : 
     156           7 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler)
     157           8 :   for (uint32_t i = 0; i < tmp->mArgs.Length(); ++i) {
     158           1 :     NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgs[i])
     159             :   }
     160           7 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
     161             : 
     162          76 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSScriptTimeoutHandler)
     163          29 :   NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler)
     164          11 :   NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
     165           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     166           0 : NS_INTERFACE_MAP_END
     167             : 
     168          51 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSScriptTimeoutHandler)
     169          46 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSScriptTimeoutHandler)
     170             : 
     171             : static bool
     172           0 : CheckCSPForEval(JSContext* aCx, nsGlobalWindow* aWindow, ErrorResult& aError)
     173             : {
     174             :   // if CSP is enabled, and setTimeout/setInterval was called with a string,
     175             :   // disable the registration and log an error
     176           0 :   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
     177           0 :   if (!doc) {
     178             :     // if there's no document, we don't have to do anything.
     179           0 :     return true;
     180             :   }
     181             : 
     182           0 :   nsCOMPtr<nsIContentSecurityPolicy> csp;
     183           0 :   aError = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
     184           0 :   if (aError.Failed()) {
     185           0 :     return false;
     186             :   }
     187             : 
     188           0 :   if (!csp) {
     189           0 :     return true;
     190             :   }
     191             : 
     192           0 :   bool allowsEval = true;
     193           0 :   bool reportViolation = false;
     194           0 :   aError = csp->GetAllowsEval(&reportViolation, &allowsEval);
     195           0 :   if (aError.Failed()) {
     196           0 :     return false;
     197             :   }
     198             : 
     199           0 :   if (reportViolation) {
     200             :     // TODO : need actual script sample in violation report.
     201           0 :     NS_NAMED_LITERAL_STRING(scriptSample,
     202             :                             "call to eval() or related function blocked by CSP");
     203             : 
     204             :     // Get the calling location.
     205           0 :     uint32_t lineNum = 0;
     206           0 :     nsAutoString fileNameString;
     207           0 :     if (!nsJSUtils::GetCallingLocation(aCx, fileNameString, &lineNum)) {
     208           0 :       fileNameString.AssignLiteral("unknown");
     209             :     }
     210             : 
     211           0 :     csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
     212             :                              fileNameString, scriptSample, lineNum,
     213           0 :                              EmptyString(), EmptyString());
     214             :   }
     215             : 
     216           0 :   return allowsEval;
     217             : }
     218             : 
     219           0 : nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler()
     220             :   : mLineNo(0)
     221           0 :   , mColumn(0)
     222             : {
     223           0 : }
     224             : 
     225          11 : nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
     226             :                                                    nsGlobalWindow *aWindow,
     227             :                                                    Function& aFunction,
     228             :                                                    nsTArray<JS::Heap<JS::Value>>&& aArguments,
     229          11 :                                                    ErrorResult& aError)
     230             :   : mLineNo(0)
     231             :   , mColumn(0)
     232          11 :   , mFunction(&aFunction)
     233             : {
     234          11 :   if (!aWindow->GetContextInternal() || !aWindow->FastGetGlobalJSObject()) {
     235             :     // This window was already closed, or never properly initialized,
     236             :     // don't let a timer be scheduled on such a window.
     237           0 :     aError.Throw(NS_ERROR_NOT_INITIALIZED);
     238           0 :     return;
     239             :   }
     240             : 
     241          11 :   Init(aCx, Move(aArguments));
     242             : }
     243             : 
     244           0 : nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
     245             :                                                    nsGlobalWindow *aWindow,
     246             :                                                    const nsAString& aExpression,
     247             :                                                    bool* aAllowEval,
     248           0 :                                                    ErrorResult& aError)
     249             :   : mLineNo(0)
     250             :   , mColumn(0)
     251           0 :   , mExpr(aExpression)
     252             : {
     253           0 :   if (!aWindow->GetContextInternal() || !aWindow->FastGetGlobalJSObject()) {
     254             :     // This window was already closed, or never properly initialized,
     255             :     // don't let a timer be scheduled on such a window.
     256           0 :     aError.Throw(NS_ERROR_NOT_INITIALIZED);
     257           0 :     return;
     258             :   }
     259             : 
     260           0 :   *aAllowEval = CheckCSPForEval(aCx, aWindow, aError);
     261           0 :   if (aError.Failed() || !*aAllowEval) {
     262           0 :     return;
     263             :   }
     264             : 
     265           0 :   Init(aCx);
     266             : }
     267             : 
     268           0 : nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
     269             :                                                    WorkerPrivate* aWorkerPrivate,
     270             :                                                    Function& aFunction,
     271           0 :                                                    nsTArray<JS::Heap<JS::Value>>&& aArguments)
     272             :   : mLineNo(0)
     273             :   , mColumn(0)
     274           0 :   , mFunction(&aFunction)
     275             : {
     276           0 :   MOZ_ASSERT(aWorkerPrivate);
     277           0 :   aWorkerPrivate->AssertIsOnWorkerThread();
     278             : 
     279           0 :   Init(aCx, Move(aArguments));
     280           0 : }
     281             : 
     282           0 : nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
     283             :                                                    WorkerPrivate* aWorkerPrivate,
     284           0 :                                                    const nsAString& aExpression)
     285             :   : mLineNo(0)
     286             :   , mColumn(0)
     287           0 :   , mExpr(aExpression)
     288             : {
     289           0 :   MOZ_ASSERT(aWorkerPrivate);
     290           0 :   aWorkerPrivate->AssertIsOnWorkerThread();
     291             : 
     292           0 :   Init(aCx);
     293           0 : }
     294             : 
     295           0 : nsJSScriptTimeoutHandler::~nsJSScriptTimeoutHandler()
     296             : {
     297           0 :   ReleaseJSObjects();
     298           0 : }
     299             : 
     300             : void
     301          11 : nsJSScriptTimeoutHandler::Init(JSContext* aCx,
     302             :                                nsTArray<JS::Heap<JS::Value>>&& aArguments)
     303             : {
     304          11 :   mozilla::HoldJSObjects(this);
     305          11 :   mArgs = Move(aArguments);
     306             : 
     307          11 :   Init(aCx);
     308          11 : }
     309             : 
     310             : void
     311          11 : nsJSScriptTimeoutHandler::Init(JSContext* aCx)
     312             : {
     313             :   // Get the calling location.
     314          11 :   nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo, &mColumn);
     315          11 : }
     316             : 
     317             : void
     318           0 : nsJSScriptTimeoutHandler::ReleaseJSObjects()
     319             : {
     320           0 :   if (mFunction) {
     321           0 :     mFunction = nullptr;
     322           0 :     mArgs.Clear();
     323           0 :     mozilla::DropJSObjects(this);
     324             :   }
     325           0 : }
     326             : 
     327             : const nsAString&
     328           0 : nsJSScriptTimeoutHandler::GetHandlerText()
     329             : {
     330           0 :   NS_ASSERTION(!mFunction, "No expression, so no handler text!");
     331           0 :   return mExpr;
     332             : }
     333             : 
     334             : already_AddRefed<nsIScriptTimeoutHandler>
     335          11 : NS_CreateJSTimeoutHandler(JSContext *aCx, nsGlobalWindow *aWindow,
     336             :                           Function& aFunction,
     337             :                           const Sequence<JS::Value>& aArguments,
     338             :                           ErrorResult& aError)
     339             : {
     340          22 :   nsTArray<JS::Heap<JS::Value>> args;
     341          11 :   if (!args.AppendElements(aArguments, fallible)) {
     342           0 :     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
     343           0 :     return nullptr;
     344             :   }
     345             : 
     346             :   RefPtr<nsJSScriptTimeoutHandler> handler =
     347          33 :     new nsJSScriptTimeoutHandler(aCx, aWindow, aFunction, Move(args), aError);
     348          11 :   return aError.Failed() ? nullptr : handler.forget();
     349             : }
     350             : 
     351             : already_AddRefed<nsIScriptTimeoutHandler>
     352           0 : NS_CreateJSTimeoutHandler(JSContext* aCx, nsGlobalWindow *aWindow,
     353             :                           const nsAString& aExpression, ErrorResult& aError)
     354             : {
     355           0 :   bool allowEval = false;
     356             :   RefPtr<nsJSScriptTimeoutHandler> handler =
     357           0 :     new nsJSScriptTimeoutHandler(aCx, aWindow, aExpression, &allowEval, aError);
     358           0 :   if (aError.Failed() || !allowEval) {
     359           0 :     return nullptr;
     360             :   }
     361             : 
     362           0 :   return handler.forget();
     363             : }
     364             : 
     365             : already_AddRefed<nsIScriptTimeoutHandler>
     366           0 : NS_CreateJSTimeoutHandler(JSContext *aCx, WorkerPrivate* aWorkerPrivate,
     367             :                           Function& aFunction,
     368             :                           const Sequence<JS::Value>& aArguments,
     369             :                           ErrorResult& aError)
     370             : {
     371           0 :   nsTArray<JS::Heap<JS::Value>> args;
     372           0 :   if (!args.AppendElements(aArguments, fallible)) {
     373           0 :     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
     374           0 :     return nullptr;
     375             :   }
     376             : 
     377             :   RefPtr<nsJSScriptTimeoutHandler> handler =
     378           0 :     new nsJSScriptTimeoutHandler(aCx, aWorkerPrivate, aFunction, Move(args));
     379           0 :   return handler.forget();
     380             : }
     381             : 
     382             : already_AddRefed<nsIScriptTimeoutHandler>
     383           0 : NS_CreateJSTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
     384             :                           const nsAString& aExpression)
     385             : {
     386             :   RefPtr<nsJSScriptTimeoutHandler> handler =
     387           0 :     new nsJSScriptTimeoutHandler(aCx, aWorkerPrivate, aExpression);
     388           0 :   return handler.forget();
     389             : }

Generated by: LCOV version 1.13