// 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 "base/command_line.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_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/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 "testing/gtest/include/gtest/gtest.h" using net::NetworkChangeNotifier; namespace content { namespace { const char kDefaultTestURL[] = "files/background_sync/test.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 OneShotPendingCallback( const base::Closure& quit, const scoped_refptr& task_runner, bool* result_out, bool result) { *result_out = result; task_runner->PostTask(FROM_HERE, quit); } void OneShotPendingDidGetSyncRegistration( const base::Callback& callback, BackgroundSyncStatus error_type, const BackgroundSyncRegistration& registration) { ASSERT_EQ(BACKGROUND_SYNC_STATUS_OK, error_type); callback.Run(registration.sync_state() == SYNC_STATE_PENDING); } void OneShotPendingDidGetSWRegistration( 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 service_worker_id = registration->id(); BackgroundSyncManager* sync_manager = sync_context->background_sync_manager(); sync_manager->GetRegistration( service_worker_id, tag, SYNC_ONE_SHOT, base::Bind(&OneShotPendingDidGetSyncRegistration, callback)); } void OneShotPendingOnIOThread( const scoped_refptr sync_context, const scoped_refptr sw_context, const std::string& tag, const GURL& url, const base::Callback& callback) { sw_context->FindRegistrationForDocument( url, base::Bind(&OneShotPendingDidGetSWRegistration, sync_context, tag, callback)); } class BackgroundSyncBrowserTest : public ContentBrowserTest { public: BackgroundSyncBrowserTest() {} ~BackgroundSyncBrowserTest() override {} void SetUp() override { NetworkChangeNotifier::SetTestNotificationsOnly(true); #if defined(OS_CHROMEOS) // ChromeOS's NetworkChangeNotifier doesn't get created in // content_browsertests, so make one now. net::NetworkChangeNotifier::CreateMock(); #endif ContentBrowserTest::SetUp(); } void SetIncognitoMode(bool incognito) { shell_ = incognito ? CreateOffTheRecordBrowser() : shell(); } 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::SpawnedTestServer( net::SpawnedTestServer::TYPE_HTTPS, net::BaseTestServer::SSLOptions( net::BaseTestServer::SSLOptions::CERT_OK), base::FilePath(FILE_PATH_LITERAL("content/test/data/")))); ASSERT_TRUE(https_server_->Start()); SetOnline(true); SetIncognitoMode(false); 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(shell_->web_contents(), script, result); } void SetOnline(bool online); // Returns true if the one-shot sync with tag is currently pending. Fails // (assertion failure) if the tag isn't registered. bool OneShotPending(const std::string& tag); bool PopConsole(const std::string& expected_msg); bool RegisterServiceWorker(); bool RegisterOneShot(const std::string& tag); bool GetRegistrationOneShot(const std::string& tag); bool GetRegistrationsOneShot(const std::vector& expected_tags); bool CompleteDelayedOneShot(); bool RejectDelayedOneShot(); private: scoped_ptr https_server_; Shell* shell_ = nullptr; DISALLOW_COPY_AND_ASSIGN(BackgroundSyncBrowserTest); }; void BackgroundSyncBrowserTest::SetOnline(bool online) { if (online) { NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests( NetworkChangeNotifier::CONNECTION_WIFI); } else { NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests( NetworkChangeNotifier::CONNECTION_NONE); } base::RunLoop().RunUntilIdle(); } bool BackgroundSyncBrowserTest::OneShotPending(const std::string& tag) { bool is_pending; base::RunLoop run_loop; StoragePartition* storage = BrowserContext::GetDefaultStoragePartition( shell_->web_contents()->GetBrowserContext()); BackgroundSyncContext* sync_context = storage->GetBackgroundSyncContext(); ServiceWorkerContextWrapper* service_worker_context = static_cast( storage->GetServiceWorkerContext()); base::Callback callback = base::Bind(&OneShotPendingCallback, run_loop.QuitClosure(), base::ThreadTaskRunnerHandle::Get(), &is_pending); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&OneShotPendingOnIOThread, make_scoped_refptr(sync_context), make_scoped_refptr(service_worker_context), tag, https_server_->GetURL(kDefaultTestURL), callback)); run_loop.Run(); return is_pending; } bool BackgroundSyncBrowserTest::PopConsole(const std::string& expected_msg) { std::string script_result; EXPECT_TRUE(RunScript("resultQueue.pop()", &script_result)); 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::RegisterOneShot(const std::string& tag) { std::string script_result; EXPECT_TRUE( RunScript(BuildScriptString("registerOneShot", tag), &script_result)); return script_result == BuildExpectedResult(tag, "registered"); } bool BackgroundSyncBrowserTest::GetRegistrationOneShot(const std::string& tag) { std::string script_result; EXPECT_TRUE(RunScript(BuildScriptString("getRegistrationOneShot", tag), &script_result)); return script_result == BuildExpectedResult(tag, "found"); } bool BackgroundSyncBrowserTest::GetRegistrationsOneShot( const std::vector& expected_tags) { std::string script_result; EXPECT_TRUE(RunScript("getRegistrationsOneShot()", &script_result)); EXPECT_TRUE(base::StartsWith(script_result, kSuccessfulOperationPrefix, base::CompareCase::INSENSITIVE_ASCII)); script_result = script_result.substr(strlen(kSuccessfulOperationPrefix)); std::vector result_tags = base::SplitString( script_result, ",", 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::CompleteDelayedOneShot() { std::string script_result; EXPECT_TRUE(RunScript("completeDelayedOneShot()", &script_result)); return script_result == BuildExpectedResult("delay", "completing"); } bool BackgroundSyncBrowserTest::RejectDelayedOneShot() { std::string script_result; EXPECT_TRUE(RunScript("rejectDelayedOneShot()", &script_result)); return script_result == BuildExpectedResult("delay", "rejecting"); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, OneShotFires) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. EXPECT_TRUE(RegisterOneShot("foo")); EXPECT_TRUE(PopConsole("foo fired")); EXPECT_FALSE(GetRegistrationOneShot("foo")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, OneShotDelaysForNetwork) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. // Prevent firing by going offline. SetOnline(false); EXPECT_TRUE(RegisterOneShot("foo")); EXPECT_TRUE(GetRegistrationOneShot("foo")); EXPECT_TRUE(OneShotPending("foo")); // Resume firing by going online. SetOnline(true); EXPECT_TRUE(PopConsole("foo fired")); EXPECT_FALSE(GetRegistrationOneShot("foo")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, WaitUntil) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. SetOnline(true); EXPECT_TRUE(RegisterOneShot("delay")); // Verify that it is firing. EXPECT_TRUE(GetRegistrationOneShot("delay")); EXPECT_FALSE(OneShotPending("delay")); // Complete the task. EXPECT_TRUE(CompleteDelayedOneShot()); EXPECT_TRUE(PopConsole("ok - delay completed")); // Verify that it finished firing. // TODO(jkarlin): Use registration.done to verify that the event actually // completed successfully. EXPECT_FALSE(GetRegistrationOneShot("delay")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, WaitUntilReject) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. SetOnline(true); EXPECT_TRUE(RegisterOneShot("delay")); // Verify that it is firing. EXPECT_TRUE(GetRegistrationOneShot("delay")); EXPECT_FALSE(OneShotPending("delay")); // Complete the task. EXPECT_TRUE(RejectDelayedOneShot()); EXPECT_TRUE(PopConsole("ok - delay rejected")); // Since the event failed the registration should still be there. // TODO(jkarlin): Use registration.done to verify that the event actually // failed. EXPECT_TRUE(GetRegistrationOneShot("delay")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, Incognito) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. SetOnline(false); EXPECT_TRUE(RegisterOneShot("normal")); EXPECT_TRUE(OneShotPending("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). SetOnline(false); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. EXPECT_FALSE(GetRegistrationOneShot("normal")); EXPECT_TRUE(RegisterOneShot("incognito")); EXPECT_TRUE(OneShotPending("incognito")); // Switch back and make sure the registration is still there. SetIncognitoMode(false); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Should be controlled. EXPECT_TRUE(GetRegistrationOneShot("normal")); EXPECT_FALSE(GetRegistrationOneShot("incognito")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, GetRegistrations) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. std::vector registered_tags; EXPECT_TRUE(GetRegistrationsOneShot(registered_tags)); SetOnline(false); registered_tags.push_back("foo"); registered_tags.push_back("bar"); for (const std::string& tag : registered_tags) EXPECT_TRUE(RegisterOneShot(tag)); EXPECT_TRUE(GetRegistrationsOneShot(registered_tags)); } } // namespace } // namespace content