| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 /* | 
|  | 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|  | 3  * Copyright (C) 2006-2016 Eyeo GmbH | 
|  | 4  * | 
|  | 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 | 
|  | 7  * published by the Free Software Foundation. | 
|  | 8  * | 
|  | 9  * Adblock Plus is distributed in the hope that it will be useful, | 
|  | 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 12  * GNU General Public License for more details. | 
|  | 13  * | 
|  | 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/>. | 
|  | 16  */ | 
|  | 17 | 
|  | 18 "use strict"; | 
|  | 19 | 
|  | 20 let {RegExpFilter, WhitelistFilter, ElemHideFilter} = require("filterClasses"); | 
|  | 21 let {SpecialSubscription} = require("subscriptionClasses"); | 
|  | 22 let {FilterStorage} = require("filterStorage"); | 
|  | 23 let {defaultMatcher} = require("matcher"); | 
|  | 24 let {FilterNotifier} = require("filterNotifier"); | 
|  | 25 | 
|  | 26 // Mapping of inspected tabs to their devpanel page | 
|  | 27 // and recorded items. We can't use a PageMap here, | 
|  | 28 // because data must persist after navigation/reload. | 
|  | 29 let panels = Object.create(null); | 
|  | 30 | 
|  | 31 function hasPanels() | 
|  | 32 { | 
|  | 33   return Object.getOwnPropertyNames(panels).length > 0; | 
|  | 34 } | 
|  | 35 | 
|  | 36 function getRequestInfo(request) | 
|  | 37 { | 
|  | 38   return { | 
|  | 39     url: request.url, | 
|  | 40     type: request.type, | 
|  | 41     docDomain: request.docDomain | 
|  | 42   }; | 
|  | 43 } | 
|  | 44 | 
|  | 45 function getFilterInfo(filter) | 
|  | 46 { | 
|  | 47   if (!filter) | 
|  | 48     return null; | 
|  | 49 | 
|  | 50   let userDefined = false; | 
|  | 51   let subscriptionTitle = null; | 
|  | 52 | 
|  | 53   for (let subscription of filter.subscriptions) | 
|  | 54   { | 
|  | 55     if (!subscription.disabled) | 
|  | 56     { | 
|  | 57       if (subscription instanceof SpecialSubscription) | 
|  | 58         userDefined = true; | 
|  | 59       else | 
|  | 60         subscriptionTitle = subscription.title; | 
|  | 61     } | 
|  | 62   } | 
|  | 63 | 
|  | 64   return { | 
|  | 65     text: filter.text, | 
|  | 66     whitelisted: filter instanceof WhitelistFilter, | 
|  | 67     userDefined: userDefined, | 
|  | 68     subscription: subscriptionTitle | 
|  | 69   }; | 
|  | 70 } | 
|  | 71 | 
|  | 72 function hasRecord(panel, request, filter) | 
|  | 73 { | 
|  | 74   return panel.records.some(record => | 
|  | 75     record.request.type      == request.type      && | 
|  | 76     record.request.url       == request.url       && | 
|  | 77     record.request.docDomain == request.docDomain && | 
|  | 78 | 
|  | 79     // Matched element hiding filters don't relate to a particular request, | 
|  | 80     // so we also have to match the CSS selector in order to distinguish them. | 
|  | 81     (request.type != "ELEMHIDE" || record.filter.selector == filter.selector) | 
|  | 82   ); | 
|  | 83 } | 
|  | 84 | 
|  | 85 function addRecord(panel, request, filter) | 
|  | 86 { | 
|  | 87   if (!hasRecord(panel, request, filter)) | 
|  | 88   { | 
|  | 89     panel.panel.sendMessage({ | 
|  | 90       type: "add-record", | 
|  | 91       request: getRequestInfo(request), | 
|  | 92       filter: getFilterInfo(filter) | 
|  | 93     }); | 
|  | 94 | 
|  | 95     panel.records.push({ | 
|  | 96       request: request, | 
|  | 97       filter: filter | 
|  | 98     }); | 
|  | 99   } | 
|  | 100 } | 
|  | 101 | 
|  | 102 function matchRequest(request) | 
|  | 103 { | 
|  | 104   return defaultMatcher.matchesAny( | 
|  | 105     request.url, | 
|  | 106     RegExpFilter.typeMap[request.type], | 
|  | 107     request.docDomain, | 
|  | 108     request.thirdParty, | 
|  | 109     request.sitekey, | 
|  | 110     request.specificOnly | 
|  | 111   ); | 
|  | 112 } | 
|  | 113 | 
|  | 114 /** | 
|  | 115  * Logs a request in the devtools panel. | 
|  | 116  * | 
|  | 117  * @param {Page}    page           The page the request occured on | 
|  | 118  * @param {string]  url            The URL of the request | 
|  | 119  * @param {string}  type           The request type | 
|  | 120  * @param {string}  docDomain      The IDN-decoded hostname of the document | 
|  | 121  * @param {boolean} thirdParty     Whether the origin of the request and documen
     t differs | 
|  | 122  * @param {string}  [sitekey]      The active sitekey if there is any | 
|  | 123  * @param {boolean} [specificOnly] Whether generic filters should be ignored | 
|  | 124  * @param {filter}  [filter]       The matched filter or null if there is no mat
     ch | 
|  | 125  */ | 
|  | 126 exports.logRequest = function(page, url, type, docDomain, thirdParty, sitekey, s
     pecificOnly, filter) | 
|  | 127 { | 
|  | 128   let panel = panels[page._id]; | 
|  | 129   if (panel && !panel.reload && !panel.reloading) | 
|  | 130   { | 
|  | 131     let request = { | 
|  | 132       url: url, | 
|  | 133       type: type, | 
|  | 134       docDomain: docDomain, | 
|  | 135       thirdParty: thirdParty, | 
|  | 136       sitekey: sitekey, | 
|  | 137       specificOnly: specificOnly | 
|  | 138     }; | 
|  | 139 | 
|  | 140     addRecord(panel, request, filter); | 
|  | 141   } | 
|  | 142 }; | 
|  | 143 | 
|  | 144 /** | 
|  | 145  * Logs active element hiding filters in the devtools panel. | 
|  | 146  * | 
|  | 147  * @param {Page}     page       The page the elements were hidden on | 
|  | 148  * @param {string[]} selectors  The CSS selectors of active elemhide filters | 
|  | 149  * @param {string}   docDomain  The IDN-decoded hostname of the document | 
|  | 150  */ | 
|  | 151 exports.logHiddenElements = function(page, selectors, docDomain) | 
|  | 152 { | 
|  | 153   let panel = panels[page._id]; | 
|  | 154   if (panel && !panel.reload && !panel.reloading) | 
|  | 155   { | 
|  | 156     for (let subscription of FilterStorage.subscriptions) | 
|  | 157     { | 
|  | 158       if (subscription.disabled) | 
|  | 159         continue; | 
|  | 160 | 
|  | 161       for (let filter of subscription.filters) | 
|  | 162       { | 
|  | 163         if (!(filter instanceof ElemHideFilter)) | 
|  | 164           continue; | 
|  | 165         if (selectors.indexOf(filter.selector) == -1) | 
|  | 166           continue; | 
|  | 167         if (!filter.isActiveOnDomain(docDomain)) | 
|  | 168           continue; | 
|  | 169 | 
|  | 170         addRecord(panel, {type: "ELEMHIDE", docDomain: docDomain}, filter); | 
|  | 171       } | 
|  | 172     } | 
|  | 173   } | 
|  | 174 }; | 
|  | 175 | 
|  | 176 /** | 
|  | 177  * Checks whether a page is inspected by the devtools panel. | 
|  | 178  * | 
|  | 179  * @param {Page} page | 
|  | 180  * @return {boolean} | 
|  | 181  */ | 
|  | 182 exports.hasPanel = function(page) | 
|  | 183 { | 
|  | 184   return page._id in panels; | 
|  | 185 }; | 
|  | 186 | 
|  | 187 function onBeforeRequest(details) | 
|  | 188 { | 
|  | 189   let panel = panels[details.tabId]; | 
|  | 190 | 
|  | 191   // Clear the devtools panel and reload the inspected tab without caching | 
|  | 192   // when a new request is issued. However, make sure that we don't end up | 
|  | 193   // in an infinite recursion if we already triggered a reload. | 
|  | 194   if (panel.reloading) | 
|  | 195   { | 
|  | 196     panel.reloading = false; | 
|  | 197   } | 
|  | 198   else | 
|  | 199   { | 
|  | 200     panel.records = []; | 
|  | 201     panel.panel.sendMessage({type: "reset"}); | 
|  | 202 | 
|  | 203     // We can't repeat the request if it isn't a GET request. Chrome would | 
|  | 204     // prompt the user to confirm reloading the page, and POST requests are | 
|  | 205     // known to cause issues on many websites if repeated. | 
|  | 206     if (details.method == "GET") | 
|  | 207       panel.reload = true; | 
|  | 208   } | 
|  | 209 } | 
|  | 210 | 
|  | 211 function onLoading(page) | 
|  | 212 { | 
|  | 213   let tabId = page._id; | 
|  | 214   let panel = panels[tabId]; | 
|  | 215 | 
|  | 216   // Reloading the tab is the only way that allows bypassing all caches, in | 
|  | 217   // order to see all requests in the devtools panel. Reloading must not be | 
|  | 218   // performed before the tab changes to "loading", otherwise it will load the | 
|  | 219   // previous URL. | 
|  | 220   if (panel && panel.reload) | 
|  | 221   { | 
|  | 222     chrome.tabs.reload(tabId, {bypassCache: true}); | 
|  | 223 | 
|  | 224     panel.reload = false; | 
|  | 225     panel.reloading = true; | 
|  | 226   } | 
|  | 227 } | 
|  | 228 | 
|  | 229 function onFilterChange(action, arg) | 
|  | 230 { | 
|  | 231   let added, filters; | 
|  | 232   switch (action) | 
|  | 233   { | 
|  | 234     case "filter.added": | 
|  | 235       added = true; | 
|  | 236       filters = [arg]; | 
|  | 237       break; | 
|  | 238 | 
|  | 239     case "filter.removed": | 
|  | 240       added = false; | 
|  | 241       filters = [arg]; | 
|  | 242       break; | 
|  | 243 | 
|  | 244     // When there haven't ever been any user filters before, the subscription is | 
|  | 245     // added, triggering a "subscription.added" instead of a "filter.added" even
     t. | 
|  | 246     case "subscription.added": | 
|  | 247       if (arg instanceof SpecialSubscription) | 
|  | 248       { | 
|  | 249         added = true; | 
|  | 250         filters = arg.filters; | 
|  | 251         break; | 
|  | 252       } | 
|  | 253 | 
|  | 254     default: | 
|  | 255       return; | 
|  | 256   } | 
|  | 257 | 
|  | 258   for (let tabId in panels) | 
|  | 259   { | 
|  | 260     let panel = panels[tabId]; | 
|  | 261 | 
|  | 262     for (let i = 0; i < panel.records.length; i++) | 
|  | 263     { | 
|  | 264       let record = panel.records[i]; | 
|  | 265 | 
|  | 266       // If an added filter matches a request shown in the devtools panel, | 
|  | 267       // update that record to show the new filter. Ignore element hiding | 
|  | 268       // filters since there are only records for element hiding filters that | 
|  | 269       // already match, and also we don't know if any new element matches. | 
|  | 270       if (added) | 
|  | 271       { | 
|  | 272         if (record.request.type == "ELEMHIDE") | 
|  | 273           continue; | 
|  | 274 | 
|  | 275         let filter = matchRequest(record.request); | 
|  | 276         if (filters.indexOf(filter) == -1) | 
|  | 277           continue; | 
|  | 278 | 
|  | 279         record.filter = filter; | 
|  | 280       } | 
|  | 281 | 
|  | 282       // If a filter shown in the devtools panel got removed, update that | 
|  | 283       // record to show the filter (if any) that matches now instead. | 
|  | 284       // However, for element hiding, just remove the record, since we don't | 
|  | 285       // know whether another filter would match until the page is reloaded. | 
|  | 286       else | 
|  | 287       { | 
|  | 288         if (filters.indexOf(record.filter) == -1) | 
|  | 289           continue; | 
|  | 290 | 
|  | 291         if (record.request.type == "ELEMHIDE") | 
|  | 292         { | 
|  | 293           panel.panel.sendMessage({ | 
|  | 294             type: "remove-record", | 
|  | 295             index: i | 
|  | 296           }); | 
|  | 297           panel.records.splice(i--, 1); | 
|  | 298           continue; | 
|  | 299         } | 
|  | 300 | 
|  | 301         record.filter = matchRequest(record.request); | 
|  | 302       } | 
|  | 303 | 
|  | 304       panel.panel.sendMessage({ | 
|  | 305         type: "update-record", | 
|  | 306         index: i, | 
|  | 307         request: getRequestInfo(record.request), | 
|  | 308         filter: getFilterInfo(record.filter) | 
|  | 309       }); | 
|  | 310     } | 
|  | 311   } | 
|  | 312 } | 
|  | 313 | 
|  | 314 ext.devtools.onCreated.addListener(function(panel) | 
|  | 315 { | 
|  | 316   let inspectedTabId = panel.inspectedTabId; | 
|  | 317   let localOnBeforeRequest = onBeforeRequest.bind(); | 
|  | 318 | 
|  | 319   chrome.webRequest.onBeforeRequest.addListener( | 
|  | 320     localOnBeforeRequest, | 
|  | 321     { | 
|  | 322       urls:  ["<all_urls>"], | 
|  | 323       types: ["main_frame"], | 
|  | 324       tabId: inspectedTabId | 
|  | 325     } | 
|  | 326   ); | 
|  | 327 | 
|  | 328   if (!hasPanels()) | 
|  | 329   { | 
|  | 330     ext.pages.onLoading.addListener(onLoading); | 
|  | 331     FilterNotifier.addListener(onFilterChange); | 
|  | 332   } | 
|  | 333 | 
|  | 334   panel.onRemoved.addListener(function() | 
|  | 335   { | 
|  | 336     delete panels[inspectedTabId]; | 
|  | 337     chrome.webRequest.onBeforeRequest.removeListener(localOnBeforeRequest); | 
|  | 338 | 
|  | 339     if (!hasPanels()) | 
|  | 340     { | 
|  | 341       FilterNotifier.removeListener(onFilterChange); | 
|  | 342       ext.pages.onLoading.removeListener(onLoading); | 
|  | 343     } | 
|  | 344   }); | 
|  | 345 | 
|  | 346   panels[inspectedTabId] = {panel: panel, records: []}; | 
|  | 347 }); | 
| OLD | NEW | 
|---|