Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/filterHits.js

Issue 6337686776315904: Issue 394 - hit statistics tool data collection (Closed)
Patch Set: Addressed Dave Comments and updated increaseFilterHits parameter Created April 16, 2015, 10:36 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « defaults/prefs.js ('k') | lib/filterListener.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 statFilter[filterType][host] = {hits: 1, latest: filter.lastHit};
112 else
113 {
114 statFilter[filterType][host].hits++;
115 statFilter[filterType][host].latest = filter.lastHit;
116 }
117 },
118
119 resetFilterHits: function()
120 {
121 this.filters = Object.create(null);
122 this.saveFilterHitsToDatabase();
123 },
124
125 sendFilterHitsToServer: function()
126 {
127 if (!Prefs.sendstats)
128 return;
129
130 let request = new XMLHttpRequest();
131 request.open("POST", Prefs.sendstats_url);
132 request.setRequestHeader("Content-Type", "application/json");
133 request.addEventListener("load", function(event)
134 {
135 FilterHits._sending = false;
136 if (request.status >= 200 && request.status < 300)
137 {
138 FilterHits._lastPush = new Date().getTime();
139 FilterHits.resetFilterHits();
140 }
141 else
142 Cu.reportError("Could not send filter hit statistics to Adblock Plus ser ver.");
143 }, false);
144
145 let {addonName, addonVersion, application,
146 applicationVersion, platform, platformVersion} = require("info");
147 let data = {
148 version: 1,
149 timeSincePush: this._lastPush,
150 addonName: addonName,
151 addonVersion: addonVersion,
152 application: application,
153 applicationVersion: applicationVersion,
154 platform: platform,
155 platformVersion: platformVersion,
156 filters: this.filters
157 };
158
159 this._sending = true;
160 request.send(JSON.stringify(data));
161 },
162
163 getStorageFile: function()
164 {
165 return FileUtils.getFile("ProfD", ["adblockplus.sqlite"]);
166 },
167
168 checkCreateTable: function(connection)
169 {
170 if (!connection.tableExists("filterhits"))
171 connection.executeSimpleSQL("CREATE TABLE filterhits (id INTEGER PRIMARY K EY, filters TEXT, date INTEGER)");
172 },
173
174 /**
175 * Load Filter hits from database
176 */
177 loadFilterHitsFromDatabase: function()
178 {
179 let storageFile = this.getStorageFile();
180 if (!storageFile)
181 return;
182
183 let connection = Services.storage.openDatabase(storageFile);
184 this.checkCreateTable(connection);
185
186 let statement = connection.createStatement("SELECT * FROM filterhits");
187 if (!this._loading)
188 {
189 this._loading = true;
190 statement.executeAsync(
191 {
192 handleResult: function(results)
193 {
194 let row = results.getNextRow();
195 if (row)
196 {
197 let filters = row.getResultByName("filters");
198 let lastDate = row.getResultByName("date");
199 FilterHits.filters = JSON.parse(filters);
200 FilterHits._lastPush = lastDate;
201 }
202 },
203
204 handleError: function(error)
205 {
206 Cu.reportError(error.message);
207 },
208
209 handleCompletion: function(reason)
210 {
211 if (reason != Ci.mozIStorageStatementCallback.REASON_FINISHED)
212 Cu.reportError("Loading of filter hits canceled or aborted.");
213 FilterHits._loading = false;
214 }
215 });
216 }
217
218 connection.asyncClose();
219 },
220
221 /**
222 * Save Filter hits to database
223 */
224 saveFilterHitsToDatabase: function()
225 {
226 let now = new Date().getTime();
227 if (!this._lastPush)
228 this._lastPush = now;
229
230 if (!this._sending && now - this._lastPush > this._pushInterval)
231 {
232 this.sendFilterHitsToServer();
233 return;
234 }
235
236 let storageFile = this.getStorageFile();
237 if (!storageFile)
238 return;
239
240 let connection = Services.storage.openDatabase(storageFile);
241 this.checkCreateTable(connection);
242
243 let statement = connection.createStatement("INSERT OR REPLACE INTO filterhit s (id, filters, date) VALUES(0, :filters, :date)");
244 statement.params.filters = JSON.stringify(this.filters);
245 statement.params.date = this._lastPush;
246 if (!this._saving)
247 {
248 this._saving = true;
249 statement.executeAsync(
250 {
251 handleError: function(aError)
252 {
253 Cu.reportError(aError.message);
254 },
255
256 handleCompletion: function(aReason)
257 {
258 if (aReason != Components.interfaces.mozIStorageStatementCallback.REAS ON_FINISHED)
259 Cu.reportError("Writing of filter hits canceled or aborted.");
260 FilterHits._saving = false;
261 }
262 });
263 }
264
265 connection.asyncClose();
266 }
267 };
268
269 FilterNotifier.addListener(function(action)
270 {
271 if (action == "load" && Prefs.sendstats)
272 FilterHits.loadFilterHitsFromDatabase();
273 });
OLDNEW
« no previous file with comments | « defaults/prefs.js ('k') | lib/filterListener.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld