Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(171)

Side by Side Diff: extensions/renderer/api_last_error.cc

Issue 2947463002: [Extensions Bindings] Add a bindings/ subdirectory under renderer (Closed)
Patch Set: . Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « extensions/renderer/api_last_error.h ('k') | extensions/renderer/api_last_error_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2017 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 "extensions/renderer/api_last_error.h"
6
7 #include "gin/converter.h"
8 #include "gin/handle.h"
9 #include "gin/object_template_builder.h"
10 #include "gin/wrappable.h"
11
12 namespace extensions {
13
14 namespace {
15
16 const char kLastErrorProperty[] = "lastError";
17 const char kScriptSuppliedValueKey[] = "script_supplied_value";
18
19 // The object corresponding to the lastError property, containing a single
20 // property ('message') with the last error. This object is stored on the parent
21 // (chrome.runtime in production) as a private property, and is returned via an
22 // accessor which marks the error as accessed.
23 class LastErrorObject final : public gin::Wrappable<LastErrorObject> {
24 public:
25 explicit LastErrorObject(const std::string& error) : error_(error) {}
26
27 static gin::WrapperInfo kWrapperInfo;
28
29 // gin::Wrappable:
30 gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
31 v8::Isolate* isolate) override {
32 DCHECK(isolate);
33 return Wrappable<LastErrorObject>::GetObjectTemplateBuilder(isolate)
34 .SetProperty("message", &LastErrorObject::error);
35 }
36
37 void Reset(const std::string& error) {
38 error_ = error;
39 accessed_ = false;
40 }
41
42 const std::string& error() const { return error_; }
43 bool accessed() const { return accessed_; }
44 void set_accessed() { accessed_ = true; }
45
46 private:
47 std::string error_;
48 bool accessed_ = false;
49
50 DISALLOW_COPY_AND_ASSIGN(LastErrorObject);
51 };
52
53 gin::WrapperInfo LastErrorObject::kWrapperInfo = {gin::kEmbedderNativeGin};
54
55 // An accessor to retrieve the last error property (curried in through data),
56 // and mark it as accessed.
57 void LastErrorGetter(v8::Local<v8::Name> property,
58 const v8::PropertyCallbackInfo<v8::Value>& info) {
59 v8::Isolate* isolate = info.GetIsolate();
60 v8::HandleScope handle_scope(isolate);
61 v8::Local<v8::Object> holder = info.Holder();
62 v8::Local<v8::Context> context = holder->CreationContext();
63
64 v8::Local<v8::Value> last_error;
65 v8::Local<v8::Private> last_error_key = v8::Private::ForApi(
66 isolate, gin::StringToSymbol(isolate, kLastErrorProperty));
67 if (!holder->GetPrivate(context, last_error_key).ToLocal(&last_error) ||
68 last_error != info.Data()) {
69 // Something funny happened - our private properties aren't set right.
70 NOTREACHED();
71 return;
72 }
73
74 v8::Local<v8::Value> return_value;
75
76 // It's possible that some script has set their own value for the last error
77 // property. If so, return that. Otherwise, return the real last error.
78 v8::Local<v8::Private> script_value_key = v8::Private::ForApi(
79 isolate, gin::StringToSymbol(isolate, kScriptSuppliedValueKey));
80 v8::Local<v8::Value> script_value;
81 if (holder->GetPrivate(context, script_value_key).ToLocal(&script_value) &&
82 !script_value->IsUndefined()) {
83 return_value = script_value;
84 } else {
85 LastErrorObject* last_error_obj = nullptr;
86 CHECK(gin::Converter<LastErrorObject*>::FromV8(isolate, last_error,
87 &last_error_obj));
88 last_error_obj->set_accessed();
89 return_value = last_error;
90 }
91
92 info.GetReturnValue().Set(return_value);
93 }
94
95 // Allow script to set the last error property.
96 void LastErrorSetter(v8::Local<v8::Name> property,
97 v8::Local<v8::Value> value,
98 const v8::PropertyCallbackInfo<void>& info) {
99 v8::Isolate* isolate = info.GetIsolate();
100 v8::HandleScope handle_scope(isolate);
101 v8::Local<v8::Object> holder = info.Holder();
102 v8::Local<v8::Context> context = holder->CreationContext();
103
104 v8::Local<v8::Private> script_value_key = v8::Private::ForApi(
105 isolate, gin::StringToSymbol(isolate, kScriptSuppliedValueKey));
106 v8::Maybe<bool> set_private =
107 holder->SetPrivate(context, script_value_key, value);
108 if (!set_private.IsJust() || !set_private.FromJust())
109 NOTREACHED();
110 }
111
112 } // namespace
113
114 APILastError::APILastError(const GetParent& get_parent,
115 const AddConsoleError& add_console_error)
116 : get_parent_(get_parent), add_console_error_(add_console_error) {}
117 APILastError::APILastError(APILastError&& other) = default;
118 APILastError::~APILastError() = default;
119
120 void APILastError::SetError(v8::Local<v8::Context> context,
121 const std::string& error) {
122 v8::Isolate* isolate = context->GetIsolate();
123 DCHECK(isolate);
124 v8::HandleScope handle_scope(isolate);
125
126 // The various accesses/sets on an object could potentially fail if script has
127 // set any crazy interceptors. For the most part, we don't care about behaving
128 // perfectly in these circumstances, but we eat the exception so callers don't
129 // have to worry about it. We also SetVerbose() so that developers will have a
130 // clue what happened if this does arise.
131 // TODO(devlin): Whether or not this needs to be verbose is debatable.
132 v8::TryCatch try_catch(isolate);
133 try_catch.SetVerbose(true);
134
135 v8::Local<v8::Object> parent = get_parent_.Run(context);
136 if (parent.IsEmpty())
137 return;
138 v8::Local<v8::String> key = gin::StringToSymbol(isolate, kLastErrorProperty);
139 v8::Local<v8::Value> v8_error;
140 // Two notes: this Get() is visible to external script, and this will actually
141 // mark the lastError as accessed, if one exists. These shouldn't be a
142 // problem (lastError is meant to be helpful, but isn't designed to handle
143 // crazy chaining, etc). However, if we decide we needed to be fancier, we
144 // could detect the presence of a current error through a GetPrivate(), and
145 // optionally throw it if one exists.
146 if (!parent->Get(context, key).ToLocal(&v8_error))
147 return;
148
149 if (!v8_error->IsUndefined()) {
150 // There may be an existing last error to overwrite.
151 LastErrorObject* last_error = nullptr;
152 if (!gin::Converter<LastErrorObject*>::FromV8(isolate, v8_error,
153 &last_error)) {
154 // If it's not a real lastError (e.g. if a script manually set it), don't
155 // do anything. We shouldn't mangle a property set by other script.
156 // TODO(devlin): Or should we? If someone sets chrome.runtime.lastError,
157 // it might be the right course of action to overwrite it.
158 return;
159 }
160 last_error->Reset(error);
161 } else {
162 v8::Local<v8::Value> last_error =
163 gin::CreateHandle(isolate, new LastErrorObject(error)).ToV8();
164 v8::Maybe<bool> set_private = parent->SetPrivate(
165 context, v8::Private::ForApi(isolate, key), last_error);
166 if (!set_private.IsJust() || !set_private.FromJust()) {
167 NOTREACHED();
168 return;
169 }
170 DCHECK(!last_error.IsEmpty());
171 // This Set() can fail, but there's nothing to do if it does (the exception
172 // will be caught by the TryCatch above).
173 ignore_result(parent->SetAccessor(context, key, &LastErrorGetter,
174 &LastErrorSetter, last_error));
175 }
176 }
177
178 void APILastError::ClearError(v8::Local<v8::Context> context,
179 bool report_if_unchecked) {
180 v8::Isolate* isolate = context->GetIsolate();
181 v8::HandleScope handle_scope(isolate);
182
183 v8::Local<v8::Object> parent;
184 LastErrorObject* last_error = nullptr;
185 v8::Local<v8::String> key;
186 v8::Local<v8::Private> private_key;
187 {
188 // See comment in SetError().
189 v8::TryCatch try_catch(isolate);
190 try_catch.SetVerbose(true);
191
192 parent = get_parent_.Run(context);
193 if (parent.IsEmpty())
194 return;
195 key = gin::StringToSymbol(isolate, kLastErrorProperty);
196 private_key = v8::Private::ForApi(isolate, key);
197 v8::Local<v8::Value> error;
198 // Access through GetPrivate() so that we don't trigger accessed().
199 if (!parent->GetPrivate(context, private_key).ToLocal(&error))
200 return;
201 if (!gin::Converter<LastErrorObject*>::FromV8(context->GetIsolate(), error,
202 &last_error)) {
203 return;
204 }
205 }
206
207 if (report_if_unchecked && !last_error->accessed()) {
208 add_console_error_.Run(
209 context, "Unchecked runtime.lastError: " + last_error->error());
210 }
211
212 // See comment in SetError().
213 v8::TryCatch try_catch(isolate);
214 try_catch.SetVerbose(true);
215
216 v8::Maybe<bool> delete_private = parent->DeletePrivate(context, private_key);
217 if (!delete_private.IsJust() || !delete_private.FromJust()) {
218 NOTREACHED();
219 return;
220 }
221 // This Delete() can fail, but there's nothing to do if it does (the exception
222 // will be caught by the TryCatch above).
223 ignore_result(parent->Delete(context, key));
224 }
225
226 bool APILastError::HasError(v8::Local<v8::Context> context) {
227 v8::Isolate* isolate = context->GetIsolate();
228 v8::HandleScope handle_scope(isolate);
229
230 // See comment in SetError().
231 v8::TryCatch try_catch(isolate);
232 try_catch.SetVerbose(true);
233
234 v8::Local<v8::Object> parent = get_parent_.Run(context);
235 if (parent.IsEmpty())
236 return false;
237 v8::Local<v8::Value> error;
238 v8::Local<v8::Private> key = v8::Private::ForApi(
239 isolate, gin::StringToSymbol(isolate, kLastErrorProperty));
240 // Access through GetPrivate() so we don't trigger accessed().
241 if (!parent->GetPrivate(context, key).ToLocal(&error))
242 return false;
243
244 LastErrorObject* last_error = nullptr;
245 return gin::Converter<LastErrorObject*>::FromV8(context->GetIsolate(), error,
246 &last_error);
247 }
248
249 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/api_last_error.h ('k') | extensions/renderer/api_last_error_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698