1 // Copyright (c) 2012 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.
5 #include "chrome/browser/extensions/extension_function_test_utils.h"
9 #include "base/files/file_path.h"
10 #include "base/json/json_reader.h"
11 #include "base/values.h"
12 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
13 #include "chrome/browser/extensions/extension_function_dispatcher.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/test/base/ui_test_utils.h"
17 #include "extensions/browser/extension_function.h"
18 #include "extensions/common/extension.h"
19 #include "extensions/common/id_util.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 using content::WebContents
;
23 using extensions::Extension
;
24 using extensions::Manifest
;
25 namespace keys
= extensions::tabs_constants
;
29 class TestFunctionDispatcherDelegate
30 : public ExtensionFunctionDispatcher::Delegate
{
32 explicit TestFunctionDispatcherDelegate(Browser
* browser
) :
34 virtual ~TestFunctionDispatcherDelegate() {}
37 virtual extensions::WindowController
* GetExtensionWindowController()
39 return browser_
->extension_window_controller();
42 virtual WebContents
* GetAssociatedWebContents() const OVERRIDE
{
51 namespace extension_function_test_utils
{
53 base::Value
* ParseJSON(const std::string
& data
) {
54 return base::JSONReader::Read(data
);
57 base::ListValue
* ParseList(const std::string
& data
) {
58 scoped_ptr
<base::Value
> result(ParseJSON(data
));
59 if (result
.get() && result
->IsType(base::Value::TYPE_LIST
))
60 return static_cast<base::ListValue
*>(result
.release());
65 base::DictionaryValue
* ParseDictionary(
66 const std::string
& data
) {
67 scoped_ptr
<base::Value
> result(ParseJSON(data
));
68 if (result
.get() && result
->IsType(base::Value::TYPE_DICTIONARY
))
69 return static_cast<base::DictionaryValue
*>(result
.release());
74 bool GetBoolean(base::DictionaryValue
* val
, const std::string
& key
) {
76 if (!val
->GetBoolean(key
, &result
))
77 ADD_FAILURE() << key
<< " does not exist or is not a boolean.";
81 int GetInteger(base::DictionaryValue
* val
, const std::string
& key
) {
83 if (!val
->GetInteger(key
, &result
))
84 ADD_FAILURE() << key
<< " does not exist or is not an integer.";
88 std::string
GetString(base::DictionaryValue
* val
, const std::string
& key
) {
90 if (!val
->GetString(key
, &result
))
91 ADD_FAILURE() << key
<< " does not exist or is not a string.";
95 base::DictionaryValue
* ToDictionary(base::Value
* val
) {
97 EXPECT_EQ(base::Value::TYPE_DICTIONARY
, val
->GetType());
98 return static_cast<base::DictionaryValue
*>(val
);
101 base::ListValue
* ToList(base::Value
* val
) {
103 EXPECT_EQ(base::Value::TYPE_LIST
, val
->GetType());
104 return static_cast<base::ListValue
*>(val
);
107 scoped_refptr
<Extension
> CreateEmptyExtension() {
108 return CreateEmptyExtensionWithLocation(Manifest::INTERNAL
);
111 scoped_refptr
<Extension
> CreateEmptyExtensionWithLocation(
112 Manifest::Location location
) {
113 scoped_ptr
<base::DictionaryValue
> test_extension_value(
114 ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}"));
115 return CreateExtension(location
, test_extension_value
.get(), std::string());
118 scoped_refptr
<Extension
> CreateEmptyExtension(
119 const std::string
& id_input
) {
120 scoped_ptr
<base::DictionaryValue
> test_extension_value(
121 ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}"));
122 return CreateExtension(Manifest::INTERNAL
, test_extension_value
.get(),
126 scoped_refptr
<Extension
> CreateExtension(
127 base::DictionaryValue
* test_extension_value
) {
128 return CreateExtension(Manifest::INTERNAL
, test_extension_value
,
132 scoped_refptr
<Extension
> CreateExtension(
133 Manifest::Location location
,
134 base::DictionaryValue
* test_extension_value
,
135 const std::string
& id_input
) {
137 const base::FilePath test_extension_path
;
139 if (!id_input
.empty())
140 id
= extensions::id_util::GenerateId(id_input
);
141 scoped_refptr
<Extension
> extension(Extension::Create(
144 *test_extension_value
,
148 EXPECT_TRUE(error
.empty()) << "Could not parse test extension " << error
;
152 bool HasPrivacySensitiveFields(base::DictionaryValue
* val
) {
154 if (val
->GetString(keys::kUrlKey
, &result
) ||
155 val
->GetString(keys::kTitleKey
, &result
) ||
156 val
->GetString(keys::kFaviconUrlKey
, &result
))
161 std::string
RunFunctionAndReturnError(UIThreadExtensionFunction
* function
,
162 const std::string
& args
,
164 return RunFunctionAndReturnError(function
, args
, browser
, NONE
);
166 std::string
RunFunctionAndReturnError(UIThreadExtensionFunction
* function
,
167 const std::string
& args
,
169 RunFunctionFlags flags
) {
170 scoped_refptr
<ExtensionFunction
> function_owner(function
);
171 // Without a callback the function will not generate a result.
172 function
->set_has_callback(true);
173 RunFunction(function
, args
, browser
, flags
);
174 EXPECT_FALSE(function
->GetResultList()) << "Did not expect a result";
175 return function
->GetError();
178 base::Value
* RunFunctionAndReturnSingleResult(
179 UIThreadExtensionFunction
* function
,
180 const std::string
& args
,
182 return RunFunctionAndReturnSingleResult(function
, args
, browser
, NONE
);
184 base::Value
* RunFunctionAndReturnSingleResult(
185 UIThreadExtensionFunction
* function
,
186 const std::string
& args
,
188 RunFunctionFlags flags
) {
189 scoped_refptr
<ExtensionFunction
> function_owner(function
);
190 // Without a callback the function will not generate a result.
191 function
->set_has_callback(true);
192 RunFunction(function
, args
, browser
, flags
);
193 EXPECT_TRUE(function
->GetError().empty()) << "Unexpected error: "
194 << function
->GetError();
195 const base::Value
* single_result
= NULL
;
196 if (function
->GetResultList() != NULL
&&
197 function
->GetResultList()->Get(0, &single_result
)) {
198 return single_result
->DeepCopy();
203 // This helps us be able to wait until an UIThreadExtensionFunction calls
205 class SendResponseDelegate
206 : public UIThreadExtensionFunction::DelegateForTests
{
208 SendResponseDelegate() : should_post_quit_(false) {}
210 virtual ~SendResponseDelegate() {}
212 void set_should_post_quit(bool should_quit
) {
213 should_post_quit_
= should_quit
;
217 return response_
.get() != NULL
;
221 EXPECT_TRUE(HasResponse());
222 return *response_
.get();
225 virtual void OnSendResponse(UIThreadExtensionFunction
* function
,
227 bool bad_message
) OVERRIDE
{
228 ASSERT_FALSE(bad_message
);
229 ASSERT_FALSE(HasResponse());
230 response_
.reset(new bool);
231 *response_
= success
;
232 if (should_post_quit_
) {
233 base::MessageLoopForUI::current()->Quit();
238 scoped_ptr
<bool> response_
;
239 bool should_post_quit_
;
242 bool RunFunction(UIThreadExtensionFunction
* function
,
243 const std::string
& args
,
245 RunFunctionFlags flags
) {
246 SendResponseDelegate response_delegate
;
247 function
->set_test_delegate(&response_delegate
);
248 scoped_ptr
<base::ListValue
> parsed_args(ParseList(args
));
249 EXPECT_TRUE(parsed_args
.get()) <<
250 "Could not parse extension function arguments: " << args
;
251 function
->SetArgs(parsed_args
.get());
253 TestFunctionDispatcherDelegate
dispatcher_delegate(browser
);
254 ExtensionFunctionDispatcher
dispatcher(
255 browser
->profile(), &dispatcher_delegate
);
256 function
->set_dispatcher(dispatcher
.AsWeakPtr());
258 function
->set_context(browser
->profile());
259 function
->set_include_incognito(flags
& INCLUDE_INCOGNITO
);
262 // If the RunImpl of |function| didn't already call SendResponse, run the
263 // message loop until they do.
264 if (!response_delegate
.HasResponse()) {
265 response_delegate
.set_should_post_quit(true);
266 content::RunMessageLoop();
269 EXPECT_TRUE(response_delegate
.HasResponse());
270 return response_delegate
.GetResponse();
273 } // namespace extension_function_test_utils