const SIMILARITY_URL = 'https://tools.wmflabs.org/similarity/search';

let SuggestionsCache = new (function() {
  const MAX_CACHE_SIZE = 10;
  const ENTRY_TTL_S = 10 * 60;

  this._cache = [];

  this._expire = () => {
    this._cache = this._cache.filter((entry) => {
      return ((new Date() - entry.date) / 1000) < ENTRY_TTL_S;
    });
  }

  this.get = key => {
    this._expire();
    for (let i = 0; i < this._cache.length; i++) {
      let entry = this._cache[i];
      if (entry.key === key) {
        return entry.value;
      }
    }
    return undefined;
  };

  this.set = (key, value) => {
    this._expire();
    if (this._cache.length == MAX_CACHE_SIZE) {
      this._cache.shift();
    }
    this._cache = this._cache.filter(entry => entry.key !== key);
    this._cache.push({key: key, value: value, date: new Date()});
  };
});

function getDocumentContents() {
  return new Promise((resolve, reject) => {
    let script = 'document.documentElement.outerHTML';
    chrome.tabs.executeScript({
      code: script
    }, (result) => { resolve(result[0]); });
  });
}

function fetchSimilarArticles(html, url) {
  let formData = new FormData();
  formData.append('html', html);
  formData.append('url', url);
  let o = {method: 'POST', mode: 'cors', body: formData};
  return window.fetch(SIMILARITY_URL, o).then(
    (response) => { return response.json() }).then(
    (response) => {
      console.log(response['debug']);
      return response['results'];
    });
}

function getSuggestionsForTab(tab, options) {
  return new Promise((resolve, reject) => {
    if (!tab.url) {
      reject('No URL!');
      return;
    }

    let whitelist = loadURLsWhitelist();
    let match = whitelist.some((w) => {
      return tab.url.match(RegExp(w));
    });
    if (!match) {
      resolve([]);
      return;
    };

    if (!options || options.canUseCache) {
      let cached = SuggestionsCache.get(tab.url);
      if (cached) {
        resolve(cached);
        return;
      }
    }

    resolve(getDocumentContents().then((html) => {
      return fetchSimilarArticles(html, tab.url);
    }).then((suggestions) => {
      SuggestionsCache.set(tab.url, suggestions);
      return suggestions;
    }));
  });
}

function getSuggestionsAndUpdateUI(tab, options) {
  getSuggestionsForTab(tab, options).then((articles) => {
    if (articles.length) {
      chrome.pageAction.show(tab.id);
    } else {
      chrome.pageAction.hide(tab.id);
    }
  }).catch((e) => {
    console.log(e);
    chrome.pageAction.hide(tab.id);
  });
}

chrome.tabs.onActivated.addListener((activeInfo) => {
  chrome.tabs.get(activeInfo.tabId, (tab) => {
    getSuggestionsAndUpdateUI(tab);
  });
});

chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  // Note: this event can happen multiple times, even after the page loads,
  // and changeInfo.status can even be undefined (despite being documented
  // as only ever being either 'loading' or 'complete').
  if (changeInfo.status == 'complete') {
    getSuggestionsAndUpdateUI(tab, {canUseCache: false});
  }
});

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
    getSuggestionsForTab(tabs[0]).then(sendResponse);
  });
  return true;
});

@@ -138,210 +138,28 @@
 10;%0A
-  const ENTRY_TTL_S = 10 * 60;%0A%0A  this._cache = %5B%5D;%0A%0A  this._expire = () =%3E %7B%0A    this._cache = this._cache.filter((entry) =%3E %7B%0A      return ((new Date() - entry.date) / 1000) %3C ENTRY_TTL_S;%0A    %7D);%0A  %7D
+%0A  this._cache = %5B%5D;
 %0A%0A  
@@ -178,36 +178,16 @@
 ey =%3E %7B%0A
-    this._expire();%0A
     for 
@@ -399,28 +399,8 @@
 %3E %7B%0A
-    this._expire();%0A
     
@@ -489,128 +489,165 @@
 his.
-_cache = this._cache.filter(entry =%3E entry.key !== key);%0A    this._cache.push(%7Bkey: key, value: value, date: new Date()%7D
+remove(key);%0A    this._cache.push(%7Bkey: key, value: value%7D);%0A  %7D;%0A%0A  this.remove = (key) =%3E %7B%0A    this._cache = this._cache.filter(entry =%3E entry.key !== key
 );%0A 
@@ -1718,27 +1718,26 @@
 che.get(tab.
-url
+id
 );%0A      if 
@@ -1966,19 +1966,18 @@
 set(tab.
-url
+id
 , sugges
@@ -2488,24 +2488,122 @@
 %0A  %7D);%0A%7D);%0A%0A
+chrome.tabs.onRemoved.addListener((tabId, removeInfo) =%3E %7B%0A  SuggestionsCache.remove(tabId);%0A%7D);%0A%0A
 chrome.tabs.

Remove cached suggestions when the url changes/tab closes, not by time.