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/api/declarative_content/content_action.h" 6 7#include "base/base64.h" 8#include "base/run_loop.h" 9#include "base/test/values_test_util.h" 10#include "chrome/browser/extensions/extension_action.h" 11#include "chrome/browser/extensions/extension_action_manager.h" 12#include "chrome/browser/extensions/extension_service_test_base.h" 13#include "chrome/browser/extensions/extension_tab_util.h" 14#include "chrome/browser/extensions/test_extension_environment.h" 15#include "chrome/browser/extensions/test_extension_system.h" 16#include "chrome/test/base/testing_profile.h" 17#include "chrome/test/base/testing_profile.h" 18#include "content/public/browser/web_contents.h" 19#include "extensions/browser/extension_system.h" 20#include "extensions/common/extension.h" 21#include "extensions/common/extension_builder.h" 22#include "extensions/common/value_builder.h" 23#include "ipc/ipc_message_utils.h" 24#include "testing/gmock/include/gmock/gmock.h" 25#include "testing/gtest/include/gtest/gtest.h" 26#include "ui/gfx/image/image_skia.h" 27#include "ui/gfx/ipc/gfx_param_traits.h" 28 29namespace extensions { 30namespace { 31 32using base::test::ParseJson; 33using testing::HasSubstr; 34 35 36scoped_ptr<base::DictionaryValue> SimpleManifest() { 37 return DictionaryBuilder() 38 .Set("name", "extension") 39 .Set("manifest_version", 2) 40 .Set("version", "1.0") 41 .Build(); 42} 43 44class RequestContentScriptTest : public ExtensionServiceTestBase { 45 public: 46 RequestContentScriptTest() 47 : extension_(ExtensionBuilder().SetManifest(SimpleManifest()).Build()) {}; 48 49 // TODO(rdevlin.cronin): This should be SetUp(), but an issues with invoking 50 // InitializeEmptyExtensionService() within SetUp() means that we have to 51 // call this manually within every test. This can be cleaned up once said 52 // issue is fixed. 53 virtual void Init() { 54 InitializeEmptyExtensionService(); 55 static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()))-> 56 SetReady(); 57 base::RunLoop().RunUntilIdle(); 58 } 59 60 Profile* profile() { return profile_.get(); } 61 Extension* extension() { return extension_.get(); } 62 63 private: 64 scoped_refptr<Extension> extension_; 65}; 66 67TEST(DeclarativeContentActionTest, InvalidCreation) { 68 TestExtensionEnvironment env; 69 std::string error; 70 bool bad_message = false; 71 scoped_refptr<const ContentAction> result; 72 73 // Test wrong data type passed. 74 error.clear(); 75 result = ContentAction::Create( 76 NULL, NULL, *ParseJson("[]"), &error, &bad_message); 77 EXPECT_TRUE(bad_message); 78 EXPECT_EQ("", error); 79 EXPECT_FALSE(result.get()); 80 81 // Test missing instanceType element. 82 error.clear(); 83 result = ContentAction::Create( 84 NULL, NULL, *ParseJson("{}"), &error, &bad_message); 85 EXPECT_TRUE(bad_message); 86 EXPECT_EQ("", error); 87 EXPECT_FALSE(result.get()); 88 89 // Test wrong instanceType element. 90 error.clear(); 91 result = ContentAction::Create(NULL, NULL, *ParseJson( 92 "{\n" 93 " \"instanceType\": \"declarativeContent.UnknownType\",\n" 94 "}"), 95 &error, &bad_message); 96 EXPECT_THAT(error, HasSubstr("invalid instanceType")); 97 EXPECT_FALSE(result.get()); 98} 99 100TEST(DeclarativeContentActionTest, ShowPageActionWithoutPageAction) { 101 TestExtensionEnvironment env; 102 103 const Extension* extension = env.MakeExtension(base::DictionaryValue()); 104 std::string error; 105 bool bad_message = false; 106 scoped_refptr<const ContentAction> result = ContentAction::Create( 107 NULL, 108 extension, 109 *ParseJson( 110 "{\n" 111 " \"instanceType\": \"declarativeContent.ShowPageAction\",\n" 112 "}"), 113 &error, 114 &bad_message); 115 EXPECT_THAT(error, testing::HasSubstr("without a page action")); 116 EXPECT_FALSE(bad_message); 117 ASSERT_FALSE(result.get()); 118} 119 120TEST(DeclarativeContentActionTest, ShowPageAction) { 121 TestExtensionEnvironment env; 122 123 const Extension* extension = env.MakeExtension( 124 *ParseJson("{\"page_action\": { \"default_title\": \"Extension\" } }")); 125 std::string error; 126 bool bad_message = false; 127 scoped_refptr<const ContentAction> result = ContentAction::Create( 128 NULL, 129 extension, 130 *ParseJson( 131 "{\n" 132 " \"instanceType\": \"declarativeContent.ShowPageAction\",\n" 133 "}"), 134 &error, 135 &bad_message); 136 EXPECT_EQ("", error); 137 EXPECT_FALSE(bad_message); 138 ASSERT_TRUE(result.get()); 139 EXPECT_EQ(ContentAction::ACTION_SHOW_PAGE_ACTION, result->GetType()); 140 141 ExtensionAction* page_action = 142 ExtensionActionManager::Get(env.profile())->GetPageAction(*extension); 143 scoped_ptr<content::WebContents> contents = env.MakeTab(); 144 const int tab_id = ExtensionTabUtil::GetTabId(contents.get()); 145 EXPECT_FALSE(page_action->GetIsVisible(tab_id)); 146 ContentAction::ApplyInfo apply_info = { 147 env.profile(), contents.get() 148 }; 149 result->Apply(extension->id(), base::Time(), &apply_info); 150 EXPECT_TRUE(page_action->GetIsVisible(tab_id)); 151 result->Apply(extension->id(), base::Time(), &apply_info); 152 EXPECT_TRUE(page_action->GetIsVisible(tab_id)); 153 result->Revert(extension->id(), base::Time(), &apply_info); 154 EXPECT_TRUE(page_action->GetIsVisible(tab_id)); 155 result->Revert(extension->id(), base::Time(), &apply_info); 156 EXPECT_FALSE(page_action->GetIsVisible(tab_id)); 157} 158 159TEST(DeclarativeContentActionTest, SetIcon) { 160 TestExtensionEnvironment env; 161 162 // Simulate the process of passing ImageData to SetIcon::Create. 163 SkBitmap bitmap; 164 EXPECT_TRUE(bitmap.tryAllocN32Pixels(19, 19)); 165 bitmap.eraseARGB(0,0,0,0); 166 uint32_t* pixels = bitmap.getAddr32(0, 0); 167 for (int i = 0; i < 19 * 19; ++i) 168 pixels[i] = i; 169 IPC::Message bitmap_pickle; 170 IPC::WriteParam(&bitmap_pickle, bitmap); 171 std::string binary_data = std::string( 172 static_cast<const char*>(bitmap_pickle.data()), bitmap_pickle.size()); 173 std::string data64; 174 base::Base64Encode(binary_data, &data64); 175 176 scoped_ptr<base::DictionaryValue> dict = 177 DictionaryBuilder().Set("instanceType", "declarativeContent.SetIcon") 178 .Set("imageData", 179 DictionaryBuilder().Set("19", data64)).Build(); 180 181 const Extension* extension = env.MakeExtension( 182 *ParseJson("{\"page_action\": { \"default_title\": \"Extension\" } }")); 183 std::string error; 184 bool bad_message = false; 185 scoped_refptr<const ContentAction> result = ContentAction::Create( 186 NULL, 187 extension, 188 *dict, 189 &error, 190 &bad_message); 191 EXPECT_EQ("", error); 192 EXPECT_FALSE(bad_message); 193 ASSERT_TRUE(result.get()); 194 EXPECT_EQ(ContentAction::ACTION_SET_ICON, result->GetType()); 195 196 ExtensionAction* page_action = 197 ExtensionActionManager::Get(env.profile())->GetPageAction(*extension); 198 scoped_ptr<content::WebContents> contents = env.MakeTab(); 199 const int tab_id = ExtensionTabUtil::GetTabId(contents.get()); 200 EXPECT_FALSE(page_action->GetIsVisible(tab_id)); 201 ContentAction::ApplyInfo apply_info = { 202 env.profile(), contents.get() 203 }; 204 205 // The declarative icon shouldn't exist unless the content action is applied. 206 EXPECT_TRUE(page_action->GetDeclarativeIcon(tab_id).bitmap()->empty()); 207 result->Apply(extension->id(), base::Time(), &apply_info); 208 EXPECT_FALSE(page_action->GetDeclarativeIcon(tab_id).bitmap()->empty()); 209 result->Revert(extension->id(), base::Time(), &apply_info); 210 EXPECT_TRUE(page_action->GetDeclarativeIcon(tab_id).bitmap()->empty()); 211} 212 213TEST_F(RequestContentScriptTest, MissingScripts) { 214 Init(); 215 std::string error; 216 bool bad_message = false; 217 scoped_refptr<const ContentAction> result = ContentAction::Create( 218 profile(), 219 extension(), 220 *ParseJson( 221 "{\n" 222 " \"instanceType\": \"declarativeContent.RequestContentScript\",\n" 223 " \"allFrames\": true,\n" 224 " \"matchAboutBlank\": true\n" 225 "}"), 226 &error, 227 &bad_message); 228 EXPECT_THAT(error, testing::HasSubstr("Missing parameter is required")); 229 EXPECT_FALSE(bad_message); 230 ASSERT_FALSE(result.get()); 231} 232 233TEST_F(RequestContentScriptTest, CSS) { 234 Init(); 235 std::string error; 236 bool bad_message = false; 237 scoped_refptr<const ContentAction> result = ContentAction::Create( 238 profile(), 239 extension(), 240 *ParseJson( 241 "{\n" 242 " \"instanceType\": \"declarativeContent.RequestContentScript\",\n" 243 " \"css\": [\"style.css\"]\n" 244 "}"), 245 &error, 246 &bad_message); 247 EXPECT_EQ("", error); 248 EXPECT_FALSE(bad_message); 249 ASSERT_TRUE(result.get()); 250 EXPECT_EQ(ContentAction::ACTION_REQUEST_CONTENT_SCRIPT, result->GetType()); 251} 252 253TEST_F(RequestContentScriptTest, JS) { 254 Init(); 255 std::string error; 256 bool bad_message = false; 257 scoped_refptr<const ContentAction> result = ContentAction::Create( 258 profile(), 259 extension(), 260 *ParseJson( 261 "{\n" 262 " \"instanceType\": \"declarativeContent.RequestContentScript\",\n" 263 " \"js\": [\"script.js\"]\n" 264 "}"), 265 &error, 266 &bad_message); 267 EXPECT_EQ("", error); 268 EXPECT_FALSE(bad_message); 269 ASSERT_TRUE(result.get()); 270 EXPECT_EQ(ContentAction::ACTION_REQUEST_CONTENT_SCRIPT, result->GetType()); 271} 272 273TEST_F(RequestContentScriptTest, CSSBadType) { 274 Init(); 275 std::string error; 276 bool bad_message = false; 277 scoped_refptr<const ContentAction> result = ContentAction::Create( 278 profile(), 279 extension(), 280 *ParseJson( 281 "{\n" 282 " \"instanceType\": \"declarativeContent.RequestContentScript\",\n" 283 " \"css\": \"style.css\"\n" 284 "}"), 285 &error, 286 &bad_message); 287 EXPECT_TRUE(bad_message); 288 ASSERT_FALSE(result.get()); 289} 290 291TEST_F(RequestContentScriptTest, JSBadType) { 292 Init(); 293 std::string error; 294 bool bad_message = false; 295 scoped_refptr<const ContentAction> result = ContentAction::Create( 296 profile(), 297 extension(), 298 *ParseJson( 299 "{\n" 300 " \"instanceType\": \"declarativeContent.RequestContentScript\",\n" 301 " \"js\": \"script.js\"\n" 302 "}"), 303 &error, 304 &bad_message); 305 EXPECT_TRUE(bad_message); 306 ASSERT_FALSE(result.get()); 307} 308 309TEST_F(RequestContentScriptTest, AllFrames) { 310 Init(); 311 std::string error; 312 bool bad_message = false; 313 scoped_refptr<const ContentAction> result = ContentAction::Create( 314 profile(), 315 extension(), 316 *ParseJson( 317 "{\n" 318 " \"instanceType\": \"declarativeContent.RequestContentScript\",\n" 319 " \"js\": [\"script.js\"],\n" 320 " \"allFrames\": true\n" 321 "}"), 322 &error, 323 &bad_message); 324 EXPECT_EQ("", error); 325 EXPECT_FALSE(bad_message); 326 ASSERT_TRUE(result.get()); 327 EXPECT_EQ(ContentAction::ACTION_REQUEST_CONTENT_SCRIPT, result->GetType()); 328} 329 330TEST_F(RequestContentScriptTest, MatchAboutBlank) { 331 Init(); 332 std::string error; 333 bool bad_message = false; 334 scoped_refptr<const ContentAction> result = ContentAction::Create( 335 profile(), 336 extension(), 337 *ParseJson( 338 "{\n" 339 " \"instanceType\": \"declarativeContent.RequestContentScript\",\n" 340 " \"js\": [\"script.js\"],\n" 341 " \"matchAboutBlank\": true\n" 342 "}"), 343 &error, 344 &bad_message); 345 EXPECT_EQ("", error); 346 EXPECT_FALSE(bad_message); 347 ASSERT_TRUE(result.get()); 348 EXPECT_EQ(ContentAction::ACTION_REQUEST_CONTENT_SCRIPT, result->GetType()); 349} 350 351TEST_F(RequestContentScriptTest, AllFramesBadType) { 352 Init(); 353 std::string error; 354 bool bad_message = false; 355 scoped_refptr<const ContentAction> result = ContentAction::Create( 356 profile(), 357 extension(), 358 *ParseJson( 359 "{\n" 360 " \"instanceType\": \"declarativeContent.RequestContentScript\",\n" 361 " \"js\": [\"script.js\"],\n" 362 " \"allFrames\": null\n" 363 "}"), 364 &error, 365 &bad_message); 366 EXPECT_TRUE(bad_message); 367 ASSERT_FALSE(result.get()); 368} 369 370TEST_F(RequestContentScriptTest, MatchAboutBlankBadType) { 371 Init(); 372 std::string error; 373 bool bad_message = false; 374 scoped_refptr<const ContentAction> result = ContentAction::Create( 375 profile(), 376 extension(), 377 *ParseJson( 378 "{\n" 379 " \"instanceType\": \"declarativeContent.RequestContentScript\",\n" 380 " \"js\": [\"script.js\"],\n" 381 " \"matchAboutBlank\": null\n" 382 "}"), 383 &error, 384 &bad_message); 385 EXPECT_TRUE(bad_message); 386 ASSERT_FALSE(result.get()); 387} 388 389} // namespace 390} // namespace extensions 391