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

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

Issue 2947463002: [Extensions Bindings] Add a bindings/ subdirectory under renderer (Closed)
Patch Set: . Created 3 years, 5 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.cc ('k') | extensions/renderer/api_request_handler.h » ('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 "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/optional.h"
10 #include "extensions/renderer/api_binding_test.h"
11 #include "extensions/renderer/api_binding_test_util.h"
12 #include "gin/converter.h"
13 #include "gin/public/context_holder.h"
14
15 namespace extensions {
16
17 namespace {
18
19 void DoNothingWithError(v8::Local<v8::Context> context,
20 const std::string& error) {}
21
22 // Returns the v8 object for the lastError.
23 v8::Local<v8::Value> GetLastError(v8::Local<v8::Object> parent,
24 v8::Local<v8::Context> context) {
25 return GetPropertyFromObject(parent, context, "lastError");
26 }
27
28 // Returns a stringified version of the lastError message, if one exists, and
29 // otherwise a stringified version of whatever the lastError property is (e.g.
30 // undefined).
31 std::string GetLastErrorMessage(v8::Local<v8::Object> parent,
32 v8::Local<v8::Context> context) {
33 v8::Local<v8::Value> last_error = GetLastError(parent, context);
34 if (last_error.IsEmpty() || !last_error->IsObject())
35 return V8ToString(last_error, context);
36 v8::Local<v8::Value> message =
37 GetPropertyFromObject(last_error.As<v8::Object>(), context, "message");
38 return V8ToString(message, context);
39 }
40
41 using ParentList =
42 std::vector<std::pair<v8::Local<v8::Context>, v8::Local<v8::Object>>>;
43 v8::Local<v8::Object> GetParent(const ParentList& parents,
44 v8::Local<v8::Context> context) {
45 // This would be simpler with a map<context, object>, but Local<> doesn't
46 // define an operator<.
47 for (const auto& parent : parents) {
48 if (parent.first == context)
49 return parent.second;
50 }
51 return v8::Local<v8::Object>();
52 }
53
54 } // namespace
55
56 using APILastErrorTest = APIBindingTest;
57
58 // Test basic functionality of the lastError object.
59 TEST_F(APILastErrorTest, TestLastError) {
60 v8::HandleScope handle_scope(isolate());
61 v8::Local<v8::Context> context = MainContext();
62 v8::Local<v8::Object> parent_object = v8::Object::New(isolate());
63
64 ParentList parents = {{context, parent_object}};
65 APILastError last_error(base::Bind(&GetParent, parents),
66 base::Bind(&DoNothingWithError));
67
68 EXPECT_FALSE(last_error.HasError(context));
69 EXPECT_EQ("undefined", GetLastErrorMessage(parent_object, context));
70 // Check that the key isn't present on the object (as opposed to simply being
71 // undefined).
72 EXPECT_FALSE(
73 parent_object->Has(context, gin::StringToV8(isolate(), "lastError"))
74 .ToChecked());
75
76 last_error.SetError(context, "Some last error");
77 EXPECT_TRUE(last_error.HasError(context));
78 EXPECT_EQ("\"Some last error\"", GetLastErrorMessage(parent_object, context));
79
80 last_error.ClearError(context, false);
81 EXPECT_FALSE(last_error.HasError(context));
82 EXPECT_EQ("undefined", GetLastErrorMessage(parent_object, context));
83 EXPECT_FALSE(
84 parent_object->Has(context, gin::StringToV8(isolate(), "lastError"))
85 .ToChecked());
86 }
87
88 // Test throwing an error if the last error wasn't checked.
89 TEST_F(APILastErrorTest, ReportIfUnchecked) {
90 v8::HandleScope handle_scope(isolate());
91 v8::Local<v8::Context> context = MainContext();
92 v8::Local<v8::Object> parent_object = v8::Object::New(isolate());
93
94 base::Optional<std::string> console_error;
95 auto log_error = [](base::Optional<std::string>* console_error,
96 v8::Local<v8::Context> context,
97 const std::string& error) { *console_error = error; };
98
99 ParentList parents = {{context, parent_object}};
100 APILastError last_error(base::Bind(&GetParent, parents),
101 base::Bind(log_error, &console_error));
102
103 {
104 v8::TryCatch try_catch(isolate());
105 last_error.SetError(context, "foo");
106 // GetLastErrorMessage() will count as accessing the error property, so we
107 // shouldn't throw an exception.
108 EXPECT_EQ("\"foo\"", GetLastErrorMessage(parent_object, context));
109 last_error.ClearError(context, true);
110 EXPECT_FALSE(console_error);
111 EXPECT_FALSE(try_catch.HasCaught());
112 }
113
114 {
115 v8::TryCatch try_catch(isolate());
116 last_error.SetError(context, "foo");
117 // GetLastError() only accesses the error object, and not the message
118 // directly (e.g. chrome.runtime.lastError vs
119 // chrome.runtime.lastError.message), but should still count as access and
120 // shouldn't throw an exception.
121 v8::Local<v8::Value> v8_error = GetLastError(parent_object, context);
122 ASSERT_FALSE(v8_error.IsEmpty());
123 EXPECT_TRUE(v8_error->IsObject());
124 last_error.ClearError(context, true);
125 EXPECT_FALSE(console_error);
126 EXPECT_FALSE(try_catch.HasCaught());
127 }
128
129 {
130 v8::TryCatch try_catch(isolate());
131 // This time, we should log an error.
132 last_error.SetError(context, "A last error");
133 last_error.ClearError(context, true);
134 ASSERT_TRUE(console_error);
135 EXPECT_EQ("Unchecked runtime.lastError: A last error", *console_error);
136 // We shouldn't have thrown an exception in order to prevent disrupting
137 // JS execution.
138 EXPECT_FALSE(try_catch.HasCaught());
139 }
140
141 {
142 v8::TryCatch try_catch(isolate());
143 last_error.SetError(context, "A last error");
144 // Access through the internal HasError() should not count as access.
145 EXPECT_TRUE(last_error.HasError(context));
146 last_error.ClearError(context, true);
147 ASSERT_TRUE(console_error);
148 EXPECT_EQ("Unchecked runtime.lastError: A last error", *console_error);
149 EXPECT_FALSE(try_catch.HasCaught());
150 }
151 }
152
153 // Test behavior when something else sets a lastError property on the parent
154 // object.
155 TEST_F(APILastErrorTest, NonLastErrorObject) {
156 v8::HandleScope handle_scope(isolate());
157 v8::Local<v8::Context> context = MainContext();
158 v8::Local<v8::Object> parent_object = v8::Object::New(isolate());
159
160 ParentList parents = {{context, parent_object}};
161 APILastError last_error(base::Bind(&GetParent, parents),
162 base::Bind(&DoNothingWithError));
163
164 auto checked_set = [context](v8::Local<v8::Object> object,
165 base::StringPiece key,
166 v8::Local<v8::Value> value) {
167 v8::Maybe<bool> success = object->Set(
168 context, gin::StringToSymbol(context->GetIsolate(), key), value);
169 ASSERT_TRUE(success.IsJust());
170 ASSERT_TRUE(success.FromJust());
171 };
172
173 // Set a "fake" lastError property on the parent.
174 v8::Local<v8::Object> fake_last_error = v8::Object::New(isolate());
175 checked_set(fake_last_error, "message",
176 gin::StringToV8(isolate(), "fake error"));
177 checked_set(parent_object, "lastError", fake_last_error);
178
179 EXPECT_EQ("\"fake error\"", GetLastErrorMessage(parent_object, context));
180
181 // The bindings shouldn't mangle an existing property (or maybe we should -
182 // see the TODO in api_last_error.cc).
183 last_error.SetError(context, "Real last error");
184 EXPECT_EQ("\"fake error\"", GetLastErrorMessage(parent_object, context));
185 last_error.ClearError(context, false);
186 EXPECT_EQ("\"fake error\"", GetLastErrorMessage(parent_object, context));
187
188 checked_set(parent_object, "lastError", v8::Undefined(isolate()));
189 EXPECT_EQ("undefined", GetLastErrorMessage(parent_object, context));
190 last_error.SetError(context, "a last error");
191 EXPECT_EQ("\"a last error\"", GetLastErrorMessage(parent_object, context));
192 checked_set(parent_object, "lastError", fake_last_error);
193 EXPECT_EQ("\"fake error\"", GetLastErrorMessage(parent_object, context));
194 }
195
196 // Test lastError in multiple different contexts.
197 TEST_F(APILastErrorTest, MultipleContexts) {
198 v8::HandleScope handle_scope(isolate());
199 v8::Local<v8::Context> context_a = MainContext();
200 v8::Local<v8::Context> context_b = AddContext();
201
202 v8::Local<v8::Object> parent_a = v8::Object::New(isolate());
203 v8::Local<v8::Object> parent_b = v8::Object::New(isolate());
204 ParentList parents = {{context_a, parent_a}, {context_b, parent_b}};
205 APILastError last_error(base::Bind(&GetParent, parents),
206 base::Bind(&DoNothingWithError));
207
208 last_error.SetError(context_a, "Last error a");
209 EXPECT_EQ("\"Last error a\"", GetLastErrorMessage(parent_a, context_a));
210 EXPECT_EQ("undefined", GetLastErrorMessage(parent_b, context_b));
211
212 last_error.SetError(context_b, "Last error b");
213 EXPECT_EQ("\"Last error a\"", GetLastErrorMessage(parent_a, context_a));
214 EXPECT_EQ("\"Last error b\"", GetLastErrorMessage(parent_b, context_b));
215
216 last_error.ClearError(context_b, false);
217 EXPECT_EQ("\"Last error a\"", GetLastErrorMessage(parent_a, context_a));
218 EXPECT_EQ("undefined", GetLastErrorMessage(parent_b, context_b));
219
220 last_error.ClearError(context_a, false);
221 EXPECT_EQ("undefined", GetLastErrorMessage(parent_a, context_a));
222 EXPECT_EQ("undefined", GetLastErrorMessage(parent_b, context_b));
223 }
224
225 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/api_last_error.cc ('k') | extensions/renderer/api_request_handler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698