| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 /* | 
|  | 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|  | 3  * Copyright (C) 2006-2015 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 let {Services} = Cu.import("resource://gre/modules/Services.jsm", null); | 
|  | 19 let {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", null); | 
|  | 20 | 
|  | 21 let {Prefs} = require("prefs"); | 
|  | 22 let {Utils} = require("utils"); | 
|  | 23 let {MILLIS_IN_DAY} = require("downloader"); | 
|  | 24 let {FilterNotifier} = require("filterNotifier"); | 
|  | 25 let {DownloadableSubscription} = require("subscriptionClasses"); | 
|  | 26 | 
|  | 27 /** | 
|  | 28  * This class reads filter hits statistics from SQLite database, | 
|  | 29  * manages them in memory and writes them back. | 
|  | 30  * @class | 
|  | 31  */ | 
|  | 32 let FilterHits = exports.FilterHits = | 
|  | 33 { | 
|  | 34   /** | 
|  | 35    * Data that shoud be sent to the server | 
|  | 36    * @type Object | 
|  | 37    */ | 
|  | 38   filters: Object.create(null), | 
|  | 39 | 
|  | 40   /** | 
|  | 41    * Time since last push | 
|  | 42    * @type Number | 
|  | 43    */ | 
|  | 44   _lastPush: 0, | 
|  | 45 | 
|  | 46   /** | 
|  | 47    * Indicates the timeframe between pushes | 
|  | 48    * @type Number | 
|  | 49    */ | 
|  | 50   _pushInterval: MILLIS_IN_DAY * 7, | 
|  | 51 | 
|  | 52   /** | 
|  | 53    * Indicates whether the data is being loaded from storage | 
|  | 54    * @type Boolean | 
|  | 55    */ | 
|  | 56   _loading: false, | 
|  | 57 | 
|  | 58   /** | 
|  | 59    * Indicates whether the data is being saved to storage | 
|  | 60    * @type Boolean | 
|  | 61    */ | 
|  | 62   _saving: false, | 
|  | 63 | 
|  | 64   /** | 
|  | 65    * Indicates whether the data is being sent to the server | 
|  | 66    * @type Boolean | 
|  | 67    */ | 
|  | 68   _sending: false, | 
|  | 69 | 
|  | 70   /** | 
|  | 71    * Increases the filter hit count | 
|  | 72    * @param {Filter} filter | 
|  | 73    * @param {String} host | 
|  | 74    */ | 
|  | 75   increaseFilterHits: function(filter, host) | 
|  | 76   { | 
|  | 77     let subscriptions = filter.subscriptions; | 
|  | 78     let inDownloadableSubscription = false; | 
|  | 79     for (let i = 0; i < subscriptions.length; i++) | 
|  | 80     { | 
|  | 81       if (subscriptions[i] instanceof DownloadableSubscription) | 
|  | 82       { | 
|  | 83         inDownloadableSubscription = true; | 
|  | 84         break; | 
|  | 85       } | 
|  | 86     } | 
|  | 87 | 
|  | 88     if (!inDownloadableSubscription) | 
|  | 89       return; | 
|  | 90 | 
|  | 91     if (!(filter.text in this.filters)) | 
|  | 92       this.filters[filter.text] = Object.create(null); | 
|  | 93 | 
|  | 94     let statFilter = this.filters[filter.text]; | 
|  | 95     let filterType = filter.thirdParty ? "thirdParty" : "firstParty"; | 
|  | 96 | 
|  | 97     if (!(filterType in statFilter)) | 
|  | 98       statFilter[filterType] = Object.create(null); | 
|  | 99 | 
|  | 100     if (!("subscriptions" in statFilter)) | 
|  | 101       statFilter.subscriptions = []; | 
|  | 102 | 
|  | 103     for (let i = 0; i < subscriptions.length; i++) | 
|  | 104     { | 
|  | 105       if (subscriptions[i] instanceof DownloadableSubscription | 
|  | 106           && statFilter.subscriptions.indexOf(subscriptions[i].url) == -1) | 
|  | 107         statFilter.subscriptions.push(subscriptions[i].url); | 
|  | 108     } | 
|  | 109 | 
|  | 110     if (!(host in statFilter[filterType])) | 
|  | 111     { | 
|  | 112       statFilter[filterType][host] = {hits: 1, latest: filter.lastHit}; | 
|  | 113     } | 
|  | 114     else | 
|  | 115     { | 
|  | 116       statFilter[filterType][host].hits++; | 
|  | 117       statFilter[filterType][host].latest = filter.lastHit; | 
|  | 118     } | 
|  | 119   }, | 
|  | 120 | 
|  | 121   resetFilterHits: function() | 
|  | 122   { | 
|  | 123     this.filters = Object.create(null); | 
|  | 124     this.saveFilterHitsToDatabase(); | 
|  | 125   }, | 
|  | 126 | 
|  | 127   sendFilterHitsToServer: function() | 
|  | 128   { | 
|  | 129     if (!Prefs.sendstats) | 
|  | 130       return; | 
|  | 131 | 
|  | 132     let request = new XMLHttpRequest(); | 
|  | 133     request.open("POST", Prefs.sendstats_url); | 
|  | 134     request.setRequestHeader("Content-Type", "application/json"); | 
|  | 135     request.addEventListener("load", function(event) | 
|  | 136     { | 
|  | 137       FilterHits._sending = false; | 
|  | 138       if (request.status >= 200 && request.status < 300) | 
|  | 139       { | 
|  | 140         FilterHits._lastPush = new Date().getTime(); | 
|  | 141         FilterHits.resetFilterHits(); | 
|  | 142       } | 
|  | 143       else | 
|  | 144       { | 
|  | 145         Cu.reportError("Could not send filter hit statistics to Adblock Plus ser
     ver."); | 
|  | 146       } | 
|  | 147     }, false); | 
|  | 148 | 
|  | 149     let {addonName, addonVersion, application, | 
|  | 150         applicationVersion, platform, platformVersion} = require("info"); | 
|  | 151     let data = { | 
|  | 152       version: 1, | 
|  | 153       timeSincePush: this._lastPush, | 
|  | 154       addonName: addonName, | 
|  | 155       addonVersion: addonVersion, | 
|  | 156       application: application, | 
|  | 157       applicationVersion: applicationVersion, | 
|  | 158       platform: platform, | 
|  | 159       platformVersion: platformVersion, | 
|  | 160       filters: this.filters | 
|  | 161     }; | 
|  | 162 | 
|  | 163     this._sending = true; | 
|  | 164     request.send(JSON.stringify(data)); | 
|  | 165   }, | 
|  | 166 | 
|  | 167   getStorageFile: function() | 
|  | 168   { | 
|  | 169     return FileUtils.getFile("ProfD", ["adblockplus.sqlite"]); | 
|  | 170   }, | 
|  | 171 | 
|  | 172   checkCreateTable: function(connection) | 
|  | 173   { | 
|  | 174     if (!connection.tableExists("filterhits")) | 
|  | 175       connection.executeSimpleSQL("CREATE TABLE filterhits (id INTEGER PRIMARY K
     EY, filters TEXT, date INTEGER)"); | 
|  | 176   }, | 
|  | 177 | 
|  | 178   /** | 
|  | 179    * Load Filter hits from database | 
|  | 180    */ | 
|  | 181   loadFilterHitsFromDatabase: function() | 
|  | 182   { | 
|  | 183     let storageFile = this.getStorageFile(); | 
|  | 184     if (!storageFile) | 
|  | 185       return; | 
|  | 186 | 
|  | 187     let connection = Services.storage.openDatabase(storageFile); | 
|  | 188     this.checkCreateTable(connection); | 
|  | 189 | 
|  | 190     let statement = connection.createStatement("SELECT * FROM filterhits"); | 
|  | 191     if (!this._loading) | 
|  | 192     { | 
|  | 193       this._loading = true; | 
|  | 194       statement.executeAsync( | 
|  | 195       { | 
|  | 196         handleResult: function(results) | 
|  | 197         { | 
|  | 198           let row = results.getNextRow(); | 
|  | 199           if (row) | 
|  | 200           { | 
|  | 201             let filters = row.getResultByName("filters"); | 
|  | 202             let lastDate = row.getResultByName("date"); | 
|  | 203             FilterHits.filters = JSON.parse(filters); | 
|  | 204             FilterHits._lastPush = lastDate; | 
|  | 205           } | 
|  | 206         }, | 
|  | 207 | 
|  | 208         handleError: function(error) | 
|  | 209         { | 
|  | 210           Cu.reportError(error.message); | 
|  | 211         }, | 
|  | 212 | 
|  | 213         handleCompletion: function(reason) | 
|  | 214         { | 
|  | 215           if (reason != Ci.mozIStorageStatementCallback.REASON_FINISHED) | 
|  | 216             Cu.reportError("Loading of filter hits canceled or aborted."); | 
|  | 217           FilterHits._loading = false; | 
|  | 218         } | 
|  | 219       }); | 
|  | 220     } | 
|  | 221 | 
|  | 222     connection.asyncClose(); | 
|  | 223   }, | 
|  | 224 | 
|  | 225   /** | 
|  | 226    * Save Filter hits to database | 
|  | 227    */ | 
|  | 228   saveFilterHitsToDatabase: function() | 
|  | 229   { | 
|  | 230     let now = new Date().getTime(); | 
|  | 231     if (!this._lastPush) | 
|  | 232       this._lastPush = now; | 
|  | 233 | 
|  | 234     if (!this._sending && now - this._lastPush > this._pushInterval) | 
|  | 235     { | 
|  | 236       this.sendFilterHitsToServer(); | 
|  | 237       return; | 
|  | 238     } | 
|  | 239 | 
|  | 240     let storageFile = this.getStorageFile(); | 
|  | 241     if (!storageFile) | 
|  | 242       return; | 
|  | 243 | 
|  | 244     let connection = Services.storage.openDatabase(storageFile); | 
|  | 245     this.checkCreateTable(connection); | 
|  | 246 | 
|  | 247     let statement = connection.createStatement("INSERT OR REPLACE INTO filterhit
     s (id, filters, date) VALUES(0, :filters, :date)"); | 
|  | 248     statement.params.filters = JSON.stringify(this.filters); | 
|  | 249     statement.params.date = this._lastPush; | 
|  | 250     if (!this._saving) | 
|  | 251     { | 
|  | 252       this._saving = true; | 
|  | 253       statement.executeAsync( | 
|  | 254       { | 
|  | 255         handleError: function(aError) | 
|  | 256         { | 
|  | 257           Cu.reportError(aError.message); | 
|  | 258         }, | 
|  | 259 | 
|  | 260         handleCompletion: function(aReason) | 
|  | 261         { | 
|  | 262           if (aReason != Components.interfaces.mozIStorageStatementCallback.REAS
     ON_FINISHED) | 
|  | 263             Cu.reportError("Writing of filter hits canceled or aborted."); | 
|  | 264           FilterHits._saving = false; | 
|  | 265         } | 
|  | 266       }); | 
|  | 267     } | 
|  | 268 | 
|  | 269     connection.asyncClose(); | 
|  | 270   } | 
|  | 271 }; | 
|  | 272 | 
|  | 273 FilterNotifier.addListener(function(action) | 
|  | 274 { | 
|  | 275   if (action == "load" && Prefs.sendstats) | 
|  | 276     FilterHits.loadFilterHitsFromDatabase(); | 
|  | 277 }); | 
| OLD | NEW | 
|---|