Bug 1377287: Add PublicSuffix.getPublicSuffix. r=liuche
authorMichael Comella <michael.l.comella@gmail.com>
Mon, 17 Jul 2017 16:37:02 -0700
changeset 422308 be3b083e82b8a0eb76d1094f63d3290a8ee8e2bf
parent 422307 f981c0d4269fc7b3621318522b0fd19167f81060
child 422309 e25baed29efd7119609ad7838f46e33ee5ecebf7
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersliuche
bugs1377287
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1377287: Add PublicSuffix.getPublicSuffix. r=liuche This should not be run in AS yet because each time it's called, it reads from the disk and even the tests on desktop take 3s+ to run. MozReview-Commit-ID: 5h4BcH3myCn
mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffix.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/util/publicsuffix/TestPublicSuffix.java
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffix.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffix.java
@@ -45,16 +45,52 @@ public class PublicSuffix {
         if (index == -1) {
             return domain;
         }
 
         return domain.substring(0, index);
     }
 
     /**
+     * Returns the public suffix with the specified number of additional parts.
+     *
+     * For example, the public suffix of "www.m.bbc.co.uk" (with 0 additional parts) is "co.uk".
+     * With 1 additional part: "bbc.co.uk".
+     *
+     * @throws IllegalArgumentException if additionalPartCount is less than zero.
+     * @throws NullPointerException if the Context or domain are null.
+     * @return the public suffix with the specified number of additional parts, or the empty string if a public suffix does not exist.
+     */
+    @NonNull
+    @WorkerThread // This method might need to load data from disk
+    public static String getPublicSuffix(@NonNull final Context context, @NonNull final String domain, final int additionalPartCount) {
+        if (context == null) { throw new NullPointerException("Expected non-null Context argument"); }
+        if (domain == null) { throw new NullPointerException("Expected non-null domain argument"); }
+
+        if (additionalPartCount < 0) {
+            throw new IllegalArgumentException("Expected additionalPartCount > 0. Got: " + additionalPartCount);
+        }
+
+        final int publicSuffixCombinedIndex = findPublicSuffixIndex(context, domain);
+        if (publicSuffixCombinedIndex < 0) {
+            return "";
+        }
+
+        final String publicSuffix = domain.substring(publicSuffixCombinedIndex + 1); // +1 to remove prefix ".".
+
+        final int nextPartIndex = publicSuffix.indexOf('.');
+        final String publicSuffixFirstPart = nextPartIndex < 0 ? publicSuffix : publicSuffix.substring(0, nextPartIndex);
+
+        final List<String> domainParts = normalizeAndSplit(domain);
+        final int publicSuffixPartsIndex = domainParts.indexOf(publicSuffixFirstPart);
+        final int returnedPartsIndex = Math.max(0, publicSuffixPartsIndex - additionalPartCount);
+        return TextUtils.join(".", domainParts.subList(returnedPartsIndex, domainParts.size()));
+    }
+
+    /**
      * Returns the index of the leftmost part of the public suffix, or -1 if not found.
      */
     @WorkerThread
     private static int findPublicSuffixIndex(Context context, String domain) {
         final List<String> parts = normalizeAndSplit(domain);
         final int partsSize = parts.size();
         final Set<String> exact = PublicSuffixPatterns.getExactSet(context);
 
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/util/publicsuffix/TestPublicSuffix.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/util/publicsuffix/TestPublicSuffix.java
@@ -1,16 +1,22 @@
 package org.mozilla.gecko.util.publicsuffix;
 
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mozilla.gecko.background.testhelpers.TestRunner;
 import org.robolectric.RuntimeEnvironment;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 @RunWith(TestRunner.class)
 public class TestPublicSuffix {
     @Test
     public void testStripPublicSuffix() {
         // Test empty value
         Assert.assertEquals("",
                 PublicSuffix.stripPublicSuffix(RuntimeEnvironment.application, ""));
 
@@ -54,9 +60,98 @@ public class TestPublicSuffix {
         Assert.assertEquals("example",
                 PublicSuffix.stripPublicSuffix(RuntimeEnvironment.application, "example.org"));
     }
 
     @Test(expected = NullPointerException.class)
     public void testStripPublicSuffixThrowsException() {
         PublicSuffix.stripPublicSuffix(RuntimeEnvironment.application, null);
     }
+
+    @Test
+    public void testGetPublicSuffixZeroAdditionalParts() {
+        final Map<String, String> inputToExpected = new HashMap<>();
+
+        // Empty value.
+        inputToExpected.put("", "");
+        inputToExpected.put(" ", "");
+
+        // Test domains with public suffix
+        inputToExpected.put("www.mozilla.org", "org");
+        inputToExpected.put("www.google.com", "com");
+        inputToExpected.put("foobar.blogspot.com", "blogspot.com");
+        inputToExpected.put("independent.co.uk", "co.uk");
+        inputToExpected.put("biz.com.ua", "com.ua");
+        inputToExpected.put("example.org", "org");
+        inputToExpected.put("example.pvt.k12.ma.us", "pvt.k12.ma.us");
+
+        // Test domain without public suffix
+        inputToExpected.put("localhost", "");
+        inputToExpected.put("firefox.mozilla", "");
+
+        // IDN domains
+        inputToExpected.put("ουτοπία.δπθ.gr", "gr");
+        inputToExpected.put("a网络A.网络.cn", "网络.cn");
+
+        // Other non-domain values
+        inputToExpected.put("192.168.0.1", "");
+        inputToExpected.put("asdflkj9uahsd", "");
+
+        // Other trailing and other types of dots
+        inputToExpected.put("www.mozilla。home.example。org", "org");
+        inputToExpected.put("example.org", "org");
+
+        for (final Map.Entry<String, String> entry : inputToExpected.entrySet()) {
+            final String input = entry.getKey();
+            final String expected = entry.getValue();
+            Assert.assertEquals("for input:" + input + "||", expected,
+                    PublicSuffix.getPublicSuffix(RuntimeEnvironment.application, input, 0));
+        }
+    }
+
+    @Test
+    public void testGetPublicSuffixNonZeroAdditionalParts() {
+        final Map<String, String> inputToExpected = new HashMap<>();
+
+        inputToExpected.put("www.mozilla.org", "mozilla.org");
+        inputToExpected.put("www.google.com", "google.com");
+        inputToExpected.put("bbc.co.uk", "bbc.co.uk");
+        inputToExpected.put("www.bbc.co.uk", "bbc.co.uk");
+
+        for (final Map.Entry<String, String> entry : inputToExpected.entrySet()) {
+            final String input = entry.getKey();
+            final String expected = entry.getValue();
+            Assert.assertEquals("for input:" + input + "||", expected,
+                    PublicSuffix.getPublicSuffix(RuntimeEnvironment.application, input, 1));
+        }
+
+        // More than one additional part.
+        Assert.assertEquals("m.bbc.co.uk",
+                PublicSuffix.getPublicSuffix(RuntimeEnvironment.application, "www.m.bbc.co.uk", 2));
+
+        // Look for more additional parts than exist in the host: the full host should be returned.
+        final List<String> inputsAndExpecteds = new ArrayList<>(Arrays.asList(
+                "google.com",
+                "www.google.com",
+                "bbc.co.uk"
+        ));
+
+        for (final String inputAndExpected : inputsAndExpecteds) {
+            Assert.assertEquals(inputAndExpected,
+                    PublicSuffix.getPublicSuffix(RuntimeEnvironment.application, inputAndExpected, 100));
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetPublicSuffixWithNegativeAdditionalPartCountThrows() {
+        PublicSuffix.getPublicSuffix(RuntimeEnvironment.application, "whatever-doesnt-matter", -1);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testGetPublicSuffixWithNullContextThrows() {
+        PublicSuffix.getPublicSuffix(null, "whatever", 0);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testGetPublicSuffixWithNullDomainThrows() {
+        PublicSuffix.getPublicSuffix(RuntimeEnvironment.application, null, 0);
+    }
 }