| Index: lib/matcher.js |
| =================================================================== |
| --- a/lib/matcher.js |
| +++ b/lib/matcher.js |
| @@ -235,30 +235,50 @@ |
| { |
| let map = this._filterMapsByType.get(type); |
| if (!map) |
| this._filterMapsByType.set(type, map = new Map()); |
| addFilterByKeyword(filter, keyword, map); |
| } |
| + let {domains} = filter; |
| + |
| let filtersByDomain = this._filterDomainMapsByKeyword.get(keyword); |
| if (!filtersByDomain) |
| - this._filterDomainMapsByKeyword.set(keyword, filtersByDomain = new Map()); |
| + { |
| + // In most cases, there is only one pure generic filter to a keyword. |
| + // Instead of Map { "foo" => Map { "" => Map { filter => true } } }, we |
| + // can just reduce it to Map { "foo" => filter } and save a lot of |
| + // memory. |
| + if (!domains) |
| + { |
| + this._filterDomainMapsByKeyword.set(keyword, filter); |
| + return; |
| + } |
| - for (let [domain, include] of filter.domains || defaultDomains) |
| + filtersByDomain = new Map(); |
| + this._filterDomainMapsByKeyword.set(keyword, filtersByDomain); |
| + } |
| + else if (!(filtersByDomain instanceof Map)) |
| + { |
| + filtersByDomain = new Map([["", filtersByDomain]]); |
| + this._filterDomainMapsByKeyword.set(keyword, filtersByDomain); |
| + } |
| + |
| + for (let [domain, include] of domains || defaultDomains) |
| { |
| if (!include && domain == "") |
| continue; |
| let map = filtersByDomain.get(domain); |
| if (!map) |
| { |
| filtersByDomain.set(domain, include ? filter : |
| - map = new Map([[filter, false]])); |
| + map = new Map([[filter, false]])); |
| } |
| else if (map.size == 1 && !(map instanceof Map)) |
| { |
| if (filter != map) |
| { |
| filtersByDomain.set(domain, new Map([[map, true], |
| [filter, include]])); |
| } |
| @@ -296,35 +316,74 @@ |
| let map = this._filterMapsByType.get(type); |
| if (map) |
| removeFilterByKeyword(filter, keyword, map); |
| } |
| let filtersByDomain = this._filterDomainMapsByKeyword.get(keyword); |
| if (filtersByDomain) |
| { |
| + // Because of the memory optimization in the add function, most of the |
| + // time this will be a filter rather than a map. |
| + if (!(filtersByDomain instanceof Map)) |
| + { |
| + this._filterDomainMapsByKeyword.delete(keyword); |
| + return; |
| + } |
| + |
| let domains = filter.domains || defaultDomains; |
| for (let domain of domains.keys()) |
| { |
| let map = filtersByDomain.get(domain); |
| if (map) |
| { |
| if (map.size > 1 || map instanceof Map) |
| { |
| map.delete(filter); |
| if (map.size == 0) |
| + { |
| filtersByDomain.delete(domain); |
| + } |
| + else if (map.size == 1) |
| + { |
| + for (let [lastFilter, include] of map) |
| + { |
| + // Reduce Map { "example.com" => Map { filter => true } } to |
| + // Map { "example.com" => filter } |
| + if (include) |
| + filtersByDomain.set(domain, lastFilter); |
| + |
| + break; |
| + } |
| + } |
| } |
| else if (filter == map) |
| { |
| filtersByDomain.delete(domain); |
| } |
| } |
| } |
| + |
| + if (filtersByDomain.size == 0) |
| + { |
| + this._filterDomainMapsByKeyword.delete(keyword); |
| + } |
| + else if (filtersByDomain.size == 1) |
| + { |
| + for (let [lastDomain, map] of filtersByDomain) |
| + { |
| + // Reduce Map { "foo" => Map { "" => filter } } to |
| + // Map { "foo" => filter } |
| + if (lastDomain == "" && !(map instanceof Map)) |
| + this._filterDomainMapsByKeyword.set(keyword, map); |
| + |
| + break; |
| + } |
| + } |
| } |
| } |
| /** |
| * Chooses a keyword to be associated with the filter |
| * @param {Filter} filter |
| * @returns {string} keyword or an empty string if no keyword could be found |
| * @protected |
| @@ -419,18 +478,31 @@ |
| } |
| _checkEntryMatchByDomain(keyword, location, typeMask, docDomain, thirdParty, |
| sitekey, specificOnly, collection) |
| { |
| let filtersByDomain = this._filterDomainMapsByKeyword.get(keyword); |
| if (filtersByDomain) |
| { |
| - // The code in this block is similar to the generateStyleSheetForDomain |
| - // function in lib/elemHide.js. |
| + // Because of the memory optimization in the add function, most of the |
| + // time this will be a filter rather than a map. |
| + if (!(filtersByDomain instanceof Map)) |
| + { |
| + if (filtersByDomain.matchesWithoutDomain(location, typeMask, |
| + thirdParty, sitekey)) |
| + { |
| + if (!collection) |
| + return filtersByDomain; |
| + |
| + collection.push(filtersByDomain); |
| + } |
| + |
| + return null; |
| + } |
| if (docDomain) |
| { |
| if (docDomain[docDomain.length - 1] == ".") |
| docDomain = docDomain.replace(/\.+$/, ""); |
| docDomain = docDomain.toLowerCase(); |
| } |