LCOV - code coverage report
Current view: top level - dom/script - ScriptSettings.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 199 391 50.9 %
Date: 2017-07-14 16:53:18 Functions: 40 55 72.7 %
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 "mozilla/dom/ScriptSettings.h"
       8             : #include "mozilla/ThreadLocal.h"
       9             : #include "mozilla/Assertions.h"
      10             : #include "mozilla/CycleCollectedJSContext.h"
      11             : 
      12             : #include "jsapi.h"
      13             : #include "xpcpublic.h"
      14             : #include "nsIGlobalObject.h"
      15             : #include "nsIDocShell.h"
      16             : #include "nsIScriptGlobalObject.h"
      17             : #include "nsIScriptContext.h"
      18             : #include "nsContentUtils.h"
      19             : #include "nsGlobalWindow.h"
      20             : #include "nsPIDOMWindow.h"
      21             : #include "nsTArray.h"
      22             : #include "nsJSUtils.h"
      23             : #include "nsDOMJSUtils.h"
      24             : #include "WorkerPrivate.h"
      25             : 
      26             : namespace mozilla {
      27             : namespace dom {
      28             : 
      29             : static MOZ_THREAD_LOCAL(ScriptSettingsStackEntry*) sScriptSettingsTLS;
      30             : static bool sScriptSettingsTLSInitialized;
      31             : 
      32             : class ScriptSettingsStack
      33             : {
      34             : public:
      35      396108 :   static ScriptSettingsStackEntry* Top() {
      36      396108 :     return sScriptSettingsTLS.get();
      37             :   }
      38             : 
      39        8404 :   static void Push(ScriptSettingsStackEntry* aEntry)
      40             :   {
      41        8404 :     MOZ_ASSERT(!aEntry->mOlder);
      42             :     // Whenever JSAPI use is disabled, the next stack entry pushed must
      43             :     // not be an AutoIncumbentScript.
      44        8404 :     MOZ_ASSERT_IF(!Top() || Top()->NoJSAPI(),
      45             :                   !aEntry->IsIncumbentScript());
      46             :     // Whenever the top entry is not an incumbent canidate, the next stack entry
      47             :     // pushed must not be an AutoIncumbentScript.
      48        8404 :     MOZ_ASSERT_IF(Top() && !Top()->IsIncumbentCandidate(),
      49             :                   !aEntry->IsIncumbentScript());
      50             : 
      51        8404 :     aEntry->mOlder = Top();
      52        8404 :     sScriptSettingsTLS.set(aEntry);
      53        8404 :   }
      54             : 
      55        8386 :   static void Pop(ScriptSettingsStackEntry* aEntry)
      56             :   {
      57        8386 :     MOZ_ASSERT(aEntry == Top());
      58        8386 :     sScriptSettingsTLS.set(aEntry->mOlder);
      59        8386 :   }
      60             : 
      61         138 :   static nsIGlobalObject* IncumbentGlobal()
      62             :   {
      63         138 :     ScriptSettingsStackEntry* entry = Top();
      64         138 :     while (entry) {
      65         138 :       if (entry->IsIncumbentCandidate()) {
      66         138 :         return entry->mGlobalObject;
      67             :       }
      68           0 :       entry = entry->mOlder;
      69             :     }
      70           0 :     return nullptr;
      71             :   }
      72             : 
      73           4 :   static ScriptSettingsStackEntry* EntryPoint()
      74             :   {
      75           4 :     ScriptSettingsStackEntry* entry = Top();
      76           6 :     while (entry) {
      77           5 :       if (entry->IsEntryCandidate()) {
      78           4 :         return entry;
      79             :       }
      80           1 :       entry = entry->mOlder;
      81             :     }
      82           0 :     return nullptr;
      83             :   }
      84             : 
      85           4 :   static nsIGlobalObject* EntryGlobal()
      86             :   {
      87           4 :     ScriptSettingsStackEntry* entry = EntryPoint();
      88           4 :     if (!entry) {
      89           0 :       return nullptr;
      90             :     }
      91           4 :     return entry->mGlobalObject;
      92             :   }
      93             : 
      94             : #ifdef DEBUG
      95       37100 :   static ScriptSettingsStackEntry* TopNonIncumbentScript()
      96             :   {
      97       37100 :     ScriptSettingsStackEntry* entry = Top();
      98       38774 :     while (entry) {
      99       37937 :       if (!entry->IsIncumbentScript()) {
     100       37100 :         return entry;
     101             :       }
     102         837 :       entry = entry->mOlder;
     103             :     }
     104           0 :     return nullptr;
     105             :   }
     106             : #endif // DEBUG
     107             : 
     108             : };
     109             : 
     110             : static unsigned long gRunToCompletionListeners = 0;
     111             : 
     112             : void
     113           0 : UseEntryScriptProfiling()
     114             : {
     115           0 :   MOZ_ASSERT(NS_IsMainThread());
     116           0 :   ++gRunToCompletionListeners;
     117           0 : }
     118             : 
     119             : void
     120           0 : UnuseEntryScriptProfiling()
     121             : {
     122           0 :   MOZ_ASSERT(NS_IsMainThread());
     123           0 :   MOZ_ASSERT(gRunToCompletionListeners > 0);
     124           0 :   --gRunToCompletionListeners;
     125           0 : }
     126             : 
     127             : void
     128           4 : InitScriptSettings()
     129             : {
     130           4 :   bool success = sScriptSettingsTLS.init();
     131           4 :   if (!success) {
     132           0 :     MOZ_CRASH();
     133             :   }
     134             : 
     135           4 :   sScriptSettingsTLS.set(nullptr);
     136           4 :   sScriptSettingsTLSInitialized = true;
     137           4 : }
     138             : 
     139             : void
     140           0 : DestroyScriptSettings()
     141             : {
     142           0 :   MOZ_ASSERT(sScriptSettingsTLS.get() == nullptr);
     143           0 : }
     144             : 
     145             : bool
     146         455 : ScriptSettingsInitialized()
     147             : {
     148         455 :   return sScriptSettingsTLSInitialized;
     149             : }
     150             : 
     151      126599 : ScriptSettingsStackEntry::ScriptSettingsStackEntry(nsIGlobalObject* aGlobal,
     152      126599 :                                                    Type aType)
     153             :   : mGlobalObject(aGlobal)
     154             :   , mType(aType)
     155      126599 :   , mOlder(nullptr)
     156             : {
     157      126598 :   MOZ_ASSERT_IF(IsIncumbentCandidate() && !NoJSAPI(), mGlobalObject);
     158      126599 :   MOZ_ASSERT(!mGlobalObject || mGlobalObject->GetGlobalJSObject(),
     159             :              "Must have an actual JS global for the duration on the stack");
     160      126599 :   MOZ_ASSERT(!mGlobalObject ||
     161             :              JS_IsGlobalObject(mGlobalObject->GetGlobalJSObject()),
     162             :              "No outer windows allowed");
     163      126599 : }
     164             : 
     165      253162 : ScriptSettingsStackEntry::~ScriptSettingsStackEntry()
     166             : {
     167             :   // We must have an actual JS global for the entire time this is on the stack.
     168      126581 :   MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->GetGlobalJSObject());
     169      126581 : }
     170             : 
     171             : // If the entry or incumbent global ends up being something that the subject
     172             : // principal doesn't subsume, we don't want to use it. This never happens on
     173             : // the web, but can happen with asymmetric privilege relationships (i.e.
     174             : // ExpandedPrincipal and System Principal).
     175             : //
     176             : // The most correct thing to use instead would be the topmost global on the
     177             : // callstack whose principal is subsumed by the subject principal. But that's
     178             : // hard to compute, so we just substitute the global of the current
     179             : // compartment. In practice, this is fine.
     180             : //
     181             : // Note that in particular things like:
     182             : //
     183             : // |SpecialPowers.wrap(crossOriginWindow).eval(open())|
     184             : //
     185             : // trigger this case. Although both the entry global and the current global
     186             : // have normal principals, the use of Gecko-specific System-Principaled JS
     187             : // puts the code from two different origins on the callstack at once, which
     188             : // doesn't happen normally on the web.
     189             : static nsIGlobalObject*
     190         628 : ClampToSubject(nsIGlobalObject* aGlobalOrNull)
     191             : {
     192         628 :   if (!aGlobalOrNull || !NS_IsMainThread()) {
     193           0 :     return aGlobalOrNull;
     194             :   }
     195             : 
     196         628 :   nsIPrincipal* globalPrin = aGlobalOrNull->PrincipalOrNull();
     197         628 :   NS_ENSURE_TRUE(globalPrin, GetCurrentGlobal());
     198         628 :   if (!nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller()->SubsumesConsideringDomain(globalPrin)) {
     199           0 :     return GetCurrentGlobal();
     200             :   }
     201             : 
     202         628 :   return aGlobalOrNull;
     203             : }
     204             : 
     205             : nsIGlobalObject*
     206           4 : GetEntryGlobal()
     207             : {
     208           4 :   return ClampToSubject(ScriptSettingsStack::EntryGlobal());
     209             : }
     210             : 
     211             : nsIDocument*
     212           2 : GetEntryDocument()
     213             : {
     214           2 :   nsIGlobalObject* global = GetEntryGlobal();
     215           4 :   nsCOMPtr<nsPIDOMWindowInner> entryWin = do_QueryInterface(global);
     216             : 
     217             :   // If our entry global isn't a window, see if it's an addon scope associated
     218             :   // with a window. If it is, the caller almost certainly wants that rather
     219             :   // than null.
     220           2 :   if (!entryWin && global) {
     221           2 :     if (auto* win = xpc::AddonWindowOrNull(global->GetGlobalJSObject())) {
     222           0 :       entryWin = win->AsInner();
     223             :     }
     224             :   }
     225             : 
     226           4 :   return entryWin ? entryWin->GetExtantDoc() : nullptr;
     227             : }
     228             : 
     229             : nsIGlobalObject*
     230         624 : GetIncumbentGlobal()
     231             : {
     232             :   // We need the current JSContext in order to check the JS for
     233             :   // scripted frames that may have appeared since anyone last
     234             :   // manipulated the stack. If it's null, that means that there
     235             :   // must be no entry global on the stack, and therefore no incumbent
     236             :   // global either.
     237         624 :   JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
     238         624 :   if (!cx) {
     239           0 :     MOZ_ASSERT(ScriptSettingsStack::EntryGlobal() == nullptr);
     240           0 :     return nullptr;
     241             :   }
     242             : 
     243             :   // See what the JS engine has to say. If we've got a scripted caller
     244             :   // override in place, the JS engine will lie to us and pretend that
     245             :   // there's nothing on the JS stack, which will cause us to check the
     246             :   // incumbent script stack below.
     247         624 :   if (JSObject* global = JS::GetScriptedCallerGlobal(cx)) {
     248         486 :     return ClampToSubject(xpc::NativeGlobal(global));
     249             :   }
     250             : 
     251             :   // Ok, nothing from the JS engine. Let's use whatever's on the
     252             :   // explicit stack.
     253         138 :   return ClampToSubject(ScriptSettingsStack::IncumbentGlobal());
     254             : }
     255             : 
     256             : nsIGlobalObject*
     257           0 : GetCurrentGlobal()
     258             : {
     259           0 :   JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
     260           0 :   if (!cx) {
     261           0 :     return nullptr;
     262             :   }
     263             : 
     264           0 :   JSObject* global = JS::CurrentGlobalOrNull(cx);
     265           0 :   if (!global) {
     266           0 :     return nullptr;
     267             :   }
     268             : 
     269           0 :   return xpc::NativeGlobal(global);
     270             : }
     271             : 
     272             : nsIPrincipal*
     273           0 : GetWebIDLCallerPrincipal()
     274             : {
     275           0 :   MOZ_ASSERT(NS_IsMainThread());
     276           0 :   ScriptSettingsStackEntry* entry = ScriptSettingsStack::EntryPoint();
     277             : 
     278             :   // If we have an entry point that is not NoJSAPI, we know it must be an
     279             :   // AutoEntryScript.
     280           0 :   if (!entry || entry->NoJSAPI()) {
     281           0 :     return nullptr;
     282             :   }
     283           0 :   AutoEntryScript* aes = static_cast<AutoEntryScript*>(entry);
     284             : 
     285           0 :   return aes->mWebIDLCallerPrincipal;
     286             : }
     287             : 
     288             : bool
     289      192974 : IsJSAPIActive()
     290             : {
     291      192974 :   ScriptSettingsStackEntry* topEntry = ScriptSettingsStack::Top();
     292      192974 :   return topEntry && !topEntry->NoJSAPI();
     293             : }
     294             : 
     295             : namespace danger {
     296             : JSContext*
     297      195418 : GetJSContext()
     298             : {
     299      195418 :   return CycleCollectedJSContext::Get()->Context();
     300             : }
     301             : } // namespace danger
     302             : 
     303             : JS::RootingContext*
     304         183 : RootingCx()
     305             : {
     306         183 :   return CycleCollectedJSContext::Get()->RootingCx();
     307             : }
     308             : 
     309      120908 : AutoJSAPI::AutoJSAPI()
     310             :   : ScriptSettingsStackEntry(nullptr, eJSAPI)
     311             :   , mCx(nullptr)
     312      120908 :   , mIsMainThread(false) // For lack of anything better
     313             : {
     314      120908 : }
     315             : 
     316      249514 : AutoJSAPI::~AutoJSAPI()
     317             : {
     318      124757 :   if (!mCx) {
     319             :     // No need to do anything here: we never managed to Init, so can't have an
     320             :     // exception on our (nonexistent) JSContext.  We also don't need to restore
     321             :     // any state on it.  Finally, we never made it to pushing outselves onto the
     322             :     // ScriptSettingsStack, so shouldn't pop.
     323      118195 :     MOZ_ASSERT(ScriptSettingsStack::Top() != this);
     324      118195 :     return;
     325             :   }
     326             : 
     327        6562 :   ReportException();
     328             : 
     329        6562 :   if (mOldWarningReporter.isSome()) {
     330        6562 :     JS::SetWarningReporter(cx(), mOldWarningReporter.value());
     331             :   }
     332             : 
     333             :   // Leave the request before popping.
     334        6562 :   if (mIsMainThread) {
     335        6530 :     mAutoRequest.reset();
     336             :   }
     337             : 
     338        6562 :   ScriptSettingsStack::Pop(this);
     339      124757 : }
     340             : 
     341             : void
     342             : WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep);
     343             : 
     344             : void
     345        6570 : AutoJSAPI::InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
     346             :                         JSContext* aCx, bool aIsMainThread)
     347             : {
     348        6570 :   MOZ_ASSERT(aCx);
     349        6570 :   MOZ_ASSERT(aCx == danger::GetJSContext());
     350        6570 :   MOZ_ASSERT(aIsMainThread == NS_IsMainThread());
     351        6570 :   MOZ_ASSERT(bool(aGlobalObject) == bool(aGlobal));
     352        6570 :   MOZ_ASSERT_IF(aGlobalObject, aGlobalObject->GetGlobalJSObject() == aGlobal);
     353             : #ifdef DEBUG
     354        6570 :   bool haveException = JS_IsExceptionPending(aCx);
     355             : #endif // DEBUG
     356             : 
     357        6570 :   mCx = aCx;
     358        6570 :   mIsMainThread = aIsMainThread;
     359        6570 :   mGlobalObject = aGlobalObject;
     360        6570 :   if (aIsMainThread) {
     361             :     // We _could_ just unconditionally emplace mAutoRequest here.  It's just not
     362             :     // needed on worker threads, and we're hoping to kill it on the main thread
     363             :     // too.
     364        6533 :     mAutoRequest.emplace(mCx);
     365             :   }
     366        6570 :   if (aGlobal) {
     367        4911 :     JS::ExposeObjectToActiveJS(aGlobal);
     368             :   }
     369        6570 :   mAutoNullableCompartment.emplace(mCx, aGlobal);
     370             : 
     371        6570 :   ScriptSettingsStack::Push(this);
     372             : 
     373        6570 :   mOldWarningReporter.emplace(JS::GetWarningReporter(aCx));
     374             : 
     375        6570 :   JS::SetWarningReporter(aCx, WarningOnlyErrorReporter);
     376             : 
     377             : #ifdef DEBUG
     378        6570 :   if (haveException) {
     379           0 :     JS::Rooted<JS::Value> exn(aCx);
     380           0 :     JS_GetPendingException(aCx, &exn);
     381             : 
     382           0 :     JS_ClearPendingException(aCx);
     383           0 :     if (exn.isObject()) {
     384           0 :       JS::Rooted<JSObject*> exnObj(aCx, &exn.toObject());
     385             : 
     386             :       // Make sure we can actually read things from it.  This UncheckedUwrap is
     387             :       // safe because we're only getting data for a debug printf.  In
     388             :       // particular, we do not expose this data to anyone, which is very
     389             :       // important; otherwise it could be a cross-origin information leak.
     390           0 :       exnObj = js::UncheckedUnwrap(exnObj);
     391           0 :       JSAutoCompartment ac(aCx, exnObj);
     392             : 
     393           0 :       nsAutoJSString stack, filename, name, message;
     394             :       int32_t line;
     395             : 
     396           0 :       JS::Rooted<JS::Value> tmp(aCx);
     397           0 :       if (!JS_GetProperty(aCx, exnObj, "filename", &tmp)) {
     398           0 :         JS_ClearPendingException(aCx);
     399             :       }
     400           0 :       if (tmp.isUndefined()) {
     401           0 :         if (!JS_GetProperty(aCx, exnObj, "fileName", &tmp)) {
     402           0 :           JS_ClearPendingException(aCx);
     403             :         }
     404             :       }
     405             : 
     406           0 :       if (!filename.init(aCx, tmp)) {
     407           0 :         JS_ClearPendingException(aCx);
     408             :       }
     409             : 
     410           0 :       if (!JS_GetProperty(aCx, exnObj, "stack", &tmp) ||
     411           0 :           !stack.init(aCx, tmp)) {
     412           0 :         JS_ClearPendingException(aCx);
     413             :       }
     414             : 
     415           0 :       if (!JS_GetProperty(aCx, exnObj, "name", &tmp) ||
     416           0 :           !name.init(aCx, tmp)) {
     417           0 :         JS_ClearPendingException(aCx);
     418             :       }
     419             : 
     420           0 :       if (!JS_GetProperty(aCx, exnObj, "message", &tmp) ||
     421           0 :           !message.init(aCx, tmp)) {
     422           0 :         JS_ClearPendingException(aCx);
     423             :       }
     424             : 
     425           0 :       if (!JS_GetProperty(aCx, exnObj, "lineNumber", &tmp) ||
     426           0 :           !JS::ToInt32(aCx, tmp, &line)) {
     427           0 :         JS_ClearPendingException(aCx);
     428           0 :         line = 0;
     429             :       }
     430             : 
     431           0 :       printf_stderr("PREEXISTING EXCEPTION OBJECT: '%s: %s'\n%s:%d\n%s\n",
     432           0 :                     NS_ConvertUTF16toUTF8(name).get(),
     433           0 :                     NS_ConvertUTF16toUTF8(message).get(),
     434           0 :                     NS_ConvertUTF16toUTF8(filename).get(), line,
     435           0 :                     NS_ConvertUTF16toUTF8(stack).get());
     436             :     } else {
     437             :       // It's a primitive... not much we can do other than stringify it.
     438           0 :       nsAutoJSString exnStr;
     439           0 :       if (!exnStr.init(aCx, exn)) {
     440           0 :         JS_ClearPendingException(aCx);
     441             :       }
     442             : 
     443           0 :       printf_stderr("PREEXISTING EXCEPTION PRIMITIVE: %s\n",
     444           0 :                     NS_ConvertUTF16toUTF8(exnStr).get());
     445             :     }
     446           0 :     MOZ_ASSERT(false, "We had an exception; we should not have");
     447             :   }
     448             : #endif // DEBUG
     449        6570 : }
     450             : 
     451        3857 : AutoJSAPI::AutoJSAPI(nsIGlobalObject* aGlobalObject,
     452             :                      bool aIsMainThread,
     453        3857 :                      Type aType)
     454             :   : ScriptSettingsStackEntry(aGlobalObject, aType)
     455        3857 :   , mIsMainThread(aIsMainThread)
     456             : {
     457        3857 :   MOZ_ASSERT(aGlobalObject);
     458        3857 :   MOZ_ASSERT(aGlobalObject->GetGlobalJSObject(), "Must have a JS global");
     459        3857 :   MOZ_ASSERT(aIsMainThread == NS_IsMainThread());
     460             : 
     461        3857 :   InitInternal(aGlobalObject, aGlobalObject->GetGlobalJSObject(),
     462        3857 :                danger::GetJSContext(), aIsMainThread);
     463        3857 : }
     464             : 
     465             : void
     466        1659 : AutoJSAPI::Init()
     467             : {
     468        1659 :   MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once");
     469             : 
     470        1659 :   InitInternal(/* aGlobalObject */ nullptr, /* aGlobal */ nullptr,
     471        3318 :                danger::GetJSContext(), NS_IsMainThread());
     472        1659 : }
     473             : 
     474             : bool
     475        1054 : AutoJSAPI::Init(nsIGlobalObject* aGlobalObject, JSContext* aCx)
     476             : {
     477        1054 :   MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once");
     478        1054 :   MOZ_ASSERT(aCx);
     479             : 
     480        1054 :   if (NS_WARN_IF(!aGlobalObject)) {
     481           0 :     return false;
     482             :   }
     483             : 
     484        1054 :   JSObject* global = aGlobalObject->GetGlobalJSObject();
     485        1054 :   if (NS_WARN_IF(!global)) {
     486           0 :     return false;
     487             :   }
     488             : 
     489        1054 :   InitInternal(aGlobalObject, global, aCx, NS_IsMainThread());
     490        1054 :   return true;
     491             : }
     492             : 
     493             : bool
     494        1054 : AutoJSAPI::Init(nsIGlobalObject* aGlobalObject)
     495             : {
     496        1054 :   return Init(aGlobalObject, danger::GetJSContext());
     497             : }
     498             : 
     499             : bool
     500         307 : AutoJSAPI::Init(JSObject* aObject)
     501             : {
     502         307 :   return Init(xpc::NativeGlobal(aObject));
     503             : }
     504             : 
     505             : bool
     506           0 : AutoJSAPI::Init(nsPIDOMWindowInner* aWindow, JSContext* aCx)
     507             : {
     508           0 :   return Init(nsGlobalWindow::Cast(aWindow), aCx);
     509             : }
     510             : 
     511             : bool
     512           0 : AutoJSAPI::Init(nsPIDOMWindowInner* aWindow)
     513             : {
     514           0 :   return Init(nsGlobalWindow::Cast(aWindow));
     515             : }
     516             : 
     517             : bool
     518           0 : AutoJSAPI::Init(nsGlobalWindow* aWindow, JSContext* aCx)
     519             : {
     520           0 :   return Init(static_cast<nsIGlobalObject*>(aWindow), aCx);
     521             : }
     522             : 
     523             : bool
     524           0 : AutoJSAPI::Init(nsGlobalWindow* aWindow)
     525             : {
     526           0 :   return Init(static_cast<nsIGlobalObject*>(aWindow));
     527             : }
     528             : 
     529             : // Even with autoJSAPIOwnsErrorReporting, the JS engine still sends warning
     530             : // reports to the JSErrorReporter as soon as they are generated. These go
     531             : // directly to the console, so we can handle them easily here.
     532             : //
     533             : // Eventually, SpiderMonkey will have a special-purpose callback for warnings
     534             : // only.
     535             : void
     536           0 : WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep)
     537             : {
     538           0 :   MOZ_ASSERT(JSREPORT_IS_WARNING(aRep->flags));
     539           0 :   if (!NS_IsMainThread()) {
     540             :     // Reporting a warning on workers is a bit complicated because we have to
     541             :     // climb our parent chain until we get to the main thread.  So go ahead and
     542             :     // just go through the worker ReportError codepath here.
     543             :     //
     544             :     // That said, it feels like we should be able to short-circuit things a bit
     545             :     // here by posting an appropriate runnable to the main thread directly...
     546             :     // Worth looking into sometime.
     547           0 :     workers::WorkerPrivate* worker = workers::GetWorkerPrivateFromContext(aCx);
     548           0 :     MOZ_ASSERT(worker);
     549             : 
     550           0 :     worker->ReportError(aCx, JS::ConstUTF8CharsZ(), aRep);
     551           0 :     return;
     552             :   }
     553             : 
     554           0 :   RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
     555           0 :   nsGlobalWindow* win = xpc::CurrentWindowOrNull(aCx);
     556           0 :   if (!win) {
     557             :     // We run addons in a separate privileged compartment, but if we're in an
     558             :     // addon compartment we should log warnings to the console of the associated
     559             :     // DOM Window.
     560           0 :     win = xpc::AddonWindowOrNull(JS::CurrentGlobalOrNull(aCx));
     561             :   }
     562           0 :   xpcReport->Init(aRep, nullptr, nsContentUtils::IsSystemCaller(aCx),
     563           0 :                   win ? win->AsInner()->WindowID() : 0);
     564           0 :   xpcReport->LogToConsole();
     565             : }
     566             : 
     567             : void
     568        6594 : AutoJSAPI::ReportException()
     569             : {
     570        6594 :   if (!HasException()) {
     571        6594 :     return;
     572             :   }
     573             : 
     574             :   // AutoJSAPI uses a JSAutoNullableCompartment, and may be in a null
     575             :   // compartment when the destructor is called. However, the JS engine
     576             :   // requires us to be in a compartment when we fetch the pending exception.
     577             :   // In this case, we enter the privileged junk scope and don't dispatch any
     578             :   // error events.
     579           0 :   JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx()));
     580           0 :   if (!errorGlobal) {
     581           0 :     if (mIsMainThread) {
     582           0 :       errorGlobal = xpc::PrivilegedJunkScope();
     583             :     } else {
     584           0 :       errorGlobal = workers::GetCurrentThreadWorkerGlobal();
     585             :     }
     586             :   }
     587           0 :   JSAutoCompartment ac(cx(), errorGlobal);
     588           0 :   JS::Rooted<JS::Value> exn(cx());
     589           0 :   js::ErrorReport jsReport(cx());
     590           0 :   if (StealException(&exn) &&
     591           0 :       jsReport.init(cx(), exn, js::ErrorReport::WithSideEffects)) {
     592           0 :     if (mIsMainThread) {
     593           0 :       RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
     594             : 
     595           0 :       RefPtr<nsGlobalWindow> win = xpc::WindowGlobalOrNull(errorGlobal);
     596           0 :       if (!win) {
     597             :         // We run addons in a separate privileged compartment, but they still
     598             :         // expect to trigger the onerror handler of their associated DOM Window.
     599           0 :         win = xpc::AddonWindowOrNull(errorGlobal);
     600             :       }
     601           0 :       nsPIDOMWindowInner* inner = win ? win->AsInner() : nullptr;
     602           0 :       bool isChrome = nsContentUtils::IsSystemPrincipal(
     603           0 :         nsContentUtils::ObjectPrincipal(errorGlobal));
     604           0 :       xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(),
     605             :                       isChrome,
     606           0 :                       inner ? inner->WindowID() : 0);
     607           0 :       if (inner && jsReport.report()->errorNumber != JSMSG_OUT_OF_MEMORY) {
     608           0 :         JS::RootingContext* rcx = JS::RootingContext::get(cx());
     609           0 :         DispatchScriptErrorEvent(inner, rcx, xpcReport, exn);
     610             :       } else {
     611           0 :         JS::Rooted<JSObject*> stack(cx(),
     612           0 :           xpc::FindExceptionStackForConsoleReport(inner, exn));
     613           0 :         xpcReport->LogToConsoleWithStack(stack);
     614             :       }
     615             :     } else {
     616             :       // On a worker, we just use the worker error reporting mechanism and don't
     617             :       // bother with xpc::ErrorReport.  This will ensure that all the right
     618             :       // events (which are a lot more complicated than in the window case) get
     619             :       // fired.
     620           0 :       workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
     621           0 :       MOZ_ASSERT(worker);
     622           0 :       MOZ_ASSERT(worker->GetJSContext() == cx());
     623             :       // Before invoking ReportError, put the exception back on the context,
     624             :       // because it may want to put it in its error events and has no other way
     625             :       // to get hold of it.  After we invoke ReportError, clear the exception on
     626             :       // cx(), just in case ReportError didn't.
     627           0 :       JS_SetPendingException(cx(), exn);
     628           0 :       worker->ReportError(cx(), jsReport.toStringResult(), jsReport.report());
     629           0 :       ClearException();
     630             :     }
     631             :   } else {
     632           0 :     NS_WARNING("OOMed while acquiring uncaught exception from JSAPI");
     633           0 :     ClearException();
     634             :   }
     635             : }
     636             : 
     637             : bool
     638           0 : AutoJSAPI::PeekException(JS::MutableHandle<JS::Value> aVal)
     639             : {
     640           0 :   MOZ_ASSERT_IF(mIsMainThread, IsStackTop());
     641           0 :   MOZ_ASSERT(HasException());
     642           0 :   MOZ_ASSERT(js::GetContextCompartment(cx()));
     643           0 :   if (!JS_GetPendingException(cx(), aVal)) {
     644           0 :     return false;
     645             :   }
     646           0 :   return true;
     647             : }
     648             : 
     649             : bool
     650           0 : AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal)
     651             : {
     652           0 :   if (!PeekException(aVal)) {
     653           0 :     return false;
     654             :   }
     655           0 :   JS_ClearPendingException(cx());
     656           0 :   return true;
     657             : }
     658             : 
     659             : #ifdef DEBUG
     660             : bool
     661       37100 : AutoJSAPI::IsStackTop() const
     662             : {
     663       37100 :   return ScriptSettingsStack::TopNonIncumbentScript() == this;
     664             : }
     665             : #endif // DEBUG
     666             : 
     667        3857 : AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
     668             :                                  const char* aReason,
     669        3857 :                                  bool aIsMainThread)
     670             :   : AutoJSAPI(aGlobalObject, aIsMainThread, eEntryScript)
     671             :   , mWebIDLCallerPrincipal(nullptr)
     672             :   // This relies on us having a cx() because the AutoJSAPI constructor already
     673             :   // ran.
     674        3857 :   , mCallerOverride(cx())
     675             : {
     676        3857 :   MOZ_ASSERT(aGlobalObject);
     677             : 
     678        3857 :   if (aIsMainThread && gRunToCompletionListeners > 0) {
     679           0 :     mDocShellEntryMonitor.emplace(cx(), aReason);
     680             :   }
     681        3857 : }
     682             : 
     683         759 : AutoEntryScript::AutoEntryScript(JSObject* aObject,
     684             :                                  const char* aReason,
     685         759 :                                  bool aIsMainThread)
     686         759 :   : AutoEntryScript(xpc::NativeGlobal(aObject), aReason, aIsMainThread)
     687             : {
     688         759 : }
     689             : 
     690        7700 : AutoEntryScript::~AutoEntryScript()
     691             : {
     692             :   // GC when we pop a script entry point. This is a useful heuristic that helps
     693             :   // us out on certain (flawed) benchmarks like sunspider, because it lets us
     694             :   // avoid GCing during the timing loop.
     695        3850 :   JS_MaybeGC(cx());
     696        3850 : }
     697             : 
     698           0 : AutoEntryScript::DocshellEntryMonitor::DocshellEntryMonitor(JSContext* aCx,
     699           0 :                                                             const char* aReason)
     700             :   : JS::dbg::AutoEntryMonitor(aCx)
     701           0 :   , mReason(aReason)
     702             : {
     703           0 : }
     704             : 
     705             : void
     706           0 : AutoEntryScript::DocshellEntryMonitor::Entry(JSContext* aCx, JSFunction* aFunction,
     707             :                                              JSScript* aScript, JS::Handle<JS::Value> aAsyncStack,
     708             :                                              const char* aAsyncCause)
     709             : {
     710           0 :   JS::Rooted<JSFunction*> rootedFunction(aCx);
     711           0 :   if (aFunction) {
     712           0 :     rootedFunction = aFunction;
     713             :   }
     714           0 :   JS::Rooted<JSScript*> rootedScript(aCx);
     715           0 :   if (aScript) {
     716           0 :     rootedScript = aScript;
     717             :   }
     718             : 
     719             :   nsCOMPtr<nsPIDOMWindowInner> window =
     720           0 :     do_QueryInterface(xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)));
     721           0 :   if (!window || !window->GetDocShell() ||
     722           0 :       !window->GetDocShell()->GetRecordProfileTimelineMarkers()) {
     723           0 :     return;
     724             :   }
     725             : 
     726           0 :   nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell();
     727           0 :   nsString filename;
     728           0 :   uint32_t lineNumber = 0;
     729             : 
     730           0 :   js::AutoStableStringChars functionName(aCx);
     731           0 :   if (rootedFunction) {
     732           0 :     JS::Rooted<JSString*> displayId(aCx, JS_GetFunctionDisplayId(rootedFunction));
     733           0 :     if (displayId) {
     734           0 :       if (!functionName.initTwoByte(aCx, displayId)) {
     735           0 :         JS_ClearPendingException(aCx);
     736           0 :         return;
     737             :       }
     738             :     }
     739             :   }
     740             : 
     741           0 :   if (!rootedScript) {
     742           0 :     rootedScript = JS_GetFunctionScript(aCx, rootedFunction);
     743             :   }
     744           0 :   if (rootedScript) {
     745           0 :     filename = NS_ConvertUTF8toUTF16(JS_GetScriptFilename(rootedScript));
     746           0 :     lineNumber = JS_GetScriptBaseLineNumber(aCx, rootedScript);
     747             :   }
     748             : 
     749           0 :   if (!filename.IsEmpty() || functionName.isTwoByte()) {
     750           0 :     const char16_t* functionNameChars = functionName.isTwoByte() ?
     751           0 :       functionName.twoByteChars() : nullptr;
     752             : 
     753           0 :     docShellForJSRunToCompletion->NotifyJSRunToCompletionStart(mReason,
     754             :                                                                functionNameChars,
     755             :                                                                filename.BeginReading(),
     756             :                                                                lineNumber, aAsyncStack,
     757           0 :                                                                aAsyncCause);
     758             :   }
     759             : }
     760             : 
     761             : void
     762           0 : AutoEntryScript::DocshellEntryMonitor::Exit(JSContext* aCx)
     763             : {
     764             :   nsCOMPtr<nsPIDOMWindowInner> window =
     765           0 :     do_QueryInterface(xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)));
     766             :   // Not really worth checking GetRecordProfileTimelineMarkers here.
     767           0 :   if (window && window->GetDocShell()) {
     768           0 :     nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell();
     769           0 :     docShellForJSRunToCompletion->NotifyJSRunToCompletionStop();
     770             :   }
     771           0 : }
     772             : 
     773         279 : AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject)
     774             :   : ScriptSettingsStackEntry(aGlobalObject, eIncumbentScript)
     775         279 :   , mCallerOverride(nsContentUtils::GetCurrentJSContextForThread())
     776             : {
     777         279 :   ScriptSettingsStack::Push(this);
     778         279 : }
     779             : 
     780         558 : AutoIncumbentScript::~AutoIncumbentScript()
     781             : {
     782         279 :   ScriptSettingsStack::Pop(this);
     783         279 : }
     784             : 
     785        1555 : AutoNoJSAPI::AutoNoJSAPI()
     786        1555 :   : ScriptSettingsStackEntry(nullptr, eNoJSAPI)
     787             : {
     788        1555 :   ScriptSettingsStack::Push(this);
     789        1555 : }
     790             : 
     791        3090 : AutoNoJSAPI::~AutoNoJSAPI()
     792             : {
     793        1545 :   ScriptSettingsStack::Pop(this);
     794        1545 : }
     795             : 
     796             : } // namespace dom
     797             : 
     798      118194 : AutoJSContext::AutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
     799      118194 :   : mCx(nullptr)
     800             : {
     801      236388 :   JS::AutoSuppressGCAnalysis nogc;
     802      118194 :   MOZ_ASSERT(!mCx, "mCx should not be initialized!");
     803      118194 :   MOZ_ASSERT(NS_IsMainThread());
     804             : 
     805      118194 :   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     806             : 
     807      118194 :   if (dom::IsJSAPIActive()) {
     808      118194 :     mCx = dom::danger::GetJSContext();
     809             :   } else {
     810           0 :     mJSAPI.Init();
     811           0 :     mCx = mJSAPI.cx();
     812             :   }
     813      118194 : }
     814             : 
     815      220581 : AutoJSContext::operator JSContext*() const
     816             : {
     817      220581 :   return mCx;
     818             : }
     819             : 
     820          44 : AutoSafeJSContext::AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
     821          44 :   : AutoJSAPI()
     822             : {
     823          44 :   MOZ_ASSERT(NS_IsMainThread());
     824             : 
     825          44 :   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     826             : 
     827          88 :   DebugOnly<bool> ok = Init(xpc::UnprivilegedJunkScope());
     828          44 :   MOZ_ASSERT(ok,
     829             :              "This is quite odd.  We should have crashed in the "
     830             :              "xpc::NativeGlobal() call if xpc::UnprivilegedJunkScope() "
     831             :              "returned null, and inited correctly otherwise!");
     832          44 : }
     833             : 
     834          25 : AutoSlowOperation::AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
     835          25 :   : AutoJSAPI()
     836             : {
     837          25 :   MOZ_ASSERT(NS_IsMainThread());
     838             : 
     839          25 :   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     840             : 
     841          25 :   Init();
     842          25 : }
     843             : 
     844             : void
     845         237 : AutoSlowOperation::CheckForInterrupt()
     846             : {
     847             :   // JS_CheckForInterrupt expects us to be in a compartment.
     848         474 :   JSAutoCompartment ac(cx(), xpc::UnprivilegedJunkScope());
     849         237 :   JS_CheckForInterrupt(cx());
     850         237 : }
     851             : 
     852             : } // namespace mozilla

Generated by: LCOV version 1.13