Expose LifecycleUnit visibility in chrome://discards.

This is useful to debug occlusion tracking issues and to get a
better understanding of which tabs might be discarded.

Bug: 775644
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: I1ad43559a96eb758d3a7796186930d6a7ffa4b39
Reviewed-on: https://chromium-review.googlesource.com/993702
Reviewed-by: Chris Hamilton <chrisha@chromium.org>
Reviewed-by: Sébastien Marchand <sebmarchand@chromium.org>
Reviewed-by: Emily Stark <estark@chromium.org>
Commit-Queue: François Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#550316}
diff --git a/chrome/browser/resources/discards/discards.html b/chrome/browser/resources/discards/discards.html
index 96ae713..8fbb143 100644
--- a/chrome/browser/resources/discards/discards.html
+++ b/chrome/browser/resources/discards/discards.html
@@ -34,6 +34,7 @@
           <th data-sort-key="utilityRank" class="sort-column">Utility Rank</th>
           <th data-sort-key="title">Tab Title</th>
           <th data-sort-key="tabUrl">Tab URL</th>
+          <th data-sort-key="visibility">Visibility</th>
           <th data-sort-key="isMedia">Media</th>
           <th data-sort-key="isDiscarded">Discarded</th>
           <th data-sort-key="discardCount">Discard Count</th>
@@ -56,6 +57,7 @@
           </div>
         </td>
         <td class="tab-url-cell"></td>
+        <td class="visibility-cell"></td>
         <td class="is-media-cell boolean-cell"></td>
         <td class="is-discarded-cell boolean-cell"></td>
         <td class="discard-count-cell"></td>
diff --git a/chrome/browser/resources/discards/discards.js b/chrome/browser/resources/discards/discards.js
index 2c2da84..d282054 100644
--- a/chrome/browser/resources/discards/discards.js
+++ b/chrome/browser/resources/discards/discards.js
@@ -71,8 +71,10 @@
     }
 
     // Compares numeric fields.
-    if (['discardCount', 'utilityRank', 'lastActiveSeconds'].includes(
-            sortKey)) {
+    // Note: Visibility is represented as a numeric value.
+    if ([
+          'visibility', 'discardCount', 'utilityRank', 'lastActiveSeconds'
+        ].includes(sortKey)) {
       return val1 - val2;
     }
 
@@ -179,6 +181,24 @@
   }
 
   /**
+   * Returns a string representation of a visibility enum value for display in
+   * a table.
+   * @param {int} visibility A value in LifecycleUnitVisibility.
+   * @return {string} A string representation of the visibility.
+   */
+  function visibilityToString(visibility) {
+    switch (visibility) {
+      case 0:
+        return 'hidden';
+      case 1:
+        return 'occluded';
+      case 2:
+        return 'visible';
+    }
+    assertNotReached('Unsupported visibility: ' + visibility);
+  }
+
+  /**
    * Returns the index of the row in the table that houses the given |element|.
    * @param {HTMLElement} element Any element in the DOM.
    */
@@ -258,6 +278,8 @@
         info.faviconUrl ? info.faviconUrl : 'chrome://favicon';
     row.querySelector('.title-div').textContent = info.title;
     row.querySelector('.tab-url-cell').textContent = info.tabUrl;
+    row.querySelector('.visibility-cell').textContent =
+        visibilityToString(info.visibility);
     row.querySelector('.is-media-cell').textContent =
         boolToString(info.isMedia);
     row.querySelector('.is-discarded-cell').textContent =
diff --git a/chrome/browser/ui/webui/discards/discards.mojom b/chrome/browser/ui/webui/discards/discards.mojom
index 52932e9bb..1cf89a7c 100644
--- a/chrome/browser/ui/webui/discards/discards.mojom
+++ b/chrome/browser/ui/webui/discards/discards.mojom
@@ -4,6 +4,13 @@
 
 module mojom;
 
+// Identical to content::Visibility.
+enum LifecycleUnitVisibility {
+  HIDDEN = 0,
+  OCCLUDED = 1,
+  VISIBLE = 2,
+};
+
 // Discard related information about a single tab in a browser.
 struct TabDiscardsInfo {
   // The URL associated with the tab. This corresponds to GetLastCommittedURL,
@@ -13,6 +20,8 @@
   string favicon_url;
   // The title of the tab, as displayed on the tab itself.
   string title;
+  // The visibility of the LifecycleUnit.
+  LifecycleUnitVisibility visibility;
   // If the tab is currently using media functionality (casting, WebRTC, playing
   // audio, etc) this is true.
   bool is_media;
diff --git a/chrome/browser/ui/webui/discards/discards_ui.cc b/chrome/browser/ui/webui/discards/discards_ui.cc
index d7b7532b1..bcf0a16 100644
--- a/chrome/browser/ui/webui/discards/discards_ui.cc
+++ b/chrome/browser/ui/webui/discards/discards_ui.cc
@@ -7,7 +7,9 @@
 #include <utility>
 #include <vector>
 
+#include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/discard_reason.h"
@@ -32,6 +34,22 @@
                 : resource_coordinator::DiscardReason::kProactive;
 }
 
+mojom::LifecycleUnitVisibility GetLifecycleUnitVisibility(
+    content::Visibility visibility) {
+  switch (visibility) {
+    case content::Visibility::HIDDEN:
+      return mojom::LifecycleUnitVisibility::HIDDEN;
+    case content::Visibility::OCCLUDED:
+      return mojom::LifecycleUnitVisibility::OCCLUDED;
+    case content::Visibility::VISIBLE:
+      return mojom::LifecycleUnitVisibility::VISIBLE;
+  }
+#if defined(COMPILER_MSVC)
+  NOTREACHED();
+  return mojom::LifecycleUnitVisibility::VISIBLE;
+#endif
+}
+
 class DiscardsDetailsProviderImpl : public mojom::DiscardsDetailsProvider {
  public:
   // This instance is deleted when the supplied pipe is destroyed.
@@ -69,6 +87,8 @@
       // showing the chrome://favicon default in that case.
       info->favicon_url = lifecycle_unit->GetIconURL();
       info->title = base::UTF16ToUTF8(lifecycle_unit->GetTitle());
+      info->visibility =
+          GetLifecycleUnitVisibility(lifecycle_unit->GetVisibility());
       info->is_media = tab_lifecycle_unit_external->IsMediaTab();
       info->is_discarded = tab_lifecycle_unit_external->IsDiscarded();
       info->discard_count = tab_lifecycle_unit_external->GetDiscardCount();
diff --git a/chrome/test/data/webui/discards/discards_browsertest.js b/chrome/test/data/webui/discards/discards_browsertest.js
index 114a987..5e1308c 100644
--- a/chrome/test/data/webui/discards/discards_browsertest.js
+++ b/chrome/test/data/webui/discards/discards_browsertest.js
@@ -20,6 +20,7 @@
   let dummy1 = {
     title: 'title 1',
     tabUrl: 'http://urlone.com',
+    visibility: 0,
     isMedia: false,
     isDiscarded: false,
     isAutoDiscardable: false,
@@ -30,6 +31,7 @@
   let dummy2 = {
     title: 'title 2',
     tabUrl: 'http://urltwo.com',
+    visibility: 1,
     isMedia: true,
     isDiscarded: true,
     isAutoDiscardable: true,
@@ -38,8 +40,9 @@
     lastActiveSeconds: 1
   };
 
-  ['title', 'tabUrl', 'isMedia', 'isDiscarded', 'isAutoDiscardable',
-      'discardCount', 'utilityRank', 'lastActiveSeconds'].forEach((sortKey) => {
+  ['title', 'tabUrl', 'visibility', 'isMedia', 'isDiscarded',
+      'isAutoDiscardable', 'discardCount', 'utilityRank', 'lastActiveSeconds']
+      .forEach((sortKey) => {
     assertTrue(
         discards.compareTabDiscardsInfos(sortKey, dummy1, dummy2) < 0);
     assertTrue(