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