// Copyright 2015 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 #include #include #include #include "base/command_line.h" #include "base/macros.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" #include "base/task_runner_util.h" #include "content/browser/background_sync/background_sync_manager.h" #include "content/browser/background_sync/background_sync_network_observer.h" #include "content/browser/background_sync/background_sync_registration_handle.h" #include "content/browser/background_sync/background_sync_status.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_registration.h" #include "content/public/browser/background_sync_context.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_switches.h" #include "content/public/test/background_sync_test_util.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" #include "content/public/test/test_utils.h" #include "content/shell/browser/shell.h" #include "net/base/network_change_notifier.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "testing/gtest/include/gtest/gtest.h" using net::NetworkChangeNotifier; namespace content { namespace { const char kDefaultTestURL[] = "/background_sync/test.html"; const char kEmptyURL[] = "/background_sync/empty.html"; const char kRegisterSyncURL[] = "/background_sync/register_sync.html"; const char kSuccessfulOperationPrefix[] = "ok - "; std::string BuildScriptString(const std::string& function, const std::string& argument) { return base::StringPrintf("%s('%s');", function.c_str(), argument.c_str()); } std::string BuildExpectedResult(const std::string& tag, const std::string& action) { return base::StringPrintf("%s%s %s", kSuccessfulOperationPrefix, tag.c_str(), action.c_str()); } void RegistrationPendingCallback( const base::Closure& quit, const scoped_refptr& task_runner, bool* result_out, bool result) { *result_out = result; task_runner->PostTask(FROM_HERE, quit); } void RegistrationPendingDidGetSyncRegistration( const base::Callback& callback, BackgroundSyncStatus error_type, scoped_ptr registration_handle) { ASSERT_EQ(BACKGROUND_SYNC_STATUS_OK, error_type); callback.Run(registration_handle->sync_state() == BackgroundSyncState::PENDING); } void RegistrationPendingDidGetSWRegistration( const scoped_refptr sync_context, const std::string& tag, const base::Callback& callback, ServiceWorkerStatusCode status, const scoped_refptr& registration) { ASSERT_EQ(SERVICE_WORKER_OK, status); int64_t service_worker_id = registration->id(); BackgroundSyncManager* sync_manager = sync_context->background_sync_manager(); sync_manager->GetRegistration( service_worker_id, tag, base::Bind(&RegistrationPendingDidGetSyncRegistration, callback)); } void RegistrationPendingOnIOThread( const scoped_refptr sync_context, const scoped_refptr sw_context, const std::string& tag, const GURL& url, const base::Callback& callback) { sw_context->FindReadyRegistrationForDocument( url, base::Bind(&RegistrationPendingDidGetSWRegistration, sync_context, tag, callback)); } void SetMaxSyncAttemptsOnIOThread( const scoped_refptr& sync_context, int max_sync_attempts) { BackgroundSyncManager* background_sync_manager = sync_context->background_sync_manager(); background_sync_manager->SetMaxSyncAttemptsForTesting(max_sync_attempts); } } // namespace class BackgroundSyncBrowserTest : public ContentBrowserTest { public: BackgroundSyncBrowserTest() {} ~BackgroundSyncBrowserTest() override {} void SetUp() override { background_sync_test_util::SetIgnoreNetworkChangeNotifier(true); ContentBrowserTest::SetUp(); } void SetIncognitoMode(bool incognito) { shell_ = incognito ? CreateOffTheRecordBrowser() : shell(); } StoragePartition* GetStorage() { WebContents* web_contents = shell_->web_contents(); return BrowserContext::GetStoragePartition( web_contents->GetBrowserContext(), web_contents->GetSiteInstance()); } BackgroundSyncContext* GetSyncContext() { return GetStorage()->GetBackgroundSyncContext(); } WebContents* web_contents() { return shell_->web_contents(); } void SetUpCommandLine(base::CommandLine* command_line) override { // TODO(jkarlin): Remove this once background sync is no longer // experimental. command_line->AppendSwitch( switches::kEnableExperimentalWebPlatformFeatures); } void SetUpOnMainThread() override { https_server_.reset( new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTPS)); https_server_->ServeFilesFromSourceDirectory("content/test/data"); ASSERT_TRUE(https_server_->Start()); SetIncognitoMode(false); SetMaxSyncAttempts(1); background_sync_test_util::SetOnline(web_contents(), true); ASSERT_TRUE(LoadTestPage(kDefaultTestURL)); ContentBrowserTest::SetUpOnMainThread(); } void TearDownOnMainThread() override { https_server_.reset(); } bool LoadTestPage(const std::string& path) { return NavigateToURL(shell_, https_server_->GetURL(path)); } bool RunScript(const std::string& script, std::string* result) { return content::ExecuteScriptAndExtractString(web_contents(), script, result); } // Returns true if the registration with tag |tag| is currently pending. Fails // (assertion failure) if the tag isn't registered. bool RegistrationPending(const std::string& tag); // Sets the BackgroundSyncManager's max sync attempts per registration. void SetMaxSyncAttempts(int max_sync_attempts); void ClearStoragePartitionData(); std::string PopConsoleString(); bool PopConsole(const std::string& expected_msg); bool RegisterServiceWorker(); bool Register(const std::string& tag); bool RegisterFromServiceWorker(const std::string& tag); bool HasTag(const std::string& tag); bool HasTagFromServiceWorker(const std::string& tag); bool MatchTags(const std::string& script_result, const std::vector& expected_tags); bool GetTags(const std::vector& expected_tags); bool GetTagsFromServiceWorker(const std::vector& expected_tags); bool CompleteDelayedSyncEvent(); bool RejectDelayedSyncEvent(); net::EmbeddedTestServer* https_server() { return https_server_.get(); } private: scoped_ptr https_server_; Shell* shell_ = nullptr; DISALLOW_COPY_AND_ASSIGN(BackgroundSyncBrowserTest); }; bool BackgroundSyncBrowserTest::RegistrationPending(const std::string& tag) { bool is_pending; base::RunLoop run_loop; StoragePartition* storage = GetStorage(); BackgroundSyncContext* sync_context = storage->GetBackgroundSyncContext(); ServiceWorkerContextWrapper* service_worker_context = static_cast( storage->GetServiceWorkerContext()); base::Callback callback = base::Bind(&RegistrationPendingCallback, run_loop.QuitClosure(), base::ThreadTaskRunnerHandle::Get(), &is_pending); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&RegistrationPendingOnIOThread, make_scoped_refptr(sync_context), make_scoped_refptr(service_worker_context), tag, https_server_->GetURL(kDefaultTestURL), callback)); run_loop.Run(); return is_pending; } void BackgroundSyncBrowserTest::SetMaxSyncAttempts(int max_sync_attempts) { base::RunLoop run_loop; StoragePartition* storage = GetStorage(); BackgroundSyncContext* sync_context = storage->GetBackgroundSyncContext(); BrowserThread::PostTaskAndReply( BrowserThread::IO, FROM_HERE, base::Bind(&SetMaxSyncAttemptsOnIOThread, make_scoped_refptr(sync_context), max_sync_attempts), run_loop.QuitClosure()); run_loop.Run(); } void BackgroundSyncBrowserTest::ClearStoragePartitionData() { // Clear data from the storage partition. Parameters are set to clear data // for service workers, for all origins, for an unbounded time range. StoragePartition* storage = GetStorage(); uint32_t storage_partition_mask = StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS; uint32_t quota_storage_mask = StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL; const 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, StoragePartition::OriginMatcherFunction(), delete_begin, delete_end, run_loop.QuitClosure()); run_loop.Run(); } std::string BackgroundSyncBrowserTest::PopConsoleString() { std::string script_result; EXPECT_TRUE(RunScript("resultQueue.pop()", &script_result)); return script_result; } bool BackgroundSyncBrowserTest::PopConsole(const std::string& expected_msg) { std::string script_result = PopConsoleString(); return script_result == expected_msg; } bool BackgroundSyncBrowserTest::RegisterServiceWorker() { std::string script_result; EXPECT_TRUE(RunScript("registerServiceWorker()", &script_result)); return script_result == BuildExpectedResult("service worker", "registered"); } bool BackgroundSyncBrowserTest::Register(const std::string& tag) { std::string script_result; EXPECT_TRUE(RunScript(BuildScriptString("register", tag), &script_result)); return script_result == BuildExpectedResult(tag, "registered"); } bool BackgroundSyncBrowserTest::RegisterFromServiceWorker( const std::string& tag) { std::string script_result; EXPECT_TRUE(RunScript(BuildScriptString("registerFromServiceWorker", tag), &script_result)); return script_result == BuildExpectedResult(tag, "register sent to SW"); } bool BackgroundSyncBrowserTest::HasTag(const std::string& tag) { std::string script_result; EXPECT_TRUE(RunScript(BuildScriptString("hasTag", tag), &script_result)); return script_result == BuildExpectedResult(tag, "found"); } bool BackgroundSyncBrowserTest::HasTagFromServiceWorker( const std::string& tag) { std::string script_result; EXPECT_TRUE(RunScript(BuildScriptString("hasTagFromServiceWorker", tag), &script_result)); EXPECT_TRUE(script_result == "ok - hasTag sent to SW"); return PopConsole(BuildExpectedResult(tag, "found")); } bool BackgroundSyncBrowserTest::MatchTags( const std::string& script_result, const std::vector& expected_tags) { EXPECT_TRUE(base::StartsWith(script_result, kSuccessfulOperationPrefix, base::CompareCase::INSENSITIVE_ASCII)); std::string tag_string = script_result.substr(strlen(kSuccessfulOperationPrefix)); std::vector result_tags = base::SplitString( tag_string, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); return std::set(expected_tags.begin(), expected_tags.end()) == std::set(result_tags.begin(), result_tags.end()); } bool BackgroundSyncBrowserTest::GetTags( const std::vector& expected_tags) { std::string script_result; EXPECT_TRUE(RunScript("getTags()", &script_result)); return MatchTags(script_result, expected_tags); } bool BackgroundSyncBrowserTest::GetTagsFromServiceWorker( const std::vector& expected_tags) { std::string script_result; EXPECT_TRUE(RunScript("getTagsFromServiceWorker()", &script_result)); EXPECT_TRUE(script_result == "ok - getTags sent to SW"); return MatchTags(PopConsoleString(), expected_tags); } bool BackgroundSyncBrowserTest::CompleteDelayedSyncEvent() { std::string script_result; EXPECT_TRUE(RunScript("completeDelayedSyncEvent()", &script_result)); return script_result == BuildExpectedResult("delay", "completing"); } bool BackgroundSyncBrowserTest::RejectDelayedSyncEvent() { std::string script_result; EXPECT_TRUE(RunScript("rejectDelayedSyncEvent()", &script_result)); return script_result == BuildExpectedResult("delay", "rejecting"); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, RegisterFromControlledDocument) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. EXPECT_TRUE(Register("foo")); EXPECT_TRUE(PopConsole("foo fired")); EXPECT_FALSE(HasTag("foo")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, RegisterFromUncontrolledDocument) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(Register("foo")); EXPECT_TRUE(PopConsole("foo fired")); EXPECT_FALSE(HasTag("foo")); } // Verify that Register works in a service worker IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, RegisterFromServiceWorker) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. EXPECT_TRUE(RegisterFromServiceWorker("foo_sw")); EXPECT_TRUE(PopConsole("ok - foo_sw registered in SW")); EXPECT_TRUE(PopConsole("foo_sw fired")); EXPECT_FALSE(HasTag("foo_sw")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, RegistrationDelaysForNetwork) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. // Prevent firing by going offline. background_sync_test_util::SetOnline(web_contents(), false); EXPECT_TRUE(Register("foo")); EXPECT_TRUE(HasTag("foo")); EXPECT_TRUE(RegistrationPending("foo")); // Resume firing by going online. background_sync_test_util::SetOnline(web_contents(), true); EXPECT_TRUE(PopConsole("foo fired")); EXPECT_FALSE(HasTag("foo")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, WaitUntil) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. background_sync_test_util::SetOnline(web_contents(), true); EXPECT_TRUE(Register("delay")); // Verify that it is firing. EXPECT_TRUE(HasTag("delay")); EXPECT_FALSE(RegistrationPending("delay")); // Complete the task. EXPECT_TRUE(CompleteDelayedSyncEvent()); EXPECT_TRUE(PopConsole("ok - delay completed")); // Verify that it finished firing. EXPECT_FALSE(HasTag("delay")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, WaitUntilReject) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. background_sync_test_util::SetOnline(web_contents(), true); EXPECT_TRUE(Register("delay")); // Verify that it is firing. EXPECT_TRUE(HasTag("delay")); EXPECT_FALSE(RegistrationPending("delay")); // Complete the task. EXPECT_TRUE(RejectDelayedSyncEvent()); EXPECT_TRUE(PopConsole("ok - delay rejected")); EXPECT_FALSE(HasTag("delay")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, Incognito) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. background_sync_test_util::SetOnline(web_contents(), false); EXPECT_TRUE(Register("normal")); EXPECT_TRUE(RegistrationPending("normal")); // Go incognito and verify that incognito doesn't see the registration. SetIncognitoMode(true); // Tell the new network observer that we're offline (it initializes from // NetworkChangeNotifier::GetCurrentConnectionType() which is not mocked out // in this test). background_sync_test_util::SetOnline(web_contents(), false); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); EXPECT_TRUE(RegisterServiceWorker()); EXPECT_FALSE(HasTag("normal")); EXPECT_TRUE(Register("incognito")); EXPECT_TRUE(RegistrationPending("incognito")); // Switch back and make sure the registration is still there. SetIncognitoMode(false); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Should be controlled. EXPECT_TRUE(HasTag("normal")); EXPECT_FALSE(HasTag("incognito")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, GetTags) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. std::vector registered_tags; EXPECT_TRUE(GetTags(registered_tags)); background_sync_test_util::SetOnline(web_contents(), false); registered_tags.push_back("foo"); registered_tags.push_back("bar"); for (const std::string& tag : registered_tags) EXPECT_TRUE(Register(tag)); EXPECT_TRUE(GetTags(registered_tags)); } // Verify that GetRegistrations works in a service worker IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, GetRegistrationsFromServiceWorker) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. std::vector registered_tags; EXPECT_TRUE(GetTags(registered_tags)); background_sync_test_util::SetOnline(web_contents(), false); registered_tags.push_back("foo_sw"); registered_tags.push_back("bar_sw"); for (const std::string& tag : registered_tags) { EXPECT_TRUE(RegisterFromServiceWorker(tag)); EXPECT_TRUE(PopConsole(BuildExpectedResult(tag, "registered in SW"))); } EXPECT_TRUE(GetTagsFromServiceWorker(registered_tags)); } // Verify that GetRegistration works in a service worker IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, HasTagFromServiceWorker) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. std::vector registered_tags; EXPECT_TRUE(GetTags(registered_tags)); background_sync_test_util::SetOnline(web_contents(), false); EXPECT_TRUE(RegisterFromServiceWorker("foo_sw")); EXPECT_TRUE(PopConsole("ok - foo_sw registered in SW")); EXPECT_TRUE(HasTagFromServiceWorker("foo_sw")); } // Verify that a background sync registration is deleted when site data is // cleared. IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, SyncRegistrationDeletedWhenClearingSiteData) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. // Prevent firing by going offline. background_sync_test_util::SetOnline(web_contents(), false); EXPECT_TRUE(Register("foo")); EXPECT_TRUE(HasTag("foo")); EXPECT_TRUE(RegistrationPending("foo")); // Simulate a user clearing site data (including Service Workers, crucially), // by clearing data from the storage partition. ClearStoragePartitionData(); EXPECT_FALSE(HasTag("foo")); } // Verify that a background sync registration, from a service worker, is deleted // when site data is cleared. IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, SyncRegistrationFromSWDeletedWhenClearingSiteData) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. std::vector registered_tags; EXPECT_TRUE(GetTags(registered_tags)); background_sync_test_util::SetOnline(web_contents(), false); EXPECT_TRUE(RegisterFromServiceWorker("foo_sw")); EXPECT_TRUE(PopConsole("ok - foo_sw registered in SW")); EXPECT_TRUE(HasTagFromServiceWorker("foo_sw")); // Simulate a user clearing site data (including Service Workers, crucially), // by clearing data from the storage partition. ClearStoragePartitionData(); EXPECT_FALSE(HasTagFromServiceWorker("foo")); } // Verify that multiple background sync registrations are deleted when site // data is cleared. IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, SyncRegistrationsDeletedWhenClearingSiteData) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. std::vector registered_tags; EXPECT_TRUE(GetTags(registered_tags)); background_sync_test_util::SetOnline(web_contents(), false); registered_tags.push_back("foo"); registered_tags.push_back("bar"); for (const std::string& tag : registered_tags) EXPECT_TRUE(Register(tag)); EXPECT_TRUE(GetTags(registered_tags)); for (const std::string& tag : registered_tags) EXPECT_TRUE(RegistrationPending(tag)); // Simulate a user clearing site data (including Service Workers, crucially), // by clearing data from the storage partition. ClearStoragePartitionData(); for (const std::string& tag : registered_tags) EXPECT_FALSE(HasTag(tag)); } // Verify that a sync event that is currently firing is deleted when site // data is cleared. IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, FiringSyncEventDeletedWhenClearingSiteData) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. background_sync_test_util::SetOnline(web_contents(), true); EXPECT_TRUE(Register("delay")); // Verify that it is firing. EXPECT_TRUE(HasTag("delay")); EXPECT_FALSE(RegistrationPending("delay")); // Simulate a user clearing site data (including Service Workers, crucially), // by clearing data from the storage partition. ClearStoragePartitionData(); // Verify that it was deleted. EXPECT_FALSE(HasTag("delay")); } // Disabled due to flakiness. See https://crbug.com/578952. IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, DISABLED_VerifyRetry) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. SetMaxSyncAttempts(2); EXPECT_TRUE(Register("delay")); EXPECT_TRUE(RejectDelayedSyncEvent()); EXPECT_TRUE(PopConsole("ok - delay rejected")); // Verify that the registration is still around and waiting to try again. EXPECT_TRUE(RegistrationPending("delay")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, RegisterFromNonMainFrame) { std::string script_result; GURL url = https_server()->GetURL(kEmptyURL); EXPECT_TRUE(RunScript(BuildScriptString("registerFromLocalFrame", url.spec()), &script_result)); EXPECT_EQ(BuildExpectedResult("iframe", "failed to register sync"), script_result); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, RegisterFromServiceWorkerWithoutMainFrameHost) { // Start a second https server to use as a second origin. net::EmbeddedTestServer alt_server(net::EmbeddedTestServer::TYPE_HTTPS); alt_server.ServeFilesFromSourceDirectory("content/test/data"); ASSERT_TRUE(alt_server.Start()); std::string script_result; GURL url = alt_server.GetURL(kRegisterSyncURL); EXPECT_TRUE(RunScript( BuildScriptString("registerFromCrossOriginServiceWorker", url.spec()), &script_result)); EXPECT_EQ(BuildExpectedResult("worker", "failed to register sync"), script_result); } } // namespace content