dom/bindings/Exceptions.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/dom/Exceptions.h"
     8 #include "js/GCAPI.h"
     9 #include "js/OldDebugAPI.h"
    10 #include "jsapi.h"
    11 #include "jsprf.h"
    12 #include "mozilla/CycleCollectedJSRuntime.h"
    13 #include "mozilla/dom/BindingUtils.h"
    14 #include "mozilla/dom/DOMException.h"
    15 #include "nsServiceManagerUtils.h"
    16 #include "nsThreadUtils.h"
    17 #include "XPCWrapper.h"
    18 #include "WorkerPrivate.h"
    19 #include "nsContentUtils.h"
    21 namespace {
    23 // We can't use nsContentUtils::IsCallerChrome because it might not exist in
    24 // xpcshell.
    25 bool
    26 IsCallerChrome()
    27 {
    28   nsCOMPtr<nsIScriptSecurityManager> secMan;
    29   secMan = XPCWrapper::GetSecurityManager();
    31   if (!secMan) {
    32     return false;
    33   }
    35   bool isChrome;
    36   return NS_SUCCEEDED(secMan->SubjectPrincipalIsSystem(&isChrome)) && isChrome;
    37 }
    39 } // anonymous namespace
    41 namespace mozilla {
    42 namespace dom {
    44 bool
    45 ThrowExceptionObject(JSContext* aCx, nsIException* aException)
    46 {
    47   // See if we really have an Exception.
    48   nsCOMPtr<Exception> exception = do_QueryInterface(aException);
    49   if (exception) {
    50     return ThrowExceptionObject(aCx, exception);
    51   }
    53   // We only have an nsIException (probably an XPCWrappedJS).  Fall back on old
    54   // wrapping.
    55   MOZ_ASSERT(NS_IsMainThread());
    57   JS::Rooted<JSObject*> glob(aCx, JS::CurrentGlobalOrNull(aCx));
    58   if (!glob) {
    59     // XXXbz Can this really be null here?
    60     return false;
    61   }
    63   JS::Rooted<JS::Value> val(aCx);
    64   if (!WrapObject(aCx, aException, &NS_GET_IID(nsIException), &val)) {
    65     return false;
    66   }
    68   JS_SetPendingException(aCx, val);
    70   return true;
    71 }
    73 bool
    74 ThrowExceptionObject(JSContext* aCx, Exception* aException)
    75 {
    76   JS::Rooted<JS::Value> thrown(aCx);
    78   // If we stored the original thrown JS value in the exception
    79   // (see XPCConvert::ConstructException) and we are in a web context
    80   // (i.e., not chrome), rethrow the original value. This only applies to JS
    81   // implemented components so we only need to check for this on the main
    82   // thread.
    83   if (NS_IsMainThread() && !IsCallerChrome() &&
    84       aException->StealJSVal(thrown.address())) {
    85     if (!JS_WrapValue(aCx, &thrown)) {
    86       return false;
    87     }
    88     JS_SetPendingException(aCx, thrown);
    89     return true;
    90   }
    92   JS::Rooted<JSObject*> glob(aCx, JS::CurrentGlobalOrNull(aCx));
    93   if (!glob) {
    94     // XXXbz Can this actually be null here?
    95     return false;
    96   }
    98   if (!WrapNewBindingObject(aCx, aException, &thrown)) {
    99     return false;
   100   }
   102   JS_SetPendingException(aCx, thrown);
   103   return true;
   104 }
   106 bool
   107 Throw(JSContext* aCx, nsresult aRv, const char* aMessage)
   108 {
   109   if (JS_IsExceptionPending(aCx)) {
   110     // Don't clobber the existing exception.
   111     return false;
   112   }
   114   CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
   115   nsCOMPtr<nsIException> existingException = runtime->GetPendingException();
   116   if (existingException) {
   117     nsresult nr;
   118     if (NS_SUCCEEDED(existingException->GetResult(&nr)) && 
   119         aRv == nr) {
   120       // Reuse the existing exception.
   122       // Clear pending exception
   123       runtime->SetPendingException(nullptr);
   125       if (!ThrowExceptionObject(aCx, existingException)) {
   126         // If we weren't able to throw an exception we're
   127         // most likely out of memory
   128         JS_ReportOutOfMemory(aCx);
   129       }
   130       return false;
   131     }
   132   }
   134   nsRefPtr<Exception> finalException = CreateException(aCx, aRv, aMessage);
   136   MOZ_ASSERT(finalException);
   137   if (!ThrowExceptionObject(aCx, finalException)) {
   138     // If we weren't able to throw an exception we're
   139     // most likely out of memory
   140     JS_ReportOutOfMemory(aCx);
   141   }
   143   return false;
   144 }
   146 already_AddRefed<Exception>
   147 CreateException(JSContext* aCx, nsresult aRv, const char* aMessage)
   148 {
   149   // Do we use DOM exceptions for this error code?
   150   switch (NS_ERROR_GET_MODULE(aRv)) {
   151   case NS_ERROR_MODULE_DOM:
   152   case NS_ERROR_MODULE_SVG:
   153   case NS_ERROR_MODULE_DOM_XPATH:
   154   case NS_ERROR_MODULE_DOM_INDEXEDDB:
   155   case NS_ERROR_MODULE_DOM_FILEHANDLE:
   156     return DOMException::Create(aRv);
   157   default:
   158     break;
   159   }
   161   // If not, use the default.
   162   // aMessage can be null, so we can't use nsDependentCString on it.
   163   nsRefPtr<Exception> exception =
   164     new Exception(nsCString(aMessage), aRv,
   165                   EmptyCString(), nullptr, nullptr);
   166   return exception.forget();
   167 }
   169 already_AddRefed<nsIStackFrame>
   170 GetCurrentJSStack()
   171 {
   172   // is there a current context available?
   173   JSContext* cx = nullptr;
   175   if (NS_IsMainThread()) {
   176     // Note, in xpcshell nsContentUtils is never initialized, but we still need
   177     // to report exceptions.
   178     if (nsContentUtils::XPConnect()) {
   179       cx = nsContentUtils::XPConnect()->GetCurrentJSContext();
   180     } else {
   181       nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
   182       cx = xpc->GetCurrentJSContext();
   183     }
   184   } else {
   185     cx = workers::GetCurrentThreadJSContext();
   186   }
   188   if (!cx) {
   189     return nullptr;
   190   }
   192   nsCOMPtr<nsIStackFrame> stack = exceptions::CreateStack(cx);
   193   if (!stack) {
   194     return nullptr;
   195   }
   197   // peel off native frames...
   198   uint32_t language;
   199   nsCOMPtr<nsIStackFrame> caller;
   200   while (stack &&
   201          NS_SUCCEEDED(stack->GetLanguage(&language)) &&
   202          language != nsIProgrammingLanguage::JAVASCRIPT &&
   203          NS_SUCCEEDED(stack->GetCaller(getter_AddRefs(caller))) &&
   204          caller) {
   205     stack = caller;
   206   }
   207   return stack.forget();
   208 }
   210 namespace exceptions {
   212 class StackDescriptionOwner {
   213 public:
   214   StackDescriptionOwner(JS::StackDescription* aDescription)
   215     : mDescription(aDescription)
   216   {
   217     mozilla::HoldJSObjects(this);
   218   }
   220   ~StackDescriptionOwner()
   221   {
   222     // Make sure to set mDescription to null before calling DropJSObjects, since
   223     // in debug builds DropJSObjects try to trace us and we don't want to trace
   224     // a dead StackDescription.
   225     if (mDescription) {
   226       JS::FreeStackDescription(nullptr, mDescription);
   227       mDescription = nullptr;
   228     }
   229     mozilla::DropJSObjects(this);
   230   }
   232   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(StackDescriptionOwner)
   233   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(StackDescriptionOwner)
   235   JS::FrameDescription& FrameAt(size_t aIndex)
   236   {
   237     MOZ_ASSERT(aIndex < mDescription->nframes);
   238     return mDescription->frames[aIndex];
   239   }
   241   unsigned NumFrames()
   242   {
   243     return mDescription->nframes;
   244   }
   246 private:
   247   JS::StackDescription* mDescription;
   248 };
   250 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(StackDescriptionOwner, AddRef)
   251 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(StackDescriptionOwner, Release)
   253 NS_IMPL_CYCLE_COLLECTION_CLASS(StackDescriptionOwner)
   254 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StackDescriptionOwner)
   255   if (tmp->mDescription) {
   256     JS::FreeStackDescription(nullptr, tmp->mDescription);
   257     tmp->mDescription = nullptr;
   258   }
   259 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   260 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(StackDescriptionOwner)
   261   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   262 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   263 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(StackDescriptionOwner)
   264   JS::StackDescription* desc = tmp->mDescription;
   265   if (tmp->mDescription) {
   266     for (size_t i = 0; i < desc->nframes; ++i) {
   267       NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDescription->frames[i].markedLocation1());
   268       NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDescription->frames[i].markedLocation2());
   269     }
   270   }
   271 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   273 class JSStackFrame : public nsIStackFrame
   274 {
   275 public:
   276   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   277   NS_DECL_CYCLE_COLLECTION_CLASS(JSStackFrame)
   278   NS_DECL_NSISTACKFRAME
   280   // A null aStackDescription or an aIndex that's out of range for the
   281   // number of frames aStackDescription has will mean that the
   282   // JSStackFrame will never look at the stack description.  Instead,
   283   // it is expected to be initialized by the caller as needed.
   284   JSStackFrame(StackDescriptionOwner* aStackDescription, size_t aIndex);
   285   virtual ~JSStackFrame();
   287   static already_AddRefed<nsIStackFrame>
   288   CreateStack(JSContext* aCx, int32_t aMaxDepth = -1);
   289   static already_AddRefed<nsIStackFrame>
   290   CreateStackFrameLocation(uint32_t aLanguage,
   291                            const char* aFilename,
   292                            const char* aFunctionName,
   293                            int32_t aLineNumber,
   294                            nsIStackFrame* aCaller);
   296 private:
   297   bool IsJSFrame() const {
   298     return mLanguage == nsIProgrammingLanguage::JAVASCRIPT;
   299   }
   301   int32_t GetLineno();
   303   nsRefPtr<StackDescriptionOwner> mStackDescription;
   304   nsCOMPtr<nsIStackFrame> mCaller;
   306   // Cached values
   307   nsString mFilename;
   308   nsString mFunname;
   309   int32_t mLineno;
   310   uint32_t mLanguage;
   312   size_t mIndex;
   314   bool mFilenameInitialized;
   315   bool mFunnameInitialized;
   316   bool mLinenoInitialized;
   317   bool mCallerInitialized;
   318 };
   320 JSStackFrame::JSStackFrame(StackDescriptionOwner* aStackDescription,
   321                            size_t aIndex)
   322   : mLineno(0)
   323 {
   324   if (aStackDescription && aIndex < aStackDescription->NumFrames()) {
   325     mStackDescription = aStackDescription;
   326     mIndex = aIndex;
   327     mFilenameInitialized = false;
   328     mFunnameInitialized = false;
   329     mLinenoInitialized = false;
   330     mCallerInitialized = false;
   331     mLanguage = nsIProgrammingLanguage::JAVASCRIPT;
   332   } else {
   333     MOZ_ASSERT(!mStackDescription);
   334     mIndex = 0;
   335     mFilenameInitialized = true;
   336     mFunnameInitialized = true;
   337     mLinenoInitialized = true;
   338     mCallerInitialized = true;
   339     mLanguage = nsIProgrammingLanguage::UNKNOWN;
   340   }
   341 }
   343 JSStackFrame::~JSStackFrame()
   344 {
   345 }
   347 NS_IMPL_CYCLE_COLLECTION(JSStackFrame, mStackDescription, mCaller)
   349 NS_IMPL_CYCLE_COLLECTING_ADDREF(JSStackFrame)
   350 NS_IMPL_CYCLE_COLLECTING_RELEASE(JSStackFrame)
   352 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSStackFrame)
   353   NS_INTERFACE_MAP_ENTRY(nsIStackFrame)
   354   NS_INTERFACE_MAP_ENTRY(nsISupports)
   355 NS_INTERFACE_MAP_END
   357 /* readonly attribute uint32_t language; */
   358 NS_IMETHODIMP JSStackFrame::GetLanguage(uint32_t* aLanguage)
   359 {
   360   *aLanguage = mLanguage;
   361   return NS_OK;
   362 }
   364 /* readonly attribute string languageName; */
   365 NS_IMETHODIMP JSStackFrame::GetLanguageName(nsACString& aLanguageName)
   366 {
   367   static const char js[] = "JavaScript";
   368   static const char cpp[] = "C++";
   370   if (IsJSFrame()) {
   371     aLanguageName.AssignASCII(js);
   372   } else {
   373     aLanguageName.AssignASCII(cpp);
   374   }
   376   return NS_OK;
   377 }
   379 /* readonly attribute AString filename; */
   380 NS_IMETHODIMP JSStackFrame::GetFilename(nsAString& aFilename)
   381 {
   382   if (!mFilenameInitialized) {
   383     JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
   384     if (const char *filename = desc.filename()) {
   385       CopyUTF8toUTF16(filename, mFilename);
   386     }
   387     mFilenameInitialized = true;
   388   }
   390   // The filename must be set to null if empty.
   391   if (mFilename.IsEmpty()) {
   392     aFilename.SetIsVoid(true);
   393   } else {
   394     aFilename.Assign(mFilename);
   395   }
   397   return NS_OK;
   398 }
   400 /* readonly attribute AString name; */
   401 NS_IMETHODIMP JSStackFrame::GetName(nsAString& aFunction)
   402 {
   403   if (!mFunnameInitialized) {
   404     JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
   405     if (JSFlatString *name = desc.funDisplayName()) {
   406       mFunname.Assign(JS_GetFlatStringChars(name),
   407                       // XXXbz Can't JS_GetStringLength on JSFlatString!
   408                       JS_GetStringLength(JS_FORGET_STRING_FLATNESS(name)));
   409     }
   410     mFunnameInitialized = true;
   411   }
   413   // The function name must be set to null if empty.
   414   if (mFunname.IsEmpty()) {
   415     aFunction.SetIsVoid(true);
   416   } else {
   417     aFunction.Assign(mFunname);
   418   }
   420   return NS_OK;
   421 }
   423 int32_t
   424 JSStackFrame::GetLineno()
   425 {
   426   if (!mLinenoInitialized) {
   427     JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
   428     mLineno = desc.lineno();
   429     mLinenoInitialized = true;
   430   }
   432   return mLineno;
   433 }
   435 /* readonly attribute int32_t lineNumber; */
   436 NS_IMETHODIMP JSStackFrame::GetLineNumber(int32_t* aLineNumber)
   437 {
   438   *aLineNumber = GetLineno();
   439   return NS_OK;
   440 }
   442 /* readonly attribute AUTF8String sourceLine; */
   443 NS_IMETHODIMP JSStackFrame::GetSourceLine(nsACString& aSourceLine)
   444 {
   445   aSourceLine.Truncate();
   446   return NS_OK;
   447 }
   449 /* readonly attribute nsIStackFrame caller; */
   450 NS_IMETHODIMP JSStackFrame::GetCaller(nsIStackFrame** aCaller)
   451 {
   452   if (!mCallerInitialized) {
   453     mCaller = new JSStackFrame(mStackDescription, mIndex+1);
   454     mCallerInitialized = true;
   455   }
   456   NS_IF_ADDREF(*aCaller = mCaller);
   457   return NS_OK;
   458 }
   460 /* AUTF8String toString (); */
   461 NS_IMETHODIMP JSStackFrame::ToString(nsACString& _retval)
   462 {
   463   _retval.Truncate();
   465   const char* frametype = IsJSFrame() ? "JS" : "native";
   467   nsString filename;
   468   nsresult rv = GetFilename(filename);
   469   NS_ENSURE_SUCCESS(rv, rv);
   471   if (filename.IsEmpty()) {
   472     filename.AssignLiteral("<unknown filename>");
   473   }
   475   nsString funname;
   476   rv = GetName(funname);
   477   NS_ENSURE_SUCCESS(rv, rv);
   479   if (funname.IsEmpty()) {
   480     funname.AssignLiteral("<TOP_LEVEL>");
   481   }
   482   static const char format[] = "%s frame :: %s :: %s :: line %d";
   483   _retval.AppendPrintf(format, frametype,
   484                        NS_ConvertUTF16toUTF8(filename).get(),
   485                        NS_ConvertUTF16toUTF8(funname).get(),
   486                        GetLineno());
   487   return NS_OK;
   488 }
   490 /* static */ already_AddRefed<nsIStackFrame>
   491 JSStackFrame::CreateStack(JSContext* aCx, int32_t aMaxDepth)
   492 {
   493   static const unsigned MAX_FRAMES = 100;
   494   if (aMaxDepth < 0) {
   495     aMaxDepth = MAX_FRAMES;
   496   }
   498   JS::StackDescription* desc = JS::DescribeStack(aCx, aMaxDepth);
   499   if (!desc) {
   500     return nullptr;
   501   }
   503   nsRefPtr<StackDescriptionOwner> descOwner = new StackDescriptionOwner(desc);
   505   nsRefPtr<JSStackFrame> first = new JSStackFrame(descOwner, 0);
   506   return first.forget();
   507 }
   509 /* static */ already_AddRefed<nsIStackFrame>
   510 JSStackFrame::CreateStackFrameLocation(uint32_t aLanguage,
   511                                        const char* aFilename,
   512                                        const char* aFunctionName,
   513                                        int32_t aLineNumber,
   514                                        nsIStackFrame* aCaller)
   515 {
   516   nsRefPtr<JSStackFrame> self = new JSStackFrame(nullptr, 0);
   518   self->mLanguage = aLanguage;
   519   self->mLineno = aLineNumber;
   520   CopyUTF8toUTF16(aFilename, self->mFilename);
   521   CopyUTF8toUTF16(aFunctionName, self->mFunname);
   523   self->mCaller = aCaller;
   525   return self.forget();
   526 }
   528 already_AddRefed<nsIStackFrame>
   529 CreateStack(JSContext* aCx, int32_t aMaxDepth)
   530 {
   531   return JSStackFrame::CreateStack(aCx, aMaxDepth);
   532 }
   534 already_AddRefed<nsIStackFrame>
   535 CreateStackFrameLocation(uint32_t aLanguage,
   536                          const char* aFilename,
   537                          const char* aFunctionName,
   538                          int32_t aLineNumber,
   539                          nsIStackFrame* aCaller)
   540 {
   541   return JSStackFrame::CreateStackFrameLocation(aLanguage, aFilename,
   542                                                 aFunctionName, aLineNumber,
   543                                                 aCaller);
   544 }
   546 } // namespace exceptions
   547 } // namespace dom
   548 } // namespace mozilla

mercurial