Merge "[TV] Bring cancelDialogs() forward to before-quit" into tizen
[platform/framework/web/wrtjs.git] / wrt_app / src / runtime.ts
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 'use strict';
18
19 import { wrt } from '../browser/wrt';  // Load first for log
20 import { addonManager } from './addon_manager';
21 import { app } from 'electron';
22 import { WebApplication } from './web_application';
23
24 class Runtime {
25   webApplication?: WebApplication = undefined;
26   isLaunched = false;
27   inspectorEnabledByVconf = false;
28
29   constructor() {
30     app.on('before-quit', (event) => {
31       console.log('before-quit');
32       this.webApplication?.quit();
33     });
34     app.on('will-quit', (event) => {
35       console.log('will-quit');
36       addonManager.deactivateAll();
37     });
38     app.on('quit', (event) => {
39       console.log('quit');
40       if (this.webApplication) {
41         this.webApplication.finalize();
42         this.webApplication = undefined;
43       }
44     });
45     app.on('browser-window-created', () => {
46       console.log('browser-window-created');
47       if (!this.isLaunched) {
48         addonManager.activateAll();
49         this.isLaunched = true;
50       }
51     });
52     app.on('window-all-closed', () => {
53       console.log('window-all-closed');
54       app.quit();
55     });
56     app.on('web-contents-created', (event, webContents) => {
57       console.log('web-contents-created');
58       if (wrt.tv)
59         this.setCookiePath();
60       webContents.on('before-input-event', (event, input) => {
61         if (this.isLaunched && this.webApplication)
62           this.handleKeyEvents(input.key);
63       });
64     });
65     app.once('ready', (event) => {
66       console.log('ready');
67       let addonAvailable = addonManager.build();
68       console.log("addonBuild : " + addonAvailable);
69       if (addonAvailable) {
70         const XWalkExtension = require('../common/wrt_xwalk_extension');
71         XWalkExtension.initialize();
72         XWalkExtension.preventCleanup();
73       }
74       wrt.tv?.importCertificate('');
75     });
76     wrt.on('app-control', (event, appControl) => {
77       console.log('app-control');
78       let loadInfo = appControl.getLoadInfo();
79       let src = loadInfo.getSrc();
80
81       if (wrt.isElectronApp()) {
82         console.log('Electron App launch');
83         const Module = require('module');
84         Module.globalPaths.push(wrt.getAppPath());
85         let filePath = src[7] === '/' ? src.substr(8) : src.substr(7); // strip "file://"
86         let pkgJson = require(filePath);
87         let pos = filePath.lastIndexOf('/');
88
89         let mainJsPath = (pos !== -1 ? filePath.substr(0, pos + 1) : '') +
90                             (pkgJson.main || 'index.js');
91         console.log('loading path:', mainJsPath);
92         Module._load(mainJsPath, Module, true);
93         app.emit('ready');
94       } else {
95         console.log('Tizen Web App launch');
96         let launchMode = appControl.getData('http://samsung.com/appcontrol/data/launch_mode');
97         if (!this.webApplication) {
98           console.log('Creating WebApplication');
99           let options: RuntimeOption = {
100             isAddonAvailable: addonManager.isAddonAvailable(),
101             launchMode: launchMode
102           }
103           this.webApplication = new WebApplication(options);
104           if (wrt.tv) {
105             this.inspectorEnabledByVconf = wrt.tv.needUseInspector();
106             if (this.inspectorEnabledByVconf && launchMode != 'backgroundExecution') {
107               this.webApplication.inspectorSrc = src;
108               src = "about:blank";
109             }
110             let useDiskCache = appControl.getData('USE_DISKCACHE');
111             let halfWindowOption = appControl.getData('http://samsung.com/appcontrol/data/half_window_support');
112             wrt.tv.setDiskCache(useDiskCache);
113             wrt.tv.handleAppControlData(launchMode, halfWindowOption);
114           }
115           this.webApplication.mainWindow.loadURL(src);
116           this.webApplication.prelaunch(src);
117         } else {
118           console.log('Handling app-control event');
119           if (this.webApplication.preloadStatus == 'readyToShow') {
120             this.webApplication.show();
121           } else {
122             if (launchMode != 'backgroundAtStartup')
123               this.webApplication.preloadStatus = 'none';
124           }
125
126           let skipReload = appControl.getData('SkipReload');
127           if (skipReload == 'Yes') {
128             console.log('skipping reload');
129             // TODO : Need to care this situation and decide to pass the addon event emitter to resume()
130             this.webApplication.resume();
131             return;
132           }
133
134           let reload = loadInfo.getReload() || this.webApplication.isAlwaysReload;
135           if (!reload) {
136             let originalUrl = this.webApplication.mainWindow.webContents.getURL();
137             if (wrt.tv) {
138               console.log(`appcontrol src = ${src}, original url = ${originalUrl}`);
139               if (src && originalUrl) {
140                 let appcontrolUrl = (new URL(src)).href;
141                 let oldUrl = (new URL(originalUrl)).href;
142                 console.log(`appcontrolUrl = ${appcontrolUrl}, oldUrl = ${oldUrl}`);
143                 // FIXME(dh81.song)
144                 // Below case it must be distinguishable for known cases
145                 //   from 'file:///index.htmlx' to 'file:///index.html'
146                 if (appcontrolUrl !== oldUrl.substr(0, appcontrolUrl.length))
147                   reload = true;
148               } else {
149                 reload = true;
150               }
151             } else if (src !== originalUrl) {
152               reload = true;
153             }
154           }
155           // handle http://tizen.org/appcontrol/operation/main operation specially.
156           // only menu-screen app can send launch request with main operation.
157           // in this case, web app should have to resume web app not reset.
158           if (reload && appControl.getOperation() == 'http://tizen.org/appcontrol/operation/main')
159             reload = false;
160           if (reload)
161             this.webApplication.handleAppControlReload(src);
162           else
163             this.webApplication.sendAppControlEvent();
164         }
165       }
166       this.setCookiePath();
167       this.launchInspector(appControl);
168     });
169     wrt.on('suspend', () => {
170       console.log('suspend');
171       this.webApplication?.suspend();
172     });
173     wrt.on('resume', () => {
174       console.log('resume');
175       this.webApplication?.resume();
176     });
177     wrt.on('low-memory', () => {
178       console.log('low-memory');
179       this.webApplication?.lowMemory();
180     });
181     wrt.on('message', (event, type, params) => {
182       console.log('message type(' + type + ') params : ' + params);
183       const app_id = params[0];
184       if (type === 'startService') {
185         require('../common/service_manager').startService(app_id, params[1]);
186         event.preventDefault();
187       } else if (type === 'stopService') {
188         require('../common/service_manager').stopService(app_id);
189         event.preventDefault();
190       }
191     });
192     wrt.on('ambient-tick', () => {
193       this.webApplication?.ambientTick();
194     });
195     wrt.on('ambient-changed', (event, ambient_mode) => {
196       console.log('ambient-changed , ambient_mode:' + ambient_mode);
197       this.webApplication?.ambientChanged(ambient_mode);
198     });
199     wrt.on('addon-installed', (event, path) => {
200       console.log('addon-installed at ' + path);
201       addonManager.checkAddon(path);
202     });
203     wrt.on('addon-uninstalled', (event, id) => {
204       console.log('addon-unistalled named ' + id);
205     });
206     wrt.on('wgt-checking-done', (event) => {
207       console.log('wgt-checking-done');
208       addonManager.updateDB();
209     });
210
211     /* FIXME: will uncheck after chromium-efl released */
212     if (wrt.getPlatformType() !== "product_tv")
213       wrt.getInstalledPkg();
214   }
215
216   private launchInspector(appControl: NativeWRTjs.AppControl) {
217     this.launchInspector = (param) => {}; // call once
218     console.log('launchInspector');
219
220     // AUL public key/Vconf - To support inspector
221     if (this.checkInspectorCondition(appControl)) {
222       let debugPort = wrt.startInspectorServer();
223       let data = { "port" :  [ debugPort.toString() ] };
224       if (this.webApplication)
225         this.webApplication.debugPort = debugPort;
226       appControl.reply(data);
227     }
228   }
229
230   private checkInspectorCondition(appControl: NativeWRTjs.AppControl) {
231     let bundleDebug = (appControl.getData('__AUL_DEBUG__') === "1");
232     return (bundleDebug || this.inspectorEnabledByVconf);
233   }
234
235   private setCookiePath() {
236     this.setCookiePath = () => {}; // call once
237     console.log('setCookiePath');
238
239     // FIX ME : It must be supplemented to set a specific path
240     wrt.setCookiePath();
241   }
242
243   private handleKeyEvents(key: string) {
244     let valid = false;
245     console.log(key + ' is pressed');
246     switch(key) {
247       case "ArrowUp":
248       case "Up":
249       case "ArrowDown":
250       case "Down":
251         valid = true;
252         break;
253       default:
254         console.log('No handler for the key ' + key);
255         break;
256     }
257     if (valid)
258       this.webApplication?.keyEvent(key);
259   }
260 }
261
262 new Runtime();