summaryrefslogtreecommitdiff
path: root/chromium/url
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/url')
-rw-r--r--chromium/url/BUILD.gn83
-rw-r--r--chromium/url/android/gurl_android.cc102
-rw-r--r--chromium/url/android/gurl_android.h27
-rw-r--r--chromium/url/android/gurl_android_unittest.cc78
-rw-r--r--chromium/url/android/java/src/org/chromium/url/GURL.java334
-rw-r--r--chromium/url/android/java/src/org/chromium/url/Parsed.java141
-rw-r--r--chromium/url/android/java/src/org/chromium/url/URI.java61
-rw-r--r--chromium/url/android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java253
-rw-r--r--chromium/url/android/parsed_android.cc96
-rw-r--r--chromium/url/android/parsed_android.h22
-rw-r--r--chromium/url/gurl_unittest.cc47
-rw-r--r--chromium/url/ipc/BUILD.gn8
-rw-r--r--chromium/url/mojom/BUILD.gn94
-rw-r--r--chromium/url/mojom/OWNERS2
-rw-r--r--chromium/url/mojom/gurl.typemap11
-rw-r--r--chromium/url/mojom/origin.typemap11
-rw-r--r--chromium/url/mojom/origin_mojom_traits.cc34
-rw-r--r--chromium/url/mojom/origin_mojom_traits.h29
-rw-r--r--chromium/url/mojom/typemaps.gni8
-rw-r--r--chromium/url/mojom/url_gurl_mojom_traits.cc40
-rw-r--r--chromium/url/mojom/url_gurl_mojom_traits.h33
-rw-r--r--chromium/url/origin.cc113
-rw-r--r--chromium/url/origin.h37
-rw-r--r--chromium/url/origin_unittest.cc109
-rw-r--r--chromium/url/scheme_host_port.cc18
-rw-r--r--chromium/url/scheme_host_port.h2
-rw-r--r--chromium/url/scheme_host_port_unittest.cc19
-rw-r--r--chromium/url/url_canon_relative.cc51
-rw-r--r--chromium/url/url_util.cc184
-rw-r--r--chromium/url/url_util.h27
-rw-r--r--chromium/url/url_util_unittest.cc20
31 files changed, 1842 insertions, 252 deletions
diff --git a/chromium/url/BUILD.gn b/chromium/url/BUILD.gn
index 71cc92c41cf..197d31ab0fb 100644
--- a/chromium/url/BUILD.gn
+++ b/chromium/url/BUILD.gn
@@ -8,6 +8,7 @@ import("//testing/test.gni")
import("features.gni")
if (is_android) {
+ import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")
}
@@ -97,12 +98,25 @@ component("url") {
}
if (is_android) {
- static_library("origin_android") {
+ source_set("gurl_android") {
sources = [
- "android/origin_android.cc",
+ "android/gurl_android.cc",
+ "android/gurl_android.h",
+ "android/parsed_android.cc",
+ "android/parsed_android.h",
]
deps = [
+ ":gurl_jni_headers",
+ ":url",
+ "//base:base",
+ ]
+ }
+
+ static_library("origin_android") {
+ sources = [ "android/origin_android.cc" ]
+
+ deps = [
":url",
":url_jni_headers",
"//base",
@@ -111,14 +125,25 @@ if (is_android) {
}
android_library("url_java") {
- java_files = [ "android/java/src/org/chromium/url/IDNStringUtil.java" ]
+ sources = [ "android/java/src/org/chromium/url/IDNStringUtil.java" ]
+ deps = [ "//base:base_java" ]
+ }
+
+ android_library("gurl_java") {
+ sources = [
+ "android/java/src/org/chromium/url/GURL.java",
+ "android/java/src/org/chromium/url/Parsed.java",
+ "android/java/src/org/chromium/url/URI.java",
+ ]
deps = [
"//base:base_java",
+ "//base:jni_java",
]
+ annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
}
android_library("origin_java") {
- java_files = [ "android/java/src/org/chromium/url/Origin.java" ]
+ sources = [ "android/java/src/org/chromium/url/Origin.java" ]
deps = [
"//base:base_java",
"//url/mojom:url_mojom_origin_java",
@@ -131,6 +156,13 @@ if (is_android) {
"android/java/src/org/chromium/url/Origin.java",
]
}
+
+ generate_jni("gurl_jni_headers") {
+ sources = [
+ "android/java/src/org/chromium/url/GURL.java",
+ "android/java/src/org/chromium/url/Parsed.java",
+ ]
+ }
}
test("url_unittests") {
@@ -187,6 +219,15 @@ test("url_unittests") {
"//url/mojom:test_url_mojom_gurl",
]
}
+ if (is_android) {
+ sources += [ "android/gurl_android_unittest.cc" ]
+ deps += [
+ ":gurl_android",
+ ":gurl_java",
+ ":gurl_javatests",
+ ":native_j_unittests_jni_headers",
+ ]
+ }
}
test("url_perftests") {
@@ -204,9 +245,7 @@ test("url_perftests") {
}
fuzzer_test("gurl_fuzzer") {
- sources = [
- "gurl_fuzzer.cc",
- ]
+ sources = [ "gurl_fuzzer.cc" ]
deps = [
":url",
"//base",
@@ -214,3 +253,33 @@ fuzzer_test("gurl_fuzzer") {
]
dict = "gurl_fuzzer.dict"
}
+
+if (is_android) {
+ android_library("gurl_javatests") {
+ testonly = true
+ sources = [
+ "android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java",
+ ]
+ deps = [
+ ":gurl_java",
+ ":gurl_jni_headers",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//base:jni_java",
+ "//third_party/android_support_test_runner:rules_java",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/junit",
+ "//third_party/mockito:mockito_java",
+ ]
+ annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+ }
+
+ # See https://bugs.chromium.org/p/chromium/issues/detail?id=908819 for why we
+ # can't put 'java' in the name here.
+ generate_jni("native_j_unittests_jni_headers") {
+ testonly = true
+ sources = [
+ "android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java",
+ ]
+ }
+}
diff --git a/chromium/url/android/gurl_android.cc b/chromium/url/android/gurl_android.cc
new file mode 100644
index 00000000000..00cfe10207c
--- /dev/null
+++ b/chromium/url/android/gurl_android.cc
@@ -0,0 +1,102 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "url/android/gurl_android.h"
+
+#include <jni.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/ptr_util.h"
+#include "url/android/parsed_android.h"
+#include "url/gurl_jni_headers/GURL_jni.h"
+#include "url/third_party/mozilla/url_parse.h"
+
+using base::android::AttachCurrentThread;
+using base::android::JavaParamRef;
+using base::android::JavaRef;
+using base::android::ScopedJavaLocalRef;
+
+namespace url {
+
+namespace {
+
+static GURL FromJString(JNIEnv* env, const JavaRef<jstring>& uri) {
+ if (!uri)
+ return GURL();
+ return GURL(base::android::ConvertJavaStringToUTF16(env, uri));
+}
+
+static std::unique_ptr<GURL> FromJavaGURL(JNIEnv* env,
+ const JavaRef<jstring>& j_spec,
+ bool is_valid,
+ jlong parsed_ptr) {
+ Parsed* parsed = reinterpret_cast<Parsed*>(parsed_ptr);
+ const std::string& spec = ConvertJavaStringToUTF8(env, j_spec);
+ std::unique_ptr<GURL> gurl =
+ std::make_unique<GURL>(spec.data(), parsed->Length(), *parsed, is_valid);
+ delete parsed;
+ return gurl;
+}
+
+static void InitFromGURL(JNIEnv* env,
+ const GURL& gurl,
+ const JavaRef<jobject>& target) {
+ Java_GURL_init(
+ env, target,
+ base::android::ConvertUTF8ToJavaString(env, gurl.possibly_invalid_spec()),
+ gurl.is_valid(),
+ ParsedAndroid::InitFromParsed(env,
+ gurl.parsed_for_possibly_invalid_spec()));
+}
+
+} // namespace
+
+// static
+std::unique_ptr<GURL> GURLAndroid::ToNativeGURL(
+ JNIEnv* env,
+ const base::android::JavaRef<jobject>& j_gurl) {
+ return base::WrapUnique<GURL>(
+ reinterpret_cast<GURL*>(Java_GURL_toNativeGURL(env, j_gurl)));
+}
+
+// static
+ScopedJavaLocalRef<jobject> GURLAndroid::FromNativeGURL(JNIEnv* env,
+ const GURL& gurl) {
+ ScopedJavaLocalRef<jobject> j_gurl = Java_GURL_Constructor(env);
+ InitFromGURL(env, gurl, j_gurl);
+ return j_gurl;
+}
+
+static void JNI_GURL_GetOrigin(JNIEnv* env,
+ const JavaParamRef<jstring>& j_spec,
+ jboolean is_valid,
+ jlong parsed_ptr,
+ const JavaParamRef<jobject>& target) {
+ std::unique_ptr<GURL> gurl = FromJavaGURL(env, j_spec, is_valid, parsed_ptr);
+ InitFromGURL(env, gurl->GetOrigin(), target);
+}
+
+static void JNI_GURL_Init(JNIEnv* env,
+ const base::android::JavaParamRef<jstring>& uri,
+ const base::android::JavaParamRef<jobject>& target) {
+ const GURL& gurl = FromJString(env, uri);
+ InitFromGURL(env, gurl, target);
+}
+
+static jlong JNI_GURL_CreateNative(JNIEnv* env,
+ const JavaParamRef<jstring>& j_spec,
+ jboolean is_valid,
+ jlong parsed_ptr) {
+ return reinterpret_cast<intptr_t>(
+ FromJavaGURL(env, j_spec, is_valid, parsed_ptr).release());
+}
+
+} // namespace url
diff --git a/chromium/url/android/gurl_android.h b/chromium/url/android/gurl_android.h
new file mode 100644
index 00000000000..733ba0644e6
--- /dev/null
+++ b/chromium/url/android/gurl_android.h
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef URL_ANDROID_GURL_ANDROID_H_
+#define URL_ANDROID_GURL_ANDROID_H_
+
+#include <memory>
+
+#include "base/android/scoped_java_ref.h"
+#include "url/gurl.h"
+
+namespace url {
+
+class GURLAndroid {
+ public:
+ static std::unique_ptr<GURL> ToNativeGURL(
+ JNIEnv* env,
+ const base::android::JavaRef<jobject>& j_gurl);
+ static base::android::ScopedJavaLocalRef<jobject> FromNativeGURL(
+ JNIEnv* env,
+ const GURL& gurl);
+};
+
+} // namespace url
+
+#endif // URL_ANDROID_GURL_ANDROID_H_
diff --git a/chromium/url/android/gurl_android_unittest.cc b/chromium/url/android/gurl_android_unittest.cc
new file mode 100644
index 00000000000..fe2b7088e4e
--- /dev/null
+++ b/chromium/url/android/gurl_android_unittest.cc
@@ -0,0 +1,78 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/android/gurl_android.h"
+#include "url/gurl.h"
+#include "url/native_j_unittests_jni_headers/GURLJavaTest_jni.h"
+
+using base::android::AttachCurrentThread;
+
+namespace url {
+
+class GURLAndroidTest : public ::testing::Test {
+ public:
+ GURLAndroidTest()
+ : j_test_(Java_GURLJavaTest_Constructor(AttachCurrentThread())) {}
+
+ const base::android::ScopedJavaGlobalRef<jobject>& j_test() {
+ return j_test_;
+ }
+
+ private:
+ base::android::ScopedJavaGlobalRef<jobject> j_test_;
+};
+
+TEST_F(GURLAndroidTest, TestGURLEquivalence) {
+ const char* cases[] = {
+ // Common Standard URLs.
+ "https://www.google.com",
+ "https://www.google.com/",
+ "https://www.google.com/maps.htm",
+ "https://www.google.com/maps/",
+ "https://www.google.com/index.html",
+ "https://www.google.com/index.html?q=maps",
+ "https://www.google.com/index.html#maps/",
+ "https://foo:bar@www.google.com/maps.htm",
+ "https://www.google.com/maps/au/index.html",
+ "https://www.google.com/maps/au/north",
+ "https://www.google.com/maps/au/north/",
+ "https://www.google.com/maps/au/index.html?q=maps#fragment/",
+ "http://www.google.com:8000/maps/au/index.html?q=maps#fragment/",
+ "https://www.google.com/maps/au/north/?q=maps#fragment",
+ "https://www.google.com/maps/au/north?q=maps#fragment",
+ // Less common standard URLs.
+ "filesystem:http://www.google.com/temporary/bar.html?baz=22",
+ "file:///temporary/bar.html?baz=22",
+ "ftp://foo/test/index.html",
+ "gopher://foo/test/index.html",
+ "ws://foo/test/index.html",
+ // Non-standard,
+ "chrome://foo/bar.html",
+ "httpa://foo/test/index.html",
+ "blob:https://foo.bar/test/index.html",
+ "about:blank",
+ "data:foobar",
+ "scheme:opaque_data",
+ // Invalid URLs.
+ "foobar",
+ };
+ JNIEnv* env = AttachCurrentThread();
+ for (const char* uri : cases) {
+ GURL gurl(uri);
+ base::android::ScopedJavaLocalRef<jobject> j_gurl =
+ Java_GURLJavaTest_createGURL(
+ env, j_test(), base::android::ConvertUTF8ToJavaString(env, uri));
+ std::unique_ptr<GURL> gurl2 = GURLAndroid::ToNativeGURL(env, j_gurl);
+ EXPECT_EQ(gurl, *gurl2);
+ }
+}
+
+JAVA_TESTS(GURLAndroidTest, j_test())
+
+} // namespace url
diff --git a/chromium/url/android/java/src/org/chromium/url/GURL.java b/chromium/url/android/java/src/org/chromium/url/GURL.java
new file mode 100644
index 00000000000..5042bbddc92
--- /dev/null
+++ b/chromium/url/android/java/src/org/chromium/url/GURL.java
@@ -0,0 +1,334 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// 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 android.os.SystemClock;
+import android.text.TextUtils;
+
+import androidx.annotation.Nullable;
+
+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.MainDex;
+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;
+import org.chromium.base.task.TaskTraits;
+
+import java.util.Random;
+
+/**
+ * A Java wrapper for GURL, Chromium's URL parsing library.
+ *
+ * This class is safe to use during startup, but will block on the native library being sufficiently
+ * loaded to use native GURL (and will not wait for content initialization). In practice it's very
+ * unlikely that this will actually block startup unless used extremely early, in which case you
+ * should probably seek an alternative solution to using GURL.
+ *
+ * The design of this class avoids destruction/finalization by caching all values necessary to
+ * reconstruct a GURL in Java, allowing it to be much faster in the common case and easier to use.
+ */
+@JNINamespace("url")
+@MainDex
+public class GURL {
+ private static final String TAG = "GURL";
+ /* package */ static final int SERIALIZER_VERSION = 1;
+ /* package */ static final char SERIALIZER_DELIMITER = '\0';
+
+ @FunctionalInterface
+ public interface ReportDebugThrowableCallback {
+ void run(Throwable throwable);
+ }
+
+ // Right now this is only collecting reports on Canary which has a relatively small population.
+ private static final int DEBUG_REPORT_PERCENTAGE = 10;
+ private static ReportDebugThrowableCallback sReportCallback;
+
+ // TODO(https://crbug.com/1039841): Right now we return a new String with each request for a
+ // GURL component other than the spec itself. Should we cache return Strings (as
+ // WeakReference?) so that callers can share String memory?
+ private String mSpec;
+ private boolean mIsValid;
+ private Parsed mParsed;
+
+ private static class Holder { private static GURL sEmptyGURL = new GURL(""); }
+
+ public static GURL emptyGURL() {
+ return Holder.sEmptyGURL;
+ }
+
+ /**
+ * Create a new GURL.
+ *
+ * @param uri The string URI representation to parse into a GURL.
+ */
+ public GURL(String uri) {
+ // Avoid a jni hop (and initializing the native library) for empty GURLs.
+ if (TextUtils.isEmpty(uri)) {
+ mSpec = "";
+ mParsed = Parsed.createEmpty();
+ return;
+ }
+ ensureNativeInitializedForGURL();
+ GURLJni.get().init(uri, this);
+ }
+
+ @CalledByNative
+ protected GURL() {}
+
+ /**
+ * Enables debug stack trace gathering for GURL.
+ *
+ * TODO(https://crbug.com/783819): Remove this when the the fraction of users hitting this
+ * drops.
+ */
+ public static void setReportDebugThrowableCallback(ReportDebugThrowableCallback callback) {
+ sReportCallback = callback;
+ }
+
+ /**
+ * Ensures that the native library is sufficiently loaded for GURL usage.
+ *
+ * This function is public so that GURL-related usage like the UrlFormatter also counts towards
+ * the "Startup.Android.GURLEnsureMainDexInitialized" histogram.
+ */
+ public static void ensureNativeInitializedForGURL() {
+ if (LibraryLoader.getInstance().isInitialized()) return;
+ long time = SystemClock.elapsedRealtime();
+ LibraryLoader.getInstance().ensureMainDexInitialized();
+ // Record metrics only for the UI thread where the delay in loading the library is relevant.
+ if (ThreadUtils.runningOnUiThread()) {
+ RecordHistogram.recordTimesHistogram("Startup.Android.GURLEnsureMainDexInitialized",
+ SystemClock.elapsedRealtime() - time);
+ if (sReportCallback != null && new Random().nextInt(100) < DEBUG_REPORT_PERCENTAGE) {
+ final Throwable throwable = new Throwable("This is not a crash, please ignore.");
+ // This isn't an assert, because by design this is possible, but we would prefer
+ // this path does not get hit more than necessary and getting stack traces from the
+ // wild will help find issues.
+ PostTask.postTask(TaskTraits.BEST_EFFORT_MAY_BLOCK,
+ () -> { sReportCallback.run(throwable); });
+ }
+ }
+ }
+
+ @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;
+ }
+
+ @CalledByNative
+ private long toNativeGURL() {
+ return GURLJni.get().createNative(mSpec, mIsValid, mParsed.toNativeParsed());
+ }
+
+ /**
+ * See native GURL::is_valid().
+ */
+ public boolean isValid() {
+ return mIsValid;
+ }
+
+ /**
+ * See native GURL::spec().
+ */
+ public String getSpec() {
+ if (isValid() || mSpec.isEmpty()) return mSpec;
+ assert false : "Trying to get the spec of an invalid URL!";
+ return "";
+ }
+
+ /**
+ * @return Either a valid Spec (see {@link #getSpec}), or an empty string.
+ */
+ public String getValidSpecOrEmpty() {
+ if (isValid()) return mSpec;
+ return "";
+ }
+
+ /**
+ * See native GURL::possibly_invalid_spec().
+ */
+ public String getPossiblyInvalidSpec() {
+ return mSpec;
+ }
+
+ private String getComponent(int begin, int length) {
+ if (length <= 0) return "";
+ return mSpec.substring(begin, begin + length);
+ }
+
+ /**
+ * See native GURL::scheme().
+ */
+ public String getScheme() {
+ return getComponent(mParsed.mSchemeBegin, mParsed.mSchemeLength);
+ }
+
+ /**
+ * See native GURL::username().
+ */
+ public String getUsername() {
+ return getComponent(mParsed.mUsernameBegin, mParsed.mUsernameLength);
+ }
+
+ /**
+ * See native GURL::password().
+ */
+ public String getPassword() {
+ return getComponent(mParsed.mPasswordBegin, mParsed.mPasswordLength);
+ }
+
+ /**
+ * See native GURL::host().
+ */
+ public String getHost() {
+ return getComponent(mParsed.mHostBegin, mParsed.mHostLength);
+ }
+
+ /**
+ * See native GURL::port().
+ *
+ * Note: Do not convert this to an integer yourself. See native GURL::IntPort().
+ */
+ public String getPort() {
+ return getComponent(mParsed.mPortBegin, mParsed.mPortLength);
+ }
+
+ /**
+ * See native GURL::path().
+ */
+ public String getPath() {
+ return getComponent(mParsed.mPathBegin, mParsed.mPathLength);
+ }
+
+ /**
+ * See native GURL::query().
+ */
+ public String getQuery() {
+ return getComponent(mParsed.mQueryBegin, mParsed.mQueryLength);
+ }
+
+ /**
+ * See native GURL::ref().
+ */
+ public String getRef() {
+ return getComponent(mParsed.mRefBegin, mParsed.mRefLength);
+ }
+
+ /**
+ * @return Whether the GURL is the empty String.
+ */
+ public boolean isEmpty() {
+ return mSpec.isEmpty();
+ }
+
+ /**
+ * See native GURL::GetOrigin().
+ */
+ public GURL getOrigin() {
+ GURL target = new GURL();
+ getOriginInternal(target);
+ return target;
+ }
+
+ protected void getOriginInternal(GURL target) {
+ GURLJni.get().getOrigin(mSpec, mIsValid, mParsed.toNativeParsed(), target);
+ }
+
+ @Override
+ public final int hashCode() {
+ return mSpec.hashCode();
+ }
+
+ @Override
+ public final boolean equals(Object other) {
+ if (other == this) return true;
+ if (!(other instanceof GURL)) return false;
+ return mSpec.equals(((GURL) other).mSpec);
+ }
+
+ /**
+ * Serialize a GURL to a String, to be used with {@link GURL#deserialize(String)}.
+ *
+ * Note that a serialized GURL should only be used internally to Chrome, and should *never* be
+ * used if coming from an untrusted source.
+ *
+ * @return A serialzed GURL.
+ */
+ public final String serialize() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(SERIALIZER_VERSION).append(SERIALIZER_DELIMITER);
+ builder.append(mIsValid).append(SERIALIZER_DELIMITER);
+ builder.append(mParsed.serialize()).append(SERIALIZER_DELIMITER);
+ builder.append(mSpec);
+ String serialization = builder.toString();
+ return Integer.toString(serialization.length()) + SERIALIZER_DELIMITER + serialization;
+ }
+
+ /**
+ * Deserialize a GURL serialized with {@link GURL#serialize()}.
+ *
+ * This function should *never* be used on a String coming from an untrusted source.
+ *
+ * @return The deserialized GURL (or null if the input is empty).
+ */
+ public static GURL deserialize(@Nullable String gurl) {
+ try {
+ if (TextUtils.isEmpty(gurl)) return emptyGURL();
+ String[] tokens = gurl.split(Character.toString(SERIALIZER_DELIMITER));
+
+ // First token MUST always be the length of the serialized data.
+ String length = tokens[0];
+ if (gurl.length() != Integer.parseInt(length) + length.length() + 1) {
+ throw new IllegalArgumentException("Serialized GURL had the wrong length.");
+ }
+
+ // Last token MUST always be the original spec - just re-parse the GURL on version
+ // changes.
+ String spec = tokens[tokens.length - 1];
+ // Special case for empty spec - it won't get its own token.
+ if (gurl.endsWith(Character.toString(SERIALIZER_DELIMITER))) spec = "";
+
+ // Second token MUST always be the version number.
+ int version = Integer.parseInt(tokens[1]);
+ if (version != SERIALIZER_VERSION) return new GURL(spec);
+
+ boolean isValid = Boolean.parseBoolean(tokens[2]);
+ Parsed parsed = Parsed.deserialize(tokens, 3);
+ GURL result = new GURL();
+ result.init(spec, isValid, parsed);
+ return result;
+ } catch (Exception e) {
+ // This is unexpected, maybe the storage got corrupted somehow?
+ Log.w(TAG, "Exception while deserializing a GURL: " + gurl, e);
+ return emptyGURL();
+ }
+ }
+
+ @NativeMethods
+ interface Natives {
+ /**
+ * Initializes the provided |target| by parsing the provided |uri|.
+ */
+ void init(String uri, GURL target);
+
+ /**
+ * Reconstructs the native GURL for this Java GURL and initializes |target| with its Origin.
+ */
+ void getOrigin(String spec, boolean isValid, long nativeParsed, GURL target);
+
+ /**
+ * Reconstructs the native GURL for this Java GURL, returning its native pointer.
+ */
+ long createNative(String spec, boolean isValid, long nativeParsed);
+ }
+}
diff --git a/chromium/url/android/java/src/org/chromium/url/Parsed.java b/chromium/url/android/java/src/org/chromium/url/Parsed.java
new file mode 100644
index 00000000000..f78f8bdf6ef
--- /dev/null
+++ b/chromium/url/android/java/src/org/chromium/url/Parsed.java
@@ -0,0 +1,141 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// 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.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+import org.chromium.base.annotations.NativeMethods;
+
+/**
+ * A java wrapper for Parsed, GURL's internal parsed URI representation.
+ */
+@MainDex
+@JNINamespace("url")
+/* package */ class Parsed {
+ /* package */ final int mSchemeBegin;
+ /* package */ final int mSchemeLength;
+ /* package */ final int mUsernameBegin;
+ /* package */ final int mUsernameLength;
+ /* package */ final int mPasswordBegin;
+ /* package */ final int mPasswordLength;
+ /* package */ final int mHostBegin;
+ /* package */ final int mHostLength;
+ /* package */ final int mPortBegin;
+ /* package */ final int mPortLength;
+ /* package */ final int mPathBegin;
+ /* package */ final int mPathLength;
+ /* package */ final int mQueryBegin;
+ /* package */ final int mQueryLength;
+ /* package */ final int mRefBegin;
+ /* package */ final int mRefLength;
+ private final Parsed mInnerUrl;
+ private final boolean mPotentiallyDanglingMarkup;
+
+ /* packaged */ static Parsed createEmpty() {
+ return new Parsed(0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, false, null);
+ }
+
+ @CalledByNative
+ private Parsed(int schemeBegin, int schemeLength, int usernameBegin, int usernameLength,
+ int passwordBegin, int passwordLength, int hostBegin, int hostLength, int portBegin,
+ int portLength, int pathBegin, int pathLength, int queryBegin, int queryLength,
+ int refBegin, int refLength, boolean potentiallyDanglingMarkup, Parsed innerUrl) {
+ mSchemeBegin = schemeBegin;
+ mSchemeLength = schemeLength;
+ mUsernameBegin = usernameBegin;
+ mUsernameLength = usernameLength;
+ mPasswordBegin = passwordBegin;
+ mPasswordLength = passwordLength;
+ mHostBegin = hostBegin;
+ mHostLength = hostLength;
+ mPortBegin = portBegin;
+ mPortLength = portLength;
+ mPathBegin = pathBegin;
+ mPathLength = pathLength;
+ mQueryBegin = queryBegin;
+ mQueryLength = queryLength;
+ mRefBegin = refBegin;
+ mRefLength = refLength;
+ mPotentiallyDanglingMarkup = potentiallyDanglingMarkup;
+ mInnerUrl = innerUrl;
+ }
+
+ /* package */ long toNativeParsed() {
+ long inner = 0;
+ if (mInnerUrl != null) {
+ inner = mInnerUrl.toNativeParsed();
+ }
+ return ParsedJni.get().createNative(mSchemeBegin, mSchemeLength, mUsernameBegin,
+ mUsernameLength, mPasswordBegin, mPasswordLength, mHostBegin, mHostLength,
+ mPortBegin, mPortLength, mPathBegin, mPathLength, mQueryBegin, mQueryLength,
+ mRefBegin, mRefLength, mPotentiallyDanglingMarkup, inner);
+ }
+
+ /* package */ String serialize() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(mSchemeBegin).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mSchemeLength).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mUsernameBegin).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mUsernameLength).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mPasswordBegin).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mPasswordLength).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mHostBegin).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mHostLength).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mPortBegin).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mPortLength).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mPathBegin).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mPathLength).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mQueryBegin).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mQueryLength).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mRefBegin).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mRefLength).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mPotentiallyDanglingMarkup).append(GURL.SERIALIZER_DELIMITER);
+ builder.append(mInnerUrl != null);
+ if (mInnerUrl != null) {
+ builder.append(GURL.SERIALIZER_DELIMITER).append(mInnerUrl.serialize());
+ }
+ return builder.toString();
+ }
+
+ /* package */ static Parsed deserialize(String[] tokens, int startIndex) {
+ int schemeBegin = Integer.parseInt(tokens[startIndex++]);
+ int schemeLength = Integer.parseInt(tokens[startIndex++]);
+ int usernameBegin = Integer.parseInt(tokens[startIndex++]);
+ int usernameLength = Integer.parseInt(tokens[startIndex++]);
+ int passwordBegin = Integer.parseInt(tokens[startIndex++]);
+ int passwordLength = Integer.parseInt(tokens[startIndex++]);
+ int hostBegin = Integer.parseInt(tokens[startIndex++]);
+ int hostLength = Integer.parseInt(tokens[startIndex++]);
+ int portBegin = Integer.parseInt(tokens[startIndex++]);
+ int portLength = Integer.parseInt(tokens[startIndex++]);
+ int pathBegin = Integer.parseInt(tokens[startIndex++]);
+ int pathLength = Integer.parseInt(tokens[startIndex++]);
+ int queryBegin = Integer.parseInt(tokens[startIndex++]);
+ int queryLength = Integer.parseInt(tokens[startIndex++]);
+ int refBegin = Integer.parseInt(tokens[startIndex++]);
+ int refLength = Integer.parseInt(tokens[startIndex++]);
+ boolean potentiallyDanglingMarkup = Boolean.parseBoolean(tokens[startIndex++]);
+ Parsed innerParsed = null;
+ if (Boolean.parseBoolean(tokens[startIndex++])) {
+ innerParsed = Parsed.deserialize(tokens, startIndex);
+ }
+ return new Parsed(schemeBegin, schemeLength, usernameBegin, usernameLength, passwordBegin,
+ passwordLength, hostBegin, hostLength, portBegin, portLength, pathBegin, pathLength,
+ queryBegin, queryLength, refBegin, refLength, potentiallyDanglingMarkup,
+ innerParsed);
+ }
+
+ @NativeMethods
+ interface Natives {
+ /**
+ * Create and return the pointer to a native Parsed.
+ */
+ long createNative(int schemeBegin, int schemeLength, int usernameBegin, int usernameLength,
+ int passwordBegin, int passwordLength, int hostBegin, int hostLength, int portBegin,
+ int portLength, int pathBegin, int pathLength, int queryBegin, int queryLength,
+ int refBegin, int refLength, boolean potentiallyDanglingMarkup, long innerUrl);
+ }
+}
diff --git a/chromium/url/android/java/src/org/chromium/url/URI.java b/chromium/url/android/java/src/org/chromium/url/URI.java
new file mode 100644
index 00000000000..546be56223e
--- /dev/null
+++ b/chromium/url/android/java/src/org/chromium/url/URI.java
@@ -0,0 +1,61 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// 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 java.net.URISyntaxException;
+
+/**
+ * An API shim around GURL that mostly matches the java.net.URI API.
+ *
+ * @deprecated Please use GURL directly in new code.
+ */
+@Deprecated
+public class URI extends GURL {
+ /**
+ * Create a new GURL with a java.net.URI API shim.
+ */
+ public URI(String uri) throws URISyntaxException {
+ super(uri);
+ if (!isValid()) {
+ throw new URISyntaxException(uri, "Uri could not be parsed as a valid GURL");
+ }
+ }
+
+ private URI() {}
+
+ /**
+ * This function is a convenience wrapper around {@link URI#URI(String)}, that wraps the thrown
+ * thrown URISyntaxException in an IllegalArgumentException and throws that instead.
+ */
+ public static URI create(String str) {
+ try {
+ return new URI(str);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public URI getOrigin() {
+ URI target = new URI();
+ getOriginInternal(target);
+ return target;
+ }
+
+ /** See {@link GURL#getRef()} */
+ public String getFragment() {
+ return getRef();
+ }
+
+ /** See {@link java.net.URI#isAbsolute()} */
+ public boolean isAbsolute() {
+ return !getScheme().isEmpty();
+ }
+
+ @Override
+ public String toString() {
+ return getPossiblyInvalidSpec();
+ }
+}
diff --git a/chromium/url/android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java b/chromium/url/android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java
new file mode 100644
index 00000000000..0517ddf7edc
--- /dev/null
+++ b/chromium/url/android/native_java_unittests/src/org/chromium/url/GURLJavaTest.java
@@ -0,0 +1,253 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// 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.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+
+import org.junit.Assert;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.CalledByNativeJavaTest;
+
+import java.net.URISyntaxException;
+
+/**
+ * Tests for {@link GURL}. GURL relies heavily on the native implementation, and the lion's share of
+ * the logic is tested there. This test is primarily to make sure everything is plumbed through
+ * correctly.
+ */
+public class GURLJavaTest {
+ @Mock
+ GURL.Natives mGURLMocks;
+
+ @CalledByNative
+ private GURLJavaTest() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @CalledByNative
+ public GURL createGURL(String uri) {
+ return new GURL(uri);
+ }
+
+ private 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());
+ }
+
+ private String prependLengthToSerialization(String serialization) {
+ return Integer.toString(serialization.length()) + GURL.SERIALIZER_DELIMITER + serialization;
+ }
+
+ // Equivalent of GURLTest.Components
+ @CalledByNativeJavaTest
+ @SuppressWarnings(value = "AuthLeak")
+ public void testComponents() {
+ GURL empty = new GURL("");
+ Assert.assertTrue(empty.isEmpty());
+ Assert.assertFalse(empty.isValid());
+
+ GURL url = new GURL("http://user:pass@google.com:99/foo;bar?q=a#ref");
+ Assert.assertFalse(url.isEmpty());
+ Assert.assertTrue(url.isValid());
+ Assert.assertTrue(url.getScheme().equals("http"));
+
+ Assert.assertEquals("http://user:pass@google.com:99/foo;bar?q=a#ref", url.getSpec());
+
+ Assert.assertEquals("http", url.getScheme());
+ Assert.assertEquals("user", url.getUsername());
+ Assert.assertEquals("pass", url.getPassword());
+ Assert.assertEquals("google.com", url.getHost());
+ Assert.assertEquals("99", url.getPort());
+ Assert.assertEquals("/foo;bar", url.getPath());
+ Assert.assertEquals("q=a", url.getQuery());
+ Assert.assertEquals("ref", url.getRef());
+
+ // Test parsing userinfo with special characters.
+ GURL urlSpecialPass = new GURL("http://user:%40!$&'()*+,;=:@google.com:12345");
+ Assert.assertTrue(urlSpecialPass.isValid());
+ // GURL canonicalizes some delimiters.
+ Assert.assertEquals("%40!$&%27()*+,%3B%3D%3A", urlSpecialPass.getPassword());
+ Assert.assertEquals("google.com", urlSpecialPass.getHost());
+ Assert.assertEquals("12345", urlSpecialPass.getPort());
+ }
+
+ // Equivalent of GURLTest.Empty
+ @CalledByNativeJavaTest
+ public void testEmpty() {
+ GURLJni.TEST_HOOKS.setInstanceForTesting(mGURLMocks);
+ doThrow(new RuntimeException("Should not need to parse empty URL"))
+ .when(mGURLMocks)
+ .init(any(), any());
+ 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());
+ GURLJni.TEST_HOOKS.setInstanceForTesting(null);
+ }
+
+ // Test that GURL and URI return the correct Origin.
+ @CalledByNativeJavaTest
+ @SuppressWarnings(value = "AuthLeak")
+ public void testOrigin() throws URISyntaxException {
+ final String kExpectedOrigin1 = "http://google.com:21/";
+ final String kExpectedOrigin2 = "";
+ GURL url1 = new GURL("filesystem:http://user:pass@google.com:21/blah#baz");
+ GURL url2 = new GURL("javascript:window.alert(\"hello,world\");");
+ URI uri = new URI("filesystem:http://user:pass@google.com:21/blah#baz");
+
+ Assert.assertEquals(kExpectedOrigin1, url1.getOrigin().getSpec());
+ Assert.assertEquals(kExpectedOrigin2, url2.getOrigin().getSpec());
+ URI origin = uri.getOrigin();
+ Assert.assertEquals(kExpectedOrigin1, origin.getSpec());
+ }
+
+ @CalledByNativeJavaTest
+ public void testWideInput() throws URISyntaxException {
+ final String kExpectedSpec = "http://xn--1xa.com/";
+
+ GURL url = new GURL("http://\u03C0.com");
+ Assert.assertEquals("http://xn--1xa.com/", url.getSpec());
+ Assert.assertEquals("http", url.getScheme());
+ Assert.assertEquals("", url.getUsername());
+ Assert.assertEquals("", url.getPassword());
+ Assert.assertEquals("xn--1xa.com", url.getHost());
+ Assert.assertEquals("", url.getPort());
+ Assert.assertEquals("/", url.getPath());
+ Assert.assertEquals("", url.getQuery());
+ Assert.assertEquals("", url.getRef());
+ }
+
+ @CalledByNativeJavaTest
+ @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"),
+ };
+
+ GURLJni.TEST_HOOKS.setInstanceForTesting(mGURLMocks);
+ doThrow(new RuntimeException("Should not re-initialize for deserialization when the "
+ + "version hasn't changed."))
+ .when(mGURLMocks)
+ .init(any(), any());
+ for (GURL url : cases) {
+ GURL out = GURL.deserialize(url.serialize());
+ deepAssertEquals(url, out);
+ }
+ GURLJni.TEST_HOOKS.setInstanceForTesting(null);
+ }
+
+ /**
+ * Tests that we re-parse the URL from the spec, which must always be the last token in the
+ * serialization, if the serialization version differs.
+ */
+ @CalledByNativeJavaTest
+ 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();
+ serialization = prependLengthToSerialization(serialization);
+ GURL out = GURL.deserialize(serialization);
+ deepAssertEquals(url, out);
+ }
+
+ /**
+ * Tests that fields that aren't visible to java code are correctly serialized.
+ */
+ @CalledByNativeJavaTest
+ 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";
+ 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.
+ */
+ @CalledByNativeJavaTest
+ public void testTruncatedDeserialization() {
+ String serialization = "123,1,true,1,2,3,4,5,6,7,8,9,10";
+ serialization = serialization.replace(',', GURL.SERIALIZER_DELIMITER);
+ GURL url = GURL.deserialize(serialization);
+ Assert.assertEquals(url, GURL.emptyGURL());
+ }
+
+ /**
+ * Tests serialized GURL truncated by storage.
+ */
+ @CalledByNativeJavaTest
+ public void testCorruptedSerializations() {
+ String serialization = new GURL("https://www.google.ca").serialize();
+ // Replace the scheme length (5) with an extra delimiter.
+ String corruptedParsed = serialization.replace('5', GURL.SERIALIZER_DELIMITER);
+ GURL url = GURL.deserialize(corruptedParsed);
+ Assert.assertEquals(GURL.emptyGURL(), url);
+
+ String corruptedVersion =
+ serialization.replaceFirst(Integer.toString(GURL.SERIALIZER_VERSION), "x");
+ url = GURL.deserialize(corruptedVersion);
+ Assert.assertEquals(GURL.emptyGURL(), url);
+ }
+}
diff --git a/chromium/url/android/parsed_android.cc b/chromium/url/android/parsed_android.cc
new file mode 100644
index 00000000000..5fd47e73893
--- /dev/null
+++ b/chromium/url/android/parsed_android.cc
@@ -0,0 +1,96 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "url/android/parsed_android.h"
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "url/gurl_jni_headers/Parsed_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::JavaRef;
+using base::android::ScopedJavaLocalRef;
+
+namespace url {
+
+namespace {
+
+ScopedJavaLocalRef<jobject> CreateJavaParsed(JNIEnv* env,
+ const Parsed& parsed,
+ const JavaRef<jobject>& inner) {
+ static constexpr bool is_signed =
+ std::is_signed<decltype(parsed.scheme.begin)>::value;
+ static constexpr size_t offset_size = sizeof(parsed.scheme.begin);
+ static_assert((is_signed && sizeof(jint) >= offset_size) ||
+ (!is_signed && sizeof(jint) > offset_size),
+ "Java size offsets for Parsed Components must be large enough "
+ "to store the full C++ offset.");
+ return Java_Parsed_Constructor(
+ env, parsed.scheme.begin, parsed.scheme.len, parsed.username.begin,
+ parsed.username.len, parsed.password.begin, parsed.password.len,
+ parsed.host.begin, parsed.host.len, parsed.port.begin, parsed.port.len,
+ parsed.path.begin, parsed.path.len, parsed.query.begin, parsed.query.len,
+ parsed.ref.begin, parsed.ref.len, parsed.potentially_dangling_markup,
+ inner);
+}
+
+} // namespace
+
+// static
+ScopedJavaLocalRef<jobject> ParsedAndroid::InitFromParsed(
+ JNIEnv* env,
+ const Parsed& parsed) {
+ ScopedJavaLocalRef<jobject> inner;
+ if (parsed.inner_parsed())
+ inner = CreateJavaParsed(env, *parsed.inner_parsed(), nullptr);
+ return CreateJavaParsed(env, parsed, inner);
+}
+
+static jlong JNI_Parsed_CreateNative(JNIEnv* env,
+ jint scheme_begin,
+ jint scheme_length,
+ jint username_begin,
+ jint username_length,
+ jint password_begin,
+ jint password_length,
+ jint host_begin,
+ jint host_length,
+ jint port_begin,
+ jint port_length,
+ jint path_begin,
+ jint path_length,
+ jint query_begin,
+ jint query_length,
+ jint ref_begin,
+ jint ref_length,
+ jboolean potentially_dangling_markup,
+ jlong inner_parsed) {
+ Parsed* parsed = new Parsed();
+ parsed->scheme.begin = scheme_begin;
+ parsed->scheme.len = scheme_length;
+ parsed->username.begin = username_begin;
+ parsed->username.len = username_length;
+ parsed->password.begin = password_begin;
+ parsed->password.len = password_length;
+ parsed->host.begin = host_begin;
+ parsed->host.len = host_length;
+ parsed->port.begin = port_begin;
+ parsed->port.len = port_length;
+ parsed->path.begin = path_begin;
+ parsed->path.len = path_length;
+ parsed->query.begin = query_begin;
+ parsed->query.len = query_length;
+ parsed->ref.begin = ref_begin;
+ parsed->ref.len = ref_length;
+ parsed->potentially_dangling_markup = potentially_dangling_markup;
+ Parsed* inner = reinterpret_cast<Parsed*>(inner_parsed);
+ if (inner) {
+ parsed->set_inner_parsed(*inner);
+ delete inner;
+ }
+ return reinterpret_cast<intptr_t>(parsed);
+}
+
+} // namespace url
diff --git a/chromium/url/android/parsed_android.h b/chromium/url/android/parsed_android.h
new file mode 100644
index 00000000000..cb8f2ba75b9
--- /dev/null
+++ b/chromium/url/android/parsed_android.h
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef URL_ANDROID_PARSED_ANDROID_H_
+#define URL_ANDROID_PARSED_ANDROID_H_
+
+#include "base/android/scoped_java_ref.h"
+#include "url/third_party/mozilla/url_parse.h"
+
+namespace url {
+
+class ParsedAndroid {
+ public:
+ static base::android::ScopedJavaLocalRef<jobject> InitFromParsed(
+ JNIEnv* env,
+ const Parsed& parsed);
+};
+
+} // namespace url
+
+#endif // URL_ANDROID_PARSED_ANDROID_H_
diff --git a/chromium/url/gurl_unittest.cc b/chromium/url/gurl_unittest.cc
index 31767db6546..7a4e8727be9 100644
--- a/chromium/url/gurl_unittest.cc
+++ b/chromium/url/gurl_unittest.cc
@@ -289,21 +289,42 @@ TEST(GURLTest, Resolve) {
bool expected_valid;
const char* expected;
} resolve_cases[] = {
- {"http://www.google.com/", "foo.html", true, "http://www.google.com/foo.html"},
- {"http://www.google.com/foo/", "bar", true, "http://www.google.com/foo/bar"},
- {"http://www.google.com/foo/", "/bar", true, "http://www.google.com/bar"},
- {"http://www.google.com/foo", "bar", true, "http://www.google.com/bar"},
- {"http://www.google.com/", "http://images.google.com/foo.html", true, "http://images.google.com/foo.html"},
- {"http://www.google.com/", "http://images.\tgoogle.\ncom/\rfoo.html", true, "http://images.google.com/foo.html"},
- {"http://www.google.com/blah/bloo?c#d", "../../../hello/./world.html?a#b", true, "http://www.google.com/hello/world.html?a#b"},
- {"http://www.google.com/foo#bar", "#com", true, "http://www.google.com/foo#com"},
- {"http://www.google.com/", "Https:images.google.com", true, "https://images.google.com/"},
+ {"http://www.google.com/", "foo.html", true,
+ "http://www.google.com/foo.html"},
+ {"http://www.google.com/foo/", "bar", true,
+ "http://www.google.com/foo/bar"},
+ {"http://www.google.com/foo/", "/bar", true, "http://www.google.com/bar"},
+ {"http://www.google.com/foo", "bar", true, "http://www.google.com/bar"},
+ {"http://www.google.com/", "http://images.google.com/foo.html", true,
+ "http://images.google.com/foo.html"},
+ {"http://www.google.com/", "http://images.\tgoogle.\ncom/\rfoo.html",
+ true, "http://images.google.com/foo.html"},
+ {"http://www.google.com/blah/bloo?c#d", "../../../hello/./world.html?a#b",
+ true, "http://www.google.com/hello/world.html?a#b"},
+ {"http://www.google.com/foo#bar", "#com", true,
+ "http://www.google.com/foo#com"},
+ {"http://www.google.com/", "Https:images.google.com", true,
+ "https://images.google.com/"},
// A non-standard base can be replaced with a standard absolute URL.
- {"data:blahblah", "http://google.com/", true, "http://google.com/"},
- {"data:blahblah", "http:google.com", true, "http://google.com/"},
+ {"data:blahblah", "http://google.com/", true, "http://google.com/"},
+ {"data:blahblah", "http:google.com", true, "http://google.com/"},
// Filesystem URLs have different paths to test.
- {"filesystem:http://www.google.com/type/", "foo.html", true, "filesystem:http://www.google.com/type/foo.html"},
- {"filesystem:http://www.google.com/type/", "../foo.html", true, "filesystem:http://www.google.com/type/foo.html"},
+ {"filesystem:http://www.google.com/type/", "foo.html", true,
+ "filesystem:http://www.google.com/type/foo.html"},
+ {"filesystem:http://www.google.com/type/", "../foo.html", true,
+ "filesystem:http://www.google.com/type/foo.html"},
+ // https://crbug.com/530123 - scheme validation (e.g. are "10.0.0.7:"
+ // or "x1:" valid schemes) when deciding if |relative| is an absolute url.
+ {"file:///some/dir/ip-relative.html", "10.0.0.7:8080/foo.html", true,
+ "file:///some/dir/10.0.0.7:8080/foo.html"},
+ {"file:///some/dir/", "1://host", true, "file:///some/dir/1://host"},
+ {"file:///some/dir/", "x1://host", true, "x1://host"},
+ {"file:///some/dir/", "X1://host", true, "x1://host"},
+ {"file:///some/dir/", "x.://host", true, "x.://host"},
+ {"file:///some/dir/", "x+://host", true, "x+://host"},
+ {"file:///some/dir/", "x-://host", true, "x-://host"},
+ {"file:///some/dir/", "x!://host", true, "file:///some/dir/x!://host"},
+ {"file:///some/dir/", "://host", true, "file:///some/dir/://host"},
};
for (size_t i = 0; i < base::size(resolve_cases); i++) {
diff --git a/chromium/url/ipc/BUILD.gn b/chromium/url/ipc/BUILD.gn
index 52968a19f17..9b3d9dbea8a 100644
--- a/chromium/url/ipc/BUILD.gn
+++ b/chromium/url/ipc/BUILD.gn
@@ -17,17 +17,13 @@ component("url_ipc") {
"//ipc",
"//url",
]
- deps = [
- "//base",
- ]
+ deps = [ "//base" ]
}
# IPC unit tests aren't build on iOS.
if (!is_ios) {
test("url_ipc_unittests") {
- sources = [
- "url_param_traits_unittest.cc",
- ]
+ sources = [ "url_param_traits_unittest.cc" ]
deps = [
":url_ipc",
diff --git a/chromium/url/mojom/BUILD.gn b/chromium/url/mojom/BUILD.gn
index e60a2a9058c..4c9f079ee9a 100644
--- a/chromium/url/mojom/BUILD.gn
+++ b/chromium/url/mojom/BUILD.gn
@@ -6,16 +6,45 @@ import("//mojo/public/tools/bindings/mojom.gni")
mojom("url_mojom_gurl") {
generate_java = true
- sources = [
- "url.mojom",
+ sources = [ "url.mojom" ]
+
+ cpp_typemaps = [
+ {
+ types = [
+ {
+ mojom = "url.mojom.Url"
+ cpp = "::GURL"
+ },
+ ]
+ traits_headers = [ "//url/mojom/url_gurl_mojom_traits.h" ]
+ traits_public_deps = [
+ ":mojom_traits",
+ "//url",
+ ]
+ },
+ ]
+
+ blink_cpp_typemaps = [
+ {
+ types = [
+ {
+ mojom = "url.mojom.Url"
+ cpp = "::blink::KURL"
+ force_serialize = true
+ },
+ ]
+ traits_headers = [
+ "//third_party/blink/renderer/platform/mojo/kurl_mojom_traits.h",
+ "//third_party/blink/renderer/platform/weborigin/kurl_hash.h",
+ ]
+ traits_public_deps = [ "//url" ]
+ },
]
}
mojom("url_mojom_origin") {
generate_java = true
- sources = [
- "origin.mojom",
- ]
+ sources = [ "origin.mojom" ]
public_deps = [
":url_mojom_gurl",
@@ -23,15 +52,64 @@ mojom("url_mojom_origin") {
]
check_includes_blink = false
+
+ cpp_typemaps = [
+ {
+ types = [
+ {
+ mojom = "url.mojom.Origin"
+ cpp = "::url::Origin"
+ },
+ ]
+ traits_headers = [ "//url/mojom/origin_mojom_traits.h" ]
+ traits_public_deps = [
+ ":mojom_traits",
+ "//url",
+ ]
+ },
+ ]
+
+ blink_cpp_typemaps = [
+ {
+ types = [
+ {
+ mojom = "url.mojom.Origin"
+ cpp = "::scoped_refptr<const ::blink::SecurityOrigin>"
+ nullable_is_same_type = true
+ },
+ ]
+ traits_headers = [ "//third_party/blink/renderer/platform/mojo/security_origin_mojom_traits.h" ]
+ traits_public_deps = [ "//url" ]
+ },
+ ]
}
mojom("test_url_mojom_gurl") {
- sources = [
- "url_test.mojom",
- ]
+ sources = [ "url_test.mojom" ]
public_deps = [
":url_mojom_gurl",
":url_mojom_origin",
]
}
+
+component("mojom_traits") {
+ output_name = "url_mojom_traits"
+
+ sources = [
+ "origin_mojom_traits.cc",
+ "origin_mojom_traits.h",
+ "url_gurl_mojom_traits.cc",
+ "url_gurl_mojom_traits.h",
+ ]
+
+ defines = [ "IS_URL_MOJOM_TRAITS_IMPL" ]
+
+ public_deps = [
+ ":url_mojom_gurl_shared",
+ ":url_mojom_origin_shared",
+ "//base",
+ "//mojo/public/cpp/base:shared_typemap_traits",
+ "//url",
+ ]
+}
diff --git a/chromium/url/mojom/OWNERS b/chromium/url/mojom/OWNERS
index ff78c432aff..06776eeacb1 100644
--- a/chromium/url/mojom/OWNERS
+++ b/chromium/url/mojom/OWNERS
@@ -2,6 +2,4 @@ per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
per-file *_mojom_traits*.*=set noparent
per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
-per-file *.typemap=set noparent
-per-file *.typemap=file://ipc/SECURITY_OWNERS
# COMPONENT: Internals>Mojo
diff --git a/chromium/url/mojom/gurl.typemap b/chromium/url/mojom/gurl.typemap
deleted file mode 100644
index 64d8507f7a9..00000000000
--- a/chromium/url/mojom/gurl.typemap
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-mojom = "//url/mojom/url.mojom"
-public_headers = [ "//url/gurl.h" ]
-traits_headers = [ "//url/mojom/url_gurl_mojom_traits.h" ]
-deps = [
- "//url",
-]
-type_mappings = [ "url.mojom.Url=::GURL" ]
diff --git a/chromium/url/mojom/origin.typemap b/chromium/url/mojom/origin.typemap
deleted file mode 100644
index 0dcf3bb0c09..00000000000
--- a/chromium/url/mojom/origin.typemap
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-mojom = "//url/mojom/origin.mojom"
-public_headers = [ "//url/origin.h" ]
-traits_headers = [ "//url/mojom/origin_mojom_traits.h" ]
-deps = [
- "//url",
-]
-type_mappings = [ "url.mojom.Origin=::url::Origin" ]
diff --git a/chromium/url/mojom/origin_mojom_traits.cc b/chromium/url/mojom/origin_mojom_traits.cc
new file mode 100644
index 00000000000..213c43bdd47
--- /dev/null
+++ b/chromium/url/mojom/origin_mojom_traits.cc
@@ -0,0 +1,34 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "url/mojom/origin_mojom_traits.h"
+
+#include "base/strings/string_piece.h"
+
+namespace mojo {
+
+// static
+bool StructTraits<url::mojom::OriginDataView, url::Origin>::Read(
+ url::mojom::OriginDataView data,
+ url::Origin* out) {
+ base::StringPiece scheme, host;
+ base::Optional<base::UnguessableToken> nonce_if_opaque;
+ if (!data.ReadScheme(&scheme) || !data.ReadHost(&host) ||
+ !data.ReadNonceIfOpaque(&nonce_if_opaque))
+ return false;
+
+ base::Optional<url::Origin> creation_result =
+ nonce_if_opaque
+ ? url::Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
+ scheme, host, data.port(), url::Origin::Nonce(*nonce_if_opaque))
+ : url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
+ scheme, host, data.port());
+ if (!creation_result)
+ return false;
+
+ *out = std::move(creation_result.value());
+ return true;
+}
+
+} // namespace mojo
diff --git a/chromium/url/mojom/origin_mojom_traits.h b/chromium/url/mojom/origin_mojom_traits.h
index ac34fe91e96..79ee1617868 100644
--- a/chromium/url/mojom/origin_mojom_traits.h
+++ b/chromium/url/mojom/origin_mojom_traits.h
@@ -5,16 +5,18 @@
#ifndef URL_MOJO_ORIGIN_MOJOM_TRAITS_H_
#define URL_MOJO_ORIGIN_MOJOM_TRAITS_H_
-#include "base/strings/string_piece.h"
+#include "base/component_export.h"
+#include "base/optional.h"
#include "base/unguessable_token.h"
#include "mojo/public/cpp/base/unguessable_token_mojom_traits.h"
-#include "url/mojom/origin.mojom.h"
+#include "url/mojom/origin.mojom-shared.h"
#include "url/origin.h"
namespace mojo {
template <>
-struct StructTraits<url::mojom::OriginDataView, url::Origin> {
+struct COMPONENT_EXPORT(URL_MOJOM_TRAITS)
+ StructTraits<url::mojom::OriginDataView, url::Origin> {
static const std::string& scheme(const url::Origin& r) {
return r.GetTupleOrPrecursorTupleIfOpaque().scheme();
}
@@ -29,26 +31,7 @@ struct StructTraits<url::mojom::OriginDataView, url::Origin> {
// TODO(nasko): Consider returning a const reference here.
return r.GetNonceForSerialization();
}
- static bool Read(url::mojom::OriginDataView data, url::Origin* out) {
- base::StringPiece scheme, host;
- base::Optional<base::UnguessableToken> nonce_if_opaque;
- if (!data.ReadScheme(&scheme) || !data.ReadHost(&host) ||
- !data.ReadNonceIfOpaque(&nonce_if_opaque))
- return false;
-
- base::Optional<url::Origin> creation_result =
- nonce_if_opaque
- ? url::Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
- scheme, host, data.port(),
- url::Origin::Nonce(*nonce_if_opaque))
- : url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
- scheme, host, data.port());
- if (!creation_result)
- return false;
-
- *out = std::move(creation_result.value());
- return true;
- }
+ static bool Read(url::mojom::OriginDataView data, url::Origin* out);
};
} // namespace mojo
diff --git a/chromium/url/mojom/typemaps.gni b/chromium/url/mojom/typemaps.gni
deleted file mode 100644
index 53875c3cd0a..00000000000
--- a/chromium/url/mojom/typemaps.gni
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-typemaps = [
- "//url/mojom/origin.typemap",
- "//url/mojom/gurl.typemap",
-]
diff --git a/chromium/url/mojom/url_gurl_mojom_traits.cc b/chromium/url/mojom/url_gurl_mojom_traits.cc
new file mode 100644
index 00000000000..4d776dba9c4
--- /dev/null
+++ b/chromium/url/mojom/url_gurl_mojom_traits.cc
@@ -0,0 +1,40 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "url/mojom/url_gurl_mojom_traits.h"
+
+#include "url/url_constants.h"
+
+namespace mojo {
+
+// static
+base::StringPiece StructTraits<url::mojom::UrlDataView, GURL>::url(
+ const GURL& r) {
+ if (r.possibly_invalid_spec().length() > url::kMaxURLChars || !r.is_valid()) {
+ return base::StringPiece();
+ }
+
+ return base::StringPiece(r.possibly_invalid_spec().c_str(),
+ r.possibly_invalid_spec().length());
+}
+
+// static
+bool StructTraits<url::mojom::UrlDataView, GURL>::Read(
+ url::mojom::UrlDataView data,
+ GURL* out) {
+ base::StringPiece url_string;
+ if (!data.ReadUrl(&url_string))
+ return false;
+
+ if (url_string.length() > url::kMaxURLChars)
+ return false;
+
+ *out = GURL(url_string);
+ if (!url_string.empty() && !out->is_valid())
+ return false;
+
+ return true;
+}
+
+} // namespace mojo
diff --git a/chromium/url/mojom/url_gurl_mojom_traits.h b/chromium/url/mojom/url_gurl_mojom_traits.h
index 57a36c96334..7d668209ce0 100644
--- a/chromium/url/mojom/url_gurl_mojom_traits.h
+++ b/chromium/url/mojom/url_gurl_mojom_traits.h
@@ -5,38 +5,19 @@
#ifndef URL_MOJOM_URL_GURL_MOJOM_TRAITS_H_
#define URL_MOJOM_URL_GURL_MOJOM_TRAITS_H_
+#include "base/component_export.h"
#include "base/strings/string_piece.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
#include "url/gurl.h"
-#include "url/mojom/url.mojom.h"
-#include "url/url_constants.h"
+#include "url/mojom/url.mojom-shared.h"
namespace mojo {
template <>
-struct StructTraits<url::mojom::UrlDataView, GURL> {
- static base::StringPiece url(const GURL& r) {
- if (r.possibly_invalid_spec().length() > url::kMaxURLChars ||
- !r.is_valid()) {
- return base::StringPiece();
- }
-
- return base::StringPiece(r.possibly_invalid_spec().c_str(),
- r.possibly_invalid_spec().length());
- }
- static bool Read(url::mojom::UrlDataView data, GURL* out) {
- base::StringPiece url_string;
- if (!data.ReadUrl(&url_string))
- return false;
-
- if (url_string.length() > url::kMaxURLChars)
- return false;
-
- *out = GURL(url_string);
- if (!url_string.empty() && !out->is_valid())
- return false;
-
- return true;
- }
+struct COMPONENT_EXPORT(URL_MOJOM_TRAITS)
+ StructTraits<url::mojom::UrlDataView, GURL> {
+ static base::StringPiece url(const GURL& r);
+ static bool Read(url::mojom::UrlDataView data, GURL* out);
};
} // namespace mojo
diff --git a/chromium/url/origin.cc b/chromium/url/origin.cc
index 0686e0525f0..26079d7730b 100644
--- a/chromium/url/origin.cc
+++ b/chromium/url/origin.cc
@@ -7,8 +7,12 @@
#include <stdint.h>
#include <algorithm>
+#include <vector>
+#include "base/base64.h"
+#include "base/containers/span.h"
#include "base/logging.h"
+#include "base/pickle.h"
#include "base/stl_util.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
@@ -42,12 +46,12 @@ Origin Origin::Create(const GURL& url) {
// It's SchemeHostPort's responsibility to filter out unrecognized schemes;
// sanity check that this is happening.
- DCHECK(tuple.IsInvalid() || url.IsStandard() ||
+ DCHECK(!tuple.IsValid() || url.IsStandard() ||
base::Contains(GetLocalSchemes(), url.scheme_piece()) ||
AllowNonStandardSchemesForAndroidWebView());
}
- if (tuple.IsInvalid())
+ if (!tuple.IsValid())
return Origin();
return Origin(std::move(tuple));
}
@@ -74,7 +78,7 @@ base::Optional<Origin> Origin::UnsafelyCreateTupleOriginWithoutNormalization(
uint16_t port) {
SchemeHostPort tuple(scheme.as_string(), host.as_string(), port,
SchemeHostPort::CHECK_CANONICALIZATION);
- if (tuple.IsInvalid())
+ if (!tuple.IsValid())
return base::nullopt;
return Origin(std::move(tuple));
}
@@ -91,7 +95,7 @@ base::Optional<Origin> Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
// For opaque origins, it is okay for the SchemeHostPort to be invalid;
// however, this should only arise when the arguments indicate the
// canonical representation of the invalid SchemeHostPort.
- if (precursor.IsInvalid() &&
+ if (!precursor.IsValid() &&
!(precursor_scheme.empty() && precursor_host.empty() &&
precursor_port == 0)) {
return base::nullopt;
@@ -105,7 +109,7 @@ Origin Origin::CreateFromNormalizedTuple(std::string scheme,
uint16_t port) {
SchemeHostPort tuple(std::move(scheme), std::move(host), port,
SchemeHostPort::ALREADY_CANONICALIZED);
- if (tuple.IsInvalid())
+ if (!tuple.IsValid())
return Origin();
return Origin(std::move(tuple));
}
@@ -171,7 +175,7 @@ bool Origin::CanBeDerivedFrom(const GURL& url) const {
// And if it is unique opaque origin, it definitely is fine. But if there
// is a precursor stored, we should fall through to compare the tuples.
- if (tuple_.IsInvalid())
+ if (!tuple_.IsValid())
return true;
}
@@ -198,7 +202,7 @@ bool Origin::CanBeDerivedFrom(const GURL& url) const {
// opaque origin. It is valid case, as any browser-initiated navigation
// to about:blank or data: URL will result in a document with such
// origin and it is valid for it to create blob: URLs.
- if (tuple_.IsInvalid())
+ if (!tuple_.IsValid())
return true;
url_tuple = SchemeHostPort(GURL(url.GetContent()));
@@ -221,7 +225,7 @@ bool Origin::CanBeDerivedFrom(const GURL& url) const {
// If |this| does not have valid precursor tuple, it is unique opaque origin,
// which is what we expect non-standard schemes to get.
- if (tuple_.IsInvalid())
+ if (!tuple_.IsValid())
return true;
// However, when there is precursor present, the schemes must match.
@@ -257,7 +261,7 @@ std::string Origin::GetDebugString() const {
: nonce_->raw_token().ToString();
std::string out = base::StrCat({Serialize(), " [internally: (", nonce, ")"});
- if (tuple_.IsInvalid())
+ if (!tuple_.IsValid())
base::StrAppend(&out, {" anonymous]"});
else
base::StrAppend(&out, {" derived from ", tuple_.Serialize(), "]"});
@@ -266,7 +270,7 @@ std::string Origin::GetDebugString() const {
Origin::Origin(SchemeHostPort tuple) : tuple_(std::move(tuple)) {
DCHECK(!opaque());
- DCHECK(!tuple_.IsInvalid());
+ DCHECK(tuple_.IsValid());
}
// Constructs an opaque origin derived from |precursor|.
@@ -279,6 +283,82 @@ Origin::Origin(const Nonce& nonce, SchemeHostPort precursor)
DCHECK_EQ(0U, port());
}
+// The pickle is saved in the following format, in order:
+// string - tuple_.GetURL().spec().
+// uint64_t (if opaque) - high bits of nonce if opaque. 0 if not initialized.
+// uint64_t (if opaque) - low bits of nonce if opaque. 0 if not initialized.
+base::Optional<std::string> Origin::SerializeWithNonce() const {
+ if (!opaque() && !tuple_.IsValid())
+ return base::nullopt;
+
+ base::Pickle pickle;
+ pickle.WriteString(tuple_.Serialize());
+ if (opaque() && !nonce_->raw_token().is_empty()) {
+ pickle.WriteUInt64(nonce_->token().GetHighForSerialization());
+ pickle.WriteUInt64(nonce_->token().GetLowForSerialization());
+ } else if (opaque()) {
+ // Nonce hasn't been initialized.
+ pickle.WriteUInt64(0);
+ pickle.WriteUInt64(0);
+ }
+
+ base::span<const uint8_t> data(
+ static_cast<const uint8_t*>(pickle.data()),
+ static_cast<const uint8_t*>(pickle.data()) + pickle.size());
+ // Base64 encode the data to make it nicer to play with.
+ return base::Base64Encode(data);
+}
+
+// static
+base::Optional<Origin> Origin::Deserialize(const std::string& value) {
+ std::string data;
+ if (!base::Base64Decode(value, &data))
+ return base::nullopt;
+ base::Pickle pickle(reinterpret_cast<char*>(&data[0]), data.size());
+ base::PickleIterator reader(pickle);
+
+ std::string pickled_url;
+ if (!reader.ReadString(&pickled_url))
+ return base::nullopt;
+ GURL url(pickled_url);
+
+ // If only a tuple was serialized, then this origin is not opaque. For opaque
+ // origins, we expect two uint64's to be left in the pickle.
+ bool is_opaque = !reader.ReachedEnd();
+
+ // Opaque origins without a tuple are ok.
+ if (!is_opaque && !url.is_valid())
+ return base::nullopt;
+ SchemeHostPort tuple(url);
+
+ // Possible successful early return if the pickled Origin was not opaque.
+ if (!is_opaque) {
+ Origin origin(tuple);
+ if (origin.opaque())
+ return base::nullopt; // Something went horribly wrong.
+ return origin;
+ }
+
+ uint64_t nonce_high = 0;
+ if (!reader.ReadUInt64(&nonce_high))
+ return base::nullopt;
+
+ uint64_t nonce_low = 0;
+ if (!reader.ReadUInt64(&nonce_low))
+ return base::nullopt;
+
+ Origin::Nonce nonce;
+ if (nonce_high != 0 && nonce_low != 0) {
+ // The serialized nonce wasn't empty, so copy it here.
+ nonce = Origin::Nonce(
+ base::UnguessableToken::Deserialize(nonce_high, nonce_low));
+ }
+ Origin origin;
+ origin.nonce_ = std::move(nonce);
+ origin.tuple_ = tuple;
+ return origin;
+}
+
std::ostream& operator<<(std::ostream& out, const url::Origin& origin) {
out << origin.GetDebugString();
return out;
@@ -351,4 +431,17 @@ bool Origin::Nonce::operator!=(const Origin::Nonce& other) const {
return !(*this == other);
}
+namespace debug {
+
+ScopedOriginCrashKey::ScopedOriginCrashKey(
+ base::debug::CrashKeyString* crash_key,
+ const url::Origin* value)
+ : base::debug::ScopedCrashKeyString(
+ crash_key,
+ value ? value->GetDebugString() : "nullptr") {}
+
+ScopedOriginCrashKey::~ScopedOriginCrashKey() = default;
+
+} // namespace debug
+
} // namespace url
diff --git a/chromium/url/origin.h b/chromium/url/origin.h
index f0b85a39628..845d5730513 100644
--- a/chromium/url/origin.h
+++ b/chromium/url/origin.h
@@ -7,10 +7,12 @@
#include <stdint.h>
+#include <memory>
#include <string>
#include "base/component_export.h"
#include "base/debug/alias.h"
+#include "base/debug/crash_logging.h"
#include "base/optional.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
@@ -53,6 +55,11 @@ struct StructTraits;
struct UrlOriginAdapter;
} // namespace mojo
+namespace net {
+class NetworkIsolationKey;
+class OpaqueNonTransientNetworkIsolationKeyTest;
+} // namespace net
+
namespace url {
namespace mojom {
@@ -144,6 +151,9 @@ class COMPONENT_EXPORT(URL) Origin {
// 2. 'filesystem' URLs behave as 'blob' URLs (that is, the origin is parsed
// out of everything in the URL which follows the scheme).
// 3. 'file' URLs all parse as ("file", "", 0).
+ //
+ // Note that the returned Origin may have a different scheme and host from
+ // |url| (e.g. in case of blob URLs - see OriginTest.ConstructFromGURL).
static Origin Create(const GURL& url);
// Creates an Origin for the resource |url| as if it were requested
@@ -288,6 +298,8 @@ class COMPONENT_EXPORT(URL) Origin {
private:
friend class blink::SecurityOrigin;
+ friend class net::NetworkIsolationKey;
+ friend class net::OpaqueNonTransientNetworkIsolationKeyTest;
friend class OriginTest;
friend struct mojo::UrlOriginAdapter;
friend struct ipc_fuzzer::FuzzTraits<Origin>;
@@ -382,6 +394,16 @@ class COMPONENT_EXPORT(URL) Origin {
// used only when trying to send an Origin across an IPC pipe.
base::Optional<base::UnguessableToken> GetNonceForSerialization() const;
+ // Serializes this Origin, including its nonce if it is opaque. If an opaque
+ // origin's |tuple_| is invalid or the nonce isn't initialized, nullopt is
+ // returned. Use of this method should be limited as an opaque origin will
+ // never be matchable in future browser sessions.
+ base::Optional<std::string> SerializeWithNonce() const;
+
+ // Deserializes an origin from |ToValueWithNonce|. Returns nullopt if the
+ // value was invalid in any way.
+ static base::Optional<Origin> Deserialize(const std::string& value);
+
// The tuple is used for both tuple origins (e.g. https://example.com:80), as
// well as for opaque origins, where it tracks the tuple origin from which
// the opaque origin was initially derived (we call this the "precursor"
@@ -408,6 +430,21 @@ COMPONENT_EXPORT(URL) bool IsSameOriginWith(const GURL& a, const GURL& b);
#define DEBUG_ALIAS_FOR_ORIGIN(var_name, origin) \
DEBUG_ALIAS_FOR_CSTR(var_name, (origin).Serialize().c_str(), 128)
+namespace debug {
+
+class COMPONENT_EXPORT(URL) ScopedOriginCrashKey
+ : public base::debug::ScopedCrashKeyString {
+ public:
+ ScopedOriginCrashKey(base::debug::CrashKeyString* crash_key,
+ const url::Origin* value);
+ ~ScopedOriginCrashKey();
+
+ ScopedOriginCrashKey(const ScopedOriginCrashKey&) = delete;
+ ScopedOriginCrashKey& operator=(const ScopedOriginCrashKey&) = delete;
+};
+
+} // namespace debug
+
} // namespace url
#endif // URL_ORIGIN_H_
diff --git a/chromium/url/origin_unittest.cc b/chromium/url/origin_unittest.cc
index 1cd9889ea16..a41281d1e8d 100644
--- a/chromium/url/origin_unittest.cc
+++ b/chromium/url/origin_unittest.cc
@@ -55,7 +55,6 @@ class OriginTest : public ::testing::Test {
AddStandardScheme("standard-but-noaccess", SchemeType::SCHEME_WITH_HOST);
AddNoAccessScheme("standard-but-noaccess");
}
- void TearDown() override { url::ResetForTests(); }
::testing::AssertionResult DoEqualityComparisons(const url::Origin& a,
const url::Origin& b,
@@ -96,7 +95,8 @@ class OriginTest : public ::testing::Test {
return origin.GetNonceForSerialization();
}
- // Wrapper around url::Origin method to expose it to tests.
+ // Wrappers around url::Origin methods to expose it to tests.
+
base::Optional<Origin> UnsafelyCreateOpaqueOriginWithoutNormalization(
base::StringPiece precursor_scheme,
base::StringPiece precursor_host,
@@ -105,6 +105,17 @@ class OriginTest : public ::testing::Test {
return Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
precursor_scheme, precursor_host, precursor_port, nonce);
}
+
+ base::Optional<std::string> SerializeWithNonce(const Origin& origin) {
+ return origin.SerializeWithNonce();
+ }
+
+ base::Optional<Origin> Deserialize(const std::string& value) {
+ return Origin::Deserialize(value);
+ }
+
+ private:
+ ScopedSchemeRegistryForTests scoped_registry_;
};
TEST_F(OriginTest, OpaqueOriginComparison) {
@@ -116,14 +127,14 @@ TEST_F(OriginTest, OpaqueOriginComparison) {
EXPECT_EQ("", opaque_a.host());
EXPECT_EQ(0, opaque_a.port());
EXPECT_EQ(SchemeHostPort(), opaque_a.GetTupleOrPrecursorTupleIfOpaque());
- EXPECT_TRUE(opaque_a.GetTupleOrPrecursorTupleIfOpaque().IsInvalid());
+ EXPECT_FALSE(opaque_a.GetTupleOrPrecursorTupleIfOpaque().IsValid());
EXPECT_TRUE(opaque_b.opaque());
EXPECT_EQ("", opaque_b.scheme());
EXPECT_EQ("", opaque_b.host());
EXPECT_EQ(0, opaque_b.port());
EXPECT_EQ(SchemeHostPort(), opaque_b.GetTupleOrPrecursorTupleIfOpaque());
- EXPECT_TRUE(opaque_b.GetTupleOrPrecursorTupleIfOpaque().IsInvalid());
+ EXPECT_FALSE(opaque_b.GetTupleOrPrecursorTupleIfOpaque().IsValid());
// Two default-constructed Origins should always be cross origin to each
// other.
@@ -377,7 +388,7 @@ TEST_F(OriginTest, ConstructFromGURL) {
.DeriveNewOpaqueOrigin();
EXPECT_TRUE(derived_opaque.opaque());
EXPECT_NE(origin, derived_opaque);
- EXPECT_FALSE(derived_opaque.GetTupleOrPrecursorTupleIfOpaque().IsInvalid());
+ EXPECT_TRUE(derived_opaque.GetTupleOrPrecursorTupleIfOpaque().IsValid());
EXPECT_EQ(origin.GetTupleOrPrecursorTupleIfOpaque(),
derived_opaque.GetTupleOrPrecursorTupleIfOpaque());
EXPECT_EQ(derived_opaque, derived_opaque);
@@ -386,8 +397,8 @@ TEST_F(OriginTest, ConstructFromGURL) {
Origin::Resolve(GURL("data:text/html,baz"), origin);
EXPECT_TRUE(derived_opaque_via_data_url.opaque());
EXPECT_NE(origin, derived_opaque_via_data_url);
- EXPECT_FALSE(derived_opaque_via_data_url.GetTupleOrPrecursorTupleIfOpaque()
- .IsInvalid());
+ EXPECT_TRUE(derived_opaque_via_data_url.GetTupleOrPrecursorTupleIfOpaque()
+ .IsValid());
EXPECT_EQ(origin.GetTupleOrPrecursorTupleIfOpaque(),
derived_opaque_via_data_url.GetTupleOrPrecursorTupleIfOpaque());
EXPECT_NE(derived_opaque, derived_opaque_via_data_url);
@@ -623,9 +634,9 @@ TEST_F(OriginTest, DomainIs) {
};
for (const auto& test_case : kTestCases) {
- SCOPED_TRACE(testing::Message() << "(url, domain): (" << test_case.url
- << ", " << test_case.lower_ascii_domain
- << ")");
+ SCOPED_TRACE(testing::Message()
+ << "(url, domain): (" << test_case.url << ", "
+ << test_case.lower_ascii_domain << ")");
GURL url(test_case.url);
ASSERT_TRUE(url.is_valid());
Origin origin = Origin::Create(url);
@@ -656,6 +667,7 @@ TEST_F(OriginTest, NonStandardScheme) {
Origin origin = Origin::Create(GURL("cow://"));
EXPECT_TRUE(origin.opaque());
}
+
TEST_F(OriginTest, NonStandardSchemeWithAndroidWebViewHack) {
EnableNonStandardSchemesForAndroidWebView();
Origin origin = Origin::Create(GURL("cow://"));
@@ -663,10 +675,10 @@ TEST_F(OriginTest, NonStandardSchemeWithAndroidWebViewHack) {
EXPECT_EQ("cow", origin.scheme());
EXPECT_EQ("", origin.host());
EXPECT_EQ(0, origin.port());
- ResetForTests();
}
TEST_F(OriginTest, CanBeDerivedFrom) {
+ AddStandardScheme("new-standard", SchemeType::SCHEME_WITH_HOST);
Origin opaque_unique_origin = Origin();
Origin regular_origin = Origin::Create(GURL("https://a.com/"));
@@ -684,7 +696,6 @@ TEST_F(OriginTest, CanBeDerivedFrom) {
non_standard_scheme_origin.DeriveNewOpaqueOrigin();
// Also, add new standard scheme that is local to the test.
- AddStandardScheme("new-standard", SchemeType::SCHEME_WITH_HOST);
Origin new_standard_origin = Origin::Create(GURL("new-standard://host/"));
Origin new_standard_opaque_precursor_origin =
new_standard_origin.DeriveNewOpaqueOrigin();
@@ -859,4 +870,78 @@ TEST_F(OriginTest, GetDebugString) {
"file:// [internally: file://example.com]");
}
+TEST_F(OriginTest, Deserialize) {
+ std::vector<GURL> valid_urls = {
+ GURL("https://a.com"), GURL("http://a"),
+ GURL("http://a:80"), GURL("file://a.com/etc/passwd"),
+ GURL("file:///etc/passwd"), GURL("http://192.168.1.1"),
+ GURL("http://[2001:db8::1]/"),
+ };
+ for (const GURL& url : valid_urls) {
+ SCOPED_TRACE(url.spec());
+ Origin origin = Origin::Create(url);
+ base::Optional<std::string> serialized = SerializeWithNonce(origin);
+ ASSERT_TRUE(serialized);
+
+ base::Optional<Origin> deserialized = Deserialize(std::move(*serialized));
+ ASSERT_TRUE(deserialized.has_value());
+
+ EXPECT_TRUE(DoEqualityComparisons(origin, deserialized.value(), true));
+ EXPECT_EQ(origin.GetDebugString(), deserialized.value().GetDebugString());
+ }
+}
+
+TEST_F(OriginTest, DeserializeInvalid) {
+ EXPECT_EQ(base::nullopt, Deserialize(std::string()));
+ EXPECT_EQ(base::nullopt, Deserialize("deadbeef"));
+ EXPECT_EQ(base::nullopt, Deserialize("0123456789"));
+ EXPECT_EQ(base::nullopt, Deserialize("https://a.com"));
+ EXPECT_EQ(base::nullopt, Deserialize("https://192.168.1.1"));
+}
+
+TEST_F(OriginTest, SerializeTBDNonce) {
+ std::vector<GURL> invalid_urls = {
+ GURL("data:uniqueness"), GURL("data:,"),
+ GURL("data:text/html,Hello!"), GURL("javascript:alert(1)"),
+ GURL("about:blank"), GURL("google.com"),
+ };
+ for (const GURL& url : invalid_urls) {
+ SCOPED_TRACE(url.spec());
+ Origin origin = Origin::Create(url);
+ base::Optional<std::string> serialized = SerializeWithNonce(origin);
+ base::Optional<Origin> deserialized = Deserialize(std::move(*serialized));
+ ASSERT_TRUE(deserialized.has_value());
+
+ // Can't use DoEqualityComparisons here since empty nonces are never ==
+ // unless they are the same object.
+ EXPECT_EQ(origin.GetDebugString(), deserialized.value().GetDebugString());
+ }
+
+ // Same basic test as above, but without a GURL to create tuple_.
+ Origin opaque;
+ base::Optional<std::string> serialized = SerializeWithNonce(opaque);
+ ASSERT_TRUE(serialized);
+
+ base::Optional<Origin> deserialized = Deserialize(std::move(*serialized));
+ ASSERT_TRUE(deserialized.has_value());
+
+ // Can't use DoEqualityComparisons here since empty nonces are never == unless
+ // they are the same object.
+ EXPECT_EQ(opaque.GetDebugString(), deserialized.value().GetDebugString());
+}
+
+TEST_F(OriginTest, DeserializeValidNonce) {
+ Origin opaque;
+ GetNonce(opaque);
+
+ base::Optional<std::string> serialized = SerializeWithNonce(opaque);
+ ASSERT_TRUE(serialized);
+
+ base::Optional<Origin> deserialized = Deserialize(std::move(*serialized));
+ ASSERT_TRUE(deserialized.has_value());
+
+ EXPECT_TRUE(DoEqualityComparisons(opaque, deserialized.value(), true));
+ EXPECT_EQ(opaque.GetDebugString(), deserialized.value().GetDebugString());
+}
+
} // namespace url
diff --git a/chromium/url/scheme_host_port.cc b/chromium/url/scheme_host_port.cc
index 18b0e8cd607..96d491267b4 100644
--- a/chromium/url/scheme_host_port.cc
+++ b/chromium/url/scheme_host_port.cc
@@ -134,15 +134,15 @@ SchemeHostPort::SchemeHostPort(std::string scheme,
ConstructPolicy policy)
: port_(0) {
if (!IsValidInput(scheme, host, port, policy)) {
- DCHECK(IsInvalid());
+ DCHECK(!IsValid());
return;
}
scheme_ = std::move(scheme);
host_ = std::move(host);
port_ = port;
- DCHECK(!IsInvalid()) << "Scheme: " << scheme_ << " Host: " << host_
- << " Port: " << port;
+ DCHECK(IsValid()) << "Scheme: " << scheme_ << " Host: " << host_
+ << " Port: " << port;
}
SchemeHostPort::SchemeHostPort(base::StringPiece scheme,
@@ -172,19 +172,19 @@ SchemeHostPort::SchemeHostPort(const GURL& url) : port_(0) {
if (!IsValidInput(scheme, host, port, ALREADY_CANONICALIZED))
return;
- scheme.CopyToString(&scheme_);
- host.CopyToString(&host_);
+ scheme_ = std::string(scheme);
+ host_ = std::string(host);
port_ = port;
}
SchemeHostPort::~SchemeHostPort() = default;
-bool SchemeHostPort::IsInvalid() const {
+bool SchemeHostPort::IsValid() const {
// It suffices to just check |scheme_| for emptiness; the other fields are
// never present without it.
DCHECK(!scheme_.empty() || host_.empty());
DCHECK(!scheme_.empty() || port_ == 0);
- return scheme_.empty();
+ return !scheme_.empty();
}
std::string SchemeHostPort::Serialize() const {
@@ -198,7 +198,7 @@ GURL SchemeHostPort::GetURL() const {
url::Parsed parsed;
std::string serialized = SerializeInternal(&parsed);
- if (IsInvalid())
+ if (!IsValid())
return GURL(std::move(serialized), parsed, false);
// SchemeHostPort does not have enough information to determine if an empty
@@ -223,7 +223,7 @@ bool SchemeHostPort::operator<(const SchemeHostPort& other) const {
std::string SchemeHostPort::SerializeInternal(url::Parsed* parsed) const {
std::string result;
- if (IsInvalid())
+ if (!IsValid())
return result;
// Reserve enough space for the "normal" case of scheme://host/.
diff --git a/chromium/url/scheme_host_port.h b/chromium/url/scheme_host_port.h
index cf88cb1ea90..6a438fe14e0 100644
--- a/chromium/url/scheme_host_port.h
+++ b/chromium/url/scheme_host_port.h
@@ -122,7 +122,7 @@ class COMPONENT_EXPORT(URL) SchemeHostPort {
const std::string& host() const { return host_; }
const std::string& scheme() const { return scheme_; }
uint16_t port() const { return port_; }
- bool IsInvalid() const;
+ bool IsValid() const;
// Serializes the SchemeHostPort tuple to a canonical form.
//
diff --git a/chromium/url/scheme_host_port_unittest.cc b/chromium/url/scheme_host_port_unittest.cc
index 874c100767b..2ebf7e78c9b 100644
--- a/chromium/url/scheme_host_port_unittest.cc
+++ b/chromium/url/scheme_host_port_unittest.cc
@@ -16,12 +16,11 @@ namespace {
class SchemeHostPortTest : public testing::Test {
public:
SchemeHostPortTest() = default;
- ~SchemeHostPortTest() override {
- // Reset any added schemes.
- url::ResetForTests();
- }
+ ~SchemeHostPortTest() override = default;
private:
+ url::ScopedSchemeRegistryForTests scoped_registry_;
+
DISALLOW_COPY_AND_ASSIGN(SchemeHostPortTest);
};
@@ -52,7 +51,7 @@ TEST_F(SchemeHostPortTest, Invalid) {
EXPECT_EQ("", invalid.scheme());
EXPECT_EQ("", invalid.host());
EXPECT_EQ(0, invalid.port());
- EXPECT_TRUE(invalid.IsInvalid());
+ EXPECT_FALSE(invalid.IsValid());
EXPECT_EQ(invalid, invalid);
const char* urls[] = {
@@ -76,7 +75,7 @@ TEST_F(SchemeHostPortTest, Invalid) {
EXPECT_EQ("", tuple.scheme());
EXPECT_EQ("", tuple.host());
EXPECT_EQ(0, tuple.port());
- EXPECT_TRUE(tuple.IsInvalid());
+ EXPECT_FALSE(tuple.IsValid());
EXPECT_EQ(tuple, tuple);
EXPECT_EQ(tuple, invalid);
EXPECT_EQ(invalid, tuple);
@@ -105,7 +104,7 @@ TEST_F(SchemeHostPortTest, ExplicitConstruction) {
EXPECT_EQ(test.scheme, tuple.scheme());
EXPECT_EQ(test.host, tuple.host());
EXPECT_EQ(test.port, tuple.port());
- EXPECT_FALSE(tuple.IsInvalid());
+ EXPECT_TRUE(tuple.IsValid());
EXPECT_EQ(tuple, tuple);
ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
}
@@ -141,7 +140,7 @@ TEST_F(SchemeHostPortTest, InvalidConstruction) {
EXPECT_EQ("", tuple.scheme());
EXPECT_EQ("", tuple.host());
EXPECT_EQ(0, tuple.port());
- EXPECT_TRUE(tuple.IsInvalid());
+ EXPECT_FALSE(tuple.IsValid());
EXPECT_EQ(tuple, tuple);
ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
}
@@ -170,7 +169,7 @@ TEST_F(SchemeHostPortTest, InvalidConstructionWithEmbeddedNulls) {
EXPECT_EQ("", tuple.scheme());
EXPECT_EQ("", tuple.host());
EXPECT_EQ(0, tuple.port());
- EXPECT_TRUE(tuple.IsInvalid());
+ EXPECT_FALSE(tuple.IsValid());
ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
}
}
@@ -205,7 +204,7 @@ TEST_F(SchemeHostPortTest, GURLConstruction) {
EXPECT_EQ(test.scheme, tuple.scheme());
EXPECT_EQ(test.host, tuple.host());
EXPECT_EQ(test.port, tuple.port());
- EXPECT_FALSE(tuple.IsInvalid());
+ EXPECT_TRUE(tuple.IsValid());
EXPECT_EQ(tuple, tuple);
ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
}
diff --git a/chromium/url/url_canon_relative.cc b/chromium/url/url_canon_relative.cc
index 607392897a3..de47861d498 100644
--- a/chromium/url/url_canon_relative.cc
+++ b/chromium/url/url_canon_relative.cc
@@ -7,6 +7,7 @@
#include <algorithm>
#include "base/logging.h"
+#include "base/strings/string_util.h"
#include "url/url_canon.h"
#include "url/url_canon_internal.h"
#include "url/url_constants.h"
@@ -62,6 +63,39 @@ bool DoesBeginSlashWindowsDriveSpec(const CHAR* spec, int start_offset,
#endif // WIN32
+template <typename CHAR>
+bool IsValidScheme(const CHAR* url, const Component& scheme) {
+ // Caller should ensure that the |scheme| is not empty.
+ DCHECK_NE(0, scheme.len);
+
+ // From https://url.spec.whatwg.org/#scheme-start-state:
+ // scheme start state:
+ // 1. If c is an ASCII alpha, append c, lowercased, to buffer, and set
+ // state to scheme state.
+ // 2. Otherwise, if state override is not given, set state to no scheme
+ // state, and decrease pointer by one.
+ // 3. Otherwise, validation error, return failure.
+ // Note that both step 2 and step 3 mean that the scheme was not valid.
+ if (!base::IsAsciiAlpha(url[scheme.begin]))
+ return false;
+
+ // From https://url.spec.whatwg.org/#scheme-state:
+ // scheme state:
+ // 1. If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E
+ // (.), append c, lowercased, to buffer.
+ // 2. Otherwise, if c is U+003A (:), then [...]
+ //
+ // We begin at |scheme.begin + 1|, because the character at |scheme.begin| has
+ // already been checked by base::IsAsciiAlpha above.
+ int scheme_end = scheme.end();
+ for (int i = scheme.begin + 1; i < scheme_end; i++) {
+ if (!CanonicalSchemeChar(url[i]))
+ return false;
+ }
+
+ return true;
+}
+
// See IsRelativeURL in the header file for usage.
template<typename CHAR>
bool DoIsRelativeURL(const char* base,
@@ -126,17 +160,14 @@ bool DoIsRelativeURL(const char* base,
}
// If the scheme isn't valid, then it's relative.
- int scheme_end = scheme.end();
- for (int i = scheme.begin; i < scheme_end; i++) {
- if (!CanonicalSchemeChar(url[i])) {
- if (!is_base_hierarchical) {
- // Don't allow relative URLs if the base scheme doesn't support it.
- return false;
- }
- *relative_component = MakeRange(begin, url_len);
- *is_relative = true;
- return true;
+ if (!IsValidScheme(url, scheme)) {
+ if (!is_base_hierarchical) {
+ // Don't allow relative URLs if the base scheme doesn't support it.
+ return false;
}
+ *relative_component = MakeRange(begin, url_len);
+ *is_relative = true;
+ return true;
}
// If the scheme is not the same, then we can't count it as relative.
diff --git a/chromium/url/url_util.cc b/chromium/url/url_util.cc
index cafe468fad7..93a106328c3 100644
--- a/chromium/url/url_util.cc
+++ b/chromium/url/url_util.cc
@@ -6,8 +6,9 @@
#include <stddef.h>
#include <string.h>
+#include <atomic>
-#include "base/debug/leak_annotations.h"
+#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/stl_util.h"
@@ -21,6 +22,12 @@ namespace url {
namespace {
+// A pair for representing a standard scheme name and the SchemeType for it.
+struct SchemeWithType {
+ std::string scheme;
+ SchemeType type;
+};
+
// List of currently registered schemes and associated properties.
struct SchemeRegistry {
// Standard format schemes (see header for details).
@@ -90,11 +97,26 @@ struct SchemeRegistry {
bool allow_non_standard_schemes = false;
};
-SchemeRegistry* GetSchemeRegistry() {
+// See the LockSchemeRegistries declaration in the header.
+bool scheme_registries_locked = false;
+
+// Ensure that the schemes aren't modified after first use.
+static std::atomic<bool> g_scheme_registries_used{false};
+
+// Gets the scheme registry without locking the schemes. This should *only* be
+// used for adding schemes to the registry.
+SchemeRegistry* GetSchemeRegistryWithoutLocking() {
static base::NoDestructor<SchemeRegistry> registry;
return registry.get();
}
+const SchemeRegistry& GetSchemeRegistry() {
+#if DCHECK_IS_ON()
+ g_scheme_registries_used.store(true);
+#endif
+ return *GetSchemeRegistryWithoutLocking();
+}
+
// Pass this enum through for methods which would like to know if whitespace
// removal is necessary.
enum WhitespaceRemovalPolicy {
@@ -102,9 +124,6 @@ enum WhitespaceRemovalPolicy {
DO_NOT_REMOVE_WHITESPACE,
};
-// See the LockSchemeRegistries declaration in the header.
-bool scheme_registries_locked = false;
-
// This template converts a given character type to the corresponding
// StringPiece type.
template<typename CHAR> struct CharToStringPiece {
@@ -154,7 +173,7 @@ bool DoIsInSchemes(const CHAR* spec,
template<typename CHAR>
bool DoIsStandard(const CHAR* spec, const Component& scheme, SchemeType* type) {
return DoIsInSchemes(spec, scheme, type,
- GetSchemeRegistry()->standard_schemes);
+ GetSchemeRegistry().standard_schemes);
}
@@ -165,7 +184,7 @@ bool DoFindAndCompareScheme(const CHAR* str,
Component* found_scheme) {
// Before extracting scheme, canonicalize the URL to remove any whitespace.
// This matches the canonicalization done in DoCanonicalize function.
- RawCanonOutputT<CHAR> whitespace_buffer;
+ STACK_UNINITIALIZED RawCanonOutputT<CHAR> whitespace_buffer;
int spec_len;
const CHAR* spec =
RemoveURLWhitespace(str, str_len, &whitespace_buffer, &spec_len, nullptr);
@@ -194,7 +213,7 @@ bool DoCanonicalize(const CHAR* spec,
// Remove any whitespace from the middle of the relative URL if necessary.
// Possibly this will result in copying to the new buffer.
- RawCanonOutputT<CHAR> whitespace_buffer;
+ STACK_UNINITIALIZED RawCanonOutputT<CHAR> whitespace_buffer;
if (whitespace_policy == REMOVE_WHITESPACE) {
spec = RemoveURLWhitespace(spec, spec_len, &whitespace_buffer, &spec_len,
&output_parsed->potentially_dangling_markup);
@@ -273,7 +292,7 @@ bool DoResolveRelative(const char* base_spec,
Parsed* output_parsed) {
// Remove any whitespace from the middle of the relative URL, possibly
// copying to the new buffer.
- RawCanonOutputT<CHAR> whitespace_buffer;
+ STACK_UNINITIALIZED RawCanonOutputT<CHAR> whitespace_buffer;
int relative_length;
const CHAR* relative = RemoveURLWhitespace(
in_relative, in_relative_length, &whitespace_buffer, &relative_length,
@@ -314,7 +333,7 @@ bool DoResolveRelative(const char* base_spec,
Parsed base_parsed_authority;
ParseStandardURL(base_spec, base_spec_len, &base_parsed_authority);
if (base_parsed_authority.host.is_nonempty()) {
- RawCanonOutputT<char> temporary_output;
+ STACK_UNINITIALIZED RawCanonOutputT<char> temporary_output;
bool did_resolve_succeed =
ResolveRelativeURL(base_spec, base_parsed_authority, false, relative,
relative_component, charset_converter,
@@ -366,7 +385,7 @@ bool DoReplaceComponents(const char* spec,
if (replacements.IsSchemeOverridden()) {
// Canonicalize the new scheme so it is 8-bit and can be concatenated with
// the existing spec.
- RawCanonOutput<128> scheme_replaced;
+ STACK_UNINITIALIZED RawCanonOutput<128> scheme_replaced;
Component scheme_replaced_parsed;
CanonicalizeScheme(replacements.sources().scheme,
replacements.components().scheme,
@@ -383,7 +402,7 @@ bool DoReplaceComponents(const char* spec,
// We now need to completely re-parse the resulting string since its meaning
// may have changed with the different scheme.
- RawCanonOutput<128> recanonicalized;
+ STACK_UNINITIALIZED RawCanonOutput<128> recanonicalized;
Parsed recanonicalized_parsed;
DoCanonicalize(scheme_replaced.data(), scheme_replaced.length(), true,
REMOVE_WHITESPACE, charset_converter, &recanonicalized,
@@ -438,8 +457,16 @@ bool DoReplaceComponents(const char* spec,
return ReplacePathURL(spec, parsed, replacements, output, out_parsed);
}
-void DoAddScheme(const char* new_scheme, std::vector<std::string>* schemes) {
- DCHECK(schemes);
+void DoSchemeModificationPreamble() {
+ // If this assert triggers, it means you've called Add*Scheme after
+ // the SchemeRegistry has been used.
+ //
+ // This normally means you're trying to set up a new scheme too late or using
+ // the SchemeRegistry too early in your application's init process. Make sure
+ // that you haven't added any static GURL initializers in tests.
+ DCHECK(!g_scheme_registries_used.load())
+ << "Trying to add a scheme after the lists have been used.";
+
// If this assert triggers, it means you've called Add*Scheme after
// LockSchemeRegistries has been called (see the header file for
// LockSchemeRegistries for more).
@@ -449,122 +476,145 @@ void DoAddScheme(const char* new_scheme, std::vector<std::string>* schemes) {
// and calls LockSchemeRegistries, and add your new scheme there.
DCHECK(!scheme_registries_locked)
<< "Trying to add a scheme after the lists have been locked.";
+}
- size_t scheme_len = strlen(new_scheme);
- if (scheme_len == 0)
- return;
-
+void DoAddScheme(const char* new_scheme, std::vector<std::string>* schemes) {
+ DoSchemeModificationPreamble();
+ DCHECK(schemes);
+ DCHECK(strlen(new_scheme) > 0);
DCHECK_EQ(base::ToLowerASCII(new_scheme), new_scheme);
- schemes->push_back(std::string(new_scheme));
+ DCHECK(std::find(schemes->begin(), schemes->end(), new_scheme) ==
+ schemes->end());
+ schemes->push_back(new_scheme);
}
void DoAddSchemeWithType(const char* new_scheme,
SchemeType type,
std::vector<SchemeWithType>* schemes) {
+ DoSchemeModificationPreamble();
DCHECK(schemes);
- // If this assert triggers, it means you've called Add*Scheme after
- // LockSchemeRegistries has been called (see the header file for
- // LockSchemeRegistries for more).
- //
- // This normally means you're trying to set up a new scheme too late in your
- // application's init process. Locate where your app does this initialization
- // and calls LockSchemeRegistries, and add your new scheme there.
- DCHECK(!scheme_registries_locked)
- << "Trying to add a scheme after the lists have been locked.";
-
- size_t scheme_len = strlen(new_scheme);
- if (scheme_len == 0)
- return;
-
+ DCHECK(strlen(new_scheme) > 0);
DCHECK_EQ(base::ToLowerASCII(new_scheme), new_scheme);
- // Duplicate the scheme into a new buffer and add it to the list of standard
- // schemes. This pointer will be leaked on shutdown.
- char* dup_scheme = new char[scheme_len + 1];
- ANNOTATE_LEAKING_OBJECT_PTR(dup_scheme);
- memcpy(dup_scheme, new_scheme, scheme_len + 1);
-
- SchemeWithType scheme_with_type;
- scheme_with_type.scheme = dup_scheme;
- scheme_with_type.type = type;
- schemes->push_back(scheme_with_type);
+ DCHECK(std::find_if(schemes->begin(), schemes->end(),
+ [&new_scheme](const SchemeWithType& scheme) {
+ return scheme.scheme == new_scheme;
+ }) == schemes->end());
+ schemes->push_back({new_scheme, type});
}
} // namespace
-void ResetForTests() {
- *GetSchemeRegistry() = SchemeRegistry();
-}
+void ClearSchemesForTests() {
+ DCHECK(!g_scheme_registries_used.load())
+ << "Schemes already used "
+ << "(use ScopedSchemeRegistryForTests to relax for tests).";
+ DCHECK(!scheme_registries_locked)
+ << "Schemes already locked "
+ << "(use ScopedSchemeRegistryForTests to relax for tests).";
+ *GetSchemeRegistryWithoutLocking() = SchemeRegistry();
+}
+
+class ScopedSchemeRegistryInternal {
+ public:
+ ScopedSchemeRegistryInternal()
+ : registry_(std::make_unique<SchemeRegistry>(
+ *GetSchemeRegistryWithoutLocking())) {
+ g_scheme_registries_used.store(false);
+ scheme_registries_locked = false;
+ }
+ ~ScopedSchemeRegistryInternal() {
+ *GetSchemeRegistryWithoutLocking() = *registry_;
+ g_scheme_registries_used.store(true);
+ scheme_registries_locked = true;
+ }
+
+ private:
+ std::unique_ptr<SchemeRegistry> registry_;
+};
+
+ScopedSchemeRegistryForTests::ScopedSchemeRegistryForTests()
+ : internal_(std::make_unique<ScopedSchemeRegistryInternal>()) {}
+
+ScopedSchemeRegistryForTests::~ScopedSchemeRegistryForTests() = default;
void EnableNonStandardSchemesForAndroidWebView() {
- GetSchemeRegistry()->allow_non_standard_schemes = true;
+ DoSchemeModificationPreamble();
+ GetSchemeRegistryWithoutLocking()->allow_non_standard_schemes = true;
}
bool AllowNonStandardSchemesForAndroidWebView() {
- return GetSchemeRegistry()->allow_non_standard_schemes;
+ return GetSchemeRegistry().allow_non_standard_schemes;
}
void AddStandardScheme(const char* new_scheme, SchemeType type) {
- DoAddSchemeWithType(new_scheme, type, &GetSchemeRegistry()->standard_schemes);
+ DoAddSchemeWithType(new_scheme, type,
+ &GetSchemeRegistryWithoutLocking()->standard_schemes);
}
void AddReferrerScheme(const char* new_scheme, SchemeType type) {
- DoAddSchemeWithType(new_scheme, type, &GetSchemeRegistry()->referrer_schemes);
+ DoAddSchemeWithType(new_scheme, type,
+ &GetSchemeRegistryWithoutLocking()->referrer_schemes);
}
void AddSecureScheme(const char* new_scheme) {
- DoAddScheme(new_scheme, &GetSchemeRegistry()->secure_schemes);
+ DoAddScheme(new_scheme, &GetSchemeRegistryWithoutLocking()->secure_schemes);
}
const std::vector<std::string>& GetSecureSchemes() {
- return GetSchemeRegistry()->secure_schemes;
+ return GetSchemeRegistry().secure_schemes;
}
void AddLocalScheme(const char* new_scheme) {
- DoAddScheme(new_scheme, &GetSchemeRegistry()->local_schemes);
+ DoAddScheme(new_scheme, &GetSchemeRegistryWithoutLocking()->local_schemes);
}
const std::vector<std::string>& GetLocalSchemes() {
- return GetSchemeRegistry()->local_schemes;
+ return GetSchemeRegistry().local_schemes;
}
void AddNoAccessScheme(const char* new_scheme) {
- DoAddScheme(new_scheme, &GetSchemeRegistry()->no_access_schemes);
+ DoAddScheme(new_scheme,
+ &GetSchemeRegistryWithoutLocking()->no_access_schemes);
}
const std::vector<std::string>& GetNoAccessSchemes() {
- return GetSchemeRegistry()->no_access_schemes;
+ return GetSchemeRegistry().no_access_schemes;
}
void AddCorsEnabledScheme(const char* new_scheme) {
- DoAddScheme(new_scheme, &GetSchemeRegistry()->cors_enabled_schemes);
+ DoAddScheme(new_scheme,
+ &GetSchemeRegistryWithoutLocking()->cors_enabled_schemes);
}
const std::vector<std::string>& GetCorsEnabledSchemes() {
- return GetSchemeRegistry()->cors_enabled_schemes;
+ return GetSchemeRegistry().cors_enabled_schemes;
}
void AddWebStorageScheme(const char* new_scheme) {
- DoAddScheme(new_scheme, &GetSchemeRegistry()->web_storage_schemes);
+ DoAddScheme(new_scheme,
+ &GetSchemeRegistryWithoutLocking()->web_storage_schemes);
}
const std::vector<std::string>& GetWebStorageSchemes() {
- return GetSchemeRegistry()->web_storage_schemes;
+ return GetSchemeRegistry().web_storage_schemes;
}
void AddCSPBypassingScheme(const char* new_scheme) {
- DoAddScheme(new_scheme, &GetSchemeRegistry()->csp_bypassing_schemes);
+ DoAddScheme(new_scheme,
+ &GetSchemeRegistryWithoutLocking()->csp_bypassing_schemes);
}
const std::vector<std::string>& GetCSPBypassingSchemes() {
- return GetSchemeRegistry()->csp_bypassing_schemes;
+ return GetSchemeRegistry().csp_bypassing_schemes;
}
void AddEmptyDocumentScheme(const char* new_scheme) {
- DoAddScheme(new_scheme, &GetSchemeRegistry()->empty_document_schemes);
+ DoAddScheme(new_scheme,
+ &GetSchemeRegistryWithoutLocking()->empty_document_schemes);
}
const std::vector<std::string>& GetEmptyDocumentSchemes() {
- return GetSchemeRegistry()->empty_document_schemes;
+ return GetSchemeRegistry().empty_document_schemes;
}
void LockSchemeRegistries() {
@@ -596,7 +646,7 @@ bool IsStandard(const base::char16* spec, const Component& scheme) {
bool IsReferrerScheme(const char* spec, const Component& scheme) {
SchemeType unused_scheme_type;
return DoIsInSchemes(spec, scheme, &unused_scheme_type,
- GetSchemeRegistry()->referrer_schemes);
+ GetSchemeRegistry().referrer_schemes);
}
bool FindAndCompareScheme(const char* str,
@@ -650,7 +700,7 @@ bool DomainIs(base::StringPiece canonical_host,
}
bool HostIsIPAddress(base::StringPiece host) {
- url::RawCanonOutputT<char, 128> ignored_output;
+ STACK_UNINITIALIZED url::RawCanonOutputT<char, 128> ignored_output;
url::CanonHostInfo host_info;
url::CanonicalizeIPAddress(host.data(), Component(0, host.length()),
&ignored_output, &host_info);
@@ -729,7 +779,7 @@ void DecodeURLEscapeSequences(const char* input,
int length,
DecodeURLMode mode,
CanonOutputW* output) {
- RawCanonOutputT<char> unescaped_chars;
+ STACK_UNINITIALIZED RawCanonOutputT<char> unescaped_chars;
for (int i = 0; i < length; i++) {
if (input[i] == '%') {
unsigned char ch;
diff --git a/chromium/url/url_util.h b/chromium/url/url_util.h
index 3ea449b1b60..d4f5e1798dd 100644
--- a/chromium/url/url_util.h
+++ b/chromium/url/url_util.h
@@ -5,6 +5,7 @@
#ifndef URL_URL_UTIL_H_
#define URL_URL_UTIL_H_
+#include <memory>
#include <string>
#include <vector>
@@ -19,8 +20,22 @@ namespace url {
// Init ------------------------------------------------------------------------
-// Resets all custom schemes to the default values. Not thread-safe.
-COMPONENT_EXPORT(URL) void ResetForTests();
+// Used for tests that need to reset schemes. Note that this can only be used
+// in conjunction with ScopedSchemeRegistryForTests.
+COMPONENT_EXPORT(URL) void ClearSchemesForTests();
+
+class ScopedSchemeRegistryInternal;
+
+// Stores the SchemeRegistry upon creation, allowing tests to modify a copy of
+// it, and restores the original SchemeRegistry when deleted.
+class COMPONENT_EXPORT(URL) ScopedSchemeRegistryForTests {
+ public:
+ ScopedSchemeRegistryForTests();
+ ~ScopedSchemeRegistryForTests();
+
+ private:
+ std::unique_ptr<ScopedSchemeRegistryInternal> internal_;
+};
// Schemes ---------------------------------------------------------------------
@@ -37,15 +52,9 @@ COMPONENT_EXPORT(URL) void EnableNonStandardSchemesForAndroidWebView();
// Whether or not SchemeHostPort and Origin allow non-standard schemes.
COMPONENT_EXPORT(URL) bool AllowNonStandardSchemesForAndroidWebView();
-// A pair for representing a standard scheme name and the SchemeType for it.
-struct COMPONENT_EXPORT(URL) SchemeWithType {
- const char* scheme;
- SchemeType type;
-};
-
// The following Add*Scheme method are not threadsafe and can not be called
// concurrently with any other url_util function. They will assert if the lists
-// of schemes have been locked (see LockSchemeRegistries).
+// of schemes have been locked (see LockSchemeRegistries), or used.
// Adds an application-defined scheme to the internal list of "standard-format"
// URL schemes. A standard-format scheme adheres to what RFC 3986 calls "generic
diff --git a/chromium/url/url_util_unittest.cc b/chromium/url/url_util_unittest.cc
index aed5fa899ac..ea4cd82aa7a 100644
--- a/chromium/url/url_util_unittest.cc
+++ b/chromium/url/url_util_unittest.cc
@@ -17,12 +17,11 @@ namespace url {
class URLUtilTest : public testing::Test {
public:
URLUtilTest() = default;
- ~URLUtilTest() override {
- // Reset any added schemes.
- ResetForTests();
- }
+ ~URLUtilTest() override = default;
private:
+ ScopedSchemeRegistryForTests scoped_registry_;
+
DISALLOW_COPY_AND_ASSIGN(URLUtilTest);
};
@@ -92,21 +91,24 @@ TEST_F(URLUtilTest, IsReferrerScheme) {
}
TEST_F(URLUtilTest, AddReferrerScheme) {
- const char kFooScheme[] = "foo";
+ static const char kFooScheme[] = "foo";
EXPECT_FALSE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme))));
+ url::ScopedSchemeRegistryForTests scoped_registry;
AddReferrerScheme(kFooScheme, url::SCHEME_WITH_HOST);
EXPECT_TRUE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme))));
}
TEST_F(URLUtilTest, ShutdownCleansUpSchemes) {
- const char kFooScheme[] = "foo";
+ static const char kFooScheme[] = "foo";
EXPECT_FALSE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme))));
- AddReferrerScheme(kFooScheme, url::SCHEME_WITH_HOST);
- EXPECT_TRUE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme))));
+ {
+ url::ScopedSchemeRegistryForTests scoped_registry;
+ AddReferrerScheme(kFooScheme, url::SCHEME_WITH_HOST);
+ EXPECT_TRUE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme))));
+ }
- ResetForTests();
EXPECT_FALSE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme))));
}