mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
Kill Google Safebrowsing.
This commit is contained in:
@@ -338,63 +338,6 @@ pref("dom.w3c_touch_events.enabled", 1);
|
||||
pref("dom.w3c_touch_events.safetyX", 0); // escape borders in units of 1/240"
|
||||
pref("dom.w3c_touch_events.safetyY", 120); // escape borders in units of 1/240"
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
// Safe browsing does nothing unless this pref is set
|
||||
pref("browser.safebrowsing.enabled", false);
|
||||
|
||||
// Prevent loading of pages identified as malware
|
||||
pref("browser.safebrowsing.malware.enabled", false);
|
||||
|
||||
pref("browser.safebrowsing.debug", false);
|
||||
pref("browser.safebrowsing.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%");
|
||||
pref("browser.safebrowsing.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
|
||||
pref("browser.safebrowsing.reportURL", "https://safebrowsing.google.com/safebrowsing/report?");
|
||||
pref("browser.safebrowsing.reportGenericURL", "http://%LOCALE%.phish-generic.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.reportErrorURL", "http://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.reportPhishURL", "http://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.reportMalwareURL", "http://%LOCALE%.malware-report.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.reportMalwareErrorURL", "http://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.appRepURL", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%");
|
||||
|
||||
pref("browser.safebrowsing.id", "Firefox");
|
||||
|
||||
// Tables for application reputation.
|
||||
pref("urlclassifier.downloadBlockTable", "goog-badbinurl-shavar");
|
||||
|
||||
// Non-enhanced mode (local url lists) URL list to check for updates
|
||||
pref("browser.safebrowsing.provider.0.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client={moz:client}&appver={moz:version}&pver=2.2&key=%GOOGLE_API_KEY%");
|
||||
|
||||
pref("browser.safebrowsing.dataProvider", 0);
|
||||
|
||||
// Does the provider name need to be localizable?
|
||||
pref("browser.safebrowsing.provider.0.name", "Google");
|
||||
pref("browser.safebrowsing.provider.0.reportURL", "https://safebrowsing.google.com/safebrowsing/report?");
|
||||
pref("browser.safebrowsing.provider.0.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client={moz:client}&appver={moz:version}&pver=2.2");
|
||||
|
||||
// HTML report pages
|
||||
pref("browser.safebrowsing.provider.0.reportGenericURL", "http://{moz:locale}.phish-generic.mozilla.com/?hl={moz:locale}");
|
||||
pref("browser.safebrowsing.provider.0.reportErrorURL", "http://{moz:locale}.phish-error.mozilla.com/?hl={moz:locale}");
|
||||
pref("browser.safebrowsing.provider.0.reportPhishURL", "http://{moz:locale}.phish-report.mozilla.com/?hl={moz:locale}");
|
||||
pref("browser.safebrowsing.provider.0.reportMalwareURL", "http://{moz:locale}.malware-report.mozilla.com/?hl={moz:locale}");
|
||||
pref("browser.safebrowsing.provider.0.reportMalwareErrorURL", "http://{moz:locale}.malware-error.mozilla.com/?hl={moz:locale}");
|
||||
|
||||
// FAQ URLs
|
||||
|
||||
// The number of random entries to send with a gethash request.
|
||||
pref("urlclassifier.gethashnoise", 4);
|
||||
|
||||
// Gethash timeout for Safebrowsing.
|
||||
pref("urlclassifier.gethash.timeout_ms", 5000);
|
||||
|
||||
// If an urlclassifier table has not been updated in this number of seconds,
|
||||
// a gethash request will be forced to check that the result is still in
|
||||
// the database.
|
||||
pref("urlclassifier.max-complete-age", 2700);
|
||||
|
||||
// URL for checking the reason for a malware warning.
|
||||
pref("browser.safebrowsing.malware.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
|
||||
#endif
|
||||
|
||||
// True if this is the first time we are showing about:firstrun
|
||||
pref("browser.firstrun.show.uidiscovery", true);
|
||||
pref("browser.firstrun.show.localepicker", true);
|
||||
|
||||
@@ -70,11 +70,6 @@ XPCOMUtils.defineLazyServiceGetter(Services, 'captivePortalDetector',
|
||||
'nsICaptivePortalDetector');
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
|
||||
"resource://gre/modules/SafeBrowsing.jsm");
|
||||
#endif
|
||||
|
||||
function getContentWindow() {
|
||||
return shell.contentBrowser.contentWindow;
|
||||
}
|
||||
@@ -353,11 +348,6 @@ var shell = {
|
||||
ppmm.addMessageListener("sms-handler", this);
|
||||
ppmm.addMessageListener("mail-handler", this);
|
||||
ppmm.addMessageListener("file-picker", this);
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
setTimeout(function() {
|
||||
SafeBrowsing.init();
|
||||
}, 5000);
|
||||
#endif
|
||||
},
|
||||
|
||||
stop: function shell_stop() {
|
||||
|
||||
@@ -17,7 +17,6 @@ MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
|
||||
MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
|
||||
# MOZ_APP_DISPLAYNAME is set by branding/configure.sh
|
||||
|
||||
MOZ_SAFE_BROWSING=1
|
||||
MOZ_SERVICES_COMMON=1
|
||||
MOZ_SERVICES_METRICS=1
|
||||
MOZ_CAPTIVEDETECT=1
|
||||
|
||||
@@ -726,46 +726,6 @@ pref("goanna.handlerService.schemes.ircs.3.uriTemplate", "chrome://browser-regio
|
||||
// By default, we don't want protocol/content handlers to be registered from a different host, see bug 402287
|
||||
pref("goanna.handlerService.allowRegisterFromDifferentHost", false);
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
pref("browser.safebrowsing.enabled", true);
|
||||
pref("browser.safebrowsing.malware.enabled", true);
|
||||
pref("browser.safebrowsing.debug", false);
|
||||
|
||||
pref("browser.safebrowsing.updateURL", "http://safebrowsing.clients.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
|
||||
pref("browser.safebrowsing.keyURL", "https://sb-ssl.google.com/safebrowsing/newkey?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
|
||||
pref("browser.safebrowsing.gethashURL", "http://safebrowsing.clients.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
|
||||
pref("browser.safebrowsing.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/report?");
|
||||
pref("browser.safebrowsing.reportGenericURL", "http://%LOCALE%.phish-generic.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.reportErrorURL", "http://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.reportPhishURL", "http://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.reportMalwareURL", "http://%LOCALE%.malware-report.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.reportMalwareErrorURL", "http://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%");
|
||||
|
||||
pref("browser.safebrowsing.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/phishing-protection/");
|
||||
pref("browser.safebrowsing.malware.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
|
||||
|
||||
#ifdef MOZILLA_OFFICIAL
|
||||
// Normally the "client ID" sent in updates is appinfo.name, but for
|
||||
// official Firefox releases from Mozilla we use a special identifier.
|
||||
pref("browser.safebrowsing.id", "navclient-auto-ffox");
|
||||
#endif
|
||||
|
||||
// Name of the about: page contributed by safebrowsing to handle display of error
|
||||
// pages on phishing/malware hits. (bug 399233)
|
||||
pref("urlclassifier.alternate_error_page", "blocked");
|
||||
|
||||
// The number of random entries to send with a gethash request.
|
||||
pref("urlclassifier.gethashnoise", 4);
|
||||
|
||||
// The list of tables that use the gethash request to confirm partial results.
|
||||
pref("urlclassifier.gethashtables", "goog-phish-shavar,goog-malware-shavar");
|
||||
|
||||
// If an urlclassifier table has not been updated in this number of seconds,
|
||||
// a gethash request will be forced to check that the result is still in
|
||||
// the database.
|
||||
pref("urlclassifier.max-complete-age", 2700);
|
||||
#endif
|
||||
|
||||
pref("browser.geolocation.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/geolocation/");
|
||||
pref("browser.mixedcontent.warning.infoURL", "http://support.mozilla.org/1/%APP%/%VERSION%/%OS%/%LOCALE%/mixed-content/");
|
||||
|
||||
@@ -950,10 +910,6 @@ pref("services.sync.prefs.sync.browser.download.manager.showWhenStarting", true)
|
||||
pref("services.sync.prefs.sync.browser.formfill.enable", true);
|
||||
pref("services.sync.prefs.sync.browser.link.open_newwindow", true);
|
||||
pref("services.sync.prefs.sync.browser.offline-apps.notify", true);
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
pref("services.sync.prefs.sync.browser.safebrowsing.enabled", true);
|
||||
pref("services.sync.prefs.sync.browser.safebrowsing.malware.enabled", true);
|
||||
#endif
|
||||
pref("services.sync.prefs.sync.browser.search.selectedEngine", true);
|
||||
pref("services.sync.prefs.sync.browser.search.update", true);
|
||||
pref("services.sync.prefs.sync.browser.sessionstore.restore_on_demand", true);
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
%customizeToolbarDTD;
|
||||
<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
|
||||
%placesDTD;
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
<!ENTITY % safebrowsingDTD SYSTEM "chrome://browser/locale/safebrowsing/phishing-afterload-warning-message.dtd">
|
||||
%safebrowsingDTD;
|
||||
#endif
|
||||
<!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
|
||||
%aboutHomeDTD;
|
||||
]>
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
var gSafeBrowsing = {
|
||||
|
||||
setReportPhishingMenu: function() {
|
||||
|
||||
// A phishing page will have a specific about:blocked content documentURI
|
||||
var isPhishingPage = content.document.documentURI.startsWith("about:blocked?e=phishingBlocked");
|
||||
|
||||
// Show/hide the appropriate menu item.
|
||||
document.getElementById("menu_HelpPopup_reportPhishingtoolmenu")
|
||||
.hidden = isPhishingPage;
|
||||
document.getElementById("menu_HelpPopup_reportPhishingErrortoolmenu")
|
||||
.hidden = !isPhishingPage;
|
||||
|
||||
var broadcasterId = isPhishingPage
|
||||
? "reportPhishingErrorBroadcaster"
|
||||
: "reportPhishingBroadcaster";
|
||||
|
||||
var broadcaster = document.getElementById(broadcasterId);
|
||||
if (!broadcaster)
|
||||
return;
|
||||
|
||||
var uri = getBrowser().currentURI;
|
||||
if (uri && (uri.schemeIs("http") || uri.schemeIs("https")))
|
||||
broadcaster.removeAttribute("disabled");
|
||||
else
|
||||
broadcaster.setAttribute("disabled", true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to report a phishing page or a false positive
|
||||
* @param name String One of "Phish", "Error", "Malware" or "MalwareError"
|
||||
* @return String the report phishing URL.
|
||||
*/
|
||||
getReportURL: function(name) {
|
||||
var reportUrl = SafeBrowsing.getReportURL(name);
|
||||
|
||||
var pageUri = gBrowser.currentURI.clone();
|
||||
|
||||
// Remove the query to avoid including potentially sensitive data
|
||||
if (pageUri instanceof Ci.nsIURL)
|
||||
pageUri.query = '';
|
||||
|
||||
reportUrl += "&url=" + encodeURIComponent(pageUri.asciiSpec);
|
||||
|
||||
return reportUrl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -125,11 +125,6 @@ XPCOMUtils.defineLazyGetter(this, "BrowserDebuggerProcess", function() {
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
|
||||
"resource://gre/modules/PageThumbs.jsm");
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
|
||||
"resource://gre/modules/SafeBrowsing.jsm");
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "gBrowserNewTabPreloader",
|
||||
"resource:///modules/BrowserNewTabPreloader.jsm", "BrowserNewTabPreloader");
|
||||
|
||||
@@ -154,9 +149,6 @@ let gInitialPages = [
|
||||
#include browser-fullZoom.js
|
||||
#include browser-places.js
|
||||
#include browser-plugins.js
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
#include browser-safebrowsing.js
|
||||
#endif
|
||||
#include browser-tabPreviews.js
|
||||
#include browser-thumbnails.js
|
||||
#include browser-webrtcUI.js
|
||||
@@ -984,11 +976,6 @@ var gBrowserInit = {
|
||||
loadOneOrMoreURIs(uriToLoad);
|
||||
}
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
// Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
|
||||
setTimeout(function() { SafeBrowsing.init(); }, 2000);
|
||||
#endif
|
||||
|
||||
Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
|
||||
Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
|
||||
Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
|
||||
@@ -2374,11 +2361,6 @@ let BrowserOnClick = {
|
||||
if (ownerDoc.documentURI.startsWith("about:certerror")) {
|
||||
this.onAboutCertError(originalTarget, ownerDoc);
|
||||
}
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
else if (ownerDoc.documentURI.startsWith("about:blocked")) {
|
||||
this.onAboutBlocked(originalTarget, ownerDoc);
|
||||
}
|
||||
#endif
|
||||
else if (ownerDoc.documentURI.startsWith("about:neterror")) {
|
||||
this.onAboutNetError(originalTarget, ownerDoc);
|
||||
}
|
||||
@@ -2428,115 +2410,6 @@ let BrowserOnClick = {
|
||||
}
|
||||
},
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
onAboutBlocked: function BrowserOnClick_onAboutBlocked(aTargetElm, aOwnerDoc) {
|
||||
let elmId = aTargetElm.getAttribute("id");
|
||||
|
||||
// The event came from a button on a malware/phishing block page
|
||||
// First check whether it's malware or phishing, so that we can
|
||||
// use the right strings/links
|
||||
let isMalware = /e=malwareBlocked/.test(aOwnerDoc.documentURI);
|
||||
let bucketName = isMalware ? "WARNING_MALWARE_PAGE_":"WARNING_PHISHING_PAGE_";
|
||||
let nsISecTel = Ci.nsISecurityUITelemetry;
|
||||
let isIframe = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
|
||||
bucketName += isIframe ? "TOP_" : "FRAME_";
|
||||
|
||||
switch (elmId) {
|
||||
case "getMeOutButton":
|
||||
getMeOutOfHere();
|
||||
break;
|
||||
|
||||
case "reportButton":
|
||||
// This is the "Why is this site blocked" button. For malware,
|
||||
// we can fetch a site-specific report, for phishing, we redirect
|
||||
// to the generic page describing phishing protection.
|
||||
|
||||
if (isMalware) {
|
||||
// Get the stop badware "why is this blocked" report url,
|
||||
// append the current url, and go there.
|
||||
try {
|
||||
let reportURL = formatURL("browser.safebrowsing.malware.reportURL", true);
|
||||
reportURL += aOwnerDoc.location.href;
|
||||
content.location = reportURL;
|
||||
} catch (e) {
|
||||
Components.utils.reportError("Couldn't get malware report URL: " + e);
|
||||
}
|
||||
}
|
||||
else { // It's a phishing site, not malware
|
||||
try {
|
||||
content.location = formatURL("browser.safebrowsing.warning.infoURL", true);
|
||||
} catch (e) {
|
||||
Components.utils.reportError("Couldn't get phishing info URL: " + e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "ignoreWarningButton":
|
||||
this.ignoreWarningButton(isMalware);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
ignoreWarningButton: function BrowserOnClick_ignoreWarningButton(aIsMalware) {
|
||||
// Allow users to override and continue through to the site,
|
||||
// but add a notify bar as a reminder, so that they don't lose
|
||||
// track after, e.g., tab switching.
|
||||
gBrowser.loadURIWithFlags(content.location.href,
|
||||
nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
|
||||
null, null, null);
|
||||
|
||||
Services.perms.add(makeURI(content.location.href), "safe-browsing",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
Ci.nsIPermissionManager.EXPIRE_SESSION);
|
||||
|
||||
let buttons = [{
|
||||
label: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.label"),
|
||||
accessKey: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.accessKey"),
|
||||
callback: function() { getMeOutOfHere(); }
|
||||
}];
|
||||
|
||||
let title;
|
||||
if (aIsMalware) {
|
||||
title = gNavigatorBundle.getString("safebrowsing.reportedAttackSite");
|
||||
buttons[1] = {
|
||||
label: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.label"),
|
||||
accessKey: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.accessKey"),
|
||||
callback: function() {
|
||||
openUILinkIn(gSafeBrowsing.getReportURL('MalwareError'), 'tab');
|
||||
}
|
||||
};
|
||||
} else {
|
||||
title = gNavigatorBundle.getString("safebrowsing.reportedWebForgery");
|
||||
buttons[1] = {
|
||||
label: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.label"),
|
||||
accessKey: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.accessKey"),
|
||||
callback: function() {
|
||||
openUILinkIn(gSafeBrowsing.getReportURL('Error'), 'tab');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let notificationBox = gBrowser.getNotificationBox();
|
||||
let value = "blocked-badware-page";
|
||||
|
||||
let previousNotification = notificationBox.getNotificationWithValue(value);
|
||||
if (previousNotification) {
|
||||
notificationBox.removeNotification(previousNotification);
|
||||
}
|
||||
|
||||
let notification = notificationBox.appendNotification(
|
||||
title,
|
||||
value,
|
||||
"chrome://global/skin/icons/blacklist_favicon.png",
|
||||
notificationBox.PRIORITY_CRITICAL_HIGH,
|
||||
buttons
|
||||
);
|
||||
// Persist the notification until the user removes so it
|
||||
// doesn't get removed on redirects.
|
||||
notification.persistence = -1;
|
||||
},
|
||||
#endif
|
||||
|
||||
onAboutNetError: function BrowserOnClick_onAboutNetError(aTargetElm, aOwnerDoc) {
|
||||
let elmId = aTargetElm.getAttribute("id");
|
||||
if (elmId != "errorTryAgain" || !/e=netOffline/.test(aOwnerDoc.documentURI))
|
||||
|
||||
@@ -131,8 +131,3 @@ browser.jar:
|
||||
# the following files are browser-specific overrides
|
||||
* content/browser/license.html (/toolkit/content/license.html)
|
||||
% override chrome://global/content/license.html chrome://browser/content/license.html
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
content/browser/report-phishing-overlay.xul (content/report-phishing-overlay.xul)
|
||||
content/browser/blockedSite.xhtml (content/blockedSite.xhtml)
|
||||
% overlay chrome://browser/content/browser.xul chrome://browser/content/report-phishing-overlay.xul
|
||||
#endif
|
||||
|
||||
@@ -33,12 +33,6 @@ struct RedirEntry {
|
||||
URI_SAFE_FOR_UNTRUSTED_CONTENT.
|
||||
*/
|
||||
static RedirEntry kRedirMap[] = {
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
{ "blocked", "chrome://browser/content/blockedSite.xhtml",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::ALLOW_SCRIPT |
|
||||
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
|
||||
#endif
|
||||
{ "certerror", "chrome://browser/content/certerror/aboutCertError.xhtml",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::ALLOW_SCRIPT |
|
||||
|
||||
@@ -85,9 +85,6 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
|
||||
{ NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
|
||||
#endif
|
||||
{ NS_FEEDSNIFFER_CONTRACTID, &kNS_FEEDSNIFFER_CID },
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "blocked", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
#endif
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "certerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "socialerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "feeds", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
|
||||
@@ -23,9 +23,6 @@ DIRS += [
|
||||
if CONFIG['MOZ_BROWSER_SIDEBAR']:
|
||||
DIRS += ['sidebar']
|
||||
|
||||
if CONFIG['MOZ_SAFE_BROWSING']:
|
||||
DIRS += ['safebrowsing']
|
||||
|
||||
DIRS += ['build']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
|
||||
@@ -66,18 +66,6 @@
|
||||
accesskey="&addonExceptions.accesskey;"
|
||||
oncommand="gSecurityPane.showAddonExceptions();"/>
|
||||
</hbox>
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
<separator class="thin"/>
|
||||
<checkbox id="blockAttackSites"
|
||||
label="&blockAttackSites.label;"
|
||||
accesskey="&blockAttackSites.accesskey;"
|
||||
preference="browser.safebrowsing.malware.enabled" />
|
||||
<checkbox id="blockWebForgeries"
|
||||
label="&blockWebForgeries.label;"
|
||||
accesskey="&blockWebForgeries.accesskey;"
|
||||
preference="browser.safebrowsing.enabled" />
|
||||
#endif
|
||||
</groupbox>
|
||||
|
||||
<!-- Passwords -->
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
@@ -635,15 +635,6 @@
|
||||
@RESPATH@/browser/modules/*
|
||||
@RESPATH@/modules/*
|
||||
|
||||
; Safe Browsing
|
||||
#ifdef MOZ_URL_CLASSIFIER
|
||||
@RESPATH@/components/nsURLClassifier.manifest
|
||||
@RESPATH@/components/nsUrlClassifierHashCompleter.js
|
||||
@RESPATH@/components/nsUrlClassifierListManager.js
|
||||
@RESPATH@/components/nsUrlClassifierLib.js
|
||||
@RESPATH@/components/url-classifier.xpt
|
||||
#endif
|
||||
|
||||
; ANGLE GLES-on-D3D rendering library
|
||||
#ifdef MOZ_ANGLE_RENDERER
|
||||
@BINPATH@/libEGL.dll
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!ENTITY safeb.palm.accept.label "Get me out of here!">
|
||||
<!ENTITY safeb.palm.decline.label "Ignore this warning">
|
||||
<!ENTITY safeb.palm.notforgery.label2 "This isn't a web forgery…">
|
||||
<!ENTITY safeb.palm.reportPage.label "Why was this page blocked?">
|
||||
|
||||
<!ENTITY safeb.blocked.malwarePage.title "Reported Attack Page!">
|
||||
<!-- Localization note (safeb.blocked.malware.shortDesc) - Please don't translate the contents of the <span id="malware_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
|
||||
<!ENTITY safeb.blocked.malwarePage.shortDesc "This web page at <span id='malware_sitename'/> has been reported as an attack page and has been blocked based on your security preferences.">
|
||||
<!ENTITY safeb.blocked.malwarePage.longDesc "<p>Attack pages try to install programs that steal private information, use your computer to attack others, or damage your system.</p><p>Some attack pages intentionally distribute harmful software, but many are compromised without the knowledge or permission of their owners.</p>">
|
||||
|
||||
<!ENTITY safeb.blocked.phishingPage.title "Reported Web Forgery!">
|
||||
<!-- Localization note (safeb.blocked.phishing.shortDesc) - Please don't translate the contents of the <span id="phishing_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
|
||||
<!ENTITY safeb.blocked.phishingPage.shortDesc "This web page at <span id='phishing_sitename'/> has been reported as a web forgery and has been blocked based on your security preferences.">
|
||||
<!ENTITY safeb.blocked.phishingPage.longDesc "<p>Web forgeries are designed to trick you into revealing personal or financial information by imitating sources you may trust.</p><p>Entering any information on this web page may result in identity theft or other fraud.</p>">
|
||||
@@ -1,6 +0,0 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!ENTITY reportPhishSiteMenu.title2 "Report Web Forgery…">
|
||||
<!ENTITY reportPhishSiteMenu.accesskey "F">
|
||||
@@ -101,10 +101,6 @@
|
||||
locale/browser/places/bookmarkProperties.properties (%chrome/browser/places/bookmarkProperties.properties)
|
||||
locale/browser/preferences/selectBookmark.dtd (%chrome/browser/preferences/selectBookmark.dtd)
|
||||
locale/browser/places/moveBookmarks.dtd (%chrome/browser/places/moveBookmarks.dtd)
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
locale/browser/safebrowsing/phishing-afterload-warning-message.dtd (%chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd)
|
||||
locale/browser/safebrowsing/report-phishing.dtd (%chrome/browser/safebrowsing/report-phishing.dtd)
|
||||
#endif
|
||||
locale/browser/feeds/subscribe.dtd (%chrome/browser/feeds/subscribe.dtd)
|
||||
locale/browser/feeds/subscribe.properties (%chrome/browser/feeds/subscribe.properties)
|
||||
locale/browser/migration/migration.dtd (%chrome/browser/migration/migration.dtd)
|
||||
|
||||
@@ -3895,14 +3895,12 @@ MOZ_SOCIAL=1
|
||||
MOZ_PREF_EXTENSIONS=1
|
||||
MOZ_PROFILELOCKING=1
|
||||
MOZ_REFLOW_PERF=
|
||||
MOZ_SAFE_BROWSING=
|
||||
MOZ_HELP_VIEWER=
|
||||
MOZ_SPELLCHECK=1
|
||||
MOZ_ANDROID_APZ=
|
||||
MOZ_TOOLKIT_SEARCH=1
|
||||
MOZ_UI_LOCALE=en-US
|
||||
MOZ_UNIVERSALCHARDET=1
|
||||
MOZ_URL_CLASSIFIER=
|
||||
MOZ_XUL=1
|
||||
MOZ_ZIPWRITER=1
|
||||
NS_PRINTING=1
|
||||
@@ -6675,34 +6673,6 @@ if test -n "$MOZ_HELP_VIEWER"; then
|
||||
AC_DEFINE(MOZ_HELP_VIEWER)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable safe browsing (anti-phishing)
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(safe-browsing,
|
||||
[ --enable-safe-browsing Enable safe browsing (anti-phishing) implementation],
|
||||
MOZ_SAFE_BROWSING=1,
|
||||
MOZ_SAFE_BROWSING= )
|
||||
if test -n "$MOZ_SAFE_BROWSING"; then
|
||||
AC_DEFINE(MOZ_SAFE_BROWSING)
|
||||
fi
|
||||
AC_SUBST(MOZ_SAFE_BROWSING)
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable url-classifier
|
||||
dnl ========================================================
|
||||
dnl Implicitly enabled by default if building with safe-browsing
|
||||
if test -n "$MOZ_SAFE_BROWSING"; then
|
||||
MOZ_URL_CLASSIFIER=1
|
||||
fi
|
||||
MOZ_ARG_ENABLE_BOOL(url-classifier,
|
||||
[ --enable-url-classifier Enable url classifier module],
|
||||
MOZ_URL_CLASSIFIER=1,
|
||||
MOZ_URL_CLASSIFIER= )
|
||||
if test -n "$MOZ_URL_CLASSIFIER"; then
|
||||
AC_DEFINE(MOZ_URL_CLASSIFIER)
|
||||
fi
|
||||
AC_SUBST(MOZ_URL_CLASSIFIER)
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Disable zipwriter
|
||||
dnl ========================================================
|
||||
|
||||
@@ -576,40 +576,6 @@ pref("shumway.disabled", true);
|
||||
// enable touch events interfaces
|
||||
pref("dom.w3c_touch_events.enabled", 1);
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
pref("browser.safebrowsing.enabled", true);
|
||||
pref("browser.safebrowsing.malware.enabled", true);
|
||||
pref("browser.safebrowsing.debug", false);
|
||||
|
||||
pref("browser.safebrowsing.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%");
|
||||
pref("browser.safebrowsing.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
|
||||
pref("browser.safebrowsing.reportURL", "https://safebrowsing.google.com/safebrowsing/report?");
|
||||
pref("browser.safebrowsing.reportGenericURL", "http://%LOCALE%.phish-generic.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.reportErrorURL", "http://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.reportPhishURL", "http://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.reportMalwareURL", "http://%LOCALE%.malware-report.mozilla.com/?hl=%LOCALE%");
|
||||
pref("browser.safebrowsing.reportMalwareErrorURL", "http://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%");
|
||||
|
||||
pref("browser.safebrowsing.malware.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
|
||||
|
||||
pref("browser.safebrowsing.id", @MOZ_APP_UA_NAME@);
|
||||
|
||||
// Name of the about: page contributed by safebrowsing to handle display of error
|
||||
// pages on phishing/malware hits. (bug 399233)
|
||||
pref("urlclassifier.alternate_error_page", "blocked");
|
||||
|
||||
// The number of random entries to send with a gethash request.
|
||||
pref("urlclassifier.gethashnoise", 4);
|
||||
|
||||
// Gethash timeout for Safebrowsing.
|
||||
pref("urlclassifier.gethash.timeout_ms", 5000);
|
||||
|
||||
// If an urlclassifier table has not been updated in this number of seconds,
|
||||
// a gethash request will be forced to check that the result is still in
|
||||
// the database.
|
||||
pref("urlclassifier.max-complete-age", 2700);
|
||||
#endif
|
||||
|
||||
// URL for posting tiles metrics.
|
||||
#ifdef RELEASE_BUILD
|
||||
pref("browser.tiles.reportURL", "https://tiles.services.mozilla.com/v2/links/click");
|
||||
|
||||
@@ -57,11 +57,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
||||
if (AppConstants.MOZ_SAFE_BROWSING) {
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
|
||||
"resource://gre/modules/SafeBrowsing.jsm");
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
@@ -381,11 +376,6 @@ var BrowserApp = {
|
||||
CastingApps.init();
|
||||
DownloadNotifications.init();
|
||||
|
||||
if (AppConstants.MOZ_SAFE_BROWSING) {
|
||||
// Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
|
||||
SafeBrowsing.init();
|
||||
};
|
||||
|
||||
// Delay this a minute because there's no rush
|
||||
setTimeout(() => {
|
||||
BrowserApp.gmpInstallManager = new GMPInstallManager();
|
||||
|
||||
@@ -15,9 +15,6 @@ contract @mozilla.org/network/protocol/about;1?what=privatebrowsing {322ba47e-70
|
||||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
||||
contract @mozilla.org/network/protocol/about;1?what=healthreport {322ba47e-7047-4f71-aebf-cb7d69325cd9}
|
||||
#endif
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
contract @mozilla.org/network/protocol/about;1?what=blocked {322ba47e-7047-4f71-aebf-cb7d69325cd9}
|
||||
#endif
|
||||
#ifdef MOZ_DEVICES
|
||||
contract @mozilla.org/network/protocol/about;1?what=devices {322ba47e-7047-4f71-aebf-cb7d69325cd9}
|
||||
#endif
|
||||
|
||||
@@ -16,8 +16,6 @@ MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/android/branding/official
|
||||
# See the --enable-android-min-sdk and --enable-android-max-sdk arguments in configure.in.
|
||||
MOZ_ANDROID_MIN_SDK_VERSION=9
|
||||
|
||||
MOZ_SAFE_BROWSING=1
|
||||
|
||||
MOZ_NO_SMART_CARDS=1
|
||||
|
||||
# Enable getUserMedia
|
||||
|
||||
@@ -457,15 +457,6 @@
|
||||
; Modules
|
||||
@BINPATH@/modules/*
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
; Safe Browsing
|
||||
@BINPATH@/components/nsURLClassifier.manifest
|
||||
@BINPATH@/components/nsUrlClassifierHashCompleter.js
|
||||
@BINPATH@/components/nsUrlClassifierListManager.js
|
||||
@BINPATH@/components/nsUrlClassifierLib.js
|
||||
@BINPATH@/components/url-classifier.xpt
|
||||
#endif
|
||||
|
||||
; GNOME hooks
|
||||
#ifdef MOZ_ENABLE_GNOME_COMPONENT
|
||||
@BINPATH@/components/@DLL_PREFIX@mozgnome@DLL_SUFFIX@
|
||||
@@ -606,9 +597,6 @@ bin/libfreebl_32int64_3.so
|
||||
@BINPATH@/components/PaymentsUI.js
|
||||
@BINPATH@/components/PaymentProviderStrategy.js
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
@BINPATH@/components/SafeBrowsing.jsm
|
||||
#endif
|
||||
@BINPATH@/components/XPIDialogService.js
|
||||
@BINPATH@/components/browsercomps.xpt
|
||||
|
||||
|
||||
@@ -63,13 +63,6 @@ let AppConstants = Object.freeze({
|
||||
false,
|
||||
#endif
|
||||
|
||||
MOZ_SAFE_BROWSING:
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
|
||||
MOZ_TELEMETRY_REPORTING:
|
||||
#ifdef MOZ_TELEMETRY_REPORTING
|
||||
true,
|
||||
|
||||
@@ -36,8 +36,7 @@ for var in ('ANDROID_PACKAGE_NAME', 'MOZ_APP_VERSION'):
|
||||
|
||||
for var in ('NIGHTLY_BUILD', 'RELEASE_BUILD', 'ACCESSIBILITY',
|
||||
'MOZILLA_OFFICIAL', 'MOZ_OFFICIAL_BRANDING', 'MOZ_SERVICES_HEALTHREPORT',
|
||||
'MOZ_DEVICES', 'MOZ_DEVICES', 'MOZ_SAFE_BROWSING',
|
||||
'MOZ_TELEMETRY_REPORTING', 'MOZ_WEBRTC'):
|
||||
'MOZ_DEVICES', 'MOZ_DEVICES', 'MOZ_TELEMETRY_REPORTING', 'MOZ_WEBRTC'):
|
||||
if CONFIG[var]:
|
||||
DEFINES[var] = 1
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ LOCAL_INCLUDES += [
|
||||
'../startup',
|
||||
'../statusfilter',
|
||||
'../typeaheadfind',
|
||||
'../url-classifier',
|
||||
]
|
||||
|
||||
if not CONFIG['MOZ_DISABLE_PARENTAL_CONTROLS']:
|
||||
|
||||
@@ -24,14 +24,6 @@
|
||||
|
||||
#include "nsTypeAheadFind.h"
|
||||
|
||||
#ifdef MOZ_URL_CLASSIFIER
|
||||
#include "ApplicationReputation.h"
|
||||
#include "nsUrlClassifierDBService.h"
|
||||
#include "nsUrlClassifierStreamUpdater.h"
|
||||
#include "nsUrlClassifierUtils.h"
|
||||
#include "nsUrlClassifierPrefixSet.h"
|
||||
#endif
|
||||
|
||||
#include "nsBrowserStatusFilter.h"
|
||||
#include "mozilla/FinalizationWitnessService.h"
|
||||
#include "mozilla/NativeOSFileInternals.h"
|
||||
@@ -77,33 +69,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsDownloadProxy)
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsTypeAheadFind)
|
||||
|
||||
#ifdef MOZ_URL_CLASSIFIER
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ApplicationReputationService,
|
||||
ApplicationReputationService::GetSingleton)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUrlClassifierPrefixSet)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUrlClassifierStreamUpdater)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsUrlClassifierUtils, Init)
|
||||
|
||||
static nsresult
|
||||
nsUrlClassifierDBServiceConstructor(nsISupports *aOuter, REFNSIID aIID,
|
||||
void **aResult)
|
||||
{
|
||||
nsresult rv;
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
NS_ENSURE_NO_AGGREGATION(aOuter);
|
||||
|
||||
nsUrlClassifierDBService *inst = nsUrlClassifierDBService::GetInstance(&rv);
|
||||
if (nullptr == inst) {
|
||||
return rv;
|
||||
}
|
||||
/* NS_ADDREF(inst); */
|
||||
rv = inst->QueryInterface(aIID, aResult);
|
||||
NS_RELEASE(inst);
|
||||
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsBrowserStatusFilter)
|
||||
#if defined(USE_MOZ_UPDATER)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsUpdateProcessor)
|
||||
@@ -128,13 +93,6 @@ NS_DEFINE_NAMED_CID(NS_DOWNLOADPLATFORM_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DOWNLOAD_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_FIND_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_TYPEAHEADFIND_CID);
|
||||
#ifdef MOZ_URL_CLASSIFIER
|
||||
NS_DEFINE_NAMED_CID(NS_APPLICATION_REPUTATION_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERPREFIXSET_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERDBSERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERSTREAMUPDATER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERUTILS_CID);
|
||||
#endif
|
||||
NS_DEFINE_NAMED_CID(NS_BROWSERSTATUSFILTER_CID);
|
||||
#if defined(USE_MOZ_UPDATER)
|
||||
NS_DEFINE_NAMED_CID(NS_UPDATEPROCESSOR_CID);
|
||||
@@ -159,13 +117,6 @@ static const Module::CIDEntry kToolkitCIDs[] = {
|
||||
{ &kNS_DOWNLOAD_CID, false, nullptr, nsDownloadProxyConstructor },
|
||||
{ &kNS_FIND_SERVICE_CID, false, nullptr, nsFindServiceConstructor },
|
||||
{ &kNS_TYPEAHEADFIND_CID, false, nullptr, nsTypeAheadFindConstructor },
|
||||
#ifdef MOZ_URL_CLASSIFIER
|
||||
{ &kNS_APPLICATION_REPUTATION_SERVICE_CID, false, nullptr, ApplicationReputationServiceConstructor },
|
||||
{ &kNS_URLCLASSIFIERPREFIXSET_CID, false, nullptr, nsUrlClassifierPrefixSetConstructor },
|
||||
{ &kNS_URLCLASSIFIERDBSERVICE_CID, false, nullptr, nsUrlClassifierDBServiceConstructor },
|
||||
{ &kNS_URLCLASSIFIERSTREAMUPDATER_CID, false, nullptr, nsUrlClassifierStreamUpdaterConstructor },
|
||||
{ &kNS_URLCLASSIFIERUTILS_CID, false, nullptr, nsUrlClassifierUtilsConstructor },
|
||||
#endif
|
||||
{ &kNS_BROWSERSTATUSFILTER_CID, false, nullptr, nsBrowserStatusFilterConstructor },
|
||||
#if defined(USE_MOZ_UPDATER)
|
||||
{ &kNS_UPDATEPROCESSOR_CID, false, nullptr, nsUpdateProcessorConstructor },
|
||||
@@ -191,14 +142,6 @@ static const Module::ContractIDEntry kToolkitContracts[] = {
|
||||
{ NS_DOWNLOADPLATFORM_CONTRACTID, &kNS_DOWNLOADPLATFORM_CID },
|
||||
{ NS_FIND_SERVICE_CONTRACTID, &kNS_FIND_SERVICE_CID },
|
||||
{ NS_TYPEAHEADFIND_CONTRACTID, &kNS_TYPEAHEADFIND_CID },
|
||||
#ifdef MOZ_URL_CLASSIFIER
|
||||
{ NS_APPLICATION_REPUTATION_SERVICE_CONTRACTID, &kNS_APPLICATION_REPUTATION_SERVICE_CID },
|
||||
{ NS_URLCLASSIFIERPREFIXSET_CONTRACTID, &kNS_URLCLASSIFIERPREFIXSET_CID },
|
||||
{ NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &kNS_URLCLASSIFIERDBSERVICE_CID },
|
||||
{ NS_URICLASSIFIERSERVICE_CONTRACTID, &kNS_URLCLASSIFIERDBSERVICE_CID },
|
||||
{ NS_URLCLASSIFIERSTREAMUPDATER_CONTRACTID, &kNS_URLCLASSIFIERSTREAMUPDATER_CID },
|
||||
{ NS_URLCLASSIFIERUTILS_CONTRACTID, &kNS_URLCLASSIFIERUTILS_CID },
|
||||
#endif
|
||||
{ NS_BROWSERSTATUSFILTER_CONTRACTID, &kNS_BROWSERSTATUSFILTER_CID },
|
||||
#if defined(USE_MOZ_UPDATER)
|
||||
{ NS_UPDATEPROCESSOR_CONTRACTID, &kNS_UPDATEPROCESSOR_CID },
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,55 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef ApplicationReputation_h__
|
||||
#define ApplicationReputation_h__
|
||||
|
||||
#include "nsIApplicationReputation.h"
|
||||
#include "nsIRequestObserver.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsISupports.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsIRequest;
|
||||
class PendingDBLookup;
|
||||
class PendingLookup;
|
||||
struct PRLogModuleInfo;
|
||||
|
||||
class ApplicationReputationService final :
|
||||
public nsIApplicationReputationService {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIAPPLICATIONREPUTATIONSERVICE
|
||||
|
||||
public:
|
||||
static ApplicationReputationService* GetSingleton();
|
||||
|
||||
private:
|
||||
friend class PendingLookup;
|
||||
friend class PendingDBLookup;
|
||||
/**
|
||||
* Global singleton object for holding this factory service.
|
||||
*/
|
||||
static ApplicationReputationService* gApplicationReputationService;
|
||||
/**
|
||||
* NSPR_LOG_MODULES=ApplicationReputation:5
|
||||
*/
|
||||
static PRLogModuleInfo* prlog;
|
||||
/**
|
||||
* This is a singleton, so disallow construction.
|
||||
*/
|
||||
ApplicationReputationService();
|
||||
~ApplicationReputationService();
|
||||
/**
|
||||
* Wrapper function for QueryReputation that makes it easier to ensure the
|
||||
* callback is called.
|
||||
*/
|
||||
nsresult QueryReputationInternal(nsIApplicationReputationQuery* aQuery,
|
||||
nsIApplicationReputationCallback* aCallback);
|
||||
};
|
||||
#endif /* ApplicationReputation_h__ */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
#!/bin/bash
|
||||
# A script to generate toolkit/components/downloads/csd.pb.{cc,h} for use in
|
||||
# nsIApplicationReputationQuery. This script assumes you have downloaded and
|
||||
# installed the protocol buffer compiler.
|
||||
# As of June 26 2014, csd.proto contains many protobufs that are currently
|
||||
# unused by ApplicationReputation. You may want to strip csd.proto of these
|
||||
# before running the protocol compiler on it.
|
||||
if [ -n $PROTOC_PATH ]; then
|
||||
PROTOC_PATH=/usr/local/bin/protoc
|
||||
fi
|
||||
|
||||
echo "Using $PROTOC_PATH as protocol compiler"
|
||||
|
||||
if [ ! -e $PROTOC_PATH ]; then
|
||||
echo "You must install the protocol compiler from " \
|
||||
"https://code.google.com/p/protobuf/downloads/list"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the protocol buffer and compile it
|
||||
CMD='wget http://src.chromium.org/chrome/trunk/src/chrome/common/safe_browsing/csd.proto -O csd.proto'
|
||||
OUTPUT_PATH=toolkit/components/downloads
|
||||
|
||||
$CMD
|
||||
$PROTOC_PATH csd.proto --cpp_out=$OUTPUT_PATH
|
||||
@@ -30,12 +30,6 @@ SOURCES += [
|
||||
'SQLFunctions.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_URL_CLASSIFIER']:
|
||||
UNIFIED_SOURCES += [
|
||||
'ApplicationReputation.cpp',
|
||||
'csd.pb.cc'
|
||||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
# Can't build unified because we need CreateEvent which some IPC code
|
||||
# included in LoadContext ends up undefining.
|
||||
|
||||
@@ -136,11 +136,7 @@ this.DownloadIntegration = {
|
||||
dontLoadObservers: false,
|
||||
dontCheckParentalControls: false,
|
||||
shouldBlockInTest: false,
|
||||
#ifdef MOZ_URL_CLASSIFIER
|
||||
dontCheckApplicationReputation: false,
|
||||
#else
|
||||
dontCheckApplicationReputation: true,
|
||||
#endif
|
||||
shouldBlockInTestForApplicationReputation: false,
|
||||
shouldKeepBlockedDataInTest: false,
|
||||
dontOpenFileAndFolder: false,
|
||||
|
||||
@@ -78,9 +78,6 @@ if CONFIG['MOZ_XUL']:
|
||||
if CONFIG['MOZ_TOOLKIT_SEARCH']:
|
||||
DIRS += ['search']
|
||||
|
||||
if CONFIG['MOZ_URL_CLASSIFIER']:
|
||||
DIRS += ['url-classifier']
|
||||
|
||||
if CONFIG['MOZ_CAPTIVEDETECT']:
|
||||
DIRS += ['captivedetect']
|
||||
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ChunkSet.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
nsresult
|
||||
ChunkSet::Serialize(nsACString& aChunkStr)
|
||||
{
|
||||
aChunkStr.Truncate();
|
||||
|
||||
uint32_t i = 0;
|
||||
while (i < mChunks.Length()) {
|
||||
if (i != 0) {
|
||||
aChunkStr.Append(',');
|
||||
}
|
||||
aChunkStr.AppendInt((int32_t)mChunks[i]);
|
||||
|
||||
uint32_t first = i;
|
||||
uint32_t last = first;
|
||||
i++;
|
||||
while (i < mChunks.Length() && (mChunks[i] == mChunks[i - 1] + 1 || mChunks[i] == mChunks[i - 1])) {
|
||||
last = i++;
|
||||
}
|
||||
|
||||
if (last != first) {
|
||||
aChunkStr.Append('-');
|
||||
aChunkStr.AppendInt((int32_t)mChunks[last]);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ChunkSet::Set(uint32_t aChunk)
|
||||
{
|
||||
size_t idx = mChunks.BinaryIndexOf(aChunk);
|
||||
if (idx == nsTArray<uint32_t>::NoIndex) {
|
||||
mChunks.InsertElementSorted(aChunk);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ChunkSet::Unset(uint32_t aChunk)
|
||||
{
|
||||
mChunks.RemoveElementSorted(aChunk);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
ChunkSet::Has(uint32_t aChunk) const
|
||||
{
|
||||
return mChunks.BinaryIndexOf(aChunk) != nsTArray<uint32_t>::NoIndex;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ChunkSet::Merge(const ChunkSet& aOther)
|
||||
{
|
||||
const uint32_t *dupIter = aOther.mChunks.Elements();
|
||||
const uint32_t *end = aOther.mChunks.Elements() + aOther.mChunks.Length();
|
||||
|
||||
for (const uint32_t *iter = dupIter; iter != end; iter++) {
|
||||
nsresult rv = Set(*iter);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ChunkSet::Remove(const ChunkSet& aOther)
|
||||
{
|
||||
uint32_t *addIter = mChunks.Elements();
|
||||
uint32_t *end = mChunks.Elements() + mChunks.Length();
|
||||
|
||||
for (uint32_t *iter = addIter; iter != end; iter++) {
|
||||
if (!aOther.Has(*iter)) {
|
||||
*addIter = *iter;
|
||||
addIter++;
|
||||
}
|
||||
}
|
||||
|
||||
mChunks.SetLength(addIter - mChunks.Elements());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ChunkSet::Clear()
|
||||
{
|
||||
mChunks.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef ChunkSet_h__
|
||||
#define ChunkSet_h__
|
||||
|
||||
|
||||
#include "Entries.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
/**
|
||||
* Store the chunk numbers as an array of uint32_t. We need chunk numbers in
|
||||
* order to ask for incremental updates from the server.
|
||||
* XXX: We should optimize this further to compress the many consecutive
|
||||
* numbers.
|
||||
*/
|
||||
class ChunkSet {
|
||||
public:
|
||||
ChunkSet() {}
|
||||
~ChunkSet() {}
|
||||
|
||||
nsresult Serialize(nsACString& aStr);
|
||||
nsresult Set(uint32_t aChunk);
|
||||
nsresult Unset(uint32_t aChunk);
|
||||
void Clear();
|
||||
nsresult Merge(const ChunkSet& aOther);
|
||||
nsresult Remove(const ChunkSet& aOther);
|
||||
|
||||
bool Has(uint32_t chunk) const;
|
||||
|
||||
uint32_t Length() const { return mChunks.Length(); }
|
||||
|
||||
nsresult Write(nsIOutputStream* aOut) {
|
||||
return WriteTArray(aOut, mChunks);
|
||||
}
|
||||
|
||||
nsresult Read(nsIInputStream* aIn, uint32_t aNumElements) {
|
||||
return ReadTArray(aIn, &mChunks, aNumElements);
|
||||
}
|
||||
|
||||
uint32_t *Begin() { return mChunks.Elements(); }
|
||||
uint32_t *End() { return mChunks.Elements() + mChunks.Length(); }
|
||||
|
||||
private:
|
||||
nsTArray<uint32_t> mChunks;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,755 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Classifier.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsIRandomGenerator.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "prlog.h"
|
||||
|
||||
// NSPR_LOG_MODULES=UrlClassifierDbService:5
|
||||
extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
|
||||
#if defined(PR_LOGGING)
|
||||
#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
|
||||
#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#define LOG_ENABLED() (false)
|
||||
#endif
|
||||
|
||||
#define STORE_DIRECTORY NS_LITERAL_CSTRING("safebrowsing")
|
||||
#define TO_DELETE_DIR_SUFFIX NS_LITERAL_CSTRING("-to_delete")
|
||||
#define BACKUP_DIR_SUFFIX NS_LITERAL_CSTRING("-backup")
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
void
|
||||
Classifier::SplitTables(const nsACString& str, nsTArray<nsCString>& tables)
|
||||
{
|
||||
tables.Clear();
|
||||
|
||||
nsACString::const_iterator begin, iter, end;
|
||||
str.BeginReading(begin);
|
||||
str.EndReading(end);
|
||||
while (begin != end) {
|
||||
iter = begin;
|
||||
FindCharInReadable(',', iter, end);
|
||||
nsDependentCSubstring table = Substring(begin,iter);
|
||||
if (!table.IsEmpty()) {
|
||||
tables.AppendElement(Substring(begin, iter));
|
||||
}
|
||||
begin = iter;
|
||||
if (begin != end) {
|
||||
begin++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Classifier::Classifier()
|
||||
{
|
||||
}
|
||||
|
||||
Classifier::~Classifier()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::SetupPathNames()
|
||||
{
|
||||
// Get the root directory where to store all the databases.
|
||||
nsresult rv = mCacheDirectory->Clone(getter_AddRefs(mStoreDirectory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mStoreDirectory->AppendNative(STORE_DIRECTORY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Make sure LookupCaches (which are persistent and survive updates)
|
||||
// are reading/writing in the right place. We will be moving their
|
||||
// files "underneath" them during backup/restore.
|
||||
for (uint32_t i = 0; i < mLookupCaches.Length(); i++) {
|
||||
mLookupCaches[i]->UpdateDirHandle(mStoreDirectory);
|
||||
}
|
||||
|
||||
// Directory where to move a backup before an update.
|
||||
rv = mCacheDirectory->Clone(getter_AddRefs(mBackupDirectory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mBackupDirectory->AppendNative(STORE_DIRECTORY + BACKUP_DIR_SUFFIX);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Directory where to move the backup so we can atomically
|
||||
// delete (really move) it.
|
||||
rv = mCacheDirectory->Clone(getter_AddRefs(mToDeleteDirectory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mToDeleteDirectory->AppendNative(STORE_DIRECTORY + TO_DELETE_DIR_SUFFIX);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::CreateStoreDirectory()
|
||||
{
|
||||
// Ensure the safebrowsing directory exists.
|
||||
bool storeExists;
|
||||
nsresult rv = mStoreDirectory->Exists(&storeExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!storeExists) {
|
||||
rv = mStoreDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
bool storeIsDir;
|
||||
rv = mStoreDirectory->IsDirectory(&storeIsDir);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!storeIsDir)
|
||||
return NS_ERROR_FILE_DESTINATION_NOT_DIR;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::Open(nsIFile& aCacheDirectory)
|
||||
{
|
||||
// Remember the Local profile directory.
|
||||
nsresult rv = aCacheDirectory.Clone(getter_AddRefs(mCacheDirectory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Create the handles to the update and backup directories.
|
||||
rv = SetupPathNames();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Clean up any to-delete directories that haven't been deleted yet.
|
||||
rv = CleanToDelete();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Check whether we have an incomplete update and recover from the
|
||||
// backup if so.
|
||||
rv = RecoverBackups();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Make sure the main store directory exists.
|
||||
rv = CreateStoreDirectory();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Build the list of know urlclassifier lists
|
||||
// XXX: Disk IO potentially on the main thread during startup
|
||||
RegenActiveTables();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
Classifier::Close()
|
||||
{
|
||||
DropStores();
|
||||
}
|
||||
|
||||
void
|
||||
Classifier::Reset()
|
||||
{
|
||||
DropStores();
|
||||
|
||||
mStoreDirectory->Remove(true);
|
||||
mBackupDirectory->Remove(true);
|
||||
mToDeleteDirectory->Remove(true);
|
||||
|
||||
CreateStoreDirectory();
|
||||
|
||||
mTableFreshness.Clear();
|
||||
RegenActiveTables();
|
||||
}
|
||||
|
||||
void
|
||||
Classifier::TableRequest(nsACString& aResult)
|
||||
{
|
||||
nsTArray<nsCString> tables;
|
||||
ActiveTables(tables);
|
||||
for (uint32_t i = 0; i < tables.Length(); i++) {
|
||||
HashStore store(tables[i], mStoreDirectory);
|
||||
|
||||
nsresult rv = store.Open();
|
||||
if (NS_FAILED(rv))
|
||||
continue;
|
||||
|
||||
aResult.Append(store.TableName());
|
||||
aResult.Append(';');
|
||||
|
||||
ChunkSet &adds = store.AddChunks();
|
||||
ChunkSet &subs = store.SubChunks();
|
||||
|
||||
if (adds.Length() > 0) {
|
||||
aResult.AppendLiteral("a:");
|
||||
nsAutoCString addList;
|
||||
adds.Serialize(addList);
|
||||
aResult.Append(addList);
|
||||
}
|
||||
|
||||
if (subs.Length() > 0) {
|
||||
if (adds.Length() > 0)
|
||||
aResult.Append(':');
|
||||
aResult.AppendLiteral("s:");
|
||||
nsAutoCString subList;
|
||||
subs.Serialize(subList);
|
||||
aResult.Append(subList);
|
||||
}
|
||||
|
||||
aResult.Append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::Check(const nsACString& aSpec,
|
||||
const nsACString& aTables,
|
||||
uint32_t aFreshnessGuarantee,
|
||||
LookupResultArray& aResults)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CL_CHECK_TIME> timer;
|
||||
|
||||
// Get the set of fragments based on the url. This is necessary because we
|
||||
// only look up at most 5 URLs per aSpec, even if aSpec has more than 5
|
||||
// components.
|
||||
nsTArray<nsCString> fragments;
|
||||
nsresult rv = LookupCache::GetLookupFragments(aSpec, &fragments);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsTArray<nsCString> activeTables;
|
||||
SplitTables(aTables, activeTables);
|
||||
|
||||
nsTArray<LookupCache*> cacheArray;
|
||||
for (uint32_t i = 0; i < activeTables.Length(); i++) {
|
||||
LOG(("Checking table %s", activeTables[i].get()));
|
||||
LookupCache *cache = GetLookupCache(activeTables[i]);
|
||||
if (cache) {
|
||||
cacheArray.AppendElement(cache);
|
||||
} else {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Now check each lookup fragment against the entries in the DB.
|
||||
for (uint32_t i = 0; i < fragments.Length(); i++) {
|
||||
Completion lookupHash;
|
||||
lookupHash.FromPlaintext(fragments[i], mCryptoHash);
|
||||
|
||||
// Get list of host keys to look up
|
||||
Completion hostKey;
|
||||
rv = LookupCache::GetKey(fragments[i], &hostKey, mCryptoHash);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Local host on the network.
|
||||
continue;
|
||||
}
|
||||
|
||||
#if DEBUG && defined(PR_LOGGING)
|
||||
if (LOG_ENABLED()) {
|
||||
nsAutoCString checking;
|
||||
lookupHash.ToHexString(checking);
|
||||
LOG(("Checking fragment %s, hash %s (%X)", fragments[i].get(),
|
||||
checking.get(), lookupHash.ToUint32()));
|
||||
}
|
||||
#endif
|
||||
for (uint32_t i = 0; i < cacheArray.Length(); i++) {
|
||||
LookupCache *cache = cacheArray[i];
|
||||
bool has, complete;
|
||||
rv = cache->Has(lookupHash, &has, &complete);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (has) {
|
||||
LookupResult *result = aResults.AppendElement();
|
||||
if (!result)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
int64_t age;
|
||||
bool found = mTableFreshness.Get(cache->TableName(), &age);
|
||||
if (!found) {
|
||||
age = 24 * 60 * 60; // just a large number
|
||||
} else {
|
||||
int64_t now = (PR_Now() / PR_USEC_PER_SEC);
|
||||
age = now - age;
|
||||
}
|
||||
|
||||
LOG(("Found a result in %s: %s (Age: %Lds)",
|
||||
cache->TableName().get(),
|
||||
complete ? "complete." : "Not complete.",
|
||||
age));
|
||||
|
||||
result->hash.complete = lookupHash;
|
||||
result->mComplete = complete;
|
||||
result->mFresh = (age < aFreshnessGuarantee);
|
||||
result->mTableName.Assign(cache->TableName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::ApplyUpdates(nsTArray<TableUpdate*>* aUpdates)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CL_UPDATE_TIME> timer;
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
PRIntervalTime clockStart = 0;
|
||||
if (LOG_ENABLED() || true) {
|
||||
clockStart = PR_IntervalNow();
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG(("Backup before update."));
|
||||
|
||||
nsresult rv = BackupTables();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Applying %d table updates.", aUpdates->Length()));
|
||||
|
||||
for (uint32_t i = 0; i < aUpdates->Length(); i++) {
|
||||
// Previous ApplyTableUpdates() may have consumed this update..
|
||||
if ((*aUpdates)[i]) {
|
||||
// Run all updates for one table
|
||||
nsCString updateTable(aUpdates->ElementAt(i)->TableName());
|
||||
rv = ApplyTableUpdates(aUpdates, updateTable);
|
||||
if (NS_FAILED(rv)) {
|
||||
if (rv != NS_ERROR_OUT_OF_MEMORY) {
|
||||
Reset();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
aUpdates->Clear();
|
||||
|
||||
rv = RegenActiveTables();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Cleaning up backups."));
|
||||
|
||||
// Move the backup directory away (signaling the transaction finished
|
||||
// successfully). This is atomic.
|
||||
rv = RemoveBackupTables();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Do the actual deletion of the backup files.
|
||||
rv = CleanToDelete();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Done applying updates."));
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
if (LOG_ENABLED() || true) {
|
||||
PRIntervalTime clockEnd = PR_IntervalNow();
|
||||
LOG(("update took %dms\n",
|
||||
PR_IntervalToMilliseconds(clockEnd - clockStart)));
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::MarkSpoiled(nsTArray<nsCString>& aTables)
|
||||
{
|
||||
for (uint32_t i = 0; i < aTables.Length(); i++) {
|
||||
LOG(("Spoiling table: %s", aTables[i].get()));
|
||||
// Spoil this table by marking it as no known freshness
|
||||
mTableFreshness.Remove(aTables[i]);
|
||||
// Remove any cached Completes for this table
|
||||
LookupCache *cache = GetLookupCache(aTables[i]);
|
||||
if (cache) {
|
||||
cache->ClearCompleteCache();
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
Classifier::DropStores()
|
||||
{
|
||||
for (uint32_t i = 0; i < mHashStores.Length(); i++) {
|
||||
delete mHashStores[i];
|
||||
}
|
||||
mHashStores.Clear();
|
||||
for (uint32_t i = 0; i < mLookupCaches.Length(); i++) {
|
||||
delete mLookupCaches[i];
|
||||
}
|
||||
mLookupCaches.Clear();
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::RegenActiveTables()
|
||||
{
|
||||
mActiveTablesCache.Clear();
|
||||
|
||||
nsTArray<nsCString> foundTables;
|
||||
ScanStoreDir(foundTables);
|
||||
|
||||
for (uint32_t i = 0; i < foundTables.Length(); i++) {
|
||||
nsCString table(foundTables[i]);
|
||||
HashStore store(table, mStoreDirectory);
|
||||
|
||||
nsresult rv = store.Open();
|
||||
if (NS_FAILED(rv))
|
||||
continue;
|
||||
|
||||
LookupCache *lookupCache = GetLookupCache(store.TableName());
|
||||
if (!lookupCache) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!lookupCache->IsPrimed())
|
||||
continue;
|
||||
|
||||
const ChunkSet &adds = store.AddChunks();
|
||||
const ChunkSet &subs = store.SubChunks();
|
||||
|
||||
if (adds.Length() == 0 && subs.Length() == 0)
|
||||
continue;
|
||||
|
||||
LOG(("Active table: %s", store.TableName().get()));
|
||||
mActiveTablesCache.AppendElement(store.TableName());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::ScanStoreDir(nsTArray<nsCString>& aTables)
|
||||
{
|
||||
nsCOMPtr<nsISimpleEnumerator> entries;
|
||||
nsresult rv = mStoreDirectory->GetDirectoryEntries(getter_AddRefs(entries));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool hasMore;
|
||||
while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
rv = entries->GetNext(getter_AddRefs(supports));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
|
||||
|
||||
nsCString leafName;
|
||||
rv = file->GetNativeLeafName(leafName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCString suffix(NS_LITERAL_CSTRING(".sbstore"));
|
||||
|
||||
int32_t dot = leafName.RFind(suffix, 0);
|
||||
if (dot != -1) {
|
||||
leafName.Cut(dot, suffix.Length());
|
||||
aTables.AppendElement(leafName);
|
||||
}
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::ActiveTables(nsTArray<nsCString>& aTables)
|
||||
{
|
||||
aTables = mActiveTablesCache;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::CleanToDelete()
|
||||
{
|
||||
bool exists;
|
||||
nsresult rv = mToDeleteDirectory->Exists(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (exists) {
|
||||
rv = mToDeleteDirectory->Remove(true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::BackupTables()
|
||||
{
|
||||
// We have to work in reverse here: first move the normal directory
|
||||
// away to be the backup directory, then copy the files over
|
||||
// to the normal directory. This ensures that if we crash the backup
|
||||
// dir always has a valid, complete copy, instead of a partial one,
|
||||
// because that's the one we will copy over the normal store dir.
|
||||
|
||||
nsCString backupDirName;
|
||||
nsresult rv = mBackupDirectory->GetNativeLeafName(backupDirName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCString storeDirName;
|
||||
rv = mStoreDirectory->GetNativeLeafName(storeDirName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mStoreDirectory->MoveToNative(nullptr, backupDirName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mStoreDirectory->CopyToNative(nullptr, storeDirName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We moved some things to new places, so move the handles around, too.
|
||||
rv = SetupPathNames();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::RemoveBackupTables()
|
||||
{
|
||||
nsCString toDeleteName;
|
||||
nsresult rv = mToDeleteDirectory->GetNativeLeafName(toDeleteName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mBackupDirectory->MoveToNative(nullptr, toDeleteName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// mBackupDirectory now points to toDelete, fix that up.
|
||||
rv = SetupPathNames();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::RecoverBackups()
|
||||
{
|
||||
bool backupExists;
|
||||
nsresult rv = mBackupDirectory->Exists(&backupExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (backupExists) {
|
||||
// Remove the safebrowsing dir if it exists
|
||||
nsCString storeDirName;
|
||||
rv = mStoreDirectory->GetNativeLeafName(storeDirName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool storeExists;
|
||||
rv = mStoreDirectory->Exists(&storeExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (storeExists) {
|
||||
rv = mStoreDirectory->Remove(true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Move the backup to the store location
|
||||
rv = mBackupDirectory->MoveToNative(nullptr, storeDirName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// mBackupDirectory now points to storeDir, fix up.
|
||||
rv = SetupPathNames();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* This will consume+delete updates from the passed nsTArray.
|
||||
*/
|
||||
nsresult
|
||||
Classifier::ApplyTableUpdates(nsTArray<TableUpdate*>* aUpdates,
|
||||
const nsACString& aTable)
|
||||
{
|
||||
LOG(("Classifier::ApplyTableUpdates(%s)", PromiseFlatCString(aTable).get()));
|
||||
|
||||
HashStore store(aTable, mStoreDirectory);
|
||||
|
||||
// take the quick exit if there is no valid update for us
|
||||
// (common case)
|
||||
uint32_t validupdates = 0;
|
||||
|
||||
for (uint32_t i = 0; i < aUpdates->Length(); i++) {
|
||||
TableUpdate *update = aUpdates->ElementAt(i);
|
||||
if (!update || !update->TableName().Equals(store.TableName()))
|
||||
continue;
|
||||
if (update->Empty()) {
|
||||
aUpdates->ElementAt(i) = nullptr;
|
||||
delete update;
|
||||
continue;
|
||||
}
|
||||
validupdates++;
|
||||
}
|
||||
|
||||
if (!validupdates) {
|
||||
// This can happen if the update was only valid for one table.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = store.Open();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = store.BeginUpdate();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Read the part of the store that is (only) in the cache
|
||||
LookupCache *prefixSet = GetLookupCache(store.TableName());
|
||||
if (!prefixSet) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
FallibleTArray<uint32_t> AddPrefixHashes;
|
||||
rv = prefixSet->GetPrefixes(AddPrefixHashes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = store.AugmentAdds(AddPrefixHashes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
AddPrefixHashes.Clear();
|
||||
|
||||
uint32_t applied = 0;
|
||||
bool updateFreshness = false;
|
||||
bool hasCompletes = false;
|
||||
|
||||
for (uint32_t i = 0; i < aUpdates->Length(); i++) {
|
||||
TableUpdate *update = aUpdates->ElementAt(i);
|
||||
if (!update || !update->TableName().Equals(store.TableName()))
|
||||
continue;
|
||||
|
||||
rv = store.ApplyUpdate(*update);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
applied++;
|
||||
|
||||
LOG(("Applied update to table %s:", store.TableName().get()));
|
||||
LOG((" %d add chunks", update->AddChunks().Length()));
|
||||
LOG((" %d add prefixes", update->AddPrefixes().Length()));
|
||||
LOG((" %d add completions", update->AddCompletes().Length()));
|
||||
LOG((" %d sub chunks", update->SubChunks().Length()));
|
||||
LOG((" %d sub prefixes", update->SubPrefixes().Length()));
|
||||
LOG((" %d sub completions", update->SubCompletes().Length()));
|
||||
LOG((" %d add expirations", update->AddExpirations().Length()));
|
||||
LOG((" %d sub expirations", update->SubExpirations().Length()));
|
||||
|
||||
if (!update->IsLocalUpdate()) {
|
||||
updateFreshness = true;
|
||||
LOG(("Remote update, updating freshness"));
|
||||
}
|
||||
|
||||
if (update->AddCompletes().Length() > 0
|
||||
|| update->SubCompletes().Length() > 0) {
|
||||
hasCompletes = true;
|
||||
LOG(("Contains Completes, keeping cache."));
|
||||
}
|
||||
|
||||
aUpdates->ElementAt(i) = nullptr;
|
||||
delete update;
|
||||
}
|
||||
|
||||
LOG(("Applied %d update(s) to %s.", applied, store.TableName().get()));
|
||||
|
||||
rv = store.Rebuild();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Not an update with Completes, clear all completes data.
|
||||
if (!hasCompletes) {
|
||||
store.ClearCompletes();
|
||||
}
|
||||
|
||||
LOG(("Table %s now has:", store.TableName().get()));
|
||||
LOG((" %d add chunks", store.AddChunks().Length()));
|
||||
LOG((" %d add prefixes", store.AddPrefixes().Length()));
|
||||
LOG((" %d add completions", store.AddCompletes().Length()));
|
||||
LOG((" %d sub chunks", store.SubChunks().Length()));
|
||||
LOG((" %d sub prefixes", store.SubPrefixes().Length()));
|
||||
LOG((" %d sub completions", store.SubCompletes().Length()));
|
||||
|
||||
rv = store.WriteFile();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// At this point the store is updated and written out to disk, but
|
||||
// the data is still in memory. Build our quick-lookup table here.
|
||||
rv = prefixSet->Build(store.AddPrefixes(), store.AddCompletes());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#if defined(DEBUG) && defined(PR_LOGGING)
|
||||
prefixSet->Dump();
|
||||
#endif
|
||||
rv = prefixSet->WriteFile();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (updateFreshness) {
|
||||
int64_t now = (PR_Now() / PR_USEC_PER_SEC);
|
||||
LOG(("Successfully updated %s", store.TableName().get()));
|
||||
mTableFreshness.Put(store.TableName(), now);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
LookupCache *
|
||||
Classifier::GetLookupCache(const nsACString& aTable)
|
||||
{
|
||||
for (uint32_t i = 0; i < mLookupCaches.Length(); i++) {
|
||||
if (mLookupCaches[i]->TableName().Equals(aTable)) {
|
||||
return mLookupCaches[i];
|
||||
}
|
||||
}
|
||||
|
||||
LookupCache *cache = new LookupCache(aTable, mStoreDirectory);
|
||||
nsresult rv = cache->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
rv = cache->Open();
|
||||
if (NS_FAILED(rv)) {
|
||||
if (rv == NS_ERROR_FILE_CORRUPTED) {
|
||||
Reset();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
mLookupCaches.AppendElement(cache);
|
||||
return cache;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::ReadNoiseEntries(const Prefix& aPrefix,
|
||||
const nsACString& aTableName,
|
||||
uint32_t aCount,
|
||||
PrefixArray* aNoiseEntries)
|
||||
{
|
||||
LookupCache *cache = GetLookupCache(aTableName);
|
||||
if (!cache) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
FallibleTArray<uint32_t> prefixes;
|
||||
nsresult rv = cache->GetPrefixes(prefixes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
size_t idx = prefixes.BinaryIndexOf(aPrefix.ToUint32());
|
||||
|
||||
if (idx == nsTArray<uint32_t>::NoIndex) {
|
||||
NS_WARNING("Could not find prefix in PrefixSet during noise lookup");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
idx -= idx % aCount;
|
||||
|
||||
for (size_t i = 0; (i < aCount) && ((idx+i) < prefixes.Length()); i++) {
|
||||
Prefix newPref;
|
||||
newPref.FromUint32(prefixes[idx+i]);
|
||||
if (newPref != aPrefix) {
|
||||
aNoiseEntries->AppendElement(newPref);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace safebrowsing
|
||||
} // namespace mozilla
|
||||
@@ -1,110 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef Classifier_h__
|
||||
#define Classifier_h__
|
||||
|
||||
#include "Entries.h"
|
||||
#include "HashStore.h"
|
||||
#include "ProtocolParser.h"
|
||||
#include "LookupCache.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
/**
|
||||
* Maintains the stores and LookupCaches for the url classifier.
|
||||
*/
|
||||
class Classifier {
|
||||
public:
|
||||
Classifier();
|
||||
~Classifier();
|
||||
|
||||
nsresult Open(nsIFile& aCacheDirectory);
|
||||
void Close();
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* Get the list of active tables and their chunks in a format
|
||||
* suitable for an update request.
|
||||
*/
|
||||
void TableRequest(nsACString& aResult);
|
||||
|
||||
/*
|
||||
* Get all tables that we know about.
|
||||
*/
|
||||
nsresult ActiveTables(nsTArray<nsCString>& aTables);
|
||||
|
||||
/**
|
||||
* Check a URL against the specified tables.
|
||||
*/
|
||||
nsresult Check(const nsACString& aSpec,
|
||||
const nsACString& tables,
|
||||
uint32_t aFreshnessGuarantee,
|
||||
LookupResultArray& aResults);
|
||||
|
||||
/**
|
||||
* Apply the table updates in the array. Takes ownership of
|
||||
* the updates in the array and clears it. Wacky!
|
||||
*/
|
||||
nsresult ApplyUpdates(nsTArray<TableUpdate*>* aUpdates);
|
||||
/**
|
||||
* Failed update. Spoil the entries so we don't block hosts
|
||||
* unnecessarily
|
||||
*/
|
||||
nsresult MarkSpoiled(nsTArray<nsCString>& aTables);
|
||||
nsresult CacheCompletions(const CacheResultArray& aResults);
|
||||
uint32_t GetHashKey(void) { return mHashKey; }
|
||||
/*
|
||||
* Get a bunch of extra prefixes to query for completion
|
||||
* and mask the real entry being requested
|
||||
*/
|
||||
nsresult ReadNoiseEntries(const Prefix& aPrefix,
|
||||
const nsACString& aTableName,
|
||||
uint32_t aCount,
|
||||
PrefixArray* aNoiseEntries);
|
||||
static void SplitTables(const nsACString& str, nsTArray<nsCString>& tables);
|
||||
|
||||
private:
|
||||
void DropStores();
|
||||
nsresult CreateStoreDirectory();
|
||||
nsresult SetupPathNames();
|
||||
nsresult RecoverBackups();
|
||||
nsresult CleanToDelete();
|
||||
nsresult BackupTables();
|
||||
nsresult RemoveBackupTables();
|
||||
nsresult RegenActiveTables();
|
||||
nsresult ScanStoreDir(nsTArray<nsCString>& aTables);
|
||||
|
||||
nsresult ApplyTableUpdates(nsTArray<TableUpdate*>* aUpdates,
|
||||
const nsACString& aTable);
|
||||
|
||||
LookupCache *GetLookupCache(const nsACString& aTable);
|
||||
|
||||
// Root dir of the Local profile.
|
||||
nsCOMPtr<nsIFile> mCacheDirectory;
|
||||
// Main directory where to store the databases.
|
||||
nsCOMPtr<nsIFile> mStoreDirectory;
|
||||
// Used for atomically updating the other dirs.
|
||||
nsCOMPtr<nsIFile> mBackupDirectory;
|
||||
nsCOMPtr<nsIFile> mToDeleteDirectory;
|
||||
nsCOMPtr<nsICryptoHash> mCryptoHash;
|
||||
nsTArray<HashStore*> mHashStores;
|
||||
nsTArray<LookupCache*> mLookupCaches;
|
||||
nsTArray<nsCString> mActiveTablesCache;
|
||||
uint32_t mHashKey;
|
||||
// Stores the last time a given table was updated (seconds).
|
||||
nsDataHashtable<nsCStringHashKey, int64_t> mTableFreshness;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,314 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This header file defines the storage types of the actual safebrowsing
|
||||
// chunk data, which may be either 32-bit hashes or complete 256-bit hashes.
|
||||
// Chunk numbers are represented in ChunkSet.h.
|
||||
|
||||
#ifndef SBEntries_h__
|
||||
#define SBEntries_h__
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
#if DEBUG
|
||||
#include "plbase64.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
#define PREFIX_SIZE 4
|
||||
#define COMPLETE_SIZE 32
|
||||
|
||||
// This is the struct that contains 4-byte hash prefixes.
|
||||
template <uint32_t S, class Comparator>
|
||||
struct SafebrowsingHash
|
||||
{
|
||||
static const uint32_t sHashSize = S;
|
||||
typedef SafebrowsingHash<S, Comparator> self_type;
|
||||
uint8_t buf[S];
|
||||
|
||||
nsresult FromPlaintext(const nsACString& aPlainText, nsICryptoHash* aHash) {
|
||||
// From the protocol doc:
|
||||
// Each entry in the chunk is composed
|
||||
// of the SHA 256 hash of a suffix/prefix expression.
|
||||
|
||||
nsresult rv = aHash->Init(nsICryptoHash::SHA256);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aHash->Update
|
||||
(reinterpret_cast<const uint8_t*>(aPlainText.BeginReading()),
|
||||
aPlainText.Length());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString hashed;
|
||||
rv = aHash->Finish(false, hashed);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(hashed.Length() >= sHashSize,
|
||||
"not enough characters in the hash");
|
||||
|
||||
memcpy(buf, hashed.BeginReading(), sHashSize);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Assign(const nsACString& aStr) {
|
||||
NS_ASSERTION(aStr.Length() >= sHashSize,
|
||||
"string must be at least sHashSize characters long");
|
||||
memcpy(buf, aStr.BeginReading(), sHashSize);
|
||||
}
|
||||
|
||||
int Compare(const self_type& aOther) const {
|
||||
return Comparator::Compare(buf, aOther.buf);
|
||||
}
|
||||
|
||||
bool operator==(const self_type& aOther) const {
|
||||
return Comparator::Compare(buf, aOther.buf) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const self_type& aOther) const {
|
||||
return Comparator::Compare(buf, aOther.buf) != 0;
|
||||
}
|
||||
|
||||
bool operator<(const self_type& aOther) const {
|
||||
return Comparator::Compare(buf, aOther.buf) < 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void ToString(nsACString& aStr) const {
|
||||
uint32_t len = ((sHashSize + 2) / 3) * 4;
|
||||
aStr.SetCapacity(len + 1);
|
||||
PL_Base64Encode((char*)buf, sHashSize, aStr.BeginWriting());
|
||||
aStr.BeginWriting()[len] = '\0';
|
||||
}
|
||||
|
||||
void ToHexString(nsACString& aStr) const {
|
||||
static const char* const lut = "0123456789ABCDEF";
|
||||
// 32 bytes is the longest hash
|
||||
size_t len = 32;
|
||||
|
||||
aStr.SetCapacity(2 * len);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
const char c = static_cast<const char>(buf[i]);
|
||||
aStr.Append(lut[(c >> 4) & 0x0F]);
|
||||
aStr.Append(lut[c & 15]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
uint32_t ToUint32() const {
|
||||
return *((uint32_t*)buf);
|
||||
}
|
||||
void FromUint32(uint32_t aHash) {
|
||||
*((uint32_t*)buf) = aHash;
|
||||
}
|
||||
};
|
||||
|
||||
class PrefixComparator {
|
||||
public:
|
||||
static int Compare(const uint8_t* a, const uint8_t* b) {
|
||||
uint32_t first = *((uint32_t*)a);
|
||||
uint32_t second = *((uint32_t*)b);
|
||||
if (first > second) {
|
||||
return 1;
|
||||
} else if (first == second) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
// Use this for 4-byte hashes
|
||||
typedef SafebrowsingHash<PREFIX_SIZE, PrefixComparator> Prefix;
|
||||
typedef nsTArray<Prefix> PrefixArray;
|
||||
|
||||
class CompletionComparator {
|
||||
public:
|
||||
static int Compare(const uint8_t* a, const uint8_t* b) {
|
||||
return memcmp(a, b, COMPLETE_SIZE);
|
||||
}
|
||||
};
|
||||
// Use this for 32-byte hashes
|
||||
typedef SafebrowsingHash<COMPLETE_SIZE, CompletionComparator> Completion;
|
||||
typedef nsTArray<Completion> CompletionArray;
|
||||
|
||||
struct AddPrefix {
|
||||
// The truncated hash.
|
||||
Prefix prefix;
|
||||
// The chunk number to which it belongs.
|
||||
uint32_t addChunk;
|
||||
|
||||
AddPrefix() : addChunk(0) {}
|
||||
|
||||
// Returns the chunk number.
|
||||
uint32_t Chunk() const { return addChunk; }
|
||||
const Prefix &PrefixHash() const { return prefix; }
|
||||
|
||||
template<class T>
|
||||
int Compare(const T& other) const {
|
||||
int cmp = prefix.Compare(other.PrefixHash());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
return addChunk - other.addChunk;
|
||||
}
|
||||
};
|
||||
|
||||
struct AddComplete {
|
||||
Completion complete;
|
||||
uint32_t addChunk;
|
||||
|
||||
AddComplete() : addChunk(0) {}
|
||||
|
||||
uint32_t Chunk() const { return addChunk; }
|
||||
// The 4-byte prefix of the sha256 hash.
|
||||
uint32_t ToUint32() const { return complete.ToUint32(); }
|
||||
// The 32-byte sha256 hash.
|
||||
const Completion &CompleteHash() const { return complete; }
|
||||
|
||||
template<class T>
|
||||
int Compare(const T& other) const {
|
||||
int cmp = complete.Compare(other.CompleteHash());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
return addChunk - other.addChunk;
|
||||
}
|
||||
};
|
||||
|
||||
struct SubPrefix {
|
||||
// The hash to subtract.
|
||||
Prefix prefix;
|
||||
// The chunk number of the add chunk to which the hash belonged.
|
||||
uint32_t addChunk;
|
||||
// The chunk number of this sub chunk.
|
||||
uint32_t subChunk;
|
||||
|
||||
SubPrefix(): addChunk(0), subChunk(0) {}
|
||||
|
||||
uint32_t Chunk() const { return subChunk; }
|
||||
uint32_t AddChunk() const { return addChunk; }
|
||||
const Prefix &PrefixHash() const { return prefix; }
|
||||
|
||||
template<class T>
|
||||
// Returns 0 if and only if the chunks are the same in every way.
|
||||
int Compare(const T& aOther) const {
|
||||
int cmp = prefix.Compare(aOther.PrefixHash());
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
if (addChunk != aOther.addChunk)
|
||||
return addChunk - aOther.addChunk;
|
||||
return subChunk - aOther.subChunk;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
int CompareAlt(const T& aOther) const {
|
||||
Prefix other;
|
||||
other.FromUint32(aOther.ToUint32());
|
||||
int cmp = prefix.Compare(other);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
return addChunk - aOther.addChunk;
|
||||
}
|
||||
};
|
||||
|
||||
struct SubComplete {
|
||||
Completion complete;
|
||||
uint32_t addChunk;
|
||||
uint32_t subChunk;
|
||||
|
||||
SubComplete() : addChunk(0), subChunk(0) {}
|
||||
|
||||
uint32_t Chunk() const { return subChunk; }
|
||||
uint32_t AddChunk() const { return addChunk; }
|
||||
const Completion &CompleteHash() const { return complete; }
|
||||
// The 4-byte prefix of the sha256 hash.
|
||||
uint32_t ToUint32() const { return complete.ToUint32(); }
|
||||
|
||||
int Compare(const SubComplete& aOther) const {
|
||||
int cmp = complete.Compare(aOther.complete);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
if (addChunk != aOther.addChunk)
|
||||
return addChunk - aOther.addChunk;
|
||||
return subChunk - aOther.subChunk;
|
||||
}
|
||||
};
|
||||
|
||||
typedef FallibleTArray<AddPrefix> AddPrefixArray;
|
||||
typedef FallibleTArray<AddComplete> AddCompleteArray;
|
||||
typedef FallibleTArray<SubPrefix> SubPrefixArray;
|
||||
typedef FallibleTArray<SubComplete> SubCompleteArray;
|
||||
|
||||
/**
|
||||
* Compares chunks by their add chunk, then their prefix.
|
||||
*/
|
||||
template<class T>
|
||||
class EntryCompare {
|
||||
public:
|
||||
typedef T elem_type;
|
||||
static int Compare(const void* e1, const void* e2) {
|
||||
const elem_type* a = static_cast<const elem_type*>(e1);
|
||||
const elem_type* b = static_cast<const elem_type*>(e2);
|
||||
return a->Compare(*b);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort an array of store entries. nsTArray::Sort uses Equal/LessThan
|
||||
* to sort, this does a single Compare so it's a bit quicker over the
|
||||
* large sorts we do.
|
||||
*/
|
||||
template<class T, class Alloc>
|
||||
void
|
||||
EntrySort(nsTArray_Impl<T, Alloc>& aArray)
|
||||
{
|
||||
qsort(aArray.Elements(), aArray.Length(), sizeof(T),
|
||||
EntryCompare<T>::Compare);
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
nsresult
|
||||
ReadTArray(nsIInputStream* aStream, nsTArray_Impl<T, Alloc>* aArray, uint32_t aNumElements)
|
||||
{
|
||||
aArray->SetLength(aNumElements);
|
||||
|
||||
void *buffer = aArray->Elements();
|
||||
nsresult rv = NS_ReadInputStreamToBuffer(aStream, &buffer,
|
||||
(aNumElements * sizeof(T)));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
nsresult
|
||||
ReadTArray(nsIInputStream* aStream, FallibleTArray<T>* aArray, uint32_t aNumElements)
|
||||
{
|
||||
if (!aArray->SetLength(aNumElements))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
void *buffer = aArray->Elements();
|
||||
nsresult rv = NS_ReadInputStreamToBuffer(aStream, &buffer,
|
||||
(aNumElements * sizeof(T)));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template<class T, class Alloc>
|
||||
nsresult
|
||||
WriteTArray(nsIOutputStream* aStream, nsTArray_Impl<T, Alloc>& aArray)
|
||||
{
|
||||
uint32_t written;
|
||||
return aStream->Write(reinterpret_cast<char*>(aArray.Elements()),
|
||||
aArray.Length() * sizeof(T),
|
||||
&written);
|
||||
}
|
||||
|
||||
} // namespace safebrowsing
|
||||
} // namespace mozilla
|
||||
#endif // SBEntries_h__
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,200 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef HashStore_h__
|
||||
#define HashStore_h__
|
||||
|
||||
#include "Entries.h"
|
||||
#include "ChunkSet.h"
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
// A table update is built from a single update chunk from the server. As the
|
||||
// protocol parser processes each chunk, it constructs a table update with the
|
||||
// new hashes.
|
||||
class TableUpdate {
|
||||
public:
|
||||
explicit TableUpdate(const nsACString& aTable)
|
||||
: mTable(aTable), mLocalUpdate(false) {}
|
||||
const nsCString& TableName() const { return mTable; }
|
||||
|
||||
bool Empty() const {
|
||||
return mAddChunks.Length() == 0 &&
|
||||
mSubChunks.Length() == 0 &&
|
||||
mAddExpirations.Length() == 0 &&
|
||||
mSubExpirations.Length() == 0 &&
|
||||
mAddPrefixes.Length() == 0 &&
|
||||
mSubPrefixes.Length() == 0 &&
|
||||
mAddCompletes.Length() == 0 &&
|
||||
mSubCompletes.Length() == 0;
|
||||
}
|
||||
|
||||
// Throughout, uint32_t aChunk refers only to the chunk number. Chunk data is
|
||||
// stored in the Prefix structures.
|
||||
void NewAddChunk(uint32_t aChunk) { mAddChunks.Set(aChunk); }
|
||||
void NewSubChunk(uint32_t aChunk) { mSubChunks.Set(aChunk); }
|
||||
|
||||
void NewAddExpiration(uint32_t aChunk) { mAddExpirations.Set(aChunk); }
|
||||
void NewSubExpiration(uint32_t aChunk) { mSubExpirations.Set(aChunk); }
|
||||
|
||||
void NewAddPrefix(uint32_t aAddChunk, const Prefix& aPrefix);
|
||||
void NewSubPrefix(uint32_t aAddChunk, const Prefix& aPrefix, uint32_t aSubChunk);
|
||||
|
||||
void NewAddComplete(uint32_t aChunk, const Completion& aCompletion);
|
||||
void NewSubComplete(uint32_t aAddChunk, const Completion& aCompletion,
|
||||
uint32_t aSubChunk);
|
||||
void SetLocalUpdate(void) { mLocalUpdate = true; }
|
||||
bool IsLocalUpdate(void) { return mLocalUpdate; }
|
||||
|
||||
ChunkSet& AddChunks() { return mAddChunks; }
|
||||
ChunkSet& SubChunks() { return mSubChunks; }
|
||||
|
||||
// Expirations for chunks.
|
||||
ChunkSet& AddExpirations() { return mAddExpirations; }
|
||||
ChunkSet& SubExpirations() { return mSubExpirations; }
|
||||
|
||||
// Hashes associated with this chunk.
|
||||
AddPrefixArray& AddPrefixes() { return mAddPrefixes; }
|
||||
SubPrefixArray& SubPrefixes() { return mSubPrefixes; }
|
||||
AddCompleteArray& AddCompletes() { return mAddCompletes; }
|
||||
SubCompleteArray& SubCompletes() { return mSubCompletes; }
|
||||
|
||||
private:
|
||||
nsCString mTable;
|
||||
// Update not from the remote server (no freshness)
|
||||
bool mLocalUpdate;
|
||||
|
||||
// The list of chunk numbers that we have for each of the type of chunks.
|
||||
ChunkSet mAddChunks;
|
||||
ChunkSet mSubChunks;
|
||||
ChunkSet mAddExpirations;
|
||||
ChunkSet mSubExpirations;
|
||||
|
||||
// 4-byte sha256 prefixes.
|
||||
AddPrefixArray mAddPrefixes;
|
||||
SubPrefixArray mSubPrefixes;
|
||||
|
||||
// 32-byte hashes.
|
||||
AddCompleteArray mAddCompletes;
|
||||
SubCompleteArray mSubCompletes;
|
||||
};
|
||||
|
||||
// There is one hash store per table.
|
||||
class HashStore {
|
||||
public:
|
||||
HashStore(const nsACString& aTableName, nsIFile* aStoreFile);
|
||||
~HashStore();
|
||||
|
||||
const nsCString& TableName() const { return mTableName; }
|
||||
|
||||
nsresult Open();
|
||||
// Add Prefixes are stored partly in the PrefixSet (contains the
|
||||
// Prefix data organized for fast lookup/low RAM usage) and partly in the
|
||||
// HashStore (Add Chunk numbers - only used for updates, slow retrieval).
|
||||
// AugmentAdds function joins the separate datasets into one complete
|
||||
// prefixes+chunknumbers dataset.
|
||||
nsresult AugmentAdds(const nsTArray<uint32_t>& aPrefixes);
|
||||
|
||||
ChunkSet& AddChunks() { return mAddChunks; }
|
||||
ChunkSet& SubChunks() { return mSubChunks; }
|
||||
AddPrefixArray& AddPrefixes() { return mAddPrefixes; }
|
||||
AddCompleteArray& AddCompletes() { return mAddCompletes; }
|
||||
SubPrefixArray& SubPrefixes() { return mSubPrefixes; }
|
||||
SubCompleteArray& SubCompletes() { return mSubCompletes; }
|
||||
|
||||
// =======
|
||||
// Updates
|
||||
// =======
|
||||
// Begin the update process. Reads the store into memory.
|
||||
nsresult BeginUpdate();
|
||||
|
||||
// Imports the data from a TableUpdate.
|
||||
nsresult ApplyUpdate(TableUpdate &aUpdate);
|
||||
|
||||
// Process expired chunks
|
||||
nsresult Expire();
|
||||
|
||||
// Rebuild the store, Incorporating all the applied updates.
|
||||
nsresult Rebuild();
|
||||
|
||||
// Write the current state of the store to disk.
|
||||
// If you call between ApplyUpdate() and Rebuild(), you'll
|
||||
// have a mess on your hands.
|
||||
nsresult WriteFile();
|
||||
|
||||
// Wipe out all Completes.
|
||||
void ClearCompletes();
|
||||
|
||||
private:
|
||||
nsresult Reset();
|
||||
|
||||
nsresult ReadHeader();
|
||||
nsresult SanityCheck();
|
||||
nsresult CalculateChecksum(nsAutoCString& aChecksum, uint32_t aFileSize,
|
||||
bool aChecksumPresent);
|
||||
nsresult CheckChecksum(nsIFile* aStoreFile, uint32_t aFileSize);
|
||||
void UpdateHeader();
|
||||
|
||||
nsresult ReadChunkNumbers();
|
||||
nsresult ReadHashes();
|
||||
|
||||
nsresult ReadAddPrefixes();
|
||||
nsresult ReadSubPrefixes();
|
||||
|
||||
nsresult WriteAddPrefixes(nsIOutputStream* aOut);
|
||||
nsresult WriteSubPrefixes(nsIOutputStream* aOut);
|
||||
|
||||
nsresult ProcessSubs();
|
||||
|
||||
// This is used for checking that the database is correct and for figuring out
|
||||
// the number of chunks, etc. to read from disk on restart.
|
||||
struct Header {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t numAddChunks;
|
||||
uint32_t numSubChunks;
|
||||
uint32_t numAddPrefixes;
|
||||
uint32_t numSubPrefixes;
|
||||
uint32_t numAddCompletes;
|
||||
uint32_t numSubCompletes;
|
||||
};
|
||||
|
||||
Header mHeader;
|
||||
|
||||
// The name of the table (must end in -shavar or -digest256, or evidently
|
||||
// -simple for unittesting.
|
||||
nsCString mTableName;
|
||||
nsCOMPtr<nsIFile> mStoreDirectory;
|
||||
|
||||
bool mInUpdate;
|
||||
|
||||
nsCOMPtr<nsIInputStream> mInputStream;
|
||||
|
||||
// Chunk numbers, stored as uint32_t arrays.
|
||||
ChunkSet mAddChunks;
|
||||
ChunkSet mSubChunks;
|
||||
|
||||
ChunkSet mAddExpirations;
|
||||
ChunkSet mSubExpirations;
|
||||
|
||||
// Chunk data for shavar tables. See Entries.h for format.
|
||||
AddPrefixArray mAddPrefixes;
|
||||
SubPrefixArray mSubPrefixes;
|
||||
|
||||
// See bug 806422 for background. We must be able to distinguish between
|
||||
// updates from the completion server and updates from the regular server.
|
||||
AddCompleteArray mAddCompletes;
|
||||
SubCompleteArray mSubCompletes;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,699 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "LookupCache.h"
|
||||
#include "HashStore.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "prlog.h"
|
||||
#include "prprf.h"
|
||||
|
||||
// We act as the main entry point for all the real lookups,
|
||||
// so note that those are not done to the actual HashStore.
|
||||
// The latter solely exists to store the data needed to handle
|
||||
// the updates from the protocol.
|
||||
|
||||
// This module has its own store, which stores the Completions,
|
||||
// mostly caching lookups that have happened over the net.
|
||||
// The prefixes are cached/checked by looking them up in the
|
||||
// PrefixSet.
|
||||
|
||||
// Data format for the ".cache" files:
|
||||
// uint32_t magic Identify the file type
|
||||
// uint32_t version Version identifier for file format
|
||||
// uint32_t numCompletions Amount of completions stored
|
||||
// 0...numCompletions 256-bit Completions
|
||||
|
||||
// Name of the lookupcomplete cache
|
||||
#define CACHE_SUFFIX ".cache"
|
||||
|
||||
// Name of the persistent PrefixSet storage
|
||||
#define PREFIXSET_SUFFIX ".pset"
|
||||
|
||||
// NSPR_LOG_MODULES=UrlClassifierDbService:5
|
||||
extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
|
||||
#if defined(PR_LOGGING)
|
||||
#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
|
||||
#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#define LOG_ENABLED() (false)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
const uint32_t LOOKUPCACHE_MAGIC = 0x1231af3e;
|
||||
const uint32_t CURRENT_VERSION = 2;
|
||||
|
||||
LookupCache::LookupCache(const nsACString& aTableName, nsIFile* aStoreDir)
|
||||
: mPrimed(false)
|
||||
, mTableName(aTableName)
|
||||
, mStoreDirectory(aStoreDir)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::Init()
|
||||
{
|
||||
mPrefixSet = new nsUrlClassifierPrefixSet();
|
||||
nsresult rv = mPrefixSet->Init(mTableName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
LookupCache::~LookupCache()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::Open()
|
||||
{
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), storeFile,
|
||||
PR_RDONLY | nsIFile::OS_READAHEAD);
|
||||
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
|
||||
Reset();
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (rv == NS_ERROR_FILE_NOT_FOUND) {
|
||||
// Simply lacking a .cache file is a recoverable error,
|
||||
// as unlike the .pset/.sbstore files it is a pure cache.
|
||||
// Just create a new empty one.
|
||||
ClearCompleteCache();
|
||||
} else {
|
||||
// Read in the .cache file
|
||||
rv = ReadHeader(inputStream);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
LOG(("ReadCompletions"));
|
||||
rv = ReadCompletions(inputStream);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = inputStream->Close();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
LOG(("Loading PrefixSet"));
|
||||
rv = LoadPrefixSet();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::UpdateDirHandle(nsIFile* aStoreDirectory)
|
||||
{
|
||||
return aStoreDirectory->Clone(getter_AddRefs(mStoreDirectory));
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::Reset()
|
||||
{
|
||||
LOG(("LookupCache resetting"));
|
||||
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
nsCOMPtr<nsIFile> prefixsetFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mStoreDirectory->Clone(getter_AddRefs(prefixsetFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = prefixsetFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = storeFile->Remove(false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = prefixsetFile->Remove(false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ClearAll();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
LookupCache::Build(AddPrefixArray& aAddPrefixes,
|
||||
AddCompleteArray& aAddCompletes)
|
||||
{
|
||||
Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_COMPLETIONS,
|
||||
static_cast<uint32_t>(aAddCompletes.Length()));
|
||||
|
||||
mCompletions.Clear();
|
||||
mCompletions.SetCapacity(aAddCompletes.Length());
|
||||
for (uint32_t i = 0; i < aAddCompletes.Length(); i++) {
|
||||
mCompletions.AppendElement(aAddCompletes[i].CompleteHash());
|
||||
}
|
||||
aAddCompletes.Clear();
|
||||
mCompletions.Sort();
|
||||
|
||||
Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_PREFIXES,
|
||||
static_cast<uint32_t>(aAddPrefixes.Length()));
|
||||
|
||||
nsresult rv = ConstructPrefixSet(aAddPrefixes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mPrimed = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#if defined(DEBUG) && defined(PR_LOGGING)
|
||||
void
|
||||
LookupCache::Dump()
|
||||
{
|
||||
if (!LOG_ENABLED())
|
||||
return;
|
||||
|
||||
for (uint32_t i = 0; i < mCompletions.Length(); i++) {
|
||||
nsAutoCString str;
|
||||
mCompletions[i].ToHexString(str);
|
||||
LOG(("Completion: %s", str.get()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
LookupCache::Has(const Completion& aCompletion,
|
||||
bool* aHas, bool* aComplete)
|
||||
{
|
||||
*aHas = *aComplete = false;
|
||||
|
||||
uint32_t prefix = aCompletion.ToUint32();
|
||||
|
||||
bool found;
|
||||
nsresult rv = mPrefixSet->Contains(prefix, &found);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Probe in %s: %X, found %d", mTableName.get(), prefix, found));
|
||||
|
||||
if (found) {
|
||||
*aHas = true;
|
||||
}
|
||||
|
||||
if (mCompletions.BinaryIndexOf(aCompletion) != nsTArray<Completion>::NoIndex) {
|
||||
LOG(("Complete in %s", mTableName.get()));
|
||||
*aComplete = true;
|
||||
*aHas = true;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::WriteFile()
|
||||
{
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIOutputStream> out;
|
||||
rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out), storeFile,
|
||||
PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
UpdateHeader();
|
||||
LOG(("Writing %d completions", mHeader.numCompletions));
|
||||
|
||||
uint32_t written;
|
||||
rv = out->Write(reinterpret_cast<char*>(&mHeader), sizeof(mHeader), &written);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = WriteTArray(out, mCompletions);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsISafeOutputStream> safeOut = do_QueryInterface(out);
|
||||
rv = safeOut->Finish();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = EnsureSizeConsistent();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIFile> psFile;
|
||||
rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mPrefixSet->StoreToFile(psFile);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store the prefixset");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
LookupCache::ClearAll()
|
||||
{
|
||||
ClearCompleteCache();
|
||||
mPrefixSet->SetPrefixes(nullptr, 0);
|
||||
mPrimed = false;
|
||||
}
|
||||
|
||||
void
|
||||
LookupCache::ClearCompleteCache()
|
||||
{
|
||||
mCompletions.Clear();
|
||||
UpdateHeader();
|
||||
}
|
||||
|
||||
void
|
||||
LookupCache::UpdateHeader()
|
||||
{
|
||||
mHeader.magic = LOOKUPCACHE_MAGIC;
|
||||
mHeader.version = CURRENT_VERSION;
|
||||
mHeader.numCompletions = mCompletions.Length();
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::EnsureSizeConsistent()
|
||||
{
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
int64_t fileSize;
|
||||
rv = storeFile->GetFileSize(&fileSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (fileSize < 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int64_t expectedSize = sizeof(mHeader)
|
||||
+ mHeader.numCompletions*sizeof(Completion);
|
||||
if (expectedSize != fileSize) {
|
||||
NS_WARNING("File length does not match. Probably corrupted.");
|
||||
Reset();
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::ReadHeader(nsIInputStream* aInputStream)
|
||||
{
|
||||
if (!aInputStream) {
|
||||
ClearCompleteCache();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aInputStream);
|
||||
nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
void *buffer = &mHeader;
|
||||
rv = NS_ReadInputStreamToBuffer(aInputStream,
|
||||
&buffer,
|
||||
sizeof(Header));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mHeader.magic != LOOKUPCACHE_MAGIC || mHeader.version != CURRENT_VERSION) {
|
||||
NS_WARNING("Unexpected header data in the store.");
|
||||
Reset();
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
LOG(("%d completions present", mHeader.numCompletions));
|
||||
|
||||
rv = EnsureSizeConsistent();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::ReadCompletions(nsIInputStream* aInputStream)
|
||||
{
|
||||
if (!mHeader.numCompletions) {
|
||||
mCompletions.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aInputStream);
|
||||
nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, sizeof(Header));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = ReadTArray(aInputStream, &mCompletions, mHeader.numCompletions);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Read %d completions", mCompletions.Length()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
LookupCache::IsCanonicalizedIP(const nsACString& aHost)
|
||||
{
|
||||
// The canonicalization process will have left IP addresses in dotted
|
||||
// decimal with no surprises.
|
||||
uint32_t i1, i2, i3, i4;
|
||||
char c;
|
||||
if (PR_sscanf(PromiseFlatCString(aHost).get(), "%u.%u.%u.%u%c",
|
||||
&i1, &i2, &i3, &i4, &c) == 4) {
|
||||
return (i1 <= 0xFF && i2 <= 0xFF && i3 <= 0xFF && i4 <= 0xFF);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
LookupCache::GetKey(const nsACString& aSpec,
|
||||
Completion* aHash,
|
||||
nsCOMPtr<nsICryptoHash>& aCryptoHash)
|
||||
{
|
||||
nsACString::const_iterator begin, end, iter;
|
||||
aSpec.BeginReading(begin);
|
||||
aSpec.EndReading(end);
|
||||
|
||||
iter = begin;
|
||||
if (!FindCharInReadable('/', iter, end)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsCSubstring& host = Substring(begin, iter);
|
||||
|
||||
if (IsCanonicalizedIP(host)) {
|
||||
nsAutoCString key;
|
||||
key.Assign(host);
|
||||
key.Append('/');
|
||||
return aHash->FromPlaintext(key, aCryptoHash);
|
||||
}
|
||||
|
||||
nsTArray<nsCString> hostComponents;
|
||||
ParseString(PromiseFlatCString(host), '.', hostComponents);
|
||||
|
||||
if (hostComponents.Length() < 2)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
int32_t last = int32_t(hostComponents.Length()) - 1;
|
||||
nsAutoCString lookupHost;
|
||||
|
||||
if (hostComponents.Length() > 2) {
|
||||
lookupHost.Append(hostComponents[last - 2]);
|
||||
lookupHost.Append('.');
|
||||
}
|
||||
|
||||
lookupHost.Append(hostComponents[last - 1]);
|
||||
lookupHost.Append('.');
|
||||
lookupHost.Append(hostComponents[last]);
|
||||
lookupHost.Append('/');
|
||||
|
||||
return aHash->FromPlaintext(lookupHost, aCryptoHash);
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
LookupCache::GetLookupFragments(const nsACString& aSpec,
|
||||
nsTArray<nsCString>* aFragments)
|
||||
|
||||
{
|
||||
aFragments->Clear();
|
||||
|
||||
nsACString::const_iterator begin, end, iter;
|
||||
aSpec.BeginReading(begin);
|
||||
aSpec.EndReading(end);
|
||||
|
||||
iter = begin;
|
||||
if (!FindCharInReadable('/', iter, end)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsCSubstring& host = Substring(begin, iter++);
|
||||
nsAutoCString path;
|
||||
path.Assign(Substring(iter, end));
|
||||
|
||||
/**
|
||||
* From the protocol doc:
|
||||
* For the hostname, the client will try at most 5 different strings. They
|
||||
* are:
|
||||
* a) The exact hostname of the url
|
||||
* b) The 4 hostnames formed by starting with the last 5 components and
|
||||
* successivly removing the leading component. The top-level component
|
||||
* can be skipped. This is not done if the hostname is a numerical IP.
|
||||
*/
|
||||
nsTArray<nsCString> hosts;
|
||||
hosts.AppendElement(host);
|
||||
|
||||
if (!IsCanonicalizedIP(host)) {
|
||||
host.BeginReading(begin);
|
||||
host.EndReading(end);
|
||||
int numHostComponents = 0;
|
||||
while (RFindInReadable(NS_LITERAL_CSTRING("."), begin, end) &&
|
||||
numHostComponents < MAX_HOST_COMPONENTS) {
|
||||
// don't bother checking toplevel domains
|
||||
if (++numHostComponents >= 2) {
|
||||
host.EndReading(iter);
|
||||
hosts.AppendElement(Substring(end, iter));
|
||||
}
|
||||
end = begin;
|
||||
host.BeginReading(begin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* From the protocol doc:
|
||||
* For the path, the client will also try at most 6 different strings.
|
||||
* They are:
|
||||
* a) the exact path of the url, including query parameters
|
||||
* b) the exact path of the url, without query parameters
|
||||
* c) the 4 paths formed by starting at the root (/) and
|
||||
* successively appending path components, including a trailing
|
||||
* slash. This behavior should only extend up to the next-to-last
|
||||
* path component, that is, a trailing slash should never be
|
||||
* appended that was not present in the original url.
|
||||
*/
|
||||
nsTArray<nsCString> paths;
|
||||
nsAutoCString pathToAdd;
|
||||
|
||||
path.BeginReading(begin);
|
||||
path.EndReading(end);
|
||||
iter = begin;
|
||||
if (FindCharInReadable('?', iter, end)) {
|
||||
pathToAdd = Substring(begin, iter);
|
||||
paths.AppendElement(pathToAdd);
|
||||
end = iter;
|
||||
}
|
||||
|
||||
int numPathComponents = 1;
|
||||
iter = begin;
|
||||
while (FindCharInReadable('/', iter, end) &&
|
||||
numPathComponents < MAX_PATH_COMPONENTS) {
|
||||
iter++;
|
||||
pathToAdd.Assign(Substring(begin, iter));
|
||||
paths.AppendElement(pathToAdd);
|
||||
numPathComponents++;
|
||||
}
|
||||
|
||||
// If we haven't already done so, add the full path
|
||||
if (!pathToAdd.Equals(path)) {
|
||||
paths.AppendElement(path);
|
||||
}
|
||||
// Check an empty path (for whole-domain blacklist entries)
|
||||
paths.AppendElement(EmptyCString());
|
||||
|
||||
for (uint32_t hostIndex = 0; hostIndex < hosts.Length(); hostIndex++) {
|
||||
for (uint32_t pathIndex = 0; pathIndex < paths.Length(); pathIndex++) {
|
||||
nsCString key;
|
||||
key.Assign(hosts[hostIndex]);
|
||||
key.Append('/');
|
||||
key.Append(paths[pathIndex]);
|
||||
LOG(("Checking fragment %s", key.get()));
|
||||
|
||||
aFragments->AppendElement(key);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
LookupCache::GetHostKeys(const nsACString& aSpec,
|
||||
nsTArray<nsCString>* aHostKeys)
|
||||
{
|
||||
nsACString::const_iterator begin, end, iter;
|
||||
aSpec.BeginReading(begin);
|
||||
aSpec.EndReading(end);
|
||||
|
||||
iter = begin;
|
||||
if (!FindCharInReadable('/', iter, end)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsCSubstring& host = Substring(begin, iter);
|
||||
|
||||
if (IsCanonicalizedIP(host)) {
|
||||
nsCString *key = aHostKeys->AppendElement();
|
||||
if (!key)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
key->Assign(host);
|
||||
key->Append("/");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTArray<nsCString> hostComponents;
|
||||
ParseString(PromiseFlatCString(host), '.', hostComponents);
|
||||
|
||||
if (hostComponents.Length() < 2) {
|
||||
// no host or toplevel host, this won't match anything in the db
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// First check with two domain components
|
||||
int32_t last = int32_t(hostComponents.Length()) - 1;
|
||||
nsCString *lookupHost = aHostKeys->AppendElement();
|
||||
if (!lookupHost)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
lookupHost->Assign(hostComponents[last - 1]);
|
||||
lookupHost->Append(".");
|
||||
lookupHost->Append(hostComponents[last]);
|
||||
lookupHost->Append("/");
|
||||
|
||||
// Now check with three domain components
|
||||
if (hostComponents.Length() > 2) {
|
||||
nsCString *lookupHost2 = aHostKeys->AppendElement();
|
||||
if (!lookupHost2)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
lookupHost2->Assign(hostComponents[last - 2]);
|
||||
lookupHost2->Append(".");
|
||||
lookupHost2->Append(*lookupHost);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool LookupCache::IsPrimed()
|
||||
{
|
||||
return mPrimed;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
template <class T>
|
||||
static void EnsureSorted(T* aArray)
|
||||
{
|
||||
typename T::elem_type* start = aArray->Elements();
|
||||
typename T::elem_type* end = aArray->Elements() + aArray->Length();
|
||||
typename T::elem_type* iter = start;
|
||||
typename T::elem_type* previous = start;
|
||||
|
||||
while (iter != end) {
|
||||
previous = iter;
|
||||
++iter;
|
||||
if (iter != end) {
|
||||
MOZ_ASSERT(*previous <= *iter);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
LookupCache::ConstructPrefixSet(AddPrefixArray& aAddPrefixes)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_CONSTRUCT_TIME> timer;
|
||||
|
||||
nsTArray<uint32_t> array;
|
||||
array.SetCapacity(aAddPrefixes.Length());
|
||||
|
||||
for (uint32_t i = 0; i < aAddPrefixes.Length(); i++) {
|
||||
array.AppendElement(aAddPrefixes[i].PrefixHash().ToUint32());
|
||||
}
|
||||
aAddPrefixes.Clear();
|
||||
|
||||
#ifdef DEBUG
|
||||
// PrefixSet requires sorted order
|
||||
EnsureSorted(&array);
|
||||
#endif
|
||||
|
||||
// construct new one, replace old entries
|
||||
nsresult rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length());
|
||||
if (NS_FAILED(rv)) {
|
||||
goto error_bailout;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
uint32_t size;
|
||||
size = mPrefixSet->SizeInMemory();
|
||||
LOG(("SB tree done, size = %d bytes\n", size));
|
||||
#endif
|
||||
|
||||
mPrimed = true;
|
||||
|
||||
return NS_OK;
|
||||
|
||||
error_bailout:
|
||||
Telemetry::Accumulate(Telemetry::URLCLASSIFIER_PS_FAILURE, 1);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::LoadPrefixSet()
|
||||
{
|
||||
nsCOMPtr<nsIFile> psFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool exists;
|
||||
rv = psFile->Exists(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (exists) {
|
||||
LOG(("stored PrefixSet exists, loading from disk"));
|
||||
rv = mPrefixSet->LoadFromFile(psFile);
|
||||
if (NS_FAILED(rv)) {
|
||||
if (rv == NS_ERROR_FILE_CORRUPTED) {
|
||||
Reset();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
mPrimed = true;
|
||||
} else {
|
||||
LOG(("no (usable) stored PrefixSet found"));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (mPrimed) {
|
||||
uint32_t size = mPrefixSet->SizeInMemory();
|
||||
LOG(("SB tree done, size = %d bytes\n", size));
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::GetPrefixes(FallibleTArray<uint32_t>& aAddPrefixes)
|
||||
{
|
||||
if (!mPrimed) {
|
||||
// This can happen if its a new table, so no error.
|
||||
LOG(("GetPrefixes from empty LookupCache"));
|
||||
return NS_OK;
|
||||
}
|
||||
return mPrefixSet->GetPrefixesNative(aAddPrefixes);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef LookupCache_h__
|
||||
#define LookupCache_h__
|
||||
|
||||
#include "Entries.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsUrlClassifierPrefixSet.h"
|
||||
#include "prlog.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
#define MAX_HOST_COMPONENTS 5
|
||||
#define MAX_PATH_COMPONENTS 4
|
||||
|
||||
class LookupResult {
|
||||
public:
|
||||
LookupResult() : mComplete(false), mNoise(false), mFresh(false), mProtocolConfirmed(false) {}
|
||||
|
||||
// The fragment that matched in the LookupCache
|
||||
union {
|
||||
Prefix prefix;
|
||||
Completion complete;
|
||||
} hash;
|
||||
|
||||
const Prefix &PrefixHash() { return hash.prefix; }
|
||||
const Completion &CompleteHash() { return hash.complete; }
|
||||
|
||||
bool Confirmed() const { return (mComplete && mFresh) || mProtocolConfirmed; }
|
||||
bool Complete() const { return mComplete; }
|
||||
|
||||
// True if we have a complete match for this hash in the table.
|
||||
bool mComplete;
|
||||
|
||||
// True if this is a noise entry, i.e. an extra entry
|
||||
// that is inserted to mask the true URL we are requesting
|
||||
bool mNoise;
|
||||
|
||||
// True if we've updated this table recently-enough.
|
||||
bool mFresh;
|
||||
|
||||
bool mProtocolConfirmed;
|
||||
|
||||
nsCString mTableName;
|
||||
};
|
||||
|
||||
typedef nsTArray<LookupResult> LookupResultArray;
|
||||
|
||||
struct CacheResult {
|
||||
AddComplete entry;
|
||||
nsCString table;
|
||||
};
|
||||
typedef nsTArray<CacheResult> CacheResultArray;
|
||||
|
||||
class LookupCache {
|
||||
public:
|
||||
// Check for a canonicalized IP address.
|
||||
static bool IsCanonicalizedIP(const nsACString& aHost);
|
||||
|
||||
// take a lookup string (www.hostname.com/path/to/resource.html) and
|
||||
// expand it into the set of fragments that should be searched for in an
|
||||
// entry
|
||||
static nsresult GetLookupFragments(const nsACString& aSpec,
|
||||
nsTArray<nsCString>* aFragments);
|
||||
// Similar to GetKey(), but if the domain contains three or more components,
|
||||
// two keys will be returned:
|
||||
// hostname.com/foo/bar -> [hostname.com]
|
||||
// mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com]
|
||||
// www.mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com]
|
||||
static nsresult GetHostKeys(const nsACString& aSpec,
|
||||
nsTArray<nsCString>* aHostKeys);
|
||||
// Get the database key for a given URI. This is the top three
|
||||
// domain components if they exist, otherwise the top two.
|
||||
// hostname.com/foo/bar -> hostname.com
|
||||
// mail.hostname.com/foo/bar -> mail.hostname.com
|
||||
// www.mail.hostname.com/foo/bar -> mail.hostname.com
|
||||
static nsresult GetKey(const nsACString& aSpec, Completion* aHash,
|
||||
nsCOMPtr<nsICryptoHash>& aCryptoHash);
|
||||
|
||||
LookupCache(const nsACString& aTableName, nsIFile* aStoreFile);
|
||||
~LookupCache();
|
||||
|
||||
const nsCString &TableName() const { return mTableName; }
|
||||
|
||||
nsresult Init();
|
||||
nsresult Open();
|
||||
// The directory handle where we operate will
|
||||
// be moved away when a backup is made.
|
||||
nsresult UpdateDirHandle(nsIFile* aStoreDirectory);
|
||||
// This will Clear() the passed arrays when done.
|
||||
nsresult Build(AddPrefixArray& aAddPrefixes,
|
||||
AddCompleteArray& aAddCompletes);
|
||||
nsresult GetPrefixes(FallibleTArray<uint32_t>& aAddPrefixes);
|
||||
void ClearCompleteCache();
|
||||
|
||||
#if DEBUG && defined(PR_LOGGING)
|
||||
void Dump();
|
||||
#endif
|
||||
nsresult WriteFile();
|
||||
nsresult Has(const Completion& aCompletion,
|
||||
bool* aHas, bool* aComplete);
|
||||
bool IsPrimed();
|
||||
|
||||
private:
|
||||
void ClearAll();
|
||||
nsresult Reset();
|
||||
void UpdateHeader();
|
||||
nsresult ReadHeader(nsIInputStream* aInputStream);
|
||||
nsresult ReadCompletions(nsIInputStream* aInputStream);
|
||||
nsresult EnsureSizeConsistent();
|
||||
nsresult LoadPrefixSet();
|
||||
// Construct a Prefix Set with known prefixes.
|
||||
// This will Clear() aAddPrefixes when done.
|
||||
nsresult ConstructPrefixSet(AddPrefixArray& aAddPrefixes);
|
||||
|
||||
struct Header {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t numCompletions;
|
||||
};
|
||||
Header mHeader;
|
||||
|
||||
bool mPrimed;
|
||||
nsCString mTableName;
|
||||
nsCOMPtr<nsIFile> mStoreDirectory;
|
||||
CompletionArray mCompletions;
|
||||
// Set of prefixes known to be in the database
|
||||
nsRefPtr<nsUrlClassifierPrefixSet> mPrefixSet;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,632 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ProtocolParser.h"
|
||||
#include "LookupCache.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "prlog.h"
|
||||
#include "prnetdb.h"
|
||||
#include "prprf.h"
|
||||
|
||||
#include "nsUrlClassifierUtils.h"
|
||||
|
||||
// NSPR_LOG_MODULES=UrlClassifierDbService:5
|
||||
extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
|
||||
#if defined(PR_LOGGING)
|
||||
#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
|
||||
#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#define LOG_ENABLED() (false)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
// Updates will fail if fed chunks larger than this
|
||||
const uint32_t MAX_CHUNK_SIZE = (1024 * 1024);
|
||||
|
||||
const uint32_t DOMAIN_SIZE = 4;
|
||||
|
||||
// Parse one stringified range of chunks of the form "n" or "n-m" from a
|
||||
// comma-separated list of chunks. Upon return, 'begin' will point to the
|
||||
// next range of chunks in the list of chunks.
|
||||
static bool
|
||||
ParseChunkRange(nsACString::const_iterator& aBegin,
|
||||
const nsACString::const_iterator& aEnd,
|
||||
uint32_t* aFirst, uint32_t* aLast)
|
||||
{
|
||||
nsACString::const_iterator iter = aBegin;
|
||||
FindCharInReadable(',', iter, aEnd);
|
||||
|
||||
nsAutoCString element(Substring(aBegin, iter));
|
||||
aBegin = iter;
|
||||
if (aBegin != aEnd)
|
||||
aBegin++;
|
||||
|
||||
uint32_t numRead = PR_sscanf(element.get(), "%u-%u", aFirst, aLast);
|
||||
if (numRead == 2) {
|
||||
if (*aFirst > *aLast) {
|
||||
uint32_t tmp = *aFirst;
|
||||
*aFirst = *aLast;
|
||||
*aLast = tmp;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (numRead == 1) {
|
||||
*aLast = *aFirst;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ProtocolParser::ProtocolParser()
|
||||
: mState(PROTOCOL_STATE_CONTROL)
|
||||
, mUpdateStatus(NS_OK)
|
||||
, mUpdateWait(0)
|
||||
, mResetRequested(false)
|
||||
{
|
||||
}
|
||||
|
||||
ProtocolParser::~ProtocolParser()
|
||||
{
|
||||
CleanupUpdates();
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::Init(nsICryptoHash* aHasher)
|
||||
{
|
||||
mCryptoHash = aHasher;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ProtocolParser::SetCurrentTable(const nsACString& aTable)
|
||||
{
|
||||
mTableUpdate = GetTableUpdate(aTable);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::AppendStream(const nsACString& aData)
|
||||
{
|
||||
if (NS_FAILED(mUpdateStatus))
|
||||
return mUpdateStatus;
|
||||
|
||||
nsresult rv;
|
||||
mPending.Append(aData);
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
if (mState == PROTOCOL_STATE_CONTROL) {
|
||||
rv = ProcessControl(&done);
|
||||
} else if (mState == PROTOCOL_STATE_CHUNK) {
|
||||
rv = ProcessChunk(&done);
|
||||
} else {
|
||||
NS_ERROR("Unexpected protocol state");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
mUpdateStatus = rv;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessControl(bool* aDone)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString line;
|
||||
*aDone = true;
|
||||
while (NextLine(line)) {
|
||||
//LOG(("Processing %s\n", line.get()));
|
||||
|
||||
if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) {
|
||||
// Set the table name from the table header line.
|
||||
SetCurrentTable(Substring(line, 2));
|
||||
} else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) {
|
||||
if (PR_sscanf(line.get(), "n:%d", &mUpdateWait) != 1) {
|
||||
LOG(("Error parsing n: '%s' (%d)", line.get(), mUpdateWait));
|
||||
mUpdateWait = 0;
|
||||
}
|
||||
} else if (line.EqualsLiteral("r:pleasereset")) {
|
||||
mResetRequested = true;
|
||||
} else if (StringBeginsWith(line, NS_LITERAL_CSTRING("u:"))) {
|
||||
rv = ProcessForward(line);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (StringBeginsWith(line, NS_LITERAL_CSTRING("a:")) ||
|
||||
StringBeginsWith(line, NS_LITERAL_CSTRING("s:"))) {
|
||||
rv = ProcessChunkControl(line);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
*aDone = false;
|
||||
return NS_OK;
|
||||
} else if (StringBeginsWith(line, NS_LITERAL_CSTRING("ad:")) ||
|
||||
StringBeginsWith(line, NS_LITERAL_CSTRING("sd:"))) {
|
||||
rv = ProcessExpirations(line);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
*aDone = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessExpirations(const nsCString& aLine)
|
||||
{
|
||||
if (!mTableUpdate) {
|
||||
NS_WARNING("Got an expiration without a table.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
const nsCSubstring &list = Substring(aLine, 3);
|
||||
nsACString::const_iterator begin, end;
|
||||
list.BeginReading(begin);
|
||||
list.EndReading(end);
|
||||
while (begin != end) {
|
||||
uint32_t first, last;
|
||||
if (ParseChunkRange(begin, end, &first, &last)) {
|
||||
for (uint32_t num = first; num <= last; num++) {
|
||||
if (aLine[0] == 'a')
|
||||
mTableUpdate->NewAddExpiration(num);
|
||||
else
|
||||
mTableUpdate->NewSubExpiration(num);
|
||||
}
|
||||
} else {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessChunkControl(const nsCString& aLine)
|
||||
{
|
||||
if (!mTableUpdate) {
|
||||
NS_WARNING("Got a chunk before getting a table.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mState = PROTOCOL_STATE_CHUNK;
|
||||
char command;
|
||||
|
||||
mChunkState.Clear();
|
||||
|
||||
if (PR_sscanf(aLine.get(),
|
||||
"%c:%d:%d:%d",
|
||||
&command,
|
||||
&mChunkState.num, &mChunkState.hashSize, &mChunkState.length)
|
||||
!= 4)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mChunkState.length > MAX_CHUNK_SIZE) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!(mChunkState.hashSize == PREFIX_SIZE || mChunkState.hashSize == COMPLETE_SIZE)) {
|
||||
NS_WARNING("Invalid hash size specified in update.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (StringEndsWith(mTableUpdate->TableName(),
|
||||
NS_LITERAL_CSTRING("-shavar")) ||
|
||||
StringEndsWith(mTableUpdate->TableName(),
|
||||
NS_LITERAL_CSTRING("-simple"))) {
|
||||
// Accommodate test tables ending in -simple for now.
|
||||
mChunkState.type = (command == 'a') ? CHUNK_ADD : CHUNK_SUB;
|
||||
} else if (StringEndsWith(mTableUpdate->TableName(),
|
||||
NS_LITERAL_CSTRING("-digest256"))) {
|
||||
LOG(("Processing digest256 data"));
|
||||
mChunkState.type = (command == 'a') ? CHUNK_ADD_DIGEST : CHUNK_SUB_DIGEST;
|
||||
}
|
||||
switch (mChunkState.type) {
|
||||
case CHUNK_ADD:
|
||||
mTableUpdate->NewAddChunk(mChunkState.num);
|
||||
break;
|
||||
case CHUNK_SUB:
|
||||
mTableUpdate->NewSubChunk(mChunkState.num);
|
||||
break;
|
||||
case CHUNK_ADD_DIGEST:
|
||||
mTableUpdate->NewAddChunk(mChunkState.num);
|
||||
break;
|
||||
case CHUNK_SUB_DIGEST:
|
||||
mTableUpdate->NewSubChunk(mChunkState.num);
|
||||
break;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessForward(const nsCString& aLine)
|
||||
{
|
||||
const nsCSubstring &forward = Substring(aLine, 2);
|
||||
return AddForward(forward);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::AddForward(const nsACString& aUrl)
|
||||
{
|
||||
if (!mTableUpdate) {
|
||||
NS_WARNING("Forward without a table name.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
ForwardedUpdate *forward = mForwards.AppendElement();
|
||||
forward->table = mTableUpdate->TableName();
|
||||
forward->url.Assign(aUrl);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessChunk(bool* aDone)
|
||||
{
|
||||
if (!mTableUpdate) {
|
||||
NS_WARNING("Processing chunk without an active table.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mChunkState.num != 0, "Must have a chunk number.");
|
||||
|
||||
if (mPending.Length() < mChunkState.length) {
|
||||
*aDone = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Pull the chunk out of the pending stream data.
|
||||
nsAutoCString chunk;
|
||||
chunk.Assign(Substring(mPending, 0, mChunkState.length));
|
||||
mPending.Cut(0, mChunkState.length);
|
||||
|
||||
*aDone = false;
|
||||
mState = PROTOCOL_STATE_CONTROL;
|
||||
|
||||
//LOG(("Handling a %d-byte chunk", chunk.Length()));
|
||||
if (StringEndsWith(mTableUpdate->TableName(),
|
||||
NS_LITERAL_CSTRING("-shavar"))) {
|
||||
return ProcessShaChunk(chunk);
|
||||
}
|
||||
if (StringEndsWith(mTableUpdate->TableName(),
|
||||
NS_LITERAL_CSTRING("-digest256"))) {
|
||||
return ProcessDigestChunk(chunk);
|
||||
}
|
||||
return ProcessPlaintextChunk(chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a plaintext chunk (currently only used in unit tests).
|
||||
*/
|
||||
nsresult
|
||||
ProtocolParser::ProcessPlaintextChunk(const nsACString& aChunk)
|
||||
{
|
||||
if (!mTableUpdate) {
|
||||
NS_WARNING("Chunk received with no table.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsTArray<nsCString> lines;
|
||||
ParseString(PromiseFlatCString(aChunk), '\n', lines);
|
||||
|
||||
// non-hashed tables need to be hashed
|
||||
for (uint32_t i = 0; i < lines.Length(); i++) {
|
||||
nsCString& line = lines[i];
|
||||
|
||||
if (mChunkState.type == CHUNK_ADD) {
|
||||
if (mChunkState.hashSize == COMPLETE_SIZE) {
|
||||
Completion hash;
|
||||
hash.FromPlaintext(line, mCryptoHash);
|
||||
mTableUpdate->NewAddComplete(mChunkState.num, hash);
|
||||
} else {
|
||||
NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks.");
|
||||
Prefix hash;
|
||||
hash.FromPlaintext(line, mCryptoHash);
|
||||
mTableUpdate->NewAddPrefix(mChunkState.num, hash);
|
||||
}
|
||||
} else {
|
||||
nsCString::const_iterator begin, iter, end;
|
||||
line.BeginReading(begin);
|
||||
line.EndReading(end);
|
||||
iter = begin;
|
||||
uint32_t addChunk;
|
||||
if (!FindCharInReadable(':', iter, end) ||
|
||||
PR_sscanf(lines[i].get(), "%d:", &addChunk) != 1) {
|
||||
NS_WARNING("Received sub chunk without associated add chunk.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
iter++;
|
||||
|
||||
if (mChunkState.hashSize == COMPLETE_SIZE) {
|
||||
Completion hash;
|
||||
hash.FromPlaintext(Substring(iter, end), mCryptoHash);
|
||||
mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
|
||||
} else {
|
||||
NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks.");
|
||||
Prefix hash;
|
||||
hash.FromPlaintext(Substring(iter, end), mCryptoHash);
|
||||
mTableUpdate->NewSubPrefix(addChunk, hash, mChunkState.num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessShaChunk(const nsACString& aChunk)
|
||||
{
|
||||
uint32_t start = 0;
|
||||
while (start < aChunk.Length()) {
|
||||
// First four bytes are the domain key.
|
||||
Prefix domain;
|
||||
domain.Assign(Substring(aChunk, start, DOMAIN_SIZE));
|
||||
start += DOMAIN_SIZE;
|
||||
|
||||
// Then a count of entries.
|
||||
uint8_t numEntries = static_cast<uint8_t>(aChunk[start]);
|
||||
start++;
|
||||
|
||||
nsresult rv;
|
||||
if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == PREFIX_SIZE) {
|
||||
rv = ProcessHostAdd(domain, numEntries, aChunk, &start);
|
||||
} else if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == COMPLETE_SIZE) {
|
||||
rv = ProcessHostAddComplete(numEntries, aChunk, &start);
|
||||
} else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == PREFIX_SIZE) {
|
||||
rv = ProcessHostSub(domain, numEntries, aChunk, &start);
|
||||
} else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == COMPLETE_SIZE) {
|
||||
rv = ProcessHostSubComplete(numEntries, aChunk, &start);
|
||||
} else {
|
||||
NS_WARNING("Unexpected chunk type/hash size!");
|
||||
LOG(("Got an unexpected chunk type/hash size: %s:%d",
|
||||
mChunkState.type == CHUNK_ADD ? "add" : "sub",
|
||||
mChunkState.hashSize));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessDigestChunk(const nsACString& aChunk)
|
||||
{
|
||||
if (mChunkState.type == CHUNK_ADD_DIGEST) {
|
||||
return ProcessDigestAdd(aChunk);
|
||||
}
|
||||
if (mChunkState.type == CHUNK_SUB_DIGEST) {
|
||||
return ProcessDigestSub(aChunk);
|
||||
}
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessDigestAdd(const nsACString& aChunk)
|
||||
{
|
||||
// The ABNF format for add chunks is (HASH)+, where HASH is 32 bytes.
|
||||
MOZ_ASSERT(aChunk.Length() % 32 == 0,
|
||||
"Chunk length in bytes must be divisible by 4");
|
||||
uint32_t start = 0;
|
||||
while (start < aChunk.Length()) {
|
||||
Completion hash;
|
||||
hash.Assign(Substring(aChunk, start, COMPLETE_SIZE));
|
||||
start += COMPLETE_SIZE;
|
||||
mTableUpdate->NewAddComplete(mChunkState.num, hash);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessDigestSub(const nsACString& aChunk)
|
||||
{
|
||||
// The ABNF format for sub chunks is (ADDCHUNKNUM HASH)+, where ADDCHUNKNUM
|
||||
// is a 4 byte chunk number, and HASH is 32 bytes.
|
||||
MOZ_ASSERT(aChunk.Length() % 36 == 0,
|
||||
"Chunk length in bytes must be divisible by 36");
|
||||
uint32_t start = 0;
|
||||
while (start < aChunk.Length()) {
|
||||
// Read ADDCHUNKNUM
|
||||
const nsCSubstring& addChunkStr = Substring(aChunk, start, 4);
|
||||
start += 4;
|
||||
|
||||
uint32_t addChunk;
|
||||
memcpy(&addChunk, addChunkStr.BeginReading(), 4);
|
||||
addChunk = PR_ntohl(addChunk);
|
||||
|
||||
// Read the hash
|
||||
Completion hash;
|
||||
hash.Assign(Substring(aChunk, start, COMPLETE_SIZE));
|
||||
start += COMPLETE_SIZE;
|
||||
|
||||
mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessHostAdd(const Prefix& aDomain, uint8_t aNumEntries,
|
||||
const nsACString& aChunk, uint32_t* aStart)
|
||||
{
|
||||
NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
|
||||
"ProcessHostAdd should only be called for prefix hashes.");
|
||||
|
||||
if (aNumEntries == 0) {
|
||||
mTableUpdate->NewAddPrefix(mChunkState.num, aDomain);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (*aStart + (PREFIX_SIZE * aNumEntries) > aChunk.Length()) {
|
||||
NS_WARNING("Chunk is not long enough to contain the expected entries.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < aNumEntries; i++) {
|
||||
Prefix hash;
|
||||
hash.Assign(Substring(aChunk, *aStart, PREFIX_SIZE));
|
||||
mTableUpdate->NewAddPrefix(mChunkState.num, hash);
|
||||
*aStart += PREFIX_SIZE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessHostSub(const Prefix& aDomain, uint8_t aNumEntries,
|
||||
const nsACString& aChunk, uint32_t *aStart)
|
||||
{
|
||||
NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
|
||||
"ProcessHostSub should only be called for prefix hashes.");
|
||||
|
||||
if (aNumEntries == 0) {
|
||||
if ((*aStart) + 4 > aChunk.Length()) {
|
||||
NS_WARNING("Received a zero-entry sub chunk without an associated add.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
|
||||
*aStart += 4;
|
||||
|
||||
uint32_t addChunk;
|
||||
memcpy(&addChunk, addChunkStr.BeginReading(), 4);
|
||||
addChunk = PR_ntohl(addChunk);
|
||||
|
||||
mTableUpdate->NewSubPrefix(addChunk, aDomain, mChunkState.num);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (*aStart + ((PREFIX_SIZE + 4) * aNumEntries) > aChunk.Length()) {
|
||||
NS_WARNING("Chunk is not long enough to contain the expected entries.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < aNumEntries; i++) {
|
||||
const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
|
||||
*aStart += 4;
|
||||
|
||||
uint32_t addChunk;
|
||||
memcpy(&addChunk, addChunkStr.BeginReading(), 4);
|
||||
addChunk = PR_ntohl(addChunk);
|
||||
|
||||
Prefix prefix;
|
||||
prefix.Assign(Substring(aChunk, *aStart, PREFIX_SIZE));
|
||||
*aStart += PREFIX_SIZE;
|
||||
|
||||
mTableUpdate->NewSubPrefix(addChunk, prefix, mChunkState.num);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessHostAddComplete(uint8_t aNumEntries,
|
||||
const nsACString& aChunk, uint32_t* aStart)
|
||||
{
|
||||
NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE,
|
||||
"ProcessHostAddComplete should only be called for complete hashes.");
|
||||
|
||||
if (aNumEntries == 0) {
|
||||
// this is totally comprehensible.
|
||||
// My sarcasm detector is going off!
|
||||
NS_WARNING("Expected > 0 entries for a 32-byte hash add.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (*aStart + (COMPLETE_SIZE * aNumEntries) > aChunk.Length()) {
|
||||
NS_WARNING("Chunk is not long enough to contain the expected entries.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < aNumEntries; i++) {
|
||||
Completion hash;
|
||||
hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE));
|
||||
mTableUpdate->NewAddComplete(mChunkState.num, hash);
|
||||
*aStart += COMPLETE_SIZE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessHostSubComplete(uint8_t aNumEntries,
|
||||
const nsACString& aChunk, uint32_t* aStart)
|
||||
{
|
||||
NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE,
|
||||
"ProcessHostSubComplete should only be called for complete hashes.");
|
||||
|
||||
if (aNumEntries == 0) {
|
||||
// this is totally comprehensible.
|
||||
NS_WARNING("Expected > 0 entries for a 32-byte hash sub.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (*aStart + ((COMPLETE_SIZE + 4) * aNumEntries) > aChunk.Length()) {
|
||||
NS_WARNING("Chunk is not long enough to contain the expected entries.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < aNumEntries; i++) {
|
||||
Completion hash;
|
||||
hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE));
|
||||
*aStart += COMPLETE_SIZE;
|
||||
|
||||
const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
|
||||
*aStart += 4;
|
||||
|
||||
uint32_t addChunk;
|
||||
memcpy(&addChunk, addChunkStr.BeginReading(), 4);
|
||||
addChunk = PR_ntohl(addChunk);
|
||||
|
||||
mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
ProtocolParser::NextLine(nsACString& aLine)
|
||||
{
|
||||
int32_t newline = mPending.FindChar('\n');
|
||||
if (newline == kNotFound) {
|
||||
return false;
|
||||
}
|
||||
aLine.Assign(Substring(mPending, 0, newline));
|
||||
mPending.Cut(0, newline + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ProtocolParser::CleanupUpdates()
|
||||
{
|
||||
for (uint32_t i = 0; i < mTableUpdates.Length(); i++) {
|
||||
delete mTableUpdates[i];
|
||||
}
|
||||
mTableUpdates.Clear();
|
||||
}
|
||||
|
||||
TableUpdate *
|
||||
ProtocolParser::GetTableUpdate(const nsACString& aTable)
|
||||
{
|
||||
for (uint32_t i = 0; i < mTableUpdates.Length(); i++) {
|
||||
if (aTable.Equals(mTableUpdates[i]->TableName())) {
|
||||
return mTableUpdates[i];
|
||||
}
|
||||
}
|
||||
|
||||
// We free automatically on destruction, ownership of these
|
||||
// updates can be transferred to DBServiceWorker, which passes
|
||||
// them back to Classifier when doing the updates, and that
|
||||
// will free them.
|
||||
TableUpdate *update = new TableUpdate(aTable);
|
||||
mTableUpdates.AppendElement(update);
|
||||
return update;
|
||||
}
|
||||
|
||||
} // namespace safebrowsing
|
||||
} // namespace mozilla
|
||||
@@ -1,119 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef ProtocolParser_h__
|
||||
#define ProtocolParser_h__
|
||||
|
||||
#include "HashStore.h"
|
||||
#include "nsICryptoHMAC.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
/**
|
||||
* Some helpers for parsing the safe
|
||||
*/
|
||||
class ProtocolParser {
|
||||
public:
|
||||
struct ForwardedUpdate {
|
||||
nsCString table;
|
||||
nsCString url;
|
||||
};
|
||||
|
||||
ProtocolParser();
|
||||
~ProtocolParser();
|
||||
|
||||
nsresult Status() const { return mUpdateStatus; }
|
||||
|
||||
nsresult Init(nsICryptoHash* aHasher);
|
||||
|
||||
void SetCurrentTable(const nsACString& aTable);
|
||||
|
||||
nsresult Begin();
|
||||
nsresult AppendStream(const nsACString& aData);
|
||||
|
||||
// Forget the table updates that were created by this pass. It
|
||||
// becomes the caller's responsibility to free them. This is shitty.
|
||||
TableUpdate *GetTableUpdate(const nsACString& aTable);
|
||||
void ForgetTableUpdates() { mTableUpdates.Clear(); }
|
||||
nsTArray<TableUpdate*> &GetTableUpdates() { return mTableUpdates; }
|
||||
|
||||
// Update information.
|
||||
const nsTArray<ForwardedUpdate> &Forwards() const { return mForwards; }
|
||||
int32_t UpdateWait() { return mUpdateWait; }
|
||||
bool ResetRequested() { return mResetRequested; }
|
||||
|
||||
private:
|
||||
nsresult ProcessControl(bool* aDone);
|
||||
nsresult ProcessExpirations(const nsCString& aLine);
|
||||
nsresult ProcessChunkControl(const nsCString& aLine);
|
||||
nsresult ProcessForward(const nsCString& aLine);
|
||||
nsresult AddForward(const nsACString& aUrl);
|
||||
nsresult ProcessChunk(bool* done);
|
||||
// Remove this, it's only used for testing
|
||||
nsresult ProcessPlaintextChunk(const nsACString& aChunk);
|
||||
nsresult ProcessShaChunk(const nsACString& aChunk);
|
||||
nsresult ProcessHostAdd(const Prefix& aDomain, uint8_t aNumEntries,
|
||||
const nsACString& aChunk, uint32_t* aStart);
|
||||
nsresult ProcessHostSub(const Prefix& aDomain, uint8_t aNumEntries,
|
||||
const nsACString& aChunk, uint32_t* aStart);
|
||||
nsresult ProcessHostAddComplete(uint8_t aNumEntries, const nsACString& aChunk,
|
||||
uint32_t *aStart);
|
||||
nsresult ProcessHostSubComplete(uint8_t numEntries, const nsACString& aChunk,
|
||||
uint32_t* start);
|
||||
// Digest chunks are very similar to shavar chunks, except digest chunks
|
||||
// always contain the full hash, so there is no need for chunk data to
|
||||
// contain prefix sizes.
|
||||
nsresult ProcessDigestChunk(const nsACString& aChunk);
|
||||
nsresult ProcessDigestAdd(const nsACString& aChunk);
|
||||
nsresult ProcessDigestSub(const nsACString& aChunk);
|
||||
bool NextLine(nsACString& aLine);
|
||||
|
||||
void CleanupUpdates();
|
||||
|
||||
enum ParserState {
|
||||
PROTOCOL_STATE_CONTROL,
|
||||
PROTOCOL_STATE_CHUNK
|
||||
};
|
||||
ParserState mState;
|
||||
|
||||
enum ChunkType {
|
||||
// Types for shavar tables.
|
||||
CHUNK_ADD,
|
||||
CHUNK_SUB,
|
||||
// Types for digest256 tables. digest256 tables differ in format from
|
||||
// shavar tables since they only contain complete hashes.
|
||||
CHUNK_ADD_DIGEST,
|
||||
CHUNK_SUB_DIGEST
|
||||
};
|
||||
|
||||
struct ChunkState {
|
||||
ChunkType type;
|
||||
uint32_t num;
|
||||
uint32_t hashSize;
|
||||
uint32_t length;
|
||||
void Clear() { num = 0; hashSize = 0; length = 0; }
|
||||
};
|
||||
ChunkState mChunkState;
|
||||
|
||||
nsCOMPtr<nsICryptoHash> mCryptoHash;
|
||||
|
||||
nsresult mUpdateStatus;
|
||||
nsCString mPending;
|
||||
|
||||
uint32_t mUpdateWait;
|
||||
bool mResetRequested;
|
||||
|
||||
nsTArray<ForwardedUpdate> mForwards;
|
||||
// Keep track of updates to apply before passing them to the DBServiceWorkers.
|
||||
nsTArray<TableUpdate*> mTableUpdates;
|
||||
// Updates to apply to the current table being parsed.
|
||||
TableUpdate *mTableUpdate;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,235 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["SafeBrowsing"];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Skip all the ones containining "test", because we never need to ask for
|
||||
// updates for them.
|
||||
function getLists(prefName) {
|
||||
let pref = Services.prefs.getCharPref(prefName);
|
||||
// Splitting an empty string returns [''], we really want an empty array.
|
||||
if (!pref) {
|
||||
return [];
|
||||
}
|
||||
return pref.split(",")
|
||||
.filter(function(value) { return value.indexOf("test-") == -1; })
|
||||
.map(function(value) { return value.trim(); });
|
||||
}
|
||||
|
||||
// These may be a comma-separated lists of tables.
|
||||
const phishingLists = getLists("urlclassifier.phishTable");
|
||||
const malwareLists = getLists("urlclassifier.malwareTable");
|
||||
const downloadBlockLists = getLists("urlclassifier.downloadBlockTable");
|
||||
const downloadAllowLists = getLists("urlclassifier.downloadAllowTable");
|
||||
const trackingProtectionLists = getLists("urlclassifier.trackingTable");
|
||||
|
||||
var debug = false;
|
||||
function log(...stuff) {
|
||||
if (!debug)
|
||||
return;
|
||||
|
||||
var d = new Date();
|
||||
let msg = "SafeBrowsing: " + d.toTimeString() + ": " + stuff.join(" ");
|
||||
Services.console.logStringMessage(msg);
|
||||
dump(msg + "\n");
|
||||
}
|
||||
|
||||
this.SafeBrowsing = {
|
||||
|
||||
init: function() {
|
||||
if (this.initialized) {
|
||||
log("Already initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
Services.prefs.addObserver("browser.safebrowsing", this.readPrefs.bind(this), false);
|
||||
Services.prefs.addObserver("privacy.trackingprotection", this.readPrefs.bind(this), false);
|
||||
this.readPrefs();
|
||||
|
||||
// Register our two types of tables, and add custom Mozilla entries
|
||||
let listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].
|
||||
getService(Ci.nsIUrlListManager);
|
||||
for (let i = 0; i < phishingLists.length; ++i) {
|
||||
listManager.registerTable(phishingLists[i], this.updateURL, this.gethashURL);
|
||||
}
|
||||
for (let i = 0; i < malwareLists.length; ++i) {
|
||||
listManager.registerTable(malwareLists[i], this.updateURL, this.gethashURL);
|
||||
}
|
||||
for (let i = 0; i < downloadBlockLists.length; ++i) {
|
||||
listManager.registerTable(downloadBlockLists[i], this.updateURL, this.gethashURL);
|
||||
}
|
||||
for (let i = 0; i < downloadAllowLists.length; ++i) {
|
||||
listManager.registerTable(downloadAllowLists[i], this.updateURL, this.gethashURL);
|
||||
}
|
||||
for (let i = 0; i < trackingProtectionLists.length; ++i) {
|
||||
listManager.registerTable(trackingProtectionLists[i],
|
||||
this.trackingUpdateURL,
|
||||
this.trackingGethashURL);
|
||||
}
|
||||
this.addMozEntries();
|
||||
|
||||
this.controlUpdateChecking();
|
||||
this.initialized = true;
|
||||
|
||||
log("init() finished");
|
||||
},
|
||||
|
||||
|
||||
initialized: false,
|
||||
phishingEnabled: false,
|
||||
malwareEnabled: false,
|
||||
|
||||
updateURL: null,
|
||||
gethashURL: null,
|
||||
|
||||
reportURL: null,
|
||||
reportGenericURL: null,
|
||||
reportErrorURL: null,
|
||||
reportPhishURL: null,
|
||||
reportMalwareURL: null,
|
||||
reportMalwareErrorURL: null,
|
||||
|
||||
|
||||
getReportURL: function(kind) {
|
||||
return this["report" + kind + "URL"];
|
||||
},
|
||||
|
||||
|
||||
readPrefs: function() {
|
||||
log("reading prefs");
|
||||
|
||||
debug = Services.prefs.getBoolPref("browser.safebrowsing.debug");
|
||||
this.phishingEnabled = Services.prefs.getBoolPref("browser.safebrowsing.enabled");
|
||||
this.malwareEnabled = Services.prefs.getBoolPref("browser.safebrowsing.malware.enabled");
|
||||
this.trackingEnabled = Services.prefs.getBoolPref("privacy.trackingprotection.enabled");
|
||||
this.updateProviderURLs();
|
||||
|
||||
// XXX The listManager backend gets confused if this is called before the
|
||||
// lists are registered. So only call it here when a pref changes, and not
|
||||
// when doing initialization. I expect to refactor this later, so pardon the hack.
|
||||
if (this.initialized) {
|
||||
this.controlUpdateChecking();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
updateProviderURLs: function() {
|
||||
try {
|
||||
var clientID = Services.prefs.getCharPref("browser.safebrowsing.id");
|
||||
} catch(e) {
|
||||
var clientID = Services.appinfo.name;
|
||||
}
|
||||
|
||||
log("initializing safe browsing URLs, client id ", clientID);
|
||||
let basePref = "browser.safebrowsing.";
|
||||
|
||||
// Urls to HTML report pages
|
||||
this.reportURL = Services.urlFormatter.formatURLPref(basePref + "reportURL");
|
||||
this.reportGenericURL = Services.urlFormatter.formatURLPref(basePref + "reportGenericURL");
|
||||
this.reportErrorURL = Services.urlFormatter.formatURLPref(basePref + "reportErrorURL");
|
||||
this.reportPhishURL = Services.urlFormatter.formatURLPref(basePref + "reportPhishURL");
|
||||
this.reportMalwareURL = Services.urlFormatter.formatURLPref(basePref + "reportMalwareURL");
|
||||
this.reportMalwareErrorURL = Services.urlFormatter.formatURLPref(basePref + "reportMalwareErrorURL");
|
||||
|
||||
// Urls used to update DB
|
||||
this.updateURL = Services.urlFormatter.formatURLPref(basePref + "updateURL");
|
||||
this.gethashURL = Services.urlFormatter.formatURLPref(basePref + "gethashURL");
|
||||
|
||||
this.updateURL = this.updateURL.replace("SAFEBROWSING_ID", clientID);
|
||||
this.gethashURL = this.gethashURL.replace("SAFEBROWSING_ID", clientID);
|
||||
this.trackingUpdateURL = Services.urlFormatter.formatURLPref(
|
||||
"browser.trackingprotection.updateURL");
|
||||
this.trackingGethashURL = Services.urlFormatter.formatURLPref(
|
||||
"browser.trackingprotection.gethashURL");
|
||||
},
|
||||
|
||||
controlUpdateChecking: function() {
|
||||
log("phishingEnabled:", this.phishingEnabled, "malwareEnabled:",
|
||||
this.malwareEnabled, "trackingEnabled:", this.trackingEnabled);
|
||||
|
||||
let listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].
|
||||
getService(Ci.nsIUrlListManager);
|
||||
|
||||
for (let i = 0; i < phishingLists.length; ++i) {
|
||||
if (this.phishingEnabled) {
|
||||
listManager.enableUpdate(phishingLists[i]);
|
||||
} else {
|
||||
listManager.disableUpdate(phishingLists[i]);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < malwareLists.length; ++i) {
|
||||
if (this.malwareEnabled) {
|
||||
listManager.enableUpdate(malwareLists[i]);
|
||||
} else {
|
||||
listManager.disableUpdate(malwareLists[i]);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < downloadBlockLists.length; ++i) {
|
||||
if (this.malwareEnabled) {
|
||||
listManager.enableUpdate(downloadBlockLists[i]);
|
||||
} else {
|
||||
listManager.disableUpdate(downloadBlockLists[i]);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < downloadAllowLists.length; ++i) {
|
||||
if (this.malwareEnabled) {
|
||||
listManager.enableUpdate(downloadAllowLists[i]);
|
||||
} else {
|
||||
listManager.disableUpdate(downloadAllowLists[i]);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < trackingProtectionLists.length; ++i) {
|
||||
if (this.trackingEnabled) {
|
||||
listManager.enableUpdate(trackingProtectionLists[i]);
|
||||
} else {
|
||||
listManager.disableUpdate(trackingProtectionLists[i]);
|
||||
}
|
||||
}
|
||||
listManager.maybeToggleUpdateChecking();
|
||||
},
|
||||
|
||||
|
||||
addMozEntries: function() {
|
||||
// Add test entries to the DB.
|
||||
// XXX bug 779008 - this could be done by DB itself?
|
||||
const phishURL = "itisatrap.org/firefox/its-a-trap.html";
|
||||
const malwareURL = "itisatrap.org/firefox/its-an-attack.html";
|
||||
|
||||
let update = "n:1000\ni:test-malware-simple\nad:1\n" +
|
||||
"a:1:32:" + malwareURL.length + "\n" +
|
||||
malwareURL;
|
||||
update += "n:1000\ni:test-phish-simple\nad:1\n" +
|
||||
"a:1:32:" + phishURL.length + "\n" +
|
||||
phishURL;
|
||||
log("addMozEntries:", update);
|
||||
|
||||
let db = Cc["@mozilla.org/url-classifier/dbservice;1"].
|
||||
getService(Ci.nsIUrlClassifierDBService);
|
||||
|
||||
// nsIUrlClassifierUpdateObserver
|
||||
let dummyListener = {
|
||||
updateUrlRequested: function() { },
|
||||
streamFinished: function() { },
|
||||
updateError: function() { },
|
||||
updateSuccess: function() { }
|
||||
};
|
||||
|
||||
try {
|
||||
db.beginUpdate(dummyListener, "test-malware-simple,test-phish-simple", "");
|
||||
db.beginStream("", "");
|
||||
db.updateStream(update);
|
||||
db.finishStream();
|
||||
db.finishUpdate();
|
||||
} catch(ex) {
|
||||
// beginUpdate will throw harmlessly if there's an existing update in progress, ignore failures.
|
||||
log("addMozEntries failed!", ex);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,458 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// This is the only implementation of nsIUrlListManager.
|
||||
// A class that manages lists, namely white and black lists for
|
||||
// phishing or malware protection. The ListManager knows how to fetch,
|
||||
// update, and store lists.
|
||||
//
|
||||
// There is a single listmanager for the whole application.
|
||||
//
|
||||
// TODO more comprehensive update tests, for example add unittest check
|
||||
// that the listmanagers tables are properly written on updates
|
||||
|
||||
// Log only if browser.safebrowsing.debug is true
|
||||
this.log = function log(...stuff) {
|
||||
var prefs_ = new G_Preferences();
|
||||
var debug = prefs_.getPref("browser.safebrowsing.debug");
|
||||
if (!debug) {
|
||||
return;
|
||||
}
|
||||
|
||||
var d = new Date();
|
||||
let msg = "listmanager: " + d.toTimeString() + ": " + stuff.join(" ");
|
||||
Services.console.logStringMessage(msg);
|
||||
dump(msg + "\n");
|
||||
}
|
||||
|
||||
this.QueryAdapter = function QueryAdapter(callback) {
|
||||
this.callback_ = callback;
|
||||
};
|
||||
|
||||
QueryAdapter.prototype.handleResponse = function(value) {
|
||||
this.callback_.handleEvent(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* A ListManager keeps track of black and white lists and knows
|
||||
* how to update them.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
this.PROT_ListManager = function PROT_ListManager() {
|
||||
log("Initializing list manager");
|
||||
this.prefs_ = new G_Preferences();
|
||||
this.updateInterval = this.prefs_.getPref("urlclassifier.updateinterval", 30 * 60) * 1000;
|
||||
|
||||
// A map of tableNames to objects of type
|
||||
// { updateUrl: <updateUrl>, gethashUrl: <gethashUrl> }
|
||||
this.tablesData = {};
|
||||
// A map of updateUrls to maps of tables requiring updates, e.g.
|
||||
// { safebrowsing-update-url: { goog-phish-shavar: true,
|
||||
// goog-malware-shavar: true }
|
||||
this.needsUpdate_ = {};
|
||||
|
||||
this.observerServiceObserver_ = new G_ObserverServiceObserver(
|
||||
'quit-application',
|
||||
BindToObject(this.shutdown_, this),
|
||||
true /*only once*/);
|
||||
|
||||
// A map of updateUrls to single-use G_Alarms. An entry exists if and only if
|
||||
// there is at least one table with updates enabled for that url. G_Alarms
|
||||
// are reset when enabling/disabling updates or on update callbacks (update
|
||||
// success, update failure, download error).
|
||||
this.updateCheckers_ = {};
|
||||
this.requestBackoffs_ = {};
|
||||
this.dbService_ = Cc["@mozilla.org/url-classifier/dbservice;1"]
|
||||
.getService(Ci.nsIUrlClassifierDBService);
|
||||
|
||||
|
||||
this.hashCompleter_ = Cc["@mozilla.org/url-classifier/hashcompleter;1"]
|
||||
.getService(Ci.nsIUrlClassifierHashCompleter);
|
||||
}
|
||||
|
||||
/**
|
||||
* xpcom-shutdown callback
|
||||
* Delete all of our data tables which seem to leak otherwise.
|
||||
*/
|
||||
PROT_ListManager.prototype.shutdown_ = function() {
|
||||
for (var name in this.tablesData) {
|
||||
delete this.tablesData[name];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new table table
|
||||
* @param tableName - the name of the table
|
||||
* @param updateUrl - the url for updating the table
|
||||
* @param gethashUrl - the url for fetching hash completions
|
||||
* @returns true if the table could be created; false otherwise
|
||||
*/
|
||||
PROT_ListManager.prototype.registerTable = function(tableName,
|
||||
updateUrl,
|
||||
gethashUrl) {
|
||||
log("registering " + tableName + " with " + updateUrl);
|
||||
if (!updateUrl) {
|
||||
log("Can't register table " + tableName + " without updateUrl");
|
||||
return false;
|
||||
}
|
||||
this.tablesData[tableName] = {};
|
||||
this.tablesData[tableName].updateUrl = updateUrl;
|
||||
this.tablesData[tableName].gethashUrl = gethashUrl;
|
||||
|
||||
// Keep track of all of our update URLs.
|
||||
if (!this.needsUpdate_[updateUrl]) {
|
||||
this.needsUpdate_[updateUrl] = {};
|
||||
/* Backoff interval should be between 30 and 60 minutes. */
|
||||
var backoffInterval = 30 * 60 * 1000;
|
||||
backoffInterval += Math.floor(Math.random() * (30 * 60 * 1000));
|
||||
|
||||
log("Creating request backoff for " + updateUrl);
|
||||
this.requestBackoffs_[updateUrl] = new RequestBackoff(2 /* max errors */,
|
||||
60*1000 /* retry interval, 1 min */,
|
||||
4 /* num requests */,
|
||||
60*60*1000 /* request time, 60 min */,
|
||||
backoffInterval /* backoff interval, 60 min */,
|
||||
8*60*60*1000 /* max backoff, 8hr */);
|
||||
|
||||
}
|
||||
this.needsUpdate_[updateUrl][tableName] = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PROT_ListManager.prototype.getGethashUrl = function(tableName) {
|
||||
if (this.tablesData[tableName] && this.tablesData[tableName].gethashUrl) {
|
||||
return this.tablesData[tableName].gethashUrl;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable updates for some tables
|
||||
* @param tables - an array of table names that need updating
|
||||
*/
|
||||
PROT_ListManager.prototype.enableUpdate = function(tableName) {
|
||||
var table = this.tablesData[tableName];
|
||||
if (table) {
|
||||
log("Enabling table updates for " + tableName);
|
||||
this.needsUpdate_[table.updateUrl][tableName] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any table associated with the updateUrl requires updates.
|
||||
* @param updateUrl - the updateUrl
|
||||
*/
|
||||
PROT_ListManager.prototype.updatesNeeded_ = function(updateUrl) {
|
||||
let updatesNeeded = false;
|
||||
for (var tableName in this.needsUpdate_[updateUrl]) {
|
||||
if (this.needsUpdate_[updateUrl][tableName]) {
|
||||
updatesNeeded = true;
|
||||
}
|
||||
}
|
||||
return updatesNeeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables updates for some tables
|
||||
* @param tables - an array of table names that no longer need updating
|
||||
*/
|
||||
PROT_ListManager.prototype.disableUpdate = function(tableName) {
|
||||
var table = this.tablesData[tableName];
|
||||
if (table) {
|
||||
log("Disabling table updates for " + tableName);
|
||||
this.needsUpdate_[table.updateUrl][tableName] = false;
|
||||
if (!this.updatesNeeded_(table.updateUrl) &&
|
||||
this.updateCheckers_[table.updateUrl]) {
|
||||
this.updateCheckers_[table.updateUrl].cancel();
|
||||
this.updateCheckers_[table.updateUrl] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we have some tables that need updating.
|
||||
*/
|
||||
PROT_ListManager.prototype.requireTableUpdates = function() {
|
||||
for (var name in this.tablesData) {
|
||||
// Tables that need updating even if other tables don't require it
|
||||
if (this.needsUpdate_[this.tablesData[name].updateUrl][name]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts as a nsIUrlClassifierCallback for getTables.
|
||||
*/
|
||||
PROT_ListManager.prototype.kickoffUpdate_ = function (onDiskTableData)
|
||||
{
|
||||
this.startingUpdate_ = false;
|
||||
var initialUpdateDelay = 3000;
|
||||
|
||||
// If the user has never downloaded tables, do the check now.
|
||||
log("needsUpdate: " + JSON.stringify(this.needsUpdate_, undefined, 2));
|
||||
for (var updateUrl in this.needsUpdate_) {
|
||||
// If we haven't already kicked off updates for this updateUrl, set a
|
||||
// non-repeating timer for it. The timer delay will be reset either on
|
||||
// updateSuccess to this.updateinterval, or backed off on downloadError.
|
||||
// Don't set the updateChecker unless at least one table has updates
|
||||
// enabled.
|
||||
if (this.updatesNeeded_(updateUrl) && !this.updateCheckers_[updateUrl]) {
|
||||
log("Initializing update checker for " + updateUrl);
|
||||
this.updateCheckers_[updateUrl] =
|
||||
new G_Alarm(BindToObject(this.checkForUpdates, this, updateUrl),
|
||||
initialUpdateDelay, false /* repeating */);
|
||||
} else {
|
||||
log("No updates needed or already initialized for " + updateUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PROT_ListManager.prototype.stopUpdateCheckers = function() {
|
||||
log("Stopping updates");
|
||||
for (var updateUrl in this.updateCheckers_) {
|
||||
this.updateCheckers_[updateUrl].cancel();
|
||||
this.updateCheckers_[updateUrl] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we have any tables that require updating. Different
|
||||
* Wardens may call us with new tables that need to be updated.
|
||||
*/
|
||||
PROT_ListManager.prototype.maybeToggleUpdateChecking = function() {
|
||||
// We update tables if we have some tables that want updates. If there
|
||||
// are no tables that want to be updated - we dont need to check anything.
|
||||
if (this.requireTableUpdates()) {
|
||||
log("Starting managing lists");
|
||||
|
||||
// Get the list of existing tables from the DBService before making any
|
||||
// update requests.
|
||||
if (!this.startingUpdate_) {
|
||||
this.startingUpdate_ = true;
|
||||
// check the current state of tables in the database
|
||||
this.dbService_.getTables(BindToObject(this.kickoffUpdate_, this));
|
||||
}
|
||||
} else {
|
||||
log("Stopping managing lists (if currently active)");
|
||||
this.stopUpdateCheckers(); // Cancel pending updates
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an exception free way to look up the data in a table. We
|
||||
* use this because at certain points our tables might not be loaded,
|
||||
* and querying them could throw.
|
||||
*
|
||||
* @param table String Name of the table that we want to consult
|
||||
* @param key Principal being used to lookup the database
|
||||
* @param callback nsIUrlListManagerCallback (ie., Function) given false or the
|
||||
* value in the table corresponding to key. If the table name does not
|
||||
* exist, we return false, too.
|
||||
*/
|
||||
PROT_ListManager.prototype.safeLookup = function(key, callback) {
|
||||
try {
|
||||
log("safeLookup: " + key);
|
||||
var cb = new QueryAdapter(callback);
|
||||
this.dbService_.lookup(key,
|
||||
BindToObject(cb.handleResponse, cb),
|
||||
true);
|
||||
} catch(e) {
|
||||
log("safeLookup masked failure for key " + key + ": " + e);
|
||||
callback.handleEvent("");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates our internal tables from the update server
|
||||
*
|
||||
* @param updateUrl: request updates for tables associated with that url, or
|
||||
* for all tables if the url is empty.
|
||||
*/
|
||||
PROT_ListManager.prototype.checkForUpdates = function(updateUrl) {
|
||||
log("checkForUpdates with " + updateUrl);
|
||||
// See if we've triggered the request backoff logic.
|
||||
if (!updateUrl) {
|
||||
return false;
|
||||
}
|
||||
if (!this.requestBackoffs_[updateUrl] ||
|
||||
!this.requestBackoffs_[updateUrl].canMakeRequest()) {
|
||||
log("Can't make update request");
|
||||
return false;
|
||||
}
|
||||
// Grab the current state of the tables from the database
|
||||
this.dbService_.getTables(BindToObject(this.makeUpdateRequest_, this,
|
||||
updateUrl));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that fires the actual HTTP update request.
|
||||
* First we reset any tables that have disappeared.
|
||||
* @param tableData List of table data already in the database, in the form
|
||||
* tablename;<chunk ranges>\n
|
||||
*/
|
||||
PROT_ListManager.prototype.makeUpdateRequest_ = function(updateUrl, tableData) {
|
||||
log("this.tablesData: " + JSON.stringify(this.tablesData, undefined, 2));
|
||||
log("existing chunks: " + tableData + "\n");
|
||||
// Disallow blank updateUrls
|
||||
if (!updateUrl) {
|
||||
return;
|
||||
}
|
||||
// An object of the form
|
||||
// { tableList: comma-separated list of tables to request,
|
||||
// tableNames: map of tables that need updating,
|
||||
// request: list of tables and existing chunk ranges from tableData
|
||||
// }
|
||||
var streamerMap = { tableList: null, tableNames: {}, request: "" };
|
||||
for (var tableName in this.tablesData) {
|
||||
// Skip tables not matching this update url
|
||||
if (this.tablesData[tableName].updateUrl != updateUrl) {
|
||||
continue;
|
||||
}
|
||||
if (this.needsUpdate_[this.tablesData[tableName].updateUrl][tableName]) {
|
||||
streamerMap.tableNames[tableName] = true;
|
||||
}
|
||||
if (!streamerMap.tableList) {
|
||||
streamerMap.tableList = tableName;
|
||||
} else {
|
||||
streamerMap.tableList += "," + tableName;
|
||||
}
|
||||
}
|
||||
// Build the request. For each table already in the database, include the
|
||||
// chunk data from the database
|
||||
var lines = tableData.split("\n");
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var fields = lines[i].split(";");
|
||||
var name = fields[0];
|
||||
if (streamerMap.tableNames[name]) {
|
||||
streamerMap.request += lines[i] + "\n";
|
||||
delete streamerMap.tableNames[name];
|
||||
}
|
||||
}
|
||||
// For each requested table that didn't have chunk data in the database,
|
||||
// request it fresh
|
||||
for (let tableName in streamerMap.tableNames) {
|
||||
streamerMap.request += tableName + ";\n";
|
||||
}
|
||||
|
||||
log("update request: " + JSON.stringify(streamerMap, undefined, 2) + "\n");
|
||||
|
||||
// Don't send an empty request.
|
||||
if (streamerMap.request.length > 0) {
|
||||
this.makeUpdateRequestForEntry_(updateUrl, streamerMap.tableList,
|
||||
streamerMap.request);
|
||||
} else {
|
||||
// We were disabled between kicking off getTables and now.
|
||||
log("Not sending empty request");
|
||||
}
|
||||
}
|
||||
|
||||
PROT_ListManager.prototype.makeUpdateRequestForEntry_ = function(updateUrl,
|
||||
tableList,
|
||||
request) {
|
||||
log("makeUpdateRequestForEntry_: request " + request +
|
||||
" update: " + updateUrl + " tablelist: " + tableList + "\n");
|
||||
var streamer = Cc["@mozilla.org/url-classifier/streamupdater;1"]
|
||||
.getService(Ci.nsIUrlClassifierStreamUpdater);
|
||||
|
||||
this.requestBackoffs_[updateUrl].noteRequest();
|
||||
|
||||
if (!streamer.downloadUpdates(
|
||||
tableList,
|
||||
request,
|
||||
updateUrl,
|
||||
BindToObject(this.updateSuccess_, this, tableList, updateUrl),
|
||||
BindToObject(this.updateError_, this, tableList, updateUrl),
|
||||
BindToObject(this.downloadError_, this, tableList, updateUrl))) {
|
||||
// Our alarm gets reset in one of the 3 callbacks.
|
||||
log("pending update, queued request until later");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function if the update request succeeded.
|
||||
* @param waitForUpdate String The number of seconds that the client should
|
||||
* wait before requesting again.
|
||||
*/
|
||||
PROT_ListManager.prototype.updateSuccess_ = function(tableList, updateUrl,
|
||||
waitForUpdate) {
|
||||
log("update success for " + tableList + " from " + updateUrl + ": " +
|
||||
waitForUpdate + "\n");
|
||||
var delay;
|
||||
if (waitForUpdate) {
|
||||
delay = parseInt(waitForUpdate, 10);
|
||||
}
|
||||
// As long as the delay is something sane (5 minutes or more), update
|
||||
// our delay time for requesting updates. We always use a non-repeating
|
||||
// timer since the delay is set differently at every callback.
|
||||
if (delay >= (5 * 60)) {
|
||||
log("Waiting " + delay + " seconds");
|
||||
delay = delay * 1000;
|
||||
} else {
|
||||
log("Ignoring delay from server, waiting " + this.updateInterval / 1000);
|
||||
delay = this.updateInterval;
|
||||
}
|
||||
this.updateCheckers_[updateUrl] =
|
||||
new G_Alarm(BindToObject(this.checkForUpdates, this, updateUrl),
|
||||
delay, false);
|
||||
|
||||
// Let the backoff object know that we completed successfully.
|
||||
this.requestBackoffs_[updateUrl].noteServerResponse(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function if the update request succeeded.
|
||||
* @param result String The error code of the failure
|
||||
*/
|
||||
PROT_ListManager.prototype.updateError_ = function(table, updateUrl, result) {
|
||||
log("update error for " + table + " from " + updateUrl + ": " + result + "\n");
|
||||
// There was some trouble applying the updates. Don't try again for at least
|
||||
// updateInterval seconds.
|
||||
this.updateCheckers_[updateUrl] =
|
||||
new G_Alarm(BindToObject(this.checkForUpdates, this, updateUrl),
|
||||
this.updateInterval, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function when the download failed
|
||||
* @param status String http status or an empty string if connection refused.
|
||||
*/
|
||||
PROT_ListManager.prototype.downloadError_ = function(table, updateUrl, status) {
|
||||
log("download error for " + table + ": " + status + "\n");
|
||||
// If status is empty, then we assume that we got an NS_CONNECTION_REFUSED
|
||||
// error. In this case, we treat this is a http 500 error.
|
||||
if (!status) {
|
||||
status = 500;
|
||||
}
|
||||
status = parseInt(status, 10);
|
||||
this.requestBackoffs_[updateUrl].noteServerResponse(status);
|
||||
var delay = this.updateInterval;
|
||||
if (this.requestBackoffs_[updateUrl].isErrorStatus(status)) {
|
||||
// Schedule an update for when our backoff is complete
|
||||
delay = this.requestBackoffs_[updateUrl].nextRequestDelay();
|
||||
} else {
|
||||
log("Got non error status for error callback?!");
|
||||
}
|
||||
this.updateCheckers_[updateUrl] =
|
||||
new G_Alarm(BindToObject(this.checkForUpdates, this, updateUrl),
|
||||
delay, false);
|
||||
|
||||
}
|
||||
|
||||
PROT_ListManager.prototype.QueryInterface = function(iid) {
|
||||
if (iid.equals(Ci.nsISupports) ||
|
||||
iid.equals(Ci.nsIUrlListManager) ||
|
||||
iid.equals(Ci.nsITimerCallback))
|
||||
return this;
|
||||
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
// An Alarm fires a callback after a certain amount of time, or at
|
||||
// regular intervals. It's a convenient replacement for
|
||||
// setTimeout/Interval when you don't want to bind to a specific
|
||||
// window.
|
||||
//
|
||||
// The ConditionalAlarm is an Alarm that cancels itself if its callback
|
||||
// returns a value that type-converts to true.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// function foo() { dump('hi'); };
|
||||
// new G_Alarm(foo, 10*1000); // Fire foo in 10 seconds
|
||||
// new G_Alarm(foo, 10*1000, true /*repeat*/); // Fire foo every 10 seconds
|
||||
// new G_Alarm(foo, 10*1000, true, 7); // Fire foo every 10 seconds
|
||||
// // seven times
|
||||
// new G_ConditionalAlarm(foo, 1000, true); // Fire every sec until foo()==true
|
||||
//
|
||||
// // Fire foo every 10 seconds until foo returns true or until it fires seven
|
||||
// // times, whichever happens first.
|
||||
// new G_ConditionalAlarm(foo, 10*1000, true /*repeating*/, 7);
|
||||
//
|
||||
// TODO: maybe pass an isFinal flag to the callback if they opted to
|
||||
// set maxTimes and this is the last iteration?
|
||||
|
||||
|
||||
/**
|
||||
* Set an alarm to fire after a given amount of time, or at specific
|
||||
* intervals.
|
||||
*
|
||||
* @param callback Function to call when the alarm fires
|
||||
* @param delayMS Number indicating the length of the alarm period in ms
|
||||
* @param opt_repeating Boolean indicating whether this should fire
|
||||
* periodically
|
||||
* @param opt_maxTimes Number indicating a maximum number of times to
|
||||
* repeat (obviously only useful when opt_repeating==true)
|
||||
*/
|
||||
this.G_Alarm =
|
||||
function G_Alarm(callback, delayMS, opt_repeating, opt_maxTimes) {
|
||||
this.debugZone = "alarm";
|
||||
this.callback_ = callback;
|
||||
this.repeating_ = !!opt_repeating;
|
||||
this.timer_ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
var type = opt_repeating ?
|
||||
this.timer_.TYPE_REPEATING_SLACK :
|
||||
this.timer_.TYPE_ONE_SHOT;
|
||||
this.maxTimes_ = opt_maxTimes ? opt_maxTimes : null;
|
||||
this.nTimes_ = 0;
|
||||
|
||||
this.observerServiceObserver_ = new G_ObserverServiceObserver(
|
||||
'xpcom-shutdown',
|
||||
BindToObject(this.cancel, this));
|
||||
|
||||
// Ask the timer to use nsITimerCallback (.notify()) when ready
|
||||
this.timer_.initWithCallback(this, delayMS, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel this timer
|
||||
*/
|
||||
G_Alarm.prototype.cancel = function() {
|
||||
if (!this.timer_) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.timer_.cancel();
|
||||
// Break circular reference created between this.timer_ and the G_Alarm
|
||||
// instance (this)
|
||||
this.timer_ = null;
|
||||
this.callback_ = null;
|
||||
|
||||
// We don't need the shutdown observer anymore
|
||||
this.observerServiceObserver_.unregister();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the timer when it fires
|
||||
*
|
||||
* @param timer Reference to the nsITimer which fired (not currently
|
||||
* passed along)
|
||||
*/
|
||||
G_Alarm.prototype.notify = function(timer) {
|
||||
// fire callback and save results
|
||||
var ret = this.callback_();
|
||||
|
||||
// If they've given us a max number of times to fire, enforce it
|
||||
this.nTimes_++;
|
||||
if (this.repeating_ &&
|
||||
typeof this.maxTimes_ == "number"
|
||||
&& this.nTimes_ >= this.maxTimes_) {
|
||||
this.cancel();
|
||||
} else if (!this.repeating_) {
|
||||
// Clear out the callback closure for TYPE_ONE_SHOT timers
|
||||
this.cancel();
|
||||
}
|
||||
// We don't cancel/cleanup timers that repeat forever until either
|
||||
// xpcom-shutdown occurs or cancel() is called explicitly.
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
G_Alarm.prototype.setDelay = function(delay) {
|
||||
this.timer_.delay = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* XPCOM cruft
|
||||
*/
|
||||
G_Alarm.prototype.QueryInterface = function(iid) {
|
||||
if (iid.equals(Components.interfaces.nsISupports) ||
|
||||
iid.equals(Components.interfaces.nsITimerCallback))
|
||||
return this;
|
||||
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An alarm with the additional property that it cancels itself if its
|
||||
* callback returns true.
|
||||
*
|
||||
* For parameter documentation, see G_Alarm
|
||||
*/
|
||||
this.G_ConditionalAlarm =
|
||||
function G_ConditionalAlarm(callback, delayMS, opt_repeating, opt_maxTimes) {
|
||||
G_Alarm.call(this, callback, delayMS, opt_repeating, opt_maxTimes);
|
||||
this.debugZone = "conditionalalarm";
|
||||
}
|
||||
|
||||
G_ConditionalAlarm.inherits = function(parentCtor) {
|
||||
var tempCtor = function(){};
|
||||
tempCtor.prototype = parentCtor.prototype;
|
||||
this.superClass_ = parentCtor.prototype;
|
||||
this.prototype = new tempCtor();
|
||||
}
|
||||
|
||||
G_ConditionalAlarm.inherits(G_Alarm);
|
||||
|
||||
/**
|
||||
* Invoked by the timer when it fires
|
||||
*
|
||||
* @param timer Reference to the nsITimer which fired (not currently
|
||||
* passed along)
|
||||
*/
|
||||
G_ConditionalAlarm.prototype.notify = function(timer) {
|
||||
// Call G_Alarm::notify
|
||||
var rv = G_Alarm.prototype.notify.call(this, timer);
|
||||
|
||||
if (this.repeating_ && rv) {
|
||||
G_Debug(this, "Callback of a repeating alarm returned true; cancelling.");
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
// A very thin wrapper around nsICryptoHash. It's not strictly
|
||||
// necessary, but makes the code a bit cleaner and gives us the
|
||||
// opportunity to verify that our implementations give the results that
|
||||
// we expect, for example if we have to interoperate with a server.
|
||||
//
|
||||
// The digest* methods reset the state of the hasher, so it's
|
||||
// necessary to call init() explicitly after them.
|
||||
//
|
||||
// Works only in Firefox 1.5+.
|
||||
//
|
||||
// IMPORTANT NOTE: Due to https://bugzilla.mozilla.org/show_bug.cgi?id=321024
|
||||
// you cannot use the cryptohasher before app-startup. The symptom of doing
|
||||
// so is a segfault in NSS.
|
||||
|
||||
/**
|
||||
* Instantiate a new hasher. You must explicitly call init() before use!
|
||||
*/
|
||||
this.G_CryptoHasher =
|
||||
function G_CryptoHasher() {
|
||||
this.debugZone = "cryptohasher";
|
||||
this.hasher_ = null;
|
||||
}
|
||||
|
||||
G_CryptoHasher.algorithms = {
|
||||
MD2: Ci.nsICryptoHash.MD2,
|
||||
MD5: Ci.nsICryptoHash.MD5,
|
||||
SHA1: Ci.nsICryptoHash.SHA1,
|
||||
SHA256: Ci.nsICryptoHash.SHA256,
|
||||
SHA384: Ci.nsICryptoHash.SHA384,
|
||||
SHA512: Ci.nsICryptoHash.SHA512,
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the hasher. This function must be called after every call
|
||||
* to one of the digest* methods.
|
||||
*
|
||||
* @param algorithm Constant from G_CryptoHasher.algorithms specifying the
|
||||
* algorithm this hasher will use
|
||||
*/
|
||||
G_CryptoHasher.prototype.init = function(algorithm) {
|
||||
var validAlgorithm = false;
|
||||
for (var alg in G_CryptoHasher.algorithms)
|
||||
if (algorithm == G_CryptoHasher.algorithms[alg])
|
||||
validAlgorithm = true;
|
||||
|
||||
if (!validAlgorithm)
|
||||
throw new Error("Invalid algorithm: " + algorithm);
|
||||
|
||||
this.hasher_ = Cc["@mozilla.org/security/hash;1"]
|
||||
.createInstance(Ci.nsICryptoHash);
|
||||
this.hasher_.init(algorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the hash's internal state with input given in a string. Can be
|
||||
* called multiple times for incrementeal hash updates.
|
||||
*
|
||||
* @param input String containing data to hash.
|
||||
*/
|
||||
G_CryptoHasher.prototype.updateFromString = function(input) {
|
||||
if (!this.hasher_)
|
||||
throw new Error("You must initialize the hasher first!");
|
||||
|
||||
var stream = Cc['@mozilla.org/io/string-input-stream;1']
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
stream.setData(input, input.length);
|
||||
this.updateFromStream(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the hash's internal state with input given in an array. Can be
|
||||
* called multiple times for incremental hash updates.
|
||||
*
|
||||
* @param input Array containing data to hash.
|
||||
*/
|
||||
G_CryptoHasher.prototype.updateFromArray = function(input) {
|
||||
if (!this.hasher_)
|
||||
throw new Error("You must initialize the hasher first!");
|
||||
|
||||
this.hasher_.update(input, input.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the hash's internal state with input given in a stream. Can be
|
||||
* called multiple times from incremental hash updates.
|
||||
*/
|
||||
G_CryptoHasher.prototype.updateFromStream = function(stream) {
|
||||
if (!this.hasher_)
|
||||
throw new Error("You must initialize the hasher first!");
|
||||
|
||||
if (stream.available())
|
||||
this.hasher_.updateFromStream(stream, stream.available());
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The hash value as a string (sequence of 8-bit values)
|
||||
*/
|
||||
G_CryptoHasher.prototype.digestRaw = function() {
|
||||
var digest = this.hasher_.finish(false /* not b64 encoded */);
|
||||
this.hasher_ = null;
|
||||
return digest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The hash value as a base64-encoded string
|
||||
*/
|
||||
G_CryptoHasher.prototype.digestBase64 = function() {
|
||||
var digest = this.hasher_.finish(true /* b64 encoded */);
|
||||
this.hasher_ = null;
|
||||
return digest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The hash value as a hex-encoded string
|
||||
*/
|
||||
G_CryptoHasher.prototype.digestHex = function() {
|
||||
var raw = this.digestRaw();
|
||||
return this.toHex_(raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a sequence of values to a hex-encoded string. The input is a
|
||||
* a string, so you can stick 16-bit values in each character.
|
||||
*
|
||||
* @param str String to conver to hex. (Often this is just a sequence of
|
||||
* 16-bit values)
|
||||
*
|
||||
* @returns String containing the hex representation of the input
|
||||
*/
|
||||
G_CryptoHasher.prototype.toHex_ = function(str) {
|
||||
var hexchars = '0123456789ABCDEF';
|
||||
var hexrep = new Array(str.length * 2);
|
||||
|
||||
for (var i = 0; i < str.length; ++i) {
|
||||
hexrep[i * 2] = hexchars.charAt((str.charCodeAt(i) >> 4) & 15);
|
||||
hexrep[i * 2 + 1] = hexchars.charAt(str.charCodeAt(i) & 15);
|
||||
}
|
||||
return hexrep.join('');
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Lame unittest function
|
||||
*/
|
||||
this.TEST_G_CryptoHasher = function TEST_G_CryptoHasher() {
|
||||
if (G_GDEBUG) {
|
||||
var z = "cryptohasher UNITTEST";
|
||||
G_debugService.enableZone(z);
|
||||
|
||||
G_Debug(z, "Starting");
|
||||
|
||||
var md5 = function(str) {
|
||||
var hasher = new G_CryptoHasher();
|
||||
hasher.init(G_CryptoHasher.algorithms.MD5);
|
||||
hasher.updateFromString(str);
|
||||
return hasher.digestHex().toLowerCase();
|
||||
};
|
||||
|
||||
// test vectors from: http://www.faqs.org/rfcs/rfc1321.html
|
||||
var vectors = {"": "d41d8cd98f00b204e9800998ecf8427e",
|
||||
"a": "0cc175b9c0f1b6a831c399e269772661",
|
||||
"abc": "900150983cd24fb0d6963f7d28e17f72",
|
||||
"message digest": "f96b697d7cb7938d525a2f31aaf161d0",
|
||||
"abcdefghijklmnopqrstuvwxyz": "c3fcd3d76192e4007dfb496cca67e13b",
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789": "d174ab98d277d9f5a5611c2c9f419d9f",
|
||||
"12345678901234567890123456789012345678901234567890123456789012345678901234567890": "57edf4a22be3c955ac49da2e2107b67a"};
|
||||
|
||||
G_Debug(z, "PASSED");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,843 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
// Generic logging/debugging functionality that:
|
||||
//
|
||||
// (*) when disabled compiles to no-ops at worst (for calls to the service)
|
||||
// and to nothing at best (calls to G_Debug() and similar are compiled
|
||||
// away when you use a jscompiler that strips dead code)
|
||||
//
|
||||
// (*) has dynamically configurable/creatable debugging "zones" enabling
|
||||
// selective logging
|
||||
//
|
||||
// (*) hides its plumbing so that all calls in different zones are uniform,
|
||||
// so you can drop files using this library into other apps that use it
|
||||
// without any configuration
|
||||
//
|
||||
// (*) can be controlled programmatically or via preferences. The
|
||||
// preferences that control the service and its zones are under
|
||||
// the preference branch "safebrowsing-debug-service."
|
||||
//
|
||||
// (*) outputs function call traces when the "loggifier" zone is enabled
|
||||
//
|
||||
// (*) can write output to logfiles so that you can get a call trace
|
||||
// from someone who is having a problem
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var G_GDEBUG = true // Enable this module
|
||||
// var G_debugService = new G_DebugService(); // in global context
|
||||
//
|
||||
// // You can use it with arbitrary primitive first arguement
|
||||
// G_Debug("myzone", "Yo yo yo"); // outputs: [myzone] Yo yo yo\n
|
||||
//
|
||||
// // But it's nice to use it with an object; it will probe for the zone name
|
||||
// function Obj() {
|
||||
// this.debugZone = "someobj";
|
||||
// }
|
||||
// Obj.prototype.foo = function() {
|
||||
// G_Debug(this, "foo called");
|
||||
// }
|
||||
// (new Obj).foo(); // outputs: [someobj] foo called\n
|
||||
//
|
||||
// G_debugService.loggifier.loggify(Obj.prototype); // enable call tracing
|
||||
//
|
||||
// // En/disable specific zones programmatically (you can also use preferences)
|
||||
// G_debugService.enableZone("somezone");
|
||||
// G_debugService.disableZone("someotherzone");
|
||||
// G_debugService.enableAllZones();
|
||||
//
|
||||
// // We also have asserts and errors:
|
||||
// G_Error(this, "Some error occurred"); // will throw
|
||||
// G_Assert(this, (x > 3), "x not greater than three!"); // will throw
|
||||
//
|
||||
// See classes below for more methods.
|
||||
//
|
||||
// TODO add code to set prefs when not found to the default value of a tristate
|
||||
// TODO add error level support
|
||||
// TODO add ability to turn off console output
|
||||
//
|
||||
// -------> TO START DEBUGGING: set G_GDEBUG to true
|
||||
|
||||
// These are the functions code will typically call. Everything is
|
||||
// wrapped in if's so we can compile it away when G_GDEBUG is false.
|
||||
|
||||
|
||||
if (typeof G_GDEBUG == "undefined") {
|
||||
throw new Error("G_GDEBUG constant must be set before loading debug.js");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write out a debugging message.
|
||||
*
|
||||
* @param who The thingy to convert into a zone name corresponding to the
|
||||
* zone to which this message belongs
|
||||
* @param msg Message to output
|
||||
*/
|
||||
this.G_Debug = function G_Debug(who, msg) {
|
||||
if (G_GDEBUG) {
|
||||
G_GetDebugZone(who).debug(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Debugs loudly
|
||||
*/
|
||||
this.G_DebugL = function G_DebugL(who, msg) {
|
||||
if (G_GDEBUG) {
|
||||
var zone = G_GetDebugZone(who);
|
||||
|
||||
if (zone.zoneIsEnabled()) {
|
||||
G_debugService.dump(
|
||||
"\n************************************************************\n");
|
||||
|
||||
G_Debug(who, msg);
|
||||
|
||||
G_debugService.dump(
|
||||
"************************************************************\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out a call tracing message
|
||||
*
|
||||
* @param who The thingy to convert into a zone name corresponding to the
|
||||
* zone to which this message belongs
|
||||
* @param msg Message to output
|
||||
*/
|
||||
this.G_TraceCall = function G_TraceCall(who, msg) {
|
||||
if (G_GDEBUG) {
|
||||
if (G_debugService.callTracingEnabled()) {
|
||||
G_debugService.dump(msg + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out an error (and throw)
|
||||
*
|
||||
* @param who The thingy to convert into a zone name corresponding to the
|
||||
* zone to which this message belongs
|
||||
* @param msg Message to output
|
||||
*/
|
||||
this.G_Error = function G_Error(who, msg) {
|
||||
if (G_GDEBUG) {
|
||||
G_GetDebugZone(who).error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert something as true and signal an error if it's not
|
||||
*
|
||||
* @param who The thingy to convert into a zone name corresponding to the
|
||||
* zone to which this message belongs
|
||||
* @param condition Boolean condition to test
|
||||
* @param msg Message to output
|
||||
*/
|
||||
this.G_Assert = function G_Assert(who, condition, msg) {
|
||||
if (G_GDEBUG) {
|
||||
G_GetDebugZone(who).assert(condition, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that takes input and returns the DebugZone
|
||||
* corresponding to it.
|
||||
*
|
||||
* @param who Arbitrary input that will be converted into a zone name. Most
|
||||
* likely an object that has .debugZone property, or a string.
|
||||
* @returns The DebugZone object corresponding to the input
|
||||
*/
|
||||
this.G_GetDebugZone = function G_GetDebugZone(who) {
|
||||
if (G_GDEBUG) {
|
||||
var zone = "?";
|
||||
|
||||
if (who && who.debugZone) {
|
||||
zone = who.debugZone;
|
||||
} else if (typeof who == "string") {
|
||||
zone = who;
|
||||
}
|
||||
|
||||
return G_debugService.getZone(zone);
|
||||
}
|
||||
}
|
||||
|
||||
// Classes that implement the functionality.
|
||||
|
||||
/**
|
||||
* A debug "zone" is a string derived from arbitrary types (but
|
||||
* typically derived from another string or an object). All debugging
|
||||
* messages using a particular zone can be enabled or disabled
|
||||
* independent of other zones. This enables you to turn on/off logging
|
||||
* of particular objects or modules. This object implements a single
|
||||
* zone and the methods required to use it.
|
||||
*
|
||||
* @constructor
|
||||
* @param service Reference to the DebugService object we use for
|
||||
* registration
|
||||
* @param prefix String indicating the unique prefix we should use
|
||||
* when creating preferences to control this zone
|
||||
* @param zone String indicating the name of the zone
|
||||
*/
|
||||
this.G_DebugZone = function G_DebugZone(service, prefix, zone) {
|
||||
if (G_GDEBUG) {
|
||||
this.debugService_ = service;
|
||||
this.prefix_ = prefix;
|
||||
this.zone_ = zone;
|
||||
this.zoneEnabledPrefName_ = prefix + ".zone." + this.zone_;
|
||||
this.settings_ = new G_DebugSettings();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Boolean indicating if this zone is enabled
|
||||
*/
|
||||
G_DebugZone.prototype.zoneIsEnabled = function() {
|
||||
if (G_GDEBUG) {
|
||||
var explicit = this.settings_.getSetting(this.zoneEnabledPrefName_, null);
|
||||
|
||||
if (explicit !== null) {
|
||||
return explicit;
|
||||
} else {
|
||||
return this.debugService_.allZonesEnabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable this logging zone
|
||||
*/
|
||||
G_DebugZone.prototype.enableZone = function() {
|
||||
if (G_GDEBUG) {
|
||||
this.settings_.setDefault(this.zoneEnabledPrefName_, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable this logging zone
|
||||
*/
|
||||
G_DebugZone.prototype.disableZone = function() {
|
||||
if (G_GDEBUG) {
|
||||
this.settings_.setDefault(this.zoneEnabledPrefName_, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a debugging message to this zone
|
||||
*
|
||||
* @param msg String of message to write
|
||||
*/
|
||||
G_DebugZone.prototype.debug = function(msg) {
|
||||
if (G_GDEBUG) {
|
||||
if (this.zoneIsEnabled()) {
|
||||
this.debugService_.dump("[" + this.zone_ + "] " + msg + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an error to this zone and throw
|
||||
*
|
||||
* @param msg String of error to write
|
||||
*/
|
||||
G_DebugZone.prototype.error = function(msg) {
|
||||
if (G_GDEBUG) {
|
||||
this.debugService_.dump("[" + this.zone_ + "] " + msg + "\n");
|
||||
throw new Error(msg);
|
||||
debugger;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert something as true and error if it is not
|
||||
*
|
||||
* @param condition Boolean condition to test
|
||||
* @param msg String of message to write if is false
|
||||
*/
|
||||
G_DebugZone.prototype.assert = function(condition, msg) {
|
||||
if (G_GDEBUG) {
|
||||
if (condition !== true) {
|
||||
G_Error(this.zone_, "ASSERT FAILED: " + msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The debug service handles auto-registration of zones, namespacing
|
||||
* the zones preferences, and various global settings such as whether
|
||||
* all zones are enabled.
|
||||
*
|
||||
* @constructor
|
||||
* @param opt_prefix Optional string indicating the unique prefix we should
|
||||
* use when creating preferences
|
||||
*/
|
||||
this.G_DebugService = function G_DebugService(opt_prefix) {
|
||||
if (G_GDEBUG) {
|
||||
this.prefix_ = opt_prefix ? opt_prefix : "safebrowsing-debug-service";
|
||||
this.consoleEnabledPrefName_ = this.prefix_ + ".alsologtoconsole";
|
||||
this.allZonesEnabledPrefName_ = this.prefix_ + ".enableallzones";
|
||||
this.callTracingEnabledPrefName_ = this.prefix_ + ".trace-function-calls";
|
||||
this.logFileEnabledPrefName_ = this.prefix_ + ".logfileenabled";
|
||||
this.logFileErrorLevelPrefName_ = this.prefix_ + ".logfile-errorlevel";
|
||||
this.zones_ = {};
|
||||
|
||||
this.loggifier = new G_Loggifier();
|
||||
this.settings_ = new G_DebugSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Error levels for reporting console messages to the log.
|
||||
G_DebugService.ERROR_LEVEL_INFO = "INFO";
|
||||
G_DebugService.ERROR_LEVEL_WARNING = "WARNING";
|
||||
G_DebugService.ERROR_LEVEL_EXCEPTION = "EXCEPTION";
|
||||
|
||||
|
||||
/**
|
||||
* @returns Boolean indicating if we should send messages to the jsconsole
|
||||
*/
|
||||
G_DebugService.prototype.alsoDumpToConsole = function() {
|
||||
if (G_GDEBUG) {
|
||||
return this.settings_.getSetting(this.consoleEnabledPrefName_, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether to log output to a file as well as the console.
|
||||
*/
|
||||
G_DebugService.prototype.logFileIsEnabled = function() {
|
||||
if (G_GDEBUG) {
|
||||
return this.settings_.getSetting(this.logFileEnabledPrefName_, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on file logging. dump() output will also go to the file specified by
|
||||
* setLogFile()
|
||||
*/
|
||||
G_DebugService.prototype.enableLogFile = function() {
|
||||
if (G_GDEBUG) {
|
||||
this.settings_.setDefault(this.logFileEnabledPrefName_, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off file logging
|
||||
*/
|
||||
G_DebugService.prototype.disableLogFile = function() {
|
||||
if (G_GDEBUG) {
|
||||
this.settings_.setDefault(this.logFileEnabledPrefName_, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns an nsIFile instance pointing to the current log file location
|
||||
*/
|
||||
G_DebugService.prototype.getLogFile = function() {
|
||||
if (G_GDEBUG) {
|
||||
return this.logFile_;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new log file location
|
||||
*/
|
||||
G_DebugService.prototype.setLogFile = function(file) {
|
||||
if (G_GDEBUG) {
|
||||
this.logFile_ = file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables sending messages to the jsconsole
|
||||
*/
|
||||
G_DebugService.prototype.enableDumpToConsole = function() {
|
||||
if (G_GDEBUG) {
|
||||
this.settings_.setDefault(this.consoleEnabledPrefName_, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables sending messages to the jsconsole
|
||||
*/
|
||||
G_DebugService.prototype.disableDumpToConsole = function() {
|
||||
if (G_GDEBUG) {
|
||||
this.settings_.setDefault(this.consoleEnabledPrefName_, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param zone Name of the zone to get
|
||||
* @returns The DebugZone object corresopnding to input. If not such
|
||||
* zone exists, a new one is created and returned
|
||||
*/
|
||||
G_DebugService.prototype.getZone = function(zone) {
|
||||
if (G_GDEBUG) {
|
||||
if (!this.zones_[zone])
|
||||
this.zones_[zone] = new G_DebugZone(this, this.prefix_, zone);
|
||||
|
||||
return this.zones_[zone];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param zone Zone to enable debugging for
|
||||
*/
|
||||
G_DebugService.prototype.enableZone = function(zone) {
|
||||
if (G_GDEBUG) {
|
||||
var toEnable = this.getZone(zone);
|
||||
toEnable.enableZone();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param zone Zone to disable debugging for
|
||||
*/
|
||||
G_DebugService.prototype.disableZone = function(zone) {
|
||||
if (G_GDEBUG) {
|
||||
var toDisable = this.getZone(zone);
|
||||
toDisable.disableZone();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Boolean indicating whether debugging is enabled for all zones
|
||||
*/
|
||||
G_DebugService.prototype.allZonesEnabled = function() {
|
||||
if (G_GDEBUG) {
|
||||
return this.settings_.getSetting(this.allZonesEnabledPrefName_, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables all debugging zones
|
||||
*/
|
||||
G_DebugService.prototype.enableAllZones = function() {
|
||||
if (G_GDEBUG) {
|
||||
this.settings_.setDefault(this.allZonesEnabledPrefName_, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables all debugging zones
|
||||
*/
|
||||
G_DebugService.prototype.disableAllZones = function() {
|
||||
if (G_GDEBUG) {
|
||||
this.settings_.setDefault(this.allZonesEnabledPrefName_, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Boolean indicating whether call tracing is enabled
|
||||
*/
|
||||
G_DebugService.prototype.callTracingEnabled = function() {
|
||||
if (G_GDEBUG) {
|
||||
return this.settings_.getSetting(this.callTracingEnabledPrefName_, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables call tracing
|
||||
*/
|
||||
G_DebugService.prototype.enableCallTracing = function() {
|
||||
if (G_GDEBUG) {
|
||||
this.settings_.setDefault(this.callTracingEnabledPrefName_, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables call tracing
|
||||
*/
|
||||
G_DebugService.prototype.disableCallTracing = function() {
|
||||
if (G_GDEBUG) {
|
||||
this.settings_.setDefault(this.callTracingEnabledPrefName_, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum error that will be reported to the log.
|
||||
*/
|
||||
G_DebugService.prototype.getLogFileErrorLevel = function() {
|
||||
if (G_GDEBUG) {
|
||||
var level = this.settings_.getSetting(this.logFileErrorLevelPrefName_,
|
||||
G_DebugService.ERROR_LEVEL_EXCEPTION);
|
||||
|
||||
return level.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum error level that will be reported to the log.
|
||||
*/
|
||||
G_DebugService.prototype.setLogFileErrorLevel = function(level) {
|
||||
if (G_GDEBUG) {
|
||||
// normalize case just to make it slightly easier to not screw up.
|
||||
level = level.toUpperCase();
|
||||
|
||||
if (level != G_DebugService.ERROR_LEVEL_INFO &&
|
||||
level != G_DebugService.ERROR_LEVEL_WARNING &&
|
||||
level != G_DebugService.ERROR_LEVEL_EXCEPTION) {
|
||||
throw new Error("Invalid error level specified: {" + level + "}");
|
||||
}
|
||||
|
||||
this.settings_.setDefault(this.logFileErrorLevelPrefName_, level);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal dump() method
|
||||
*
|
||||
* @param msg String of message to dump
|
||||
*/
|
||||
G_DebugService.prototype.dump = function(msg) {
|
||||
if (G_GDEBUG) {
|
||||
dump(msg);
|
||||
|
||||
if (this.alsoDumpToConsole()) {
|
||||
try {
|
||||
var console = Components.classes['@mozilla.org/consoleservice;1']
|
||||
.getService(Components.interfaces.nsIConsoleService);
|
||||
console.logStringMessage(msg);
|
||||
} catch(e) {
|
||||
dump("G_DebugZone ERROR: COULD NOT DUMP TO CONSOLE\n");
|
||||
}
|
||||
}
|
||||
|
||||
this.maybeDumpToFile(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified message to the log file, if file logging is enabled.
|
||||
*/
|
||||
G_DebugService.prototype.maybeDumpToFile = function(msg) {
|
||||
if (this.logFileIsEnabled() && this.logFile_) {
|
||||
|
||||
/* try to get the correct line end character for this platform */
|
||||
if (!this._LINE_END_CHAR)
|
||||
this._LINE_END_CHAR =
|
||||
Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
|
||||
.OS == "WINNT" ? "\r\n" : "\n";
|
||||
if (this._LINE_END_CHAR != "\n")
|
||||
msg = msg.replace(/\n/g, this._LINE_END_CHAR);
|
||||
|
||||
try {
|
||||
var stream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
stream.init(this.logFile_,
|
||||
0x02 | 0x08 | 0x10 /* PR_WRONLY | PR_CREATE_FILE | PR_APPEND */
|
||||
-1 /* default perms */, 0 /* no special behavior */);
|
||||
stream.write(msg, msg.length);
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements nsIConsoleListener.observe(). Gets called when an error message
|
||||
* gets reported to the console and sends it to the log file as well.
|
||||
*/
|
||||
G_DebugService.prototype.observe = function(consoleMessage) {
|
||||
if (G_GDEBUG) {
|
||||
var errorLevel = this.getLogFileErrorLevel();
|
||||
|
||||
// consoleMessage can be either nsIScriptError or nsIConsoleMessage. The
|
||||
// latter does not have things like line number, etc. So we special case
|
||||
// it first.
|
||||
if (!(consoleMessage instanceof Ci.nsIScriptError)) {
|
||||
// Only report these messages if the error level is INFO.
|
||||
if (errorLevel == G_DebugService.ERROR_LEVEL_INFO) {
|
||||
this.maybeDumpToFile(G_DebugService.ERROR_LEVEL_INFO + ": " +
|
||||
consoleMessage.message + "\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We make a local copy of these fields because writing to it doesn't seem
|
||||
// to work.
|
||||
var flags = consoleMessage.flags;
|
||||
var sourceName = consoleMessage.sourceName;
|
||||
var lineNumber = consoleMessage.lineNumber;
|
||||
|
||||
// Sometimes, a scripterror instance won't have any flags set. We
|
||||
// default to exception.
|
||||
if (!flags) {
|
||||
flags = Ci.nsIScriptError.exceptionFlag;
|
||||
}
|
||||
|
||||
// Default the filename and line number if they aren't set.
|
||||
if (!sourceName) {
|
||||
sourceName = "<unknown>";
|
||||
}
|
||||
|
||||
if (!lineNumber) {
|
||||
lineNumber = "<unknown>";
|
||||
}
|
||||
|
||||
// Report the error in the log file.
|
||||
if (flags & Ci.nsIScriptError.warningFlag) {
|
||||
// Only report warnings if the error level is warning or better.
|
||||
if (errorLevel == G_DebugService.ERROR_LEVEL_WARNING ||
|
||||
errorLevel == G_DebugService.ERROR_LEVEL_INFO) {
|
||||
this.reportScriptError_(consoleMessage.message,
|
||||
sourceName,
|
||||
lineNumber,
|
||||
G_DebugService.ERROR_LEVEL_WARNING);
|
||||
}
|
||||
} else if (flags & Ci.nsIScriptError.exceptionFlag) {
|
||||
// Always report exceptions.
|
||||
this.reportScriptError_(consoleMessage.message,
|
||||
sourceName,
|
||||
lineNumber,
|
||||
G_DebugService.ERROR_LEVEL_EXCEPTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private helper to report an nsIScriptError instance to the log/console.
|
||||
*/
|
||||
G_DebugService.prototype.reportScriptError_ = function(message, sourceName,
|
||||
lineNumber, label) {
|
||||
message = "\n------------------------------------------------------------\n" +
|
||||
label + ": " + message +
|
||||
"\nlocation: " + sourceName + ", " + "line: " + lineNumber +
|
||||
"\n------------------------------------------------------------\n\n";
|
||||
|
||||
dump(message);
|
||||
this.maybeDumpToFile(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A class that instruments methods so they output a call trace,
|
||||
* including the values of their actual parameters and return value.
|
||||
* This code is mostly stolen from Aaron Boodman's original
|
||||
* implementation in clobber utils.
|
||||
*
|
||||
* Note that this class uses the "loggifier" debug zone, so you'll see
|
||||
* a complete call trace when that zone is enabled.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
this.G_Loggifier = function G_Loggifier() {
|
||||
if (G_GDEBUG) {
|
||||
// Careful not to loggify ourselves!
|
||||
this.mark_(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks an object as having been loggified. Loggification is not
|
||||
* idempotent :)
|
||||
*
|
||||
* @param obj Object to be marked
|
||||
*/
|
||||
G_Loggifier.prototype.mark_ = function(obj) {
|
||||
if (G_GDEBUG) {
|
||||
obj.__loggified_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param obj Object to be examined
|
||||
* @returns Boolean indicating if the object has been loggified
|
||||
*/
|
||||
G_Loggifier.prototype.isLoggified = function(obj) {
|
||||
if (G_GDEBUG) {
|
||||
return !!obj.__loggified_;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to extract the class name from the constructor definition.
|
||||
* Assumes the object was created using new.
|
||||
*
|
||||
* @param constructor String containing the definition of a constructor,
|
||||
* for example what you'd get by examining obj.constructor
|
||||
* @returns Name of the constructor/object if it could be found, else "???"
|
||||
*/
|
||||
G_Loggifier.prototype.getFunctionName_ = function(constructor) {
|
||||
if (G_GDEBUG) {
|
||||
return constructor.name || "???";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps all the methods in an object so that call traces are
|
||||
* automatically outputted.
|
||||
*
|
||||
* @param obj Object to loggify. SHOULD BE THE PROTOTYPE OF A USER-DEFINED
|
||||
* object. You can get into trouble if you attempt to
|
||||
* loggify something that isn't, for example the Window.
|
||||
*
|
||||
* Any additional parameters are considered method names which should not be
|
||||
* loggified.
|
||||
*
|
||||
* Usage:
|
||||
* G_debugService.loggifier.loggify(MyClass.prototype,
|
||||
* "firstMethodNotToLog",
|
||||
* "secondMethodNotToLog",
|
||||
* ... etc ...);
|
||||
*/
|
||||
G_Loggifier.prototype.loggify = function(obj) {
|
||||
if (G_GDEBUG) {
|
||||
if (!G_debugService.callTracingEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof window != "undefined" && obj == window ||
|
||||
this.isLoggified(obj)) // Don't go berserk!
|
||||
return;
|
||||
|
||||
var zone = G_GetDebugZone(obj);
|
||||
if (!zone || !zone.zoneIsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.mark_(obj);
|
||||
|
||||
// Helper function returns an instrumented version of
|
||||
// objName.meth, with "this" bound properly. (BTW, because we're
|
||||
// in a conditional here, functions will only be defined as
|
||||
// they're encountered during execution, so declare this helper
|
||||
// before using it.)
|
||||
|
||||
let wrap = function (meth, objName, methName) {
|
||||
return function() {
|
||||
|
||||
// First output the call along with actual parameters
|
||||
var args = new Array(arguments.length);
|
||||
var argsString = "";
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
args[i] = arguments[i];
|
||||
argsString += (i == 0 ? "" : ", ");
|
||||
|
||||
if (typeof args[i] == "function") {
|
||||
argsString += "[function]";
|
||||
} else {
|
||||
argsString += args[i];
|
||||
}
|
||||
}
|
||||
|
||||
G_TraceCall(this, "> " + objName + "." + methName + "(" +
|
||||
argsString + ")");
|
||||
|
||||
// Then run the function, capturing the return value and throws
|
||||
try {
|
||||
var retVal = meth.apply(this, arguments);
|
||||
var reportedRetVal = retVal;
|
||||
|
||||
if (typeof reportedRetVal == "undefined")
|
||||
reportedRetVal = "void";
|
||||
else if (reportedRetVal === "")
|
||||
reportedRetVal = "\"\" (empty string)";
|
||||
} catch (e) {
|
||||
if (e && !e.__logged) {
|
||||
G_TraceCall(this, "Error: " + e.message + ". " +
|
||||
e.fileName + ": " + e.lineNumber);
|
||||
try {
|
||||
e.__logged = true;
|
||||
} catch (e2) {
|
||||
// Sometimes we can't add the __logged flag because it's an
|
||||
// XPC wrapper
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
throw e; // Re-throw!
|
||||
}
|
||||
|
||||
// And spit it out already
|
||||
G_TraceCall(
|
||||
this,
|
||||
"< " + objName + "." + methName + ": " + reportedRetVal);
|
||||
|
||||
return retVal;
|
||||
};
|
||||
};
|
||||
|
||||
var ignoreLookup = {};
|
||||
|
||||
if (arguments.length > 1) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
ignoreLookup[arguments[i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap each method of obj
|
||||
for (var p in obj) {
|
||||
// Work around bug in Firefox. In ffox typeof RegExp is "function",
|
||||
// so make sure this really is a function. Bug as of FFox 1.5b2.
|
||||
if (typeof obj[p] == "function" && obj[p].call && !ignoreLookup[p]) {
|
||||
var objName = this.getFunctionName_(obj.constructor);
|
||||
obj[p] = wrap(obj[p], objName, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple abstraction around debug settings. The thing with debug settings is
|
||||
* that we want to be able to specify a default in the application's startup,
|
||||
* but have that default be overridable by the user via their prefs.
|
||||
*
|
||||
* To generalize this, we package up a dictionary of defaults with the
|
||||
* preferences tree. If a setting isn't in the preferences tree, then we grab it
|
||||
* from the defaults.
|
||||
*/
|
||||
this.G_DebugSettings = function G_DebugSettings() {
|
||||
this.defaults_ = {};
|
||||
this.prefs_ = new G_Preferences();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a settings, optionally defaulting to a given value if it
|
||||
* doesn't exist. If no default is specified, the default is |undefined|.
|
||||
*/
|
||||
G_DebugSettings.prototype.getSetting = function(name, opt_default) {
|
||||
var override = this.prefs_.getPref(name, null);
|
||||
|
||||
if (override !== null) {
|
||||
return override;
|
||||
} else if (typeof this.defaults_[name] != "undefined") {
|
||||
return this.defaults_[name];
|
||||
} else {
|
||||
return opt_default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default value for a setting. If the user doesn't override it with a
|
||||
* preference, this is the value which will be returned by getSetting().
|
||||
*/
|
||||
G_DebugSettings.prototype.setDefault = function(name, val) {
|
||||
this.defaults_[name] = val;
|
||||
}
|
||||
|
||||
var G_debugService = new G_DebugService(); // Instantiate us!
|
||||
|
||||
if (G_GDEBUG) {
|
||||
G_debugService.enableAllZones();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// Stubs for the debugging aids scattered through this component.
|
||||
// They will be expanded if you compile yourself a debug build.
|
||||
|
||||
this.G_Debug = function G_Debug(who, msg) { }
|
||||
this.G_Assert = function G_Assert(who, condition, msg) { }
|
||||
this.G_Error = function G_Error(who, msg) { }
|
||||
this.G_debugService = { __noSuchMethod__: function() { } };
|
||||
|
||||
#endif
|
||||
@@ -1,82 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
/**
|
||||
* lang.js - Some missing JavaScript language features
|
||||
*/
|
||||
|
||||
/**
|
||||
* Partially applies a function to a particular "this object" and zero or
|
||||
* more arguments. The result is a new function with some arguments of the first
|
||||
* function pre-filled and the value of |this| "pre-specified".
|
||||
*
|
||||
* Remaining arguments specified at call-time are appended to the pre-
|
||||
* specified ones.
|
||||
*
|
||||
* Usage:
|
||||
* var barMethBound = BindToObject(myFunction, myObj, "arg1", "arg2");
|
||||
* barMethBound("arg3", "arg4");
|
||||
*
|
||||
* @param fn {string} Reference to the function to be bound
|
||||
*
|
||||
* @param self {object} Specifies the object which |this| should point to
|
||||
* when the function is run. If the value is null or undefined, it will default
|
||||
* to the global object.
|
||||
*
|
||||
* @returns {function} A partially-applied form of the speficied function.
|
||||
*/
|
||||
this.BindToObject = function BindToObject(fn, self, opt_args) {
|
||||
var boundargs = fn.boundArgs_ || [];
|
||||
boundargs = boundargs.concat(Array.slice(arguments, 2, arguments.length));
|
||||
|
||||
if (fn.boundSelf_)
|
||||
self = fn.boundSelf_;
|
||||
if (fn.boundFn_)
|
||||
fn = fn.boundFn_;
|
||||
|
||||
var newfn = function() {
|
||||
// Combine the static args and the new args into one big array
|
||||
var args = boundargs.concat(Array.slice(arguments));
|
||||
return fn.apply(self, args);
|
||||
}
|
||||
|
||||
newfn.boundArgs_ = boundargs;
|
||||
newfn.boundSelf_ = self;
|
||||
newfn.boundFn_ = fn;
|
||||
|
||||
return newfn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit the prototype methods from one constructor into another.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* function ParentClass(a, b) { }
|
||||
* ParentClass.prototype.foo = function(a) { }
|
||||
*
|
||||
* function ChildClass(a, b, c) {
|
||||
* ParentClass.call(this, a, b);
|
||||
* }
|
||||
*
|
||||
* ChildClass.inherits(ParentClass);
|
||||
*
|
||||
* var child = new ChildClass("a", "b", "see");
|
||||
* child.foo(); // works
|
||||
*
|
||||
* In addition, a superclass' implementation of a method can be invoked
|
||||
* as follows:
|
||||
*
|
||||
* ChildClass.prototype.foo = function(a) {
|
||||
* ChildClass.superClass_.foo.call(this, a);
|
||||
* // other code
|
||||
* };
|
||||
*/
|
||||
Function.prototype.inherits = function(parentCtor) {
|
||||
var tempCtor = function(){};
|
||||
tempCtor.prototype = parentCtor.prototype;
|
||||
this.superClass_ = parentCtor.prototype;
|
||||
this.prototype = new tempCtor();
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
// A couple of classes to simplify creating observers.
|
||||
//
|
||||
// // Example1:
|
||||
//
|
||||
// function doSomething() { ... }
|
||||
// var observer = new G_ObserverWrapper(topic, doSomething);
|
||||
// someObj.addObserver(topic, observer);
|
||||
//
|
||||
// // Example2:
|
||||
//
|
||||
// function doSomething() { ... }
|
||||
// new G_ObserverServiceObserver("profile-after-change",
|
||||
// doSomething,
|
||||
// true /* run only once */);
|
||||
|
||||
|
||||
/**
|
||||
* This class abstracts the admittedly simple boilerplate required of
|
||||
* an nsIObserver. It saves you the trouble of implementing the
|
||||
* indirection of your own observe() function.
|
||||
*
|
||||
* @param topic String containing the topic the observer will filter for
|
||||
*
|
||||
* @param observeFunction Reference to the function to call when the
|
||||
* observer fires
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
this.G_ObserverWrapper = function G_ObserverWrapper(topic, observeFunction) {
|
||||
this.debugZone = "observer";
|
||||
this.topic_ = topic;
|
||||
this.observeFunction_ = observeFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* XPCOM
|
||||
*/
|
||||
G_ObserverWrapper.prototype.QueryInterface = function(iid) {
|
||||
if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIObserver))
|
||||
return this;
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the thingy being observed
|
||||
*/
|
||||
G_ObserverWrapper.prototype.observe = function(subject, topic, data) {
|
||||
if (topic == this.topic_)
|
||||
this.observeFunction_(subject, topic, data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This class abstracts the admittedly simple boilerplate required of
|
||||
* observing an observerservice topic. It implements the indirection
|
||||
* required, and automatically registers to hear the topic.
|
||||
*
|
||||
* @param topic String containing the topic the observer will filter for
|
||||
*
|
||||
* @param observeFunction Reference to the function to call when the
|
||||
* observer fires
|
||||
*
|
||||
* @param opt_onlyOnce Boolean indicating if the observer should unregister
|
||||
* after it has fired
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
this.G_ObserverServiceObserver =
|
||||
function G_ObserverServiceObserver(topic, observeFunction, opt_onlyOnce) {
|
||||
this.debugZone = "observerserviceobserver";
|
||||
this.topic_ = topic;
|
||||
this.observeFunction_ = observeFunction;
|
||||
this.onlyOnce_ = !!opt_onlyOnce;
|
||||
|
||||
this.observer_ = new G_ObserverWrapper(this.topic_,
|
||||
BindToObject(this.observe_, this));
|
||||
this.observerService_ = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
this.observerService_.addObserver(this.observer_, this.topic_, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the observer from the observerservice
|
||||
*/
|
||||
G_ObserverServiceObserver.prototype.unregister = function() {
|
||||
this.observerService_.removeObserver(this.observer_, this.topic_);
|
||||
this.observerService_ = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the observerservice
|
||||
*/
|
||||
G_ObserverServiceObserver.prototype.observe_ = function(subject, topic, data) {
|
||||
this.observeFunction_(subject, topic, data);
|
||||
if (this.onlyOnce_)
|
||||
this.unregister();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
this.TEST_G_Observer = function TEST_G_Observer() {
|
||||
if (G_GDEBUG) {
|
||||
|
||||
var z = "observer UNITTEST";
|
||||
G_debugService.enableZone(z);
|
||||
|
||||
G_Debug(z, "Starting");
|
||||
|
||||
var regularObserverRan = 0;
|
||||
var observerServiceObserverRan = 0;
|
||||
|
||||
let regularObserver = function () {
|
||||
regularObserverRan++;
|
||||
};
|
||||
|
||||
let observerServiceObserver = function () {
|
||||
observerServiceObserverRan++;
|
||||
};
|
||||
|
||||
var service = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
var topic = "google-observer-test";
|
||||
|
||||
var o1 = new G_ObserverWrapper(topic, regularObserver);
|
||||
service.addObserver(o1, topic, false);
|
||||
|
||||
new G_ObserverServiceObserver(topic,
|
||||
observerServiceObserver, true /* once */);
|
||||
|
||||
// Notifications happen synchronously, so this is easy
|
||||
service.notifyObservers(null, topic, null);
|
||||
service.notifyObservers(null, topic, null);
|
||||
|
||||
G_Assert(z, regularObserverRan == 2, "Regular observer broken");
|
||||
G_Assert(z, observerServiceObserverRan == 1, "ObsServObs broken");
|
||||
|
||||
service.removeObserver(o1, topic);
|
||||
G_Debug(z, "PASSED");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,276 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
// Class for manipulating preferences. Aside from wrapping the pref
|
||||
// service, useful functionality includes:
|
||||
//
|
||||
// - abstracting prefobserving so that you can observe preferences
|
||||
// without implementing nsIObserver
|
||||
//
|
||||
// - getters that return a default value when the pref doesn't exist
|
||||
// (instead of throwing)
|
||||
//
|
||||
// - get-and-set getters
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var p = new PROT_Preferences();
|
||||
// dump(p.getPref("some-true-pref")); // shows true
|
||||
// dump(p.getPref("no-such-pref", true)); // shows true
|
||||
// dump(p.getPref("no-such-pref", null)); // shows null
|
||||
//
|
||||
// function observe(prefThatChanged) {
|
||||
// dump("Pref changed: " + prefThatChanged);
|
||||
// };
|
||||
//
|
||||
// p.addObserver("somepref", observe);
|
||||
// p.setPref("somepref", true); // dumps
|
||||
// p.removeObserver("somepref", observe);
|
||||
//
|
||||
// TODO: should probably have the prefobserver pass in the new and old
|
||||
// values
|
||||
|
||||
// TODO(tc): Maybe remove this class and just call natively since we're no
|
||||
// longer an extension.
|
||||
|
||||
/**
|
||||
* A class that wraps the preferences service.
|
||||
*
|
||||
* @param opt_startPoint A starting point on the prefs tree to resolve
|
||||
* names passed to setPref and getPref.
|
||||
*
|
||||
* @param opt_useDefaultPranch Set to true to work against the default
|
||||
* preferences tree instead of the profile one.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
this.G_Preferences =
|
||||
function G_Preferences(opt_startPoint, opt_getDefaultBranch) {
|
||||
this.debugZone = "prefs";
|
||||
this.observers_ = {};
|
||||
this.getDefaultBranch_ = !!opt_getDefaultBranch;
|
||||
|
||||
this.startPoint_ = opt_startPoint || null;
|
||||
}
|
||||
|
||||
G_Preferences.setterMap_ = { "string": "setCharPref",
|
||||
"boolean": "setBoolPref",
|
||||
"number": "setIntPref" };
|
||||
|
||||
G_Preferences.getterMap_ = {};
|
||||
G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_STRING] = "getCharPref";
|
||||
G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_BOOL] = "getBoolPref";
|
||||
G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_INT] = "getIntPref";
|
||||
|
||||
G_Preferences.prototype.__defineGetter__('prefs_', function() {
|
||||
var prefs;
|
||||
var prefSvc = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefService);
|
||||
|
||||
if (this.getDefaultBranch_) {
|
||||
prefs = prefSvc.getDefaultBranch(this.startPoint_);
|
||||
} else {
|
||||
prefs = prefSvc.getBranch(this.startPoint_);
|
||||
}
|
||||
|
||||
// QI to prefs in case we want to add observers
|
||||
prefs.QueryInterface(Ci.nsIPrefBranchInternal);
|
||||
return prefs;
|
||||
});
|
||||
|
||||
/**
|
||||
* Stores a key/value in a user preference. Valid types for val are string,
|
||||
* boolean, and number. Complex values are not yet supported (but feel free to
|
||||
* add them!).
|
||||
*/
|
||||
G_Preferences.prototype.setPref = function(key, val) {
|
||||
var datatype = typeof(val);
|
||||
|
||||
if (datatype == "number" && (val % 1 != 0)) {
|
||||
throw new Error("Cannot store non-integer numbers in preferences.");
|
||||
}
|
||||
|
||||
var meth = G_Preferences.setterMap_[datatype];
|
||||
|
||||
if (!meth) {
|
||||
throw new Error("Pref datatype {" + datatype + "} not supported.");
|
||||
}
|
||||
|
||||
return this.prefs_[meth](key, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a user preference. Valid types for the value are the same as for
|
||||
* setPref. If the preference is not found, opt_default will be returned
|
||||
* instead.
|
||||
*/
|
||||
G_Preferences.prototype.getPref = function(key, opt_default) {
|
||||
var type = this.prefs_.getPrefType(key);
|
||||
|
||||
// zero means that the specified pref didn't exist
|
||||
if (type == Ci.nsIPrefBranch.PREF_INVALID) {
|
||||
return opt_default;
|
||||
}
|
||||
|
||||
var meth = G_Preferences.getterMap_[type];
|
||||
|
||||
if (!meth) {
|
||||
throw new Error("Pref datatype {" + type + "} not supported.");
|
||||
}
|
||||
|
||||
// If a pref has been cleared, it will have a valid type but won't
|
||||
// be gettable, so this will throw.
|
||||
try {
|
||||
return this.prefs_[meth](key);
|
||||
} catch(e) {
|
||||
return opt_default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a preference.
|
||||
*
|
||||
* @param which Name of preference to obliterate
|
||||
*/
|
||||
G_Preferences.prototype.clearPref = function(which) {
|
||||
try {
|
||||
// This throws if the pref doesn't exist, which is fine because a
|
||||
// nonexistent pref is cleared
|
||||
this.prefs_.clearUserPref(which);
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an observer for a given pref.
|
||||
*
|
||||
* @param which String containing the pref to listen to
|
||||
* @param callback Function to be called when the pref changes. This
|
||||
* function will receive a single argument, a string
|
||||
* holding the preference name that changed
|
||||
*/
|
||||
G_Preferences.prototype.addObserver = function(which, callback) {
|
||||
// Need to store the observer we create so we can eventually unregister it
|
||||
if (!this.observers_[which])
|
||||
this.observers_[which] = { callbacks: [], observers: [] };
|
||||
|
||||
/* only add an observer if the callback hasn't been registered yet */
|
||||
if (this.observers_[which].callbacks.indexOf(callback) == -1) {
|
||||
var observer = new G_PreferenceObserver(callback);
|
||||
this.observers_[which].callbacks.push(callback);
|
||||
this.observers_[which].observers.push(observer);
|
||||
this.prefs_.addObserver(which, observer, false /* strong reference */);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an observer for a given pref.
|
||||
*
|
||||
* @param which String containing the pref to stop listening to
|
||||
* @param callback Function to remove as an observer
|
||||
*/
|
||||
G_Preferences.prototype.removeObserver = function(which, callback) {
|
||||
var ix = this.observers_[which].callbacks.indexOf(callback);
|
||||
G_Assert(this, ix != -1, "Tried to unregister a nonexistent observer");
|
||||
this.observers_[which].callbacks.splice(ix, 1);
|
||||
var observer = this.observers_[which].observers.splice(ix, 1)[0];
|
||||
this.prefs_.removeObserver(which, observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all preference observers registered through this object.
|
||||
*/
|
||||
G_Preferences.prototype.removeAllObservers = function() {
|
||||
for (var which in this.observers_) {
|
||||
for each (var observer in this.observers_[which].observers) {
|
||||
this.prefs_.removeObserver(which, observer);
|
||||
}
|
||||
}
|
||||
this.observers_ = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that knows how to observe preference changes and
|
||||
* invoke a callback when they do
|
||||
*
|
||||
* @constructor
|
||||
* @param callback Function to call when the preference changes
|
||||
*/
|
||||
this.G_PreferenceObserver =
|
||||
function G_PreferenceObserver(callback) {
|
||||
this.debugZone = "prefobserver";
|
||||
this.callback_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the pref system when a preference changes. Passes the
|
||||
* message along to the callback.
|
||||
*
|
||||
* @param subject The nsIPrefBranch that changed
|
||||
* @param topic String "nsPref:changed" (aka
|
||||
* NS_PREFBRANCH_PREFCHANGE_OBSERVER_ID -- but where does it
|
||||
* live???)
|
||||
* @param data Name of the pref that changed
|
||||
*/
|
||||
G_PreferenceObserver.prototype.observe = function(subject, topic, data) {
|
||||
G_Debug(this, "Observed pref change: " + data);
|
||||
this.callback_(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* XPCOM cruft
|
||||
*
|
||||
* @param iid Interface id of the interface the caller wants
|
||||
*/
|
||||
G_PreferenceObserver.prototype.QueryInterface = function(iid) {
|
||||
if (iid.equals(Ci.nsISupports) ||
|
||||
iid.equals(Ci.nsIObserver) ||
|
||||
iid.equals(Ci.nsISupportsWeakReference))
|
||||
return this;
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// UNITTESTS
|
||||
this.TEST_G_Preferences = function TEST_G_Preferences() {
|
||||
if (G_GDEBUG) {
|
||||
var z = "preferences UNITTEST";
|
||||
G_debugService.enableZone(z);
|
||||
G_Debug(z, "Starting");
|
||||
|
||||
var p = new G_Preferences();
|
||||
|
||||
var testPref = "test-preferences-unittest";
|
||||
var noSuchPref = "test-preferences-unittest-aypabtu";
|
||||
|
||||
// Used to test observing
|
||||
var observeCount = 0;
|
||||
let observe = function (prefChanged) {
|
||||
G_Assert(z, prefChanged == testPref, "observer broken");
|
||||
observeCount++;
|
||||
};
|
||||
|
||||
// Test setting, getting, and observing
|
||||
p.addObserver(testPref, observe);
|
||||
p.setPref(testPref, true);
|
||||
G_Assert(z, p.getPref(testPref), "get or set broken");
|
||||
G_Assert(z, observeCount == 1, "observer adding not working");
|
||||
|
||||
p.removeObserver(testPref, observe);
|
||||
|
||||
p.setPref(testPref, false);
|
||||
G_Assert(z, observeCount == 1, "observer removal not working");
|
||||
G_Assert(z, !p.getPref(testPref), "get broken");
|
||||
|
||||
// Remember to clean up the prefs we've set, and test removing prefs
|
||||
// while we're at it
|
||||
p.clearPref(noSuchPref);
|
||||
G_Assert(z, !p.getPref(noSuchPref, false), "clear broken");
|
||||
|
||||
p.clearPref(testPref);
|
||||
|
||||
G_Debug(z, "PASSED");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,133 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
// A helper class that knows how to parse from and serialize to
|
||||
// protocol4. This is a simple, historical format used by some Google
|
||||
// interfaces, for example the Toolbar (i.e., ancient services).
|
||||
//
|
||||
// Protocol4 consists of a newline-separated sequence of name/value
|
||||
// pairs (strings). Each line consists of the name, the value length,
|
||||
// and the value itself, all separated by colons. Example:
|
||||
//
|
||||
// foo:6:barbaz\n
|
||||
// fritz:33:issickofdynamicallytypedlanguages\n
|
||||
|
||||
|
||||
/**
|
||||
* This class knows how to serialize/deserialize maps to/from their
|
||||
* protocol4 representation.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
this.G_Protocol4Parser = function G_Protocol4Parser() {
|
||||
this.debugZone = "protocol4";
|
||||
|
||||
this.protocol4RegExp_ = new RegExp("([^:]+):\\d+:(.*)$");
|
||||
this.newlineRegExp_ = new RegExp("(\\r)?\\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a map from a protocol4 string. Silently skips invalid lines.
|
||||
*
|
||||
* @param text String holding the protocol4 representation
|
||||
*
|
||||
* @returns Object as an associative array with keys and values
|
||||
* given in text. The empty object is returned if none
|
||||
* are parsed.
|
||||
*/
|
||||
G_Protocol4Parser.prototype.parse = function(text) {
|
||||
|
||||
var response = {};
|
||||
if (!text)
|
||||
return response;
|
||||
|
||||
// Responses are protocol4: (repeated) name:numcontentbytes:content\n
|
||||
var lines = text.split(this.newlineRegExp_);
|
||||
for (var i = 0; i < lines.length; i++)
|
||||
if (this.protocol4RegExp_.exec(lines[i]))
|
||||
response[RegExp.$1] = RegExp.$2;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a protocol4 string from a map (object). Throws an error on
|
||||
* an invalid input.
|
||||
*
|
||||
* @param map Object as an associative array with keys and values
|
||||
* given as strings.
|
||||
*
|
||||
* @returns text String holding the protocol4 representation
|
||||
*/
|
||||
G_Protocol4Parser.prototype.serialize = function(map) {
|
||||
if (typeof map != "object")
|
||||
throw new Error("map must be an object");
|
||||
|
||||
var text = "";
|
||||
for (var key in map) {
|
||||
if (typeof map[key] != "string")
|
||||
throw new Error("Keys and values must be strings");
|
||||
|
||||
text += key + ":" + map[key].length + ":" + map[key] + "\n";
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Cheesey unittests
|
||||
*/
|
||||
this.TEST_G_Protocol4Parser = function TEST_G_Protocol4Parser() {
|
||||
if (G_GDEBUG) {
|
||||
var z = "protocol4 UNITTEST";
|
||||
G_debugService.enableZone(z);
|
||||
|
||||
G_Debug(z, "Starting");
|
||||
|
||||
var p = new G_Protocol4Parser();
|
||||
|
||||
let isEmpty = function (map) {
|
||||
for (var key in map)
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
G_Assert(z, isEmpty(p.parse(null)), "Parsing null broken");
|
||||
G_Assert(z, isEmpty(p.parse("")), "Parsing nothing broken");
|
||||
|
||||
var t = "foo:3:bar";
|
||||
G_Assert(z, p.parse(t)["foo"] === "bar", "Parsing one line broken");
|
||||
|
||||
t = "foo:3:bar\n";
|
||||
G_Assert(z, p.parse(t)["foo"] === "bar", "Parsing line with lf broken");
|
||||
|
||||
t = "foo:3:bar\r\n";
|
||||
G_Assert(z, p.parse(t)["foo"] === "bar", "Parsing with crlf broken");
|
||||
|
||||
|
||||
t = "foo:3:bar\nbar:3:baz\r\nbom:3:yaz\n";
|
||||
G_Assert(z, p.parse(t)["foo"] === "bar", "First in multiline");
|
||||
G_Assert(z, p.parse(t)["bar"] === "baz", "Second in multiline");
|
||||
G_Assert(z, p.parse(t)["bom"] === "yaz", "Third in multiline");
|
||||
G_Assert(z, p.parse(t)[""] === undefined, "Nonexistent in multiline");
|
||||
|
||||
// Test serialization
|
||||
|
||||
var original = {
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"foobar": "baz",
|
||||
"hello there": "how are you?" ,
|
||||
};
|
||||
var deserialized = p.parse(p.serialize(original));
|
||||
for (var key in original)
|
||||
G_Assert(z, original[key] === deserialized[key],
|
||||
"Trouble (de)serializing " + key);
|
||||
|
||||
G_Debug(z, "PASSED");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,137 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
/**
|
||||
* This class helps us batch a series of async calls to the db.
|
||||
* If any of the tokens is in the database, we fire callback with
|
||||
* true as a param. If all the tokens are not in the database,
|
||||
* we fire callback with false as a param.
|
||||
* This is an "Abstract" base class. Subclasses need to supply
|
||||
* the condition_ method.
|
||||
*
|
||||
* @param tokens Array of strings to lookup in the db
|
||||
* @param tableName String name of the table
|
||||
* @param callback Function callback function that takes true if the condition
|
||||
* passes.
|
||||
*/
|
||||
this.MultiQuerier =
|
||||
function MultiQuerier(tokens, tableName, callback) {
|
||||
this.tokens_ = tokens;
|
||||
this.tableName_ = tableName;
|
||||
this.callback_ = callback;
|
||||
this.dbservice_ = Cc["@mozilla.org/url-classifier/dbservice;1"]
|
||||
.getService(Ci.nsIUrlClassifierDBService);
|
||||
// We put the current token in this variable.
|
||||
this.key_ = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the remaining tokens against the db.
|
||||
*/
|
||||
MultiQuerier.prototype.run = function() {
|
||||
if (this.tokens_.length == 0) {
|
||||
this.callback_.handleEvent(false);
|
||||
this.dbservice_ = null;
|
||||
this.callback_ = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.key_ = this.tokens_.pop();
|
||||
G_Debug(this, "Looking up " + this.key_ + " in " + this.tableName_);
|
||||
this.dbservice_.exists(this.tableName_, this.key_,
|
||||
BindToObject(this.result_, this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback from the db. If the returned value passes the this.condition_
|
||||
* test, go ahead and call the main callback.
|
||||
*/
|
||||
MultiQuerier.prototype.result_ = function(value) {
|
||||
if (this.condition_(value)) {
|
||||
this.callback_.handleEvent(true)
|
||||
this.dbservice_ = null;
|
||||
this.callback_ = null;
|
||||
} else {
|
||||
this.run();
|
||||
}
|
||||
}
|
||||
|
||||
// Subclasses must override this.
|
||||
MultiQuerier.prototype.condition_ = function(value) {
|
||||
throw "MultiQuerier is an abstract base class";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Concrete MultiQuerier that stops if the key exists in the db.
|
||||
*/
|
||||
this.ExistsMultiQuerier =
|
||||
function ExistsMultiQuerier(tokens, tableName, callback) {
|
||||
MultiQuerier.call(this, tokens, tableName, callback);
|
||||
this.debugZone = "existsMultiQuerier";
|
||||
}
|
||||
|
||||
ExistsMultiQuerier.inherits = function(parentCtor) {
|
||||
var tempCtor = function(){};
|
||||
tempCtor.prototype = parentCtor.prototype;
|
||||
this.superClass_ = parentCtor.prototype;
|
||||
this.prototype = new tempCtor();
|
||||
}
|
||||
ExistsMultiQuerier.inherits(MultiQuerier);
|
||||
|
||||
ExistsMultiQuerier.prototype.condition_ = function(value) {
|
||||
return value.length > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Concrete MultiQuerier that looks up a key, decrypts it, then
|
||||
* checks the the resulting regular expressions for a match.
|
||||
* @param tokens Array of hosts
|
||||
*/
|
||||
this.EnchashMultiQuerier =
|
||||
function EnchashMultiQuerier(tokens, tableName, callback, url) {
|
||||
MultiQuerier.call(this, tokens, tableName, callback);
|
||||
this.url_ = url;
|
||||
this.enchashDecrypter_ = new PROT_EnchashDecrypter();
|
||||
this.debugZone = "enchashMultiQuerier";
|
||||
}
|
||||
|
||||
EnchashMultiQuerier.inherits = function(parentCtor) {
|
||||
var tempCtor = function(){};
|
||||
tempCtor.prototype = parentCtor.prototype;
|
||||
this.superClass_ = parentCtor.prototype;
|
||||
this.prototype = new tempCtor();
|
||||
}
|
||||
EnchashMultiQuerier.inherits(MultiQuerier);
|
||||
|
||||
EnchashMultiQuerier.prototype.run = function() {
|
||||
if (this.tokens_.length == 0) {
|
||||
this.callback_.handleEvent(false);
|
||||
this.dbservice_ = null;
|
||||
this.callback_ = null;
|
||||
return;
|
||||
}
|
||||
var host = this.tokens_.pop();
|
||||
this.key_ = host;
|
||||
var lookupKey = this.enchashDecrypter_.getLookupKey(host);
|
||||
this.dbservice_.exists(this.tableName_, lookupKey,
|
||||
BindToObject(this.result_, this));
|
||||
}
|
||||
|
||||
EnchashMultiQuerier.prototype.condition_ = function(encryptedValue) {
|
||||
if (encryptedValue.length > 0) {
|
||||
// We have encrypted regular expressions for this host. Let's
|
||||
// decrypt them and see if we have a match.
|
||||
var decrypted = this.enchashDecrypter_.decryptData(encryptedValue,
|
||||
this.key_);
|
||||
var res = this.enchashDecrypter_.parseRegExps(decrypted);
|
||||
for (var j = 0; j < res.length; j++) {
|
||||
if (res[j].test(this.url_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This implements logic for stopping requests if the server starts to return
|
||||
// too many errors. If we get MAX_ERRORS errors in ERROR_PERIOD minutes, we
|
||||
// back off for TIMEOUT_INCREMENT minutes. If we get another error
|
||||
// immediately after we restart, we double the timeout and add
|
||||
// TIMEOUT_INCREMENT minutes, etc.
|
||||
//
|
||||
// This is similar to the logic used by the search suggestion service.
|
||||
|
||||
// HTTP responses that count as an error. We also include any 5xx response
|
||||
// as an error.
|
||||
this.HTTP_FOUND = 302;
|
||||
this.HTTP_SEE_OTHER = 303;
|
||||
this.HTTP_TEMPORARY_REDIRECT = 307;
|
||||
|
||||
/**
|
||||
* @param maxErrors Number of times to request before backing off.
|
||||
* @param retryIncrement Time (ms) for each retry before backing off.
|
||||
* @param maxRequests Number the number of requests needed to trigger backoff
|
||||
* @param requestPeriod Number time (ms) in which maxRequests have to occur to
|
||||
* trigger the backoff behavior (0 to disable maxRequests)
|
||||
* @param timeoutIncrement Number time (ms) the starting timeout period
|
||||
* we double this time for consecutive errors
|
||||
* @param maxTimeout Number time (ms) maximum timeout period
|
||||
*/
|
||||
this.RequestBackoff =
|
||||
function RequestBackoff(maxErrors, retryIncrement,
|
||||
maxRequests, requestPeriod,
|
||||
timeoutIncrement, maxTimeout) {
|
||||
this.MAX_ERRORS_ = maxErrors;
|
||||
this.RETRY_INCREMENT_ = retryIncrement;
|
||||
this.MAX_REQUESTS_ = maxRequests;
|
||||
this.REQUEST_PERIOD_ = requestPeriod;
|
||||
this.TIMEOUT_INCREMENT_ = timeoutIncrement;
|
||||
this.MAX_TIMEOUT_ = maxTimeout;
|
||||
|
||||
// Queue of ints keeping the time of all requests
|
||||
this.requestTimes_ = [];
|
||||
|
||||
this.numErrors_ = 0;
|
||||
this.errorTimeout_ = 0;
|
||||
this.nextRequestTime_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the object for reuse. This deliberately doesn't clear requestTimes_.
|
||||
*/
|
||||
RequestBackoff.prototype.reset = function() {
|
||||
this.numErrors_ = 0;
|
||||
this.errorTimeout_ = 0;
|
||||
this.nextRequestTime_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if we can make a request.
|
||||
*/
|
||||
RequestBackoff.prototype.canMakeRequest = function() {
|
||||
var now = Date.now();
|
||||
if (now < this.nextRequestTime_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (this.requestTimes_.length < this.MAX_REQUESTS_ ||
|
||||
(now - this.requestTimes_[0]) > this.REQUEST_PERIOD_);
|
||||
}
|
||||
|
||||
RequestBackoff.prototype.noteRequest = function() {
|
||||
var now = Date.now();
|
||||
this.requestTimes_.push(now);
|
||||
|
||||
// We only care about keeping track of MAX_REQUESTS
|
||||
if (this.requestTimes_.length > this.MAX_REQUESTS_)
|
||||
this.requestTimes_.shift();
|
||||
}
|
||||
|
||||
RequestBackoff.prototype.nextRequestDelay = function() {
|
||||
return Math.max(0, this.nextRequestTime_ - Date.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify this object of the last server response. If it's an error,
|
||||
*/
|
||||
RequestBackoff.prototype.noteServerResponse = function(status) {
|
||||
if (this.isErrorStatus(status)) {
|
||||
this.numErrors_++;
|
||||
|
||||
if (this.numErrors_ < this.MAX_ERRORS_)
|
||||
this.errorTimeout_ = this.RETRY_INCREMENT_;
|
||||
else if (this.numErrors_ == this.MAX_ERRORS_)
|
||||
this.errorTimeout_ = this.TIMEOUT_INCREMENT_;
|
||||
else
|
||||
this.errorTimeout_ *= 2;
|
||||
|
||||
this.errorTimeout_ = Math.min(this.errorTimeout_, this.MAX_TIMEOUT_);
|
||||
this.nextRequestTime_ = Date.now() + this.errorTimeout_;
|
||||
} else {
|
||||
// Reset error timeout, allow requests to go through.
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We consider 302, 303, 307, 4xx, and 5xx http responses to be errors.
|
||||
* @param status Number http status
|
||||
* @return Boolean true if we consider this http status an error
|
||||
*/
|
||||
RequestBackoff.prototype.isErrorStatus = function(status) {
|
||||
return ((400 <= status && status <= 599) ||
|
||||
HTTP_FOUND == status ||
|
||||
HTTP_SEE_OTHER == status ||
|
||||
HTTP_TEMPORARY_REDIRECT == status);
|
||||
}
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// XXX: This should all be moved into the dbservice class so it happens
|
||||
// in the background thread.
|
||||
|
||||
/**
|
||||
* Abstract base class for a lookup table.
|
||||
* @construction
|
||||
*/
|
||||
this.UrlClassifierTable = function UrlClassifierTable() {
|
||||
this.debugZone = "urlclassifier-table";
|
||||
this.name = '';
|
||||
this.needsUpdate = false;
|
||||
this.enchashDecrypter_ = new PROT_EnchashDecrypter();
|
||||
this.wrappedJSObject = this;
|
||||
}
|
||||
|
||||
UrlClassifierTable.prototype.QueryInterface = function(iid) {
|
||||
if (iid.equals(Components.interfaces.nsISupports) ||
|
||||
iid.equals(Components.interfaces.nsIUrlClassifierTable))
|
||||
return this;
|
||||
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses need to implement this method.
|
||||
*/
|
||||
UrlClassifierTable.prototype.exists = function(url, callback) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Url table implementation
|
||||
this.UrlClassifierTableUrl = function UrlClassifierTableUrl() {
|
||||
UrlClassifierTable.call(this);
|
||||
}
|
||||
|
||||
UrlClassifierTableUrl.inherits = function(parentCtor) {
|
||||
var tempCtor = function(){};
|
||||
tempCtor.prototype = parentCtor.prototype;
|
||||
this.superClass_ = parentCtor.prototype;
|
||||
this.prototype = new tempCtor();
|
||||
}
|
||||
UrlClassifierTableUrl.inherits(UrlClassifierTable);
|
||||
|
||||
/**
|
||||
* Look up a URL in a URL table
|
||||
*/
|
||||
UrlClassifierTableUrl.prototype.exists = function(url, callback) {
|
||||
// nsIUrlClassifierUtils.canonicalizeURL is the old way of canonicalizing a
|
||||
// URL. Unfortunately, it doesn't normalize numeric domains so alternate IP
|
||||
// formats (hex, octal, etc) won't trigger a match.
|
||||
// this.enchashDecrypter_.getCanonicalUrl does the right thing and
|
||||
// normalizes a URL to 4 decimal numbers, but the update server may still be
|
||||
// giving us encoded IP addresses. So to be safe, we check both cases.
|
||||
var urlUtils = Cc["@mozilla.org/url-classifier/utils;1"]
|
||||
.getService(Ci.nsIUrlClassifierUtils);
|
||||
var oldCanonicalized = urlUtils.canonicalizeURL(url);
|
||||
var canonicalized = this.enchashDecrypter_.getCanonicalUrl(url);
|
||||
G_Debug(this, "Looking up: " + url + " (" + oldCanonicalized + " and " +
|
||||
canonicalized + ")");
|
||||
(new ExistsMultiQuerier([oldCanonicalized, canonicalized],
|
||||
this.name,
|
||||
callback)).run();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Domain table implementation
|
||||
|
||||
this.UrlClassifierTableDomain = function UrlClassifierTableDomain() {
|
||||
UrlClassifierTable.call(this);
|
||||
this.debugZone = "urlclassifier-table-domain";
|
||||
this.ioService_ = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
}
|
||||
|
||||
UrlClassifierTableDomain.inherits = function(parentCtor) {
|
||||
var tempCtor = function(){};
|
||||
tempCtor.prototype = parentCtor.prototype;
|
||||
this.superClass_ = parentCtor.prototype;
|
||||
this.prototype = new tempCtor();
|
||||
}
|
||||
UrlClassifierTableDomain.inherits(UrlClassifierTable);
|
||||
|
||||
/**
|
||||
* Look up a URL in a domain table
|
||||
* We also try to lookup domain + first path component (e.g.,
|
||||
* www.mozilla.org/products).
|
||||
*
|
||||
* @returns Boolean true if the url domain is in the table
|
||||
*/
|
||||
UrlClassifierTableDomain.prototype.exists = function(url, callback) {
|
||||
var canonicalized = this.enchashDecrypter_.getCanonicalUrl(url);
|
||||
var urlObj = this.ioService_.newURI(canonicalized, null, null);
|
||||
var host = '';
|
||||
try {
|
||||
host = urlObj.host;
|
||||
} catch (e) { }
|
||||
var hostComponents = host.split(".");
|
||||
|
||||
// Try to get the path of the URL. Pseudo urls (like wyciwyg:) throw
|
||||
// errors when trying to convert to an nsIURL so we wrap in a try/catch
|
||||
// block.
|
||||
var path = ""
|
||||
try {
|
||||
urlObj.QueryInterface(Ci.nsIURL);
|
||||
path = urlObj.filePath;
|
||||
} catch (e) { }
|
||||
|
||||
var pathComponents = path.split("/");
|
||||
|
||||
// We don't have a good way map from hosts to domains, so we instead try
|
||||
// each possibility. Could probably optimize to start at the second dot?
|
||||
var possible = [];
|
||||
for (var i = 0; i < hostComponents.length - 1; i++) {
|
||||
host = hostComponents.slice(i).join(".");
|
||||
possible.push(host);
|
||||
|
||||
// The path starts with a "/", so we are interested in the second path
|
||||
// component if it is available
|
||||
if (pathComponents.length >= 2 && pathComponents[1].length > 0) {
|
||||
host = host + "/" + pathComponents[1];
|
||||
possible.push(host);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the possible domains against the db.
|
||||
(new ExistsMultiQuerier(possible, this.name, callback)).run();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Enchash table implementation
|
||||
|
||||
this.UrlClassifierTableEnchash = function UrlClassifierTableEnchash() {
|
||||
UrlClassifierTable.call(this);
|
||||
this.debugZone = "urlclassifier-table-enchash";
|
||||
}
|
||||
|
||||
UrlClassifierTableEnchash.inherits = function(parentCtor) {
|
||||
var tempCtor = function(){};
|
||||
tempCtor.prototype = parentCtor.prototype;
|
||||
this.superClass_ = parentCtor.prototype;
|
||||
this.prototype = new tempCtor();
|
||||
}
|
||||
UrlClassifierTableEnchash.inherits(UrlClassifierTable);
|
||||
|
||||
/**
|
||||
* Look up a URL in an enchashDB. We try all sub domains (up to MAX_DOTS).
|
||||
*/
|
||||
UrlClassifierTableEnchash.prototype.exists = function(url, callback) {
|
||||
url = this.enchashDecrypter_.getCanonicalUrl(url);
|
||||
var host = this.enchashDecrypter_.getCanonicalHost(url,
|
||||
PROT_EnchashDecrypter.MAX_DOTS);
|
||||
|
||||
var possible = [];
|
||||
for (var i = 0; i < PROT_EnchashDecrypter.MAX_DOTS + 1; i++) {
|
||||
possible.push(host);
|
||||
|
||||
var index = host.indexOf(".");
|
||||
if (index == -1)
|
||||
break;
|
||||
host = host.substring(index + 1);
|
||||
}
|
||||
// Run the possible domains against the db.
|
||||
(new EnchashMultiQuerier(possible, this.name, callback, url)).run();
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
// A class that serializes and deserializes opaque key/value string to
|
||||
// string maps to/from maps (trtables). It knows how to create
|
||||
// trtables from the serialized format, so it also understands
|
||||
// meta-information like the name of the table and the table's
|
||||
// version. See docs for the protocol description.
|
||||
//
|
||||
// TODO: wireformatreader: if you have multiple updates for one table
|
||||
// in a call to deserialize, the later ones will be merged
|
||||
// (all but the last will be ignored). To fix, merge instead
|
||||
// of replace when you have an existing table, and only do so once.
|
||||
// TODO must have blank line between successive types -- problem?
|
||||
// TODO doesn't tolerate blank lines very well
|
||||
//
|
||||
// Maybe: These classes could use a LOT more cleanup, but it's not a
|
||||
// priority at the moment. For example, the tablesData/Known
|
||||
// maps should be combined into a single object, the parser
|
||||
// for a given type should be separate from the version info,
|
||||
// and there should be synchronous interfaces for testing.
|
||||
|
||||
|
||||
/**
|
||||
* A class that knows how to serialize and deserialize meta-information.
|
||||
* This meta information is the table name and version number, and
|
||||
* in its serialized form looks like the first line below:
|
||||
*
|
||||
* [name-of-table X.Y update?]
|
||||
* ...key/value pairs to add or delete follow...
|
||||
* <blank line ends the table>
|
||||
*
|
||||
* The X.Y is the version number and the optional "update" token means
|
||||
* that the table is a differential from the curent table the extension
|
||||
* has. Its absence means that this is a full, new table.
|
||||
*/
|
||||
this.PROT_VersionParser =
|
||||
function PROT_VersionParser(type, opt_major, opt_minor, opt_requireMac) {
|
||||
this.debugZone = "versionparser";
|
||||
this.type = type;
|
||||
this.major = 0;
|
||||
this.minor = 0;
|
||||
|
||||
this.badHeader = false;
|
||||
|
||||
// Should the wireformatreader compute a mac?
|
||||
this.mac = false;
|
||||
this.macval = "";
|
||||
this.macFailed = false;
|
||||
this.requireMac = !!opt_requireMac;
|
||||
|
||||
this.update = false;
|
||||
this.needsUpdate = false; // used by ListManager to determine update policy
|
||||
// Used by ListerManager to see if we have read data for this table from
|
||||
// disk. Once we read a table from disk, we are not going to do so again
|
||||
// but instead update remotely if necessary.
|
||||
this.didRead = false;
|
||||
if (opt_major)
|
||||
this.major = parseInt(opt_major);
|
||||
if (opt_minor)
|
||||
this.minor = parseInt(opt_minor);
|
||||
}
|
||||
|
||||
/** Import the version information from another VersionParser
|
||||
* @params version a version parser object
|
||||
*/
|
||||
PROT_VersionParser.prototype.ImportVersion = function(version) {
|
||||
this.major = version.major;
|
||||
this.minor = version.minor;
|
||||
|
||||
this.mac = version.mac;
|
||||
this.macFailed = version.macFailed;
|
||||
this.macval = version.macval;
|
||||
// Don't set requireMac, since we create vparsers from scratch and doesn't
|
||||
// know about it
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string like [goog-white-black 1.1] from internal information
|
||||
*
|
||||
* @returns String
|
||||
*/
|
||||
PROT_VersionParser.prototype.toString = function() {
|
||||
var s = "[" + this.type + " " + this.major + "." + this.minor + "]";
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string like 1.123 with the version number. This is the
|
||||
* format we store in prefs.
|
||||
* @return String
|
||||
*/
|
||||
PROT_VersionParser.prototype.versionString = function() {
|
||||
return this.major + "." + this.minor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string like 1:1 from internal information used for
|
||||
* fetching updates from the server. Called by the listmanager.
|
||||
*
|
||||
* @returns String
|
||||
*/
|
||||
PROT_VersionParser.prototype.toUrl = function() {
|
||||
return this.major + ":" + this.minor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the old format, [type major.minor [update]]
|
||||
*
|
||||
* @returns true if the string could be parsed, false otherwise
|
||||
*/
|
||||
PROT_VersionParser.prototype.processOldFormat_ = function(line) {
|
||||
if (line[0] != '[' || line.slice(-1) != ']')
|
||||
return false;
|
||||
|
||||
var description = line.slice(1, -1);
|
||||
|
||||
// Get the type name and version number of this table
|
||||
var tokens = description.split(" ");
|
||||
this.type = tokens[0];
|
||||
var majorminor = tokens[1].split(".");
|
||||
this.major = parseInt(majorminor[0]);
|
||||
this.minor = parseInt(majorminor[1]);
|
||||
if (isNaN(this.major) || isNaN(this.minor))
|
||||
return false;
|
||||
|
||||
if (tokens.length >= 3) {
|
||||
this.update = tokens[2] == "update";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a string like [name-of-table 1.1 [update]][mac=MAC] and figures out the
|
||||
* type and corresponding version numbers.
|
||||
* @returns true if the string could be parsed, false otherwise
|
||||
*/
|
||||
PROT_VersionParser.prototype.fromString = function(line) {
|
||||
G_Debug(this, "Calling fromString with line: " + line);
|
||||
if (line[0] != '[' || line.slice(-1) != ']')
|
||||
return false;
|
||||
|
||||
// There could be two [][], so take care of it
|
||||
var secondBracket = line.indexOf('[', 1);
|
||||
var firstPart = null;
|
||||
var secondPart = null;
|
||||
|
||||
if (secondBracket != -1) {
|
||||
firstPart = line.substring(0, secondBracket);
|
||||
secondPart = line.substring(secondBracket);
|
||||
G_Debug(this, "First part: " + firstPart + " Second part: " + secondPart);
|
||||
} else {
|
||||
firstPart = line;
|
||||
G_Debug(this, "Old format: " + firstPart);
|
||||
}
|
||||
|
||||
if (!this.processOldFormat_(firstPart))
|
||||
return false;
|
||||
|
||||
if (secondPart && !this.processOptTokens_(secondPart))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process optional tokens
|
||||
*
|
||||
* @param line A string [token1=val1 token2=val2...]
|
||||
* @returns true if the string could be parsed, false otherwise
|
||||
*/
|
||||
PROT_VersionParser.prototype.processOptTokens_ = function(line) {
|
||||
if (line[0] != '[' || line.slice(-1) != ']')
|
||||
return false;
|
||||
var description = line.slice(1, -1);
|
||||
// Get the type name and version number of this table
|
||||
var tokens = description.split(" ");
|
||||
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
G_Debug(this, "Processing optional token: " + tokens[i]);
|
||||
var tokenparts = tokens[i].split("=");
|
||||
switch(tokenparts[0]){
|
||||
case "mac":
|
||||
this.mac = true;
|
||||
if (tokenparts.length < 2) {
|
||||
G_Debug(this, "Found mac flag but not mac value!");
|
||||
return false;
|
||||
}
|
||||
// The mac value may have "=" in it, so we can't just use tokenparts[1].
|
||||
// Instead, just take the rest of tokens[i] after the first "="
|
||||
this.macval = tokens[i].substr(tokens[i].indexOf("=")+1);
|
||||
break;
|
||||
default:
|
||||
G_Debug(this, "Found unrecognized token: " + tokenparts[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
this.TEST_PROT_WireFormat = function TEST_PROT_WireFormat() {
|
||||
if (G_GDEBUG) {
|
||||
var z = "versionparser UNITTEST";
|
||||
G_Debug(z, "Starting");
|
||||
|
||||
var vp = new PROT_VersionParser("dummy");
|
||||
G_Assert(z, vp.fromString("[foo-bar-url 1.234]"),
|
||||
"failed to parse old format");
|
||||
G_Assert(z, "foo-bar-url" == vp.type, "failed to parse type");
|
||||
G_Assert(z, "1" == vp.major, "failed to parse major");
|
||||
G_Assert(z, "234" == vp.minor, "failed to parse minor");
|
||||
|
||||
vp = new PROT_VersionParser("dummy");
|
||||
G_Assert(z, vp.fromString("[foo-bar-url 1.234][mac=567]"),
|
||||
"failed to parse new format");
|
||||
G_Assert(z, "foo-bar-url" == vp.type, "failed to parse type");
|
||||
G_Assert(z, "1" == vp.major, "failed to parse major");
|
||||
G_Assert(z, "234" == vp.minor, "failed to parse minor");
|
||||
G_Assert(z, true == vp.mac, "failed to parse mac");
|
||||
G_Assert(z, "567" == vp.macval, "failed to parse macval");
|
||||
|
||||
G_Debug(z, "PASSED");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,122 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// A simple class that encapsulates a request. You'll notice the
|
||||
// style here is different from the rest of the extension; that's
|
||||
// because this was re-used from really old code we had. At some
|
||||
// point it might be nice to replace this with something better
|
||||
// (e.g., something that has explicit onerror handler, ability
|
||||
// to set headers, and so on).
|
||||
|
||||
/**
|
||||
* Because we might be in a component, we can't just assume that
|
||||
* XMLHttpRequest exists. So we use this tiny factory function to wrap the
|
||||
* XPCOM version.
|
||||
*
|
||||
* @return XMLHttpRequest object
|
||||
*/
|
||||
this.PROT_NewXMLHttpRequest = function PROT_NewXMLHttpRequest() {
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
// Need the following so we get onerror/load/progresschange
|
||||
request.QueryInterface(Ci.nsIJSXMLHttpRequest);
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper class that does HTTP GETs and calls back a function with
|
||||
* the content it receives. Asynchronous, so uses a closure for the
|
||||
* callback.
|
||||
*
|
||||
* Note, that XMLFetcher is only used for SafeBrowsing, therefore
|
||||
* we inherit from nsILoadContext, so we can use the callbacks on the
|
||||
* channel to separate the safebrowsing cookie based on a reserved
|
||||
* appId.
|
||||
* @constructor
|
||||
*/
|
||||
this.PROT_XMLFetcher = function PROT_XMLFetcher() {
|
||||
this.debugZone = "xmlfetcher";
|
||||
this._request = PROT_NewXMLHttpRequest();
|
||||
// implements nsILoadContext
|
||||
this.appId = Ci.nsIScriptSecurityManager.SAFEBROWSING_APP_ID;
|
||||
this.isInBrowserElement = false;
|
||||
this.usePrivateBrowsing = false;
|
||||
this.isContent = false;
|
||||
}
|
||||
|
||||
PROT_XMLFetcher.prototype = {
|
||||
/**
|
||||
* Function that will be called back upon fetch completion.
|
||||
*/
|
||||
_callback: null,
|
||||
|
||||
|
||||
/**
|
||||
* Fetches some content.
|
||||
*
|
||||
* @param page URL to fetch
|
||||
* @param callback Function to call back when complete.
|
||||
*/
|
||||
get: function(page, callback) {
|
||||
this._request.abort(); // abort() is asynchronous, so
|
||||
this._request = PROT_NewXMLHttpRequest();
|
||||
this._callback = callback;
|
||||
var asynchronous = true;
|
||||
this._request.open("GET", page, asynchronous);
|
||||
this._request.channel.notificationCallbacks = this;
|
||||
|
||||
// Create a closure
|
||||
var self = this;
|
||||
this._request.addEventListener("readystatechange", function() {
|
||||
self.readyStateChange(self);
|
||||
}, false);
|
||||
|
||||
this._request.send(null);
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this._request.abort();
|
||||
this._request = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called periodically by the request to indicate some state change. 4
|
||||
* means content has been received.
|
||||
*/
|
||||
readyStateChange: function(fetcher) {
|
||||
if (fetcher._request.readyState != 4)
|
||||
return;
|
||||
|
||||
// If the request fails, on trunk we get status set to
|
||||
// NS_ERROR_NOT_AVAILABLE. On 1.8.1 branch we get an exception
|
||||
// forwarded from nsIHttpChannel::GetResponseStatus. To be consistent
|
||||
// between branch and trunk, we send back NS_ERROR_NOT_AVAILABLE for
|
||||
// http failures.
|
||||
var responseText = null;
|
||||
var status = Components.results.NS_ERROR_NOT_AVAILABLE;
|
||||
try {
|
||||
G_Debug(this, "xml fetch status code: \"" +
|
||||
fetcher._request.status + "\"");
|
||||
status = fetcher._request.status;
|
||||
responseText = fetcher._request.responseText;
|
||||
} catch(e) {
|
||||
G_Debug(this, "Caught exception trying to read xmlhttprequest " +
|
||||
"status/response.");
|
||||
G_Debug(this, e);
|
||||
}
|
||||
if (fetcher._callback)
|
||||
fetcher._callback(responseText, status);
|
||||
},
|
||||
|
||||
// nsIInterfaceRequestor
|
||||
getInterface: function(iid) {
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIInterfaceRequestor,
|
||||
Ci.nsISupports,
|
||||
Ci.nsILoadContext])
|
||||
};
|
||||
@@ -1,74 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
TEST_DIRS += ['tests']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIUrlClassifierDBService.idl',
|
||||
'nsIUrlClassifierHashCompleter.idl',
|
||||
'nsIUrlClassifierPrefixSet.idl',
|
||||
'nsIUrlClassifierStreamUpdater.idl',
|
||||
'nsIUrlClassifierUtils.idl',
|
||||
'nsIUrlListManager.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'url-classifier'
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ChunkSet.cpp',
|
||||
'Classifier.cpp',
|
||||
'LookupCache.cpp',
|
||||
'nsCheckSummedOutputStream.cpp',
|
||||
'nsUrlClassifierDBService.cpp',
|
||||
'nsUrlClassifierProxies.cpp',
|
||||
'nsUrlClassifierUtils.cpp',
|
||||
'ProtocolParser.cpp',
|
||||
]
|
||||
|
||||
# define conflicting LOG() macros
|
||||
SOURCES += [
|
||||
'nsUrlClassifierPrefixSet.cpp',
|
||||
'nsUrlClassifierStreamUpdater.cpp',
|
||||
]
|
||||
|
||||
# contains variables that conflict with LookupCache.cpp
|
||||
SOURCES += [
|
||||
'HashStore.cpp',
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'nsURLClassifier.manifest',
|
||||
'nsUrlClassifierHashCompleter.js',
|
||||
]
|
||||
|
||||
# Same as JS components that are run through the pre-processor.
|
||||
EXTRA_PP_COMPONENTS += [
|
||||
'nsUrlClassifierLib.js',
|
||||
'nsUrlClassifierListManager.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'SafeBrowsing.jsm',
|
||||
]
|
||||
|
||||
EXPORTS += [
|
||||
'Entries.h',
|
||||
'LookupCache.h',
|
||||
'nsUrlClassifierPrefixSet.h',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
MSVC_ENABLE_PGO = True
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../build',
|
||||
'/ipc/chromium/src',
|
||||
]
|
||||
|
||||
CXXFLAGS += CONFIG['SQLITE_CFLAGS']
|
||||
@@ -1,59 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsCheckSummedOutputStream.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsCheckSummedOutputStream
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(nsCheckSummedOutputStream,
|
||||
nsSafeFileOutputStream,
|
||||
nsISafeOutputStream,
|
||||
nsIOutputStream,
|
||||
nsIFileOutputStream)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCheckSummedOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
|
||||
int32_t behaviorFlags)
|
||||
{
|
||||
nsresult rv;
|
||||
mHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mHash->Init(nsICryptoHash::MD5);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return nsSafeFileOutputStream::Init(file, ioFlags, perm, behaviorFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCheckSummedOutputStream::Finish()
|
||||
{
|
||||
nsresult rv = mHash->Finish(false, mCheckSum);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint32_t written;
|
||||
rv = nsSafeFileOutputStream::Write(reinterpret_cast<const char*>(mCheckSum.BeginReading()),
|
||||
mCheckSum.Length(), &written);
|
||||
NS_ASSERTION(written == mCheckSum.Length(), "Error writing stream checksum");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return nsSafeFileOutputStream::Finish();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCheckSummedOutputStream::Write(const char *buf, uint32_t count, uint32_t *result)
|
||||
{
|
||||
nsresult rv = mHash->Update(reinterpret_cast<const uint8_t*>(buf), count);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return nsSafeFileOutputStream::Write(buf, count, result);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1,54 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsCheckSummedOutputStream_h__
|
||||
#define nsCheckSummedOutputStream_h__
|
||||
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsString.h"
|
||||
#include "../../../netwerk/base/nsFileStreams.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
|
||||
class nsCheckSummedOutputStream : public nsSafeFileOutputStream
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// Size of MD5 hash in bytes
|
||||
static const uint32_t CHECKSUM_SIZE = 16;
|
||||
|
||||
nsCheckSummedOutputStream() {}
|
||||
|
||||
NS_IMETHOD Finish() override;
|
||||
NS_IMETHOD Write(const char *buf, uint32_t count, uint32_t *result) override;
|
||||
NS_IMETHOD Init(nsIFile* file, int32_t ioFlags, int32_t perm, int32_t behaviorFlags) override;
|
||||
|
||||
protected:
|
||||
virtual ~nsCheckSummedOutputStream() { nsSafeFileOutputStream::Close(); }
|
||||
|
||||
nsCOMPtr<nsICryptoHash> mHash;
|
||||
nsAutoCString mCheckSum;
|
||||
};
|
||||
|
||||
// returns a file output stream which can be QI'ed to nsIFileOutputStream.
|
||||
inline nsresult
|
||||
NS_NewCheckSummedOutputStream(nsIOutputStream **result,
|
||||
nsIFile *file,
|
||||
int32_t ioFlags = -1,
|
||||
int32_t perm = -1,
|
||||
int32_t behaviorFlags = 0)
|
||||
{
|
||||
nsCOMPtr<nsIFileOutputStream> out = new nsCheckSummedOutputStream();
|
||||
nsresult rv = out->Init(file, ioFlags, perm, behaviorFlags);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
NS_ADDREF(*result = out); // cannot use nsCOMPtr::swap
|
||||
return rv;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,219 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
%{C++
|
||||
#include "Entries.h"
|
||||
#include "LookupCache.h"
|
||||
class nsUrlClassifierLookupResult;
|
||||
%}
|
||||
[ptr] native ResultArray(nsTArray<mozilla::safebrowsing::LookupResult>);
|
||||
[ptr] native CacheCompletionArray(nsTArray<mozilla::safebrowsing::CacheResult>);
|
||||
[ptr] native PrefixArray(mozilla::safebrowsing::PrefixArray);
|
||||
|
||||
interface nsIUrlClassifierHashCompleter;
|
||||
interface nsIPrincipal;
|
||||
|
||||
// Interface for JS function callbacks
|
||||
[scriptable, function, uuid(4ca27b6b-a674-4b3d-ab30-d21e2da2dffb)]
|
||||
interface nsIUrlClassifierCallback : nsISupports {
|
||||
void handleEvent(in ACString value);
|
||||
};
|
||||
|
||||
/**
|
||||
* The nsIUrlClassifierUpdateObserver interface is implemented by
|
||||
* clients streaming updates to the url-classifier (usually
|
||||
* nsUrlClassifierStreamUpdater.
|
||||
*/
|
||||
[scriptable, uuid(9fa11561-5816-4e1b-bcc9-b629ca05cce6)]
|
||||
interface nsIUrlClassifierUpdateObserver : nsISupports {
|
||||
/**
|
||||
* The update requested a new URL whose contents should be downloaded
|
||||
* and sent to the classifier as a new stream.
|
||||
*
|
||||
* @param url The url that was requested.
|
||||
* @param table The table name that this URL's contents will be associated
|
||||
* with. This should be passed back to beginStream().
|
||||
*/
|
||||
void updateUrlRequested(in ACString url,
|
||||
in ACString table);
|
||||
|
||||
/**
|
||||
* A stream update has completed.
|
||||
*
|
||||
* @param status The state of the update process.
|
||||
* @param delay The amount of time the updater should wait to fetch the
|
||||
* next URL in ms.
|
||||
*/
|
||||
void streamFinished(in nsresult status, in unsigned long delay);
|
||||
|
||||
/* The update has encountered an error and should be cancelled */
|
||||
void updateError(in nsresult error);
|
||||
|
||||
/**
|
||||
* The update has completed successfully.
|
||||
*
|
||||
* @param requestedTimeout The number of seconds that the caller should
|
||||
* wait before trying to update again.
|
||||
**/
|
||||
void updateSuccess(in unsigned long requestedTimeout);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a proxy class that is instantiated and called from the JS thread.
|
||||
* It provides async methods for querying and updating the database. As the
|
||||
* methods complete, they call the callback function.
|
||||
*/
|
||||
[scriptable, uuid(3f9e61e5-01bd-45d0-8dd2-f1abcd20dbb7)]
|
||||
interface nsIUrlClassifierDBService : nsISupports
|
||||
{
|
||||
/**
|
||||
* Looks up a URI in the specified tables.
|
||||
*
|
||||
* @param principal: The principal containing the URI to search.
|
||||
* @param c: The callback will be called with a comma-separated list
|
||||
* of tables to which the key belongs.
|
||||
*/
|
||||
void lookup(in nsIPrincipal principal,
|
||||
in ACString tables,
|
||||
in nsIUrlClassifierCallback c);
|
||||
|
||||
/**
|
||||
* Lists the tables along with which chunks are available in each table.
|
||||
* This list is in the format of the request body:
|
||||
* tablename;chunkdata\n
|
||||
* tablename2;chunkdata2\n
|
||||
*
|
||||
* For example:
|
||||
* goog-phish-regexp;a:10,14,30-40s:56,67
|
||||
* goog-white-regexp;a:1-3,5
|
||||
*/
|
||||
void getTables(in nsIUrlClassifierCallback c);
|
||||
|
||||
/**
|
||||
* Set the nsIUrlClassifierCompleter object for a given table. This
|
||||
* object will be used to request complete versions of partial
|
||||
* hashes.
|
||||
*/
|
||||
void setHashCompleter(in ACString tableName,
|
||||
in nsIUrlClassifierHashCompleter completer);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Incremental update methods.
|
||||
//
|
||||
// An update to the database has the following steps:
|
||||
//
|
||||
// 1) The update process is started with beginUpdate(). The client
|
||||
// passes an nsIUrlClassifierUpdateObserver object which will be
|
||||
// notified as the update is processed by the dbservice.
|
||||
// 2) The client sends an initial update stream to the dbservice,
|
||||
// using beginStream/updateStream/finishStream.
|
||||
// 3) While reading this initial update stream, the dbservice may
|
||||
// request additional streams from the client as requested by the
|
||||
// update stream.
|
||||
// 4) For each additional update stream, the client feeds the
|
||||
// contents to the dbservice using beginStream/updateStream/endStream.
|
||||
// 5) Once all streams have been processed, the client calls
|
||||
// finishUpdate. When the dbservice has finished processing
|
||||
// all streams, it will notify the observer that the update process
|
||||
// is complete.
|
||||
|
||||
/**
|
||||
* Begin an update process. Will throw NS_ERROR_NOT_AVAILABLE if there
|
||||
* is already an update in progress.
|
||||
*
|
||||
* @param updater The update observer tied to this update.
|
||||
* @param tables A comma-separated list of tables included in this update.
|
||||
*/
|
||||
void beginUpdate(in nsIUrlClassifierUpdateObserver updater,
|
||||
in ACString tables);
|
||||
|
||||
/**
|
||||
* Begin a stream update. This should be called once per url being
|
||||
* fetched.
|
||||
*
|
||||
* @param table The table the contents of this stream will be associated
|
||||
* with, or empty for the initial stream.
|
||||
*/
|
||||
void beginStream(in ACString table);
|
||||
|
||||
/**
|
||||
* Update the table incrementally.
|
||||
*/
|
||||
void updateStream(in ACString updateChunk);
|
||||
|
||||
// It would be nice to have an updateFromStream method to round out the
|
||||
// interface, but it's tricky because of XPCOM proxies.
|
||||
|
||||
/**
|
||||
* Finish an individual stream update. Must be called for every
|
||||
* beginStream() call, before the next beginStream() or finishUpdate().
|
||||
*
|
||||
* The update observer's streamFinished will be called once the
|
||||
* stream has been processed.
|
||||
*/
|
||||
void finishStream();
|
||||
|
||||
/**
|
||||
* Finish an incremental update. This will attempt to commit any
|
||||
* pending changes and resets the update interface.
|
||||
*
|
||||
* The update observer's updateSucceeded or updateError methods
|
||||
* will be called when the update has been processed.
|
||||
*/
|
||||
void finishUpdate();
|
||||
|
||||
/**
|
||||
* Cancel an incremental update. This rolls back any pending changes.
|
||||
* and resets the update interface.
|
||||
*
|
||||
* The update observer's updateError method will be called when the
|
||||
* update has been rolled back.
|
||||
*/
|
||||
void cancelUpdate();
|
||||
|
||||
/**
|
||||
* Reset the url-classifier database. This call will delete the existing
|
||||
* database, emptying all tables. Mostly intended for use in unit tests.
|
||||
*/
|
||||
void resetDatabase();
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for the actual worker thread. Implementations of this need not
|
||||
* be thread aware and just work on the database.
|
||||
*/
|
||||
[scriptable, uuid(b7b505d0-bfa2-44db-abf8-6e2bfc25bbab)]
|
||||
interface nsIUrlClassifierDBServiceWorker : nsIUrlClassifierDBService
|
||||
{
|
||||
// Open the DB connection
|
||||
void openDb();
|
||||
// Provide a way to forcibly close the db connection.
|
||||
void closeDb();
|
||||
|
||||
[noscript]void cacheCompletions(in CacheCompletionArray completions);
|
||||
[noscript]void cacheMisses(in PrefixArray misses);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is an internal helper interface for communication between the
|
||||
* main thread and the dbservice worker thread. It is called for each
|
||||
* lookup to provide a set of possible results, which the main thread
|
||||
* may need to expand using an nsIUrlClassifierCompleter.
|
||||
*/
|
||||
[uuid(b903dc8f-dff1-42fe-894b-36e7a59bb801)]
|
||||
interface nsIUrlClassifierLookupCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* The lookup process is complete.
|
||||
*
|
||||
* @param results
|
||||
* If this parameter is null, there were no results found.
|
||||
* If not, it contains an array of nsUrlClassifierEntry objects
|
||||
* with possible matches. The callee is responsible for freeing
|
||||
* this array.
|
||||
*/
|
||||
void lookupComplete(in ResultArray results);
|
||||
};
|
||||
@@ -1,65 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* This interface is implemented by nsIUrlClassifierHashCompleter clients.
|
||||
*/
|
||||
[scriptable, uuid(da16de40-df26-414d-bde7-c4faf4504868)]
|
||||
interface nsIUrlClassifierHashCompleterCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* A complete hash has been found that matches the partial hash.
|
||||
* This method may be called 0-n times for a given
|
||||
* nsIUrlClassifierCompleter::complete() call.
|
||||
*
|
||||
* @param hash
|
||||
* The 128-bit hash that was discovered.
|
||||
* @param table
|
||||
* The name of the table that this hash belongs to.
|
||||
* @param chunkId
|
||||
* The database chunk that this hash belongs to.
|
||||
*/
|
||||
void completion(in ACString hash,
|
||||
in ACString table,
|
||||
in uint32_t chunkId);
|
||||
|
||||
/**
|
||||
* The completion is complete. This method is called once per
|
||||
* nsIUrlClassifierCompleter::complete() call, after all completion()
|
||||
* calls are finished.
|
||||
*
|
||||
* @param status
|
||||
* NS_OK if the request completed successfully, or an error code.
|
||||
*/
|
||||
void completionFinished(in nsresult status);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clients updating the url-classifier database have the option of sending
|
||||
* partial (32-bit) hashes of URL fragments to be blacklisted. If the
|
||||
* url-classifier encounters one of these truncated hashes, it will ask an
|
||||
* nsIUrlClassifierCompleter instance to asynchronously provide the complete
|
||||
* hash, along with some associated metadata.
|
||||
* This is only ever used for testing and should absolutely be deleted (I
|
||||
* think).
|
||||
*/
|
||||
[scriptable, uuid(231fb2ad-ea8a-4e63-a331-eafc3b434811)]
|
||||
interface nsIUrlClassifierHashCompleter : nsISupports
|
||||
{
|
||||
/**
|
||||
* Request a completed hash from the given gethash url.
|
||||
*
|
||||
* @param partialHash
|
||||
* The 32-bit hash encountered by the url-classifier.
|
||||
* @param gethashUrl
|
||||
* The gethash url to use.
|
||||
* @param callback
|
||||
* An nsIUrlClassifierCompleterCallback instance.
|
||||
*/
|
||||
void complete(in ACString partialHash,
|
||||
in ACString gethashUrl,
|
||||
in nsIUrlClassifierHashCompleterCallback callback);
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIFile.idl"
|
||||
|
||||
// Note that the PrefixSet name is historical and we do properly support
|
||||
// duplicated values, so it's really a Prefix Trie.
|
||||
// All methods are thread-safe.
|
||||
[scriptable, uuid(3d8579f0-75fa-4e00-ba41-38661d5b5d17)]
|
||||
interface nsIUrlClassifierPrefixSet : nsISupports
|
||||
{
|
||||
// Initialize the PrefixSet. Give it a name for memory reporting.
|
||||
void init(in ACString aName);
|
||||
// Fills the PrefixSet with the given array of prefixes.
|
||||
// Can send an empty Array to clear the tree.
|
||||
// Requires array to be sorted.
|
||||
void setPrefixes([const, array, size_is(aLength)] in unsigned long aPrefixes,
|
||||
in unsigned long aLength);
|
||||
void getPrefixes(out unsigned long aCount,
|
||||
[array, size_is(aCount), retval] out unsigned long aPrefixes);
|
||||
// Do a lookup in the PrefixSet, return whether the value is present.
|
||||
boolean contains(in unsigned long aPrefix);
|
||||
boolean isEmpty();
|
||||
void loadFromFile(in nsIFile aFile);
|
||||
void storeToFile(in nsIFile aFile);
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIUrlClassifierDBService.idl"
|
||||
|
||||
/**
|
||||
* This is a class to manage large table updates from the server. Rather than
|
||||
* downloading the whole update and then updating the sqlite database, we
|
||||
* update tables as the data is streaming in.
|
||||
*/
|
||||
[scriptable, uuid(e1797597-f4d6-4dd3-a1e1-745ad352cd80)]
|
||||
interface nsIUrlClassifierStreamUpdater : nsISupports
|
||||
{
|
||||
/**
|
||||
* Try to download updates from updateUrl. If an update is already in
|
||||
* progress, queues the requested update. This is used in nsIUrlListManager
|
||||
* as well as in testing.
|
||||
* @param aRequestTables Comma-separated list of tables included in this
|
||||
* update.
|
||||
* @param aRequestBody The body for the request.
|
||||
* @param aUpdateUrl The plaintext url from which to request updates.
|
||||
* @param aSuccessCallback Called after a successful update.
|
||||
* @param aUpdateErrorCallback Called for problems applying the update
|
||||
* @param aDownloadErrorCallback Called if we get an http error or a
|
||||
* connection refused error.
|
||||
*/
|
||||
boolean downloadUpdates(in ACString aRequestTables,
|
||||
in ACString aRequestBody,
|
||||
in ACString aUpdateUrl,
|
||||
in nsIUrlClassifierCallback aSuccessCallback,
|
||||
in nsIUrlClassifierCallback aUpdateErrorCallback,
|
||||
in nsIUrlClassifierCallback aDownloadErrorCallback);
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIUrlListManager.idl"
|
||||
|
||||
// A map that contains a string keys mapped to string values.
|
||||
|
||||
[scriptable, uuid(fd1f8334-1859-472d-b01f-4ac6b1121ce4)]
|
||||
interface nsIUrlClassifierTable : nsISupports
|
||||
{
|
||||
/**
|
||||
* The name used to identify this table
|
||||
*/
|
||||
attribute ACString name;
|
||||
|
||||
/**
|
||||
* Set to false if we don't want to update this table.
|
||||
*/
|
||||
attribute boolean needsUpdate;
|
||||
|
||||
/**
|
||||
* In the simple case, exists just looks up the string in the
|
||||
* table and call the callback after the query returns with true or
|
||||
* false. It's possible that something more complex happens
|
||||
* (e.g., canonicalize the url).
|
||||
*/
|
||||
void exists(in ACString key, in nsIUrlListManagerCallback cb);
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
/**
|
||||
* Some utility methods used by the url classifier.
|
||||
*/
|
||||
|
||||
interface nsIURI;
|
||||
|
||||
[scriptable, uuid(e4f0e59c-b922-48b0-a7b6-1735c1f96fed)]
|
||||
interface nsIUrlClassifierUtils : nsISupports
|
||||
{
|
||||
/**
|
||||
* Get the lookup string for a given URI. This normalizes the hostname,
|
||||
* url-decodes the string, and strips off the protocol.
|
||||
*
|
||||
* @param uri URI to get the lookup key for.
|
||||
*
|
||||
* @returns String containing the canonicalized URI.
|
||||
*/
|
||||
ACString getKeyForURI(in nsIURI uri);
|
||||
};
|
||||
@@ -1,65 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIPrincipal;
|
||||
|
||||
/**
|
||||
* Interface for a class that manages updates of the url classifier database.
|
||||
*/
|
||||
|
||||
// Interface for JS function callbacks
|
||||
[scriptable, function, uuid(fa4caf12-d057-4e7e-81e9-ce066ceee90b)]
|
||||
interface nsIUrlListManagerCallback : nsISupports {
|
||||
void handleEvent(in ACString value);
|
||||
};
|
||||
|
||||
|
||||
[scriptable, uuid(5d5ed98f-72cd-46b6-a9fe-76418adfdfeb)]
|
||||
interface nsIUrlListManager : nsISupports
|
||||
{
|
||||
/**
|
||||
* Get the gethash url for this table
|
||||
*/
|
||||
ACString getGethashUrl(in ACString tableName);
|
||||
|
||||
/**
|
||||
* Add a table to the list of tables we are managing. The name is a
|
||||
* string of the format provider_name-semantic_type-table_type. For
|
||||
* @param tableName A string of the format
|
||||
* provider_name-semantic_type-table_type. For example,
|
||||
* goog-white-enchash or goog-black-url.
|
||||
* @param updateUrl The URL from which to fetch updates.
|
||||
* @param gethashUrl The URL from which to fetch hash completions.
|
||||
*/
|
||||
boolean registerTable(in ACString tableName,
|
||||
in ACString updateUrl,
|
||||
in ACString gethashUrl);
|
||||
|
||||
/**
|
||||
* Turn on update checking for a table. I.e., during the next server
|
||||
* check, download updates for this table.
|
||||
*/
|
||||
void enableUpdate(in ACString tableName);
|
||||
|
||||
/**
|
||||
* Turn off update checking for a table.
|
||||
*/
|
||||
void disableUpdate(in ACString tableName);
|
||||
|
||||
/**
|
||||
* Toggle update checking, if necessary.
|
||||
*/
|
||||
void maybeToggleUpdateChecking();
|
||||
|
||||
/**
|
||||
* Lookup a key. Should not raise exceptions. Calls the callback
|
||||
* function with a comma-separated list of tables to which the key
|
||||
* belongs.
|
||||
*/
|
||||
void safeLookup(in nsIPrincipal key,
|
||||
in nsIUrlListManagerCallback cb);
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
component {26a4a019-2827-4a89-a85c-5931a678823a} nsUrlClassifierLib.js
|
||||
contract @mozilla.org/url-classifier/jslib;1 {26a4a019-2827-4a89-a85c-5931a678823a}
|
||||
component {ca168834-cc00-48f9-b83c-fd018e58cae3} nsUrlClassifierListManager.js
|
||||
contract @mozilla.org/url-classifier/listmanager;1 {ca168834-cc00-48f9-b83c-fd018e58cae3}
|
||||
component {9111de73-9322-4bfc-8b65-2b727f3e6ec8} nsUrlClassifierHashCompleter.js
|
||||
contract @mozilla.org/url-classifier/hashcompleter;1 {9111de73-9322-4bfc-8b65-2b727f3e6ec8}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,238 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsUrlClassifierDBService_h_
|
||||
#define nsUrlClassifierDBService_h_
|
||||
|
||||
#include <nsISupportsUtils.h>
|
||||
|
||||
#include "nsID.h"
|
||||
#include "nsInterfaceHashtable.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsUrlClassifierPrefixSet.h"
|
||||
#include "nsIUrlClassifierHashCompleter.h"
|
||||
#include "nsIUrlListManager.h"
|
||||
#include "nsIUrlClassifierDBService.h"
|
||||
#include "nsIURIClassifier.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
#include "nsICryptoHMAC.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
#include "Entries.h"
|
||||
#include "LookupCache.h"
|
||||
|
||||
// The hash length for a domain key.
|
||||
#define DOMAIN_LENGTH 4
|
||||
|
||||
// The hash length of a partial hash entry.
|
||||
#define PARTIAL_LENGTH 4
|
||||
|
||||
// The hash length of a complete hash entry.
|
||||
#define COMPLETE_LENGTH 32
|
||||
|
||||
using namespace mozilla::safebrowsing;
|
||||
|
||||
class nsUrlClassifierDBServiceWorker;
|
||||
class nsIThread;
|
||||
class nsIURI;
|
||||
class UrlClassifierDBServiceWorkerProxy;
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
class Classifier;
|
||||
class ProtocolParser;
|
||||
class TableUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
// This is a proxy class that just creates a background thread and delagates
|
||||
// calls to the background thread.
|
||||
class nsUrlClassifierDBService final : public nsIUrlClassifierDBService,
|
||||
public nsIURIClassifier,
|
||||
public nsIObserver
|
||||
{
|
||||
public:
|
||||
// This is thread safe. It throws an exception if the thread is busy.
|
||||
nsUrlClassifierDBService();
|
||||
|
||||
nsresult Init();
|
||||
|
||||
static nsUrlClassifierDBService* GetInstance(nsresult *result);
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_URLCLASSIFIERDBSERVICE_CID)
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIURLCLASSIFIERDBSERVICE
|
||||
NS_DECL_NSIURICLASSIFIER
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
bool GetCompleter(const nsACString& tableName,
|
||||
nsIUrlClassifierHashCompleter** completer);
|
||||
nsresult CacheCompletions(mozilla::safebrowsing::CacheResultArray *results);
|
||||
nsresult CacheMisses(mozilla::safebrowsing::PrefixArray *results);
|
||||
|
||||
static nsIThread* BackgroundThread();
|
||||
|
||||
private:
|
||||
// No subclassing
|
||||
~nsUrlClassifierDBService();
|
||||
|
||||
// Disallow copy constructor
|
||||
nsUrlClassifierDBService(nsUrlClassifierDBService&);
|
||||
|
||||
nsresult LookupURI(nsIPrincipal* aPrincipal,
|
||||
const nsACString& tables,
|
||||
nsIUrlClassifierCallback* c,
|
||||
bool forceCheck, bool *didCheck);
|
||||
|
||||
// Close db connection and join the background thread if it exists.
|
||||
nsresult Shutdown();
|
||||
|
||||
// Check if the key is on a known-clean host.
|
||||
nsresult CheckClean(const nsACString &lookupKey,
|
||||
bool *clean);
|
||||
|
||||
// Read everything into mGethashTables and mDisallowCompletionTables
|
||||
nsresult ReadTablesFromPrefs();
|
||||
|
||||
// Build a comma-separated list of tables to check
|
||||
void BuildTables(bool trackingProtectionEnabled, nsCString& tables);
|
||||
|
||||
nsRefPtr<nsUrlClassifierDBServiceWorker> mWorker;
|
||||
nsRefPtr<UrlClassifierDBServiceWorkerProxy> mWorkerProxy;
|
||||
|
||||
nsInterfaceHashtable<nsCStringHashKey, nsIUrlClassifierHashCompleter> mCompleters;
|
||||
|
||||
// TRUE if the nsURIClassifier implementation should check for malware
|
||||
// uris on document loads.
|
||||
bool mCheckMalware;
|
||||
|
||||
// TRUE if the nsURIClassifier implementation should check for phishing
|
||||
// uris on document loads.
|
||||
bool mCheckPhishing;
|
||||
|
||||
// TRUE if the nsURIClassifier implementation should check for tracking
|
||||
// uris on document loads.
|
||||
bool mCheckTracking;
|
||||
|
||||
// TRUE if a BeginUpdate() has been called without an accompanying
|
||||
// CancelUpdate()/FinishUpdate(). This is used to prevent competing
|
||||
// updates, not to determine whether an update is still being
|
||||
// processed.
|
||||
bool mInUpdate;
|
||||
|
||||
// The list of tables that can use the default hash completer object.
|
||||
nsTArray<nsCString> mGethashTables;
|
||||
|
||||
// The list of tables that should never be hash completed.
|
||||
nsTArray<nsCString> mDisallowCompletionsTables;
|
||||
|
||||
// Thread that we do the updates on.
|
||||
static nsIThread* gDbBackgroundThread;
|
||||
};
|
||||
|
||||
class nsUrlClassifierDBServiceWorker final :
|
||||
public nsIUrlClassifierDBServiceWorker
|
||||
{
|
||||
public:
|
||||
nsUrlClassifierDBServiceWorker();
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIURLCLASSIFIERDBSERVICE
|
||||
NS_DECL_NSIURLCLASSIFIERDBSERVICEWORKER
|
||||
|
||||
nsresult Init(uint32_t aGethashNoise, nsCOMPtr<nsIFile> aCacheDir);
|
||||
|
||||
// Queue a lookup for the worker to perform, called in the main thread.
|
||||
// tables is a comma-separated list of tables to query
|
||||
nsresult QueueLookup(const nsACString& lookupKey,
|
||||
const nsACString& tables,
|
||||
nsIUrlClassifierLookupCallback* callback);
|
||||
|
||||
// Handle any queued-up lookups. We call this function during long-running
|
||||
// update operations to prevent lookups from blocking for too long.
|
||||
nsresult HandlePendingLookups();
|
||||
|
||||
// Perform a blocking classifier lookup for a given url. Can be called on
|
||||
// either the main thread or the worker thread.
|
||||
nsresult DoLocalLookup(const nsACString& spec,
|
||||
const nsACString& tables,
|
||||
LookupResultArray* results);
|
||||
|
||||
private:
|
||||
// No subclassing
|
||||
~nsUrlClassifierDBServiceWorker();
|
||||
|
||||
// Disallow copy constructor
|
||||
nsUrlClassifierDBServiceWorker(nsUrlClassifierDBServiceWorker&);
|
||||
|
||||
// Applies the current transaction and resets the update/working times.
|
||||
nsresult ApplyUpdate();
|
||||
|
||||
// Reset the in-progress update stream
|
||||
void ResetStream();
|
||||
|
||||
// Reset the in-progress update
|
||||
void ResetUpdate();
|
||||
|
||||
// Perform a classifier lookup for a given url.
|
||||
nsresult DoLookup(const nsACString& spec,
|
||||
const nsACString& tables,
|
||||
nsIUrlClassifierLookupCallback* c);
|
||||
|
||||
nsresult AddNoise(const Prefix aPrefix,
|
||||
const nsCString tableName,
|
||||
uint32_t aCount,
|
||||
LookupResultArray& results);
|
||||
|
||||
// Can only be used on the background thread
|
||||
nsCOMPtr<nsICryptoHash> mCryptoHash;
|
||||
|
||||
nsAutoPtr<mozilla::safebrowsing::Classifier> mClassifier;
|
||||
// The class that actually parses the update chunks.
|
||||
nsAutoPtr<ProtocolParser> mProtocolParser;
|
||||
|
||||
// Directory where to store the SB databases.
|
||||
nsCOMPtr<nsIFile> mCacheDir;
|
||||
|
||||
// XXX: maybe an array of autoptrs. Or maybe a class specifically
|
||||
// storing a series of updates.
|
||||
nsTArray<mozilla::safebrowsing::TableUpdate*> mTableUpdates;
|
||||
|
||||
int32_t mUpdateWait;
|
||||
|
||||
// Entries that cannot be completed. We expect them to die at
|
||||
// the next update
|
||||
PrefixArray mMissCache;
|
||||
|
||||
nsresult mUpdateStatus;
|
||||
nsTArray<nsCString> mUpdateTables;
|
||||
|
||||
nsCOMPtr<nsIUrlClassifierUpdateObserver> mUpdateObserver;
|
||||
bool mInStream;
|
||||
|
||||
// The number of noise entries to add to the set of lookup results.
|
||||
uint32_t mGethashNoise;
|
||||
|
||||
// Pending lookups are stored in a queue for processing. The queue
|
||||
// is protected by mPendingLookupLock.
|
||||
mozilla::Mutex mPendingLookupLock;
|
||||
|
||||
class PendingLookup {
|
||||
public:
|
||||
mozilla::TimeStamp mStartTime;
|
||||
nsCString mKey;
|
||||
nsCString mTables;
|
||||
nsCOMPtr<nsIUrlClassifierLookupCallback> mCallback;
|
||||
};
|
||||
|
||||
// list of pending lookups
|
||||
nsTArray<PendingLookup> mPendingLookups;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsUrlClassifierDBService, NS_URLCLASSIFIERDBSERVICE_CID)
|
||||
|
||||
#endif // nsUrlClassifierDBService_h_
|
||||
@@ -1,466 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
// COMPLETE_LENGTH and PARTIAL_LENGTH copied from nsUrlClassifierDBService.h,
|
||||
// they correspond to the length, in bytes, of a hash prefix and the total
|
||||
// hash.
|
||||
const COMPLETE_LENGTH = 32;
|
||||
const PARTIAL_LENGTH = 4;
|
||||
|
||||
// These backoff related constants are taken from v2 of the Google Safe Browsing
|
||||
// API. All times are in milliseconds.
|
||||
// BACKOFF_ERRORS: the number of errors incurred until we start to back off.
|
||||
// BACKOFF_INTERVAL: the initial time to wait once we start backing
|
||||
// off.
|
||||
// BACKOFF_MAX: as the backoff time doubles after each failure, this is a
|
||||
// ceiling on the time to wait.
|
||||
|
||||
const BACKOFF_ERRORS = 2;
|
||||
const BACKOFF_INTERVAL = 30 * 60 * 1000;
|
||||
const BACKOFF_MAX = 8 * 60 * 60 * 1000;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function HashCompleter() {
|
||||
// The current HashCompleterRequest in flight. Once it is started, it is set
|
||||
// to null. It may be used by multiple calls to |complete| in succession to
|
||||
// avoid creating multiple requests to the same gethash URL.
|
||||
this._currentRequest = null;
|
||||
// A map of gethashUrls to HashCompleterRequests that haven't yet begun.
|
||||
this._pendingRequests = {};
|
||||
|
||||
// A map of gethash URLs to RequestBackoff objects.
|
||||
this._backoffs = {};
|
||||
|
||||
// Whether we have been informed of a shutdown by the xpcom-shutdown event.
|
||||
this._shuttingDown = false;
|
||||
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", true);
|
||||
}
|
||||
|
||||
HashCompleter.prototype = {
|
||||
classID: Components.ID("{9111de73-9322-4bfc-8b65-2b727f3e6ec8}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIUrlClassifierHashCompleter,
|
||||
Ci.nsIRunnable,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsITimerCallback,
|
||||
Ci.nsISupports]),
|
||||
|
||||
// This is mainly how the HashCompleter interacts with other components.
|
||||
// Even though it only takes one partial hash and callback, subsequent
|
||||
// calls are made into the same HTTP request by using a thread dispatch.
|
||||
complete: function HC_complete(aPartialHash, aGethashUrl, aCallback) {
|
||||
if (!aGethashUrl) {
|
||||
throw Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!this._currentRequest) {
|
||||
this._currentRequest = new HashCompleterRequest(this, aGethashUrl);
|
||||
}
|
||||
if (this._currentRequest.gethashUrl == aGethashUrl) {
|
||||
this._currentRequest.add(aPartialHash, aCallback);
|
||||
} else {
|
||||
if (!this._pendingRequests[aGethashUrl]) {
|
||||
this._pendingRequests[aGethashUrl] =
|
||||
new HashCompleterRequest(this, aGethashUrl);
|
||||
}
|
||||
this._pendingRequests[aGethashUrl].add(aPartialHash, aCallback);
|
||||
}
|
||||
|
||||
if (!this._backoffs[aGethashUrl]) {
|
||||
// Initialize request backoffs separately, since requests are deleted
|
||||
// after they are dispatched.
|
||||
var jslib = Cc["@mozilla.org/url-classifier/jslib;1"]
|
||||
.getService().wrappedJSObject;
|
||||
this._backoffs[aGethashUrl] = new jslib.RequestBackoff(
|
||||
BACKOFF_ERRORS /* max errors */,
|
||||
60*1000 /* retry interval, 1 min */,
|
||||
10 /* keep track of max requests */,
|
||||
0 /* don't throttle on successful requests per time period */,
|
||||
BACKOFF_INTERVAL /* backoff interval, 60 min */,
|
||||
BACKOFF_MAX /* max backoff, 8hr */);
|
||||
}
|
||||
// Start off this request. Without dispatching to a thread, every call to
|
||||
// complete makes an individual HTTP request.
|
||||
Services.tm.currentThread.dispatch(this, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
},
|
||||
|
||||
// This is called after several calls to |complete|, or after the
|
||||
// currentRequest has finished. It starts off the HTTP request by making a
|
||||
// |begin| call to the HashCompleterRequest.
|
||||
run: function() {
|
||||
// Clear everything on shutdown
|
||||
if (this._shuttingDown) {
|
||||
this._currentRequest = null;
|
||||
this._pendingRequests = null;
|
||||
for (var url in this._backoffs) {
|
||||
this._backoffs[url] = null;
|
||||
}
|
||||
throw Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// If we don't have an in-flight request, make one
|
||||
let pendingUrls = Object.keys(this._pendingRequests);
|
||||
if (!this._currentRequest && (pendingUrls.length > 0)) {
|
||||
let nextUrl = pendingUrls[0];
|
||||
this._currentRequest = this._pendingRequests[nextUrl];
|
||||
delete this._pendingRequests[nextUrl];
|
||||
}
|
||||
|
||||
if (this._currentRequest) {
|
||||
try {
|
||||
this._currentRequest.begin();
|
||||
} finally {
|
||||
// If |begin| fails, we should get rid of our request.
|
||||
this._currentRequest = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Pass the server response status to the RequestBackoff for the given
|
||||
// gethashUrl and fetch the next pending request, if there is one.
|
||||
finishRequest: function(url, aStatus) {
|
||||
this._backoffs[url].noteServerResponse(aStatus);
|
||||
Services.tm.currentThread.dispatch(this, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
},
|
||||
|
||||
// Returns true if we can make a request from the given url, false otherwise.
|
||||
canMakeRequest: function(aGethashUrl) {
|
||||
return this._backoffs[aGethashUrl].canMakeRequest();
|
||||
},
|
||||
|
||||
// Notifies the RequestBackoff of a new request so we can throttle based on
|
||||
// max requests/time period. This must be called before a channel is opened,
|
||||
// and finishRequest must be called once the response is received.
|
||||
noteRequest: function(aGethashUrl) {
|
||||
return this._backoffs[aGethashUrl].noteRequest();
|
||||
},
|
||||
|
||||
observe: function HC_observe(aSubject, aTopic, aData) {
|
||||
if (aTopic == "xpcom-shutdown") {
|
||||
this._shuttingDown = true;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function HashCompleterRequest(aCompleter, aGethashUrl) {
|
||||
// HashCompleter object that created this HashCompleterRequest.
|
||||
this._completer = aCompleter;
|
||||
// The internal set of hashes and callbacks that this request corresponds to.
|
||||
this._requests = [];
|
||||
// nsIChannel that the hash completion query is transmitted over.
|
||||
this._channel = null;
|
||||
// Response body of hash completion. Created in onDataAvailable.
|
||||
this._response = "";
|
||||
// Whether we have been informed of a shutdown by the xpcom-shutdown event.
|
||||
this._shuttingDown = false;
|
||||
this.gethashUrl = aGethashUrl;
|
||||
}
|
||||
HashCompleterRequest.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver,
|
||||
Ci.nsIStreamListener,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupports]),
|
||||
|
||||
// This is called by the HashCompleter to add a hash and callback to the
|
||||
// HashCompleterRequest. It must be called before calling |begin|.
|
||||
add: function HCR_add(aPartialHash, aCallback) {
|
||||
this._requests.push({
|
||||
partialHash: aPartialHash,
|
||||
callback: aCallback,
|
||||
responses: []
|
||||
});
|
||||
},
|
||||
|
||||
// This initiates the HTTP request. It can fail due to backoff timings and
|
||||
// will notify all callbacks as necessary. We notify the backoff object on
|
||||
// begin.
|
||||
begin: function HCR_begin() {
|
||||
if (!this._completer.canMakeRequest(this.gethashUrl)) {
|
||||
dump("hashcompleter: Can't make request to " + this.gethashUrl + "\n");
|
||||
this.notifyFailure(Cr.NS_ERROR_ABORT);
|
||||
return;
|
||||
}
|
||||
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
|
||||
try {
|
||||
this.openChannel();
|
||||
// Notify the RequestBackoff if opening the channel succeeded. At this
|
||||
// point, finishRequest must be called.
|
||||
this._completer.noteRequest(this.gethashUrl);
|
||||
}
|
||||
catch (err) {
|
||||
this.notifyFailure(err);
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
|
||||
notify: function HCR_notify() {
|
||||
// If we haven't gotten onStopRequest, just cancel. This will call us
|
||||
// with onStopRequest since we implement nsIStreamListener on the
|
||||
// channel.
|
||||
if (this._channel && this._channel.isPending()) {
|
||||
dump("hashcompleter: cancelling request to " + this.gethashUrl + "\n");
|
||||
this._channel.cancel(Cr.NS_BINDING_ABORTED);
|
||||
}
|
||||
},
|
||||
|
||||
// Creates an nsIChannel for the request and fills the body.
|
||||
openChannel: function HCR_openChannel() {
|
||||
let loadFlags = Ci.nsIChannel.INHIBIT_CACHING |
|
||||
Ci.nsIChannel.LOAD_BYPASS_CACHE;
|
||||
|
||||
let uri = Services.io.newURI(this.gethashUrl, null, null);
|
||||
let channel = Services.io.newChannelFromURI2(uri,
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_NORMAL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
channel.loadFlags = loadFlags;
|
||||
|
||||
// Disable keepalive.
|
||||
let httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
||||
httpChannel.setRequestHeader("Connection", "close", false);
|
||||
|
||||
this._channel = channel;
|
||||
|
||||
let body = this.buildRequest();
|
||||
this.addRequestBody(body);
|
||||
|
||||
// Set a timer that cancels the channel after timeout_ms in case we
|
||||
// don't get a gethash response.
|
||||
this.timer_ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
// Ask the timer to use nsITimerCallback (.notify()) when ready
|
||||
let timeout = Services.prefs.getIntPref(
|
||||
"urlclassifier.gethash.timeout_ms");
|
||||
this.timer_.initWithCallback(this, timeout, this.timer_.TYPE_ONE_SHOT);
|
||||
channel.asyncOpen(this, null);
|
||||
},
|
||||
|
||||
// Returns a string for the request body based on the contents of
|
||||
// this._requests.
|
||||
buildRequest: function HCR_buildRequest() {
|
||||
// Sometimes duplicate entries are sent to HashCompleter but we do not need
|
||||
// to propagate these to the server. (bug 633644)
|
||||
let prefixes = [];
|
||||
|
||||
for (let i = 0; i < this._requests.length; i++) {
|
||||
let request = this._requests[i];
|
||||
if (prefixes.indexOf(request.partialHash) == -1) {
|
||||
prefixes.push(request.partialHash);
|
||||
}
|
||||
}
|
||||
|
||||
// Randomize the order to obscure the original request from noise
|
||||
// unbiased Fisher-Yates shuffle
|
||||
let i = prefixes.length;
|
||||
while (i--) {
|
||||
let j = Math.floor(Math.random() * (i + 1));
|
||||
let temp = prefixes[i];
|
||||
prefixes[i] = prefixes[j];
|
||||
prefixes[j] = temp;
|
||||
}
|
||||
|
||||
let body;
|
||||
body = PARTIAL_LENGTH + ":" + (PARTIAL_LENGTH * prefixes.length) +
|
||||
"\n" + prefixes.join("");
|
||||
|
||||
return body;
|
||||
},
|
||||
|
||||
// Sets the request body of this._channel.
|
||||
addRequestBody: function HCR_addRequestBody(aBody) {
|
||||
let inputStream = Cc["@mozilla.org/io/string-input-stream;1"].
|
||||
createInstance(Ci.nsIStringInputStream);
|
||||
|
||||
inputStream.setData(aBody, aBody.length);
|
||||
|
||||
let uploadChannel = this._channel.QueryInterface(Ci.nsIUploadChannel);
|
||||
uploadChannel.setUploadStream(inputStream, "text/plain", -1);
|
||||
|
||||
let httpChannel = this._channel.QueryInterface(Ci.nsIHttpChannel);
|
||||
httpChannel.requestMethod = "POST";
|
||||
},
|
||||
|
||||
// Parses the response body and eventually adds items to the |responses| array
|
||||
// for elements of |this._requests|.
|
||||
handleResponse: function HCR_handleResponse() {
|
||||
if (this._response == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
let start = 0;
|
||||
|
||||
let length = this._response.length;
|
||||
while (start != length) {
|
||||
start = this.handleTable(start);
|
||||
}
|
||||
},
|
||||
|
||||
// This parses a table entry in the response body and calls |handleItem|
|
||||
// for complete hash in the table entry.
|
||||
handleTable: function HCR_handleTable(aStart) {
|
||||
let body = this._response.substring(aStart);
|
||||
|
||||
// deal with new line indexes as there could be
|
||||
// new line characters in the data parts.
|
||||
let newlineIndex = body.indexOf("\n");
|
||||
if (newlineIndex == -1) {
|
||||
throw errorWithStack();
|
||||
}
|
||||
let header = body.substring(0, newlineIndex);
|
||||
let entries = header.split(":");
|
||||
if (entries.length != 3) {
|
||||
throw errorWithStack();
|
||||
}
|
||||
|
||||
let list = entries[0];
|
||||
let addChunk = parseInt(entries[1]);
|
||||
let dataLength = parseInt(entries[2]);
|
||||
|
||||
if (dataLength % COMPLETE_LENGTH != 0 ||
|
||||
dataLength == 0 ||
|
||||
dataLength > body.length - (newlineIndex + 1)) {
|
||||
throw errorWithStack();
|
||||
}
|
||||
|
||||
let data = body.substr(newlineIndex + 1, dataLength);
|
||||
for (let i = 0; i < (dataLength / COMPLETE_LENGTH); i++) {
|
||||
this.handleItem(data.substr(i * COMPLETE_LENGTH, COMPLETE_LENGTH), list,
|
||||
addChunk);
|
||||
}
|
||||
|
||||
return aStart + newlineIndex + 1 + dataLength;
|
||||
},
|
||||
|
||||
// This adds a complete hash to any entry in |this._requests| that matches
|
||||
// the hash.
|
||||
handleItem: function HCR_handleItem(aData, aTableName, aChunkId) {
|
||||
for (let i = 0; i < this._requests.length; i++) {
|
||||
let request = this._requests[i];
|
||||
if (aData.substring(0,4) == request.partialHash) {
|
||||
request.responses.push({
|
||||
completeHash: aData,
|
||||
tableName: aTableName,
|
||||
chunkId: aChunkId,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// notifySuccess and notifyFailure are used to alert the callbacks with
|
||||
// results. notifySuccess makes |completion| and |completionFinished| calls
|
||||
// while notifyFailure only makes a |completionFinished| call with the error
|
||||
// code.
|
||||
notifySuccess: function HCR_notifySuccess() {
|
||||
for (let i = 0; i < this._requests.length; i++) {
|
||||
let request = this._requests[i];
|
||||
for (let j = 0; j < request.responses.length; j++) {
|
||||
let response = request.responses[j];
|
||||
request.callback.completion(response.completeHash, response.tableName,
|
||||
response.chunkId);
|
||||
}
|
||||
|
||||
request.callback.completionFinished(Cr.NS_OK);
|
||||
}
|
||||
},
|
||||
|
||||
notifyFailure: function HCR_notifyFailure(aStatus) {
|
||||
dump("hashcompleter: notifying failure\n");
|
||||
for (let i = 0; i < this._requests.length; i++) {
|
||||
let request = this._requests[i];
|
||||
request.callback.completionFinished(aStatus);
|
||||
}
|
||||
},
|
||||
|
||||
onDataAvailable: function HCR_onDataAvailable(aRequest, aContext,
|
||||
aInputStream, aOffset, aCount) {
|
||||
let sis = Cc["@mozilla.org/scriptableinputstream;1"].
|
||||
createInstance(Ci.nsIScriptableInputStream);
|
||||
sis.init(aInputStream);
|
||||
this._response += sis.readBytes(aCount);
|
||||
},
|
||||
|
||||
onStartRequest: function HCR_onStartRequest(aRequest, aContext) {
|
||||
// At this point no data is available for us and we have no reason to
|
||||
// terminate the connection, so we do nothing until |onStopRequest|.
|
||||
},
|
||||
|
||||
onStopRequest: function HCR_onStopRequest(aRequest, aContext, aStatusCode) {
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
|
||||
if (this._shuttingDown) {
|
||||
throw Cr.NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
// Default HTTP status to service unavailable, in case we can't retrieve
|
||||
// the true status from the channel.
|
||||
let httpStatus = 503;
|
||||
if (Components.isSuccessCode(aStatusCode)) {
|
||||
let channel = aRequest.QueryInterface(Ci.nsIHttpChannel);
|
||||
let success = channel.requestSucceeded;
|
||||
httpStatus = channel.responseStatus;
|
||||
if (!success) {
|
||||
aStatusCode = Cr.NS_ERROR_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
let success = Components.isSuccessCode(aStatusCode);
|
||||
// Notify the RequestBackoff once a response is received.
|
||||
this._completer.finishRequest(this.gethashUrl, httpStatus);
|
||||
|
||||
if (success) {
|
||||
try {
|
||||
this.handleResponse();
|
||||
}
|
||||
catch (err) {
|
||||
dump(err.stack);
|
||||
aStatusCode = err.value;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
this.notifySuccess();
|
||||
} else {
|
||||
this.notifyFailure(aStatusCode);
|
||||
}
|
||||
},
|
||||
|
||||
observe: function HCR_observe(aSubject, aTopic, aData) {
|
||||
if (aTopic != "xpcom-shutdown") {
|
||||
return;
|
||||
}
|
||||
|
||||
this._shuttingDown = true;
|
||||
if (this._channel) {
|
||||
this._channel.cancel(Cr.NS_ERROR_ABORT);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Converts a URL safe base64 string to a normal base64 string. Will not change
|
||||
// normal base64 strings. This is modelled after the same function in
|
||||
// nsUrlClassifierUtils.h.
|
||||
function unUrlsafeBase64(aStr) {
|
||||
return !aStr ? "" : aStr.replace(/-/g, "+")
|
||||
.replace(/_/g, "/");
|
||||
}
|
||||
|
||||
function errorWithStack() {
|
||||
let err = new Error();
|
||||
err.value = Cr.NS_ERROR_FAILURE;
|
||||
return err;
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HashCompleter]);
|
||||
@@ -1,35 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// We wastefully reload the same JS files across components. This puts all
|
||||
// the common JS files used by safebrowsing and url-classifier into a
|
||||
// single component.
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const G_GDEBUG = false;
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
#include ./content/moz/lang.js
|
||||
#include ./content/moz/preferences.js
|
||||
#include ./content/moz/debug.js
|
||||
#include ./content/moz/alarm.js
|
||||
#include ./content/moz/cryptohasher.js
|
||||
#include ./content/moz/observer.js
|
||||
#include ./content/moz/protocol4.js
|
||||
|
||||
#include ./content/request-backoff.js
|
||||
#include ./content/xml-fetcher.js
|
||||
|
||||
// Expose this whole component.
|
||||
var lib = this;
|
||||
|
||||
function UrlClassifierLib() {
|
||||
this.wrappedJSObject = lib;
|
||||
}
|
||||
UrlClassifierLib.prototype.classID = Components.ID("{26a4a019-2827-4a89-a85c-5931a678823a}");
|
||||
UrlClassifierLib.prototype.QueryInterface = XPCOMUtils.generateQI([]);
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([UrlClassifierLib]);
|
||||
@@ -1,53 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
#include ./content/listmanager.js
|
||||
|
||||
var modScope = this;
|
||||
function Init() {
|
||||
// Pull the library in.
|
||||
var jslib = Cc["@mozilla.org/url-classifier/jslib;1"]
|
||||
.getService().wrappedJSObject;
|
||||
Function.prototype.inherits = function(parentCtor) {
|
||||
var tempCtor = function(){};
|
||||
tempCtor.prototype = parentCtor.prototype;
|
||||
this.superClass_ = parentCtor.prototype;
|
||||
this.prototype = new tempCtor();
|
||||
},
|
||||
modScope.G_Preferences = jslib.G_Preferences;
|
||||
modScope.G_PreferenceObserver = jslib.G_PreferenceObserver;
|
||||
modScope.G_ObserverServiceObserver = jslib.G_ObserverServiceObserver;
|
||||
modScope.G_Debug = jslib.G_Debug;
|
||||
modScope.G_Assert = jslib.G_Assert;
|
||||
modScope.G_debugService = jslib.G_debugService;
|
||||
modScope.G_Alarm = jslib.G_Alarm;
|
||||
modScope.BindToObject = jslib.BindToObject;
|
||||
modScope.PROT_XMLFetcher = jslib.PROT_XMLFetcher;
|
||||
modScope.RequestBackoff = jslib.RequestBackoff;
|
||||
|
||||
// We only need to call Init once.
|
||||
modScope.Init = function() {};
|
||||
}
|
||||
|
||||
function RegistrationData()
|
||||
{
|
||||
}
|
||||
RegistrationData.prototype = {
|
||||
classID: Components.ID("{ca168834-cc00-48f9-b83c-fd018e58cae3}"),
|
||||
_xpcom_factory: {
|
||||
createInstance: function(outer, iid) {
|
||||
if (outer != null)
|
||||
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
Init();
|
||||
return (new PROT_ListManager()).QueryInterface(iid);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RegistrationData]);
|
||||
@@ -1,437 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
#include "nsUrlClassifierPrefixSet.h"
|
||||
#include "nsIUrlClassifierPrefixSet.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
#include "prlog.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
// NSPR_LOG_MODULES=UrlClassifierPrefixSet:5
|
||||
#if defined(PR_LOGGING)
|
||||
static const PRLogModuleInfo *gUrlClassifierPrefixSetLog = nullptr;
|
||||
#define LOG(args) PR_LOG(gUrlClassifierPrefixSetLog, PR_LOG_DEBUG, args)
|
||||
#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierPrefixSetLog, 4)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#define LOG_ENABLED() (false)
|
||||
#endif
|
||||
|
||||
NS_IMPL_ISUPPORTS(
|
||||
nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet, nsIMemoryReporter)
|
||||
|
||||
MOZ_DEFINE_MALLOC_SIZE_OF(UrlClassifierMallocSizeOf)
|
||||
|
||||
nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet()
|
||||
: mTotalPrefixes(0)
|
||||
, mMemoryInUse(0)
|
||||
, mMemoryReportPath()
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!gUrlClassifierPrefixSetLog)
|
||||
gUrlClassifierPrefixSetLog = PR_NewLogModule("UrlClassifierPrefixSet");
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::Init(const nsACString& aName)
|
||||
{
|
||||
mMemoryReportPath =
|
||||
nsPrintfCString(
|
||||
"explicit/storage/prefix-set/%s",
|
||||
(!aName.IsEmpty() ? PromiseFlatCString(aName).get() : "?!")
|
||||
);
|
||||
|
||||
RegisterWeakMemoryReporter(this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet()
|
||||
{
|
||||
UnregisterWeakMemoryReporter(this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::SetPrefixes(const uint32_t* aArray, uint32_t aLength)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (aLength <= 0) {
|
||||
if (mIndexPrefixes.Length() > 0) {
|
||||
LOG(("Clearing PrefixSet"));
|
||||
mIndexDeltas.Clear();
|
||||
mIndexPrefixes.Clear();
|
||||
mTotalPrefixes = 0;
|
||||
}
|
||||
} else {
|
||||
rv = MakePrefixSet(aArray, aLength);
|
||||
}
|
||||
|
||||
mMemoryInUse = SizeOfIncludingThis(UrlClassifierMallocSizeOf);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierPrefixSet::MakePrefixSet(const uint32_t* aPrefixes, uint32_t aLength)
|
||||
{
|
||||
if (aLength == 0) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for (uint32_t i = 1; i < aLength; i++) {
|
||||
MOZ_ASSERT(aPrefixes[i] >= aPrefixes[i-1]);
|
||||
}
|
||||
#endif
|
||||
|
||||
mIndexPrefixes.Clear();
|
||||
mIndexDeltas.Clear();
|
||||
mTotalPrefixes = aLength;
|
||||
|
||||
mIndexPrefixes.AppendElement(aPrefixes[0]);
|
||||
mIndexDeltas.AppendElement();
|
||||
|
||||
uint32_t numOfDeltas = 0;
|
||||
uint32_t totalDeltas = 0;
|
||||
uint32_t currentItem = aPrefixes[0];
|
||||
for (uint32_t i = 1; i < aLength; i++) {
|
||||
if ((numOfDeltas >= DELTAS_LIMIT) ||
|
||||
(aPrefixes[i] - currentItem >= MAX_INDEX_DIFF)) {
|
||||
mIndexDeltas.AppendElement();
|
||||
mIndexDeltas[mIndexDeltas.Length() - 1].Compact();
|
||||
mIndexPrefixes.AppendElement(aPrefixes[i]);
|
||||
numOfDeltas = 0;
|
||||
} else {
|
||||
uint16_t delta = aPrefixes[i] - currentItem;
|
||||
mIndexDeltas[mIndexDeltas.Length() - 1].AppendElement(delta);
|
||||
numOfDeltas++;
|
||||
totalDeltas++;
|
||||
}
|
||||
currentItem = aPrefixes[i];
|
||||
}
|
||||
|
||||
mIndexPrefixes.Compact();
|
||||
mIndexDeltas.Compact();
|
||||
|
||||
LOG(("Total number of indices: %d", aLength));
|
||||
LOG(("Total number of deltas: %d", totalDeltas));
|
||||
LOG(("Total number of delta chunks: %d", mIndexDeltas.Length()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierPrefixSet::GetPrefixesNative(FallibleTArray<uint32_t>& outArray)
|
||||
{
|
||||
if (!outArray.SetLength(mTotalPrefixes)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
uint32_t prefixIdxLength = mIndexPrefixes.Length();
|
||||
uint32_t prefixCnt = 0;
|
||||
|
||||
for (uint32_t i = 0; i < prefixIdxLength; i++) {
|
||||
uint32_t prefix = mIndexPrefixes[i];
|
||||
|
||||
outArray[prefixCnt++] = prefix;
|
||||
for (uint32_t j = 0; j < mIndexDeltas[i].Length(); j++) {
|
||||
prefix += mIndexDeltas[i][j];
|
||||
outArray[prefixCnt++] = prefix;
|
||||
}
|
||||
}
|
||||
|
||||
NS_ASSERTION(mTotalPrefixes == prefixCnt, "Lengths are inconsistent");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::GetPrefixes(uint32_t* aCount,
|
||||
uint32_t** aPrefixes)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCount);
|
||||
*aCount = 0;
|
||||
NS_ENSURE_ARG_POINTER(aPrefixes);
|
||||
*aPrefixes = nullptr;
|
||||
|
||||
FallibleTArray<uint32_t> prefixes;
|
||||
nsresult rv = GetPrefixesNative(prefixes);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint64_t itemCount = prefixes.Length();
|
||||
uint32_t* prefixArray = static_cast<uint32_t*>(nsMemory::Alloc(itemCount * sizeof(uint32_t)));
|
||||
NS_ENSURE_TRUE(prefixArray, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
memcpy(prefixArray, prefixes.Elements(), sizeof(uint32_t) * itemCount);
|
||||
|
||||
*aCount = itemCount;
|
||||
*aPrefixes = prefixArray;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t nsUrlClassifierPrefixSet::BinSearch(uint32_t start,
|
||||
uint32_t end,
|
||||
uint32_t target)
|
||||
{
|
||||
while (start != end && end >= start) {
|
||||
uint32_t i = start + ((end - start) >> 1);
|
||||
uint32_t value = mIndexPrefixes[i];
|
||||
if (value < target) {
|
||||
start = i + 1;
|
||||
} else if (value > target) {
|
||||
end = i - 1;
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::Contains(uint32_t aPrefix, bool* aFound)
|
||||
{
|
||||
*aFound = false;
|
||||
|
||||
if (mIndexPrefixes.Length() == 0) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t target = aPrefix;
|
||||
|
||||
// We want to do a "Price is Right" binary search, that is, we want to find
|
||||
// the index of the value either equal to the target or the closest value
|
||||
// that is less than the target.
|
||||
//
|
||||
if (target < mIndexPrefixes[0]) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// |binsearch| does not necessarily return the correct index (when the
|
||||
// target is not found) but rather it returns an index at least one away
|
||||
// from the correct index.
|
||||
// Because of this, we need to check if the target lies before the beginning
|
||||
// of the indices.
|
||||
|
||||
uint32_t i = BinSearch(0, mIndexPrefixes.Length() - 1, target);
|
||||
if (mIndexPrefixes[i] > target && i > 0) {
|
||||
i--;
|
||||
}
|
||||
|
||||
// Now search through the deltas for the target.
|
||||
uint32_t diff = target - mIndexPrefixes[i];
|
||||
uint32_t deltaSize = mIndexDeltas[i].Length();
|
||||
uint32_t deltaIndex = 0;
|
||||
|
||||
while (diff > 0 && deltaIndex < deltaSize) {
|
||||
diff -= mIndexDeltas[i][deltaIndex];
|
||||
deltaIndex++;
|
||||
}
|
||||
|
||||
if (diff == 0) {
|
||||
*aFound = true;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize)
|
||||
{
|
||||
return aHandleReport->Callback(
|
||||
EmptyCString(), mMemoryReportPath, KIND_HEAP, UNITS_BYTES,
|
||||
mMemoryInUse,
|
||||
NS_LITERAL_CSTRING("Memory used by the prefix set for a URL classifier."),
|
||||
aData);
|
||||
}
|
||||
|
||||
size_t
|
||||
nsUrlClassifierPrefixSet::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||
{
|
||||
size_t n = 0;
|
||||
n += aMallocSizeOf(this);
|
||||
n += mIndexDeltas.SizeOfExcludingThis(aMallocSizeOf);
|
||||
for (uint32_t i = 0; i < mIndexDeltas.Length(); i++) {
|
||||
n += mIndexDeltas[i].SizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
n += mIndexPrefixes.SizeOfExcludingThis(aMallocSizeOf);
|
||||
return n;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::IsEmpty(bool * aEmpty)
|
||||
{
|
||||
*aEmpty = (mIndexPrefixes.Length() == 0);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose& fileFd)
|
||||
{
|
||||
uint32_t magic;
|
||||
int32_t read;
|
||||
|
||||
read = PR_Read(fileFd, &magic, sizeof(uint32_t));
|
||||
NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
|
||||
|
||||
if (magic == PREFIXSET_VERSION_MAGIC) {
|
||||
uint32_t indexSize;
|
||||
uint32_t deltaSize;
|
||||
|
||||
read = PR_Read(fileFd, &indexSize, sizeof(uint32_t));
|
||||
NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FILE_CORRUPTED);
|
||||
read = PR_Read(fileFd, &deltaSize, sizeof(uint32_t));
|
||||
NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FILE_CORRUPTED);
|
||||
|
||||
if (indexSize == 0) {
|
||||
LOG(("stored PrefixSet is empty!"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (deltaSize > (indexSize * DELTAS_LIMIT)) {
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
nsTArray<uint32_t> indexStarts;
|
||||
indexStarts.SetLength(indexSize);
|
||||
mIndexPrefixes.SetLength(indexSize);
|
||||
mIndexDeltas.SetLength(indexSize);
|
||||
|
||||
mTotalPrefixes = indexSize;
|
||||
|
||||
int32_t toRead = indexSize*sizeof(uint32_t);
|
||||
read = PR_Read(fileFd, mIndexPrefixes.Elements(), toRead);
|
||||
NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
|
||||
read = PR_Read(fileFd, indexStarts.Elements(), toRead);
|
||||
NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
|
||||
if (indexSize != 0 && indexStarts[0] != 0) {
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
for (uint32_t i = 0; i < indexSize; i++) {
|
||||
uint32_t numInDelta = i == indexSize - 1 ? deltaSize - indexStarts[i]
|
||||
: indexStarts[i + 1] - indexStarts[i];
|
||||
if (numInDelta > 0) {
|
||||
mIndexDeltas[i].SetLength(numInDelta);
|
||||
mTotalPrefixes += numInDelta;
|
||||
toRead = numInDelta * sizeof(uint16_t);
|
||||
read = PR_Read(fileFd, mIndexDeltas[i].Elements(), toRead);
|
||||
NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG(("Version magic mismatch, not loading"));
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mIndexPrefixes.Length() == mIndexDeltas.Length());
|
||||
LOG(("Loading PrefixSet successful"));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::LoadFromFile(nsIFile* aFile)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FILELOAD_TIME> timer;
|
||||
|
||||
nsresult rv;
|
||||
AutoFDClose fileFd;
|
||||
rv = aFile->OpenNSPRFileDesc(PR_RDONLY | nsIFile::OS_READAHEAD,
|
||||
0, &fileFd.rwget());
|
||||
if (!NS_FAILED(rv)) {
|
||||
rv = LoadFromFd(fileFd);
|
||||
mMemoryInUse = SizeOfIncludingThis(UrlClassifierMallocSizeOf);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose& fileFd)
|
||||
{
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FALLOCATE_TIME> timer;
|
||||
int64_t size = 4 * sizeof(uint32_t);
|
||||
uint32_t deltas = mTotalPrefixes - mIndexPrefixes.Length();
|
||||
size += 2 * mIndexPrefixes.Length() * sizeof(uint32_t);
|
||||
size += deltas * sizeof(uint16_t);
|
||||
|
||||
mozilla::fallocate(fileFd, size);
|
||||
}
|
||||
|
||||
int32_t written;
|
||||
int32_t writelen = sizeof(uint32_t);
|
||||
uint32_t magic = PREFIXSET_VERSION_MAGIC;
|
||||
written = PR_Write(fileFd, &magic, writelen);
|
||||
NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
|
||||
|
||||
uint32_t indexSize = mIndexPrefixes.Length();
|
||||
uint32_t indexDeltaSize = mIndexDeltas.Length();
|
||||
uint32_t totalDeltas = 0;
|
||||
|
||||
// Store the shape of mIndexDeltas by noting at which "count" of total
|
||||
// indexes a new subarray starts. This is slightly cumbersome but keeps
|
||||
// file format compatibility.
|
||||
// If we ever update the format, we can gain space by storing the delta
|
||||
// subarray sizes, which fit in bytes.
|
||||
nsTArray<uint32_t> indexStarts;
|
||||
indexStarts.AppendElement(0);
|
||||
|
||||
for (uint32_t i = 0; i < indexDeltaSize; i++) {
|
||||
uint32_t deltaLength = mIndexDeltas[i].Length();
|
||||
totalDeltas += deltaLength;
|
||||
indexStarts.AppendElement(totalDeltas);
|
||||
}
|
||||
|
||||
written = PR_Write(fileFd, &indexSize, writelen);
|
||||
NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
|
||||
written = PR_Write(fileFd, &totalDeltas, writelen);
|
||||
NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
|
||||
|
||||
writelen = indexSize * sizeof(uint32_t);
|
||||
written = PR_Write(fileFd, mIndexPrefixes.Elements(), writelen);
|
||||
NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
|
||||
written = PR_Write(fileFd, indexStarts.Elements(), writelen);
|
||||
NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
|
||||
if (totalDeltas > 0) {
|
||||
for (uint32_t i = 0; i < indexDeltaSize; i++) {
|
||||
writelen = mIndexDeltas[i].Length() * sizeof(uint16_t);
|
||||
written = PR_Write(fileFd, mIndexDeltas[i].Elements(), writelen);
|
||||
NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
LOG(("Saving PrefixSet successful\n"));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::StoreToFile(nsIFile* aFile)
|
||||
{
|
||||
AutoFDClose fileFd;
|
||||
nsresult rv = aFile->OpenNSPRFileDesc(PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE,
|
||||
0644, &fileFd.rwget());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return StoreToFd(fileFd);
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsUrlClassifierPrefixSet_h_
|
||||
#define nsUrlClassifierPrefixSet_h_
|
||||
|
||||
#include "nsISupportsUtils.h"
|
||||
#include "nsID.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsIUrlClassifierPrefixSet.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
|
||||
class nsUrlClassifierPrefixSet final
|
||||
: public nsIUrlClassifierPrefixSet
|
||||
, public nsIMemoryReporter
|
||||
{
|
||||
public:
|
||||
nsUrlClassifierPrefixSet();
|
||||
|
||||
NS_IMETHOD Init(const nsACString& aName) override;
|
||||
NS_IMETHOD SetPrefixes(const uint32_t* aArray, uint32_t aLength) override;
|
||||
NS_IMETHOD GetPrefixes(uint32_t* aCount, uint32_t** aPrefixes) override;
|
||||
NS_IMETHOD Contains(uint32_t aPrefix, bool* aFound) override;
|
||||
NS_IMETHOD IsEmpty(bool* aEmpty) override;
|
||||
NS_IMETHOD LoadFromFile(nsIFile* aFile) override;
|
||||
NS_IMETHOD StoreToFile(nsIFile* aFile) override;
|
||||
|
||||
nsresult GetPrefixesNative(FallibleTArray<uint32_t>& outArray);
|
||||
size_t SizeInMemory() { return mMemoryInUse; };
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIMEMORYREPORTER
|
||||
|
||||
protected:
|
||||
virtual ~nsUrlClassifierPrefixSet();
|
||||
|
||||
static const uint32_t DELTAS_LIMIT = 120;
|
||||
static const uint32_t MAX_INDEX_DIFF = (1 << 16);
|
||||
static const uint32_t PREFIXSET_VERSION_MAGIC = 1;
|
||||
|
||||
nsresult MakePrefixSet(const uint32_t* aArray, uint32_t aLength);
|
||||
uint32_t BinSearch(uint32_t start, uint32_t end, uint32_t target);
|
||||
nsresult LoadFromFd(mozilla::AutoFDClose& fileFd);
|
||||
nsresult StoreToFd(mozilla::AutoFDClose& fileFd);
|
||||
|
||||
// Return the estimated size of the set on disk and in memory, in bytes.
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
// list of fully stored prefixes, that also form the
|
||||
// start of a run of deltas in mIndexDeltas.
|
||||
nsTArray<uint32_t> mIndexPrefixes;
|
||||
// array containing arrays of deltas from indices.
|
||||
// Index to the place that matches the closest lower
|
||||
// prefix from mIndexPrefix. Then every "delta" corresponds
|
||||
// to a prefix in the PrefixSet.
|
||||
nsTArray<nsTArray<uint16_t> > mIndexDeltas;
|
||||
// how many prefixes we have.
|
||||
uint32_t mTotalPrefixes;
|
||||
|
||||
// memory report collection might happen while we're updating the prefixset
|
||||
// on another thread, so pre-compute and remember the size (bug 1050108).
|
||||
mozilla::Atomic<size_t> mMemoryInUse;
|
||||
nsCString mMemoryReportPath;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,319 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsUrlClassifierProxies.h"
|
||||
#include "nsUrlClassifierDBService.h"
|
||||
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
|
||||
using namespace mozilla::safebrowsing;
|
||||
|
||||
static nsresult
|
||||
DispatchToWorkerThread(nsIRunnable* r)
|
||||
{
|
||||
nsIThread* t = nsUrlClassifierDBService::BackgroundThread();
|
||||
if (!t)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return t->Dispatch(r, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(UrlClassifierDBServiceWorkerProxy,
|
||||
nsIUrlClassifierDBServiceWorker)
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::Lookup(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aTables,
|
||||
nsIUrlClassifierCallback* aCB)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r = new LookupRunnable(mTarget, aPrincipal, aTables,
|
||||
aCB);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::LookupRunnable::Run()
|
||||
{
|
||||
(void) mTarget->Lookup(mPrincipal, mLookupTables, mCB);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::GetTables(nsIUrlClassifierCallback* aCB)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r = new GetTablesRunnable(mTarget, aCB);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::GetTablesRunnable::Run()
|
||||
{
|
||||
mTarget->GetTables(mCB);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::SetHashCompleter
|
||||
(const nsACString&, nsIUrlClassifierHashCompleter*)
|
||||
{
|
||||
NS_NOTREACHED("This method should not be called!");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::BeginUpdate
|
||||
(nsIUrlClassifierUpdateObserver* aUpdater,
|
||||
const nsACString& aTables)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r = new BeginUpdateRunnable(mTarget, aUpdater,
|
||||
aTables);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::BeginUpdateRunnable::Run()
|
||||
{
|
||||
mTarget->BeginUpdate(mUpdater, mTables);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::BeginStream(const nsACString& aTable)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
new BeginStreamRunnable(mTarget, aTable);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::BeginStreamRunnable::Run()
|
||||
{
|
||||
mTarget->BeginStream(mTable);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::UpdateStream(const nsACString& aUpdateChunk)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
new UpdateStreamRunnable(mTarget, aUpdateChunk);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::UpdateStreamRunnable::Run()
|
||||
{
|
||||
mTarget->UpdateStream(mUpdateChunk);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::FinishStream()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(mTarget,
|
||||
&nsIUrlClassifierDBServiceWorker::FinishStream);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::DoLocalLookupRunnable::Run()
|
||||
{
|
||||
mTarget->DoLocalLookup(mSpec, mTables, mResults);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
UrlClassifierDBServiceWorkerProxy::DoLocalLookup(const nsACString& spec,
|
||||
const nsACString& tables,
|
||||
LookupResultArray* results)
|
||||
|
||||
{
|
||||
// Run synchronously on background thread. NS_DISPATCH_SYNC does *not* do
|
||||
// what we want -- it continues processing events on the main thread loop
|
||||
// before the Dispatch returns.
|
||||
nsCOMPtr<nsIRunnable> r = new DoLocalLookupRunnable(mTarget, spec, tables, results);
|
||||
nsIThread* t = nsUrlClassifierDBService::BackgroundThread();
|
||||
if (!t)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
mozilla::SyncRunnable::DispatchToThread(t, r);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::FinishUpdate()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(mTarget,
|
||||
&nsIUrlClassifierDBServiceWorker::FinishUpdate);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::CancelUpdate()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(mTarget,
|
||||
&nsIUrlClassifierDBServiceWorker::CancelUpdate);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::ResetDatabase()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(mTarget,
|
||||
&nsIUrlClassifierDBServiceWorker::ResetDatabase);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::OpenDb()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(mTarget,
|
||||
&nsIUrlClassifierDBServiceWorker::OpenDb);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::CloseDb()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(mTarget,
|
||||
&nsIUrlClassifierDBServiceWorker::CloseDb);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::CacheCompletions(CacheResultArray * aEntries)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r = new CacheCompletionsRunnable(mTarget, aEntries);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::CacheCompletionsRunnable::Run()
|
||||
{
|
||||
mTarget->CacheCompletions(mEntries);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::CacheMisses(PrefixArray * aEntries)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r = new CacheMissesRunnable(mTarget, aEntries);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::CacheMissesRunnable::Run()
|
||||
{
|
||||
mTarget->CacheMisses(mEntries);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_ISUPPORTS(UrlClassifierLookupCallbackProxy,
|
||||
nsIUrlClassifierLookupCallback)
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierLookupCallbackProxy::LookupComplete
|
||||
(LookupResultArray * aResults)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r = new LookupCompleteRunnable(mTarget, aResults);
|
||||
return NS_DispatchToMainThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierLookupCallbackProxy::LookupCompleteRunnable::Run()
|
||||
{
|
||||
mTarget->LookupComplete(mResults);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(UrlClassifierCallbackProxy,
|
||||
nsIUrlClassifierCallback)
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierCallbackProxy::HandleEvent(const nsACString& aValue)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r = new HandleEventRunnable(mTarget, aValue);
|
||||
return NS_DispatchToMainThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierCallbackProxy::HandleEventRunnable::Run()
|
||||
{
|
||||
mTarget->HandleEvent(mValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(UrlClassifierUpdateObserverProxy,
|
||||
nsIUrlClassifierUpdateObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierUpdateObserverProxy::UpdateUrlRequested
|
||||
(const nsACString& aURL,
|
||||
const nsACString& aTable)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
new UpdateUrlRequestedRunnable(mTarget, aURL, aTable);
|
||||
return NS_DispatchToMainThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierUpdateObserverProxy::UpdateUrlRequestedRunnable::Run()
|
||||
{
|
||||
mTarget->UpdateUrlRequested(mURL, mTable);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierUpdateObserverProxy::StreamFinished(nsresult aStatus,
|
||||
uint32_t aDelay)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
new StreamFinishedRunnable(mTarget, aStatus, aDelay);
|
||||
return NS_DispatchToMainThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierUpdateObserverProxy::StreamFinishedRunnable::Run()
|
||||
{
|
||||
mTarget->StreamFinished(mStatus, mDelay);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierUpdateObserverProxy::UpdateError(nsresult aError)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
new UpdateErrorRunnable(mTarget, aError);
|
||||
return NS_DispatchToMainThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierUpdateObserverProxy::UpdateErrorRunnable::Run()
|
||||
{
|
||||
mTarget->UpdateError(mError);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierUpdateObserverProxy::UpdateSuccess(uint32_t aRequestedTimeout)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
new UpdateSuccessRunnable(mTarget, aRequestedTimeout);
|
||||
return NS_DispatchToMainThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierUpdateObserverProxy::UpdateSuccessRunnable::Run()
|
||||
{
|
||||
mTarget->UpdateSuccess(mRequestedTimeout);
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -1,339 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsUrlClassifierProxies_h
|
||||
#define nsUrlClassifierProxies_h
|
||||
|
||||
#include "nsIUrlClassifierDBService.h"
|
||||
#include "nsUrlClassifierDBService.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "LookupCache.h"
|
||||
|
||||
|
||||
/**
|
||||
* Thread proxy from the main thread to the worker thread.
|
||||
*/
|
||||
class UrlClassifierDBServiceWorkerProxy final :
|
||||
public nsIUrlClassifierDBServiceWorker
|
||||
{
|
||||
public:
|
||||
explicit UrlClassifierDBServiceWorkerProxy(nsUrlClassifierDBServiceWorker* aTarget)
|
||||
: mTarget(aTarget)
|
||||
{ }
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIURLCLASSIFIERDBSERVICE
|
||||
NS_DECL_NSIURLCLASSIFIERDBSERVICEWORKER
|
||||
|
||||
class LookupRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
LookupRunnable(nsUrlClassifierDBServiceWorker* aTarget,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aTables,
|
||||
nsIUrlClassifierCallback* aCB)
|
||||
: mTarget(aTarget)
|
||||
, mPrincipal(aPrincipal)
|
||||
, mLookupTables(aTables)
|
||||
, mCB(aCB)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsRefPtr<nsUrlClassifierDBServiceWorker> mTarget;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCString mLookupTables;
|
||||
nsCOMPtr<nsIUrlClassifierCallback> mCB;
|
||||
};
|
||||
|
||||
class GetTablesRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
GetTablesRunnable(nsUrlClassifierDBServiceWorker* aTarget,
|
||||
nsIUrlClassifierCallback* aCB)
|
||||
: mTarget(aTarget)
|
||||
, mCB(aCB)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsRefPtr<nsUrlClassifierDBServiceWorker> mTarget;
|
||||
nsCOMPtr<nsIUrlClassifierCallback> mCB;
|
||||
};
|
||||
|
||||
class BeginUpdateRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
BeginUpdateRunnable(nsUrlClassifierDBServiceWorker* aTarget,
|
||||
nsIUrlClassifierUpdateObserver* aUpdater,
|
||||
const nsACString& aTables)
|
||||
: mTarget(aTarget)
|
||||
, mUpdater(aUpdater)
|
||||
, mTables(aTables)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsRefPtr<nsUrlClassifierDBServiceWorker> mTarget;
|
||||
nsCOMPtr<nsIUrlClassifierUpdateObserver> mUpdater;
|
||||
nsCString mTables;
|
||||
};
|
||||
|
||||
class BeginStreamRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
BeginStreamRunnable(nsUrlClassifierDBServiceWorker* aTarget,
|
||||
const nsACString& aTable)
|
||||
: mTarget(aTarget)
|
||||
, mTable(aTable)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsRefPtr<nsUrlClassifierDBServiceWorker> mTarget;
|
||||
nsCString mTable;
|
||||
};
|
||||
|
||||
class UpdateStreamRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
UpdateStreamRunnable(nsUrlClassifierDBServiceWorker* aTarget,
|
||||
const nsACString& aUpdateChunk)
|
||||
: mTarget(aTarget)
|
||||
, mUpdateChunk(aUpdateChunk)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsRefPtr<nsUrlClassifierDBServiceWorker> mTarget;
|
||||
nsCString mUpdateChunk;
|
||||
};
|
||||
|
||||
class CacheCompletionsRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
CacheCompletionsRunnable(nsUrlClassifierDBServiceWorker* aTarget,
|
||||
mozilla::safebrowsing::CacheResultArray *aEntries)
|
||||
: mTarget(aTarget)
|
||||
, mEntries(aEntries)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsRefPtr<nsUrlClassifierDBServiceWorker> mTarget;
|
||||
mozilla::safebrowsing::CacheResultArray *mEntries;
|
||||
};
|
||||
|
||||
class CacheMissesRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
CacheMissesRunnable(nsUrlClassifierDBServiceWorker* aTarget,
|
||||
mozilla::safebrowsing::PrefixArray *aEntries)
|
||||
: mTarget(aTarget)
|
||||
, mEntries(aEntries)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsRefPtr<nsUrlClassifierDBServiceWorker> mTarget;
|
||||
mozilla::safebrowsing::PrefixArray *mEntries;
|
||||
};
|
||||
|
||||
class DoLocalLookupRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
DoLocalLookupRunnable(nsUrlClassifierDBServiceWorker* aTarget,
|
||||
const nsACString& spec,
|
||||
const nsACString& tables,
|
||||
mozilla::safebrowsing::LookupResultArray* results)
|
||||
: mTarget(aTarget)
|
||||
, mSpec(spec)
|
||||
, mTables(tables)
|
||||
, mResults(results)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
private:
|
||||
nsRefPtr<nsUrlClassifierDBServiceWorker> mTarget;
|
||||
|
||||
nsCString mSpec;
|
||||
nsCString mTables;
|
||||
mozilla::safebrowsing::LookupResultArray* mResults;
|
||||
};
|
||||
|
||||
public:
|
||||
nsresult DoLocalLookup(const nsACString& spec,
|
||||
const nsACString& tables,
|
||||
mozilla::safebrowsing::LookupResultArray* results);
|
||||
|
||||
private:
|
||||
~UrlClassifierDBServiceWorkerProxy() {}
|
||||
|
||||
nsRefPtr<nsUrlClassifierDBServiceWorker> mTarget;
|
||||
};
|
||||
|
||||
// The remaining classes here are all proxies to the main thread
|
||||
|
||||
class UrlClassifierLookupCallbackProxy final :
|
||||
public nsIUrlClassifierLookupCallback
|
||||
{
|
||||
public:
|
||||
explicit UrlClassifierLookupCallbackProxy(nsIUrlClassifierLookupCallback* aTarget)
|
||||
: mTarget(new nsMainThreadPtrHolder<nsIUrlClassifierLookupCallback>(aTarget))
|
||||
{ }
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIURLCLASSIFIERLOOKUPCALLBACK
|
||||
|
||||
class LookupCompleteRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
LookupCompleteRunnable(const nsMainThreadPtrHandle<nsIUrlClassifierLookupCallback>& aTarget,
|
||||
mozilla::safebrowsing::LookupResultArray *aResults)
|
||||
: mTarget(aTarget)
|
||||
, mResults(aResults)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsIUrlClassifierLookupCallback> mTarget;
|
||||
mozilla::safebrowsing::LookupResultArray * mResults;
|
||||
};
|
||||
|
||||
private:
|
||||
~UrlClassifierLookupCallbackProxy() {}
|
||||
|
||||
nsMainThreadPtrHandle<nsIUrlClassifierLookupCallback> mTarget;
|
||||
};
|
||||
|
||||
class UrlClassifierCallbackProxy final : public nsIUrlClassifierCallback
|
||||
{
|
||||
public:
|
||||
explicit UrlClassifierCallbackProxy(nsIUrlClassifierCallback* aTarget)
|
||||
: mTarget(new nsMainThreadPtrHolder<nsIUrlClassifierCallback>(aTarget))
|
||||
{ }
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIURLCLASSIFIERCALLBACK
|
||||
|
||||
class HandleEventRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
HandleEventRunnable(const nsMainThreadPtrHandle<nsIUrlClassifierCallback>& aTarget,
|
||||
const nsACString& aValue)
|
||||
: mTarget(aTarget)
|
||||
, mValue(aValue)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsIUrlClassifierCallback> mTarget;
|
||||
nsCString mValue;
|
||||
};
|
||||
|
||||
private:
|
||||
~UrlClassifierCallbackProxy() {}
|
||||
|
||||
nsMainThreadPtrHandle<nsIUrlClassifierCallback> mTarget;
|
||||
};
|
||||
|
||||
class UrlClassifierUpdateObserverProxy final :
|
||||
public nsIUrlClassifierUpdateObserver
|
||||
{
|
||||
public:
|
||||
explicit UrlClassifierUpdateObserverProxy(nsIUrlClassifierUpdateObserver* aTarget)
|
||||
: mTarget(new nsMainThreadPtrHolder<nsIUrlClassifierUpdateObserver>(aTarget))
|
||||
{ }
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIURLCLASSIFIERUPDATEOBSERVER
|
||||
|
||||
class UpdateUrlRequestedRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
UpdateUrlRequestedRunnable(const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver>& aTarget,
|
||||
const nsACString& aURL,
|
||||
const nsACString& aTable)
|
||||
: mTarget(aTarget)
|
||||
, mURL(aURL)
|
||||
, mTable(aTable)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver> mTarget;
|
||||
nsCString mURL, mTable;
|
||||
};
|
||||
|
||||
class StreamFinishedRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
StreamFinishedRunnable(const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver>& aTarget,
|
||||
nsresult aStatus, uint32_t aDelay)
|
||||
: mTarget(aTarget)
|
||||
, mStatus(aStatus)
|
||||
, mDelay(aDelay)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver> mTarget;
|
||||
nsresult mStatus;
|
||||
uint32_t mDelay;
|
||||
};
|
||||
|
||||
class UpdateErrorRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
UpdateErrorRunnable(const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver>& aTarget,
|
||||
nsresult aError)
|
||||
: mTarget(aTarget)
|
||||
, mError(aError)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver> mTarget;
|
||||
nsresult mError;
|
||||
};
|
||||
|
||||
class UpdateSuccessRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
UpdateSuccessRunnable(const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver>& aTarget,
|
||||
uint32_t aRequestedTimeout)
|
||||
: mTarget(aTarget)
|
||||
, mRequestedTimeout(aRequestedTimeout)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver> mTarget;
|
||||
uint32_t mRequestedTimeout;
|
||||
};
|
||||
|
||||
private:
|
||||
~UrlClassifierUpdateObserverProxy() {}
|
||||
|
||||
nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver> mTarget;
|
||||
};
|
||||
|
||||
#endif // nsUrlClassifierProxies_h
|
||||
@@ -1,629 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCRT.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIStringStream.h"
|
||||
#include "nsIUploadChannel.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIUrlClassifierDBService.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
#include "nsUrlClassifierStreamUpdater.h"
|
||||
#include "prlog.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "mozilla/LoadContext.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
static const char* gQuitApplicationMessage = "quit-application";
|
||||
|
||||
#undef LOG
|
||||
|
||||
// NSPR_LOG_MODULES=UrlClassifierStreamUpdater:5
|
||||
#if defined(PR_LOGGING)
|
||||
static const PRLogModuleInfo *gUrlClassifierStreamUpdaterLog = nullptr;
|
||||
#define LOG(args) PR_LOG(gUrlClassifierStreamUpdaterLog, PR_LOG_DEBUG, args)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#endif
|
||||
|
||||
|
||||
// This class does absolutely nothing, except pass requests onto the DBService.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// nsIUrlClassiferStreamUpdater implementation
|
||||
// Handles creating/running the stream listener
|
||||
|
||||
nsUrlClassifierStreamUpdater::nsUrlClassifierStreamUpdater()
|
||||
: mIsUpdating(false), mInitialized(false), mDownloadError(false),
|
||||
mBeganStream(false), mChannel(nullptr)
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!gUrlClassifierStreamUpdaterLog)
|
||||
gUrlClassifierStreamUpdaterLog = PR_NewLogModule("UrlClassifierStreamUpdater");
|
||||
#endif
|
||||
LOG(("nsUrlClassifierStreamUpdater init [this=%p]", this));
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsUrlClassifierStreamUpdater,
|
||||
nsIUrlClassifierStreamUpdater,
|
||||
nsIUrlClassifierUpdateObserver,
|
||||
nsIRequestObserver,
|
||||
nsIStreamListener,
|
||||
nsIObserver,
|
||||
nsIInterfaceRequestor,
|
||||
nsITimerCallback)
|
||||
|
||||
/**
|
||||
* Clear out the update.
|
||||
*/
|
||||
void
|
||||
nsUrlClassifierStreamUpdater::DownloadDone()
|
||||
{
|
||||
LOG(("nsUrlClassifierStreamUpdater::DownloadDone [this=%p]", this));
|
||||
mIsUpdating = false;
|
||||
|
||||
mPendingUpdates.Clear();
|
||||
mDownloadError = false;
|
||||
mSuccessCallback = nullptr;
|
||||
mUpdateErrorCallback = nullptr;
|
||||
mDownloadErrorCallback = nullptr;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// nsIUrlClassifierStreamUpdater implementation
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl,
|
||||
const nsACString & aRequestBody,
|
||||
const nsACString & aStreamTable)
|
||||
{
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsCString spec;
|
||||
aUpdateUrl->GetSpec(spec);
|
||||
LOG(("Fetching update %s from %s", aRequestBody.Data(), spec.get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult rv;
|
||||
uint32_t loadFlags = nsIChannel::INHIBIT_CACHING |
|
||||
nsIChannel::LOAD_BYPASS_CACHE;
|
||||
rv = NS_NewChannel(getter_AddRefs(mChannel),
|
||||
aUpdateUrl,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
nsIContentPolicy::TYPE_OTHER,
|
||||
nullptr, // aLoadGroup
|
||||
this, // aInterfaceRequestor
|
||||
loadFlags);
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mBeganStream = false;
|
||||
|
||||
// If aRequestBody is empty, construct it for the test.
|
||||
if (!aRequestBody.IsEmpty()) {
|
||||
rv = AddRequestBody(aRequestBody);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Set the appropriate content type for file/data URIs, for unit testing
|
||||
// purposes.
|
||||
// This is only used for testing and should be deleted.
|
||||
bool match;
|
||||
if ((NS_SUCCEEDED(aUpdateUrl->SchemeIs("file", &match)) && match) ||
|
||||
(NS_SUCCEEDED(aUpdateUrl->SchemeIs("data", &match)) && match)) {
|
||||
mChannel->SetContentType(NS_LITERAL_CSTRING("application/vnd.google.safebrowsing-update"));
|
||||
} else {
|
||||
// We assume everything else is an HTTP request.
|
||||
|
||||
// Disable keepalive.
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Connection"), NS_LITERAL_CSTRING("close"), false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Create a custom LoadContext for SafeBrowsing, so we can use callbacks on
|
||||
// the channel to query the appId which allows separation of safebrowsing
|
||||
// cookies in a separate jar.
|
||||
nsCOMPtr<nsIInterfaceRequestor> sbContext =
|
||||
new mozilla::LoadContext(NECKO_SAFEBROWSING_APP_ID);
|
||||
rv = mChannel->SetNotificationCallbacks(sbContext);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Make the request.
|
||||
rv = mChannel->AsyncOpen(this, nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mStreamTable = aStreamTable;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl,
|
||||
const nsACString & aRequestBody,
|
||||
const nsACString & aStreamTable)
|
||||
{
|
||||
LOG(("(pre) Fetching update from %s\n", PromiseFlatCString(aUpdateUrl).get()));
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), aUpdateUrl);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString urlSpec;
|
||||
uri->GetAsciiSpec(urlSpec);
|
||||
|
||||
LOG(("(post) Fetching update from %s\n", urlSpec.get()));
|
||||
|
||||
return FetchUpdate(uri, aRequestBody, aStreamTable);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::DownloadUpdates(
|
||||
const nsACString &aRequestTables,
|
||||
const nsACString &aRequestBody,
|
||||
const nsACString &aUpdateUrl,
|
||||
nsIUrlClassifierCallback *aSuccessCallback,
|
||||
nsIUrlClassifierCallback *aUpdateErrorCallback,
|
||||
nsIUrlClassifierCallback *aDownloadErrorCallback,
|
||||
bool *_retval)
|
||||
{
|
||||
NS_ENSURE_ARG(aSuccessCallback);
|
||||
NS_ENSURE_ARG(aUpdateErrorCallback);
|
||||
NS_ENSURE_ARG(aDownloadErrorCallback);
|
||||
|
||||
if (mIsUpdating) {
|
||||
LOG(("Already updating, queueing update %s from %s", aRequestBody.Data(),
|
||||
aUpdateUrl.Data()));
|
||||
*_retval = false;
|
||||
PendingRequest *request = mPendingRequests.AppendElement();
|
||||
request->mTables = aRequestTables;
|
||||
request->mRequest = aRequestBody;
|
||||
request->mUrl = aUpdateUrl;
|
||||
request->mSuccessCallback = aSuccessCallback;
|
||||
request->mUpdateErrorCallback = aUpdateErrorCallback;
|
||||
request->mDownloadErrorCallback = aDownloadErrorCallback;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aUpdateUrl.IsEmpty()) {
|
||||
NS_ERROR("updateUrl not set");
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (!mInitialized) {
|
||||
// Add an observer for shutdown so we can cancel any pending list
|
||||
// downloads. quit-application is the same event that the download
|
||||
// manager listens for and uses to cancel pending downloads.
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (!observerService)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
observerService->AddObserver(this, gQuitApplicationMessage, false);
|
||||
|
||||
mDBService = do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
rv = mDBService->BeginUpdate(this, aRequestTables);
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
LOG(("Service busy, already updating, queuing update %s from %s",
|
||||
aRequestBody.Data(), aUpdateUrl.Data()));
|
||||
*_retval = false;
|
||||
PendingRequest *request = mPendingRequests.AppendElement();
|
||||
request->mTables = aRequestTables;
|
||||
request->mRequest = aRequestBody;
|
||||
request->mUrl = aUpdateUrl;
|
||||
request->mSuccessCallback = aSuccessCallback;
|
||||
request->mUpdateErrorCallback = aUpdateErrorCallback;
|
||||
request->mDownloadErrorCallback = aDownloadErrorCallback;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mSuccessCallback = aSuccessCallback;
|
||||
mUpdateErrorCallback = aUpdateErrorCallback;
|
||||
mDownloadErrorCallback = aDownloadErrorCallback;
|
||||
|
||||
mIsUpdating = true;
|
||||
*_retval = true;
|
||||
|
||||
LOG(("FetchUpdate: %s", aUpdateUrl.Data()));
|
||||
//LOG(("requestBody: %s", aRequestBody.Data()));
|
||||
|
||||
return FetchUpdate(aUpdateUrl, aRequestBody, EmptyCString());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// nsIUrlClassifierUpdateObserver implementation
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::UpdateUrlRequested(const nsACString &aUrl,
|
||||
const nsACString &aTable)
|
||||
{
|
||||
LOG(("Queuing requested update from %s\n", PromiseFlatCString(aUrl).get()));
|
||||
|
||||
PendingUpdate *update = mPendingUpdates.AppendElement();
|
||||
if (!update)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
// Allow data: and file: urls for unit testing purposes, otherwise assume http
|
||||
if (StringBeginsWith(aUrl, NS_LITERAL_CSTRING("data:")) ||
|
||||
StringBeginsWith(aUrl, NS_LITERAL_CSTRING("file:"))) {
|
||||
update->mUrl = aUrl;
|
||||
} else {
|
||||
// For unittesting update urls to localhost should use http, not https
|
||||
// (otherwise the connection will fail silently, since there will be no
|
||||
// cert available).
|
||||
if (!StringBeginsWith(aUrl, NS_LITERAL_CSTRING("localhost"))) {
|
||||
update->mUrl = NS_LITERAL_CSTRING("https://") + aUrl;
|
||||
} else {
|
||||
update->mUrl = NS_LITERAL_CSTRING("http://") + aUrl;
|
||||
}
|
||||
}
|
||||
update->mTable = aTable;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierStreamUpdater::FetchNext()
|
||||
{
|
||||
if (mPendingUpdates.Length() == 0) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PendingUpdate &update = mPendingUpdates[0];
|
||||
LOG(("Fetching update url: %s\n", update.mUrl.get()));
|
||||
nsresult rv = FetchUpdate(update.mUrl, EmptyCString(),
|
||||
update.mTable);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("Error fetching update url: %s\n", update.mUrl.get()));
|
||||
// We can commit the urls that we've applied so far. This is
|
||||
// probably a transient server problem, so trigger backoff.
|
||||
mDownloadErrorCallback->HandleEvent(EmptyCString());
|
||||
mDownloadError = true;
|
||||
mDBService->FinishUpdate();
|
||||
return rv;
|
||||
}
|
||||
|
||||
mPendingUpdates.RemoveElementAt(0);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierStreamUpdater::FetchNextRequest()
|
||||
{
|
||||
if (mPendingRequests.Length() == 0) {
|
||||
LOG(("No more requests, returning"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PendingRequest &request = mPendingRequests[0];
|
||||
LOG(("Stream updater: fetching next request: %s, %s",
|
||||
request.mTables.get(), request.mUrl.get()));
|
||||
bool dummy;
|
||||
DownloadUpdates(
|
||||
request.mTables,
|
||||
request.mRequest,
|
||||
request.mUrl,
|
||||
request.mSuccessCallback,
|
||||
request.mUpdateErrorCallback,
|
||||
request.mDownloadErrorCallback,
|
||||
&dummy);
|
||||
request.mSuccessCallback = nullptr;
|
||||
request.mUpdateErrorCallback = nullptr;
|
||||
request.mDownloadErrorCallback = nullptr;
|
||||
mPendingRequests.RemoveElementAt(0);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::StreamFinished(nsresult status,
|
||||
uint32_t requestedDelay)
|
||||
{
|
||||
// We are a service and may not be reset with Init between calls, so reset
|
||||
// mBeganStream manually.
|
||||
mBeganStream = false;
|
||||
LOG(("nsUrlClassifierStreamUpdater::StreamFinished [%x, %d]", status, requestedDelay));
|
||||
if (NS_FAILED(status) || mPendingUpdates.Length() == 0) {
|
||||
// We're done.
|
||||
LOG(("nsUrlClassifierStreamUpdater::Done [this=%p]", this));
|
||||
mDBService->FinishUpdate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Wait the requested amount of time before starting a new stream.
|
||||
// This appears to be a duplicate timer (see bug 1110891)
|
||||
nsresult rv;
|
||||
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = mTimer->InitWithCallback(this, requestedDelay,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Unable to initialize timer, fetching next safebrowsing item immediately");
|
||||
return FetchNext();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::UpdateSuccess(uint32_t requestedTimeout)
|
||||
{
|
||||
LOG(("nsUrlClassifierStreamUpdater::UpdateSuccess [this=%p]", this));
|
||||
if (mPendingUpdates.Length() != 0) {
|
||||
NS_WARNING("Didn't fetch all safebrowsing update redirects");
|
||||
}
|
||||
|
||||
// DownloadDone() clears mSuccessCallback, so we save it off here.
|
||||
nsCOMPtr<nsIUrlClassifierCallback> successCallback = mDownloadError ? nullptr : mSuccessCallback.get();
|
||||
DownloadDone();
|
||||
|
||||
nsAutoCString strTimeout;
|
||||
strTimeout.AppendInt(requestedTimeout);
|
||||
if (successCallback) {
|
||||
LOG(("nsUrlClassifierStreamUpdater::UpdateSuccess callback [this=%p]",
|
||||
this));
|
||||
successCallback->HandleEvent(strTimeout);
|
||||
} else {
|
||||
LOG(("nsUrlClassifierStreamUpdater::UpdateSuccess skipping callback [this=%p]",
|
||||
this));
|
||||
}
|
||||
// Now fetch the next request
|
||||
LOG(("stream updater: calling into fetch next request"));
|
||||
FetchNextRequest();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::UpdateError(nsresult result)
|
||||
{
|
||||
LOG(("nsUrlClassifierStreamUpdater::UpdateError [this=%p]", this));
|
||||
|
||||
// DownloadDone() clears mUpdateErrorCallback, so we save it off here.
|
||||
nsCOMPtr<nsIUrlClassifierCallback> errorCallback = mDownloadError ? nullptr : mUpdateErrorCallback.get();
|
||||
|
||||
DownloadDone();
|
||||
|
||||
nsAutoCString strResult;
|
||||
strResult.AppendInt(static_cast<uint32_t>(result));
|
||||
if (errorCallback) {
|
||||
errorCallback->HandleEvent(strResult);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierStreamUpdater::AddRequestBody(const nsACString &aRequestBody)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIStringInputStream> strStream =
|
||||
do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = strStream->SetData(aRequestBody.BeginReading(),
|
||||
aRequestBody.Length());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIUploadChannel> uploadChannel = do_QueryInterface(mChannel, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = uploadChannel->SetUploadStream(strStream,
|
||||
NS_LITERAL_CSTRING("text/plain"),
|
||||
-1);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// nsIStreamListenerObserver implementation
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::OnStartRequest(nsIRequest *request,
|
||||
nsISupports* context)
|
||||
{
|
||||
nsresult rv;
|
||||
bool downloadError = false;
|
||||
nsAutoCString strStatus;
|
||||
nsresult status = NS_OK;
|
||||
|
||||
// Only update if we got http success header
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
|
||||
if (httpChannel) {
|
||||
rv = httpChannel->GetStatus(&status);
|
||||
LOG(("nsUrlClassifierStreamUpdater::OnStartRequest (status=%x, this=%p)",
|
||||
status, this));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (NS_FAILED(status)) {
|
||||
// Assume we're overloading the server and trigger backoff.
|
||||
downloadError = true;
|
||||
} else {
|
||||
bool succeeded = false;
|
||||
rv = httpChannel->GetRequestSucceeded(&succeeded);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("nsUrlClassifierStreamUpdater::OnStartRequest (%s)", succeeded ?
|
||||
"succeeded" : "failed"));
|
||||
if (!succeeded) {
|
||||
// 404 or other error, pass error status back
|
||||
LOG(("HTTP request returned failure code."));
|
||||
|
||||
uint32_t requestStatus;
|
||||
rv = httpChannel->GetResponseStatus(&requestStatus);
|
||||
LOG(("HTTP request returned failure code: %d.", requestStatus));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
strStatus.AppendInt(requestStatus);
|
||||
downloadError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (downloadError) {
|
||||
LOG(("nsUrlClassifierStreamUpdater::Download error [this=%p]", this));
|
||||
|
||||
// It's possible for mDownloadErrorCallback to be null on shutdown.
|
||||
if (mDownloadErrorCallback) {
|
||||
mDownloadErrorCallback->HandleEvent(strStatus);
|
||||
}
|
||||
|
||||
mDownloadError = true;
|
||||
status = NS_ERROR_ABORT;
|
||||
} else if (NS_SUCCEEDED(status)) {
|
||||
MOZ_ASSERT(mDownloadErrorCallback);
|
||||
mBeganStream = true;
|
||||
LOG(("nsUrlClassifierStreamUpdater::Beginning stream [this=%p]", this));
|
||||
rv = mDBService->BeginStream(mStreamTable);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mStreamTable.Truncate();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::OnDataAvailable(nsIRequest *request,
|
||||
nsISupports* context,
|
||||
nsIInputStream *aIStream,
|
||||
uint64_t aSourceOffset,
|
||||
uint32_t aLength)
|
||||
{
|
||||
if (!mDBService)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
LOG(("OnDataAvailable (%d bytes)", aLength));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// Copy the data into a nsCString
|
||||
nsCString chunk;
|
||||
rv = NS_ConsumeStream(aIStream, aLength, chunk);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
//LOG(("Chunk (%d): %s\n\n", chunk.Length(), chunk.get()));
|
||||
rv = mDBService->UpdateStream(chunk);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::OnStopRequest(nsIRequest *request, nsISupports* context,
|
||||
nsresult aStatus)
|
||||
{
|
||||
if (!mDBService)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
LOG(("OnStopRequest (status %x, beganStream %s, this=%p)", aStatus,
|
||||
mBeganStream ? "true" : "false", this));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
// Success, finish this stream and move on to the next.
|
||||
rv = mDBService->FinishStream();
|
||||
} else if (mBeganStream) {
|
||||
LOG(("OnStopRequest::Canceling update [this=%p]", this));
|
||||
// We began this stream and couldn't finish it. We have to cancel the
|
||||
// update, it's not in a consistent state.
|
||||
rv = mDBService->CancelUpdate();
|
||||
} else {
|
||||
LOG(("OnStopRequest::Finishing update [this=%p]", this));
|
||||
// The fetch failed, but we didn't start the stream (probably a
|
||||
// server or connection error). We can commit what we've applied
|
||||
// so far, and request again later.
|
||||
rv = mDBService->FinishUpdate();
|
||||
}
|
||||
|
||||
mChannel = nullptr;
|
||||
|
||||
// If the fetch failed, return the network status rather than NS_OK, the
|
||||
// result of finishing a possibly-empty update
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
return rv;
|
||||
}
|
||||
return aStatus;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// nsIObserver implementation
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::Observe(nsISupports *aSubject, const char *aTopic,
|
||||
const char16_t *aData)
|
||||
{
|
||||
if (nsCRT::strcmp(aTopic, gQuitApplicationMessage) == 0) {
|
||||
if (mIsUpdating && mChannel) {
|
||||
LOG(("Cancel download"));
|
||||
nsresult rv;
|
||||
rv = mChannel->Cancel(NS_ERROR_ABORT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mIsUpdating = false;
|
||||
mChannel = nullptr;
|
||||
}
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// nsIInterfaceRequestor implementation
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::GetInterface(const nsIID & eventSinkIID, void* *_retval)
|
||||
{
|
||||
return QueryInterface(eventSinkIID, _retval);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// nsITimerCallback implementation
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::Notify(nsITimer *timer)
|
||||
{
|
||||
LOG(("nsUrlClassifierStreamUpdater::Notify [%p]", this));
|
||||
|
||||
mTimer = nullptr;
|
||||
|
||||
// Start the update process up again.
|
||||
FetchNext();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsUrlClassifierStreamUpdater_h_
|
||||
#define nsUrlClassifierStreamUpdater_h_
|
||||
|
||||
#include <nsISupportsUtils.h>
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIUrlClassifierStreamUpdater.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsITimer.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
// Forward declare pointers
|
||||
class nsIURI;
|
||||
|
||||
class nsUrlClassifierStreamUpdater final : public nsIUrlClassifierStreamUpdater,
|
||||
public nsIUrlClassifierUpdateObserver,
|
||||
public nsIStreamListener,
|
||||
public nsIObserver,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
nsUrlClassifierStreamUpdater();
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIURLCLASSIFIERSTREAMUPDATER
|
||||
NS_DECL_NSIURLCLASSIFIERUPDATEOBSERVER
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
|
||||
private:
|
||||
// No subclassing
|
||||
~nsUrlClassifierStreamUpdater() {}
|
||||
|
||||
// When the dbservice sends an UpdateComplete or UpdateFailure, we call this
|
||||
// to reset the stream updater.
|
||||
void DownloadDone();
|
||||
|
||||
// Disallow copy constructor
|
||||
nsUrlClassifierStreamUpdater(nsUrlClassifierStreamUpdater&);
|
||||
|
||||
nsresult AddRequestBody(const nsACString &aRequestBody);
|
||||
|
||||
// Fetches an update for a single table.
|
||||
nsresult FetchUpdate(nsIURI *aURI,
|
||||
const nsACString &aRequestBody,
|
||||
const nsACString &aTable);
|
||||
// Dumb wrapper so we don't have to create URIs.
|
||||
nsresult FetchUpdate(const nsACString &aURI,
|
||||
const nsACString &aRequestBody,
|
||||
const nsACString &aTable);
|
||||
|
||||
// Fetches the next table, from mPendingUpdates.
|
||||
nsresult FetchNext();
|
||||
// Fetches the next request, from mPendingRequests
|
||||
nsresult FetchNextRequest();
|
||||
|
||||
|
||||
bool mIsUpdating;
|
||||
bool mInitialized;
|
||||
bool mDownloadError;
|
||||
bool mBeganStream;
|
||||
nsCString mStreamTable;
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
nsCOMPtr<nsIUrlClassifierDBService> mDBService;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
|
||||
struct PendingRequest {
|
||||
nsCString mTables;
|
||||
nsCString mRequest;
|
||||
nsCString mUrl;
|
||||
nsCOMPtr<nsIUrlClassifierCallback> mSuccessCallback;
|
||||
nsCOMPtr<nsIUrlClassifierCallback> mUpdateErrorCallback;
|
||||
nsCOMPtr<nsIUrlClassifierCallback> mDownloadErrorCallback;
|
||||
};
|
||||
nsTArray<PendingRequest> mPendingRequests;
|
||||
|
||||
struct PendingUpdate {
|
||||
nsCString mUrl;
|
||||
nsCString mTable;
|
||||
};
|
||||
nsTArray<PendingUpdate> mPendingUpdates;
|
||||
|
||||
nsCOMPtr<nsIUrlClassifierCallback> mSuccessCallback;
|
||||
nsCOMPtr<nsIUrlClassifierCallback> mUpdateErrorCallback;
|
||||
nsCOMPtr<nsIUrlClassifierCallback> mDownloadErrorCallback;
|
||||
};
|
||||
|
||||
#endif // nsUrlClassifierStreamUpdater_h_
|
||||
@@ -1,364 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsEscape.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsUrlClassifierUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "plbase64.h"
|
||||
#include "prprf.h"
|
||||
|
||||
static char int_to_hex_digit(int32_t i)
|
||||
{
|
||||
NS_ASSERTION((i >= 0) && (i <= 15), "int too big in int_to_hex_digit");
|
||||
return static_cast<char>(((i < 10) ? (i + '0') : ((i - 10) + 'A')));
|
||||
}
|
||||
|
||||
static bool
|
||||
IsDecimal(const nsACString & num)
|
||||
{
|
||||
for (uint32_t i = 0; i < num.Length(); i++) {
|
||||
if (!isdigit(num[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsHex(const nsACString & num)
|
||||
{
|
||||
if (num.Length() < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (num[0] != '0' || !(num[1] == 'x' || num[1] == 'X')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 2; i < num.Length(); i++) {
|
||||
if (!isxdigit(num[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsOctal(const nsACString & num)
|
||||
{
|
||||
if (num.Length() < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (num[0] != '0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 1; i < num.Length(); i++) {
|
||||
if (!isdigit(num[i]) || num[i] == '8' || num[i] == '9') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsUrlClassifierUtils::nsUrlClassifierUtils() : mEscapeCharmap(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierUtils::Init()
|
||||
{
|
||||
// Everything but alpha numerics, - and .
|
||||
mEscapeCharmap = new Charmap(0xffffffff, 0xfc009fff, 0xf8000001, 0xf8000001,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff);
|
||||
if (!mEscapeCharmap)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsUrlClassifierUtils, nsIUrlClassifierUtils)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// nsIUrlClassifierUtils
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierUtils::GetKeyForURI(nsIURI * uri, nsACString & _retval)
|
||||
{
|
||||
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
|
||||
if (!innerURI)
|
||||
innerURI = uri;
|
||||
|
||||
nsAutoCString host;
|
||||
innerURI->GetAsciiHost(host);
|
||||
|
||||
if (host.IsEmpty()) {
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
|
||||
nsresult rv = CanonicalizeHostname(host, _retval);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString path;
|
||||
rv = innerURI->GetPath(path);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// strip out anchors
|
||||
int32_t ref = path.FindChar('#');
|
||||
if (ref != kNotFound)
|
||||
path.SetLength(ref);
|
||||
|
||||
nsAutoCString temp;
|
||||
rv = CanonicalizePath(path, temp);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
_retval.Append(temp);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// non-interface methods
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierUtils::CanonicalizeHostname(const nsACString & hostname,
|
||||
nsACString & _retval)
|
||||
{
|
||||
nsAutoCString unescaped;
|
||||
if (!NS_UnescapeURL(PromiseFlatCString(hostname).get(),
|
||||
PromiseFlatCString(hostname).Length(),
|
||||
0, unescaped)) {
|
||||
unescaped.Assign(hostname);
|
||||
}
|
||||
|
||||
nsAutoCString cleaned;
|
||||
CleanupHostname(unescaped, cleaned);
|
||||
|
||||
nsAutoCString temp;
|
||||
ParseIPAddress(cleaned, temp);
|
||||
if (!temp.IsEmpty()) {
|
||||
cleaned.Assign(temp);
|
||||
}
|
||||
|
||||
ToLowerCase(cleaned);
|
||||
SpecialEncode(cleaned, false, _retval);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierUtils::CanonicalizePath(const nsACString & path,
|
||||
nsACString & _retval)
|
||||
{
|
||||
_retval.Truncate();
|
||||
|
||||
nsAutoCString decodedPath(path);
|
||||
nsAutoCString temp;
|
||||
while (NS_UnescapeURL(decodedPath.get(), decodedPath.Length(), 0, temp)) {
|
||||
decodedPath.Assign(temp);
|
||||
temp.Truncate();
|
||||
}
|
||||
|
||||
SpecialEncode(decodedPath, true, _retval);
|
||||
// XXX: lowercase the path?
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsUrlClassifierUtils::CleanupHostname(const nsACString & hostname,
|
||||
nsACString & _retval)
|
||||
{
|
||||
_retval.Truncate();
|
||||
|
||||
const char* curChar = hostname.BeginReading();
|
||||
const char* end = hostname.EndReading();
|
||||
char lastChar = '\0';
|
||||
while (curChar != end) {
|
||||
unsigned char c = static_cast<unsigned char>(*curChar);
|
||||
if (c == '.' && (lastChar == '\0' || lastChar == '.')) {
|
||||
// skip
|
||||
} else {
|
||||
_retval.Append(*curChar);
|
||||
}
|
||||
lastChar = c;
|
||||
++curChar;
|
||||
}
|
||||
|
||||
// cut off trailing dots
|
||||
while (_retval.Length() > 0 && _retval[_retval.Length() - 1] == '.') {
|
||||
_retval.SetLength(_retval.Length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsUrlClassifierUtils::ParseIPAddress(const nsACString & host,
|
||||
nsACString & _retval)
|
||||
{
|
||||
_retval.Truncate();
|
||||
nsACString::const_iterator iter, end;
|
||||
host.BeginReading(iter);
|
||||
host.EndReading(end);
|
||||
|
||||
if (host.Length() <= 15) {
|
||||
// The Windows resolver allows a 4-part dotted decimal IP address to
|
||||
// have a space followed by any old rubbish, so long as the total length
|
||||
// of the string doesn't get above 15 characters. So, "10.192.95.89 xy"
|
||||
// is resolved to 10.192.95.89.
|
||||
// If the string length is greater than 15 characters, e.g.
|
||||
// "10.192.95.89 xy.wildcard.example.com", it will be resolved through
|
||||
// DNS.
|
||||
|
||||
if (FindCharInReadable(' ', iter, end)) {
|
||||
end = iter;
|
||||
}
|
||||
}
|
||||
|
||||
for (host.BeginReading(iter); iter != end; iter++) {
|
||||
if (!(isxdigit(*iter) || *iter == 'x' || *iter == 'X' || *iter == '.')) {
|
||||
// not an IP
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
host.BeginReading(iter);
|
||||
nsTArray<nsCString> parts;
|
||||
ParseString(PromiseFlatCString(Substring(iter, end)), '.', parts);
|
||||
if (parts.Length() > 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If any potentially-octal numbers (start with 0 but not hex) have
|
||||
// non-octal digits, no part of the ip can be in octal
|
||||
// XXX: this came from the old javascript implementation, is it really
|
||||
// supposed to be like this?
|
||||
bool allowOctal = true;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < parts.Length(); i++) {
|
||||
const nsCString& part = parts[i];
|
||||
if (part[0] == '0') {
|
||||
for (uint32_t j = 1; j < part.Length(); j++) {
|
||||
if (part[j] == 'x') {
|
||||
break;
|
||||
}
|
||||
if (part[j] == '8' || part[j] == '9') {
|
||||
allowOctal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < parts.Length(); i++) {
|
||||
nsAutoCString canonical;
|
||||
|
||||
if (i == parts.Length() - 1) {
|
||||
CanonicalNum(parts[i], 5 - parts.Length(), allowOctal, canonical);
|
||||
} else {
|
||||
CanonicalNum(parts[i], 1, allowOctal, canonical);
|
||||
}
|
||||
|
||||
if (canonical.IsEmpty()) {
|
||||
_retval.Truncate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_retval.IsEmpty()) {
|
||||
_retval.Assign(canonical);
|
||||
} else {
|
||||
_retval.Append('.');
|
||||
_retval.Append(canonical);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
nsUrlClassifierUtils::CanonicalNum(const nsACString& num,
|
||||
uint32_t bytes,
|
||||
bool allowOctal,
|
||||
nsACString& _retval)
|
||||
{
|
||||
_retval.Truncate();
|
||||
|
||||
if (num.Length() < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t val;
|
||||
if (allowOctal && IsOctal(num)) {
|
||||
if (PR_sscanf(PromiseFlatCString(num).get(), "%o", &val) != 1) {
|
||||
return;
|
||||
}
|
||||
} else if (IsDecimal(num)) {
|
||||
if (PR_sscanf(PromiseFlatCString(num).get(), "%u", &val) != 1) {
|
||||
return;
|
||||
}
|
||||
} else if (IsHex(num)) {
|
||||
if (PR_sscanf(PromiseFlatCString(num).get(), num[1] == 'X' ? "0X%x" : "0x%x",
|
||||
&val) != 1) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
while (bytes--) {
|
||||
char buf[20];
|
||||
PR_snprintf(buf, sizeof(buf), "%u", val & 0xff);
|
||||
if (_retval.IsEmpty()) {
|
||||
_retval.Assign(buf);
|
||||
} else {
|
||||
_retval = nsDependentCString(buf) + NS_LITERAL_CSTRING(".") + _retval;
|
||||
}
|
||||
val >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
// This function will encode all "special" characters in typical url
|
||||
// encoding, that is %hh where h is a valid hex digit. It will also fold
|
||||
// any duplicated slashes.
|
||||
bool
|
||||
nsUrlClassifierUtils::SpecialEncode(const nsACString & url,
|
||||
bool foldSlashes,
|
||||
nsACString & _retval)
|
||||
{
|
||||
bool changed = false;
|
||||
const char* curChar = url.BeginReading();
|
||||
const char* end = url.EndReading();
|
||||
|
||||
unsigned char lastChar = '\0';
|
||||
while (curChar != end) {
|
||||
unsigned char c = static_cast<unsigned char>(*curChar);
|
||||
if (ShouldURLEscape(c)) {
|
||||
_retval.Append('%');
|
||||
_retval.Append(int_to_hex_digit(c / 16));
|
||||
_retval.Append(int_to_hex_digit(c % 16));
|
||||
|
||||
changed = true;
|
||||
} else if (foldSlashes && (c == '/' && lastChar == '/')) {
|
||||
// skip
|
||||
} else {
|
||||
_retval.Append(*curChar);
|
||||
}
|
||||
lastChar = c;
|
||||
curChar++;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool
|
||||
nsUrlClassifierUtils::ShouldURLEscape(const unsigned char c) const
|
||||
{
|
||||
return c <= 32 || c == '%' || c >=127;
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsUrlClassifierUtils_h_
|
||||
#define nsUrlClassifierUtils_h_
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIUrlClassifierUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsUrlClassifierUtils final : public nsIUrlClassifierUtils
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* A fast, bit-vector map for ascii characters.
|
||||
*
|
||||
* Internally stores 256 bits in an array of 8 ints.
|
||||
* Does quick bit-flicking to lookup needed characters.
|
||||
*/
|
||||
class Charmap
|
||||
{
|
||||
public:
|
||||
Charmap(uint32_t b0, uint32_t b1, uint32_t b2, uint32_t b3,
|
||||
uint32_t b4, uint32_t b5, uint32_t b6, uint32_t b7)
|
||||
{
|
||||
mMap[0] = b0; mMap[1] = b1; mMap[2] = b2; mMap[3] = b3;
|
||||
mMap[4] = b4; mMap[5] = b5; mMap[6] = b6; mMap[7] = b7;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a quick lookup to see if the letter is in the map.
|
||||
*/
|
||||
bool Contains(unsigned char c) const
|
||||
{
|
||||
return mMap[c >> 5] & (1 << (c & 31));
|
||||
}
|
||||
|
||||
private:
|
||||
// Store the 256 bits in an 8 byte array.
|
||||
uint32_t mMap[8];
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
nsUrlClassifierUtils();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIURLCLASSIFIERUTILS
|
||||
|
||||
nsresult Init();
|
||||
|
||||
nsresult CanonicalizeHostname(const nsACString & hostname,
|
||||
nsACString & _retval);
|
||||
nsresult CanonicalizePath(const nsACString & url, nsACString & _retval);
|
||||
|
||||
// This function will encode all "special" characters in typical url encoding,
|
||||
// that is %hh where h is a valid hex digit. The characters which are encoded
|
||||
// by this function are any ascii characters under 32(control characters and
|
||||
// space), 37(%), and anything 127 or above (special characters). Url is the
|
||||
// string to encode, ret is the encoded string. Function returns true if
|
||||
// ret != url.
|
||||
bool SpecialEncode(const nsACString & url,
|
||||
bool foldSlashes,
|
||||
nsACString & _retval);
|
||||
|
||||
void ParseIPAddress(const nsACString & host, nsACString & _retval);
|
||||
void CanonicalNum(const nsACString & num,
|
||||
uint32_t bytes,
|
||||
bool allowOctal,
|
||||
nsACString & _retval);
|
||||
|
||||
private:
|
||||
~nsUrlClassifierUtils() {}
|
||||
|
||||
// Disallow copy constructor
|
||||
nsUrlClassifierUtils(const nsUrlClassifierUtils&);
|
||||
|
||||
// Function to tell if we should encode a character.
|
||||
bool ShouldURLEscape(const unsigned char c) const;
|
||||
|
||||
void CleanupHostname(const nsACString & host, nsACString & _retval);
|
||||
|
||||
nsAutoPtr<Charmap> mEscapeCharmap;
|
||||
};
|
||||
|
||||
#endif // nsUrlClassifierUtils_h_
|
||||
@@ -1,326 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include "nsEscape.h"
|
||||
#include "nsString.h"
|
||||
#include "nsUrlClassifierUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "stdlib.h"
|
||||
#include "TestHarness.h"
|
||||
|
||||
static int gTotalTests = 0;
|
||||
static int gPassedTests = 0;
|
||||
|
||||
static char int_to_hex_digit(int32_t i) {
|
||||
NS_ASSERTION((i >= 0) && (i <= 15), "int too big in int_to_hex_digit");
|
||||
return static_cast<char>(((i < 10) ? (i + '0') : ((i - 10) + 'A')));
|
||||
}
|
||||
|
||||
static void CheckEquals(nsCString & expected, nsCString & actual)
|
||||
{
|
||||
if (!(expected).Equals((actual))) {
|
||||
fail("expected |%s| but got |%s|", (expected).get(), (actual).get());
|
||||
} else {
|
||||
gPassedTests++;
|
||||
}
|
||||
gTotalTests++;
|
||||
}
|
||||
|
||||
void TestUnescapeHelper(const char* in, const char* expected)
|
||||
{
|
||||
nsCString out, strIn(in), strExp(expected);
|
||||
nsUrlClassifierUtils utils;
|
||||
|
||||
NS_UnescapeURL(strIn.get(), strIn.Length(), esc_AlwaysCopy, out);
|
||||
CheckEquals(strExp, out);
|
||||
}
|
||||
|
||||
// Make sure Unescape from nsEncode.h's unescape does what the server does.
|
||||
void TestUnescape()
|
||||
{
|
||||
// test empty string
|
||||
TestUnescapeHelper("\0", "\0");
|
||||
|
||||
// Test docoding of all characters.
|
||||
nsCString allCharsEncoded, allCharsEncodedLowercase, allCharsAsString;
|
||||
for (int32_t i = 1; i < 256; ++i) {
|
||||
allCharsEncoded.Append('%');
|
||||
allCharsEncoded.Append(int_to_hex_digit(i / 16));
|
||||
allCharsEncoded.Append((int_to_hex_digit(i % 16)));
|
||||
|
||||
allCharsEncodedLowercase.Append('%');
|
||||
allCharsEncodedLowercase.Append(tolower(int_to_hex_digit(i / 16)));
|
||||
allCharsEncodedLowercase.Append(tolower(int_to_hex_digit(i % 16)));
|
||||
|
||||
allCharsAsString.Append(static_cast<char>(i));
|
||||
}
|
||||
|
||||
nsUrlClassifierUtils utils;
|
||||
nsCString out;
|
||||
NS_UnescapeURL(allCharsEncoded.get(), allCharsEncoded.Length(), esc_AlwaysCopy, out);
|
||||
CheckEquals(allCharsAsString, out);
|
||||
|
||||
out.Truncate();
|
||||
NS_UnescapeURL(allCharsEncodedLowercase.get(), allCharsEncodedLowercase.Length(), esc_AlwaysCopy, out);
|
||||
CheckEquals(allCharsAsString, out);
|
||||
|
||||
// Test %-related edge cases
|
||||
TestUnescapeHelper("%", "%");
|
||||
TestUnescapeHelper("%xx", "%xx");
|
||||
TestUnescapeHelper("%%", "%%");
|
||||
TestUnescapeHelper("%%%", "%%%");
|
||||
TestUnescapeHelper("%%%%", "%%%%");
|
||||
TestUnescapeHelper("%1", "%1");
|
||||
TestUnescapeHelper("%1z", "%1z");
|
||||
TestUnescapeHelper("a%1z", "a%1z");
|
||||
TestUnescapeHelper("abc%d%e%fg%hij%klmno%", "abc%d%e%fg%hij%klmno%");
|
||||
|
||||
// A few more tests
|
||||
TestUnescapeHelper("%25", "%");
|
||||
TestUnescapeHelper("%25%32%35", "%25");
|
||||
}
|
||||
|
||||
void TestEncodeHelper(const char* in, const char* expected)
|
||||
{
|
||||
nsCString out, strIn(in), strExp(expected);
|
||||
nsUrlClassifierUtils utils;
|
||||
|
||||
utils.SpecialEncode(strIn, true, out);
|
||||
CheckEquals(strExp, out);
|
||||
}
|
||||
|
||||
void TestEnc()
|
||||
{
|
||||
// Test empty string
|
||||
TestEncodeHelper("", "");
|
||||
|
||||
// Test that all characters we shouldn't encode ([33-36],[38,126]) are not.
|
||||
nsCString noenc;
|
||||
for (int32_t i = 33; i < 127; i++) {
|
||||
if (i != 37) { // skip %
|
||||
noenc.Append(static_cast<char>(i));
|
||||
}
|
||||
}
|
||||
nsUrlClassifierUtils utils;
|
||||
nsCString out;
|
||||
utils.SpecialEncode(noenc, false, out);
|
||||
CheckEquals(noenc, out);
|
||||
|
||||
// Test that all the chars that we should encode [0,32],37,[127,255] are
|
||||
nsCString yesAsString, yesExpectedString;
|
||||
for (int32_t i = 1; i < 256; i++) {
|
||||
if (i < 33 || i == 37 || i > 126) {
|
||||
yesAsString.Append(static_cast<char>(i));
|
||||
yesExpectedString.Append('%');
|
||||
yesExpectedString.Append(int_to_hex_digit(i / 16));
|
||||
yesExpectedString.Append(int_to_hex_digit(i % 16));
|
||||
}
|
||||
}
|
||||
|
||||
out.Truncate();
|
||||
utils.SpecialEncode(yesAsString, false, out);
|
||||
CheckEquals(yesExpectedString, out);
|
||||
|
||||
TestEncodeHelper("blah//blah", "blah/blah");
|
||||
}
|
||||
|
||||
void TestCanonicalizeHelper(const char* in, const char* expected)
|
||||
{
|
||||
nsCString out, strIn(in), strExp(expected);
|
||||
nsUrlClassifierUtils utils;
|
||||
|
||||
utils.CanonicalizePath(strIn, out);
|
||||
CheckEquals(strExp, out);
|
||||
}
|
||||
|
||||
void TestCanonicalize()
|
||||
{
|
||||
// Test repeated %-decoding. Note: %25 --> %, %32 --> 2, %35 --> 5
|
||||
TestCanonicalizeHelper("%25", "%25");
|
||||
TestCanonicalizeHelper("%25%32%35", "%25");
|
||||
TestCanonicalizeHelper("asdf%25%32%35asd", "asdf%25asd");
|
||||
TestCanonicalizeHelper("%%%25%32%35asd%%", "%25%25%25asd%25%25");
|
||||
TestCanonicalizeHelper("%25%32%35%25%32%35%25%32%35", "%25%25%25");
|
||||
TestCanonicalizeHelper("%25", "%25");
|
||||
TestCanonicalizeHelper("%257Ea%2521b%2540c%2523d%2524e%25f%255E00%252611%252A22%252833%252944_55%252B",
|
||||
"~a!b@c#d$e%25f^00&11*22(33)44_55+");
|
||||
|
||||
TestCanonicalizeHelper("", "");
|
||||
TestCanonicalizeHelper("%31%36%38%2e%31%38%38%2e%39%39%2e%32%36/%2E%73%65%63%75%72%65/%77%77%77%2E%65%62%61%79%2E%63%6F%6D/",
|
||||
"168.188.99.26/.secure/www.ebay.com/");
|
||||
TestCanonicalizeHelper("195.127.0.11/uploads/%20%20%20%20/.verify/.eBaysecure=updateuserdataxplimnbqmn-xplmvalidateinfoswqpcmlx=hgplmcx/",
|
||||
"195.127.0.11/uploads/%20%20%20%20/.verify/.eBaysecure=updateuserdataxplimnbqmn-xplmvalidateinfoswqpcmlx=hgplmcx/");
|
||||
// Added in bug 489455. %00 should no longer be changed to %01.
|
||||
TestCanonicalizeHelper("%00", "%00");
|
||||
}
|
||||
|
||||
void TestParseIPAddressHelper(const char *in, const char *expected)
|
||||
{
|
||||
nsCString out, strIn(in), strExp(expected);
|
||||
nsUrlClassifierUtils utils;
|
||||
utils.Init();
|
||||
|
||||
utils.ParseIPAddress(strIn, out);
|
||||
CheckEquals(strExp, out);
|
||||
}
|
||||
|
||||
void TestParseIPAddress()
|
||||
{
|
||||
TestParseIPAddressHelper("123.123.0.0.1", "");
|
||||
TestParseIPAddressHelper("255.0.0.1", "255.0.0.1");
|
||||
TestParseIPAddressHelper("12.0x12.01234", "12.18.2.156");
|
||||
TestParseIPAddressHelper("276.2.3", "20.2.0.3");
|
||||
TestParseIPAddressHelper("012.034.01.055", "10.28.1.45");
|
||||
TestParseIPAddressHelper("0x12.0x43.0x44.0x01", "18.67.68.1");
|
||||
TestParseIPAddressHelper("167838211", "10.1.2.3");
|
||||
TestParseIPAddressHelper("3279880203", "195.127.0.11");
|
||||
TestParseIPAddressHelper("0x12434401", "18.67.68.1");
|
||||
TestParseIPAddressHelper("413960661", "24.172.137.213");
|
||||
TestParseIPAddressHelper("03053104725", "24.172.137.213");
|
||||
TestParseIPAddressHelper("030.0254.0x89d5", "24.172.137.213");
|
||||
TestParseIPAddressHelper("1.234.4.0377", "1.234.4.255");
|
||||
TestParseIPAddressHelper("1.2.3.00x0", "");
|
||||
TestParseIPAddressHelper("10.192.95.89 xy", "10.192.95.89");
|
||||
TestParseIPAddressHelper("10.192.95.89 xyz", "");
|
||||
TestParseIPAddressHelper("1.2.3.0x0", "1.2.3.0");
|
||||
TestParseIPAddressHelper("1.2.3.4", "1.2.3.4");
|
||||
}
|
||||
|
||||
void TestCanonicalNumHelper(const char *in, uint32_t bytes,
|
||||
bool allowOctal, const char *expected)
|
||||
{
|
||||
nsCString out, strIn(in), strExp(expected);
|
||||
nsUrlClassifierUtils utils;
|
||||
utils.Init();
|
||||
|
||||
utils.CanonicalNum(strIn, bytes, allowOctal, out);
|
||||
CheckEquals(strExp, out);
|
||||
}
|
||||
|
||||
void TestCanonicalNum()
|
||||
{
|
||||
TestCanonicalNumHelper("", 1, true, "");
|
||||
TestCanonicalNumHelper("10", 0, true, "");
|
||||
TestCanonicalNumHelper("45", 1, true, "45");
|
||||
TestCanonicalNumHelper("0x10", 1, true, "16");
|
||||
TestCanonicalNumHelper("367", 2, true, "1.111");
|
||||
TestCanonicalNumHelper("012345", 3, true, "0.20.229");
|
||||
TestCanonicalNumHelper("0173", 1, true, "123");
|
||||
TestCanonicalNumHelper("09", 1, false, "9");
|
||||
TestCanonicalNumHelper("0x120x34", 2, true, "");
|
||||
TestCanonicalNumHelper("0x12fc", 2, true, "18.252");
|
||||
TestCanonicalNumHelper("3279880203", 4, true, "195.127.0.11");
|
||||
TestCanonicalNumHelper("0x0000059", 1, true, "89");
|
||||
TestCanonicalNumHelper("0x00000059", 1, true, "89");
|
||||
TestCanonicalNumHelper("0x0000067", 1, true, "103");
|
||||
}
|
||||
|
||||
void TestHostnameHelper(const char *in, const char *expected)
|
||||
{
|
||||
nsCString out, strIn(in), strExp(expected);
|
||||
nsUrlClassifierUtils utils;
|
||||
utils.Init();
|
||||
|
||||
utils.CanonicalizeHostname(strIn, out);
|
||||
CheckEquals(strExp, out);
|
||||
}
|
||||
|
||||
void TestHostname()
|
||||
{
|
||||
TestHostnameHelper("abcd123;[]", "abcd123;[]");
|
||||
TestHostnameHelper("abc.123", "abc.123");
|
||||
TestHostnameHelper("abc..123", "abc.123");
|
||||
TestHostnameHelper("trailing.", "trailing");
|
||||
TestHostnameHelper("i love trailing dots....", "i%20love%20trailing%20dots");
|
||||
TestHostnameHelper(".leading", "leading");
|
||||
TestHostnameHelper("..leading", "leading");
|
||||
TestHostnameHelper(".dots.", "dots");
|
||||
TestHostnameHelper(".both.", "both");
|
||||
TestHostnameHelper(".both..", "both");
|
||||
TestHostnameHelper("..both.", "both");
|
||||
TestHostnameHelper("..both..", "both");
|
||||
TestHostnameHelper("..a.b.c.d..", "a.b.c.d");
|
||||
TestHostnameHelper("..127.0.0.1..", "127.0.0.1");
|
||||
TestHostnameHelper("asdf!@#$a", "asdf!@#$a");
|
||||
TestHostnameHelper("AB CD 12354", "ab%20cd%2012354");
|
||||
TestHostnameHelper("\1\2\3\4\112\177", "%01%02%03%04j%7F");
|
||||
TestHostnameHelper("<>.AS/-+", "<>.as/-+");
|
||||
// Added in bug 489455. %00 should no longer be changed to %01.
|
||||
TestHostnameHelper("%00", "%00");
|
||||
}
|
||||
|
||||
void TestLongHostname()
|
||||
{
|
||||
static const int kTestSize = 1024 * 150;
|
||||
char *str = static_cast<char*>(malloc(kTestSize + 1));
|
||||
memset(str, 'x', kTestSize);
|
||||
str[kTestSize] = '\0';
|
||||
|
||||
nsUrlClassifierUtils utils;
|
||||
utils.Init();
|
||||
|
||||
nsAutoCString out;
|
||||
nsDependentCString in(str);
|
||||
PRIntervalTime clockStart = PR_IntervalNow();
|
||||
utils.CanonicalizeHostname(in, out);
|
||||
PRIntervalTime clockEnd = PR_IntervalNow();
|
||||
|
||||
CheckEquals(in, out);
|
||||
|
||||
printf("CanonicalizeHostname on long string (%dms)\n",
|
||||
PR_IntervalToMilliseconds(clockEnd - clockStart));
|
||||
}
|
||||
|
||||
void TestFragmentSet()
|
||||
{
|
||||
nsUrlClassifierFragmentSet set;
|
||||
set.Init(3);
|
||||
|
||||
set.Put(NS_LITERAL_CSTRING("a"));
|
||||
set.Put(NS_LITERAL_CSTRING("b"));
|
||||
set.Put(NS_LITERAL_CSTRING("c"));
|
||||
|
||||
// At this point, adding a fourth element would push "a" off.
|
||||
// Make sure that set.Has("a") moves it to the front of the list
|
||||
set.Has(NS_LITERAL_CSTRING("a"));
|
||||
|
||||
// Now add a new item. This should now push "b" off the list,
|
||||
// but leave "a"
|
||||
set.Put(NS_LITERAL_CSTRING("d"));
|
||||
|
||||
gTotalTests++;
|
||||
if (set.Has(NS_LITERAL_CSTRING("a")))
|
||||
gPassedTests++;
|
||||
else
|
||||
fail("set.Has(\"a\") failed.");
|
||||
|
||||
gTotalTests++;
|
||||
if (!set.Has(NS_LITERAL_CSTRING("b")))
|
||||
gPassedTests++;
|
||||
else
|
||||
fail("!set.Has(\"b\") failed.");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ScopedXPCOM xpcom("URLClassiferUtils");
|
||||
|
||||
TestUnescape();
|
||||
TestEnc();
|
||||
TestCanonicalize();
|
||||
TestCanonicalNum();
|
||||
TestParseIPAddress();
|
||||
TestHostname();
|
||||
TestLongHostname();
|
||||
TestFragmentSet();
|
||||
|
||||
if (gPassedTests == gTotalTests)
|
||||
passed(__FILE__);
|
||||
printf("%d of %d tests passed\n", gPassedTests, gTotalTests);
|
||||
// Non-zero return status signals test failure to build system.
|
||||
|
||||
return (gPassedTests != gTotalTests);
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
toolkit.jar:
|
||||
+ content/global/url-classifier/unittests.xul (unittests.xul)
|
||||
@@ -1,72 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
// Modified by evil.js
|
||||
var scriptItem;
|
||||
|
||||
var scriptItem1 = "untouched";
|
||||
var imageItem1 = "untouched";
|
||||
var frameItem1 = "untouched";
|
||||
var scriptItem2 = "untouched";
|
||||
var imageItem2 = "untouched";
|
||||
var frameItem2 = "untouched";
|
||||
|
||||
function checkLoads() {
|
||||
window.parent.is(scriptItem1, "spoiled", "Should not block tracking js 1");
|
||||
window.parent.is(scriptItem2, "spoiled", "Should not block tracking js 2");
|
||||
window.parent.is(imageItem1, "spoiled", "Should not block tracking img 1");
|
||||
window.parent.is(imageItem2, "spoiled", "Should not block tracking img 2");
|
||||
window.parent.is(frameItem1, "spoiled", "Should not block tracking iframe 1");
|
||||
window.parent.is(frameItem2, "spoiled", "Should not block tracking iframe 2");
|
||||
window.parent.is(window.document.blockedTrackingNodeCount, 0,
|
||||
"No elements should be blocked");
|
||||
|
||||
// End (parent) test.
|
||||
window.parent.clearPermissions();
|
||||
window.parent.SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="checkLoads()">
|
||||
|
||||
<!-- Try loading from a tracking script URI (1) -->
|
||||
<script id="badscript1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="scriptItem1 = 'spoiled';"></script>
|
||||
|
||||
<!-- Try loading from a tracking image URI (1) -->
|
||||
<img id="badimage1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg" onload="imageItem1 = 'spoiled';"/>
|
||||
|
||||
<!-- Try loading from a tracking frame URI (1) -->
|
||||
<iframe id="badframe1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html" onload="frameItem1 = 'spoiled';"></iframe>
|
||||
|
||||
<script>
|
||||
// Try loading from a tracking script URI (2) - The loader may follow a
|
||||
// different path depending on whether the resource is loaded from JS or HTML.
|
||||
var newScript = document.createElement("script");
|
||||
newScript.id = "badscript2";
|
||||
newScript.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js";
|
||||
newScript.addEventListener("load", function onload() {scriptItem2 = 'spoiled';});
|
||||
document.body.appendChild(newScript);
|
||||
|
||||
/// Try loading from a tracking image URI (2)
|
||||
var newImage = document.createElement("img");
|
||||
newImage.id = "badimage2";
|
||||
newImage.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg";
|
||||
newImage.addEventListener("load", function onload() {imageItem2 = 'spoiled'});
|
||||
document.body.appendChild(newImage);
|
||||
|
||||
// Try loading from a tracking iframe URI (2)
|
||||
var newFrame = document.createElement("iframe");
|
||||
newFrame.id = "badframe2";
|
||||
newFrame.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html"
|
||||
newFrame.addEventListener("load", function onload() {frameItem2 = 'spoiled'});
|
||||
document.body.appendChild(newFrame);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
allowlistAnnotatedFrame.html
|
||||
classifiedAnnotatedFrame.html
|
||||
|
||||
[test_lookup_system_principal.html]
|
||||
[test_classified_annotations.html]
|
||||
[test_allowlisted_annotations.html]
|
||||
@@ -1,135 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
|
||||
<script type="text/javascript">
|
||||
"use strict";
|
||||
|
||||
var scriptItem = "untouched";
|
||||
var scriptItem1 = "untouched";
|
||||
var scriptItem2 = "untouched";
|
||||
var imageItem1 = "untouched";
|
||||
var imageItem2 = "untouched";
|
||||
var frameItem1 = "untouched";
|
||||
var frameItem2 = "untouched";
|
||||
|
||||
var badids = [
|
||||
"badscript1",
|
||||
"badscript2",
|
||||
"badimage1",
|
||||
"badimage2",
|
||||
"badframe1",
|
||||
"badframe2",
|
||||
"badcss"
|
||||
];
|
||||
|
||||
function checkLoads() {
|
||||
window.parent.is(
|
||||
scriptItem1, "untouched", "Should not load tracking javascript");
|
||||
window.parent.is(
|
||||
scriptItem2, "untouched", "Should not load tracking javascript (2)");
|
||||
|
||||
window.parent.is(
|
||||
imageItem1, "untouched", "Should not load tracking images");
|
||||
window.parent.is(
|
||||
imageItem2, "untouched", "Should not load tracking images (2)");
|
||||
|
||||
window.parent.is(
|
||||
frameItem1, "untouched", "Should not load tracking iframes");
|
||||
window.parent.is(
|
||||
frameItem2, "untouched", "Should not load tracking iframes (2)");
|
||||
|
||||
var elt = document.getElementById("styleCheck");
|
||||
var style = document.defaultView.getComputedStyle(elt, "");
|
||||
window.parent.isnot(
|
||||
style.visibility, "hidden", "Should not load tracking css");
|
||||
|
||||
window.parent.is(window.document.blockedTrackingNodeCount, badids.length,
|
||||
"Should identify all tracking elements");
|
||||
|
||||
var blockedTrackingNodes = window.document.blockedTrackingNodes;
|
||||
|
||||
// Make sure that every node in blockedTrackingNodes exists in the tree
|
||||
// (that may not always be the case but do not expect any nodes to disappear
|
||||
// from the tree here)
|
||||
var allNodeMatch = true;
|
||||
for (var i = 0; i < blockedTrackingNodes.length; i++) {
|
||||
var nodeMatch = false;
|
||||
for (var j = 0; j < badids.length && !nodeMatch; j++) {
|
||||
nodeMatch |=
|
||||
(blockedTrackingNodes[i] == document.getElementById(badids[j]));
|
||||
}
|
||||
|
||||
allNodeMatch &= nodeMatch;
|
||||
}
|
||||
window.parent.is(allNodeMatch, true,
|
||||
"All annotated nodes are expected in the tree");
|
||||
|
||||
// Make sure that every node with a badid (see badids) is found in the
|
||||
// blockedTrackingNodes. This tells us if we are neglecting to annotate
|
||||
// some nodes
|
||||
allNodeMatch = true;
|
||||
for (var j = 0; j < badids.length; j++) {
|
||||
var nodeMatch = false;
|
||||
for (var i = 0; i < blockedTrackingNodes.length && !nodeMatch; i++) {
|
||||
nodeMatch |=
|
||||
(blockedTrackingNodes[i] == document.getElementById(badids[j]));
|
||||
}
|
||||
|
||||
allNodeMatch &= nodeMatch;
|
||||
}
|
||||
window.parent.is(allNodeMatch, true,
|
||||
"All tracking nodes are expected to be annotated as such");
|
||||
|
||||
// Unset prefs, etc.
|
||||
window.parent.cleanup();
|
||||
// End (parent) test.
|
||||
window.parent.SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Try loading from a tracking CSS URI -->
|
||||
<link id="badcss" rel="stylesheet" type="text/css" href="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="checkLoads()">
|
||||
|
||||
<!-- Try loading from a tracking script URI (1): evil.js onload will have updated the scriptItem variable -->
|
||||
<script id="badscript1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="scriptItem1 = scriptItem;"></script>
|
||||
|
||||
<!-- Try loading from a tracking image URI (1) -->
|
||||
<img id="badimage1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?reload=true" onload="imageItem1 = 'spoiled';"/>
|
||||
|
||||
<!-- Try loading from a tracking frame URI (1) -->
|
||||
<iframe id="badframe1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html" onload="frameItem1 = 'spoiled';"></iframe>
|
||||
|
||||
<script>
|
||||
// Try loading from a tracking script URI (2) - The loader may follow a different path depending on whether the resource is loaded from JS or HTML.
|
||||
var newScript = document.createElement("script");
|
||||
newScript.id = "badscript2";
|
||||
newScript.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js";
|
||||
newScript.addEventListener("load", function() {scriptItem2 = scriptItem;});
|
||||
document.body.appendChild(newScript);
|
||||
|
||||
/// Try loading from a tracking image URI (2)
|
||||
var newImage = document.createElement("img");
|
||||
newImage.id = "badimage2";
|
||||
newImage.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?reload=true";
|
||||
newImage.addEventListener("load", function() {imageItem2 = 'spoiled'});
|
||||
document.body.appendChild(newImage);
|
||||
|
||||
// Try loading from a tracking iframe URI (2)
|
||||
var newFrame = document.createElement("iframe");
|
||||
newFrame.id = "badframe2";
|
||||
newFrame.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html"
|
||||
newFrame.addEventListener("load", function() {frameItem2 = 'spoiled'});
|
||||
document.body.appendChild(newFrame);
|
||||
</script>
|
||||
|
||||
The following should not be hidden:
|
||||
<div id="styleCheck">STYLE TEST</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,48 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var scriptItem = "untouched";
|
||||
|
||||
function checkLoads() {
|
||||
// Make sure the javascript did not load.
|
||||
window.parent.is(scriptItem, "untouched", "Should not load bad javascript");
|
||||
|
||||
// Make sure the css did not load.
|
||||
var elt = document.getElementById("styleCheck");
|
||||
var style = document.defaultView.getComputedStyle(elt, "");
|
||||
window.parent.isnot(style.visibility, "hidden", "Should not load bad css");
|
||||
|
||||
// Call parent.loadTestFrame again to test classification metadata in HTTP
|
||||
// cache entries.
|
||||
if (window.parent.firstLoad) {
|
||||
window.parent.info("Reloading from cache...");
|
||||
window.parent.firstLoad = false;
|
||||
window.parent.loadTestFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
// End (parent) test.
|
||||
window.parent.SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Try loading from a malware javascript URI -->
|
||||
<script type="text/javascript" src="http://malware.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"></script>
|
||||
|
||||
<!-- Try loading from a malware css URI -->
|
||||
<link rel="stylesheet" type="text/css" href="http://malware.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
|
||||
|
||||
<!-- XXX How is this part of the test supposed to work (= be checked)? -->
|
||||
<!-- Try loading a marked-as-malware css through an @import from a clean URI -->
|
||||
<link rel="stylesheet" type="text/css" href="import.css"></link>
|
||||
</head>
|
||||
|
||||
<body onload="checkLoads()">
|
||||
The following should not be hidden:
|
||||
<div id="styleCheck">STYLE TEST</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,10 +0,0 @@
|
||||
onmessage = function() {
|
||||
try {
|
||||
importScripts("evilWorker.js");
|
||||
} catch(ex) {
|
||||
postMessage("success");
|
||||
return;
|
||||
}
|
||||
|
||||
postMessage("failure");
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
#styleCheck { visibility: hidden; }
|
||||
@@ -1 +0,0 @@
|
||||
scriptItem = "loaded malware javascript!";
|
||||
@@ -1,3 +0,0 @@
|
||||
onmessage = function() {
|
||||
postMessage("loaded bad file");
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
/* malware.example.com is in the malware database.
|
||||
classifierBad.css does not actually exist. */
|
||||
@import url("http://malware.example.com/tests/docshell/test/classifierBad.css");
|
||||
@@ -1,15 +0,0 @@
|
||||
[DEFAULT]
|
||||
skip-if = buildapp == 'b2g' || e10s
|
||||
support-files =
|
||||
classifierFrame.html
|
||||
cleanWorker.js
|
||||
evil.css
|
||||
evil.js
|
||||
evilWorker.js
|
||||
import.css
|
||||
raptor.jpg
|
||||
track.html
|
||||
workerFrame.html
|
||||
|
||||
[test_classifier.html]
|
||||
[test_classifier_worker.html]
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
@@ -1,86 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the URI Classifier</title>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var Cc = SpecialPowers.Cc;
|
||||
var Ci = SpecialPowers.Ci;
|
||||
|
||||
// Add https://allowlisted.example.com to the permissions manager
|
||||
SpecialPowers.addPermission("trackingprotection",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
{ url: "https://allowlisted.example.com" });
|
||||
|
||||
// Add some URLs to the tracking database
|
||||
var testData = "tracking.example.com/";
|
||||
var testUpdate =
|
||||
"n:1000\ni:test-track-simple\nad:1\n" +
|
||||
"a:524:32:" + testData.length + "\n" +
|
||||
testData;
|
||||
|
||||
var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"]
|
||||
.getService(Ci.nsIUrlClassifierDBService);
|
||||
|
||||
function clearPermissions() {
|
||||
SpecialPowers.removePermission("trackingprotection",
|
||||
{ url: "https://allowlisted.example.com" });
|
||||
ok(!SpecialPowers.testPermission("trackingprotection",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
{ url: "https://allowlisted.example.com" }));
|
||||
}
|
||||
|
||||
function doUpdate(update) {
|
||||
var listener = {
|
||||
QueryInterface: function(iid)
|
||||
{
|
||||
if (iid.equals(Ci.nsISupports) ||
|
||||
iid.equals(Ci.nsIUrlClassifierUpdateObserver))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
updateUrlRequested: function(url) { },
|
||||
streamFinished: function(status) { },
|
||||
updateError: function(errorCode) {
|
||||
ok(false, "Couldn't update classifier.");
|
||||
// Abort test.
|
||||
SimpleTest.finish();
|
||||
},
|
||||
updateSuccess: function(requestedTimeout) {
|
||||
document.getElementById("testFrame").src = "allowlistAnnotatedFrame.html";
|
||||
}
|
||||
};
|
||||
|
||||
dbService.beginUpdate(listener, "test-track-simple", "");
|
||||
dbService.beginStream("", "");
|
||||
dbService.updateStream(update);
|
||||
dbService.finishStream();
|
||||
dbService.finishUpdate();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{"set" : [["urlclassifier.trackingTable", "test-track-simple"],
|
||||
["privacy.trackingprotection.enabled", true],
|
||||
["channelclassifier.allowlist_example", true]]},
|
||||
function() { doUpdate(testUpdate); });
|
||||
|
||||
// Expected finish() call is in "allowlistedAnnotatedFrame.html".
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</pre>
|
||||
<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,81 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the URI Classifier</title>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var Cc = SpecialPowers.Cc;
|
||||
var Ci = SpecialPowers.Ci;
|
||||
|
||||
// Add some URLs to the tracking database
|
||||
var testData = "tracking.example.com/";
|
||||
var testUpdate =
|
||||
"n:1000\ni:test-track-simple\nad:1\n" +
|
||||
"a:524:32:" + testData.length + "\n" +
|
||||
testData;
|
||||
|
||||
function cleanup() {
|
||||
SpecialPowers.clearUserPref("privacy.trackingprotection.enabled");
|
||||
SpecialPowers.clearUserPref("channelclassifier.allowlist_example");
|
||||
}
|
||||
|
||||
var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"]
|
||||
.getService(Ci.nsIUrlClassifierDBService);
|
||||
|
||||
function doUpdate(update) {
|
||||
var listener = {
|
||||
QueryInterface: function(iid)
|
||||
{
|
||||
if (iid.equals(Ci.nsISupports) ||
|
||||
iid.equals(Ci.nsIUrlClassifierUpdateObserver))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
updateUrlRequested: function(url) { },
|
||||
streamFinished: function(status) { },
|
||||
updateError: function(errorCode) {
|
||||
ok(false, "Couldn't update classifier.");
|
||||
// Abort test.
|
||||
SimpleTest.finish();
|
||||
},
|
||||
updateSuccess: function(requestedTimeout) {
|
||||
SpecialPowers.setBoolPref("privacy.trackingprotection.enabled", true);
|
||||
// Make sure chrome:// URIs are processed. This does not white-list
|
||||
// any URIs unless 'https://allowlisted.example.com' is added in the
|
||||
// permission manager (see test_allowlisted_annotations.html)
|
||||
SpecialPowers.setBoolPref("channelclassifier.allowlist_example", true);
|
||||
document.getElementById("testFrame").src = "classifiedAnnotatedFrame.html";
|
||||
}
|
||||
};
|
||||
|
||||
dbService.beginUpdate(listener, "test-track-simple", "");
|
||||
dbService.beginStream("", "");
|
||||
dbService.updateStream(update);
|
||||
dbService.finishStream();
|
||||
dbService.finishUpdate();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{"set" : [["urlclassifier.trackingTable", "test-track-simple"]]},
|
||||
function() { doUpdate(testUpdate); });
|
||||
|
||||
// Expected finish() call is in "classifiedAnnotatedFrame.html".
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</pre>
|
||||
<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,78 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the URI Classifier</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var Cc = SpecialPowers.Cc;
|
||||
var Ci = SpecialPowers.Ci;
|
||||
var firstLoad = true;
|
||||
|
||||
// Add some URLs to the malware database.
|
||||
var testData = "malware.example.com/";
|
||||
var testUpdate =
|
||||
"n:1000\ni:test-malware-simple\nad:1\n" +
|
||||
"a:524:32:" + testData.length + "\n" +
|
||||
testData;
|
||||
|
||||
var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"]
|
||||
.getService(Ci.nsIUrlClassifierDBService);
|
||||
|
||||
function loadTestFrame() {
|
||||
document.getElementById("testFrame").src = "classifierFrame.html";
|
||||
}
|
||||
|
||||
function doUpdate(update) {
|
||||
var listener = {
|
||||
QueryInterface: SpecialPowers.wrapCallback(function(iid)
|
||||
{
|
||||
if (iid.equals(Ci.nsISupports) ||
|
||||
iid.equals(Ci.nsIUrlClassifierUpdateObserver))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}),
|
||||
updateUrlRequested: function(url) { },
|
||||
streamFinished: function(status) { },
|
||||
updateError: function(errorCode) {
|
||||
ok(false, "Couldn't update classifier.");
|
||||
// Abort test.
|
||||
SimpleTest.finish();
|
||||
},
|
||||
updateSuccess: function(requestedTimeout) {
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{"set" : [["browser.safebrowsing.malware.enabled", true]]},
|
||||
loadTestFrame);
|
||||
}
|
||||
};
|
||||
|
||||
dbService.beginUpdate(listener, "test-malware-simple", "");
|
||||
dbService.beginStream("", "");
|
||||
dbService.updateStream(update);
|
||||
dbService.finishStream();
|
||||
dbService.finishUpdate();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{"set" : [["urlclassifier.malwareTable", "test-malware-simple"],
|
||||
["urlclassifier.phishTable", "test-phish-simple"]]},
|
||||
function() { doUpdate(testUpdate); });
|
||||
|
||||
// Expected finish() call is in "classifierFrame.html".
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</pre>
|
||||
<iframe id="testFrame" onload=""></iframe>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user