Wrapping blocked filesystems into TabSpecificContentSettings

BUG=63703
TEST=TabSpecificContentSettings* in `unit_tests`


Review URL: http://codereview.chromium.org/6966036

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@87006 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 18492f9..6cf1f84 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7853,6 +7853,9 @@
       <message name="IDS_COOKIES_INDEXED_DBS" desc="Label for Indexed Databases (name of an HTML standard)">
         Indexed Databases
       </message>
+      <message name="IDS_COOKIES_FILE_SYSTEMS" desc="Label for the folder under which a list of file systems (name of an HTML standard) are displayed">
+        File Systems
+      </message>
       <message name="IDS_COOKIES_LAST_ACCESSED_LABEL" desc="The last access date label">
         Last accessed:
       </message>
diff --git a/chrome/browser/chrome_worker_message_filter.cc b/chrome/browser/chrome_worker_message_filter.cc
index 453ac5d..1b7fa2a7 100644
--- a/chrome/browser/chrome_worker_message_filter.cc
+++ b/chrome/browser/chrome_worker_message_filter.cc
@@ -47,7 +47,8 @@
 
   *result = content_setting != CONTENT_SETTING_BLOCK;
 
-  // Find the worker instance and forward the message to all attached documents.
+  // Record access to database for potential display in UI: Find the worker
+  // instance and forward the message to all attached documents.
   WorkerProcessHost::Instances::const_iterator i;
   for (i = process_->instances().begin(); i != process_->instances().end();
        ++i) {
@@ -71,9 +72,28 @@
 void ChromeWorkerMessageFilter::OnAllowFileSystem(int worker_route_id,
                                                   const GURL& url,
                                                   bool* result) {
-  // TODO(kinuko): Need to notify the UI thread to indicate that
-  // there's a blocked content.  See the above for inspiration.
   ContentSetting content_setting =
       host_content_settings_map_->GetCookieContentSetting(url, url, true);
+
   *result = content_setting != CONTENT_SETTING_BLOCK;
+
+  // Record access to file system for potential display in UI: Find the worker
+  // instance and forward the message to all attached documents.
+  WorkerProcessHost::Instances::const_iterator i;
+  for (i = process_->instances().begin(); i != process_->instances().end();
+       ++i) {
+    if (i->worker_route_id() != worker_route_id)
+      continue;
+    const WorkerDocumentSet::DocumentInfoSet& documents =
+        i->worker_document_set()->documents();
+    for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
+         documents.begin(); doc != documents.end(); ++doc) {
+      BrowserThread::PostTask(
+          BrowserThread::UI, FROM_HERE,
+          NewRunnableFunction(
+              &TabSpecificContentSettings::FileSystemAccessed,
+              doc->render_process_id(), doc->render_view_id(), url, !*result));
+    }
+    break;
+  }
 }
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.cc b/chrome/browser/content_settings/tab_specific_content_settings.cc
index e643bba..7449a6d40 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.cc
+++ b/chrome/browser/content_settings/tab_specific_content_settings.cc
@@ -10,6 +10,7 @@
 #include "base/utf_string_conversions.h"
 #include "chrome/browser/browsing_data_appcache_helper.h"
 #include "chrome/browser/browsing_data_database_helper.h"
+#include "chrome/browser/browsing_data_file_system_helper.h"
 #include "chrome/browser/browsing_data_indexed_db_helper.h"
 #include "chrome/browser/browsing_data_local_storage_helper.h"
 #include "chrome/browser/content_settings/content_settings_details.h"
@@ -24,6 +25,7 @@
 #include "content/common/notification_service.h"
 #include "content/common/view_messages.h"
 #include "net/base/cookie_monster.h"
+#include "webkit/fileapi/file_system_types.h"
 
 namespace {
 typedef std::list<TabSpecificContentSettings*> TabSpecificList;
@@ -35,6 +37,7 @@
   return cookies_->GetAllCookies().empty() &&
       appcaches_->empty() &&
       databases_->empty() &&
+      file_systems_->empty() &&
       indexed_dbs_->empty() &&
       local_storages_->empty() &&
       session_storages_->empty();
@@ -131,6 +134,15 @@
     settings->OnIndexedDBAccessed(url, description, blocked_by_policy);
 }
 
+void TabSpecificContentSettings::FileSystemAccessed(int render_process_id,
+                                                    int render_view_id,
+                                                    const GURL& url,
+                                                    bool blocked_by_policy) {
+  TabSpecificContentSettings* settings = Get(render_process_id, render_view_id);
+  if (settings)
+    settings->OnFileSystemAccessed(url, blocked_by_policy);
+}
+
 bool TabSpecificContentSettings::IsContentBlocked(
     ContentSettingsType content_type) const {
   DCHECK(content_type != CONTENT_SETTINGS_TYPE_GEOLOCATION)
@@ -270,7 +282,7 @@
     blocked_local_shared_objects_.indexed_dbs()->AddIndexedDB(
         url, description);
     OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES, std::string());
-  }else {
+  } else {
     allowed_local_shared_objects_.indexed_dbs()->AddIndexedDB(
         url, description);
     OnContentAccessed(CONTENT_SETTINGS_TYPE_COOKIES);
@@ -321,6 +333,19 @@
   }
 }
 
+void TabSpecificContentSettings::OnFileSystemAccessed(
+    const GURL& url,
+    bool blocked_by_policy) {
+  if (blocked_by_policy) {
+    blocked_local_shared_objects_.file_systems()->AddFileSystem(url,
+        fileapi::kFileSystemTypeTemporary, 0);
+    OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES, std::string());
+  } else {
+    allowed_local_shared_objects_.file_systems()->AddFileSystem(url,
+        fileapi::kFileSystemTypeTemporary, 0);
+    OnContentAccessed(CONTENT_SETTINGS_TYPE_COOKIES);
+  }
+}
 void TabSpecificContentSettings::OnGeolocationPermissionSet(
     const GURL& requesting_origin,
     bool allowed) {
@@ -454,6 +479,7 @@
     : cookies_(new net::CookieMonster(NULL, NULL)),
       appcaches_(new CannedBrowsingDataAppCacheHelper(profile)),
       databases_(new CannedBrowsingDataDatabaseHelper(profile)),
+      file_systems_(new CannedBrowsingDataFileSystemHelper(profile)),
       indexed_dbs_(new CannedBrowsingDataIndexedDBHelper(profile)),
       local_storages_(new CannedBrowsingDataLocalStorageHelper(profile)),
       session_storages_(new CannedBrowsingDataLocalStorageHelper(profile)) {
@@ -475,6 +501,7 @@
   cookies_->SetKeepExpiredCookies();
   appcaches_->Reset();
   databases_->Reset();
+  file_systems_->Reset();
   indexed_dbs_->Reset();
   local_storages_->Reset();
   session_storages_->Reset();
@@ -488,5 +515,6 @@
                               session_storages_->Clone(),
                               appcaches_->Clone(),
                               indexed_dbs_->Clone(),
+                              file_systems_->Clone(),
                               true);
 }
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.h b/chrome/browser/content_settings/tab_specific_content_settings.h
index f8924eac..c90c8e5 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.h
+++ b/chrome/browser/content_settings/tab_specific_content_settings.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
 
@@ -18,6 +18,7 @@
 
 class CannedBrowsingDataAppCacheHelper;
 class CannedBrowsingDataDatabaseHelper;
+class CannedBrowsingDataFileSystemHelper;
 class CannedBrowsingDataIndexedDBHelper;
 class CannedBrowsingDataLocalStorageHelper;
 class CookiesTreeModel;
@@ -34,7 +35,7 @@
 class TabSpecificContentSettings : public TabContentsObserver,
                                    public NotificationObserver {
  public:
-  TabSpecificContentSettings(TabContents* tab);
+  explicit TabSpecificContentSettings(TabContents* tab);
 
   virtual ~TabSpecificContentSettings();
 
@@ -95,6 +96,15 @@
                                 const string16& description,
                                 bool blocked_by_policy);
 
+  // Called when a specific file system in the current page was accessed.
+  // If access was blocked due to the user's content settings,
+  // |blocked_by_policy| should be true, and this function should invoke
+  // OnContentBlocked.
+  static void FileSystemAccessed(int render_process_id,
+                                 int render_view_id,
+                                 const GURL& url,
+                                 bool blocked_by_policy);
+
   // Resets the |content_blocked_| and |content_accessed_| arrays, except for
   // CONTENT_SETTINGS_TYPE_COOKIES related information.
   void ClearBlockedContentSettingsExceptForCookies();
@@ -171,6 +181,8 @@
                        const std::string& cookie_line,
                        const net::CookieOptions& options,
                        bool blocked_by_policy);
+  void OnFileSystemAccessed(const GURL& url,
+                            bool blocked_by_policy);
   void OnIndexedDBAccessed(const GURL& url,
                            const string16& description,
                            bool blocked_by_policy);
@@ -200,6 +212,9 @@
     CannedBrowsingDataDatabaseHelper* databases() const {
       return databases_;
     }
+    CannedBrowsingDataFileSystemHelper* file_systems() const {
+      return file_systems_;
+    }
     CannedBrowsingDataIndexedDBHelper* indexed_dbs() const {
       return indexed_dbs_;
     }
@@ -220,6 +235,7 @@
     scoped_refptr<net::CookieMonster> cookies_;
     scoped_refptr<CannedBrowsingDataAppCacheHelper> appcaches_;
     scoped_refptr<CannedBrowsingDataDatabaseHelper> databases_;
+    scoped_refptr<CannedBrowsingDataFileSystemHelper> file_systems_;
     scoped_refptr<CannedBrowsingDataIndexedDBHelper> indexed_dbs_;
     scoped_refptr<CannedBrowsingDataLocalStorageHelper> local_storages_;
     scoped_refptr<CannedBrowsingDataLocalStorageHelper> session_storages_;
diff --git a/chrome/browser/content_settings/tab_specific_content_settings_unittest.cc b/chrome/browser/content_settings/tab_specific_content_settings_unittest.cc
index d0e7553..777cc794 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings_unittest.cc
+++ b/chrome/browser/content_settings/tab_specific_content_settings_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
 
@@ -78,6 +78,20 @@
   EXPECT_FALSE(content_settings.IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
 }
 
+TEST_F(TabSpecificContentSettingsTest, BlockedFileSystems) {
+  TabSpecificContentSettings content_settings(contents());
+
+  // Access a file system.
+  content_settings.OnFileSystemAccessed(GURL("http://google.com"), false);
+  EXPECT_FALSE(
+      content_settings.IsContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES));
+
+  // Block access to a file system.
+  content_settings.OnFileSystemAccessed(GURL("http://google.com"), true);
+  EXPECT_TRUE(
+      content_settings.IsContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES));
+}
+
 TEST_F(TabSpecificContentSettingsTest, AllowedContent) {
   TabSpecificContentSettings content_settings(contents());
   net::CookieOptions options;
diff --git a/chrome/browser/cookies_tree_model.cc b/chrome/browser/cookies_tree_model.cc
index 2760059..695ea8b 100644
--- a/chrome/browser/cookies_tree_model.cc
+++ b/chrome/browser/cookies_tree_model.cc
@@ -68,7 +68,7 @@
 CookieTreeNode::DetailedInfo CookieTreeCookieNode::GetDetailedInfo() const {
   return DetailedInfo(parent()->parent()->GetTitle(),
                       DetailedInfo::TYPE_COOKIE,
-                      cookie_, NULL, NULL, NULL, NULL, NULL);
+                      cookie_, NULL, NULL, NULL, NULL, NULL, NULL);
 }
 
 namespace {
@@ -148,7 +148,7 @@
 CookieTreeNode::DetailedInfo CookieTreeAppCacheNode::GetDetailedInfo() const {
   return DetailedInfo(parent()->parent()->GetTitle(),
                       DetailedInfo::TYPE_APPCACHE,
-                      NULL, NULL, NULL, NULL, appcache_info_, NULL);
+                      NULL, NULL, NULL, NULL, appcache_info_, NULL, NULL);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -172,7 +172,7 @@
 CookieTreeNode::DetailedInfo CookieTreeDatabaseNode::GetDetailedInfo() const {
   return DetailedInfo(parent()->parent()->GetTitle(),
                       DetailedInfo::TYPE_DATABASE,
-                      NULL, database_info_, NULL, NULL, NULL, NULL);
+                      NULL, database_info_, NULL, NULL, NULL, NULL, NULL);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -198,7 +198,7 @@
 CookieTreeLocalStorageNode::GetDetailedInfo() const {
   return DetailedInfo(parent()->parent()->GetTitle(),
                       DetailedInfo::TYPE_LOCAL_STORAGE,
-                      NULL, NULL, local_storage_info_, NULL, NULL, NULL);
+                      NULL, NULL, local_storage_info_, NULL, NULL, NULL, NULL);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -219,7 +219,8 @@
 CookieTreeSessionStorageNode::GetDetailedInfo() const {
   return DetailedInfo(parent()->parent()->GetTitle(),
                       DetailedInfo::TYPE_SESSION_STORAGE,
-                      NULL, NULL, NULL, session_storage_info_, NULL, NULL);
+                      NULL, NULL, NULL, session_storage_info_, NULL, NULL,
+                      NULL);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -244,7 +245,30 @@
 CookieTreeNode::DetailedInfo CookieTreeIndexedDBNode::GetDetailedInfo() const {
   return DetailedInfo(parent()->parent()->GetTitle(),
                       DetailedInfo::TYPE_INDEXED_DB,
-                      NULL, NULL, NULL, NULL, NULL, indexed_db_info_);
+                      NULL, NULL, NULL, NULL, NULL, indexed_db_info_, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookieTreeFileSystemNode, public:
+
+CookieTreeFileSystemNode::CookieTreeFileSystemNode(
+    BrowsingDataFileSystemHelper::FileSystemInfo* file_system_info)
+    : CookieTreeNode(UTF8ToUTF16(
+          file_system_info->origin.spec())),
+      file_system_info_(file_system_info) {
+}
+
+CookieTreeFileSystemNode::~CookieTreeFileSystemNode() {}
+
+void CookieTreeFileSystemNode::DeleteStoredObjects() {
+  GetModel()->file_system_helper_->DeleteFileSystemOrigin(
+      file_system_info_->origin);
+}
+
+CookieTreeNode::DetailedInfo CookieTreeFileSystemNode::GetDetailedInfo() const {
+  return DetailedInfo(parent()->parent()->GetTitle(),
+                      DetailedInfo::TYPE_FILE_SYSTEM,
+                      NULL, NULL, NULL, NULL, NULL, NULL, file_system_info_);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -286,7 +310,7 @@
 CookieTreeNode::DetailedInfo CookieTreeRootNode::GetDetailedInfo() const {
   return DetailedInfo(string16(),
                       DetailedInfo::TYPE_ROOT,
-                      NULL, NULL, NULL, NULL, NULL, NULL);
+                      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -306,6 +330,7 @@
       session_storages_child_(NULL),
       appcaches_child_(NULL),
       indexed_dbs_child_(NULL),
+      file_systems_child_(NULL),
       url_(url) {}
 
 CookieTreeOriginNode::~CookieTreeOriginNode() {}
@@ -313,7 +338,7 @@
 CookieTreeNode::DetailedInfo CookieTreeOriginNode::GetDetailedInfo() const {
   return DetailedInfo(GetTitle(),
                       DetailedInfo::TYPE_ORIGIN,
-                      NULL, NULL, NULL, NULL, NULL, NULL);
+                      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 }
 
 CookieTreeCookiesNode* CookieTreeOriginNode::GetOrCreateCookiesNode() {
@@ -366,6 +391,14 @@
   return indexed_dbs_child_;
 }
 
+CookieTreeFileSystemsNode* CookieTreeOriginNode::GetOrCreateFileSystemsNode() {
+  if (file_systems_child_)
+    return file_systems_child_;
+  file_systems_child_ = new CookieTreeFileSystemsNode;
+  AddChildSortedByTitle(file_systems_child_);
+  return file_systems_child_;
+}
+
 void CookieTreeOriginNode::CreateContentException(
     HostContentSettingsMap* content_settings, ContentSetting setting) const {
   if (CanCreateContentException()) {
@@ -393,7 +426,7 @@
 CookieTreeNode::DetailedInfo CookieTreeCookiesNode::GetDetailedInfo() const {
   return DetailedInfo(parent()->GetTitle(),
                       DetailedInfo::TYPE_COOKIES,
-                      NULL, NULL, NULL, NULL, NULL, NULL);
+                      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -409,7 +442,7 @@
 CookieTreeNode::DetailedInfo CookieTreeAppCachesNode::GetDetailedInfo() const {
   return DetailedInfo(parent()->GetTitle(),
                       DetailedInfo::TYPE_APPCACHES,
-                      NULL, NULL, NULL, NULL, NULL, NULL);
+                      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -424,7 +457,7 @@
 CookieTreeNode::DetailedInfo CookieTreeDatabasesNode::GetDetailedInfo() const {
   return DetailedInfo(parent()->GetTitle(),
                       DetailedInfo::TYPE_DATABASES,
-                      NULL, NULL, NULL, NULL, NULL, NULL);
+                      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -440,7 +473,7 @@
 CookieTreeLocalStoragesNode::GetDetailedInfo() const {
   return DetailedInfo(parent()->GetTitle(),
                       DetailedInfo::TYPE_LOCAL_STORAGES,
-                      NULL, NULL, NULL, NULL, NULL, NULL);
+                      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -456,7 +489,7 @@
 CookieTreeSessionStoragesNode::GetDetailedInfo() const {
   return DetailedInfo(parent()->GetTitle(),
                       DetailedInfo::TYPE_SESSION_STORAGES,
-                      NULL, NULL, NULL, NULL, NULL, NULL);
+                      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -472,7 +505,23 @@
 CookieTreeIndexedDBsNode::GetDetailedInfo() const {
   return DetailedInfo(parent()->GetTitle(),
                       DetailedInfo::TYPE_INDEXED_DBS,
-                      NULL, NULL, NULL, NULL, NULL, NULL);
+                      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CookieTreeFileSystemsNode, public:
+
+CookieTreeFileSystemsNode::CookieTreeFileSystemsNode()
+    : CookieTreeNode(l10n_util::GetStringUTF16(IDS_COOKIES_FILE_SYSTEMS)) {
+}
+
+CookieTreeFileSystemsNode::~CookieTreeFileSystemsNode() {}
+
+CookieTreeNode::DetailedInfo
+CookieTreeFileSystemsNode::GetDetailedInfo() const {
+  return DetailedInfo(parent()->GetTitle(),
+                      DetailedInfo::TYPE_FILE_SYSTEMS,
+                      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -488,6 +537,7 @@
 }
 
 void CookieTreeNode::AddChildSortedByTitle(CookieTreeNode* new_child) {
+  DCHECK(new_child);
   std::vector<CookieTreeNode*>::iterator iter =
       lower_bound(children().begin(),
                   children().end(),
@@ -506,6 +556,7 @@
     BrowsingDataLocalStorageHelper* session_storage_helper,
     BrowsingDataAppCacheHelper* appcache_helper,
     BrowsingDataIndexedDBHelper* indexed_db_helper,
+    BrowsingDataFileSystemHelper* file_system_helper,
     bool use_cookie_source)
     : ALLOW_THIS_IN_INITIALIZER_LIST(ui::TreeNodeModel<CookieTreeNode>(
           new CookieTreeRootNode(this))),
@@ -515,6 +566,7 @@
       local_storage_helper_(local_storage_helper),
       session_storage_helper_(session_storage_helper),
       indexed_db_helper_(indexed_db_helper),
+      file_system_helper_(file_system_helper),
       batch_update_(0),
       use_cookie_source_(use_cookie_source) {
   LoadCookies();
@@ -540,6 +592,11 @@
     indexed_db_helper_->StartFetching(NewCallback(
         this, &CookiesTreeModel::OnIndexedDBModelInfoLoaded));
   }
+
+  if (file_system_helper_) {
+    file_system_helper_->StartFetching(NewCallback(
+        this, &CookiesTreeModel::OnFileSystemModelInfoLoaded));
+  }
 }
 
 CookiesTreeModel::~CookiesTreeModel() {
@@ -551,6 +608,8 @@
     appcache_helper_->CancelNotification();
   if (indexed_db_helper_)
     indexed_db_helper_->CancelNotification();
+  if (file_system_helper_)
+    file_system_helper_->CancelNotification();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -588,6 +647,8 @@
       return DATABASE;  // ditto
     case CookieTreeNode::DetailedInfo::TYPE_INDEXED_DB:
       return DATABASE;  // ditto
+    case CookieTreeNode::DetailedInfo::TYPE_FILE_SYSTEM:
+      return DATABASE;  // ditto
     default:
       break;
   }
@@ -848,6 +909,39 @@
   NotifyObserverEndBatch();
 }
 
+void CookiesTreeModel::OnFileSystemModelInfoLoaded(
+    const FileSystemInfoList& file_system_info) {
+  file_system_info_list_ = file_system_info;
+  PopulateFileSystemInfoWithFilter(std::wstring());
+}
+
+void CookiesTreeModel::PopulateFileSystemInfoWithFilter(
+    const std::wstring& filter) {
+  if (file_system_info_list_.empty())
+    return;
+  CookieTreeRootNode* root = static_cast<CookieTreeRootNode*>(GetRoot());
+  NotifyObserverBeginBatch();
+  for (FileSystemInfoList::iterator file_system_info =
+       file_system_info_list_.begin();
+       file_system_info != file_system_info_list_.end();
+       ++file_system_info) {
+    GURL origin(file_system_info->origin);
+
+    if (!filter.size() ||
+        (CookieTreeOriginNode::TitleForUrl(origin).find(filter) !=
+         std::wstring::npos)) {
+      CookieTreeOriginNode* origin_node =
+          root->GetOrCreateOriginNode(origin);
+      CookieTreeFileSystemsNode* file_systems_node =
+          origin_node->GetOrCreateFileSystemsNode();
+      file_systems_node->AddFileSystemNode(
+          new CookieTreeFileSystemNode(&(*file_system_info)));
+    }
+  }
+  NotifyObserverTreeNodeChanged(root);
+  NotifyObserverEndBatch();
+}
+
 void CookiesTreeModel::NotifyObserverBeginBatch() {
   // Only notify the model once if we're batching in a nested manner.
   if (batch_update_++ == 0) {
diff --git a/chrome/browser/cookies_tree_model.h b/chrome/browser/cookies_tree_model.h
index 9515f51..1f1e8448 100644
--- a/chrome/browser/cookies_tree_model.h
+++ b/chrome/browser/cookies_tree_model.h
@@ -18,6 +18,7 @@
 #include "base/utf_string_conversions.h"
 #include "chrome/browser/browsing_data_appcache_helper.h"
 #include "chrome/browser/browsing_data_database_helper.h"
+#include "chrome/browser/browsing_data_file_system_helper.h"
 #include "chrome/browser/browsing_data_indexed_db_helper.h"
 #include "chrome/browser/browsing_data_local_storage_helper.h"
 #include "chrome/common/content_settings.h"
@@ -31,6 +32,8 @@
 class CookieTreeCookiesNode;
 class CookieTreeDatabaseNode;
 class CookieTreeDatabasesNode;
+class CookieTreeFileSystemsNode;
+class CookieTreeFileSystemNode;
 class CookieTreeLocalStorageNode;
 class CookieTreeLocalStoragesNode;
 class CookieTreeSessionStorageNode;
@@ -66,6 +69,8 @@
       TYPE_APPCACHE,  // This is used for CookieTreeAppCacheNode.
       TYPE_INDEXED_DBS,  // This is used for CookieTreeIndexedDBsNode.
       TYPE_INDEXED_DB,  // This is used for CookieTreeIndexedDBNode.
+      TYPE_FILE_SYSTEMS, // This is used for CookieTreeFileSystemsNode.
+      TYPE_FILE_SYSTEM, // This is used for CookieTreeFileSystemNode.
     };
 
     // TODO(viettrungluu): Figure out whether we want to store |origin| as a
@@ -79,7 +84,8 @@
         const BrowsingDataLocalStorageHelper::LocalStorageInfo*
             session_storage_info,
         const appcache::AppCacheInfo* appcache_info,
-        const BrowsingDataIndexedDBHelper::IndexedDBInfo* indexed_db_info)
+        const BrowsingDataIndexedDBHelper::IndexedDBInfo* indexed_db_info,
+        const BrowsingDataFileSystemHelper::FileSystemInfo* file_system_info)
         : origin(UTF16ToWideHack(origin)),
           node_type(node_type),
           cookie(cookie),
@@ -87,12 +93,14 @@
           local_storage_info(local_storage_info),
           session_storage_info(session_storage_info),
           appcache_info(appcache_info),
-          indexed_db_info(indexed_db_info) {
+          indexed_db_info(indexed_db_info),
+          file_system_info(file_system_info) {
       DCHECK((node_type != TYPE_DATABASE) || database_info);
       DCHECK((node_type != TYPE_LOCAL_STORAGE) || local_storage_info);
       DCHECK((node_type != TYPE_SESSION_STORAGE) || session_storage_info);
       DCHECK((node_type != TYPE_APPCACHE) || appcache_info);
       DCHECK((node_type != TYPE_INDEXED_DB) || indexed_db_info);
+      DCHECK((node_type != TYPE_FILE_SYSTEM) || file_system_info);
     }
 #if !defined(WCHAR_T_IS_UTF16)
     DetailedInfo(const std::wstring& origin, NodeType node_type,
@@ -103,7 +111,8 @@
         const BrowsingDataLocalStorageHelper::LocalStorageInfo*
             session_storage_info,
         const appcache::AppCacheInfo* appcache_info,
-        const BrowsingDataIndexedDBHelper::IndexedDBInfo* indexed_db_info)
+        const BrowsingDataIndexedDBHelper::IndexedDBInfo* indexed_db_info,
+        const BrowsingDataFileSystemHelper::FileSystemInfo* file_system_info)
         : origin(origin),
           node_type(node_type),
           cookie(cookie),
@@ -111,12 +120,14 @@
           local_storage_info(local_storage_info),
           session_storage_info(session_storage_info),
           appcache_info(appcache_info),
-          indexed_db_info(indexed_db_info) {
+          indexed_db_info(indexed_db_info),
+          file_system_info(file_system_info) {
       DCHECK((node_type != TYPE_DATABASE) || database_info);
       DCHECK((node_type != TYPE_LOCAL_STORAGE) || local_storage_info);
       DCHECK((node_type != TYPE_SESSION_STORAGE) || session_storage_info);
       DCHECK((node_type != TYPE_APPCACHE) || appcache_info);
       DCHECK((node_type != TYPE_INDEXED_DB) || indexed_db_info);
+      DCHECK((node_type != TYPE_FILE_SYSTEM) || file_system_info);
     }
 #endif
 
@@ -129,6 +140,7 @@
         session_storage_info;
     const appcache::AppCacheInfo* appcache_info;
     const BrowsingDataIndexedDBHelper::IndexedDBInfo* indexed_db_info;
+    const BrowsingDataFileSystemHelper::FileSystemInfo* file_system_info;
   };
 
   CookieTreeNode() {}
@@ -198,6 +210,7 @@
   CookieTreeSessionStoragesNode* GetOrCreateSessionStoragesNode();
   CookieTreeAppCachesNode* GetOrCreateAppCachesNode();
   CookieTreeIndexedDBsNode* GetOrCreateIndexedDBsNode();
+  CookieTreeFileSystemsNode* GetOrCreateFileSystemsNode();
 
   // Creates an content exception for this origin of type
   // CONTENT_SETTINGS_TYPE_COOKIES.
@@ -219,6 +232,7 @@
   CookieTreeSessionStoragesNode* session_storages_child_;
   CookieTreeAppCachesNode* appcaches_child_;
   CookieTreeIndexedDBsNode* indexed_dbs_child_;
+  CookieTreeFileSystemsNode* file_systems_child_;
 
   // The URL for which this node was initially created.
   GURL url_;
@@ -334,6 +348,42 @@
   DISALLOW_COPY_AND_ASSIGN(CookieTreeDatabasesNode);
 };
 
+// CookieTreeFileSystemNode --------------------------------------------------
+class CookieTreeFileSystemNode : public CookieTreeNode {
+ public:
+  friend class CookieTreeFileSystemsNode;
+
+  // Does not take ownership of file_system_info, and file_system_info should
+  // remain valid at least as long as the CookieTreeFileSystemNode is valid.
+  explicit CookieTreeFileSystemNode(
+      BrowsingDataFileSystemHelper::FileSystemInfo* file_system_info);
+  virtual ~CookieTreeFileSystemNode();
+
+  virtual void DeleteStoredObjects();
+  virtual DetailedInfo GetDetailedInfo() const;
+
+ private:
+  // file_system_info_ is not owned by the node, and is expected to remain
+  // valid as long as the CookieTreeFileSystemNode is valid.
+  BrowsingDataFileSystemHelper::FileSystemInfo* file_system_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(CookieTreeFileSystemNode);
+};
+
+class CookieTreeFileSystemsNode : public CookieTreeNode {
+ public:
+  CookieTreeFileSystemsNode();
+  virtual ~CookieTreeFileSystemsNode();
+
+  virtual DetailedInfo GetDetailedInfo() const;
+
+  void AddFileSystemNode(CookieTreeFileSystemNode* child) {
+    AddChildSortedByTitle(child);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CookieTreeFileSystemsNode);
+};
 
 // CookieTreeLocalStorageNode -------------------------------------------------
 class CookieTreeLocalStorageNode : public CookieTreeNode {
@@ -470,6 +520,7 @@
       BrowsingDataLocalStorageHelper* session_storage_helper,
       BrowsingDataAppCacheHelper* appcache_helper,
       BrowsingDataIndexedDBHelper* indexed_db_helper,
+      BrowsingDataFileSystemHelper* file_system_helper,
       bool use_cookie_source);
   virtual ~CookiesTreeModel();
 
@@ -512,6 +563,8 @@
       SessionStorageInfoList;
   typedef std::vector<BrowsingDataIndexedDBHelper::IndexedDBInfo>
       IndexedDBInfoList;
+  typedef std::vector<BrowsingDataFileSystemHelper::FileSystemInfo>
+      FileSystemInfoList;
 
   void LoadCookies();
   void LoadCookiesWithFilter(const std::wstring& filter);
@@ -524,12 +577,15 @@
       const LocalStorageInfoList& local_storage_info);
   void OnIndexedDBModelInfoLoaded(
       const IndexedDBInfoList& indexed_db_info);
+  void OnFileSystemModelInfoLoaded(
+      const FileSystemInfoList& file_system_info);
 
   void PopulateAppCacheInfoWithFilter(const std::wstring& filter);
   void PopulateDatabaseInfoWithFilter(const std::wstring& filter);
   void PopulateLocalStorageInfoWithFilter(const std::wstring& filter);
   void PopulateSessionStorageInfoWithFilter(const std::wstring& filter);
   void PopulateIndexedDBInfoWithFilter(const std::wstring& filter);
+  void PopulateFileSystemInfoWithFilter(const std::wstring& filter);
 
   void NotifyObserverBeginBatch();
   void NotifyObserverEndBatch();
@@ -545,9 +601,11 @@
   scoped_refptr<BrowsingDataLocalStorageHelper> local_storage_helper_;
   scoped_refptr<BrowsingDataLocalStorageHelper> session_storage_helper_;
   scoped_refptr<BrowsingDataIndexedDBHelper> indexed_db_helper_;
+  scoped_refptr<BrowsingDataFileSystemHelper> file_system_helper_;
   LocalStorageInfoList local_storage_info_list_;
   LocalStorageInfoList session_storage_info_list_;
   IndexedDBInfoList indexed_db_info_list_;
+  FileSystemInfoList file_system_info_list_;
 
   // The CookiesTreeModel maintains a separate list of observers that are
   // specifically of the type CookiesTreeModel::Observer.
@@ -567,6 +625,7 @@
   friend class CookieTreeDatabaseNode;
   friend class CookieTreeLocalStorageNode;
   friend class CookieTreeIndexedDBNode;
+  friend class CookieTreeFileSystemNode;
 
   DISALLOW_COPY_AND_ASSIGN(CookiesTreeModel);
 };
diff --git a/chrome/browser/cookies_tree_model_unittest.cc b/chrome/browser/cookies_tree_model_unittest.cc
index 9ff0a5c..7a0f7ad 100644
--- a/chrome/browser/cookies_tree_model_unittest.cc
+++ b/chrome/browser/cookies_tree_model_unittest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/content_settings/stub_settings_observer.h"
 #include "chrome/browser/mock_browsing_data_appcache_helper.h"
 #include "chrome/browser/mock_browsing_data_database_helper.h"
+#include "chrome/browser/mock_browsing_data_file_system_helper.h"
 #include "chrome/browser/mock_browsing_data_indexed_db_helper.h"
 #include "chrome/browser/mock_browsing_data_local_storage_helper.h"
 #include "chrome/test/testing_profile.h"
@@ -19,6 +20,8 @@
 #include "net/url_request/url_request_context_getter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#include "base/utf_string_conversions.h"
+
 
 namespace {
 
@@ -44,6 +47,8 @@
       new MockBrowsingDataAppCacheHelper(profile_.get());
     mock_browsing_data_indexed_db_helper_ =
       new MockBrowsingDataIndexedDBHelper(profile_.get());
+    mock_browsing_data_file_system_helper_ =
+      new MockBrowsingDataFileSystemHelper(profile_.get());
   }
 
   CookiesTreeModel* CreateCookiesTreeModelWithInitialSample() {
@@ -57,6 +62,7 @@
         mock_browsing_data_session_storage_helper_,
         mock_browsing_data_appcache_helper_,
         mock_browsing_data_indexed_db_helper_,
+        mock_browsing_data_file_system_helper_,
         false);
     mock_browsing_data_database_helper_->AddDatabaseSamples();
     mock_browsing_data_database_helper_->Notify();
@@ -66,19 +72,24 @@
     mock_browsing_data_session_storage_helper_->Notify();
     mock_browsing_data_indexed_db_helper_->AddIndexedDBSamples();
     mock_browsing_data_indexed_db_helper_->Notify();
+    mock_browsing_data_file_system_helper_->AddFileSystemSamples();
+    mock_browsing_data_file_system_helper_->Notify();
     {
       SCOPED_TRACE("Initial State 3 cookies, 2 databases, 2 local storages, "
-                   "2 session storages, 2 indexed DBs");
-      // 32 because there's the root, then foo1 -> cookies -> a,
+                   "2 session storages, 2 indexed DBs, 3 filesystems");
+      // 41 because there's the root, then foo1 -> cookies -> a,
       // foo2 -> cookies -> b, foo3 -> cookies -> c,
       // dbhost1 -> database -> db1, dbhost2 -> database -> db2,
+      // fshost1 -> filesystem -> http://fshost1:1/,
+      // fshost2 -> filesystem -> http://fshost2:1/,
+      // fshost3 -> filesystem -> http://fshost3:1/,
       // host1 -> localstorage -> http://host1:1/,
       // host2 -> localstorage -> http://host2:2/.
       // host1 -> sessionstorage -> http://host1:1/,
       // host2 -> sessionstorage -> http://host2:2/,
       // idbhost1 -> indexeddb -> http://idbhost1:1/,
       // idbhost2 -> indexeddb -> http://idbhost2:2/.
-      EXPECT_EQ(32, cookies_model->GetRoot()->GetTotalNodeCount());
+      EXPECT_EQ(41, cookies_model->GetRoot()->GetTotalNodeCount());
       EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model));
       EXPECT_EQ("http://host1:1/,http://host2:2/",
                 GetDisplayedLocalStorages(cookies_model));
@@ -86,6 +97,8 @@
                 GetDisplayedSessionStorages(cookies_model));
       EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
                 GetDisplayedIndexedDBs(cookies_model));
+      EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+                GetDisplayedFileSystems(cookies_model));
     }
     return cookies_model;
   }
@@ -129,6 +142,9 @@
                    ",";
           case CookieTreeNode::DetailedInfo::TYPE_INDEXED_DB:
             return node->GetDetailedInfo().indexed_db_info->origin + ",";
+          case CookieTreeNode::DetailedInfo::TYPE_FILE_SYSTEM:
+            return node->GetDetailedInfo().file_system_info->origin.spec() +
+                   ",";
           default:
             return "";
         }
@@ -162,6 +178,11 @@
         node, CookieTreeNode::DetailedInfo::TYPE_INDEXED_DB);
   }
 
+  std::string GetFileSystemsOfChildren(const CookieTreeNode* node) {
+    return GetNodesOfChildren(
+        node, CookieTreeNode::DetailedInfo::TYPE_FILE_SYSTEM);
+  }
+
   // Get the nodes names displayed in the view (if we had one) in the order
   // they are displayed, as a comma seperated string.
   // Ex: EXPECT_STREQ("X,Y", GetDisplayedNodes(cookies_view, type).c_str());
@@ -205,6 +226,11 @@
                              CookieTreeNode::DetailedInfo::TYPE_INDEXED_DB);
   }
 
+  std::string GetDisplayedFileSystems(CookiesTreeModel* cookies_model) {
+    return GetDisplayedNodes(cookies_model,
+                             CookieTreeNode::DetailedInfo::TYPE_FILE_SYSTEM);
+  }
+
   // Do not call on the root.
   void DeleteStoredObjects(CookieTreeNode* node) {
     node->DeleteStoredObjects();
@@ -229,6 +255,8 @@
       mock_browsing_data_appcache_helper_;
   scoped_refptr<MockBrowsingDataIndexedDBHelper>
       mock_browsing_data_indexed_db_helper_;
+  scoped_refptr<MockBrowsingDataFileSystemHelper>
+      mock_browsing_data_file_system_helper_;
 };
 
 TEST_F(CookiesTreeModelTest, RemoveAll) {
@@ -249,12 +277,15 @@
               GetDisplayedSessionStorages(cookies_model.get()));
     EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
               GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
   }
 
   mock_browsing_data_database_helper_->Reset();
   mock_browsing_data_local_storage_helper_->Reset();
   mock_browsing_data_session_storage_helper_->Reset();
   mock_browsing_data_indexed_db_helper_->Reset();
+  mock_browsing_data_file_system_helper_->Reset();
 
   cookies_model->DeleteAllStoredObjects();
 
@@ -269,6 +300,7 @@
     EXPECT_TRUE(mock_browsing_data_local_storage_helper_->AllDeleted());
     EXPECT_FALSE(mock_browsing_data_session_storage_helper_->AllDeleted());
     EXPECT_TRUE(mock_browsing_data_indexed_db_helper_->AllDeleted());
+    EXPECT_TRUE(mock_browsing_data_file_system_helper_->AllDeleted());
   }
 }
 
@@ -277,64 +309,182 @@
       CreateCookiesTreeModelWithInitialSample());
   net::CookieMonster* monster = profile_->GetCookieMonster();
 
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(0));
+  // Children start out arranged as follows:
+  //
+  // 0. `foo1`
+  // 1. `foo2`
+  // 2. `foo3`
+  // 3. `fshost1`
+  // 4. `fshost2`
+  // 5. `fshost3`
+  // 6. `gdbhost1`
+  // 7. `gdbhost2`
+  // 8. `host1`
+  // 9. `host2`
+  // 10. `idbhost1`
+  // 11. `idbhost2`
+  //
+  // Here, we'll remove them one by one, starting from the end, and
+  // check that the state makes sense.
+
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(11));
   {
-    SCOPED_TRACE("First cookie origin removed");
-    EXPECT_STREQ("B,C", GetMonsterCookies(monster).c_str());
-    EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str());
+    SCOPED_TRACE("`idbhost2` removed.");
+    EXPECT_STREQ("A,B,C", GetMonsterCookies(monster).c_str());
+    EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
     EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedSessionStorages(cookies_model.get()));
-    EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("http://idbhost1:1/",
               GetDisplayedIndexedDBs(cookies_model.get()));
-    EXPECT_EQ(29, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ(38, cookies_model->GetRoot()->GetTotalNodeCount());
   }
-
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(2));
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(10));
   {
-    SCOPED_TRACE("First database origin removed");
-    EXPECT_STREQ("B,C", GetMonsterCookies(monster).c_str());
-    EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db2", GetDisplayedDatabases(cookies_model.get()));
+    SCOPED_TRACE("`idbhost1` removed.");
+    EXPECT_STREQ("A,B,C", GetMonsterCookies(monster).c_str());
+    EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedSessionStorages(cookies_model.get()));
-    EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
-              GetDisplayedIndexedDBs(cookies_model.get()));
-    EXPECT_EQ(26, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ(35, cookies_model->GetRoot()->GetTotalNodeCount());
   }
-
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(3));
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(9));
   {
-    SCOPED_TRACE("First local storage origin removed");
-    EXPECT_STREQ("B,C", GetMonsterCookies(monster).c_str());
-    EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db2", GetDisplayedDatabases(cookies_model.get()));
-    EXPECT_EQ("http://host2:2/",
+    SCOPED_TRACE("`host2` removed.");
+    EXPECT_STREQ("A,B,C", GetMonsterCookies(monster).c_str());
+    EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://host1:1/",
               GetDisplayedLocalStorages(cookies_model.get()));
-    EXPECT_EQ("http://host2:2/",
+    EXPECT_EQ("http://host1:1/",
               GetDisplayedSessionStorages(cookies_model.get()));
-    EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
-              GetDisplayedIndexedDBs(cookies_model.get()));
-    EXPECT_EQ(21, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ(30, cookies_model->GetRoot()->GetTotalNodeCount());
   }
-
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(8));
+  {
+    SCOPED_TRACE("`host1` removed.");
+    EXPECT_STREQ("A,B,C", GetMonsterCookies(monster).c_str());
+    EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedLocalStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedSessionStorages(cookies_model.get()));
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ(25, cookies_model->GetRoot()->GetTotalNodeCount());
+  }
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(7));
+  {
+    SCOPED_TRACE("`gdbhost2` removed.");
+    EXPECT_STREQ("A,B,C", GetMonsterCookies(monster).c_str());
+    EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("db1", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedLocalStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedSessionStorages(cookies_model.get()));
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ(22, cookies_model->GetRoot()->GetTotalNodeCount());
+  }
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(6));
+  {
+    SCOPED_TRACE("`gdbhost1` removed.");
+    EXPECT_STREQ("A,B,C", GetMonsterCookies(monster).c_str());
+    EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedLocalStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedSessionStorages(cookies_model.get()));
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ(19, cookies_model->GetRoot()->GetTotalNodeCount());
+  }
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(5));
+  {
+    SCOPED_TRACE("`fshost3` removed.");
+    EXPECT_STREQ("A,B,C", GetMonsterCookies(monster).c_str());
+    EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedLocalStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedSessionStorages(cookies_model.get()));
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ(16, cookies_model->GetRoot()->GetTotalNodeCount());
+  }
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(4));
   {
-    SCOPED_TRACE("First IndexedDB origin removed");
-    EXPECT_STREQ("B,C", GetMonsterCookies(monster).c_str());
-    EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db2", GetDisplayedDatabases(cookies_model.get()));
-    EXPECT_EQ("http://host2:2/",
-              GetDisplayedLocalStorages(cookies_model.get()));
-    EXPECT_EQ("http://host2:2/",
-              GetDisplayedSessionStorages(cookies_model.get()));
-    EXPECT_EQ("http://idbhost2:2/",
-              GetDisplayedIndexedDBs(cookies_model.get()));
-    EXPECT_EQ(18, cookies_model->GetRoot()->GetTotalNodeCount());
+    SCOPED_TRACE("`fshost2` removed.");
+    EXPECT_STREQ("A,B,C", GetMonsterCookies(monster).c_str());
+    EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedLocalStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedSessionStorages(cookies_model.get()));
+    EXPECT_EQ("http://fshost1:1/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ(13, cookies_model->GetRoot()->GetTotalNodeCount());
+  }
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(3));
+  {
+    SCOPED_TRACE("`fshost1` removed.");
+    EXPECT_STREQ("A,B,C", GetMonsterCookies(monster).c_str());
+    EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedLocalStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedSessionStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ(10, cookies_model->GetRoot()->GetTotalNodeCount());
+  }
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(2));
+  {
+    SCOPED_TRACE("`foo3` removed.");
+    EXPECT_STREQ("A,B", GetMonsterCookies(monster).c_str());
+    EXPECT_STREQ("A,B", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedLocalStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedSessionStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ(7, cookies_model->GetRoot()->GetTotalNodeCount());
+  }
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(1));
+  {
+    SCOPED_TRACE("`foo2` removed.");
+    EXPECT_STREQ("A", GetMonsterCookies(monster).c_str());
+    EXPECT_STREQ("A", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedLocalStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedSessionStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ(4, cookies_model->GetRoot()->GetTotalNodeCount());
+  }
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(0));
+  {
+    SCOPED_TRACE("`foo1` removed.");
+    EXPECT_STREQ("", GetMonsterCookies(monster).c_str());
+    EXPECT_STREQ("", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedLocalStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedSessionStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ(1, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 }
 
@@ -348,15 +498,18 @@
     SCOPED_TRACE("First origin removed");
     EXPECT_STREQ("B,C", GetMonsterCookies(monster).c_str());
     EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    // 28 because in this case, the origin remains, although the COOKIES
+    // 39 because in this case, the origin remains, although the COOKIES
     // node beneath it has been deleted. So, we have
     // root -> foo1 -> cookies -> a, foo2, foo3 -> cookies -> c
     // dbhost1 -> database -> db1, dbhost2 -> database -> db2,
+    // fshost1 -> filesystem -> http://fshost1:1/,
+    // fshost2 -> filesystem -> http://fshost2:1/,
+    // fshost3 -> filesystem -> http://fshost3:1/,
     // host1 -> localstorage -> http://host1:1/,
     // host2 -> localstorage -> http://host2:2/,
     // idbhost1 -> sessionstorage -> http://idbhost1:1/,
     // idbhost2 -> sessionstorage -> http://idbhost2:2/.
-    EXPECT_EQ(30, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ(39, cookies_model->GetRoot()->GetTotalNodeCount());
     EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
@@ -364,9 +517,11 @@
               GetDisplayedSessionStorages(cookies_model.get()));
     EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
               GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
   }
 
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(3)->GetChild(0));
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(6)->GetChild(0));
   {
     SCOPED_TRACE("First database removed");
     EXPECT_STREQ("B,C", GetMonsterCookies(monster).c_str());
@@ -378,10 +533,12 @@
               GetDisplayedSessionStorages(cookies_model.get()));
     EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
               GetDisplayedIndexedDBs(cookies_model.get()));
-    EXPECT_EQ(28, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ(37, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(5)->GetChild(0));
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(8)->GetChild(0));
   {
     SCOPED_TRACE("First origin removed");
     EXPECT_STREQ("B,C", GetMonsterCookies(monster).c_str());
@@ -393,7 +550,9 @@
               GetDisplayedSessionStorages(cookies_model.get()));
     EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
               GetDisplayedIndexedDBs(cookies_model.get()));
-    EXPECT_EQ(26, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ(35, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 }
 
@@ -414,20 +573,25 @@
               GetDisplayedSessionStorages(cookies_model.get()));
     EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
               GetDisplayedIndexedDBs(cookies_model.get()));
-    // 28 because in this case, the origin remains, although the COOKIES
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    // 39 because in this case, the origin remains, although the COOKIES
     // node beneath it has been deleted. So, we have
     // root -> foo1 -> cookies -> a, foo2, foo3 -> cookies -> c
     // dbhost1 -> database -> db1, dbhost2 -> database -> db2,
+    // fshost1 -> filesystem -> http://fshost1:1/,
+    // fshost2 -> filesystem -> http://fshost2:1/,
+    // fshost3 -> filesystem -> http://fshost3:1/,
     // host1 -> localstorage -> http://host1:1/,
-    // host2 -> localstorage -> http://host2:2/.
+    // host2 -> localstorage -> http://host2:2/,
     // host1 -> sessionstorage -> http://host1:1/,
     // host2 -> sessionstorage -> http://host2:2/,
     // idbhost1 -> sessionstorage -> http://idbhost1:1/,
     // idbhost2 -> sessionstorage -> http://idbhost2:2/.
-    EXPECT_EQ(30, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ(39, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(3)->GetChild(0));
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(6)->GetChild(0));
   {
     SCOPED_TRACE("First database removed");
     EXPECT_STREQ("A,C", GetMonsterCookies(monster).c_str());
@@ -439,10 +603,12 @@
               GetDisplayedSessionStorages(cookies_model.get()));
     EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
               GetDisplayedIndexedDBs(cookies_model.get()));
-    EXPECT_EQ(28, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ(37, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(5)->GetChild(0));
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(8)->GetChild(0));
   {
     SCOPED_TRACE("First origin removed");
     EXPECT_STREQ("A,C", GetMonsterCookies(monster).c_str());
@@ -454,7 +620,9 @@
               GetDisplayedSessionStorages(cookies_model.get()));
     EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
               GetDisplayedIndexedDBs(cookies_model.get()));
-    EXPECT_EQ(26, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ(35, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 }
 
@@ -470,6 +638,7 @@
                                  mock_browsing_data_session_storage_helper_,
                                  mock_browsing_data_appcache_helper_,
                                  mock_browsing_data_indexed_db_helper_,
+                                 mock_browsing_data_file_system_helper_,
                                  false);
   mock_browsing_data_database_helper_->AddDatabaseSamples();
   mock_browsing_data_database_helper_->Notify();
@@ -479,20 +648,24 @@
   mock_browsing_data_session_storage_helper_->Notify();
   mock_browsing_data_indexed_db_helper_->AddIndexedDBSamples();
   mock_browsing_data_indexed_db_helper_->Notify();
+  mock_browsing_data_file_system_helper_->AddFileSystemSamples();
+  mock_browsing_data_file_system_helper_->Notify();
 
   {
     SCOPED_TRACE("Initial State 4 cookies, 2 databases, 2 local storages, "
-        "2 session storages, 2 indexed DBs");
-    // 33 because there's the root, then foo1 -> cookies -> a,
+        "2 session storages, 2 indexed DBs, 3 file systems");
+    // 42 because there's the root, then foo1 -> cookies -> a,
     // foo2 -> cookies -> b, foo3 -> cookies -> c,d
     // dbhost1 -> database -> db1, dbhost2 -> database -> db2,
+    // fshost1 -> filesystem -> http://fshost1:1/,
+    // fshost2 -> filesystem -> http://fshost2:1/,
+    // fshost3 -> filesystem -> http://fshost3:1/,
     // host1 -> localstorage -> http://host1:1/,
-    // host2 -> localstorage -> http://host2:2/.
     // host1 -> sessionstorage -> http://host1:1/,
     // host2 -> sessionstorage -> http://host2:2/,
     // idbhost1 -> sessionstorage -> http://idbhost1:1/,
     // idbhost2 -> sessionstorage -> http://idbhost2:2/.
-    EXPECT_EQ(33, cookies_model.GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ(42, cookies_model.GetRoot()->GetTotalNodeCount());
     EXPECT_STREQ("A,B,C,D", GetMonsterCookies(monster).c_str());
     EXPECT_STREQ("A,B,C,D", GetDisplayedCookies(&cookies_model).c_str());
     EXPECT_EQ("db1,db2", GetDisplayedDatabases(&cookies_model));
@@ -502,6 +675,8 @@
               GetDisplayedSessionStorages(&cookies_model));
     EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
               GetDisplayedIndexedDBs(&cookies_model));
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(&cookies_model));
   }
   DeleteStoredObjects(cookies_model.GetRoot()->GetChild(2));
   {
@@ -515,7 +690,9 @@
               GetDisplayedSessionStorages(&cookies_model));
     EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
               GetDisplayedIndexedDBs(&cookies_model));
-    EXPECT_EQ(29, cookies_model.GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(&cookies_model));
+    EXPECT_EQ(38, cookies_model.GetRoot()->GetTotalNodeCount());
   }
 }
 
@@ -532,6 +709,7 @@
                                  mock_browsing_data_session_storage_helper_,
                                  mock_browsing_data_appcache_helper_,
                                  mock_browsing_data_indexed_db_helper_,
+                                 mock_browsing_data_file_system_helper_,
                                  false);
   mock_browsing_data_database_helper_->AddDatabaseSamples();
   mock_browsing_data_database_helper_->Notify();
@@ -541,20 +719,25 @@
   mock_browsing_data_session_storage_helper_->Notify();
   mock_browsing_data_indexed_db_helper_->AddIndexedDBSamples();
   mock_browsing_data_indexed_db_helper_->Notify();
+  mock_browsing_data_file_system_helper_->AddFileSystemSamples();
+  mock_browsing_data_file_system_helper_->Notify();
 
   {
     SCOPED_TRACE("Initial State 5 cookies, 2 databases, 2 local storages, "
                  "2 session storages, 2 indexed DBs");
-    // 34 because there's the root, then foo1 -> cookies -> a,
+    // 43 because there's the root, then foo1 -> cookies -> a,
     // foo2 -> cookies -> b, foo3 -> cookies -> c,d,e
     // dbhost1 -> database -> db1, dbhost2 -> database -> db2,
+    // fshost1 -> filesystem -> http://fshost1:1/,
+    // fshost2 -> filesystem -> http://fshost2:1/,
+    // fshost3 -> filesystem -> http://fshost3:1/,
     // host1 -> localstorage -> http://host1:1/,
-    // host2 -> localstorage -> http://host2:2/.
+    // host2 -> localstorage -> http://host2:2/,
     // host1 -> sessionstorage -> http://host1:1/,
     // host2 -> sessionstorage -> http://host2:2/,
     // idbhost1 -> sessionstorage -> http://idbhost1:1/,
     // idbhost2 -> sessionstorage -> http://idbhost2:2/.
-    EXPECT_EQ(34, cookies_model.GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ(43, cookies_model.GetRoot()->GetTotalNodeCount());
     EXPECT_STREQ("A,B,C,D,E", GetMonsterCookies(monster).c_str());
     EXPECT_STREQ("A,B,C,D,E", GetDisplayedCookies(&cookies_model).c_str());
     EXPECT_EQ("db1,db2", GetDisplayedDatabases(&cookies_model));
@@ -564,6 +747,8 @@
               GetDisplayedSessionStorages(&cookies_model));
     EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
               GetDisplayedIndexedDBs(&cookies_model));
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(&cookies_model));
   }
   DeleteStoredObjects(cookies_model.GetRoot()->GetChild(2)->GetChild(0)->
       GetChild(1));
@@ -571,7 +756,7 @@
     SCOPED_TRACE("Middle cookie in third origin removed");
     EXPECT_STREQ("A,B,C,E", GetMonsterCookies(monster).c_str());
     EXPECT_STREQ("A,B,C,E", GetDisplayedCookies(&cookies_model).c_str());
-    EXPECT_EQ(33, cookies_model.GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ(42, cookies_model.GetRoot()->GetTotalNodeCount());
     EXPECT_EQ("db1,db2", GetDisplayedDatabases(&cookies_model));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(&cookies_model));
@@ -579,6 +764,8 @@
               GetDisplayedSessionStorages(&cookies_model));
     EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
               GetDisplayedIndexedDBs(&cookies_model));
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(&cookies_model));
   }
 }
 
@@ -595,6 +782,7 @@
                                  mock_browsing_data_session_storage_helper_,
                                  mock_browsing_data_appcache_helper_,
                                  mock_browsing_data_indexed_db_helper_,
+                                 mock_browsing_data_file_system_helper_,
                                  false);
   {
     SCOPED_TRACE("Initial State 5 cookies");
@@ -632,6 +820,7 @@
       new MockBrowsingDataLocalStorageHelper(profile_.get()),
       new MockBrowsingDataAppCacheHelper(profile_.get()),
       new MockBrowsingDataIndexedDBHelper(profile_.get()),
+      new MockBrowsingDataFileSystemHelper(profile_.get()),
       false);
 
   {
@@ -662,6 +851,7 @@
       new MockBrowsingDataLocalStorageHelper(profile_.get()),
       new MockBrowsingDataAppCacheHelper(profile_.get()),
       new MockBrowsingDataIndexedDBHelper(profile_.get()),
+      new MockBrowsingDataFileSystemHelper(profile_.get()),
       false);
 
   TestingProfile profile;
diff --git a/chrome/browser/mock_browsing_data_file_system_helper.cc b/chrome/browser/mock_browsing_data_file_system_helper.cc
new file mode 100644
index 0000000..0c8c5ba
--- /dev/null
+++ b/chrome/browser/mock_browsing_data_file_system_helper.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2011 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.
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "chrome/browser/mock_browsing_data_file_system_helper.h"
+
+MockBrowsingDataFileSystemHelper::MockBrowsingDataFileSystemHelper(
+    Profile* profile)
+    : profile_(profile) {
+}
+
+MockBrowsingDataFileSystemHelper::~MockBrowsingDataFileSystemHelper() {
+}
+
+void MockBrowsingDataFileSystemHelper::StartFetching(
+    Callback1<const std::vector<FileSystemInfo>& >::Type* callback) {
+  callback_.reset(callback);
+}
+
+void MockBrowsingDataFileSystemHelper::CancelNotification() {
+  callback_.reset(NULL);
+}
+
+void MockBrowsingDataFileSystemHelper::DeleteFileSystemOrigin(
+    const GURL& origin) {
+  std::string key = origin.spec();
+  CHECK(file_systems_.find(key) != file_systems_.end());
+  last_deleted_origin_ = origin;
+  file_systems_[key] = false;
+}
+
+void MockBrowsingDataFileSystemHelper::AddFileSystem(
+    const GURL& origin, bool has_persistent, bool has_temporary) {
+  response_.push_back(BrowsingDataFileSystemHelper::FileSystemInfo(
+      origin, has_persistent, has_temporary, 0, 0));
+  file_systems_[origin.spec()] = true;
+}
+
+void MockBrowsingDataFileSystemHelper::AddFileSystemSamples() {
+  AddFileSystem(GURL("http://fshost1:1/"), false, true);
+  AddFileSystem(GURL("http://fshost2:2/"), true, false);
+  AddFileSystem(GURL("http://fshost3:3/"), true, true);
+}
+
+void MockBrowsingDataFileSystemHelper::Notify() {
+  CHECK(callback_.get());
+  callback_->Run(response_);
+}
+
+void MockBrowsingDataFileSystemHelper::Reset() {
+  for (std::map<const std::string, bool>::iterator i = file_systems_.begin();
+       i != file_systems_.end(); ++i)
+    i->second = true;
+}
+
+bool MockBrowsingDataFileSystemHelper::AllDeleted() {
+  for (std::map<const std::string, bool>::const_iterator i =
+            file_systems_.begin();
+       i != file_systems_.end(); ++i) {
+    if (i->second)
+      return false;
+  }
+  return true;
+}
diff --git a/chrome/browser/mock_browsing_data_file_system_helper.h b/chrome/browser/mock_browsing_data_file_system_helper.h
new file mode 100644
index 0000000..de55d52a
--- /dev/null
+++ b/chrome/browser/mock_browsing_data_file_system_helper.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2011 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 CHROME_BROWSER_MOCK_BROWSING_DATA_FILE_SYSTEM_HELPER_H_
+#define CHROME_BROWSER_MOCK_BROWSING_DATA_FILE_SYSTEM_HELPER_H_
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include "base/callback.h"
+#include "chrome/browser/browsing_data_file_system_helper.h"
+#include "webkit/fileapi/file_system_types.h"
+
+// Mock for BrowsingDataFileSystemHelper.
+// Use AddFileSystemSamples() or add directly to response_ vector, then call
+// Notify().
+class MockBrowsingDataFileSystemHelper : public BrowsingDataFileSystemHelper {
+ public:
+  explicit MockBrowsingDataFileSystemHelper(Profile* profile);
+
+  virtual void StartFetching(
+      Callback1<const std::vector<FileSystemInfo>& >::Type* callback);
+
+  virtual void CancelNotification();
+
+  virtual void DeleteFileSystemOrigin(const GURL& origin);
+
+  // Adds a specific filesystem.
+  void AddFileSystem(const GURL& origin,
+                     bool has_persistent,
+                     bool has_temporary);
+
+  // Adds some FilesystemInfo samples.
+  void AddFileSystemSamples();
+
+  // Notifies the callback.
+  void Notify();
+
+  // Marks all filesystems as existing.
+  void Reset();
+
+  // Returns true if all filesystemss since the last Reset() invokation were
+  // deleted.
+  bool AllDeleted();
+
+  GURL last_deleted_origin_;
+
+ private:
+  virtual ~MockBrowsingDataFileSystemHelper();
+
+  Profile* profile_;
+
+  scoped_ptr<Callback1<const std::vector<FileSystemInfo>& >::Type >
+      callback_;
+
+  // Stores which filesystems exist.
+  std::map<const std::string, bool> file_systems_;
+
+  std::vector<FileSystemInfo> response_;
+};
+
+#endif  // CHROME_BROWSER_MOCK_BROWSING_DATA_FILE_SYSTEM_HELPER_H_
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc
index ea616037..93d1df7 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc
@@ -379,7 +379,7 @@
   ContentSetting setting = host_content_settings_map_->GetCookieContentSetting(
       origin_url, top_origin_url, true);
   *allowed = setting != CONTENT_SETTING_BLOCK;
-  // If content was blocked, tell the UI to display the blocked content icon.
+  // Record access to DOM storage for potential display in UI.
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
       NewRunnableFunction(
@@ -391,14 +391,18 @@
                                                   const GURL& origin_url,
                                                   const GURL& top_origin_url,
                                                   bool* allowed) {
-  // TODO(kinuko): Need to notify the UI thread to indicate that
-  // there's a blocked content.  See the above for inspiration.
   ContentSetting setting = host_content_settings_map_->GetCookieContentSetting(
       origin_url, top_origin_url, true);
   DCHECK((setting == CONTENT_SETTING_ALLOW) ||
          (setting == CONTENT_SETTING_BLOCK) ||
          (setting == CONTENT_SETTING_SESSION_ONLY));
   *allowed = setting != CONTENT_SETTING_BLOCK;
+  // Record access to file system for potential display in UI.
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      NewRunnableFunction(
+          &TabSpecificContentSettings::FileSystemAccessed,
+          render_process_id_, render_view_id, origin_url, !*allowed));
 }
 
 void ChromeRenderMessageFilter::OnAllowIndexedDB(int render_view_id,
diff --git a/chrome/browser/ui/webui/options/cookies_view_handler.cc b/chrome/browser/ui/webui/options/cookies_view_handler.cc
index 33f8c29..2dc093f 100644
--- a/chrome/browser/ui/webui/options/cookies_view_handler.cc
+++ b/chrome/browser/ui/webui/options/cookies_view_handler.cc
@@ -8,6 +8,7 @@
 #include "base/values.h"
 #include "chrome/browser/browsing_data_appcache_helper.h"
 #include "chrome/browser/browsing_data_database_helper.h"
+#include "chrome/browser/browsing_data_file_system_helper.h"
 #include "chrome/browser/browsing_data_indexed_db_helper.h"
 #include "chrome/browser/browsing_data_local_storage_helper.h"
 #include "chrome/browser/profiles/profile.h"
@@ -142,6 +143,7 @@
         NULL,
         new BrowsingDataAppCacheHelper(profile),
         BrowsingDataIndexedDBHelper::Create(profile),
+        BrowsingDataFileSystemHelper::Create(profile),
         false));
     cookies_tree_model_->AddCookiesTreeObserver(this);
   }
@@ -149,7 +151,7 @@
 
 void CookiesViewHandler::UpdateSearchResults(const ListValue* args) {
   std::string query;
-  if (!args->GetString(0, &query)){
+  if (!args->GetString(0, &query)) {
     return;
   }
 
@@ -165,7 +167,7 @@
 
 void CookiesViewHandler::Remove(const ListValue* args) {
   std::string node_path;
-  if (!args->GetString(0, &node_path)){
+  if (!args->GetString(0, &node_path)) {
     return;
   }
 
@@ -179,7 +181,7 @@
 
 void CookiesViewHandler::LoadChildren(const ListValue* args) {
   std::string node_path;
-  if (!args->GetString(0, &node_path)){
+  if (!args->GetString(0, &node_path)) {
     return;
   }
 
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 31b7ce7..70b39c4 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -75,6 +75,8 @@
         'browser/mock_browsing_data_appcache_helper.h',
         'browser/mock_browsing_data_database_helper.cc',
         'browser/mock_browsing_data_database_helper.h',
+        'browser/mock_browsing_data_file_system_helper.cc',
+        'browser/mock_browsing_data_file_system_helper.h',
         'browser/mock_browsing_data_indexed_db_helper.cc',
         'browser/mock_browsing_data_indexed_db_helper.h',
         'browser/mock_browsing_data_local_storage_helper.cc',