LCOV - code coverage report
Current view: directory - dom/base - nsJSTimeoutHandler.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 163 3 1.8 %
Date: 2012-06-02 Functions: 25 3 12.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim: set ts=2 sw=2 et tw=78: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is mozilla.org code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Netscape Communications Corporation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *    Mark Hammond <mhammond@skippinet.com.au>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "nsCOMPtr.h"
      41                 : #include "nsIScriptContext.h"
      42                 : #include "nsIArray.h"
      43                 : #include "nsIScriptTimeoutHandler.h"
      44                 : #include "nsIXPConnect.h"
      45                 : #include "nsIJSRuntimeService.h"
      46                 : #include "nsJSUtils.h"
      47                 : #include "nsDOMJSUtils.h"
      48                 : #include "nsContentUtils.h"
      49                 : #include "nsJSEnvironment.h"
      50                 : #include "nsServiceManagerUtils.h"
      51                 : #include "nsDOMError.h"
      52                 : #include "nsGlobalWindow.h"
      53                 : #include "nsIContentSecurityPolicy.h"
      54                 : #include "nsAlgorithm.h"
      55                 : 
      56                 : static const char kSetIntervalStr[] = "setInterval";
      57                 : static const char kSetTimeoutStr[] = "setTimeout";
      58                 : 
      59                 : // Our JS nsIScriptTimeoutHandler implementation.
      60                 : class nsJSScriptTimeoutHandler: public nsIScriptTimeoutHandler
      61                 : {
      62                 : public:
      63                 :   // nsISupports
      64               0 :   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
      65            1464 :   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsJSScriptTimeoutHandler)
      66                 : 
      67                 :   nsJSScriptTimeoutHandler();
      68                 :   ~nsJSScriptTimeoutHandler();
      69                 : 
      70                 :   virtual const PRUnichar *GetHandlerText();
      71               0 :   virtual JSObject *GetScriptObject() {
      72               0 :     return mFunObj;
      73                 :   }
      74               0 :   virtual void GetLocation(const char **aFileName, PRUint32 *aLineNo) {
      75               0 :     *aFileName = mFileName.get();
      76               0 :     *aLineNo = mLineNo;
      77               0 :   }
      78                 : 
      79               0 :   virtual PRUint32 GetScriptTypeID() {
      80               0 :         return nsIProgrammingLanguage::JAVASCRIPT;
      81                 :   }
      82               0 :   virtual PRUint32 GetScriptVersion() {
      83               0 :         return mVersion;
      84                 :   }
      85                 : 
      86               0 :   virtual nsIArray *GetArgv() {
      87               0 :     return mArgv;
      88                 :   }
      89                 : 
      90                 :   nsresult Init(nsGlobalWindow *aWindow, bool *aIsInterval,
      91                 :                 PRInt32 *aInterval);
      92                 : 
      93                 :   void ReleaseJSObjects();
      94                 : 
      95                 : private:
      96                 : 
      97                 :   nsCOMPtr<nsIScriptContext> mContext;
      98                 : 
      99                 :   // filename, line number and JS language version string of the
     100                 :   // caller of setTimeout()
     101                 :   nsCString mFileName;
     102                 :   PRUint32 mLineNo;
     103                 :   PRUint32 mVersion;
     104                 :   nsCOMPtr<nsIJSArgArray> mArgv;
     105                 : 
     106                 :   // The JS expression to evaluate or function to call, if !mExpr
     107                 :   JSFlatString *mExpr;
     108                 :   JSObject *mFunObj;
     109                 : };
     110                 : 
     111                 : 
     112                 : // nsJSScriptTimeoutHandler
     113                 : // QueryInterface implementation for nsJSScriptTimeoutHandler
     114            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler)
     115               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler)
     116               0 :   tmp->ReleaseJSObjects();
     117               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     118               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSScriptTimeoutHandler)
     119               0 :   if (NS_UNLIKELY(cb.WantDebugInfo())) {
     120               0 :     nsCAutoString foo("nsJSScriptTimeoutHandler");
     121               0 :     if (tmp->mExpr) {
     122               0 :       foo.AppendLiteral(" [");
     123               0 :       foo.Append(tmp->mFileName);
     124               0 :       foo.AppendLiteral(":");
     125               0 :       foo.AppendInt(tmp->mLineNo);
     126               0 :       foo.AppendLiteral("]");
     127                 :     }
     128               0 :     else if (tmp->mFunObj) {
     129               0 :       JSFunction* fun = JS_GetObjectFunction(tmp->mFunObj);
     130               0 :       if (fun && JS_GetFunctionId(fun)) {
     131               0 :         JSFlatString *funId = JS_ASSERT_STRING_IS_FLAT(JS_GetFunctionId(fun));
     132               0 :         size_t size = 1 + JS_PutEscapedFlatString(NULL, 0, funId, 0);
     133               0 :         char *name = new char[size];
     134               0 :         if (name) {
     135               0 :           JS_PutEscapedFlatString(name, size, funId, 0);
     136               0 :           foo.AppendLiteral(" [");
     137               0 :           foo.Append(name);
     138               0 :           delete[] name;
     139               0 :           foo.AppendLiteral("]");
     140                 :         }
     141                 :       }
     142                 :     }
     143                 :     cb.DescribeRefCountedNode(tmp->mRefCnt.get(),
     144               0 :                               sizeof(nsJSScriptTimeoutHandler), foo.get());
     145                 :   }
     146                 :   else {
     147               0 :     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSScriptTimeoutHandler,
     148                 :                                       tmp->mRefCnt.get())
     149                 :   }
     150                 : 
     151               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
     152               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArgv)
     153               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
     154               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     155                 : 
     156               0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler)
     157               0 :   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mExpr)
     158               0 :   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mFunObj)
     159               0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
     160                 : 
     161               0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSScriptTimeoutHandler)
     162               0 :   NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler)
     163               0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     164               0 : NS_INTERFACE_MAP_END
     165                 : 
     166               0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSScriptTimeoutHandler)
     167               0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSScriptTimeoutHandler)
     168                 : 
     169               0 : nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler() :
     170                 :   mLineNo(0),
     171                 :   mVersion(nsnull),
     172                 :   mExpr(nsnull),
     173               0 :   mFunObj(nsnull)
     174                 : {
     175               0 : }
     176                 : 
     177               0 : nsJSScriptTimeoutHandler::~nsJSScriptTimeoutHandler()
     178                 : {
     179               0 :   ReleaseJSObjects();
     180               0 : }
     181                 : 
     182                 : void
     183               0 : nsJSScriptTimeoutHandler::ReleaseJSObjects()
     184                 : {
     185               0 :   if (mExpr || mFunObj) {
     186               0 :     if (mExpr) {
     187               0 :       NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
     188               0 :       mExpr = nsnull;
     189               0 :     } else if (mFunObj) {
     190               0 :       NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
     191               0 :       mFunObj = nsnull;
     192                 :     } else {
     193               0 :       NS_WARNING("No func and no expr - roots may not have been removed");
     194                 :     }
     195                 :   }
     196               0 : }
     197                 : 
     198                 : nsresult
     199               0 : nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
     200                 :                                PRInt32 *aInterval)
     201                 : {
     202               0 :   mContext = aWindow->GetContextInternal();
     203               0 :   if (!mContext) {
     204                 :     // This window was already closed, or never properly initialized,
     205                 :     // don't let a timer be scheduled on such a window.
     206                 : 
     207               0 :     return NS_ERROR_NOT_INITIALIZED;
     208                 :   }
     209                 : 
     210               0 :   nsAXPCNativeCallContext *ncc = nsnull;
     211               0 :   nsresult rv = nsContentUtils::XPConnect()->
     212               0 :     GetCurrentNativeCallContext(&ncc);
     213               0 :   NS_ENSURE_SUCCESS(rv, rv);
     214                 : 
     215               0 :   if (!ncc)
     216               0 :     return NS_ERROR_NOT_AVAILABLE;
     217                 : 
     218               0 :   JSContext *cx = nsnull;
     219                 : 
     220               0 :   rv = ncc->GetJSContext(&cx);
     221               0 :   NS_ENSURE_SUCCESS(rv, rv);
     222                 : 
     223                 :   PRUint32 argc;
     224               0 :   jsval *argv = nsnull;
     225                 : 
     226               0 :   ncc->GetArgc(&argc);
     227               0 :   ncc->GetArgvPtr(&argv);
     228                 : 
     229               0 :   JSFlatString *expr = nsnull;
     230               0 :   JSObject *funobj = nsnull;
     231                 : 
     232               0 :   JSAutoRequest ar(cx);
     233                 : 
     234               0 :   if (argc < 1) {
     235                 :     ::JS_ReportError(cx, "Function %s requires at least 2 parameter",
     236               0 :                      *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
     237               0 :     return NS_ERROR_DOM_TYPE_ERR;
     238                 :   }
     239                 : 
     240               0 :   int32_t interval = 0;
     241               0 :   if (argc > 1 && !::JS_ValueToECMAInt32(cx, argv[1], &interval)) {
     242                 :     ::JS_ReportError(cx,
     243                 :                      "Second argument to %s must be a millisecond interval",
     244               0 :                      aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
     245               0 :     return NS_ERROR_DOM_TYPE_ERR;
     246                 :   }
     247                 : 
     248               0 :   if (argc == 1) {
     249                 :     // If no interval was specified, treat this like a timeout, to avoid
     250                 :     // setting an interval of 0 milliseconds.
     251               0 :     *aIsInterval = false;
     252                 :   }
     253                 : 
     254               0 :   switch (::JS_TypeOfValue(cx, argv[0])) {
     255                 :   case JSTYPE_FUNCTION:
     256               0 :     funobj = JSVAL_TO_OBJECT(argv[0]);
     257               0 :     break;
     258                 : 
     259                 :   case JSTYPE_STRING:
     260                 :   case JSTYPE_OBJECT:
     261                 :     {
     262               0 :       JSString *str = ::JS_ValueToString(cx, argv[0]);
     263               0 :       if (!str)
     264               0 :         return NS_ERROR_OUT_OF_MEMORY;
     265                 : 
     266               0 :       expr = ::JS_FlattenString(cx, str);
     267               0 :       if (!expr)
     268               0 :           return NS_ERROR_OUT_OF_MEMORY;
     269                 : 
     270               0 :       argv[0] = STRING_TO_JSVAL(str);
     271                 :     }
     272               0 :     break;
     273                 : 
     274                 :   default:
     275                 :     ::JS_ReportError(cx, "useless %s call (missing quotes around argument?)",
     276               0 :                      *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
     277                 : 
     278                 :     // Return an error that nsGlobalWindow can recognize and turn into NS_OK.
     279               0 :     return NS_ERROR_DOM_TYPE_ERR;
     280                 :   }
     281                 : 
     282               0 :   if (expr) {
     283                 :     // if CSP is enabled, and setTimeout/setInterval was called with a string
     284                 :     // or object, disable the registration and log an error
     285               0 :     nsCOMPtr<nsIDocument> doc = do_QueryInterface(aWindow->GetExtantDocument());
     286                 : 
     287               0 :     if (doc) {
     288               0 :       nsCOMPtr<nsIContentSecurityPolicy> csp;
     289               0 :       nsresult rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
     290               0 :       NS_ENSURE_SUCCESS(rv, rv);
     291                 : 
     292               0 :       if (csp) {
     293                 :         bool allowsEval;
     294                 :         // this call will send violation reports as warranted (and return true if
     295                 :         // reportOnly is set).
     296               0 :         rv = csp->GetAllowsEval(&allowsEval);
     297               0 :         NS_ENSURE_SUCCESS(rv, rv);
     298                 : 
     299               0 :         if (!allowsEval) {
     300                 :           ::JS_ReportError(cx, "call to %s blocked by CSP",
     301               0 :                             *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
     302                 : 
     303                 :           // Note: Our only caller knows to turn NS_ERROR_DOM_TYPE_ERR into NS_OK.
     304               0 :           return NS_ERROR_DOM_TYPE_ERR;
     305                 :         }
     306                 :       }
     307                 :     } // if there's no document, we don't have to do anything.
     308                 : 
     309               0 :     rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
     310               0 :     NS_ENSURE_SUCCESS(rv, rv);
     311                 : 
     312               0 :     mExpr = expr;
     313                 : 
     314                 :     // Get the calling location.
     315                 :     const char *filename;
     316               0 :     if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo)) {
     317               0 :       mFileName.Assign(filename);
     318                 :     }
     319               0 :   } else if (funobj) {
     320               0 :     rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
     321               0 :     NS_ENSURE_SUCCESS(rv, rv);
     322                 : 
     323               0 :     mFunObj = funobj;
     324                 : 
     325                 :     // Create our arg array.  argc is the number of arguments passed
     326                 :     // to setTimeout or setInterval; the first two are our callback
     327                 :     // and the delay, so only arguments after that need to go in our
     328                 :     // array.
     329               0 :     nsCOMPtr<nsIJSArgArray> array;
     330                 :     // NS_MAX(argc - 2, 0) wouldn't work right because argc is unsigned.
     331               0 :     rv = NS_CreateJSArgv(cx, NS_MAX(argc, 2u) - 2, nsnull,
     332               0 :                          getter_AddRefs(array));
     333               0 :     if (NS_FAILED(rv)) {
     334               0 :       return NS_ERROR_OUT_OF_MEMORY;
     335                 :     }
     336                 : 
     337                 :     PRUint32 dummy;
     338               0 :     jsval *jsargv = nsnull;
     339               0 :     array->GetArgs(&dummy, reinterpret_cast<void **>(&jsargv));
     340                 : 
     341                 :     // jsargv might be null if we have argc <= 2
     342               0 :     if (jsargv) {
     343               0 :       for (PRInt32 i = 2; (PRUint32)i < argc; ++i) {
     344               0 :         jsargv[i - 2] = argv[i];
     345                 :       }
     346                 :     } else {
     347               0 :       NS_ASSERTION(argc <= 2, "Why do we have no jsargv when we have arguments?");
     348                 :     }
     349               0 :     mArgv = array;
     350                 :   } else {
     351               0 :     NS_WARNING("No func and no expr - why are we here?");
     352                 :   }
     353               0 :   *aInterval = interval;
     354               0 :   return NS_OK;
     355                 : }
     356                 : 
     357                 : const PRUnichar *
     358               0 : nsJSScriptTimeoutHandler::GetHandlerText()
     359                 : {
     360               0 :   NS_ASSERTION(mExpr, "No expression, so no handler text!");
     361               0 :   return ::JS_GetFlatStringChars(mExpr);
     362                 : }
     363                 : 
     364               0 : nsresult NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow,
     365                 :                                    bool *aIsInterval,
     366                 :                                    PRInt32 *aInterval,
     367                 :                                    nsIScriptTimeoutHandler **aRet)
     368                 : {
     369               0 :   *aRet = nsnull;
     370               0 :   nsJSScriptTimeoutHandler *handler = new nsJSScriptTimeoutHandler();
     371               0 :   if (!handler)
     372               0 :     return NS_ERROR_OUT_OF_MEMORY;
     373                 : 
     374               0 :   nsresult rv = handler->Init(aWindow, aIsInterval, aInterval);
     375               0 :   if (NS_FAILED(rv)) {
     376               0 :     delete handler;
     377               0 :     return rv;
     378                 :   }
     379                 : 
     380               0 :   NS_ADDREF(*aRet = handler);
     381                 : 
     382               0 :   return NS_OK;
     383            4392 : }

Generated by: LCOV version 1.7