| Index: lib/requestBlocker.js | 
| =================================================================== | 
| rename from webrequest.js | 
| rename to lib/requestBlocker.js | 
| --- a/webrequest.js | 
| +++ b/lib/requestBlocker.js | 
| @@ -15,41 +15,28 @@ | 
| * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| */ | 
|  | 
| -var FilterNotifier = require("filterNotifier").FilterNotifier; | 
| -var RegExpFilter = require("filterClasses").RegExpFilter; | 
| -var platform = require("info").platform; | 
| -var devtools = require("devtools"); | 
| +/** @module requestBlocker */ | 
|  | 
| -ext.webRequest.getIndistinguishableTypes().forEach(function(types) | 
| +"use strict"; | 
| + | 
| +let {RegExpFilter, BlockingFilter} = require("filterClasses"); | 
| +let {defaultMatcher} = require("matcher"); | 
| +let {FilterNotifier} = require("filterNotifier"); | 
| +let {Prefs} = require("prefs"); | 
| +let {checkWhitelisted, getKey} = require("whitelisting"); | 
| +let {stringifyURL, extractHostFromFrame, isThirdParty} = require("url"); | 
| +let {port} = require("messaging"); | 
| +let devtools = require("devtools"); | 
| + | 
| +ext.webRequest.getIndistinguishableTypes().forEach(types => | 
| { | 
| -  for (var i = 1; i < types.length; i++) | 
| +  for (let i = 1; i < types.length; i++) | 
| RegExpFilter.typeMap[types[i]] = RegExpFilter.typeMap[types[0]]; | 
| }); | 
|  | 
| -FilterNotifier.addListener(function(action, arg) | 
| -{ | 
| -  switch (action) | 
| -  { | 
| -    case "filter.added": | 
| -    case "filter.removed": | 
| -    case "filter.disabled": | 
| -      // Only request blocking/whitelisting filters have | 
| -      // an effect on the webRequest handler behavior. | 
| -      if (!(arg instanceof RegExpFilter)) | 
| -        break; | 
| -    case "subscription.added": | 
| -    case "subscription.removed": | 
| -    case "subscription.disabled": | 
| -    case "subscription.updated": | 
| -    case "load": | 
| -      ext.webRequest.handlerBehaviorChanged(); | 
| -      break; | 
| -  } | 
| -}); | 
| - | 
| function onBeforeRequestAsync(page, url, type, docDomain, | 
| -                              thirdParty, key, specificOnly, | 
| -                              filter) | 
| +                              thirdParty, sitekey, | 
| +                              specificOnly, filter) | 
| { | 
| if (filter) | 
| FilterNotifier.triggerListeners("filter.hitCount", filter, 0, 0, page); | 
| @@ -57,64 +44,125 @@ | 
| if (devtools) | 
| devtools.logRequest( | 
| page, url, type, docDomain, | 
| -      thirdParty, key, specificOnly, | 
| -      filter | 
| +      thirdParty, sitekey, | 
| +      specificOnly, filter | 
| ); | 
| } | 
|  | 
| -function onBeforeRequest(url, type, page, frame) | 
| +ext.webRequest.onBeforeRequest.addListener((url, type, page, frame) => | 
| { | 
| if (checkWhitelisted(page, frame)) | 
| return true; | 
|  | 
| -  var urlString = stringifyURL(url); | 
| -  var docDomain = extractHostFromFrame(frame); | 
| -  var thirdParty = isThirdParty(url, docDomain); | 
| -  var key = getKey(page, frame); | 
| +  let urlString = stringifyURL(url); | 
| +  let docDomain = extractHostFromFrame(frame); | 
| +  let thirdParty = isThirdParty(url, docDomain); | 
| +  let sitekey = getKey(page, frame); | 
|  | 
| -  var specificOnly = !!checkWhitelisted( | 
| +  let specificOnly = !!checkWhitelisted( | 
| page, frame, RegExpFilter.typeMap.GENERICBLOCK | 
| ); | 
|  | 
| -  var filter = defaultMatcher.matchesAny( | 
| +  let filter = defaultMatcher.matchesAny( | 
| urlString, RegExpFilter.typeMap[type], | 
| -    docDomain, thirdParty, key, specificOnly | 
| +    docDomain, thirdParty, sitekey, specificOnly | 
| ); | 
|  | 
| setTimeout(onBeforeRequestAsync, 0, page, urlString, | 
| type, docDomain, | 
| -                                      thirdParty, key, | 
| +                                      thirdParty, sitekey, | 
| specificOnly, filter); | 
|  | 
| return !(filter instanceof BlockingFilter); | 
| -} | 
| +}); | 
|  | 
| -ext.webRequest.onBeforeRequest.addListener(onBeforeRequest); | 
| +port.on("filters.collapse", (message, sender) => | 
| +{ | 
| +  if (checkWhitelisted(sender.page, sender.frame)) | 
| +    return false; | 
|  | 
| -if (platform == "chromium") | 
| -{ | 
| -  function onHeadersReceived(details) | 
| +  let typeMask = RegExpFilter.typeMap[message.mediatype]; | 
| +  let documentHost = extractHostFromFrame(sender.frame); | 
| +  let sitekey = getKey(sender.page, sender.frame); | 
| +  let blocked = false; | 
| + | 
| +  let specificOnly = checkWhitelisted( | 
| +    sender.page, sender.frame, | 
| +    RegExpFilter.typeMap.GENERICBLOCK | 
| +  ); | 
| + | 
| +  for (let url of message.urls) | 
| { | 
| -    var page = new ext.Page({id: details.tabId}); | 
| -    var frame = ext.getFrame(details.tabId, details.frameId); | 
| +    let urlObj = new URL(url, message.baseURL); | 
| +    let filter = defaultMatcher.matchesAny( | 
| +      stringifyURL(urlObj), | 
| +      typeMask, documentHost, | 
| +      isThirdParty(urlObj, documentHost), | 
| +      sitekey, specificOnly | 
| +    ); | 
|  | 
| -    if (!frame || frame.url.href != details.url) | 
| -      return; | 
| - | 
| -    for (var i = 0; i < details.responseHeaders.length; i++) | 
| +    if (filter instanceof BlockingFilter) | 
| { | 
| -      var header = details.responseHeaders[i]; | 
| -      if (header.name.toLowerCase() == "x-adblock-key" && header.value) | 
| -        processKey(header.value, page, frame); | 
| +      if (filter.collapse != null) | 
| +        return filter.collapse; | 
| +      blocked = true; | 
| } | 
| } | 
|  | 
| -  chrome.webRequest.onHeadersReceived.addListener( | 
| -    onHeadersReceived, | 
| +  return blocked && Prefs.hidePlaceholders; | 
| +}); | 
| + | 
| +let ignoreFilterNotifications = false; | 
| +FilterNotifier.addListener((action, arg) => | 
| +{ | 
| +  // Avoid triggering filters.behaviorChanged multiple times | 
| +  // when multiple filter hanges happen at the same time. | 
| +  if (ignoreFilterNotifications) | 
| +    return; | 
| + | 
| +  if (action != "load") | 
| +  { | 
| +    let parts = action.split("."); | 
| +    let [category, event] = parts; | 
| +    if (category == "subscription") | 
| { | 
| -      urls: ["http://*/*", "https://*/*"], | 
| -      types: ["main_frame", "sub_frame"] | 
| -    }, | 
| -    ["responseHeaders"] | 
| -  ); | 
| -} | 
| +      if (event != "added"  && | 
| +          event != "removed" && | 
| +          event != "updated" && | 
| +          event != "disabled") | 
| +        return; | 
| + | 
| +      // Ignore empty subscriptions. This includes subscriptions | 
| +      // that have just been added, but not downloaded yet. | 
| +      if (arg.filters.length == 0) | 
| +        return; | 
| +    } | 
| +    else if (category == "filter") | 
| +    { | 
| +      if (event != "added" && | 
| +          event != "removed" && | 
| +          event != "disabled") | 
| +        return; | 
| + | 
| +      // Ignore all types of filters but request filters, | 
| +      // only these have an effect on the handler behavior. | 
| +      if (!(arg instanceof RegExpFilter)) | 
| +        return; | 
| +    } | 
| +    else | 
| +      return; | 
| + | 
| +    // Ignore disabled subscriptions and filters, unless they just got | 
| +    // disabled, otherwise they have no effect on the handler behavior. | 
| +    if (arg.disabled && event != "disabled") | 
| +      return; | 
| +  } | 
| + | 
| +  ignoreFilterNotifications = true; | 
| +  setTimeout(() => | 
| +  { | 
| +    ignoreFilterNotifications = false; | 
| +    ext.webRequest.handlerBehaviorChanged(); | 
| +    FilterNotifier.triggerListeners("filter.behaviorChanged"); | 
| +  }); | 
| +}); | 
|  |