• 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  let currentDevice = deviceinfo.deviceType.toLowerCase();
132  try {
133    let defaultSelectNumber = 500;
134    let defaultSelectMode = picker.DocumentSelectMode.MIXED;
135    documentSelectOptions.maxSelectNumber = defaultSelectNumber;
136    documentSelectOptions.selectMode = defaultSelectMode;
137    let mode = param.getMode();
138    switch (mode) {
139      case FileSelectorMode.FileOpenMode:
140        documentSelectOptions.maxSelectNumber = 1;
141        documentSelectOptions.selectMode = picker.DocumentSelectMode.FILE;
142        break;
143      case FileSelectorMode.FileOpenMultipleMode:
144        documentSelectOptions.selectMode = picker.DocumentSelectMode.FILE;
145        break;
146      case FileSelectorMode.FileOpenFolderMode:
147        documentSelectOptions.selectMode = picker.DocumentSelectMode.FOLDER;
148        break;
149      case FileSelectorMode.FileSaveMode:
150        break;
151      default:
152        break;
153    }
154    documentSelectOptions.fileSuffixFilters = [];
155    let suffix = param.getAcceptType().join(',');
156    if (suffix) {
157      documentSelectOptions.fileSuffixFilters.push(suffix);
158    }
159    if (currentDevice !== 'phone') {
160    documentSelectOptions.fileSuffixFilters.push('.*');
161    }
162 } catch (error) {
163    console.log('selectFile error: ' + + JSON.stringify(error));
164    return documentSelectOptions;
165 }
166  return documentSelectOptions;
167}
168
169function isContainImageMimeType(acceptTypes) {
170  if (!(acceptTypes instanceof Array) || acceptTypes.length < 1) {
171    return false;
172  }
173
174  let imageTypes = ['tif', 'xbm', 'tiff', 'pjp', 'jfif', 'bmp', 'avif', 'apng', 'ico',
175                    'webp', 'svg', 'gif', 'svgz', 'jpg', 'jpeg', 'png', 'pjpeg'];
176  for (let i = 0; i < acceptTypes.length; i++) {
177    for (let j = 0; j < imageTypes.length; j++) {
178      if (acceptTypes[i].includes(imageTypes[j])) {
179        return true;
180      }
181    }
182  }
183  return false;
184}
185
186function isContainVideoMimeType(acceptTypes) {
187  if (!(acceptTypes instanceof Array) || acceptTypes.length < 1) {
188    return false;
189  }
190
191  let videoTypes = ['ogm', 'ogv', 'mpg', 'mp4', 'mpeg', 'm4v', 'webm'];
192  for (let i = 0; i < acceptTypes.length; i++) {
193    for (let j = 0; j < videoTypes.length; j++) {
194      if (acceptTypes[i].includes(videoTypes[j])) {
195        return true;
196      }
197    }
198  }
199  return false;
200}
201
202function selectPicture(param, selectResult) {
203  try {
204    let photoResultArray = [];
205    let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
206    if (param.getMode() === FileSelectorMode.FileOpenMode) {
207      console.log('allow select single photo or video');
208      photoSelectOptions.maxSelectNumber = 1;
209    }
210    let acceptTypes = param.getAcceptType();
211    photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE;
212    if (isContainImageMimeType(acceptTypes) && !isContainVideoMimeType(acceptTypes)) {
213      photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
214    }
215    if (!isContainImageMimeType(acceptTypes) && isContainVideoMimeType(acceptTypes)) {
216      photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE;
217    }
218
219    let photoPicker = new photoAccessHelper.PhotoViewPicker();
220    photoPicker.select(photoSelectOptions).then((photoSelectResult) => {
221      if (photoSelectResult.photoUris.length <= 0) {
222        return;
223      }
224      for (let i = 0; i < photoSelectResult.photoUris.length; i++) {
225        photoResultArray.push(photoSelectResult.photoUris[i]);
226      }
227      selectResult.handleFileList(photoResultArray);
228    });
229  } catch (error) {
230    console.log('selectPicture error' + JSON.stringify(error));
231  }
232}
233
234Object.defineProperty(webview.WebviewController.prototype, 'getCertificate', {
235  value: function (callback) {
236    if (arguments.length !== 0 && arguments.length !== 1) {
237      throw new BusinessError(PARAM_CHECK_ERROR,
238        'BusinessError 401: Parameter error. The number of params must be zero or one.');
239    }
240
241    let certChainData = this.innerGetCertificate();
242    if (callback === undefined) {
243      console.log('get certificate promise');
244      return getCertificatePromise(certChainData);
245    } else {
246      console.log('get certificate async callback');
247      if (typeof callback !== 'function') {
248        throw new BusinessError(PARAM_CHECK_ERROR,
249          'BusinessError 401: Parameter error. The type of 'callback' must be function.' );
250      }
251      getCertificatePromise(certChainData).then(x509CertArray => {
252        callback(undefined, x509CertArray);
253      }).catch(error => {
254        callback(error, undefined);
255      });
256    }
257  }
258});
259
260Object.defineProperty(webview.WebviewController.prototype, 'fileSelectorShowFromUserWeb', {
261  value:  function (callback) {
262    let currentDevice = deviceinfo.deviceType.toLowerCase();
263    if (needShowDialog(callback.fileparam)) {
264      ActionSheet.show({
265        title: '选择上传',
266        autoCancel: true,
267        confirm: {
268          defaultFocus: true,
269          value: '取消',
270          style: DialogButtonStyle.DEFAULT,
271          action: () => {
272            console.log('Get Alert Dialog handled');
273          }
274        },
275        cancel: () => {
276          console.log('actionSheet canceled');
277        },
278        alignment: DialogAlignment.Bottom,
279        offset: { dx: 0, dy: -10 },
280        sheets: [
281          {
282            icon: $r('sys.media.ohos_ic_public_albums'),
283            title: '图片',
284            action: () => {
285              selectPicture(callback.fileparam, callback.fileresult);
286            }
287          },
288          {
289            icon: $r('sys.media.ohos_ic_public_camera'),
290            title: '拍照',
291            action: () => {
292              takePhoto(callback.fileparam, callback.fileresult);
293             }
294          },
295          {
296            icon: $r('sys.media.ohos_ic_public_email'),
297            title: '文件',
298            action: () => {
299              selectFile(callback.fileparam, callback.fileresult);
300            }
301          }
302        ]
303      });
304    } else if (currentDevice === 'phone' && callback.fileparam.isCapture()) {
305      console.log('take photo will be directly invoked due to the capture property');
306      takePhoto(callback.fileparam, callback.fileresult);
307    } else {
308      console.log('selectFile will be invoked by web');
309      selectFile(callback.fileparam, callback.fileresult);
310    }
311  }
312});
313
314Object.defineProperty(webview.WebviewController.prototype, 'requestPermissionsFromUserWeb', {
315  value:  function (callback) {
316    let accessManger = accessControl.createAtManager();
317    let abilityContext = getContext(this);
318    accessManger.requestPermissionsFromUser(abilityContext, ['ohos.permission.READ_PASTEBOARD'])
319      .then((PermissionRequestResult) => {
320        if (PermissionRequestResult.authResults[0] === 0) {
321          console.log('requestPermissionsFromUserWeb is allowed');
322          callback.request.grant(callback.request.getAccessibleResource());
323        }
324        else {
325          console.log('requestPermissionsFromUserWeb is refused');
326          callback.request.deny();
327        }
328      })
329      .catch((error) => {
330        callback.request.deny();
331      });
332  }
333});
334
335Object.defineProperty(webview.WebviewController.prototype, 'openAppLink', {
336  value:  function (callback) {
337    let abilityContext = getContext(this);
338    try {
339      let option = {
340        appLinkingOnly: true
341      };
342      console.log('begin openAppLink');
343      abilityContext.openLink(callback.url, option, null)
344        .then(() => {
345          console.log('applink success openLink');
346          callback.result.cancelLoad();
347        })
348        .catch((error) => {
349          console.log(`applink openLink ErrorCode: ${error.code},  Message: ${error.message}`);
350          callback.result.continueLoad();
351        });
352    } catch (err) {
353      console.log(`applink openLink ErrorCode: ${err.code},  Message: ${err.message}`);
354      callback.result.continueLoad();
355    }
356  }
357});
358
359export default webview;
360