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

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..5f7aec2a92b5c39143ee441d10d14e85f182d37a
--- /dev/null
+++ b/chrome/browser/extensions/api/media_galleries_private/gallery_watch_manager.cc
@@ -0,0 +1,393 @@
+// 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<Profile*, extensions::GalleryWatchManager*>
+ WatchManagerMap;
+WatchManagerMap* g_gallery_watch_managers = NULL;
+
+// Dispatches the gallery changed event on the UI thread.
+void SendGalleryChangedEventOnUIThread(
+ Profile* profile,
+ uint64 gallery_id,
+ const std::set<std::string>& extension_ids) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(profile);
+ MediaGalleriesPrivateEventRouter* router =
+ MediaGalleriesPrivateAPIFactory::GetForProfile(profile)->event_router();
+ if (!router)
+ return;
+ router->OnGalleryChanged(gallery_id, extension_ids);
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// GalleryFilePathWatcher //
+///////////////////////////////////////////////////////////////////////////////
+
+// This class does a recursive watch on the gallery file path and sends
+// notifications to the extensions about the gallery changed event. This class
+// lives on the file thread.
+class GalleryFilePathWatcher : public base::RefCounted<GalleryFilePathWatcher> {
+ public:
+ GalleryFilePathWatcher(Profile* profile,
+ uint64 gallery_id,
+ const FilePath& path,
+ const std::string& extension_id);
+
+ // Adds the extension reference to the watched gallery.
+ void AddExtension(const std::string& extension_id);
+
+ // Removes the extension reference to the watched gallery.
+ 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 {
+ 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;
Lei Zhang 2012/12/19 01:03:47 So if you can have multiple instances of an extens
Lei Zhang 2012/12/19 01:13:25 Come to think of it, this is a very weird case. Ev
kmadhusu 2012/12/19 23:13:17 The chances of hitting this use case is very less.
+ };
+
+ 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.
+ Profile* profile_;
+
+ // The gallery identifier, e.g "1".
+ uint64 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(Profile* profile,
+ uint64 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) {
+}
+
+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(
+ 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(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(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()) {
Lei Zhang 2012/12/19 01:03:47 Put this in DeleteAllWatchers().
kmadhusu 2012/12/19 21:55:55 Done.
+ // User closed the extension/browser without stoping the gallery watchers.
Lei Zhang 2012/12/19 01:03:47 I don't find this comment useful. No user will eve
kmadhusu 2012/12/19 21:55:55 Removed.
+ DeleteAllWatchers();
+ }
+ DCHECK(gallery_watchers_.empty());
+}
+
+bool GalleryWatchManager::StartGalleryWatch(
+ uint64 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()) {
Lei Zhang 2012/12/19 01:03:47 HasOneRef() is rarely used in our code base. I thi
kmadhusu 2012/12/19 21:55:55 Done.
+ // 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(Profile* profile)
+ : profile_(profile) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(profile_);
+}
+
+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