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 12 namespace content { 13 14 namespace { 15 16 const char kConversionException[] = 17 "Error: Failed conversion between PP_Var and V8 value"; 18 const char kInvalidException[] = "Error: An invalid exception was thrown."; 19 20 } // namespace 21 22 PepperTryCatch::PepperTryCatch(PepperPluginInstanceImpl* instance, 23 V8VarConverter::AllowObjectVars convert_objects) 24 : instance_(instance), 25 convert_objects_(convert_objects) {} 26 27 PepperTryCatch::~PepperTryCatch() {} 28 29 v8::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 45 ppapi::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 60 PepperTryCatchV8::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 76 PepperTryCatchV8::~PepperTryCatchV8() { 77 ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(exception_); 78 } 79 80 bool PepperTryCatchV8::HasException() { 81 return GetContext().IsEmpty() || exception_.type != PP_VARTYPE_UNDEFINED; 82 } 83 84 v8::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 89 bool 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 110 void PepperTryCatchV8::ThrowException(const char* message) { 111 SetException(message); 112 ThrowException(); 113 } 114 115 void PepperTryCatchV8::SetException(const char* message) { 116 if (HasException()) 117 return; 118 119 exception_ = ppapi::StringVar::StringToPPVar(message); 120 } 121 122 PepperTryCatchVar::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 134 PepperTryCatchVar::~PepperTryCatchVar() { 135 if (!context_.IsEmpty()) 136 context_->Exit(); 137 } 138 139 bool 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 160 v8::Handle<v8::Context> PepperTryCatchVar::GetContext() { 161 // When calling into JS from the plugin, always use the plugin context. 162 return instance_->GetMainWorldContext(); 163 } 164 165 void 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