1 /* 2 * Copyright 2018 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.os.Build; 20 import android.webkit.SafeBrowsingResponse; 21 import android.webkit.WebResourceError; 22 import android.webkit.WebResourceRequest; 23 import android.webkit.WebResourceResponse; 24 import android.webkit.WebView; 25 import android.webkit.WebViewClient; 26 27 import androidx.annotation.IntDef; 28 import androidx.annotation.NonNull; 29 import androidx.annotation.RequiresApi; 30 import androidx.annotation.RestrictTo; 31 import androidx.webkit.internal.WebResourceErrorImpl; 32 import androidx.webkit.internal.WebViewFeatureInternal; 33 34 import org.chromium.support_lib_boundary.WebViewClientBoundaryInterface; 35 import org.chromium.support_lib_boundary.util.Features; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.lang.reflect.InvocationHandler; 40 41 /** 42 * Compatibility version of {@link android.webkit.WebViewClient}. 43 */ 44 // Note: some methods are marked as RequiresApi 21, because only an up-to-date WebView APK would 45 // ever invoke these methods (and WebView can only be updated on Lollipop and above). The app can 46 // still construct a WebViewClientCompat on a pre-Lollipop devices, and explicitly invoke these 47 // methods, so each of these methods must also handle this case. 48 public class WebViewClientCompat extends WebViewClient implements WebViewClientBoundaryInterface { 49 private static final String[] sSupportedFeatures = new String[] { 50 Features.VISUAL_STATE_CALLBACK, 51 Features.RECEIVE_WEB_RESOURCE_ERROR, 52 Features.RECEIVE_HTTP_ERROR, 53 Features.SHOULD_OVERRIDE_WITH_REDIRECTS, 54 Features.SAFE_BROWSING_HIT, 55 }; 56 57 /** @hide */ 58 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 59 @IntDef(value = { 60 WebViewClient.SAFE_BROWSING_THREAT_UNKNOWN, 61 WebViewClient.SAFE_BROWSING_THREAT_MALWARE, 62 WebViewClient.SAFE_BROWSING_THREAT_PHISHING, 63 WebViewClient.SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE 64 }) 65 @Retention(RetentionPolicy.SOURCE) 66 public @interface SafeBrowsingThreat {} 67 68 /** 69 * Returns the list of features this client supports. This feature list should always be a 70 * subset of the Features declared in WebViewFeature. 71 * 72 * @hide 73 */ 74 @Override 75 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) getSupportedFeatures()76 public final String[] getSupportedFeatures() { 77 return sSupportedFeatures; 78 } 79 80 /** 81 * Notify the host application that {@link android.webkit.WebView} content left over from 82 * previous page navigations will no longer be drawn. 83 * 84 * <p>This callback can be used to determine the point at which it is safe to make a recycled 85 * {@link android.webkit.WebView} visible, ensuring that no stale content is shown. It is called 86 * at the earliest point at which it can be guaranteed that {@link WebView#onDraw} will no 87 * longer draw any content from previous navigations. The next draw will display either the 88 * {@link WebView#setBackgroundColor background color} of the {@link WebView}, or some of the 89 * contents of the newly loaded page. 90 * 91 * <p>This method is called when the body of the HTTP response has started loading, is reflected 92 * in the DOM, and will be visible in subsequent draws. This callback occurs early in the 93 * document loading process, and as such you should expect that linked resources (for example, 94 * CSS and images) may not be available. 95 * 96 * <p>For more fine-grained notification of visual state updates, see {@link 97 * WebViewCompat#postVisualStateCallback}. 98 * 99 * <p>Please note that all the conditions and recommendations applicable to 100 * {@link WebViewCompat#postVisualStateCallback} also apply to this API. 101 * 102 * <p>This callback is only called for main frame navigations. 103 * 104 * <p>This method is called only if {@link WebViewFeature#VISUAL_STATE_CALLBACK} is supported. 105 * You can check whether that flag is supported using {@link 106 * WebViewFeature#isFeatureSupported(String)}. 107 * 108 * @param view The {@link android.webkit.WebView} for which the navigation occurred. 109 * @param url The URL corresponding to the page navigation that triggered this callback. 110 */ 111 @Override onPageCommitVisible(@onNull WebView view, @NonNull String url)112 public void onPageCommitVisible(@NonNull WebView view, @NonNull String url) { 113 } 114 115 /** 116 * Invoked by chromium (for WebView APks 67+) for the {@code onReceivedError} event. 117 * Applications are not meant to override this, and should instead override the non-final {@link 118 * onReceivedError(WebView, WebResourceRequest, WebResourceErrorCompat)} method. 119 * 120 * @hide 121 */ 122 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 123 @Override 124 @RequiresApi(21) onReceivedError(@onNull WebView view, @NonNull WebResourceRequest request, @NonNull InvocationHandler handler)125 public final void onReceivedError(@NonNull WebView view, @NonNull WebResourceRequest request, 126 /* WebResourceError */ @NonNull InvocationHandler handler) { 127 onReceivedError(view, request, new WebResourceErrorImpl(handler)); 128 } 129 130 /** 131 * Invoked by chromium (in legacy WebView APKs) for the {@code onReceivedError} event on {@link 132 * Build.VERSION_CODES.M} and above. Applications are not meant to override this, and should 133 * instead override the non-final {@link onReceivedError(WebView, WebResourceRequest, 134 * WebResourceErrorCompat)} method. 135 * 136 * @hide 137 */ 138 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 139 @Override 140 @RequiresApi(23) onReceivedError(@onNull WebView view, @NonNull WebResourceRequest request, @NonNull WebResourceError error)141 public final void onReceivedError(@NonNull WebView view, @NonNull WebResourceRequest request, 142 @NonNull WebResourceError error) { 143 if (Build.VERSION.SDK_INT < 23) return; 144 onReceivedError(view, request, new WebResourceErrorImpl(error)); 145 } 146 147 /** 148 * Report web resource loading error to the host application. These errors usually indicate 149 * inability to connect to the server. Note that unlike the deprecated version of the callback, 150 * the new version will be called for any resource (iframe, image, etc.), not just for the main 151 * page. Thus, it is recommended to perform minimum required work in this callback. 152 * 153 * <p>This method is called only if {@link WebViewFeature#RECEIVE_WEB_RESOURCE_ERROR} is 154 * supported. You can check whether that flag is supported using {@link 155 * WebViewFeature#isFeatureSupported(String)}. 156 * 157 * @param view The WebView that is initiating the callback. 158 * @param request The originating request. 159 * @param error Information about the error occurred. 160 */ 161 @SuppressWarnings("deprecation") // for invoking the old onReceivedError. 162 @RequiresApi(21) onReceivedError(@onNull WebView view, @NonNull WebResourceRequest request, @NonNull WebResourceErrorCompat error)163 public void onReceivedError(@NonNull WebView view, @NonNull WebResourceRequest request, 164 @NonNull WebResourceErrorCompat error) { 165 if (Build.VERSION.SDK_INT < 21) return; 166 if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE) 167 || !WebViewFeature.isFeatureSupported( 168 WebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION)) { 169 // If the WebView APK drops supports for these APIs in the future, simply do nothing. 170 return; 171 } 172 if (request.isForMainFrame()) { 173 onReceivedError(view, 174 error.getErrorCode(), error.getDescription().toString(), 175 request.getUrl().toString()); 176 } 177 } 178 179 /** 180 * Notify the host application that an HTTP error has been received from the server while 181 * loading a resource. HTTP errors have status codes >= 400. This callback will be called 182 * for any resource (iframe, image, etc.), not just for the main page. Thus, it is recommended 183 * to perform minimum required work in this callback. Note that the content of the server 184 * response may not be provided within the {@code errorResponse} parameter. 185 * 186 * <p>This method is called only if {@link WebViewFeature#RECEIVE_HTTP_ERROR} is supported. You 187 * can check whether that flag is supported using {@link 188 * WebViewFeature#isFeatureSupported(String)}. 189 * 190 * @param view The WebView that is initiating the callback. 191 * @param request The originating request. 192 * @param errorResponse Information about the error occurred. 193 */ 194 @Override onReceivedHttpError(@onNull WebView view, @NonNull WebResourceRequest request, @NonNull WebResourceResponse errorResponse)195 public void onReceivedHttpError(@NonNull WebView view, @NonNull WebResourceRequest request, 196 @NonNull WebResourceResponse errorResponse) { 197 } 198 199 /** 200 * Invoked by chromium (for WebView APks 67+) for the {@code onSafeBrowsingHit} event. 201 * Applications are not meant to override this, and should instead override the non-final {@link 202 * onSafeBrowsingHit(WebView, WebResourceRequest, int, SafeBrowsingResponseCompat)} method. 203 * 204 * @hide 205 */ 206 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 207 @Override onSafeBrowsingHit(@onNull WebView view, @NonNull WebResourceRequest request, @SafeBrowsingThreat int threatType, @NonNull InvocationHandler handler)208 public final void onSafeBrowsingHit(@NonNull WebView view, @NonNull WebResourceRequest request, 209 @SafeBrowsingThreat int threatType, 210 /* SafeBrowsingResponse */ @NonNull InvocationHandler handler) { 211 onSafeBrowsingHit(view, request, threatType, 212 SafeBrowsingResponseCompat.fromInvocationHandler(handler)); 213 } 214 215 /** 216 * Invoked by chromium (in legacy WebView APKs) for the {@code onSafeBrowsingHit} event on 217 * {@link Build.VERSION_CODES.O_MR1} and above. Applications are not meant to override this, and 218 * should instead override the non-final {@link onSafeBrowsingHit(WebView, WebResourceRequest, 219 * int, SafeBrowsingResponseCompat)} method. 220 * 221 * @hide 222 */ 223 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 224 @Override 225 @RequiresApi(27) onSafeBrowsingHit(@onNull WebView view, @NonNull WebResourceRequest request, @SafeBrowsingThreat int threatType, @NonNull SafeBrowsingResponse response)226 public final void onSafeBrowsingHit(@NonNull WebView view, @NonNull WebResourceRequest request, 227 @SafeBrowsingThreat int threatType, @NonNull SafeBrowsingResponse response) { 228 onSafeBrowsingHit(view, request, threatType, 229 SafeBrowsingResponseCompat.fromSafeBrowsingResponse(response)); 230 } 231 232 /** 233 * Notify the host application that a loading URL has been flagged by Safe Browsing. 234 * 235 * The application must invoke the callback to indicate the preferred response. The default 236 * behavior is to show an interstitial to the user, with the reporting checkbox visible. 237 * 238 * If the application needs to show its own custom interstitial UI, the callback can be invoked 239 * asynchronously with {@link SafeBrowsingResponseCompat#backToSafety} or {@link 240 * SafeBrowsingResponseCompat#proceed}, depending on user response. 241 * 242 * @param view The WebView that hit the malicious resource. 243 * @param request Object containing the details of the request. 244 * @param threatType The reason the resource was caught by Safe Browsing, corresponding to a 245 * {@code SAFE_BROWSING_THREAT_*} value. 246 * @param callback Applications must invoke one of the callback methods. 247 */ onSafeBrowsingHit(@onNull WebView view, @NonNull WebResourceRequest request, @SafeBrowsingThreat int threatType, @NonNull SafeBrowsingResponseCompat callback)248 public void onSafeBrowsingHit(@NonNull WebView view, @NonNull WebResourceRequest request, 249 @SafeBrowsingThreat int threatType, @NonNull SafeBrowsingResponseCompat callback) { 250 if (WebViewFeature.isFeatureSupported( 251 WebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL)) { 252 callback.showInterstitial(true); 253 } else { 254 // This should not happen, but in case the WebView APK eventually drops support for 255 // showInterstitial(), raise a runtime exception and require the WebView APK to handle 256 // this. 257 throw WebViewFeatureInternal.getUnsupportedOperationException(); 258 } 259 } 260 261 /** 262 * Give the host application a chance to take over the control when a new 263 * url is about to be loaded in the current WebView. If WebViewClient is not 264 * provided, by default WebView will ask Activity Manager to choose the 265 * proper handler for the url. If WebViewClient is provided, return {@code true} 266 * means the host application handles the url, while return {@code false} means the 267 * current WebView handles the url. 268 * 269 * <p>Notes: 270 * <ul> 271 * <li>This method is not called for requests using the POST "method".</li> 272 * <li>This method is also called for subframes with non-http schemes, thus it is 273 * strongly disadvised to unconditionally call {@link WebView#loadUrl(String)} 274 * with the request's url from inside the method and then return {@code true}, 275 * as this will make WebView to attempt loading a non-http url, and thus fail.</li> 276 * </ul> 277 * 278 * <p>This method is called only if {@link WebViewFeature#SHOULD_OVERRIDE_WITH_REDIRECTS} is 279 * supported. You can check whether that flag is supported using {@link 280 * WebViewFeature#isFeatureSupported(String)}. 281 * 282 * @param view The WebView that is initiating the callback. 283 * @param request Object containing the details of the request. 284 * @return {@code true} if the host application wants to leave the current WebView 285 * and handle the url itself, otherwise return {@code false}. 286 */ 287 @Override 288 @SuppressWarnings("deprecation") // for invoking the old shouldOverrideUrlLoading. 289 @RequiresApi(21) shouldOverrideUrlLoading(@onNull WebView view, @NonNull WebResourceRequest request)290 public boolean shouldOverrideUrlLoading(@NonNull WebView view, 291 @NonNull WebResourceRequest request) { 292 if (Build.VERSION.SDK_INT < 21) return false; 293 return shouldOverrideUrlLoading(view, request.getUrl().toString()); 294 } 295 } 296