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.internal; 18 19 import android.annotation.SuppressLint; 20 import android.net.Uri; 21 import android.os.Bundle; 22 import android.os.CancellationSignal; 23 import android.webkit.ValueCallback; 24 import android.webkit.WebChromeClient; 25 import android.webkit.WebView; 26 import android.webkit.WebViewClient; 27 28 import androidx.annotation.UiThread; 29 import androidx.webkit.PrerenderException; 30 import androidx.webkit.PrerenderOperationCallback; 31 import androidx.webkit.Profile; 32 import androidx.webkit.SpeculativeLoadingParameters; 33 import androidx.webkit.WebMessageCompat; 34 import androidx.webkit.WebMessagePortCompat; 35 import androidx.webkit.WebNavigationClient; 36 import androidx.webkit.WebViewCompat; 37 import androidx.webkit.WebViewRenderProcess; 38 import androidx.webkit.WebViewRenderProcessClient; 39 40 import org.chromium.support_lib_boundary.ProfileBoundaryInterface; 41 import org.chromium.support_lib_boundary.WebViewProviderBoundaryInterface; 42 import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil; 43 import org.jspecify.annotations.NonNull; 44 import org.jspecify.annotations.Nullable; 45 46 import java.lang.reflect.InvocationHandler; 47 import java.util.concurrent.Executor; 48 49 /** 50 * Adapter for WebViewProviderBoundaryInterface providing the functionality expected of 51 * WebViewCompat, this adapter is the support library version of 52 * {@link android.webkit.WebViewProvider}. 53 */ 54 @SuppressWarnings("JavadocReference") // WebViewProvider is hidden. 55 public class WebViewProviderAdapter { 56 final WebViewProviderBoundaryInterface mImpl; 57 WebViewProviderAdapter(@onNull WebViewProviderBoundaryInterface impl)58 public WebViewProviderAdapter(@NonNull WebViewProviderBoundaryInterface impl) { 59 mImpl = impl; 60 } 61 62 /** 63 * Adapter method WebViewCompat.insertVisualStateCallback(). 64 */ insertVisualStateCallback( long requestId, WebViewCompat.@NonNull VisualStateCallback callback)65 public void insertVisualStateCallback( 66 long requestId, WebViewCompat.@NonNull VisualStateCallback callback) { 67 mImpl.insertVisualStateCallback(requestId, 68 BoundaryInterfaceReflectionUtil.createInvocationHandlerFor( 69 new VisualStateCallbackAdapter(callback))); 70 } 71 72 /** 73 * Adapter method for {@link WebViewCompat#createWebMessageChannel(WebView)}. 74 */ createWebMessageChannel()75 public WebMessagePortCompat @NonNull [] createWebMessageChannel() { 76 InvocationHandler[] invocationHandlers = mImpl.createWebMessageChannel(); 77 WebMessagePortCompat[] messagePorts = new WebMessagePortCompat[invocationHandlers.length]; 78 for (int n = 0; n < invocationHandlers.length; n++) { 79 messagePorts[n] = new WebMessagePortImpl(invocationHandlers[n]); 80 } 81 return messagePorts; 82 } 83 84 /** 85 * Adapter method for {@link WebViewCompat#postWebMessage(WebView, WebMessageCompat, Uri)}. 86 */ postWebMessage(@onNull WebMessageCompat message, @NonNull Uri targetOrigin)87 public void postWebMessage(@NonNull WebMessageCompat message, @NonNull Uri targetOrigin) { 88 mImpl.postMessageToMainFrame( 89 BoundaryInterfaceReflectionUtil.createInvocationHandlerFor( 90 new WebMessageAdapter(message)), targetOrigin); 91 } 92 93 /** 94 * Adapter method for {@link WebViewCompat#addWebMessageListener(android.webkit.WebView, 95 * String, java.util.List, androidx.webkit.WebViewCompat.WebMessageListener)}. 96 */ addWebMessageListener(@onNull String jsObjectName, String @NonNull [] allowedOriginRules, WebViewCompat.@NonNull WebMessageListener listener)97 public void addWebMessageListener(@NonNull String jsObjectName, 98 String @NonNull [] allowedOriginRules, 99 WebViewCompat.@NonNull WebMessageListener listener) { 100 mImpl.addWebMessageListener(jsObjectName, allowedOriginRules, 101 BoundaryInterfaceReflectionUtil.createInvocationHandlerFor( 102 new WebMessageListenerAdapter(listener))); 103 } 104 105 /** 106 * Adapter method for {@link WebViewCompat#addWebMessageListener(android.webkit.WebView, 107 * String, Set)} 108 */ addDocumentStartJavaScript( @onNull String script, String @NonNull [] allowedOriginRules)109 public @NonNull ScriptHandlerImpl addDocumentStartJavaScript( 110 @NonNull String script, String @NonNull [] allowedOriginRules) { 111 return ScriptHandlerImpl.toScriptHandler( 112 mImpl.addDocumentStartJavaScript(script, allowedOriginRules)); 113 } 114 115 /** 116 * Adapter method for {@link WebViewCompat#removeWebMessageListener(String)}. 117 */ removeWebMessageListener(@onNull String jsObjectName)118 public void removeWebMessageListener(@NonNull String jsObjectName) { 119 mImpl.removeWebMessageListener(jsObjectName); 120 } 121 122 /** 123 * Adapter method for {@link WebViewCompat#getWebViewClient()}. 124 */ getWebViewClient()125 public @NonNull WebViewClient getWebViewClient() { 126 return mImpl.getWebViewClient(); 127 } 128 129 /** 130 * Adapter method for {@link WebViewCompat#getWebChromeClient()}. 131 */ getWebChromeClient()132 public @Nullable WebChromeClient getWebChromeClient() { 133 return mImpl.getWebChromeClient(); 134 } 135 136 /** 137 * Adapter method for {@link WebViewCompat#getWebViewRenderer()}. 138 */ getWebViewRenderProcess()139 public @Nullable WebViewRenderProcess getWebViewRenderProcess() { 140 return WebViewRenderProcessImpl.forInvocationHandler(mImpl.getWebViewRenderer()); 141 } 142 143 /** 144 * Adapter method for {@link WebViewCompat#getWebViewRendererClient()}. 145 */ getWebViewRenderProcessClient()146 public @Nullable WebViewRenderProcessClient getWebViewRenderProcessClient() { 147 InvocationHandler handler = mImpl.getWebViewRendererClient(); 148 if (handler == null) return null; 149 return ((WebViewRenderProcessClientAdapter) 150 BoundaryInterfaceReflectionUtil.getDelegateFromInvocationHandler( 151 handler)).getWebViewRenderProcessClient(); 152 } 153 154 /** 155 * Adapter method for {@link WebViewCompat#setWebViewRendererClient(WebViewRendererClient)}. 156 */ 157 // WebViewRenderProcessClient is a callback class, so it should be last. See 158 // https://issuetracker.google.com/issues/139770271. 159 @SuppressLint("LambdaLast") setWebViewRenderProcessClient(@ullable Executor executor, @Nullable WebViewRenderProcessClient webViewRenderProcessClient)160 public void setWebViewRenderProcessClient(@Nullable Executor executor, 161 @Nullable WebViewRenderProcessClient webViewRenderProcessClient) { 162 InvocationHandler handler = webViewRenderProcessClient != null 163 ? BoundaryInterfaceReflectionUtil.createInvocationHandlerFor( 164 new WebViewRenderProcessClientAdapter(executor, webViewRenderProcessClient)) 165 : null; 166 mImpl.setWebViewRendererClient(handler); 167 } 168 169 /** 170 * Adapter method for {@link WebViewCompat#setProfile(WebView, String)}. 171 */ setProfileWithName(@onNull String profileName)172 public void setProfileWithName(@NonNull String profileName) { 173 mImpl.setProfile(profileName); 174 } 175 176 /** 177 * Adapter method for {@link WebViewCompat#getProfile(WebView)}. 178 */ getProfile()179 public @NonNull Profile getProfile() { 180 ProfileBoundaryInterface profile = BoundaryInterfaceReflectionUtil.castToSuppLibClass( 181 ProfileBoundaryInterface.class, mImpl.getProfile()); 182 183 return new ProfileImpl(profile); 184 } 185 186 /** 187 * Adapter method for {@link WebViewCompat#isAudioMuted(WebView)}. 188 */ isAudioMuted()189 public boolean isAudioMuted() { 190 return mImpl.isAudioMuted(); 191 } 192 193 /** 194 * Adapter method for {@link WebViewCompat#setAudioMuted(WebView, boolean)}. 195 */ setAudioMuted(boolean mute)196 public void setAudioMuted(boolean mute) { 197 mImpl.setAudioMuted(mute); 198 } 199 200 /** 201 * Adapter method for 202 * {@link WebViewCompat#prerenderUrlAsync(WebView, String, CancellationSignal, Executor, 203 * PrerenderOperationCallback)}. 204 */ prerenderUrlAsync( @onNull String url, @Nullable CancellationSignal cancellationSignal, @NonNull Executor callbackExecutor, @NonNull PrerenderOperationCallback callback)205 public void prerenderUrlAsync( 206 @NonNull String url, 207 @Nullable CancellationSignal cancellationSignal, 208 @NonNull Executor callbackExecutor, 209 @NonNull PrerenderOperationCallback callback) { 210 211 ValueCallback<Void> activationCallback = (value) -> { 212 // value will always be null. 213 callback.onPrerenderActivated(); 214 }; 215 ValueCallback<Throwable> errorCallback = (throwable) -> { 216 callback.onError(new PrerenderException("Prerender operation failed", throwable)); 217 }; 218 mImpl.prerenderUrl( 219 url, 220 cancellationSignal, 221 callbackExecutor, 222 activationCallback, 223 errorCallback); 224 } 225 226 /** 227 * Adapter method for 228 * {@link WebViewCompat#prerenderUrl(WebView, String, CancellationSignal, Executor, 229 * SpeculativeLoadingParameters, PrerenderOperationCallback)}. 230 */ prerenderUrlAsync( @onNull String url, @Nullable CancellationSignal cancellationSignal, @NonNull Executor callbackExecutor, @NonNull SpeculativeLoadingParameters params, @NonNull PrerenderOperationCallback callback)231 public void prerenderUrlAsync( 232 @NonNull String url, 233 @Nullable CancellationSignal cancellationSignal, 234 @NonNull Executor callbackExecutor, 235 @NonNull SpeculativeLoadingParameters params, 236 @NonNull PrerenderOperationCallback callback) { 237 238 InvocationHandler paramsBoundaryInterface = 239 BoundaryInterfaceReflectionUtil.createInvocationHandlerFor( 240 new SpeculativeLoadingParametersAdapter(params)); 241 ValueCallback<Void> activationCallback = (value) -> { 242 // value will always be null. 243 callback.onPrerenderActivated(); 244 }; 245 ValueCallback<Throwable> errorCallback = (throwable) -> { 246 callback.onError(new PrerenderException("Prerender operation failed", throwable)); 247 }; 248 mImpl.prerenderUrl( 249 url, 250 cancellationSignal, 251 callbackExecutor, 252 paramsBoundaryInterface, 253 activationCallback, 254 errorCallback); 255 } 256 257 /** 258 * Adapter method for {@link WebViewCompat#saveState(WebView, Bundle, int, boolean)}. 259 */ 260 @UiThread saveState( @onNull Bundle outState, int maxSizeBytes, boolean includeForwardState)261 public void saveState( 262 @NonNull Bundle outState, 263 int maxSizeBytes, 264 boolean includeForwardState) { 265 mImpl.saveState(outState, maxSizeBytes, includeForwardState); 266 } 267 268 /** 269 * Adapter method for {@link WebViewCompat#saveState(WebView, Bundle, int, boolean)}. 270 */ 271 @UiThread setWebNavigationClient( @onNull WebNavigationClient client)272 public void setWebNavigationClient( 273 @NonNull WebNavigationClient client) { 274 InvocationHandler clientBoundaryInterface = 275 BoundaryInterfaceReflectionUtil.createInvocationHandlerFor( 276 new WebNavigationClientAdapter(client)); 277 mImpl.setWebViewNavigationClient(clientBoundaryInterface); 278 } 279 280 /** 281 * Adapter method for {@link WebViewCompat#getWebN(WebView, Bundle, int, boolean)}. 282 */ 283 @UiThread getWebNavigationClient()284 public @NonNull WebNavigationClient getWebNavigationClient() { 285 InvocationHandler client = mImpl.getWebViewNavigationClient(); 286 if (client == null) return null; 287 return ((WebNavigationClientAdapter) 288 BoundaryInterfaceReflectionUtil.getDelegateFromInvocationHandler( 289 client)).getWebNavigationClient(); 290 } 291 } 292