@exodus/react-native-webview 11.26.1-exodus.9 → 13.16.0-exodus.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -63
- package/android/build.gradle +83 -110
- package/android/gradle.properties +3 -4
- package/android/src/main/AndroidManifest.xml +12 -0
- package/android/src/main/AndroidManifestNew.xml +26 -0
- package/android/src/main/java/com/reactnativecommunity/webview/RNCBasicAuthCredential.java +11 -0
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebChromeClient.java +407 -0
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java +468 -0
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewClient.java +330 -0
- package/android/src/main/java/com/reactnativecommunity/webview/{WebViewConfig.java → RNCWebViewConfig.java} +3 -4
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewFileProvider.java +1 -1
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt +746 -0
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewMessagingModule.kt +9 -0
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewModuleImpl.java +554 -0
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewPackage.java +57 -12
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewWrapper.kt +39 -0
- package/android/src/main/java/com/reactnativecommunity/webview/events/SubResourceErrorEvent.kt +25 -0
- package/android/src/main/java/com/reactnativecommunity/webview/events/TopCustomMenuSelectionEvent.kt +24 -0
- package/android/src/main/java/com/reactnativecommunity/webview/events/TopHttpErrorEvent.kt +25 -0
- package/android/src/main/java/com/reactnativecommunity/webview/events/TopNewWindowEvent.kt +25 -0
- package/android/src/main/java/com/reactnativecommunity/webview/events/TopRenderProcessGoneEvent.kt +25 -0
- package/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewManager.java +570 -0
- package/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewModule.java +57 -0
- package/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewManager.java +341 -0
- package/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewModule.java +59 -0
- package/apple/RCTConvert+WKDataDetectorTypes.h +11 -0
- package/apple/RCTConvert+WKDataDetectorTypes.m +27 -0
- package/apple/RNCWebView.h +26 -100
- package/apple/RNCWebView.mm +555 -0
- package/apple/RNCWebViewDecisionManager.h +20 -0
- package/apple/RNCWebViewDecisionManager.m +47 -0
- package/apple/RNCWebViewImpl.h +164 -0
- package/apple/{RNCWebView.m → RNCWebViewImpl.m} +802 -225
- package/apple/RNCWebViewManager.h +4 -8
- package/apple/RNCWebViewManager.mm +221 -0
- package/apple/RNCWebViewModule.h +23 -0
- package/apple/RNCWebViewModule.mm +34 -0
- package/index.d.ts +2 -3
- package/lib/NativeRNCWebViewModule.d.ts +8 -0
- package/lib/NativeRNCWebViewModule.js +1 -0
- package/lib/RNCWebViewNativeComponent.d.ts +245 -0
- package/lib/RNCWebViewNativeComponent.js +1 -0
- package/lib/WebView.android.d.ts +0 -1
- package/lib/WebView.android.js +1 -135
- package/lib/WebView.d.ts +2 -3
- package/lib/WebView.ios.d.ts +0 -1
- package/lib/WebView.ios.js +1 -114
- package/lib/WebView.js +1 -11
- package/lib/WebView.macos.d.ts +6 -0
- package/lib/WebView.macos.js +1 -0
- package/lib/WebView.styles.d.ts +37 -11
- package/lib/WebView.styles.js +1 -33
- package/lib/WebView.windows.d.ts +17 -0
- package/lib/WebView.windows.js +1 -0
- package/lib/WebViewNativeComponent.macos.d.ts +3 -0
- package/lib/WebViewNativeComponent.macos.js +1 -0
- package/lib/WebViewNativeComponent.windows.d.ts +3 -0
- package/lib/WebViewNativeComponent.windows.js +1 -0
- package/lib/WebViewShared.d.ts +30 -9
- package/lib/WebViewShared.js +1 -174
- package/lib/WebViewTypes.d.ts +514 -98
- package/lib/WebViewTypes.js +1 -6
- package/lib/index.d.ts +0 -1
- package/lib/index.js +1 -3
- package/lib/validation.d.ts +3 -0
- package/lib/validation.js +1 -0
- package/package.json +57 -33
- package/react-native-webview.podspec +32 -5
- package/react-native.config.js +22 -18
- package/src/NativeRNCWebViewModule.ts +13 -0
- package/src/RNCWebViewNativeComponent.ts +348 -0
- package/src/WebView.android.tsx +345 -0
- package/src/WebView.ios.tsx +341 -0
- package/src/WebView.macos.tsx +252 -0
- package/src/WebView.styles.ts +41 -0
- package/src/WebView.tsx +25 -0
- package/src/WebView.windows.tsx +217 -0
- package/src/WebViewNativeComponent.macos.ts +7 -0
- package/src/WebViewNativeComponent.windows.ts +8 -0
- package/src/WebViewShared.tsx +476 -0
- package/src/WebViewTypes.ts +1402 -0
- package/src/__tests__/WebViewShared-test.js +323 -0
- package/src/__tests__/__snapshots__/WebViewShared-test.js.snap +8 -0
- package/src/__tests__/validation-test.js +38 -0
- package/src/index.ts +4 -0
- package/src/validation.ts +20 -0
- package/android/.editorconfig +0 -6
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java +0 -1408
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewModule.java +0 -506
- package/apple/RNCWebViewManager.m +0 -278
- package/lib/WebViewNativeComponent.android.d.ts +0 -4
- package/lib/WebViewNativeComponent.android.js +0 -3
- package/lib/WebViewNativeComponent.ios.d.ts +0 -4
- package/lib/WebViewNativeComponent.ios.js +0 -3
|
@@ -1,1408 +0,0 @@
|
|
|
1
|
-
package com.reactnativecommunity.webview;
|
|
2
|
-
|
|
3
|
-
import android.annotation.SuppressLint;
|
|
4
|
-
import android.annotation.TargetApi;
|
|
5
|
-
import android.app.Activity;
|
|
6
|
-
import android.app.DownloadManager;
|
|
7
|
-
import android.content.Context;
|
|
8
|
-
import android.content.pm.ActivityInfo;
|
|
9
|
-
import android.content.pm.PackageManager;
|
|
10
|
-
import android.graphics.Bitmap;
|
|
11
|
-
import android.graphics.Color;
|
|
12
|
-
import android.Manifest;
|
|
13
|
-
import android.net.http.SslError;
|
|
14
|
-
import android.net.Uri;
|
|
15
|
-
import android.os.Build;
|
|
16
|
-
import android.os.Environment;
|
|
17
|
-
import android.os.Message;
|
|
18
|
-
import android.os.SystemClock;
|
|
19
|
-
import android.text.TextUtils;
|
|
20
|
-
import android.util.Log;
|
|
21
|
-
import android.view.Gravity;
|
|
22
|
-
import android.view.MotionEvent;
|
|
23
|
-
import android.view.View;
|
|
24
|
-
import android.view.ViewGroup;
|
|
25
|
-
import android.view.ViewGroup.LayoutParams;
|
|
26
|
-
import android.view.WindowManager;
|
|
27
|
-
import android.webkit.ConsoleMessage;
|
|
28
|
-
import android.webkit.CookieManager;
|
|
29
|
-
import android.webkit.DownloadListener;
|
|
30
|
-
import android.webkit.GeolocationPermissions;
|
|
31
|
-
import android.webkit.HttpAuthHandler;
|
|
32
|
-
import android.webkit.JavascriptInterface;
|
|
33
|
-
import android.webkit.SslErrorHandler;
|
|
34
|
-
import android.webkit.PermissionRequest;
|
|
35
|
-
import android.webkit.ValueCallback;
|
|
36
|
-
import android.webkit.WebChromeClient;
|
|
37
|
-
import android.webkit.WebResourceRequest;
|
|
38
|
-
import android.webkit.WebResourceResponse;
|
|
39
|
-
import android.webkit.WebSettings;
|
|
40
|
-
import android.webkit.WebView;
|
|
41
|
-
import android.webkit.WebViewClient;
|
|
42
|
-
import android.widget.FrameLayout;
|
|
43
|
-
|
|
44
|
-
import androidx.annotation.NonNull;
|
|
45
|
-
import androidx.annotation.Nullable;
|
|
46
|
-
import androidx.annotation.RequiresApi;
|
|
47
|
-
import androidx.core.content.ContextCompat;
|
|
48
|
-
import androidx.core.util.Pair;
|
|
49
|
-
import androidx.webkit.WebSettingsCompat;
|
|
50
|
-
import androidx.webkit.WebViewFeature;
|
|
51
|
-
|
|
52
|
-
import com.facebook.common.logging.FLog;
|
|
53
|
-
import com.facebook.react.modules.core.PermissionAwareActivity;
|
|
54
|
-
import com.facebook.react.modules.core.PermissionListener;
|
|
55
|
-
import com.facebook.react.views.scroll.ScrollEvent;
|
|
56
|
-
import com.facebook.react.views.scroll.ScrollEventType;
|
|
57
|
-
import com.facebook.react.views.scroll.OnScrollDispatchHelper;
|
|
58
|
-
import com.facebook.react.bridge.Arguments;
|
|
59
|
-
import com.facebook.react.bridge.CatalystInstance;
|
|
60
|
-
import com.facebook.react.bridge.LifecycleEventListener;
|
|
61
|
-
import com.facebook.react.bridge.ReactContext;
|
|
62
|
-
import com.facebook.react.bridge.ReadableArray;
|
|
63
|
-
import com.facebook.react.bridge.ReadableMap;
|
|
64
|
-
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
|
65
|
-
import com.facebook.react.bridge.WritableMap;
|
|
66
|
-
import com.facebook.react.bridge.WritableNativeArray;
|
|
67
|
-
import com.facebook.react.bridge.WritableNativeMap;
|
|
68
|
-
import com.facebook.react.common.MapBuilder;
|
|
69
|
-
import com.facebook.react.common.build.ReactBuildConfig;
|
|
70
|
-
import com.facebook.react.module.annotations.ReactModule;
|
|
71
|
-
import com.facebook.react.uimanager.SimpleViewManager;
|
|
72
|
-
import com.facebook.react.uimanager.ThemedReactContext;
|
|
73
|
-
import com.facebook.react.uimanager.UIManagerModule;
|
|
74
|
-
import com.facebook.react.uimanager.annotations.ReactProp;
|
|
75
|
-
import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
|
|
76
|
-
import com.facebook.react.uimanager.events.Event;
|
|
77
|
-
import com.facebook.react.uimanager.events.EventDispatcher;
|
|
78
|
-
import com.reactnativecommunity.webview.RNCWebViewModule.ShouldOverrideUrlLoadingLock.ShouldOverrideCallbackState;
|
|
79
|
-
import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
|
|
80
|
-
import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
|
|
81
|
-
import com.reactnativecommunity.webview.events.TopLoadingProgressEvent;
|
|
82
|
-
import com.reactnativecommunity.webview.events.TopLoadingStartEvent;
|
|
83
|
-
import com.reactnativecommunity.webview.events.TopMessageEvent;
|
|
84
|
-
import com.reactnativecommunity.webview.events.TopShouldStartLoadWithRequestEvent;
|
|
85
|
-
|
|
86
|
-
import org.json.JSONException;
|
|
87
|
-
import org.json.JSONObject;
|
|
88
|
-
|
|
89
|
-
import java.io.UnsupportedEncodingException;
|
|
90
|
-
import java.lang.IllegalArgumentException;
|
|
91
|
-
import java.net.MalformedURLException;
|
|
92
|
-
import java.net.URL;
|
|
93
|
-
import java.net.URLEncoder;
|
|
94
|
-
import java.util.ArrayList;
|
|
95
|
-
import java.util.Collections;
|
|
96
|
-
import java.util.HashMap;
|
|
97
|
-
import java.util.List;
|
|
98
|
-
import java.util.Locale;
|
|
99
|
-
import java.util.Map;
|
|
100
|
-
import java.util.concurrent.atomic.AtomicReference;
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Manages instances of {@link WebView}
|
|
104
|
-
* <p>
|
|
105
|
-
* Can accept following commands:
|
|
106
|
-
* - GO_BACK
|
|
107
|
-
* - GO_FORWARD
|
|
108
|
-
* - RELOAD
|
|
109
|
-
* - LOAD_URL
|
|
110
|
-
* <p>
|
|
111
|
-
* {@link WebView} instances could emit following direct events:
|
|
112
|
-
* - topLoadingFinish
|
|
113
|
-
* - topLoadingStart
|
|
114
|
-
* - topLoadingStart
|
|
115
|
-
* - topLoadingProgress
|
|
116
|
-
* - topShouldStartLoadWithRequest
|
|
117
|
-
* <p>
|
|
118
|
-
* Each event will carry the following properties:
|
|
119
|
-
* - target - view's react tag
|
|
120
|
-
* - url - url set for the webview
|
|
121
|
-
* - loading - whether webview is in a loading state
|
|
122
|
-
* - title - title of the current page
|
|
123
|
-
* - canGoBack - boolean, whether there is anything on a history stack to go back
|
|
124
|
-
* - canGoForward - boolean, whether it is possible to request GO_FORWARD command
|
|
125
|
-
*/
|
|
126
|
-
@ReactModule(name = RNCWebViewManager.REACT_CLASS)
|
|
127
|
-
public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|
128
|
-
private static final String TAG = "RNCWebViewManager";
|
|
129
|
-
|
|
130
|
-
public static final int COMMAND_GO_BACK = 1;
|
|
131
|
-
public static final int COMMAND_GO_FORWARD = 2;
|
|
132
|
-
public static final int COMMAND_RELOAD = 3;
|
|
133
|
-
public static final int COMMAND_STOP_LOADING = 4;
|
|
134
|
-
public static final int COMMAND_POST_MESSAGE = 5;
|
|
135
|
-
public static final int COMMAND_INJECT_JAVASCRIPT = 6;
|
|
136
|
-
public static final int COMMAND_LOAD_URL = 7;
|
|
137
|
-
public static final int COMMAND_FOCUS = 8;
|
|
138
|
-
|
|
139
|
-
// android commands
|
|
140
|
-
public static final int COMMAND_CLEAR_FORM_DATA = 1000;
|
|
141
|
-
public static final int COMMAND_CLEAR_CACHE = 1001;
|
|
142
|
-
public static final int COMMAND_CLEAR_HISTORY = 1002;
|
|
143
|
-
|
|
144
|
-
protected static final String REACT_CLASS = "RNCWebView";
|
|
145
|
-
protected static final String HTML_ENCODING = "UTF-8";
|
|
146
|
-
protected static final String HTML_MIME_TYPE = "text/html";
|
|
147
|
-
protected static final String JAVASCRIPT_INTERFACE = "ReactNativeWebView";
|
|
148
|
-
protected static final String HTTP_METHOD_POST = "POST";
|
|
149
|
-
// Use `webView.loadUrl("about:blank")` to reliably reset the view
|
|
150
|
-
// state and release page resources (including any running JavaScript).
|
|
151
|
-
protected static final String BLANK_URL = "about:blank";
|
|
152
|
-
protected static final int SHOULD_OVERRIDE_URL_LOADING_TIMEOUT = 250;
|
|
153
|
-
protected static final String DEFAULT_DOWNLOADING_MESSAGE = "Downloading";
|
|
154
|
-
protected static final String DEFAULT_LACK_PERMISSION_TO_DOWNLOAD_MESSAGE =
|
|
155
|
-
"Cannot download files as permission was denied. Please provide permission to write to storage, in order to download files.";
|
|
156
|
-
protected WebViewConfig mWebViewConfig;
|
|
157
|
-
|
|
158
|
-
protected RNCWebChromeClient mWebChromeClient = null;
|
|
159
|
-
protected boolean mAllowsProtectedMedia = false;
|
|
160
|
-
protected @Nullable String mUserAgent = null;
|
|
161
|
-
protected @Nullable String mUserAgentWithApplicationName = null;
|
|
162
|
-
protected @Nullable String mDownloadingMessage = null;
|
|
163
|
-
protected @Nullable String mLackPermissionToDownloadMessage = null;
|
|
164
|
-
|
|
165
|
-
public RNCWebViewManager() {
|
|
166
|
-
mWebViewConfig = new WebViewConfig() {
|
|
167
|
-
public void configWebView(WebView webView) {
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
public RNCWebViewManager(WebViewConfig webViewConfig) {
|
|
173
|
-
mWebViewConfig = webViewConfig;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
@Override
|
|
177
|
-
public String getName() {
|
|
178
|
-
return REACT_CLASS;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
protected RNCWebView createRNCWebViewInstance(ThemedReactContext reactContext) {
|
|
182
|
-
return new RNCWebView(reactContext);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
@Override
|
|
186
|
-
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
187
|
-
protected WebView createViewInstance(ThemedReactContext reactContext) {
|
|
188
|
-
RNCWebView webView = createRNCWebViewInstance(reactContext);
|
|
189
|
-
setupWebChromeClient(reactContext, webView);
|
|
190
|
-
reactContext.addLifecycleEventListener(webView);
|
|
191
|
-
mWebViewConfig.configWebView(webView);
|
|
192
|
-
WebSettings settings = webView.getSettings();
|
|
193
|
-
settings.setBuiltInZoomControls(true);
|
|
194
|
-
settings.setDisplayZoomControls(false);
|
|
195
|
-
settings.setDomStorageEnabled(true);
|
|
196
|
-
settings.setSupportMultipleWindows(true);
|
|
197
|
-
|
|
198
|
-
settings.setAllowFileAccess(false);
|
|
199
|
-
settings.setAllowContentAccess(false);
|
|
200
|
-
settings.setAllowFileAccessFromFileURLs(false);
|
|
201
|
-
settings.setJavaScriptCanOpenWindowsAutomatically(false);
|
|
202
|
-
settings.setAllowUniversalAccessFromFileURLs(false);
|
|
203
|
-
setMixedContentMode(webView, "never");
|
|
204
|
-
|
|
205
|
-
// Fixes broken full-screen modals/galleries due to body height being 0.
|
|
206
|
-
webView.setLayoutParams(
|
|
207
|
-
new LayoutParams(LayoutParams.MATCH_PARENT,
|
|
208
|
-
LayoutParams.MATCH_PARENT));
|
|
209
|
-
|
|
210
|
-
if (ReactBuildConfig.DEBUG) {
|
|
211
|
-
WebView.setWebContentsDebuggingEnabled(true);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return webView;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
private String getDownloadingMessage() {
|
|
218
|
-
return mDownloadingMessage == null ? DEFAULT_DOWNLOADING_MESSAGE : mDownloadingMessage;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
private String getLackPermissionToDownloadMessage() {
|
|
222
|
-
return mDownloadingMessage == null ? DEFAULT_LACK_PERMISSION_TO_DOWNLOAD_MESSAGE : mLackPermissionToDownloadMessage;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
@ReactProp(name = "javaScriptEnabled")
|
|
226
|
-
public void setJavaScriptEnabled(WebView view, boolean enabled) {
|
|
227
|
-
view.getSettings().setJavaScriptEnabled(enabled);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
@ReactProp(name = "setBuiltInZoomControls")
|
|
231
|
-
public void setBuiltInZoomControls(WebView view, boolean enabled) {
|
|
232
|
-
view.getSettings().setBuiltInZoomControls(enabled);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
@ReactProp(name = "setDisplayZoomControls")
|
|
236
|
-
public void setDisplayZoomControls(WebView view, boolean enabled) {
|
|
237
|
-
view.getSettings().setDisplayZoomControls(enabled);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
@ReactProp(name = "setSupportMultipleWindows")
|
|
241
|
-
public void setSupportMultipleWindows(WebView view, boolean enabled){
|
|
242
|
-
view.getSettings().setSupportMultipleWindows(enabled);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
@ReactProp(name = "showsHorizontalScrollIndicator")
|
|
246
|
-
public void setShowsHorizontalScrollIndicator(WebView view, boolean enabled) {
|
|
247
|
-
view.setHorizontalScrollBarEnabled(enabled);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
@ReactProp(name = "showsVerticalScrollIndicator")
|
|
251
|
-
public void setShowsVerticalScrollIndicator(WebView view, boolean enabled) {
|
|
252
|
-
view.setVerticalScrollBarEnabled(enabled);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
@ReactProp(name = "downloadingMessage")
|
|
256
|
-
public void setDownloadingMessage(WebView view, String message) {
|
|
257
|
-
mDownloadingMessage = message;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
@ReactProp(name = "lackPermissionToDownloadMessage")
|
|
261
|
-
public void setLackPermissionToDownlaodMessage(WebView view, String message) {
|
|
262
|
-
mLackPermissionToDownloadMessage = message;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
@ReactProp(name = "cacheEnabled")
|
|
266
|
-
public void setCacheEnabled(WebView view, boolean enabled) {
|
|
267
|
-
view.getSettings().setCacheMode(enabled ? WebSettings.LOAD_DEFAULT : WebSettings.LOAD_NO_CACHE);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
@ReactProp(name = "cacheMode")
|
|
271
|
-
public void setCacheMode(WebView view, String cacheModeString) {
|
|
272
|
-
Integer cacheMode;
|
|
273
|
-
switch (cacheModeString) {
|
|
274
|
-
case "LOAD_CACHE_ONLY":
|
|
275
|
-
cacheMode = WebSettings.LOAD_CACHE_ONLY;
|
|
276
|
-
break;
|
|
277
|
-
case "LOAD_CACHE_ELSE_NETWORK":
|
|
278
|
-
cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK;
|
|
279
|
-
break;
|
|
280
|
-
case "LOAD_NO_CACHE":
|
|
281
|
-
cacheMode = WebSettings.LOAD_NO_CACHE;
|
|
282
|
-
break;
|
|
283
|
-
case "LOAD_DEFAULT":
|
|
284
|
-
default:
|
|
285
|
-
cacheMode = WebSettings.LOAD_DEFAULT;
|
|
286
|
-
break;
|
|
287
|
-
}
|
|
288
|
-
view.getSettings().setCacheMode(cacheMode);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
@ReactProp(name = "androidHardwareAccelerationDisabled")
|
|
292
|
-
public void setHardwareAccelerationDisabled(WebView view, boolean disabled) {
|
|
293
|
-
if (disabled) {
|
|
294
|
-
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
@ReactProp(name = "androidLayerType")
|
|
299
|
-
public void setLayerType(WebView view, String layerTypeString) {
|
|
300
|
-
int layerType = View.LAYER_TYPE_NONE;
|
|
301
|
-
switch (layerTypeString) {
|
|
302
|
-
case "hardware":
|
|
303
|
-
layerType = View.LAYER_TYPE_HARDWARE;
|
|
304
|
-
break;
|
|
305
|
-
case "software":
|
|
306
|
-
layerType = View.LAYER_TYPE_SOFTWARE;
|
|
307
|
-
break;
|
|
308
|
-
}
|
|
309
|
-
view.setLayerType(layerType, null);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
@ReactProp(name = "overScrollMode")
|
|
314
|
-
public void setOverScrollMode(WebView view, String overScrollModeString) {
|
|
315
|
-
Integer overScrollMode;
|
|
316
|
-
switch (overScrollModeString) {
|
|
317
|
-
case "never":
|
|
318
|
-
overScrollMode = View.OVER_SCROLL_NEVER;
|
|
319
|
-
break;
|
|
320
|
-
case "content":
|
|
321
|
-
overScrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS;
|
|
322
|
-
break;
|
|
323
|
-
case "always":
|
|
324
|
-
default:
|
|
325
|
-
overScrollMode = View.OVER_SCROLL_ALWAYS;
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
328
|
-
view.setOverScrollMode(overScrollMode);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
@ReactProp(name = "nestedScrollEnabled")
|
|
332
|
-
public void setNestedScrollEnabled(WebView view, boolean enabled) {
|
|
333
|
-
((RNCWebView) view).setNestedScrollEnabled(enabled);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
@ReactProp(name = "thirdPartyCookiesEnabled")
|
|
337
|
-
public void setThirdPartyCookiesEnabled(WebView view, boolean enabled) {
|
|
338
|
-
CookieManager.getInstance().setAcceptThirdPartyCookies(view, enabled);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
@ReactProp(name = "textZoom")
|
|
342
|
-
public void setTextZoom(WebView view, int value) {
|
|
343
|
-
view.getSettings().setTextZoom(value);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
@ReactProp(name = "scalesPageToFit")
|
|
347
|
-
public void setScalesPageToFit(WebView view, boolean enabled) {
|
|
348
|
-
view.getSettings().setLoadWithOverviewMode(enabled);
|
|
349
|
-
view.getSettings().setUseWideViewPort(enabled);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
@ReactProp(name = "domStorageEnabled")
|
|
353
|
-
public void setDomStorageEnabled(WebView view, boolean enabled) {
|
|
354
|
-
view.getSettings().setDomStorageEnabled(enabled);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
@ReactProp(name = "userAgent")
|
|
358
|
-
public void setUserAgent(WebView view, @Nullable String userAgent) {
|
|
359
|
-
if (userAgent != null) {
|
|
360
|
-
mUserAgent = userAgent;
|
|
361
|
-
} else {
|
|
362
|
-
mUserAgent = null;
|
|
363
|
-
}
|
|
364
|
-
this.setUserAgentString(view);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
protected void setUserAgentString(WebView view) {
|
|
368
|
-
if(mUserAgent != null) {
|
|
369
|
-
view.getSettings().setUserAgentString(mUserAgent);
|
|
370
|
-
} else if(mUserAgentWithApplicationName != null) {
|
|
371
|
-
view.getSettings().setUserAgentString(mUserAgentWithApplicationName);
|
|
372
|
-
} else {
|
|
373
|
-
// handle unsets of `userAgent` prop
|
|
374
|
-
view.getSettings().setUserAgentString(WebSettings.getDefaultUserAgent(view.getContext()));
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
|
379
|
-
@ReactProp(name = "mediaPlaybackRequiresUserAction")
|
|
380
|
-
public void setMediaPlaybackRequiresUserAction(WebView view, boolean requires) {
|
|
381
|
-
view.getSettings().setMediaPlaybackRequiresUserGesture(requires);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
@ReactProp(name = "saveFormDataDisabled")
|
|
385
|
-
public void setSaveFormDataDisabled(WebView view, boolean disable) {
|
|
386
|
-
view.getSettings().setSaveFormData(!disable);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
@ReactProp(name = "injectedJavaScript")
|
|
390
|
-
public void setInjectedJavaScript(WebView view, @Nullable String injectedJavaScript) {
|
|
391
|
-
((RNCWebView) view).setInjectedJavaScript(injectedJavaScript);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
@ReactProp(name = "injectedJavaScriptBeforeContentLoaded")
|
|
395
|
-
public void setInjectedJavaScriptBeforeContentLoaded(WebView view, @Nullable String injectedJavaScriptBeforeContentLoaded) {
|
|
396
|
-
((RNCWebView) view).setInjectedJavaScriptBeforeContentLoaded(injectedJavaScriptBeforeContentLoaded);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
@ReactProp(name = "messagingEnabled")
|
|
400
|
-
public void setMessagingEnabled(WebView view, boolean enabled) {
|
|
401
|
-
((RNCWebView) view).setMessagingEnabled(enabled);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
@ReactProp(name = "messagingModuleName")
|
|
405
|
-
public void setMessagingModuleName(WebView view, String moduleName) {
|
|
406
|
-
((RNCWebView) view).setMessagingModuleName(moduleName);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
@ReactProp(name = "incognito")
|
|
410
|
-
public void setIncognito(WebView view, boolean enabled) {
|
|
411
|
-
// Don't do anything when incognito is disabled
|
|
412
|
-
if (!enabled) {
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Remove all previous cookies
|
|
417
|
-
CookieManager.getInstance().removeAllCookies(null);
|
|
418
|
-
|
|
419
|
-
// Disable caching
|
|
420
|
-
view.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
|
|
421
|
-
view.clearHistory();
|
|
422
|
-
view.clearCache(true);
|
|
423
|
-
|
|
424
|
-
// No form data or autofill enabled
|
|
425
|
-
view.clearFormData();
|
|
426
|
-
view.getSettings().setSavePassword(false);
|
|
427
|
-
view.getSettings().setSaveFormData(false);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
@ReactProp(name = "source")
|
|
431
|
-
public void setSource(WebView view, @Nullable ReadableMap source) {
|
|
432
|
-
if (source != null) {
|
|
433
|
-
if (source.hasKey("uri")) {
|
|
434
|
-
String url = source.getString("uri");
|
|
435
|
-
String previousUrl = view.getUrl();
|
|
436
|
-
if (previousUrl != null && previousUrl.equals(url)) {
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
if (source.hasKey("method")) {
|
|
440
|
-
String method = source.getString("method");
|
|
441
|
-
if (method.equalsIgnoreCase(HTTP_METHOD_POST)) {
|
|
442
|
-
byte[] postData = null;
|
|
443
|
-
if (source.hasKey("body")) {
|
|
444
|
-
String body = source.getString("body");
|
|
445
|
-
try {
|
|
446
|
-
postData = body.getBytes("UTF-8");
|
|
447
|
-
} catch (UnsupportedEncodingException e) {
|
|
448
|
-
postData = body.getBytes();
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
if (postData == null) {
|
|
452
|
-
postData = new byte[0];
|
|
453
|
-
}
|
|
454
|
-
view.postUrl(url, postData);
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
HashMap<String, String> headerMap = new HashMap<>();
|
|
459
|
-
if (source.hasKey("headers")) {
|
|
460
|
-
ReadableMap headers = source.getMap("headers");
|
|
461
|
-
ReadableMapKeySetIterator iter = headers.keySetIterator();
|
|
462
|
-
while (iter.hasNextKey()) {
|
|
463
|
-
String key = iter.nextKey();
|
|
464
|
-
if ("user-agent".equals(key.toLowerCase(Locale.ENGLISH))) {
|
|
465
|
-
if (view.getSettings() != null) {
|
|
466
|
-
view.getSettings().setUserAgentString(headers.getString(key));
|
|
467
|
-
}
|
|
468
|
-
} else {
|
|
469
|
-
headerMap.put(key, headers.getString(key));
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
view.loadUrl(url, headerMap);
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
view.loadUrl(BLANK_URL);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
@ReactProp(name = "basicAuthCredential")
|
|
481
|
-
public void setBasicAuthCredential(WebView view, @Nullable ReadableMap credential) {
|
|
482
|
-
@Nullable BasicAuthCredential basicAuthCredential = null;
|
|
483
|
-
if (credential != null) {
|
|
484
|
-
if (credential.hasKey("username") && credential.hasKey("password")) {
|
|
485
|
-
String username = credential.getString("username");
|
|
486
|
-
String password = credential.getString("password");
|
|
487
|
-
basicAuthCredential = new BasicAuthCredential(username, password);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
((RNCWebView) view).setBasicAuthCredential(basicAuthCredential);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
@ReactProp(name = "onContentSizeChange")
|
|
494
|
-
public void setOnContentSizeChange(WebView view, boolean sendContentSizeChangeEvents) {
|
|
495
|
-
((RNCWebView) view).setSendContentSizeChangeEvents(sendContentSizeChangeEvents);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
@ReactProp(name = "mixedContentMode")
|
|
499
|
-
public void setMixedContentMode(WebView view, @Nullable String mixedContentMode) {
|
|
500
|
-
if (mixedContentMode == null || "never".equals(mixedContentMode)) {
|
|
501
|
-
view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
|
|
502
|
-
} else if ("always".equals(mixedContentMode)) {
|
|
503
|
-
view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
|
|
504
|
-
} else if ("compatibility".equals(mixedContentMode)) {
|
|
505
|
-
view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
@ReactProp(name = "urlPrefixesForDefaultIntent")
|
|
510
|
-
public void setUrlPrefixesForDefaultIntent(
|
|
511
|
-
WebView view,
|
|
512
|
-
@Nullable ReadableArray urlPrefixesForDefaultIntent) {
|
|
513
|
-
RNCWebViewClient client = ((RNCWebView) view).getRNCWebViewClient();
|
|
514
|
-
if (client != null && urlPrefixesForDefaultIntent != null) {
|
|
515
|
-
client.setUrlPrefixesForDefaultIntent(urlPrefixesForDefaultIntent);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
@ReactProp(name = "geolocationEnabled")
|
|
520
|
-
public void setGeolocationEnabled(
|
|
521
|
-
WebView view,
|
|
522
|
-
@Nullable Boolean isGeolocationEnabled) {
|
|
523
|
-
view.getSettings().setGeolocationEnabled(isGeolocationEnabled != null && isGeolocationEnabled);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
@ReactProp(name = "onScroll")
|
|
527
|
-
public void setOnScroll(WebView view, boolean hasScrollEvent) {
|
|
528
|
-
((RNCWebView) view).setHasScrollEvent(hasScrollEvent);
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
@ReactProp(name = "forceDarkOn")
|
|
532
|
-
public void setForceDarkOn(WebView view, boolean enabled) {
|
|
533
|
-
// Only Android 10+ support dark mode
|
|
534
|
-
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
|
535
|
-
// Switch WebView dark mode
|
|
536
|
-
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
|
|
537
|
-
int forceDarkMode = enabled ? WebSettingsCompat.FORCE_DARK_ON : WebSettingsCompat.FORCE_DARK_OFF;
|
|
538
|
-
WebSettingsCompat.setForceDark(view.getSettings(), forceDarkMode);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// Set how WebView content should be darkened.
|
|
542
|
-
// PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING: checks for the "color-scheme" <meta> tag.
|
|
543
|
-
// If present, it uses media queries. If absent, it applies user-agent (automatic)
|
|
544
|
-
// More information about Force Dark Strategy can be found here:
|
|
545
|
-
// https://developer.android.com/reference/androidx/webkit/WebSettingsCompat#setForceDarkStrategy(android.webkit.WebSettings)
|
|
546
|
-
if (enabled && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
|
|
547
|
-
WebSettingsCompat.setForceDarkStrategy(view.getSettings(), WebSettingsCompat.DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
@ReactProp(name = "minimumFontSize")
|
|
553
|
-
public void setMinimumFontSize(WebView view, int fontSize) {
|
|
554
|
-
view.getSettings().setMinimumFontSize(fontSize);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
@ReactProp(name = "allowsProtectedMedia")
|
|
558
|
-
public void setAllowsProtectedMedia(WebView view, boolean enabled) {
|
|
559
|
-
// This variable is used to keep consistency
|
|
560
|
-
// in case a new WebChromeClient is created
|
|
561
|
-
// (eg. when mAllowsFullScreenVideo changes)
|
|
562
|
-
mAllowsProtectedMedia = enabled;
|
|
563
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
564
|
-
WebChromeClient client = view.getWebChromeClient();
|
|
565
|
-
if (client != null && client instanceof RNCWebChromeClient) {
|
|
566
|
-
((RNCWebChromeClient) client).setAllowsProtectedMedia(enabled);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
@Override
|
|
572
|
-
protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
|
|
573
|
-
// Do not register default touch emitter and let WebView implementation handle touches
|
|
574
|
-
view.setWebViewClient(new RNCWebViewClient());
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
@Override
|
|
578
|
-
public Map getExportedCustomDirectEventTypeConstants() {
|
|
579
|
-
Map export = super.getExportedCustomDirectEventTypeConstants();
|
|
580
|
-
if (export == null) {
|
|
581
|
-
export = MapBuilder.newHashMap();
|
|
582
|
-
}
|
|
583
|
-
// Default events but adding them here explicitly for clarity
|
|
584
|
-
export.put(TopLoadingStartEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingStart"));
|
|
585
|
-
export.put(TopLoadingFinishEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingFinish"));
|
|
586
|
-
export.put(TopLoadingErrorEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingError"));
|
|
587
|
-
export.put(TopMessageEvent.EVENT_NAME, MapBuilder.of("registrationName", "onMessage"));
|
|
588
|
-
// !Default events but adding them here explicitly for clarity
|
|
589
|
-
|
|
590
|
-
export.put(TopLoadingProgressEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingProgress"));
|
|
591
|
-
export.put(TopShouldStartLoadWithRequestEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShouldStartLoadWithRequest"));
|
|
592
|
-
export.put(ScrollEventType.getJSEventName(ScrollEventType.SCROLL), MapBuilder.of("registrationName", "onScroll"));
|
|
593
|
-
return export;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
@Override
|
|
597
|
-
public @Nullable
|
|
598
|
-
Map<String, Integer> getCommandsMap() {
|
|
599
|
-
return MapBuilder.<String, Integer>builder()
|
|
600
|
-
.put("goBack", COMMAND_GO_BACK)
|
|
601
|
-
.put("goForward", COMMAND_GO_FORWARD)
|
|
602
|
-
.put("reload", COMMAND_RELOAD)
|
|
603
|
-
.put("stopLoading", COMMAND_STOP_LOADING)
|
|
604
|
-
.put("postMessage", COMMAND_POST_MESSAGE)
|
|
605
|
-
.put("injectJavaScript", COMMAND_INJECT_JAVASCRIPT)
|
|
606
|
-
.put("loadUrl", COMMAND_LOAD_URL)
|
|
607
|
-
.put("requestFocus", COMMAND_FOCUS)
|
|
608
|
-
.put("clearFormData", COMMAND_CLEAR_FORM_DATA)
|
|
609
|
-
.put("clearCache", COMMAND_CLEAR_CACHE)
|
|
610
|
-
.put("clearHistory", COMMAND_CLEAR_HISTORY)
|
|
611
|
-
.build();
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
@Override
|
|
615
|
-
public void receiveCommand(@NonNull WebView root, String commandId, @Nullable ReadableArray args) {
|
|
616
|
-
switch (commandId) {
|
|
617
|
-
case "goBack":
|
|
618
|
-
root.goBack();
|
|
619
|
-
break;
|
|
620
|
-
case "goForward":
|
|
621
|
-
root.goForward();
|
|
622
|
-
break;
|
|
623
|
-
case "reload":
|
|
624
|
-
root.reload();
|
|
625
|
-
break;
|
|
626
|
-
case "stopLoading":
|
|
627
|
-
root.stopLoading();
|
|
628
|
-
break;
|
|
629
|
-
case "postMessage":
|
|
630
|
-
try {
|
|
631
|
-
RNCWebView reactWebView = (RNCWebView) root;
|
|
632
|
-
JSONObject eventInitDict = new JSONObject();
|
|
633
|
-
eventInitDict.put("data", args.getString(0));
|
|
634
|
-
reactWebView.evaluateJavascriptWithFallback("document.dispatchEvent(new MessageEvent('message', " + eventInitDict.toString() + "));");
|
|
635
|
-
} catch (JSONException e) {
|
|
636
|
-
throw new RuntimeException(e);
|
|
637
|
-
}
|
|
638
|
-
break;
|
|
639
|
-
case "injectJavaScript":
|
|
640
|
-
RNCWebView reactWebView = (RNCWebView) root;
|
|
641
|
-
reactWebView.evaluateJavascriptWithFallback(args.getString(0));
|
|
642
|
-
break;
|
|
643
|
-
case "loadUrl":
|
|
644
|
-
if (args == null) {
|
|
645
|
-
throw new RuntimeException("Arguments for loading an url are null!");
|
|
646
|
-
}
|
|
647
|
-
((RNCWebView) root).progressChangedFilter.setWaitingForCommandLoadUrl(false);
|
|
648
|
-
root.loadUrl(args.getString(0));
|
|
649
|
-
break;
|
|
650
|
-
case "requestFocus":
|
|
651
|
-
root.requestFocus();
|
|
652
|
-
break;
|
|
653
|
-
case "clearFormData":
|
|
654
|
-
root.clearFormData();
|
|
655
|
-
break;
|
|
656
|
-
case "clearCache":
|
|
657
|
-
boolean includeDiskFiles = args != null && args.getBoolean(0);
|
|
658
|
-
root.clearCache(includeDiskFiles);
|
|
659
|
-
break;
|
|
660
|
-
case "clearHistory":
|
|
661
|
-
root.clearHistory();
|
|
662
|
-
break;
|
|
663
|
-
}
|
|
664
|
-
super.receiveCommand(root, commandId, args);
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
@Override
|
|
668
|
-
public void onDropViewInstance(WebView webView) {
|
|
669
|
-
super.onDropViewInstance(webView);
|
|
670
|
-
((ThemedReactContext) webView.getContext()).removeLifecycleEventListener((RNCWebView) webView);
|
|
671
|
-
((RNCWebView) webView).cleanupCallbacksAndDestroy();
|
|
672
|
-
mWebChromeClient = null;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
public static RNCWebViewModule getModule(ReactContext reactContext) {
|
|
676
|
-
return reactContext.getNativeModule(RNCWebViewModule.class);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
protected void setupWebChromeClient(ReactContext reactContext, WebView webView) {
|
|
680
|
-
Activity activity = reactContext.getCurrentActivity();
|
|
681
|
-
if (mWebChromeClient != null) {
|
|
682
|
-
mWebChromeClient.onHideCustomView();
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
mWebChromeClient = new RNCWebChromeClient(reactContext, webView) {
|
|
686
|
-
@Override
|
|
687
|
-
public Bitmap getDefaultVideoPoster() {
|
|
688
|
-
return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
|
|
689
|
-
}
|
|
690
|
-
};
|
|
691
|
-
mWebChromeClient.setAllowsProtectedMedia(mAllowsProtectedMedia);
|
|
692
|
-
webView.setWebChromeClient(mWebChromeClient);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
protected static class RNCWebViewClient extends WebViewClient {
|
|
696
|
-
|
|
697
|
-
protected boolean mLastLoadFailed = false;
|
|
698
|
-
protected @Nullable
|
|
699
|
-
ReadableArray mUrlPrefixesForDefaultIntent;
|
|
700
|
-
protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;
|
|
701
|
-
protected @Nullable String ignoreErrFailedForThisURL = null;
|
|
702
|
-
protected @Nullable BasicAuthCredential basicAuthCredential = null;
|
|
703
|
-
|
|
704
|
-
public void setIgnoreErrFailedForThisURL(@Nullable String url) {
|
|
705
|
-
ignoreErrFailedForThisURL = url;
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
public void setBasicAuthCredential(@Nullable BasicAuthCredential credential) {
|
|
709
|
-
basicAuthCredential = credential;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
@Override
|
|
713
|
-
public void onPageFinished(WebView webView, String url) {
|
|
714
|
-
super.onPageFinished(webView, url);
|
|
715
|
-
|
|
716
|
-
if (!mLastLoadFailed) {
|
|
717
|
-
RNCWebView reactWebView = (RNCWebView) webView;
|
|
718
|
-
|
|
719
|
-
reactWebView.callInjectedJavaScript();
|
|
720
|
-
|
|
721
|
-
emitFinishEvent(webView, url);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
@Override
|
|
726
|
-
public void onPageStarted(WebView webView, String url, Bitmap favicon) {
|
|
727
|
-
super.onPageStarted(webView, url, favicon);
|
|
728
|
-
mLastLoadFailed = false;
|
|
729
|
-
|
|
730
|
-
RNCWebView reactWebView = (RNCWebView) webView;
|
|
731
|
-
reactWebView.callInjectedJavaScriptBeforeContentLoaded();
|
|
732
|
-
|
|
733
|
-
((RNCWebView) webView).dispatchEvent(
|
|
734
|
-
webView,
|
|
735
|
-
new TopLoadingStartEvent(
|
|
736
|
-
webView.getId(),
|
|
737
|
-
createWebViewEvent(webView, url)));
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
@Override
|
|
741
|
-
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
|
742
|
-
final RNCWebView rncWebView = (RNCWebView) view;
|
|
743
|
-
final boolean isJsDebugging = ((ReactContext) view.getContext()).getJavaScriptContextHolder().get() == 0;
|
|
744
|
-
|
|
745
|
-
if (!isJsDebugging && rncWebView.mCatalystInstance != null) {
|
|
746
|
-
final Pair<Integer, AtomicReference<ShouldOverrideCallbackState>> lock = RNCWebViewModule.shouldOverrideUrlLoadingLock.getNewLock();
|
|
747
|
-
final int lockIdentifier = lock.first;
|
|
748
|
-
final AtomicReference<ShouldOverrideCallbackState> lockObject = lock.second;
|
|
749
|
-
|
|
750
|
-
final WritableMap event = createWebViewEvent(view, url);
|
|
751
|
-
event.putInt("lockIdentifier", lockIdentifier);
|
|
752
|
-
rncWebView.sendDirectMessage("onShouldStartLoadWithRequest", event);
|
|
753
|
-
|
|
754
|
-
try {
|
|
755
|
-
assert lockObject != null;
|
|
756
|
-
synchronized (lockObject) {
|
|
757
|
-
final long startTime = SystemClock.elapsedRealtime();
|
|
758
|
-
while (lockObject.get() == ShouldOverrideCallbackState.UNDECIDED) {
|
|
759
|
-
if (SystemClock.elapsedRealtime() - startTime > SHOULD_OVERRIDE_URL_LOADING_TIMEOUT) {
|
|
760
|
-
FLog.w(TAG, "Did not receive response to shouldOverrideUrlLoading in time, defaulting to allow loading.");
|
|
761
|
-
RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
|
|
762
|
-
return false;
|
|
763
|
-
}
|
|
764
|
-
lockObject.wait(SHOULD_OVERRIDE_URL_LOADING_TIMEOUT);
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
} catch (InterruptedException e) {
|
|
768
|
-
FLog.e(TAG, "shouldOverrideUrlLoading was interrupted while waiting for result.", e);
|
|
769
|
-
RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
|
|
770
|
-
return false;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
final boolean shouldOverride = lockObject.get() == ShouldOverrideCallbackState.SHOULD_OVERRIDE;
|
|
774
|
-
RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
|
|
775
|
-
|
|
776
|
-
return shouldOverride;
|
|
777
|
-
} else {
|
|
778
|
-
FLog.w(TAG, "Couldn't use blocking synchronous call for onShouldStartLoadWithRequest due to debugging or missing Catalyst instance, falling back to old event-and-load.");
|
|
779
|
-
progressChangedFilter.setWaitingForCommandLoadUrl(true);
|
|
780
|
-
((RNCWebView) view).dispatchEvent(
|
|
781
|
-
view,
|
|
782
|
-
new TopShouldStartLoadWithRequestEvent(
|
|
783
|
-
view.getId(),
|
|
784
|
-
createWebViewEvent(view, url)));
|
|
785
|
-
return true;
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
@TargetApi(Build.VERSION_CODES.N)
|
|
790
|
-
@Override
|
|
791
|
-
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
|
792
|
-
final String url = request.getUrl().toString();
|
|
793
|
-
return this.shouldOverrideUrlLoading(view, url);
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
@Override
|
|
797
|
-
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
|
|
798
|
-
if (basicAuthCredential != null) {
|
|
799
|
-
handler.proceed(basicAuthCredential.username, basicAuthCredential.password);
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
super.onReceivedHttpAuthRequest(view, handler, host, realm);
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
@Override
|
|
806
|
-
public void onReceivedSslError(final WebView webView, final SslErrorHandler handler, final SslError error) {
|
|
807
|
-
// onReceivedSslError is called for most requests, per Android docs: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%2520android.webkit.SslErrorHandler,%2520android.net.http.SslError)
|
|
808
|
-
// WebView.getUrl() will return the top-level window URL.
|
|
809
|
-
// If a top-level navigation triggers this error handler, the top-level URL will be the failing URL (not the URL of the currently-rendered page).
|
|
810
|
-
// This is desired behavior. We later use these values to determine whether the request is a top-level navigation or a subresource request.
|
|
811
|
-
String topWindowUrl = webView.getUrl();
|
|
812
|
-
String failingUrl = error.getUrl();
|
|
813
|
-
|
|
814
|
-
// Cancel request after obtaining top-level URL.
|
|
815
|
-
// If request is cancelled before obtaining top-level URL, undesired behavior may occur.
|
|
816
|
-
// Undesired behavior: Return value of WebView.getUrl() may be the current URL instead of the failing URL.
|
|
817
|
-
handler.cancel();
|
|
818
|
-
|
|
819
|
-
if (!topWindowUrl.equalsIgnoreCase(failingUrl)) {
|
|
820
|
-
// If error is not due to top-level navigation, then do not call onReceivedError()
|
|
821
|
-
Log.w(TAG, "Resource blocked from loading due to SSL error. Blocked URL: "+failingUrl);
|
|
822
|
-
return;
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
int code = error.getPrimaryError();
|
|
826
|
-
String description = "";
|
|
827
|
-
String descriptionPrefix = "SSL error: ";
|
|
828
|
-
|
|
829
|
-
// https://developer.android.com/reference/android/net/http/SslError.html
|
|
830
|
-
switch (code) {
|
|
831
|
-
case SslError.SSL_DATE_INVALID:
|
|
832
|
-
description = "The date of the certificate is invalid";
|
|
833
|
-
break;
|
|
834
|
-
case SslError.SSL_EXPIRED:
|
|
835
|
-
description = "The certificate has expired";
|
|
836
|
-
break;
|
|
837
|
-
case SslError.SSL_IDMISMATCH:
|
|
838
|
-
description = "Hostname mismatch";
|
|
839
|
-
break;
|
|
840
|
-
case SslError.SSL_INVALID:
|
|
841
|
-
description = "A generic error occurred";
|
|
842
|
-
break;
|
|
843
|
-
case SslError.SSL_NOTYETVALID:
|
|
844
|
-
description = "The certificate is not yet valid";
|
|
845
|
-
break;
|
|
846
|
-
case SslError.SSL_UNTRUSTED:
|
|
847
|
-
description = "The certificate authority is not trusted";
|
|
848
|
-
break;
|
|
849
|
-
default:
|
|
850
|
-
description = "Unknown SSL Error";
|
|
851
|
-
break;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
description = descriptionPrefix + description;
|
|
855
|
-
|
|
856
|
-
this.onReceivedError(
|
|
857
|
-
webView,
|
|
858
|
-
code,
|
|
859
|
-
description,
|
|
860
|
-
failingUrl
|
|
861
|
-
);
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
@Override
|
|
865
|
-
public void onReceivedError(
|
|
866
|
-
WebView webView,
|
|
867
|
-
int errorCode,
|
|
868
|
-
String description,
|
|
869
|
-
String failingUrl) {
|
|
870
|
-
|
|
871
|
-
if (ignoreErrFailedForThisURL != null
|
|
872
|
-
&& failingUrl.equals(ignoreErrFailedForThisURL)
|
|
873
|
-
&& errorCode == -1
|
|
874
|
-
&& description.equals("net::ERR_FAILED")) {
|
|
875
|
-
|
|
876
|
-
// This is a workaround for a bug in the WebView.
|
|
877
|
-
// See these chromium issues for more context:
|
|
878
|
-
// https://bugs.chromium.org/p/chromium/issues/detail?id=1023678
|
|
879
|
-
// https://bugs.chromium.org/p/chromium/issues/detail?id=1050635
|
|
880
|
-
// This entire commit should be reverted once this bug is resolved in chromium.
|
|
881
|
-
setIgnoreErrFailedForThisURL(null);
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
super.onReceivedError(webView, errorCode, description, failingUrl);
|
|
886
|
-
mLastLoadFailed = true;
|
|
887
|
-
|
|
888
|
-
// In case of an error JS side expect to get a finish event first, and then get an error event
|
|
889
|
-
// Android WebView does it in the opposite way, so we need to simulate that behavior
|
|
890
|
-
emitFinishEvent(webView, failingUrl);
|
|
891
|
-
|
|
892
|
-
WritableMap eventData = createWebViewEvent(webView, failingUrl);
|
|
893
|
-
eventData.putDouble("code", errorCode);
|
|
894
|
-
eventData.putString("description", description);
|
|
895
|
-
|
|
896
|
-
((RNCWebView) webView).dispatchEvent(
|
|
897
|
-
webView,
|
|
898
|
-
new TopLoadingErrorEvent(webView.getId(), eventData));
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
protected void emitFinishEvent(WebView webView, String url) {
|
|
902
|
-
((RNCWebView) webView).dispatchEvent(
|
|
903
|
-
webView,
|
|
904
|
-
new TopLoadingFinishEvent(
|
|
905
|
-
webView.getId(),
|
|
906
|
-
createWebViewEvent(webView, url)));
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
protected WritableMap createWebViewEvent(WebView webView, String url) {
|
|
910
|
-
WritableMap event = Arguments.createMap();
|
|
911
|
-
event.putDouble("target", webView.getId());
|
|
912
|
-
// Don't use webView.getUrl() here, the URL isn't updated to the new value yet in callbacks
|
|
913
|
-
// like onPageFinished
|
|
914
|
-
event.putString("url", url);
|
|
915
|
-
event.putBoolean("loading", !mLastLoadFailed && webView.getProgress() != 100);
|
|
916
|
-
event.putString("title", webView.getTitle());
|
|
917
|
-
event.putBoolean("canGoBack", webView.canGoBack());
|
|
918
|
-
event.putBoolean("canGoForward", webView.canGoForward());
|
|
919
|
-
return event;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) {
|
|
923
|
-
mUrlPrefixesForDefaultIntent = specialUrls;
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
public void setProgressChangedFilter(RNCWebView.ProgressChangedFilter filter) {
|
|
927
|
-
progressChangedFilter = filter;
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
protected static class RNCWebChromeClient extends WebChromeClient implements LifecycleEventListener {
|
|
932
|
-
protected static final FrameLayout.LayoutParams FULLSCREEN_LAYOUT_PARAMS = new FrameLayout.LayoutParams(
|
|
933
|
-
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER);
|
|
934
|
-
|
|
935
|
-
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
|
936
|
-
protected static final int FULLSCREEN_SYSTEM_UI_VISIBILITY = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
|
937
|
-
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
|
938
|
-
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
|
939
|
-
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
|
940
|
-
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
|
941
|
-
View.SYSTEM_UI_FLAG_IMMERSIVE |
|
|
942
|
-
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
|
943
|
-
|
|
944
|
-
protected static final int COMMON_PERMISSION_REQUEST = 3;
|
|
945
|
-
|
|
946
|
-
protected ReactContext mReactContext;
|
|
947
|
-
protected View mWebView;
|
|
948
|
-
|
|
949
|
-
protected View mVideoView;
|
|
950
|
-
protected WebChromeClient.CustomViewCallback mCustomViewCallback;
|
|
951
|
-
|
|
952
|
-
/*
|
|
953
|
-
* - Permissions -
|
|
954
|
-
* As native permissions are asynchronously handled by the PermissionListener, many fields have
|
|
955
|
-
* to be stored to send permissions results to the webview
|
|
956
|
-
*/
|
|
957
|
-
|
|
958
|
-
// Webview camera & audio permission callback
|
|
959
|
-
protected PermissionRequest permissionRequest;
|
|
960
|
-
// Webview camera & audio permission already granted
|
|
961
|
-
protected List<String> grantedPermissions;
|
|
962
|
-
|
|
963
|
-
// Webview geolocation permission callback
|
|
964
|
-
protected GeolocationPermissions.Callback geolocationPermissionCallback;
|
|
965
|
-
// Webview geolocation permission origin callback
|
|
966
|
-
protected String geolocationPermissionOrigin;
|
|
967
|
-
|
|
968
|
-
// true if native permissions dialog is shown, false otherwise
|
|
969
|
-
protected boolean permissionsRequestShown = false;
|
|
970
|
-
// Pending Android permissions for the next request
|
|
971
|
-
protected List<String> pendingPermissions = new ArrayList<>();
|
|
972
|
-
|
|
973
|
-
protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;
|
|
974
|
-
|
|
975
|
-
// True if protected media should be allowed, false otherwise
|
|
976
|
-
protected boolean mAllowsProtectedMedia = false;
|
|
977
|
-
|
|
978
|
-
public RNCWebChromeClient(ReactContext reactContext, WebView webView) {
|
|
979
|
-
this.mReactContext = reactContext;
|
|
980
|
-
this.mWebView = webView;
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
@Override
|
|
984
|
-
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
|
|
985
|
-
|
|
986
|
-
final WebView newWebView = new WebView(view.getContext());
|
|
987
|
-
final WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
|
|
988
|
-
transport.setWebView(newWebView);
|
|
989
|
-
resultMsg.sendToTarget();
|
|
990
|
-
|
|
991
|
-
return true;
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
@Override
|
|
995
|
-
public boolean onConsoleMessage(ConsoleMessage message) {
|
|
996
|
-
if (ReactBuildConfig.DEBUG) {
|
|
997
|
-
return super.onConsoleMessage(message);
|
|
998
|
-
}
|
|
999
|
-
// Ignore console logs in non debug builds.
|
|
1000
|
-
return true;
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
@Override
|
|
1004
|
-
public void onProgressChanged(WebView webView, int newProgress) {
|
|
1005
|
-
super.onProgressChanged(webView, newProgress);
|
|
1006
|
-
final String url = webView.getUrl();
|
|
1007
|
-
if (progressChangedFilter.isWaitingForCommandLoadUrl()) {
|
|
1008
|
-
return;
|
|
1009
|
-
}
|
|
1010
|
-
WritableMap event = Arguments.createMap();
|
|
1011
|
-
event.putDouble("target", webView.getId());
|
|
1012
|
-
event.putString("title", webView.getTitle());
|
|
1013
|
-
event.putString("url", url);
|
|
1014
|
-
event.putBoolean("canGoBack", webView.canGoBack());
|
|
1015
|
-
event.putBoolean("canGoForward", webView.canGoForward());
|
|
1016
|
-
event.putDouble("progress", (float) newProgress / 100);
|
|
1017
|
-
((RNCWebView) webView).dispatchEvent(
|
|
1018
|
-
webView,
|
|
1019
|
-
new TopLoadingProgressEvent(
|
|
1020
|
-
webView.getId(),
|
|
1021
|
-
event));
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
1025
|
-
@Override
|
|
1026
|
-
public void onPermissionRequest(final PermissionRequest request) {
|
|
1027
|
-
request.deny();
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
@Override
|
|
1032
|
-
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
|
|
1033
|
-
|
|
1034
|
-
// Always deny
|
|
1035
|
-
callback.invoke(origin, false, false);
|
|
1036
|
-
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
private PermissionAwareActivity getPermissionAwareActivity() {
|
|
1040
|
-
Activity activity = mReactContext.getCurrentActivity();
|
|
1041
|
-
if (activity == null) {
|
|
1042
|
-
throw new IllegalStateException("Tried to use permissions API while not attached to an Activity.");
|
|
1043
|
-
} else if (!(activity instanceof PermissionAwareActivity)) {
|
|
1044
|
-
throw new IllegalStateException("Tried to use permissions API but the host Activity doesn't implement PermissionAwareActivity.");
|
|
1045
|
-
}
|
|
1046
|
-
return (PermissionAwareActivity) activity;
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType) {
|
|
1050
|
-
getModule(mReactContext).startPhotoPickerIntent(filePathCallback, acceptType);
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
protected void openFileChooser(ValueCallback<Uri> filePathCallback) {
|
|
1054
|
-
getModule(mReactContext).startPhotoPickerIntent(filePathCallback, "");
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) {
|
|
1058
|
-
getModule(mReactContext).startPhotoPickerIntent(filePathCallback, acceptType);
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
1062
|
-
@Override
|
|
1063
|
-
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
|
|
1064
|
-
String[] acceptTypes = fileChooserParams.getAcceptTypes();
|
|
1065
|
-
boolean allowMultiple = fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE;
|
|
1066
|
-
return getModule(mReactContext).startPhotoPickerIntent(filePathCallback, acceptTypes, allowMultiple);
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
@Override
|
|
1070
|
-
public void onHostResume() {
|
|
1071
|
-
if (mVideoView != null && mVideoView.getSystemUiVisibility() != FULLSCREEN_SYSTEM_UI_VISIBILITY) {
|
|
1072
|
-
mVideoView.setSystemUiVisibility(FULLSCREEN_SYSTEM_UI_VISIBILITY);
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
@Override
|
|
1077
|
-
public void onHostPause() { }
|
|
1078
|
-
|
|
1079
|
-
@Override
|
|
1080
|
-
public void onHostDestroy() { }
|
|
1081
|
-
|
|
1082
|
-
protected ViewGroup getRootView() {
|
|
1083
|
-
return (ViewGroup) mReactContext.getCurrentActivity().findViewById(android.R.id.content);
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
public void setProgressChangedFilter(RNCWebView.ProgressChangedFilter filter) {
|
|
1087
|
-
progressChangedFilter = filter;
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
/**
|
|
1091
|
-
* Set whether or not protected media should be allowed
|
|
1092
|
-
* /!\ Setting this to false won't revoke permission already granted to the current webpage.
|
|
1093
|
-
* In order to do so, you'd need to reload the page /!\
|
|
1094
|
-
*/
|
|
1095
|
-
public void setAllowsProtectedMedia(boolean enabled) {
|
|
1096
|
-
mAllowsProtectedMedia = enabled;
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
/**
|
|
1101
|
-
* Subclass of {@link WebView} that implements {@link LifecycleEventListener} interface in order
|
|
1102
|
-
* to call {@link WebView#destroy} on activity destroy event and also to clear the client
|
|
1103
|
-
*/
|
|
1104
|
-
protected static class RNCWebView extends WebView implements LifecycleEventListener {
|
|
1105
|
-
protected @Nullable
|
|
1106
|
-
String injectedJS;
|
|
1107
|
-
protected @Nullable
|
|
1108
|
-
String injectedJSBeforeContentLoaded;
|
|
1109
|
-
|
|
1110
|
-
protected boolean messagingEnabled = false;
|
|
1111
|
-
protected @Nullable
|
|
1112
|
-
String messagingModuleName;
|
|
1113
|
-
protected @Nullable
|
|
1114
|
-
RNCWebViewClient mRNCWebViewClient;
|
|
1115
|
-
protected @Nullable
|
|
1116
|
-
CatalystInstance mCatalystInstance;
|
|
1117
|
-
protected boolean sendContentSizeChangeEvents = false;
|
|
1118
|
-
private OnScrollDispatchHelper mOnScrollDispatchHelper;
|
|
1119
|
-
protected boolean hasScrollEvent = false;
|
|
1120
|
-
protected boolean nestedScrollEnabled = false;
|
|
1121
|
-
protected ProgressChangedFilter progressChangedFilter;
|
|
1122
|
-
|
|
1123
|
-
/**
|
|
1124
|
-
* WebView must be created with an context of the current activity
|
|
1125
|
-
* <p>
|
|
1126
|
-
* Activity Context is required for creation of dialogs internally by WebView
|
|
1127
|
-
* Reactive Native needed for access to ReactNative internal system functionality
|
|
1128
|
-
*/
|
|
1129
|
-
public RNCWebView(ThemedReactContext reactContext) {
|
|
1130
|
-
super(reactContext);
|
|
1131
|
-
this.createCatalystInstance();
|
|
1132
|
-
progressChangedFilter = new ProgressChangedFilter();
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
public void setIgnoreErrFailedForThisURL(String url) {
|
|
1136
|
-
mRNCWebViewClient.setIgnoreErrFailedForThisURL(url);
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
public void setBasicAuthCredential(BasicAuthCredential credential) {
|
|
1140
|
-
mRNCWebViewClient.setBasicAuthCredential(credential);
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
public void setSendContentSizeChangeEvents(boolean sendContentSizeChangeEvents) {
|
|
1144
|
-
this.sendContentSizeChangeEvents = sendContentSizeChangeEvents;
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
public void setHasScrollEvent(boolean hasScrollEvent) {
|
|
1148
|
-
this.hasScrollEvent = hasScrollEvent;
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
public void setNestedScrollEnabled(boolean nestedScrollEnabled) {
|
|
1152
|
-
this.nestedScrollEnabled = nestedScrollEnabled;
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
@Override
|
|
1156
|
-
public void onHostResume() {
|
|
1157
|
-
// do nothing
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
@Override
|
|
1161
|
-
public void onHostPause() {
|
|
1162
|
-
// do nothing
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
@Override
|
|
1166
|
-
public void onHostDestroy() {
|
|
1167
|
-
cleanupCallbacksAndDestroy();
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
@Override
|
|
1171
|
-
public boolean onTouchEvent(MotionEvent event) {
|
|
1172
|
-
if (this.nestedScrollEnabled) {
|
|
1173
|
-
requestDisallowInterceptTouchEvent(true);
|
|
1174
|
-
}
|
|
1175
|
-
return super.onTouchEvent(event);
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
@Override
|
|
1179
|
-
protected void onSizeChanged(int w, int h, int ow, int oh) {
|
|
1180
|
-
super.onSizeChanged(w, h, ow, oh);
|
|
1181
|
-
|
|
1182
|
-
if (sendContentSizeChangeEvents) {
|
|
1183
|
-
dispatchEvent(
|
|
1184
|
-
this,
|
|
1185
|
-
new ContentSizeChangeEvent(
|
|
1186
|
-
this.getId(),
|
|
1187
|
-
w,
|
|
1188
|
-
h
|
|
1189
|
-
)
|
|
1190
|
-
);
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
@Override
|
|
1195
|
-
public void setWebViewClient(WebViewClient client) {
|
|
1196
|
-
super.setWebViewClient(client);
|
|
1197
|
-
if (client instanceof RNCWebViewClient) {
|
|
1198
|
-
mRNCWebViewClient = (RNCWebViewClient) client;
|
|
1199
|
-
mRNCWebViewClient.setProgressChangedFilter(progressChangedFilter);
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
WebChromeClient mWebChromeClient;
|
|
1204
|
-
@Override
|
|
1205
|
-
public void setWebChromeClient(WebChromeClient client) {
|
|
1206
|
-
this.mWebChromeClient = client;
|
|
1207
|
-
super.setWebChromeClient(client);
|
|
1208
|
-
if (client instanceof RNCWebChromeClient) {
|
|
1209
|
-
((RNCWebChromeClient) client).setProgressChangedFilter(progressChangedFilter);
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
public @Nullable
|
|
1214
|
-
RNCWebViewClient getRNCWebViewClient() {
|
|
1215
|
-
return mRNCWebViewClient;
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
public void setInjectedJavaScript(@Nullable String js) {
|
|
1219
|
-
injectedJS = js;
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
public void setInjectedJavaScriptBeforeContentLoaded(@Nullable String js) {
|
|
1223
|
-
injectedJSBeforeContentLoaded = js;
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
protected RNCWebViewBridge createRNCWebViewBridge(RNCWebView webView) {
|
|
1227
|
-
return new RNCWebViewBridge(webView);
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
protected void createCatalystInstance() {
|
|
1231
|
-
ReactContext reactContext = (ReactContext) this.getContext();
|
|
1232
|
-
|
|
1233
|
-
if (reactContext != null) {
|
|
1234
|
-
mCatalystInstance = reactContext.getCatalystInstance();
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
@SuppressLint("AddJavascriptInterface")
|
|
1239
|
-
public void setMessagingEnabled(boolean enabled) {
|
|
1240
|
-
if (messagingEnabled == enabled) {
|
|
1241
|
-
return;
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
messagingEnabled = enabled;
|
|
1245
|
-
|
|
1246
|
-
if (enabled) {
|
|
1247
|
-
addJavascriptInterface(createRNCWebViewBridge(this), JAVASCRIPT_INTERFACE);
|
|
1248
|
-
} else {
|
|
1249
|
-
removeJavascriptInterface(JAVASCRIPT_INTERFACE);
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
public void setMessagingModuleName(String moduleName) {
|
|
1254
|
-
messagingModuleName = moduleName;
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
protected void evaluateJavascriptWithFallback(String script) {
|
|
1258
|
-
evaluateJavascript(script, null);
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
public void callInjectedJavaScript() {
|
|
1262
|
-
if (getSettings().getJavaScriptEnabled() &&
|
|
1263
|
-
injectedJS != null &&
|
|
1264
|
-
!TextUtils.isEmpty(injectedJS)) {
|
|
1265
|
-
evaluateJavascriptWithFallback("(function() {\n" + injectedJS + ";\n})();");
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
public void callInjectedJavaScriptBeforeContentLoaded() {
|
|
1270
|
-
if (getSettings().getJavaScriptEnabled() &&
|
|
1271
|
-
injectedJSBeforeContentLoaded != null &&
|
|
1272
|
-
!TextUtils.isEmpty(injectedJSBeforeContentLoaded)) {
|
|
1273
|
-
evaluateJavascriptWithFallback("(function() {\n" + injectedJSBeforeContentLoaded + ";\n})();");
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
public void onMessage(String message) {
|
|
1278
|
-
ReactContext reactContext = (ReactContext) this.getContext();
|
|
1279
|
-
RNCWebView mContext = this;
|
|
1280
|
-
|
|
1281
|
-
if (mRNCWebViewClient != null) {
|
|
1282
|
-
WebView webView = this;
|
|
1283
|
-
webView.post(new Runnable() {
|
|
1284
|
-
@Override
|
|
1285
|
-
public void run() {
|
|
1286
|
-
if (mRNCWebViewClient == null) {
|
|
1287
|
-
return;
|
|
1288
|
-
}
|
|
1289
|
-
WritableMap data = mRNCWebViewClient.createWebViewEvent(webView, webView.getUrl());
|
|
1290
|
-
data.putString("data", message);
|
|
1291
|
-
|
|
1292
|
-
if (mCatalystInstance != null) {
|
|
1293
|
-
mContext.sendDirectMessage("onMessage", data);
|
|
1294
|
-
} else {
|
|
1295
|
-
dispatchEvent(webView, new TopMessageEvent(webView.getId(), data));
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
});
|
|
1299
|
-
} else {
|
|
1300
|
-
WritableMap eventData = Arguments.createMap();
|
|
1301
|
-
eventData.putString("data", message);
|
|
1302
|
-
|
|
1303
|
-
if (mCatalystInstance != null) {
|
|
1304
|
-
this.sendDirectMessage("onMessage", eventData);
|
|
1305
|
-
} else {
|
|
1306
|
-
dispatchEvent(this, new TopMessageEvent(this.getId(), eventData));
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
protected void sendDirectMessage(final String method, WritableMap data) {
|
|
1312
|
-
WritableNativeMap event = new WritableNativeMap();
|
|
1313
|
-
event.putMap("nativeEvent", data);
|
|
1314
|
-
|
|
1315
|
-
WritableNativeArray params = new WritableNativeArray();
|
|
1316
|
-
params.pushMap(event);
|
|
1317
|
-
|
|
1318
|
-
mCatalystInstance.callFunction(messagingModuleName, method, params);
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
|
|
1322
|
-
super.onScrollChanged(x, y, oldX, oldY);
|
|
1323
|
-
|
|
1324
|
-
if (!hasScrollEvent) {
|
|
1325
|
-
return;
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
if (mOnScrollDispatchHelper == null) {
|
|
1329
|
-
mOnScrollDispatchHelper = new OnScrollDispatchHelper();
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
if (mOnScrollDispatchHelper.onScrollChanged(x, y)) {
|
|
1333
|
-
ScrollEvent event = ScrollEvent.obtain(
|
|
1334
|
-
this.getId(),
|
|
1335
|
-
ScrollEventType.SCROLL,
|
|
1336
|
-
x,
|
|
1337
|
-
y,
|
|
1338
|
-
mOnScrollDispatchHelper.getXFlingVelocity(),
|
|
1339
|
-
mOnScrollDispatchHelper.getYFlingVelocity(),
|
|
1340
|
-
this.computeHorizontalScrollRange(),
|
|
1341
|
-
this.computeVerticalScrollRange(),
|
|
1342
|
-
this.getWidth(),
|
|
1343
|
-
this.getHeight());
|
|
1344
|
-
|
|
1345
|
-
dispatchEvent(this, event);
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
protected void dispatchEvent(WebView webView, Event event) {
|
|
1350
|
-
ReactContext reactContext = (ReactContext) webView.getContext();
|
|
1351
|
-
EventDispatcher eventDispatcher =
|
|
1352
|
-
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
|
|
1353
|
-
eventDispatcher.dispatchEvent(event);
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
protected void cleanupCallbacksAndDestroy() {
|
|
1357
|
-
setWebViewClient(null);
|
|
1358
|
-
destroy();
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
@Override
|
|
1362
|
-
public void destroy() {
|
|
1363
|
-
if (mWebChromeClient != null) {
|
|
1364
|
-
mWebChromeClient.onHideCustomView();
|
|
1365
|
-
}
|
|
1366
|
-
super.destroy();
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
protected class RNCWebViewBridge {
|
|
1370
|
-
RNCWebView mContext;
|
|
1371
|
-
|
|
1372
|
-
RNCWebViewBridge(RNCWebView c) {
|
|
1373
|
-
mContext = c;
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
/**
|
|
1377
|
-
* This method is called whenever JavaScript running within the web view calls:
|
|
1378
|
-
* - window[JAVASCRIPT_INTERFACE].postMessage
|
|
1379
|
-
*/
|
|
1380
|
-
@JavascriptInterface
|
|
1381
|
-
public void postMessage(String message) {
|
|
1382
|
-
mContext.onMessage(message);
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
protected static class ProgressChangedFilter {
|
|
1387
|
-
private boolean waitingForCommandLoadUrl = false;
|
|
1388
|
-
|
|
1389
|
-
public void setWaitingForCommandLoadUrl(boolean isWaiting) {
|
|
1390
|
-
waitingForCommandLoadUrl = isWaiting;
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
public boolean isWaitingForCommandLoadUrl() {
|
|
1394
|
-
return waitingForCommandLoadUrl;
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
class BasicAuthCredential {
|
|
1401
|
-
String username;
|
|
1402
|
-
String password;
|
|
1403
|
-
|
|
1404
|
-
BasicAuthCredential(String username, String password) {
|
|
1405
|
-
this.username = username;
|
|
1406
|
-
this.password = password;
|
|
1407
|
-
}
|
|
1408
|
-
}
|