| LEFT | RIGHT | 
|---|
| 1 /* | 1 /* | 
| 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
| 3  * Copyright (C) 2006-present eyeo GmbH | 3  * Copyright (C) 2006-present 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 /** @module hitLogger */ | 18 /** @module hitLogger */ | 
| 19 | 19 | 
| 20 "use strict"; | 20 "use strict"; | 
| 21 | 21 | 
| 22 const {desc} = require("../adblockpluscore/lib/coreUtils"); |  | 
| 23 const {extractHostFromFrame} = require("./url"); | 22 const {extractHostFromFrame} = require("./url"); | 
| 24 const {EventEmitter} = require("../adblockpluscore/lib/events"); | 23 const {EventEmitter} = require("../adblockpluscore/lib/events"); | 
| 25 const {FilterStorage} = require("../adblockpluscore/lib/filterStorage"); | 24 const {FilterStorage} = require("../adblockpluscore/lib/filterStorage"); | 
| 26 const {port} = require("./messaging"); | 25 const {port} = require("./messaging"); | 
| 27 const {RegExpFilter, | 26 const {RegExpFilter, | 
| 28        ElemHideFilter} = require("../adblockpluscore/lib/filterClasses"); | 27        ElemHideFilter} = require("../adblockpluscore/lib/filterClasses"); | 
| 29 | 28 | 
| 30 const nonRequestTypes = exports.nonRequestTypes = [ | 29 const nonRequestTypes = exports.nonRequestTypes = [ | 
| 31   "DOCUMENT", "ELEMHIDE", "GENERICBLOCK", "GENERICHIDE", "CSP" | 30   "DOCUMENT", "ELEMHIDE", "GENERICBLOCK", "GENERICHIDE", "CSP" | 
| 32 ]; | 31 ]; | 
| 33 | 32 | 
| 34 let HitLogger = exports.HitLogger = Object.create(new EventEmitter(), desc({ | 33 let eventEmitter = new EventEmitter(); | 
| 35   off(tabId, listener) |  | 
| 36   { |  | 
| 37     Object.getPrototypeOf(this).off(tabId, listener); |  | 
| 38 |  | 
| 39     // EventEmitter leaves an empty array once the last listener for an event |  | 
| 40     // is removed. Usually that's fine, but in this case we need _listeners.size |  | 
| 41     // and _listeners.has to be accurate. |  | 
| 42     let listeners = this._listeners.get(tabId); |  | 
| 43     if (listeners && listeners.length == 0) |  | 
| 44       this._listeners.delete(tabId); |  | 
| 45   } |  | 
| 46 })); |  | 
| 47 | 34 | 
| 48 /** | 35 /** | 
| 49  * Checks whether a tab is inspected by anything. | 36  * @namespace | 
| 50  * | 37  * @static | 
| 51  * @param {number} tabId |  | 
| 52  * @return {boolean} |  | 
| 53  */ | 38  */ | 
| 54 let hasListener = exports.hasListener = function(tabId) | 39 let HitLogger = exports.HitLogger = { | 
| 55 { | 40   /** | 
| 56   return HitLogger._listeners.has(tabId); | 41    * Adds a listener for requests, filter hits etc related to the tab. | 
|  | 42    * | 
|  | 43    * Note: Calling code is responsible for removing the listener again, | 
|  | 44    *       it will not be automatically removed when the tab is closed. | 
|  | 45    * | 
|  | 46    * @param {number} tabId | 
|  | 47    * @param {function} listener | 
|  | 48    */ | 
|  | 49   addListener: eventEmitter.on.bind(eventEmitter), | 
|  | 50 | 
|  | 51   /** | 
|  | 52    * Removes a listener for the tab. | 
|  | 53    * | 
|  | 54    * @param {number} tabId | 
|  | 55    * @param {function} listener | 
|  | 56    */ | 
|  | 57   removeListener: eventEmitter.off.bind(eventEmitter), | 
|  | 58 | 
|  | 59   /** | 
|  | 60    * Checks whether a tab is being inspected by anything. | 
|  | 61    * | 
|  | 62    * @param {number} tabId | 
|  | 63    * @return {boolean} | 
|  | 64    */ | 
|  | 65   hasListener(tabId) | 
|  | 66   { | 
|  | 67     let listeners = eventEmitter._listeners.get(tabId); | 
|  | 68     return listeners && listeners.length > 0; | 
|  | 69   } | 
| 57 }; | 70 }; | 
| 58 | 71 | 
| 59 /** | 72 /** | 
| 60  * Logs a request associated with a tab or multiple tabs. | 73  * Logs a request associated with a tab or multiple tabs. | 
| 61  * | 74  * | 
| 62  * @param {number[]} tabIds        The tabIds associated with the request | 75  * @param {number[]} tabIds | 
| 63  * @param {string}   url           The URL of the request | 76  *   The tabIds associated with the request | 
| 64  * @param {string}   type          The request type | 77  * @param {Object} request | 
| 65  * @param {string}   docDomain     The IDN-decoded hostname of the document | 78  *   The request to log | 
| 66  * @param {boolean}  thirdParty    Whether the origin of the request and | 79  * @param {string} request.url | 
| 67  *                                 document differs | 80  *   The URL of the request | 
| 68  * @param {?string}  sitekey       The active sitekey if there is any | 81  * @param {string} request.type | 
| 69  * @param {?boolean} specificOnly  Whether generic filters should be ignored | 82  *  The request type | 
| 70  * @param {?BlockingFilter} filter The matched filter or null if there is no | 83  * @param {string} request.docDomain | 
| 71  *                                 match | 84  *  The hostname of the document | 
|  | 85  * @param {boolean} request.thirdParty | 
|  | 86  *   Whether the origin of the request and document differs | 
|  | 87  * @param {?string} request.sitekey | 
|  | 88  *   The active sitekey if there is any | 
|  | 89  * @param {?boolean} request.specificOnly | 
|  | 90  *   Whether generic filters should be ignored | 
|  | 91  * @param {?BlockingFilter} filter | 
|  | 92  *  The matched filter or null if there is no match | 
| 72  */ | 93  */ | 
| 73 exports.logRequest = function(tabIds, url, type, docDomain, | 94 exports.logRequest = (tabIds, request, filter) => | 
| 74                               thirdParty, sitekey, |  | 
| 75                               specificOnly, filter) |  | 
| 76 { | 95 { | 
| 77   for (let tabId of tabIds) | 96   for (let tabId of tabIds) | 
| 78   { | 97     eventEmitter.emit(tabId, request, filter); | 
| 79     if (hasListener(tabId)) |  | 
| 80     { |  | 
| 81       let request = {url, type, docDomain, thirdParty, sitekey, specificOnly}; |  | 
| 82       HitLogger.emit(tabId, request, filter); |  | 
| 83     } |  | 
| 84   } |  | 
| 85 }; | 98 }; | 
| 86 | 99 | 
| 87 /** | 100 /** | 
| 88  * Logs active element hiding filters for a tab. | 101  * Logs active element hiding filters for a tab. | 
| 89  * | 102  * | 
| 90  * @param {number}   tabId      The ID of the tab, the elements were hidden in | 103  * @param {number}   tabId      The ID of the tab, the elements were hidden in | 
| 91  * @param {string[]} selectors  The selectors of applied ElemHideFilters | 104  * @param {string[]} selectors  The selectors of applied ElemHideFilters | 
| 92  * @param {string[]} filters    The text of applied ElemHideEmulationFilters | 105  * @param {string[]} filters    The text of applied ElemHideEmulationFilters | 
| 93  * @param {string}   docDomain  The IDN-decoded hostname of the document | 106  * @param {string}   docDomain  The hostname of the document | 
| 94  */ | 107  */ | 
| 95 function logHiddenElements(tabId, selectors, filters, docDomain) | 108 function logHiddenElements(tabId, selectors, filters, docDomain) | 
| 96 { | 109 { | 
| 97   if (hasListener(tabId)) | 110   if (HitLogger.hasListener(tabId)) | 
| 98   { | 111   { | 
| 99     for (let subscription of FilterStorage.subscriptions) | 112     for (let subscription of FilterStorage.subscriptions) | 
| 100     { | 113     { | 
| 101       if (subscription.disabled) | 114       if (subscription.disabled) | 
| 102         continue; | 115         continue; | 
| 103 | 116 | 
| 104       for (let filter of subscription.filters) | 117       for (let filter of subscription.filters) | 
| 105       { | 118       { | 
| 106         // We only know the exact filter in case of element hiding emulation. | 119         // We only know the exact filter in case of element hiding emulation. | 
| 107         // For regular element hiding filters, the content script only knows | 120         // For regular element hiding filters, the content script only knows | 
| 108         // the selector, so we have to find a filter that has an identical | 121         // the selector, so we have to find a filter that has an identical | 
| 109         // selector and is active on the domain the match was reported from. | 122         // selector and is active on the domain the match was reported from. | 
| 110         let isActiveElemHideFilter = filter instanceof ElemHideFilter && | 123         let isActiveElemHideFilter = filter instanceof ElemHideFilter && | 
| 111                                      selectors.includes(filter.selector) && | 124                                      selectors.includes(filter.selector) && | 
| 112                                      filter.isActiveOnDomain(docDomain); | 125                                      filter.isActiveOnDomain(docDomain); | 
| 113 | 126 | 
| 114         if (isActiveElemHideFilter || filters.includes(filter.text)) | 127         if (isActiveElemHideFilter || filters.includes(filter.text)) | 
| 115           HitLogger.emit(tabId, {type: "ELEMHIDE", docDomain}, filter); | 128           eventEmitter.emit(tabId, {type: "ELEMHIDE", docDomain}, filter); | 
| 116       } | 129       } | 
| 117     } | 130     } | 
| 118   } | 131   } | 
| 119 } | 132 } | 
| 120 | 133 | 
| 121 /** | 134 /** | 
| 122  * Logs a whitelisting filter that disables (some kind of) | 135  * Logs a whitelisting filter that disables (some kind of) | 
| 123  * blocking for a particular document. | 136  * blocking for a particular document. | 
| 124  * | 137  * | 
| 125  * @param {number}       tabId     The tabId the whitelisting is active for | 138  * @param {number}       tabId     The tabId the whitelisting is active for | 
| 126  * @param {string}       url       The url of the whitelisted document | 139  * @param {string}       url       The url of the whitelisted document | 
| 127  * @param {number}       typeMask  The bit mask of whitelisting types checked | 140  * @param {number}       typeMask  The bit mask of whitelisting types checked | 
| 128  *                                 for | 141  *                                 for | 
| 129  * @param {string}       docDomain The IDN-decoded hostname of the parent | 142  * @param {string}       docDomain The hostname of the parent document | 
| 130  *                                 document |  | 
| 131  * @param {WhitelistFilter} filter The matched whitelisting filter | 143  * @param {WhitelistFilter} filter The matched whitelisting filter | 
| 132  */ | 144  */ | 
| 133 exports.logWhitelistedDocument = function(tabId, url, typeMask, docDomain, | 145 exports.logWhitelistedDocument = (tabId, url, typeMask, docDomain, filter) => | 
| 134                                           filter) |  | 
| 135 { | 146 { | 
| 136   if (hasListener(tabId)) | 147   if (HitLogger.hasListener(tabId)) | 
| 137   { | 148   { | 
| 138     for (let type of nonRequestTypes) | 149     for (let type of nonRequestTypes) | 
| 139     { | 150     { | 
| 140       if (typeMask & filter.contentType & RegExpFilter.typeMap[type]) | 151       if (typeMask & filter.contentType & RegExpFilter.typeMap[type]) | 
| 141         HitLogger.emit(tabId, {url, type, docDomain}, filter); | 152         eventEmitter.emit(tabId, {url, type, docDomain}, filter); | 
| 142     } | 153     } | 
| 143   } | 154   } | 
| 144 }; | 155 }; | 
| 145 | 156 | 
| 146 port.on("hitLogger.traceElemHide", (message, sender) => | 157 port.on("hitLogger.traceElemHide", (message, sender) => | 
| 147 { | 158 { | 
| 148   logHiddenElements( | 159   logHiddenElements( | 
| 149     sender.page.id, message.selectors, message.filters, | 160     sender.page.id, message.selectors, message.filters, | 
| 150     extractHostFromFrame(sender.frame) | 161     extractHostFromFrame(sender.frame) | 
| 151   ); | 162   ); | 
| 152 }); | 163 }); | 
| LEFT | RIGHT | 
|---|