@react-native-ohos/react-native-webview 13.15.2-rc.1 → 13.15.3-1

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/OpenHarmony-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/OpenHarmony-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.15.2-rc.1",
9
+ "version": "13.15.3-1",
10
10
  "dependencies": {
11
11
  "@rnoh/react-native-openharmony": "../react_native_openharmony.har"
12
12
  }
@@ -45,7 +45,7 @@ export interface WebViewProps extends ViewRawProps {
45
45
  cacheMode: CACHE_MODE
46
46
  domStorageEnabled: boolean
47
47
  scalesPageToFit: boolean
48
- hasOnShouldStartLoadWithRequestEvent?: boolean
48
+ hasOnShouldStartLoadWithRequestEvent?: boolean
49
49
  messagingModuleName: string
50
50
  webviewDebuggingEnabled: boolean
51
51
  scrollEnabled: boolean
@@ -65,8 +65,22 @@ export interface WebViewProps extends ViewRawProps {
65
65
  geolocationEnabled: boolean
66
66
  applicationNameForUserAgent: string
67
67
  pullToRefreshEnabled: boolean
68
+ mediaCapturePermissionGrantType?: 'prompt' | 'grant' | 'deny' | 'grantIfSameHostElsePrompt' | 'grantIfSameHostElseDeny'
69
+ menuItems?: Array<MenuItem> | undefined;
68
70
  }
69
71
 
72
+ export interface MenuItem {
73
+ label: string,
74
+ key: string
75
+ }
76
+
77
+ export enum WebMediaCapturePermissionGrantType {
78
+ GRANT_IF_SAME_HOST_ELSE_PROMPT = "grantIfSameHostElsePrompt",
79
+ GRANT_IF_SAME_HOST_ELSE_DENY = "grantIfSameHostElseDeny",
80
+ DENY = "deny",
81
+ GRANT = "grant",
82
+ PROMPT = "prompt",
83
+ }
70
84
 
71
85
  export class RNCWebViewBridge {
72
86
  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';
@@ -425,33 +427,130 @@ export struct RNCWebView {
425
427
  return false
426
428
  }
427
429
 
430
+ onCreateMenu = (menuItems: Array<TextMenuItem>) => {
431
+ let customMenuItems = this.descriptorWrapper.rawProps.menuItems
432
+ if (customMenuItems == undefined) {
433
+ return menuItems;
434
+ } else if (customMenuItems.length == 0) {
435
+ return [];
436
+ } else {
437
+ let menuItemsArray: Array<TextMenuItem> = [];
438
+ for (let index = 0; index < customMenuItems.length; index++) {
439
+ let element: MenuItem = customMenuItems[index];
440
+ let customItem: TextMenuItem = {
441
+ content: element.label,
442
+ id: TextMenuItemId.of(element.key)
443
+ };
444
+ menuItemsArray.push(customItem)
445
+ }
446
+ return menuItemsArray;
447
+ }
448
+ }
449
+
450
+ onMenuItemClick(menuItem: TextMenuItem): boolean {
451
+ if (this.descriptorWrapper.rawProps.menuItems != undefined) {
452
+ this.controller.runJavaScript("(function(){return {selection: window.getSelection().toString()} })()")
453
+ .then((result: string) => {
454
+ this.webViewBaseOperate?.customMenuSelection(menuItem, result)
455
+ })
456
+ .catch((error: string | Error) => {
457
+ Logger.info('[RNOH] function onMenuItemClick error: ' + error);
458
+ })
459
+ // 禁用系统默认行为
460
+ return true;
461
+ }
462
+ return false;
463
+ }
464
+
465
+ onPermissionRequestAlert(event: OnPermissionRequestEvent){
466
+ AlertDialog.show({
467
+ title: this.resourceToString($r('app.string.on_confirm')) + event.request.getOrigin(),
468
+ message: this.getPermissionDialogMessage(event),
469
+ alignment: DialogAlignment.Center,
470
+ primaryButton: {
471
+ value: $r('app.string.deny'),
472
+ action: () => {
473
+ event.request.deny();
474
+ }
475
+ },
476
+ secondaryButton: {
477
+ value: $r('app.string.on_confirm'),
478
+ action: () => {
479
+ event.request.grant(event.request.getAccessibleResource());
480
+ }
481
+ },
482
+ cancel: () => {
483
+ event.request.deny();
484
+ }
485
+ })
486
+ }
487
+
488
+ async handleMediaCapturePermissionGrantType(resources: string[], domain: string) {
489
+ let permissionList: Array<Permissions> = [];
490
+ resources.forEach(resource => {
491
+ switch (resource.toString()) {
492
+ case 'TYPE_VIDEO_CAPTURE':
493
+ permissionList.push('ohos.permission.CAMERA');
494
+ break;
495
+ case 'TYPE_AUDIO_CAPTURE':
496
+ permissionList.push('ohos.permission.MICROPHONE');
497
+ break;
498
+ }
499
+ });
500
+
501
+ await atManager.requestPermissionsFromUser(this.ctx.uiAbilityContext, permissionList);
502
+ }
503
+
428
504
  build() {
429
505
  Stack() {
430
506
  if ( deviceInfo.sdkApiVersion >= 20) {
431
507
  Web({ src: "", controller: this.controller, renderMode: this.renderMode })
432
508
  .mixedMode(this.mode)
433
- .onPermissionRequest((event: OnPermissionRequestEvent) => {
509
+ .onPermissionRequest(async (event: OnPermissionRequestEvent) => {
434
510
  if (event && (this.getPermissionDialogMessage(event) != "TYPE_SENSOR")) {
435
- AlertDialog.show({
436
- title: this.resourceToString($r('app.string.on_confirm')) + event.request.getOrigin(),
437
- message: this.getPermissionDialogMessage(event),
438
- alignment: DialogAlignment.Center,
439
- primaryButton: {
440
- value: $r('app.string.deny'),
441
- action: () => {
511
+ const resources: string[] = event.request.getAccessibleResource();
512
+ if (resources.indexOf('TYPE_AUDIO_CAPTURE') >= 0 || resources.indexOf('TYPE_VIDEO_CAPTURE') >= 0 ) {
513
+ const originUrl = this.webViewBaseOperate?.verifyURLFormat(event.request.getOrigin(), this.source.html,
514
+ this.source.baseUrl);
515
+ const currentUrl = this.webViewBaseOperate?.verifyURLFormat(this.controller.getUrl(), this.source.html,
516
+ this.source.baseUrl);
517
+ const originDomain: string = OSUrl.URL.parseURL(originUrl).hostname;
518
+ const currentDomain: string = OSUrl.URL.parseURL(currentUrl).hostname;
519
+ let mediaCapturePermissionGrantType: string = WebMediaCapturePermissionGrantType.PROMPT
520
+ if (this.descriptorWrapper.rawProps.mediaCapturePermissionGrantType != undefined) {
521
+ mediaCapturePermissionGrantType = this.descriptorWrapper.rawProps.mediaCapturePermissionGrantType;
522
+ }
523
+ if (mediaCapturePermissionGrantType != WebMediaCapturePermissionGrantType.DENY) {
524
+ await this.handleMediaCapturePermissionGrantType(resources, originDomain)
525
+ }
526
+ switch (mediaCapturePermissionGrantType) {
527
+ case WebMediaCapturePermissionGrantType.GRANT_IF_SAME_HOST_ELSE_PROMPT:
528
+ if (originDomain != currentDomain) {
529
+ this.onPermissionRequestAlert(event)
530
+ } else {
531
+ event.request.grant(resources);
532
+ }
533
+ break;
534
+ case WebMediaCapturePermissionGrantType.GRANT_IF_SAME_HOST_ELSE_DENY:
535
+ if (originDomain != currentDomain) {
536
+ event.request.deny();
537
+ } else {
538
+ event.request.grant(resources);
539
+ }
540
+ break;
541
+ case WebMediaCapturePermissionGrantType.DENY:
442
542
  event.request.deny();
443
- }
444
- },
445
- secondaryButton: {
446
- value: $r('app.string.on_confirm'),
447
- action: () => {
448
- event.request.grant(event.request.getAccessibleResource());
449
- }
450
- },
451
- cancel: () => {
452
- event.request.deny();
543
+ break;
544
+ case WebMediaCapturePermissionGrantType.GRANT:
545
+ event.request.grant(resources);
546
+ break;
547
+ case WebMediaCapturePermissionGrantType.PROMPT:
548
+ this.onPermissionRequestAlert(event)
549
+ break;
453
550
  }
454
- })
551
+ } else {
552
+ this.onPermissionRequestAlert(event)
553
+ }
455
554
  }
456
555
  })
457
556
  .width(this.webviewWidth)
@@ -499,31 +598,58 @@ export struct RNCWebView {
499
598
  .onRenderExited((event: OnRenderExitedEvent) => this.webViewBaseOperate?.onRenderExited(event))
500
599
  .onLoadIntercept((event: OnLoadInterceptEvent) => this.onLoadIntercept(event))
501
600
  .onTitleReceive((event: OnTitleReceiveEvent) => this.webViewBaseOperate?.onTitleReceive(event))
601
+ .editMenuOptions({
602
+ onCreateMenu: this.onCreateMenu.bind(this),
603
+ onMenuItemClick: this.onMenuItemClick.bind(this),
604
+ })
502
605
  } else {
503
606
  Web({ src: "", controller: this.controller, renderMode: this.renderMode })
504
607
  .mixedMode(this.mode)
505
- .onPermissionRequest((event: OnPermissionRequestEvent) => {
608
+ .onPermissionRequest(async (event: OnPermissionRequestEvent) => {
506
609
  if (event && (this.getPermissionDialogMessage(event) != "TYPE_SENSOR")) {
507
- AlertDialog.show({
508
- title: this.resourceToString($r('app.string.on_confirm')) + event.request.getOrigin(),
509
- message: this.getPermissionDialogMessage(event),
510
- alignment: DialogAlignment.Center,
511
- primaryButton: {
512
- value: $r('app.string.deny'),
513
- action: () => {
610
+ const resources: string[] = event.request.getAccessibleResource();
611
+ if (resources.indexOf('TYPE_AUDIO_CAPTURE') >= 0 || resources.indexOf('TYPE_VIDEO_CAPTURE') >= 0 ) {
612
+ const originUrl = this.webViewBaseOperate?.verifyURLFormat(event.request.getOrigin(), this.source.html,
613
+ this.source.baseUrl);
614
+ const currentUrl = this.webViewBaseOperate?.verifyURLFormat(this.controller.getUrl(), this.source.html,
615
+ this.source.baseUrl);
616
+ const originDomain: string = OSUrl.URL.parseURL(originUrl).hostname;
617
+ const currentDomain: string = OSUrl.URL.parseURL(currentUrl).hostname;
618
+ let mediaCapturePermissionGrantType: string = WebMediaCapturePermissionGrantType.PROMPT
619
+ if (this.descriptorWrapper.rawProps.mediaCapturePermissionGrantType != undefined) {
620
+ mediaCapturePermissionGrantType = this.descriptorWrapper.rawProps.mediaCapturePermissionGrantType;
621
+ }
622
+ if (mediaCapturePermissionGrantType != WebMediaCapturePermissionGrantType.DENY) {
623
+ await this.handleMediaCapturePermissionGrantType(resources, originDomain)
624
+ }
625
+ switch (mediaCapturePermissionGrantType) {
626
+ case WebMediaCapturePermissionGrantType.GRANT_IF_SAME_HOST_ELSE_PROMPT:
627
+ if (originDomain != currentDomain) {
628
+ this.onPermissionRequestAlert(event)
629
+ } else {
630
+ event.request.grant(resources);
631
+ }
632
+ break;
633
+ case WebMediaCapturePermissionGrantType.GRANT_IF_SAME_HOST_ELSE_DENY:
634
+ if (originDomain != currentDomain) {
635
+ event.request.deny();
636
+ } else {
637
+ event.request.grant(resources);
638
+ }
639
+ break;
640
+ case WebMediaCapturePermissionGrantType.DENY:
514
641
  event.request.deny();
515
- }
516
- },
517
- secondaryButton: {
518
- value: $r('app.string.on_confirm'),
519
- action: () => {
520
- event.request.grant(event.request.getAccessibleResource());
521
- }
522
- },
523
- cancel: () => {
524
- event.request.deny();
642
+ break;
643
+ case WebMediaCapturePermissionGrantType.GRANT:
644
+ event.request.grant(resources);
645
+ break;
646
+ case WebMediaCapturePermissionGrantType.PROMPT:
647
+ this.onPermissionRequestAlert(event)
648
+ break;
525
649
  }
526
- })
650
+ } else {
651
+ this.onPermissionRequestAlert(event)
652
+ }
527
653
  }
528
654
  })
529
655
 
@@ -571,7 +697,10 @@ export struct RNCWebView {
571
697
  .onRenderExited((event: OnRenderExitedEvent) => this.webViewBaseOperate?.onRenderExited(event))
572
698
  .onLoadIntercept((event: OnLoadInterceptEvent) => this.onLoadIntercept(event))
573
699
  .onTitleReceive((event: OnTitleReceiveEvent) => this.webViewBaseOperate?.onTitleReceive(event))
574
-
700
+ .editMenuOptions({
701
+ onCreateMenu: this.onCreateMenu.bind(this),
702
+ onMenuItemClick: this.onMenuItemClick.bind(this),
703
+ })
575
704
  }
576
705
  }
577
706
  .width(this.webviewWidth)
@@ -454,4 +454,17 @@ export class BaseOperate {
454
454
  Logger.error(TAG, `[RNOH] onTitleReceive Errorcode: ${error.code}`);
455
455
  }
456
456
  }
457
+
458
+ customMenuSelection(menuItem: TextMenuItem, selectionText: string) {
459
+ try {
460
+ selectionText = JSON.parse(selectionText).selection;
461
+ this.eventEmitter!.emit('customMenuSelection', {
462
+ label: menuItem.content.toString(),
463
+ key: JSON.parse(JSON.stringify(menuItem.id)).id_,
464
+ selectedText: selectionText
465
+ })
466
+ } catch (error) {
467
+ Logger.error(TAG, `[RNOH] customMenuSelection Errorcode: ${error.code}`);
468
+ }
469
+ }
457
470
  }
Binary file
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "Thibault Malbranche <malbranche.thibault@gmail.com>"
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "13.15.2-rc.1",
12
+ "version": "13.15.3-1",
13
13
  "homepage": "https://gitcode.com/openharmony-sig/rntpc_react-native-webview/tree/br_rnoh0.77#readme",
14
14
  "scripts": {
15
15
  "macos": "react-native run-macos --scheme WebviewExample --project-path example/macos",
@@ -48,6 +48,7 @@
48
48
  "@react-native/eslint-config": "0.73.2",
49
49
  "@react-native/metro-config": "0.73.5",
50
50
  "@react-native/typescript-config": "0.73.1",
51
+ "@react-native-oh/react-native-harmony-cli": "npm:@react-native-oh/react-native-harmony-cli@0.77.18-1",
51
52
  "@rnx-kit/metro-config": "1.3.15",
52
53
  "@semantic-release/git": "7.0.16",
53
54
  "@types/invariant": "^2.2.30",
@@ -67,7 +67,7 @@ const WebViewComponent = forwardRef<{}, IOSWebViewProps & { scalesPageToFit: boo
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,