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