| OLD | NEW | 
|---|
| 1 /* | 1 /* | 
| 2  * This file is part of Adblock Plus <http://adblockplus.org/>, | 2  * This file is part of Adblock Plus <http://adblockplus.org/>, | 
| 3  * Copyright (C) 2006-2013 Eyeo GmbH | 3  * Copyright (C) 2006-2013 Eyeo GmbH | 
| 4  * | 4  * | 
| 5  * Adblock Plus is free software: you can redistribute it and/or modify | 5  * Adblock Plus is free software: you can redistribute it and/or modify | 
| 6  * it under the terms of the GNU General Public License version 3 as | 6  * it under the terms of the GNU General Public License version 3 as | 
| 7  * published by the Free Software Foundation. | 7  * published by the Free Software Foundation. | 
| 8  * | 8  * | 
| 9  * Adblock Plus is distributed in the hope that it will be useful, | 9  * Adblock Plus is distributed in the hope that it will be useful, | 
| 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| 12  * GNU General Public License for more details. | 12  * GNU General Public License for more details. | 
| 13  * | 13  * | 
| 14  * You should have received a copy of the GNU General Public License | 14  * You should have received a copy of the GNU General Public License | 
| 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| 16  */ | 16  */ | 
| 17 | 17 | 
| 18 var FilterNotifier = require("filterNotifier").FilterNotifier; | 18 var FilterNotifier = require("filterNotifier").FilterNotifier; | 
| 19 | 19 | 
| 20 chrome.webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: ["http://*
     /*", "https://*/*"]}, ["blocking"]); |  | 
| 21 chrome.webRequest.onHeadersReceived.addListener(onHeadersReceived, {urls: ["http
     ://*/*", "https://*/*"]}, ["responseHeaders"]); |  | 
| 22 chrome.tabs.onRemoved.addListener(forgetTab); |  | 
| 23 |  | 
| 24 var onFilterChangeTimeout = null; | 20 var onFilterChangeTimeout = null; | 
| 25 function onFilterChange() | 21 function onFilterChange() | 
| 26 { | 22 { | 
| 27   onFilterChangeTimeout = null; | 23   onFilterChangeTimeout = null; | 
| 28   chrome.webRequest.handlerBehaviorChanged(); | 24   ext.webRequest.handlerBehaviorChanged(); | 
| 29 } | 25 } | 
| 30 | 26 | 
| 31 var importantNotifications = { | 27 var importantNotifications = { | 
| 32   'filter.added': true, | 28   'filter.added': true, | 
| 33   'filter.removed': true, | 29   'filter.removed': true, | 
| 34   'filter.disabled': true, | 30   'filter.disabled': true, | 
| 35   'subscription.added': true, | 31   'subscription.added': true, | 
| 36   'subscription.removed': true, | 32   'subscription.removed': true, | 
| 37   'subscription.disabled': true, | 33   'subscription.disabled': true, | 
| 38   'subscription.updated': true, | 34   'subscription.updated': true, | 
| 39   'load': true | 35   'load': true | 
| 40 }; | 36 }; | 
| 41 | 37 | 
| 42 FilterNotifier.addListener(function(action) | 38 FilterNotifier.addListener(function(action) | 
| 43 { | 39 { | 
| 44   if (action in importantNotifications) | 40   if (action in importantNotifications) | 
| 45   { | 41   { | 
| 46     // Execute delayed to prevent multiple executions in a quick succession | 42     // Execute delayed to prevent multiple executions in a quick succession | 
| 47     if (onFilterChangeTimeout != null) | 43     if (onFilterChangeTimeout != null) | 
| 48       window.clearTimeout(onFilterChangeTimeout); | 44       window.clearTimeout(onFilterChangeTimeout); | 
| 49     onFilterChangeTimeout = window.setTimeout(onFilterChange, 2000); | 45     onFilterChangeTimeout = window.setTimeout(onFilterChange, 2000); | 
| 50   } | 46   } | 
| 51 }); | 47 }); | 
| 52 | 48 | 
| 53 var frames = {}; | 49 var frames = new TabMap(); | 
| 54 | 50 | 
| 55 function onBeforeRequest(details) | 51 function onBeforeRequest(url, type, tab, frameId, parentFrameId) | 
| 56 { | 52 { | 
| 57   if (details.tabId == -1) | 53   if (!tab) | 
| 58     return {}; | 54     return true; | 
| 59 |  | 
| 60   var type = details.type; |  | 
| 61 | 55 | 
| 62   // Assume that the first request belongs to the top frame. Chrome may give the | 56   // Assume that the first request belongs to the top frame. Chrome may give the | 
| 63   // top frame the type "object" instead of "main_frame". | 57   // top frame the type "object" instead of "main_frame". | 
| 64   // https://code.google.com/p/chromium/issues/detail?id=281711 | 58   // https://code.google.com/p/chromium/issues/detail?id=281711 | 
| 65   if (details.frameId == 0 && !(details.tabId in frames) && type == "object") | 59   if (frameId == 0 && !frames.has(tab) && type == "object") | 
| 66     type = "main_frame"; | 60     type = "main_frame"; | 
| 67 | 61 | 
| 68   if (type == "main_frame" || type == "sub_frame") | 62   if (type == "main_frame" || type == "sub_frame") | 
| 69     recordFrame(details.tabId, details.frameId, details.parentFrameId, details.u
     rl); | 63   { | 
|  | 64     recordFrame(tab, frameId, parentFrameId, url); | 
| 70 | 65 | 
| 71   if (type == "main_frame") | 66     if (type == "main_frame") | 
| 72     return {}; | 67       return true; | 
| 73 | 68 | 
| 74   // Type names match Mozilla's with main_frame and sub_frame being the only exc
     eptions. | 69     type = "subdocument"; | 
| 75   if (type == "sub_frame") | 70     frameId = parentFrameId; | 
| 76     type = "SUBDOCUMENT"; | 71   } | 
| 77   else |  | 
| 78     type = type.toUpperCase(); |  | 
| 79 | 72 | 
| 80   var frame = (type != "SUBDOCUMENT" ? details.frameId : details.parentFrameId); | 73   var filter = checkRequest(type.toUpperCase(), tab, url, frameId); | 
| 81   var filter = checkRequest(type, details.tabId, details.url, frame); | 74   FilterNotifier.triggerListeners("filter.hitCount", filter, 0, 0, tab); | 
| 82   FilterNotifier.triggerListeners("filter.hitCount", filter, 0, 0, details.tabId
     ); | 75   return !(filter instanceof BlockingFilter); | 
| 83   if (filter instanceof BlockingFilter) |  | 
| 84     return {cancel: true}; |  | 
| 85   else |  | 
| 86     return {}; |  | 
| 87 } | 76 } | 
| 88 | 77 | 
| 89 function onHeadersReceived(details) | 78 function recordFrame(tab, frameId, parentFrameId, url) | 
| 90 { | 79 { | 
| 91   if (details.tabId == -1) | 80   var framesOfTab = frames.get(tab); | 
| 92     return; |  | 
| 93 | 81 | 
| 94   var type = details.type; | 82   if (!framesOfTab) | 
| 95   if (type != "main_frame" && type != "sub_frame") | 83     frames.set(tab, (framesOfTab = {})); | 
| 96     return; |  | 
| 97 | 84 | 
| 98   var url = getFrameUrl(details.tabId, details.frameId); | 85   framesOfTab[frameId] = {url: url, parent: parentFrameId}; | 
| 99   if (url != details.url) | 86 } | 
| 100     return; |  | 
| 101 | 87 | 
| 102   var key = null; | 88 function getFrameData(tab, frameId) | 
| 103   var signature = null; | 89 { | 
| 104   for (var i = 0; i < details.responseHeaders.length; i++) | 90   var framesOfTab = frames.get(tab); | 
|  | 91 | 
|  | 92   if (framesOfTab) | 
| 105   { | 93   { | 
| 106     var header = details.responseHeaders[i]; | 94     if (frameId in framesOfTab) | 
| 107     if (header.name.toLowerCase() == "x-adblock-key" && header.value) | 95       return framesOfTab[frameId]; | 
| 108     { |  | 
| 109       var index = header.value.indexOf("_"); |  | 
| 110       if (index >= 0) |  | 
| 111       { |  | 
| 112         var key = header.value.substr(0, index); |  | 
| 113         var signature = header.value.substr(index + 1); |  | 
| 114         break; |  | 
| 115       } |  | 
| 116     } |  | 
| 117   } |  | 
| 118   if (!key) |  | 
| 119     return; |  | 
| 120 | 96 | 
| 121   var parentUrl = null; | 97     // We don't know anything about javascript: or data: frames, use top frame | 
| 122   if (type == "sub_frame") | 98     if (frameId != -1) | 
| 123     parentUrl = getFrameUrl(details.tabId, details.parentFrameId); | 99       return framesOfTab[0]; | 
| 124   if (!parentUrl) |  | 
| 125     parentUrl = url; |  | 
| 126   var docDomain = extractHostFromURL(parentUrl); |  | 
| 127   var keyMatch = defaultMatcher.matchesByKey(url, key.replace(/=/g, ""), docDoma
     in); |  | 
| 128   if (keyMatch) |  | 
| 129   { |  | 
| 130     // Website specifies a key that we know but is the signature valid? |  | 
| 131     var uri = new URI(url); |  | 
| 132     var host = uri.asciiHost; |  | 
| 133     if (uri.port > 0) |  | 
| 134       host += ":" + uri.port; |  | 
| 135 |  | 
| 136     var params = [ |  | 
| 137       uri.path.replace(/#.*/, ""),  // REQUEST_URI |  | 
| 138       host,                         // HTTP_HOST |  | 
| 139       window.navigator.userAgent    // HTTP_USER_AGENT |  | 
| 140     ]; |  | 
| 141     if (verifySignature(key, signature, params.join("\0"))) |  | 
| 142       frames[details.tabId][details.frameId].keyException = true; |  | 
| 143   } | 100   } | 
| 144 } | 101 } | 
| 145 | 102 | 
| 146 function recordFrame(tabId, frameId, parentFrameId, frameUrl) | 103 function getFrameUrl(tab, frameId) | 
| 147 { | 104 { | 
| 148   if (!(tabId in frames)) | 105   var frameData = getFrameData(tab, frameId); | 
| 149     frames[tabId] = {}; |  | 
| 150   frames[tabId][frameId] = {url: frameUrl, parent: parentFrameId}; |  | 
| 151 } |  | 
| 152 |  | 
| 153 function getFrameData(tabId, frameId) |  | 
| 154 { |  | 
| 155   if (tabId in frames && frameId in frames[tabId]) |  | 
| 156     return frames[tabId][frameId]; |  | 
| 157   else if (frameId > 0 && tabId in frames && 0 in frames[tabId]) |  | 
| 158   { |  | 
| 159     // We don't know anything about javascript: or data: frames, use top frame |  | 
| 160     return frames[tabId][0]; |  | 
| 161   } |  | 
| 162   return null; |  | 
| 163 } |  | 
| 164 |  | 
| 165 function getFrameUrl(tabId, frameId) |  | 
| 166 { |  | 
| 167   var frameData = getFrameData(tabId, frameId); |  | 
| 168   return (frameData ? frameData.url : null); | 106   return (frameData ? frameData.url : null); | 
| 169 } | 107 } | 
| 170 | 108 | 
| 171 function forgetTab(tabId) | 109 function checkRequest(type, tab, url, frameId) | 
| 172 { | 110 { | 
| 173   delete frames[tabId]; | 111   if (isFrameWhitelisted(tab, frameId)) | 
| 174 } |  | 
| 175 |  | 
| 176 function checkRequest(type, tabId, url, frameId) |  | 
| 177 { |  | 
| 178   if (isFrameWhitelisted(tabId, frameId)) |  | 
| 179     return false; | 112     return false; | 
| 180 | 113 | 
| 181   var documentUrl = getFrameUrl(tabId, frameId); | 114   var documentUrl = getFrameUrl(tab, frameId); | 
| 182   if (!documentUrl) | 115   if (!documentUrl) | 
| 183     return false; | 116     return false; | 
| 184 | 117 | 
| 185   var requestHost = extractHostFromURL(url); | 118   var requestHost = extractHostFromURL(url); | 
| 186   var documentHost = extractHostFromURL(documentUrl); | 119   var documentHost = extractHostFromURL(documentUrl); | 
| 187   var thirdParty = isThirdParty(requestHost, documentHost); | 120   var thirdParty = isThirdParty(requestHost, documentHost); | 
| 188   return defaultMatcher.matchesAny(url, type, documentHost, thirdParty); | 121   return defaultMatcher.matchesAny(url, type, documentHost, thirdParty); | 
| 189 } | 122 } | 
| 190 | 123 | 
| 191 function isFrameWhitelisted(tabId, frameId, type) | 124 function isFrameWhitelisted(tab, frameId, type) | 
| 192 { | 125 { | 
| 193   var parent = frameId; | 126   var parent = frameId; | 
| 194   var parentData = getFrameData(tabId, parent); | 127   var parentData = getFrameData(tab, parent); | 
| 195   while (parentData) | 128   while (parentData) | 
| 196   { | 129   { | 
| 197     var frame = parent; | 130     var frame = parent; | 
| 198     var frameData = parentData; | 131     var frameData = parentData; | 
| 199 | 132 | 
| 200     parent = frameData.parent; | 133     parent = frameData.parent; | 
| 201     parentData = getFrameData(tabId, parent); | 134     parentData = getFrameData(tab, parent); | 
| 202 | 135 | 
| 203     var frameUrl = frameData.url; | 136     var frameUrl = frameData.url; | 
| 204     var parentUrl = (parentData ? parentData.url : frameUrl); | 137     var parentUrl = (parentData ? parentData.url : frameUrl); | 
| 205     if ("keyException" in frameData || isWhitelisted(frameUrl, parentUrl, type)) | 138     if ("keyException" in frameData || isWhitelisted(frameUrl, parentUrl, type)) | 
| 206       return true; | 139       return true; | 
| 207   } | 140   } | 
| 208   return false; | 141   return false; | 
| 209 } | 142 } | 
|  | 143 | 
|  | 144 ext.webRequest.onBeforeRequest.addListener(onBeforeRequest, ["http://*/*", "http
     s://*/*"]); | 
|  | 145 | 
|  | 146 if (require("info").platform == "chromium") | 
|  | 147 { | 
|  | 148   function onHeadersReceived(details) | 
|  | 149   { | 
|  | 150     if (details.tabId == -1) | 
|  | 151       return; | 
|  | 152 | 
|  | 153     var type = details.type; | 
|  | 154     if (type != "main_frame" && type != "sub_frame") | 
|  | 155       return; | 
|  | 156 | 
|  | 157     var tab = new Tab({id: details.tabId}); | 
|  | 158     var url = getFrameUrl(tab, details.frameId); | 
|  | 159     if (url != details.url) | 
|  | 160       return; | 
|  | 161 | 
|  | 162     var key = null; | 
|  | 163     var signature = null; | 
|  | 164     for (var i = 0; i < details.responseHeaders.length; i++) | 
|  | 165     { | 
|  | 166       var header = details.responseHeaders[i]; | 
|  | 167       if (header.name.toLowerCase() == "x-adblock-key" && header.value) | 
|  | 168       { | 
|  | 169         var index = header.value.indexOf("_"); | 
|  | 170         if (index >= 0) | 
|  | 171         { | 
|  | 172           key = header.value.substr(0, index); | 
|  | 173           signature = header.value.substr(index + 1); | 
|  | 174           break; | 
|  | 175         } | 
|  | 176       } | 
|  | 177     } | 
|  | 178     if (!key) | 
|  | 179       return; | 
|  | 180 | 
|  | 181     var parentUrl = null; | 
|  | 182     if (type == "sub_frame") | 
|  | 183       parentUrl = getFrameUrl(tab, details.parentFrameId); | 
|  | 184     if (!parentUrl) | 
|  | 185       parentUrl = url; | 
|  | 186     var docDomain = extractHostFromURL(parentUrl); | 
|  | 187     var keyMatch = defaultMatcher.matchesByKey(url, key.replace(/=/g, ""), docDo
     main); | 
|  | 188     if (keyMatch) | 
|  | 189     { | 
|  | 190       // Website specifies a key that we know but is the signature valid? | 
|  | 191       var uri = new URI(url); | 
|  | 192       var host = uri.asciiHost; | 
|  | 193       if (uri.port > 0) | 
|  | 194         host += ":" + uri.port; | 
|  | 195 | 
|  | 196       var params = [ | 
|  | 197         uri.path.replace(/#.*/, ""),  // REQUEST_URI | 
|  | 198         host,                         // HTTP_HOST | 
|  | 199         window.navigator.userAgent    // HTTP_USER_AGENT | 
|  | 200       ]; | 
|  | 201       if (verifySignature(key, signature, params.join("\0"))) | 
|  | 202         frames.get(tab)[details.frameId].keyException = true; | 
|  | 203     } | 
|  | 204   } | 
|  | 205 | 
|  | 206   chrome.webRequest.onHeadersReceived.addListener(onHeadersReceived, {urls: ["ht
     tp://*/*", "https://*/*"]}, ["responseHeaders"]); | 
|  | 207 } | 
| OLD | NEW | 
|---|