diff options
author | shashishekhar@chromium.org <shashishekhar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-10 07:43:37 +0000 |
---|---|---|
committer | shashishekhar@chromium.org <shashishekhar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-10 07:43:37 +0000 |
commit | 83bfe12526275b95775024d77940d56dc0484939 (patch) | |
tree | b3f9fb0ab7c07839aff7a3286b8859f79f4d4e8a | |
parent | d30312c50388d4eb51a34c5e168f7abf5feb257c (diff) | |
download | chromium_src-83bfe12526275b95775024d77940d56dc0484939.zip chromium_src-83bfe12526275b95775024d77940d56dc0484939.tar.gz chromium_src-83bfe12526275b95775024d77940d56dc0484939.tar.bz2 |
Add an observer for article updates.
Define an observer that allows clients to listen to article updates.
In a subsequent CL, I will hook the webui to observe article changes.
BUG=288015
Review URL: https://codereview.chromium.org/100463005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@239703 0039d316-1c4b-4281-b951-d872f2087c98
8 files changed, 199 insertions, 3 deletions
diff --git a/components/dom_distiller.gypi b/components/dom_distiller.gypi index ddc7b2e..1b6e6b4 100644 --- a/components/dom_distiller.gypi +++ b/components/dom_distiller.gypi @@ -76,6 +76,7 @@ 'dom_distiller/core/dom_distiller_database.h', 'dom_distiller/core/dom_distiller_model.cc', 'dom_distiller/core/dom_distiller_model.h', + 'dom_distiller/core/dom_distiller_observer.h', 'dom_distiller/core/dom_distiller_service.cc', 'dom_distiller/core/dom_distiller_service.h', 'dom_distiller/core/dom_distiller_store.cc', diff --git a/components/dom_distiller/core/dom_distiller_observer.h b/components/dom_distiller/core/dom_distiller_observer.h new file mode 100644 index 0000000..4a99f69 --- /dev/null +++ b/components/dom_distiller/core/dom_distiller_observer.h @@ -0,0 +1,39 @@ +// Copyright 2013 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. + +#ifndef COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_OBSERVER_H_ +#define COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_OBSERVER_H_ + +#include <vector> +#include "components/dom_distiller/core/article_entry.h" + +namespace dom_distiller { + +// Provides notifications for any mutations to entries of the reading list. +class DomDistillerObserver { + public: + // An update to an article entry. + struct ArticleUpdate { + enum UpdateType { + ADD, + UPDATE, + REMOVE + }; + std::string entry_id; + UpdateType update_type; + }; + + virtual void ArticleEntriesUpdated( + const std::vector<ArticleUpdate>& updates) = 0; + + protected: + DomDistillerObserver() {} + virtual ~DomDistillerObserver() {} + + DISALLOW_COPY_AND_ASSIGN(DomDistillerObserver); +}; + +} // namespace dom_distiller + +#endif // COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_OBSERVER_H_ diff --git a/components/dom_distiller/core/dom_distiller_service.cc b/components/dom_distiller/core/dom_distiller_service.cc index fb2b380..35f8203 100644 --- a/components/dom_distiller/core/dom_distiller_service.cc +++ b/components/dom_distiller/core/dom_distiller_service.cc @@ -144,4 +144,14 @@ void DomDistillerService::AddDistilledPageToList(const ArticleEntry& entry, store_->AddEntry(entry); } +void DomDistillerService::AddObserver(DomDistillerObserver* observer) { + DCHECK(observer); + store_->AddObserver(observer); +} + +void DomDistillerService::RemoveObserver(DomDistillerObserver* observer) { + DCHECK(observer); + store_->RemoveObserver(observer); +} + } // namespace dom_distiller diff --git a/components/dom_distiller/core/dom_distiller_service.h b/components/dom_distiller/core/dom_distiller_service.h index a75ba70..fc27d35 100644 --- a/components/dom_distiller/core/dom_distiller_service.h +++ b/components/dom_distiller/core/dom_distiller_service.h @@ -20,10 +20,11 @@ namespace dom_distiller { class DistilledPageProto; class DistillerFactory; +class DomDistillerObserver; class DomDistillerStoreInterface; class TaskTracker; -class ViewRequestDelegate; class ViewerHandle; +class ViewRequestDelegate; // Provide a view of the article list and ways of interacting with it. class DomDistillerService { @@ -55,6 +56,9 @@ class DomDistillerService { scoped_ptr<ViewerHandle> ViewUrl(ViewRequestDelegate* delegate, const GURL& url); + void AddObserver(DomDistillerObserver* observer); + void RemoveObserver(DomDistillerObserver* observer); + private: void CancelTask(TaskTracker* task); void AddDistilledPageToList(const ArticleEntry& entry, diff --git a/components/dom_distiller/core/dom_distiller_service_unittest.cc b/components/dom_distiller/core/dom_distiller_service_unittest.cc index 6770b3f..a017eab 100644 --- a/components/dom_distiller/core/dom_distiller_service_unittest.cc +++ b/components/dom_distiller/core/dom_distiller_service_unittest.cc @@ -91,6 +91,9 @@ class FakeDomDistillerStore : public DomDistillerStoreInterface { return NULL; } + virtual void AddObserver(DomDistillerObserver* observer) OVERRIDE {} + virtual void RemoveObserver(DomDistillerObserver* observer) OVERRIDE {} + private: DomDistillerModel model_; }; diff --git a/components/dom_distiller/core/dom_distiller_store.cc b/components/dom_distiller/core/dom_distiller_store.cc index 72d2470..ef585d0 100644 --- a/components/dom_distiller/core/dom_distiller_store.cc +++ b/components/dom_distiller/core/dom_distiller_store.cc @@ -121,6 +121,14 @@ bool DomDistillerStore::RemoveEntry(const ArticleEntry& entry) { return true; } +void DomDistillerStore::AddObserver(DomDistillerObserver* observer) { + observers_.AddObserver(observer); +} + +void DomDistillerStore::RemoveObserver(DomDistillerObserver* observer) { + observers_.RemoveObserver(observer); +} + std::vector<ArticleEntry> DomDistillerStore::GetEntries() const { return model_.GetEntries(); } @@ -168,11 +176,45 @@ SyncError DomDistillerStore::ProcessSyncChanges( return SyncError(); } +void DomDistillerStore::NotifyObservers(const syncer::SyncChangeList& changes) { + if (observers_.might_have_observers() && changes.size() > 0) { + std::vector<DomDistillerObserver::ArticleUpdate> article_changes; + for (SyncChangeList::const_iterator it = changes.begin(); + it != changes.end(); + ++it) { + DomDistillerObserver::ArticleUpdate article_update; + switch (it->change_type()) { + case SyncChange::ACTION_ADD: + article_update.update_type = DomDistillerObserver::ArticleUpdate::ADD; + break; + case SyncChange::ACTION_UPDATE: + article_update.update_type = + DomDistillerObserver::ArticleUpdate::UPDATE; + break; + case SyncChange::ACTION_DELETE: + article_update.update_type = + DomDistillerObserver::ArticleUpdate::REMOVE; + break; + case SyncChange::ACTION_INVALID: + NOTREACHED(); + break; + } + const ArticleEntry& entry = GetEntryFromChange(*it); + article_update.entry_id = entry.entry_id(); + article_changes.push_back(article_update); + } + FOR_EACH_OBSERVER(DomDistillerObserver, + observers_, + ArticleEntriesUpdated(article_changes)); + } +} + void DomDistillerStore::ApplyChangesToModel( const SyncChangeList& changes, SyncChangeList* changes_applied, SyncChangeList* changes_missing) { model_.ApplyChangesToModel(changes, changes_applied, changes_missing); + NotifyObservers(*changes_applied); } void DomDistillerStore::OnDatabaseInit(bool success) { diff --git a/components/dom_distiller/core/dom_distiller_store.h b/components/dom_distiller/core/dom_distiller_store.h index d61b451..4328853 100644 --- a/components/dom_distiller/core/dom_distiller_store.h +++ b/components/dom_distiller/core/dom_distiller_store.h @@ -9,9 +9,11 @@ #include "base/containers/hash_tables.h" #include "base/memory/weak_ptr.h" +#include "base/observer_list.h" #include "components/dom_distiller/core/article_entry.h" #include "components/dom_distiller/core/dom_distiller_database.h" #include "components/dom_distiller/core/dom_distiller_model.h" +#include "components/dom_distiller/core/dom_distiller_observer.h" #include "sync/api/sync_change.h" #include "sync/api/sync_data.h" #include "sync/api/sync_error.h" @@ -47,8 +49,9 @@ class DomDistillerStoreInterface { // Gets a copy of all the current entries. virtual std::vector<ArticleEntry> GetEntries() const = 0; - // TODO(cjhopman): This should have a way to observe changes to the underlying - // model. + virtual void AddObserver(DomDistillerObserver* observer) = 0; + + virtual void RemoveObserver(DomDistillerObserver* observer) = 0; }; // Implements syncing/storing of DomDistiller entries. This keeps three @@ -91,6 +94,8 @@ class DomDistillerStore : public syncer::SyncableService, ArticleEntry* entry) OVERRIDE; virtual bool GetEntryByUrl(const GURL& url, ArticleEntry* entry) OVERRIDE; virtual std::vector<ArticleEntry> GetEntries() const OVERRIDE; + virtual void AddObserver(DomDistillerObserver* observer) OVERRIDE; + virtual void RemoveObserver(DomDistillerObserver* observer) OVERRIDE; // syncer::SyncableService implementation. virtual syncer::SyncMergeResult MergeDataAndStartSyncing( @@ -131,10 +136,13 @@ class DomDistillerStore : public syncer::SyncableService, syncer::SyncChangeList* changes_applied, syncer::SyncChangeList* changes_missing); + void NotifyObservers(const syncer::SyncChangeList& changes); + scoped_ptr<syncer::SyncChangeProcessor> sync_processor_; scoped_ptr<syncer::SyncErrorFactory> error_factory_; scoped_ptr<DomDistillerDatabaseInterface> database_; bool database_loaded_; + ObserverList<DomDistillerObserver> observers_; DomDistillerModel model_; diff --git a/components/dom_distiller/core/dom_distiller_store_unittest.cc b/components/dom_distiller/core/dom_distiller_store_unittest.cc index b1da8a0..09ea61b 100644 --- a/components/dom_distiller/core/dom_distiller_store_unittest.cc +++ b/components/dom_distiller/core/dom_distiller_store_unittest.cc @@ -192,6 +192,35 @@ ArticleEntry GetSampleEntry(int id) { return entries[id % 9]; } +class FakeDistillerObserver : public DomDistillerObserver { + public: + MOCK_METHOD1(ArticleEntriesUpdated, void(const std::vector<ArticleUpdate>&)); + virtual ~FakeDistillerObserver() {} +}; + +MATCHER_P(AreUpdatesEqual, expected_updates, "") { + if (arg.size() != expected_updates.size()) + return false; + std::vector<DomDistillerObserver::ArticleUpdate>::const_iterator expected, + actual; + for (expected = expected_updates.begin(), actual = arg.begin(); + expected != expected_updates.end(); + ++expected, ++actual) { + if (expected->entry_id != actual->entry_id) { + *result_listener << " Mismatched entry id. Expected: " + << expected->entry_id << " actual: " << actual->entry_id; + return false; + } + if (expected->update_type != actual->update_type) { + *result_listener << " Mismatched update. Expected: " + << expected->update_type + << " actual: " << actual->update_type; + return false; + } + } + return true; +} + } // namespace class DomDistillerStoreTest : public testing::Test { @@ -488,4 +517,64 @@ TEST_F(DomDistillerStoreTest, TestSyncMergeWithSecondDomDistillerStore) { EXPECT_TRUE(AreEntriesEqual(other_store->GetEntries(), expected_model)); } +TEST_F(DomDistillerStoreTest, TestObserver) { + CreateStore(); + FakeDistillerObserver observer; + store_->AddObserver(&observer); + fake_db_->InitCallback(true); + fake_db_->LoadCallback(true); + std::vector<DomDistillerObserver::ArticleUpdate> expected_updates; + DomDistillerObserver::ArticleUpdate update; + update.entry_id = GetSampleEntry(0).entry_id(); + update.update_type = DomDistillerObserver::ArticleUpdate::ADD; + expected_updates.push_back(update); + EXPECT_CALL(observer, + ArticleEntriesUpdated(AreUpdatesEqual(expected_updates))); + store_->AddEntry(GetSampleEntry(0)); + + expected_updates.clear(); + update.entry_id = GetSampleEntry(1).entry_id(); + update.update_type = DomDistillerObserver::ArticleUpdate::ADD; + expected_updates.push_back(update); + EXPECT_CALL(observer, + ArticleEntriesUpdated(AreUpdatesEqual(expected_updates))); + store_->AddEntry(GetSampleEntry(1)); + + expected_updates.clear(); + update.entry_id = GetSampleEntry(0).entry_id(); + update.update_type = DomDistillerObserver::ArticleUpdate::REMOVE; + expected_updates.clear(); + expected_updates.push_back(update); + EXPECT_CALL(observer, + ArticleEntriesUpdated(AreUpdatesEqual(expected_updates))); + store_->RemoveEntry(GetSampleEntry(0)); + + // Add entry_id = 3 and update entry_id = 1. + expected_updates.clear(); + SyncDataList change_data; + change_data.push_back(CreateSyncData(GetSampleEntry(3))); + ArticleEntry updated_entry(GetSampleEntry(1)); + updated_entry.set_title("changed_title"); + change_data.push_back(CreateSyncData(updated_entry)); + update.entry_id = GetSampleEntry(3).entry_id(); + update.update_type = DomDistillerObserver::ArticleUpdate::ADD; + expected_updates.push_back(update); + update.entry_id = GetSampleEntry(1).entry_id(); + update.update_type = DomDistillerObserver::ArticleUpdate::UPDATE; + expected_updates.push_back(update); + + EXPECT_CALL(observer, + ArticleEntriesUpdated(AreUpdatesEqual(expected_updates))); + + FakeSyncErrorFactory* fake_error_factory = new FakeSyncErrorFactory(); + EntryMap fake_model; + FakeSyncChangeProcessor* fake_sync_change_processor = + new FakeSyncChangeProcessor(&fake_model); + store_->MergeDataAndStartSyncing( + kDomDistillerModelType, + change_data, + make_scoped_ptr<SyncChangeProcessor>(fake_sync_change_processor), + make_scoped_ptr<SyncErrorFactory>(fake_error_factory)); +} + } // namespace dom_distiller |