Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(372)

Unified Diff: chrome/browser/extensions/api/media_galleries_private/gallery_watch_manager.cc

Issue 11535008: Implement mediaGalleriesPrivate api to notify extensions about gallery changed events. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed review comments Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/extensions/api/media_galleries_private/gallery_watch_manager.cc
diff --git a/chrome/browser/extensions/api/media_galleries_private/gallery_watch_manager.cc b/chrome/browser/extensions/api/media_galleries_private/gallery_watch_manager.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c925562a7e813ba6b367b55645c802949afc327d
--- /dev/null
+++ b/chrome/browser/extensions/api/media_galleries_private/gallery_watch_manager.cc
@@ -0,0 +1,397 @@
+// Copyright (c) 2012 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.
+//
+// GalleryWatchManager implementation.
+
+#include "chrome/browser/extensions/api/media_galleries_private/gallery_watch_manager.h"
+
+#include <list>
+#include <set>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path_watcher.h"
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time.h"
+#include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.h"
+#include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api_factory.h"
+#include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_event_router.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace extensions {
+
+class GalleryWatchManager;
+
+namespace {
+
+using content::BrowserThread;
+
+// Map to keep track of profile specific GalleryWatchManager objects.
+// Key: Profile*.
+// Value: GalleryWatchManager*.
+// This map owns the GalleryWatchManager object.
+typedef std::map<const Profile*, extensions::GalleryWatchManager*>
+ WatchManagerMap;
+WatchManagerMap* g_gallery_watch_managers = NULL;
+
+// Dispatches the gallery changed event on the UI thread.
+void SendGalleryChangedEventOnUIThread(
+ const Profile* profile,
+ const std::string& gallery_id,
+ const std::set<std::string>& extension_ids) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(profile);
+ MediaGalleriesPrivateEventRouter* router =
+ MediaGalleriesPrivateAPIFactory::GetForProfile(
+ const_cast<Profile*>(profile))->event_router();
Lei Zhang 2012/12/18 01:45:16 GalleryWatchManager shouldn't promise to use a con
kmadhusu 2012/12/18 21:32:39 Done.
+ if (!router)
+ return;
+ router->OnGalleryChanged(gallery_id, extension_ids);
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// GalleryFilePathWatcher //
+///////////////////////////////////////////////////////////////////////////////
+
+// Gallery file path watcher delegate to handle the gallery change
Lei Zhang 2012/12/18 01:45:16 From this comment, I still have trouble understand
kmadhusu 2012/12/18 21:32:39 How about this? // This class does a recursive wat
Lei Zhang 2012/12/19 01:03:47 Your comment still does not answer the question of
kmadhusu 2012/12/19 21:55:55 sure. Done.
+// notifications. This class manages all the extension usage and forwards
+// the gallery change notifications to the extensions. This class lives on
+// the FILE thread. This class is instantiated per gallery watch path and does
+// recursive watches.
+class GalleryFilePathWatcher : public base::RefCounted<GalleryFilePathWatcher> {
+ public:
+ GalleryFilePathWatcher(const Profile* profile,
+ const std::string& gallery_id,
+ const FilePath& path,
+ const std::string& extension_id);
+
+ // Adds the extension specified by the |extension_id| to the
+ // ExtensionWatchInfoMap and initiate the watch operation.
Lei Zhang 2012/12/18 01:45:16 What does "initiate the watch operation" mean? How
kmadhusu 2012/12/18 21:32:39 This function adds an extension reference to the w
+ void AddExtension(const std::string& extension_id);
+
+ // Cancels the watch for the extension specified by the |extension_id|.
+ void RemoveExtension(const std::string& extension_id);
+
+ // Handles the extension unloaded/uninstalled/destroyed event.
+ void OnExtensionDestroyed(const std::string& extension_id);
+
+ // Sets up the watch operation for the specified |gallery_path_|. On
+ // success, returns true.
+ bool SetupWatch();
+
+ // Removes all the extension references when the browser profile is in
+ // shutdown mode.
+ void RemoveAllWatchReferences();
+
+ private:
+ friend class base::RefCounted<GalleryFilePathWatcher>;
+
+ // Keeps track of extension watch details.
+ struct ExtensionWatchInfo {
Lei Zhang 2012/12/18 01:45:16 Are you keeping track of how many times a particul
kmadhusu 2012/12/18 21:32:39 Yes
+ ExtensionWatchInfo();
+
+ // Number of watches in this extension, e.g "3"
+ unsigned int watch_count;
+
+ // Used to manage the gallery changed events.
+ base::Time last_gallery_changed_event;
+ };
+
+ typedef std::map<std::string, ExtensionWatchInfo> ExtensionWatchInfoMap;
+
+ // Private because GalleryFilePathWatcher is ref-counted.
+ virtual ~GalleryFilePathWatcher();
+
+ // FilePathWatcher callback.
+ void OnFilePathChanged(const FilePath& path, bool error);
+
+ // Remove the watch references for the extension specified by the
+ // |extension_id|.
+ void RemoveExtensionReferences(const std::string& extension_id);
+
+ // Current profile.
+ const Profile* profile_;
+
+ // The gallery identifier, e.g "1".
+ const std::string gallery_id_;
+
+ // The gallery file path watcher.
+ base::files::FilePathWatcher file_watcher_;
+
+ // The gallery file path, e.g "C:\My Pictures".
+ FilePath gallery_path_;
+
+ // Map to keep track of the extension and its corresponding watch count.
+ // Key: Extension identifier, e.g "qoueruoweuroiwueroiwujkshdf".
+ // Value: Watch information.
+ ExtensionWatchInfoMap extension_watch_info_map_;
+
+ // Used to provide a weak pointer to FilePathWatcher callback.
+ base::WeakPtrFactory<GalleryFilePathWatcher> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GalleryFilePathWatcher);
+};
+
+GalleryFilePathWatcher::GalleryFilePathWatcher(const Profile* profile,
+ const std::string& gallery_id,
+ const FilePath& path,
+ const std::string& extension_id)
+ : profile_(profile),
+ gallery_id_(gallery_id),
+ weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ gallery_path_ = path;
+ AddExtension(extension_id);
+}
+
+void GalleryFilePathWatcher::AddExtension(const std::string& extension_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ ExtensionWatchInfoMap::iterator it =
+ extension_watch_info_map_.find(extension_id);
+ if (it != extension_watch_info_map_.end()) {
+ it->second.watch_count++;
+ } else {
+ extension_watch_info_map_.insert(
+ ExtensionWatchInfoMap::value_type(extension_id, ExtensionWatchInfo()));
+ }
+ AddRef();
+}
+
+void GalleryFilePathWatcher::RemoveExtension(const std::string& extension_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ ExtensionWatchInfoMap::iterator it =
+ extension_watch_info_map_.find(extension_id);
+ if (it == extension_watch_info_map_.end())
+ return;
+ // If entry found - decrease it's count and remove if necessary
+ it->second.watch_count--;
+ if (0 == it->second.watch_count)
+ extension_watch_info_map_.erase(it);
+ Release();
+}
+
+void GalleryFilePathWatcher::OnExtensionDestroyed(
+ const std::string& extension_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ RemoveExtensionReferences(extension_id);
+}
+
+bool GalleryFilePathWatcher::SetupWatch() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ return file_watcher_.Watch(
+ gallery_path_, true,
+ base::Bind(&GalleryFilePathWatcher::OnFilePathChanged,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void GalleryFilePathWatcher::RemoveAllWatchReferences() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ std::set<std::string> extension_ids;
+ for (ExtensionWatchInfoMap::iterator iter = extension_watch_info_map_.begin();
+ iter != extension_watch_info_map_.end(); ++iter)
+ extension_ids.insert(iter->first);
+
+ for (std::set<std::string>::const_iterator it = extension_ids.begin();
+ it != extension_ids.end(); ++it)
+ RemoveExtensionReferences(*it);
+}
+
+GalleryFilePathWatcher::~GalleryFilePathWatcher() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+}
+
+GalleryFilePathWatcher::ExtensionWatchInfo::ExtensionWatchInfo()
+ : watch_count(1) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
Lei Zhang 2012/12/18 01:45:16 Structs should not do anything other can carry val
kmadhusu 2012/12/18 21:32:39 Done.
+}
+
+void GalleryFilePathWatcher::OnFilePathChanged(const FilePath& path,
+ bool error) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (error || (path != gallery_path_))
+ return;
+
+ std::set<std::string> extension_ids;
+ for (ExtensionWatchInfoMap::iterator iter = extension_watch_info_map_.begin();
+ iter != extension_watch_info_map_.end(); ++iter) {
+ if (!iter->second.last_gallery_changed_event.is_null()) {
+ // Ignore gallery change event if it is received too frequently.
+ // For example, when an user copies/deletes 1000 media files from a
+ // gallery, this callback is called 1000 times within a span of 10ms.
+ // GalleryWatchManager should not send 1000 gallery changed events to
+ // the watching extension.
+ const int kMinSecondsToIgnoreGalleryChangedEvent = 3;
+ base::TimeDelta diff =
+ base::Time::Now() - iter->second.last_gallery_changed_event;
+ if (diff.InSeconds() < kMinSecondsToIgnoreGalleryChangedEvent)
+ continue;
+ }
+ iter->second.last_gallery_changed_event = base::Time::Now();
+ extension_ids.insert(iter->first);
+ }
+ if (!extension_ids.empty()) {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::Bind(SendGalleryChangedEventOnUIThread, profile_, gallery_id_,
+ extension_ids));
+ }
+}
+
+void GalleryFilePathWatcher::RemoveExtensionReferences(
+ const std::string& extension_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ ExtensionWatchInfoMap::iterator it =
+ extension_watch_info_map_.find(extension_id);
+ if (it == extension_watch_info_map_.end())
+ return;
+ const ExtensionWatchInfo watch_info = it->second;
+ for (unsigned int i = 0; i < watch_info.watch_count; ++i)
+ Release();
+ extension_watch_info_map_.erase(it);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GalleryWatchManager //
+///////////////////////////////////////////////////////////////////////////////
+
+// static
+GalleryWatchManager* GalleryWatchManager::GetForProfile(
+ const Profile* profile) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(profile);
+ bool has_watch_manager = (g_gallery_watch_managers &&
+ GalleryWatchManager::HasForProfile(profile));
+ if (!g_gallery_watch_managers)
+ g_gallery_watch_managers = new WatchManagerMap;
+ if (!has_watch_manager)
+ (*g_gallery_watch_managers)[profile] = new GalleryWatchManager(profile);
+ return (*g_gallery_watch_managers)[profile];
+}
+
+// static
+bool GalleryWatchManager::HasForProfile(const Profile* profile) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(profile);
+ if (!g_gallery_watch_managers)
+ return false;
+ WatchManagerMap::const_iterator it = g_gallery_watch_managers->find(profile);
+ return (it != g_gallery_watch_managers->end());
+}
+
+// static
+void GalleryWatchManager::OnProfileShutdown(const Profile* profile) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(profile);
+ if (!g_gallery_watch_managers || g_gallery_watch_managers->empty())
+ return;
+ WatchManagerMap::iterator it = g_gallery_watch_managers->find(profile);
+ if (it == g_gallery_watch_managers->end())
+ return;
+ delete it->second;
+ g_gallery_watch_managers->erase(it);
+ if (g_gallery_watch_managers->empty())
+ delete g_gallery_watch_managers;
+}
+
+GalleryWatchManager::~GalleryWatchManager() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (!gallery_watchers_.empty()) {
+ // User closed the extension/browser without stoping the gallery watchers.
+ DeleteAllWatchers();
+ }
+ DCHECK(gallery_watchers_.empty());
+}
+
+bool GalleryWatchManager::StartGalleryWatch(
+ const std::string& gallery_id,
+ const FilePath& watch_path,
+ const std::string& extension_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ WatcherMap::const_iterator iter = gallery_watchers_.find(watch_path);
+ if (iter != gallery_watchers_.end()) {
+ // Already watched.
+ iter->second->AddExtension(extension_id);
+ return true;
+ }
+
+ // Need to add a new watcher.
+ scoped_refptr<GalleryFilePathWatcher> watch(
+ new GalleryFilePathWatcher(profile_, gallery_id, watch_path,
+ extension_id));
+ if (!watch->SetupWatch())
+ return false;
+ gallery_watchers_[watch_path] = watch;
+ return true;
+}
+
+void GalleryWatchManager::StopGalleryWatch(
+ const FilePath& watch_path,
+ const std::string& extension_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ WatcherMap::iterator iter = gallery_watchers_.find(watch_path);
+ if (iter == gallery_watchers_.end())
+ return;
+ // Remove the renderer process for this watch.
+ iter->second->RemoveExtension(extension_id);
+ if (iter->second->HasOneRef()) {
+ // There are no references other than the one |gallery_watchers_| holds.
+ iter->second = NULL;
+ gallery_watchers_.erase(iter);
+ }
+}
+
+void GalleryWatchManager::OnExtensionDestroyed(
+ const std::string& extension_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ std::list<FilePath> watchers_to_erase;
+ for (WatcherMap::iterator iter = gallery_watchers_.begin();
+ iter != gallery_watchers_.end(); ++iter) {
+ // Remove the renderer process for this watch.
+ iter->second->OnExtensionDestroyed(extension_id);
+ if (iter->second->HasOneRef()) {
+ // There are no references other than the one |gallery_watchers_| holds.
+ watchers_to_erase.push_back(iter->first);
+ }
+ }
+
+ for (std::list<FilePath>::const_iterator path = watchers_to_erase.begin();
+ path != watchers_to_erase.end(); ++path) {
+ WatcherMap::iterator iter = gallery_watchers_.find(*path);
+ DCHECK(iter != gallery_watchers_.end());
+ iter->second = NULL;
+ gallery_watchers_.erase(iter);
+ }
+}
+
+GalleryWatchManager::GalleryWatchManager(const Profile* profile)
+ : profile_(profile) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+}
+
+void GalleryWatchManager::DeleteAllWatchers() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ std::list<FilePath> watchers_to_erase;
+ for (WatcherMap::iterator iter = gallery_watchers_.begin();
+ iter != gallery_watchers_.end(); ++iter) {
+ iter->second->RemoveAllWatchReferences();
+ // Verify that there are no references other than the one
+ // |gallery_watchers_| holds.
+ DCHECK(iter->second->HasOneRef());
+ watchers_to_erase.push_back(iter->first);
+ }
+
+ for (std::list<FilePath>::const_iterator path = watchers_to_erase.begin();
+ path != watchers_to_erase.end(); ++path) {
+ WatcherMap::iterator iter = gallery_watchers_.find(*path);
+ DCHECK(iter != gallery_watchers_.end());
+ iter->second = NULL;
+ gallery_watchers_.erase(iter);
+ }
+}
+
+} // namespace extensions

Powered by Google App Engine
This is Rietveld 408576698