| Index: lib/filterValidation.js | 
| =================================================================== | 
| --- a/lib/filterValidation.js | 
| +++ b/lib/filterValidation.js | 
| @@ -19,58 +19,75 @@ | 
|  | 
| let {Filter, InvalidFilter, ElemHideBase} = require("filterClasses"); | 
|  | 
| +function isValidCSSSelector(selector) | 
| +{ | 
| +  let style = document.createElement("style"); | 
| +  document.documentElement.appendChild(style); | 
| +  let sheet = style.sheet; | 
| +  document.documentElement.removeChild(style); | 
| + | 
| +  try | 
| +  { | 
| +    document.querySelector(selector); | 
| +    sheet.insertRule(selector + "{}", 0); | 
| +  } | 
| +  catch (e) | 
| +  { | 
| +    return false; | 
| +  } | 
| +  return true; | 
| +} | 
| + | 
| +/** | 
| + * @typedef ParsedFilter | 
| + * @property {?Filter} [filter]  The parsed filter if it is valid. Or null if | 
| + *                               the given string is empty or a filter list header. | 
| + * @property {string} [error]    An error message indicated that the filter cannot | 
| + *                               be parsed or contains an invalid CSS selector. | 
| + */ | 
| + | 
| +let parseFilter = | 
| /** | 
| * Parses and validates a filter given by the user. | 
| * | 
| * @param {string}  text | 
| - * @param {Boolean} [ignoreHeaders=false]  If true, no exception is raised | 
| - *                                         for filter list headers, instead | 
| - *                                         the function will return null. | 
| - * @return {Filter} | 
| - * @throws Will throw an exception if filter cannot be | 
| - *         parsed or contains an invalid CSS selector. | 
| - * @static | 
| + * @param {Boolean} [ignoreHeaders=false]  If true, {filter: null} is | 
| +                                           returned instead an error | 
| +                                           for filter list headers. | 
| + * @return {ParsedFilter} | 
| */ | 
| -function parseFilter(text, ignoreHeaders) | 
| +exports.parseFilter = function(text, ignoreHeaders) | 
| { | 
| +  let filter = null; | 
| text = Filter.normalize(text); | 
| -  if (!text) | 
| -    return null; | 
|  | 
| -  if (text[0] == "[") | 
| +  if (text) | 
| { | 
| -    if (ignoreHeaders) | 
| -      return null; | 
| +    if (text[0] != "[") | 
| +    { | 
| +      filter = Filter.fromText(text); | 
|  | 
| -    throw ext.i18n.getMessage("unexpected_filter_list_header"); | 
| -  } | 
| +      if (filter instanceof InvalidFilter) | 
| +        return {error: filter.reason}; | 
|  | 
| -  let filter = Filter.fromText(text); | 
| - | 
| -  if (filter instanceof InvalidFilter) | 
| -    throw filter.reason; | 
| - | 
| -  if (filter instanceof ElemHideBase) | 
| -  { | 
| -    let style = document.createElement("style"); | 
| -    document.documentElement.appendChild(style); | 
| -    let sheet = style.sheet; | 
| -    document.documentElement.removeChild(style); | 
| - | 
| -    try | 
| +      if (filter instanceof ElemHideBase && !isValidCSSSelector(filter.selector)) | 
| +        return {error: ext.i18n.getMessage("invalid_css_selector", "'" + filter.selector + "'")}; | 
| +    } | 
| +    else if (!ignoreHeaders) | 
| { | 
| -      document.querySelector(filter.selector); | 
| -      sheet.insertRule(filter.selector + "{}", 0); | 
| -    } | 
| -    catch (error) | 
| -    { | 
| -      throw ext.i18n.getMessage("invalid_css_selector", "'" + filter.selector + "'"); | 
| +      return {error: ext.i18n.getMessage("unexpected_filter_list_header")}; | 
| } | 
| } | 
|  | 
| -  return filter; | 
| -} | 
| -exports.parseFilter = parseFilter; | 
| +  return {filter: filter}; | 
| +}; | 
| + | 
| +/** | 
| + * @typedef ParsedFilters | 
| + * @property {Filter[]} [filters]  The parsed filters if all of them are valid. | 
| + * @property {string} [error]      An error message indicated that any filter cannot | 
| + *                                 be parsed or contains an invalid CSS selector. | 
| + */ | 
|  | 
| /** | 
| * Parses and validates a newline-separated list of filters given by the user. | 
| @@ -78,10 +95,8 @@ | 
| * @param {string}  text | 
| * @param {Boolean} [ignoreHeaders=false]  If true, filter list headers | 
| *                                         will be stripped instead of | 
| - *                                         raising an exception. | 
| - * @return {Filter[]} | 
| - * @throws Will throw an exception if one of the filters cannot | 
| -           be parsed or contains an invalid CSS selector. | 
| + *                                         returning an error. | 
| + * @return {ParsedFilters} | 
| */ | 
| exports.parseFilters = function(text, ignoreHeaders) | 
| { | 
| @@ -90,19 +105,14 @@ | 
|  | 
| for (let i = 0; i < lines.length; i++) | 
| { | 
| -    let filter; | 
| -    try | 
| -    { | 
| -      filter = parseFilter(lines[i], ignoreHeaders); | 
| -    } | 
| -    catch (error) | 
| -    { | 
| -      throw ext.i18n.getMessage("line", (i + 1).toString()) + ": " + error; | 
| -    } | 
| +    let {filter, error} = parseFilter(lines[i], ignoreHeaders); | 
| + | 
| +    if (error) | 
| +      return {error: ext.i18n.getMessage("line", (i + 1).toString()) + ": " + error}; | 
|  | 
| if (filter) | 
| filters.push(filter); | 
| } | 
|  | 
| -  return filters; | 
| +  return {filters: filters}; | 
| }; | 
|  |