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.getTop(packageName); 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 global.$app_define$ = pageDefine; 108 global.$app_require$ = pageRequireModule; 109 global.Image = imageObj; 110 global.OffscreenCanvas = offscreenCanvasObj; 111 112 let functionCode: string; 113 if (typeof code !== 'function') { 114 functionCode = `(function(global){\n\n"use strict";\n\n ${code} \n\n})(this.__appProto__)`; 115 } 116 117 // Compile js bundle code and get result. 118 if (typeof code === 'function') { 119 code.call(global, options); 120 } else { 121 compileBundle(functionCode, page.doc.url, options, services); 122 } 123 return result; 124} 125 126/** 127 * Run bundle code by a new function. 128 * @param {string} functionCode - Js bundle code. 129 * @param {Object[]} args - Global methods for compile js bundle code. 130 * @return {*} 131 */ 132export function compileBundle(functionCode: string, file: string, ...args: object[]): any { 133 const funcKeys: string[] = []; 134 const funcValues: Function[] = []; 135 args.forEach((module) => { 136 for (const key in module) { 137 funcKeys.push(key); 138 funcValues.push(module[key]); 139 } 140 }); 141 142 // If failed to run code on native, then run code on framework. 143 if (!compileBundleNative(funcKeys, funcValues, functionCode, file)) { 144 Log.error(`Compile js bundle failed, typeof code is not 'function'`) 145 return; 146 } 147} 148 149/** 150 * Call a new function generated on the V8 native side. 151 * @param {string[]} funcKeys 152 * @param {Function[]} funcValues 153 * @param {string} functionCode 154 * @return {boolean} Return true if no error occurred. 155 */ 156function compileBundleNative(funcKeys: string[], funcValues: Function[], functionCode: string, file: string): boolean { 157 if (typeof compileAndRunBundle !== 'function') { 158 return false; 159 } 160 161 let isSuccess: boolean = false; 162 const bundle: string = `(function (${funcKeys.toString()}) {${functionCode}})`; 163 try { 164 const compileFunction: Function = compileAndRunBundle(bundle, file); 165 if (compileFunction && typeof compileFunction === 'function') { 166 compileFunction(...funcValues); 167 isSuccess = true; 168 } 169 } catch (e) { 170 Log.error(e); 171 } 172 return isSuccess; 173} 174