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.os.Handler;
20 import android.webkit.WebMessage;
21 import android.webkit.WebMessagePort;
22 
23 import androidx.annotation.RequiresApi;
24 import androidx.webkit.WebMessageCompat;
25 import androidx.webkit.WebMessagePortCompat;
26 
27 import org.chromium.support_lib_boundary.WebMessagePortBoundaryInterface;
28 import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil;
29 import org.jspecify.annotations.NonNull;
30 import org.jspecify.annotations.Nullable;
31 
32 import java.lang.reflect.InvocationHandler;
33 import java.lang.reflect.Proxy;
34 
35 /**
36  * Implementation of {@link WebMessagePortCompat}.
37  * This class uses either the framework, the WebView APK, or both, to implement
38  * {@link WebMessagePortCompat} functionality.
39  */
40 public class WebMessagePortImpl extends WebMessagePortCompat {
41     private WebMessagePort mFrameworksImpl;
42     private WebMessagePortBoundaryInterface mBoundaryInterface;
43 
WebMessagePortImpl(@onNull WebMessagePort frameworksImpl)44     public WebMessagePortImpl(@NonNull WebMessagePort frameworksImpl) {
45         mFrameworksImpl = frameworksImpl;
46     }
47 
WebMessagePortImpl(@onNull InvocationHandler invocationHandler)48     public WebMessagePortImpl(@NonNull InvocationHandler invocationHandler) {
49         mBoundaryInterface = BoundaryInterfaceReflectionUtil.castToSuppLibClass(
50                 WebMessagePortBoundaryInterface.class, invocationHandler);
51     }
52 
53     @RequiresApi(23)
getFrameworksImpl()54     private WebMessagePort getFrameworksImpl() {
55         if (mFrameworksImpl == null) {
56             mFrameworksImpl = WebViewGlueCommunicator.getCompatConverter().convertWebMessagePort(
57                     Proxy.getInvocationHandler(mBoundaryInterface));
58         }
59         return mFrameworksImpl;
60     }
61 
getBoundaryInterface()62     private WebMessagePortBoundaryInterface getBoundaryInterface() {
63         if (mBoundaryInterface == null) {
64             mBoundaryInterface = BoundaryInterfaceReflectionUtil.castToSuppLibClass(
65                     WebMessagePortBoundaryInterface.class,
66                     WebViewGlueCommunicator.getCompatConverter().convertWebMessagePort(
67                             mFrameworksImpl));
68         }
69         return mBoundaryInterface;
70     }
71 
72     @Override
postMessage(@onNull WebMessageCompat message)73     public void postMessage(@NonNull WebMessageCompat message) {
74         final ApiFeature.M feature = WebViewFeatureInternal.WEB_MESSAGE_PORT_POST_MESSAGE;
75         // Only String type is supported by framework.
76         if (feature.isSupportedByFramework() && message.getType() == WebMessageCompat.TYPE_STRING) {
77             ApiHelperForM.postMessage(getFrameworksImpl(), compatToFrameworkMessage(message));
78         } else if (feature.isSupportedByWebView()
79                 && WebMessageAdapter.isMessagePayloadTypeSupportedByWebView(message.getType())) {
80             getBoundaryInterface().postMessage(
81                     BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
82                             new WebMessageAdapter(message)));
83         } else {
84             throw WebViewFeatureInternal.getUnsupportedOperationException();
85         }
86     }
87 
88     @Override
close()89     public void close() {
90         final ApiFeature.M feature = WebViewFeatureInternal.WEB_MESSAGE_PORT_CLOSE;
91         if (feature.isSupportedByFramework()) {
92             ApiHelperForM.close(getFrameworksImpl());
93         } else if (feature.isSupportedByWebView()) {
94             getBoundaryInterface().close();
95         } else {
96             throw WebViewFeatureInternal.getUnsupportedOperationException();
97         }
98     }
99 
100     @Override
setWebMessageCallback(final @NonNull WebMessageCallbackCompat callback)101     public void setWebMessageCallback(final @NonNull WebMessageCallbackCompat callback) {
102         final ApiFeature.M feature = WebViewFeatureInternal.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK;
103         if (feature.isSupportedByWebView()) {
104             // We prefer use WebView impl, since the impl in framework does not support
105             // WebMessageCompat types other than String.
106             getBoundaryInterface().setWebMessageCallback(
107                     BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
108                             new WebMessageCallbackAdapter(callback)));
109         } else if (feature.isSupportedByFramework()) {
110             ApiHelperForM.setWebMessageCallback(getFrameworksImpl(), callback);
111         } else {
112             throw WebViewFeatureInternal.getUnsupportedOperationException();
113         }
114     }
115 
116     @Override
setWebMessageCallback(@ullable Handler handler, final @NonNull WebMessageCallbackCompat callback)117     public void setWebMessageCallback(@Nullable Handler handler,
118             final @NonNull WebMessageCallbackCompat callback) {
119         final ApiFeature.M feature = WebViewFeatureInternal.CREATE_WEB_MESSAGE_CHANNEL;
120         if (feature.isSupportedByWebView()) {
121             // We prefer use WebView impl, since the impl in framework does not support
122             // WebMessageCompat types other than String.
123             getBoundaryInterface().setWebMessageCallback(
124                     BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
125                             new WebMessageCallbackAdapter(callback)), handler);
126         } else if (feature.isSupportedByFramework()) {
127             ApiHelperForM.setWebMessageCallback(getFrameworksImpl(), callback, handler);
128         } else {
129             throw WebViewFeatureInternal.getUnsupportedOperationException();
130         }
131     }
132 
133     @RequiresApi(23)
134     @Override
getFrameworkPort()135     public @NonNull WebMessagePort getFrameworkPort() {
136         return getFrameworksImpl();
137     }
138 
139     @Override
getInvocationHandler()140     public @NonNull InvocationHandler getInvocationHandler() {
141         return Proxy.getInvocationHandler(getBoundaryInterface());
142     }
143 
144     /**
145      * Convert an array of {@link WebMessagePort} objects into an array containing objects of the
146      * corresponding support library class {@link WebMessagePortCompat}.
147      */
portsToCompat( WebMessagePort @ullable [] ports)148     public static WebMessagePortCompat @Nullable [] portsToCompat(
149             WebMessagePort @Nullable [] ports) {
150         if (ports == null) return null;
151         WebMessagePortCompat[] compatPorts = new WebMessagePortCompat[ports.length];
152         for (int n = 0; n < ports.length; n++) {
153             compatPorts[n] = new WebMessagePortImpl(ports[n]);
154         }
155         return compatPorts;
156     }
157 
158     /**
159      * Convert an array of {@link WebMessagePortCompat} objects into an array containing objects of
160      * the corresponding framework class {@link WebMessagePort}.
161      */
162     @RequiresApi(23)
compatToPorts( WebMessagePortCompat @ullable [] compatPorts)163     public static WebMessagePort @Nullable [] compatToPorts(
164             WebMessagePortCompat @Nullable [] compatPorts) {
165         if (compatPorts == null) return null;
166         WebMessagePort[] ports = new WebMessagePort[compatPorts.length];
167         for (int n = 0; n < ports.length; n++) {
168             ports[n] = compatPorts[n].getFrameworkPort();
169         }
170         return ports;
171     }
172 
173     /**
174      * Convert a {@link WebMessageCompat} into the corresponding framework class {@link WebMessage}.
175      */
176     @RequiresApi(23)
compatToFrameworkMessage(@onNull WebMessageCompat message)177     public static @NonNull WebMessage compatToFrameworkMessage(@NonNull WebMessageCompat message) {
178         return ApiHelperForM.createWebMessage(message);
179     }
180 
181     /**
182      * Convert a {@link WebMessage} into the corresponding support library class
183      * {@link WebMessageCompat}.
184      */
185     @RequiresApi(23)
frameworkMessageToCompat(@onNull WebMessage message)186     public static @NonNull WebMessageCompat frameworkMessageToCompat(@NonNull WebMessage message) {
187         return ApiHelperForM.createWebMessageCompat(message);
188     }
189 }
190