1 // Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights 2 // reserved. Use of this source code is governed by a BSD-style license that 3 // can be found in the LICENSE file. 4 5 #include "tests/ceftests/extensions/extension_test_handler.h" 6 #include "tests/ceftests/test_util.h" 7 #include "tests/shared/browser/extension_util.h" 8 9 #define STORAGE_TEST_GROUP_ALL(name, test_class) \ 10 EXTENSION_TEST_GROUP_ALL(ChromeStorage##name, test_class) 11 #define STORAGE_TEST_GROUP_MINIMAL(name, test_class) \ 12 EXTENSION_TEST_GROUP_MINIMAL(ChromeStorage##name, test_class) 13 14 namespace { 15 16 const char kExtensionPath[] = "storage-extension"; 17 const char kSuccessMessage[] = "success"; 18 19 // Base class for testing chrome.storage methods. 20 // See https://developer.chrome.com/extensions/storage 21 class StorageTestHandler : public ExtensionTestHandler { 22 public: StorageTestHandler(RequestContextType request_context_type)23 explicit StorageTestHandler(RequestContextType request_context_type) 24 : ExtensionTestHandler(request_context_type) { 25 // Only creating the extension browser. 26 set_create_main_browser(false); 27 } 28 29 // CefExtensionHandler methods: OnExtensionLoaded(CefRefPtr<CefExtension> extension)30 void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override { 31 EXPECT_TRUE(CefCurrentlyOn(TID_UI)); 32 EXPECT_FALSE(got_loaded_); 33 got_loaded_.yes(); 34 35 // Verify |extension| contents. 36 EXPECT_FALSE(extension->GetIdentifier().empty()); 37 EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(), 38 client::extension_util::GetInternalExtensionResourcePath( 39 extension->GetPath()) 40 .c_str()); 41 TestDictionaryEqual(CreateManifest(), extension->GetManifest()); 42 43 EXPECT_FALSE(extension_); 44 extension_ = extension; 45 46 CreateBrowserForExtension(); 47 } 48 OnExtensionUnloaded(CefRefPtr<CefExtension> extension)49 void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override { 50 EXPECT_TRUE(CefCurrentlyOn(TID_UI)); 51 EXPECT_TRUE(extension_); 52 EXPECT_TRUE(extension_->IsSame(extension)); 53 EXPECT_FALSE(got_unloaded_); 54 got_unloaded_.yes(); 55 extension_ = nullptr; 56 57 // Execute asynchronously so call stacks have a chance to unwind. 58 // Will close the browser windows. 59 CefPostTask(TID_UI, base::BindOnce(&StorageTestHandler::DestroyTest, this)); 60 } 61 62 // CefLoadHandler methods: OnLoadingStateChange(CefRefPtr<CefBrowser> browser,bool isLoading,bool canGoBack,bool canGoForward)63 void OnLoadingStateChange(CefRefPtr<CefBrowser> browser, 64 bool isLoading, 65 bool canGoBack, 66 bool canGoForward) override { 67 CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension(); 68 EXPECT_TRUE(extension); 69 EXPECT_TRUE(extension_->IsSame(extension)); 70 71 if (isLoading) { 72 EXPECT_FALSE(extension_browser_); 73 extension_browser_ = browser; 74 } else { 75 EXPECT_TRUE(browser->IsSame(extension_browser_)); 76 77 const std::string& url = browser->GetMainFrame()->GetURL(); 78 EXPECT_STREQ(extension_url_.c_str(), url.c_str()); 79 } 80 } 81 82 // CefResourceRequestHandler methods: GetResourceHandler(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request)83 CefRefPtr<CefResourceHandler> GetResourceHandler( 84 CefRefPtr<CefBrowser> browser, 85 CefRefPtr<CefFrame> frame, 86 CefRefPtr<CefRequest> request) override { 87 EXPECT_TRUE(browser->IsSame(extension_browser_)); 88 89 CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension(); 90 EXPECT_TRUE(extension); 91 EXPECT_TRUE(extension_->IsSame(extension)); 92 93 const std::string& url = request->GetURL(); 94 EXPECT_STREQ(extension_url_.c_str(), url.c_str()); 95 96 EXPECT_FALSE(got_url_request_); 97 got_url_request_.yes(); 98 99 // Handle the resource request. 100 return RoutingTestHandler::GetResourceHandler(browser, frame, request); 101 } 102 103 protected: OnLoadExtensions()104 void OnLoadExtensions() override { 105 LoadExtension(kExtensionPath, CreateManifest()); 106 } 107 OnMessage(CefRefPtr<CefBrowser> browser,const std::string & message)108 bool OnMessage(CefRefPtr<CefBrowser> browser, 109 const std::string& message) override { 110 if (message == "extension_onload") { 111 // From body onLoad in the extension browser. 112 EXPECT_TRUE(browser->IsSame(extension_browser_)); 113 EXPECT_FALSE(got_body_onload_); 114 got_body_onload_.yes(); 115 TriggerStorageApiJSFunction(); 116 return true; 117 } 118 EXPECT_TRUE(browser->IsSame(extension_browser_)); 119 EXPECT_FALSE(got_success_message_); 120 got_success_message_.yes(); 121 EXPECT_STREQ(kSuccessMessage, message.c_str()); 122 TriggerDestroyTest(); 123 return true; 124 } 125 OnDestroyTest()126 void OnDestroyTest() override { 127 extension_browser_ = nullptr; 128 129 EXPECT_TRUE(got_loaded_); 130 EXPECT_TRUE(got_url_request_); 131 EXPECT_TRUE(got_body_onload_); 132 EXPECT_TRUE(got_trigger_api_function_); 133 EXPECT_TRUE(got_success_message_); 134 EXPECT_TRUE(got_unloaded_); 135 } 136 137 // Create a manifest that grants access to the storage API. CreateManifest() const138 virtual CefRefPtr<CefDictionaryValue> CreateManifest() const { 139 ApiPermissionsList api_permissions; 140 api_permissions.push_back("storage"); 141 return CreateDefaultManifest(api_permissions); 142 } 143 144 // Add resources in the extension browser. OnAddExtensionResources(const std::string & origin)145 virtual void OnAddExtensionResources(const std::string& origin) { 146 extension_url_ = origin + "extension.html"; 147 AddResource(extension_url_, GetExtensionHTML(), "text/html"); 148 } 149 150 // Returns the chrome.storage.* JS that is executed in the extension browser 151 // when the triggerStorageApi() JS function is called. 152 virtual std::string GetStorageApiJS() const = 0; 153 154 // Returns the JS that will be loaded in the extension browser. This 155 // implements the triggerStorageApi() JS function called from 156 // TriggerStorageApiJSFunction(). GetExtensionJS() const157 virtual std::string GetExtensionJS() const { 158 return "function triggerStorageApi() {" + GetStorageApiJS() + "}"; 159 } 160 161 // Returns the HTML that will be loaded in the extension browser. GetExtensionHTML() const162 virtual std::string GetExtensionHTML() const { 163 return "<html><head><script>" + GetExtensionJS() + 164 "</script></head><body onLoad=" + GetMessageJS("extension_onload") + 165 ">Extension</body></html>"; 166 } 167 TriggerDestroyTest()168 virtual void TriggerDestroyTest() { 169 // Execute asynchronously so call stacks have a chance to unwind. 170 CefPostTask(TID_UI, base::BindOnce(&StorageTestHandler::UnloadExtension, 171 this, extension_)); 172 } 173 extension() const174 CefRefPtr<CefExtension> extension() const { return extension_; } extension_url() const175 std::string extension_url() const { return extension_url_; } extension_browser() const176 CefRefPtr<CefBrowser> extension_browser() const { return extension_browser_; } 177 got_success_message() const178 bool got_success_message() const { return got_success_message_; } 179 180 private: CreateBrowserForExtension()181 void CreateBrowserForExtension() { 182 const std::string& identifier = extension_->GetIdentifier(); 183 EXPECT_FALSE(identifier.empty()); 184 const std::string& origin = 185 client::extension_util::GetExtensionOrigin(identifier); 186 EXPECT_FALSE(origin.empty()); 187 188 // Add extension resources. 189 OnAddExtensionResources(origin); 190 191 // Create a browser to host the extension. 192 CreateBrowser(extension_url_, request_context()); 193 } 194 TriggerStorageApiJSFunction()195 void TriggerStorageApiJSFunction() { 196 EXPECT_FALSE(got_trigger_api_function_); 197 got_trigger_api_function_.yes(); 198 199 extension_browser_->GetMainFrame()->ExecuteJavaScript( 200 "triggerStorageApi();", extension_url_, 0); 201 } 202 203 CefRefPtr<CefExtension> extension_; 204 std::string extension_url_; 205 CefRefPtr<CefBrowser> extension_browser_; 206 207 TrackCallback got_loaded_; 208 TrackCallback got_url_request_; 209 TrackCallback got_body_onload_; 210 TrackCallback got_trigger_api_function_; 211 TrackCallback got_success_message_; 212 TrackCallback got_unloaded_; 213 }; 214 } // namespace 215 216 namespace { 217 218 // Test for chrome.storage.local.set(object items, function callback) 219 // and for chrome.storage.local.get(string or array of string or object keys, 220 // function callback) 221 class LocalStorageTestHandler : public StorageTestHandler { 222 public: LocalStorageTestHandler(RequestContextType request_context_type)223 explicit LocalStorageTestHandler(RequestContextType request_context_type) 224 : StorageTestHandler(request_context_type) {} 225 226 protected: GetStorageApiJS() const227 std::string GetStorageApiJS() const override { 228 return "chrome.storage.local.set({\"local_key_1\": \"local_value_1\"}, " 229 "function() {" 230 "chrome.storage.local.get(\"local_key_1\", function (items) {" 231 "if(items[\"local_key_1\"] == \"local_value_1\") {" + 232 GetMessageJS(kSuccessMessage) + 233 "}});" 234 "});"; 235 } 236 237 private: 238 IMPLEMENT_REFCOUNTING(LocalStorageTestHandler); 239 DISALLOW_COPY_AND_ASSIGN(LocalStorageTestHandler); 240 }; 241 } // namespace 242 243 STORAGE_TEST_GROUP_ALL(LocalStorage, LocalStorageTestHandler) 244 245 namespace { 246 247 // Test for chrome.storage.local.getBytesInUse(string or array of string keys, 248 // function callback) 249 class LocalStorageGetBytesInUseTestHandler : public StorageTestHandler { 250 public: LocalStorageGetBytesInUseTestHandler(RequestContextType request_context_type)251 explicit LocalStorageGetBytesInUseTestHandler( 252 RequestContextType request_context_type) 253 : StorageTestHandler(request_context_type) {} 254 255 protected: GetStorageApiJS() const256 std::string GetStorageApiJS() const override { 257 return "chrome.storage.local.set({\"local_key_2\": \"local_value_2\"}, " 258 "function() {" 259 "chrome.storage.local.getBytesInUse(\"local_key_2\", function " 260 "(bytesInUse) {" 261 "if (bytesInUse == 26) {" + 262 GetMessageJS(kSuccessMessage) + 263 "}});" 264 "});"; 265 } 266 267 private: 268 IMPLEMENT_REFCOUNTING(LocalStorageGetBytesInUseTestHandler); 269 DISALLOW_COPY_AND_ASSIGN(LocalStorageGetBytesInUseTestHandler); 270 }; 271 } // namespace 272 273 STORAGE_TEST_GROUP_MINIMAL(LocalStorageGetBytesInUse, 274 LocalStorageGetBytesInUseTestHandler) 275 276 namespace { 277 278 // Test for chrome.storage.local.remove(string or array of string keys, function 279 // callback) 280 class LocalStorageRemoveTestHandler : public StorageTestHandler { 281 public: LocalStorageRemoveTestHandler(RequestContextType request_context_type)282 explicit LocalStorageRemoveTestHandler( 283 RequestContextType request_context_type) 284 : StorageTestHandler(request_context_type) {} 285 286 protected: GetStorageApiJS() const287 std::string GetStorageApiJS() const override { 288 return "chrome.storage.local.set({\"local_key_3\": \"local_value_3\"}, " 289 "function() {" 290 "chrome.storage.local.remove(\"local_key_3\", function () {" 291 "chrome.storage.local.get(\"local_key_3\", function(items) {" 292 "if (items[\"local_key_3\"] == undefined) {" + 293 GetMessageJS(kSuccessMessage) + 294 "}})})" 295 "});"; 296 } 297 298 private: 299 IMPLEMENT_REFCOUNTING(LocalStorageRemoveTestHandler); 300 DISALLOW_COPY_AND_ASSIGN(LocalStorageRemoveTestHandler); 301 }; 302 } // namespace 303 304 STORAGE_TEST_GROUP_MINIMAL(LocalStorageRemove, LocalStorageRemoveTestHandler) 305 306 namespace { 307 308 // Test for chrome.storage.local.clear(function callback) 309 class LocalStorageClearTestHandler : public StorageTestHandler { 310 public: LocalStorageClearTestHandler(RequestContextType request_context_type)311 explicit LocalStorageClearTestHandler(RequestContextType request_context_type) 312 : StorageTestHandler(request_context_type) {} 313 314 protected: GetStorageApiJS() const315 std::string GetStorageApiJS() const override { 316 return "var value1Cleared = false;" 317 "var value2Cleared = false;" 318 "function checkCleared() {" 319 "if (value1Cleared && value2Cleared) {" + 320 GetMessageJS(kSuccessMessage) + 321 "}}" 322 "chrome.storage.local.set({\"local_key_4\": \"local_value_4\"," 323 "\"local_key_5\": \"local_value_5\"}, function() {" 324 "chrome.storage.local.clear(function () {" 325 326 "chrome.storage.local.get(\"local_key_4\", function(items) {" 327 "if (items[\"local_key_4\"] == undefined) {" 328 "value1Cleared = true;" 329 "checkCleared();" 330 "}});" 331 332 "chrome.storage.local.get(\"local_key_5\", function(items) {" 333 "if (items[\"local_key_5\"] == undefined) {" 334 "value2Cleared = true;" 335 "checkCleared();" 336 "}});" 337 "})});"; 338 } 339 340 private: 341 IMPLEMENT_REFCOUNTING(LocalStorageClearTestHandler); 342 DISALLOW_COPY_AND_ASSIGN(LocalStorageClearTestHandler); 343 }; 344 } // namespace 345 346 STORAGE_TEST_GROUP_MINIMAL(LocalStorageClear, LocalStorageClearTestHandler) 347 348 namespace { 349 350 // Test for chrome.storage.sync.set(object items, function callback) 351 // and for chrome.storage.sync.get(string or array of string or object keys, 352 // function callback) 353 class SyncStorageTestHandler : public StorageTestHandler { 354 public: SyncStorageTestHandler(RequestContextType request_context_type)355 explicit SyncStorageTestHandler(RequestContextType request_context_type) 356 : StorageTestHandler(request_context_type) {} 357 358 protected: GetStorageApiJS() const359 std::string GetStorageApiJS() const override { 360 return "chrome.storage.sync.set({\"sync_key_1\": \"sync_value_1\"}, " 361 "function() {" 362 "chrome.storage.sync.get(\"sync_key_1\", function (items) {" 363 "if (items[\"sync_key_1\"] == \"sync_value_1\") {" + 364 GetMessageJS(kSuccessMessage) + 365 "}});" 366 "});"; 367 } 368 369 private: 370 IMPLEMENT_REFCOUNTING(SyncStorageTestHandler); 371 DISALLOW_COPY_AND_ASSIGN(SyncStorageTestHandler); 372 }; 373 } // namespace 374 375 STORAGE_TEST_GROUP_ALL(SyncStorage, SyncStorageTestHandler) 376 377 namespace { 378 379 // Test for chrome.storage.sync.getBytesInUse(string or array of string keys, 380 // function callback) 381 class SyncStorageGetBytesInUseTestHandler : public StorageTestHandler { 382 public: SyncStorageGetBytesInUseTestHandler(RequestContextType request_context_type)383 explicit SyncStorageGetBytesInUseTestHandler( 384 RequestContextType request_context_type) 385 : StorageTestHandler(request_context_type) {} 386 387 protected: GetStorageApiJS() const388 std::string GetStorageApiJS() const override { 389 return "chrome.storage.sync.set({\"sync_key_2\": \"sync_value_2\"}, " 390 "function() {" 391 "chrome.storage.sync.getBytesInUse(\"sync_key_2\", function " 392 "(bytesInUse) {" 393 "if (bytesInUse == 24) {" + 394 GetMessageJS(kSuccessMessage) + 395 "}});" 396 "});"; 397 } 398 399 private: 400 IMPLEMENT_REFCOUNTING(SyncStorageGetBytesInUseTestHandler); 401 DISALLOW_COPY_AND_ASSIGN(SyncStorageGetBytesInUseTestHandler); 402 }; 403 } // namespace 404 405 STORAGE_TEST_GROUP_MINIMAL(SyncStorageGetBytesInUse, 406 SyncStorageGetBytesInUseTestHandler) 407 408 namespace { 409 410 // Test for chrome.storage.sync.remove(string or array of string keys, function 411 // callback) 412 class SyncStorageRemoveTestHandler : public StorageTestHandler { 413 public: SyncStorageRemoveTestHandler(RequestContextType request_context_type)414 explicit SyncStorageRemoveTestHandler(RequestContextType request_context_type) 415 : StorageTestHandler(request_context_type) {} 416 417 protected: GetStorageApiJS() const418 std::string GetStorageApiJS() const override { 419 return "chrome.storage.sync.set({\"sync_key_3\": \"sync_value_3\"}, " 420 "function() {" 421 "chrome.storage.sync.remove(\"sync_key_3\", function () {" 422 "chrome.storage.sync.get(\"sync_key_3\", function(items) {" 423 "if (items[\"sync_key_3\"] == undefined) {" + 424 GetMessageJS(kSuccessMessage) + 425 "}})})" 426 "});"; 427 } 428 429 private: 430 IMPLEMENT_REFCOUNTING(SyncStorageRemoveTestHandler); 431 DISALLOW_COPY_AND_ASSIGN(SyncStorageRemoveTestHandler); 432 }; 433 } // namespace 434 435 STORAGE_TEST_GROUP_MINIMAL(SyncStorageRemove, SyncStorageRemoveTestHandler) 436 437 namespace { 438 439 // Test for chrome.storage.sync.clear(function callback) 440 class SyncStorageClearTestHandler : public StorageTestHandler { 441 public: SyncStorageClearTestHandler(RequestContextType request_context_type)442 explicit SyncStorageClearTestHandler(RequestContextType request_context_type) 443 : StorageTestHandler(request_context_type) {} 444 445 protected: GetStorageApiJS() const446 std::string GetStorageApiJS() const override { 447 return "var value1Cleared = false;" 448 "var value2Cleared = false;" 449 450 "function checkCleared() {" 451 "if (value1Cleared && value2Cleared) {" + 452 GetMessageJS(kSuccessMessage) + 453 "}}" 454 455 "chrome.storage.sync.set({\"sync_key_4\": \"sync_value_4\"," 456 "\"sync_key_5\": \"sync_value_5\"}, function() {" 457 "chrome.storage.sync.clear(function () {" 458 459 "chrome.storage.sync.get(\"sync_key_4\", function(items) {" 460 "if (items[\"sync_key_4\"] == undefined) {" 461 "value1Cleared = true;" 462 "checkCleared();" 463 "}});" 464 465 "chrome.storage.sync.get(\"sync_key_5\", function(items) {" 466 "if (items[\"sync_key_5\"] == undefined) {" 467 "value2Cleared = true;" 468 "checkCleared();" 469 "}});" 470 471 "})});"; 472 } 473 474 private: 475 IMPLEMENT_REFCOUNTING(SyncStorageClearTestHandler); 476 DISALLOW_COPY_AND_ASSIGN(SyncStorageClearTestHandler); 477 }; 478 } // namespace 479 480 STORAGE_TEST_GROUP_MINIMAL(SyncStorageClear, SyncStorageClearTestHandler) 481