@modos189/nativescript-webview-x 1.0.1 → 1.0.3

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 CHANGED
@@ -45,30 +45,34 @@ _Available in both `@modos189/nativescript-webview-x` and `@modos189/nativescrip
45
45
 
46
46
  ### Static properties
47
47
 
48
- | Static property | Type | Description |
49
- | --- | --- | --- |
50
- | `userAgentTransform` | `((defaultUA: string \| null) => string \| null) \| null` | Set once at app startup before any `WebViewX` is created. Applied automatically during native view initialization, before the first URL loads. `defaultUA` is the platform default UA string on Android system WebView, `null` on iOS/GeckoView (unavailable synchronously). Return the desired UA string, or `null` to leave the platform default unchanged. |
48
+ _None._
51
49
 
52
50
  ### Properties
53
51
 
54
52
  | Property | Type | Description |
55
53
  | --- | --- | --- |
56
54
  | `src` | `string` | URL to load (data-binding supported) |
55
+ | `userAgent` | `string` | Set a custom User-Agent string |
57
56
  | `debugMode` | `boolean` | Enable remote WebView debugging |
58
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` |
59
59
 
60
60
  ### Methods
61
61
 
62
62
  | Method | Returns | Description |
63
63
  | --- | --- | --- |
64
- | `getUserAgentOverride()` | `string \| null` | Return the active UA override, or `null` if none is set (platform default is used) |
65
- | `setUserAgentOverride(ua: string \| null)` | `void` | Set a custom UA string for this instance; pass `null` or empty string to clear the override and restore the platform default. Applies to subsequent navigations |
66
- | `getDefaultUserAgent()` | `string` | **Android only.** Return the system default UA string for this device, unaffected by any override. Useful as the input to `userAgentTransform` |
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) |
67
67
 
68
68
  ### Events
69
69
 
70
70
  | Event | Description |
71
71
  | --- | --- |
72
+ | `loadStarted` | Navigation started. `args.url` contains the target URL |
73
+ | `loadFinished` | Navigation finished. `args.error` is set on failure |
74
+ | `loadProgress` | Android: page load progress. `args.progress` is 0–100 |
75
+ | `titleChanged` | Page title changed. `args.title` contains the new title |
72
76
  | `popupNavigate` | Android: fired on each navigation inside a popup; set `args.cancel = true` to intercept and dismiss the popup (e.g. capture OAuth redirect). `args.url` contains the target URL. |
73
77
 
74
78
 
@@ -1,14 +1,12 @@
1
1
  export * from '@nativescript-community/ui-webview/index.android';
2
2
  import { AWebView } from '@nativescript-community/ui-webview/index.android';
3
3
  export declare class WebViewX extends AWebView {
4
- static userAgentTransform: ((defaultUA: string | null) => string | null) | null;
5
4
  static get popupNavigateEvent(): string;
6
5
  private _popupClient;
7
- private _userAgentOverride;
6
+ private _localResourceClient;
8
7
  _onPopupNavigate(url: string): boolean;
9
8
  initNativeView(): void;
10
9
  disposeNativeView(): void;
11
- getUserAgentOverride(): string | null;
12
- setUserAgentOverride(ua: string | null): void;
13
- getDefaultUserAgent(): string;
10
+ registerLocalResource(resourceName: string, path: string): void;
11
+ unregisterLocalResource(resourceName: string): void;
14
12
  }
package/index.android.js CHANGED
@@ -5,7 +5,7 @@ export class WebViewX extends AWebView {
5
5
  constructor() {
6
6
  super(...arguments);
7
7
  this._popupClient = null;
8
- this._userAgentOverride = null;
8
+ this._localResourceClient = null;
9
9
  }
10
10
  static get popupNavigateEvent() {
11
11
  return POPUP_NAVIGATE_EVENT;
@@ -25,14 +25,11 @@ export class WebViewX extends AWebView {
25
25
  if (!nv)
26
26
  return;
27
27
  const wv = nv;
28
- if (WebViewX.userAgentTransform) {
29
- const defaultUA = android.webkit.WebSettings.getDefaultUserAgent(this._context);
30
- const newUA = WebViewX.userAgentTransform(defaultUA);
31
- if (newUA !== null) {
32
- wv.getSettings().setUserAgentString(newUA);
33
- this._userAgentOverride = newUA;
34
- }
35
- }
28
+ // shouldInterceptRequest is called on Chromium's network thread; NativeScript cannot
29
+ // safely dispatch to V8 from there — wrap the NS client so it runs entirely in Java.
30
+ const nsClient = wv.getWebViewClient();
31
+ this._localResourceClient = new com.modos189.webviewx.LocalResourceWebViewClient(nsClient);
32
+ wv.setWebViewClient(this._localResourceClient);
36
33
  const existing = wv.getWebChromeClient();
37
34
  this._popupClient = new com.modos189.webviewx.PopupWebChromeClient(existing, true, this._context);
38
35
  const interceptor = new com.modos189.webviewx.PopupWebChromeClient.PopupUrlInterceptor({
@@ -47,20 +44,25 @@ export class WebViewX extends AWebView {
47
44
  settings.setSupportMultipleWindows(true);
48
45
  }
49
46
  disposeNativeView() {
47
+ this._localResourceClient = null;
50
48
  this._popupClient = null;
51
49
  super.disposeNativeView();
52
50
  }
53
- getUserAgentOverride() {
54
- return this._userAgentOverride;
55
- }
56
- setUserAgentOverride(ua) {
57
- this._userAgentOverride = ua || null;
58
- const wv = this.nativeViewProtected;
59
- if (wv)
60
- wv.getSettings().setUserAgentString(this._userAgentOverride);
51
+ registerLocalResource(resourceName, path) {
52
+ super.registerLocalResource(resourceName, path);
53
+ if (this._localResourceClient) {
54
+ const filepath = this.resolveLocalResourceFilePath(path);
55
+ if (filepath) {
56
+ const fixedName = this.fixLocalResourceName(resourceName);
57
+ this._localResourceClient.registerResource(fixedName, filepath);
58
+ }
59
+ }
61
60
  }
62
- getDefaultUserAgent() {
63
- return android.webkit.WebSettings.getDefaultUserAgent(this._context);
61
+ unregisterLocalResource(resourceName) {
62
+ super.unregisterLocalResource(resourceName);
63
+ if (this._localResourceClient) {
64
+ this._localResourceClient.unregisterResource(this.fixLocalResourceName(resourceName));
65
+ }
64
66
  }
65
67
  [supportPopupsProperty.setNative](value) {
66
68
  const nv = this.nativeViewProtected;
@@ -72,5 +74,4 @@ export class WebViewX extends AWebView {
72
74
  this._popupClient?.setSupportPopups(value);
73
75
  }
74
76
  }
75
- WebViewX.userAgentTransform = null;
76
77
  //# sourceMappingURL=index.android.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.android.js","sourceRoot":"","sources":["../../../packages/webview-x/index.android.ts"],"names":[],"mappings":"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"}
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,15 +2,7 @@ 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
- /**
6
- * Set once before any instance is created; applied automatically during native view init before the first URL loads.
7
- * Receives the platform default UA (string on Android WebView, null on iOS/GeckoView where it is unavailable synchronously).
8
- * Return the desired UA string, or null to leave the platform default unchanged.
9
- */
10
- static userAgentTransform: ((defaultUA: string | null) => string | null) | null;
11
5
  static readonly popupNavigateEvent: string;
12
- getUserAgentOverride(): string | null;
13
- setUserAgentOverride(ua: string | null): void;
14
- /** Android system WebView only: returns the device default UA string (unaffected by any override). */
15
- getDefaultUserAgent(): string;
6
+ registerLocalResource(resourceName: string, path: string): void;
7
+ unregisterLocalResource(resourceName: string): void;
16
8
  }
package/index.ios.d.ts CHANGED
@@ -1,8 +1,4 @@
1
1
  export * from '@nativescript-community/ui-webview/index.ios';
2
2
  import { AWebView } from '@nativescript-community/ui-webview/index.ios';
3
3
  export declare class WebViewX extends AWebView {
4
- static userAgentTransform: ((defaultUA: string | null) => string | null) | null;
5
- initNativeView(): void;
6
- getUserAgentOverride(): string | null;
7
- setUserAgentOverride(ua: string | null): void;
8
4
  }
package/index.ios.js CHANGED
@@ -1,23 +1,5 @@
1
1
  export * from '@nativescript-community/ui-webview/index.ios';
2
2
  import { AWebView } from '@nativescript-community/ui-webview/index.ios';
3
3
  export class WebViewX extends AWebView {
4
- initNativeView() {
5
- super.initNativeView();
6
- if (WebViewX.userAgentTransform) {
7
- const newUA = WebViewX.userAgentTransform(null);
8
- if (newUA !== null && this.nativeViewProtected) {
9
- this.nativeViewProtected.customUserAgent = newUA;
10
- }
11
- }
12
- }
13
- getUserAgentOverride() {
14
- return this.nativeViewProtected?.customUserAgent ?? null;
15
- }
16
- setUserAgentOverride(ua) {
17
- if (this.nativeViewProtected) {
18
- this.nativeViewProtected.customUserAgent = ua || null;
19
- }
20
- }
21
4
  }
22
- WebViewX.userAgentTransform = null;
23
5
  //# sourceMappingURL=index.ios.js.map
package/index.ios.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.ios.js","sourceRoot":"","sources":["../../../packages/webview-x/index.ios.ts"],"names":[],"mappings":"AAAA,cAAc,8CAA8C,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,8CAA8C,CAAC;AAExE,MAAM,OAAO,QAAS,SAAQ,QAAQ;IAGpC,cAAc;QACZ,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,IAAI,QAAQ,CAAC,kBAAkB,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,QAAQ,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;YAChD,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC/C,IAAI,CAAC,mBAAmB,CAAC,eAAe,GAAG,KAAK,CAAC;YACnD,CAAC;QACH,CAAC;IACH,CAAC;IAED,oBAAoB;QAClB,OAAO,IAAI,CAAC,mBAAmB,EAAE,eAAe,IAAI,IAAI,CAAC;IAC3D,CAAC;IAED,oBAAoB,CAAC,EAAiB;QACpC,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,IAAI,CAAC,mBAAmB,CAAC,eAAe,GAAG,EAAE,IAAI,IAAI,CAAC;QACxD,CAAC;IACH,CAAC;;AApBM,2BAAkB,GAAyD,IAAI,CAAC"}
1
+ {"version":3,"file":"index.ios.js","sourceRoot":"","sources":["../../../packages/webview-x/index.ios.ts"],"names":[],"mappings":"AAAA,cAAc,8CAA8C,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,8CAA8C,CAAC;AAExE,MAAM,OAAO,QAAS,SAAQ,QAAQ;CAAG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modos189/nativescript-webview-x",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Add a plugin description",
5
5
  "main": "index",
6
6
  "types": "index.d.ts",
@@ -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
+ }