diff options
Diffstat (limited to 'url/android')
20 files changed, 282 insertions, 488 deletions
diff --git a/url/android/gurl_android.cc b/url/android/gurl_android.cc index bf398a132..8de79bbcd 100644 --- a/url/android/gurl_android.cc +++ b/url/android/gurl_android.cc @@ -15,9 +15,10 @@ #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/memory/ptr_util.h" +#include "base/strings/string_util.h" #include "url/android/parsed_android.h" -#include "url/gurl_jni_headers/GURL_jni.h" #include "url/third_party/mozilla/url_parse.h" +#include "url/url_jni_headers/GURL_jni.h" using base::android::AttachCurrentThread; using base::android::JavaParamRef; @@ -49,6 +50,10 @@ static std::unique_ptr<GURL> FromJavaGURL(JNIEnv* env, static void InitFromGURL(JNIEnv* env, const GURL& gurl, const JavaRef<jobject>& target) { + // Ensure that the spec only contains US-ASCII (single-byte characters) or the + // parsed indices will be wrong as the indices are in bytes while Java Strings + // are always 16-bit. + DCHECK(base::IsStringASCII(gurl.possibly_invalid_spec())); Java_GURL_init( env, target, base::android::ConvertUTF8ToJavaString(env, gurl.possibly_invalid_spec()), @@ -157,4 +162,39 @@ static jlong JNI_GURL_CreateNative(JNIEnv* env, FromJavaGURL(env, j_spec, is_valid, parsed_ptr).release()); } +static void JNI_GURL_ReplaceComponents( + JNIEnv* env, + const JavaParamRef<jstring>& j_spec, + jboolean is_valid, + jlong parsed_ptr, + const JavaParamRef<jstring>& j_username_replacement, + jboolean clear_username, + const JavaParamRef<jstring>& j_password_replacement, + jboolean clear_password, + const JavaParamRef<jobject>& j_result) { + GURL::Replacements replacements; + + // Replacement strings must remain in scope for ReplaceComponents(). + std::string username; + std::string password; + + if (clear_username) { + replacements.ClearUsername(); + } else if (j_username_replacement) { + username = ConvertJavaStringToUTF8(env, j_username_replacement); + replacements.SetUsernameStr(username); + } + + if (clear_password) { + replacements.ClearPassword(); + } else if (j_password_replacement) { + password = ConvertJavaStringToUTF8(env, j_password_replacement); + replacements.SetPasswordStr(password); + } + + std::unique_ptr<GURL> original = + FromJavaGURL(env, j_spec, is_valid, parsed_ptr); + InitFromGURL(env, original->ReplaceComponents(replacements), j_result); +} + } // namespace url diff --git a/url/android/gurl_android.h b/url/android/gurl_android.h index 8b356070d..98cceaf0f 100644 --- a/url/android/gurl_android.h +++ b/url/android/gurl_android.h @@ -8,12 +8,13 @@ #include <memory> #include "base/android/scoped_java_ref.h" +#include "base/component_export.h" #include "base/containers/span.h" #include "url/gurl.h" namespace url { -class GURLAndroid { +class COMPONENT_EXPORT(URL) GURLAndroid { public: static std::unique_ptr<GURL> ToNativeGURL( JNIEnv* env, diff --git a/url/android/gurl_test_init.cc b/url/android/gurl_test_init.cc new file mode 100644 index 000000000..94a66acdd --- /dev/null +++ b/url/android/gurl_test_init.cc @@ -0,0 +1,27 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "url/android/gurl_test_init.h" +#include "url/url_util.h" + +namespace url { +// Registers enough to have //url parsing work as expected. +// Does not directly reference //content or //chrome to save on compile times. +void RegisterSchemesForRobolectric() { + // Schemes from content/common/url_schemes.cc: + url::AddStandardScheme("chrome", SCHEME_WITH_HOST); + url::AddStandardScheme("chrome-untrusted", SCHEME_WITH_HOST); + url::AddStandardScheme("chrome-error", SCHEME_WITH_HOST); + url::AddNoAccessScheme("chrome-error"); + + // Schemes from chrome/common/chrome_content_client.cc: + url::AddStandardScheme("isolated-app", SCHEME_WITH_HOST); + url::AddStandardScheme("chrome-native", SCHEME_WITH_HOST); + url::AddNoAccessScheme("chrome-native"); + url::AddStandardScheme("chrome-search", SCHEME_WITH_HOST); + url::AddStandardScheme("chrome-distiller", SCHEME_WITH_HOST); + url::AddStandardScheme("android-app", SCHEME_WITH_HOST); + url::AddLocalScheme("content"); +} +} // namespace url diff --git a/url/android/gurl_test_init.h b/url/android/gurl_test_init.h new file mode 100644 index 000000000..c8f958535 --- /dev/null +++ b/url/android/gurl_test_init.h @@ -0,0 +1,11 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_ANDROID_GURL_TEST_INIT_H_ +#define URL_ANDROID_GURL_TEST_INIT_H_ + +namespace url { +void RegisterSchemesForRobolectric(); +} +#endif // URL_ANDROID_GURL_TEST_INIT_H_ diff --git a/url/android/java/src/org/chromium/url/GURL.java b/url/android/java/src/org/chromium/url/GURL.java index 09698e4bb..8aaa4903b 100644 --- a/url/android/java/src/org/chromium/url/GURL.java +++ b/url/android/java/src/org/chromium/url/GURL.java @@ -11,11 +11,12 @@ import androidx.annotation.Nullable; import com.google.errorprone.annotations.DoNotMock; +import org.jni_zero.CalledByNative; +import org.jni_zero.JNINamespace; +import org.jni_zero.NativeMethods; + import org.chromium.base.Log; import org.chromium.base.ThreadUtils; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeMethods; import org.chromium.base.library_loader.LibraryLoader; import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.task.PostTask; @@ -37,7 +38,7 @@ import java.util.Random; * reconstruct a GURL in Java, allowing it to be much faster in the common case and easier to use. */ @JNINamespace("url") -@DoNotMock("Create a real instance instead. For Robolectric, see JUnitTestGURLs.java") +@DoNotMock("Create a real instance instead.") public class GURL { private static final String TAG = "GURL"; /* package */ static final int SERIALIZER_VERSION = 1; @@ -135,8 +136,6 @@ public class GURL { @CalledByNative private void init(String spec, boolean isValid, Parsed parsed) { mSpec = spec; - // Ensure that the spec only contains US-ASCII or the parsed indices will be wrong. - assert mSpec.matches("\\A\\p{ASCII}*\\z"); mIsValid = isValid; mParsed = parsed; } @@ -267,6 +266,36 @@ public class GURL { return getNatives().domainIs(mSpec, mIsValid, mParsed.toNativeParsed(), domain); } + /** + * Returns a copy of the URL with components replaced. See native GURL::ReplaceComponents(). + * + * <p>Rules for replacement: 1. If a `clear*` boolean param is true, the component will be + * removed from the result. 2. Otherwise if the corresponding string param is non-null, its + * value will be used to replace the component. 3. If the string is null and the `clear*` + * boolean is false, the component will not be modified. + * + * @param username Username replacement. + * @param clearUsername True if the result should not contain a username. + * @param password Password replacement. + * @param clearPassword True if the result should not contain a password. + * @return Copy of the URL with replacements applied. + */ + public GURL replaceComponents( + String username, boolean clearUsername, String password, boolean clearPassword) { + GURL result = new GURL(); + getNatives() + .replaceComponents( + mSpec, + mIsValid, + mParsed.toNativeParsed(), + username, + clearUsername, + password, + clearPassword, + result); + return result; + } + @Override public final int hashCode() { return mSpec.hashCode(); @@ -405,5 +434,19 @@ public class GURL { * Reconstructs the native GURL for this Java GURL, returning its native pointer. */ long createNative(String spec, boolean isValid, long nativeParsed); + + /** + * Reconstructs the native GURL for this Java GURL and initializes |result| with the result + * of ReplaceComponents. + */ + void replaceComponents( + String spec, + boolean isValid, + long nativeParsed, + String username, + boolean clearUsername, + String password, + boolean clearPassword, + GURL result); } } diff --git a/url/android/java/src/org/chromium/url/IDNStringUtil.java b/url/android/java/src/org/chromium/url/IDNStringUtil.java index 10957b673..3887c2b38 100644 --- a/url/android/java/src/org/chromium/url/IDNStringUtil.java +++ b/url/android/java/src/org/chromium/url/IDNStringUtil.java @@ -4,8 +4,8 @@ package org.chromium.url; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; +import org.jni_zero.CalledByNative; +import org.jni_zero.JNINamespace; import java.net.IDN; diff --git a/url/android/java/src/org/chromium/url/Origin.java b/url/android/java/src/org/chromium/url/Origin.java index 87ce87066..9426264a0 100644 --- a/url/android/java/src/org/chromium/url/Origin.java +++ b/url/android/java/src/org/chromium/url/Origin.java @@ -4,9 +4,9 @@ package org.chromium.url; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeMethods; +import org.jni_zero.CalledByNative; +import org.jni_zero.JNINamespace; +import org.jni_zero.NativeMethods; /** An origin is either a (scheme, host, port) tuple or is opaque. */ @JNINamespace("url") diff --git a/url/android/java/src/org/chromium/url/Parsed.java b/url/android/java/src/org/chromium/url/Parsed.java index 75d12cb9a..d87e3a9f3 100644 --- a/url/android/java/src/org/chromium/url/Parsed.java +++ b/url/android/java/src/org/chromium/url/Parsed.java @@ -4,9 +4,9 @@ package org.chromium.url; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeMethods; +import org.jni_zero.CalledByNative; +import org.jni_zero.JNINamespace; +import org.jni_zero.NativeMethods; /** * A java wrapper for Parsed, GURL's internal parsed URI representation. diff --git a/url/android/javatests/src/org/chromium/url/GURLJavaTest.java b/url/android/javatests/src/org/chromium/url/GURLJavaTest.java index e684e5103..29d613e20 100644 --- a/url/android/javatests/src/org/chromium/url/GURLJavaTest.java +++ b/url/android/javatests/src/org/chromium/url/GURLJavaTest.java @@ -30,8 +30,7 @@ import java.net.URISyntaxException; @RunWith(BaseJUnit4ClassRunner.class) @Batch(Batch.UNIT_TESTS) public class GURLJavaTest { - @Mock - GURL.Natives mGURLMocks; + @Mock GURL.Natives mGURLMocks; @Before public void setUp() { @@ -159,45 +158,47 @@ public class GURLJavaTest { @SuppressWarnings(value = "AuthLeak") public void testSerialization() { GURL cases[] = { - // Common Standard URLs. - new GURL("https://www.google.com"), - new GURL("https://www.google.com/"), - new GURL("https://www.google.com/maps.htm"), - new GURL("https://www.google.com/maps/"), - new GURL("https://www.google.com/index.html"), - new GURL("https://www.google.com/index.html?q=maps"), - new GURL("https://www.google.com/index.html#maps/"), - new GURL("https://foo:bar@www.google.com/maps.htm"), - new GURL("https://www.google.com/maps/au/index.html"), - new GURL("https://www.google.com/maps/au/north"), - new GURL("https://www.google.com/maps/au/north/"), - new GURL("https://www.google.com/maps/au/index.html?q=maps#fragment/"), - new GURL("http://www.google.com:8000/maps/au/index.html?q=maps#fragment/"), - new GURL("https://www.google.com/maps/au/north/?q=maps#fragment"), - new GURL("https://www.google.com/maps/au/north?q=maps#fragment"), - // Less common standard URLs. - new GURL("filesystem:http://www.google.com/temporary/bar.html?baz=22"), - new GURL("file:///temporary/bar.html?baz=22"), - new GURL("ftp://foo/test/index.html"), - new GURL("gopher://foo/test/index.html"), - new GURL("ws://foo/test/index.html"), - // Non-standard, - new GURL("chrome://foo/bar.html"), - new GURL("httpa://foo/test/index.html"), - new GURL("blob:https://foo.bar/test/index.html"), - new GURL("about:blank"), - new GURL("data:foobar"), - new GURL("scheme:opaque_data"), - // Invalid URLs. - new GURL("foobar"), - // URLs containing the delimiter - new GURL("https://www.google.ca/" + GURL.SERIALIZER_DELIMITER + ",foo"), - new GURL("https://www.foo" + GURL.SERIALIZER_DELIMITER + "bar.com"), + // Common Standard URLs. + new GURL("https://www.google.com"), + new GURL("https://www.google.com/"), + new GURL("https://www.google.com/maps.htm"), + new GURL("https://www.google.com/maps/"), + new GURL("https://www.google.com/index.html"), + new GURL("https://www.google.com/index.html?q=maps"), + new GURL("https://www.google.com/index.html#maps/"), + new GURL("https://foo:bar@www.google.com/maps.htm"), + new GURL("https://www.google.com/maps/au/index.html"), + new GURL("https://www.google.com/maps/au/north"), + new GURL("https://www.google.com/maps/au/north/"), + new GURL("https://www.google.com/maps/au/index.html?q=maps#fragment/"), + new GURL("http://www.google.com:8000/maps/au/index.html?q=maps#fragment/"), + new GURL("https://www.google.com/maps/au/north/?q=maps#fragment"), + new GURL("https://www.google.com/maps/au/north?q=maps#fragment"), + // Less common standard URLs. + new GURL("filesystem:http://www.google.com/temporary/bar.html?baz=22"), + new GURL("file:///temporary/bar.html?baz=22"), + new GURL("ftp://foo/test/index.html"), + new GURL("gopher://foo/test/index.html"), + new GURL("ws://foo/test/index.html"), + // Non-standard, + new GURL("chrome://foo/bar.html"), + new GURL("httpa://foo/test/index.html"), + new GURL("blob:https://foo.bar/test/index.html"), + new GURL("about:blank"), + new GURL("data:foobar"), + new GURL("scheme:opaque_data"), + // Invalid URLs. + new GURL("foobar"), + // URLs containing the delimiter + new GURL("https://www.google.ca/" + GURL.SERIALIZER_DELIMITER + ",foo"), + new GURL("https://www.foo" + GURL.SERIALIZER_DELIMITER + "bar.com"), }; GURLJni.TEST_HOOKS.setInstanceForTesting(mGURLMocks); - doThrow(new RuntimeException("Should not re-initialize for deserialization when the " - + "version hasn't changed.")) + doThrow( + new RuntimeException( + "Should not re-initialize for deserialization when the " + + "version hasn't changed.")) .when(mGURLMocks) .init(any(), any()); for (GURL url : cases) { @@ -215,36 +216,35 @@ public class GURLJavaTest { @Test public void testSerializationWithVersionSkew() { GURL url = new GURL("https://www.google.com"); - String serialization = (GURL.SERIALIZER_VERSION + 1) - + ",0,0,0,0,foo,https://url.bad,blah,0,".replace(',', GURL.SERIALIZER_DELIMITER) - + url.getSpec(); + String serialization = + (GURL.SERIALIZER_VERSION + 1) + + ",0,0,0,0,foo,https://url.bad,blah,0," + .replace(',', GURL.SERIALIZER_DELIMITER) + + url.getSpec(); serialization = prependLengthToSerialization(serialization); GURL out = GURL.deserialize(serialization); deepAssertEquals(url, out); } - /** - * Tests that fields that aren't visible to java code are correctly serialized. - */ + /** Tests that fields that aren't visible to java code are correctly serialized. */ @SmallTest @Test public void testSerializationOfPrivateFields() { - String serialization = GURL.SERIALIZER_VERSION - + ",true," - // Outer Parsed. - + "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,false,true," - // Inner Parsed. - + "17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,true,false," - + "chrome://foo/bar.html"; + String serialization = + GURL.SERIALIZER_VERSION + + ",true," + // Outer Parsed. + + "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,false,true," + // Inner Parsed. + + "17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,true,false," + + "chrome://foo/bar.html"; serialization = serialization.replace(',', GURL.SERIALIZER_DELIMITER); serialization = prependLengthToSerialization(serialization); GURL url = GURL.deserialize(serialization); Assert.assertEquals(url.serialize(), serialization); } - /** - * Tests serialized GURL truncated by storage. - */ + /** Tests serialized GURL truncated by storage. */ @SmallTest @Test public void testTruncatedDeserialization() { @@ -254,9 +254,7 @@ public class GURLJavaTest { Assert.assertEquals(url, GURL.emptyGURL()); } - /** - * Tests serialized GURL truncated by storage. - */ + /** Tests serialized GURL truncated by storage. */ @SmallTest @Test public void testCorruptedSerializations() { @@ -288,6 +286,26 @@ public class GURLJavaTest { Assert.assertFalse(url1.domainIs("images.google.com")); } + // Test that replaceComponents is hooked up correctly. + @SmallTest + @Test + @SuppressWarnings(value = "AuthLeak") + public void testReplaceComponents() { + GURL url = new GURL("http://user:pass@google.com:99/foo;bar?q=a#ref"); + + GURL unchanged = url.replaceComponents(null, false, null, false); + Assert.assertEquals("user", unchanged.getUsername()); + Assert.assertEquals("pass", unchanged.getPassword()); + + GURL cleared = url.replaceComponents(null, true, null, true); + Assert.assertTrue(cleared.getUsername().isEmpty()); + Assert.assertTrue(cleared.getPassword().isEmpty()); + + GURL changed = url.replaceComponents("newusername", false, "newpassword", false); + Assert.assertEquals("newusername", changed.getUsername()); + Assert.assertEquals("newpassword", changed.getPassword()); + } + // Tests Mojom conversion. @SmallTest @Test @@ -306,7 +324,8 @@ public class GURLJavaTest { Assert.assertEquals("", new GURL(new String(new byte[] {1, 1, 1})).toMojom().url); // Too long. - Assert.assertEquals("", + Assert.assertEquals( + "", new GURL("https://www.google.com/".concat("a".repeat(2 * 1024 * 1024))) .toMojom() .url); diff --git a/url/android/javatests/src/org/chromium/url/GURLJavaTestHelper.java b/url/android/javatests/src/org/chromium/url/GURLJavaTestHelper.java index 975b009dc..2999295bd 100644 --- a/url/android/javatests/src/org/chromium/url/GURLJavaTestHelper.java +++ b/url/android/javatests/src/org/chromium/url/GURLJavaTestHelper.java @@ -4,9 +4,9 @@ package org.chromium.url; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeMethods; +import org.jni_zero.CalledByNative; +import org.jni_zero.JNINamespace; +import org.jni_zero.NativeMethods; /** * Helpers for GURLJavaTest that need to call into native code. diff --git a/url/android/javatests/src/org/chromium/url/JUnitTestGURLsTest.java b/url/android/javatests/src/org/chromium/url/JUnitTestGURLsTest.java deleted file mode 100644 index a23967c24..000000000 --- a/url/android/javatests/src/org/chromium/url/JUnitTestGURLsTest.java +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.url; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; - -import androidx.test.filters.SmallTest; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import org.chromium.base.Log; -import org.chromium.base.test.BaseJUnit4ClassRunner; -import org.chromium.base.test.util.Batch; - -import java.util.Map; - -/** - * Tests for JUnitTestGURLs. - */ -@RunWith(BaseJUnit4ClassRunner.class) -@Batch(Batch.UNIT_TESTS) -public class JUnitTestGURLsTest { - private static final String TAG = "JUnitTestGURLs"; - - @Mock - GURL.Natives mGURLMocks; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - private RuntimeException getErrorForGURL(GURL gurl) { - String serialized = gurl.serialize(); - Assert.assertEquals(-1, serialized.indexOf(",")); - serialized = serialized.replace(GURL.SERIALIZER_DELIMITER, ','); - - return new RuntimeException("Please update the serialization in JUnitTestGURLs.java for " - + gurl.getPossiblyInvalidSpec() + " to: '" + serialized + "'"); - } - - @SmallTest - @Test - public void testGURLEquivalence() throws Throwable { - doThrow(new RuntimeException("Deserialization required re-initialization.")) - .when(mGURLMocks) - .init(any(), any()); - - Throwable exception = null; - for (Map.Entry<String, String> entry : JUnitTestGURLs.sGURLMap.entrySet()) { - GURL gurl = new GURL(entry.getKey()); - try { - GURLJni.TEST_HOOKS.setInstanceForTesting(mGURLMocks); - GURL deserialized = JUnitTestGURLs.getGURL(entry.getKey()); - GURLJni.TEST_HOOKS.setInstanceForTesting(null); - GURLJavaTest.deepAssertEquals(deserialized, gurl); - } catch (Throwable e) { - GURLJni.TEST_HOOKS.setInstanceForTesting(null); - exception = getErrorForGURL(gurl); - Log.e(TAG, "Error: ", exception); - } - } - if (exception != null) throw exception; - } -} diff --git a/url/android/javatests/src/org/chromium/url/OriginJavaTestHelper.java b/url/android/javatests/src/org/chromium/url/OriginJavaTestHelper.java index 2eb9550ba..b4f628a70 100644 --- a/url/android/javatests/src/org/chromium/url/OriginJavaTestHelper.java +++ b/url/android/javatests/src/org/chromium/url/OriginJavaTestHelper.java @@ -4,8 +4,8 @@ package org.chromium.url; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeMethods; +import org.jni_zero.JNINamespace; +import org.jni_zero.NativeMethods; /** * Helpers for OriginJavaTest that need to call into native code. diff --git a/url/android/junit/src/org/chromium/url/ShadowGURLTest.java b/url/android/junit/src/org/chromium/url/ShadowGURLTest.java deleted file mode 100644 index a491de1a3..000000000 --- a/url/android/junit/src/org/chromium/url/ShadowGURLTest.java +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.url; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.annotation.Config; - -import org.chromium.base.test.BaseRobolectricTestRunner; - -/** Tests of {@link ShadowGURL}. */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(shadows = {ShadowGURL.class}) -public class ShadowGURLTest { - /* package */ static void deepAssertEquals(GURL expected, GURL actual) { - Assert.assertEquals(expected, actual); - Assert.assertEquals(expected.getScheme(), actual.getScheme()); - Assert.assertEquals(expected.getUsername(), actual.getUsername()); - Assert.assertEquals(expected.getPassword(), actual.getPassword()); - Assert.assertEquals(expected.getHost(), actual.getHost()); - Assert.assertEquals(expected.getPort(), actual.getPort()); - Assert.assertEquals(expected.getPath(), actual.getPath()); - Assert.assertEquals(expected.getQuery(), actual.getQuery()); - Assert.assertEquals(expected.getRef(), actual.getRef()); - } - - @Test - public void testComponents() { - GURL url = new GURL(JUnitTestGURLs.SEARCH_URL); - Assert.assertFalse(url.isEmpty()); - Assert.assertTrue(url.isValid()); - - Assert.assertEquals(JUnitTestGURLs.SEARCH_URL, url.getSpec()); - Assert.assertEquals("https", url.getScheme()); - Assert.assertEquals("", url.getUsername()); - Assert.assertEquals("", url.getPassword()); - Assert.assertEquals("www.google.com", url.getHost()); - Assert.assertEquals("", url.getPort()); - Assert.assertEquals("/search", url.getPath()); - Assert.assertEquals("q=test", url.getQuery()); - Assert.assertEquals("", url.getRef()); - } - - @Test - public void testEmpty() { - GURL url = new GURL(""); - Assert.assertFalse(url.isValid()); - - Assert.assertEquals("", url.getSpec()); - Assert.assertEquals("", url.getScheme()); - Assert.assertEquals("", url.getUsername()); - Assert.assertEquals("", url.getPassword()); - Assert.assertEquals("", url.getHost()); - Assert.assertEquals("", url.getPort()); - Assert.assertEquals("", url.getPath()); - Assert.assertEquals("", url.getQuery()); - Assert.assertEquals("", url.getRef()); - } - - @Test - public void testSerialization() { - GURL gurl = new GURL(JUnitTestGURLs.URL_1_WITH_PATH); - GURL deserialized = GURL.deserialize(gurl.serialize()); - - deepAssertEquals(deserialized, gurl); - } -} diff --git a/url/android/origin_android.cc b/url/android/origin_android.cc index a75a9a369..b44125ae1 100644 --- a/url/android/origin_android.cc +++ b/url/android/origin_android.cc @@ -11,13 +11,22 @@ #include "base/android/scoped_java_ref.h" #include "base/memory/ptr_util.h" #include "url/android/gurl_android.h" -#include "url/origin_jni_headers/Origin_jni.h" +#include "url/url_jni_headers/Origin_jni.h" namespace url { -base::android::ScopedJavaLocalRef<jobject> Origin::CreateJavaObject() const { +// friend +Origin CreateOpaqueOriginForAndroid(const std::string& scheme, + const std::string& host, + uint16_t port, + const base::UnguessableToken& nonce_token) { + return Origin::CreateOpaqueFromNormalizedPrecursorTuple( + scheme, host, port, Origin::Nonce(nonce_token)); +} + +base::android::ScopedJavaLocalRef<jobject> Origin::ToJavaObject() const { JNIEnv* env = base::android::AttachCurrentThread(); - const base::UnguessableToken* token = Origin::GetNonceForSerialization(); + const base::UnguessableToken* token = GetNonceForSerialization(); return Java_Origin_Constructor( env, base::android::ConvertUTF8ToJavaString(env, tuple_.scheme()), base::android::ConvertUTF8ToJavaString(env, tuple_.host()), tuple_.port(), @@ -34,42 +43,15 @@ Origin Origin::FromJavaObject( return std::move(*origin); } -// static -jlong Origin::CreateNative(JNIEnv* env, - const base::android::JavaRef<jstring>& java_scheme, - const base::android::JavaRef<jstring>& java_host, - uint16_t port, - bool is_opaque, - uint64_t token_high_bits, - uint64_t token_low_bits) { - const std::string& scheme = ConvertJavaStringToUTF8(env, java_scheme); - const std::string& host = ConvertJavaStringToUTF8(env, java_host); - - absl::optional<base::UnguessableToken> nonce_token = - base::UnguessableToken::Deserialize(token_high_bits, token_low_bits); - bool has_nonce = nonce_token.has_value(); - CHECK(has_nonce == is_opaque); - Origin::Nonce nonce; - if (has_nonce) { - nonce = Origin::Nonce(nonce_token.value()); - } - Origin origin = is_opaque - ? Origin::CreateOpaqueFromNormalizedPrecursorTuple( - scheme, host, port, nonce) - : Origin::CreateFromNormalizedTuple(scheme, host, port); - return reinterpret_cast<intptr_t>(new Origin(origin)); -} - static base::android::ScopedJavaLocalRef<jobject> JNI_Origin_CreateOpaque( JNIEnv* env) { - return Origin().CreateJavaObject(); + return Origin().ToJavaObject(); } static base::android::ScopedJavaLocalRef<jobject> JNI_Origin_CreateFromGURL( JNIEnv* env, const base::android::JavaParamRef<jobject>& j_gurl) { - return Origin::Create(*GURLAndroid::ToNativeGURL(env, j_gurl)) - .CreateJavaObject(); + return Origin::Create(*GURLAndroid::ToNativeGURL(env, j_gurl)).ToJavaObject(); } static jlong JNI_Origin_CreateNative( @@ -80,8 +62,19 @@ static jlong JNI_Origin_CreateNative( jboolean is_opaque, jlong token_high_bits, jlong token_low_bits) { - return Origin::CreateNative(env, java_scheme, java_host, port, is_opaque, - token_high_bits, token_low_bits); + const std::string& scheme = ConvertJavaStringToUTF8(env, java_scheme); + const std::string& host = ConvertJavaStringToUTF8(env, java_host); + + Origin origin; + if (is_opaque) { + absl::optional<base::UnguessableToken> nonce_token = + base::UnguessableToken::Deserialize(token_high_bits, token_low_bits); + origin = + CreateOpaqueOriginForAndroid(scheme, host, port, nonce_token.value()); + } else { + origin = Origin::CreateFromNormalizedTuple(scheme, host, port); + } + return reinterpret_cast<intptr_t>(new Origin(origin)); } } // namespace url diff --git a/url/android/origin_java_test_helper.cc b/url/android/origin_java_test_helper.cc index 62554d87d..3815d9764 100644 --- a/url/android/origin_java_test_helper.cc +++ b/url/android/origin_java_test_helper.cc @@ -21,8 +21,7 @@ static void JNI_OriginJavaTestHelper_TestOriginEquivalence(JNIEnv* env) { Origin::Create(GURL("http://a.com:8000")).DeriveNewOpaqueOrigin(), }; for (const Origin& origin : cases) { - base::android::ScopedJavaLocalRef<jobject> j_origin = - origin.CreateJavaObject(); + base::android::ScopedJavaLocalRef<jobject> j_origin = origin.ToJavaObject(); Origin sameOrigin = Origin::FromJavaObject(j_origin); if (origin != sameOrigin) { std::stringstream ss; diff --git a/url/android/parsed_android.cc b/url/android/parsed_android.cc index 36d8aa255..e3b0d242a 100644 --- a/url/android/parsed_android.cc +++ b/url/android/parsed_android.cc @@ -7,7 +7,7 @@ #include <jni.h> #include "base/android/jni_android.h" -#include "url/gurl_jni_headers/Parsed_jni.h" +#include "url/url_jni_headers/Parsed_jni.h" using base::android::AttachCurrentThread; using base::android::JavaRef; diff --git a/url/android/parsed_android.h b/url/android/parsed_android.h index 244ada55f..81daa4386 100644 --- a/url/android/parsed_android.h +++ b/url/android/parsed_android.h @@ -6,12 +6,14 @@ #define URL_ANDROID_PARSED_ANDROID_H_ #include "base/android/scoped_java_ref.h" +#include "base/component_export.h" #include "url/third_party/mozilla/url_parse.h" namespace url { class ParsedAndroid { public: + COMPONENT_EXPORT(URL) static base::android::ScopedJavaLocalRef<jobject> InitFromParsed( JNIEnv* env, const Parsed& parsed); diff --git a/url/android/robolectric_test_main.cc b/url/android/robolectric_jni_onload.cc index 28fb4d241..d6fd0ee27 100644 --- a/url/android/robolectric_test_main.cc +++ b/url/android/robolectric_jni_onload.cc @@ -1,15 +1,15 @@ -// Copyright 2022 The Chromium Authors +// Copyright 2023 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <jni.h> #include "base/android/base_jni_onload.h" #include "base/android/jni_android.h" +#include "url/android/gurl_test_init.h" extern "C" JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { base::android::InitVM(vm); base::android::OnJNIOnLoadInit(); - // TODO(1223993): Initialize GURL schemes, like in - // content::RegisterContentSchemes(). + url::RegisterSchemesForRobolectric(); return JNI_VERSION_1_4; } diff --git a/url/android/test/java/src/org/chromium/url/JUnitTestGURLs.java b/url/android/test/java/src/org/chromium/url/JUnitTestGURLs.java index 9f19c6c90..2eba46068 100644 --- a/url/android/test/java/src/org/chromium/url/JUnitTestGURLs.java +++ b/url/android/test/java/src/org/chromium/url/JUnitTestGURLs.java @@ -4,171 +4,35 @@ package org.chromium.url; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - /** - * A Helper class for JUnit tests to be able to use GURLs without requiring native initialization. - * This should be used sparingly, when converting junit tests to Batched Instrumentation tests is - * not feasible. - * - * If any more complex GURL behaviour is tested, like comparing Origins, the test should be written - * as an Instrumentation test instead - you should never mock GURL. + * A collection of test GURLs. */ public class JUnitTestGURLs { - // In order to add a test URL: - // 1. Add the URL String as a constant here. - // 2. Add the constant to the map below, with a placeholder string for the GURL serialization. - // 3. Run JUnitTestGURLsTest (eg. './tools/autotest.py -C out/Debug JUnitTestGURLsTest'). - // 4. Check logcat output or test exception for the correct serialization String, and place it - // in the map. - public static final String EXAMPLE_URL = "https://www.example.com/"; - public static final String HTTP_URL = "http://www.example.com/"; - public static final String URL_1 = "https://www.one.com/"; - public static final String URL_1_NUMERAL = "https://www.1.com/"; - public static final String URL_1_WITH_PATH = "https://www.one.com/some_path.html"; - public static final String URL_2 = "https://www.two.com/"; - public static final String URL_3 = "https://www.three.com/"; - public static final String MAPS_URL = "https://maps.google.com/"; - public static final String SEARCH_URL = "https://www.google.com/search?q=test"; - public static final String SEARCH_2_URL = "https://www.google.com/search?q=query"; - public static final String INITIAL_URL = "https://initial.com"; - public static final String SPECULATED_URL = "https://speculated.com"; - public static final String NTP_URL = "chrome://newtab/"; - public static final String NTP_NATIVE_URL = "chrome-native://newtab/"; - public static final String DOM_DISILLER_URL = "chrome-distiller://url"; - public static final String RED_1 = "https://www.red.com/page1"; - public static final String RED_2 = "https://www.red.com/page2"; - public static final String RED_3 = "https://www.red.com/page3"; - public static final String BLUE_1 = "https://www.blue.com/page1"; - public static final String BLUE_2 = "https://www.blue.com/page2"; - public static final String BLUE_3 = "https://www.blue.com/page3"; - public static final String AMP_URL = - "https://www.google.com/amp/www.nyt.com/ampthml/blogs.html"; - public static final String AMP_CACHE_URL = - "https://www.google.com/amp/s/www.nyt.com/ampthml/blogs.html"; - public static final String TEXT_FRAGMENT_URL = "https://www.example.com/#:~:text=selector"; - public static final String MULTI_TEXT_FRAGMENT_URL = - "https://www.example.com/#:~:text=selector1&text=selector2&text=selector3"; - public static final String INVALID_URL = "http://0x100.0/"; - public static final String GOOGLE_URL = "http://www.google.com/"; - public static final String GOOGLE_URL_DOGS = "http://www.google.com/dogs"; - public static final String GOOGLE_URL_DOGS_FUN = "http://www.google.com/dogs-are-fun"; - public static final String GOOGLE_URL_DOG = "http://www.google.com/dog"; - public static final String GOOGLE_URL_CAT = "http://www.google.com/cat"; - public static final String GOOGLE_URL_PIG = "http://www.google.com/pig"; - public static final String ABOUT_BLANK = "about:blank"; - public static final String CHROME_ABOUT = "chrome://about"; - - // Map of URL string to GURL serialization. - /* package */ static final Map<String, String> sGURLMap; - static { - Map<String, String> map = new HashMap<>(); - map.put(EXAMPLE_URL, - "82,1,true,0,5,0,-1,0,-1,8,15,0,-1,23,1,0,-1,0,-1," - + "false,false,https://www.example.com/"); - map.put(HTTP_URL, - "81,1,true,0,4,0,-1,0,-1,7,15,0,-1,22,1,0,-1,0,-1," - + "false,false,http://www.example.com/"); - map.put(URL_1, - "78,1,true,0,5,0,-1,0,-1,8,11,0,-1,19,1,0,-1,0,-1," - + "false,false,https://www.one.com/"); - map.put(URL_1_NUMERAL, - "75,1,true,0,5,0,-1,0,-1,8,9,0,-1,17,1,0,-1,0,-1," - + "false,false,https://www.1.com/"); - map.put(URL_1_WITH_PATH, - "93,1,true,0,5,0,-1,0,-1,8,11,0,-1,19,15,0,-1,0,-1," - + "false,false,https://www.one.com/some_path.html"); - map.put(URL_2, - "78,1,true,0,5,0,-1,0,-1,8,11,0,-1,19,1,0,-1,0,-1," - + "false,false,https://www.two.com/"); - map.put(URL_3, - "80,1,true,0,5,0,-1,0,-1,8,13,0,-1,21,1,0,-1,0,-1,false,false,https://www.three.com/"); - map.put(RED_1, - "83,1,true,0,5,0,-1,0,-1,8,11,0,-1,19,6,0,-1,0,-1," - + "false,false,https://www.red.com/page1"); - map.put(RED_2, - "83,1,true,0,5,0,-1,0,-1,8,11,0,-1,19,6,0,-1,0,-1," - + "false,false,https://www.red.com/page2"); - map.put(RED_3, - "83,1,true,0,5,0,-1,0,-1,8,11,0,-1,19,6,0,-1,0,-1," - + "false,false,https://www.red.com/page3"); - map.put(BLUE_1, - "84,1,true,0,5,0,-1,0,-1,8,12,0,-1,20,6,0,-1,0,-1," - + "false,false,https://www.blue.com/page1"); - map.put(BLUE_2, - "84,1,true,0,5,0,-1,0,-1,8,12,0,-1,20,6,0,-1,0,-1," - + "false,false,https://www.blue.com/page2"); - map.put(BLUE_3, - "84,1,true,0,5,0,-1,0,-1,8,12,0,-1,20,6,0,-1,0,-1," - + "false,false,https://www.blue.com/page3"); - map.put(SEARCH_URL, - "94,1,true,0,5,0,-1,0,-1,8,14,0,-1,22,7,30,6,0,-1," - + "false,false,https://www.google.com/search?q=test"); - map.put(SEARCH_2_URL, - "95,1,true,0,5,0,-1,0,-1,8,14,0,-1,22,7,30,7,0,-1," - + "false,false,https://www.google.com/search?q=query"); - map.put(INITIAL_URL, - "78,1,true,0,5,0,-1,0,-1,8,11,0,-1,19,1,0,-1,0,-1," - + "false,false,https://initial.com/"); - map.put(SPECULATED_URL, - "81,1,true,0,5,0,-1,0,-1,8,14,0,-1,22,1,0,-1,0,-1," - + "false,false,https://speculated.com/"); - map.put(NTP_URL, - "73,1,true,0,6,0,-1,0,-1,9,6,0,-1,15,1,0,-1,0,-1," - + "false,false,chrome://newtab/"); - map.put(NTP_NATIVE_URL, - "82,1,true,0,13,0,-1,0,-1,16,6,0,-1,22,1,0,-1,0,-1,false,false," - + "chrome-native://newtab/"); - map.put(DOM_DISILLER_URL, - "82,1,true,0,16,0,-1,0,-1,19,3,0,-1,22,1,0,-1,0,-1,false,false," - + "chrome-distiller://url/"); - map.put(MAPS_URL, - "82,1,true,0,5,0,-1,0,-1,8,15,0,-1,23,1,0,-1,0,-1,false,false,https://maps.google.com/"); - map.put(AMP_URL, - "116,1,true,0,5,0,-1,0,-1,8,14,0,-1,22,35,0,-1,0,-1,false,false,https://www.google.com/amp/www.nyt.com/ampthml/blogs.html"); - map.put(AMP_CACHE_URL, - "118,1,true,0,5,0,-1,0,-1,8,14,0,-1,22,37,0,-1,0,-1,false,false,https://www.google.com/amp/s/www.nyt.com/ampthml/blogs.html"); - map.put(TEXT_FRAGMENT_URL, - "100,1,true,0,5,0,-1,0,-1,8,15,0,-1,23,1,0,-1,25,16,false,false,https://www.example.com/#:~:text=selector"); - map.put(MULTI_TEXT_FRAGMENT_URL, - "131,1,true,0,5,0,-1,0,-1,8,15,0,-1,23,1,0,-1,25,47,false,false,https://www.example.com/#:~:text=selector1&text=selector2&text=selector3"); - map.put(INVALID_URL, - "73,1,false,0,4,0,-1,0,-1,7,7,0,-1,14,1,0,-1,0,-1,false,false,http://0x100.0/"); - map.put(GOOGLE_URL, - "80,1,true,0,4,0,-1,0,-1,7,14,0,-1,21,1,0,-1,0,-1,false,false,http://www.google.com/"); - map.put(GOOGLE_URL_DOGS, - "84,1,true,0,4,0,-1,0,-1,7,14,0,-1,21,5,0,-1,0,-1,false,false,http://www.google.com/dogs"); - map.put(GOOGLE_URL_DOGS_FUN, - "93,1,true,0,4,0,-1,0,-1,7,14,0,-1,21,13,0,-1,0,-1,false,false,http://www.google.com/dogs-are-fun"); - map.put(GOOGLE_URL_DOG, - "83,1,true,0,4,0,-1,0,-1,7,14,0,-1,21,4,0,-1,0,-1,false,false,http://www.google.com/dog"); - map.put(GOOGLE_URL_CAT, - "83,1,true,0,4,0,-1,0,-1,7,14,0,-1,21,4,0,-1,0,-1,false,false,http://www.google.com/cat"); - map.put(GOOGLE_URL_PIG, - "83,1,true,0,4,0,-1,0,-1,7,14,0,-1,21,4,0,-1,0,-1,false,false,http://www.google.com/pig"); - map.put(ABOUT_BLANK, - "68,1,true,0,5,0,-1,0,-1,0,-1,0,-1,6,5,0,-1,0,-1,false,false,about:blank"); - map.put(CHROME_ABOUT, - "72,1,true,0,6,0,-1,0,-1,9,5,0,-1,14,1,0,-1,0,-1,false,false,chrome://about/"); - sGURLMap = Collections.unmodifiableMap(map); - } - - /** - * @return the GURL resulting from parsing the provided url. Must be registered in |sGURLMap|. - */ - public static GURL getGURL(String url) { - String serialized = sGURLMap.get(url); - if (serialized == null) { - throw new IllegalArgumentException("URL " + url + " not found"); - } - serialized = serialized.replace(',', GURL.SERIALIZER_DELIMITER); - GURL gurl = GURL.deserialize(serialized); - // If you're here looking to use an empty GURL, just use GURL.emptyGURL() directly. - if (gurl.isEmpty()) { - throw new RuntimeException("Could not deserialize: " + serialized); - } - return gurl; - } + public static final GURL EXAMPLE_URL = new GURL("https://www.example.com/"); + public static final GURL HTTP_URL = new GURL("http://www.example.com/"); + public static final GURL URL_1 = new GURL("https://www.one.com/"); + public static final GURL URL_1_WITH_PATH = new GURL("https://www.one.com/some_path.html"); + public static final GURL URL_2 = new GURL("https://www.two.com/"); + public static final GURL URL_3 = new GURL("https://www.three.com/"); + public static final GURL MAPS_URL = new GURL("https://maps.google.com/"); + public static final GURL SEARCH_URL = new GURL("https://www.google.com/search?q=test"); + public static final GURL SEARCH_2_URL = new GURL("https://www.google.com/search?q=query"); + public static final GURL INITIAL_URL = new GURL("https://initial.com"); + public static final GURL NTP_URL = new GURL("chrome://newtab/"); + public static final GURL NTP_NATIVE_URL = new GURL("chrome-native://newtab/"); + public static final GURL RED_1 = new GURL("https://www.red.com/page1"); + public static final GURL RED_2 = new GURL("https://www.red.com/page2"); + public static final GURL RED_3 = new GURL("https://www.red.com/page3"); + public static final GURL BLUE_1 = new GURL("https://www.blue.com/page1"); + public static final GURL BLUE_2 = new GURL("https://www.blue.com/page2"); + public static final GURL BLUE_3 = new GURL("https://www.blue.com/page3"); + public static final GURL TEXT_FRAGMENT_URL = + new GURL("https://www.example.com/#:~:text=selector"); + public static final GURL INVALID_URL = new GURL("http://0x100.0/"); + public static final GURL GOOGLE_URL = new GURL("http://www.google.com/"); + public static final GURL GOOGLE_URL_DOGS = new GURL("http://www.google.com/dogs"); + public static final GURL GOOGLE_URL_DOG = new GURL("http://www.google.com/dog"); + public static final GURL GOOGLE_URL_CAT = new GURL("http://www.google.com/cat"); + public static final GURL ABOUT_BLANK = new GURL("about:blank"); + public static final GURL CHROME_ABOUT = new GURL("chrome://about"); } diff --git a/url/android/test/java/src/org/chromium/url/ShadowGURL.java b/url/android/test/java/src/org/chromium/url/ShadowGURL.java deleted file mode 100644 index 53e1da192..000000000 --- a/url/android/test/java/src/org/chromium/url/ShadowGURL.java +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.url; - -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; - -import org.chromium.url.GURL.Natives; - -/** - * Shadow of {@link GURL}. Lets Robolectric tests use {@code GURL} without the native libraries - * loaded. - * - * <p>This shadow can create only GURLs listed in {@link JUnitTestGURLs}. - */ -@Implements(GURL.class) -public class ShadowGURL { - /** - * The {@link GURL.Natives} implementation used by a shadowed {@link GURL}. - */ - private static class NativesImpl implements GURL.Natives { - @Override - public void init(String url, GURL target) { - target.initForTesting(JUnitTestGURLs.getGURL(url)); - } - - @Override - public void getOrigin(String spec, boolean isValid, long nativeParsed, GURL target) { - throw new UnsupportedOperationException( - "ShadowGURL.NativesImpl#getOrigin is not implemented"); - } - - @Override - public boolean domainIs(String spec, boolean isValid, long nativeParsed, String domain) { - throw new UnsupportedOperationException( - "ShadowGURL.NativesImpl#domainIs is not implemented"); - } - - @Override - public long createNative(String spec, boolean isValid, long nativeParsed) { - throw new UnsupportedOperationException( - "ShadowGURL.NativesImpl#createNative is not implemented"); - } - } - private static final NativesImpl sNativesInstance = new NativesImpl(); - - /** - * We could instead shadow {@code GURLJni#get}, but that would require tests using this to load - * both shadows. - */ - @Implementation - protected static Natives getNatives() { - return sNativesInstance; - } - - @Implementation - protected static void ensureNativeInitializedForGURL() { - // Skip native initialization. - } -} |