gdata: Add GCache Contents section to chrome:drive-internals

This section is used to show contents in the GCache directory.

BUG=135328
TEST=the new section is shown in chrome:drive-internals properly

Review URL: https://chromiumcodereview.appspot.com/10823039

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@148752 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 1594dcd..c7e3f0b 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -183,6 +183,7 @@
         <include name="IDR_CRYPTOHOME_JS" file="resources\chromeos\cryptohome.js" type="BINDATA" />
         <!-- manifest file of ChromeVox accessibility extension -->
         <include name="IDR_CHROMEVOX_MANIFEST" file="resources\chromeos\access_chromevox\manifest.json" type="BINDATA" />
+        <include name="IDR_DRIVE_INTERNALS_CSS" file="resources\chromeos\drive_internals.css" type="BINDATA" />
         <include name="IDR_DRIVE_INTERNALS_HTML" file="resources\chromeos\drive_internals.html" flattenhtml="true" type="BINDATA" />
         <include name="IDR_DRIVE_INTERNALS_JS" file="resources\chromeos\drive_internals.js" type="BINDATA" />
         <include name="IDR_GAIA_AUTH_MANIFEST" file="resources\gaia_auth\manifest.json" type="BINDATA" />
diff --git a/chrome/browser/resources/chromeos/drive_internals.css b/chrome/browser/resources/chromeos/drive_internals.css
new file mode 100644
index 0000000..19cf7a63
--- /dev/null
+++ b/chrome/browser/resources/chromeos/drive_internals.css
@@ -0,0 +1,8 @@
+/* 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.
+ */
+
+#gcache-contents {
+  font-size: small;
+}
diff --git a/chrome/browser/resources/chromeos/drive_internals.html b/chrome/browser/resources/chromeos/drive_internals.html
index 15c5c724..87eab853 100644
--- a/chrome/browser/resources/chromeos/drive_internals.html
+++ b/chrome/browser/resources/chromeos/drive_internals.html
@@ -3,17 +3,20 @@
   <head>
     <title>drive-internals</title>
     <meta charset="utf-8">
+    <link rel="stylesheet" href="drive_internals.css">
     <script src="chrome://resources/js/util.js"></script>
     <script src="chrome://drive-internals/drive_internals.js"></script>
   </head>
   <body>
     <h1>Drive Internals</h1>
     <h2>Authentication Status</h2>
+    <ul>
+      <li>Has refresh token: <span id='has-refresh-token'></span></li>
+      <li>Has access token: <span id='has-access-token'></span></li>
+    </ul>
+    <h2>GCache Contents</h2>
     <table>
-      <tbody>
-        <tr><td>Has refresh token</td><td id='has-refresh-token'></td></tr>
-        <tr><td>Has access token</td><td id='has-access-token'></td></tr>
-      </tbody>
+      <tbody id='gcache-contents'></tbody>
     </table>
   </body>
 </html>
diff --git a/chrome/browser/resources/chromeos/drive_internals.js b/chrome/browser/resources/chromeos/drive_internals.js
index e372a1ce9..1490f92 100644
--- a/chrome/browser/resources/chromeos/drive_internals.js
+++ b/chrome/browser/resources/chromeos/drive_internals.js
@@ -4,11 +4,55 @@
 
 /**
  * Updates the Authentication Status section.
- * @param {Object} auth_status Dictionary containing auth status.
+ * @param {Object} authStatus Dictionary containing auth status.
  */
-function UpdateAuthStatus(auth_status) {
-  $('has-refresh-token').textContent = auth_status['has-refresh-token'];
-  $('has-access-token').textContent = auth_status['has-access-token'];
+function updateAuthStatus(authStatus) {
+  $('has-refresh-token').textContent = authStatus['has-refresh-token'];
+  $('has-access-token').textContent = authStatus['has-access-token'];
+}
+
+/**
+ * Updates the GCache Contents section.
+ * @param {Array} gcacheContents List of dictionaries describing metadata
+ * of files and directories under the GCache directory.
+ */
+function updateGCacheContents(gcacheContents) {
+  var tbody = $('gcache-contents');
+  // Add a header row.
+  var tr = document.createElement('tr');
+  tr.appendChild(createElementFromText('th', 'Path'));
+  tr.appendChild(createElementFromText('th', 'Size'));
+  tr.appendChild(createElementFromText('th', 'Last Modified'));
+  tbody.appendChild(tr);
+
+  for (var i = 0; i < gcacheContents.length; i++) {
+    var entry = gcacheContents[i];
+    var tr = document.createElement('tr');
+
+    // Add some suffix based on the type.
+    var path = entry.path;
+    if (entry.is_directory)
+      path += '/';
+    else if (entry.is_symbolic_link)
+      path += '@';
+
+    tr.appendChild(createElementFromText('td', path));
+    tr.appendChild(createElementFromText('td', entry.size));
+    tr.appendChild(createElementFromText('td', entry.last_modified));
+    tbody.appendChild(tr);
+  }
+}
+
+/**
+ * Creates an element named |elementName| containing the content |text|.
+ * @param {string} elementName Name of the new element to be created.
+ * @param {string} text Text to be contained in the new element.
+ * @return {HTMLElement} The newly created HTML element.
+ */
+function createElementFromText(elementName, text) {
+  var element = document.createElement(elementName);
+  element.appendChild(document.createTextNode(text));
+  return element;
 }
 
 document.addEventListener('DOMContentLoaded', function() {
diff --git a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
index c214993..09ad4d00 100644
--- a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
@@ -5,13 +5,17 @@
 #include "chrome/browser/ui/webui/chromeos/drive_internals_ui.h"
 
 #include "base/bind.h"
+#include "base/file_util.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/gdata/gdata_auth_service.h"
+#include "chrome/browser/chromeos/gdata/gdata_cache.h"
 #include "chrome/browser/chromeos/gdata/gdata_documents_service.h"
 #include "chrome/browser/chromeos/gdata/gdata_system_service.h"
+#include "chrome/browser/chromeos/gdata/gdata_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/chrome_web_ui_data_source.h"
 #include "chrome/common/url_constants.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_message_handler.h"
 #include "grit/browser_resources.h"
@@ -20,6 +24,59 @@
 
 namespace {
 
+// Gets metadata of all files and directories in |root_path|
+// recursively. Stores the result as a list of dictionaries like:
+//
+// [{ path: 'GCache/v1/tmp/<resource_id>',
+//    size: 12345,
+//    is_directory: false,
+//    last_modified: '2005-08-09T09:57:00-08:00',
+//  },...]
+//
+// The list is sorted by the path.
+void GetGCacheContents(const FilePath& root_path,
+                       base::ListValue* gcache_contents) {
+  using file_util::FileEnumerator;
+  // Use this map to sort the result list by the path.
+  std::map<FilePath, DictionaryValue*> files;
+
+  const int options = (file_util::FileEnumerator::FILES |
+                       file_util::FileEnumerator::DIRECTORIES |
+                       file_util::FileEnumerator::SHOW_SYM_LINKS);
+  FileEnumerator enumerator(
+      root_path,
+      true,  // recursive
+      static_cast<FileEnumerator::FileType>(options));
+
+  for (FilePath current = enumerator.Next(); !current.empty();
+       current = enumerator.Next()) {
+    FileEnumerator::FindInfo find_info;
+    enumerator.GetFindInfo(&find_info);
+    int64 size = FileEnumerator::GetFilesize(find_info);
+    const bool is_directory = FileEnumerator::IsDirectory(find_info);
+    const bool is_symbolic_link = FileEnumerator::IsLink(find_info);
+    const base::Time last_modified =
+        FileEnumerator::GetLastModifiedTime(find_info);
+
+    base::DictionaryValue* entry = new base::DictionaryValue;
+    entry->SetString("path", current.value());
+    // Use double instead of integer for large files.
+    entry->SetDouble("size", size);
+    entry->SetBoolean("is_directory", is_directory);
+    entry->SetBoolean("is_symbolic_link", is_symbolic_link);
+    entry->SetString("last_modified",
+                     gdata::util::FormatTimeAsString(last_modified));
+
+    files[current] = entry;
+  }
+
+  // Convert |files| into |gcache_contents|.
+  for (std::map<FilePath, DictionaryValue*>::const_iterator
+           iter = files.begin(); iter != files.end(); ++iter) {
+    gcache_contents->Append(iter->second);
+  }
+}
+
 // Class to handle messages from chrome://drive-internals.
 class DriveInternalsWebUIHandler : public content::WebUIMessageHandler {
  public:
@@ -58,7 +115,24 @@
                            documents_service->HasRefreshToken());
     auth_status.SetBoolean("has-access-token",
                            documents_service->HasAccessToken());
-    web_ui()->CallJavascriptFunction("UpdateAuthStatus", auth_status);
+    web_ui()->CallJavascriptFunction("updateAuthStatus", auth_status);
+
+    // Start updating the GCache contents section.
+    const FilePath root_path =
+        gdata::GDataCache::GetCacheRootPath(profile);
+    base::ListValue* gcache_contents = new ListValue;
+    content::BrowserThread::PostBlockingPoolTaskAndReply(
+        FROM_HERE,
+        base::Bind(&GetGCacheContents, root_path, gcache_contents),
+        base::Bind(&DriveInternalsWebUIHandler::OnGetGCacheContents,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   base::Owned(gcache_contents)));
+  }
+
+  // Called when GetGCacheContents() is complete.
+  void OnGetGCacheContents(base::ListValue* gcache_contents) {
+    DCHECK(gcache_contents);
+    web_ui()->CallJavascriptFunction("updateGCacheContents", *gcache_contents);
   }
 
   base::WeakPtrFactory<DriveInternalsWebUIHandler> weak_ptr_factory_;
@@ -73,6 +147,7 @@
 
   ChromeWebUIDataSource* source =
       new ChromeWebUIDataSource(chrome::kChromeUIDriveInternalsHost);
+  source->add_resource_path("drive_internals.css", IDR_DRIVE_INTERNALS_CSS);
   source->add_resource_path("drive_internals.js", IDR_DRIVE_INTERNALS_JS);
   source->set_default_resource(IDR_DRIVE_INTERNALS_HTML);