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 static org.chromium.support_lib_boundary.WebMessagePayloadBoundaryInterface.WebMessagePayloadType; 20 21 import androidx.webkit.WebMessageCompat; 22 import androidx.webkit.WebMessagePortCompat; 23 24 import org.chromium.support_lib_boundary.WebMessageBoundaryInterface; 25 import org.chromium.support_lib_boundary.WebMessagePayloadBoundaryInterface; 26 import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil; 27 import org.chromium.support_lib_boundary.util.Features; 28 import org.jspecify.annotations.NonNull; 29 import org.jspecify.annotations.Nullable; 30 31 import java.lang.reflect.InvocationHandler; 32 import java.util.Objects; 33 34 /** 35 * Adapter between {@link WebMessageCompat} and 36 * {@link org.chromium.support_lib_boundary.WebMessageBoundaryInterface}. 37 * This class is used to pass a PostMessage from the app to Chromium. 38 */ 39 public class WebMessageAdapter implements WebMessageBoundaryInterface { 40 private final WebMessageCompat mWebMessageCompat; 41 42 private static final String[] sFeatures = {Features.WEB_MESSAGE_ARRAY_BUFFER}; 43 WebMessageAdapter(@onNull WebMessageCompat webMessage)44 public WebMessageAdapter(@NonNull WebMessageCompat webMessage) { 45 this.mWebMessageCompat = webMessage; 46 } 47 48 /** 49 * @deprecated Keep backwards compatibility with old version of WebView. This method is 50 * equivalent to {@link WebMessagePayloadBoundaryInterface#getAsString()}. 51 */ 52 @Deprecated 53 @Override getData()54 public @Nullable String getData() { 55 return mWebMessageCompat.getData(); 56 } 57 58 @Override getMessagePayload()59 public @Nullable InvocationHandler getMessagePayload() { 60 final WebMessagePayloadAdapter adapter; 61 switch (mWebMessageCompat.getType()) { 62 case WebMessageCompat.TYPE_STRING: 63 adapter = new WebMessagePayloadAdapter(mWebMessageCompat.getData()); 64 break; 65 case WebMessageCompat.TYPE_ARRAY_BUFFER: 66 adapter = new WebMessagePayloadAdapter( 67 Objects.requireNonNull(mWebMessageCompat.getArrayBuffer())); 68 break; 69 default: 70 throw new IllegalStateException( 71 "Unknown web message payload type: " + mWebMessageCompat.getType()); 72 } 73 return BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(adapter); 74 } 75 76 @Override getPorts()77 public InvocationHandler @Nullable [] getPorts() { 78 WebMessagePortCompat[] ports = mWebMessageCompat.getPorts(); 79 if (ports == null) return null; 80 81 InvocationHandler[] invocationHandlers = new InvocationHandler[ports.length]; 82 for (int n = 0; n < ports.length; n++) { 83 invocationHandlers[n] = ports[n].getInvocationHandler(); 84 } 85 return invocationHandlers; 86 } 87 88 @Override getSupportedFeatures()89 public String @NonNull [] getSupportedFeatures() { 90 // getData() and getPorts() are not covered by feature flags. 91 return sFeatures; 92 } 93 94 /** 95 * Utility method to check if the WebMessageCompat payload type is supported by WebView. 96 */ isMessagePayloadTypeSupportedByWebView( @ebMessageCompat.Type final int type)97 public static boolean isMessagePayloadTypeSupportedByWebView( 98 @WebMessageCompat.Type final int type) { 99 return type == WebMessageCompat.TYPE_STRING 100 || (type == WebMessageCompat.TYPE_ARRAY_BUFFER 101 && WebViewFeatureInternal.WEB_MESSAGE_ARRAY_BUFFER.isSupportedByWebView()); 102 } 103 104 // ==================================================================================== 105 // Methods related to converting a WebMessageBoundaryInterface into a WebMessageCompat. 106 // ==================================================================================== 107 108 /** 109 * Utility method used to convert PostMessages from the Chromium side to 110 * {@link WebMessageCompat} objects - a class apps recognize. 111 * Return null when the WebMessageCompat payload type is not supported by AndroidX now. 112 */ 113 @SuppressWarnings("deprecation") webMessageCompatFromBoundaryInterface( @onNull WebMessageBoundaryInterface boundaryInterface)114 public static @Nullable WebMessageCompat webMessageCompatFromBoundaryInterface( 115 @NonNull WebMessageBoundaryInterface boundaryInterface) { 116 final WebMessagePortCompat[] ports = toWebMessagePortCompats( 117 boundaryInterface.getPorts()); 118 if (WebViewFeatureInternal.WEB_MESSAGE_ARRAY_BUFFER.isSupportedByWebView()) { 119 WebMessagePayloadBoundaryInterface payloadInterface = 120 BoundaryInterfaceReflectionUtil.castToSuppLibClass( 121 WebMessagePayloadBoundaryInterface.class, 122 boundaryInterface.getMessagePayload()); 123 final @WebMessagePayloadType int type = payloadInterface.getType(); 124 switch (type) { 125 case WebMessagePayloadType.TYPE_STRING: 126 return new WebMessageCompat(payloadInterface.getAsString(), ports); 127 case WebMessagePayloadType.TYPE_ARRAY_BUFFER: 128 return new WebMessageCompat(payloadInterface.getAsArrayBuffer(), ports); 129 } 130 // Unsupported message type. 131 return null; 132 } 133 // MessagePayload not supported by WebView, fallback. 134 return new WebMessageCompat(boundaryInterface.getData(), ports); 135 } 136 toWebMessagePortCompats( InvocationHandler[] ports)137 private static WebMessagePortCompat @NonNull [] toWebMessagePortCompats( 138 InvocationHandler[] ports) { 139 WebMessagePortCompat[] compatPorts = new WebMessagePortCompat[ports.length]; 140 for (int n = 0; n < ports.length; n++) { 141 compatPorts[n] = new WebMessagePortImpl(ports[n]); 142 } 143 return compatPorts; 144 } 145 } 146