diff --git a/harmony/rn_webview/src/main/ets/RNCWebView.ets b/harmony/rn_webview/src/main/ets/RNCWebView.ets index 17b4156fb..ae79933f0 100644 --- a/harmony/rn_webview/src/main/ets/RNCWebView.ets +++ b/harmony/rn_webview/src/main/ets/RNCWebView.ets @@ -25,7 +25,7 @@ import { RNComponentContext } from '@rnoh/react-native-openharmony'; import webview from '@ohos.web.webview'; import { url as OSUrl } from '@kit.ArkTS'; -import { RNC } from '@rnoh/react-native-openharmony/generated'; +import { RNC, TM } from '@rnoh/react-native-openharmony/generated'; import Logger from './Logger'; import { BaseOperate } from './WebViewBaseOperate'; import { @@ -45,6 +45,7 @@ import { REFRESH_OFFSET, MINHEIGHT, } from './Magic'; +import { WebViewTurboModule } from './WebViewTurboModule'; import { bundleManager } from '@kit.AbilityKit'; export const TAG = "WebView" @@ -73,7 +74,6 @@ export struct RNCWebView { overScrollMode: OverScrollMode = OverScrollMode.NEVER; progress: number = ZERO; cacheMode: number = CacheMode.Default; - lockIdentifier: string = ""; requestUrl: string = ""; bundleName: string = ""; messagingEnabled: boolean = false; @@ -97,6 +97,9 @@ export struct RNCWebView { private cleanUpCallbacks: (() => void)[] = [] private descriptorWrapper: WebViewDescriptor = Object() as WebViewDescriptor private webViewBaseOperate: BaseOperate | null = null + private shouldInterceptLoad: boolean = true + private callLoadTimer: number = -1 + static readonly SHOULD_OVERRIDE_URL_LOADING_TIMEOUT: number = 250 aboutToAppear() { try { @@ -269,6 +272,7 @@ export struct RNCWebView { let uri = this.source.uri if (this.source.html != undefined && this.source.html != "") { try { + this.resetIntercept() this.controller.loadData( this.source.html, "text/html", @@ -280,8 +284,10 @@ export struct RNCWebView { Logger.error(TAG, "error:" + error) } } else if (uri != undefined && uri != "") { + this.resetIntercept() this.controller.loadUrl(uri, this.headers); } else { + this.resetIntercept() this.controller.loadUrl(uri, this.headers); } if (!this.hasRegisterJavaScriptProxy) { @@ -352,8 +358,41 @@ export struct RNCWebView { } onLoadIntercept(event: OnLoadInterceptEvent): boolean { + Logger.debug(TAG, `onLoadIntercept request url:${event.data.getRequestUrl()} ,shouldInterceptLoad:${this.shouldInterceptLoad}`) + if (!this.shouldInterceptLoad) { + return false + } + + let lockIdentifier = this.webViewBaseOperate?.getLockIdentifier() + let webViewTurboModule = this.ctx.rnInstance.getUITurboModule(TM.RNCWebViewModule.NAME) + this.callLoadTimer = setTimeout(()=>{ + Logger.debug(TAG, "call setTimeout") + webViewTurboModule.callLoadFunction(lockIdentifier) + }, RNCWebView.SHOULD_OVERRIDE_URL_LOADING_TIMEOUT) + + webViewTurboModule.setLoadCallback(lockIdentifier, ()=>{ + Logger.debug(TAG, "call setLoadCallback") + clearTimeout(this.callLoadTimer) + this.shouldInterceptLoad = false + if (this.html != "") { + try { + this.controller.loadData( + this.source.html, + "text/html", + "UTF-8", + this.source.baseUrl, + " " + ); + } catch (error) { + Logger.error(TAG, "error: " + error) + } + } else if (this.source.uri != "") { + Logger.debug(TAG, `uri: ` + this.source.uri); + this.controller.loadUrl(this.descriptorWrapper.rawProps.newSource.uri, this.headers) + } + }) this.webViewBaseOperate?.emitShouldStartLoadWithRequest(event) - return false + return true } onOverrideUrlLoading(event: WebResourceRequest) { @@ -361,6 +400,15 @@ export struct RNCWebView { return false } + resetIntercept() { + Logger.debug(TAG,"resetIntercept") + this.ctx.rnInstance.getUITurboModule(TM.RNCWebViewModule.NAME) + .clearLoadFunction(this.webViewBaseOperate?.getLockIdentifier()) + clearTimeout(this.callLoadTimer) + this.shouldInterceptLoad = true + this.webViewBaseOperate?.setLockIdentifier(BaseOperate.generateLockIdentifier()) + } + build() { Stack() { Web({ src: "", controller: this.controller, renderMode: this.renderMode }) @@ -444,6 +492,7 @@ export struct RNCWebView { this.html = this.source.html if (this.controllerAttached) { try { + this.resetIntercept() this.controller.loadData( this.source.html, "text/html", @@ -459,6 +508,7 @@ export struct RNCWebView { Logger.debug(TAG, `[RNOH] newDescriptor props update uri: ` + this.source.uri); this.url = this.source.uri as string; if (this.controllerAttached) { + this.resetIntercept() this.controller.loadUrl(this.descriptorWrapper.rawProps.newSource.uri, this.headers) } } @@ -529,13 +579,14 @@ export struct RNCWebView { let result: WebViewEventParams = this.createWebViewEvent("onMessage") if (result) { result.data = data.toString() - result.lockIdentifier = ZERO + result.lockIdentifier = this.webViewBaseOperate?.getLockIdentifier() this.eventEmitter!.emit("message", result as ResultType); } } } }; this.controller.registerJavaScriptProxy(bridge, JAVASCRIPT_INTERFACE, ["postMessage"]) + this.resetIntercept() this.source.uri ? this.controller.loadUrl(this.source.uri, this.headers) : this.controller.refresh() this.hasRegisterJavaScriptProxy = true diff --git a/harmony/rn_webview/src/main/ets/RNCWebViewPackage.ts b/harmony/rn_webview/src/main/ets/RNCWebViewPackage.ts index ee5135ecc..9a320ead4 100644 --- a/harmony/rn_webview/src/main/ets/RNCWebViewPackage.ts +++ b/harmony/rn_webview/src/main/ets/RNCWebViewPackage.ts @@ -26,8 +26,10 @@ import type { DescriptorWrapperFactoryByDescriptorType, DescriptorWrapperFactoryByDescriptorTypeCtx, } from '@rnoh/react-native-openharmony/ts'; -import { RNPackage } from '@rnoh/react-native-openharmony/ts'; -import { RNC } from '@rnoh/react-native-openharmony/generated/ts'; +import { RNPackage, UITurboModuleFactory, UITurboModule } from '@rnoh/react-native-openharmony/ts'; +import { RNC, TM } from '@rnoh/react-native-openharmony/generated/ts'; +import { WebViewTurboModule } from './WebViewTurboModule' +import { UITurboModuleContext } from '@rnoh/react-native-openharmony/src/main/ets/RNOH/RNOHContext'; export class RNCWebViewPackage extends RNPackage { createDescriptorWrapperFactoryByDescriptorType(ctx: DescriptorWrapperFactoryByDescriptorTypeCtx): DescriptorWrapperFactoryByDescriptorType { @@ -35,4 +37,21 @@ export class RNCWebViewPackage extends RNPackage { [RNC.RNCWebView.NAME]: (ctx) => new RNC.RNCWebView.DescriptorWrapper(ctx.descriptor) } } + + createUITurboModuleFactory(ctx: UITurboModuleContext): UITurboModuleFactory { + return new WebViewTurboModulesFactory(ctx); + } +} + +class WebViewTurboModulesFactory extends UITurboModuleFactory { + createTurboModule(name: string): UITurboModule | null { + if (name === TM.RNCWebViewModule.NAME) { + return new WebViewTurboModule(this.ctx); + } + return null; + } + + hasTurboModule(name: string): boolean { + return name === TM.RNCWebViewModule.NAME; + } } \ No newline at end of file diff --git a/harmony/rn_webview/src/main/ets/WebViewBaseOperate.ets b/harmony/rn_webview/src/main/ets/WebViewBaseOperate.ets index cf86012e0..09c91d481 100644 --- a/harmony/rn_webview/src/main/ets/WebViewBaseOperate.ets +++ b/harmony/rn_webview/src/main/ets/WebViewBaseOperate.ets @@ -17,13 +17,15 @@ interface CreateWebViewEventInterface { } export class BaseOperate { + private static LOCK_IDENTIFIER = 0 private eventEmitter: RNC.RNCWebView.EventEmitter private controller: webview.WebviewController - private title: string = '' + private lockIdentifier: number = 0 constructor(eventEmitter: RNC.RNCWebView.EventEmitter, controller: webview.WebviewController) { this.eventEmitter = eventEmitter this.controller = controller + this.lockIdentifier = BaseOperate.generateLockIdentifier() } static setThirdPartyCookiesEnabled(status: boolean) { @@ -44,6 +46,19 @@ export class BaseOperate { } } + static generateLockIdentifier(): number { + BaseOperate.LOCK_IDENTIFIER = BaseOperate.LOCK_IDENTIFIER + 1 + return BaseOperate.LOCK_IDENTIFIER + } + + getLockIdentifier():number { + return this.lockIdentifier + } + + setLockIdentifier(lockIdentifier: number) { + this.lockIdentifier = lockIdentifier + } + emitProgressChange(params: ProgressInterface) { try { this.eventEmitter!.emit('loadingProgress', { @@ -52,7 +67,7 @@ export class BaseOperate { title: this.controller.getTitle(), canGoBack: this.controller.accessBackward(), canGoForward: this.controller.accessForward(), - lockIdentifier: 0, + lockIdentifier: this.lockIdentifier, progress: params.progress / ONE_HUNDRED }) } catch (error) { @@ -68,7 +83,7 @@ export class BaseOperate { title: this.controller.getTitle(), canGoBack: this.controller.accessBackward(), canGoForward: this.controller.accessForward(), - lockIdentifier: 0, + lockIdentifier: this.lockIdentifier, navigationType: "other", mainDocumentURL: "" }) @@ -85,7 +100,7 @@ export class BaseOperate { title: this.controller.getTitle(), canGoBack: this.controller.accessBackward(), canGoForward: this.controller.accessForward(), - lockIdentifier: 0, + lockIdentifier: this.lockIdentifier, navigationType: "other", mainDocumentURL: "" }) @@ -106,7 +121,7 @@ export class BaseOperate { title: this.controller.getTitle(), canGoBack: this.controller.accessBackward(), canGoForward: this.controller.accessForward(), - lockIdentifier: 0, + lockIdentifier: this.lockIdentifier, domain: "", code: event.error.getErrorCode(), description: event.error.getErrorInfo() @@ -124,7 +139,7 @@ export class BaseOperate { title: this.controller.getTitle(), canGoBack: this.controller.accessBackward(), canGoForward: this.controller.accessForward(), - lockIdentifier: 0, + lockIdentifier: this.lockIdentifier, description: event.response.getResponseData(), statusCode: event.response.getResponseCode() }) @@ -159,7 +174,7 @@ export class BaseOperate { title: this.controller.getTitle(), canGoBack: this.controller.accessBackward(), canGoForward: this.controller.accessForward(), - lockIdentifier: 0, + lockIdentifier: this.lockIdentifier, navigationType: "other", mainDocumentURL: "", isTopFrame: false @@ -177,7 +192,7 @@ export class BaseOperate { title: this.controller.getTitle(), canGoBack: this.controller.accessBackward(), canGoForward: this.controller.accessForward(), - lockIdentifier: 0, + lockIdentifier: this.lockIdentifier, navigationType: "other", mainDocumentURL: "", isTopFrame: false diff --git a/harmony/rn_webview/src/main/ets/WebViewTurboModule.ts b/harmony/rn_webview/src/main/ets/WebViewTurboModule.ts new file mode 100644 index 000000000..043fd00f0 --- /dev/null +++ b/harmony/rn_webview/src/main/ets/WebViewTurboModule.ts @@ -0,0 +1,37 @@ +import { UITurboModule } from '@rnoh/react-native-openharmony/ts'; +import { TM } from '@rnoh/react-native-openharmony/generated/ts'; +import Logger from './Logger'; + +const TAG = "WebViewTurboModule" + +export class WebViewTurboModule extends UITurboModule implements TM.RNCWebViewModule.Spec { + private loadCallbackMap : Map void> = new Map(); + + isFileUploadSupported(): Promise { + return Promise.resolve(true) + } + + shouldStartLoadWithLockIdentifier(shouldStart: boolean, lockIdentifier: number): void { + Logger.debug(TAG,`shouldStartLoadWithLockIdentifier shouldStart:${shouldStart},lockIdentifier:${lockIdentifier}`) + if (shouldStart) { + this.callLoadFunction(lockIdentifier) + }else { + this.clearLoadFunction(lockIdentifier) + } + } + + setLoadCallback(lockIdentifier:number, cb: ()=> void) { + this.loadCallbackMap.set(lockIdentifier,cb) + } + + callLoadFunction(lockIdentifier:number){ + Logger.debug(TAG,`callLoadFunction function:${JSON.stringify(this.loadCallbackMap.get(lockIdentifier))}`) + this.loadCallbackMap.get(lockIdentifier)?.() + this.clearLoadFunction(lockIdentifier) + } + + clearLoadFunction(lockIdentifier:number){ + Logger.debug(TAG,`clearLoadFunction lockIdentifier:${lockIdentifier} `) + this.loadCallbackMap.delete(lockIdentifier) + } +} \ No newline at end of file diff --git a/src/NativeRNCWebViewModule.ts b/src/NativeRNCWebViewModule.ts new file mode 100644 index 000000000..220d2390f --- /dev/null +++ b/src/NativeRNCWebViewModule.ts @@ -0,0 +1,13 @@ +import type { TurboModule } from 'react-native'; +import { TurboModuleRegistry } from 'react-native'; +import { Double } from 'react-native/Libraries/Types/CodegenTypes'; + +export interface Spec extends TurboModule { + isFileUploadSupported(): Promise; + shouldStartLoadWithLockIdentifier( + shouldStart: boolean, + lockIdentifier: Double + ): void; +} + +export default TurboModuleRegistry.getEnforcing('RNCWebViewModule'); diff --git a/src/WebView.harmony.tsx b/src/WebView.harmony.tsx index 0aa109195..cc91720ee 100644 --- a/src/WebView.harmony.tsx +++ b/src/WebView.harmony.tsx @@ -5,10 +5,10 @@ import React, { useRef } from 'react'; import { Image, View, ImageSourcePropType, HostComponent } from 'react-native'; -import { Double } from 'react-native/Libraries/Types/CodegenTypes'; import invariant from 'invariant'; import RNCWebView, { Commands, NativeProps } from './RNCWebViewNativeComponent'; +import RNCWebViewModule from './NativeRNCWebViewModule'; import { defaultOriginWhitelist, @@ -46,11 +46,6 @@ const useWarnIfChanges = (value: T, name: string) => { } }; -const shouldStartLoadWithLockIdentifier: ( - shouldStart: boolean, - lockIdentifier: Double -) => void = () => { } - const WebViewComponent = forwardRef<{}, IOSWebViewProps & { scalesPageToFit: boolean, minimumFontSize: number, thirdPartyCookiesEnabled: boolean, geolocationEnabled: boolean }>( ( { @@ -113,7 +108,7 @@ const WebViewComponent = forwardRef<{}, IOSWebViewProps & { scalesPageToFit: boo const onShouldStartLoadWithRequestCallback = useCallback( (shouldStart: boolean, _url: string, lockIdentifier = 0) => { - shouldStartLoadWithLockIdentifier( + RNCWebViewModule.shouldStartLoadWithLockIdentifier( shouldStart, lockIdentifier ); @@ -328,4 +323,4 @@ const isFileUploadSupported: () => Promise = async () => true; const WebView = Object.assign(WebViewComponent, { isFileUploadSupported }); -export default WebView; +export default WebView; \ No newline at end of file