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

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

Issue 2657613005: [Extensions Bindings] Add chrome.runtime.lastError support (Closed)
Patch Set: . Created 3 years, 10 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
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 "extensions/renderer/api_binding_test.h"
10 #include "extensions/renderer/api_binding_test_util.h"
11 #include "gin/converter.h"
12 #include "gin/public/context_holder.h"
13
14 namespace extensions {
15
16 namespace {
17
18 std::string GetLastError(v8::Local<v8::Object> parent,
19 v8::Local<v8::Context> context) {
20 v8::Local<v8::Value> last_error =
21 GetPropertyFromObject(parent, context, "lastError");
22 if (last_error.IsEmpty() || !last_error->IsObject())
23 return V8ToString(last_error, context);
24 v8::Local<v8::Value> message =
25 GetPropertyFromObject(last_error.As<v8::Object>(), context, "message");
26 return V8ToString(message, context);
27 }
28
29 using ParentList =
30 std::vector<std::pair<v8::Local<v8::Context>, v8::Local<v8::Object>>>;
31 v8::Local<v8::Object> GetParent(
32 const ParentList& parents,
33 v8::Local<v8::Context> context) {
34 // This would be simpler with a map<context, object>, but Local<> doesn't
35 // define an operator<.
36 for (const auto& parent : parents) {
37 if (parent.first == context)
38 return parent.second;
39 }
40 return v8::Local<v8::Object>();
41 }
42
43 } // namespace
44
45 using APILastErrorTest = APIBindingTest;
46
47 // Test basic functionality of the lastError object.
48 TEST_F(APILastErrorTest, TestLastError) {
49 v8::HandleScope handle_scope(isolate());
50 v8::Local<v8::Context> context = ContextLocal();
51 v8::Local<v8::Object> parent_object = v8::Object::New(isolate());
52
53 ParentList parents = {{context, parent_object}};
54 APILastError last_error(base::Bind(&GetParent, parents));
55
56 EXPECT_EQ("undefined", GetLastError(parent_object, context));
57
58 last_error.SetError(context, "Some last error");
59 EXPECT_EQ("\"Some last error\"", GetLastError(parent_object, context));
60
61 last_error.ClearError(context, false);
62 EXPECT_EQ("undefined", GetLastError(parent_object, context));
63 }
64
65 // Test throwing an error if the last error wasn't checked.
66 TEST_F(APILastErrorTest, ReportIfUnchecked) {
67 v8::HandleScope handle_scope(isolate());
68 v8::Local<v8::Context> context = ContextLocal();
69 v8::Local<v8::Object> parent_object = v8::Object::New(isolate());
70
71 ParentList parents = {{context, parent_object}};
72 APILastError last_error(base::Bind(&GetParent, parents));
73
74 bool did_throw = false;
75 auto message_listener = [](v8::Local<v8::Message> message,
76 v8::Local<v8::Value> data) {
77 ASSERT_FALSE(data.IsEmpty());
78 ASSERT_TRUE(data->IsExternal());
79 bool* did_throw = static_cast<bool*>(data.As<v8::External>()->Value());
80 *did_throw = true;
81 EXPECT_EQ("Uncaught Error: A last error",
82 gin::V8ToString(message->Get()));
83 };
84
85 isolate()->AddMessageListener(message_listener,
86 v8::External::New(isolate(), &did_throw));
87 base::ScopedClosureRunner remove_message_listener(base::Bind(
88 [](v8::Isolate* isolate, v8::MessageCallback listener) {
89 isolate->RemoveMessageListeners(listener);
90 },
91 isolate(), message_listener));
92
93 last_error.SetError(context, "foo");
94 // GetLastError() will count as accessing the error property, so we shouldn't
95 // throw an exception.
96 EXPECT_EQ("\"foo\"", GetLastError(parent_object, context));
97 last_error.ClearError(context, true);
98 EXPECT_FALSE(did_throw);
99
100 // This time, we should throw an exception.
101 last_error.SetError(context, "A last error");
102 last_error.ClearError(context, true);
103 EXPECT_TRUE(did_throw);
Devlin 2017/01/27 21:41:48 Strangely, this test fails. I've verified that we
jbroman 2017/01/30 22:49:52 This is a subtlety about how throwing exceptions w
Devlin 2017/01/31 17:15:00 Using the TryCatch here worked, thanks! It's stil
jbroman 2017/02/06 18:31:26 That's a red herring (due to a bug in APIBindingTe
Devlin 2017/02/06 20:28:22 As discussed offline, the real question here is wh
jbroman 2017/02/07 18:19:42 Filed a v8 bug (https://bugs.chromium.org/p/v8/iss
104 }
105
106 // Test behavior when something else sets a lastError property on the parent
107 // object.
108 TEST_F(APILastErrorTest, NonLastErrorObject) {
109 v8::HandleScope handle_scope(isolate());
110 v8::Local<v8::Context> context = ContextLocal();
111 v8::Local<v8::Object> parent_object = v8::Object::New(isolate());
112
113 ParentList parents = {{context, parent_object}};
114 APILastError last_error(base::Bind(&GetParent, parents));
115
116 auto checked_set = [context](v8::Local<v8::Object> object,
117 base::StringPiece key,
118 v8::Local<v8::Value> value) {
119 v8::Maybe<bool> success =
120 object->Set(context, gin::StringToSymbol(context->GetIsolate(), key),
121 value);
122 ASSERT_TRUE(success.IsJust());
123 ASSERT_TRUE(success.FromJust());
124 };
125
126 // Set a "fake" lastError property on the parent.
127 v8::Local<v8::Object> fake_last_error = v8::Object::New(isolate());
128 checked_set(fake_last_error, "message",
129 gin::StringToV8(isolate(), "fake error"));
130 checked_set(parent_object, "lastError", fake_last_error);
131
132 EXPECT_EQ("\"fake error\"", GetLastError(parent_object, context));
133
134 // The bindings shouldn't mangle an existing property.
135 // TODO(devlin): Or should we? If someone sets chrome.runtime.lastError, it
136 // might be the right course of action to overwrite it.
137 last_error.SetError(context, "Real last error");
138 EXPECT_EQ("\"fake error\"", GetLastError(parent_object, context));
139 last_error.ClearError(context, false);
140 EXPECT_EQ("\"fake error\"", GetLastError(parent_object, context));
141 }
142
143 // Test lastError in multiple different contexts.
144 TEST_F(APILastErrorTest, MultipleContexts) {
145 v8::HandleScope handle_scope(isolate());
146 v8::Local<v8::Context> context_a = ContextLocal();
147 v8::Local<v8::Context> context_b = v8::Context::New(isolate());
148 gin::ContextHolder holder_b(isolate());
149 holder_b.SetContext(context_b);
150
151 v8::Local<v8::Object> parent_a = v8::Object::New(isolate());
152 v8::Local<v8::Object> parent_b = v8::Object::New(isolate());
153 ParentList parents = {{context_a, parent_a}, {context_b, parent_b}};
154 APILastError last_error(base::Bind(&GetParent, parents));
155
156 last_error.SetError(context_a, "Last error a");
157 EXPECT_EQ("\"Last error a\"", GetLastError(parent_a, context_a));
158 EXPECT_EQ("undefined", GetLastError(parent_b, context_b));
159
160 last_error.SetError(context_b, "Last error b");
161 EXPECT_EQ("\"Last error a\"", GetLastError(parent_a, context_a));
162 EXPECT_EQ("\"Last error b\"", GetLastError(parent_b, context_b));
163
164 last_error.ClearError(context_b, false);
165 EXPECT_EQ("\"Last error a\"", GetLastError(parent_a, context_a));
166 EXPECT_EQ("undefined", GetLastError(parent_b, context_b));
167
168 last_error.ClearError(context_a, false);
169 EXPECT_EQ("undefined", GetLastError(parent_a, context_a));
170 EXPECT_EQ("undefined", GetLastError(parent_b, context_b));
171 }
172
173 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698