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

Side by Side Diff: issue-reporter.js

Issue 29583568: Issue 5880 - Basic issue reporter implementation (Closed) Base URL: https://hg.adblockplus.org/adblockpluschrome
Patch Set: Do not offer issue reporter for local pages Created Oct. 19, 2017, 2:06 p.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 | « issue-reporter.html ('k') | polyfill.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-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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"). replace(/"/g, "&quot;");
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;
Felix Dahlke 2017/10/19 16:48:04 Why not make this a pref?
Wladimir Palant 2017/10/19 17:54:25 Why make it a pref? It used to be a pref in the le
Felix Dahlke 2017/10/19 18:21:31 Two reasons: 1. Feels wrong to hard code URLs in
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 }
OLDNEW
« no previous file with comments | « issue-reporter.html ('k') | polyfill.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld