1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19/* 20 * 2021.01.08 - Strengthen init modules, getter and setter of modules. 21 * Copyright (c) 2021 Huawei Device Co., Ltd. 22 */ 23 24/* 25 * native module register 26 */ 27import { Log } from '../../utils/index'; 28import { interceptCallback } from '../manage/event/callbackIntercept'; 29import { getPluginModule } from '../extend/mediaquery/plugins'; 30import Page from './index'; 31import Document from '../../vdom/Document'; 32import { TaskCenter } from '../manage/event/TaskCenter'; 33 34let nativeModulesPkg: object = {}; 35 36/** 37 * for UT 38 */ 39export function getModule(moduleName) { 40 return nativeModulesPkg[moduleName]; 41} 42 43/** 44 * for UT 45 */ 46export function allModules() { 47 return nativeModulesPkg; 48} 49 50/** 51 * for UT 52 */ 53export function clearModules() { 54 nativeModulesPkg = {}; 55} 56 57/** 58 * Init modules for an app page. 59 * @param {Object} modules 60 */ 61export function initModules(modules: object): void { 62 for (const moduleName in modules) { 63 /* Obtains the module package name. 64 * If modulename does not contain the package name, the default package name is _global_. 65 */ 66 const s: string[] = moduleName.split('.'); 67 let pkg: string = '_global_'; 68 let cls: string; 69 if (s.length === 1) { 70 cls = s[0]; 71 } else { 72 pkg = s[0]; 73 cls = s[1]; 74 } 75 let clss: object = nativeModulesPkg[pkg]; 76 if (!clss) { 77 clss = {}; 78 nativeModulesPkg[pkg] = clss; 79 } 80 let methods: object = clss[cls]; 81 if (!methods) { 82 methods = {}; 83 clss[cls] = methods; 84 } 85 86 // Push each non-existed new method. 87 for (let method of modules[moduleName]) { 88 method = { 89 name: method 90 }; 91 if (!methods[method.name]) { 92 methods[method.name] = method; 93 } 94 } 95 } 96} 97 98/** 99 * Get a module of methods for an app page. 100 * @param {Page | Function} app 101 * @param {string} name 102 * @return {Object} 103 */ 104export function requireModule(app: Page | Function, name: string): object { 105 const s: string[] = name.split('.'); 106 let pkg: string = s[0]; 107 let cls: string = s[1]; 108 const moduleName: string = s[1]; 109 let clss: object = nativeModulesPkg[pkg]; 110 111 // If can not found pkg from nativeModulesPkg, then use '_global_' as pkg to find again. 112 if (!clss && !cls) { 113 cls = pkg; 114 pkg = '_global_'; 115 clss = nativeModulesPkg[pkg]; 116 } 117 if (cls) { 118 const target: object = {}; 119 const methods: object = clss[cls]; 120 bind(app, target, methods, name, true); 121 if (pkg && (pkg === 'system' || pkg === '@system')) { 122 const module: object = getPluginModule(target, moduleName); 123 if (module) { 124 return module; 125 } 126 } 127 return target; 128 } else { 129 const target: object = {}; 130 for (const clsName in clss) { 131 target[clsName] = {}; 132 const methods: object = clss[clsName]; 133 bind(app, target[clsName], methods, name + '.' + clsName, true); 134 } 135 return target; 136 } 137} 138 139function isGlobalModule(name: string) { 140 const clss: object = nativeModulesPkg['_global_']; 141 const globalModules: string[] = Object.keys(clss); 142 if (globalModules.indexOf(name) === -1) { 143 return false; 144 } 145 return true; 146} 147 148function bind(app: Page | Function, target: object, methods: object, moduleName: string, needPromise: boolean) { 149 for (const methodName in methods) { 150 Object.defineProperty(target, methodName, { 151 configurable: true, 152 enumerable: true, 153 get: function moduleGetter() { 154 if (this._modifyMethods && this._modifyMethods[methodName] && 155 typeof this._modifyMethods[methodName] === 'function') { 156 return this._modifyMethods[methodName]; 157 } 158 return (...args) => { 159 let promise; 160 if (!isGlobalModule(moduleName)) { 161 const ret: any = interceptCallback(args, needPromise); 162 args = needPromise ? ret.args : ret; 163 promise = needPromise ? ret.promise : undefined; 164 } 165 const appInstance: Page = typeof app === 'function' ? app() : app; 166 if (moduleName === 'system.router' && methodName === 'getParams') { 167 if (appInstance.routerParams) { 168 return appInstance.routerParams.paramsData; 169 } 170 } 171 const ret: any = appInstance.callTasks({ 172 module: moduleName, 173 method: methodName, 174 args: args 175 }); 176 177 // API return exception. 178 if (ret && ret.__EXCEPTION__) { 179 const e: any = new Error(); 180 e.code = ret.code; 181 e.message = ret.message; 182 throw e; 183 } 184 if (ret && ret.__PROMISE__ && promise) { 185 return promise.promise; 186 } else { 187 const taskCenter: Document | TaskCenter = appInstance.doc && appInstance.doc.taskCenter; 188 if (taskCenter) { 189 if (args[0] && args[0].__onlyPromise) { 190 taskCenter.removeCallback(args[0].__callbackId); 191 } else if (args.length > 1 && args[1] && args[1].__onlyPromise) { 192 taskCenter.removeCallback(args[1].__callbackId); 193 } 194 } 195 return ret; 196 } 197 }; 198 }, 199 set: function moduleSetter(value: Function) { 200 if (typeof value === 'function') { 201 this._modifyMethods = this._modifyMethods || {}; 202 this._modifyMethods[methodName] = value; 203 } 204 } 205 }); 206 } 207} 208 209/** 210 * Register a custom component options. 211 * @param {Page} app 212 * @param {string} name 213 * @param {Object} def - The content of component. 214 * @return {*} 215 */ 216export function registerCustomComponent(app: Page, name: string, def: object): any { 217 const { customComponentMap } = app; 218 if (customComponentMap[name]) { 219 Log.error(`Define a component(${name}) that already exists.`); 220 return; 221 } 222 customComponentMap[name] = def; 223} 224