Add profile indicator feature

This experimental feature adds an indicator dot next to the OP's
username in order to indicate whether the OP has participated in more
threads in the same forum.

The dot can indicate the following states:
* Blue: the OP has only participated in the current thread.
* Orange: the OP has participated in other threads, but at least the
last 5 ones have been read by the user.
* Red: the OP has participated in other threads, some of which haven't
yet been read by the user.

Change-Id: Ib358d88dacfe0e713247d7757cf7eea1a2b4f8e9
diff --git a/src/console_inject.js b/src/console_inject.js
index 646beba..8858baf 100644
--- a/src/console_inject.js
+++ b/src/console_inject.js
@@ -62,48 +62,73 @@
   });
 };
 
-function injectStyles(css) {
+function injectStylesheet(stylesheetName) {
   var link = document.createElement('link');
   link.setAttribute('rel', 'stylesheet');
-  link.setAttribute(
-      'href', 'data:text/css;charset=UTF-8,' + encodeURIComponent(css));
+  link.setAttribute('href', stylesheetName);
   document.head.appendChild(link);
 }
 
+function injectStyles(css) {
+  injectStylesheet('data:text/css;charset=UTF-8,' + encodeURIComponent(css));
+}
+
+function injectScript(scriptName) {
+  var script = document.createElement('script');
+  script.src = scriptName;
+  document.head.appendChild(script);
+}
+
 var observerOptions = {
   childList: true,
   attributes: true,
   subtree: true,
 }
 
-var intersectionOptions =
-    {
-      root: document.querySelector('.scrollable-content'),
-      rootMargin: '0px',
-      threshold: 1.0,
-    }
+var intersectionOptions = {
+  root: document.querySelector('.scrollable-content'),
+  rootMargin: '0px',
+  threshold: 1.0,
+};
 
-    chrome.storage.sync.get(null, function(items) {
-      options = items;
+chrome.storage.sync.get(null, function(items) {
+  options = items;
 
-      mutationObserver = new MutationObserver(mutationCallback);
-      mutationObserver.observe(
-          document.querySelector('.scrollable-content'), observerOptions);
+  mutationObserver = new MutationObserver(mutationCallback);
+  mutationObserver.observe(
+      document.querySelector('.scrollable-content'), observerOptions);
 
-      intersectionObserver =
-          new IntersectionObserver(intersectionCallback, intersectionOptions);
+  intersectionObserver =
+      new IntersectionObserver(intersectionCallback, intersectionOptions);
 
-      if (options.fixedtoolbar) {
-        injectStyles(
-            'ec-bulk-actions{position: sticky; top: 0; background: white; z-index: 96;}');
-      }
+  if (options.fixedtoolbar) {
+    injectStyles(
+        'ec-bulk-actions{position: sticky; top: 0; background: white; z-index: 96;}');
+  }
 
-      if (options.increasecontrast) {
-        injectStyles('.thread-summary.read{background: #ecedee!important;}');
-      }
+  if (options.increasecontrast) {
+    injectStyles('.thread-summary.read{background: #ecedee!important;}');
+  }
 
-      if (options.stickysidebarheaders) {
-        injectStyles(
-            'material-drawer .main-header{background: #fff; position: sticky; top: 0; z-index: 1;}');
-      }
+  if (options.stickysidebarheaders) {
+    injectStyles(
+        'material-drawer .main-header{background: #fff; position: sticky; top: 0; z-index: 1;}');
+  }
+
+  if (options.profileindicator) {
+    injectScript(chrome.runtime.getURL('console_profileindicator_inject.js'));
+    injectStylesheet(
+        chrome.runtime.getURL('console_profileindicator_inject.css'));
+
+    // In order to pass i18n strings to the injected script, which doesn't have
+    // access to the chrome.i18n API.
+    window.addEventListener('geti18nString', evt => {
+      var request = evt.detail;
+      var response = {
+        string: chrome.i18n.getMessage(request.msg),
+        requestId: request.id
+      };
+      window.dispatchEvent(new CustomEvent('sendi18nString', {detail: response}));
     });
+  }
+});