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