OLD | NEW |
| (Empty) |
1 /* | |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | |
3 * Copyright (C) 2006-present 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 window.ext = {}; | |
21 | |
22 let reportData = new DOMParser().parseFromString("<report></report>", "text/xml"
); | |
23 | |
24 let pages = { | |
25 "typeSelectorPage": [initTypeSelector, leaveTypeSelector], | |
26 "commentPage": [initCommentPage, leaveCommentPage], | |
27 "sendPage": [initSendPage, leaveSendPage] | |
28 }; | |
29 | |
30 document.addEventListener("DOMContentLoaded", () => | |
31 { | |
32 document.getElementById("cancel").addEventListener("click", () => | |
33 { | |
34 window.close(); | |
35 }); | |
36 | |
37 document.getElementById("continue").addEventListener("click", () => | |
38 { | |
39 if (!document.getElementById("continue").disabled) | |
40 pages[getCurrentPage()][1](); | |
41 }); | |
42 | |
43 document.addEventListener("keydown", event => | |
44 { | |
45 let blacklistedElements = new Set(["textarea", "button", "a"]) | |
46 | |
47 if (event.key == "Enter" && !blacklistedElements.has(event.target.localName)
) | |
48 document.getElementById("continue").click(); | |
49 else if (event.key == "Escape") | |
50 document.getElementById("cancel").click(); | |
51 }); | |
52 | |
53 browser.runtime.sendMessage({ | |
54 type: "app.get", | |
55 what: "doclink", | |
56 link: "reporter_privacy" | |
57 }).then(url => | |
58 { | |
59 document.getElementById("privacyPolicy").href = url; | |
60 }); | |
61 | |
62 initDataCollector(); | |
63 }); | |
64 | |
65 function getCurrentPage() | |
66 { | |
67 return document.querySelector(".page:not([hidden])").id; | |
68 } | |
69 | |
70 function setCurrentPage(pageId) | |
71 { | |
72 if (!pages.hasOwnProperty(pageId)) | |
73 return; | |
74 | |
75 let previousPage = document.querySelector(".page:not([hidden])"); | |
76 if (previousPage) | |
77 previousPage.hidden = true; | |
78 | |
79 document.getElementById(pageId).hidden = false; | |
80 pages[pageId][0](); | |
81 } | |
82 | |
83 function censorURL(url) | |
84 { | |
85 return url.replace(/([?;&\/#][^?;&\/#]+?=)[^?;&\/#]+/g, "$1*"); | |
86 } | |
87 | |
88 function encodeHTML(str) | |
89 { | |
90 return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").
replace(/"/g, """); | |
91 } | |
92 | |
93 function serializeReportData() | |
94 { | |
95 let result = new XMLSerializer().serializeToString(reportData); | |
96 | |
97 // Insert line breaks before each new tag | |
98 result = result.replace(/(<[^\/]([^"<>]*|"[^"]*")*>)/g, "\n$1"); | |
99 result = result.replace(/^\n+/, ""); | |
100 return result; | |
101 } | |
102 | |
103 function retrieveAddonInfo() | |
104 { | |
105 let element = reportData.createElement("adblock-plus"); | |
106 return browser.runtime.sendMessage({ | |
107 type: "app.get", | |
108 what: "addonVersion" | |
109 }).then(addonVersion => | |
110 { | |
111 element.setAttribute("version", addonVersion); | |
112 return browser.runtime.sendMessage({ | |
113 type: "app.get", | |
114 what: "localeInfo" | |
115 }); | |
116 }).then(({locale}) => | |
117 { | |
118 element.setAttribute("locale", locale); | |
119 reportData.documentElement.appendChild(element); | |
120 }); | |
121 } | |
122 | |
123 function retrieveApplicationInfo() | |
124 { | |
125 let element = reportData.createElement("application"); | |
126 return browser.runtime.sendMessage({ | |
127 type: "app.get", | |
128 what: "application" | |
129 }).then(application => | |
130 { | |
131 element.setAttribute("name", application); | |
132 return browser.runtime.sendMessage({ | |
133 type: "app.get", | |
134 what: "applicationVersion" | |
135 }); | |
136 }).then(applicationVersion => | |
137 { | |
138 element.setAttribute("version", applicationVersion); | |
139 element.setAttribute("userAgent", navigator.userAgent); | |
140 reportData.documentElement.appendChild(element); | |
141 }); | |
142 } | |
143 | |
144 function retrievePlatformInfo() | |
145 { | |
146 let element = reportData.createElement("platform"); | |
147 return browser.runtime.sendMessage({ | |
148 type: "app.get", | |
149 what: "platform" | |
150 }).then(platform => | |
151 { | |
152 element.setAttribute("name", platform); | |
153 return browser.runtime.sendMessage({ | |
154 type: "app.get", | |
155 what: "platformVersion" | |
156 }); | |
157 }).then(platformVersion => | |
158 { | |
159 element.setAttribute("version", platformVersion); | |
160 reportData.documentElement.appendChild(element); | |
161 }); | |
162 } | |
163 | |
164 function retrieveTabURL(tabId) | |
165 { | |
166 return browser.tabs.get(tabId).then(tab => | |
167 { | |
168 let element = reportData.createElement("window"); | |
169 if (tab.url) | |
170 element.setAttribute("url", censorURL(tab.url)); | |
171 reportData.documentElement.appendChild(element); | |
172 }); | |
173 } | |
174 | |
175 function retrieveSubscriptions() | |
176 { | |
177 return browser.runtime.sendMessage({ | |
178 type: "subscriptions.get", | |
179 ignoreDisabled: true, | |
180 downloadable: true | |
181 }).then(subscriptions => | |
182 { | |
183 let element = reportData.createElement("subscriptions"); | |
184 for (let subscription of subscriptions) | |
185 { | |
186 if (!/^(http|https|ftp):/.test(subscription.url)) | |
187 continue; | |
188 | |
189 let now = Math.round(Date.now() / 1000); | |
190 let subscriptionElement = reportData.createElement("subscription"); | |
191 subscriptionElement.setAttribute("id", subscription.url); | |
192 if (subscription.lastDownload) | |
193 subscriptionElement.setAttribute("lastDownloadAttempt", subscription.las
tDownload - now); | |
194 subscriptionElement.setAttribute("downloadStatus", subscription.downloadSt
atus); | |
195 element.appendChild(subscriptionElement); | |
196 } | |
197 reportData.documentElement.appendChild(element); | |
198 }); | |
199 } | |
200 | |
201 function initDataCollector() | |
202 { | |
203 Promise.resolve().then(() => | |
204 { | |
205 let tabId = parseInt(location.search.replace(/^\?/, ""), 10) || 0; | |
206 let handlers = [ | |
207 retrieveAddonInfo(), | |
208 retrieveApplicationInfo(), | |
209 retrievePlatformInfo(), | |
210 retrieveTabURL(tabId), | |
211 retrieveSubscriptions() | |
212 ]; | |
213 return Promise.all(handlers); | |
214 }).then(() => | |
215 { | |
216 setCurrentPage("typeSelectorPage"); | |
217 }).catch(e => | |
218 { | |
219 if (!e.name && e.message) | |
220 e = e.message; | |
221 alert(e); | |
222 window.close(); | |
223 }); | |
224 } | |
225 | |
226 function initTypeSelector() | |
227 { | |
228 document.getElementById("typeFalsePositive").focus(); | |
229 | |
230 | |
231 for (let checkbox of document.querySelectorAll("input[name='type']")) | |
232 { | |
233 checkbox.addEventListener("click", () => | |
234 { | |
235 if (document.querySelector("input[name='type']:checked")) | |
236 document.getElementById("continue").disabled = false; | |
237 }); | |
238 } | |
239 } | |
240 | |
241 function leaveTypeSelector() | |
242 { | |
243 let checkbox = document.querySelector("input[name='type']:checked"); | |
244 reportData.documentElement.setAttribute("type", checkbox.value); | |
245 setCurrentPage("commentPage"); | |
246 } | |
247 | |
248 function initCommentPage() | |
249 { | |
250 let continueButton = document.getElementById("continue"); | |
251 continueButton.disabled = true; | |
252 continueButton.textContent = browser.i18n.getMessage("issueReporter_sendButton
_label"); | |
253 | |
254 let emailElement = reportData.createElement("email"); | |
255 let emailField = document.getElementById("email"); | |
256 let anonymousSubmissionField = document.getElementById("anonymousSubmission"); | |
257 let validateEmail = () => | |
258 { | |
259 document.getElementById("anonymousSubmissionWarning").setAttribute("data-inv
isible", !anonymousSubmissionField.checked); | |
260 if (anonymousSubmissionField.checked) | |
261 { | |
262 emailField.value = ""; | |
263 emailField.disabled = true; | |
264 continueButton.disabled = false; | |
265 if (emailElement.parentNode) | |
266 emailElement.parentNode.removeChild(emailElement); | |
267 } | |
268 else | |
269 { | |
270 emailField.disabled = false; | |
271 | |
272 let value = emailField.value.trim(); | |
273 emailElement.textContent = value; | |
274 reportData.documentElement.appendChild(emailElement); | |
275 continueButton.disabled = value == "" || !emailField.validity.valid; | |
276 } | |
277 }; | |
278 emailField.addEventListener("input", validateEmail); | |
279 anonymousSubmissionField.addEventListener("click", validateEmail); | |
280 | |
281 let commentElement = reportData.createElement("comment"); | |
282 document.getElementById("comment").addEventListener("input", event => | |
283 { | |
284 if (commentElement.parentNode) | |
285 commentElement.parentNode.removeChild(commentElement); | |
286 | |
287 let value = event.target.value.trim(); | |
288 commentElement.textContent = value.substr(0, 1000); | |
289 if (value) | |
290 reportData.documentElement.appendChild(commentElement); | |
291 document.getElementById("commentLengthWarning").setAttribute("data-invisible
", value.length <= 1000); | |
292 }); | |
293 | |
294 document.getElementById("showData").addEventListener("click", event => | |
295 { | |
296 event.preventDefault(); | |
297 | |
298 // window.open() won't open data: URIs in Chrome | |
299 browser.tabs.getCurrent().then(tab => | |
300 { | |
301 browser.tabs.create({ | |
302 url: "data:text/xml;charset=utf-8," + encodeURIComponent(serializeReport
Data()), | |
303 openerTabId: tab.id | |
304 }); | |
305 }) | |
306 }); | |
307 | |
308 emailField.focus(); | |
309 } | |
310 | |
311 function leaveCommentPage() | |
312 { | |
313 setCurrentPage("sendPage"); | |
314 } | |
315 | |
316 function initSendPage() | |
317 { | |
318 document.getElementById("cancel").hidden = true; | |
319 | |
320 let continueButton = document.getElementById("continue"); | |
321 continueButton.textContent = browser.i18n.getMessage("issueReporter_doneButton
_label"); | |
322 continueButton.disabled = true; | |
323 | |
324 let uuid = new Uint16Array(8); | |
325 window.crypto.getRandomValues(uuid); | |
326 uuid[3] = uuid[3] & 0x0FFF | 0x4000; // version 4 | |
327 uuid[4] = uuid[4] & 0x3FFF | 0x8000; // variant 1 | |
328 | |
329 let uuidString = ""; | |
330 for (let i = 0; i < uuid.length; i++) | |
331 { | |
332 let component = uuid[i].toString(16); | |
333 while (component.length < 4) | |
334 component = "0" + component; | |
335 uuidString += component; | |
336 if (i >= 1 && i<= 4) | |
337 uuidString += "-"; | |
338 } | |
339 | |
340 let params = new URLSearchParams({ | |
341 version: 1, | |
342 guid: uuidString, | |
343 lang: reportData.getElementsByTagName("adblock-plus")[0].getAttribute("local
e") | |
344 }); | |
345 let url = "https://reports.adblockplus.org/submitReport?" + params; | |
346 | |
347 let reportSent = event => | |
348 { | |
349 let success = false; | |
350 let errorMessage = browser.i18n.getMessage("filters_subscription_lastDownloa
d_connectionError"); | |
351 try | |
352 { | |
353 success = request.status == 200; | |
354 if (request.status != 0) | |
355 errorMessage = request.status + " " + request.statusText; | |
356 } | |
357 catch (e) | |
358 { | |
359 // Getting request status might throw if no connection was established | |
360 } | |
361 | |
362 let result; | |
363 try | |
364 { | |
365 result = request.responseText; | |
366 } | |
367 catch (e) | |
368 { | |
369 result = ""; | |
370 } | |
371 | |
372 if (!success) | |
373 { | |
374 let errorElement = document.getElementById("error"); | |
375 let template = browser.i18n.getMessage("issueReporter_errorMessage").repla
ce(/[\r\n\s]+/g, " "); | |
376 | |
377 let [, beforeLink, linkText, afterLink] = /(.*)\[link\](.*)\[\/link\](.*)/
.exec(template) || [null, "", template, ""]; | |
378 beforeLink = beforeLink.replace(/\?1\?/g, errorMessage); | |
379 afterLink = afterLink.replace(/\?1\?/g, errorMessage); | |
380 | |
381 while (errorElement.firstChild) | |
382 errorElement.removeChild(errorElement.firstChild); | |
383 | |
384 let link = document.createElement("a"); | |
385 link.textContent = linkText; | |
386 browser.runtime.sendMessage({ | |
387 type: "app.get", | |
388 what: "doclink", | |
389 link: "reporter_connect_issue" | |
390 }).then(url => | |
391 { | |
392 link.href = url; | |
393 }); | |
394 | |
395 | |
396 errorElement.appendChild(document.createTextNode(beforeLink)); | |
397 errorElement.appendChild(link); | |
398 errorElement.appendChild(document.createTextNode(afterLink)); | |
399 | |
400 errorElement.hidden = false; | |
401 } | |
402 | |
403 result = result.replace(/%CONFIRMATION%/g, encodeHTML(browser.i18n.getMessag
e("issueReporter_confirmationMessage"))); | |
404 result = result.replace(/%KNOWNISSUE%/g, encodeHTML(browser.i18n.getMessage(
"issueReporter_knownIssueMessage"))); | |
405 result = result.replace(/(<html)\b/, '$1 dir="' + encodeHTML(window.getCompu
tedStyle(document.documentElement, "").direction + '"')); | |
406 | |
407 document.getElementById("sendReportMessage").hidden = true; | |
408 document.getElementById("sendingProgressContainer").hidden = true; | |
409 | |
410 let resultFrame = document.getElementById("result"); | |
411 resultFrame.setAttribute("src", "data:text/html;charset=utf-8," + encodeURIC
omponent(result)); | |
412 resultFrame.hidden = false; | |
413 | |
414 document.getElementById("continue").disabled = false; | |
415 }; | |
416 | |
417 let request = new XMLHttpRequest(); | |
418 request.open("POST", url); | |
419 request.setRequestHeader("Content-Type", "text/xml"); | |
420 request.setRequestHeader("X-Adblock-Plus", "1"); | |
421 request.addEventListener("load", reportSent); | |
422 request.addEventListener("error", reportSent); | |
423 request.upload.addEventListener("progress", event => | |
424 { | |
425 if (!event.lengthComputable) | |
426 return; | |
427 | |
428 let progress = Math.round(event.loaded / event.total * 100); | |
429 if (event.loaded > 0) | |
430 { | |
431 let progress = document.getElementById("sendingProgress"); | |
432 progress.max = event.total; | |
433 progress.value = event.loaded; | |
434 } | |
435 }); | |
436 request.send(serializeReportData()); | |
437 } | |
438 | |
439 function leaveSendPage() | |
440 { | |
441 window.close(); | |
442 } | |
OLD | NEW |