From 054d81987df8f183576a9445f5db71f8cc56cb8b Mon Sep 17 00:00:00 2001 From: DongHyun Song Date: Wed, 6 Jan 2021 12:32:25 +0900 Subject: [PATCH 01/16] [Tizen6.5 Migration][Service] Support simple notification popup User apps can use this notification posting API by calling webapis.postPlainNotification() - title: string, mandatory - message: string, mandatory - timeout: number, optional (10s default) Actually, this feature is alternative simple solution to show notification popup of tizen.UserNotification webapi. Global notification webapis is not included in TV platform. Related patch: https://review.tizen.org/gerrit/250942/ Change-Id: Ibf51f707be4b1edf3fc50dd94041df38cbdae168 Signed-off-by: DongHyun Song --- wrt_app/service/device_api_router.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/wrt_app/service/device_api_router.ts b/wrt_app/service/device_api_router.ts index 7cb70cb..7f6548f 100644 --- a/wrt_app/service/device_api_router.ts +++ b/wrt_app/service/device_api_router.ts @@ -37,21 +37,30 @@ export class DeviceAPIRouter { } initWebapis() { - global.webapis = global.webapis ?? {}; + let app_info = global.tizen.application.getAppInfo(this.serviceId); + if (app_info) { + this.packageId = app_info.packageId; + } + global.webapis = global.webapis ?? {}; global.webapis.getCallerAppId = () => { return this.callerAppId; } global.webapis.getServiceId = () => { return this.serviceId; } - let app_info = global.tizen.application.getAppInfo(this.serviceId); - if (app_info) { - this.packageId = app_info.packageId; - } global.webapis.getPackageId = () => { return this.packageId; } + global.webapis.postPlainNotification = (title: string, message: string, timeout?: number) => { + return wrt.postPlainNotification(title, message, timeout ?? 10); + } + Object.defineProperties(global.webapis, { + getCallerAppId: { writable: false, enumerable: true }, + getPackageId: { writable: false, enumerable: true }, + getServiceId: { writable: false, enumerable: true }, + postPlainNotification: { writable: false, enumerable: true }, + }); this.initProductWebapis(); } -- 2.7.4 From 01d638f95796fba2fbc1e7a8dee138e00eb2543f Mon Sep 17 00:00:00 2001 From: liwei Date: Fri, 8 Jan 2021 17:52:11 +0800 Subject: [PATCH 02/16] [VD] Get app forcereload flag in webapplication constructor For wrt autotest tc which check "force.loadDefaultURI" metadata, get app forcereload flag(force.loadDefaultURI) in constructor, otherwise it's hard to make a scenario by test script to check this metadata. Change-Id: I38ae46846687ab52a874d8dff22d62f581b9491f Signed-off-by: liwei --- wrt_app/src/web_application.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) mode change 100755 => 100644 wrt_app/src/web_application.ts diff --git a/wrt_app/src/web_application.ts b/wrt_app/src/web_application.ts old mode 100755 new mode 100644 index 25a2441..dee5636 --- a/wrt_app/src/web_application.ts +++ b/wrt_app/src/web_application.ts @@ -27,6 +27,7 @@ export class WebApplication { backgroundExecution: boolean; defaultBackgroundColor: string; defaultTransparent: boolean; + isAlwaysReload: boolean; mainWindow: Electron.BrowserWindow; multitaskingSupport: boolean; notificationPermissionMap?: Map; @@ -60,6 +61,7 @@ export class WebApplication { this.backgroundExecution = false; } 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')); @@ -458,8 +460,7 @@ Then you can get profile log from the initial loading.`; } private needReload(src: string) { - let isAlwaysReload = (wrt.tv ? wrt.tv.isAlwaysReload() : false); - if (isAlwaysReload) { + if (this.isAlwaysReload) { return true; } let reload = false; -- 2.7.4 From 486ed93ebf9d237de154cd431219727f46221e42 Mon Sep 17 00:00:00 2001 From: liwei Date: Fri, 15 Jan 2021 10:19:22 +0800 Subject: [PATCH 03/16] [Tizen6.5 Migration][Service] Print appcontrol data when service app is launched Now in many scenario UI app launch service app, service app will check tizen.application.getCurrentApplication().getRequestedAppControl() operation and data, so print this data is good for debugging. (ex. 3201506003227, STARZY) Related patch: https://review.tizen.org/gerrit/248053/ Change-Id: I9f7b6c6319ddc0e16089ef826855ce064aeb754f Signed-off-by: liwei --- wrt_app/common/service_runner.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/wrt_app/common/service_runner.ts b/wrt_app/common/service_runner.ts index 8913929..4a0d1f1 100644 --- a/wrt_app/common/service_runner.ts +++ b/wrt_app/common/service_runner.ts @@ -16,6 +16,15 @@ function isServiceApplication() { function isGlobalService() { return global['serviceType'] === 'GLOBAL'; } +function printAppControlData(id: string) { + var reqAppControl = global.tizen.application.getCurrentApplication().getRequestedAppControl(); + var appControlData = reqAppControl.appControl.data; + console.log(`id: ${id}, appControlData operation: ${reqAppControl.appControl.operation}`); + for (var dataIndex in appControlData) { + for (var valueIndex in appControlData[dataIndex].value) + console.log(`data[${dataIndex}][${valueIndex}]: ${appControlData[dataIndex].value[valueIndex]}`); + } +} function registerExtensionResolver(id: string) { if (wrt.tv) { @@ -63,6 +72,7 @@ export function start(id: string, filename: string) { // FIXME: this is for awaking up uv loop. // uv loop is sleeping for a few second with tizen webapis's aync callback fakeTimer = setInterval(() => {}, 100); + printAppControlData(id); try { app = require(filename); if (app.onStart !== undefined) { -- 2.7.4 From ae44edd7eb4d09634531b218e16f3830ceee4ab5 Mon Sep 17 00:00:00 2001 From: DongHyun Song Date: Thu, 17 Dec 2020 14:07:33 +0900 Subject: [PATCH 04/16] [Tizen6.5 Migration][Service] Add getPkgApiVersion for global model xwalk.utils.getPkgApiVersion will return required_version of config.xml. (a.k.a. api version) This getPkgApiVersion() is used to check api privileges which is allowed in Tizen platform version. Without this patch, getPkgApiVersion() will returns wrt-service package's api version in its manifest. Reference: https://review.tizen.org/gerrit/247389/ Change-Id: Id084f2eda53e3310a2d7c5842d6bd1be4ca34fe0 Signed-off-by: DongHyun Song --- wrt_app/service/device_api_router.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wrt_app/service/device_api_router.ts b/wrt_app/service/device_api_router.ts index 7cb70cb..50f6c99 100644 --- a/wrt_app/service/device_api_router.ts +++ b/wrt_app/service/device_api_router.ts @@ -16,6 +16,7 @@ export class DeviceAPIRouter { packageId: string; callerAppId: string; permissions: string[]; + pkgApiVersion: string; constructor(id: string, isGlobal: boolean) { this.id = id; @@ -24,10 +25,12 @@ export class DeviceAPIRouter { this.callerAppId = ids[1] ?? ''; this.packageId = this.serviceId.split('.')[0]; this.permissions = []; + this.pkgApiVersion = ''; this.initWebapis(); if (isGlobal) { this.permissions = wrt.getPrivileges(this.id); + this.pkgApiVersion = wrt.getPkgApiVersion(this.id); this.refineApplicationApis(); this.refinePackageApis(); this.refineFilesystemApis() @@ -276,5 +279,8 @@ export class DeviceAPIRouter { throw 'Permission denied'; } } + global.xwalk.utils.getPkgApiVersion = () => { + return this.pkgApiVersion; + } } } -- 2.7.4 From dfee77be66960867ceb25f0bceba8565e802702a Mon Sep 17 00:00:00 2001 From: liwei Date: Thu, 21 Jan 2021 11:25:11 +0800 Subject: [PATCH 05/16] fixup![Tizen6.5 Migration][Service] Print appcontrol data when service app is launched When service app is launched by "new Service()", appcontrol is not set, so need pre-check appcontrol. Change-Id: I6c9d312e2addee1e7e0c5308ce637f93ecede196 Signed-off-by: liwei --- wrt_app/common/service_runner.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/wrt_app/common/service_runner.ts b/wrt_app/common/service_runner.ts index 4a0d1f1..3522d75 100644 --- a/wrt_app/common/service_runner.ts +++ b/wrt_app/common/service_runner.ts @@ -17,12 +17,14 @@ function isGlobalService() { return global['serviceType'] === 'GLOBAL'; } function printAppControlData(id: string) { - var reqAppControl = global.tizen.application.getCurrentApplication().getRequestedAppControl(); - var appControlData = reqAppControl.appControl.data; - console.log(`id: ${id}, appControlData operation: ${reqAppControl.appControl.operation}`); - for (var dataIndex in appControlData) { - for (var valueIndex in appControlData[dataIndex].value) - console.log(`data[${dataIndex}][${valueIndex}]: ${appControlData[dataIndex].value[valueIndex]}`); + let reqAppControl = global.tizen.application.getCurrentApplication().getRequestedAppControl(); + if (reqAppControl) { + console.log(`id: ${id}, appControlData operation: ${reqAppControl.appControl.operation}`); + let appControlData = reqAppControl.appControl.data; + for (let dataIndex in appControlData) { + for (let valueIndex in appControlData[dataIndex].value) + console.log(`data[${dataIndex}][${valueIndex}]: ${appControlData[dataIndex].value[valueIndex]}`); + } } } -- 2.7.4 From 957df0f518550850d777b644581b5c4d9956305a Mon Sep 17 00:00:00 2001 From: DongHyun Song Date: Wed, 6 Jan 2021 14:35:30 +0900 Subject: [PATCH 06/16] Refactors initDisplayDelay function for readability Segregates two parts according to 'firstLaunch'. If first launch, - handle splash screen (place out of 'initDisplayDelay()') - add delay show event If not first launch - enable window Change-Id: Ic8104bc683bf3785581c4c3e6582d3fe738bed89 Signed-off-by: DongHyun Song --- wrt_app/src/web_application.ts | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/wrt_app/src/web_application.ts b/wrt_app/src/web_application.ts index a48dfa2..e6cc16c 100644 --- a/wrt_app/src/web_application.ts +++ b/wrt_app/src/web_application.ts @@ -70,7 +70,7 @@ export class WebApplication { this.setupEventListener(options); this.mainWindow = new WRTWindow(this.getWindowOption(options)); - this.initDisplayDelay(true); + this.initDisplayDelay(); this.setupMainWindowEventListener(); } @@ -290,26 +290,28 @@ export class WebApplication { }); } - private initDisplayDelay(firstLaunch: boolean) { - // TODO: On 6.0, this causes a black screen on relaunch - if (firstLaunch) - this.firstRendered = false; + private enableWindow() { this.suspended = false; + // TODO: On 6.0, this causes a black screen on relaunch if (this.showTimer) clearTimeout(this.showTimer); - let splashShown = this.preloadStatus !== 'preload' && firstLaunch && wrt.showSplashScreen(); - if (!splashShown && !wrt.tv) { - this.showTimer = setTimeout(() => { - if (!this.suspended) { - console.log('FrameRendered not obtained from engine. To show window, timer fired'); - this.mainWindow.emit('ready-to-show'); - } - }, 2000); - } - if (!firstLaunch && !this.backgroundRunnable()) + if (!this.backgroundRunnable()) this.mainWindow.setEnabled(true); } + private initDisplayDelay() { + let splashShown = this.preloadStatus !== 'preload' && wrt.showSplashScreen(); + if (splashShown || wrt.tv) + return; + + this.showTimer = setTimeout(() => { + if (!this.suspended) { + console.log('FrameRendered not obtained from engine. To show window, timer fired'); + this.mainWindow.emit('ready-to-show'); + } + }, 2000); + } + private backgroundRunnable(): boolean { return this.backgroundSupport || this.backgroundExecution; } @@ -489,7 +491,7 @@ Then you can get profile log from the initial loading.`; private handleAppControlReload(url: string) { console.log('WebApplication : handleAppControlReload'); this.closeWindows(); - this.initDisplayDelay(false); + this.enableWindow(); this.mainWindow.loadURL(url); } -- 2.7.4 From c6e40415c63ae8d007e979255e0aeb0205aa2514 Mon Sep 17 00:00:00 2001 From: Youngsoo Choi Date: Fri, 22 Jan 2021 12:33:36 +0900 Subject: [PATCH 07/16] Don't use rpm command in spec file The use of rpm command will block OBS build process. This replace it with cat command to read chromium-efl version. Change-Id: I30e738eeb69a4d205fab5b9f73ac2f66b77d165e Signed-off-by: Youngsoo Choi --- packaging/wrtjs.spec | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/packaging/wrtjs.spec b/packaging/wrtjs.spec index 842226c..f962ec8 100755 --- a/packaging/wrtjs.spec +++ b/packaging/wrtjs.spec @@ -130,6 +130,8 @@ install -d %{buildroot}%{_datadir}/aul install -d %{buildroot}%{_resourcedir} install -d %{buildroot}%{WRTJS_APP_DIR} +mkdir -p %{buildroot}%{_datadir}/wrtjs +echo %{version} > %{buildroot}%{_datadir}/wrtjs/version %if 0%{?_use_tpk} %if 0%{?__package_signing} install -m 0644 tizen/downloadable/vd_sign/author-signature.xml %{buildroot}%{WRTJS_APP_DIR} @@ -186,19 +188,13 @@ cp -r %{app_dir}/* %{buildroot}%{_resourcedir}/ %endif %pre -if (( $(rpm -q chromium-efl | wc -l) > 1 )); then - echo -e "\033[31m*****************************************************" - echo -e "Please remove chromium-efl rpm(s) except for one:\n" - rpm -q chromium-efl - echo -e "*****************************************************\033[m" - exit 1 -fi - -export CHROMIUM_REVISION=$(rpm -q chromium-efl | awk -F. '{print $2}') -export WRTJS_REVISION=$(echo %{version} | awk -F. '{print $2}') -if [[ "${CHROMIUM_REVISION}" == "" ]]; then +export CHROMIUM_REVISION= +if [ -f "%{_datadir}/chromium-efl/version" ]; then + CHROMIUM_REVISION=$(cat %{_datadir}/chromium-efl/version | awk -F. '{print $2}') +else CHROMIUM_REVISION="not installed" fi +export WRTJS_REVISION=$(echo %{version} | awk -F. '{print $2}') echo -e "\033[32m*****************************************************" echo -e "* chromium-efl revision : ${CHROMIUM_REVISION}" @@ -243,6 +239,7 @@ rm -fr %{buildroot} %if "%{_vd_cfg_product_type}" != "LFD" %{_datadir}/aul/wrt.loader %endif +%{_datadir}/wrtjs/version %{_libdir}/%{crosswalk_extensions_service}/plugins.json %{_resourcedir}/* %if 0%{?__package_signing} -- 2.7.4 From 51a7882ab7ea3f5fedc35e0f1ec4267f854509a5 Mon Sep 17 00:00:00 2001 From: DongHyun Song Date: Thu, 12 Nov 2020 17:17:18 +0900 Subject: [PATCH 08/16] [Service][Builtin] Introduce Appmanifest installer This built-in service introduces to support W3C AppManifest installation, which is written JSON manifest file. The overall goal of this feature is that 1) Convert JSON manifest to config.xml - download proper icon 2) Archive .wgt and request to install .wgt - tizen.archive is newly necessary to load To support installation of archived wgt file from this feature, there are 2 consideration to be solved. a) there is no signature file b) at least, public level certification is necessary Change-Id: Iaf09bdfb3e6c5d6e8471ef91ea13fda863641db0 Signed-off-by: DongHyun Song --- packaging/plugins.json | 5 + wrt_app/common/service_manager.ts | 1 + wrt_app/service/builtins/appmanifest_loader.ts | 203 +++++++++++++++++++++++++ wrt_app/service/builtins/wasm_builder.ts | 16 +- 4 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 wrt_app/service/builtins/appmanifest_loader.ts diff --git a/packaging/plugins.json b/packaging/plugins.json index 7838bf7..ed9a833 100644 --- a/packaging/plugins.json +++ b/packaging/plugins.json @@ -79,5 +79,10 @@ "name":"tizen.notification", "lib":"/usr/lib/tizen-extensions-crosswalk/libtizen_notification.so", "entry_points": ["tizen.StatusNotification","tizen.UserNotification", "tizen.NotificationDetailInfo"] + }, + { + "name":"tizen.archive", + "lib":"/usr/lib/tizen-extensions-crosswalk/libtizen_archive.so", + "entry_points": [] } ] diff --git a/wrt_app/common/service_manager.ts b/wrt_app/common/service_manager.ts index face443..544aec7 100644 --- a/wrt_app/common/service_manager.ts +++ b/wrt_app/common/service_manager.ts @@ -60,6 +60,7 @@ export function handleBuiltinService(serviceId: string, serviceName: string) { } let need_stop = (serviceName.substr(0, 5) === 'stop_'); if (need_stop) { + console.log(`${serviceName} will be terminated.`); workers[serviceId].terminate(); } else { console.log(`Builtin service is ${serviceName}`); diff --git a/wrt_app/service/builtins/appmanifest_loader.ts b/wrt_app/service/builtins/appmanifest_loader.ts new file mode 100644 index 0000000..2c8597b --- /dev/null +++ b/wrt_app/service/builtins/appmanifest_loader.ts @@ -0,0 +1,203 @@ +import '../../common/init'; +import { isMainThread, workerData } from 'worker_threads'; +import { wrt } from '../../browser/wrt'; +import * as fs from 'fs'; +import * as https from 'https'; +import * as XWalkExtension from '../../common/wrt_xwalk_extension'; + +function getManifestFile(manifestUrl: string) { + console.log('manifestUrl : '+manifestUrl); + return new Promise((resolve, reject) => { + const req = https.request(manifestUrl, (res) => { + res.setEncoding('utf8'); + let responseBody = ''; + res.on('data', (data) => { + responseBody += data; + }); + res.on('end', () => { + resolve(JSON.parse(responseBody)); + }); + }).on('error', (err) => { + console.log(`error : ${err}`); + reject(err); + }); + req.end(); + }); +} + +async function downloadIcon(iconSrc: string, iconFile: string) { + console.log('iconSrc : ' + iconSrc); + return new Promise((resolve, reject) => { + const req = https.request(iconSrc, (res) => { + const Stream = require('stream').Transform; + let data = new Stream(); + res.on('data', (chunk) => { + data.push(chunk); + }); + res.on('end', () => { + fs.writeFileSync(iconFile, data.read()); + resolve('done'); + }); + }).on('error', (err) => { + console.log(`error : ${err}`); + reject(err); + }); + req.end(); + }); +} + +function copyManifest(filePath: string, manifestData: string) { + fs.writeFileSync(filePath, manifestData); +} + +function concatWithBaseUrl(url: string, baseUrl: string) { + if (url.indexOf('http:') == -1 && url.indexOf('https:') == -1) { + return baseUrl.substr(0, baseUrl.lastIndexOf('/') + 1) + url; + } + return url; +} + +let baseWorkingDir = '/home/owner/content/Downloads/AppManifest'; +let downloadVirtualDir = 'downloads/AppManifest'; +let iconFile: string = ''; +let iconName: string = ''; +let manifestFile: string = ''; +let convertedConfigXml: string = ''; +let refCount: number = 0; + +function getAppName(appName: string) { + appName = appName.replace(/ /g, ''); + console.log('appName : ' + appName); + return appName; +} + +function makeWorkingFolder(appName: string) { + let workingDir = `${baseWorkingDir}/${appName}`; + fs.rmdirSync(workingDir, { recursive: true }); + fs.mkdir(workingDir, { recursive: true }, (err) => { + if (err) + console.log(`mkdir error : ${err}`) + }); +} + +async function handleIcon(appName: string, manifestUrl: string, manifest: any) { + let lengthOfIcons = manifest['icons'].length; + let lastIcon = manifest['icons'][lengthOfIcons - 1]; + let iconSrc = concatWithBaseUrl(lastIcon['src'], manifestUrl); + iconName = iconSrc.substr(iconSrc.lastIndexOf('/') + 1); + iconFile = `${baseWorkingDir}/${appName}/${iconName}`; + await downloadIcon(iconSrc, iconFile); + iconFile = `${downloadVirtualDir}/${appName}/${iconName}`; + refCount++; +} + +function handleStartUrl(appName: string, manifestUrl: string, manifest: any) { + let startUrl = concatWithBaseUrl(manifest['start_url'], manifestUrl); + manifest['start_url'] = startUrl; + manifestFile = `${baseWorkingDir}/${appName}/appmanifest.json`; + copyManifest(manifestFile, JSON.stringify(manifest)); + manifestFile = `${downloadVirtualDir}/${appName}/appmanifest.json`; + refCount++; + return startUrl; +} + +function makeRandomId() { + return Math.random().toString(36).substring(2, 12); +} + +function convertConfigXml(appName: string, startUrl: string) { + convertedConfigXml = `${baseWorkingDir}/${appName}/config.xml`; + let id = makeRandomId(); + let configXml = ``; + configXml += ``; + configXml += ``; + configXml += `` + configXml += ``; + configXml += `${appName}`; + configXml += ``; + configXml += ``; + copyManifest(convertedConfigXml, configXml); + convertedConfigXml = `${downloadVirtualDir}/${appName}/config.xml`; + refCount++; +} + +function cleanUpAndQuit(appName: string) { + let workingDir = `${baseWorkingDir}/${appName}`; + fs.rmdirSync(workingDir, { recursive: true }); + process.exit(); +} + +function installWgt(appName: string) { + let onInstallation = { + onprogress: (packageId: string, percentage: string) => { + console.log("On installation(" + packageId + ") : progress(" + percentage + ")"); + }, + oncomplete: (packageId: string) => { + console.log("Installation(" + packageId + ") Complete"); + wrt.postPlainNotification(appName, 'Install Success', 5); + cleanUpAndQuit(appName); + } + }; + let wgtPath = `${baseWorkingDir}/${appName}/${appName}.wgt`; + console.log(`wgtPath: ${wgtPath}`); + global.tizen.package.install(wgtPath, onInstallation, (err: any) => { + console.log("Error occurred on installation : " + err.name); + cleanUpAndQuit(appName); + }); +} + +function makeWgt(appName: string) { + let wgtPath = `${downloadVirtualDir}/${appName}/${appName}.wgt`; + let onArchive = (archive: any) => { + function progressCallback(opId: string, val: number, name: any) { + console.log('opId: ' + opId + ' with progress val: ' + (val * 100).toFixed(0) + '%'); + } + function successCallback() { + console.log(`File added : ${refCount}`); + refCount--; + if (!refCount) { + installWgt(appName); + } + } + console.log(`convertedConfigXml : ${convertedConfigXml}`); + console.log(`manifestFile : ${manifestFile}`); + console.log(`iconFile : ${iconFile}`); + let defaultArchiveFileEntryOption = { destination:'', stripSourceDirectory: true}; + archive.add(convertedConfigXml, successCallback, null, progressCallback, defaultArchiveFileEntryOption); + archive.add(manifestFile, successCallback, null, progressCallback, defaultArchiveFileEntryOption); + if (iconFile) + archive.add(iconFile, successCallback, null, progressCallback, defaultArchiveFileEntryOption); + } + global.tizen.archive.open(wgtPath, 'w', onArchive, () => { }, { overwrite: true }); +} + +async function parseAndHandleManifest(manifestUrl: string) { + let manifest: any = await getManifestFile(manifestUrl); + let appName = getAppName(manifest['name']); + try { + makeWorkingFolder(appName); + if (manifest['icons']) { + await handleIcon(appName, manifestUrl, manifest); + } + if (manifest['start_url']) { + let startUrl = handleStartUrl(appName, manifestUrl, manifest); + convertConfigXml(appName, startUrl); + } + makeWgt(appName); + } catch (e) { + console.log(`Exception: ${e}`); + cleanUpAndQuit(appName); + } +} + +export function run(manifestUrl: string) { + console.log(`Appmanifest parser starts for ${manifestUrl}`); + setInterval(() => { }, 500); + wrt.tv?.delayShutdown(); + XWalkExtension.initialize(); + parseAndHandleManifest(manifestUrl); +} + +if (!isMainThread) { + run(decodeURIComponent(workerData.id)); +} diff --git a/wrt_app/service/builtins/wasm_builder.ts b/wrt_app/service/builtins/wasm_builder.ts index 54177d0..e23ac17 100644 --- a/wrt_app/service/builtins/wasm_builder.ts +++ b/wrt_app/service/builtins/wasm_builder.ts @@ -1,13 +1,13 @@ import '../../common/init'; -import { isMainThread, parentPort, workerData } from 'worker_threads'; +import { isMainThread, workerData } from 'worker_threads'; import { wrt } from '../../browser/wrt'; import * as fs from 'fs'; function compileWasmForCaching(files: string[]) { try { - files.forEach(async file_path => { - console.log(`Requesting WASM compilation for building a cache, file_path:(${file_path})`); - let source = fs.readFileSync(file_path); + files.forEach(async filePath => { + console.log(`Requesting WASM compilation for building a cache, file_path:(${filePath})`); + let source = fs.readFileSync(filePath); let file = new Uint8Array(source); await WebAssembly.compile(file); }); @@ -16,12 +16,12 @@ function compileWasmForCaching(files: string[]) { } } -export function run(app_id: string) { - console.log(`wasm_builder.js starts, app_id:(${app_id})`); +export function run(appId: string) { + console.log(`wasm_builder.js starts, app_id:(${appId})`); let tv = wrt.tv as NativeWRTjs.TVExtension; tv.setWasmFlags(); - tv.setDiskCache(app_id); - let files = tv.getWasmFiles(app_id); + tv.setDiskCache(appId); + let files = tv.getWasmFiles(appId); console.log(files); tv.delayShutdown(); compileWasmForCaching(files); -- 2.7.4 From 81b700da16e52295da22da13b22568384313044e Mon Sep 17 00:00:00 2001 From: Youngsoo Choi Date: Thu, 28 Jan 2021 11:51:40 +0900 Subject: [PATCH 09/16] [M85][MDE] Support MDE interfaces This supports MDE interfaces. Reference: https://review.tizen.org/gerrit/248308 Together with: https://review.tizen.org/gerrit/252383 Change-Id: Ifa6f5d96606979918d4b134abcd48405e5e4654b Signed-off-by: Youngsoo Choi --- wrt_app/service/device_api_router.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/wrt_app/service/device_api_router.ts b/wrt_app/service/device_api_router.ts index 735eb57..9e2495b 100644 --- a/wrt_app/service/device_api_router.ts +++ b/wrt_app/service/device_api_router.ts @@ -64,9 +64,38 @@ export class DeviceAPIRouter { getServiceId: { writable: false, enumerable: true }, postPlainNotification: { writable: false, enumerable: true }, }); + this.initMDEWebapis(); this.initProductWebapis(); } + initMDEWebapis() { + if (wrt['mde'] && !global.webapis.mde) { + let mde = wrt.mde as NativeWRTjs.MDEExtension; + global.webapis.mde = {}; + if (wrt.tv) { + global.webapis.mde.deInitVirtualEventGenerator = (type: number) => { + return mde.deInitVirtualEventGenerator(type); + } + global.webapis.mde.generateVirtualKeyEvent = (keycode: number, state: number) => { + return mde.generateVirtualKeyEvent(keycode, state); + } + global.webapis.mde.generateVirtualMouseButtonEvent = (button: number, status: number) => { + return mde.generateVirtualMouseButtonEvent(button, status); + } + global.webapis.mde.generateVirtualMouseMoveEvent = (x: number, y: number, move_count: number) => { + return mde.generateVirtualMouseMoveEvent(x, y, move_count); + } + global.webapis.mde.initVirtualEventGenerator = (type: number) => { + return mde.initVirtualEventGenerator(type); + } + } + global.webapis.mde.launchBrowserFromUrl = (url: string) => { + return mde.launchBrowserFromUrl(url); + } + Object.defineProperty(global.webapis, 'mde', { writable: false, enumerable: true }); + } + } + initProductWebapis() { // for TV profile if (wrt.tv && !global.webapis.productinfo) { -- 2.7.4 From 73bfef03506dee58ff00e5b2a116ba0662f4cc26 Mon Sep 17 00:00:00 2001 From: Youngsoo Choi Date: Mon, 1 Feb 2021 11:21:48 +0900 Subject: [PATCH 10/16] Set DEFAULT_TIZEN_VERSION to 6.5 This sets DEFAULT_TIZEN_VERSION to 6.5 as per current tizen branch version. Change-Id: I9d5d20edef1a89f5865909bb5eff691c49f7b264 Signed-off-by: Youngsoo Choi --- tizen/build/common.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tizen/build/common.sh b/tizen/build/common.sh index 98325fe..e2bcb49 100755 --- a/tizen/build/common.sh +++ b/tizen/build/common.sh @@ -4,7 +4,7 @@ export SCRIPTDIR=$(readlink -e $(dirname $0)) export TOPDIR=$(readlink -f "${SCRIPTDIR}/../..") export CHROME_SRC="${TOPDIR}" -export LATEST_TIZEN_VERSION=6.0 +export LATEST_TIZEN_VERSION=6.5 export DEFAULT_TIZEN_VERSION=$LATEST_TIZEN_VERSION if [ "$(echo "$@" | grep -e "--tizen")" != "" ]; then @@ -82,9 +82,13 @@ function setupAndExecuteTargetBuild() { if [ "$PROFILE" == "" ]; then if [[ $platform == "tv" ]]; then - PROFILE=tztv_${DEFAULT_TIZEN_VERSION}_arm-nikem + #TODO: Once 6.5 tv repo is ready, uncomment following line + #PROFILE=tztv_${DEFAULT_TIZEN_VERSION}_arm-nikem + PROFILE=tztv_6.0_arm-nikem elif [[ $platform == "da" ]]; then - PROFILE=tzda_${DEFAULT_TIZEN_VERSION}_arm-kantm + #TODO: Once 6.5 fhub repo is ready, uncomment following line + #PROFILE=tzda_${DEFAULT_TIZEN_VERSION}_arm-kantm + PROFILE=tzda_6.0_arm-kantm elif [[ $platform == "wearable" ]]; then PROFILE=tzwr_5.5_arm-spin else -- 2.7.4 From c5579c01ee9b8cd8ea20aba220dbf5f7c8c73fd5 Mon Sep 17 00:00:00 2001 From: SangYong Park Date: Mon, 8 Feb 2021 14:51:30 +0900 Subject: [PATCH 11/16] Fix addon api and support chrome extension addon app Related: https://review.tizen.org/gerrit/#/c/platform/framework/web/chromium-efl/+/253273/ Change-Id: Icc2c0533199bb075d1ecf64bc7d22ed39be4513b Signed-off-by: SangYong Park --- wrt_app/src/addon_manager.ts | 113 ++++++++++++++++++++++++++----------------- wrt_app/src/runtime.ts | 29 ++--------- 2 files changed, 72 insertions(+), 70 deletions(-) diff --git a/wrt_app/src/addon_manager.ts b/wrt_app/src/addon_manager.ts index 4d25a33..aeb6943 100644 --- a/wrt_app/src/addon_manager.ts +++ b/wrt_app/src/addon_manager.ts @@ -20,17 +20,18 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import { EventEmitter } from 'events'; -import { ipcMain } from 'electron'; +import { ipcMain, session } from 'electron'; import { wrt } from '../browser/wrt'; interface AddonModule { - activate: () => void; - deactivate: () => void; + activate?: () => void; + deactivate?: () => void; + id?: string; } interface AddonInfo { name: string; - version: string; + isChromeExtension: boolean; path: string; pkgid: string; activate: boolean; @@ -126,15 +127,13 @@ class AddonManager { constructor() { ipcMain.on(InternalEvent.installed, (sender, name) => { console.log('Addon event : INSTALLED ' + name); - this.build(); + this.loadJsonDB(); return this.activateByName(name); }); ipcMain.on(InternalEvent.uninstalled, (sender, name, pkgid) => { console.log('Addon event : UNINSTALLED ' + name); this.deactivateByName(name); - /* FIXME: will uncheck after chromium-efl released */ - if (wrt.getPlatformType() !== "product_tv") - wrt.reqUninstallPkg(pkgid); + wrt.requestUninstallAddon(pkgid); return true; }); ipcMain.on(InternalEvent.activate, (sender, name) => { @@ -147,9 +146,8 @@ class AddonManager { }); } - private loadJsonDB(dbPath?: string): void { - if (!dbPath) - dbPath = path.join(ADN_PATH, ADDONS_DB_FILE); + private loadJsonDB(): void { + const dbPath = path.join(ADN_PATH, ADDONS_DB_FILE); let list; try { list = JSON.parse(fs.readFileSync(dbPath, 'utf-8')); @@ -160,47 +158,73 @@ class AddonManager { this.addonList = list; } - build(): boolean { - this.loadJsonDB(); - for (let addon of this.addonList) { - if (addon.activate == false) - continue; - console.log('addon ' + addon.name + '(' + addon.path + ') registered'); - } + initialize(): boolean { + wrt.on('addon-installed', (event: any, pathList: string[]) => { + for (let path of pathList) { + console.log('addon-installed at ' + path); + this.checkAddon(path); + } + this.updateDB(); + }); + + wrt.on('addon-uninstalled', (event: any, id: string) => { + console.log('addon-unistalled named ' + id); + }); + + wrt.requestAddonList(); return this.isAddonAvailable(); } - private getModule(addon: AddonInfo): AddonModule | undefined { + private activate(addon: AddonInfo) { + console.log('activate: ' + addon.path + ' name:' + addon.name); + if (!addon.activate) + return; if (addon.module) - return addon.module; + return; + try { - const addonModule = require(addon.path); - let addonInstance = new addonModule(this.eventManager.createBinder(addon)); - addon.module = addonInstance; + if (addon.isChromeExtension) { + session.defaultSession.loadExtension(addon.path).then((extension: Electron.Extension) => { + if (addon.module) + addon.module.id = extension.id; + else { + console.log('addon was deactivated') + session.defaultSession.removeExtension(extension.id); + } + }); + addon.module = {}; + } else { + const addonModule = require(addon.path); + let addonInstance = new addonModule(this.eventManager.createBinder(addon)); + addon.module = addonInstance; + if (addonInstance.activate) + addonInstance.activate(); + else + console.log('addon.activate not defined!'); + } } catch (e) { console.error('error on creating addon instance : ' + e); } - return addon.module; - } - - private activate(addon: AddonInfo) { - console.log('activate: ' + addon.path + ' name:' + addon.name); - if (!addon.activate) - return; - let addonInstance = this.getModule(addon); - if (addonInstance && addonInstance.activate) - addonInstance.activate(); - else - console.log('addon.activate not defined!'); } private deactivate(addon: AddonInfo) { console.log('deactivate: ' + addon.path + ' name:' + addon.name); - let addonInstance = this.getModule(addon); - if (addonInstance && addonInstance.deactivate) - addonInstance.deactivate(); - else - console.log('addon.deactivate not defined!'); + let addonInstance = addon.module; + if (!addonInstance) { + console.log('addon is not loaded'); + return; + } + + if (addon.isChromeExtension) { + if (addonInstance.id) + session.defaultSession.removeExtension(addonInstance.id); + } else { + if (addonInstance.deactivate) + addonInstance.deactivate(); + else + console.log('addon.deactivate not defined!'); + } + addon.module = undefined; } private getAddonInfo(name: string): AddonInfo | undefined { @@ -249,7 +273,7 @@ class AddonManager { this.eventManager.emit(eventName, ...args); } - checkAddon(appPath: string) { + private checkAddon(appPath: string) { let manifest; try { let manifestPath = path.join(appPath, MANIFEST_FILE); @@ -268,16 +292,17 @@ class AddonManager { } } + const isChromeExtension = ('manifest_version' in manifest); this.addonList.push({ name: manifest.name, - version: manifest.version, - path: path.join(appPath, manifest.main), + isChromeExtension, + path: isChromeExtension ? appPath : path.join(appPath, manifest.main), pkgid: arr[index], activate: true }); } - updateDB() { + private updateDB() { let dbPath = path.join(ADN_PATH, ADDONS_DB_FILE); let currentAddonList; try { diff --git a/wrt_app/src/runtime.ts b/wrt_app/src/runtime.ts index a729356..925a2e5 100644 --- a/wrt_app/src/runtime.ts +++ b/wrt_app/src/runtime.ts @@ -23,7 +23,6 @@ import { WebApplication } from './web_application'; class Runtime { webApplication?: WebApplication = undefined; - isLaunched = false; constructor() { app.on('before-quit', (event: any) => { @@ -46,10 +45,6 @@ class Runtime { app.on('browser-window-created', () => { console.log('browser-window-created'); - if (!this.isLaunched) { - addonManager.activateAll(); - this.isLaunched = true; - } }); app.on('window-all-closed', () => { @@ -60,14 +55,13 @@ class Runtime { app.on('web-contents-created', (event: any, webContents: any) => { console.log('web-contents-created'); webContents.on('before-input-event', (event: any, input: any) => { - if (this.isLaunched && this.webApplication) - this.handleKeyEvents(input.key); + this.handleKeyEvents(input.key); }); }); app.once('ready', (event: any) => { console.log('ready'); - let addonAvailable = addonManager.build(); + let addonAvailable = addonManager.initialize(); console.log("addonBuild : " + addonAvailable); if (addonAvailable) { const XWalkExtension = require('../common/wrt_xwalk_extension'); @@ -127,24 +121,6 @@ class Runtime { console.log('ambient-changed , ambient_mode:' + ambient_mode); this.webApplication?.ambientChanged(ambient_mode); }); - - wrt.on('addon-installed', (event: any, path: string) => { - console.log('addon-installed at ' + path); - addonManager.checkAddon(path); - }); - - wrt.on('addon-uninstalled', (event: any, id: string) => { - console.log('addon-unistalled named ' + id); - }); - - wrt.on('wgt-checking-done', (event: any) => { - console.log('wgt-checking-done'); - addonManager.updateDB(); - }); - - /* FIXME: will uncheck after chromium-efl released */ - if (wrt.getPlatformType() !== "product_tv") - wrt.getInstalledPkg(); } private handleAppControlForElectronApp(appControl: any) { @@ -165,6 +141,7 @@ class Runtime { private createWebApplicationAndLoadUrl(appControl: any) { console.log('Creating WebApplication'); + addonManager.activateAll(); let launchMode = appControl.getData('http://samsung.com/appcontrol/data/launch_mode'); let options: RuntimeOption = { isAddonAvailable: addonManager.isAddonAvailable(), -- 2.7.4 From 8fdbbe55276275c63760ebc6bd48a830c1410e29 Mon Sep 17 00:00:00 2001 From: Youngsoo Choi Date: Tue, 19 Jan 2021 11:53:31 +0900 Subject: [PATCH 12/16] [M85][Service] Drop thread privilege of service app This drops the privilege of service app and sets it to User::Pkg::{PKG_ID}. With this changes, the service app is under control of kernel smack rule. Reference: https://review.tizen.org/gerrit/249081 Together with: https://review.tizen.org/gerrit/251775 Change-Id: I0cd14f159b61b17dc395fd938144a0646529a2d9 Signed-off-by: Youngsoo Choi --- wrt_app/service/access_control_manager.ts | 4 +++- wrt_app/service/device_api_router.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/wrt_app/service/access_control_manager.ts b/wrt_app/service/access_control_manager.ts index 87afb7a..b3d804c 100644 --- a/wrt_app/service/access_control_manager.ts +++ b/wrt_app/service/access_control_manager.ts @@ -1,3 +1,4 @@ +import { wrt } from '../browser/wrt'; function checkSystemInfoApiPrivilege(func: any, permissions: string[]) { let override_func = func; @@ -10,7 +11,8 @@ function checkSystemInfoApiPrivilege(func: any, permissions: string[]) { } } -export function initialize(permissions: string[]) { +export function initialize(packageId: string, appId: string, permissions: string[]) { + wrt.security?.dropThreadPrivilege(packageId, appId); let tizen = global.tizen; if (!permissions.includes("http://tizen.org/privilege/alarm")) { tizen.alarm.add = diff --git a/wrt_app/service/device_api_router.ts b/wrt_app/service/device_api_router.ts index 9e2495b..c083c11 100644 --- a/wrt_app/service/device_api_router.ts +++ b/wrt_app/service/device_api_router.ts @@ -154,7 +154,7 @@ export class DeviceAPIRouter { initAccessControlManager() { console.log(`permissions : ${this.permissions}`); const AccessControlManager = require('./access_control_manager'); - AccessControlManager.initialize(this.permissions); + AccessControlManager.initialize(this.packageId, this.serviceId, this.permissions); } getServiceId() { -- 2.7.4 From 8d93875633c43a9be7156f997e3a34ba4928fb37 Mon Sep 17 00:00:00 2001 From: DongHyun Song Date: Thu, 18 Feb 2021 18:50:38 +0900 Subject: [PATCH 13/16] [Service][VD] move mount/umount to JS side Explicitly handles mount/umount on JS side to simplify logic of WRTServiceManager. Related chromium-efl patch: https://review.tizen.org/gerrit/253835/ Change-Id: I89874f752d8113d6582a238acce9b0b328b635ab Signed-off-by: DongHyun Song --- wrt_app/common/service_runner.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wrt_app/common/service_runner.ts b/wrt_app/common/service_runner.ts index 3522d75..13baa02 100644 --- a/wrt_app/common/service_runner.ts +++ b/wrt_app/common/service_runner.ts @@ -108,6 +108,9 @@ export function stop(id: string) { function run() { let id = workerData.id; + // FIXME: this should be 'wrt.tv?.serviceMount(id)' after Tizen 6.5 release + (wrt.tv as any)?.serviceMount(id); + let filename = workerData.filename; start(id, filename); @@ -120,6 +123,7 @@ function run() { setTimeout(() => { XWalkExtension.cleanup(); parentPort?.postMessage("will-terminate"); + (wrt.tv as any)?.serviceUmount(id); }, message.delay); } }); -- 2.7.4 From fb1486338c019c8d3ea4803f4252f76ff8189c62 Mon Sep 17 00:00:00 2001 From: liwei Date: Fri, 5 Mar 2021 11:09:41 +0800 Subject: [PATCH 14/16] [VD] Remove timeout when app is quit in non-multitasking scenario In TV, visibility change callback is moved earlier,so this timeout is unncessary. Timeout logic is added in https://review.tizen.org/gerrit/208100/ Change-Id: I5467b688987a23f7b72cb0984b38671538da5f40 Signed-off-by: liwei --- wrt_app/src/web_application.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/wrt_app/src/web_application.ts b/wrt_app/src/web_application.ts index e6cc16c..6afc4ac 100644 --- a/wrt_app/src/web_application.ts +++ b/wrt_app/src/web_application.ts @@ -413,11 +413,8 @@ Then you can get profile log from the initial loading.`; this.flushData(); if (!this.backgroundRunnable()) { if (!this.multitaskingSupport) { - // FIXME : terminate app after visibilitychange event handling - setTimeout(() => { - console.log('multitasking is not supported; quitting app') - app.quit(); - }, 1000); + console.log('multitasking is not supported; quitting app') + app.quit(); } else { this.windowList.forEach((window) => window.setEnabled(false)); } -- 2.7.4 From a47b04acbef17108bfc13c0cf3ca0331b95181c5 Mon Sep 17 00:00:00 2001 From: DongHyun Song Date: Thu, 11 Mar 2021 13:45:26 +0900 Subject: [PATCH 15/16] [Service] Move dropThreadPrivilege() before app starting If applying app smack label before of webapis related works, there are fails of loading webapis due to smack deny. Of course, at first time, before loading webapis, app label should be applied on the node worker thread. But kind of modules are checking the privileges with 'pid', not 'tid' So, webapis loading are not loaded all successfully. This patch makes to defer privilege drop after webapis related works. (access_control_manager, device_api_router) Change-Id: I89849e4271cddde2e22a6bca77f77b503ee30cd8 Signed-off-by: DongHyun Song --- wrt_app/common/service_runner.ts | 6 ++++++ wrt_app/service/access_control_manager.ts | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/wrt_app/common/service_runner.ts b/wrt_app/common/service_runner.ts index 13baa02..f372d96 100644 --- a/wrt_app/common/service_runner.ts +++ b/wrt_app/common/service_runner.ts @@ -75,7 +75,13 @@ export function start(id: string, filename: string) { // uv loop is sleeping for a few second with tizen webapis's aync callback fakeTimer = setInterval(() => {}, 100); printAppControlData(id); + try { + let ids = id.split(':'); + let serviceId = ids[0]; + let packageId = serviceId.split('.')[0]; + wrt.security?.dropThreadPrivilege(packageId, serviceId); + app = require(filename); if (app.onStart !== undefined) { app.onStart(); diff --git a/wrt_app/service/access_control_manager.ts b/wrt_app/service/access_control_manager.ts index b3d804c..49b063e 100644 --- a/wrt_app/service/access_control_manager.ts +++ b/wrt_app/service/access_control_manager.ts @@ -12,7 +12,6 @@ function checkSystemInfoApiPrivilege(func: any, permissions: string[]) { } export function initialize(packageId: string, appId: string, permissions: string[]) { - wrt.security?.dropThreadPrivilege(packageId, appId); let tizen = global.tizen; if (!permissions.includes("http://tizen.org/privilege/alarm")) { tizen.alarm.add = -- 2.7.4 From 96a245880b341f10c494eff565165349223d05fc Mon Sep 17 00:00:00 2001 From: Youngsoo Choi Date: Fri, 22 Jan 2021 12:33:49 +0900 Subject: [PATCH 16/16] [Service] Add MDE webapis This adds following MDE APIs to get device name and login id. >> webapis.mde.getCurrentLoginId() >> webapis.mde.getDeviceName() Together with: https://review.tizen.org/gerrit/252719 Change-Id: I070ca304e3b95c07c9426ad6f32b012c5c1f01ca Signed-off-by: Youngsoo Choi --- wrt_app/service/device_api_router.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wrt_app/service/device_api_router.ts b/wrt_app/service/device_api_router.ts index c083c11..4278274 100644 --- a/wrt_app/service/device_api_router.ts +++ b/wrt_app/service/device_api_router.ts @@ -89,6 +89,12 @@ export class DeviceAPIRouter { return mde.initVirtualEventGenerator(type); } } + global.webapis.mde.getCurrentLoginId = () => { + return mde.getCurrentLoginId(); + } + global.webapis.mde.getDeviceName = () => { + return mde.getDeviceName(); + } global.webapis.mde.launchBrowserFromUrl = (url: string) => { return mde.launchBrowserFromUrl(url); } -- 2.7.4