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 "base/file_util.h" 6 #include "base/files/file_path.h" 7 #include "base/files/scoped_temp_dir.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/message_loop/message_loop_proxy.h" 11 #include "base/platform_file.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "base/time/time.h" 14 #include "net/base/net_errors.h" 15 #include "net/base/test_completion_callback.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 #include "third_party/sqlite/sqlite3.h" 18 #include "webkit/browser/database/database_tracker.h" 19 #include "webkit/browser/quota/mock_special_storage_policy.h" 20 #include "webkit/browser/quota/quota_manager.h" 21 #include "webkit/common/database/database_identifier.h" 22 23 namespace { 24 25 const char kOrigin1Url[] = "http://origin1"; 26 const char kOrigin2Url[] = "http://protected_origin2"; 27 28 class TestObserver : public webkit_database::DatabaseTracker::Observer { 29 public: 30 TestObserver() 31 : new_notification_received_(false), 32 observe_size_changes_(true), 33 observe_scheduled_deletions_(true) { 34 } 35 TestObserver(bool observe_size_changes, bool observe_scheduled_deletions) 36 : new_notification_received_(false), 37 observe_size_changes_(observe_size_changes), 38 observe_scheduled_deletions_(observe_scheduled_deletions) { 39 } 40 41 virtual ~TestObserver() {} 42 virtual void OnDatabaseSizeChanged(const std::string& origin_identifier, 43 const base::string16& database_name, 44 int64 database_size) OVERRIDE { 45 if (!observe_size_changes_) 46 return; 47 new_notification_received_ = true; 48 origin_identifier_ = origin_identifier; 49 database_name_ = database_name; 50 database_size_ = database_size; 51 } 52 virtual void OnDatabaseScheduledForDeletion( 53 const std::string& origin_identifier, 54 const base::string16& database_name) OVERRIDE { 55 if (!observe_scheduled_deletions_) 56 return; 57 new_notification_received_ = true; 58 origin_identifier_ = origin_identifier; 59 database_name_ = database_name; 60 } 61 bool DidReceiveNewNotification() { 62 bool temp_new_notification_received = new_notification_received_; 63 new_notification_received_ = false; 64 return temp_new_notification_received; 65 } 66 std::string GetNotificationOriginIdentifier() { 67 return origin_identifier_; 68 } 69 base::string16 GetNotificationDatabaseName() { return database_name_; } 70 int64 GetNotificationDatabaseSize() { return database_size_; } 71 72 private: 73 bool new_notification_received_; 74 bool observe_size_changes_; 75 bool observe_scheduled_deletions_; 76 std::string origin_identifier_; 77 base::string16 database_name_; 78 int64 database_size_; 79 }; 80 81 void CheckNotificationReceived(TestObserver* observer, 82 const std::string& expected_origin_identifier, 83 const base::string16& expected_database_name, 84 int64 expected_database_size) { 85 EXPECT_TRUE(observer->DidReceiveNewNotification()); 86 EXPECT_EQ(expected_origin_identifier, 87 observer->GetNotificationOriginIdentifier()); 88 EXPECT_EQ(expected_database_name, 89 observer->GetNotificationDatabaseName()); 90 EXPECT_EQ(expected_database_size, 91 observer->GetNotificationDatabaseSize()); 92 } 93 94 class TestQuotaManagerProxy : public quota::QuotaManagerProxy { 95 public: 96 TestQuotaManagerProxy() 97 : QuotaManagerProxy(NULL, NULL), 98 registered_client_(NULL) { 99 } 100 101 virtual void RegisterClient(quota::QuotaClient* client) OVERRIDE { 102 EXPECT_FALSE(registered_client_); 103 registered_client_ = client; 104 } 105 106 virtual void NotifyStorageAccessed(quota::QuotaClient::ID client_id, 107 const GURL& origin, 108 quota::StorageType type) OVERRIDE { 109 EXPECT_EQ(quota::QuotaClient::kDatabase, client_id); 110 EXPECT_EQ(quota::kStorageTypeTemporary, type); 111 accesses_[origin] += 1; 112 } 113 114 virtual void NotifyStorageModified(quota::QuotaClient::ID client_id, 115 const GURL& origin, 116 quota::StorageType type, 117 int64 delta) OVERRIDE { 118 EXPECT_EQ(quota::QuotaClient::kDatabase, client_id); 119 EXPECT_EQ(quota::kStorageTypeTemporary, type); 120 modifications_[origin].first += 1; 121 modifications_[origin].second += delta; 122 } 123 124 // Not needed for our tests. 125 virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {} 126 virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {} 127 128 void SimulateQuotaManagerDestroyed() { 129 if (registered_client_) { 130 registered_client_->OnQuotaManagerDestroyed(); 131 registered_client_ = NULL; 132 } 133 } 134 135 bool WasAccessNotified(const GURL& origin) { 136 return accesses_[origin] != 0; 137 } 138 139 bool WasModificationNotified(const GURL& origin, int64 amount) { 140 return modifications_[origin].first != 0 && 141 modifications_[origin].second == amount; 142 } 143 144 void reset() { 145 accesses_.clear(); 146 modifications_.clear(); 147 } 148 149 quota::QuotaClient* registered_client_; 150 151 // Map from origin to count of access notifications. 152 std::map<GURL, int> accesses_; 153 154 // Map from origin to <count, sum of deltas> 155 std::map<GURL, std::pair<int, int64> > modifications_; 156 157 protected: 158 virtual ~TestQuotaManagerProxy() { 159 EXPECT_FALSE(registered_client_); 160 } 161 }; 162 163 164 bool EnsureFileOfSize(const base::FilePath& file_path, int64 length) { 165 base::PlatformFileError error_code(base::PLATFORM_FILE_ERROR_FAILED); 166 base::PlatformFile file = 167 base::CreatePlatformFile( 168 file_path, 169 base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE, 170 NULL, 171 &error_code); 172 if (error_code != base::PLATFORM_FILE_OK) 173 return false; 174 if (!base::TruncatePlatformFile(file, length)) 175 error_code = base::PLATFORM_FILE_ERROR_FAILED; 176 base::ClosePlatformFile(file); 177 return error_code == base::PLATFORM_FILE_OK; 178 } 179 180 } // namespace 181 182 namespace webkit_database { 183 184 // We declare a helper class, and make it a friend of DatabaseTracker using 185 // the FRIEND_TEST() macro, and we implement all tests we want to run as 186 // static methods of this class. Then we make our TEST() targets call these 187 // static functions. This allows us to run each test in normal mode and 188 // incognito mode without writing the same code twice. 189 class DatabaseTracker_TestHelper_Test { 190 public: 191 static void TestDeleteOpenDatabase(bool incognito_mode) { 192 // Initialize the tracker database. 193 base::ScopedTempDir temp_dir; 194 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 195 scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy = 196 new quota::MockSpecialStoragePolicy; 197 special_storage_policy->AddProtected(GURL(kOrigin2Url)); 198 scoped_refptr<DatabaseTracker> tracker( 199 new DatabaseTracker(temp_dir.path(), 200 incognito_mode, 201 special_storage_policy.get(), 202 NULL, 203 NULL)); 204 205 // Create and open three databases. 206 int64 database_size = 0; 207 const std::string kOrigin1 = 208 webkit_database::GetIdentifierFromOrigin(GURL(kOrigin1Url)); 209 const std::string kOrigin2 = 210 webkit_database::GetIdentifierFromOrigin(GURL(kOrigin2Url)); 211 const base::string16 kDB1 = ASCIIToUTF16("db1"); 212 const base::string16 kDB2 = ASCIIToUTF16("db2"); 213 const base::string16 kDB3 = ASCIIToUTF16("db3"); 214 const base::string16 kDescription = ASCIIToUTF16("database_description"); 215 216 tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, 217 &database_size); 218 tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0, 219 &database_size); 220 tracker->DatabaseOpened(kOrigin2, kDB3, kDescription, 0, 221 &database_size); 222 223 EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append( 224 base::FilePath::FromWStringHack(UTF16ToWide( 225 tracker->GetOriginDirectory(kOrigin1)))))); 226 EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append( 227 base::FilePath::FromWStringHack(UTF16ToWide( 228 tracker->GetOriginDirectory(kOrigin2)))))); 229 EXPECT_EQ(1, file_util::WriteFile( 230 tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1)); 231 EXPECT_EQ(2, file_util::WriteFile( 232 tracker->GetFullDBFilePath(kOrigin2, kDB2), "aa", 2)); 233 EXPECT_EQ(3, file_util::WriteFile( 234 tracker->GetFullDBFilePath(kOrigin2, kDB3), "aaa", 3)); 235 tracker->DatabaseModified(kOrigin1, kDB1); 236 tracker->DatabaseModified(kOrigin2, kDB2); 237 tracker->DatabaseModified(kOrigin2, kDB3); 238 239 // Delete db1. Should also delete origin1. 240 TestObserver observer; 241 tracker->AddObserver(&observer); 242 net::TestCompletionCallback callback; 243 int result = tracker->DeleteDatabase(kOrigin1, kDB1, callback.callback()); 244 EXPECT_EQ(net::ERR_IO_PENDING, result); 245 ASSERT_FALSE(callback.have_result()); 246 EXPECT_TRUE(observer.DidReceiveNewNotification()); 247 EXPECT_EQ(kOrigin1, observer.GetNotificationOriginIdentifier()); 248 EXPECT_EQ(kDB1, observer.GetNotificationDatabaseName()); 249 tracker->DatabaseClosed(kOrigin1, kDB1); 250 result = callback.GetResult(result); 251 EXPECT_EQ(net::OK, result); 252 EXPECT_FALSE(base::PathExists( 253 tracker->DatabaseDirectory().AppendASCII(kOrigin1))); 254 255 // Recreate db1. 256 tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, 257 &database_size); 258 EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append( 259 base::FilePath::FromWStringHack(UTF16ToWide( 260 tracker->GetOriginDirectory(kOrigin1)))))); 261 EXPECT_EQ(1, file_util::WriteFile( 262 tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1)); 263 tracker->DatabaseModified(kOrigin1, kDB1); 264 265 // Setup file modification times. db1 and db2 are modified now, db3 three 266 // days ago. 267 EXPECT_TRUE(file_util::SetLastModifiedTime( 268 tracker->GetFullDBFilePath(kOrigin1, kDB1), base::Time::Now())); 269 EXPECT_TRUE(file_util::SetLastModifiedTime( 270 tracker->GetFullDBFilePath(kOrigin2, kDB2), base::Time::Now())); 271 base::Time three_days_ago = base::Time::Now(); 272 three_days_ago -= base::TimeDelta::FromDays(3); 273 EXPECT_TRUE(file_util::SetLastModifiedTime( 274 tracker->GetFullDBFilePath(kOrigin2, kDB3), three_days_ago)); 275 276 // Delete databases modified since yesterday. db2 is whitelisted. 277 base::Time yesterday = base::Time::Now(); 278 yesterday -= base::TimeDelta::FromDays(1); 279 result = tracker->DeleteDataModifiedSince( 280 yesterday, callback.callback()); 281 EXPECT_EQ(net::ERR_IO_PENDING, result); 282 ASSERT_FALSE(callback.have_result()); 283 EXPECT_TRUE(observer.DidReceiveNewNotification()); 284 tracker->DatabaseClosed(kOrigin1, kDB1); 285 tracker->DatabaseClosed(kOrigin2, kDB2); 286 result = callback.GetResult(result); 287 EXPECT_EQ(net::OK, result); 288 EXPECT_FALSE(base::PathExists( 289 tracker->DatabaseDirectory().AppendASCII(kOrigin1))); 290 EXPECT_TRUE( 291 base::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB2))); 292 EXPECT_TRUE( 293 base::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB3))); 294 295 tracker->DatabaseClosed(kOrigin2, kDB3); 296 tracker->RemoveObserver(&observer); 297 } 298 299 static void TestDatabaseTracker(bool incognito_mode) { 300 // Initialize the tracker database. 301 base::ScopedTempDir temp_dir; 302 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 303 scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy = 304 new quota::MockSpecialStoragePolicy; 305 special_storage_policy->AddProtected(GURL(kOrigin2Url)); 306 scoped_refptr<DatabaseTracker> tracker( 307 new DatabaseTracker(temp_dir.path(), 308 incognito_mode, 309 special_storage_policy.get(), 310 NULL, 311 NULL)); 312 313 // Add two observers. 314 TestObserver observer1; 315 TestObserver observer2; 316 tracker->AddObserver(&observer1); 317 tracker->AddObserver(&observer2); 318 319 // Open three new databases. 320 int64 database_size = 0; 321 const std::string kOrigin1 = 322 webkit_database::GetIdentifierFromOrigin(GURL(kOrigin1Url)); 323 const std::string kOrigin2 = 324 webkit_database::GetIdentifierFromOrigin(GURL(kOrigin2Url)); 325 const base::string16 kDB1 = ASCIIToUTF16("db1"); 326 const base::string16 kDB2 = ASCIIToUTF16("db2"); 327 const base::string16 kDB3 = ASCIIToUTF16("db3"); 328 const base::string16 kDescription = ASCIIToUTF16("database_description"); 329 330 // Get the info for kOrigin1 and kOrigin2 331 DatabaseTracker::CachedOriginInfo* origin1_info = 332 tracker->GetCachedOriginInfo(kOrigin1); 333 DatabaseTracker::CachedOriginInfo* origin2_info = 334 tracker->GetCachedOriginInfo(kOrigin1); 335 EXPECT_TRUE(origin1_info); 336 EXPECT_TRUE(origin2_info); 337 338 339 tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, 340 &database_size); 341 EXPECT_EQ(0, database_size); 342 tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0, 343 &database_size); 344 EXPECT_EQ(0, database_size); 345 tracker->DatabaseOpened(kOrigin1, kDB3, kDescription, 0, 346 &database_size); 347 EXPECT_EQ(0, database_size); 348 349 // Write some data to each file and check that the listeners are 350 // called with the appropriate values. 351 EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append( 352 base::FilePath::FromWStringHack(UTF16ToWide( 353 tracker->GetOriginDirectory(kOrigin1)))))); 354 EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append( 355 base::FilePath::FromWStringHack(UTF16ToWide( 356 tracker->GetOriginDirectory(kOrigin2)))))); 357 EXPECT_EQ(1, file_util::WriteFile( 358 tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1)); 359 EXPECT_EQ(2, file_util::WriteFile( 360 tracker->GetFullDBFilePath(kOrigin2, kDB2), "aa", 2)); 361 EXPECT_EQ(4, file_util::WriteFile( 362 tracker->GetFullDBFilePath(kOrigin1, kDB3), "aaaa", 4)); 363 tracker->DatabaseModified(kOrigin1, kDB1); 364 CheckNotificationReceived(&observer1, kOrigin1, kDB1, 1); 365 CheckNotificationReceived(&observer2, kOrigin1, kDB1, 1); 366 tracker->DatabaseModified(kOrigin2, kDB2); 367 CheckNotificationReceived(&observer1, kOrigin2, kDB2, 2); 368 CheckNotificationReceived(&observer2, kOrigin2, kDB2, 2); 369 tracker->DatabaseModified(kOrigin1, kDB3); 370 CheckNotificationReceived(&observer1, kOrigin1, kDB3, 4); 371 CheckNotificationReceived(&observer2, kOrigin1, kDB3, 4); 372 373 // Close all databases 374 tracker->DatabaseClosed(kOrigin1, kDB1); 375 tracker->DatabaseClosed(kOrigin2, kDB2); 376 tracker->DatabaseClosed(kOrigin1, kDB3); 377 378 // Open an existing database and check the reported size 379 tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, 380 &database_size); 381 EXPECT_EQ(1, database_size); 382 tracker->DatabaseClosed(kOrigin1, kDB1); 383 384 // Remove an observer; this should clear all caches. 385 tracker->RemoveObserver(&observer2); 386 387 // Close the tracker database and clear all caches. 388 // Then make sure that DatabaseOpened() still returns the correct result. 389 tracker->CloseTrackerDatabaseAndClearCaches(); 390 tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, 391 &database_size); 392 EXPECT_EQ(1, database_size); 393 tracker->DatabaseClosed(kOrigin1, kDB1); 394 395 // Remove all observers. 396 tracker->RemoveObserver(&observer1); 397 398 // Trying to delete a database in use should fail 399 tracker->DatabaseOpened(kOrigin1, kDB3, kDescription, 0, 400 &database_size); 401 EXPECT_FALSE(tracker->DeleteClosedDatabase(kOrigin1, kDB3)); 402 origin1_info = tracker->GetCachedOriginInfo(kOrigin1); 403 EXPECT_TRUE(origin1_info); 404 EXPECT_EQ(4, origin1_info->GetDatabaseSize(kDB3)); 405 tracker->DatabaseClosed(kOrigin1, kDB3); 406 407 // Delete a database and make sure the space used by that origin is updated 408 EXPECT_TRUE(tracker->DeleteClosedDatabase(kOrigin1, kDB3)); 409 origin1_info = tracker->GetCachedOriginInfo(kOrigin1); 410 EXPECT_TRUE(origin1_info); 411 EXPECT_EQ(1, origin1_info->GetDatabaseSize(kDB1)); 412 EXPECT_EQ(0, origin1_info->GetDatabaseSize(kDB3)); 413 414 // Get all data for all origins 415 std::vector<OriginInfo> origins_info; 416 EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info)); 417 EXPECT_EQ(size_t(2), origins_info.size()); 418 EXPECT_EQ(kOrigin1, origins_info[0].GetOriginIdentifier()); 419 EXPECT_EQ(1, origins_info[0].TotalSize()); 420 EXPECT_EQ(1, origins_info[0].GetDatabaseSize(kDB1)); 421 EXPECT_EQ(0, origins_info[0].GetDatabaseSize(kDB3)); 422 423 EXPECT_EQ(kOrigin2, origins_info[1].GetOriginIdentifier()); 424 EXPECT_EQ(2, origins_info[1].TotalSize()); 425 426 // Trying to delete an origin with databases in use should fail 427 tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, 428 &database_size); 429 EXPECT_FALSE(tracker->DeleteOrigin(kOrigin1, false)); 430 origin1_info = tracker->GetCachedOriginInfo(kOrigin1); 431 EXPECT_TRUE(origin1_info); 432 EXPECT_EQ(1, origin1_info->GetDatabaseSize(kDB1)); 433 tracker->DatabaseClosed(kOrigin1, kDB1); 434 435 // Delete an origin that doesn't have any database in use 436 EXPECT_TRUE(tracker->DeleteOrigin(kOrigin1, false)); 437 origins_info.clear(); 438 EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info)); 439 EXPECT_EQ(size_t(1), origins_info.size()); 440 EXPECT_EQ(kOrigin2, origins_info[0].GetOriginIdentifier()); 441 442 origin1_info = tracker->GetCachedOriginInfo(kOrigin1); 443 EXPECT_TRUE(origin1_info); 444 EXPECT_EQ(0, origin1_info->TotalSize()); 445 } 446 447 static void DatabaseTrackerQuotaIntegration() { 448 const GURL kOrigin(kOrigin1Url); 449 const std::string kOriginId = 450 webkit_database::GetIdentifierFromOrigin(kOrigin); 451 const base::string16 kName = ASCIIToUTF16("name"); 452 const base::string16 kDescription = ASCIIToUTF16("description"); 453 454 base::ScopedTempDir temp_dir; 455 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 456 457 // Initialize the tracker with a QuotaManagerProxy 458 scoped_refptr<TestQuotaManagerProxy> test_quota_proxy( 459 new TestQuotaManagerProxy); 460 scoped_refptr<DatabaseTracker> tracker( 461 new DatabaseTracker(temp_dir.path(), 462 false /* incognito */, 463 NULL, 464 test_quota_proxy.get(), 465 NULL)); 466 EXPECT_TRUE(test_quota_proxy->registered_client_); 467 468 // Create a database and modify it a couple of times, close it, 469 // then delete it. Observe the tracker notifies accordingly. 470 471 int64 database_size = 0; 472 tracker->DatabaseOpened(kOriginId, kName, kDescription, 0, 473 &database_size); 474 EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin)); 475 test_quota_proxy->reset(); 476 477 base::FilePath db_file(tracker->GetFullDBFilePath(kOriginId, kName)); 478 EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName())); 479 EXPECT_TRUE(EnsureFileOfSize(db_file, 10)); 480 tracker->DatabaseModified(kOriginId, kName); 481 EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 10)); 482 test_quota_proxy->reset(); 483 484 EXPECT_TRUE(EnsureFileOfSize(db_file, 100)); 485 tracker->DatabaseModified(kOriginId, kName); 486 EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 90)); 487 test_quota_proxy->reset(); 488 489 tracker->DatabaseClosed(kOriginId, kName); 490 EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin)); 491 EXPECT_EQ(net::OK, tracker->DeleteDatabase( 492 kOriginId, kName, net::CompletionCallback())); 493 EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, -100)); 494 test_quota_proxy->reset(); 495 496 // Create a database and modify it, try to delete it while open, 497 // then close it (at which time deletion will actually occur). 498 // Observe the tracker notifies accordingly. 499 500 tracker->DatabaseOpened(kOriginId, kName, kDescription, 0, 501 &database_size); 502 EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin)); 503 test_quota_proxy->reset(); 504 505 db_file = tracker->GetFullDBFilePath(kOriginId, kName); 506 EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName())); 507 EXPECT_TRUE(EnsureFileOfSize(db_file, 100)); 508 tracker->DatabaseModified(kOriginId, kName); 509 EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 100)); 510 test_quota_proxy->reset(); 511 512 EXPECT_EQ(net::ERR_IO_PENDING, 513 tracker->DeleteDatabase(kOriginId, kName, 514 net::CompletionCallback())); 515 EXPECT_FALSE(test_quota_proxy->WasModificationNotified(kOrigin, -100)); 516 517 tracker->DatabaseClosed(kOriginId, kName); 518 EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin)); 519 EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, -100)); 520 test_quota_proxy->reset(); 521 522 // Create a database and up the file size without telling 523 // the tracker about the modification, than simulate a 524 // a renderer crash. 525 // Observe the tracker notifies accordingly. 526 527 tracker->DatabaseOpened(kOriginId, kName, kDescription, 0, 528 &database_size); 529 EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin)); 530 test_quota_proxy->reset(); 531 db_file = tracker->GetFullDBFilePath(kOriginId, kName); 532 EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName())); 533 EXPECT_TRUE(EnsureFileOfSize(db_file, 100)); 534 DatabaseConnections crashed_renderer_connections; 535 crashed_renderer_connections.AddConnection(kOriginId, kName); 536 EXPECT_FALSE(test_quota_proxy->WasModificationNotified(kOrigin, 100)); 537 tracker->CloseDatabases(crashed_renderer_connections); 538 EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 100)); 539 540 // Cleanup. 541 crashed_renderer_connections.RemoveAllConnections(); 542 test_quota_proxy->SimulateQuotaManagerDestroyed(); 543 } 544 545 static void DatabaseTrackerClearSessionOnlyDatabasesOnExit() { 546 int64 database_size = 0; 547 const std::string kOrigin1 = 548 webkit_database::GetIdentifierFromOrigin(GURL(kOrigin1Url)); 549 const std::string kOrigin2 = 550 webkit_database::GetIdentifierFromOrigin(GURL(kOrigin2Url)); 551 const base::string16 kDB1 = ASCIIToUTF16("db1"); 552 const base::string16 kDB2 = ASCIIToUTF16("db2"); 553 const base::string16 kDescription = ASCIIToUTF16("database_description"); 554 555 // Initialize the tracker database. 556 base::MessageLoop message_loop; 557 base::ScopedTempDir temp_dir; 558 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 559 base::FilePath origin1_db_dir; 560 base::FilePath origin2_db_dir; 561 { 562 scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy = 563 new quota::MockSpecialStoragePolicy; 564 special_storage_policy->AddSessionOnly(GURL(kOrigin2Url)); 565 scoped_refptr<DatabaseTracker> tracker( 566 new DatabaseTracker(temp_dir.path(), 567 false, 568 special_storage_policy.get(), 569 NULL, 570 base::MessageLoopProxy::current().get())); 571 572 // Open two new databases. 573 tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, 574 &database_size); 575 EXPECT_EQ(0, database_size); 576 tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0, 577 &database_size); 578 EXPECT_EQ(0, database_size); 579 580 // Write some data to each file. 581 base::FilePath db_file; 582 db_file = tracker->GetFullDBFilePath(kOrigin1, kDB1); 583 EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName())); 584 EXPECT_TRUE(EnsureFileOfSize(db_file, 1)); 585 586 db_file = tracker->GetFullDBFilePath(kOrigin2, kDB2); 587 EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName())); 588 EXPECT_TRUE(EnsureFileOfSize(db_file, 2)); 589 590 // Store the origin database directories as long as they still exist. 591 origin1_db_dir = tracker->GetFullDBFilePath(kOrigin1, kDB1).DirName(); 592 origin2_db_dir = tracker->GetFullDBFilePath(kOrigin2, kDB2).DirName(); 593 594 tracker->DatabaseModified(kOrigin1, kDB1); 595 tracker->DatabaseModified(kOrigin2, kDB2); 596 597 // Close all databases. 598 tracker->DatabaseClosed(kOrigin1, kDB1); 599 tracker->DatabaseClosed(kOrigin2, kDB2); 600 601 tracker->Shutdown(); 602 } 603 604 // At this point, the database tracker should be gone. Create a new one. 605 scoped_refptr<DatabaseTracker> tracker( 606 new DatabaseTracker(temp_dir.path(), false, NULL, NULL, NULL)); 607 608 // Get all data for all origins. 609 std::vector<OriginInfo> origins_info; 610 EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info)); 611 // kOrigin1 was not session-only, so it survived. kOrigin2 was session-only 612 // and it got deleted. 613 EXPECT_EQ(size_t(1), origins_info.size()); 614 EXPECT_EQ(kOrigin1, origins_info[0].GetOriginIdentifier()); 615 EXPECT_TRUE( 616 base::PathExists(tracker->GetFullDBFilePath(kOrigin1, kDB1))); 617 EXPECT_EQ(base::FilePath(), tracker->GetFullDBFilePath(kOrigin2, kDB2)); 618 619 // The origin directory of kOrigin1 remains, but the origin directory of 620 // kOrigin2 is deleted. 621 EXPECT_TRUE(base::PathExists(origin1_db_dir)); 622 EXPECT_FALSE(base::PathExists(origin2_db_dir)); 623 } 624 625 static void DatabaseTrackerSetForceKeepSessionState() { 626 int64 database_size = 0; 627 const std::string kOrigin1 = 628 webkit_database::GetIdentifierFromOrigin(GURL(kOrigin1Url)); 629 const std::string kOrigin2 = 630 webkit_database::GetIdentifierFromOrigin(GURL(kOrigin2Url)); 631 const base::string16 kDB1 = ASCIIToUTF16("db1"); 632 const base::string16 kDB2 = ASCIIToUTF16("db2"); 633 const base::string16 kDescription = ASCIIToUTF16("database_description"); 634 635 // Initialize the tracker database. 636 base::MessageLoop message_loop; 637 base::ScopedTempDir temp_dir; 638 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 639 base::FilePath origin1_db_dir; 640 base::FilePath origin2_db_dir; 641 { 642 scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy = 643 new quota::MockSpecialStoragePolicy; 644 special_storage_policy->AddSessionOnly(GURL(kOrigin2Url)); 645 scoped_refptr<DatabaseTracker> tracker( 646 new DatabaseTracker(temp_dir.path(), 647 false, 648 special_storage_policy.get(), 649 NULL, 650 base::MessageLoopProxy::current().get())); 651 tracker->SetForceKeepSessionState(); 652 653 // Open two new databases. 654 tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, 655 &database_size); 656 EXPECT_EQ(0, database_size); 657 tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0, 658 &database_size); 659 EXPECT_EQ(0, database_size); 660 661 // Write some data to each file. 662 base::FilePath db_file; 663 db_file = tracker->GetFullDBFilePath(kOrigin1, kDB1); 664 EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName())); 665 EXPECT_TRUE(EnsureFileOfSize(db_file, 1)); 666 667 db_file = tracker->GetFullDBFilePath(kOrigin2, kDB2); 668 EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName())); 669 EXPECT_TRUE(EnsureFileOfSize(db_file, 2)); 670 671 // Store the origin database directories as long as they still exist. 672 origin1_db_dir = tracker->GetFullDBFilePath(kOrigin1, kDB1).DirName(); 673 origin2_db_dir = tracker->GetFullDBFilePath(kOrigin2, kDB2).DirName(); 674 675 tracker->DatabaseModified(kOrigin1, kDB1); 676 tracker->DatabaseModified(kOrigin2, kDB2); 677 678 // Close all databases. 679 tracker->DatabaseClosed(kOrigin1, kDB1); 680 tracker->DatabaseClosed(kOrigin2, kDB2); 681 682 tracker->Shutdown(); 683 } 684 685 // At this point, the database tracker should be gone. Create a new one. 686 scoped_refptr<DatabaseTracker> tracker( 687 new DatabaseTracker(temp_dir.path(), false, NULL, NULL, NULL)); 688 689 // Get all data for all origins. 690 std::vector<OriginInfo> origins_info; 691 EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info)); 692 // No origins were deleted. 693 EXPECT_EQ(size_t(2), origins_info.size()); 694 EXPECT_TRUE( 695 base::PathExists(tracker->GetFullDBFilePath(kOrigin1, kDB1))); 696 EXPECT_TRUE( 697 base::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB2))); 698 699 EXPECT_TRUE(base::PathExists(origin1_db_dir)); 700 EXPECT_TRUE(base::PathExists(origin2_db_dir)); 701 } 702 703 static void EmptyDatabaseNameIsValid() { 704 const GURL kOrigin(kOrigin1Url); 705 const std::string kOriginId = 706 webkit_database::GetIdentifierFromOrigin(kOrigin); 707 const base::string16 kEmptyName; 708 const base::string16 kDescription(ASCIIToUTF16("description")); 709 const base::string16 kChangedDescription( 710 ASCIIToUTF16("changed_description")); 711 712 // Initialize a tracker database, no need to put it on disk. 713 const bool kUseInMemoryTrackerDatabase = true; 714 base::ScopedTempDir temp_dir; 715 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 716 scoped_refptr<DatabaseTracker> tracker( 717 new DatabaseTracker(temp_dir.path(), kUseInMemoryTrackerDatabase, 718 NULL, NULL, NULL)); 719 720 // Starts off with no databases. 721 std::vector<OriginInfo> infos; 722 EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos)); 723 EXPECT_TRUE(infos.empty()); 724 725 // Create a db with an empty name. 726 int64 database_size = -1; 727 tracker->DatabaseOpened(kOriginId, kEmptyName, kDescription, 0, 728 &database_size); 729 EXPECT_EQ(0, database_size); 730 tracker->DatabaseModified(kOriginId, kEmptyName); 731 EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos)); 732 EXPECT_EQ(1u, infos.size()); 733 EXPECT_EQ(kDescription, infos[0].GetDatabaseDescription(kEmptyName)); 734 EXPECT_FALSE(tracker->GetFullDBFilePath(kOriginId, kEmptyName).empty()); 735 tracker->DatabaseOpened(kOriginId, kEmptyName, kChangedDescription, 0, 736 &database_size); 737 infos.clear(); 738 EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos)); 739 EXPECT_EQ(1u, infos.size()); 740 EXPECT_EQ(kChangedDescription, infos[0].GetDatabaseDescription(kEmptyName)); 741 tracker->DatabaseClosed(kOriginId, kEmptyName); 742 tracker->DatabaseClosed(kOriginId, kEmptyName); 743 744 // Deleting it should return to the initial state. 745 EXPECT_EQ(net::OK, tracker->DeleteDatabase(kOriginId, kEmptyName, 746 net::CompletionCallback())); 747 infos.clear(); 748 EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos)); 749 EXPECT_TRUE(infos.empty()); 750 } 751 752 static void HandleSqliteError() { 753 const GURL kOrigin(kOrigin1Url); 754 const std::string kOriginId = 755 webkit_database::GetIdentifierFromOrigin(kOrigin); 756 const base::string16 kName(ASCIIToUTF16("name")); 757 const base::string16 kDescription(ASCIIToUTF16("description")); 758 759 // Initialize a tracker database, no need to put it on disk. 760 const bool kUseInMemoryTrackerDatabase = true; 761 base::ScopedTempDir temp_dir; 762 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 763 scoped_refptr<DatabaseTracker> tracker( 764 new DatabaseTracker(temp_dir.path(), kUseInMemoryTrackerDatabase, 765 NULL, NULL, NULL)); 766 767 // Setup to observe OnScheduledForDelete notifications. 768 TestObserver observer(false, true); 769 tracker->AddObserver(&observer); 770 771 // Verify does no harm when there is no such database. 772 tracker->HandleSqliteError(kOriginId, kName, SQLITE_CORRUPT); 773 EXPECT_FALSE(tracker->IsDatabaseScheduledForDeletion(kOriginId, kName)); 774 EXPECT_FALSE(observer.DidReceiveNewNotification()); 775 776 // -------------------------------------------------------- 777 // Create a record of a database in the tracker db and create 778 // a spoof_db_file on disk in the expected location. 779 int64 database_size = 0; 780 tracker->DatabaseOpened(kOriginId, kName, kDescription, 0, 781 &database_size); 782 base::FilePath spoof_db_file = tracker->GetFullDBFilePath(kOriginId, kName); 783 EXPECT_FALSE(tracker->GetFullDBFilePath(kOriginId, kName).empty()); 784 EXPECT_TRUE(file_util::CreateDirectory(spoof_db_file.DirName())); 785 EXPECT_TRUE(EnsureFileOfSize(spoof_db_file, 1)); 786 787 // Verify does no harm with a non-error is reported. 788 tracker->HandleSqliteError(kOriginId, kName, SQLITE_OK); 789 EXPECT_FALSE(tracker->IsDatabaseScheduledForDeletion(kOriginId, kName)); 790 EXPECT_FALSE(observer.DidReceiveNewNotification()); 791 792 // Verify that with a connection open, the db is scheduled for deletion, 793 // but that the file still exists. 794 tracker->HandleSqliteError(kOriginId, kName, SQLITE_CORRUPT); 795 EXPECT_TRUE(tracker->IsDatabaseScheduledForDeletion(kOriginId, kName)); 796 EXPECT_TRUE(observer.DidReceiveNewNotification()); 797 EXPECT_TRUE(base::PathExists(spoof_db_file)); 798 799 // Verify that once closed, the file is deleted and the record in the 800 // tracker db is removed. 801 tracker->DatabaseClosed(kOriginId, kName); 802 EXPECT_FALSE(base::PathExists(spoof_db_file)); 803 EXPECT_TRUE(tracker->GetFullDBFilePath(kOriginId, kName).empty()); 804 805 // -------------------------------------------------------- 806 // Create another record of a database in the tracker db and create 807 // a spoof_db_file on disk in the expected location. 808 tracker->DatabaseOpened(kOriginId, kName, kDescription, 0, 809 &database_size); 810 base::FilePath spoof_db_file2 = tracker->GetFullDBFilePath(kOriginId, kName); 811 EXPECT_FALSE(tracker->GetFullDBFilePath(kOriginId, kName).empty()); 812 EXPECT_NE(spoof_db_file, spoof_db_file2); 813 EXPECT_TRUE(file_util::CreateDirectory(spoof_db_file2.DirName())); 814 EXPECT_TRUE(EnsureFileOfSize(spoof_db_file2, 1)); 815 816 // Verify that with no connection open, the db is deleted immediately. 817 tracker->DatabaseClosed(kOriginId, kName); 818 tracker->HandleSqliteError(kOriginId, kName, SQLITE_CORRUPT); 819 EXPECT_FALSE(tracker->IsDatabaseScheduledForDeletion(kOriginId, kName)); 820 EXPECT_FALSE(observer.DidReceiveNewNotification()); 821 EXPECT_TRUE(tracker->GetFullDBFilePath(kOriginId, kName).empty()); 822 EXPECT_FALSE(base::PathExists(spoof_db_file2)); 823 824 tracker->RemoveObserver(&observer); 825 } 826 }; 827 828 TEST(DatabaseTrackerTest, DeleteOpenDatabase) { 829 DatabaseTracker_TestHelper_Test::TestDeleteOpenDatabase(false); 830 } 831 832 TEST(DatabaseTrackerTest, DeleteOpenDatabaseIncognitoMode) { 833 DatabaseTracker_TestHelper_Test::TestDeleteOpenDatabase(true); 834 } 835 836 TEST(DatabaseTrackerTest, DatabaseTracker) { 837 DatabaseTracker_TestHelper_Test::TestDatabaseTracker(false); 838 } 839 840 TEST(DatabaseTrackerTest, DatabaseTrackerIncognitoMode) { 841 DatabaseTracker_TestHelper_Test::TestDatabaseTracker(true); 842 } 843 844 TEST(DatabaseTrackerTest, DatabaseTrackerQuotaIntegration) { 845 // There is no difference in behavior between incognito and not. 846 DatabaseTracker_TestHelper_Test::DatabaseTrackerQuotaIntegration(); 847 } 848 849 TEST(DatabaseTrackerTest, DatabaseTrackerClearSessionOnlyDatabasesOnExit) { 850 // Only works for regular mode. 851 DatabaseTracker_TestHelper_Test:: 852 DatabaseTrackerClearSessionOnlyDatabasesOnExit(); 853 } 854 855 TEST(DatabaseTrackerTest, DatabaseTrackerSetForceKeepSessionState) { 856 // Only works for regular mode. 857 DatabaseTracker_TestHelper_Test::DatabaseTrackerSetForceKeepSessionState(); 858 } 859 860 TEST(DatabaseTrackerTest, EmptyDatabaseNameIsValid) { 861 DatabaseTracker_TestHelper_Test::EmptyDatabaseNameIsValid(); 862 } 863 864 TEST(DatabaseTrackerTest, HandleSqliteError) { 865 DatabaseTracker_TestHelper_Test::HandleSqliteError(); 866 } 867 868 } // namespace webkit_database 869