text: /*
   * Copyright (C) 2013 Google Inc. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are
   * met:
   *
   *     * Redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer.
   *     * Redistributions in binary form must reproduce the above
   * copyright notice, this list of conditions and the following disclaimer
   * in the documentation and/or other materials provided with the
   * distribution.
   *     * Neither the name of Google Inc. nor the names of its
   * contributors may be used to endorse or promote products derived from
   * this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  #include "third_party/blink/renderer/platform/bindings/exception_state.h"
  
  #include "base/notreached.h"
  #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
  #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
  
  namespace blink {
  
  ExceptionState::CreateDOMExceptionFunction
      ExceptionState::s_create_dom_exception_func_ = nullptr;
  
  // static
  void ExceptionState::SetCreateDOMExceptionFunction(
      CreateDOMExceptionFunction func) {
    DCHECK(!s_create_dom_exception_func_);
    s_create_dom_exception_func_ = func;
    DCHECK(s_create_dom_exception_func_);
  }
  
  void ExceptionState::ThrowException(ExceptionCode exception_code,
                                      const String& message) {
    // SecurityError is thrown via ThrowSecurityError, and _careful_ consideration
    // must be given to the data exposed to JavaScript via |sanitized_message|.
    DCHECK_NE(exception_code, ToExceptionCode(DOMExceptionCode::kSecurityError));
  
    const String& processed_message = AddExceptionContext(message);
  
    v8::Local<v8::Value> exception;
    switch (static_cast<ESErrorType>(exception_code)) {
      case ESErrorType::kError:
        exception = V8ThrowException::CreateError(isolate_, processed_message);
        break;
      case ESErrorType::kRangeError:
        exception =
            V8ThrowException::CreateRangeError(isolate_, processed_message);
        break;
      case ESErrorType::kReferenceError:
        exception =
            V8ThrowException::CreateReferenceError(isolate_, processed_message);
        break;
      case ESErrorType::kSyntaxError:
        exception =
            V8ThrowException::CreateSyntaxError(isolate_, processed_message);
        break;
      case ESErrorType::kTypeError:
        exception =
            V8ThrowException::CreateTypeError(isolate_, processed_message);
        break;
      default:
        if (IsDOMExceptionCode(exception_code)) {
          exception = s_create_dom_exception_func_(
              isolate_, static_cast<DOMExceptionCode>(exception_code),
              processed_message, String());
        } else {
          NOTREACHED();
          exception = s_create_dom_exception_func_(
              isolate_, DOMExceptionCode::kUnknownError, processed_message,
              String());
        }
    }
  
    SetException(exception_code, processed_message, exception);
  }
  
  void ExceptionState::ThrowDOMException(DOMExceptionCode exception_code,
                                         const String& message) {
    // SecurityError is thrown via ThrowSecurityError, and _careful_ consideration
    // must be given to the data exposed to JavaScript via |sanitized_message|.
    DCHECK_NE(exception_code, DOMExceptionCode::kSecurityError);
  
    const String& processed_message = AddExceptionContext(message);
    SetException(ToExceptionCode(exception_code), processed_message,
                 s_create_dom_exception_func_(isolate_, exception_code,
                                              processed_message, String()));
  }
  
  void ExceptionState::ThrowSecurityError(const String& sanitized_message,
                                          const String& unsanitized_message) {
    const String& final_sanitized = AddExceptionContext(sanitized_message);
    const String& final_unsanitized = AddExceptionContext(unsanitized_message);
    SetException(
        ToExceptionCode(DOMExceptionCode::kSecurityError), final_sanitized,
        s_create_dom_exception_func_(isolate_, DOMExceptionCode::kSecurityError,
                                     final_sanitized, final_unsanitized));
  }
  
  void ExceptionState::ThrowRangeError(const String& message) {
    SetException(ToExceptionCode(ESErrorType::kRangeError), message,
                 V8ThrowException::CreateRangeError(
                     isolate_, AddExceptionContext(message)));
  }
  
  void ExceptionState::ThrowTypeError(const String& message) {
    SetException(ToExceptionCode(ESErrorType::kTypeError), message,
                 V8ThrowException::CreateTypeError(isolate_,
                                                   AddExceptionContext(message)));
  }
  
  void ExceptionState::ThrowWasmCompileError(const String& message) {
    SetException(ToExceptionCode(ESErrorType::kWasmCompileError), message,
                 V8ThrowException::CreateWasmCompileError(
                     isolate_, AddExceptionContext(message)));
  }
  
  void ExceptionState::ThrowDOMException(DOMExceptionCode exception_code,
                                         const char* message) {
    ThrowDOMException(exception_code, String(message));
  }
  
  void ExceptionState::ThrowSecurityError(const char* sanitized_message,
                                          const char* unsanitized_message) {
    ThrowSecurityError(String(sanitized_message), String(unsanitized_message));
  }
  
  void ExceptionState::ThrowRangeError(const char* message) {
    ThrowRangeError(String(message));
  }
  
  void ExceptionState::ThrowTypeError(const char* message) {
    ThrowTypeError(String(message));
  }
  
  void ExceptionState::ThrowWasmCompileError(const char* message) {
    ThrowWasmCompileError(String(message));
  }
  
  void ExceptionState::RethrowV8Exception(v8::Local<v8::Value> value) {
    SetException(
        static_cast<ExceptionCode>(InternalExceptionType::kRethrownException),
        String(), value);
  }
  
  void ExceptionState::ClearException() {
    code_ = 0;
    message_ = String();
    exception_.Clear();
  }
  
  void ExceptionState::SetException(ExceptionCode exception_code,
                                    const String& message,
                                    v8::Local<v8::Value> exception) {
    CHECK(exception_code);
  
    code_ = exception_code;
    message_ = message;
    if (exception.IsEmpty()) {
      exception_.Clear();
    } else {
      DCHECK(isolate_);
      exception_.Set(isolate_, exception);
    }
  }
  
  void ExceptionState::PushContextScope(ContextScope* scope) {
    scope->SetParent(context_stack_top_);
    context_stack_top_ = scope;
  }
  
  void ExceptionState::PopContextScope() {
    DCHECK(!context_stack_top_);
    context_stack_top_ = context_stack_top_->GetParent();
  }
  
  namespace {
  
  String AddContextToMessage(const String& message,
                             const ExceptionContext& context) {
    const char* c = context.GetClassName();
    const char* p = context.GetPropertyName();
    const String& m = message;
  
    switch (context.GetContext()) {
      case ExceptionState::kConstructionContext:
        return ExceptionMessages::FailedToConstruct(c, m);
      case ExceptionState::kExecutionContext:
        return ExceptionMessages::FailedToExecute(p, c, m);
      case ExceptionState::kGetterContext:
        return ExceptionMessages::FailedToGet(p, c, m);
      case ExceptionState::kSetterContext:
        return ExceptionMessages::FailedToSet(p, c, m);
      case ExceptionState::kEnumerationContext:
        return ExceptionMessages::FailedToEnumerate(c, m);
      case ExceptionState::kQueryContext:
        break;
      case ExceptionState::kIndexedGetterContext:
        return ExceptionMessages::FailedToGetIndexed(c, m);
      case ExceptionState::kIndexedSetterContext:
        return ExceptionMessages::FailedToSetIndexed(c, m);
      case ExceptionState::kIndexedDeletionContext:
        return ExceptionMessages::FailedToDeleteIndexed(c, m);
      case ExceptionState::kNamedGetterContext:
        return ExceptionMessages::FailedToGetNamed(c, m);
      case ExceptionState::kNamedSetterContext:
        return ExceptionMessages::FailedToSetNamed(c, m);
      case ExceptionState::kNamedDeletionContext:
        return ExceptionMessages::FailedToDeleteNamed(c, m);
      case ExceptionState::kUnknownContext:
        break;
      default:
        NOTREACHED();
        break;
    }
    return m;
  }
  
  }  // namespace
  
  String ExceptionState::AddExceptionContext(
      const String& original_message) const {
    if (original_message.IsEmpty())
      return original_message;
  
    String message = original_message;
    for (const ContextScope* scope = context_stack_top_; scope;
         scope = scope->GetParent()) {
      message = AddContextToMessage(message, scope->GetContext());
    }
    message = AddContextToMessage(message, main_context_);
    return message;
  }
  
  NonThrowableExceptionState::NonThrowableExceptionState()
      : ExceptionState(nullptr,
                       ExceptionState::kUnknownContext,
                       nullptr,
                       nullptr),
        file_(""),
        line_(0) {}
  
  NonThrowableExceptionState::NonThrowableExceptionState(const char* file,
                                                         int line)
      : ExceptionState(nullptr,
                       ExceptionState::kUnknownContext,
                       nullptr,
                       nullptr),
        file_(file),
        line_(line) {}
  
  void NonThrowableExceptionState::ThrowDOMException(
      DOMExceptionCode exception_code,
      const String& message) {
    DCHECK_AT(false, file_, line_) << "DOMExeption should not be thrown.";
  }
  
  void NonThrowableExceptionState::ThrowRangeError(const String& message) {
    DCHECK_AT(false, file_, line_) << "RangeError should not be thrown.";
  }
  
  void NonThrowableExceptionState::ThrowSecurityError(
      const String& sanitized_message,
      const String&) {
    DCHECK_AT(false, file_, line_) << "SecurityError should not be thrown.";
  }
  
  void NonThrowableExceptionState::ThrowTypeError(const String& message) {
    DCHECK_AT(false, file_, line_) << "TypeError should not be thrown.";
  }
  
  void NonThrowableExceptionState::ThrowWasmCompileError(const String& message) {
    DCHECK_AT(false, file_, line_)
        << "WebAssembly.CompileError should not be thrown.";
  }
  
  void NonThrowableExceptionState::RethrowV8Exception(v8::Local<v8::Value>) {
    DCHECK_AT(false, file_, line_) << "An exception should not be rethrown.";
  }
  
  void DummyExceptionStateForTesting::ThrowDOMException(
      DOMExceptionCode exception_code,
      const String& message) {
    SetException(ToExceptionCode(exception_code), message,
                 v8::Local<v8::Value>());
  }
  
  void DummyExceptionStateForTesting::ThrowRangeError(const String& message) {
    SetException(ToExceptionCode(ESErrorType::kRangeError), message,
                 v8::Local<v8::Value>());
  }
  
  void DummyExceptionStateForTesting::ThrowSecurityError(
      const String& sanitized_message,
      const String&) {
    SetException(ToExceptionCode(DOMExceptionCode::kSecurityError),
                 sanitized_message, v8::Local<v8::Value>());
  }
  
  void DummyExceptionStateForTesting::ThrowTypeError(const String& message) {
    SetException(ToExceptionCode(ESErrorType::kTypeError), message,
                 v8::Local<v8::Value>());
  }
  
  void DummyExceptionStateForTesting::ThrowWasmCompileError(
      const String& message) {
    SetException(ToExceptionCode(ESErrorType::kWasmCompileError), message,
                 v8::Local<v8::Value>());
  }
  
  void DummyExceptionStateForTesting::RethrowV8Exception(v8::Local<v8::Value>) {
    SetException(
        static_cast<ExceptionCode>(InternalExceptionType::kRethrownException),
        String(), v8::Local<v8::Value>());
  }
  
  }  // namespace blink
  