diff options
Diffstat (limited to 'content/browser')
11 files changed, 584 insertions, 9 deletions
diff --git a/content/browser/background_sync/background_sync_browsertest.cc b/content/browser/background_sync/background_sync_browsertest.cc index 9fe51e5..47fd6e0 100644 --- a/content/browser/background_sync/background_sync_browsertest.cc +++ b/content/browser/background_sync/background_sync_browsertest.cc @@ -163,6 +163,9 @@ class BackgroundSyncBrowserTest : public ContentBrowserTest { bool GetRegistrationsOneShot(const std::vector<std::string>& expected_tags); bool CompleteDelayedOneShot(); bool RejectDelayedOneShot(); + bool NotifyWhenDoneOneShot(const std::string& tag); + bool NotifyWhenDoneImmediateOneShot(const std::string& expected_msg); + bool StoreRegistrationOneShot(const std::string& tag); private: scoped_ptr<net::SpawnedTestServer> https_server_; @@ -276,6 +279,27 @@ bool BackgroundSyncBrowserTest::RejectDelayedOneShot() { return script_result == BuildExpectedResult("delay", "rejecting"); } +bool BackgroundSyncBrowserTest::NotifyWhenDoneOneShot(const std::string& tag) { + EXPECT_TRUE(content::ExecuteScript( + shell_->web_contents(), BuildScriptString("notifyWhenDoneOneShot", tag))); + return PopConsole(BuildExpectedResult(tag, "done")); +} + +bool BackgroundSyncBrowserTest::NotifyWhenDoneImmediateOneShot( + const std::string& expected_msg) { + std::string script_result; + EXPECT_TRUE(RunScript("notifyWhenDoneImmediateOneShot()", &script_result)); + return script_result == expected_msg; +} + +bool BackgroundSyncBrowserTest::StoreRegistrationOneShot( + const std::string& tag) { + std::string script_result; + EXPECT_TRUE( + RunScript(BuildScriptString("storeRegistration", tag), &script_result)); + return script_result == BuildExpectedResult(tag, "stored"); +} + IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, OneShotFires) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. @@ -421,6 +445,78 @@ IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, UnregisterMidSync) { EXPECT_TRUE(PopConsole("ok - unregister completed")); } +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, CallDoneBeforeSyncSucceeds) { + EXPECT_TRUE(RegisterServiceWorker()); + EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. + + SetOnline(false); + EXPECT_TRUE(RegisterOneShot("foo")); + EXPECT_TRUE(NotifyWhenDoneOneShot("foo")); + + SetOnline(true); + // The ordering of PopConsole messages tells us that the event fired + // before done resolved. + EXPECT_TRUE(PopConsole("foo fired")); + EXPECT_TRUE(PopConsole("foo done result: true")); +} + +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, CallDoneBeforeSyncFails) { + EXPECT_TRUE(RegisterServiceWorker()); + EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. + + SetOnline(true); + EXPECT_TRUE(RegisterOneShot("delay")); + EXPECT_FALSE(OneShotPending("delay")); + EXPECT_TRUE(NotifyWhenDoneOneShot("delay")); + + EXPECT_TRUE(RejectDelayedOneShot()); + // The ordering of PopConsole messages tells us that the event fired + // before done resolved. + EXPECT_TRUE(PopConsole("ok - delay rejected")); + EXPECT_TRUE(PopConsole("delay done result: false")); +} + +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, CallDoneAfterSyncSuceeds) { + EXPECT_TRUE(RegisterServiceWorker()); + EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. + + SetOnline(false); + EXPECT_TRUE(RegisterOneShot("foo")); + EXPECT_TRUE(StoreRegistrationOneShot("foo")); + + SetOnline(true); + EXPECT_TRUE(PopConsole("foo fired")); + EXPECT_FALSE(GetRegistrationOneShot("foo")); + EXPECT_TRUE(NotifyWhenDoneImmediateOneShot("ok - foo result: true")); +} + +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, + CallDoneAfterSyncUnregistered) { + EXPECT_TRUE(RegisterServiceWorker()); + EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. + + SetOnline(false); + EXPECT_TRUE(RegisterOneShot("foo")); + EXPECT_TRUE(StoreRegistrationOneShot("foo")); + EXPECT_TRUE(UnregisterOneShot("foo")); + EXPECT_FALSE(GetRegistrationOneShot("foo")); + EXPECT_TRUE(NotifyWhenDoneImmediateOneShot("ok - foo result: false")); +} + +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, CallDoneAfterSyncFails) { + EXPECT_TRUE(RegisterServiceWorker()); + EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. + + SetOnline(true); + EXPECT_TRUE(RegisterOneShot("delay")); + EXPECT_FALSE(OneShotPending("delay")); + EXPECT_TRUE(StoreRegistrationOneShot("delay")); + + EXPECT_TRUE(RejectDelayedOneShot()); + EXPECT_TRUE(PopConsole("ok - delay rejected")); + EXPECT_TRUE(NotifyWhenDoneImmediateOneShot("ok - delay result: false")); +} + } // namespace } // namespace content diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc index 938a87b..3e28bf3 100644 --- a/content/browser/background_sync/background_sync_manager.cc +++ b/content/browser/background_sync/background_sync_manager.cc @@ -703,7 +703,7 @@ void BackgroundSyncManager::UnregisterImpl( return; } - const RefCountedRegistration* existing_registration = + RefCountedRegistration* existing_registration = LookupActiveRegistration(sw_registration_id, registration_key); if (!existing_registration || @@ -715,6 +715,24 @@ void BackgroundSyncManager::UnregisterImpl( return; } + DCHECK(!existing_registration->value()->HasCompleted()); + + bool firing = existing_registration->value()->sync_state() == + BACKGROUND_SYNC_STATE_FIRING || + existing_registration->value()->sync_state() == + BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING; + + existing_registration->value()->set_sync_state( + firing ? BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING + : BACKGROUND_SYNC_STATE_UNREGISTERED); + + if (!firing) { + // If the registration is currently firing then wait to run + // RunDoneCallbacks until after it has finished as it might + // change state to SUCCESS first. + existing_registration->value()->RunDoneCallbacks(); + } + RemoveActiveRegistration(sw_registration_id, registration_key); StoreRegistrations(sw_registration_id, @@ -754,6 +772,69 @@ void BackgroundSyncManager::UnregisterDidStore(int64 sw_registration_id, FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_OK)); } +void BackgroundSyncManager::NotifyWhenDone( + BackgroundSyncRegistrationHandle::HandleId handle_id, + const StatusAndStateCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (disabled_) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_STORAGE_ERROR, + BACKGROUND_SYNC_STATE_FAILED)); + return; + } + + scoped_ptr<BackgroundSyncRegistrationHandle> registration_handle = + DuplicateRegistrationHandle(handle_id); + + op_scheduler_.ScheduleOperation( + base::Bind(&BackgroundSyncManager::NotifyWhenDoneImpl, + weak_ptr_factory_.GetWeakPtr(), + base::Passed(registration_handle.Pass()), callback)); +} + +void BackgroundSyncManager::NotifyWhenDoneImpl( + scoped_ptr<BackgroundSyncRegistrationHandle> registration_handle, + const StatusAndStateCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK_EQ(SYNC_ONE_SHOT, registration_handle->options()->periodicity); + + if (disabled_) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_STORAGE_ERROR, + BACKGROUND_SYNC_STATE_FAILED)); + return; + } + + if (!registration_handle->registration()->HasCompleted()) { + registration_handle->registration()->AddDoneCallback( + base::Bind(&BackgroundSyncManager::NotifyWhenDoneDidFinish, + weak_ptr_factory_.GetWeakPtr(), callback)); + op_scheduler_.CompleteOperationAndRunNext(); + return; + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_OK, + registration_handle->sync_state())); + op_scheduler_.CompleteOperationAndRunNext(); +} + +void BackgroundSyncManager::NotifyWhenDoneDidFinish( + const StatusAndStateCallback& callback, + BackgroundSyncState sync_state) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (disabled_) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_STORAGE_ERROR, + BACKGROUND_SYNC_STATE_FAILED)); + return; + } + + callback.Run(BACKGROUND_SYNC_STATUS_OK, sync_state); +} + void BackgroundSyncManager::GetRegistrationImpl( int64 sw_registration_id, const RegistrationKey& registration_key, @@ -1025,10 +1106,19 @@ void BackgroundSyncManager::EventCompleteImpl( if (registration->options()->periodicity == SYNC_ONE_SHOT) { if (status_code != SERVICE_WORKER_OK) { - // TODO(jkarlin) Fire the sync event on the next page load controlled by + // TODO(jkarlin): Insert retry logic here. Be sure to check if the state + // is UNREGISTERED_WHILE_FIRING first. If so then set the state to failed + // if it was already out of retry attempts otherwise keep the state as + // unregistered. Then call RunDoneCallbacks(); (crbug.com/501838) + // TODO(jkarlin): Fire the sync event on the next page load controlled + // by // this registration. (crbug.com/479665) registration->set_sync_state(BACKGROUND_SYNC_STATE_FAILED); + registration->RunDoneCallbacks(); } else { + registration->set_sync_state(BACKGROUND_SYNC_STATE_SUCCESS); + registration->RunDoneCallbacks(); + RegistrationKey key(*registration); // Remove the registration if it's still active. RefCountedRegistration* active_registration = diff --git a/content/browser/background_sync/background_sync_manager.h b/content/browser/background_sync/background_sync_manager.h index 673f41f..285f68b 100644 --- a/content/browser/background_sync/background_sync_manager.h +++ b/content/browser/background_sync/background_sync_manager.h @@ -21,6 +21,7 @@ #include "content/browser/cache_storage/cache_storage_scheduler.h" #include "content/browser/service_worker/service_worker_context_observer.h" #include "content/browser/service_worker/service_worker_storage.h" +#include "content/common/background_sync_service.mojom.h" #include "content/common/content_export.h" #include "content/common/service_worker/service_worker_status_code.h" #include "url/gurl.h" @@ -53,6 +54,8 @@ class CONTENT_EXPORT BackgroundSyncManager using StatusAndRegistrationCallback = base::Callback<void(BackgroundSyncStatus, scoped_ptr<BackgroundSyncRegistrationHandle>)>; + using StatusAndStateCallback = + base::Callback<void(BackgroundSyncStatus, BackgroundSyncState)>; using StatusAndRegistrationsCallback = base::Callback<void( BackgroundSyncStatus, scoped_ptr<ScopedVector<BackgroundSyncRegistrationHandle>>)>; @@ -242,6 +245,17 @@ class CONTENT_EXPORT BackgroundSyncManager const StatusCallback& callback, ServiceWorkerStatusCode status); + // NotifyWhenDone and its callbacks. See + // BackgroundSyncRegistrationHandle::NotifyWhenDone for detailed + // documentation. + void NotifyWhenDone(BackgroundSyncRegistrationHandle::HandleId handle_id, + const StatusAndStateCallback& callback); + void NotifyWhenDoneImpl( + scoped_ptr<BackgroundSyncRegistrationHandle> registration_handle, + const StatusAndStateCallback& callback); + void NotifyWhenDoneDidFinish(const StatusAndStateCallback& callback, + BackgroundSyncState status); + // GetRegistration callbacks void GetRegistrationImpl(int64 sw_registration_id, const RegistrationKey& registration_key, diff --git a/content/browser/background_sync/background_sync_manager_unittest.cc b/content/browser/background_sync/background_sync_manager_unittest.cc index d073b06..d8cd693 100644 --- a/content/browser/background_sync/background_sync_manager_unittest.cc +++ b/content/browser/background_sync/background_sync_manager_unittest.cc @@ -82,6 +82,16 @@ void OneShotDelayedCallback( *out_callback = callback; } +void NotifyWhenDoneCallback(bool* was_called, + BackgroundSyncStatus* out_status, + BackgroundSyncState* out_state, + BackgroundSyncStatus status, + BackgroundSyncState state) { + *was_called = true; + *out_status = status; + *out_state = state; +} + class TestPowerSource : public base::PowerMonitorSource { public: void GeneratePowerStateEvent(bool on_battery_power) { @@ -1023,6 +1033,218 @@ TEST_F(BackgroundSyncManagerTest, OneShotFiresOnRegistration) { EXPECT_FALSE(GetRegistration(sync_options_1_)); } +TEST_F(BackgroundSyncManagerTest, NotifyWhenDoneAfterEventSuccess) { + InitSyncEventTest(); + + EXPECT_TRUE(Register(sync_options_1_)); + EXPECT_EQ(1, sync_events_called_); + + bool notify_done_called = false; + BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK; + BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS; + callback_registration_handle_->NotifyWhenDone(base::Bind( + &NotifyWhenDoneCallback, ¬ify_done_called, &status, &sync_state)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(notify_done_called); + EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status); + EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, sync_state); +} + +TEST_F(BackgroundSyncManagerTest, NotifyWhenDoneBeforeEventSuccess) { + InitDelayedSyncEventTest(); + + RegisterAndVerifySyncEventDelayed(sync_options_1_); + + bool notify_done_called = false; + BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK; + BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS; + callback_registration_handle_->NotifyWhenDone(base::Bind( + &NotifyWhenDoneCallback, ¬ify_done_called, &status, &sync_state)); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(notify_done_called); + + // Finish firing the event. + sync_fired_callback_.Run(SERVICE_WORKER_OK); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, sync_events_called_); + EXPECT_TRUE(notify_done_called); + EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status); + EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, sync_state); +} + +TEST_F(BackgroundSyncManagerTest, + NotifyWhenDoneBeforeUnregisteredEventSuccess) { + InitDelayedSyncEventTest(); + + RegisterAndVerifySyncEventDelayed(sync_options_1_); + + bool notify_done_called = false; + BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK; + BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS; + callback_registration_handle_->NotifyWhenDone(base::Bind( + &NotifyWhenDoneCallback, ¬ify_done_called, &status, &sync_state)); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(notify_done_called); + + // Unregistering should set the state to UNREGISTERED but done shouldn't + // be called until the event finishes firing, at which point its state should + // be SUCCESS. + EXPECT_TRUE(Unregister(callback_registration_handle_.get())); + EXPECT_FALSE(GetRegistration(sync_options_1_)); + + // Finish firing the event. + sync_fired_callback_.Run(SERVICE_WORKER_OK); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, sync_events_called_); + EXPECT_TRUE(notify_done_called); + EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status); + EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, sync_state); +} + +TEST_F(BackgroundSyncManagerTest, + NotifyWhenDoneBeforeUnregisteredEventFailure) { + InitDelayedSyncEventTest(); + + RegisterAndVerifySyncEventDelayed(sync_options_1_); + + bool notify_done_called = false; + BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK; + BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS; + callback_registration_handle_->NotifyWhenDone(base::Bind( + &NotifyWhenDoneCallback, ¬ify_done_called, &status, &sync_state)); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(notify_done_called); + + // Unregistering should set the state to UNREGISTERED but done shouldn't + // be called until the event finishes firing, at which point its state should + // be FAILED. + EXPECT_TRUE(Unregister(callback_registration_handle_.get())); + EXPECT_FALSE(GetRegistration(sync_options_1_)); + + // Finish firing the event. + sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, sync_events_called_); + EXPECT_TRUE(notify_done_called); + EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status); + EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, sync_state); +} + +TEST_F(BackgroundSyncManagerTest, NotifyWhenDoneBeforeUnregisteredEventFires) { + InitSyncEventTest(); + + SetNetwork(net::NetworkChangeNotifier::CONNECTION_NONE); + EXPECT_TRUE(Register(sync_options_1_)); + EXPECT_TRUE(Unregister(callback_registration_handle_.get())); + + bool notify_done_called = false; + BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK; + BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS; + callback_registration_handle_->NotifyWhenDone(base::Bind( + &NotifyWhenDoneCallback, ¬ify_done_called, &status, &sync_state)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(notify_done_called); + EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status); + EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, sync_state); +} + +TEST_F(BackgroundSyncManagerTest, + NotifyWhenDoneBeforeEventSuccessDroppedHandle) { + InitDelayedSyncEventTest(); + + RegisterAndVerifySyncEventDelayed(sync_options_1_); + + bool notify_done_called = false; + BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK; + BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS; + + callback_registration_handle_->NotifyWhenDone(base::Bind( + &NotifyWhenDoneCallback, ¬ify_done_called, &status, &sync_state)); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(notify_done_called); + + // Drop the client's handle to the registration before the event fires, ensure + // that the done callback is still run. + callback_registration_handle_ = nullptr; + + // Finish firing the event. + sync_fired_callback_.Run(SERVICE_WORKER_OK); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, sync_events_called_); + EXPECT_TRUE(notify_done_called); + EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status); + EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, sync_state); +} + +TEST_F(BackgroundSyncManagerTest, NotifyWhenDoneAfterEventFailure) { + InitFailedSyncEventTest(); + + EXPECT_TRUE(Register(sync_options_1_)); + EXPECT_EQ(1, sync_events_called_); + + bool notify_done_called = false; + BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK; + BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS; + callback_registration_handle_->NotifyWhenDone(base::Bind( + &NotifyWhenDoneCallback, ¬ify_done_called, &status, &sync_state)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(notify_done_called); + EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status); + EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, sync_state); +} + +TEST_F(BackgroundSyncManagerTest, NotifyWhenDoneBeforeEventFailure) { + InitDelayedSyncEventTest(); + + RegisterAndVerifySyncEventDelayed(sync_options_1_); + + bool notify_done_called = false; + BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK; + BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS; + callback_registration_handle_->NotifyWhenDone(base::Bind( + &NotifyWhenDoneCallback, ¬ify_done_called, &status, &sync_state)); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(notify_done_called); + + // Finish firing the event. + sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(notify_done_called); + EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status); + EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, sync_state); +} + +TEST_F(BackgroundSyncManagerTest, NotifyWhenDoneAfterUnregistered) { + EXPECT_TRUE(Register(sync_options_1_)); + EXPECT_TRUE(Unregister(callback_registration_handle_.get())); + + bool notify_done_called = false; + BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK; + BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS; + callback_registration_handle_->NotifyWhenDone(base::Bind( + &NotifyWhenDoneCallback, ¬ify_done_called, &status, &sync_state)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(notify_done_called); + EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status); + EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, sync_state); +} + +TEST_F(BackgroundSyncManagerTest, NotifyWhenDoneBeforeUnregistered) { + Register(sync_options_1_); + bool notify_done_called = false; + BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK; + BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS; + callback_registration_handle_->NotifyWhenDone(base::Bind( + &NotifyWhenDoneCallback, ¬ify_done_called, &status, &sync_state)); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(notify_done_called); + + EXPECT_TRUE(Unregister(callback_registration_handle_.get())); + EXPECT_TRUE(notify_done_called); + EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status); + EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, sync_state); +} + // TODO(jkarlin): Change this to a periodic test as one-shots can't be power // dependent according to spec. TEST_F(BackgroundSyncManagerTest, OneShotFiresOnPowerChange) { diff --git a/content/browser/background_sync/background_sync_registration.cc b/content/browser/background_sync/background_sync_registration.cc index d6a122c..30bbf9e 100644 --- a/content/browser/background_sync/background_sync_registration.cc +++ b/content/browser/background_sync/background_sync_registration.cc @@ -4,6 +4,12 @@ #include "content/browser/background_sync/background_sync_registration.h" +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" + namespace content { const BackgroundSyncRegistration::RegistrationId @@ -12,6 +18,9 @@ const BackgroundSyncRegistration::RegistrationId const BackgroundSyncRegistration::RegistrationId BackgroundSyncRegistration::kInitialId = 0; +BackgroundSyncRegistration::BackgroundSyncRegistration() = default; +BackgroundSyncRegistration::~BackgroundSyncRegistration() = default; + bool BackgroundSyncRegistration::Equals( const BackgroundSyncRegistration& other) const { return options_.Equals(other.options_); @@ -21,4 +30,36 @@ bool BackgroundSyncRegistration::IsValid() const { return id_ != kInvalidRegistrationId; } +void BackgroundSyncRegistration::AddDoneCallback( + const StateCallback& callback) { + DCHECK(!HasCompleted()); + notify_done_callbacks_.push_back(callback); +} + +void BackgroundSyncRegistration::RunDoneCallbacks() { + DCHECK(HasCompleted()); + + for (auto& callback : notify_done_callbacks_) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, sync_state_)); + } + + notify_done_callbacks_.clear(); +} + +bool BackgroundSyncRegistration::HasCompleted() const { + switch (sync_state_) { + case BACKGROUND_SYNC_STATE_PENDING: + case BACKGROUND_SYNC_STATE_FIRING: + case BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING: + return false; + case BACKGROUND_SYNC_STATE_FAILED: + case BACKGROUND_SYNC_STATE_SUCCESS: + case BACKGROUND_SYNC_STATE_UNREGISTERED: + return true; + } + NOTREACHED(); + return false; +} + } // namespace content diff --git a/content/browser/background_sync/background_sync_registration.h b/content/browser/background_sync/background_sync_registration.h index 91f5ded..741c888 100644 --- a/content/browser/background_sync/background_sync_registration.h +++ b/content/browser/background_sync/background_sync_registration.h @@ -5,6 +5,9 @@ #ifndef CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_REGISTRATION_H_ #define CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_REGISTRATION_H_ +#include <list> + +#include "base/callback.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "content/browser/background_sync/background_sync.pb.h" @@ -18,13 +21,18 @@ namespace content { class CONTENT_EXPORT BackgroundSyncRegistration { public: using RegistrationId = int64_t; + using StateCallback = base::Callback<void(BackgroundSyncState)>; + static const RegistrationId kInitialId; - BackgroundSyncRegistration() = default; - ~BackgroundSyncRegistration() = default; + BackgroundSyncRegistration(); + ~BackgroundSyncRegistration(); bool Equals(const BackgroundSyncRegistration& other) const; bool IsValid() const; + void AddDoneCallback(const StateCallback& callback); + void RunDoneCallbacks(); + bool HasCompleted() const; const BackgroundSyncRegistrationOptions* options() const { return &options_; } BackgroundSyncRegistrationOptions* options() { return &options_; } @@ -42,6 +50,8 @@ class CONTENT_EXPORT BackgroundSyncRegistration { RegistrationId id_ = kInvalidRegistrationId; BackgroundSyncState sync_state_ = BACKGROUND_SYNC_STATE_PENDING; + std::list<StateCallback> notify_done_callbacks_; + DISALLOW_COPY_AND_ASSIGN(BackgroundSyncRegistration); }; diff --git a/content/browser/background_sync/background_sync_registration_handle.cc b/content/browser/background_sync/background_sync_registration_handle.cc index ff89cca..4e5e8e8 100644 --- a/content/browser/background_sync/background_sync_registration_handle.cc +++ b/content/browser/background_sync/background_sync_registration_handle.cc @@ -26,6 +26,15 @@ void BackgroundSyncRegistrationHandle::Unregister( sw_registration_id, options()->periodicity, handle_id_, callback); } +void BackgroundSyncRegistrationHandle::NotifyWhenDone( + const StatusAndStateCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(IsValid()); + DCHECK(background_sync_manager_); + + background_sync_manager_->NotifyWhenDone(handle_id_, callback); +} + bool BackgroundSyncRegistrationHandle::IsValid() const { return registration_ != nullptr; } diff --git a/content/browser/background_sync/background_sync_registration_handle.h b/content/browser/background_sync/background_sync_registration_handle.h index 564bd58..f2915d1 100644 --- a/content/browser/background_sync/background_sync_registration_handle.h +++ b/content/browser/background_sync/background_sync_registration_handle.h @@ -10,6 +10,7 @@ #include "base/memory/weak_ptr.h" #include "content/browser/background_sync/background_sync_registration.h" #include "content/browser/background_sync/background_sync_status.h" +#include "content/common/background_sync_service.mojom.h" #include "content/common/content_export.h" namespace content { @@ -25,6 +26,8 @@ class CONTENT_EXPORT BackgroundSyncRegistrationHandle { public: using HandleId = int; using StatusCallback = base::Callback<void(BackgroundSyncStatus)>; + using StatusAndStateCallback = + base::Callback<void(BackgroundSyncStatus, BackgroundSyncState)>; ~BackgroundSyncRegistrationHandle(); @@ -42,6 +45,15 @@ class CONTENT_EXPORT BackgroundSyncRegistrationHandle { // with BACKGROUND_SYNC_STATUS_OK if it succeeds. void Unregister(int64_t service_worker_id, const StatusCallback& callback); + // Runs |callback| when the registration associated with |handle_id| + // completes.The provided status is BACKGROUND_SYNC_STATUS_OK if the operation + // succeeded. The provided state is BACKGROUND_SYNC_STATE_SUCCESS on success, + // BACKGRUOND_SYNC_STATE_FAILED on final failure, and + // BACKGROUND_SYNC_STATE_UNREGISTERED if the registration was unregistered + // before it could complete. NotifyWhenDone should only be called for + // SYNC_ONE_SHOT registrations. + void NotifyWhenDone(const StatusAndStateCallback& callback); + // Returns true if the handle is backed by a BackgroundSyncRegistration in the // BackgroundSyncManager. bool IsValid() const; diff --git a/content/browser/background_sync/background_sync_service_impl.cc b/content/browser/background_sync/background_sync_service_impl.cc index 9f51f9a..b8c03df 100644 --- a/content/browser/background_sync/background_sync_service_impl.cc +++ b/content/browser/background_sync/background_sync_service_impl.cc @@ -227,6 +227,23 @@ void BackgroundSyncServiceImpl::ReleaseRegistration( active_handles_.Remove(handle_id); } +void BackgroundSyncServiceImpl::NotifyWhenDone( + BackgroundSyncRegistrationHandle::HandleId handle_id, + const NotifyWhenDoneCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + BackgroundSyncRegistrationHandle* registration = + active_handles_.Lookup(handle_id); + if (!registration) { + callback.Run(BACKGROUND_SYNC_ERROR_NOT_ALLOWED, + BACKGROUND_SYNC_STATE_FAILED); + return; + } + + registration->NotifyWhenDone( + base::Bind(&BackgroundSyncServiceImpl::OnNotifyWhenDoneResult, + weak_ptr_factory_.GetWeakPtr(), callback)); +} + void BackgroundSyncServiceImpl::OnRegisterResult( const RegisterCallback& callback, BackgroundSyncStatus status, @@ -271,4 +288,12 @@ void BackgroundSyncServiceImpl::OnGetRegistrationsResult( mojo_registrations.Pass()); } +void BackgroundSyncServiceImpl::OnNotifyWhenDoneResult( + const NotifyWhenDoneCallback& callback, + BackgroundSyncStatus status, + BackgroundSyncState sync_state) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + callback.Run(static_cast<content::BackgroundSyncError>(status), sync_state); +} + } // namespace content diff --git a/content/browser/background_sync/background_sync_service_impl.h b/content/browser/background_sync/background_sync_service_impl.h index 7e17b7f..f00c668 100644 --- a/content/browser/background_sync/background_sync_service_impl.h +++ b/content/browser/background_sync/background_sync_service_impl.h @@ -53,6 +53,8 @@ class CONTENT_EXPORT BackgroundSyncServiceImpl const DuplicateRegistrationHandleCallback& callback) override; void ReleaseRegistration( BackgroundSyncRegistrationHandle::HandleId handle_id) override; + void NotifyWhenDone(BackgroundSyncRegistrationHandle::HandleId handle_id, + const NotifyWhenDoneCallback& callback) override; void OnRegisterResult(const RegisterCallback& callback, BackgroundSyncStatus status, @@ -63,6 +65,9 @@ class CONTENT_EXPORT BackgroundSyncServiceImpl const GetRegistrationsCallback& callback, BackgroundSyncStatus status, scoped_ptr<ScopedVector<BackgroundSyncRegistrationHandle>> result); + void OnNotifyWhenDoneResult(const NotifyWhenDoneCallback& callback, + BackgroundSyncStatus status, + BackgroundSyncState sync_state); // Called when an error is detected on binding_. void OnConnectionError(); diff --git a/content/browser/background_sync/background_sync_service_impl_unittest.cc b/content/browser/background_sync/background_sync_service_impl_unittest.cc index 346bdd1..ca2e45c 100644 --- a/content/browser/background_sync/background_sync_service_impl_unittest.cc +++ b/content/browser/background_sync/background_sync_service_impl_unittest.cc @@ -59,6 +59,16 @@ void ErrorAndRegistrationCallback(bool* called, *out_registration = registration.Clone(); } +void ErrorAndStateCallback(bool* called, + BackgroundSyncError* out_error, + BackgroundSyncState* out_state, + BackgroundSyncError error, + BackgroundSyncState state) { + *called = true; + *out_error = error; + *out_state = state; +} + void ErrorCallback(bool* called, BackgroundSyncError* out_error, BackgroundSyncError error) { @@ -197,11 +207,11 @@ class BackgroundSyncServiceImplTest : public testing::Test { } void UnregisterOneShot( - SyncRegistrationPtr sync, + int32 handle_id, const BackgroundSyncService::UnregisterCallback& callback) { service_impl_->Unregister( BackgroundSyncPeriodicity::BACKGROUND_SYNC_PERIODICITY_ONE_SHOT, - sync->handle_id, sw_registration_id_, callback); + handle_id, sw_registration_id_, callback); base::RunLoop().RunUntilIdle(); } @@ -222,6 +232,13 @@ class BackgroundSyncServiceImplTest : public testing::Test { base::RunLoop().RunUntilIdle(); } + void NotifyWhenDone( + int32 handle_id, + const BackgroundSyncService::NotifyWhenDoneCallback& callback) { + service_impl_->NotifyWhenDone(handle_id, callback); + base::RunLoop().RunUntilIdle(); + } + scoped_ptr<TestBrowserThreadBundle> thread_bundle_; scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_; scoped_ptr<EmbeddedWorkerTestHelper> embedded_worker_helper_; @@ -292,7 +309,7 @@ TEST_F(BackgroundSyncServiceImplTest, Unregister) { BackgroundSyncError unregister_error; SyncRegistrationPtr reg; UnregisterOneShot( - default_sync_registration_.Clone(), + default_sync_registration_->handle_id, base::Bind(&ErrorCallback, &unregister_called, &unregister_error)); EXPECT_TRUE(unregister_called); EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NOT_ALLOWED, @@ -310,8 +327,9 @@ TEST_F(BackgroundSyncServiceImplTest, UnregisterWithRegisteredSync) { ®ister_error, ®)); EXPECT_TRUE(register_called); EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NONE, register_error); - UnregisterOneShot(reg.Pass(), base::Bind(&ErrorCallback, &unregister_called, - &unregister_error)); + UnregisterOneShot( + reg->handle_id, + base::Bind(&ErrorCallback, &unregister_called, &unregister_error)); EXPECT_TRUE(unregister_called); EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NONE, unregister_error); } @@ -379,4 +397,37 @@ TEST_F(BackgroundSyncServiceImplTest, GetRegistrationsWithRegisteredSync) { EXPECT_EQ(1UL, array_size); } +TEST_F(BackgroundSyncServiceImplTest, NotifyWhenDone) { + // Register a sync event. + bool register_called = false; + BackgroundSyncError register_error; + SyncRegistrationPtr reg; + RegisterOneShot(default_sync_registration_.Clone(), + base::Bind(&ErrorAndRegistrationCallback, ®ister_called, + ®ister_error, ®)); + EXPECT_TRUE(register_called); + EXPECT_EQ(BACKGROUND_SYNC_ERROR_NONE, register_error); + + // Unregister it. + bool unregister_called = false; + BackgroundSyncError unregister_error; + UnregisterOneShot( + reg->handle_id, + base::Bind(&ErrorCallback, &unregister_called, &unregister_error)); + EXPECT_TRUE(unregister_called); + EXPECT_EQ(BACKGROUND_SYNC_ERROR_NONE, unregister_error); + + // Call NotifyWhenDone and verify that it calls back with unregistered. + bool notify_done_called = false; + BackgroundSyncError notify_done_error = BACKGROUND_SYNC_ERROR_NONE; + BackgroundSyncState notify_done_sync_state = BACKGROUND_SYNC_STATE_SUCCESS; + + NotifyWhenDone(reg->handle_id, + base::Bind(&ErrorAndStateCallback, ¬ify_done_called, + ¬ify_done_error, ¬ify_done_sync_state)); + EXPECT_TRUE(notify_done_called); + EXPECT_EQ(BACKGROUND_SYNC_ERROR_NONE, notify_done_error); + EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, notify_done_sync_state); +} + } // namespace content |