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