function sanitizeCssSelectors(selectors, suffix) {
  // Produce two distinct lists of selectors to sequester selectors that are
  // history sensitive (:visited), so that we can disallow properties in the
  // property groups for the history sensitive ones.
  var historySensitiveSelectors = [];
  var historyInsensitiveSelectors = [];

  // Remove any spaces that are not operators.
  var k = 0, i;
  for (i = 0; i < selectors.length; ++i) {
    if (!(selectors[i] == ' '
          && (selectors[i-1] == '>' || selectors[i+1] == '>'))) {
      selectors[k++] = selectors[i];
    }
  }
  selectors.length = k;

  // Split around commas.  If there is an error in one of the comma separated
  // bits, we throw the whole away, but the failure of one selector does not
  // affect others.
  var n = selectors.length, start = 0;
  for (i = 0; i < n; ++i) {
    if (selectors[i] == ',') {
      processSelector(start, i);
      start = i+1;
    }
  }
  processSelector(start, n);


  function processSelector(start, end) {
    var historySensitive = false;

    // Space around commas is not an operator.
    if (selectors[start] === ' ') { ++start; }
    if (end-1 !== start && selectors[end] === ' ') { --end; }

    // Split the selector into element selectors, content around
    // space (ancestor operator) and '>' (descendant operator).
    var out = [];
    var lastOperator = start;
    var elSelector = '';
    for (var i = start; i < end; ++i) {
      var tok = selectors[i];
      var isChild = (tok === '>');
      if (isChild || tok === ' ') {
        // We've found the end of a single link in the selector chain.
        // We disallow absolute positions relative to html.
        elSelector = processElementSelector(lastOperator, i, false);
        if (!elSelector || (isChild && /^html/i.test(elSelector))) {
          return;
        }
        lastOperator = i+1;
        out.push(elSelector, isChild ? ' > ' : ' ');
      }
    }
    elSelector = processElementSelector(lastOperator, end, true);
    if (!elSelector) { return; }
    out.push(elSelector);

    function processElementSelector(start, end, last) {
      var debugStart = start, debugEnd = end;

      // Split the element selector into three parts.
      // DIV.foo#bar:hover
      //    ^       ^
      // el classes pseudo
      var element, classId, pseudoSelector, tok, elType;
      element = '';
      if (start < end) {
        tok = selectors[start].toLowerCase();
        if (tok === '*'
            || (tok === 'body' && start+1 !== end && !last)
            || ('number' === typeof (elType = html4.ELEMENTS[tok])
                && !(elType & html4.eflags.UNSAFE))) {
          ++start;
          element = tok;
        }
      }
      classId = '';
      while (start < end) {
        tok = selectors[start];
        if (tok.charAt(0) === '#') {
          if (/^#_|__$|[^#0-9A-Za-z:_\-]/.test(tok)) { return null; }
          // Rewrite ID elements to include the suffix.
          classId += tok + '-' + suffix;
        } else if (tok === '.') {
          if (++start < end
              && /^[0-9A-Za-z:_\-]+$/.test(tok = selectors[start])
              && !/^_|__$/.test(tok)) {
            classId += '.' + tok;
          } else {
            return null;
          }
        } else {
          break;
        }
        ++start;
      }
      pseudoSelector = '';
      if (start < end && selectors[start] === ':') {
        tok = selectors[++start];
        if (tok === 'visited' || tok === 'link') {
          if (!/^[a*]?$/.test(element)) {
            return null;
          }
          historySensitive = true;
          pseudoSelector = ':' + tok;
          element = 'a';
          ++start;
        }
      }
      if (start === end) {
        return element + classId + pseudoSelector;
      }
      return null;
    }


    var safeSelector = out.join('');
    if (/^body\b/.test(safeSelector)) {
      // Substitute the class that is attached to pseudo body elements for
      // the body element.
      safeSelector = '.vdoc-body___.' + suffix + safeSelector.substring(4);
    } else {
      // Namespace the selector so that it only matches under
      // a node with suffix in its CLASS attribute.
      safeSelector = '.' + suffix + ' ' + safeSelector;
    }

    (historySensitive
     ? historySensitiveSelectors
     : historyInsensitiveSelectors).push(safeSelector);
  }

  return [historyInsensitiveSelectors, historySensitiveSelectors];
}
Given a series of tokens, returns two lists of sanitized selectors.
@param {Array.<string>} selectors In the form produces by csslexer.js.
@param {string} suffix a suffix that is added to all IDs and which is
   used as a CLASS names so that the returned selectors will only match
   nodes under one with suffix as a class name.
   If suffix is {@code "sfx"}, the selector
   {@code ["a", "#foo", " ", "b", ".bar"]} will be namespaced to
   {@code [".sfx", " ", "a", "#foo-sfx", " ", "b", ".bar"]}.
@return {Array.<Array.<string>>} an array of length 2 where the zeroeth
   element contains history-insensitive selectors and the first element
   contains history-sensitive selectors.