| // 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 <set> |
| #include <string> |
| #include <vector> |
| |
| #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<base::SingleThreadTaskRunner>& task_runner, |
| bool* result_out, |
| bool result) { |
| *result_out = result; |
| task_runner->PostTask(FROM_HERE, quit); |
| } |
| |
| void OneShotPendingDidGetSyncRegistration( |
| const base::Callback<void(bool)>& 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<BackgroundSyncContext> sync_context, |
| const std::string& tag, |
| const base::Callback<void(bool)>& callback, |
| ServiceWorkerStatusCode status, |
| const scoped_refptr<ServiceWorkerRegistration>& 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<BackgroundSyncContext> sync_context, |
| const scoped_refptr<ServiceWorkerContextWrapper> sw_context, |
| const std::string& tag, |
| const GURL& url, |
| const base::Callback<void(bool)>& 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<std::string>& expected_tags); |
| bool CompleteDelayedOneShot(); |
| bool RejectDelayedOneShot(); |
| |
| private: |
| scoped_ptr<net::SpawnedTestServer> 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<ServiceWorkerContextWrapper*>( |
| storage->GetServiceWorkerContext()); |
| |
| base::Callback<void(bool)> 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<std::string>& 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<std::string> result_tags = base::SplitString( |
| script_result, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); |
| |
| return std::set<std::string>(expected_tags.begin(), expected_tags.end()) == |
| std::set<std::string>(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<std::string> 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 |