@modos189/nativescript-webview-x-gecko 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 +10 -5
- package/common.d.ts +24 -1
- package/common.js +4 -1
- package/common.js.map +1 -1
- package/gecko-bridge-loader.d.ts +1 -0
- package/gecko-bridge-loader.js +4 -0
- package/gecko-bridge-loader.js.map +1 -0
- package/index.android.d.ts +26 -3
- package/index.android.js +163 -15
- package/index.android.js.map +1 -1
- package/index.d.ts +29 -3
- package/index.ios.d.ts +0 -4
- package/index.ios.js +0 -18
- package/index.ios.js.map +1 -1
- package/package.json +1 -1
- package/platforms/android/assets/execute-js/background.js +26 -0
- package/platforms/android/assets/execute-js/content.js +36 -0
- package/platforms/android/assets/execute-js/manifest.json +22 -0
- package/platforms/android/java/com/modos189/webviewxgecko/GeckoDelegates.java +59 -0
- package/platforms/android/java/com/modos189/webviewxgecko/GeckoJsBridge.java +173 -0
package/README.md
CHANGED
|
@@ -45,29 +45,34 @@ _Available in both `@modos189/nativescript-webview-x` and `@modos189/nativescrip
|
|
|
45
45
|
|
|
46
46
|
### Static properties
|
|
47
47
|
|
|
48
|
-
|
|
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
|
-
| `
|
|
65
|
-
| `
|
|
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) |
|
|
66
67
|
|
|
67
68
|
### Events
|
|
68
69
|
|
|
69
70
|
| Event | Description |
|
|
70
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 |
|
|
71
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. |
|
|
72
77
|
|
|
73
78
|
|
package/common.d.ts
CHANGED
|
@@ -1 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
import { EventData } from '@nativescript/core';
|
|
2
|
+
export declare const LOAD_STARTED_EVENT = "loadStarted";
|
|
3
|
+
export declare const LOAD_FINISHED_EVENT = "loadFinished";
|
|
4
|
+
export declare const LOAD_PROGRESS_EVENT = "loadProgress";
|
|
5
|
+
export declare const TITLE_CHANGED_EVENT = "titleChanged";
|
|
6
|
+
export interface LoadStartedEventData extends EventData {
|
|
7
|
+
eventName: typeof LOAD_STARTED_EVENT;
|
|
8
|
+
url: string;
|
|
9
|
+
}
|
|
10
|
+
export interface LoadFinishedEventData extends EventData {
|
|
11
|
+
eventName: typeof LOAD_FINISHED_EVENT;
|
|
12
|
+
url: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface LoadProgressEventData extends EventData {
|
|
16
|
+
eventName: typeof LOAD_PROGRESS_EVENT;
|
|
17
|
+
url: string;
|
|
18
|
+
progress: number;
|
|
19
|
+
}
|
|
20
|
+
export interface TitleChangedEventData extends EventData {
|
|
21
|
+
eventName: typeof TITLE_CHANGED_EVENT;
|
|
22
|
+
url: string;
|
|
23
|
+
title: string;
|
|
24
|
+
}
|
package/common.js
CHANGED
package/common.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../packages/webview-x-gecko/common.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../packages/webview-x-gecko/common.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAAC;AAChD,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const geckoBridgeJs = "(function(){if(!window.nsWebViewBridge){var i={};window.nsWebViewBridge={on:function(n,e){i[n]||(i[n]=[]),i[n].push(e)},addEventListener:function(n,e){this.on(n,e)},off:function(n,e){if(!n){i={};return}if(i[n]){if(!e){delete i[n];return}i[n]=i[n].filter(function(t){return t!==e}),i[n].length===0&&delete i[n]}},removeEventListener:function(n,e){this.off(n,e)},emit:function(n,e){document.dispatchEvent(new CustomEvent(\"__ns_bridge_emit__\",{detail:{eventName:n,data:JSON.stringify(e)}}))},onNativeEvent:function(n,e){var t=i[n];if(t)for(var f=0;f<t.length&&!(t[f]&&t[f](e)===!1);f++);}};var r=window;typeof CustomEvent<\"u\"?r.dispatchEvent(new CustomEvent(\"ns-bridge-ready\",{detail:r.nsWebViewBridge})):r.dispatchEvent(new Event(\"ns-bridge-ready\")),r.dispatchEvent(new Event(\"ns-brige-ready\"))}})();";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// AUTO-GENERATED by tools/scripts/generate-gecko-bridge.js - do not edit directly.
|
|
2
|
+
// Source: packages/webview-x-gecko/www-src/bridge.gecko.js
|
|
3
|
+
export const geckoBridgeJs = '(function(){if(!window.nsWebViewBridge){var i={};window.nsWebViewBridge={on:function(n,e){i[n]||(i[n]=[]),i[n].push(e)},addEventListener:function(n,e){this.on(n,e)},off:function(n,e){if(!n){i={};return}if(i[n]){if(!e){delete i[n];return}i[n]=i[n].filter(function(t){return t!==e}),i[n].length===0&&delete i[n]}},removeEventListener:function(n,e){this.off(n,e)},emit:function(n,e){document.dispatchEvent(new CustomEvent("__ns_bridge_emit__",{detail:{eventName:n,data:JSON.stringify(e)}}))},onNativeEvent:function(n,e){var t=i[n];if(t)for(var f=0;f<t.length&&!(t[f]&&t[f](e)===!1);f++);}};var r=window;typeof CustomEvent<"u"?r.dispatchEvent(new CustomEvent("ns-bridge-ready",{detail:r.nsWebViewBridge})):r.dispatchEvent(new Event("ns-bridge-ready")),r.dispatchEvent(new Event("ns-brige-ready"))}})();';
|
|
4
|
+
//# sourceMappingURL=gecko-bridge-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gecko-bridge-loader.js","sourceRoot":"","sources":["../../../packages/webview-x-gecko/gecko-bridge-loader.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,2DAA2D;AAC3D,MAAM,CAAC,MAAM,aAAa,GACxB,gyBAAgyB,CAAC"}
|
package/index.android.d.ts
CHANGED
|
@@ -1,19 +1,42 @@
|
|
|
1
1
|
import { View, Property } from '@nativescript/core';
|
|
2
|
+
export * from './common';
|
|
2
3
|
export declare const srcProperty: Property<WebViewX, string>;
|
|
3
4
|
export declare const debugModeProperty: Property<WebViewX, boolean>;
|
|
4
5
|
export declare const supportPopupsProperty: Property<WebViewX, boolean>;
|
|
6
|
+
export declare const userAgentProperty: Property<WebViewX, string>;
|
|
7
|
+
export declare const autoInjectJSBridgeProperty: Property<WebViewX, boolean>;
|
|
5
8
|
export declare class WebViewX extends View {
|
|
6
|
-
static userAgentTransform: ((defaultUA: string | null) => string | null) | null;
|
|
7
9
|
static get popupNavigateEvent(): string;
|
|
10
|
+
static get loadStartedEvent(): string;
|
|
11
|
+
static get loadFinishedEvent(): string;
|
|
12
|
+
static get loadProgressEvent(): string;
|
|
13
|
+
static get titleChangedEvent(): string;
|
|
8
14
|
nativeViewProtected: org.mozilla.geckoview.GeckoView;
|
|
9
15
|
private _session;
|
|
10
16
|
private _popupHelper;
|
|
17
|
+
_currentUrl: string;
|
|
18
|
+
_currentTitle: string;
|
|
19
|
+
_tempSuspendSrcLoading: boolean;
|
|
11
20
|
src: string;
|
|
12
21
|
debugMode: boolean;
|
|
13
22
|
supportPopups: boolean;
|
|
23
|
+
userAgent: string;
|
|
24
|
+
autoInjectJSBridge: boolean;
|
|
25
|
+
private _bridgeListener;
|
|
14
26
|
_onPopupNavigate(url: string): boolean;
|
|
27
|
+
_onLoadStarted(url: string): void;
|
|
28
|
+
_onLoadFinished(url: string, error?: string): Promise<void>;
|
|
29
|
+
emitToWebView(eventName: string, data: any): void;
|
|
30
|
+
onWebViewEvent(eventName: string, data: any): void;
|
|
31
|
+
_loadProgress(progress: number): void;
|
|
32
|
+
_titleChanged(title: string): void;
|
|
33
|
+
/** Returns the current page title (cached from GeckoDelegates.ContentDelegate.onTitleChange). */
|
|
34
|
+
getTitle(): Promise<string | undefined>;
|
|
35
|
+
/**
|
|
36
|
+
* Execute JavaScript in the current page's context and return the result.
|
|
37
|
+
* Uses a built-in WebExtension bridge; requires GeckoView 65+.
|
|
38
|
+
*/
|
|
39
|
+
executeJavaScript<T = any>(code: string): Promise<T>;
|
|
15
40
|
createNativeView(): org.mozilla.geckoview.GeckoView;
|
|
16
41
|
disposeNativeView(): void;
|
|
17
|
-
getUserAgentOverride(): string | null;
|
|
18
|
-
setUserAgentOverride(ua: string | null): void;
|
|
19
42
|
}
|
package/index.android.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Application, View, Property, booleanConverter } from '@nativescript/core';
|
|
2
|
+
import { geckoBridgeJs as _GECKO_BRIDGE_JS } from './gecko-bridge-loader';
|
|
3
|
+
import { LOAD_STARTED_EVENT, LOAD_FINISHED_EVENT, LOAD_PROGRESS_EVENT, TITLE_CHANGED_EVENT } from './common';
|
|
4
|
+
export * from './common';
|
|
2
5
|
const POPUP_NAVIGATE_EVENT = 'popupNavigate';
|
|
3
6
|
// Explicit type annotation required to break circular inference with [xProperty.setNative]
|
|
4
7
|
export const srcProperty = new Property({
|
|
@@ -15,15 +18,39 @@ export const supportPopupsProperty = new Property({
|
|
|
15
18
|
defaultValue: true,
|
|
16
19
|
valueConverter: booleanConverter,
|
|
17
20
|
});
|
|
21
|
+
export const userAgentProperty = new Property({
|
|
22
|
+
name: 'userAgent',
|
|
23
|
+
});
|
|
24
|
+
export const autoInjectJSBridgeProperty = new Property({
|
|
25
|
+
name: 'autoInjectJSBridge',
|
|
26
|
+
defaultValue: true,
|
|
27
|
+
valueConverter: booleanConverter,
|
|
28
|
+
});
|
|
18
29
|
export class WebViewX extends View {
|
|
19
30
|
constructor() {
|
|
20
31
|
super(...arguments);
|
|
21
32
|
this._session = null;
|
|
22
33
|
this._popupHelper = null;
|
|
34
|
+
this._currentUrl = '';
|
|
35
|
+
this._currentTitle = '';
|
|
36
|
+
this._tempSuspendSrcLoading = false;
|
|
37
|
+
this._bridgeListener = null;
|
|
23
38
|
}
|
|
24
39
|
static get popupNavigateEvent() {
|
|
25
40
|
return POPUP_NAVIGATE_EVENT;
|
|
26
41
|
}
|
|
42
|
+
static get loadStartedEvent() {
|
|
43
|
+
return LOAD_STARTED_EVENT;
|
|
44
|
+
}
|
|
45
|
+
static get loadFinishedEvent() {
|
|
46
|
+
return LOAD_FINISHED_EVENT;
|
|
47
|
+
}
|
|
48
|
+
static get loadProgressEvent() {
|
|
49
|
+
return LOAD_PROGRESS_EVENT;
|
|
50
|
+
}
|
|
51
|
+
static get titleChangedEvent() {
|
|
52
|
+
return TITLE_CHANGED_EVENT;
|
|
53
|
+
}
|
|
27
54
|
_onPopupNavigate(url) {
|
|
28
55
|
const args = {
|
|
29
56
|
eventName: POPUP_NAVIGATE_EVENT,
|
|
@@ -33,17 +60,109 @@ export class WebViewX extends View {
|
|
|
33
60
|
this.notify(args);
|
|
34
61
|
return args.cancel === true;
|
|
35
62
|
}
|
|
63
|
+
_onLoadStarted(url) {
|
|
64
|
+
this._currentUrl = url;
|
|
65
|
+
this._currentTitle = ''; // reset stale title on each new navigation
|
|
66
|
+
this.notify({
|
|
67
|
+
eventName: LOAD_STARTED_EVENT,
|
|
68
|
+
object: this,
|
|
69
|
+
url,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async _onLoadFinished(url, error) {
|
|
73
|
+
if (!error) {
|
|
74
|
+
try {
|
|
75
|
+
this._tempSuspendSrcLoading = true;
|
|
76
|
+
this.src = url;
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
this._tempSuspendSrcLoading = false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!error && this.autoInjectJSBridge) {
|
|
83
|
+
try {
|
|
84
|
+
await this.executeJavaScript(_GECKO_BRIDGE_JS);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// bridge injection is best-effort
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
this.notify({
|
|
91
|
+
eventName: LOAD_FINISHED_EVENT,
|
|
92
|
+
object: this,
|
|
93
|
+
url,
|
|
94
|
+
error,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
emitToWebView(eventName, data) {
|
|
98
|
+
const code = `window.nsWebViewBridge&&nsWebViewBridge.onNativeEvent(${JSON.stringify(eventName)},${JSON.stringify(data)});`;
|
|
99
|
+
this.executeJavaScript(code).catch(() => {
|
|
100
|
+
// ignore — page may not have bridge
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
onWebViewEvent(eventName, data) {
|
|
104
|
+
this.notify({ eventName, data });
|
|
105
|
+
}
|
|
106
|
+
_loadProgress(progress) {
|
|
107
|
+
this.notify({
|
|
108
|
+
eventName: LOAD_PROGRESS_EVENT,
|
|
109
|
+
object: this,
|
|
110
|
+
url: this._currentUrl,
|
|
111
|
+
progress,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
_titleChanged(title) {
|
|
115
|
+
this._currentTitle = title;
|
|
116
|
+
this.notify({
|
|
117
|
+
eventName: TITLE_CHANGED_EVENT,
|
|
118
|
+
object: this,
|
|
119
|
+
url: this._currentUrl,
|
|
120
|
+
title,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/** Returns the current page title (cached from GeckoDelegates.ContentDelegate.onTitleChange). */
|
|
124
|
+
async getTitle() {
|
|
125
|
+
return this._currentTitle || undefined;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Execute JavaScript in the current page's context and return the result.
|
|
129
|
+
* Uses a built-in WebExtension bridge; requires GeckoView 65+.
|
|
130
|
+
*/
|
|
131
|
+
executeJavaScript(code) {
|
|
132
|
+
return new Promise((resolve, reject) => {
|
|
133
|
+
const bridge = com.modos189.webviewxgecko.GeckoJsBridge.getInstance();
|
|
134
|
+
const id = bridge.nextId();
|
|
135
|
+
bridge.executeScript(id, code, new com.modos189.webviewxgecko.GeckoJsBridge.JsCallback({
|
|
136
|
+
onResult(jsonResult) {
|
|
137
|
+
resolve(_parseJsResult(jsonResult));
|
|
138
|
+
},
|
|
139
|
+
onError(error) {
|
|
140
|
+
reject(new Error(error));
|
|
141
|
+
},
|
|
142
|
+
}));
|
|
143
|
+
});
|
|
144
|
+
}
|
|
36
145
|
createNativeView() {
|
|
37
146
|
const runtime = com.modos189.webviewxgecko.GeckoPopupHelper.getRuntime(Application.android.context);
|
|
147
|
+
com.modos189.webviewxgecko.GeckoJsBridge.getInstance().setup(runtime);
|
|
148
|
+
const selfRef = new WeakRef(this);
|
|
149
|
+
this._bridgeListener = new com.modos189.webviewxgecko.GeckoJsBridge.BridgeEventListener({
|
|
150
|
+
onBridgeEvent(eventName, dataJson) {
|
|
151
|
+
const owner = selfRef.deref();
|
|
152
|
+
if (!owner)
|
|
153
|
+
return;
|
|
154
|
+
let data;
|
|
155
|
+
try {
|
|
156
|
+
data = JSON.parse(dataJson);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
data = dataJson;
|
|
160
|
+
}
|
|
161
|
+
owner.onWebViewEvent(eventName, data);
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
com.modos189.webviewxgecko.GeckoJsBridge.getInstance().addBridgeListener(this._bridgeListener);
|
|
38
165
|
const session = new org.mozilla.geckoview.GeckoSession();
|
|
39
|
-
if (WebViewX.userAgentTransform) {
|
|
40
|
-
const newUA = WebViewX.userAgentTransform(null);
|
|
41
|
-
if (newUA !== null) {
|
|
42
|
-
session.getSettings().setUserAgentOverride(newUA);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
// GeckoPopupHelper sets the NavigationDelegate and manages popup windows.
|
|
46
|
-
// this._context is the Activity context — required for Dialog creation.
|
|
47
166
|
this._popupHelper = new com.modos189.webviewxgecko.GeckoPopupHelper(session, this._context, true);
|
|
48
167
|
const interceptor = new com.modos189.webviewxgecko.GeckoPopupHelper.PopupUrlInterceptor({
|
|
49
168
|
shouldHandleExternally: (url) => {
|
|
@@ -51,6 +170,16 @@ export class WebViewX extends View {
|
|
|
51
170
|
},
|
|
52
171
|
});
|
|
53
172
|
this._popupHelper.setUrlInterceptor(interceptor);
|
|
173
|
+
// GeckoDelegates are Java wrappers - NativeScript cannot proxy GeckoView interfaces directly
|
|
174
|
+
// (Java 8 default methods + @UiThread annotations break the runtime DEX factory).
|
|
175
|
+
session.setProgressDelegate(new com.modos189.webviewxgecko.GeckoDelegates.ProgressDelegate(new com.modos189.webviewxgecko.GeckoDelegates.ProgressListener({
|
|
176
|
+
onPageStart: (url) => this._onLoadStarted(url),
|
|
177
|
+
onPageStop: (success) => this._onLoadFinished(this._currentUrl, success ? undefined : 'load-failed'),
|
|
178
|
+
onProgressChange: (progress) => this._loadProgress(progress),
|
|
179
|
+
})));
|
|
180
|
+
session.setContentDelegate(new com.modos189.webviewxgecko.GeckoDelegates.ContentDelegate(new com.modos189.webviewxgecko.GeckoDelegates.ContentListener({
|
|
181
|
+
onTitleChange: (title) => this._titleChanged(title),
|
|
182
|
+
})));
|
|
54
183
|
session.open(runtime);
|
|
55
184
|
this._session = session;
|
|
56
185
|
const geckoView = new org.mozilla.geckoview.GeckoView(this._context);
|
|
@@ -58,14 +187,22 @@ export class WebViewX extends View {
|
|
|
58
187
|
return geckoView;
|
|
59
188
|
}
|
|
60
189
|
disposeNativeView() {
|
|
190
|
+
if (this._bridgeListener) {
|
|
191
|
+
com.modos189.webviewxgecko.GeckoJsBridge.getInstance().removeBridgeListener(this._bridgeListener);
|
|
192
|
+
this._bridgeListener = null;
|
|
193
|
+
}
|
|
61
194
|
this._popupHelper = null;
|
|
62
195
|
if (this._session) {
|
|
196
|
+
this._session.setProgressDelegate(null);
|
|
197
|
+
this._session.setContentDelegate(null);
|
|
63
198
|
this._session.close();
|
|
64
199
|
this._session = null;
|
|
65
200
|
}
|
|
66
201
|
super.disposeNativeView();
|
|
67
202
|
}
|
|
68
203
|
[srcProperty.setNative](value) {
|
|
204
|
+
if (this._tempSuspendSrcLoading)
|
|
205
|
+
return;
|
|
69
206
|
if (value && this._session) {
|
|
70
207
|
this._session.loadUri(value);
|
|
71
208
|
}
|
|
@@ -73,18 +210,29 @@ export class WebViewX extends View {
|
|
|
73
210
|
[debugModeProperty.setNative](value) {
|
|
74
211
|
com.modos189.webviewxgecko.GeckoPopupHelper.setRemoteDebuggingEnabled(!!value);
|
|
75
212
|
}
|
|
76
|
-
getUserAgentOverride() {
|
|
77
|
-
return this._session?.getSettings().getUserAgentOverride() ?? null;
|
|
78
|
-
}
|
|
79
|
-
setUserAgentOverride(ua) {
|
|
80
|
-
this._session?.getSettings().setUserAgentOverride(ua || null);
|
|
81
|
-
}
|
|
82
213
|
[supportPopupsProperty.setNative](value) {
|
|
83
214
|
this._popupHelper?.setSupportPopups(!!value);
|
|
84
215
|
}
|
|
216
|
+
[userAgentProperty.setNative](value) {
|
|
217
|
+
this._session?.getSettings().setUserAgentOverride(value || null);
|
|
218
|
+
}
|
|
219
|
+
[autoInjectJSBridgeProperty.setNative](_value) {
|
|
220
|
+
// value stored as a property field; read in _onLoadFinished
|
|
221
|
+
}
|
|
85
222
|
}
|
|
86
|
-
WebViewX.userAgentTransform = null;
|
|
87
223
|
srcProperty.register(WebViewX);
|
|
88
224
|
debugModeProperty.register(WebViewX);
|
|
89
225
|
supportPopupsProperty.register(WebViewX);
|
|
226
|
+
userAgentProperty.register(WebViewX);
|
|
227
|
+
autoInjectJSBridgeProperty.register(WebViewX);
|
|
228
|
+
function _parseJsResult(jsonString) {
|
|
229
|
+
if (!jsonString || jsonString === 'null')
|
|
230
|
+
return null;
|
|
231
|
+
try {
|
|
232
|
+
return JSON.parse(jsonString);
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return jsonString;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
90
238
|
//# 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-gecko/index.android.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.android.js","sourceRoot":"","sources":["../../../packages/webview-x-gecko/index.android.ts"],"names":[],"mappings":"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"}
|
package/index.d.ts
CHANGED
|
@@ -4,13 +4,39 @@ import { Property } from '@nativescript/core';
|
|
|
4
4
|
export declare const srcProperty: Property<WebViewX, string>;
|
|
5
5
|
export declare const debugModeProperty: Property<WebViewX, boolean>;
|
|
6
6
|
export declare const supportPopupsProperty: Property<WebViewX, boolean>;
|
|
7
|
+
export declare const autoInjectJSBridgeProperty: Property<WebViewX, boolean>;
|
|
8
|
+
|
|
9
|
+
export * from './common';
|
|
10
|
+
import { LoadStartedEventData, LoadFinishedEventData, LoadProgressEventData, TitleChangedEventData } from './common';
|
|
7
11
|
|
|
8
12
|
export declare class WebViewX extends View {
|
|
9
|
-
static userAgentTransform: ((defaultUA: string | null) => string | null) | null;
|
|
10
13
|
static readonly popupNavigateEvent: string;
|
|
14
|
+
static readonly loadStartedEvent: string;
|
|
15
|
+
static readonly loadFinishedEvent: string;
|
|
16
|
+
static readonly loadProgressEvent: string;
|
|
17
|
+
static readonly titleChangedEvent: string;
|
|
18
|
+
|
|
11
19
|
src: string;
|
|
12
20
|
debugMode: boolean;
|
|
13
21
|
supportPopups: boolean;
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
userAgent: string;
|
|
23
|
+
autoInjectJSBridge: boolean;
|
|
24
|
+
|
|
25
|
+
on(event: 'loadStarted', callback: (args: LoadStartedEventData) => void, thisArg?: any): void;
|
|
26
|
+
on(event: 'loadFinished', callback: (args: LoadFinishedEventData) => void, thisArg?: any): void;
|
|
27
|
+
on(event: 'loadProgress', callback: (args: LoadProgressEventData) => void, thisArg?: any): void;
|
|
28
|
+
on(event: 'titleChanged', callback: (args: TitleChangedEventData) => void, thisArg?: any): void;
|
|
29
|
+
on(event: string, callback: (args: any) => void, thisArg?: any): void;
|
|
30
|
+
|
|
31
|
+
/** Returns the current page title (last value received from the engine). */
|
|
32
|
+
getTitle(): Promise<string | undefined>;
|
|
33
|
+
|
|
34
|
+
/** Execute JavaScript in the current page's context and return the result. */
|
|
35
|
+
executeJavaScript<T = any>(code: string): Promise<T>;
|
|
36
|
+
|
|
37
|
+
/** Emit an event into the page's nsWebViewBridge. */
|
|
38
|
+
emitToWebView(eventName: string, data: any): void;
|
|
39
|
+
|
|
40
|
+
/** Called when the page's nsWebViewBridge.emit() fires. Dispatches a NativeScript event. */
|
|
41
|
+
onWebViewEvent(eventName: string, data: any): void;
|
|
16
42
|
}
|
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-gecko/index.ios.ts"],"names":[],"mappings":"AAAA,cAAc,8CAA8C,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,8CAA8C,CAAC;AAExE,MAAM,OAAO,QAAS,SAAQ,QAAQ;
|
|
1
|
+
{"version":3,"file":"index.ios.js","sourceRoot":"","sources":["../../../packages/webview-x-gecko/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
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// "native" matches the name passed to extension.setMessageDelegate(bridge, "native")
|
|
4
|
+
const port = browser.runtime.connectNative('native');
|
|
5
|
+
|
|
6
|
+
async function sendMessageToTab(message) {
|
|
7
|
+
const tabs = await browser.tabs.query({});
|
|
8
|
+
if (tabs.length === 0) {
|
|
9
|
+
throw new Error('no tabs available');
|
|
10
|
+
}
|
|
11
|
+
return await browser.tabs.sendMessage(tabs[tabs.length - 1].id, message);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Forward nsWebViewBridge.emit() events from content script to native
|
|
15
|
+
browser.runtime.onMessage.addListener((message) => {
|
|
16
|
+
if (message.type === 'bridge-emit') {
|
|
17
|
+
port.postMessage(message);
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
port.onMessage.addListener((request) => {
|
|
23
|
+
sendMessageToTab(request)
|
|
24
|
+
.then((resp) => port.postMessage(resp))
|
|
25
|
+
.catch((e) => port.postMessage({ id: request.id, error: e.toString() }));
|
|
26
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
browser.runtime.onMessage.addListener((data) => {
|
|
4
|
+
if (data.inject === undefined) {
|
|
5
|
+
return Promise.resolve();
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
// wrappedJSObject bypasses Xray vision so eval runs in the page's JS context
|
|
9
|
+
const result = window.wrappedJSObject.eval(data.inject);
|
|
10
|
+
let serialized;
|
|
11
|
+
if (result === undefined) {
|
|
12
|
+
serialized = 'null';
|
|
13
|
+
} else {
|
|
14
|
+
try {
|
|
15
|
+
serialized = JSON.stringify(result);
|
|
16
|
+
if (serialized === undefined) serialized = 'null';
|
|
17
|
+
} catch (_e) {
|
|
18
|
+
serialized = String(result);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return Promise.resolve({ id: data.id, result: serialized });
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return Promise.resolve({ id: data.id, error: e.toString() });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Forward nsWebViewBridge.emit() calls from the page to the native layer.
|
|
28
|
+
// The bridge JS (injected on loadFinished) dispatches this CustomEvent on document
|
|
29
|
+
// when window.nsWebViewBridge.emit() is called.
|
|
30
|
+
document.addEventListener('__ns_bridge_emit__', (event) => {
|
|
31
|
+
browser.runtime.sendMessage({
|
|
32
|
+
type: 'bridge-emit',
|
|
33
|
+
eventName: event.detail.eventName,
|
|
34
|
+
data: event.detail.data,
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 2,
|
|
3
|
+
"name": "execute-js",
|
|
4
|
+
"version": "1.0",
|
|
5
|
+
"browser_specific_settings": {
|
|
6
|
+
"gecko": {
|
|
7
|
+
"strict_min_version": "65.0",
|
|
8
|
+
"id": "execute-js@app.com"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"background": {
|
|
12
|
+
"scripts": ["background.js"]
|
|
13
|
+
},
|
|
14
|
+
"permissions": ["nativeMessaging", "geckoViewAddons", "tabs", "<all_urls>"],
|
|
15
|
+
"content_scripts": [
|
|
16
|
+
{
|
|
17
|
+
"matches": ["<all_urls>"],
|
|
18
|
+
"js": ["content.js"],
|
|
19
|
+
"run_at": "document_start"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
package com.modos189.webviewxgecko;
|
|
2
|
+
|
|
3
|
+
import org.mozilla.geckoview.GeckoSession;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* NativeScript cannot generate runtime DEX proxies for GeckoView interfaces directly
|
|
7
|
+
* (Java 8 default methods + @UiThread annotations break the DEX factory).
|
|
8
|
+
* Each delegate accepts a simple custom listener that NativeScript can proxy instead.
|
|
9
|
+
*/
|
|
10
|
+
public class GeckoDelegates {
|
|
11
|
+
|
|
12
|
+
public interface ProgressListener {
|
|
13
|
+
void onPageStart(String url);
|
|
14
|
+
void onPageStop(boolean success);
|
|
15
|
+
void onProgressChange(int progress);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public interface ContentListener {
|
|
19
|
+
void onTitleChange(String title);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public static class ProgressDelegate implements GeckoSession.ProgressDelegate {
|
|
23
|
+
private final ProgressListener listener;
|
|
24
|
+
|
|
25
|
+
public ProgressDelegate(ProgressListener listener) {
|
|
26
|
+
this.listener = listener;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Override
|
|
30
|
+
public void onPageStart(GeckoSession session, String url) {
|
|
31
|
+
if (listener != null) listener.onPageStart(url != null ? url : "");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Override
|
|
35
|
+
public void onPageStop(GeckoSession session, boolean success) {
|
|
36
|
+
if (listener != null) listener.onPageStop(success);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Override
|
|
40
|
+
public void onProgressChange(GeckoSession session, int progress) {
|
|
41
|
+
if (listener != null) listener.onProgressChange(progress);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public static class ContentDelegate implements GeckoSession.ContentDelegate {
|
|
46
|
+
private final ContentListener listener;
|
|
47
|
+
|
|
48
|
+
public ContentDelegate(ContentListener listener) {
|
|
49
|
+
this.listener = listener;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@Override
|
|
53
|
+
public void onTitleChange(GeckoSession session, String title) {
|
|
54
|
+
if (listener != null && title != null && !title.isEmpty()) {
|
|
55
|
+
listener.onTitleChange(title);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
package com.modos189.webviewxgecko;
|
|
2
|
+
|
|
3
|
+
import android.os.Handler;
|
|
4
|
+
import android.os.Looper;
|
|
5
|
+
import android.util.Log;
|
|
6
|
+
|
|
7
|
+
import org.json.JSONException;
|
|
8
|
+
import org.json.JSONObject;
|
|
9
|
+
import org.mozilla.geckoview.GeckoRuntime;
|
|
10
|
+
import org.mozilla.geckoview.WebExtension;
|
|
11
|
+
import org.mozilla.geckoview.WebExtensionController;
|
|
12
|
+
|
|
13
|
+
import java.util.ArrayList;
|
|
14
|
+
import java.util.List;
|
|
15
|
+
import java.util.concurrent.ConcurrentHashMap;
|
|
16
|
+
import java.util.concurrent.CopyOnWriteArrayList;
|
|
17
|
+
import java.util.concurrent.atomic.AtomicInteger;
|
|
18
|
+
import java.lang.ref.WeakReference;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Singleton bridge for executing JavaScript in GeckoView pages via a built-in WebExtension.
|
|
22
|
+
* NativeScript cannot implement WebExtension delegate interfaces directly (Java 8 default
|
|
23
|
+
* methods break the DEX proxy factory), so this class acts as the concrete delegate.
|
|
24
|
+
*/
|
|
25
|
+
public class GeckoJsBridge implements WebExtension.MessageDelegate, WebExtension.PortDelegate {
|
|
26
|
+
|
|
27
|
+
private static final String TAG = "GeckoJsBridge";
|
|
28
|
+
private static final String EXTENSION_ID = "execute-js@app.com";
|
|
29
|
+
private static final String EXTENSION_URL = "resource://android/assets/execute-js/";
|
|
30
|
+
private static final String PORT_NAME = "native";
|
|
31
|
+
|
|
32
|
+
private static volatile GeckoJsBridge sInstance;
|
|
33
|
+
|
|
34
|
+
private WebExtension.Port mPort;
|
|
35
|
+
private final ConcurrentHashMap<String, JsCallback> mPendingCallbacks = new ConcurrentHashMap<>();
|
|
36
|
+
// Accessed only on main thread after posting via mMainHandler
|
|
37
|
+
private final List<JSONObject> mPendingMessages = new ArrayList<>();
|
|
38
|
+
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
|
|
39
|
+
private final AtomicInteger mIdCounter = new AtomicInteger(0);
|
|
40
|
+
|
|
41
|
+
public interface JsCallback {
|
|
42
|
+
void onResult(String jsonResult);
|
|
43
|
+
void onError(String error);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public interface BridgeEventListener {
|
|
47
|
+
void onBridgeEvent(String eventName, String dataJson);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private final CopyOnWriteArrayList<WeakReference<BridgeEventListener>> mBridgeListeners = new CopyOnWriteArrayList<>();
|
|
51
|
+
|
|
52
|
+
public void addBridgeListener(BridgeEventListener listener) {
|
|
53
|
+
mBridgeListeners.add(new WeakReference<>(listener));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public void removeBridgeListener(BridgeEventListener listener) {
|
|
57
|
+
mBridgeListeners.removeIf(ref -> {
|
|
58
|
+
BridgeEventListener l = ref.get();
|
|
59
|
+
return l == null || l == listener;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private void notifyBridgeListeners(String eventName, String dataJson) {
|
|
64
|
+
for (WeakReference<BridgeEventListener> ref : mBridgeListeners) {
|
|
65
|
+
BridgeEventListener listener = ref.get();
|
|
66
|
+
if (listener != null) {
|
|
67
|
+
listener.onBridgeEvent(eventName, dataJson);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public static GeckoJsBridge getInstance() {
|
|
73
|
+
if (sInstance == null) {
|
|
74
|
+
synchronized (GeckoJsBridge.class) {
|
|
75
|
+
if (sInstance == null) {
|
|
76
|
+
sInstance = new GeckoJsBridge();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return sInstance;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private GeckoJsBridge() {}
|
|
84
|
+
|
|
85
|
+
public String nextId() {
|
|
86
|
+
return String.valueOf(mIdCounter.incrementAndGet());
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Idempotent - safe to call on every WebViewX creation. */
|
|
90
|
+
public void setup(GeckoRuntime runtime) {
|
|
91
|
+
runtime.getWebExtensionController()
|
|
92
|
+
.ensureBuiltIn(EXTENSION_URL, EXTENSION_ID)
|
|
93
|
+
.accept(
|
|
94
|
+
extension -> mMainHandler.post(() -> {
|
|
95
|
+
if (extension == null) return;
|
|
96
|
+
// Disable then re-enable so the background script reconnects
|
|
97
|
+
// and triggers onConnect even if the extension was already loaded.
|
|
98
|
+
runtime.getWebExtensionController()
|
|
99
|
+
.disable(extension, WebExtensionController.EnableSource.APP);
|
|
100
|
+
runtime.getWebExtensionController()
|
|
101
|
+
.enable(extension, WebExtensionController.EnableSource.APP);
|
|
102
|
+
extension.setMessageDelegate(GeckoJsBridge.this, PORT_NAME);
|
|
103
|
+
}),
|
|
104
|
+
e -> Log.e(TAG, "Failed to install WebExtension", e)
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public void executeScript(final String id, final String code, final JsCallback callback) {
|
|
109
|
+
final JSONObject msg;
|
|
110
|
+
try {
|
|
111
|
+
msg = new JSONObject();
|
|
112
|
+
msg.put("id", id);
|
|
113
|
+
msg.put("inject", code);
|
|
114
|
+
} catch (JSONException e) {
|
|
115
|
+
callback.onError(e.getMessage());
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
mPendingCallbacks.put(id, callback);
|
|
120
|
+
|
|
121
|
+
mMainHandler.post(() -> {
|
|
122
|
+
if (mPort != null) {
|
|
123
|
+
mPort.postMessage(msg);
|
|
124
|
+
} else {
|
|
125
|
+
mPendingMessages.add(msg);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@Override
|
|
131
|
+
public void onConnect(WebExtension.Port port) {
|
|
132
|
+
mPort = port;
|
|
133
|
+
port.setDelegate(this);
|
|
134
|
+
|
|
135
|
+
// Flush any calls that arrived before the port was ready
|
|
136
|
+
for (JSONObject msg : mPendingMessages) {
|
|
137
|
+
port.postMessage(msg);
|
|
138
|
+
}
|
|
139
|
+
mPendingMessages.clear();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@Override
|
|
143
|
+
public void onPortMessage(Object message, WebExtension.Port port) {
|
|
144
|
+
if (!(message instanceof JSONObject)) return;
|
|
145
|
+
final JSONObject obj = (JSONObject) message;
|
|
146
|
+
try {
|
|
147
|
+
if ("bridge-emit".equals(obj.optString("type"))) {
|
|
148
|
+
final String eventName = obj.optString("eventName", "");
|
|
149
|
+
final String data = obj.optString("data", "null");
|
|
150
|
+
mMainHandler.post(() -> notifyBridgeListeners(eventName, data));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
final String id = obj.getString("id");
|
|
155
|
+
final JsCallback callback = mPendingCallbacks.remove(id);
|
|
156
|
+
if (callback == null) return;
|
|
157
|
+
|
|
158
|
+
if (obj.has("error")) {
|
|
159
|
+
callback.onError(obj.getString("error"));
|
|
160
|
+
} else {
|
|
161
|
+
// optString falls back to "null" when key is absent or value is JSON null
|
|
162
|
+
callback.onResult(obj.optString("result", "null"));
|
|
163
|
+
}
|
|
164
|
+
} catch (JSONException e) {
|
|
165
|
+
Log.e(TAG, "Error processing JS result message", e);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@Override
|
|
170
|
+
public void onDisconnect(WebExtension.Port port) {
|
|
171
|
+
mPort = null;
|
|
172
|
+
}
|
|
173
|
+
}
|