Bug 1769547 - Do not MOZ_CRASH() on missing process r=nika
[gecko.git] / widget / android / WebExecutorSupport.cpp
blob21cda516da08c2987ef6255dce128721a2668cd6
1 /* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <algorithm>
8 #include "GeckoViewStreamListener.h"
9 #include "InetAddress.h" // for java::sdk::InetAddress and java::sdk::UnknownHostException
10 #include "ReferrerInfo.h"
11 #include "WebExecutorSupport.h"
13 #include "nsIAsyncVerifyRedirectCallback.h"
14 #include "nsIHttpChannel.h"
15 #include "nsIHttpChannelInternal.h"
16 #include "nsIHttpHeaderVisitor.h"
17 #include "nsIDNSService.h"
18 #include "nsIDNSListener.h"
19 #include "nsIDNSRecord.h"
20 #include "nsINSSErrorsService.h"
21 #include "nsNetUtil.h" // for NS_NewURI, NS_NewChannel, NS_NewStreamLoader
22 #include "nsIPrivateBrowsingChannel.h"
23 #include "nsIUploadChannel2.h"
24 #include "nsIX509Cert.h"
26 #include "mozilla/Preferences.h"
27 #include "mozilla/net/CookieJarSettings.h"
28 #include "mozilla/net/DNS.h" // for NetAddr
29 #include "mozilla/java/GeckoWebExecutorWrappers.h"
30 #include "mozilla/java/WebMessageWrappers.h"
31 #include "mozilla/java/WebRequestErrorWrappers.h"
32 #include "mozilla/java/WebResponseWrappers.h"
34 namespace mozilla {
35 using namespace net;
37 namespace widget {
39 static void CompleteWithError(java::GeckoResult::Param aResult,
40 nsresult aStatus, nsIChannel* aChannel) {
41 nsCOMPtr<nsINSSErrorsService> errSvc =
42 do_GetService("@mozilla.org/nss_errors_service;1");
43 MOZ_ASSERT(errSvc);
45 uint32_t errorClass;
46 nsresult rv = errSvc->GetErrorClass(aStatus, &errorClass);
47 if (NS_FAILED(rv)) {
48 errorClass = 0;
51 jni::ByteArray::LocalRef certBytes;
52 if (aChannel) {
53 std::tie(certBytes, std::ignore) =
54 GeckoViewStreamListener::CertificateFromChannel(aChannel);
57 java::WebRequestError::LocalRef error = java::WebRequestError::FromGeckoError(
58 int64_t(aStatus), NS_ERROR_GET_MODULE(aStatus), errorClass, certBytes);
60 aResult->CompleteExceptionally(error.Cast<jni::Throwable>());
63 static void CompleteWithError(java::GeckoResult::Param aResult,
64 nsresult aStatus) {
65 CompleteWithError(aResult, aStatus, nullptr);
68 class ByteBufferStream final : public nsIInputStream {
69 public:
70 NS_DECL_THREADSAFE_ISUPPORTS
72 explicit ByteBufferStream(jni::ByteBuffer::Param buffer)
73 : mBuffer(buffer), mPosition(0), mClosed(false) {
74 MOZ_ASSERT(mBuffer);
75 MOZ_ASSERT(mBuffer->Address());
78 NS_IMETHOD
79 Close() override {
80 mClosed = true;
81 return NS_OK;
84 NS_IMETHOD
85 Available(uint64_t* aResult) override {
86 if (mClosed) {
87 return NS_BASE_STREAM_CLOSED;
90 *aResult = (mBuffer->Capacity() - mPosition);
91 return NS_OK;
94 NS_IMETHOD
95 Read(char* aBuf, uint32_t aCount, uint32_t* aCountRead) override {
96 if (mClosed) {
97 return NS_BASE_STREAM_CLOSED;
100 *aCountRead = uint32_t(
101 std::min(uint64_t(mBuffer->Capacity() - mPosition), uint64_t(aCount)));
103 if (*aCountRead > 0) {
104 memcpy(aBuf, (char*)mBuffer->Address() + mPosition, *aCountRead);
105 mPosition += *aCountRead;
108 return NS_OK;
111 NS_IMETHOD
112 ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount,
113 uint32_t* aResult) override {
114 return NS_ERROR_NOT_IMPLEMENTED;
117 NS_IMETHOD
118 IsNonBlocking(bool* aResult) override {
119 *aResult = false;
120 return NS_OK;
123 protected:
124 virtual ~ByteBufferStream() {}
126 const jni::ByteBuffer::GlobalRef mBuffer;
127 uint64_t mPosition;
128 bool mClosed;
131 NS_IMPL_ISUPPORTS(ByteBufferStream, nsIInputStream)
133 class LoaderListener final : public GeckoViewStreamListener {
134 public:
135 explicit LoaderListener(java::GeckoResult::Param aResult,
136 bool aAllowRedirects, bool testStreamFailure)
137 : GeckoViewStreamListener(),
138 mResult(aResult),
139 mTestStreamFailure(testStreamFailure),
140 mAllowRedirects(aAllowRedirects) {
141 MOZ_ASSERT(mResult);
144 NS_IMETHOD
145 OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
146 uint64_t aOffset, uint32_t aCount) override {
147 MOZ_ASSERT(mStream);
149 if (mTestStreamFailure) {
150 return NS_ERROR_UNEXPECTED;
153 // We only need this for the ReadSegments call, the value is unused.
154 uint32_t countRead;
155 nsresult rv =
156 aInputStream->ReadSegments(WriteSegment, this, aCount, &countRead);
157 NS_ENSURE_SUCCESS(rv, rv);
158 return rv;
161 NS_IMETHOD
162 AsyncOnChannelRedirect(nsIChannel* aOldChannel, nsIChannel* aNewChannel,
163 uint32_t flags,
164 nsIAsyncVerifyRedirectCallback* callback) override {
165 if (!mAllowRedirects) {
166 return NS_ERROR_ABORT;
169 callback->OnRedirectVerifyCallback(NS_OK);
170 return NS_OK;
173 void SendWebResponse(java::WebResponse::Param aResponse) override {
174 mResult->Complete(aResponse);
177 void CompleteWithError(nsresult aStatus, nsIChannel* aChannel) override {
178 ::CompleteWithError(mResult, aStatus, aChannel);
181 virtual ~LoaderListener() {}
183 const java::GeckoResult::GlobalRef mResult;
184 const bool mTestStreamFailure;
185 bool mAllowRedirects;
188 class DNSListener final : public nsIDNSListener {
189 public:
190 NS_DECL_THREADSAFE_ISUPPORTS
192 DNSListener(const nsCString& aHost, java::GeckoResult::Param aResult)
193 : mHost(aHost), mResult(aResult) {}
195 NS_IMETHOD
196 OnLookupComplete(nsICancelable* aRequest, nsIDNSRecord* aRecord,
197 nsresult aStatus) override {
198 if (NS_FAILED(aStatus)) {
199 CompleteUnknownHostError();
200 return NS_OK;
203 nsresult rv = CompleteWithRecord(aRecord);
204 if (NS_FAILED(rv)) {
205 CompleteUnknownHostError();
206 return NS_OK;
209 return NS_OK;
212 void CompleteUnknownHostError() {
213 java::sdk::UnknownHostException::LocalRef error =
214 java::sdk::UnknownHostException::New();
215 mResult->CompleteExceptionally(error.Cast<jni::Throwable>());
218 private:
219 nsresult CompleteWithRecord(nsIDNSRecord* aRecord) {
220 nsTArray<NetAddr> addrs;
221 nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord);
222 if (!rec) {
223 return NS_ERROR_UNEXPECTED;
225 nsresult rv = rec->GetAddresses(addrs);
226 NS_ENSURE_SUCCESS(rv, rv);
228 jni::ByteArray::LocalRef bytes;
229 auto objects =
230 jni::ObjectArray::New<java::sdk::InetAddress>(addrs.Length());
231 for (size_t i = 0; i < addrs.Length(); i++) {
232 const auto& addr = addrs[i];
233 if (addr.raw.family == AF_INET) {
234 bytes = jni::ByteArray::New(
235 reinterpret_cast<const int8_t*>(&addr.inet.ip), 4);
236 } else if (addr.raw.family == AF_INET6) {
237 bytes = jni::ByteArray::New(
238 reinterpret_cast<const int8_t*>(&addr.inet6.ip), 16);
239 } else {
240 // We don't handle this, skip it.
241 continue;
244 objects->SetElement(i,
245 java::sdk::InetAddress::GetByAddress(mHost, bytes));
248 mResult->Complete(objects);
249 return NS_OK;
252 virtual ~DNSListener() {}
254 const nsCString mHost;
255 const java::GeckoResult::GlobalRef mResult;
258 NS_IMPL_ISUPPORTS(DNSListener, nsIDNSListener)
260 static nsresult ConvertCacheMode(int32_t mode, int32_t& result) {
261 switch (mode) {
262 case java::WebRequest::CACHE_MODE_DEFAULT:
263 result = nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT;
264 break;
265 case java::WebRequest::CACHE_MODE_NO_STORE:
266 result = nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE;
267 break;
268 case java::WebRequest::CACHE_MODE_RELOAD:
269 result = nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD;
270 break;
271 case java::WebRequest::CACHE_MODE_NO_CACHE:
272 result = nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE;
273 break;
274 case java::WebRequest::CACHE_MODE_FORCE_CACHE:
275 result = nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE;
276 break;
277 case java::WebRequest::CACHE_MODE_ONLY_IF_CACHED:
278 result = nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED;
279 break;
280 default:
281 return NS_ERROR_UNEXPECTED;
284 return NS_OK;
287 static nsresult SetupHttpChannel(nsIHttpChannel* aHttpChannel,
288 nsIChannel* aChannel,
289 java::WebRequest::Param aRequest) {
290 const auto req = java::WebRequest::LocalRef(aRequest);
291 const auto reqBase = java::WebMessage::LocalRef(req.Cast<java::WebMessage>());
293 // Method
294 nsresult rv = aHttpChannel->SetRequestMethod(aRequest->Method()->ToCString());
295 NS_ENSURE_SUCCESS(rv, rv);
297 // Headers
298 const auto keys = reqBase->GetHeaderKeys();
299 const auto values = reqBase->GetHeaderValues();
300 nsCString contentType;
301 for (size_t i = 0; i < keys->Length(); i++) {
302 const auto key = jni::String::LocalRef(keys->GetElement(i))->ToCString();
303 const auto value =
304 jni::String::LocalRef(values->GetElement(i))->ToCString();
306 if (key.LowerCaseEqualsASCII("content-type")) {
307 contentType = value;
310 // We clobber any duplicate keys here because we've already merged them
311 // in the upstream WebRequest.
312 rv = aHttpChannel->SetRequestHeader(key, value, false /* merge */);
313 NS_ENSURE_SUCCESS(rv, rv);
316 // Body
317 const auto body = req->Body();
318 if (body) {
319 nsCOMPtr<nsIInputStream> stream = new ByteBufferStream(body);
321 nsCOMPtr<nsIUploadChannel2> uploadChannel(do_QueryInterface(aChannel, &rv));
322 NS_ENSURE_SUCCESS(rv, rv);
324 rv = uploadChannel->ExplicitSetUploadStream(
325 stream, contentType, -1, aRequest->Method()->ToCString(), false);
326 NS_ENSURE_SUCCESS(rv, rv);
329 // Referrer
330 RefPtr<nsIURI> referrerUri;
331 const auto referrer = req->Referrer();
332 if (referrer) {
333 rv = NS_NewURI(getter_AddRefs(referrerUri), referrer->ToString());
334 NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI);
337 nsCOMPtr<nsIReferrerInfo> referrerInfo = new dom::ReferrerInfo(referrerUri);
338 rv = aHttpChannel->SetReferrerInfoWithoutClone(referrerInfo);
339 NS_ENSURE_SUCCESS(rv, rv);
341 // Cache mode
342 nsCOMPtr<nsIHttpChannelInternal> internalChannel(
343 do_QueryInterface(aChannel, &rv));
344 NS_ENSURE_SUCCESS(rv, rv);
346 int32_t cacheMode;
347 rv = ConvertCacheMode(req->CacheMode(), cacheMode);
348 NS_ENSURE_SUCCESS(rv, rv);
350 rv = internalChannel->SetFetchCacheMode(cacheMode);
351 NS_ENSURE_SUCCESS(rv, rv);
353 if (req->BeConservative()) {
354 rv = internalChannel->SetBeConservative(true);
355 NS_ENSURE_SUCCESS(rv, rv);
358 // We don't have any UI
359 rv = internalChannel->SetBlockAuthPrompt(true);
360 NS_ENSURE_SUCCESS(rv, rv);
362 return NS_OK;
365 nsresult WebExecutorSupport::CreateStreamLoader(
366 java::WebRequest::Param aRequest, int32_t aFlags,
367 java::GeckoResult::Param aResult) {
368 const auto req = java::WebRequest::LocalRef(aRequest);
369 const auto reqBase = java::WebMessage::LocalRef(req.Cast<java::WebMessage>());
371 nsCOMPtr<nsIURI> uri;
372 nsresult rv = NS_NewURI(getter_AddRefs(uri), reqBase->Uri()->ToString());
373 NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI);
375 nsCOMPtr<nsIChannel> channel;
376 rv = NS_NewChannel(getter_AddRefs(channel), uri,
377 nsContentUtils::GetSystemPrincipal(),
378 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
379 nsIContentPolicy::TYPE_OTHER);
380 NS_ENSURE_SUCCESS(rv, rv);
382 if (aFlags & java::GeckoWebExecutor::FETCH_FLAGS_ANONYMOUS) {
383 channel->SetLoadFlags(nsIRequest::LOAD_ANONYMOUS);
386 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
387 if (aFlags & java::GeckoWebExecutor::FETCH_FLAGS_PRIVATE) {
388 nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
389 NS_ENSURE_TRUE(pbChannel, NS_ERROR_FAILURE);
390 pbChannel->SetPrivate(true);
391 cookieJarSettings = CookieJarSettings::Create(CookieJarSettings::ePrivate);
392 } else {
393 cookieJarSettings = CookieJarSettings::Create(CookieJarSettings::eRegular);
395 MOZ_ASSERT(cookieJarSettings);
397 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
398 loadInfo->SetCookieJarSettings(cookieJarSettings);
400 // setup http/https specific things
401 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel, &rv));
402 if (httpChannel) {
403 rv = SetupHttpChannel(httpChannel, channel, aRequest);
404 NS_ENSURE_SUCCESS(rv, rv);
407 // set up the listener
408 const bool allowRedirects =
409 !(aFlags & java::GeckoWebExecutor::FETCH_FLAGS_NO_REDIRECTS);
410 const bool testStreamFailure =
411 (aFlags & java::GeckoWebExecutor::FETCH_FLAGS_STREAM_FAILURE_TEST);
413 RefPtr<LoaderListener> listener =
414 new LoaderListener(aResult, allowRedirects, testStreamFailure);
416 rv = channel->SetNotificationCallbacks(listener);
417 NS_ENSURE_SUCCESS(rv, rv);
419 // Finally, open the channel
420 return channel->AsyncOpen(listener);
423 void WebExecutorSupport::Fetch(jni::Object::Param aRequest, int32_t aFlags,
424 jni::Object::Param aResult) {
425 const auto request = java::WebRequest::LocalRef(aRequest);
426 auto result = java::GeckoResult::LocalRef(aResult);
428 nsresult rv = CreateStreamLoader(request, aFlags, result);
429 if (NS_FAILED(rv)) {
430 CompleteWithError(result, rv);
434 static nsresult ResolveHost(nsCString& host, java::GeckoResult::Param result) {
435 nsresult rv;
436 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
437 NS_ENSURE_SUCCESS(rv, rv);
439 nsCOMPtr<nsICancelable> cancelable;
440 RefPtr<DNSListener> listener = new DNSListener(host, result);
441 rv = dns->AsyncResolveNative(host, nsIDNSService::RESOLVE_TYPE_DEFAULT, 0,
442 nullptr, listener, nullptr /* aListenerTarget */,
443 OriginAttributes(), getter_AddRefs(cancelable));
444 return rv;
447 void WebExecutorSupport::Resolve(jni::String::Param aUri,
448 jni::Object::Param aResult) {
449 auto result = java::GeckoResult::LocalRef(aResult);
451 nsCString uri = aUri->ToCString();
452 nsresult rv = ResolveHost(uri, result);
453 if (NS_FAILED(rv)) {
454 java::sdk::UnknownHostException::LocalRef error =
455 java::sdk::UnknownHostException::New();
456 result->CompleteExceptionally(error.Cast<jni::Throwable>());
460 } // namespace widget
461 } // namespace mozilla