From 78f26f53072d0634ba7c4bf9e2e647d66c38e15f Mon Sep 17 00:00:00 2001 From: DongHyun Song Date: Wed, 24 Mar 2021 14:59:51 +0900 Subject: [PATCH] [VD] Segregates WebApplicationDelegateTV for TV profile WebApplicationDelegateTV will implements TV profile feature via inherited from WebApplicationDelegate base class. This patch will improve code readability of WebApplication class. Change-Id: Ic785f47fc19ad69a93701744eab2a50e6175d7be Signed-off-by: DongHyun Song --- wrt_app/common/web_application_delegate.ts | 46 ++++ wrt_app/src/runtime.ts | 2 +- wrt_app/src/tv/web_application_tv.ts | 268 +++++++++++++++++++++ wrt_app/src/web_application.ts | 254 ++++--------------- 4 files changed, 356 insertions(+), 214 deletions(-) create mode 100644 wrt_app/common/web_application_delegate.ts create mode 100644 wrt_app/src/tv/web_application_tv.ts diff --git a/wrt_app/common/web_application_delegate.ts b/wrt_app/common/web_application_delegate.ts new file mode 100644 index 00000000..fcc48726 --- /dev/null +++ b/wrt_app/common/web_application_delegate.ts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +import { WebApplication } from '../src/web_application'; + +export class WebApplicationDelegate { + webApplication: WebApplication; + constructor(webApplication: WebApplication) { + this.webApplication = webApplication; + } + initialize(options: RuntimeOption) { } + backgroundExecutable() { return false; } + beforeQuit() { } + clearCache() { } + clearSuface(webContents: any) { } + handleAppControlEvent(appControl: any) { return true; } + handleProxyInfo(authInfo: any, callback: any) { return false; } + needInpectorGuide() { return false; } + needReload(src: string) { + let originalUrl = this.webApplication.mainWindow.webContents.getURL(); + if (src !== originalUrl) { + return true; + } + return false; + } + needShowTimer() { return true; } + isPreloading() { return false; } + onDidFinishLoad() { } + profileName() { return 'common' } + show() { } +} diff --git a/wrt_app/src/runtime.ts b/wrt_app/src/runtime.ts index 925a2e57..bd3bd013 100644 --- a/wrt_app/src/runtime.ts +++ b/wrt_app/src/runtime.ts @@ -98,7 +98,7 @@ class Runtime { wrt.on('low-memory', () => { console.log('low-memory'); - this.webApplication?.lowMemory(); + this.webApplication?.clearCache(); }); wrt.on('message', (event: any, type: string, params: string[]) => { diff --git a/wrt_app/src/tv/web_application_tv.ts b/wrt_app/src/tv/web_application_tv.ts new file mode 100644 index 00000000..83e4b880 --- /dev/null +++ b/wrt_app/src/tv/web_application_tv.ts @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +import { protocol } from 'electron'; +import { wrt } from '../../browser/wrt'; +import { WebApplication } from '../web_application'; +import { WebApplicationDelegate } from '../../common/web_application_delegate'; + +export class WebApplicationDelegateTV extends WebApplicationDelegate { + accessiblePath?: string[]; + backgroundExecution: boolean = false; + inspectorSrc: string = ''; + isAlwaysReload: boolean = false; + preloadStatus: string = 'none'; + runningStatus: string = 'none'; + tv: any = (wrt.tv as NativeWRTjs.TVExtension); + + constructor(webApplication: WebApplication) { + super(webApplication); + } + + initialize(options: RuntimeOption) { + if (options.launchMode == 'backgroundAtStartup') { + console.log('backgroundAtStartup'); + this.preloadStatus = 'preload'; + } else { + this.preloadStatus = 'none'; + } + if (options.launchMode == 'backgroundExecution') { + console.log('backgroundExecution'); + this.backgroundExecution = true; + } else { + this.backgroundExecution = false; + } + this.isAlwaysReload = this.tv.isAlwaysReload(); + this.accessiblePath = this.tv.getAccessiblePath(); + + this.webApplication.multitaskingSupport = this.tv.getMultitaskingSupport(); + this.webApplication.defaultBackgroundColor = '#0000'; + this.webApplication.defaultTransparent = true; + + this.initAccessiblePath(); + this.initEventListener(); + } + + private initAccessiblePath() { + if (this.accessiblePath) { + console.log(`accessiblePath: ${this.accessiblePath}`); + protocol.interceptFileProtocol('file', (request: any, callback: any) => { + if (request.url) { + let parsed_info = new URL(request.url); + let access_path = parsed_info.host + decodeURI(parsed_info.pathname); + console.log(`check path: : ${access_path}`); + for (let path of (this.accessiblePath as string[])) { + if (access_path.startsWith(path)) { + callback(access_path); + return; + } + } + if (access_path.indexOf("/shared/res/") > -1) { + callback(access_path); + return; + } + else { + console.log(`invalid accesspath: ${access_path}`); + (callback as any)(403); + } + } else { + console.log('request url is empty'); + (callback as any)(403); + } + }, (error: Error) => { + console.log(error); + }); + } + } + + initEventListener() { + wrt.on('app-status-changed', (event: any, status: string) => { + console.log(`runningStatus: ${status}, ${this.webApplication.loadFinished}`); + this.runningStatus = status; + if (this.runningStatus === 'DialogClose' && this.inspectorSrc) { + console.log(`runningStatus is DialogClose, src is ${this.inspectorSrc}`); + this.webApplication.mainWindow.loadURL(this.inspectorSrc); + this.inspectorSrc = ''; + } else if (this.runningStatus == 'behind' && this.webApplication.loadFinished) { + // TODO : Need to care this situation and decide to pass the addon event emitter to suspend() + this.webApplication.suspend(); + } + }); + } + + onDidFinishLoad() { + if (this.inspectorSrc) + this.showInspectorGuide(); + else + this.suspendByStatus(); + } + + private showInspectorGuide() { + console.log('WebApplication : showInspectorGuide'); + this.showInspectorGuide = () => { }; // call once + const message = `${this.webApplication.debugPort.toString()} +Fast RWI is used, [about:blank] is loaded fist instead of +[${this.inspectorSrc}] +Click OK button will start the real loading. +Notes: +Please connect to RWI in PC before click OK button. +Then you can get network log from the initial loading. +Please click Record button in Timeline panel in PC before click OK button, +Then you can get profile log from the initial loading.`; + let webContents = this.webApplication.mainWindow.webContents; + this.tv.showDialog(webContents, message); + if (this.preloadStatus !== 'none') { + setTimeout(() => { + this.tv.cancelDialogs(webContents); + }, 5000); + } + } + + private suspendByStatus() { + if (this.preloadStatus === 'preload' || + this.runningStatus === 'behind') { + console.log('WebApplication : suspendByStatus'); + console.log(`preloadStatus: ${this.preloadStatus}, runningStatus: ${this.runningStatus}`); + // TODO : Need to care this situation and decide to pass the addon event emitter to suspend() + this.webApplication.suspend(); + if (this.preloadStatus === 'preload') + this.runningStatus = 'preload'; + this.tv.notifyAppStatus(this.runningStatus); + } + } + + private handlePreloadState(launchMode: string) { + if (this.preloadStatus === 'preload') { + this.show(); + } else { + if (launchMode != 'backgroundAtStartup') + this.preloadStatus = 'none'; + } + } + + backgroundExecutable() { + return this.backgroundExecution; + } + + isPreloading() { + if (this.preloadStatus == 'preload') { + console.log('preloading show is skipped!'); + return true; + } + return false; + } + + show() { + this.preloadStatus = 'none'; + } + + beforeQuit() { + this.inspectorSrc = ''; + this.tv.cancelDialogs(this.webApplication.mainWindow.webContents); + } + + clearSurface(webContents: any) { + this.tv.clearSurface(webContents); + } + + clearCache() { + console.log('clearcache with low-memory'); + this.webApplication.windowList.forEach((window) => { + //clear webframe cache + this.tv.clearWebCache(window.webContents); + window.webContents.session.clearCache(function() { + console.log('clear session Cache complete'); + }) + }); + } + + needInpectorGuide() { + let inspectorEnabledByVconf = this.tv.needUseInspector(); + if (inspectorEnabledByVconf && !this.backgroundExecution) { + this.inspectorSrc = this.webApplication.contentSrc; + this.webApplication.contentSrc = 'about:blank'; + return true; + } + return false; + } + + needReload(src: string) { + if (this.isAlwaysReload) { + return true; + } + let reload = false; + let originalUrl = this.webApplication.mainWindow.webContents.getURL(); + console.log(`appcontrol src = ${src}, original url = ${originalUrl}`); + if (src && originalUrl) { + let appcontrolUrl = (new URL(src)).href; + let oldUrl = (new URL(originalUrl)).href; + console.log(`appcontrolUrl = ${appcontrolUrl}, oldUrl = ${oldUrl}`); + // FIXME(dh81.song) + // Below case it must be distinguishable for known cases + // from 'file:///index.htmlx' to 'file:///index.html' + if (appcontrolUrl !== oldUrl.substr(0, appcontrolUrl.length)) + reload = true; + } else { + reload = true; + } + return reload; + } + + needShowTimer() { + return false; + } + + handleAppControlEvent(appControl: any) { + let launchMode = appControl.getData('http://samsung.com/appcontrol/data/launch_mode'); + this.handlePreloadState(launchMode); + + let skipReload = appControl.getData('SkipReload'); + if (skipReload == 'Yes') { + console.log('skipping reload'); + // TODO : Need to care this situation and decide to pass the addon event emitter to resume() + this.webApplication.resume(); + return false; + } + return true; + } + + handleProxyInfo(authInfo: any, callback: any) { + if (!authInfo.isProxy) + return false; + + let usrname = ''; + let passwd = ''; + let vconfProxy = this.tv.getProxy(); + if (vconfProxy) { + let proxyInfo = new URL(vconfProxy); + usrname = proxyInfo.username; + passwd = proxyInfo.password; + } + if (usrname && passwd) { + callback(usrname, passwd); + } else { + console.log('Login, but usrname and passwd is empty!!!'); + callback('', ''); + } + return true; + } + + profileName() { + return 'TV'; + } +} diff --git a/wrt_app/src/web_application.ts b/wrt_app/src/web_application.ts index 7fa658ed..f2e3d1a5 100644 --- a/wrt_app/src/web_application.ts +++ b/wrt_app/src/web_application.ts @@ -16,59 +16,42 @@ 'use strict'; -import { app, protocol } from 'electron'; +import { app } from 'electron'; import { wrt } from '../browser/wrt'; import * as WRTWebContents from '../browser/wrt_web_contents'; import { WRTWindow } from '../browser/wrt_window'; import { addonManager } from './addon_manager'; +import { WebApplicationDelegate } from '../common/web_application_delegate'; +import { WebApplicationDelegateTV } from './tv/web_application_tv'; export class WebApplication { - accessiblePath?: string[]; - backgroundExecution: boolean; - defaultBackgroundColor: string; - defaultTransparent: boolean; - isAlwaysReload: boolean; + defaultBackgroundColor: string = (wrt.getPlatformType() === "product_wearable") ? '#000' : '#FFF'; + defaultTransparent: boolean = false; mainWindow: Electron.BrowserWindow; - multitaskingSupport: boolean; + multitaskingSupport: boolean = true; notificationPermissionMap?: Map; - preloadStatus: string; showTimer?: NodeJS.Timeout; - backgroundSupport = wrt.getBackgroundSupport(); - debugPort = 0; - firstRendered = false; - contentSrc = ''; - inspectorSrc = ''; - loadFinished = false; + backgroundSupport: boolean = wrt.getBackgroundSupport(); + debugPort: number = 0; + firstRendered: boolean = false; + contentSrc: string = ''; + loadFinished: boolean = false; pendingCallbacks: Map = new Map(); - pendingID = 0; - runningStatus = 'none'; - suspended = false; + pendingID: number = 0; + suspended: boolean = false; windowList: Electron.BrowserWindow[] = []; - inQuit = false; + inQuit: boolean = false; + profileDelegate: WebApplicationDelegate; constructor(options: RuntimeOption) { - if (options.launchMode == 'backgroundAtStartup') { - console.log('backgroundAtStartup'); - this.preloadStatus = 'preload'; - } else { - this.preloadStatus = 'none'; - } - if (options.launchMode == 'backgroundExecution') { - console.log('backgroundExecution'); - this.backgroundExecution = true; + if (wrt.tv) { + this.profileDelegate = new WebApplicationDelegateTV(this); + this.profileDelegate.initialize(options); } else { - this.backgroundExecution = false; + this.profileDelegate = new WebApplicationDelegate(this); } - this.accessiblePath = wrt.tv?.getAccessiblePath(); - this.isAlwaysReload = (wrt.tv ? wrt.tv.isAlwaysReload() : false); - this.multitaskingSupport = (wrt.tv ? wrt.tv.getMultitaskingSupport() : true); - this.defaultBackgroundColor = (wrt.tv ? '#0000' : - ((wrt.getPlatformType() === "product_wearable") ? '#000' : '#FFF')); - this.defaultTransparent = (wrt.tv ? true : false); - this.setupEventListener(options); - this.mainWindow = new WRTWindow(this.getWindowOption(options)); this.initDisplayDelay(); this.setupMainWindowEventListener(); @@ -148,22 +131,7 @@ export class WebApplication { app.on('login', (event: any, webContents: any, request: any, authInfo: any, callback: any) => { console.log(`Login info is required, isproxy: ${authInfo.isProxy}`); event.preventDefault(); - let usrname = ''; - let passwd = ''; - if (wrt.tv && authInfo.isProxy) { - let vconfProxy = wrt.tv.getProxy(); - if (vconfProxy) { - let proxyInfo = new URL(vconfProxy); - usrname = proxyInfo.username; - passwd = proxyInfo.password; - } - if (usrname && passwd) { - callback(usrname, passwd); - } else { - console.log('Login, but usrname and passwd is empty!!!'); - callback('', ''); - } - } else { + if (!this.profileDelegate.handleProxyInfo(authInfo, callback)) { const id = ++this.pendingID; console.log(`Raising a login info request with id: ${id}`); this.pendingCallbacks.set(id, callback); @@ -171,36 +139,6 @@ export class WebApplication { } }); - if (this.accessiblePath) { - console.log(`accessiblePath: ${this.accessiblePath}`); - protocol.interceptFileProtocol('file', (request: any, callback: any) => { - if (request.url) { - let parsed_info = new URL(request.url); - let access_path = parsed_info.host + decodeURI(parsed_info.pathname); - console.log(`check path: : ${access_path}`); - for (let path of (this.accessiblePath as string[])) { - if (access_path.startsWith(path)) { - callback(access_path); - return; - } - } - if (access_path.indexOf("/shared/res/") > -1) { - callback(access_path); - return; - } - else { - console.log(`invalid accesspath: ${access_path}`); - (callback as any)(403); - } - } else { - console.log('request url is empty'); - (callback as any)(403); - } - }, (error: Error) => { - console.log(error); - }); - } - wrt.on('permission-response', (event: any, id: number, result: boolean) => { console.log(`permission-response for ${id} is ${result}`); let callback = this.pendingCallbacks.get(id); @@ -222,21 +160,6 @@ export class WebApplication { this.pendingCallbacks.delete(id); } }); - - wrt.on('app-status-changed', (event: any, status: string) => { - console.log(`runningStatus: ${status}, ${this.loadFinished}`); - if (!wrt.tv) - return; - this.runningStatus = status; - if (this.runningStatus === 'DialogClose' && this.inspectorSrc) { - console.log(`runningStatus is DialogClose, src is ${this.inspectorSrc}`); - this.mainWindow.loadURL(this.inspectorSrc); - this.inspectorSrc = ''; - } else if (this.runningStatus == 'behind' && this.loadFinished) { - // TODO : Need to care this situation and decide to pass the addon event emitter to suspend() - this.suspend(); - } - }); } private getWindowOption(options: RuntimeOption): NativeWRTjs.WRTWindowConstructorOptions { @@ -261,11 +184,8 @@ export class WebApplication { clearTimeout(this.showTimer); wrt.hideSplashScreen(0); this.firstRendered = true; - if (this.preloadStatus == 'preload') { - this.preloadStatus = 'readyToShow'; - console.log('preloading show is skipped!'); + if (this.profileDelegate.isPreloading()) return; - } this.show(); }); @@ -281,11 +201,8 @@ export class WebApplication { addonManager.emit('contentDidFinishLoad', this.mainWindow.id); if (wrt.isIMEWebApp()) { this.activateIMEWebHelperClient(); - } else if (wrt.tv) { - if (this.inspectorSrc) - this.showInspectorGuide(); - else - this.suspendByStatus(); + } else { + this.profileDelegate.onDidFinishLoad(); } }); } @@ -300,8 +217,11 @@ export class WebApplication { } private initDisplayDelay() { - let splashShown = this.preloadStatus !== 'preload' && wrt.showSplashScreen(); - if (splashShown || wrt.tv) + if (this.profileDelegate.isPreloading()) + return; + + let splashShown = wrt.showSplashScreen(); + if (splashShown || !this.profileDelegate.needShowTimer()) return; this.showTimer = setTimeout(() => { @@ -312,60 +232,18 @@ export class WebApplication { }, 2000); } - private backgroundRunnable(): boolean { - return this.backgroundSupport || this.backgroundExecution; - } - - private suspendByStatus() { - if (this.preloadStatus === 'readyToShow' || - this.preloadStatus === 'preload' || - this.runningStatus === 'behind') { - console.log('WebApplication : suspendByStatus'); - console.log(`preloadStatus: ${this.preloadStatus}, runningStatus: ${this.runningStatus}`); - // TODO : Need to care this situation and decide to pass the addon event emitter to suspend() - this.suspend(); - if (this.runningStatus !== 'behind') - (wrt.tv as NativeWRTjs.TVExtension).notifyAppStatus('preload'); - } - } - - private showInspectorGuide() { - console.log('WebApplication : showInspectorGuide'); - this.showInspectorGuide = () => {}; // call once - const message = `${this.debugPort.toString()} -Fast RWI is used, [about:blank] is loaded fist instead of -[${this.inspectorSrc}] -Click OK button will start the real loading. -Notes: -Please connect to RWI in PC before click OK button. -Then you can get network log from the initial loading. -Please click Record button in Timeline panel in PC before click OK button, -Then you can get profile log from the initial loading.`; - let tv = wrt.tv as NativeWRTjs.TVExtension; - tv.showDialog(this.mainWindow.webContents, message); - - if (this.preloadStatus !== 'none') { - setTimeout(() => { - tv.cancelDialogs(this.mainWindow.webContents); - }, 5000); - } + private backgroundRunnable() { + return this.backgroundSupport || this.profileDelegate.backgroundExecutable(); } handleAppControlEvent(appControl: any) { - let launchMode = appControl.getData('http://samsung.com/appcontrol/data/launch_mode'); - this.handlePreloadState(launchMode); - - let skipReload = appControl.getData('SkipReload'); - if (skipReload == 'Yes') { - console.log('skipping reload'); - // TODO : Need to care this situation and decide to pass the addon event emitter to resume() - this.resume(); + if (!this.profileDelegate.handleAppControlEvent(appControl)) { return; } let loadInfo = appControl.getLoadInfo(); let src = loadInfo.getSrc(); - let reload = loadInfo.getReload() || this.needReload(src); + let reload = loadInfo.getReload() || this.profileDelegate.needReload(src); // handle http://tizen.org/appcontrol/operation/main operation specially. // only menu-screen app can send launch request with main operation. // in this case, web app should have to resume web app not reset. @@ -379,13 +257,10 @@ Then you can get profile log from the initial loading.`; private launchInspectorIfNeeded(appControl: NativeWRTjs.AppControl) { console.log('launchInspectorIfNeeded'); - let inspectorEnabledByVconf = wrt.tv ? wrt.tv.needUseInspector() : false; - if (inspectorEnabledByVconf && !this.backgroundExecution) { - this.inspectorSrc = this.contentSrc; - this.contentSrc = 'about:blank'; - } + let needInpectorGuide = this.profileDelegate.needInpectorGuide(); let hasAulDebug = (appControl.getData('__AUL_DEBUG__') === '1'); - if (hasAulDebug || inspectorEnabledByVconf) { + + if (hasAulDebug || needInpectorGuide) { let debugPort = wrt.startInspectorServer(); let data = { "port": [debugPort.toString()] }; this.debugPort = debugPort; @@ -447,11 +322,8 @@ Then you can get profile log from the initial loading.`; beforeQuit() { console.log('WebApplication : beforeQuit'); + this.profileDelegate.beforeQuit(); addonManager.emit('lcQuit', this.mainWindow.id); - if (wrt.tv) { - this.inspectorSrc = ''; - wrt.tv.cancelDialogs(this.mainWindow.webContents); - } if (this.debugPort) { console.log('stop inspector server'); this.debugPort = 0; @@ -460,32 +332,6 @@ Then you can get profile log from the initial loading.`; this.inQuit = true; } - private needReload(src: string) { - if (this.isAlwaysReload) { - return true; - } - let reload = false; - let originalUrl = this.mainWindow.webContents.getURL(); - if (wrt.tv) { - console.log(`appcontrol src = ${src}, original url = ${originalUrl}`); - if (src && originalUrl) { - let appcontrolUrl = (new URL(src)).href; - let oldUrl = (new URL(originalUrl)).href; - console.log(`appcontrolUrl = ${appcontrolUrl}, oldUrl = ${oldUrl}`); - // FIXME(dh81.song) - // Below case it must be distinguishable for known cases - // from 'file:///index.htmlx' to 'file:///index.html' - if (appcontrolUrl !== oldUrl.substr(0, appcontrolUrl.length)) - reload = true; - } else { - reload = true; - } - } else if (src !== originalUrl) { - reload = true; - } - return reload; - } - private handleAppControlReload(url: string) { console.log('WebApplication : handleAppControlReload'); this.closeWindows(); @@ -493,15 +339,6 @@ Then you can get profile log from the initial loading.`; this.mainWindow.loadURL(url); } - private handlePreloadState(launchMode: string) { - if (this.preloadStatus == 'readyToShow') { - this.show(); - } else { - if (launchMode != 'backgroundAtStartup') - this.preloadStatus = 'none'; - } - } - private flushData() { console.log('WebApplication : FlushData'); this.windowList.forEach((window) => window.webContents.session.flushStorageData()); @@ -527,17 +364,17 @@ Then you can get profile log from the initial loading.`; show() { console.log('WebApplication : show'); - this.preloadStatus = 'none'; - if (this.backgroundExecution) { + if (this.profileDelegate.backgroundExecutable()) { console.log('skip showing while backgroundExecution mode'); } else if (!this.mainWindow.isVisible()) { console.log('show window'); this.mainWindow.show(); } + this.profileDelegate.show(); } private closeWindows() { - wrt.tv?.clearSurface(this.mainWindow.webContents); + this.profileDelegate.clearSuface(this.mainWindow.webContents); this.windowList.forEach((window) => { if (window != this.mainWindow) window.destroy(); @@ -566,17 +403,8 @@ Then you can get profile log from the initial loading.`; addonManager.emit('lcPrelaunch', this.mainWindow.id, url); } - lowMemory() { - console.log('WebApplication : lowMemory to clearcache'); - if (!wrt.tv) - return; - this.windowList.forEach((window) => { - //clear webframe cache - (wrt.tv as NativeWRTjs.TVExtension).clearWebCache(window.webContents); - window.webContents.session.clearCache(function() { - console.log('clear session Cache complete'); - }) - }); + clearCache() { + this.profileDelegate.clearCache(); } ambientTick() { -- 2.34.1