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