[VD]Show window for preload app in deeplink scenario
[platform/framework/web/wrtjs.git] / wrt_app / src / tv / web_application_tv.ts
1 /*
2  * Copyright (c) 2021 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';
20 import { WebApplication } from '../web_application';
21 import { WebApplicationDelegate } from '../../common/web_application_delegate';
22
23 export class WebApplicationDelegateTV extends WebApplicationDelegate {
24   backgroundExecutionLaunchMode: boolean = false;
25   backgroundExecutionMetaData: string = 'false';
26   inspectorSrc: string = '';
27   isAlwaysReload: boolean = false;
28   preloadStatus: string = 'none';
29   runningStatus: string = 'none';
30   launchMode: string = 'none';
31   needDispatchTizenVisibilityChange: boolean = false;
32   tv: any = (wrt.tv as NativeWRTjs.TVExtension);
33
34   constructor(webApplication: WebApplication) {
35     super(webApplication);
36   }
37
38   initialize(options: RuntimeOption) {
39     this.launchMode = options.launchMode;
40     if (options.launchMode == 'backgroundAtStartup') {
41       console.log('backgroundAtStartup');
42       this.preloadStatus = 'preload';
43     } else {
44       this.preloadStatus = 'none';
45     }
46     if (options.launchMode == 'backgroundExecution') {
47       console.log('launch mode is backgroundExecution');
48       this.backgroundExecutionLaunchMode = true;
49     } else {
50       this.backgroundExecutionLaunchMode = false;
51     }
52     this.isAlwaysReload = this.tv.isAlwaysReload();
53     this.webApplication.multitaskingSupport = this.tv.getMultitaskingSupport();
54     this.webApplication.defaultBackgroundColor = '#0000';
55     this.webApplication.defaultTransparent = true;
56     this.backgroundExecutionMetaData = this.tv.getMetadata('http://samsung.com/tv/metadata/background.execution.support');
57
58     this.initEventListener();
59   }
60
61   initEventListener() {
62     wrt.on('app-status-changed', (event: any, status: string) => {
63       console.log(`runningStatus: ${status}, ${this.webApplication.loadFinished}`);
64       this.runningStatus = status;
65       if (this.runningStatus === 'DialogClose' && this.inspectorSrc) {
66         console.log(`runningStatus is DialogClose, src is ${this.inspectorSrc}`);
67         this.webApplication.mainWindow.loadURL(this.inspectorSrc);
68         this.inspectorSrc = '';
69         this.needDispatchTizenVisibilityChange = true;
70       } else if (this.runningStatus == 'behind' && this.webApplication.loadFinished) {
71         // TODO : Need to care this situation and decide to pass the addon event emitter to suspend()
72         this.webApplication.suspend();
73       }
74     });
75   }
76
77   onDidFinishLoad() {
78     if (this.inspectorSrc) {
79       this.showInspectorGuide();
80     } else {
81       this.onDidFinishLoad = () => { };
82       this.suspendByStatus();
83     }
84
85     if (this.needDispatchTizenVisibilityChange)
86       this.sendTizenVisibilityEvent(this.webApplication.mainWindow.isVisible());
87   }
88
89   private sendTizenVisibilityEvent = (visibility: boolean) => {
90     console.log(`sendTizenVisibilityEvent call`);
91     this.needDispatchTizenVisibilityChange = false;
92     const kTizenvisibilityEventScript = `(function(){
93 var __event = document.createEvent("CustomEvent");
94 __event.initCustomEvent('tizenvisibilitychange', true, true, {visible: ${visibility} | 0});
95 document.dispatchEvent(__event);
96 for (var i=0; i < window.frames.length; i++)
97   window.frames[i].document.dispatchEvent(__event);
98 })()`;
99     let webContents = this.webApplication.mainWindow.webContents;
100     wrt.executeJS(webContents, kTizenvisibilityEventScript);
101   }
102
103   private showInspectorGuide() {
104     console.log('WebApplication : showInspectorGuide');
105     this.showInspectorGuide = () => { }; // call once
106     const message = `${this.webApplication.debugPort.toString()}
107 Fast RWI is used, [about:blank] is loaded fist instead of
108 [${this.inspectorSrc}]
109 Click OK button will start the real loading.
110 Notes:
111 Please connect to RWI in PC before click OK button.
112 Then you can get network log from the initial loading.
113 Please click Record button in Timeline panel in PC before click OK button,
114 Then you can get profile log from the initial loading.`;
115     let webContents = this.webApplication.mainWindow.webContents;
116     this.tv.showDialog(webContents, message);
117     if (this.preloadStatus !== 'none') {
118       setTimeout(() => {
119         this.tv.cancelDialogs(webContents);
120       }, 5000);
121     }
122   }
123
124   private needSuspend() {
125     return this.launchMode !== 'runningAsBackground' &&
126       (this.preloadStatus === 'preload' || this.runningStatus === 'behind');
127   }
128
129   private suspendByStatus() {
130     if (this.needSuspend()) {
131       console.log('WebApplication : suspendByStatus');
132       console.log(`preloadStatus: ${this.preloadStatus}, runningStatus: ${this.runningStatus}`);
133
134       this.webApplication.suspend();
135       if (this.preloadStatus === 'preload')
136         this.runningStatus = 'preload';
137       this.tv.notifyAppStatus(this.runningStatus);
138     }
139   }
140
141   backgroundExecutableLaunchMode() {
142     return this.backgroundExecutionLaunchMode;
143   }
144
145   isPreloading() {
146     if (this.preloadStatus === 'preload' ||
147         this.launchMode === 'runningAsBackground') {
148       console.log(`preloadStatus is ${this.preloadStatus} or ${this.launchMode}, show is skipped`);
149       return true;
150     }
151     return false;
152   }
153
154   canIgnoreSuspend() {
155     if (this.launchMode === 'runningAsForeground' ||
156         this.launchMode === 'runningAsBackground') {
157       console.log('WebApplication : view_suspend & multitasking feature is skipped!');
158       return true;
159     }
160     return false;
161   }
162
163   beforeQuit() {
164     this.inspectorSrc = '';
165     this.tv.cancelDialogs(this.webApplication.mainWindow.webContents);
166   }
167
168   clearSurface(webContents: any) {
169     this.tv.clearSurface(webContents);
170   }
171
172   clearCache() {
173     console.log('clearcache with low-memory');
174     this.webApplication.windowList.forEach((window) => {
175       //clear webframe cache
176       this.tv.clearWebCache(window.webContents);
177       window.webContents.session.clearCache().then(() => {
178         console.log('clear session Cache complete');
179       });
180     });
181   }
182
183   needInpectorGuide() {
184     let inspectorEnabledByVconf = this.tv.needUseInspector();
185     if (inspectorEnabledByVconf && !this.backgroundExecutionLaunchMode) {
186       this.inspectorSrc = this.webApplication.contentSrc;
187       this.webApplication.contentSrc = 'about:blank';
188       return true;
189     }
190     return false;
191   }
192
193   needReload(src: string) {
194     if (this.isAlwaysReload) {
195       return true;
196     }
197     let reload = false;
198     let originalUrl = this.webApplication.mainWindow.webContents.getURL();
199     console.log(`appcontrol src = ${src}, original url = ${originalUrl}`);
200     if (src && originalUrl) {
201       let appcontrolUrl = (new URL(src)).href;
202       let oldUrl = (new URL(originalUrl)).href;
203       console.log(`appcontrolUrl = ${appcontrolUrl}, oldUrl = ${oldUrl}`);
204       // FIXME(dh81.song)
205       // Below case it must be distinguishable for known cases
206       //   from 'file:///index.htmlx' to 'file:///index.html'
207       if (appcontrolUrl !== oldUrl.substr(0, appcontrolUrl.length))
208         reload = true;
209     } else {
210       reload = true;
211     }
212     return reload;
213   }
214
215   needShowTimer() {
216     return false;
217   }
218
219   handleAppControlEvent(appControl: any) {
220     this.launchMode = appControl.getData('http://samsung.com/appcontrol/data/launch_mode');
221     this.preloadStatus = 'none';
222
223     if (this.launchMode === 'runningAsBackground') {
224       this.webApplication.suspended = false;
225       this.webApplication.windowList.forEach((window) => window.setEnabled(true));
226       this.webApplication.windowList[this.webApplication.windowList.length - 1].hide();
227       this.webApplication.sendAppControlEvent();
228       return false;
229     } else if (this.launchMode === 'runningAsForeground') {
230       this.webApplication.resume();
231       this.webApplication.sendAppControlEvent();
232       return false;
233     } else {
234       if (!this.webApplication.mainWindow.isVisible())
235         this.webApplication.show();
236
237       let skipReload = appControl.getData('SkipReload');
238       if (skipReload == 'Yes') {
239         console.log('skipping reload');
240         // TODO : Need to care this situation and decide to pass the addon event emitter to resume()
241         this.webApplication.resume();
242         return false;
243       }
244     }
245
246     if ('true' === this.backgroundExecutionMetaData) {
247         console.log(`handle AppControl background exectution app to show`);
248         this.webApplication.mainWindow.show();
249     }
250     return true;
251   }
252
253   handleProxyInfo(authInfo: any, callback: any) {
254     if (!authInfo.isProxy)
255       return false;
256
257     let usrname = '';
258     let passwd = '';
259     let vconfProxy = this.tv.getProxy();
260     if (vconfProxy) {
261       let proxyInfo = new URL(vconfProxy);
262       usrname = proxyInfo.username;
263       passwd = proxyInfo.password;
264     }
265     if (usrname && passwd) {
266       callback(usrname, passwd);
267     } else {
268       console.log('Login, but usrname and passwd is empty!!!');
269       callback('', '');
270     }
271     return true;
272   }
273
274   profileName() {
275     return 'TV';
276   }
277
278   focus(webContents: any) {
279     this.tv.focus(webContents);
280   }
281 }