• 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/**
21 * @fileOverview
22 * page controls from native
23 *
24 * - init bundle
25 *
26 * corresponded with the API of page manager (framework.js)
27 */
28
29import {
30  Log
31} from '../../../utils/index';
32import { removePrefix } from '../../util/index';
33import {
34  defineFn,
35  bootstrap
36} from './bundle';
37import { updateActions } from '../api/misc';
38import { getPageGlobal } from '../../app/helper';
39import { Image } from '../Image';
40import { OffscreenCanvas } from '../OffscreenCanvas';
41import Page from '../index';
42import { Services } from '../../app/index';
43import { requireModule } from '../register';
44import { App } from '../../app/App';
45
46interface ParseOptions {
47  $app_define$(...args: any[]): void; // eslint-disable-line camelcase
48  $app_bootstrap$(name: string): void; // eslint-disable-line camelcase
49  $app_require$(name: string): void; // eslint-disable-line camelcase
50  Image(): void;
51  OffscreenCanvas(width, height): void;
52}
53
54/**
55 * Init a page by run code with data.
56 * @param {Page} page
57 * @param {string} code
58 * @param {Object} data
59 * @param {Services} services
60 * @return {*}
61 */
62export function init(page: Page, code: string | Function, data: object, services: Services): any {
63  Log.debug('Intialize a page with:\n', data);
64  let result;
65
66  // Methods to parse code.
67  const pageDefine = (...args) => defineFn(page, ...args);
68  const pageBoot = (name) => {
69    result = bootstrap(page, name, data);
70    updateActions(page);
71    page.doc.taskCenter.send('dom', { action: 'createFinish' }, []);
72    Log.debug(`After initialized a page(${page.id}).`);
73  };
74
75  const packageName = page.packageName;
76  const appFunction = () => {
77    if (page && page.doc) {
78      return page;
79    }
80    // card not has packageName
81    if (packageName === 'notset') {
82      return page;
83    }
84    const instance = App.pageMap.get(page.id);
85    return instance || page;
86  };
87
88  const pageRequireModule = name => requireModule(appFunction, removePrefix(name));
89
90  const imageObj: () => Image = function() {
91    return new Image(page);
92  };
93  const offscreenCanvasObj: (width, height) => OffscreenCanvas = function(width, height) {
94    return new OffscreenCanvas(page, width, height);
95  };
96  const options: ParseOptions = {
97    $app_define$: pageDefine,
98    $app_bootstrap$: pageBoot,
99    $app_require$: pageRequireModule,
100    Image: imageObj,
101    OffscreenCanvas: offscreenCanvasObj
102  };
103
104  // Support page global and init language.
105  global.__appProto__ = getPageGlobal(page.packageName);
106  global.language = page.options.language;
107
108  let functionCode: string;
109  if (typeof code !== 'function') {
110    functionCode = `(function(global){\n\n"use strict";\n\n ${code} \n\n})(this.__appProto__)`;
111  }
112
113  // Compile js bundle code and get result.
114  if (typeof code === 'function') {
115    code.call(global, options);
116  } else {
117    compileBundle(functionCode, page.doc.url, options, services);
118  }
119  return result;
120}
121
122/**
123 * Run bundle code by a new function.
124 * @param {string} functionCode - Js bundle code.
125 * @param {Object[]} args - Global methods for compile js bundle code.
126 * @return {*}
127 */
128export function compileBundle(functionCode: string, file: string, ...args: object[]): any {
129  const funcKeys: string[] = [];
130  const funcValues: Function[] = [];
131  args.forEach((module) => {
132    for (const key in module) {
133      funcKeys.push(key);
134      funcValues.push(module[key]);
135    }
136  });
137
138  // If failed to run code on native, then run code on framework.
139  if (!compileBundleNative(funcKeys, funcValues, functionCode, file)) {
140    const resolveFunction: Function = new Function(funcKeys.toString(), functionCode);
141    return resolveFunction(...funcValues);
142  }
143}
144
145/**
146 * Call a new function generated on the V8 native side.
147 * @param {string[]} funcKeys
148 * @param {Function[]} funcValues
149 * @param {string} functionCode
150 * @return {boolean} Return true if no error occurred.
151 */
152function compileBundleNative(funcKeys: string[], funcValues: Function[], functionCode: string, file: string): boolean {
153  if (typeof compileAndRunBundle !== 'function') {
154    return false;
155  }
156
157  let isSuccess: boolean = false;
158  const bundle: string = `(function (${funcKeys.toString()}) {${functionCode}})`;
159  try {
160    const compileFunction: Function = compileAndRunBundle(bundle, file);
161    if (compileFunction && typeof compileFunction === 'function') {
162      compileFunction(...funcValues);
163      isSuccess = true;
164    }
165  } catch (e) {
166    Log.error(e);
167  }
168  return isSuccess;
169}
170