summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoWebExecutor.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoWebExecutor.java')
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoWebExecutor.java189
1 files changed, 189 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoWebExecutor.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoWebExecutor.java
new file mode 100644
index 0000000000..1546451056
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoWebExecutor.java
@@ -0,0 +1,189 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * vim: ts=4 sw=4 expandtab:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.geckoview;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Locale;
+import org.mozilla.gecko.GeckoThread;
+import org.mozilla.gecko.annotation.WrapForJNI;
+
+/**
+ * GeckoWebExecutor is responsible for fetching a {@link WebRequest} and delivering a {@link
+ * WebResponse} to the caller via {@link #fetch(WebRequest)}. Example:
+ *
+ * <pre>
+ * final GeckoWebExecutor executor = new GeckoWebExecutor();
+ *
+ * final GeckoResult&lt;WebResponse&gt; result = executor.fetch(
+ * new WebRequest.Builder("https://example.org/json")
+ * .header("Accept", "application/json")
+ * .build());
+ *
+ * result.then(response -&gt; {
+ * // Do something with response
+ * });
+ * </pre>
+ */
+@AnyThread
+public class GeckoWebExecutor {
+ // We don't use this right now because we access GeckoThread directly, but
+ // it's future-proofing for a world where we allow multiple GeckoRuntimes.
+ private final GeckoRuntime mRuntime;
+
+ @WrapForJNI(dispatchTo = "gecko", stubName = "Fetch")
+ private static native void nativeFetch(
+ WebRequest request, int flags, GeckoResult<WebResponse> result);
+
+ @WrapForJNI(dispatchTo = "gecko", stubName = "Resolve")
+ private static native void nativeResolve(String host, GeckoResult<InetAddress[]> result);
+
+ @WrapForJNI(calledFrom = "gecko", exceptionMode = "nsresult")
+ private static ByteBuffer createByteBuffer(final int capacity) {
+ return ByteBuffer.allocateDirect(capacity);
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ FETCH_FLAGS_NONE,
+ FETCH_FLAGS_ANONYMOUS,
+ FETCH_FLAGS_NO_REDIRECTS,
+ FETCH_FLAGS_PRIVATE,
+ FETCH_FLAGS_STREAM_FAILURE_TEST,
+ })
+ public @interface FetchFlags {}
+
+ /** No special treatment. */
+ public static final int FETCH_FLAGS_NONE = 0;
+
+ /** Don't send cookies or other user data along with the request. */
+ @WrapForJNI public static final int FETCH_FLAGS_ANONYMOUS = 1;
+
+ /** Don't automatically follow redirects. */
+ @WrapForJNI public static final int FETCH_FLAGS_NO_REDIRECTS = 1 << 1;
+
+ // There was supposed to be another flag, which we then decided not to implement.
+ // That's the reason there's no value 1 << 2, and it can absolutely be used :)
+
+ /** Associates this download with the current private browsing session */
+ @WrapForJNI public static final int FETCH_FLAGS_PRIVATE = 1 << 3;
+
+ /** This flag causes a read error in the {@link WebResponse} body. Useful for testing. */
+ @WrapForJNI public static final int FETCH_FLAGS_STREAM_FAILURE_TEST = 1 << 10;
+
+ /**
+ * Create a new GeckoWebExecutor instance.
+ *
+ * @param runtime A GeckoRuntime instance
+ */
+ public GeckoWebExecutor(final @NonNull GeckoRuntime runtime) {
+ mRuntime = runtime;
+ }
+
+ /**
+ * Send the given {@link WebRequest}.
+ *
+ * @param request A {@link WebRequest} instance
+ * @return A {@link GeckoResult} which will be completed with a {@link WebResponse}. If the
+ * request fails to complete, the {@link GeckoResult} will be completed exceptionally with a
+ * {@link WebRequestError}.
+ * @throws IllegalArgumentException if request is null or otherwise unusable.
+ */
+ public @NonNull GeckoResult<WebResponse> fetch(final @NonNull WebRequest request) {
+ return fetch(request, FETCH_FLAGS_NONE);
+ }
+
+ /**
+ * Send the given {@link WebRequest} with specified flags.
+ *
+ * @param request A {@link WebRequest} instance
+ * @param flags The specified flags. One or more of the {@link #FETCH_FLAGS_NONE FETCH_*} flags.
+ * @return A {@link GeckoResult} which will be completed with a {@link WebResponse}. If the
+ * request fails to complete, the {@link GeckoResult} will be completed exceptionally with a
+ * {@link WebRequestError}.
+ * @throws IllegalArgumentException if request is null or otherwise unusable.
+ */
+ public @NonNull GeckoResult<WebResponse> fetch(
+ final @NonNull WebRequest request, final @FetchFlags int flags) {
+ if (request.body != null && !request.body.isDirect()) {
+ throw new IllegalArgumentException("Request body must be a direct ByteBuffer");
+ }
+
+ if (request.cacheMode < WebRequest.CACHE_MODE_FIRST
+ || request.cacheMode > WebRequest.CACHE_MODE_LAST) {
+ throw new IllegalArgumentException("Unknown cache mode");
+ }
+
+ final String uri = request.uri.toLowerCase(Locale.ROOT);
+ // We don't need to fully validate the URI here, just a sanity check
+ if (!uri.startsWith("http") && !uri.startsWith("blob")) {
+ throw new IllegalArgumentException(
+ "Unsupported URI scheme: " + (uri.length() > 10 ? uri.substring(0, 10) : uri));
+ }
+
+ final GeckoResult<WebResponse> result = new GeckoResult<>();
+
+ if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
+ nativeFetch(request, flags, result);
+ } else {
+ GeckoThread.queueNativeCallUntil(
+ GeckoThread.State.PROFILE_READY,
+ this,
+ "nativeFetch",
+ WebRequest.class,
+ request,
+ flags,
+ GeckoResult.class,
+ result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Resolves the specified host name.
+ *
+ * @param host An Internet host name, e.g. mozilla.org.
+ * @return A {@link GeckoResult} which will be fulfilled with a {@link List} of {@link
+ * InetAddress}. In case of failure, the {@link GeckoResult} will be completed exceptionally
+ * with a {@link java.net.UnknownHostException}.
+ */
+ public @NonNull GeckoResult<InetAddress[]> resolve(final @NonNull String host) {
+ final GeckoResult<InetAddress[]> result = new GeckoResult<>();
+
+ if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
+ nativeResolve(host, result);
+ } else {
+ GeckoThread.queueNativeCallUntil(
+ GeckoThread.State.PROFILE_READY,
+ this,
+ "nativeResolve",
+ String.class,
+ host,
+ GeckoResult.class,
+ result);
+ }
+ return result;
+ }
+
+ /**
+ * This causes a speculative connection to be made to the host in the specified URI. This is
+ * useful if an app thinks it may be making a request to that host in the near future. If no
+ * request is made, the connection will be cleaned up after an unspecified amount of time.
+ *
+ * @param uri A URI String.
+ */
+ public void speculativeConnect(final @NonNull String uri) {
+ GeckoThread.speculativeConnect(uri);
+ }
+}