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

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

Issue 2753303003: [Extensions Bindings] Remove APIBindingsSystemTestWithRealAPI (Closed)
Patch Set: lazyboy's Created 3 years, 9 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "extensions/renderer/api_bindings_system.h" 5 #include "extensions/renderer/api_bindings_system.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/macros.h" 8 #include "base/macros.h"
9 #include "base/memory/ptr_util.h" 9 #include "base/memory/ptr_util.h"
10 #include "base/stl_util.h" 10 #include "base/stl_util.h"
(...skipping 17 matching lines...) Expand all
28 const char kAlphaAPIName[] = "alpha"; 28 const char kAlphaAPIName[] = "alpha";
29 const char kAlphaAPISpec[] = 29 const char kAlphaAPISpec[] =
30 "{" 30 "{"
31 " 'types': [{" 31 " 'types': [{"
32 " 'id': 'alpha.objRef'," 32 " 'id': 'alpha.objRef',"
33 " 'type': 'object'," 33 " 'type': 'object',"
34 " 'properties': {" 34 " 'properties': {"
35 " 'prop1': {'type': 'string'}," 35 " 'prop1': {'type': 'string'},"
36 " 'prop2': {'type': 'integer', 'optional': true}" 36 " 'prop2': {'type': 'integer', 'optional': true}"
37 " }" 37 " }"
38 " }, {"
39 " 'id': 'alpha.enumRef',"
40 " 'type': 'string',"
41 " 'enum': ['cat', 'dog']"
38 " }]," 42 " }],"
39 " 'functions': [{" 43 " 'functions': [{"
40 " 'name': 'functionWithCallback'," 44 " 'name': 'functionWithCallback',"
41 " 'parameters': [{" 45 " 'parameters': [{"
42 " 'name': 'str'," 46 " 'name': 'str',"
43 " 'type': 'string'" 47 " 'type': 'string'"
44 " }, {" 48 " }, {"
45 " 'name': 'callback'," 49 " 'name': 'callback',"
46 " 'type': 'function'" 50 " 'type': 'function'"
47 " }]" 51 " }]"
48 " }, {" 52 " }, {"
49 " 'name': 'functionWithRefAndCallback'," 53 " 'name': 'functionWithRefAndCallback',"
50 " 'parameters': [{" 54 " 'parameters': [{"
51 " 'name': 'ref'," 55 " 'name': 'ref',"
52 " '$ref': 'alpha.objRef'" 56 " '$ref': 'alpha.objRef'"
53 " }, {" 57 " }, {"
54 " 'name': 'callback'," 58 " 'name': 'callback',"
55 " 'type': 'function'" 59 " 'type': 'function'"
56 " }]" 60 " }]"
61 " }, {"
62 " 'name': 'functionWithEnum',"
63 " 'parameters': [{'name': 'e', '$ref': 'alpha.enumRef'}]"
57 " }]," 64 " }],"
58 " 'events': [{" 65 " 'events': [{"
59 " 'name': 'alphaEvent'" 66 " 'name': 'alphaEvent'"
60 " }]" 67 " }]"
61 "}"; 68 "}";
62 69
63 // Another fake API for testing. 70 // Another fake API for testing.
64 const char kBetaAPIName[] = "beta"; 71 const char kBetaAPIName[] = "beta";
65 const char kBetaAPISpec[] = 72 const char kBetaAPISpec[] =
66 "{" 73 "{"
(...skipping 13 matching lines...) Expand all
80 "}"; 87 "}";
81 88
82 bool AllowAllAPIs(const std::string& name) { 89 bool AllowAllAPIs(const std::string& name) {
83 return true; 90 return true;
84 } 91 }
85 92
86 } // namespace 93 } // namespace
87 94
88 // The base class to test the APIBindingsSystem. This allows subclasses to 95 // The base class to test the APIBindingsSystem. This allows subclasses to
89 // retrieve API schemas differently. 96 // retrieve API schemas differently.
90 class APIBindingsSystemTestBase : public APIBindingTest { 97 class APIBindingsSystemTest : public APIBindingTest {
91 public: 98 public:
92 // Returns the DictionaryValue representing the schema with the given API 99 // Returns the DictionaryValue representing the schema with the given API
93 // name. 100 // name.
94 virtual const base::DictionaryValue& GetAPISchema( 101 const base::DictionaryValue& GetAPISchema(const std::string& api_name) {
95 const std::string& api_name) = 0; 102 EXPECT_TRUE(base::ContainsKey(api_schemas_, api_name));
103 return *api_schemas_[api_name];
104 }
96 105
97 // Stores the request in |last_request_|. 106 // Stores the request in |last_request_|.
98 void OnAPIRequest(std::unique_ptr<APIRequestHandler::Request> request, 107 void OnAPIRequest(std::unique_ptr<APIRequestHandler::Request> request,
99 v8::Local<v8::Context> context) { 108 v8::Local<v8::Context> context) {
100 ASSERT_FALSE(last_request_); 109 ASSERT_FALSE(last_request_);
101 last_request_ = std::move(request); 110 last_request_ = std::move(request);
102 } 111 }
103 112
104 void OnEventListenersChanged(const std::string& event_name, 113 void OnEventListenersChanged(const std::string& event_name,
105 binding::EventListenersChanged changed, 114 binding::EventListenersChanged changed,
106 v8::Local<v8::Context> context) {} 115 v8::Local<v8::Context> context) {}
107 116
108 protected: 117 protected:
109 APIBindingsSystemTestBase() {} 118 APIBindingsSystemTest() {}
110 void SetUp() override { 119 void SetUp() override {
111 APIBindingTest::SetUp(); 120 APIBindingTest::SetUp();
121
122 // Create the fake API schemas.
123 {
124 struct APIData {
125 const char* name;
126 const char* spec;
127 } api_data[] = {
128 {kAlphaAPIName, kAlphaAPISpec},
129 {kBetaAPIName, kBetaAPISpec},
130 {kGammaAPIName, kGammaAPISpec},
131 };
132 for (const auto& api : api_data) {
133 std::unique_ptr<base::DictionaryValue> api_schema =
134 DictionaryValueFromString(api.spec);
135 ASSERT_TRUE(api_schema);
136 api_schemas_[api.name] = std::move(api_schema);
137 }
138 }
139
112 bindings_system_ = base::MakeUnique<APIBindingsSystem>( 140 bindings_system_ = base::MakeUnique<APIBindingsSystem>(
113 base::Bind(&RunFunctionOnGlobalAndIgnoreResult), 141 base::Bind(&RunFunctionOnGlobalAndIgnoreResult),
114 base::Bind(&RunFunctionOnGlobalAndReturnHandle), 142 base::Bind(&RunFunctionOnGlobalAndReturnHandle),
115 base::Bind(&APIBindingsSystemTestBase::GetAPISchema, 143 base::Bind(&APIBindingsSystemTest::GetAPISchema,
116 base::Unretained(this)), 144 base::Unretained(this)),
117 base::Bind(&APIBindingsSystemTestBase::OnAPIRequest, 145 base::Bind(&APIBindingsSystemTest::OnAPIRequest,
118 base::Unretained(this)), 146 base::Unretained(this)),
119 base::Bind(&APIBindingsSystemTestBase::OnEventListenersChanged, 147 base::Bind(&APIBindingsSystemTest::OnEventListenersChanged,
120 base::Unretained(this)), 148 base::Unretained(this)),
121 APILastError(APILastError::GetParent())); 149 APILastError(APILastError::GetParent()));
122 } 150 }
123 151
124 void TearDown() override { 152 void TearDown() override {
125 // Dispose all contexts now so that we call WillReleaseContext(). 153 // Dispose all contexts now so that we call WillReleaseContext().
126 DisposeAllContexts(); 154 DisposeAllContexts();
127 bindings_system_.reset(); 155 bindings_system_.reset();
128 APIBindingTest::TearDown(); 156 APIBindingTest::TearDown();
129 } 157 }
130 158
131 void OnWillDisposeContext(v8::Local<v8::Context> context) override { 159 void OnWillDisposeContext(v8::Local<v8::Context> context) override {
132 bindings_system_->WillReleaseContext(context); 160 bindings_system_->WillReleaseContext(context);
133 } 161 }
134 162
135 // Checks that |last_request_| exists and was provided with the 163 // Checks that |last_request_| exists and was provided with the
136 // |expected_name| and |expected_arguments|. 164 // |expected_name| and |expected_arguments|.
137 void ValidateLastRequest(const std::string& expected_name, 165 void ValidateLastRequest(const std::string& expected_name,
138 const std::string& expected_arguments); 166 const std::string& expected_arguments) {
167 ASSERT_TRUE(last_request());
168 // Note that even if no arguments are provided by the API call, we should
169 // have an empty list.
170 ASSERT_TRUE(last_request()->arguments);
171 EXPECT_EQ(expected_name, last_request()->method_name);
172 EXPECT_EQ(ReplaceSingleQuotes(expected_arguments),
173 ValueToString(*last_request()->arguments));
174 }
175
176 void CallFunctionOnObject(v8::Local<v8::Context> context,
177 v8::Local<v8::Object> object,
178 const std::string& script_source) {
179 std::string wrapped_script_source =
180 base::StringPrintf("(function(obj) { %s })", script_source.c_str());
181
182 v8::Local<v8::Function> func =
183 FunctionFromString(context, wrapped_script_source);
184 ASSERT_FALSE(func.IsEmpty());
185
186 v8::Local<v8::Value> argv[] = {object};
187 RunFunction(func, context, 1, argv);
188 }
139 189
140 const APIRequestHandler::Request* last_request() const { 190 const APIRequestHandler::Request* last_request() const {
141 return last_request_.get(); 191 return last_request_.get();
142 } 192 }
143 void reset_last_request() { last_request_.reset(); } 193 void reset_last_request() { last_request_.reset(); }
144 APIBindingsSystem* bindings_system() { return bindings_system_.get(); } 194 APIBindingsSystem* bindings_system() { return bindings_system_.get(); }
145 195
146 private: 196 private:
197 // The API schemas for the fake APIs.
198 std::map<std::string, std::unique_ptr<base::DictionaryValue>> api_schemas_;
199
147 // The APIBindingsSystem associated with the test. Safe to use across multiple 200 // The APIBindingsSystem associated with the test. Safe to use across multiple
148 // contexts. 201 // contexts.
149 std::unique_ptr<APIBindingsSystem> bindings_system_; 202 std::unique_ptr<APIBindingsSystem> bindings_system_;
150 203
151 // The last request to be received from the APIBindingsSystem, or null if 204 // The last request to be received from the APIBindingsSystem, or null if
152 // there is none. 205 // there is none.
153 std::unique_ptr<APIRequestHandler::Request> last_request_; 206 std::unique_ptr<APIRequestHandler::Request> last_request_;
154 207
155 DISALLOW_COPY_AND_ASSIGN(APIBindingsSystemTestBase);
156 };
157
158 void APIBindingsSystemTestBase::ValidateLastRequest(
159 const std::string& expected_name,
160 const std::string& expected_arguments) {
161 ASSERT_TRUE(last_request());
162 // Note that even if no arguments are provided by the API call, we should have
163 // an empty list.
164 ASSERT_TRUE(last_request()->arguments);
165 EXPECT_EQ(expected_name, last_request()->method_name);
166 EXPECT_EQ(ReplaceSingleQuotes(expected_arguments),
167 ValueToString(*last_request()->arguments));
168 }
169
170 // An implementation that works with fake/supplied APIs, for easy testability.
171 class APIBindingsSystemTest : public APIBindingsSystemTestBase {
172 protected:
173 APIBindingsSystemTest() {}
174
175 // Calls a function constructed from |script_source| on the given |object|.
176 void CallFunctionOnObject(v8::Local<v8::Context> context,
177 v8::Local<v8::Object> object,
178 const std::string& script_source);
179
180 private:
181 const base::DictionaryValue& GetAPISchema(
182 const std::string& api_name) override {
183 EXPECT_TRUE(base::ContainsKey(api_schemas_, api_name));
184 return *api_schemas_[api_name];
185 }
186 void SetUp() override;
187
188 // The API schemas for the fake APIs.
189 std::map<std::string, std::unique_ptr<base::DictionaryValue>> api_schemas_;
190
191 DISALLOW_COPY_AND_ASSIGN(APIBindingsSystemTest); 208 DISALLOW_COPY_AND_ASSIGN(APIBindingsSystemTest);
192 }; 209 };
193 210
194 void APIBindingsSystemTest::CallFunctionOnObject(
195 v8::Local<v8::Context> context,
196 v8::Local<v8::Object> object,
197 const std::string& script_source) {
198 std::string wrapped_script_source =
199 base::StringPrintf("(function(obj) { %s })", script_source.c_str());
200
201 v8::Local<v8::Function> func =
202 FunctionFromString(context, wrapped_script_source);
203 ASSERT_FALSE(func.IsEmpty());
204
205 v8::Local<v8::Value> argv[] = {object};
206 RunFunction(func, context, 1, argv);
207 }
208
209 void APIBindingsSystemTest::SetUp() {
210 // Create the fake API schemas.
211 {
212 struct APIData {
213 const char* name;
214 const char* spec;
215 } api_data[] = {
216 {kAlphaAPIName, kAlphaAPISpec},
217 {kBetaAPIName, kBetaAPISpec},
218 {kGammaAPIName, kGammaAPISpec},
219 };
220 for (const auto& api : api_data) {
221 std::unique_ptr<base::DictionaryValue> api_schema =
222 DictionaryValueFromString(api.spec);
223 ASSERT_TRUE(api_schema);
224 api_schemas_[api.name] = std::move(api_schema);
225 }
226 }
227
228 APIBindingsSystemTestBase::SetUp();
229 }
230
231 // Tests API object initialization, calling a method on the supplied APIs, and 211 // Tests API object initialization, calling a method on the supplied APIs, and
232 // triggering the callback for the request. 212 // triggering the callback for the request.
233 TEST_F(APIBindingsSystemTest, TestInitializationAndCallbacks) { 213 TEST_F(APIBindingsSystemTest, TestInitializationAndCallbacks) {
234 v8::HandleScope handle_scope(isolate()); 214 v8::HandleScope handle_scope(isolate());
235 v8::Local<v8::Context> context = MainContext(); 215 v8::Local<v8::Context> context = MainContext();
236 216
237 v8::Local<v8::Object> alpha_api = bindings_system()->CreateAPIInstance( 217 v8::Local<v8::Object> alpha_api = bindings_system()->CreateAPIInstance(
238 kAlphaAPIName, context, isolate(), base::Bind(&AllowAllAPIs), nullptr); 218 kAlphaAPIName, context, isolate(), base::Bind(&AllowAllAPIs), nullptr);
239 ASSERT_FALSE(alpha_api.IsEmpty()); 219 ASSERT_FALSE(alpha_api.IsEmpty());
240 v8::Local<v8::Object> beta_api = bindings_system()->CreateAPIInstance( 220 v8::Local<v8::Object> beta_api = bindings_system()->CreateAPIInstance(
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
278 258
279 bindings_system()->CompleteRequest(last_request()->request_id, 259 bindings_system()->CompleteRequest(last_request()->request_id,
280 base::ListValue(), std::string()); 260 base::ListValue(), std::string());
281 261
282 EXPECT_EQ("[]", GetStringPropertyFromObject(context->Global(), context, 262 EXPECT_EQ("[]", GetStringPropertyFromObject(context->Global(), context,
283 "callbackArguments")); 263 "callbackArguments"));
284 reset_last_request(); 264 reset_last_request();
285 } 265 }
286 266
287 { 267 {
268 // Test an invalid invocation -> throwing error.
269 const char kTestCall[] =
270 "(function(obj) { obj.functionWithEnum('mouse') })";
271 v8::Local<v8::Function> function = FunctionFromString(context, kTestCall);
272 v8::Local<v8::Value> args[] = {alpha_api};
273 RunFunctionAndExpectError(function, context, arraysize(args), args,
274 "Uncaught TypeError: Invalid invocation");
275 EXPECT_FALSE(last_request());
276 reset_last_request(); // Just to not pollute future results.
277 }
278
279 {
288 // Test an event registration -> event occurrence. 280 // Test an event registration -> event occurrence.
289 const char kTestCall[] = 281 const char kTestCall[] =
290 "obj.alphaEvent.addListener(function() {\n" 282 "obj.alphaEvent.addListener(function() {\n"
291 " this.eventArguments = Array.from(arguments);\n" 283 " this.eventArguments = Array.from(arguments);\n"
292 "});\n"; 284 "});\n";
293 CallFunctionOnObject(context, alpha_api, kTestCall); 285 CallFunctionOnObject(context, alpha_api, kTestCall);
294 286
295 const char kResponseArgsJson[] = "['response',1,{'key':42}]"; 287 const char kResponseArgsJson[] = "['response',1,{'key':42}]";
296 std::unique_ptr<base::ListValue> expected_args = 288 std::unique_ptr<base::ListValue> expected_args =
297 ListValueFromString(kResponseArgsJson); 289 ListValueFromString(kResponseArgsJson);
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
438 430
439 { 431 {
440 // Test a simple call -> response. 432 // Test a simple call -> response.
441 const char kTestCall[] = "obj.functionWithExternalRef({prop1: 'foo'});"; 433 const char kTestCall[] = "obj.functionWithExternalRef({prop1: 'foo'});";
442 CallFunctionOnObject(context, gamma_api, kTestCall); 434 CallFunctionOnObject(context, gamma_api, kTestCall);
443 ValidateLastRequest("gamma.functionWithExternalRef", "[{'prop1':'foo'}]"); 435 ValidateLastRequest("gamma.functionWithExternalRef", "[{'prop1':'foo'}]");
444 reset_last_request(); 436 reset_last_request();
445 } 437 }
446 } 438 }
447 439
448 // An implementation using real API schemas.
449 class APIBindingsSystemTestWithRealAPI : public APIBindingsSystemTestBase {
450 protected:
451 APIBindingsSystemTestWithRealAPI() {}
452
453 // Executes the given |script_source| in |context|, expecting no exceptions.
454 void ExecuteScript(v8::Local<v8::Context> context,
455 const std::string& script_source);
456
457 // Executes the given |script_source| in |context| and compares a caught
458 // error to |expected_error|.
459 void ExecuteScriptAndExpectError(v8::Local<v8::Context> context,
460 const std::string& script_source,
461 const std::string& expected_error);
462
463 private:
464 const base::DictionaryValue& GetAPISchema(
465 const std::string& api_name) override {
466 const base::DictionaryValue* schema =
467 ExtensionAPI::GetSharedInstance()->GetSchema(api_name);
468 EXPECT_TRUE(schema);
469 return *schema;
470 }
471
472 DISALLOW_COPY_AND_ASSIGN(APIBindingsSystemTestWithRealAPI);
473 };
474
475 void APIBindingsSystemTestWithRealAPI::ExecuteScript(
476 v8::Local<v8::Context> context,
477 const std::string& script_source) {
478 v8::TryCatch try_catch(isolate());
479 // V8ValueFromScriptSource runs the source and returns the result; here, we
480 // only care about running the source.
481 V8ValueFromScriptSource(context, script_source);
482 EXPECT_FALSE(try_catch.HasCaught())
483 << gin::V8ToString(try_catch.Message()->Get());
484 }
485
486 void APIBindingsSystemTestWithRealAPI::ExecuteScriptAndExpectError(
487 v8::Local<v8::Context> context,
488 const std::string& script_source,
489 const std::string& expected_error) {
490 v8::TryCatch try_catch(isolate());
491 V8ValueFromScriptSource(context, script_source);
492 ASSERT_TRUE(try_catch.HasCaught()) << script_source;
493 EXPECT_EQ(expected_error, gin::V8ToString(try_catch.Message()->Get()));
494 }
495
496 // The following test demonstrates how APIBindingsSystem can be used with "real"
497 // Extension APIs; that is, using the raw Extension API schemas, rather than a
498 // substituted test schema. This is useful to both show how the system is
499 // intended to be used in the future as well as to make sure that it works with
500 // actual APIs.
501 TEST_F(APIBindingsSystemTestWithRealAPI, RealAPIs) {
502 v8::HandleScope handle_scope(isolate());
503 v8::Local<v8::Context> context = MainContext();
504
505 v8::Local<v8::Object> chrome = v8::Object::New(isolate());
506 {
507 v8::Maybe<bool> res = context->Global()->Set(
508 context, gin::StringToV8(isolate(), "chrome"), chrome);
509 ASSERT_TRUE(res.IsJust());
510 ASSERT_TRUE(res.FromJust());
511 }
512
513 auto add_api_to_chrome = [this, &chrome,
514 &context](const std::string& api_name) {
515 v8::Local<v8::Object> api = bindings_system()->CreateAPIInstance(
516 api_name, context, context->GetIsolate(), base::Bind(&AllowAllAPIs),
517 nullptr);
518 ASSERT_FALSE(api.IsEmpty()) << api_name;
519 v8::Maybe<bool> res = chrome->Set(
520 context, gin::StringToV8(context->GetIsolate(), api_name), api);
521 ASSERT_TRUE(res.IsJust());
522 ASSERT_TRUE(res.FromJust());
523 };
524
525 // Pick two relatively small APIs that don't have any custom bindings (which
526 // aren't supported yet).
527 add_api_to_chrome("idle");
528 add_api_to_chrome("power");
529
530 // Test passing methods.
531 {
532 const char kTestCall[] = "chrome.power.requestKeepAwake('display');";
533 ExecuteScript(context, kTestCall);
534 ValidateLastRequest("power.requestKeepAwake", "['display']");
535 EXPECT_EQ(-1, last_request()->request_id);
536 reset_last_request();
537 }
538
539 {
540 const char kTestCall[] = "chrome.power.releaseKeepAwake()";
541 ExecuteScript(context, kTestCall);
542 ValidateLastRequest("power.releaseKeepAwake", "[]");
543 EXPECT_EQ(-1, last_request()->request_id);
544 reset_last_request();
545 }
546
547 {
548 const char kTestCall[] = "chrome.idle.queryState(30, function() {})";
549 ExecuteScript(context, kTestCall);
550 ValidateLastRequest("idle.queryState", "[30]");
551 EXPECT_NE(-1, last_request()->request_id);
552 reset_last_request();
553 }
554
555 {
556 const char kTestCall[] = "chrome.idle.setDetectionInterval(30);";
557 ExecuteScript(context, kTestCall);
558 ValidateLastRequest("idle.setDetectionInterval", "[30]");
559 EXPECT_EQ(-1, last_request()->request_id);
560 reset_last_request();
561 }
562
563 // Check catching errors.
564 const char kError[] = "Uncaught TypeError: Invalid invocation";
565 {
566 // "disp" is not an allowed enum value.
567 const char kTestCall[] = "chrome.power.requestKeepAwake('disp');";
568 ExecuteScriptAndExpectError(context, kTestCall, kError);
569 EXPECT_FALSE(last_request());
570 reset_last_request(); // Just to not pollute future results.
571 }
572
573 {
574 // The queryState() param has a minimum of 15.
575 const char kTestCall[] = "chrome.idle.queryState(10, function() {});";
576 ExecuteScriptAndExpectError(context, kTestCall, kError);
577 EXPECT_FALSE(last_request());
578 reset_last_request(); // Just to not pollute future results.
579 }
580
581 {
582 const char kTestCall[] =
583 "chrome.idle.onStateChanged.addListener(state => {\n"
584 " this.idleState = state;\n"
585 "});\n";
586 ExecuteScript(context, kTestCall);
587 EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(),
588 context, "idleState"));
589 bindings_system()->FireEventInContext("idle.onStateChanged", context,
590 *ListValueFromString("['active']"));
591
592 EXPECT_EQ("\"active\"", GetStringPropertyFromObject(context->Global(),
593 context, "idleState"));
594 }
595 }
596
597 } // namespace extensions 440 } // namespace extensions
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698