|  | // Copyright 2019 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 "content/browser/background_sync/background_sync_base_browsertest.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <set> | 
|  | #include <vector> | 
|  | #include "base/metrics/field_trial_param_associator.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "base/task/post_task.h" | 
|  | #include "content/browser/background_sync/background_sync_manager.h" | 
|  | #include "content/browser/storage_partition_impl.h" | 
|  | #include "content/public/browser/browser_context.h" | 
|  | #include "content/public/browser/browser_task_traits.h" | 
|  | #include "content/public/browser/service_worker_context.h" | 
|  | #include "content/public/test/background_sync_test_util.h" | 
|  | #include "content/public/test/content_browser_test_utils.h" | 
|  | #include "content/shell/browser/shell.h" | 
|  | #include "content/test/mock_background_sync_controller.h" | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | BackgroundSyncBaseBrowserTest::BackgroundSyncBaseBrowserTest() {} | 
|  | BackgroundSyncBaseBrowserTest::~BackgroundSyncBaseBrowserTest() {} | 
|  |  | 
|  | std::string BackgroundSyncBaseBrowserTest::BuildScriptString( | 
|  | const std::string& function, | 
|  | const std::string& argument) { | 
|  | return base::StringPrintf("%s('%s');", function.c_str(), argument.c_str()); | 
|  | } | 
|  |  | 
|  | std::string BackgroundSyncBaseBrowserTest::BuildExpectedResult( | 
|  | const std::string& tag, | 
|  | const std::string& action) { | 
|  | return base::StringPrintf("%s%s %s", kSuccessfulOperationPrefix, tag.c_str(), | 
|  | action.c_str()); | 
|  | } | 
|  |  | 
|  | bool BackgroundSyncBaseBrowserTest::RegistrationPending( | 
|  | const std::string& tag) { | 
|  | bool is_pending; | 
|  | base::RunLoop run_loop; | 
|  |  | 
|  | StoragePartitionImpl* storage = GetStorage(); | 
|  | BackgroundSyncContextImpl* sync_context = storage->GetBackgroundSyncContext(); | 
|  | ServiceWorkerContextWrapper* service_worker_context = | 
|  | static_cast<ServiceWorkerContextWrapper*>( | 
|  | storage->GetServiceWorkerContext()); | 
|  |  | 
|  | auto callback = base::BindOnce( | 
|  | &BackgroundSyncBaseBrowserTest::RegistrationPendingCallback, | 
|  | base::Unretained(this), run_loop.QuitClosure(), | 
|  | base::ThreadTaskRunnerHandle::Get(), &is_pending); | 
|  |  | 
|  | RunOrPostTaskOnThread( | 
|  | FROM_HERE, ServiceWorkerContext::GetCoreThreadId(), | 
|  | base::BindOnce( | 
|  | &BackgroundSyncBaseBrowserTest::RegistrationPendingOnCoreThread, | 
|  | base::Unretained(this), base::WrapRefCounted(sync_context), | 
|  | base::WrapRefCounted(service_worker_context), tag, | 
|  | https_server_->GetURL(kDefaultTestURL), std::move(callback))); | 
|  |  | 
|  | run_loop.Run(); | 
|  |  | 
|  | return is_pending; | 
|  | } | 
|  |  | 
|  | bool BackgroundSyncBaseBrowserTest::CompleteDelayedSyncEvent() { | 
|  | std::string script_result; | 
|  | EXPECT_TRUE(RunScript("completeDelayedSyncEvent()", &script_result)); | 
|  | return script_result == BuildExpectedResult("delay", "completing"); | 
|  | } | 
|  |  | 
|  | void BackgroundSyncBaseBrowserTest::RegistrationPendingCallback( | 
|  | base::OnceClosure quit, | 
|  | const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | 
|  | bool* result_out, | 
|  | bool result) { | 
|  | *result_out = result; | 
|  | task_runner->PostTask(FROM_HERE, std::move(quit)); | 
|  | } | 
|  |  | 
|  | void BackgroundSyncBaseBrowserTest::RegistrationPendingDidGetSyncRegistration( | 
|  | const std::string& tag, | 
|  | base::OnceCallback<void(bool)> callback, | 
|  | BackgroundSyncStatus error_type, | 
|  | std::vector<std::unique_ptr<BackgroundSyncRegistration>> registrations) { | 
|  | ASSERT_EQ(BACKGROUND_SYNC_STATUS_OK, error_type); | 
|  | // Find the right registration in the list and check its status. | 
|  | for (const auto& registration : registrations) { | 
|  | if (registration->options()->tag == tag) { | 
|  | std::move(callback).Run(registration->sync_state() == | 
|  | blink::mojom::BackgroundSyncState::PENDING); | 
|  | return; | 
|  | } | 
|  | } | 
|  | ADD_FAILURE() << "Registration should exist"; | 
|  | } | 
|  |  | 
|  | void BackgroundSyncBaseBrowserTest::RegistrationPendingDidGetSWRegistration( | 
|  | const scoped_refptr<BackgroundSyncContextImpl> sync_context, | 
|  | const std::string& tag, | 
|  | base::OnceCallback<void(bool)> callback, | 
|  | blink::ServiceWorkerStatusCode status, | 
|  | scoped_refptr<ServiceWorkerRegistration> registration) { | 
|  | ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, status); | 
|  | int64_t service_worker_id = registration->id(); | 
|  | BackgroundSyncManager* sync_manager = sync_context->background_sync_manager(); | 
|  | sync_manager->GetOneShotSyncRegistrations( | 
|  | service_worker_id, | 
|  | base::BindOnce(&BackgroundSyncBaseBrowserTest:: | 
|  | RegistrationPendingDidGetSyncRegistration, | 
|  | base::Unretained(this), tag, std::move(callback))); | 
|  | } | 
|  |  | 
|  | void BackgroundSyncBaseBrowserTest::RegistrationPendingOnCoreThread( | 
|  | const scoped_refptr<BackgroundSyncContextImpl> sync_context, | 
|  | const scoped_refptr<ServiceWorkerContextWrapper> sw_context, | 
|  | const std::string& tag, | 
|  | const GURL& url, | 
|  | base::OnceCallback<void(bool)> callback) { | 
|  | sw_context->FindReadyRegistrationForClientUrl( | 
|  | url, base::BindOnce(&BackgroundSyncBaseBrowserTest:: | 
|  | RegistrationPendingDidGetSWRegistration, | 
|  | base::Unretained(this), sync_context, tag, | 
|  | std::move(callback))); | 
|  | } | 
|  |  | 
|  | void BackgroundSyncBaseBrowserTest::SetTestClockOnCoreThread( | 
|  | BackgroundSyncContextImpl* sync_context, | 
|  | base::SimpleTestClock* clock) { | 
|  | DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId()); | 
|  | DCHECK(clock); | 
|  |  | 
|  | BackgroundSyncManager* background_sync_manager = | 
|  | sync_context->background_sync_manager(); | 
|  | background_sync_manager->set_clock(clock); | 
|  | } | 
|  |  | 
|  | void BackgroundSyncBaseBrowserTest::SetUp() { | 
|  | const char kTrialName[] = "BackgroundSync"; | 
|  | const char kGroupName[] = "BackgroundSync"; | 
|  | const char kFeatureName[] = "PeriodicBackgroundSync"; | 
|  | scoped_refptr<base::FieldTrial> trial = | 
|  | base::FieldTrialList::CreateFieldTrial(kTrialName, kGroupName); | 
|  | std::map<std::string, std::string> params; | 
|  | params["max_sync_attempts"] = "1"; | 
|  | params["min_periodic_sync_events_interval_sec"] = "5"; | 
|  | base::FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams( | 
|  | kTrialName, kGroupName, params); | 
|  | std::unique_ptr<base::FeatureList> feature_list( | 
|  | std::make_unique<base::FeatureList>()); | 
|  | feature_list->RegisterFieldTrialOverride( | 
|  | kFeatureName, base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get()); | 
|  | scoped_feature_list_.InitWithFeatureList(std::move(feature_list)); | 
|  |  | 
|  | background_sync_test_util::SetIgnoreNetworkChanges(true); | 
|  | ContentBrowserTest::SetUp(); | 
|  | } | 
|  |  | 
|  | void BackgroundSyncBaseBrowserTest::SetIncognitoMode(bool incognito) { | 
|  | shell_ = incognito ? CreateOffTheRecordBrowser() : shell(); | 
|  | // Let any async shell creation logic finish. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | StoragePartitionImpl* BackgroundSyncBaseBrowserTest::GetStorage() { | 
|  | WebContents* web_contents = shell_->web_contents(); | 
|  | return static_cast<StoragePartitionImpl*>(BrowserContext::GetStoragePartition( | 
|  | web_contents->GetBrowserContext(), web_contents->GetSiteInstance())); | 
|  | } | 
|  |  | 
|  | WebContents* BackgroundSyncBaseBrowserTest::web_contents() { | 
|  | return shell_->web_contents(); | 
|  | } | 
|  |  | 
|  | void BackgroundSyncBaseBrowserTest::SetUpOnMainThread() { | 
|  | https_server_ = std::make_unique<net::EmbeddedTestServer>( | 
|  | net::EmbeddedTestServer::TYPE_HTTPS); | 
|  | https_server_->ServeFilesFromSourceDirectory(GetTestDataFilePath()); | 
|  | ASSERT_TRUE(https_server_->Start()); | 
|  |  | 
|  | SetIncognitoMode(false); | 
|  | background_sync_test_util::SetOnline(web_contents(), true); | 
|  | ASSERT_TRUE(LoadTestPage(kDefaultTestURL)); | 
|  |  | 
|  | ContentBrowserTest::SetUpOnMainThread(); | 
|  | } | 
|  |  | 
|  | void BackgroundSyncBaseBrowserTest::TearDownOnMainThread() { | 
|  | https_server_.reset(); | 
|  | } | 
|  |  | 
|  | bool BackgroundSyncBaseBrowserTest::LoadTestPage(const std::string& path) { | 
|  | return NavigateToURL(shell_, https_server_->GetURL(path)); | 
|  | } | 
|  |  | 
|  | bool BackgroundSyncBaseBrowserTest::RunScript(const std::string& script, | 
|  | std::string* result) { | 
|  | return content::ExecuteScriptAndExtractString(web_contents(), script, result); | 
|  | } | 
|  |  | 
|  | void BackgroundSyncBaseBrowserTest::SetTestClock(base::SimpleTestClock* clock) { | 
|  | StoragePartitionImpl* storage = GetStorage(); | 
|  | BackgroundSyncContextImpl* sync_context = storage->GetBackgroundSyncContext(); | 
|  |  | 
|  | // TODO(crbug.com/824858): Remove the else branch after the feature is | 
|  | // enabled. Also, try to make a RunOrPostTaskOnThreadAndReply() function so | 
|  | // the if/else isn't needed. | 
|  | if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) { | 
|  | SetTestClockOnCoreThread(sync_context, clock); | 
|  | } else { | 
|  | base::RunLoop run_loop; | 
|  | base::PostTaskAndReply( | 
|  | FROM_HERE, ServiceWorkerContext::GetCoreThreadId(), | 
|  | base::BindOnce(&BackgroundSyncBaseBrowserTest::SetTestClockOnCoreThread, | 
|  | base::Unretained(this), base::Unretained(sync_context), | 
|  | clock), | 
|  | run_loop.QuitClosure()); | 
|  | run_loop.Run(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void BackgroundSyncBaseBrowserTest::ClearStoragePartitionData() { | 
|  | // Clear data from the storage partition.  Parameters are set to clear data | 
|  | // for service workers, for all origins, for an unbounded time range. | 
|  | StoragePartitionImpl* storage = GetStorage(); | 
|  |  | 
|  | uint32_t storage_partition_mask = | 
|  | StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS; | 
|  | uint32_t quota_storage_mask = | 
|  | StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL; | 
|  | GURL delete_origin = GURL(); | 
|  | const base::Time delete_begin = base::Time(); | 
|  | base::Time delete_end = base::Time::Max(); | 
|  |  | 
|  | base::RunLoop run_loop; | 
|  |  | 
|  | storage->ClearData(storage_partition_mask, quota_storage_mask, delete_origin, | 
|  | delete_begin, delete_end, run_loop.QuitClosure()); | 
|  |  | 
|  | run_loop.Run(); | 
|  | } | 
|  |  | 
|  | std::string BackgroundSyncBaseBrowserTest::PopConsoleString() { | 
|  | std::string script_result; | 
|  | EXPECT_TRUE(RunScript("resultQueue.pop()", &script_result)); | 
|  | return script_result; | 
|  | } | 
|  |  | 
|  | bool BackgroundSyncBaseBrowserTest::PopConsole( | 
|  | const std::string& expected_msg) { | 
|  | std::string script_result = PopConsoleString(); | 
|  | return script_result == expected_msg; | 
|  | } | 
|  |  | 
|  | bool BackgroundSyncBaseBrowserTest::RegisterServiceWorker() { | 
|  | std::string script_result; | 
|  | EXPECT_TRUE(RunScript("registerServiceWorker()", &script_result)); | 
|  | return script_result == BuildExpectedResult("service worker", "registered"); | 
|  | } | 
|  |  | 
|  | }  // namespace content |