1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.webkit;
18 
19 import android.webkit.WebSettings;
20 
21 import androidx.annotation.RequiresFeature;
22 
23 import org.jspecify.annotations.NonNull;
24 import org.jspecify.annotations.Nullable;
25 
26 import java.util.HashMap;
27 import java.util.Map;
28 
29 /**
30  * Parameters for customizing the prefetch. Use the {@link Builder} to
31  * construct.
32  */
33 @RequiresFeature(name = WebViewFeature.PROFILE_URL_PREFETCH,
34         enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
35 @Profile.ExperimentalUrlPrefetch
36 public final class SpeculativeLoadingParameters {
37     private final @NonNull Map<String, String> mAdditionalHeaders;
38     private final @Nullable NoVarySearchHeader mExpectedNoVarySearchHeader;
39     private final boolean mIsJavaScriptEnabled;
40 
SpeculativeLoadingParameters(@onNull Map<String, String> additionalHeaders, @Nullable NoVarySearchHeader noVarySearchHeader, boolean isJavaScriptEnabled)41     private SpeculativeLoadingParameters(@NonNull Map<String, String> additionalHeaders,
42             @Nullable NoVarySearchHeader noVarySearchHeader, boolean isJavaScriptEnabled) {
43         mAdditionalHeaders = additionalHeaders;
44         mExpectedNoVarySearchHeader = noVarySearchHeader;
45         mIsJavaScriptEnabled = isJavaScriptEnabled;
46     }
47 
48     /**
49      * @return The map of the additional headers built using {@link Builder}.
50      */
getAdditionalHeaders()51     public @NonNull Map<String, String> getAdditionalHeaders() {
52         return mAdditionalHeaders;
53     }
54 
55     /**
56      * @return The noVarySearch model built using {@link Builder}.
57      */
getExpectedNoVarySearchData()58     public @Nullable NoVarySearchHeader getExpectedNoVarySearchData() {
59         return mExpectedNoVarySearchHeader;
60     }
61 
62     /**
63      * @return The javascript enabled setting built using {@link Builder}.
64      */
isJavaScriptEnabled()65     public boolean isJavaScriptEnabled() {
66         return mIsJavaScriptEnabled;
67     }
68 
69     /**
70      * A builder class to use to construct the {@link SpeculativeLoadingParameters}.
71      */
72     public static final class Builder {
73         private final @NonNull Map<String, String> mAdditionalHeaders;
74         private @Nullable NoVarySearchHeader mExpectedNoVarySearchHeader;
75         private boolean mIsJavaScriptEnabled;
76 
Builder()77         public Builder() {
78             mAdditionalHeaders = new HashMap<>();
79             mExpectedNoVarySearchHeader = null;
80             mIsJavaScriptEnabled = false;
81         }
82 
83         /**
84          * Use to finish building the PrefetchParams
85          *
86          * @return built PrefetchParams object.
87          */
88         @RequiresFeature(name = WebViewFeature.PROFILE_URL_PREFETCH,
89                 enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
90         @Profile.ExperimentalUrlPrefetch
build()91         public @NonNull SpeculativeLoadingParameters build() {
92             return new SpeculativeLoadingParameters(mAdditionalHeaders, mExpectedNoVarySearchHeader,
93                     mIsJavaScriptEnabled);
94         }
95 
96         /**
97          * Sets the header value for the given key. If called multiple times
98          * for the same key, the latest value will be used.
99          * <p>
100          * Header keys must be RFC 2616-compliant.
101          * <p>
102          * The logic for handling additional header isn't guaranteed to match the
103          * {@link android.webkit.WebView#loadUrl(String, Map)}'s logic and is subject to change
104          * in the future.
105          */
106         @Profile.ExperimentalUrlPrefetch
addAdditionalHeader(@onNull String key, @NonNull String value)107         public @NonNull Builder addAdditionalHeader(@NonNull String key, @NonNull String value) {
108             mAdditionalHeaders.put(key, value);
109             return this;
110         }
111 
112         /**
113          * Sets multiple headers at once. The headers passed in here will
114          * be merged with any that have been previously set (duplicate keys
115          * will be overridden).
116          * <p>
117          * Header keys must be RFC 2616-compliant.
118          */
119         @Profile.ExperimentalUrlPrefetch
addAdditionalHeaders(@onNull Map<String, String> additionalHeaders)120         public @NonNull Builder addAdditionalHeaders(@NonNull Map<String, String>
121                 additionalHeaders) {
122             mAdditionalHeaders.putAll(additionalHeaders);
123             return this;
124         }
125 
126         /**
127          * Sets the "No-Vary-Search data that's expected to be returned via. the
128          * header in the prefetch's response. This is used to help determine if
129          * WebView#loadUrl should either use an in-flight prefetch response to
130          * render the web contents or handle the URL as it typically does
131          * (i.e. start a network request).
132          */
133         @Profile.ExperimentalUrlPrefetch
setExpectedNoVarySearchData( @onNull NoVarySearchHeader expectedNoVarySearchHeader)134         public @NonNull Builder setExpectedNoVarySearchData(
135                 @NonNull NoVarySearchHeader expectedNoVarySearchHeader) {
136             mExpectedNoVarySearchHeader = expectedNoVarySearchHeader;
137             return this;
138         }
139 
140         /**
141          * {@code true} if the WebView's that will be loading the prefetched
142          * response will have javascript enabled. This affects whether or not
143          * client hints header is sent with the prefetch request.
144          * <p>
145          * Note: This flag is ignored for prefetches initiated by the
146          * prerendering API. The value from
147          * {@link WebSettings#getJavaScriptEnabled()} will be used instead.
148          */
149         @Profile.ExperimentalUrlPrefetch
setJavaScriptEnabled(boolean javaScriptEnabled)150         public @NonNull Builder setJavaScriptEnabled(boolean javaScriptEnabled) {
151             mIsJavaScriptEnabled = javaScriptEnabled;
152             return this;
153         }
154 
155     }
156 }
157