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;
18 
19 import android.os.Handler;
20 import android.webkit.WebMessagePort;
21 
22 import androidx.annotation.AnyThread;
23 import androidx.annotation.RequiresApi;
24 import androidx.annotation.RequiresFeature;
25 import androidx.annotation.RestrictTo;
26 
27 import org.jspecify.annotations.NonNull;
28 import org.jspecify.annotations.Nullable;
29 
30 import java.lang.reflect.InvocationHandler;
31 
32 /**
33  * <p>The Java representation of the
34  * <a href="https://html.spec.whatwg.org/multipage/comms.html#messageport">
35  * HTML5 message ports.</a>
36  *
37  * <p>A Message port represents one endpoint of a Message Channel. In Android
38  * WebView, there is no separate Message Channel object. When a message channel
39  * is created, both ports are tangled to each other and started, and then
40  * returned in a MessagePort array, see {@link WebViewCompat#createWebMessageChannel}
41  * for creating a message channel.
42  *
43  * <p>When a message port is first created or received via transfer, it does not
44  * have a WebMessageCallback to receive web messages. The messages are queued until
45  * a WebMessageCallback is set.
46  *
47  * <p>A message port should be closed when it is not used by the embedder application
48  * anymore. A closed port cannot be transferred or cannot be reopened to send
49  * messages. Close can be called multiple times.
50  *
51  * <p>When a port is transferred to JS, it cannot be used to send or receive messages
52  * at the Java side anymore. Different from HTML5 Spec, a port cannot be transferred
53  * if one of these has ever happened: i. a message callback was set, ii. a message was
54  * posted on it. A transferred port cannot be closed by the application, since
55  * the ownership is also transferred.
56  *
57  * <p>It is possible to transfer both ports of a channel to JS, for example for
58  * communication between subframes.
59  */
60 @AnyThread
61 public abstract class WebMessagePortCompat {
62     /**
63      * The listener for handling MessagePort events. The message callback
64      * methods are called on the main thread. If the embedder application
65      * wants to receive the messages on a different thread, it can do this
66      * by passing a Handler in
67      *  {@link WebMessagePortCompat#setWebMessageCallback(Handler, WebMessageCallbackCompat)}.
68      * In the latter case, the application should be extra careful for thread safety
69      * since WebMessagePort methods should be called on main thread.
70      */
71     public abstract static class WebMessageCallbackCompat {
72         /**
73          * Message callback for receiving onMessage events.
74          *
75          * <p>This method is called only if {@link WebViewFeature#WEB_MESSAGE_CALLBACK_ON_MESSAGE}
76          * is supported. You can check whether that flag is supported using
77          * {@link WebViewFeature#isFeatureSupported(String)}.
78          *
79          * @param port  the WebMessagePort that the message is destined for
80          * @param message  the message from the entangled port.
81          */
onMessage(@onNull WebMessagePortCompat port, @Nullable WebMessageCompat message)82         public void onMessage(@NonNull WebMessagePortCompat port,
83                 @Nullable WebMessageCompat message) { }
84     }
85 
86     /**
87      */
88     @RestrictTo(RestrictTo.Scope.LIBRARY)
WebMessagePortCompat()89     public WebMessagePortCompat() { }
90 
91     /**
92      * Post a WebMessage to the entangled port.
93      *
94      * <p>
95      * This method should only be called if
96      * {@link WebViewFeature#isFeatureSupported(String)}
97      * returns true for {@link WebViewFeature#WEB_MESSAGE_PORT_POST_MESSAGE}.
98      *
99      * <p>
100      * When posting a {@link WebMessageCompat} with type {@link WebMessageCompat#TYPE_ARRAY_BUFFER},
101      * this method should check if {@link WebViewFeature#isFeatureSupported(String)} returns true
102      * for {@link WebViewFeature#WEB_MESSAGE_ARRAY_BUFFER}. Example:
103      * <pre class="prettyprint">
104      * if (message.getType() == WebMessageCompat.TYPE_ARRAY_BUFFER) {
105      *     if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER) {
106      *         // ArrayBuffer message is supported, send message here.
107      *         port.postMessage(message);
108      *     }
109      * }
110      * </pre>
111      *
112      * @param message  the message from Java to JS.
113      *
114      * @throws IllegalStateException If message port is already transferred or closed.
115      */
116     @RequiresFeature(name = WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE,
117             enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
postMessage(@onNull WebMessageCompat message)118     public abstract void postMessage(@NonNull WebMessageCompat message);
119 
120     /**
121      * Close the message port and free any resources associated with it.
122      *
123      * <p>
124      * This method should only be called if
125      * {@link WebViewFeature#isFeatureSupported(String)}
126      * returns true for {@link WebViewFeature#WEB_MESSAGE_PORT_CLOSE}.
127      */
128     @RequiresFeature(name = WebViewFeature.WEB_MESSAGE_PORT_CLOSE,
129             enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
close()130     public abstract void close();
131 
132     /**
133      * Sets a callback to receive message events on the main thread.
134      *
135      * <p>
136      * This method should only be called if
137      * {@link WebViewFeature#isFeatureSupported(String)}
138      * returns true for {@link WebViewFeature#WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK}.
139      *
140      * @param callback  the message callback.
141      */
142     @RequiresFeature(name = WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK,
143             enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
setWebMessageCallback(@onNull WebMessageCallbackCompat callback)144     public abstract void setWebMessageCallback(@NonNull WebMessageCallbackCompat callback);
145 
146     /**
147      * Sets a callback to receive message events on the handler that is provided
148      * by the application. If the handler is null the message events are received on the main
149      * thread.
150      *
151      * <p>
152      * This method should only be called if
153      * {@link WebViewFeature#isFeatureSupported(String)}
154      * returns true for {@link WebViewFeature#WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK}.
155      *
156      * @param handler   the handler to receive the message events.
157      * @param callback  the message callback.
158      */
159     @RequiresFeature(name = WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK,
160             enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
setWebMessageCallback(@ullable Handler handler, @NonNull WebMessageCallbackCompat callback)161     public abstract void setWebMessageCallback(@Nullable Handler handler,
162             @NonNull WebMessageCallbackCompat callback);
163 
164     /**
165      * Internal getter returning the private {@link WebMessagePort} implementing this class. This is
166      * only available on devices with an Android versions supporting WebMessagePorts.
167      */
168     @RequiresApi(23)
169     @RestrictTo(RestrictTo.Scope.LIBRARY)
getFrameworkPort()170     public abstract @NonNull WebMessagePort getFrameworkPort();
171 
172     /**
173      * Internal getter returning the private {@link java.lang.reflect.InvocationHandler}
174      * implementing this class. This is only available on devices where the support library glue in
175      * the WebView APK supports {@link WebMessagePortCompat}.
176      */
177     @RestrictTo(RestrictTo.Scope.LIBRARY)
getInvocationHandler()178     public abstract @NonNull InvocationHandler getInvocationHandler();
179 
180 }
181