• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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