1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/renderer/pepper/pepper_try_catch.h" 6 7#include "content/renderer/pepper/pepper_plugin_instance_impl.h" 8#include "gin/converter.h" 9#include "ppapi/shared_impl/ppapi_globals.h" 10#include "ppapi/shared_impl/var_tracker.h" 11 12namespace content { 13 14namespace { 15 16const char kConversionException[] = 17 "Error: Failed conversion between PP_Var and V8 value"; 18const char kInvalidException[] = "Error: An invalid exception was thrown."; 19 20} // namespace 21 22PepperTryCatch::PepperTryCatch(PepperPluginInstanceImpl* instance, 23 V8VarConverter::AllowObjectVars convert_objects) 24 : instance_(instance), 25 convert_objects_(convert_objects) {} 26 27PepperTryCatch::~PepperTryCatch() {} 28 29v8::Handle<v8::Value> PepperTryCatch::ToV8(PP_Var var) { 30 if (HasException()) { 31 SetException(kConversionException); 32 return v8::Handle<v8::Value>(); 33 } 34 35 V8VarConverter converter(instance_->pp_instance(), convert_objects_); 36 v8::Handle<v8::Value> result; 37 bool success = converter.ToV8Value(var, GetContext(), &result); 38 if (!success) { 39 SetException(kConversionException); 40 return v8::Handle<v8::Value>(); 41 } 42 return result; 43} 44 45ppapi::ScopedPPVar PepperTryCatch::FromV8(v8::Handle<v8::Value> v8_value) { 46 if (HasException() || v8_value.IsEmpty()) { 47 SetException(kConversionException); 48 return ppapi::ScopedPPVar(); 49 } 50 ppapi::ScopedPPVar result; 51 V8VarConverter converter(instance_->pp_instance(), convert_objects_); 52 bool success = converter.FromV8ValueSync(v8_value, GetContext(), &result); 53 if (!success) { 54 SetException(kConversionException); 55 return ppapi::ScopedPPVar(); 56 } 57 return result; 58} 59 60PepperTryCatchV8::PepperTryCatchV8( 61 PepperPluginInstanceImpl* instance, 62 V8VarConverter::AllowObjectVars convert_objects, 63 v8::Isolate* isolate) 64 : PepperTryCatch(instance, convert_objects), 65 exception_(PP_MakeUndefined()) { 66 // Typically when using PepperTryCatchV8 we are passed an isolate. We verify 67 // that this isolate is the same as the plugin isolate. 68 DCHECK(isolate == instance_->GetIsolate()); 69 70 // We assume that a handle scope and context has been setup by the user of 71 // this class. This is typically true because this class is used when calling 72 // into the plugin from JavaScript. We want to use whatever v8 context the 73 // caller is in. 74} 75 76PepperTryCatchV8::~PepperTryCatchV8() { 77 ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(exception_); 78} 79 80bool PepperTryCatchV8::HasException() { 81 return GetContext().IsEmpty() || exception_.type != PP_VARTYPE_UNDEFINED; 82} 83 84v8::Handle<v8::Context> PepperTryCatchV8::GetContext() { 85 // When calling from JS into the plugin always use the current context. 86 return instance_->GetIsolate()->GetCurrentContext(); 87} 88 89bool PepperTryCatchV8::ThrowException() { 90 if (!HasException()) 91 return false; 92 93 // If there is no context then we have an exception but we don't try to throw 94 // it into v8. 95 if (GetContext().IsEmpty()) 96 return true; 97 98 std::string message(kInvalidException); 99 ppapi::StringVar* message_var = ppapi::StringVar::FromPPVar(exception_); 100 if (message_var) 101 message = message_var->value(); 102 instance_->GetIsolate()->ThrowException(v8::Exception::Error( 103 gin::StringToV8(instance_->GetIsolate(), message))); 104 105 ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(exception_); 106 exception_ = PP_MakeUndefined(); 107 return true; 108} 109 110void PepperTryCatchV8::ThrowException(const char* message) { 111 SetException(message); 112 ThrowException(); 113} 114 115void PepperTryCatchV8::SetException(const char* message) { 116 if (HasException()) 117 return; 118 119 exception_ = ppapi::StringVar::StringToPPVar(message); 120} 121 122PepperTryCatchVar::PepperTryCatchVar(PepperPluginInstanceImpl* instance, 123 PP_Var* exception) 124 : PepperTryCatch(instance, V8VarConverter::kAllowObjectVars), 125 handle_scope_(instance_->GetIsolate()), 126 context_(GetContext()), 127 exception_(exception), 128 exception_is_set_(false) { 129 // We switch to the plugin context if it's not empty. 130 if (!context_.IsEmpty()) 131 context_->Enter(); 132} 133 134PepperTryCatchVar::~PepperTryCatchVar() { 135 if (!context_.IsEmpty()) 136 context_->Exit(); 137} 138 139bool PepperTryCatchVar::HasException() { 140 if (exception_is_set_) 141 return true; 142 143 std::string exception_message; 144 if (GetContext().IsEmpty()) { 145 exception_message = "The v8 context has been destroyed."; 146 } else if (try_catch_.HasCaught()) { 147 v8::String::Utf8Value utf8(try_catch_.Message()->Get()); 148 exception_message = std::string(*utf8, utf8.length()); 149 } 150 151 if (!exception_message.empty()) { 152 exception_is_set_ = true; 153 if (exception_) 154 *exception_ = ppapi::StringVar::StringToPPVar(exception_message); 155 } 156 157 return exception_is_set_; 158} 159 160v8::Handle<v8::Context> PepperTryCatchVar::GetContext() { 161 // When calling into JS from the plugin, always use the plugin context. 162 return instance_->GetMainWorldContext(); 163} 164 165void PepperTryCatchVar::SetException(const char* message) { 166 if (exception_is_set_) 167 return; 168 169 if (exception_) 170 *exception_ = ppapi::StringVar::StringToPPVar(message, strlen(message)); 171 exception_is_set_ = true; 172} 173 174} // namespace content 175