| /* |
| * 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 |