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