LCOV - code coverage report
Current view: directory - js/xpconnect/src - XPCThreadContext.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 226 161 71.2 %
Date: 2012-06-02 Functions: 24 16 66.7 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=8 sw=4 et tw=80:
       3                 :  *
       4                 :  * ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is Mozilla Communicator client code, released
      18                 :  * March 31, 1998.
      19                 :  *
      20                 :  * The Initial Developer of the Original Code is
      21                 :  * Netscape Communications Corporation.
      22                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      23                 :  * the Initial Developer. All Rights Reserved.
      24                 :  *
      25                 :  * Contributor(s):
      26                 :  *   John Bandhauer <jband@netscape.com> (original author)
      27                 :  *
      28                 :  * Alternatively, the contents of this file may be used under the terms of
      29                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      30                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      32                 :  * of those above. If you wish to allow use of your version of this file only
      33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      34                 :  * use your version of this file under the terms of the MPL, indicate your
      35                 :  * decision by deleting the provisions above and replace them with the notice
      36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      37                 :  * the provisions above, a recipient may use your version of this file under
      38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      39                 :  *
      40                 :  * ***** END LICENSE BLOCK ***** */
      41                 : 
      42                 : /* Implement global service to track stack of JSContext per thread. */
      43                 : 
      44                 : #include "xpcprivate.h"
      45                 : #include "XPCWrapper.h"
      46                 : #include "mozilla/Mutex.h"
      47                 : #include "nsDOMJSUtils.h"
      48                 : #include "nsIScriptGlobalObject.h"
      49                 : #include "nsNullPrincipal.h"
      50                 : 
      51                 : using namespace mozilla;
      52                 : 
      53                 : /***************************************************************************/
      54                 : 
      55            4218 : XPCJSContextStack::~XPCJSContextStack()
      56                 : {
      57            1406 :     if (mOwnSafeJSContext) {
      58            1403 :         JS_DestroyContext(mOwnSafeJSContext);
      59            1403 :         mOwnSafeJSContext = nsnull;
      60                 :     }
      61            5624 : }
      62                 : 
      63                 : JSContext*
      64          435334 : XPCJSContextStack::Pop()
      65                 : {
      66          435334 :     MOZ_ASSERT(!mStack.IsEmpty());
      67                 : 
      68          435334 :     uint32_t idx = mStack.Length() - 1; // The thing we're popping
      69                 : 
      70          435334 :     JSContext *cx = mStack[idx].cx;
      71                 : 
      72          435334 :     mStack.RemoveElementAt(idx);
      73          435334 :     if (idx == 0)
      74           26826 :         return cx;
      75                 : 
      76          408508 :     --idx; // Advance to new top of the stack
      77                 : 
      78          408508 :     XPCJSContextInfo &e = mStack[idx];
      79          408508 :     NS_ASSERTION(!e.suspendDepth || e.cx, "Shouldn't have suspendDepth without a cx!");
      80          408508 :     if (e.cx) {
      81          220322 :         if (e.suspendDepth) {
      82          159500 :             JS_ResumeRequest(e.cx, e.suspendDepth);
      83          159500 :             e.suspendDepth = 0;
      84                 :         }
      85                 : 
      86          220322 :         if (e.savedFrameChain) {
      87                 :             // Pop() can be called outside any request for e.cx.
      88          440644 :             JSAutoRequest ar(e.cx);
      89          220322 :             JS_RestoreFrameChain(e.cx);
      90          220322 :             e.savedFrameChain = false;
      91                 :         }
      92                 :     }
      93          408508 :     return cx;
      94                 : }
      95                 : 
      96                 : static nsIPrincipal*
      97           44453 : GetPrincipalFromCx(JSContext *cx)
      98                 : {
      99           44453 :     nsIScriptContextPrincipal* scp = GetScriptContextPrincipalFromJSContext(cx);
     100           44453 :     if (scp) {
     101               0 :         nsIScriptObjectPrincipal* globalData = scp->GetObjectPrincipal();
     102               0 :         if (globalData)
     103               0 :             return globalData->GetPrincipal();
     104                 :     }
     105           44453 :     return nsnull;
     106                 : }
     107                 : 
     108                 : bool
     109          435334 : XPCJSContextStack::Push(JSContext *cx)
     110                 : {
     111          435334 :     if (mStack.Length() == 0) {
     112           26826 :         mStack.AppendElement(cx);
     113           26826 :         return true;
     114                 :     }
     115                 : 
     116          408508 :     XPCJSContextInfo &e = mStack[mStack.Length() - 1];
     117          408508 :     if (e.cx) {
     118          220322 :         if (e.cx == cx) {
     119           44453 :             nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
     120           44453 :             if (ssm) {
     121           44453 :                 if (nsIPrincipal* globalObjectPrincipal = GetPrincipalFromCx(cx)) {
     122               0 :                     nsIPrincipal* subjectPrincipal = ssm->GetCxSubjectPrincipal(cx);
     123               0 :                     bool equals = false;
     124               0 :                     globalObjectPrincipal->Equals(subjectPrincipal, &equals);
     125               0 :                     if (equals) {
     126               0 :                         mStack.AppendElement(cx);
     127               0 :                         return true;
     128                 :                     }
     129                 :                 }
     130                 :             }
     131                 :         }
     132                 : 
     133                 :         {
     134                 :             // Push() can be called outside any request for e.cx.
     135          440644 :             JSAutoRequest ar(e.cx);
     136          220322 :             if (!JS_SaveFrameChain(e.cx))
     137               0 :                 return false;
     138          440644 :             e.savedFrameChain = true;
     139                 :         }
     140                 : 
     141          220322 :         if (!cx)
     142          159500 :             e.suspendDepth = JS_SuspendRequest(e.cx);
     143                 :     }
     144                 : 
     145          408508 :     mStack.AppendElement(cx);
     146          408508 :     return true;
     147                 : }
     148                 : 
     149                 : #ifdef DEBUG
     150                 : bool
     151               0 : XPCJSContextStack::DEBUG_StackHasJSContext(JSContext *cx)
     152                 : {
     153               0 :     for (PRUint32 i = 0; i < mStack.Length(); i++)
     154               0 :         if (cx == mStack[i].cx)
     155               0 :             return true;
     156               0 :     return false;
     157                 : }
     158                 : #endif
     159                 : 
     160                 : static JSBool
     161               0 : SafeGlobalResolve(JSContext *cx, JSObject *obj, jsid id)
     162                 : {
     163                 :     JSBool resolved;
     164               0 :     return JS_ResolveStandardClass(cx, obj, id, &resolved);
     165                 : }
     166                 : 
     167                 : static void
     168            1403 : SafeFinalize(JSContext* cx, JSObject* obj)
     169                 : {
     170                 :     nsIScriptObjectPrincipal* sop =
     171            1403 :         static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(obj));
     172            1403 :     NS_IF_RELEASE(sop);
     173            1403 : }
     174                 : 
     175                 : static JSClass global_class = {
     176                 :     "global_for_XPCJSContextStack_SafeJSContext",
     177                 :     XPCONNECT_GLOBAL_FLAGS,
     178                 :     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     179                 :     JS_EnumerateStub, SafeGlobalResolve, JS_ConvertStub, SafeFinalize,
     180                 :     NULL, NULL, NULL, NULL, TraceXPCGlobal
     181                 : };
     182                 : 
     183                 : // We just use the same reporter as the component loader
     184                 : // XXX #include angels cry.
     185                 : extern void
     186                 : mozJSLoaderErrorReporter(JSContext *cx, const char *message, JSErrorReport *rep);
     187                 : 
     188                 : JSContext*
     189          102120 : XPCJSContextStack::GetSafeJSContext()
     190                 : {
     191          102120 :     if (mSafeJSContext)
     192          100716 :         return mSafeJSContext;
     193                 : 
     194                 :     // Start by getting the principal holder and principal for this
     195                 :     // context.  If we can't manage that, don't bother with the rest.
     196            2808 :     nsRefPtr<nsNullPrincipal> principal = new nsNullPrincipal();
     197            1404 :     nsresult rv = principal->Init();
     198            1404 :     if (NS_FAILED(rv))
     199               0 :         return NULL;
     200                 : 
     201            4212 :     nsCOMPtr<nsIScriptObjectPrincipal> sop = new PrincipalHolder(principal);
     202                 : 
     203            2808 :     nsRefPtr<nsXPConnect> xpc = nsXPConnect::GetXPConnect();
     204            1404 :     if (!xpc)
     205               0 :         return NULL;
     206                 : 
     207            1404 :     XPCJSRuntime* xpcrt = xpc->GetRuntime();
     208            1404 :     if (!xpcrt)
     209               0 :         return NULL;
     210                 : 
     211            1404 :     JSRuntime *rt = xpcrt->GetJSRuntime();
     212            1404 :     if (!rt)
     213               0 :         return NULL;
     214                 : 
     215            1404 :     mSafeJSContext = JS_NewContext(rt, 8192);
     216            1404 :     if (!mSafeJSContext)
     217               0 :         return NULL;
     218                 : 
     219                 :     JSObject *glob;
     220                 :     {
     221                 :         // scoped JS Request
     222            2808 :         JSAutoRequest req(mSafeJSContext);
     223                 : 
     224            1404 :         JS_SetErrorReporter(mSafeJSContext, mozJSLoaderErrorReporter);
     225                 : 
     226                 :         JSCompartment *compartment;
     227                 :         nsresult rv = xpc_CreateGlobalObject(mSafeJSContext, &global_class,
     228                 :                                              principal, principal, false,
     229            1404 :                                              &glob, &compartment);
     230            1404 :         if (NS_FAILED(rv))
     231               0 :             glob = nsnull;
     232                 : 
     233            1404 :         if (glob) {
     234                 :             // Make sure the context is associated with a proper compartment
     235                 :             // and not the default compartment.
     236            1404 :             JS_SetGlobalObject(mSafeJSContext, glob);
     237                 : 
     238                 :             // Note: make sure to set the private before calling
     239                 :             // InitClasses
     240            1404 :             nsIScriptObjectPrincipal* priv = nsnull;
     241            1404 :             sop.swap(priv);
     242            1404 :             JS_SetPrivate(glob, priv);
     243                 :         }
     244                 : 
     245                 :         // After this point either glob is null and the
     246                 :         // nsIScriptObjectPrincipal ownership is either handled by the
     247                 :         // nsCOMPtr or dealt with, or we'll release in the finalize
     248                 :         // hook.
     249            1404 :         if (glob && NS_FAILED(xpc->InitClasses(mSafeJSContext, glob))) {
     250               0 :             glob = nsnull;
     251                 :         }
     252                 :     }
     253            1404 :     if (mSafeJSContext && !glob) {
     254                 :         // Destroy the context outside the scope of JSAutoRequest that
     255                 :         // uses the context in its destructor.
     256               0 :         JS_DestroyContext(mSafeJSContext);
     257               0 :         mSafeJSContext = nsnull;
     258                 :     }
     259                 : 
     260                 :     // Save it off so we can destroy it later.
     261            1404 :     mOwnSafeJSContext = mSafeJSContext;
     262                 : 
     263            1404 :     return mSafeJSContext;
     264                 : }
     265                 : 
     266                 : /***************************************************************************/
     267                 : 
     268                 : PRUintn           XPCPerThreadData::gTLSIndex       = BAD_TLS_INDEX;
     269                 : Mutex*            XPCPerThreadData::gLock           = nsnull;
     270                 : XPCPerThreadData* XPCPerThreadData::gThreads        = nsnull;
     271                 : XPCPerThreadData *XPCPerThreadData::sMainThreadData = nsnull;
     272                 : void *            XPCPerThreadData::sMainJSThread   = nsnull;
     273                 : 
     274            1407 : XPCPerThreadData::XPCPerThreadData()
     275                 :     :   mJSContextStack(new XPCJSContextStack()),
     276                 :         mNextThread(nsnull),
     277                 :         mCallContext(nsnull),
     278                 :         mResolveName(JSID_VOID),
     279                 :         mResolvingWrapper(nsnull),
     280                 :         mExceptionManager(nsnull),
     281                 :         mException(nsnull),
     282                 :         mExceptionManagerNotAvailable(false),
     283                 :         mAutoRoots(nsnull)
     284                 : #ifdef XPC_CHECK_WRAPPER_THREADSAFETY
     285            1407 :       , mWrappedNativeThreadsafetyReportDepth(0)
     286                 : #endif
     287                 : {
     288            1407 :     MOZ_COUNT_CTOR(xpcPerThreadData);
     289            1407 :     if (gLock) {
     290            2814 :         MutexAutoLock lock(*gLock);
     291            1407 :         mNextThread = gThreads;
     292            1407 :         gThreads = this;
     293                 :     }
     294            1407 : }
     295                 : 
     296                 : void
     297            2809 : XPCPerThreadData::Cleanup()
     298                 : {
     299            5618 :     while (mAutoRoots)
     300               0 :         mAutoRoots->Unlink();
     301            2809 :     NS_IF_RELEASE(mExceptionManager);
     302            2809 :     NS_IF_RELEASE(mException);
     303            2809 :     delete mJSContextStack;
     304            2809 :     mJSContextStack = nsnull;
     305                 : 
     306            2809 :     if (mCallContext)
     307               0 :         mCallContext->SystemIsBeingShutDown();
     308            2809 : }
     309                 : 
     310            1406 : XPCPerThreadData::~XPCPerThreadData()
     311                 : {
     312                 :     /* Be careful to ensure that both any update to |gThreads| and the
     313                 :        decision about whether or not to destroy the lock, are done
     314                 :        atomically.  See bug 557586. */
     315            1406 :     bool doDestroyLock = false;
     316                 : 
     317            1406 :     MOZ_COUNT_DTOR(xpcPerThreadData);
     318                 : 
     319            1406 :     Cleanup();
     320                 : 
     321                 :     // Unlink 'this' from the list of threads.
     322            1406 :     if (gLock) {
     323            2812 :         MutexAutoLock lock(*gLock);
     324            1406 :         if (gThreads == this)
     325            1406 :             gThreads = mNextThread;
     326                 :         else {
     327               0 :             XPCPerThreadData* cur = gThreads;
     328               0 :             while (cur) {
     329               0 :                 if (cur->mNextThread == this) {
     330               0 :                     cur->mNextThread = mNextThread;
     331               0 :                     break;
     332                 :                 }
     333               0 :                 cur = cur->mNextThread;
     334                 :             }
     335                 :         }
     336            1406 :         if (!gThreads)
     337            1403 :             doDestroyLock = true;
     338                 :     }
     339                 : 
     340            1406 :     if (gLock && doDestroyLock) {
     341            1403 :         delete gLock;
     342            1403 :         gLock = nsnull;
     343                 :     }
     344            1406 : }
     345                 : 
     346                 : static void
     347            1406 : xpc_ThreadDataDtorCB(void* ptr)
     348                 : {
     349            1406 :     XPCPerThreadData* data = (XPCPerThreadData*) ptr;
     350            1406 :     delete data;
     351            1406 : }
     352                 : 
     353           11922 : void XPCPerThreadData::TraceJS(JSTracer *trc)
     354                 : {
     355                 : #ifdef XPC_TRACK_AUTOMARKINGPTR_STATS
     356                 :     {
     357                 :         static int maxLength = 0;
     358                 :         int length = 0;
     359                 :         for (AutoMarkingPtr* p = mAutoRoots; p; p = p->GetNext())
     360                 :             length++;
     361                 :         if (length > maxLength)
     362                 :             maxLength = length;
     363                 :         printf("XPC gc on thread %x with %d AutoMarkingPtrs (%d max so far)\n",
     364                 :                this, length, maxLength);
     365                 :     }
     366                 : #endif
     367                 : 
     368           11922 :     if (mAutoRoots)
     369               7 :         mAutoRoots->TraceJS(trc);
     370           11922 : }
     371                 : 
     372           11922 : void XPCPerThreadData::MarkAutoRootsAfterJSFinalize()
     373                 : {
     374           11922 :     if (mAutoRoots)
     375               7 :         mAutoRoots->MarkAfterJSFinalize();
     376           11922 : }
     377                 : 
     378                 : // static
     379                 : XPCPerThreadData*
     380            2812 : XPCPerThreadData::GetDataImpl(JSContext *cx)
     381                 : {
     382                 :     XPCPerThreadData* data;
     383                 : 
     384            2812 :     if (!gLock) {
     385            1404 :         gLock = new Mutex("XPCPerThreadData.gLock");
     386                 :     }
     387                 : 
     388            2812 :     if (gTLSIndex == BAD_TLS_INDEX) {
     389            2808 :         MutexAutoLock lock(*gLock);
     390                 :         // check again now that we have the lock...
     391            1404 :         if (gTLSIndex == BAD_TLS_INDEX) {
     392            1404 :             if (PR_FAILURE ==
     393            1404 :                 PR_NewThreadPrivateIndex(&gTLSIndex, xpc_ThreadDataDtorCB)) {
     394               0 :                 NS_ERROR("PR_NewThreadPrivateIndex failed!");
     395               0 :                 gTLSIndex = BAD_TLS_INDEX;
     396               0 :                 return nsnull;
     397                 :             }
     398                 :         }
     399                 :     }
     400                 : 
     401            2812 :     data = (XPCPerThreadData*) PR_GetThreadPrivate(gTLSIndex);
     402            2812 :     if (!data) {
     403            1407 :         data = new XPCPerThreadData();
     404            1407 :         if (!data || !data->IsValid()) {
     405               0 :             NS_ERROR("new XPCPerThreadData() failed!");
     406               0 :             delete data;
     407               0 :             return nsnull;
     408                 :         }
     409            1407 :         if (PR_FAILURE == PR_SetThreadPrivate(gTLSIndex, data)) {
     410               0 :             NS_ERROR("PR_SetThreadPrivate failed!");
     411               0 :             delete data;
     412               0 :             return nsnull;
     413                 :         }
     414                 :     }
     415                 : 
     416            2812 :     if (cx && !sMainJSThread && NS_IsMainThread()) {
     417            1404 :         sMainJSThread = js::GetOwnerThread(cx);
     418                 : 
     419            1404 :         sMainThreadData = data;
     420                 : 
     421            1404 :         sMainThreadData->mThread = PR_GetCurrentThread();
     422                 :     }
     423                 : 
     424            2812 :     return data;
     425                 : }
     426                 : 
     427                 : // static
     428                 : void
     429            1403 : XPCPerThreadData::CleanupAllThreads()
     430                 : {
     431                 :     // I've questioned the sense of cleaning up other threads' data from the
     432                 :     // start. But I got talked into it. Now I see that we *can't* do all the
     433                 :     // cleaup while holding this lock. So, we are going to go to the trouble
     434                 :     // to copy out the data that needs to be cleaned up *outside* of
     435                 :     // the lock. Yuk!
     436                 : 
     437            1403 :     XPCJSContextStack** stacks = nsnull;
     438            1403 :     int count = 0;
     439                 :     int i;
     440                 : 
     441            1403 :     if (gLock) {
     442            2806 :         MutexAutoLock lock(*gLock);
     443                 : 
     444            2806 :         for (XPCPerThreadData* cur = gThreads; cur; cur = cur->mNextThread)
     445            1403 :             count++;
     446                 : 
     447            2806 :         stacks = (XPCJSContextStack**) new XPCJSContextStack*[count] ;
     448            1403 :         if (stacks) {
     449            1403 :             i = 0;
     450            2806 :             for (XPCPerThreadData* cur = gThreads; cur; cur = cur->mNextThread) {
     451            1403 :                 stacks[i++] = cur->mJSContextStack;
     452            1403 :                 cur->mJSContextStack = nsnull;
     453            1403 :                 cur->Cleanup();
     454                 :             }
     455                 :         }
     456                 :     }
     457                 : 
     458            1403 :     if (stacks) {
     459            2806 :         for (i = 0; i < count; i++)
     460            1403 :             delete stacks[i];
     461            1403 :         delete [] stacks;
     462                 :     }
     463                 : 
     464            1403 :     if (gTLSIndex != BAD_TLS_INDEX)
     465            1403 :         PR_SetThreadPrivate(gTLSIndex, nsnull);
     466            1403 : }
     467                 : 
     468                 : // static
     469                 : XPCPerThreadData*
     470           71532 : XPCPerThreadData::IterateThreads(XPCPerThreadData** iteratorp)
     471                 : {
     472           71532 :     *iteratorp = (*iteratorp == nsnull) ? gThreads : (*iteratorp)->mNextThread;
     473           71532 :     return *iteratorp;
     474                 : }
     475                 : 
     476               0 : NS_IMPL_ISUPPORTS1(nsXPCJSContextStackIterator, nsIJSContextStackIterator)
     477                 : 
     478                 : NS_IMETHODIMP
     479               0 : nsXPCJSContextStackIterator::Reset(nsIJSContextStack *aStack)
     480                 : {
     481               0 :     NS_ASSERTION(aStack == nsXPConnect::GetXPConnect(),
     482                 :                  "aStack must be implemented by XPConnect singleton");
     483               0 :     XPCPerThreadData* data = XPCPerThreadData::GetData(nsnull);
     484               0 :     if (!data)
     485               0 :         return NS_ERROR_FAILURE;
     486               0 :     mStack = data->GetJSContextStack()->GetStack();
     487               0 :     if (mStack->IsEmpty())
     488               0 :         mStack = nsnull;
     489                 :     else
     490               0 :         mPosition = mStack->Length() - 1;
     491                 : 
     492               0 :     return NS_OK;
     493                 : }
     494                 : 
     495                 : NS_IMETHODIMP
     496               0 : nsXPCJSContextStackIterator::Done(bool *aDone)
     497                 : {
     498               0 :     *aDone = !mStack;
     499               0 :     return NS_OK;
     500                 : }
     501                 : 
     502                 : NS_IMETHODIMP
     503               0 : nsXPCJSContextStackIterator::Prev(JSContext **aContext)
     504                 : {
     505               0 :     if (!mStack)
     506               0 :         return NS_ERROR_NOT_INITIALIZED;
     507                 : 
     508               0 :     *aContext = mStack->ElementAt(mPosition).cx;
     509                 : 
     510               0 :     if (mPosition == 0)
     511               0 :         mStack = nsnull;
     512                 :     else
     513               0 :         --mPosition;
     514                 : 
     515               0 :     return NS_OK;
     516                 : }
     517                 : 

Generated by: LCOV version 1.7