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