Add ability to detect and linkify hashtags in github and googlesource + lots of cleanup

Removing the following:
* tasks.js and bots.js content scripts for swarming. They do not work anymore.
* Displaying status of autorollers in the popup. This does not work anymore.
* No longer getting # of infra alerts, instead added a link to am.skia.org.

Note: The extension will not work in skia.googlesource.com till a bug is filed to whitelist it.

Bug: skia:9612
Bug: skia:9613
Change-Id: I6cfb04d1a72960538873cc22b9fcb8a941b10404
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/252187
Reviewed-by: Joe Gregorio <jcgregorio@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Ravi Mistry <rmistry@google.com>
diff --git a/chromeextensions/skia-dev/bot_mapping.js b/chromeextensions/skia-dev/bot_mapping.js
deleted file mode 100644
index 38bde50..0000000
--- a/chromeextensions/skia-dev/bot_mapping.js
+++ /dev/null
@@ -1,47 +0,0 @@
-var botMapping = {
-  locations: {
-    "skia-rpi-0(0[1-9]|1[0-6])": "Skolo Shelf 3a",
-    "skia-rpi-0(1[7-9]|2[0-9]|3[0-2])": "Skolo Shelf 3b",
-    "skia-rpi-0(3[3-9]|4[0-8])": "Skolo Shelf 2a",
-    "skia-rpi-0(8[1-9]|9[0-6])": "Skolo Shelf 2b",
-    "skia-rpi-0(49|5[0-9]|6[0-4])": "Skolo Shelf 1a",
-    "skia-rpi-0(6[5-9]|7[0-6]|80)": "Skolo Shelf 1b",
-
-    "skia-e-linux-00[1-4]": "Skolo Door Bottom Shelf",
-    "skia-e-linux-020": "Skolo Door C",
-    "skia-e-linux-021": "Skolo Door E",
-    "skia-e-linux-022": "Skolo Door G",
-
-    "skia-e-win.+": "Skia Office Shelf",
-
-    "skia-(vm|ct-vm).*": "GCE",
-    "ct-vm.*": "GCE",
-
-    ".+(a3|a4|m3)": "Chrome Golo",
-    ".+m5": "Chrome Golo (bare metal)",
-  },
-  ifBroken: {
-    "skia-(vm|ct-vm).*": "Reboot in Cloud Console (see above)",
-    "ct-vm.*": "Reboot in Cloud Console (see above)",
-
-    "skia.*": "<a href='https://goto.google.com/skolo-maintenance'>go/skolo-maintenance</a>",
-
-    "skia-e-win.+": "<a href='https://goto.google.com/skolo-maintenance'>go/skolo-maintenance</a>",
-
-    ".+(m3|m5|a3)": "<a href='https://bugs.chromium.org/p/chromium/issues/entry?summary=[Device%20Restart]%20for%20_id_&description=Please%20Reboot%20_id_&cc=rmistry@google.com&components=Infra%3ELabs&labels=Pri-2,Infra-Troopers,Restrict-View-Google'> File a bug</a>",
-  },
-  useJumphost: {
-    "skia-rpi-.+": true,
-    "skia-e-linux.+": true,
-  },
-  golo: {
-    ".+(m3|m5)": true,
-  },
-  chrome: {
-    ".+(a3|a4)": true,
-  },
-  cloudConsole: {
-    "skia-(vm|ct-vm).*": true,
-    "ct-vm.*": true,
-  }
-}
\ No newline at end of file
diff --git a/chromeextensions/skia-dev/bots.js b/chromeextensions/skia-dev/bots.js
deleted file mode 100644
index 2a1c3db..0000000
--- a/chromeextensions/skia-dev/bots.js
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2016 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.
-
-(function(){
-
-// Doing a conditional write allows selecting, copying and paste to work
-// instead of the selection constantly going away.
-function overwriteIfDifferent(elem, str) {
-  if (elem.innerHTML !== str) {
-    elem.innerHTML = str;
-  }
-}
-
-function drawExtraTableEntries(location, ssh, ifBroken) {
-  var elem = document.getElementById("extra_location");
-  if (location) {
-    overwriteIfDifferent(elem, `<td>Physical Location</td><td colspan="2">${location}</td>`);
-  } else {
-    elem.innerHTML = "";
-  }
-
-  elem = document.getElementById("extra_ssh");
-  if (ssh) {
-    overwriteIfDifferent(elem, `<td>To SSH:</td><td colspan="2">${ssh}</td>`);
-  } else {
-    elem.innerHTML = "";
-  }
-
-  elem = document.getElementById("extra_ifBroken");
-  if (ifBroken) {
-    overwriteIfDifferent(elem, `<td>If can't SSH:</td><td colspan="2">${ifBroken}</td>`);
-  } else {
-    elem.innerHTML = "";
-  }
-}
-
-window.addEventListener("WebComponentsReady", function(e) {
-  // Create our extra <tr> elements.
-  var botTable = document.getElementsByTagName("table")[0];
-  var newRow = document.createElement("tr");
-  newRow.className = "outline";
-  newRow.id="extra_location";
-  var tbody = botTable.getElementsByTagName("tbody")[0];
-  tbody.appendChild(newRow);
-  newRow = document.createElement("tr");
-  newRow.className = "outline";
-  newRow.id="extra_ssh";
-  tbody.appendChild(newRow);
-  newRow = document.createElement("tr");
-  newRow.className = "outline";
-  newRow.id="extra_ifBroken";
-  tbody.appendChild(newRow);
-
-  window.setInterval(function(){
-    var id = document.querySelector(".swarming-app input").value;
-    var state = document.getElementsByClassName("bot_state")[0];
-    state = state.textContent.trim();
-    if (!state || !id) {
-      return;
-    }
-    state = JSON.parse(state) || {};
-
-    var location = "";
-    var ifBroken = "";
-    var ssh = "";
-
-    for (r in botMapping.locations) {
-      if (id.match(r)) {
-        location = botMapping.locations[r];
-        break;
-      }
-    }
-    for (r in botMapping.ifBroken) {
-      if (id.match(r)) {
-        ifBroken = botMapping.ifBroken[r];
-        var idRegex = /_id_/g;
-        ifBroken = ifBroken.replace(idRegex, id);
-        break;
-      }
-    }
-    for (r in botMapping.useJumphost) {
-      if (id.match(r)) {
-        ssh = `ssh -t -t chrome-bot@$JUMPHOST_IP "ssh chrome-bot@${state.ip}"`;
-        break;
-      }
-    }
-    for (r in botMapping.golo) {
-      if (id.match(r)) {
-        ssh = `ssh ${id}.golo <a href="https://chrome-internal.googlesource.com/infra/infra_internal/+/master/doc/ssh.md">For Access</a>`;
-        break;
-      }
-    }
-    for (r in botMapping.chrome) {
-      if (id.match(r)) {
-        ssh = `ssh ${id}.chrome <a href="https://chrome-internal.googlesource.com/infra/infra_internal/+/master/doc/ssh.md">For Access</a>`;
-        break;
-      }
-    }
-    for (r in botMapping.cloudConsole) {
-      if (id.match(r)) {
-        ssh = `SSH via Cloud Console (see above)`;
-        break;
-      }
-    }
-    if (ifBroken && !ssh) {
-      ssh = "unreachable by ssh";
-    }
-
-    drawExtraTableEntries(location, ssh, ifBroken);
-  }, 100);
-
-});
-
-})();
\ No newline at end of file
diff --git a/chromeextensions/skia-dev/hashtag.js b/chromeextensions/skia-dev/hashtag.js
new file mode 100644
index 0000000..7256240
--- /dev/null
+++ b/chromeextensions/skia-dev/hashtag.js
@@ -0,0 +1,47 @@
+// Copyright (c) 2019 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.
+
+function processTextNode(textNode) {
+  let origText = textNode.textContent;
+  // Replace all hashtags with links to hashtag.skia.org.
+  let newText = origText.replace(/(^|\s)#(\S+)/g,
+                                 '$1<a href="https://hashtag.skia.org/?hashtag=$2">#$2</a>');
+  if( newText !== origText) {
+      console.debug(`hashtag.js changing '${origText}' to '${newText}'`);
+      const newSpan = document.createElement('span');
+      newSpan.innerHTML = newText;
+      textNode.parentNode.replaceChild(newSpan, textNode);
+  }
+}
+
+function processHTML() {
+  // Create a TreeWalker to accept non-empty text nodes that are not children of
+  // <script> or <style>.
+  const treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
+    acceptNode: (node) => {
+      if (node.nodeName !== '#text'
+            || node.parentNode.nodeName === 'SCRIPT'
+            || node.parentNode.nodeName === 'STYLE') {
+        // Skip text nodes that are not children of <script> or <style>.
+        return NodeFilter.FILTER_SKIP;
+      } else if(node.textContent.length === 0) {
+        // Skip empty text nodes.
+        return NodeFilter.FILTER_SKIP;
+      } else {
+        return NodeFilter.FILTER_ACCEPT;
+      }
+    }
+  }, false );
+
+  // Gather all text nodes to process. Do not modify them yet or the treeWalker
+  // will become invalid.
+  const nodeList = [];
+  while (treeWalker.nextNode()) {
+    nodeList.push(treeWalker.currentNode);
+  }
+  // Now iterate over all text nodes looking for hashtags.
+  nodeList.forEach((el) => processTextNode(el));
+}
+
+processHTML();
diff --git a/chromeextensions/skia-dev/manifest.json b/chromeextensions/skia-dev/manifest.json
index 0f1a1cd..e83e059 100644
--- a/chromeextensions/skia-dev/manifest.json
+++ b/chromeextensions/skia-dev/manifest.json
@@ -1,6 +1,6 @@
 {
    "name": "Assorted Tools for Skia Developers",
-   "version": "3.6",
+   "version": "3.7",
    "description": "Assorted Tools for Skia Developers.",
    "permissions": [
      "notifications",
@@ -10,15 +10,16 @@
      "*://skia.org/*",
      "https://gold.skia.org/",
      "https://perf.skia.org/",
-     "https://promalerts.skia.org/",
      "http://skia-tree-status.appspot.com/",
      "*://skia-review.googlesource.com/",
-     "https://chromium-swarm.appspot.com/*"
+     "*://skia.googlesource.com/*",
+     "*://github.com/google/skia*"
    ],
    "background": {
      "scripts": [
        "node_modules/skia-common-js/common.js",
-       "background.js"]
+       "background.js"
+     ]
    },
    "browser_action": {
      "name": "Display Skia status.",
@@ -27,19 +28,9 @@
    },
    "content_scripts": [
     {
-      "matches": ["https://chromium-swarm.appspot.com/task?*"],
-      "css": ["styles.css"],
-      "js": [
-        "tasks.js"
-      ]
-    },
-    {
-      "matches": ["https://chromium-swarm.appspot.com/bot?*"],
-      "css": ["styles.css"],
-      "js": [
-        "bot_mapping.js",
-        "bots.js"
-      ]
+      "matches": ["*://skia.googlesource.com/*", "*://github.com/google/skia*"],
+      "js": ["hashtag.js"],
+      "run_at": "document_end"
     }
   ],
    "icons": {
diff --git a/chromeextensions/skia-dev/popup.html b/chromeextensions/skia-dev/popup.html
index 4c0baae..56b0c80 100644
--- a/chromeextensions/skia-dev/popup.html
+++ b/chromeextensions/skia-dev/popup.html
@@ -108,37 +108,7 @@
           Untriaged Images: <span id="image-alerts"></span>
         </td>
         <td>
-          Infra Alerts: <span id="infra-alerts"></span>
-        </td>
-      </tr>
-      <tr>
-        <td>
-          <b>Chromium Roll</b>
-          <br/>
-          Current: <span id="current-cr-roll"></span>
-          <br/>
-          Last: <span id="last-cr-roll"></span>
-        </td>
-        <td>
-          <b>Android Roll</b>
-          <br/>
-          Current: <span id="current-android-roll"></span>
-          <br/>
-          Last: <span id="last-android-roll"></span>
-        </td>
-        <td>
-          <b>Fuchsia Roll</b>
-          <br/>
-          Current: <span id="current-fuchsia-roll"></span>
-          <br/>
-          Last: <span id="last-fuchsia-roll"></span>
-        </td>
-        <td>
-          <b>Google3 Roll</b>
-          <br/>
-          Current: <span id="current-g3-roll"></span>
-          <br/>
-          Last: <span id="last-g3-roll"></span>
+          <a href="https://am.skia.org/?tab=1">Infra Alerts</a>
         </td>
       </tr>
     </table>
diff --git a/chromeextensions/skia-dev/popup.js b/chromeextensions/skia-dev/popup.js
index 0bc5fc9..8dcba24 100644
--- a/chromeextensions/skia-dev/popup.js
+++ b/chromeextensions/skia-dev/popup.js
@@ -81,93 +81,6 @@
     });
   }
 
-  function setCrRollStatus() {
-    var rollURL = 'https://autoroll.skia.org/json/status';
-    sk.get(rollURL).then(function(resp) {
-      var json = JSON.parse(resp);
-      var currentStatus = json.currentRoll ? json.currentRoll.result : 'up to date';
-      document.getElementById('current-cr-roll').innerHTML =
-          linkify(currentStatus, 'https://autoroll.skia.org/');
-      document.getElementById('last-cr-roll').innerHTML =
-          linkify(json.lastRoll.result, 'https://autoroll.skia.org/');
-    }).catch(function() {
-      document.getElementById('errors').innerHTML += 'Error connecting to Cr autoroller</br>';
-    });
-  }
-
-  function setAndroidRollStatus() {
-    var rollURL = 'https://storage.googleapis.com/skia-android-autoroller/roll-status.json';
-    sk.get(rollURL).then(function(resp) {
-      var rolls = JSON.parse(resp).rolls;
-      var currentStatus = rolls[0].status === 'succeeded' ? 'up to date' : rolls[0].status;
-      var linkToRolls = 'https://googleplex-android-review.git.corp.google.com/q/' +
-                        'owner:31977622648%40project.gserviceaccount.com';
-      document.getElementById('current-android-roll').innerHTML =
-          linkify(currentStatus, linkToRolls);
-      document.getElementById('last-android-roll').innerHTML =
-          linkify(rolls[1].status, linkToRolls);
-    }).catch(function() {
-      document.getElementById('errors').innerHTML += 'Error connecting to Android autoroller</br>';
-    });
-  }
-
-  function setFuchsiaRollStatus() {
-    var rollURL = 'https://fuchsia-roll.skia.org/json/status';
-    sk.get(rollURL).then(function(resp) {
-      var json = JSON.parse(resp);
-      var currentStatus = json.currentRoll ? json.currentRoll.result : 'up to date';
-      document.getElementById('current-fuchsia-roll').innerHTML =
-          linkify(currentStatus, 'https://fuchsia-roll.skia.org/');
-      document.getElementById('last-fuchsia-roll').innerHTML =
-          linkify(json.lastRoll.result, 'https://fuchsia-roll.skia.org/');
-    }).catch(function() {
-      document.getElementById('errors').innerHTML += 'Error connecting to Fuchsia autoroller</br>';
-    });
-  }
-
-  function setGoogle3RollStatus() {
-    var rollURL = 'https://google3-roll.skia.org/json/status';
-    sk.get(rollURL).then(function(resp) {
-      var json = JSON.parse(resp);
-      var currentStatus = json.currentRoll ? json.currentRoll.result : 'up to date';
-      document.getElementById('current-g3-roll').innerHTML =
-          linkify(currentStatus, 'https://google3-roll.skia.org/');
-      document.getElementById('last-g3-roll').innerHTML =
-          linkify(json.lastRoll.result, 'https://google3-roll.skia.org/');
-    }).catch(function() {
-      document.getElementById('errors').innerHTML += 'Error connecting to G3 autoroller</br>';
-    });
-  }
-
-  function setInfraAlerts() {
-    var alertsURL = 'https://promalerts.skia.org/api/v1/alerts/groups';
-    sk.get(alertsURL).then(function(resp) {
-      var activeInfraAlerts = 0;
-      var json = JSON.parse(resp);
-      var alertGroups = json.data;
-      alertGroups.forEach(function(alertGroup) {
-        if (!alertGroup.blocks) {
-          return;
-        }
-        alertGroup.blocks.forEach(function(block) {
-          block.alerts.forEach(function(al) {
-            if (al.labels.category != "infra") {
-              return;
-            }
-            if (al.silenced) {
-              return;
-            }
-            activeInfraAlerts++;
-          });
-        });
-      });
-      document.getElementById('infra-alerts').innerHTML =
-        linkify(activeInfraAlerts, 'http://alerts.skia.org/infra');
-    }).catch(function() {
-      document.getElementById('errors').innerHTML += 'Error connecting to alertserver</br>';
-    });
-  }
-
   function addGerritChanges() {
     var username = loggedInAs();
     if (!username) {
@@ -329,11 +242,6 @@
     setTrooperSheriffRobocopWrangler();
     setPerfAlerts();
     setGoldAlerts();
-    setCrRollStatus();
-    setAndroidRollStatus();
-    setFuchsiaRollStatus();
-    setGoogle3RollStatus();
-    setInfraAlerts();
     addGerritChanges();
   }
 
diff --git a/chromeextensions/skia-dev/styles.css b/chromeextensions/skia-dev/styles.css
deleted file mode 100644
index c473c86..0000000
--- a/chromeextensions/skia-dev/styles.css
+++ /dev/null
@@ -1,8 +0,0 @@
-.outline > td {
-    border: 1px solid #BBB;
-    padding: 5px;
-}
-
-main a[href] {
-    color: #1F78B4;
-}
\ No newline at end of file
diff --git a/chromeextensions/skia-dev/tasks.js b/chromeextensions/skia-dev/tasks.js
deleted file mode 100644
index fb1af6b..0000000
--- a/chromeextensions/skia-dev/tasks.js
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) 2016 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.
-
-(function(){
-
-function addTableEntry(elem, title, linkHref, linkText) {
-  if (elem.children.length != 2){
-    elem.innerHTML = "";
-    var newCol = document.createElement("td");
-    elem.appendChild(newCol);
-    newCol = document.createElement("td");
-    var newLink = document.createElement("a");
-    newCol.appendChild(newLink);
-    elem.appendChild(newCol);
-  }
-
-  var first = elem.children[0];
-  var link = elem.children[1].children[0];
-
-  first.innerText = title;
-
-  link.href = linkHref;
-  link.innerText = linkText;
-  link.rel = "noopener";
-  link.target = "_blank";
-}
-
-function drawExtraTableEntries(jobID, gerritIssue, gerritPatchSet, sourceRepo, sourceRevision, retryOf, parentTaskId, thisTaskId) {
-  var elem = document.getElementById("extra_task_scheduler");
-  if (jobID) {
-    var link = "https://task-scheduler.skia.org/job/" + jobID;
-    addTableEntry(elem, "Forced Job ID", link, jobID);
-  } else {
-    elem.innerHTML = "";
-  }
-
-  elem = document.getElementById("extra_gerrit");
-  if (gerritIssue && gerritPatchSet) {
-    var link = `https://skia-review.googlesource.com/c/${gerritIssue}/${gerritPatchSet}`;
-    addTableEntry(elem, "Associated CL", link, "review.skia.org/" + gerritIssue);
-  } else {
-    elem.innerHTML = "";
-  }
-
-  elem = document.getElementById("extra_commit");
-  if (sourceRepo && sourceRevision && gerritIssue && gerritPatchSet) {
-    var key = "Commit Patchset Was Applied To";
-    var link = sourceRepo.replace("%s", sourceRevision);
-    var shortRevision = sourceRevision.substring(0, 12);
-    addTableEntry(elem, key, link, shortRevision);
-  } else {
-    elem.innerHTML = "";
-  }
-
-  elem = document.getElementById("extra_retry_of");
-  if (retryOf) {
-    var link = `https://chromium-swarm.appspot.com/tasklist?c=name&c=state&c=created_ts&c=user&f=sk_id%3A${retryOf}&l=10&s=created_ts%3Adesc`;
-    addTableEntry(elem, "Retry of Task", link, retryOf);
-  } else {
-    elem.innerHTML = "";
-  }
-
-  elem = document.getElementById("extra_depends_on");
-  if (parentTaskId) {
-    var link = `https://chromium-swarm.appspot.com/tasklist?c=name&c=state&c=created_ts&c=user&f=sk_id%3A${parentTaskId}&l=10&s=created_ts%3Adesc`;
-    addTableEntry(elem, "Parent Task", link, parentTaskId);
-  } else {
-    elem.innerHTML = "";
-  }
-
-  elem = document.getElementById("extra_dependents");
-  if (thisTaskId) {
-    var link = `https://chromium-swarm.appspot.com/tasklist?c=name&c=state&c=created_ts&c=user&f=sk_parent_task_id%3A${thisTaskId}&l=10&s=created_ts%3Adesc`;
-    addTableEntry(elem, "Child Tasks", link, "Find in Task List");
-  } else {
-    elem.innerHTML = "";
-  }
-}
-
-function createExtraRow(parentNode, newID) {
-  var newRow = document.createElement("tr");
-  newRow.className = "outline";
-  newRow.id = newID;
-  parentNode.appendChild(newRow);
-}
-
-window.addEventListener("WebComponentsReady", function(e) {
-  // Create our extra <tr> elements.
-  var moreDetails = document.getElementById("more_details");
-  createExtraRow(moreDetails.parentNode, "extra_task_scheduler");
-  createExtraRow(moreDetails.parentNode, "extra_gerrit");
-  createExtraRow(moreDetails.parentNode, "extra_commit");
-  createExtraRow(moreDetails.parentNode, "extra_retry_of");
-  createExtraRow(moreDetails.parentNode, "extra_depends_on");
-  createExtraRow(moreDetails.parentNode, "extra_dependents");
-
-  window.setInterval(function(){
-    var id = document.querySelector(".swarming-app input").value;
-
-    if (!id) {
-      return;
-    }
-
-    // a map of all task tags_
-    var skData = {};
-
-    // The tbody #more_details has all of the tags in it, which are in the form
-    // tag:value.  We iterate through all of the tds looking for those that
-    // are needed to render our extra <tr> elements.
-    var cells = moreDetails.getElementsByTagName("td");
-    for (var i = 0; i < cells.length; i++) {
-      var content = cells[i].textContent.trim();
-      var split = content.split(":", 1);
-      var tag = split[0];
-      var rest = content.substring(tag.length + 1);
-      skData[tag] = rest;
-    }
-
-    drawExtraTableEntries(
-        skData.sk_forced_job_id,
-        skData.sk_issue,
-        skData.sk_patchset,
-        skData.source_repo,
-        skData.source_revision,
-        skData.sk_retry_of,
-        skData.sk_parent_task_id,
-        skData.sk_id);
-  }, 100);
-
-});
-})();
-