1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 const UPDATE_BEGIN = "safebrowsing-update-begin";
6 const UPDATE_FINISH = "safebrowsing-update-finished";
7 const JSLOG_PREF = "browser.safebrowsing.debug";
9 window.onunload = function() {
16 window.onload = function() {
28 let classifier = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
31 let featureNames = classifier.getFeatureNames();
33 let fragment = document.createDocumentFragment();
34 featureNames.forEach(featureName => {
35 let container = document.createElement("label");
36 container.className = "toggle-container-with-text";
37 fragment.appendChild(container);
39 let checkbox = document.createElement("input");
40 checkbox.id = "feature_" + featureName;
41 checkbox.type = "checkbox";
42 checkbox.checked = true;
43 container.appendChild(checkbox);
45 let span = document.createElement("span");
46 container.appendChild(span);
48 let text = document.createTextNode(featureName);
49 span.appendChild(text);
52 let list = document.getElementById("search-features");
53 list.appendChild(fragment);
55 let btn = document.getElementById("search-button");
56 btn.addEventListener("click", this.search);
63 let list = document.getElementById("search-features");
64 while (list.firstChild) {
65 list.firstChild.remove();
68 let btn = document.getElementById("search-button");
69 btn.removeEventListener("click", this.search);
76 let input = document.getElementById("search-input").value;
80 uri = Services.io.newURI(input);
82 Search.reportError("url-classifier-search-error-invalid-url");
86 Search.reportError("url-classifier-search-error-invalid-url");
90 let classifier = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
94 let featureNames = classifier.getFeatureNames();
96 featureNames.forEach(featureName => {
97 if (document.getElementById("feature_" + featureName).checked) {
98 let feature = classifier.getFeatureByName(featureName);
100 features.push(feature);
105 if (!features.length) {
106 Search.reportError("url-classifier-search-error-no-features");
111 document.getElementById("search-listtype").value == 0
112 ? Ci.nsIUrlClassifierFeature.blocklist
113 : Ci.nsIUrlClassifierFeature.entitylist;
114 classifier.asyncClassifyLocalWithFeatures(uri, features, listType, list =>
115 Search.showResults(list)
122 let errorMessage = document.getElementById("search-error-message");
123 errorMessage.style.display = "none";
127 let errorMessage = document.getElementById("search-error-message");
128 document.l10n.setAttributes(errorMessage, msg);
129 errorMessage.style.display = "";
133 let resultTitle = document.getElementById("result-title");
134 resultTitle.style.display = "none";
136 let resultTable = document.getElementById("result-table");
137 resultTable.style.display = "none";
140 showResults(results) {
141 let fragment = document.createDocumentFragment();
142 results.forEach(result => {
143 let tr = document.createElement("tr");
144 fragment.appendChild(tr);
146 let th = document.createElement("th");
148 th.appendChild(document.createTextNode(result.feature.name));
150 let td = document.createElement("td");
153 let featureName = document.createElement("div");
154 document.l10n.setAttributes(
156 "url-classifier-search-result-uri",
157 { uri: result.uri.spec }
159 td.appendChild(featureName);
161 let list = document.createElement("div");
162 document.l10n.setAttributes(list, "url-classifier-search-result-list", {
165 td.appendChild(list);
168 let resultTable = document.getElementById("result-table");
169 while (resultTable.firstChild) {
170 resultTable.firstChild.remove();
173 resultTable.appendChild(fragment);
174 resultTable.style.display = "";
176 let resultTitle = document.getElementById("result-title");
177 resultTitle.style.display = "";
187 updatingProvider: "",
190 this.providers = new Set();
191 let branch = Services.prefs.getBranch("browser.safebrowsing.provider.");
192 let children = branch.getChildList("");
193 for (let child of children) {
194 let provider = child.split(".")[0];
195 if (this.isActiveProvider(provider)) {
196 this.providers.add(provider);
206 Services.obs.removeObserver(this.onBeginUpdate, UPDATE_BEGIN);
207 Services.obs.removeObserver(this.onFinishUpdate, UPDATE_FINISH);
210 onBeginUpdate(aSubject, aTopic, aData) {
211 this.updatingProvider = aData;
212 let p = this.updatingProvider;
214 // Disable update button for the provider while we are doing update.
215 document.getElementById("update-" + p).disabled = true;
217 let elem = document.getElementById(p + "-col-lastupdateresult");
218 document.l10n.setAttributes(elem, "url-classifier-updating");
221 onFinishUpdate(aSubject, aTopic, aData) {
222 let p = this.updatingProvider;
223 this.updatingProvider = "";
225 // It is possible that we get update-finished event only because
226 // about::url-classifier is opened after update-begin event is fired.
234 document.getElementById("update-" + p).disabled = false;
236 let elem = document.getElementById(p + "-col-lastupdateresult");
237 if (aData.startsWith("success")) {
238 document.l10n.setAttributes(elem, "url-classifier-success");
239 } else if (aData.startsWith("update error")) {
240 document.l10n.setAttributes(elem, "url-classifier-update-error", {
241 error: aData.split(": ")[1],
243 } else if (aData.startsWith("download error")) {
244 document.l10n.setAttributes(elem, "url-classifier-download-error", {
245 error: aData.split(": ")[1],
248 elem.childNodes[0].nodeValue = aData;
253 // Handle begin update
254 this.onBeginUpdate = this.onBeginUpdate.bind(this);
255 Services.obs.addObserver(this.onBeginUpdate, UPDATE_BEGIN);
257 // Handle finish update
258 this.onFinishUpdate = this.onFinishUpdate.bind(this);
259 Services.obs.addObserver(this.onFinishUpdate, UPDATE_FINISH);
262 // This should only be called once because we assume number of providers
265 let tbody = document.getElementById("provider-table-body");
267 for (let provider of this.providers) {
268 let tr = document.createElement("tr");
269 let cols = document.getElementById("provider-head-row").childNodes;
270 for (let column of cols) {
274 let td = document.createElement("td");
275 td.id = provider + "-" + column.id;
277 if (column.id === "col-update") {
278 let btn = document.createElement("button");
279 btn.id = "update-" + provider;
280 btn.addEventListener("click", () => {
281 this.update(provider);
284 document.l10n.setAttributes(btn, "url-classifier-trigger-update");
286 } else if (column.id === "col-lastupdateresult") {
287 document.l10n.setAttributes(td, "url-classifier-not-available");
289 td.appendChild(document.createTextNode(""));
293 tbody.appendChild(tr);
297 refresh(listProviders = this.providers) {
298 for (let provider of listProviders) {
300 values["col-provider"] = provider;
303 "browser.safebrowsing.provider." + provider + ".lastupdatetime";
304 let lut = Services.prefs.getCharPref(pref, "");
305 values["col-lastupdatetime"] = lut ? new Date(lut * 1) : null;
307 pref = "browser.safebrowsing.provider." + provider + ".nextupdatetime";
308 let nut = Services.prefs.getCharPref(pref, "");
309 values["col-nextupdatetime"] = nut ? new Date(nut * 1) : null;
311 let listmanager = Cc[
312 "@mozilla.org/url-classifier/listmanager;1"
313 ].getService(Ci.nsIUrlListManager);
314 let bot = listmanager.getBackOffTime(provider);
315 values["col-backofftime"] = bot ? new Date(bot * 1) : null;
317 for (let key of Object.keys(values)) {
318 let elem = document.getElementById(provider + "-" + key);
320 elem.removeAttribute("data-l10n-id");
321 elem.childNodes[0].nodeValue = values[key];
323 document.l10n.setAttributes(elem, "url-classifier-not-available");
329 // Call update for the provider.
331 let listmanager = Cc[
332 "@mozilla.org/url-classifier/listmanager;1"
333 ].getService(Ci.nsIUrlListManager);
335 let pref = "browser.safebrowsing.provider." + provider + ".lists";
336 let tables = Services.prefs.getCharPref(pref, "");
338 if (!listmanager.forceUpdates(tables)) {
339 // This may because of back-off algorithm.
340 let elem = document.getElementById(provider + "-col-lastupdateresult");
341 document.l10n.setAttributes(elem, "url-classifier-cannot-update");
345 // if we can find any table registered an updateURL in the listmanager,
346 // the provider is active. This is used to filter out google v2 provider
347 // without changing the preference.
348 isActiveProvider(provider) {
349 let listmanager = Cc[
350 "@mozilla.org/url-classifier/listmanager;1"
351 ].getService(Ci.nsIUrlListManager);
353 let pref = "browser.safebrowsing.provider." + provider + ".lists";
354 let tables = Services.prefs.getCharPref(pref, "").split(",");
356 for (let i = 0; i < tables.length; i++) {
357 let updateUrl = listmanager.getUpdateUrl(tables[i]);
371 // Tables that show cahe entries.
372 showCacheEnties: null,
375 this.showCacheEnties = new Set();
382 Services.obs.removeObserver(this.refresh, UPDATE_FINISH);
386 this.refresh = this.refresh.bind(this);
387 Services.obs.addObserver(this.refresh, UPDATE_FINISH);
391 this.createCacheEntries();
393 let refreshBtn = document.getElementById("refresh-cache-btn");
394 refreshBtn.addEventListener("click", () => {
398 let clearBtn = document.getElementById("clear-cache-btn");
399 clearBtn.addEventListener("click", () => {
400 let dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
401 Ci.nsIUrlClassifierDBService
403 dbservice.clearCache();
404 // Since clearCache is async call, we just simply assume it will be
405 // updated in 100 milli-seconds.
413 this.clearCacheEntries();
414 this.createCacheEntries();
417 clearCacheEntries() {
418 let ctbody = document.getElementById("cache-table-body");
419 while (ctbody.firstChild) {
420 ctbody.firstChild.remove();
423 let cetbody = document.getElementById("cache-entries-table-body");
424 while (cetbody.firstChild) {
425 cetbody.firstChild.remove();
429 createCacheEntries() {
430 function createRow(tds, body, cols) {
431 let tr = document.createElement("tr");
432 tds.forEach(function(v, i, a) {
433 let td = document.createElement("td");
434 if (i == 0 && tds.length != cols) {
435 td.setAttribute("colspan", cols - tds.length + 1);
438 if (typeof v === "object") {
440 document.l10n.setAttributes(td, v.l10n);
442 td.removeAttribute("data-l10n-id");
446 td.removeAttribute("data-l10n-id");
452 body.appendChild(tr);
455 let dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
456 Ci.nsIUrlClassifierInfo
459 for (let provider of Provider.providers) {
460 let pref = "browser.safebrowsing.provider." + provider + ".lists";
461 let tables = Services.prefs.getCharPref(pref, "").split(",");
463 for (let table of tables) {
464 dbservice.getCacheInfo(table, {
465 onGetCacheComplete: aCache => {
466 let entries = aCache.entries;
467 if (entries.length === 0) {
468 this.showCacheEnties.delete(table);
472 let positiveCacheCount = 0;
473 for (let i = 0; i < entries.length; i++) {
474 let entry = entries.queryElementAt(
476 Ci.nsIUrlClassifierCacheEntry
478 let matches = entry.matches;
479 positiveCacheCount += matches.length;
481 // If we don't have to show cache entries for this table then just
482 // skip the following code.
483 if (!this.showCacheEnties.has(table)) {
490 new Date(entry.expiry * 1000).toString(),
494 if (matches.length >= 1) {
495 let match = matches.queryElementAt(
497 Ci.nsIUrlClassifierPositiveCacheEntry
501 new Date(match.expiry * 1000).toString(),
503 tds = tds.concat(list);
506 { l10n: "url-classifier-not-available" },
507 { l10n: "url-classifier-not-available" },
512 document.getElementById("cache-entries-table-body"),
517 } while (j < matches.length);
520 // Create cache information entries.
521 let chk = document.createElement("input");
522 chk.type = "checkbox";
523 chk.checked = this.showCacheEnties.has(table);
524 chk.addEventListener("click", () => {
526 this.showCacheEnties.add(table);
528 this.showCacheEnties.delete(table);
533 let tds = [table, entries.length, positiveCacheCount, chk];
536 document.getElementById("cache-table-body"),
544 let entries_div = document.getElementById("cache-entries");
545 entries_div.style.display =
546 this.showCacheEnties.size == 0 ? "none" : "block";
554 // url-classifier NSPR Log modules.
556 "UrlClassifierDbService",
557 "nsChannelClassifier",
559 "UrlClassifierProtocolParser",
560 "UrlClassifierStreamUpdater",
561 "UrlClassifierPrefixSet",
562 "ApplicationReputation",
572 Services.prefs.removeObserver(JSLOG_PREF, this.refreshJSDebug);
576 this.refreshJSDebug = this.refreshJSDebug.bind(this);
577 Services.prefs.addObserver(JSLOG_PREF, this.refreshJSDebug);
581 // This function update the log module text field if we click
582 // safebrowsing log module check box.
583 function logModuleUpdate(module) {
584 let txt = document.getElementById("log-modules");
585 let chk = document.getElementById("chk-" + module);
587 let dst = chk.checked ? "," + module + ":5" : "";
588 let re = new RegExp(",?" + module + ":[0-9]");
590 let str = txt.value.replace(re, dst);
592 str = txt.value === str ? str + dst : str;
594 txt.value = str.replace(/^,/, "");
597 let setLog = document.getElementById("set-log-modules");
598 setLog.addEventListener("click", this.nsprlog);
600 let setLogFile = document.getElementById("set-log-file");
601 setLogFile.addEventListener("click", this.logfile);
603 let setJSLog = document.getElementById("js-log");
604 setJSLog.addEventListener("click", this.jslog);
606 let modules = document.getElementById("log-modules");
607 let sbModules = document.getElementById("sb-log-modules");
608 for (let module of this.modules) {
609 let container = document.createElement("label");
610 container.className = "toggle-container-with-text";
611 sbModules.appendChild(container);
613 let chk = document.createElement("input");
614 chk.id = "chk-" + module;
615 chk.type = "checkbox";
617 chk.addEventListener("click", () => {
618 logModuleUpdate(module);
620 container.appendChild(chk, modules);
622 let span = document.createElement("span");
623 span.appendChild(document.createTextNode(module));
624 container.appendChild(span, modules);
627 this.modules.map(logModuleUpdate);
629 let file = Services.dirsvc.get("TmpD", Ci.nsIFile);
630 file.append("safebrowsing.log");
632 let logFile = document.getElementById("log-file");
633 logFile.value = file.path;
635 let curLog = document.getElementById("cur-log-modules");
636 curLog.childNodes[0].nodeValue = "";
638 let curLogFile = document.getElementById("cur-log-file");
639 curLogFile.childNodes[0].nodeValue = "";
643 this.refreshJSDebug();
645 // Disable configure log modules if log modules are already set
646 // by environment variable.
647 let env = Cc["@mozilla.org/process/environment;1"].getService(
652 env.get("MOZ_LOG") ||
653 env.get("MOZ_LOG_MODULES") ||
654 env.get("NSPR_LOG_MODULES");
656 if (logModules.length) {
657 document.getElementById("set-log-modules").disabled = true;
658 for (let module of this.modules) {
659 document.getElementById("chk-" + module).disabled = true;
662 let curLogModules = document.getElementById("cur-log-modules");
663 curLogModules.childNodes[0].nodeValue = logModules;
666 // Disable set log file if log file is already set
667 // by environment variable.
668 let logFile = env.get("MOZ_LOG_FILE") || env.get("NSPR_LOG_FILE");
669 if (logFile.length) {
670 document.getElementById("set-log-file").disabled = true;
671 document.getElementById("log-file").value = logFile;
676 let enabled = Services.prefs.getBoolPref(JSLOG_PREF, false);
678 let jsChk = document.getElementById("js-log");
679 jsChk.checked = enabled;
681 let curJSLog = document.getElementById("cur-js-log");
683 document.l10n.setAttributes(curJSLog, "url-classifier-enabled");
685 document.l10n.setAttributes(curJSLog, "url-classifier-disabled");
690 let enabled = Services.prefs.getBoolPref(JSLOG_PREF, false);
691 Services.prefs.setBoolPref(JSLOG_PREF, !enabled);
695 // Turn off debugging for all the modules.
696 let children = Services.prefs.getBranch("logging.").getChildList("");
697 for (let pref of children) {
698 if (!pref.startsWith("config.")) {
699 Services.prefs.clearUserPref(`logging.${pref}`);
703 let value = document.getElementById("log-modules").value;
704 let logModules = value.split(",");
705 for (let module of logModules) {
706 let [key, value] = module.split(":");
707 Services.prefs.setIntPref(`logging.${key}`, parseInt(value, 10));
710 let curLogModules = document.getElementById("cur-log-modules");
711 curLogModules.childNodes[0].nodeValue = value;
715 let logFile = document.getElementById("log-file").value.trim();
716 Services.prefs.setCharPref("logging.config.LOG_FILE", logFile);
718 let curLogFile = document.getElementById("cur-log-file");
719 curLogFile.childNodes[0].nodeValue = logFile;