@modos189/nativescript-webview-x 1.0.2 → 1.1.0
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 +3 -0
- package/index.android.d.ts +36 -0
- package/index.android.js +204 -1
- package/index.android.js.map +1 -1
- package/index.d.ts +8 -4
- package/package.json +1 -1
- package/platforms/android/include.gradle +1 -0
- package/platforms/android/java/com/modos189/webviewx/LocalResourceWebViewClient.java +168 -0
package/README.md
CHANGED
|
@@ -55,12 +55,15 @@ _None._
|
|
|
55
55
|
| `userAgent` | `string` | Set a custom User-Agent string |
|
|
56
56
|
| `debugMode` | `boolean` | Enable remote WebView debugging |
|
|
57
57
|
| `supportPopups` | `boolean` | Open `window.open()` / `target="_blank"` links in a native popup. Default: `true` |
|
|
58
|
+
| `autoInjectJSBridge` | `boolean` | Inject `window.nsWebViewBridge` on every `loadFinished`. Default: `true` |
|
|
58
59
|
|
|
59
60
|
### Methods
|
|
60
61
|
|
|
61
62
|
| Method | Returns | Description |
|
|
62
63
|
| --- | --- | --- |
|
|
63
64
|
| `getTitle()` | `Promise<string | undefined>` | Return the current page title |
|
|
65
|
+
| `executeJavaScript(code: string)` | `Promise<any>` | Execute JavaScript in the page context and return the JSON-serialised result |
|
|
66
|
+
| `emitToWebView(eventName: string, data: any)` | `void` | Emit an event into the page's `nsWebViewBridge` (calls `onNativeEvent` inside the WebView) |
|
|
64
67
|
|
|
65
68
|
### Events
|
|
66
69
|
|
package/index.android.d.ts
CHANGED
|
@@ -1,9 +1,45 @@
|
|
|
1
1
|
export * from '@nativescript-community/ui-webview/index.android';
|
|
2
2
|
import { AWebView } from '@nativescript-community/ui-webview/index.android';
|
|
3
|
+
import { Property } from '@nativescript/core';
|
|
4
|
+
/**
|
|
5
|
+
* Origin rules limiting where document-start scripts are injected. Defaults to all origins.
|
|
6
|
+
*/
|
|
7
|
+
export declare const documentStartScriptAllowedOriginsProperty: Property<WebViewX, string[]>;
|
|
3
8
|
export declare class WebViewX extends AWebView {
|
|
4
9
|
static get popupNavigateEvent(): string;
|
|
10
|
+
documentStartScriptAllowedOrigins: string[];
|
|
5
11
|
private _popupClient;
|
|
12
|
+
private _localResourceClient;
|
|
13
|
+
/** Scripts injected at document start, in registration order */
|
|
14
|
+
private _documentStartScripts;
|
|
15
|
+
/** Handle to the ui-webview bridge registered as a document-start script */
|
|
16
|
+
private _bridgeScriptHandler;
|
|
6
17
|
_onPopupNavigate(url: string): boolean;
|
|
7
18
|
initNativeView(): void;
|
|
8
19
|
disposeNativeView(): void;
|
|
20
|
+
registerLocalResource(resourceName: string, path: string): void;
|
|
21
|
+
unregisterLocalResource(resourceName: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* Auto-inject a JS file at document start on every navigation (parity with iOS WKUserScript),
|
|
24
|
+
* instead of the base class's asynchronous injection on onPageFinished.
|
|
25
|
+
* Falls back to the base behaviour on WebViews without DOCUMENT_START_SCRIPT support.
|
|
26
|
+
*/
|
|
27
|
+
autoLoadJavaScriptFile(resourceName: string, filepath: string): void;
|
|
28
|
+
removeAutoLoadJavaScriptFile(resourceName: string): void;
|
|
29
|
+
private _registerDocumentStartScript;
|
|
30
|
+
/**
|
|
31
|
+
* Register the ui-webview bridge as a document-start script so window.nsWebViewBridge
|
|
32
|
+
* exists before any page script. The androidWebViewBridge Java interface (added via
|
|
33
|
+
* addJavascriptInterface) is available from the very start of the document, so the bridge
|
|
34
|
+
* has no blocking dependency and must NOT be wrapped in a DOMContentLoaded guard.
|
|
35
|
+
*/
|
|
36
|
+
private _registerBridgeDocumentStartScript;
|
|
37
|
+
private _removeBridgeDocumentStartScript;
|
|
38
|
+
/**
|
|
39
|
+
* (Re)register the bridge followed by all user scripts in registration order, so the bridge
|
|
40
|
+
* always runs first. Safe to call when handlers are already null (e.g. after view recycling)
|
|
41
|
+
*/
|
|
42
|
+
private _reRegisterAllDocumentStartScripts;
|
|
43
|
+
private _removeDocumentStartScript;
|
|
44
|
+
private _buildOriginRules;
|
|
9
45
|
}
|
package/index.android.js
CHANGED
|
@@ -1,10 +1,64 @@
|
|
|
1
1
|
export * from '@nativescript-community/ui-webview/index.android';
|
|
2
|
-
import { AWebView, supportPopupsProperty } from '@nativescript-community/ui-webview/index.android';
|
|
2
|
+
import { AWebView, autoInjectJSBridgeProperty, supportPopupsProperty } from '@nativescript-community/ui-webview/index.android';
|
|
3
|
+
import { webViewBridge } from '@nativescript-community/ui-webview/nativescript-webview-bridge-loader';
|
|
4
|
+
import { File, Property } from '@nativescript/core';
|
|
3
5
|
const POPUP_NAVIGATE_EVENT = 'popupNavigate';
|
|
6
|
+
/**
|
|
7
|
+
* Whether the current WebView provider supports WebViewCompat.addDocumentStartJavaScript.
|
|
8
|
+
* Gated by the WebView provider version, not the Android API level, so the check is deferred
|
|
9
|
+
* to first use rather than evaluated at module load time. Cached for the app lifetime since
|
|
10
|
+
* the provider version cannot change without a restart.
|
|
11
|
+
*/
|
|
12
|
+
let _documentStartScriptSupported;
|
|
13
|
+
function isDocumentStartScriptSupported() {
|
|
14
|
+
if (_documentStartScriptSupported === undefined) {
|
|
15
|
+
try {
|
|
16
|
+
_documentStartScriptSupported = androidx.webkit.WebViewFeature.isFeatureSupported(androidx.webkit.WebViewFeature.DOCUMENT_START_SCRIPT);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
_documentStartScriptSupported = false;
|
|
20
|
+
}
|
|
21
|
+
if (!_documentStartScriptSupported) {
|
|
22
|
+
console.warn('WebViewX: DOCUMENT_START_SCRIPT not supported by the installed WebView provider; autoLoadJavaScriptFile will fall back to onPageFinished injection.');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return _documentStartScriptSupported;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Wrap script so its body runs at DOMContentLoaded, mirroring iOS WKUserScript's
|
|
29
|
+
* atDocumentEnd timing. Listeners fire in registration order, so script order is preserved.
|
|
30
|
+
*/
|
|
31
|
+
function wrapDocumentStartScript(code) {
|
|
32
|
+
return `(function(){var f=function(){\n${code}\n};if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',f);}else{f();}})();`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Origin rules limiting where document-start scripts are injected. Defaults to all origins.
|
|
36
|
+
*/
|
|
37
|
+
export const documentStartScriptAllowedOriginsProperty = new Property({
|
|
38
|
+
name: 'documentStartScriptAllowedOrigins',
|
|
39
|
+
defaultValue: ['*'],
|
|
40
|
+
valueConverter(value) {
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
if (typeof value === 'string') {
|
|
45
|
+
return value
|
|
46
|
+
.split(',')
|
|
47
|
+
.map((origin) => origin.trim())
|
|
48
|
+
.filter((origin) => !!origin);
|
|
49
|
+
}
|
|
50
|
+
return ['*'];
|
|
51
|
+
},
|
|
52
|
+
});
|
|
4
53
|
export class WebViewX extends AWebView {
|
|
5
54
|
constructor() {
|
|
6
55
|
super(...arguments);
|
|
7
56
|
this._popupClient = null;
|
|
57
|
+
this._localResourceClient = null;
|
|
58
|
+
/** Scripts injected at document start, in registration order */
|
|
59
|
+
this._documentStartScripts = new Map();
|
|
60
|
+
/** Handle to the ui-webview bridge registered as a document-start script */
|
|
61
|
+
this._bridgeScriptHandler = null;
|
|
8
62
|
}
|
|
9
63
|
static get popupNavigateEvent() {
|
|
10
64
|
return POPUP_NAVIGATE_EVENT;
|
|
@@ -24,6 +78,11 @@ export class WebViewX extends AWebView {
|
|
|
24
78
|
if (!nv)
|
|
25
79
|
return;
|
|
26
80
|
const wv = nv;
|
|
81
|
+
// shouldInterceptRequest is called on Chromium's network thread; NativeScript cannot
|
|
82
|
+
// safely dispatch to V8 from there — wrap the NS client so it runs entirely in Java.
|
|
83
|
+
const nsClient = wv.getWebViewClient();
|
|
84
|
+
this._localResourceClient = new com.modos189.webviewx.LocalResourceWebViewClient(nsClient);
|
|
85
|
+
wv.setWebViewClient(this._localResourceClient);
|
|
27
86
|
const existing = wv.getWebChromeClient();
|
|
28
87
|
this._popupClient = new com.modos189.webviewx.PopupWebChromeClient(existing, true, this._context);
|
|
29
88
|
const interceptor = new com.modos189.webviewx.PopupWebChromeClient.PopupUrlInterceptor({
|
|
@@ -36,11 +95,154 @@ export class WebViewX extends AWebView {
|
|
|
36
95
|
const settings = wv.getSettings();
|
|
37
96
|
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
|
38
97
|
settings.setSupportMultipleWindows(true);
|
|
98
|
+
// ScriptHandlers are bound to a specific WebView instance - re-register on the
|
|
99
|
+
// (re)created native view so document-start scripts survive view recycling
|
|
100
|
+
this._reRegisterAllDocumentStartScripts();
|
|
39
101
|
}
|
|
40
102
|
disposeNativeView() {
|
|
103
|
+
// Handlers belong to the native view being destroyed; drop them
|
|
104
|
+
this._bridgeScriptHandler = null;
|
|
105
|
+
for (const entry of this._documentStartScripts.values()) {
|
|
106
|
+
entry.handler = null;
|
|
107
|
+
}
|
|
108
|
+
this._localResourceClient = null;
|
|
41
109
|
this._popupClient = null;
|
|
42
110
|
super.disposeNativeView();
|
|
43
111
|
}
|
|
112
|
+
registerLocalResource(resourceName, path) {
|
|
113
|
+
super.registerLocalResource(resourceName, path);
|
|
114
|
+
if (this._localResourceClient) {
|
|
115
|
+
const filepath = this.resolveLocalResourceFilePath(path);
|
|
116
|
+
if (filepath) {
|
|
117
|
+
const fixedName = this.fixLocalResourceName(resourceName);
|
|
118
|
+
this._localResourceClient.registerResource(fixedName, filepath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
unregisterLocalResource(resourceName) {
|
|
123
|
+
super.unregisterLocalResource(resourceName);
|
|
124
|
+
if (this._localResourceClient) {
|
|
125
|
+
this._localResourceClient.unregisterResource(this.fixLocalResourceName(resourceName));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Auto-inject a JS file at document start on every navigation (parity with iOS WKUserScript),
|
|
130
|
+
* instead of the base class's asynchronous injection on onPageFinished.
|
|
131
|
+
* Falls back to the base behaviour on WebViews without DOCUMENT_START_SCRIPT support.
|
|
132
|
+
*/
|
|
133
|
+
autoLoadJavaScriptFile(resourceName, filepath) {
|
|
134
|
+
if (!isDocumentStartScriptSupported()) {
|
|
135
|
+
super.autoLoadJavaScriptFile(resourceName, filepath);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const resolved = this.resolveLocalResourceFilePath(filepath);
|
|
139
|
+
if (!resolved) {
|
|
140
|
+
super.autoLoadJavaScriptFile(resourceName, filepath);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const scriptCode = File.fromPath(resolved).readTextSync();
|
|
144
|
+
const entry = {
|
|
145
|
+
code: wrapDocumentStartScript(scriptCode),
|
|
146
|
+
handler: null,
|
|
147
|
+
};
|
|
148
|
+
// Re-registering the same name mirrors WKUserScript remove+append
|
|
149
|
+
this._removeDocumentStartScript(resourceName);
|
|
150
|
+
this._documentStartScripts.set(resourceName, entry);
|
|
151
|
+
this._registerDocumentStartScript(resourceName, entry);
|
|
152
|
+
}
|
|
153
|
+
removeAutoLoadJavaScriptFile(resourceName) {
|
|
154
|
+
this._removeDocumentStartScript(resourceName);
|
|
155
|
+
// Also clear any fallback entry stored by the base implementation
|
|
156
|
+
super.removeAutoLoadJavaScriptFile(resourceName);
|
|
157
|
+
}
|
|
158
|
+
_registerDocumentStartScript(resourceName, entry) {
|
|
159
|
+
const nv = this.nativeViewProtected;
|
|
160
|
+
if (!nv)
|
|
161
|
+
return;
|
|
162
|
+
const wv = nv;
|
|
163
|
+
entry.handler = androidx.webkit.WebViewCompat.addDocumentStartJavaScript(wv, entry.code, this._buildOriginRules());
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Register the ui-webview bridge as a document-start script so window.nsWebViewBridge
|
|
167
|
+
* exists before any page script. The androidWebViewBridge Java interface (added via
|
|
168
|
+
* addJavascriptInterface) is available from the very start of the document, so the bridge
|
|
169
|
+
* has no blocking dependency and must NOT be wrapped in a DOMContentLoaded guard.
|
|
170
|
+
*/
|
|
171
|
+
_registerBridgeDocumentStartScript() {
|
|
172
|
+
const nv = this.nativeViewProtected;
|
|
173
|
+
if (!nv || !this.autoInjectJSBridge || !isDocumentStartScriptSupported()) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const wv = nv;
|
|
177
|
+
this._bridgeScriptHandler = androidx.webkit.WebViewCompat.addDocumentStartJavaScript(wv, webViewBridge, this._buildOriginRules());
|
|
178
|
+
}
|
|
179
|
+
_removeBridgeDocumentStartScript() {
|
|
180
|
+
if (this._bridgeScriptHandler) {
|
|
181
|
+
try {
|
|
182
|
+
this._bridgeScriptHandler.remove();
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// handler already invalid (e.g. native view gone) - ignore
|
|
186
|
+
}
|
|
187
|
+
this._bridgeScriptHandler = null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* (Re)register the bridge followed by all user scripts in registration order, so the bridge
|
|
192
|
+
* always runs first. Safe to call when handlers are already null (e.g. after view recycling)
|
|
193
|
+
*/
|
|
194
|
+
_reRegisterAllDocumentStartScripts() {
|
|
195
|
+
if (!this.nativeViewProtected || !isDocumentStartScriptSupported()) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
this._removeBridgeDocumentStartScript();
|
|
199
|
+
for (const entry of this._documentStartScripts.values()) {
|
|
200
|
+
if (entry.handler) {
|
|
201
|
+
try {
|
|
202
|
+
entry.handler.remove();
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// ignore
|
|
206
|
+
}
|
|
207
|
+
entry.handler = null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
this._registerBridgeDocumentStartScript();
|
|
211
|
+
for (const [name, entry] of this._documentStartScripts) {
|
|
212
|
+
this._registerDocumentStartScript(name, entry);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
_removeDocumentStartScript(resourceName) {
|
|
216
|
+
const entry = this._documentStartScripts.get(resourceName);
|
|
217
|
+
if (!entry)
|
|
218
|
+
return;
|
|
219
|
+
if (entry.handler) {
|
|
220
|
+
try {
|
|
221
|
+
entry.handler.remove();
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
// handler already invalid (e.g. native view gone) - ignore
|
|
225
|
+
}
|
|
226
|
+
entry.handler = null;
|
|
227
|
+
}
|
|
228
|
+
this._documentStartScripts.delete(resourceName);
|
|
229
|
+
}
|
|
230
|
+
_buildOriginRules() {
|
|
231
|
+
const set = new java.util.HashSet();
|
|
232
|
+
const origins = this.documentStartScriptAllowedOrigins || ['*'];
|
|
233
|
+
for (const origin of origins) {
|
|
234
|
+
set.add(origin);
|
|
235
|
+
}
|
|
236
|
+
return set;
|
|
237
|
+
}
|
|
238
|
+
[documentStartScriptAllowedOriginsProperty.setNative]() {
|
|
239
|
+
// Re-register the bridge and all user scripts so the new origin rules take effect
|
|
240
|
+
this._reRegisterAllDocumentStartScripts();
|
|
241
|
+
}
|
|
242
|
+
[autoInjectJSBridgeProperty.setNative]() {
|
|
243
|
+
// Toggling the bridge re-registers everything (the bridge is only added when enabled)
|
|
244
|
+
this._reRegisterAllDocumentStartScripts();
|
|
245
|
+
}
|
|
44
246
|
[supportPopupsProperty.setNative](value) {
|
|
45
247
|
const nv = this.nativeViewProtected;
|
|
46
248
|
if (!nv)
|
|
@@ -51,4 +253,5 @@ export class WebViewX extends AWebView {
|
|
|
51
253
|
this._popupClient?.setSupportPopups(value);
|
|
52
254
|
}
|
|
53
255
|
}
|
|
256
|
+
documentStartScriptAllowedOriginsProperty.register(WebViewX);
|
|
54
257
|
//# sourceMappingURL=index.android.js.map
|
package/index.android.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.android.js","sourceRoot":"","sources":["../../../packages/webview-x/index.android.ts"],"names":[],"mappings":"AAAA,cAAc,kDAAkD,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,kDAAkD,CAAC;
|
|
1
|
+
{"version":3,"file":"index.android.js","sourceRoot":"","sources":["../../../packages/webview-x/index.android.ts"],"names":[],"mappings":"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"}
|
package/index.d.ts
CHANGED
|
@@ -2,10 +2,14 @@ export * from '@nativescript-community/ui-webview';
|
|
|
2
2
|
import { AWebView } from '@nativescript-community/ui-webview';
|
|
3
3
|
|
|
4
4
|
export declare class WebViewX extends AWebView {
|
|
5
|
+
static readonly popupNavigateEvent: string;
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Return the desired UA string, or null to leave the platform default unchanged.
|
|
7
|
+
* Android: origin rules limiting where document-start scripts are injected.
|
|
8
|
+
* Defaults to ['*'] (all origins).
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
documentStartScriptAllowedOrigins: string[];
|
|
11
|
+
registerLocalResource(resourceName: string, path: string): void;
|
|
12
|
+
unregisterLocalResource(resourceName: string): void;
|
|
13
|
+
autoLoadJavaScriptFile(resourceName: string, filepath: string): void;
|
|
14
|
+
removeAutoLoadJavaScriptFile(resourceName: string): void;
|
|
11
15
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
package com.modos189.webviewx;
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap;
|
|
4
|
+
import android.webkit.WebResourceError;
|
|
5
|
+
import android.webkit.WebResourceRequest;
|
|
6
|
+
import android.webkit.WebResourceResponse;
|
|
7
|
+
import android.webkit.WebView;
|
|
8
|
+
import android.webkit.WebViewClient;
|
|
9
|
+
|
|
10
|
+
import java.io.FileInputStream;
|
|
11
|
+
import java.io.File;
|
|
12
|
+
import java.util.HashMap;
|
|
13
|
+
import java.util.HashSet;
|
|
14
|
+
import java.util.Map;
|
|
15
|
+
import java.util.Set;
|
|
16
|
+
import java.util.concurrent.ConcurrentHashMap;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Wraps the NativeScript WebViewClient to handle shouldInterceptRequest entirely in Java.
|
|
20
|
+
*
|
|
21
|
+
* Android calls shouldInterceptRequest on Chromium's network thread; NativeScript cannot
|
|
22
|
+
* safely dispatch JS callbacks from non-UI threads, causing fatal crashes
|
|
23
|
+
* ("Cannot find runtime/object id for instance=...WebViewClient...").
|
|
24
|
+
* All other callbacks run on the UI thread and are delegated to the original client unchanged.
|
|
25
|
+
*/
|
|
26
|
+
public class LocalResourceWebViewClient extends WebViewClient {
|
|
27
|
+
|
|
28
|
+
private static final String INTERCEPT_SCHEME = "x-local";
|
|
29
|
+
|
|
30
|
+
private static final Map<String, String> EXT_TO_MIME = new HashMap<>();
|
|
31
|
+
static {
|
|
32
|
+
EXT_TO_MIME.put("js", "application/javascript");
|
|
33
|
+
EXT_TO_MIME.put("mjs", "application/javascript");
|
|
34
|
+
EXT_TO_MIME.put("css", "text/css");
|
|
35
|
+
EXT_TO_MIME.put("html", "text/html");
|
|
36
|
+
EXT_TO_MIME.put("htm", "text/html");
|
|
37
|
+
EXT_TO_MIME.put("json", "application/json");
|
|
38
|
+
EXT_TO_MIME.put("xml", "text/xml");
|
|
39
|
+
EXT_TO_MIME.put("txt", "text/plain");
|
|
40
|
+
EXT_TO_MIME.put("png", "image/png");
|
|
41
|
+
EXT_TO_MIME.put("jpg", "image/jpeg");
|
|
42
|
+
EXT_TO_MIME.put("jpeg", "image/jpeg");
|
|
43
|
+
EXT_TO_MIME.put("gif", "image/gif");
|
|
44
|
+
EXT_TO_MIME.put("webp", "image/webp");
|
|
45
|
+
EXT_TO_MIME.put("svg", "image/svg+xml");
|
|
46
|
+
EXT_TO_MIME.put("ico", "image/x-icon");
|
|
47
|
+
EXT_TO_MIME.put("woff", "font/woff");
|
|
48
|
+
EXT_TO_MIME.put("woff2", "font/woff2");
|
|
49
|
+
EXT_TO_MIME.put("ttf", "font/ttf");
|
|
50
|
+
EXT_TO_MIME.put("otf", "font/otf");
|
|
51
|
+
EXT_TO_MIME.put("mp4", "video/mp4");
|
|
52
|
+
EXT_TO_MIME.put("webm", "video/webm");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private static final Set<String> TEXT_MIME_PREFIXES = new HashSet<>();
|
|
56
|
+
static {
|
|
57
|
+
TEXT_MIME_PREFIXES.add("text/");
|
|
58
|
+
TEXT_MIME_PREFIXES.add("application/javascript");
|
|
59
|
+
TEXT_MIME_PREFIXES.add("application/json");
|
|
60
|
+
TEXT_MIME_PREFIXES.add("image/svg+xml");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private final WebViewClient delegate;
|
|
64
|
+
private final ConcurrentHashMap<String, String> resourceMap = new ConcurrentHashMap<>();
|
|
65
|
+
|
|
66
|
+
public LocalResourceWebViewClient(WebViewClient delegate) {
|
|
67
|
+
this.delegate = delegate;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public void registerResource(String name, String filePath) {
|
|
71
|
+
resourceMap.put(name, filePath);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public void unregisterResource(String name) {
|
|
75
|
+
resourceMap.remove(name);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@Override
|
|
79
|
+
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
|
80
|
+
String url = request.getUrl().toString();
|
|
81
|
+
if (!url.startsWith(INTERCEPT_SCHEME + "://")) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
String resourceName = url.substring(INTERCEPT_SCHEME.length() + 3); // strip "x-local://"
|
|
85
|
+
String filePath = resourceMap.get(resourceName);
|
|
86
|
+
if (filePath == null) return null;
|
|
87
|
+
File file = new File(filePath);
|
|
88
|
+
if (!file.exists()) return null;
|
|
89
|
+
try {
|
|
90
|
+
String ext = "";
|
|
91
|
+
int dot = filePath.lastIndexOf('.');
|
|
92
|
+
if (dot >= 0) ext = filePath.substring(dot + 1).toLowerCase();
|
|
93
|
+
String mime = EXT_TO_MIME.containsKey(ext) ? EXT_TO_MIME.get(ext) : "application/octet-stream";
|
|
94
|
+
boolean isText = false;
|
|
95
|
+
for (String prefix : TEXT_MIME_PREFIXES) {
|
|
96
|
+
if (mime.startsWith(prefix)) { isText = true; break; }
|
|
97
|
+
}
|
|
98
|
+
String encoding = isText ? "UTF-8" : "binary";
|
|
99
|
+
WebResourceResponse response = new WebResourceResponse(
|
|
100
|
+
mime, encoding, new FileInputStream(file));
|
|
101
|
+
Map<String, String> headers = new HashMap<>();
|
|
102
|
+
headers.put("Access-Control-Allow-Origin", "*");
|
|
103
|
+
response.setResponseHeaders(headers);
|
|
104
|
+
return response;
|
|
105
|
+
} catch (Exception e) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@Override
|
|
111
|
+
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
|
112
|
+
return delegate.shouldOverrideUrlLoading(view, request);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@Override
|
|
116
|
+
@SuppressWarnings("deprecation")
|
|
117
|
+
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
|
118
|
+
return delegate.shouldOverrideUrlLoading(view, url);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@Override
|
|
122
|
+
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
|
123
|
+
delegate.onPageStarted(view, url, favicon);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@Override
|
|
127
|
+
public void onPageFinished(WebView view, String url) {
|
|
128
|
+
delegate.onPageFinished(view, url);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@Override
|
|
132
|
+
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
|
|
133
|
+
delegate.onReceivedError(view, request, error);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@Override
|
|
137
|
+
@SuppressWarnings("deprecation")
|
|
138
|
+
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
|
|
139
|
+
delegate.onReceivedError(view, errorCode, description, failingUrl);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@Override
|
|
143
|
+
public void onReceivedHttpError(WebView view, WebResourceRequest request,
|
|
144
|
+
WebResourceResponse errorResponse) {
|
|
145
|
+
delegate.onReceivedHttpError(view, request, errorResponse);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@Override
|
|
149
|
+
public void onReceivedSslError(WebView view, android.webkit.SslErrorHandler handler,
|
|
150
|
+
android.net.http.SslError error) {
|
|
151
|
+
delegate.onReceivedSslError(view, handler, error);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@Override
|
|
155
|
+
public void onLoadResource(WebView view, String url) {
|
|
156
|
+
delegate.onLoadResource(view, url);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@Override
|
|
160
|
+
public void onPageCommitVisible(WebView view, String url) {
|
|
161
|
+
delegate.onPageCommitVisible(view, url);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@Override
|
|
165
|
+
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
|
|
166
|
+
delegate.doUpdateVisitedHistory(view, url, isReload);
|
|
167
|
+
}
|
|
168
|
+
}
|