| Index: lib/devtools.js | 
| =================================================================== | 
| new file mode 100644 | 
| --- /dev/null | 
| +++ b/lib/devtools.js | 
| @@ -0,0 +1,239 @@ | 
| +/* | 
| + * This file is part of Adblock Plus <http://adblockplus.org/>, | 
| + * Copyright (C) 2006-2015 Eyeo GmbH | 
| + * | 
| + * Adblock Plus is free software: you can redistribute it and/or modify | 
| + * it under the terms of the GNU General Public License version 3 as | 
| + * published by the Free Software Foundation. | 
| + * | 
| + * Adblock Plus is distributed in the hope that it will be useful, | 
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| + * GNU General Public License for more details. | 
| + * | 
| + * You should have received a copy of the GNU General Public License | 
| + * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| + */ | 
| + | 
| +let info = require("info"); | 
| +if (info.platform == "chromium") | 
| +{ | 
| +  let {WhitelistFilter} = require("filterClasses"); | 
| +  let {SpecialSubscription} = require("subscriptionClasses"); | 
| +  let {Filter, ElemHideFilter} = require("filterClasses"); | 
| +  let {FilterStorage} = require("filterStorage"); | 
| +  let {defaultMatcher} = require("matcher"); | 
| +  let {FilterNotifier} = require("filterNotifier"); | 
| + | 
| +  let panels = Object.create(null); | 
| + | 
| +  function getFilterInfo(filter) | 
| +  { | 
| +    if (!filter) | 
| +      return null; | 
| + | 
| +    let userDefined = false; | 
| +    let subscriptionTitle = null; | 
| + | 
| +    for (let subscription of filter.subscriptions) | 
| +    { | 
| +      if (!subscription.disabled) | 
| +      { | 
| +        if (subscription instanceof SpecialSubscription) | 
| +          userDefined = true; | 
| +        else | 
| +          subscriptionTitle = subscription.title; | 
| +      } | 
| +    } | 
| + | 
| +    return { | 
| +      text: filter.text, | 
| +      whitelisted: filter instanceof WhitelistFilter, | 
| +      userDefined: userDefined, | 
| +      subscription: subscriptionTitle | 
| +    }; | 
| +  } | 
| + | 
| +  function addRecord(panel, request, filter) | 
| +  { | 
| +    panel.port.postMessage({ | 
| +      type: "add-record", | 
| +      request: request, | 
| +      filter: getFilterInfo(filter) | 
| +    }); | 
| + | 
| +    panel.records.push({ | 
| +      request: request, | 
| +      filter: filter | 
| +    }); | 
| +  } | 
| + | 
| +  function matchRequest(request) | 
| +  { | 
| +    return defaultMatcher.matchesAny( | 
| +      request.url, | 
| +      request.type, | 
| +      request.docDomain, | 
| +      isThirdParty( | 
| +        extractHostFromURL(request.url), | 
| +        request.docDomain | 
| +      ), | 
| +      request.sitekey | 
| +    ); | 
| +  } | 
| + | 
| +  function logRequest(tabId, url, type, docDomain, sitekey, filter) | 
| +  { | 
| +    let panel = panels[tabId]; | 
| +    if (panel) | 
| +    { | 
| +      let request = { | 
| +        url: url, | 
| +        type: type, | 
| +        docDomain: docDomain, | 
| +        sitekey: sitekey | 
| +      }; | 
| + | 
| +      addRecord(panel, request, filter); | 
| +    } | 
| +  } | 
| +  exports.logRequest = logRequest; | 
| + | 
| +  function logHiddenElements(tabId, selectors, docDomain) | 
| +  { | 
| +    let panel = panels[tabId]; | 
| +    if (panel) | 
| +    { | 
| +      for (let subscription of FilterStorage.subscriptions) | 
| +      { | 
| +        if (subscription.disabled) | 
| +          continue; | 
| + | 
| +        for (let filter of subscription.filters) | 
| +        { | 
| +          if (!(filter instanceof ElemHideFilter)) | 
| +            continue; | 
| +          if (selectors.indexOf(filter.selector) == -1) | 
| +            continue; | 
| +          if (!filter.isActiveOnDomain(docDomain)) | 
| +            continue; | 
| +          if (panel.records.some(record => record.request.type == "ELEMHIDE" && | 
| +                                           record.request.docDomain == docDomain && | 
| +                                           record.filter.selector == filter.selector)) | 
| +            continue; | 
| + | 
| +          addRecord(panel, {type: "ELEMHIDE", docDomain: docDomain}, filter); | 
| +        } | 
| +      } | 
| +    } | 
| +  }; | 
| +  exports.logHiddenElements = logHiddenElements; | 
| + | 
| +  function hasPanel(page) | 
| +  { | 
| +    return page._id in panels; | 
| +  } | 
| +  exports.hasPanel = hasPanel; | 
| + | 
| +  function onMessage(msg) | 
| +  { | 
| +    let filter = Filter.fromText(msg.filter); | 
| + | 
| +    switch (msg.action) | 
| +    { | 
| +      case "add": | 
| +        FilterStorage.addFilter(filter); | 
| +        break; | 
| + | 
| +      case "remove": | 
| +        FilterStorage.removeFilter(filter); | 
| +        break; | 
| +    } | 
| +  } | 
| + | 
| +  chrome.runtime.onConnect.addListener(function(port) | 
| +  { | 
| +    let match = port.name.match(/^devtools-(\d+)$/); | 
| +    if (match) | 
| +    { | 
| +      let tabId = match[1]; | 
| +      panels[tabId] = {port: port, records: []}; | 
| + | 
| +      port.onMessage.addListener(onMessage); | 
| +      port.onDisconnect.addListener(() => delete panels[tabId]); | 
| +    } | 
| +  }); | 
| + | 
| +  chrome.webNavigation.onBeforeNavigate.addListener(function(details) | 
| +  { | 
| +    let panel = panels[details.tabId]; | 
| +    if (panel && details.frameId == 0) | 
| +    { | 
| +      // We have to flush the in-memory cache on page load. | 
| +      // Otherwise requests answered from the in-memory cache | 
| +      // will not be shown in the devtools panel. | 
| +      chrome.webRequest.handlerBehaviorChanged(); | 
| + | 
| +      panel.records = []; | 
| +      panel.port.postMessage({type: "reset"}); | 
| +    } | 
| +  }); | 
| + | 
| +  FilterNotifier.addListener(function(action, filter) | 
| +  { | 
| +    if (action != "filter.added" && action != "filter.removed") | 
| +      return; | 
| + | 
| +    for (let tabId in panels) | 
| +    { | 
| +      let panel = panels[tabId]; | 
| + | 
| +      for (let i = 0; i < panel.records.length; i++) | 
| +      { | 
| +        let record = panel.records[i]; | 
| + | 
| +        if (action == "filter.added") | 
| +        { | 
| +          if (record.request.type == "ELEMHIDE") | 
| +            continue; | 
| + | 
| +          if (matchRequest(record.request) != filter) | 
| +            continue; | 
| + | 
| +          record.filter = filter; | 
| +        } | 
| + | 
| +        if (action == "filter.removed") | 
| +        { | 
| +          if (record.filter != filter) | 
| +            continue; | 
| + | 
| +          if (record.request.type == "ELEMHIDE") | 
| +          { | 
| +            panel.port.postMessage({ | 
| +              type: "remove-record", | 
| +              index: i | 
| +            }); | 
| +            panel.records.splice(i--, 1); | 
| +            continue; | 
| +          } | 
| + | 
| +          record.filter = matchRequest(record.request); | 
| +        } | 
| + | 
| +        panel.port.postMessage({ | 
| +          type: "update-record", | 
| +          index: i, | 
| +          request: record.request, | 
| +          filter: getFilterInfo(record.filter) | 
| +        }); | 
| +      } | 
| +    } | 
| +  }); | 
| +} | 
| +else | 
| +{ | 
| +  exports.logRequest        = () => {}; | 
| +  exports.logHiddenElements = () => {}; | 
| +  exports.hasPanel          = () => false; | 
| +} | 
|  |