// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/extension_function_test_utils.h" #include #include "base/files/file_path.h" #include "base/json/json_reader.h" #include "base/values.h" #include "chrome/browser/extensions/api/tabs/tabs_constants.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/test/base/ui_test_utils.h" #include "extensions/browser/api_test_utils.h" #include "extensions/browser/extension_function.h" #include "extensions/browser/extension_function_dispatcher.h" #include "extensions/common/extension.h" #include "extensions/common/id_util.h" #include "testing/gtest/include/gtest/gtest.h" using content::WebContents; using extensions::Extension; using extensions::Manifest; namespace keys = extensions::tabs_constants; namespace { class TestFunctionDispatcherDelegate : public extensions::ExtensionFunctionDispatcher::Delegate { public: explicit TestFunctionDispatcherDelegate(Browser* browser) : browser_(browser) {} virtual ~TestFunctionDispatcherDelegate() {} private: virtual extensions::WindowController* GetExtensionWindowController() const OVERRIDE { return browser_->extension_window_controller(); } virtual WebContents* GetAssociatedWebContents() const OVERRIDE { return NULL; } Browser* browser_; }; } // namespace namespace extension_function_test_utils { base::Value* ParseJSON(const std::string& data) { return base::JSONReader::Read(data); } base::ListValue* ParseList(const std::string& data) { base::Value* result = ParseJSON(data); base::ListValue* list = NULL; result->GetAsList(&list); return list; } base::DictionaryValue* ParseDictionary( const std::string& data) { base::Value* result = ParseJSON(data); base::DictionaryValue* dict = NULL; result->GetAsDictionary(&dict); return dict; } bool GetBoolean(base::DictionaryValue* val, const std::string& key) { bool result = false; if (!val->GetBoolean(key, &result)) ADD_FAILURE() << key << " does not exist or is not a boolean."; return result; } int GetInteger(base::DictionaryValue* val, const std::string& key) { int result = 0; if (!val->GetInteger(key, &result)) ADD_FAILURE() << key << " does not exist or is not an integer."; return result; } std::string GetString(base::DictionaryValue* val, const std::string& key) { std::string result; if (!val->GetString(key, &result)) ADD_FAILURE() << key << " does not exist or is not a string."; return result; } base::DictionaryValue* ToDictionary(base::Value* val) { EXPECT_TRUE(val); EXPECT_EQ(base::Value::TYPE_DICTIONARY, val->GetType()); return static_cast(val); } base::ListValue* ToList(base::Value* val) { EXPECT_TRUE(val); EXPECT_EQ(base::Value::TYPE_LIST, val->GetType()); return static_cast(val); } scoped_refptr CreateEmptyExtension() { return CreateEmptyExtensionWithLocation(Manifest::INTERNAL); } scoped_refptr CreateEmptyExtensionWithLocation( Manifest::Location location) { scoped_ptr test_extension_value( ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}")); return CreateExtension(location, test_extension_value.get(), std::string()); } scoped_refptr CreateEmptyExtension( const std::string& id_input) { scoped_ptr test_extension_value( ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}")); return CreateExtension(Manifest::INTERNAL, test_extension_value.get(), id_input); } scoped_refptr CreateExtension( base::DictionaryValue* test_extension_value) { return CreateExtension(Manifest::INTERNAL, test_extension_value, std::string()); } scoped_refptr CreateExtension( Manifest::Location location, base::DictionaryValue* test_extension_value, const std::string& id_input) { std::string error; const base::FilePath test_extension_path; std::string id; if (!id_input.empty()) id = extensions::id_util::GenerateId(id_input); scoped_refptr extension(Extension::Create( test_extension_path, location, *test_extension_value, Extension::NO_FLAGS, id, &error)); EXPECT_TRUE(error.empty()) << "Could not parse test extension " << error; return extension; } bool HasPrivacySensitiveFields(base::DictionaryValue* val) { std::string result; if (val->GetString(keys::kUrlKey, &result) || val->GetString(keys::kTitleKey, &result) || val->GetString(keys::kFaviconUrlKey, &result)) return true; return false; } std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function, const std::string& args, Browser* browser) { return RunFunctionAndReturnError(function, args, browser, NONE); } std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function, const std::string& args, Browser* browser, RunFunctionFlags flags) { scoped_refptr function_owner(function); // Without a callback the function will not generate a result. function->set_has_callback(true); RunFunction(function, args, browser, flags); EXPECT_FALSE(function->GetResultList()) << "Did not expect a result"; return function->GetError(); } base::Value* RunFunctionAndReturnSingleResult( UIThreadExtensionFunction* function, const std::string& args, Browser* browser) { return RunFunctionAndReturnSingleResult(function, args, browser, NONE); } base::Value* RunFunctionAndReturnSingleResult( UIThreadExtensionFunction* function, const std::string& args, Browser* browser, RunFunctionFlags flags) { scoped_refptr function_owner(function); // Without a callback the function will not generate a result. function->set_has_callback(true); RunFunction(function, args, browser, flags); EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: " << function->GetError(); const base::Value* single_result = NULL; if (function->GetResultList() != NULL && function->GetResultList()->Get(0, &single_result)) { return single_result->DeepCopy(); } return NULL; } // This helps us be able to wait until an UIThreadExtensionFunction calls // SendResponse. class SendResponseDelegate : public UIThreadExtensionFunction::DelegateForTests { public: SendResponseDelegate() : should_post_quit_(false) {} virtual ~SendResponseDelegate() {} void set_should_post_quit(bool should_quit) { should_post_quit_ = should_quit; } bool HasResponse() { return response_.get() != NULL; } bool GetResponse() { EXPECT_TRUE(HasResponse()); return *response_.get(); } virtual void OnSendResponse(UIThreadExtensionFunction* function, bool success, bool bad_message) OVERRIDE { ASSERT_FALSE(bad_message); ASSERT_FALSE(HasResponse()); response_.reset(new bool); *response_ = success; if (should_post_quit_) { base::MessageLoopForUI::current()->Quit(); } } private: scoped_ptr response_; bool should_post_quit_; }; bool RunFunction(UIThreadExtensionFunction* function, const std::string& args, Browser* browser, RunFunctionFlags flags) { TestFunctionDispatcherDelegate dispatcher_delegate(browser); scoped_ptr dispatcher( new extensions::ExtensionFunctionDispatcher(browser->profile(), &dispatcher_delegate)); // TODO(yoz): The cast is a hack; these flags should be defined in // only one place. See crbug.com/394840. return extensions::api_test_utils::RunFunction( function, args, browser->profile(), dispatcher.Pass(), static_cast(flags)); } } // namespace extension_function_test_utils