tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DOMException.cpp (13868B)


      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/DOMException.h"
      8 
      9 #include "js/StructuredClone.h"
     10 #include "js/TypeDecls.h"
     11 #include "mozilla/ErrorResult.h"
     12 #include "mozilla/HoldDropJSObjects.h"
     13 #include "mozilla/dom/DOMExceptionBinding.h"
     14 #include "mozilla/dom/Document.h"
     15 #include "mozilla/dom/Exceptions.h"
     16 #include "nsCOMPtr.h"
     17 #include "nsContentUtils.h"
     18 #include "nsIException.h"
     19 #include "xpcprivate.h"
     20 
     21 using namespace mozilla;
     22 using namespace mozilla::dom;
     23 
     24 enum DOM4ErrorTypeCodeMap {
     25  /* DOM4 errors from
     26     http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#domexception */
     27  IndexSizeError = DOMException_Binding::INDEX_SIZE_ERR,
     28  HierarchyRequestError = DOMException_Binding::HIERARCHY_REQUEST_ERR,
     29  WrongDocumentError = DOMException_Binding::WRONG_DOCUMENT_ERR,
     30  InvalidCharacterError = DOMException_Binding::INVALID_CHARACTER_ERR,
     31  NoModificationAllowedError =
     32      DOMException_Binding::NO_MODIFICATION_ALLOWED_ERR,
     33  NotFoundError = DOMException_Binding::NOT_FOUND_ERR,
     34  NotSupportedError = DOMException_Binding::NOT_SUPPORTED_ERR,
     35  // Can't remove until setNamedItem is removed
     36  InUseAttributeError = DOMException_Binding::INUSE_ATTRIBUTE_ERR,
     37  InvalidStateError = DOMException_Binding::INVALID_STATE_ERR,
     38  SyntaxError = DOMException_Binding::SYNTAX_ERR,
     39  InvalidModificationError = DOMException_Binding::INVALID_MODIFICATION_ERR,
     40  NamespaceError = DOMException_Binding::NAMESPACE_ERR,
     41  InvalidAccessError = DOMException_Binding::INVALID_ACCESS_ERR,
     42  TypeMismatchError = DOMException_Binding::TYPE_MISMATCH_ERR,
     43  SecurityError = DOMException_Binding::SECURITY_ERR,
     44  NetworkError = DOMException_Binding::NETWORK_ERR,
     45  AbortError = DOMException_Binding::ABORT_ERR,
     46  URLMismatchError = DOMException_Binding::URL_MISMATCH_ERR,
     47  QuotaExceededError = DOMException_Binding::QUOTA_EXCEEDED_ERR,
     48  TimeoutError = DOMException_Binding::TIMEOUT_ERR,
     49  InvalidNodeTypeError = DOMException_Binding::INVALID_NODE_TYPE_ERR,
     50  DataCloneError = DOMException_Binding::DATA_CLONE_ERR,
     51  EncodingError = 0,
     52 
     53  /* IndexedDB errors
     54     http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#exceptions */
     55  UnknownError = 0,
     56  ConstraintError = 0,
     57  DataError = 0,
     58  TransactionInactiveError = 0,
     59  ReadOnlyError = 0,
     60  VersionError = 0,
     61 
     62  /* File API errors http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException */
     63  NotReadableError = 0,
     64 
     65  /* FileHandle API errors */
     66  FileHandleInactiveError = 0,
     67 
     68  /* WebCrypto errors
     69     https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-DataError
     70   */
     71  OperationError = 0,
     72 
     73  /* Push API errors */
     74  NotAllowedError = 0,
     75 };
     76 
     77 #define DOM4_MSG_DEF(name, message, nsresult) \
     78  {(nsresult), name, #name, message},
     79 #define DOM_MSG_DEF(val, message) \
     80  {(val), NS_ERROR_GET_CODE(val), #val, message},
     81 
     82 static constexpr struct ResultStruct {
     83  nsresult mNSResult;
     84  uint16_t mCode;
     85  const char* mName;
     86  const char* mMessage;
     87 } sDOMErrorMsgMap[] = {
     88 #include "domerr.msg"
     89 };
     90 
     91 #undef DOM4_MSG_DEF
     92 #undef DOM_MSG_DEF
     93 
     94 static void NSResultToNameAndMessage(nsresult aNSResult, nsCString& aName,
     95                                     nsCString& aMessage, uint16_t* aCode) {
     96  aName.Truncate();
     97  aMessage.Truncate();
     98  *aCode = 0;
     99  for (uint32_t idx = 0; idx < std::size(sDOMErrorMsgMap); idx++) {
    100    if (aNSResult == sDOMErrorMsgMap[idx].mNSResult) {
    101      aName.Rebind(sDOMErrorMsgMap[idx].mName,
    102                   strlen(sDOMErrorMsgMap[idx].mName));
    103      aMessage.Rebind(sDOMErrorMsgMap[idx].mMessage,
    104                      strlen(sDOMErrorMsgMap[idx].mMessage));
    105      *aCode = sDOMErrorMsgMap[idx].mCode;
    106      return;
    107    }
    108  }
    109 
    110  NS_WARNING("Huh, someone is throwing non-DOM errors using the DOM module!");
    111 }
    112 
    113 nsresult NS_GetNameAndMessageForDOMNSResult(nsresult aNSResult,
    114                                            nsACString& aName,
    115                                            nsACString& aMessage,
    116                                            uint16_t* aCode) {
    117  nsCString name;
    118  nsCString message;
    119  uint16_t code = 0;
    120  NSResultToNameAndMessage(aNSResult, name, message, &code);
    121 
    122  if (!name.IsEmpty() && !message.IsEmpty()) {
    123    aName = name;
    124    aMessage = message;
    125    if (aCode) {
    126      *aCode = code;
    127    }
    128    return NS_OK;
    129  }
    130 
    131  return NS_ERROR_NOT_AVAILABLE;
    132 }
    133 
    134 namespace mozilla::dom {
    135 
    136 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Exception)
    137  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    138  NS_INTERFACE_MAP_ENTRY(Exception)
    139  NS_INTERFACE_MAP_ENTRY(nsIException)
    140  NS_INTERFACE_MAP_ENTRY(nsISupports)
    141 NS_INTERFACE_MAP_END
    142 
    143 NS_IMPL_CYCLE_COLLECTING_ADDREF(Exception)
    144 NS_IMPL_CYCLE_COLLECTING_RELEASE(Exception)
    145 
    146 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(Exception,
    147                                                      (mLocation, mData),
    148                                                      (mThrownJSVal))
    149 
    150 Exception::Exception(const nsACString& aMessage, nsresult aResult,
    151                     const nsACString& aName, nsIStackFrame* aLocation,
    152                     nsISupports* aData)
    153    : mMessage(aMessage),
    154      mResult(aResult),
    155      mName(aName),
    156      mData(aData),
    157      mHoldingJSVal(false) {
    158  if (aLocation) {
    159    mLocation = aLocation;
    160  } else {
    161    mLocation = GetCurrentJSStack();
    162    // it is legal for there to be no active JS stack, if C++ code
    163    // is operating on a JS-implemented interface pointer without
    164    // having been called in turn by JS.  This happens in the JS
    165    // component loader.
    166  }
    167 }
    168 
    169 Exception::Exception(nsCString&& aMessage, nsresult aResult, nsCString&& aName)
    170    : mMessage(std::move(aMessage)),
    171      mResult(aResult),
    172      mName(std::move(aName)),
    173      mHoldingJSVal(false) {}
    174 
    175 Exception::~Exception() {
    176  if (mHoldingJSVal) {
    177    MOZ_ASSERT(NS_IsMainThread());
    178 
    179    mozilla::DropJSObjects(this);
    180  }
    181 }
    182 
    183 bool Exception::StealJSVal(JS::Value* aVp) {
    184  MOZ_ASSERT(NS_IsMainThread());
    185 
    186  if (mHoldingJSVal) {
    187    *aVp = mThrownJSVal;
    188 
    189    mozilla::DropJSObjects(this);
    190    mHoldingJSVal = false;
    191    return true;
    192  }
    193 
    194  return false;
    195 }
    196 
    197 void Exception::StowJSVal(JS::Value& aVp) {
    198  MOZ_ASSERT(NS_IsMainThread());
    199 
    200  mThrownJSVal = aVp;
    201  if (!mHoldingJSVal) {
    202    mozilla::HoldJSObjects(this);
    203    mHoldingJSVal = true;
    204  }
    205 }
    206 
    207 void Exception::GetName(nsAString& aName) {
    208  if (!mName.IsEmpty()) {
    209    CopyUTF8toUTF16(mName, aName);
    210  } else {
    211    aName.Truncate();
    212 
    213    const char* name = nullptr;
    214    nsXPCException::NameAndFormatForNSResult(mResult, &name, nullptr);
    215 
    216    if (name) {
    217      CopyUTF8toUTF16(mozilla::MakeStringSpan(name), aName);
    218    }
    219  }
    220 }
    221 
    222 void Exception::GetFilename(JSContext* aCx, nsACString& aFilename) {
    223  if (mLocation) {
    224    mLocation->GetFilename(aCx, aFilename);
    225    return;
    226  }
    227 
    228  aFilename.Truncate();
    229 }
    230 
    231 void Exception::ToString(JSContext* aCx, nsACString& _retval) {
    232  static const char defaultMsg[] = "<no message>";
    233  static const char defaultLocation[] = "<unknown>";
    234  static const char format[] = "[Exception... \"%s\"  nsresult: \"0x%" PRIx32
    235                               " (%s)\"  location: \"%s\"  data: %s]";
    236 
    237  nsCString location;
    238 
    239  if (mLocation) {
    240    // we need to free this if it does not fail
    241    mLocation->ToString(aCx, location);
    242  }
    243 
    244  if (location.IsEmpty()) {
    245    location.Assign(defaultLocation);
    246  }
    247 
    248  const char* msg = mMessage.IsEmpty() ? nullptr : mMessage.get();
    249 
    250  const char* resultName = mName.IsEmpty() ? nullptr : mName.get();
    251  if (!resultName && !nsXPCException::NameAndFormatForNSResult(
    252                         mResult, &resultName, (!msg) ? &msg : nullptr)) {
    253    if (!msg) {
    254      msg = defaultMsg;
    255    }
    256    resultName = "<unknown>";
    257  }
    258  const char* data = mData ? "yes" : "no";
    259 
    260  _retval.Truncate();
    261  _retval.AppendPrintf(format, msg, static_cast<uint32_t>(mResult), resultName,
    262                       location.get(), data);
    263 }
    264 
    265 JSObject* Exception::WrapObject(JSContext* cx,
    266                                JS::Handle<JSObject*> aGivenProto) {
    267  return Exception_Binding::Wrap(cx, this, aGivenProto);
    268 }
    269 
    270 void Exception::GetMessageMoz(nsString& retval) {
    271  CopyUTF8toUTF16(mMessage, retval);
    272 }
    273 
    274 uint32_t Exception::Result() const { return (uint32_t)mResult; }
    275 
    276 uint32_t Exception::SourceId(JSContext* aCx) const {
    277  if (mLocation) {
    278    return mLocation->GetSourceId(aCx);
    279  }
    280 
    281  return 0;
    282 }
    283 
    284 uint32_t Exception::LineNumber(JSContext* aCx) const {
    285  if (mLocation) {
    286    return mLocation->GetLineNumber(aCx);
    287  }
    288 
    289  return 0;
    290 }
    291 
    292 uint32_t Exception::ColumnNumber(JSContext* aCx) const {
    293  if (mLocation) {
    294    return mLocation->GetColumnNumber(aCx);
    295  }
    296 
    297  return 0;
    298 }
    299 
    300 already_AddRefed<nsIStackFrame> Exception::GetLocation() const {
    301  nsCOMPtr<nsIStackFrame> location = mLocation;
    302  return location.forget();
    303 }
    304 
    305 nsISupports* Exception::GetData() const { return mData; }
    306 
    307 void Exception::GetStack(JSContext* aCx, nsAString& aStack) const {
    308  if (mLocation) {
    309    mLocation->GetFormattedStack(aCx, aStack);
    310  }
    311 }
    312 
    313 void Exception::Stringify(JSContext* aCx, nsString& retval) {
    314  nsCString str;
    315  ToString(aCx, str);
    316  CopyUTF8toUTF16(str, retval);
    317 }
    318 
    319 DOMException::DOMException(nsresult aRv, const nsACString& aMessage,
    320                           const nsACString& aName, uint16_t aCode,
    321                           nsIStackFrame* aLocation)
    322    : Exception(aMessage, aRv, aName, aLocation, nullptr), mCode(aCode) {}
    323 DOMException::DOMException(nsresult aRv, nsCString&& aMessage,
    324                           nsCString&& aName, uint16_t aCode)
    325    : Exception(std::move(aMessage), aRv, std::move(aName)), mCode(aCode) {}
    326 
    327 void DOMException::ToString(JSContext* aCx, nsACString& aReturn) {
    328  aReturn.Truncate();
    329 
    330  static const char defaultMsg[] = "<no message>";
    331  static const char defaultLocation[] = "<unknown>";
    332  static const char defaultName[] = "<unknown>";
    333  static const char format[] =
    334      "[Exception... \"%s\"  code: \"%d\" nsresult: \"0x%" PRIx32
    335      " (%s)\"  location: \"%s\"]";
    336 
    337  nsAutoCString location;
    338 
    339  if (location.IsEmpty()) {
    340    location = defaultLocation;
    341  }
    342 
    343  const char* msg = !mMessage.IsEmpty() ? mMessage.get() : defaultMsg;
    344  const char* resultName = !mName.IsEmpty() ? mName.get() : defaultName;
    345 
    346  aReturn.AppendPrintf(format, msg, mCode, static_cast<uint32_t>(mResult),
    347                       resultName, location.get());
    348 }
    349 
    350 void DOMException::GetName(nsString& retval) { CopyUTF8toUTF16(mName, retval); }
    351 
    352 already_AddRefed<DOMException> DOMException::Constructor(
    353    GlobalObject& /* unused */, const nsAString& aMessage,
    354    const Optional<nsAString>& aName) {
    355  nsresult exceptionResult = NS_OK;
    356  uint16_t exceptionCode = 0;
    357  nsCString name("Error"_ns);
    358 
    359  if (aName.WasPassed()) {
    360    CopyUTF16toUTF8(aName.Value(), name);
    361    for (uint32_t idx = 0; idx < std::size(sDOMErrorMsgMap); idx++) {
    362      if (name.EqualsASCII(sDOMErrorMsgMap[idx].mName)) {
    363        exceptionResult = sDOMErrorMsgMap[idx].mNSResult;
    364        exceptionCode = sDOMErrorMsgMap[idx].mCode;
    365        break;
    366      }
    367    }
    368  }
    369 
    370  RefPtr<DOMException> retval = new DOMException(
    371      exceptionResult, NS_ConvertUTF16toUTF8(aMessage), name, exceptionCode);
    372  return retval.forget();
    373 }
    374 
    375 JSObject* DOMException::WrapObject(JSContext* aCx,
    376                                   JS::Handle<JSObject*> aGivenProto) {
    377  return DOMException_Binding::Wrap(aCx, this, aGivenProto);
    378 }
    379 
    380 /* static */
    381 already_AddRefed<DOMException> DOMException::Create(nsresult aRv) {
    382  nsCString name;
    383  nsCString message;
    384  uint16_t code;
    385  NSResultToNameAndMessage(aRv, name, message, &code);
    386  RefPtr<DOMException> inst = new DOMException(aRv, message, name, code);
    387  return inst.forget();
    388 }
    389 
    390 /* static */
    391 already_AddRefed<DOMException> DOMException::Create(
    392    nsresult aRv, const nsACString& aMessage) {
    393  nsCString name;
    394  nsCString message;
    395  uint16_t code;
    396  NSResultToNameAndMessage(aRv, name, message, &code);
    397  RefPtr<DOMException> inst = new DOMException(aRv, aMessage, name, code);
    398  return inst.forget();
    399 }
    400 
    401 static bool ReadAsCString(JSContext* aCx, JSStructuredCloneReader* aReader,
    402                          nsCString& aString) {
    403  JS::Rooted<JSString*> jsMessage(aCx);
    404  if (!JS_ReadString(aReader, &jsMessage)) {
    405    return false;
    406  }
    407  return AssignJSString(aCx, aString, jsMessage);
    408 }
    409 
    410 already_AddRefed<DOMException> DOMException::ReadStructuredClone(
    411    JSContext* aCx, nsIGlobalObject* aGlobal,
    412    JSStructuredCloneReader* aReader) {
    413  uint32_t reserved;
    414  nsresult rv;
    415  nsCString message;
    416  nsCString name;
    417  uint16_t code;
    418 
    419  if (!JS_ReadBytes(aReader, &reserved, 4) || !JS_ReadBytes(aReader, &rv, 4) ||
    420      !ReadAsCString(aCx, aReader, message) ||
    421      !ReadAsCString(aCx, aReader, name) || !JS_ReadBytes(aReader, &code, 2)) {
    422    return nullptr;
    423  };
    424 
    425  return do_AddRef(
    426      new DOMException(rv, std::move(message), std::move(name), code));
    427 }
    428 
    429 bool DOMException::WriteStructuredClone(
    430    JSContext* aCx, JSStructuredCloneWriter* aWriter) const {
    431  JS::Rooted<JS::Value> messageValue(aCx);
    432  JS::Rooted<JS::Value> nameValue(aCx);
    433  if (!NonVoidByteStringToJsval(aCx, mMessage, &messageValue) ||
    434      !NonVoidByteStringToJsval(aCx, mName, &nameValue)) {
    435    return false;
    436  }
    437 
    438  JS::Rooted<JSString*> message(aCx, messageValue.toString());
    439  JS::Rooted<JSString*> name(aCx, nameValue.toString());
    440 
    441  static_assert(sizeof(nsresult) == 4);
    442 
    443  // A reserved field. Use this to indicate stack serialization support etc.
    444  uint32_t reserved = 0;
    445  return JS_WriteBytes(aWriter, &reserved, 4) &&
    446         JS_WriteBytes(aWriter, &mResult, 4) &&
    447         JS_WriteString(aWriter, message) && JS_WriteString(aWriter, name) &&
    448         JS_WriteBytes(aWriter, &mCode, 2);
    449 };
    450 
    451 }  // namespace mozilla::dom