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