Bug 1468503 - Implement nsIEffectiveTLDService.hasRootDomain, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 13 Jun 2018 12:29:39 -0700
changeset 807201 ca68fa3212d349d7edac9777b259039c22fae998
parent 807200 b75ad30847fe46f49fcd93975de49609bd820248
child 807202 73a994c463a0b1f6cab41b8c48f04cb99a1c0008
push id113043
push userrwood@mozilla.com
push dateWed, 13 Jun 2018 21:05:33 +0000
reviewerssmaug
bugs1468503
milestone62.0a1
Bug 1468503 - Implement nsIEffectiveTLDService.hasRootDomain, r=smaug
netwerk/dns/nsEffectiveTLDService.cpp
netwerk/dns/nsIEffectiveTLDService.idl
toolkit/components/cleardata/ClearDataService.js
--- a/netwerk/dns/nsEffectiveTLDService.cpp
+++ b/netwerk/dns/nsEffectiveTLDService.cpp
@@ -341,8 +341,40 @@ bool
 nsEffectiveTLDService::LookupForAdd(const nsACString& aHost, TLDCacheEntry** aEntry)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   const uint32_t hash = HashString(aHost.BeginReading(), aHost.Length());
   *aEntry = &mMruTable[hash % kTableSize];
   return (*aEntry)->mHost == aHost;
 }
+
+NS_IMETHODIMP
+nsEffectiveTLDService::HasRootDomain(const nsACString& aInput,
+                                     const nsACString& aHost,
+                                     bool* aResult)
+{
+  if (NS_WARN_IF(!aResult)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aResult = false;
+
+  // If the strings are the same, we obviously have a match.
+  if (aInput == aHost) {
+    *aResult = true;
+    return NS_OK;
+  }
+
+  // If aHost is not found, we know we do not have it as a root domain.
+  int32_t index = nsAutoCString(aInput).Find(aHost.BeginReading());
+  if (index == kNotFound) {
+    return NS_OK;
+  }
+
+  // Otherwise, we have aHost as our root domain iff the index of aHost is
+  // aHost.length subtracted from our length and (since we do not have an
+  // exact match) the character before the index is a dot or slash.
+  *aResult = index > 0 &&
+             (uint32_t)index == aInput.Length() - aHost.Length() &&
+             (aInput[index - 1] == '.' || aInput[index - 1] == '/');
+  return NS_OK;
+}
--- a/netwerk/dns/nsIEffectiveTLDService.idl
+++ b/netwerk/dns/nsIEffectiveTLDService.idl
@@ -116,10 +116,21 @@ interface nsIEffectiveTLDService : nsISu
      *              "bbc.co.uk" would throw NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS.
      *
      * @param   aHost   The host to be analyzed. Any additional parts (e.g. scheme,
      *                  port, or path) will cause this method to throw. ASCII/ACE and
      *                  UTF8 encodings are acceptable as input; normalization will
      *                  be performed as specified in getBaseDomain().
      */
     ACString getNextSubDomain(in AUTF8String aHost);
+
+    /**
+     * Returns true if the |aInput| in is part of the root domain of |aHost|.
+     * For example, if |aInput| is "www.mozilla.org", and we pass in
+     * "mozilla.org" as |aHost|, this will return true.  It would return false
+     * the other way around.
+     *
+     * @param aInput The host to be analyzed.
+     * @param aHost  The host to compare to.
+     */
+    bool hasRootDomain(in AUTF8String aInput, in AUTF8String aHost);
 };
 
--- a/toolkit/components/cleardata/ClearDataService.js
+++ b/toolkit/components/cleardata/ClearDataService.js
@@ -13,16 +13,19 @@ XPCOMUtils.defineLazyModuleGetters(this,
   OfflineAppCacheHelper: "resource://gre/modules/offlineAppCache.jsm",
   ServiceWorkerCleanUp: "resource://gre/modules/ServiceWorkerCleanUp.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
 });
 
 XPCOMUtils.defineLazyServiceGetter(this, "sas",
                                    "@mozilla.org/storage/activity-service;1",
                                    "nsIStorageActivityService");
+XPCOMUtils.defineLazyServiceGetter(this, "eTLDService",
+                                   "@mozilla.org/network/effective-tld-service;1",
+                                   "nsIEffectiveTLDService");
 
 // A Cleaner is an object with 3 methods. These methods must return a Promise
 // object. Here a description of these methods:
 // * deleteAll() - this method _must_ exist. When called, it deletes all the
 //                 data owned by the cleaner.
 // * deleteByPrincipal() - this method is implemented only if the cleaner knows
 //                         how to delete data by nsIPrincipal. If not
 //                         implemented, deleteByHost will be used instead.
@@ -183,17 +186,17 @@ const PluginDataCleaner = {
       new Promise(aResolve => setTimeout(aResolve, 10000 /* 10 seconds */))
     ]);
   },
 };
 
 const DownloadsCleaner = {
   deleteByHost(aHost, aOriginAttributes) {
     return Downloads.getList(Downloads.ALL).then(aList => {
-      aList.removeFinished(aDownload => hasRootDomain(
+      aList.removeFinished(aDownload => eTLDService.hasRootDomain(
         Services.io.newURI(aDownload.source.url).host, aHost));
     });
   },
 
   deleteByRange(aFrom, aTo) {
     // Convert microseconds back to milliseconds for date comparisons.
     let rangeBeginMs = aFrom / 1000;
     let rangeEndMs = aTo / 1000;
@@ -208,17 +211,17 @@ const DownloadsCleaner = {
     return Downloads.getList(Downloads.ALL).then(aList => {
       aList.removeFinished(null);
     });
   },
 };
 
 const PasswordsCleaner = {
   deleteByHost(aHost, aOriginAttributes) {
-    return this._deleteInternal(aLogin => hasRootDomain(aLogin.hostname, aHost));
+    return this._deleteInternal(aLogin => eTLDService.hasRootDomain(aLogin.hostname, aHost));
   },
 
   deleteAll() {
     return this._deleteInternal(() => true);
   },
 
   _deleteInternal(aCb) {
     return new Promise(aResolve => {
@@ -485,17 +488,17 @@ const AuthCacheCleaner = {
 
 const PermissionsCleaner = {
   deleteByHost(aHost, aOriginAttributes) {
     return new Promise(aResolve => {
       let enumerator = Services.perms.enumerator;
       while (enumerator.hasMoreElements()) {
         let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
         try {
-          if (hasRootDomain(perm.principal.URI.host, aHost)) {
+          if (eTLDService.hasRootDomain(perm.principal.URI.host, aHost)) {
             Services.perms.removePermission(perm);
           }
         } catch (ex) {
           // Ignore entry
         }
       }
 
       aResolve();
@@ -561,17 +564,17 @@ const SecuritySettingsCleaner = {
       for (let type of [Ci.nsISiteSecurityService.HEADER_HSTS,
                         Ci.nsISiteSecurityService.HEADER_HPKP]) {
         // Also remove HSTS/HPKP/OMS information for subdomains by enumerating
         // the information in the site security service.
         let enumerator = sss.enumerate(type);
         while (enumerator.hasMoreElements()) {
           let entry = enumerator.getNext();
           let hostname = entry.QueryInterface(Ci.nsISiteSecurityState).hostname;
-          if (hasRootDomain(hostname, aHost)) {
+          if (eTLDService.hasRootDomain(hostname, aHost)) {
             // This uri is used as a key to remove the state.
             let uri = Services.io.newURI("https://" + hostname);
             sss.removeState(type, uri, 0, entry.originAttributes);
           }
         }
       }
 
       aResolve();
@@ -759,32 +762,8 @@ ClearDataService.prototype = Object.free
       return aHelper(c.cleaner).catch(() => { resultFlags |= c.flag; });
     });
     Promise.all(promises).then(() => { aCallback.onDataDeleted(resultFlags); });
     return Cr.NS_OK;
   },
 });
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ClearDataService]);
-
-/**
- * Returns true if the string passed in is part of the root domain of the
- * current string.  For example, if this is "www.mozilla.org", and we pass in
- * "mozilla.org", this will return true.  It would return false the other way
- * around.
- */
-function hasRootDomain(str, aDomain) {
-  let index = str.indexOf(aDomain);
-  // If aDomain is not found, we know we do not have it as a root domain.
-  if (index == -1)
-    return false;
-
-  // If the strings are the same, we obviously have a match.
-  if (str == aDomain)
-    return true;
-
-  // Otherwise, we have aDomain as our root domain iff the index of aDomain is
-  // aDomain.length subtracted from our length and (since we do not have an
-  // exact match) the character before the index is a dot or slash.
-  let prevChar = str[index - 1];
-  return (index == (str.length - aDomain.length)) &&
-         (prevChar == "." || prevChar == "/");
-}