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

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: Disable SetupGalleryWatch browser test on ChromeOS Created 7 years, 11 months 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..651266ead09f7dc32528134c4074faa5ebed66fa
--- /dev/null
+++ b/chrome/browser/extensions/api/media_galleries_private/gallery_watch_manager.cc
@@ -0,0 +1,377 @@
+// 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/callback.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path_watcher.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/time.h"
+#include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_event_router.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace extensions {
+
+namespace {
+
+using content::BrowserThread;
+
+// Map to keep track of profile specific GalleryWatchManager objects.
+// Key: Profile identifier.
+// Value: GalleryWatchManager*.
+// This map owns the GalleryWatchManager object.
+typedef std::map<void*, extensions::GalleryWatchManager*> WatchManagerMap;
+WatchManagerMap* g_gallery_watch_managers = NULL;
+
+// Dispatches the gallery changed event on the UI thread.
+void SendGalleryChangedEventOnUIThread(
+ base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router,
+ chrome::MediaGalleryPrefId gallery_id,
+ const std::set<std::string>& extension_ids) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (event_router.get())
+ event_router->OnGalleryChanged(gallery_id, extension_ids);
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// GalleryWatchManager::GalleryFilePathWatcher //
+///////////////////////////////////////////////////////////////////////////////
+
+// This class does a recursive watch on the gallery file path and holds a list
+// of extensions that are watching the gallery. When there is a file system
+// activity within the gallery, GalleryFilePathWatcher notifies the interested
+// extensions. This class lives on the file thread.
+class GalleryWatchManager::GalleryFilePathWatcher
+ : public base::RefCounted<GalleryFilePathWatcher> {
+ public:
+ // |on_destroyed_callback| is called when the last GalleryFilePathWatcher
+ // reference goes away.
+ GalleryFilePathWatcher(
+ base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router,
+ chrome::MediaGalleryPrefId gallery_id,
+ const FilePath& path,
+ const std::string& extension_id,
+ const base::Closure& on_destroyed_callback);
+
+ // 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"
+ 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);
+
+ // Used to notify the interested extensions about the gallery changed event.
+ base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router_;
+
+ // The gallery identifier, e.g "1".
+ chrome::MediaGalleryPrefId gallery_id_;
+
+ // The gallery file path watcher.
+ base::files::FilePathWatcher file_watcher_;
+
+ // The gallery file path, e.g "C:\My Pictures".
+ FilePath gallery_path_;
+
+ // A callback to call when |this| object is destroyed.
+ base::Closure on_destroyed_callback_;
+
+ // 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);
+};
+
+GalleryWatchManager::GalleryFilePathWatcher::GalleryFilePathWatcher(
+ base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router,
+ chrome::MediaGalleryPrefId gallery_id,
+ const FilePath& path,
+ const std::string& extension_id,
+ const base::Closure& on_destroyed_callback)
+ : event_router_(event_router),
+ gallery_id_(gallery_id),
+ on_destroyed_callback_(on_destroyed_callback),
+ weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ gallery_path_ = path;
+ AddExtension(extension_id);
+}
+
+void GalleryWatchManager::GalleryFilePathWatcher::AddExtension(
+ const std::string& extension_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ extension_watch_info_map_[extension_id].watch_count++;
+ AddRef();
+}
+
+void GalleryWatchManager::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 GalleryWatchManager::GalleryFilePathWatcher::OnExtensionDestroyed(
+ const std::string& extension_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ RemoveExtensionReferences(extension_id);
+}
+
+bool GalleryWatchManager::GalleryFilePathWatcher::SetupWatch() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ return file_watcher_.Watch(
+ gallery_path_, true,
+ base::Bind(&GalleryFilePathWatcher::OnFilePathChanged,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void GalleryWatchManager::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);
+}
+
+GalleryWatchManager::GalleryFilePathWatcher::~GalleryFilePathWatcher() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ on_destroyed_callback_.Run();
+}
+
+GalleryWatchManager::GalleryFilePathWatcher::ExtensionWatchInfo::
+ExtensionWatchInfo()
+ : watch_count(0) {
+}
+
+void GalleryWatchManager::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, event_router_,
+ gallery_id_, extension_ids));
+ }
+}
+
+void GalleryWatchManager::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;
+ int watch_count = it->second.watch_count;
+ extension_watch_info_map_.erase(it);
+ for (int i = 0; i < watch_count; ++i)
+ Release();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GalleryWatchManager //
+///////////////////////////////////////////////////////////////////////////////
+
+// static
+GalleryWatchManager* GalleryWatchManager::GetForProfile(
+ void* profile_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(profile_id);
+ bool has_watch_manager = (g_gallery_watch_managers &&
+ GalleryWatchManager::HasForProfile(profile_id));
+ if (!g_gallery_watch_managers)
+ g_gallery_watch_managers = new WatchManagerMap;
+ if (!has_watch_manager)
+ (*g_gallery_watch_managers)[profile_id] = new GalleryWatchManager;
+ return (*g_gallery_watch_managers)[profile_id];
+}
+
+// static
+bool GalleryWatchManager::HasForProfile(void* profile_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(profile_id);
+ if (!g_gallery_watch_managers)
+ return false;
+ WatchManagerMap::const_iterator it =
+ g_gallery_watch_managers->find(profile_id);
+ return (it != g_gallery_watch_managers->end());
+}
+
+// static
+void GalleryWatchManager::OnProfileShutdown(void* profile_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(profile_id);
+ if (!g_gallery_watch_managers || g_gallery_watch_managers->empty())
+ return;
+ WatchManagerMap::iterator it = g_gallery_watch_managers->find(profile_id);
+ 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));
+ DeleteAllWatchers();
+}
+
+bool GalleryWatchManager::StartGalleryWatch(
+ chrome::MediaGalleryPrefId gallery_id,
+ const FilePath& watch_path,
+ const std::string& extension_id,
+ base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router) {
+ 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(
+ event_router, gallery_id, watch_path, extension_id,
+ base::Bind(&GalleryWatchManager::RemoveGalleryFilePathWatcherEntry,
+ base::Unretained(this),
+ watch_path)));
+ if (!watch->SetupWatch())
+ return false;
+ gallery_watchers_[watch_path] = watch.get();
+ 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);
+}
+
+void GalleryWatchManager::OnExtensionDestroyed(
+ const std::string& extension_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ std::list<FilePath> watchers_to_notify;
+ for (WatcherMap::iterator iter = gallery_watchers_.begin();
+ iter != gallery_watchers_.end(); ++iter)
+ watchers_to_notify.push_back(iter->first);
+
+ for (std::list<FilePath>::const_iterator path = watchers_to_notify.begin();
+ path != watchers_to_notify.end(); ++path) {
+ WatcherMap::iterator iter = gallery_watchers_.find(*path);
+ if (iter == gallery_watchers_.end())
+ continue;
+ iter->second->OnExtensionDestroyed(extension_id);
+ }
+}
+
+GalleryWatchManager::GalleryWatchManager() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+}
+
+void GalleryWatchManager::DeleteAllWatchers() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (gallery_watchers_.empty())
+ return;
+
+ for (WatcherMap::iterator iter = gallery_watchers_.begin();
+ iter != gallery_watchers_.end(); ++iter)
+ iter->second->RemoveAllWatchReferences();
+}
+
+void GalleryWatchManager::RemoveGalleryFilePathWatcherEntry(
+ const FilePath& watch_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ gallery_watchers_.erase(watch_path);
+}
+
+} // namespace extensions

Powered by Google App Engine
This is Rietveld 408576698