1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "url/android/gurl_android.h"
6
7 #include <jni.h>
8
9 #include <cstdint>
10 #include <string>
11 #include <vector>
12
13 #include "base/android/jni_android.h"
14 #include "base/android/jni_string.h"
15 #include "base/functional/bind.h"
16 #include "base/functional/callback.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/strings/string_util.h"
19 #include "url/android/parsed_android.h"
20 #include "url/third_party/mozilla/url_parse.h"
21 #include "url/url_jni_headers/GURL_jni.h"
22
23 using base::android::AttachCurrentThread;
24 using base::android::JavaParamRef;
25 using base::android::JavaRef;
26 using base::android::ScopedJavaLocalRef;
27
28 namespace url {
29
30 namespace {
31
FromJString(JNIEnv * env,const JavaRef<jstring> & uri)32 static GURL FromJString(JNIEnv* env, const JavaRef<jstring>& uri) {
33 if (!uri)
34 return GURL();
35 return GURL(base::android::ConvertJavaStringToUTF16(env, uri));
36 }
37
FromJavaGURL(JNIEnv * env,const JavaRef<jstring> & j_spec,bool is_valid,jlong parsed_ptr)38 static std::unique_ptr<GURL> FromJavaGURL(JNIEnv* env,
39 const JavaRef<jstring>& j_spec,
40 bool is_valid,
41 jlong parsed_ptr) {
42 Parsed* parsed = reinterpret_cast<Parsed*>(parsed_ptr);
43 const std::string& spec = ConvertJavaStringToUTF8(env, j_spec);
44 std::unique_ptr<GURL> gurl =
45 std::make_unique<GURL>(spec.data(), parsed->Length(), *parsed, is_valid);
46 delete parsed;
47 return gurl;
48 }
49
InitFromGURL(JNIEnv * env,const GURL & gurl,const JavaRef<jobject> & target)50 static void InitFromGURL(JNIEnv* env,
51 const GURL& gurl,
52 const JavaRef<jobject>& target) {
53 // Ensure that the spec only contains US-ASCII (single-byte characters) or the
54 // parsed indices will be wrong as the indices are in bytes while Java Strings
55 // are always 16-bit.
56 DCHECK(base::IsStringASCII(gurl.possibly_invalid_spec()));
57 Java_GURL_init(
58 env, target,
59 base::android::ConvertUTF8ToJavaString(env, gurl.possibly_invalid_spec()),
60 gurl.is_valid(),
61 ParsedAndroid::InitFromParsed(env,
62 gurl.parsed_for_possibly_invalid_spec()));
63 }
64
65 // As |GetArrayLength| makes no guarantees about the returned value (e.g., it
66 // may be -1 if |array| is not a valid Java array), provide a safe wrapper
67 // that always returns a valid, non-negative size.
68 template <typename JavaArrayType>
SafeGetArrayLength(JNIEnv * env,const JavaRef<JavaArrayType> & jarray)69 size_t SafeGetArrayLength(JNIEnv* env, const JavaRef<JavaArrayType>& jarray) {
70 DCHECK(jarray);
71 jsize length = env->GetArrayLength(jarray.obj());
72 DCHECK_GE(length, 0) << "Invalid array length: " << length;
73 return static_cast<size_t>(std::max(0, length));
74 }
75
76 } // namespace
77
78 // static
ToNativeGURL(JNIEnv * env,const base::android::JavaRef<jobject> & j_gurl)79 std::unique_ptr<GURL> GURLAndroid::ToNativeGURL(
80 JNIEnv* env,
81 const base::android::JavaRef<jobject>& j_gurl) {
82 return base::WrapUnique<GURL>(
83 reinterpret_cast<GURL*>(Java_GURL_toNativeGURL(env, j_gurl)));
84 }
85
JavaGURLArrayToGURLVector(JNIEnv * env,const base::android::JavaRef<jobjectArray> & array,std::vector<GURL> * out)86 void GURLAndroid::JavaGURLArrayToGURLVector(
87 JNIEnv* env,
88 const base::android::JavaRef<jobjectArray>& array,
89 std::vector<GURL>* out) {
90 DCHECK(out);
91 DCHECK(out->empty());
92 if (!array)
93 return;
94 size_t len = SafeGetArrayLength(env, array);
95 for (size_t i = 0; i < len; ++i) {
96 ScopedJavaLocalRef<jobject> j_gurl(
97 env, static_cast<jobject>(env->GetObjectArrayElement(array.obj(), i)));
98 out->emplace_back(
99 *reinterpret_cast<GURL*>(Java_GURL_toNativeGURL(env, j_gurl)));
100 }
101 }
102
103 // static
FromNativeGURL(JNIEnv * env,const GURL & gurl)104 ScopedJavaLocalRef<jobject> GURLAndroid::FromNativeGURL(JNIEnv* env,
105 const GURL& gurl) {
106 ScopedJavaLocalRef<jobject> j_gurl = Java_GURL_Constructor(env);
107 InitFromGURL(env, gurl, j_gurl);
108 return j_gurl;
109 }
110
111 // static
EmptyGURL(JNIEnv * env)112 ScopedJavaLocalRef<jobject> GURLAndroid::EmptyGURL(JNIEnv* env) {
113 return Java_GURL_emptyGURL(env);
114 }
115
116 // static
ToJavaArrayOfGURLs(JNIEnv * env,base::span<ScopedJavaLocalRef<jobject>> v)117 ScopedJavaLocalRef<jobjectArray> GURLAndroid::ToJavaArrayOfGURLs(
118 JNIEnv* env,
119 base::span<ScopedJavaLocalRef<jobject>> v) {
120 jclass clazz = org_chromium_url_GURL_clazz(env);
121 DCHECK(clazz);
122 jobjectArray joa = env->NewObjectArray(v.size(), clazz, nullptr);
123 base::android::CheckException(env);
124
125 for (size_t i = 0; i < v.size(); ++i) {
126 env->SetObjectArrayElement(joa, i, v[i].obj());
127 }
128 return ScopedJavaLocalRef<jobjectArray>(env, joa);
129 }
130
JNI_GURL_GetOrigin(JNIEnv * env,const JavaParamRef<jstring> & j_spec,jboolean is_valid,jlong parsed_ptr,const JavaParamRef<jobject> & target)131 static void JNI_GURL_GetOrigin(JNIEnv* env,
132 const JavaParamRef<jstring>& j_spec,
133 jboolean is_valid,
134 jlong parsed_ptr,
135 const JavaParamRef<jobject>& target) {
136 std::unique_ptr<GURL> gurl = FromJavaGURL(env, j_spec, is_valid, parsed_ptr);
137 InitFromGURL(env, gurl->DeprecatedGetOriginAsURL(), target);
138 }
139
JNI_GURL_DomainIs(JNIEnv * env,const JavaParamRef<jstring> & j_spec,jboolean is_valid,jlong parsed_ptr,const JavaParamRef<jstring> & j_domain)140 static jboolean JNI_GURL_DomainIs(JNIEnv* env,
141 const JavaParamRef<jstring>& j_spec,
142 jboolean is_valid,
143 jlong parsed_ptr,
144 const JavaParamRef<jstring>& j_domain) {
145 std::unique_ptr<GURL> gurl = FromJavaGURL(env, j_spec, is_valid, parsed_ptr);
146 const std::string& domain = ConvertJavaStringToUTF8(env, j_domain);
147 return gurl->DomainIs(domain);
148 }
149
JNI_GURL_Init(JNIEnv * env,const base::android::JavaParamRef<jstring> & uri,const base::android::JavaParamRef<jobject> & target)150 static void JNI_GURL_Init(JNIEnv* env,
151 const base::android::JavaParamRef<jstring>& uri,
152 const base::android::JavaParamRef<jobject>& target) {
153 const GURL& gurl = FromJString(env, uri);
154 InitFromGURL(env, gurl, target);
155 }
156
JNI_GURL_CreateNative(JNIEnv * env,const JavaParamRef<jstring> & j_spec,jboolean is_valid,jlong parsed_ptr)157 static jlong JNI_GURL_CreateNative(JNIEnv* env,
158 const JavaParamRef<jstring>& j_spec,
159 jboolean is_valid,
160 jlong parsed_ptr) {
161 return reinterpret_cast<intptr_t>(
162 FromJavaGURL(env, j_spec, is_valid, parsed_ptr).release());
163 }
164
JNI_GURL_ReplaceComponents(JNIEnv * env,const JavaParamRef<jstring> & j_spec,jboolean is_valid,jlong parsed_ptr,const JavaParamRef<jstring> & j_username_replacement,jboolean clear_username,const JavaParamRef<jstring> & j_password_replacement,jboolean clear_password,const JavaParamRef<jobject> & j_result)165 static void JNI_GURL_ReplaceComponents(
166 JNIEnv* env,
167 const JavaParamRef<jstring>& j_spec,
168 jboolean is_valid,
169 jlong parsed_ptr,
170 const JavaParamRef<jstring>& j_username_replacement,
171 jboolean clear_username,
172 const JavaParamRef<jstring>& j_password_replacement,
173 jboolean clear_password,
174 const JavaParamRef<jobject>& j_result) {
175 GURL::Replacements replacements;
176
177 // Replacement strings must remain in scope for ReplaceComponents().
178 std::string username;
179 std::string password;
180
181 if (clear_username) {
182 replacements.ClearUsername();
183 } else if (j_username_replacement) {
184 username = ConvertJavaStringToUTF8(env, j_username_replacement);
185 replacements.SetUsernameStr(username);
186 }
187
188 if (clear_password) {
189 replacements.ClearPassword();
190 } else if (j_password_replacement) {
191 password = ConvertJavaStringToUTF8(env, j_password_replacement);
192 replacements.SetPasswordStr(password);
193 }
194
195 std::unique_ptr<GURL> original =
196 FromJavaGURL(env, j_spec, is_valid, parsed_ptr);
197 InitFromGURL(env, original->ReplaceComponents(replacements), j_result);
198 }
199
200 } // namespace url
201