• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16let cert = requireInternal('security.cert');
17let webview = requireInternal('web.webview');
18let picker = requireNapi('file.picker');
19let photoAccessHelper = requireNapi('file.photoAccessHelper');
20let cameraPicker = requireNapi('multimedia.cameraPicker');
21let camera = requireNapi('multimedia.camera');
22let accessControl = requireNapi('abilityAccessCtrl');
23let deviceinfo = requireInternal('deviceInfo');
24const PARAM_CHECK_ERROR = 401;
25
26const ERROR_MSG_INVALID_PARAM = 'Invalid input parameter';
27
28let errMsgMap = new Map();
29errMsgMap.set(PARAM_CHECK_ERROR, ERROR_MSG_INVALID_PARAM);
30
31class BusinessError extends Error {
32  constructor(code, errorMsg = 'undefined') {
33    if (errorMsg === 'undefined') {
34      let msg = errMsgMap.get(code);
35      super(msg);
36    } else {
37      super(errorMsg);
38    }
39    this.code = code;
40  }
41}
42
43function getCertificatePromise(certChainData) {
44  let x509CertArray = [];
45  if (!(certChainData instanceof Array)) {
46    console.log('failed, cert chain data type is not array');
47    return Promise.all(x509CertArray);
48  }
49
50  for (let i = 0; i < certChainData.length; i++) {
51    let encodeBlobData = {
52      data: certChainData[i],
53      encodingFormat: cert.EncodingFormat.FORMAT_DER
54    };
55    x509CertArray[i] = cert.createX509Cert(encodeBlobData);
56  }
57
58  return Promise.all(x509CertArray);
59}
60
61function takePhoto(param, selectResult) {
62  try {
63    let pickerProfileOptions = {
64      'cameraPosition': camera.CameraPosition.CAMERA_POSITION_BACK,
65    };
66    let acceptTypes = param.getAcceptType();
67    let mediaType = [];
68    if (isContainImageMimeType(acceptTypes) && !isContainVideoMimeType(acceptTypes)) {
69      mediaType.push(cameraPicker.PickerMediaType.PHOTO);
70    } else if (!isContainImageMimeType(acceptTypes) && isContainVideoMimeType(acceptTypes)) {
71      mediaType.push(cameraPicker.PickerMediaType.VIDEO);
72    } else {
73      mediaType.push(cameraPicker.PickerMediaType.PHOTO);
74      mediaType.push(cameraPicker.PickerMediaType.VIDEO);
75    }
76    cameraPicker.pick(getContext(this), mediaType, pickerProfileOptions)
77    .then((pickerResult) => {
78      if (pickerResult.resultCode === 0) {
79        selectResult.handleFileList([pickerResult.resultUri]);
80      }
81    }).catch((error) => {
82      console.log('selectFile error:' + JSON.stringify(error));
83    });
84
85  } catch (error) {
86    console.log('the pick call failed, error code' + JSON.stringify(error));
87  }
88}
89
90function needShowDialog(params) {
91  let result = false;
92  try {
93    let currentDevice = deviceinfo.deviceType.toLowerCase();
94    if (currentDevice !== 'phone') {
95      return false;
96    }
97    if (params.isCapture()) {
98      console.log('input element contain capture tag, not show dialog');
99      return false;
100    }
101    let acceptTypes = params.getAcceptType();
102    if (isContainImageMimeType(acceptTypes) || isContainVideoMimeType(acceptTypes)) {
103      result = true;
104    }
105  } catch (error) {
106    console.log('show dialog happend error:' + JSON.stringify(error));
107  }
108  return result;
109}
110
111function selectFile(param, result) {
112  try {
113    let documentSelectOptions = createDocumentSelectionOptions(param);
114    let documentPicker = new picker.DocumentViewPicker();
115    documentPicker.select(documentSelectOptions)
116      .then((documentSelectResult) => {
117        if (documentSelectResult && documentSelectResult.length > 0) {
118          let filePath = documentSelectResult;
119          result.handleFileList(filePath);
120        }
121      }).catch((error) => {
122        console.log('selectFile error: ' + JSON.stringify(error));
123      });
124  } catch (error) {
125    console.log('picker error: ' + JSON.stringify(error));
126  }
127}
128
129function createDocumentSelectionOptions(param) {
130  let documentSelectOptions = new picker.DocumentSelectOptions();
131  try {
132    let defaultSelectNumber = 500;
133    let defaultSelectMode = picker.DocumentSelectMode.MIXED;
134    documentSelectOptions.maxSelectNumber = defaultSelectNumber;
135    documentSelectOptions.selectMode = defaultSelectMode;
136    let mode = param.getMode();
137    switch (mode) {
138      case FileSelectorMode.FileOpenMode:
139        documentSelectOptions.maxSelectNumber = 1;
140        documentSelectOptions.selectMode = picker.DocumentSelectMode.FILE;
141        break;
142      case FileSelectorMode.FileOpenMultipleMode:
143        documentSelectOptions.selectMode = picker.DocumentSelectMode.FILE;
144        break;
145      case FileSelectorMode.FileOpenFolderMode:
146        documentSelectOptions.selectMode = picker.DocumentSelectMode.FOLDER;
147        break;
148      case FileSelectorMode.FileSaveMode:
149        break;
150      default:
151        break;
152    }
153    documentSelectOptions.fileSuffixFilters = [];
154    let suffix = param.getAcceptType().join(',');
155    if (suffix) {
156      documentSelectOptions.fileSuffixFilters.push(suffix);
157    }
158 } catch (error) {
159    console.log('selectFile error: ' + + JSON.stringify(error));
160    return documentSelectOptions;
161 }
162  return documentSelectOptions;
163}
164
165function isContainImageMimeType(acceptTypes) {
166  if (!(acceptTypes instanceof Array) || acceptTypes.length < 1) {
167    return false;
168  }
169
170  let imageTypes = ['tif', 'xbm', 'tiff', 'pjp', 'jfif', 'bmp', 'avif', 'apng', 'ico',
171                    'webp', 'svg', 'gif', 'svgz', 'jpg', 'jpeg', 'png', 'pjpeg'];
172  for (let i = 0; i < acceptTypes.length; i++) {
173    for (let j = 0; j < imageTypes.length; j++) {
174      if (acceptTypes[i].includes(imageTypes[j])) {
175        return true;
176      }
177    }
178  }
179  return false;
180}
181
182function isContainVideoMimeType(acceptTypes) {
183  if (!(acceptTypes instanceof Array) || acceptTypes.length < 1) {
184    return false;
185  }
186
187  let videoTypes = ['ogm', 'ogv', 'mpg', 'mp4', 'mpeg', 'm4v', 'webm'];
188  for (let i = 0; i < acceptTypes.length; i++) {
189    for (let j = 0; j < videoTypes.length; j++) {
190      if (acceptTypes[i].includes(videoTypes[j])) {
191        return true;
192      }
193    }
194  }
195  return false;
196}
197
198function selectPicture(param, selectResult) {
199  try {
200    let photoResultArray = [];
201    let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
202    if (param.getMode() === FileSelectorMode.FileOpenMode) {
203      console.log('allow select single photo or video');
204      photoSelectOptions.maxSelectNumber = 1;
205    }
206    let acceptTypes = param.getAcceptType();
207    photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE;
208    if (isContainImageMimeType(acceptTypes) && !isContainVideoMimeType(acceptTypes)) {
209      photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
210    }
211    if (!isContainImageMimeType(acceptTypes) && isContainVideoMimeType(acceptTypes)) {
212      photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE;
213    }
214
215    let photoPicker = new photoAccessHelper.PhotoViewPicker();
216    photoPicker.select(photoSelectOptions).then((photoSelectResult) => {
217      if (photoSelectResult.photoUris.length <= 0) {
218        return;
219      }
220      for (let i = 0; i < photoSelectResult.photoUris.length; i++) {
221        photoResultArray.push(photoSelectResult.photoUris[i]);
222      }
223      selectResult.handleFileList(photoResultArray);
224    });
225  } catch (error) {
226    console.log('selectPicture error' + JSON.stringify(error));
227  }
228}
229
230Object.defineProperty(webview.WebviewController.prototype, 'getCertificate', {
231  value: function (callback) {
232    if (arguments.length !== 0 && arguments.length !== 1) {
233      throw new BusinessError(PARAM_CHECK_ERROR,
234        'BusinessError 401: Parameter error. The number of params must be zero or one.');
235    }
236
237    let certChainData = this.innerGetCertificate();
238    if (callback === undefined) {
239      console.log('get certificate promise');
240      return getCertificatePromise(certChainData);
241    } else {
242      console.log('get certificate async callback');
243      if (typeof callback !== 'function') {
244        throw new BusinessError(PARAM_CHECK_ERROR,
245          'BusinessError 401: Parameter error. The type of 'callback' must be function.' );
246      }
247      getCertificatePromise(certChainData).then(x509CertArray => {
248        callback(undefined, x509CertArray);
249      }).catch(error => {
250        callback(error, undefined);
251      });
252    }
253  }
254});
255
256Object.defineProperty(webview.WebviewController.prototype, 'fileSelectorShowFromUserWeb', {
257  value:  function (callback) {
258    if (needShowDialog(callback.fileparam)) {
259      ActionSheet.show({
260        title: '选择上传',
261        autoCancel: true,
262        confirm: {
263          defaultFocus: true,
264          value: '取消',
265          style: DialogButtonStyle.DEFAULT,
266          action: () => {
267            console.log('Get Alert Dialog handled');
268          }
269        },
270        cancel: () => {
271          console.log('actionSheet canceled');
272        },
273        alignment: DialogAlignment.Bottom,
274        offset: { dx: 0, dy: -10 },
275        sheets: [
276          {
277            icon: $r('sys.media.ohos_ic_public_albums'),
278            title: '图片',
279            action: () => {
280              selectPicture(callback.fileparam, callback.fileresult);
281            }
282          },
283          {
284            icon: $r('sys.media.ohos_ic_public_camera'),
285            title: '拍照',
286            action: () => {
287              takePhoto(callback.fileparam, callback.fileresult);
288             }
289          },
290          {
291            icon: $r('sys.media.ohos_ic_public_email'),
292            title: '文件',
293            action: () => {
294              selectFile(callback.fileparam, callback.fileresult);
295            }
296          }
297        ]
298      });
299    } else if (callback.fileparam.isCapture()) {
300      console.log('take photo will be directly invoked due to the capture property');
301      takePhoto(callback.fileparam, callback.fileresult);
302    } else {
303      console.log('selectFile will be invoked by web');
304      selectFile(callback.fileparam, callback.fileresult);
305    }
306  }
307});
308
309Object.defineProperty(webview.WebviewController.prototype, 'requestPermissionsFromUserWeb', {
310  value:  function (callback) {
311    let accessManger = accessControl.createAtManager();
312    let abilityContext = getContext(this);
313    accessManger.requestPermissionsFromUser(abilityContext, ['ohos.permission.READ_PASTEBOARD'])
314      .then((PermissionRequestResult) => {
315        if (PermissionRequestResult.authResults[0] === 0) {
316          console.log('requestPermissionsFromUserWeb is allowed');
317          callback.request.grant(callback.request.getAccessibleResource());
318        }
319        else {
320          console.log('requestPermissionsFromUserWeb is refused');
321          callback.request.deny();
322        }
323      })
324      .catch((error) => {
325        callback.request.deny();
326      });
327  }
328});
329
330Object.defineProperty(webview.WebviewController.prototype, 'openAppLink', {
331  value:  function (callback) {
332    let abilityContext = getContext(this);
333    try {
334      let option = {
335        appLinkingOnly: true
336      };
337      console.log('begin openAppLink');
338      abilityContext.openLink(callback.url, option, null)
339        .then(() => {
340          console.log('applink success openLink');
341          callback.result.cancelLoad();
342        })
343        .catch((error) => {
344          console.log(`applink openLink ErrorCode: ${error.code},  Message: ${error.message}`);
345          callback.result.continueLoad();
346        });
347    } catch (err) {
348      console.log(`applink openLink ErrorCode: ${err.code},  Message: ${err.message}`);
349      callback.result.continueLoad();
350    }
351  }
352});
353
354export default webview;
355