@react-native-ohos/react-native-webview 13.16.1-rc.1 → 13.16.2-beta.2

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
@@ -4,9 +4,9 @@ This project is based on [react-native-webview@13.15.0](https://github.com/react
4
4
 
5
5
  ## Documentation
6
6
 
7
- - [中文](https://gitee.com/react-native-oh-library/usage-docs/blob/master/zh-cn/react-native-webview.md)
7
+ - [中文](https://gitcode.com/CPF-RN/usage-docs/blob/master/zh-cn/react-native-webview.md)
8
8
 
9
- - [English](https://gitee.com/react-native-oh-library/usage-docs/blob/master/en/react-native-webview.md)
9
+ - [English](https://gitcode.com/CPF-RN/usage-docs/blob/master/en/react-native-webview.md)
10
10
 
11
11
  ## License
12
12
 
@@ -6,7 +6,7 @@
6
6
  "name": "@react-native-ohos/react-native-webview",
7
7
  "description": "main cpai architecture",
8
8
  "main": "index.ets",
9
- "version": "13.16.1-rc.1",
9
+ "version": "13.16.2-beta.2",
10
10
  "dependencies": {
11
11
  "@rnoh/react-native-openharmony": "../react_native_openharmony.har"
12
12
  }
@@ -64,8 +64,22 @@ export interface WebViewProps extends ViewRawProps {
64
64
  geolocationEnabled: boolean
65
65
  applicationNameForUserAgent: string
66
66
  pullToRefreshEnabled: boolean
67
+ mediaCapturePermissionGrantType?: 'prompt' | 'grant' | 'deny' | 'grantIfSameHostElsePrompt' | 'grantIfSameHostElseDeny'
68
+ menuItems?: Array<MenuItem> | undefined;
67
69
  }
68
70
 
71
+ export interface MenuItem {
72
+ label: string,
73
+ key: string
74
+ }
75
+
76
+ export enum WebMediaCapturePermissionGrantType {
77
+ GRANT_IF_SAME_HOST_ELSE_PROMPT = "grantIfSameHostElsePrompt",
78
+ GRANT_IF_SAME_HOST_ELSE_DENY = "grantIfSameHostElseDeny",
79
+ DENY = "deny",
80
+ GRANT = "grant",
81
+ PROMPT = "prompt",
82
+ }
69
83
 
70
84
  export class RNCWebViewBridge {
71
85
  postMessage!: (data: string) => void;
@@ -25,9 +25,11 @@ import {
25
25
  WebViewDescriptor,
26
26
  REFRESH_OFFSET,
27
27
  MINHEIGHT,
28
+ WebMediaCapturePermissionGrantType,
29
+ MenuItem
28
30
  } from './Magic';
29
31
 
30
- import { abilityAccessCtrl, bundleManager } from '@kit.AbilityKit';
32
+ import { abilityAccessCtrl, bundleManager, Permissions } from '@kit.AbilityKit';
31
33
  import { WebViewTurboModule, ShouldStartParams, ShouldOverrideCallbackState } from './WebViewTurboModule';
32
34
  import { AnyThreadRNInstance, WorkerTaskRunnable } from '@rnoh/react-native-openharmony/src/main/ets/RNOH';
33
35
  import buffer from '@ohos.buffer';
@@ -404,34 +406,131 @@ export struct RNCWebView {
404
406
  onSslErrorEventCallback(event: SslErrorEvent){
405
407
  this.webViewBaseOperate?.emitOnSslErrorEventCallback(event);
406
408
  }
409
+
410
+ onCreateMenu = (menuItems: Array<TextMenuItem>) => {
411
+ let customMenuItems = this.descriptorWrapper.rawProps.menuItems
412
+ if (customMenuItems == undefined) {
413
+ return menuItems;
414
+ } else if (customMenuItems.length == 0) {
415
+ return [];
416
+ } else {
417
+ let menuItemsArray: Array<TextMenuItem> = [];
418
+ for (let index = 0; index < customMenuItems.length; index++) {
419
+ let element: MenuItem = customMenuItems[index];
420
+ let customItem: TextMenuItem = {
421
+ content: element.label,
422
+ id: TextMenuItemId.of(element.key)
423
+ };
424
+ menuItemsArray.push(customItem)
425
+ }
426
+ return menuItemsArray;
427
+ }
428
+ }
429
+
430
+ onMenuItemClick(menuItem: TextMenuItem): boolean {
431
+ if (this.descriptorWrapper.rawProps.menuItems != undefined) {
432
+ this.controller.runJavaScript("(function(){return {selection: window.getSelection().toString()} })()")
433
+ .then((result: string) => {
434
+ this.webViewBaseOperate?.customMenuSelection(menuItem, result)
435
+ })
436
+ .catch((error: string | Error) => {
437
+ Logger.info('[RNOH] function onMenuItemClick error: ' + error);
438
+ })
439
+ // 禁用系统默认行为
440
+ return true;
441
+ }
442
+ return false;
443
+ }
444
+
445
+ onPermissionRequestAlert(event: OnPermissionRequestEvent){
446
+ AlertDialog.show({
447
+ title: this.resourceToString($r('app.string.on_confirm')) + event.request.getOrigin(),
448
+ message: this.getPermissionDialogMessage(event),
449
+ alignment: DialogAlignment.Center,
450
+ primaryButton: {
451
+ value: $r('app.string.deny'),
452
+ action: () => {
453
+ event.request.deny();
454
+ }
455
+ },
456
+ secondaryButton: {
457
+ value: $r('app.string.on_confirm'),
458
+ action: () => {
459
+ event.request.grant(event.request.getAccessibleResource());
460
+ }
461
+ },
462
+ cancel: () => {
463
+ event.request.deny();
464
+ }
465
+ })
466
+ }
407
467
 
468
+ async handleMediaCapturePermissionGrantType(resources: string[], domain: string) {
469
+ let permissionList: Array<Permissions> = [];
470
+ resources.forEach(resource => {
471
+ switch (resource.toString()) {
472
+ case 'TYPE_VIDEO_CAPTURE':
473
+ permissionList.push('ohos.permission.CAMERA');
474
+ break;
475
+ case 'TYPE_AUDIO_CAPTURE':
476
+ permissionList.push('ohos.permission.MICROPHONE');
477
+ break;
478
+ }
479
+ });
480
+
481
+ await atManager.requestPermissionsFromUser(this.ctx.uiAbilityContext, permissionList);
482
+ }
483
+
408
484
  build() {
409
485
  Stack() {
410
486
  if ( deviceInfo.sdkApiVersion >= 20) {
411
487
  Web({ src: "", controller: this.controller, renderMode: this.renderMode })
412
488
  .mixedMode(this.mode)
413
- .onPermissionRequest((event: OnPermissionRequestEvent) => {
489
+ .onPermissionRequest(async (event: OnPermissionRequestEvent) => {
414
490
  if (event && (this.getPermissionDialogMessage(event) != "TYPE_SENSOR")) {
415
- AlertDialog.show({
416
- title: this.resourceToString($r('app.string.on_confirm')) + event.request.getOrigin(),
417
- message: this.getPermissionDialogMessage(event),
418
- alignment: DialogAlignment.Center,
419
- primaryButton: {
420
- value: $r('app.string.deny'),
421
- action: () => {
491
+ const resources: string[] = event.request.getAccessibleResource();
492
+ if (resources.indexOf('TYPE_AUDIO_CAPTURE') >= 0 || resources.indexOf('TYPE_VIDEO_CAPTURE') >= 0 ) {
493
+ const originUrl = this.webViewBaseOperate?.verifyURLFormat(event.request.getOrigin(), this.source.html,
494
+ this.source.baseUrl);
495
+ const currentUrl = this.webViewBaseOperate?.verifyURLFormat(this.controller.getUrl(), this.source.html,
496
+ this.source.baseUrl);
497
+ const originDomain: string = OSUrl.URL.parseURL(originUrl).hostname;
498
+ const currentDomain: string = OSUrl.URL.parseURL(currentUrl).hostname;
499
+ let mediaCapturePermissionGrantType: string = WebMediaCapturePermissionGrantType.PROMPT
500
+ if (this.descriptorWrapper.rawProps.mediaCapturePermissionGrantType != undefined) {
501
+ mediaCapturePermissionGrantType = this.descriptorWrapper.rawProps.mediaCapturePermissionGrantType;
502
+ }
503
+ if (mediaCapturePermissionGrantType != WebMediaCapturePermissionGrantType.DENY) {
504
+ await this.handleMediaCapturePermissionGrantType(resources, originDomain)
505
+ }
506
+ switch (mediaCapturePermissionGrantType) {
507
+ case WebMediaCapturePermissionGrantType.GRANT_IF_SAME_HOST_ELSE_PROMPT:
508
+ if (originDomain != currentDomain) {
509
+ this.onPermissionRequestAlert(event)
510
+ } else {
511
+ event.request.grant(resources);
512
+ }
513
+ break;
514
+ case WebMediaCapturePermissionGrantType.GRANT_IF_SAME_HOST_ELSE_DENY:
515
+ if (originDomain != currentDomain) {
516
+ event.request.deny();
517
+ } else {
518
+ event.request.grant(resources);
519
+ }
520
+ break;
521
+ case WebMediaCapturePermissionGrantType.DENY:
422
522
  event.request.deny();
423
- }
424
- },
425
- secondaryButton: {
426
- value: $r('app.string.on_confirm'),
427
- action: () => {
428
- event.request.grant(event.request.getAccessibleResource());
429
- }
430
- },
431
- cancel: () => {
432
- event.request.deny();
523
+ break;
524
+ case WebMediaCapturePermissionGrantType.GRANT:
525
+ event.request.grant(resources);
526
+ break;
527
+ case WebMediaCapturePermissionGrantType.PROMPT:
528
+ this.onPermissionRequestAlert(event)
529
+ break;
433
530
  }
434
- })
531
+ } else {
532
+ this.onPermissionRequestAlert(event)
533
+ }
435
534
  }
436
535
  })
437
536
  .width(this.webviewWidth)
@@ -480,31 +579,58 @@ export struct RNCWebView {
480
579
  .onLoadIntercept((event: OnLoadInterceptEvent) => this.onLoadIntercept(event))
481
580
  .onTitleReceive((event: OnTitleReceiveEvent) => this.webViewBaseOperate?.onTitleReceive(event))
482
581
  .onSslErrorEvent((event: SslErrorEvent) => this.onSslErrorEventCallback(event))
582
+ .editMenuOptions({
583
+ onCreateMenu: this.onCreateMenu.bind(this),
584
+ onMenuItemClick: this.onMenuItemClick.bind(this),
585
+ })
483
586
  } else {
484
587
  Web({ src: "", controller: this.controller, renderMode: this.renderMode })
485
588
  .mixedMode(this.mode)
486
- .onPermissionRequest((event: OnPermissionRequestEvent) => {
589
+ .onPermissionRequest(async (event: OnPermissionRequestEvent) => {
487
590
  if (event && (this.getPermissionDialogMessage(event) != "TYPE_SENSOR")) {
488
- AlertDialog.show({
489
- title: this.resourceToString($r('app.string.on_confirm')) + event.request.getOrigin(),
490
- message: this.getPermissionDialogMessage(event),
491
- alignment: DialogAlignment.Center,
492
- primaryButton: {
493
- value: $r('app.string.deny'),
494
- action: () => {
591
+ const resources: string[] = event.request.getAccessibleResource();
592
+ if (resources.indexOf('TYPE_AUDIO_CAPTURE') >= 0 || resources.indexOf('TYPE_VIDEO_CAPTURE') >= 0 ) {
593
+ const originUrl = this.webViewBaseOperate?.verifyURLFormat(event.request.getOrigin(), this.source.html,
594
+ this.source.baseUrl);
595
+ const currentUrl = this.webViewBaseOperate?.verifyURLFormat(this.controller.getUrl(), this.source.html,
596
+ this.source.baseUrl);
597
+ const originDomain: string = OSUrl.URL.parseURL(originUrl).hostname;
598
+ const currentDomain: string = OSUrl.URL.parseURL(currentUrl).hostname;
599
+ let mediaCapturePermissionGrantType: string = WebMediaCapturePermissionGrantType.PROMPT
600
+ if (this.descriptorWrapper.rawProps.mediaCapturePermissionGrantType != undefined) {
601
+ mediaCapturePermissionGrantType = this.descriptorWrapper.rawProps.mediaCapturePermissionGrantType;
602
+ }
603
+ if (mediaCapturePermissionGrantType != WebMediaCapturePermissionGrantType.DENY) {
604
+ await this.handleMediaCapturePermissionGrantType(resources, originDomain)
605
+ }
606
+ switch (mediaCapturePermissionGrantType) {
607
+ case WebMediaCapturePermissionGrantType.GRANT_IF_SAME_HOST_ELSE_PROMPT:
608
+ if (originDomain != currentDomain) {
609
+ this.onPermissionRequestAlert(event)
610
+ } else {
611
+ event.request.grant(resources);
612
+ }
613
+ break;
614
+ case WebMediaCapturePermissionGrantType.GRANT_IF_SAME_HOST_ELSE_DENY:
615
+ if (originDomain != currentDomain) {
616
+ event.request.deny();
617
+ } else {
618
+ event.request.grant(resources);
619
+ }
620
+ break;
621
+ case WebMediaCapturePermissionGrantType.DENY:
495
622
  event.request.deny();
496
- }
497
- },
498
- secondaryButton: {
499
- value: $r('app.string.on_confirm'),
500
- action: () => {
501
- event.request.grant(event.request.getAccessibleResource());
502
- }
503
- },
504
- cancel: () => {
505
- event.request.deny();
623
+ break;
624
+ case WebMediaCapturePermissionGrantType.GRANT:
625
+ event.request.grant(resources);
626
+ break;
627
+ case WebMediaCapturePermissionGrantType.PROMPT:
628
+ this.onPermissionRequestAlert(event)
629
+ break;
506
630
  }
507
- })
631
+ } else {
632
+ this.onPermissionRequestAlert(event)
633
+ }
508
634
  }
509
635
  })
510
636
 
@@ -553,6 +679,10 @@ export struct RNCWebView {
553
679
  .onLoadIntercept((event: OnLoadInterceptEvent) => this.onLoadIntercept(event))
554
680
  .onTitleReceive((event: OnTitleReceiveEvent) => this.webViewBaseOperate?.onTitleReceive(event))
555
681
  .onSslErrorEvent((event: SslErrorEvent) => this.onSslErrorEventCallback(event))
682
+ .editMenuOptions({
683
+ onCreateMenu: this.onCreateMenu.bind(this),
684
+ onMenuItemClick: this.onMenuItemClick.bind(this),
685
+ })
556
686
  }
557
687
  }
558
688
  .width(this.webviewWidth)
@@ -498,4 +498,17 @@ export class BaseOperate {
498
498
  Logger.error(TAG, `[RNOH] onTitleReceive Errorcode: ${error.code}`);
499
499
  }
500
500
  }
501
+
502
+ customMenuSelection(menuItem: TextMenuItem, selectionText: string) {
503
+ try {
504
+ selectionText = JSON.parse(selectionText).selection;
505
+ this.eventEmitter!.emit('customMenuSelection', {
506
+ label: menuItem.content.toString(),
507
+ key: JSON.parse(JSON.stringify(menuItem.id)).id_,
508
+ selectedText: selectionText
509
+ })
510
+ } catch (error) {
511
+ Logger.error(TAG, `[RNOH] customMenuSelection Errorcode: ${error.code}`);
512
+ }
513
+ }
501
514
  }
Binary file
package/package.json CHANGED
@@ -9,9 +9,10 @@
9
9
  "Thibault Malbranche <malbranche.thibault@gmail.com>"
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "13.16.1-rc.1",
12
+ "version": "13.16.2-beta.2",
13
13
  "homepage": "https://gitcode.com/openharmony-sig/rntpc_react-native-webview/tree/br_rnoh0.77#readme",
14
14
  "scripts": {
15
+ "pack": "tsc --noEmit false --emitDeclarationOnly --declaration --rootDir src --outDir lib && babel --extensions \".ts,.tsx\" --out-dir lib src && npm pack --ignore-scripts",
15
16
  "macos": "react-native run-macos --scheme WebviewExample --project-path example/macos",
16
17
  "start": "react-native start",
17
18
  "windows": "install-windows-test-app --project-directory example/windows && react-native run-windows --root example --arch x64",
@@ -48,7 +49,7 @@
48
49
  "@react-native/eslint-config": "0.73.2",
49
50
  "@react-native/metro-config": "0.73.5",
50
51
  "@react-native/typescript-config": "0.73.1",
51
- "@react-native-oh/react-native-harmony-cli": "file:./packages/rnoh-react-native-harmony-cli-0.82.1.tgz",
52
+ "@react-native-oh/react-native-harmony-cli": "npm:@react-native-oh/react-native-harmony-cli@0.82.30",
52
53
  "@rnx-kit/metro-config": "1.3.15",
53
54
  "@semantic-release/git": "7.0.16",
54
55
  "@types/invariant": "^2.2.30",
@@ -78,6 +79,7 @@
78
79
  },
79
80
  "files": [
80
81
  "src",
82
+ "lib",
81
83
  "harmony",
82
84
  "react-native-webview.podspec",
83
85
  "!**/__tests__",
@@ -67,7 +67,7 @@ const WebViewComponent = forwardRef<{}, IOSWebViewProps & {onLoadSubResourceErro
67
67
  injectedJavaScript,
68
68
  injectedJavaScriptBeforeContentLoaded,
69
69
  injectedJavaScriptObject,
70
- menuItems=[],
70
+ menuItems,
71
71
  injectedJavaScriptForMainFrameOnly = true,
72
72
  injectedJavaScriptBeforeContentLoadedForMainFrameOnly = true,
73
73
  thirdPartyCookiesEnabled = false,