| Index: webrequest.js | 
| =================================================================== | 
| --- a/webrequest.js | 
| +++ b/webrequest.js | 
| @@ -1,16 +1,16 @@ | 
| /* | 
| * This Source Code is subject to the terms of the Mozilla Public License | 
| * version 2.0 (the "License"). You can obtain a copy of the License at | 
| * http://mozilla.org/MPL/2.0/. | 
| */ | 
| chrome.webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: ["http://*/*", "https://*/*"]}, ["blocking"]); | 
| -chrome.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: ["http://*/*", "https://*/*"]}, ["requestHeaders", "blocking"]); | 
| +chrome.webRequest.onHeadersReceived.addListener(onHeadersReceived, {urls: ["http://*/*", "https://*/*"]}, ["responseHeaders"]); | 
| chrome.tabs.onRemoved.addListener(forgetTab); | 
| var frames = {}; | 
| function onBeforeRequest(details) | 
| { | 
| if (details.tabId == -1) | 
| return {}; | 
| @@ -41,29 +41,71 @@ function onBeforeRequest(details) | 
| return {redirectUrl: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg=="}; | 
| else | 
| return {cancel: true}; | 
| } | 
| else | 
| return {}; | 
| } | 
| -function onBeforeSendHeaders(details) | 
| +function onHeadersReceived(details) | 
| { | 
| - var match = defaultMatcher.matchesAny(details.url, "DONOTTRACK", null, false); | 
| - if (match && match instanceof BlockingFilter) | 
| + if (details.tabId == -1) | 
| + return; | 
| + | 
| + var type = details.type; | 
| + if (type != "main_frame" && type != "sub_frame") | 
| + return; | 
| + | 
| + var url = getFrameUrl(details.tabId, details.frameId); | 
| + if (url != details.url) | 
| + return; | 
| + | 
| + var key = null; | 
| + var signature = null; | 
| + for (var i = 0; i < details.responseHeaders.length; i++) | 
| { | 
| - var headers = details.requestHeaders || []; | 
| - if (!headers.some(function(header) { header.name == "DNT";})) | 
| + var header = details.responseHeaders[i]; | 
| + if (header.name.toLowerCase() == "x-adblock-key" && header.value) | 
| { | 
| - headers.push({name: "DNT", value: "1"}); | 
| - return {requestHeaders: headers}; | 
| + var index = header.value.indexOf("_"); | 
| + if (index >= 0) | 
| + { | 
| + var key = header.value.substr(0, index); | 
| + var signature = header.value.substr(index + 1); | 
| + break; | 
| + } | 
| } | 
| } | 
| - return null; | 
| + if (!key) | 
| + return; | 
| + | 
| + var parentUrl = null; | 
| + if (type == "sub_frame") | 
| + parentUrl = getFrameUrl(details.tabId, details.parentFrameId); | 
| + if (!parentUrl) | 
| + parentUrl = url; | 
| + var docDomain = extractHostFromURL(parentUrl); | 
| + var keyMatch = defaultMatcher.matchesByKey(url, key.replace(/=/g, ""), docDomain); | 
| + if (keyMatch) | 
| + { | 
| + // Website specifies a key that we know but is the signature valid? | 
| + var uri = new URI(url); | 
| + var host = uri.asciiHost; | 
| + if (uri.port > 0) | 
| + host += ":" + uri.port; | 
| + | 
| + var params = [ | 
| + uri.path.replace(/#.*/, ""), // REQUEST_URI | 
| + host, // HTTP_HOST | 
| + window.navigator.userAgent // HTTP_USER_AGENT | 
| + ]; | 
| + if (verifySignature(key, signature, params.join("\0"))) | 
| + frames[details.tabId][details.frameId].keyException = true; | 
| + } | 
| } | 
| function recordFrame(tabId, frameId, parentFrameId, frameUrl) | 
| { | 
| if (!(tabId in frames)) | 
| frames[tabId] = {}; | 
| frames[tabId][frameId] = {url: frameUrl, parent: parentFrameId}; | 
| } | 
| @@ -105,12 +147,14 @@ function checkRequest(type, tabId, url, | 
| function isFrameWhitelisted(tabId, frameId, type) | 
| { | 
| var parent = frameId; | 
| while (parent != -1) | 
| { | 
| var parentUrl = getFrameUrl(tabId, parent); | 
| if (parentUrl && isWhitelisted(parentUrl, type)) | 
| return true; | 
| + if (parentUrl && "keyException" in frames[tabId][frameId]) | 
| + return true; | 
| parent = getFrameParent(tabId, parent); | 
| } | 
| return false; | 
| } |