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.
4
5#include "chrome/browser/extensions/extension_function_test_utils.h"
6
7#include <string>
8
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/profiles/profile.h"
14#include "chrome/browser/ui/browser.h"
15#include "chrome/test/base/ui_test_utils.h"
16#include "components/crx_file/id_util.h"
17#include "extensions/browser/api_test_utils.h"
18#include "extensions/browser/extension_function.h"
19#include "extensions/browser/extension_function_dispatcher.h"
20#include "extensions/common/extension.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23using content::WebContents;
24using extensions::Extension;
25using extensions::Manifest;
26namespace keys = extensions::tabs_constants;
27
28namespace {
29
30class TestFunctionDispatcherDelegate
31    : public extensions::ExtensionFunctionDispatcher::Delegate {
32 public:
33  explicit TestFunctionDispatcherDelegate(Browser* browser) :
34      browser_(browser) {}
35  virtual ~TestFunctionDispatcherDelegate() {}
36
37 private:
38  virtual extensions::WindowController* GetExtensionWindowController()
39      const OVERRIDE {
40    return browser_->extension_window_controller();
41  }
42
43  virtual WebContents* GetAssociatedWebContents() const OVERRIDE {
44    return NULL;
45  }
46
47  Browser* browser_;
48};
49
50}  // namespace
51
52namespace extension_function_test_utils {
53
54base::Value* ParseJSON(const std::string& data) {
55  return base::JSONReader::Read(data);
56}
57
58base::ListValue* ParseList(const std::string& data) {
59  base::Value* result = ParseJSON(data);
60  base::ListValue* list = NULL;
61  result->GetAsList(&list);
62  return list;
63}
64
65base::DictionaryValue* ParseDictionary(
66    const std::string& data) {
67  base::Value* result = ParseJSON(data);
68  base::DictionaryValue* dict = NULL;
69  result->GetAsDictionary(&dict);
70  return dict;
71}
72
73bool GetBoolean(base::DictionaryValue* val, const std::string& key) {
74  bool result = false;
75  if (!val->GetBoolean(key, &result))
76      ADD_FAILURE() << key << " does not exist or is not a boolean.";
77  return result;
78}
79
80int GetInteger(base::DictionaryValue* val, const std::string& key) {
81  int result = 0;
82  if (!val->GetInteger(key, &result))
83    ADD_FAILURE() << key << " does not exist or is not an integer.";
84  return result;
85}
86
87std::string GetString(base::DictionaryValue* val, const std::string& key) {
88  std::string result;
89  if (!val->GetString(key, &result))
90    ADD_FAILURE() << key << " does not exist or is not a string.";
91  return result;
92}
93
94base::DictionaryValue* ToDictionary(base::Value* val) {
95  EXPECT_TRUE(val);
96  EXPECT_EQ(base::Value::TYPE_DICTIONARY, val->GetType());
97  return static_cast<base::DictionaryValue*>(val);
98}
99
100base::ListValue* ToList(base::Value* val) {
101  EXPECT_TRUE(val);
102  EXPECT_EQ(base::Value::TYPE_LIST, val->GetType());
103  return static_cast<base::ListValue*>(val);
104}
105
106scoped_refptr<Extension> CreateEmptyExtensionWithLocation(
107    Manifest::Location location) {
108  scoped_ptr<base::DictionaryValue> test_extension_value(
109      ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}"));
110  return CreateExtension(location, test_extension_value.get(), std::string());
111}
112
113scoped_refptr<Extension> CreateExtension(
114    base::DictionaryValue* test_extension_value) {
115  return CreateExtension(Manifest::INTERNAL, test_extension_value,
116                         std::string());
117}
118
119scoped_refptr<Extension> CreateExtension(
120    Manifest::Location location,
121    base::DictionaryValue* test_extension_value,
122    const std::string& id_input) {
123  std::string error;
124  const base::FilePath test_extension_path;
125  std::string id;
126  if (!id_input.empty())
127    id = crx_file::id_util::GenerateId(id_input);
128  scoped_refptr<Extension> extension(Extension::Create(
129      test_extension_path,
130      location,
131      *test_extension_value,
132      Extension::NO_FLAGS,
133      id,
134      &error));
135  EXPECT_TRUE(error.empty()) << "Could not parse test extension " << error;
136  return extension;
137}
138
139bool HasPrivacySensitiveFields(base::DictionaryValue* val) {
140  std::string result;
141  if (val->GetString(keys::kUrlKey, &result) ||
142      val->GetString(keys::kTitleKey, &result) ||
143      val->GetString(keys::kFaviconUrlKey, &result))
144    return true;
145  return false;
146}
147
148std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function,
149                                      const std::string& args,
150                                      Browser* browser) {
151  return RunFunctionAndReturnError(function, args, browser, NONE);
152}
153std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function,
154                                      const std::string& args,
155                                      Browser* browser,
156                                      RunFunctionFlags flags) {
157  scoped_refptr<ExtensionFunction> function_owner(function);
158  // Without a callback the function will not generate a result.
159  function->set_has_callback(true);
160  RunFunction(function, args, browser, flags);
161  EXPECT_FALSE(function->GetResultList()) << "Did not expect a result";
162  return function->GetError();
163}
164
165base::Value* RunFunctionAndReturnSingleResult(
166    UIThreadExtensionFunction* function,
167    const std::string& args,
168    Browser* browser) {
169  return RunFunctionAndReturnSingleResult(function, args, browser, NONE);
170}
171base::Value* RunFunctionAndReturnSingleResult(
172    UIThreadExtensionFunction* function,
173    const std::string& args,
174    Browser* browser,
175    RunFunctionFlags flags) {
176  scoped_refptr<ExtensionFunction> function_owner(function);
177  // Without a callback the function will not generate a result.
178  function->set_has_callback(true);
179  RunFunction(function, args, browser, flags);
180  EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: "
181      << function->GetError();
182  const base::Value* single_result = NULL;
183  if (function->GetResultList() != NULL &&
184      function->GetResultList()->Get(0, &single_result)) {
185    return single_result->DeepCopy();
186  }
187  return NULL;
188}
189
190// This helps us be able to wait until an UIThreadExtensionFunction calls
191// SendResponse.
192class SendResponseDelegate
193    : public UIThreadExtensionFunction::DelegateForTests {
194 public:
195  SendResponseDelegate() : should_post_quit_(false) {}
196
197  virtual ~SendResponseDelegate() {}
198
199  void set_should_post_quit(bool should_quit) {
200    should_post_quit_ = should_quit;
201  }
202
203  bool HasResponse() {
204    return response_.get() != NULL;
205  }
206
207  bool GetResponse() {
208    EXPECT_TRUE(HasResponse());
209    return *response_.get();
210  }
211
212  virtual void OnSendResponse(UIThreadExtensionFunction* function,
213                              bool success,
214                              bool bad_message) OVERRIDE {
215    ASSERT_FALSE(bad_message);
216    ASSERT_FALSE(HasResponse());
217    response_.reset(new bool);
218    *response_ = success;
219    if (should_post_quit_) {
220      base::MessageLoopForUI::current()->Quit();
221    }
222  }
223
224 private:
225  scoped_ptr<bool> response_;
226  bool should_post_quit_;
227};
228
229bool RunFunction(UIThreadExtensionFunction* function,
230                 const std::string& args,
231                 Browser* browser,
232                 RunFunctionFlags flags) {
233  scoped_ptr<base::ListValue> parsed_args(ParseList(args));
234  EXPECT_TRUE(parsed_args.get())
235      << "Could not parse extension function arguments: " << args;
236  return RunFunction(function, parsed_args.Pass(), browser, flags);
237}
238
239bool RunFunction(UIThreadExtensionFunction* function,
240                 scoped_ptr<base::ListValue> args,
241                 Browser* browser,
242                 RunFunctionFlags flags) {
243  TestFunctionDispatcherDelegate dispatcher_delegate(browser);
244  scoped_ptr<extensions::ExtensionFunctionDispatcher> dispatcher(
245      new extensions::ExtensionFunctionDispatcher(browser->profile(),
246                                                  &dispatcher_delegate));
247  // TODO(yoz): The cast is a hack; these flags should be defined in
248  // only one place.  See crbug.com/394840.
249  return extensions::api_test_utils::RunFunction(
250      function,
251      args.Pass(),
252      browser->profile(),
253      dispatcher.Pass(),
254      static_cast<extensions::api_test_utils::RunFunctionFlags>(flags));
255}
256
257} // namespace extension_function_test_utils
258