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 "extensions/browser/extension_function.h"
17#include "extensions/browser/extension_function_dispatcher.h"
18#include "extensions/common/extension.h"
19#include "extensions/common/id_util.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22using content::WebContents;
23using extensions::Extension;
24using extensions::Manifest;
25namespace keys = extensions::tabs_constants;
26
27namespace {
28
29class TestFunctionDispatcherDelegate
30    : public extensions::ExtensionFunctionDispatcher::Delegate {
31 public:
32  explicit TestFunctionDispatcherDelegate(Browser* browser) :
33      browser_(browser) {}
34  virtual ~TestFunctionDispatcherDelegate() {}
35
36 private:
37  virtual extensions::WindowController* GetExtensionWindowController()
38      const OVERRIDE {
39    return browser_->extension_window_controller();
40  }
41
42  virtual WebContents* GetAssociatedWebContents() const OVERRIDE {
43    return NULL;
44  }
45
46  Browser* browser_;
47};
48
49}  // namespace
50
51namespace extension_function_test_utils {
52
53base::Value* ParseJSON(const std::string& data) {
54  return base::JSONReader::Read(data);
55}
56
57base::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());
61  else
62    return NULL;
63}
64
65base::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());
70  else
71    return NULL;
72}
73
74bool GetBoolean(base::DictionaryValue* val, const std::string& key) {
75  bool result = false;
76  if (!val->GetBoolean(key, &result))
77      ADD_FAILURE() << key << " does not exist or is not a boolean.";
78  return result;
79}
80
81int GetInteger(base::DictionaryValue* val, const std::string& key) {
82  int result = 0;
83  if (!val->GetInteger(key, &result))
84    ADD_FAILURE() << key << " does not exist or is not an integer.";
85  return result;
86}
87
88std::string GetString(base::DictionaryValue* val, const std::string& key) {
89  std::string result;
90  if (!val->GetString(key, &result))
91    ADD_FAILURE() << key << " does not exist or is not a string.";
92  return result;
93}
94
95base::DictionaryValue* ToDictionary(base::Value* val) {
96  EXPECT_TRUE(val);
97  EXPECT_EQ(base::Value::TYPE_DICTIONARY, val->GetType());
98  return static_cast<base::DictionaryValue*>(val);
99}
100
101base::ListValue* ToList(base::Value* val) {
102  EXPECT_TRUE(val);
103  EXPECT_EQ(base::Value::TYPE_LIST, val->GetType());
104  return static_cast<base::ListValue*>(val);
105}
106
107scoped_refptr<Extension> CreateEmptyExtension() {
108  return CreateEmptyExtensionWithLocation(Manifest::INTERNAL);
109}
110
111scoped_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());
116}
117
118scoped_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(),
123                         id_input);
124}
125
126scoped_refptr<Extension> CreateExtension(
127    base::DictionaryValue* test_extension_value) {
128  return CreateExtension(Manifest::INTERNAL, test_extension_value,
129                         std::string());
130}
131
132scoped_refptr<Extension> CreateExtension(
133    Manifest::Location location,
134    base::DictionaryValue* test_extension_value,
135    const std::string& id_input) {
136  std::string error;
137  const base::FilePath test_extension_path;
138  std::string id;
139  if (!id_input.empty())
140    id = extensions::id_util::GenerateId(id_input);
141  scoped_refptr<Extension> extension(Extension::Create(
142      test_extension_path,
143      location,
144      *test_extension_value,
145      Extension::NO_FLAGS,
146      id,
147      &error));
148  EXPECT_TRUE(error.empty()) << "Could not parse test extension " << error;
149  return extension;
150}
151
152bool HasPrivacySensitiveFields(base::DictionaryValue* val) {
153  std::string result;
154  if (val->GetString(keys::kUrlKey, &result) ||
155      val->GetString(keys::kTitleKey, &result) ||
156      val->GetString(keys::kFaviconUrlKey, &result))
157    return true;
158  return false;
159}
160
161std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function,
162                                      const std::string& args,
163                                      Browser* browser) {
164  return RunFunctionAndReturnError(function, args, browser, NONE);
165}
166std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function,
167                                      const std::string& args,
168                                      Browser* browser,
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();
176}
177
178base::Value* RunFunctionAndReturnSingleResult(
179    UIThreadExtensionFunction* function,
180    const std::string& args,
181    Browser* browser) {
182  return RunFunctionAndReturnSingleResult(function, args, browser, NONE);
183}
184base::Value* RunFunctionAndReturnSingleResult(
185    UIThreadExtensionFunction* function,
186    const std::string& args,
187    Browser* browser,
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();
199  }
200  return NULL;
201}
202
203// This helps us be able to wait until an UIThreadExtensionFunction calls
204// SendResponse.
205class SendResponseDelegate
206    : public UIThreadExtensionFunction::DelegateForTests {
207 public:
208  SendResponseDelegate() : should_post_quit_(false) {}
209
210  virtual ~SendResponseDelegate() {}
211
212  void set_should_post_quit(bool should_quit) {
213    should_post_quit_ = should_quit;
214  }
215
216  bool HasResponse() {
217    return response_.get() != NULL;
218  }
219
220  bool GetResponse() {
221    EXPECT_TRUE(HasResponse());
222    return *response_.get();
223  }
224
225  virtual void OnSendResponse(UIThreadExtensionFunction* function,
226                              bool success,
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();
234    }
235  }
236
237 private:
238  scoped_ptr<bool> response_;
239  bool should_post_quit_;
240};
241
242bool RunFunction(UIThreadExtensionFunction* function,
243                 const std::string& args,
244                 Browser* browser,
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());
252
253  TestFunctionDispatcherDelegate dispatcher_delegate(browser);
254  extensions::ExtensionFunctionDispatcher dispatcher(browser->profile(),
255                                                     &dispatcher_delegate);
256  function->set_dispatcher(dispatcher.AsWeakPtr());
257
258  function->set_browser_context(browser->profile());
259  function->set_include_incognito(flags & INCLUDE_INCOGNITO);
260  function->Run()->Execute();
261
262  // If the RunAsync 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();
267  }
268
269  EXPECT_TRUE(response_delegate.HasResponse());
270  return response_delegate.GetResponse();
271}
272
273} // namespace extension_function_test_utils
274