// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/renderer/pepper/pepper_try_catch.h" #include "content/renderer/pepper/pepper_plugin_instance_impl.h" #include "content/renderer/pepper/v8_var_converter.h" #include "gin/converter.h" #include "ppapi/shared_impl/ppapi_globals.h" #include "ppapi/shared_impl/var_tracker.h" namespace content { namespace { const char kConversionException[] = "Error: Failed conversion between PP_Var and V8 value"; const char kInvalidException[] = "Error: An invalid exception was thrown."; } // namespace PepperTryCatch::PepperTryCatch(PepperPluginInstanceImpl* instance, V8VarConverter* var_converter) : instance_(instance), var_converter_(var_converter) {} PepperTryCatch::~PepperTryCatch() {} v8::Local PepperTryCatch::ToV8(PP_Var var) { if (HasException()) { SetException(kConversionException); return v8::Local(); } v8::Local result; bool success = var_converter_->ToV8Value(var, GetContext(), &result); if (!success) { SetException(kConversionException); return v8::Local(); } return result; } ppapi::ScopedPPVar PepperTryCatch::FromV8(v8::Local v8_value) { if (HasException() || v8_value.IsEmpty()) { SetException(kConversionException); return ppapi::ScopedPPVar(); } ppapi::ScopedPPVar result; bool success = var_converter_->FromV8ValueSync(v8_value, GetContext(), &result); if (!success) { SetException(kConversionException); return ppapi::ScopedPPVar(); } return result; } PepperTryCatchV8::PepperTryCatchV8(PepperPluginInstanceImpl* instance, V8VarConverter* var_converter, v8::Isolate* isolate) : PepperTryCatch(instance, var_converter), exception_(PP_MakeUndefined()) { // Typically when using PepperTryCatchV8 we are passed an isolate. We verify // that this isolate is the same as the plugin isolate. DCHECK(isolate == instance_->GetIsolate()); // We assume that a handle scope and context has been setup by the user of // this class. This is typically true because this class is used when calling // into the plugin from JavaScript. We want to use whatever v8 context the // caller is in. } PepperTryCatchV8::~PepperTryCatchV8() { ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(exception_); } bool PepperTryCatchV8::HasException() { return GetContext().IsEmpty() || exception_.type != PP_VARTYPE_UNDEFINED; } v8::Local PepperTryCatchV8::GetContext() { // When calling from JS into the plugin always use the current context. return instance_->GetIsolate()->GetCurrentContext(); } bool PepperTryCatchV8::ThrowException() { if (!HasException()) return false; // If there is no context then we have an exception but we don't try to throw // it into v8. if (GetContext().IsEmpty()) return true; std::string message(kInvalidException); ppapi::StringVar* message_var = ppapi::StringVar::FromPPVar(exception_); if (message_var) message = message_var->value(); instance_->GetIsolate()->ThrowException(v8::Exception::Error( gin::StringToV8(instance_->GetIsolate(), message))); ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(exception_); exception_ = PP_MakeUndefined(); return true; } void PepperTryCatchV8::ThrowException(const char* message) { SetException(message); ThrowException(); } void PepperTryCatchV8::SetException(const char* message) { if (HasException()) return; exception_ = ppapi::StringVar::StringToPPVar(message); } PepperTryCatchVar::PepperTryCatchVar(PepperPluginInstanceImpl* instance, V8VarConverter* var_converter, PP_Var* exception) : PepperTryCatch(instance, var_converter), handle_scope_(instance_->GetIsolate()), try_catch_(instance_->GetIsolate()), exception_(exception), exception_is_set_(false) { // Store a handle to the context here for 2 reasons: // 1) To hold a handle to it in case all other handles are destroyed. // 2) Because calling PepperPluginInstanceImpl::GetMainWorldContext() later // can result in trying to access the plugin element. However the plugin // element may have been destroyed during the PepperTryCatchVar (for // example if a script is executed which destroys the plugin element). So // we want to avoid accessing the plugin element again beyond this point. context_ = instance_->GetMainWorldContext(); // We switch to the plugin context if it's not empty. if (!context_.IsEmpty()) context_->Enter(); } PepperTryCatchVar::~PepperTryCatchVar() { if (!context_.IsEmpty()) context_->Exit(); } bool PepperTryCatchVar::HasException() { if (exception_is_set_) return true; std::string exception_message; if (context_.IsEmpty()) { exception_message = "The v8 context has been destroyed."; } else if (try_catch_.HasCaught()) { v8::Local message(try_catch_.Message()); if (!message.IsEmpty()) { v8::String::Utf8Value utf8(handle_scope_.GetIsolate(), try_catch_.Message()->Get()); exception_message = std::string(*utf8, utf8.length()); } else { exception_message = "There was a v8 exception."; } } if (!exception_message.empty()) { exception_is_set_ = true; if (exception_) *exception_ = ppapi::StringVar::StringToPPVar(exception_message); } return exception_is_set_; } v8::Local PepperTryCatchVar::GetContext() { return context_; } void PepperTryCatchVar::SetException(const char* message) { if (exception_is_set_) return; if (exception_) *exception_ = ppapi::StringVar::StringToPPVar(message, strlen(message)); exception_is_set_ = true; } } // namespace content