Apply typescript to maintain consistency with js api from chromium-efl.
If you change with wrtjs and chromium-efl together,
please append '-R {GBS-ROOT of chromium-efl}/local/repos/ -C' to build command.
Change-Id: I0231b02d4d3f79e79b0275a130929e7c46bf7d62
Signed-off-by: SangYong Park <sy302.park@samsung.com>
/tizen/build/gbs.conf
+out/
+*list
+wrtjs*manifest
+
URL: https://www.tizen.org
Source: %{name}-%{version}.tar.gz
+%define app_dir out/gen/app
%define crosswalk_extensions_service tizen-extensions-crosswalk-service
+BuildRequires: pkgconfig(chromium-efl)
+
%if "%{?tizen_profile_name}" != "tv"
BuildRequires: ninja
BuildRequires: pkgconfig(dlog)
ninja -C %{_outdir} wrt-loader
%endif
+./build/tools/node ./node_modules/.bin/tsc
+absolute_appdir=$PWD/%{app_dir}
+(cd wrt_app/ && find . -type f ! -name '*.ts' -exec cp --parents {} ${absolute_appdir} \;)
+
%install
%define _resourcedir /usr/share/wrt/app
install -d %{buildroot}%{_bindir}
mkdir -p %{buildroot}%{_libdir}/%{crosswalk_extensions_service}
cp packaging/plugins.json %{buildroot}%{_libdir}/%{crosswalk_extensions_service}
-cp -r wrt_app/* %{buildroot}%{_resourcedir}/
+cp -r %{app_dir}/* %{buildroot}%{_resourcedir}/
%post
--- /dev/null
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es2017",
+ "outDir": "out/gen/app",
+ "allowJs": true,
+ "strict": true,
+ },
+ "include": [
+ "/usr/include/wrt/*.d.ts",
+ "wrt_app/**/*",
+ "wrtjs.d.ts"
+ ]
+}
'use strict';
-const common = require('../common/addonapi');
-common.defineProperties(exports);
-
-const moduleList = require('./module-list');
+import * as common from '../common/addonapi';
+import moduleList from './module-list';
+common.defineProperties(exports);
for (const module of moduleList) {
Object.defineProperty(exports, module.name, {
get: common.memoizedGetter(() => require(`./modules/${module.file}`))
'use strict'
// Browser side add-on modules, please sort alphabetically
-module.exports = [
+export default [
{ name: 'messaging', file: 'messaging' },
{ name: 'window', file: 'window' }
-]
+];
+++ /dev/null
-'use strict';
-
-const { ipcMain } = require('electron');
-
-module.exports = {
- on: function (channel, listener) {
- ipcMain.on(channel, listener);
- }
-};
\ No newline at end of file
--- /dev/null
+'use strict';
+
+import { ipcMain } from 'electron';
+
+export const on = function (channel: string, listener: (event: Electron.IpcMainEvent, ...args: any[]) => void) {
+ ipcMain.on(channel, listener);
+};
+++ /dev/null
-'use strict';
-
-const { dialog, TopLevelWindow } = require('electron');
-
-module.exports = {
- loadURL: function (winId, url) {
- let window = TopLevelWindow.fromId(winId);
- window.loadURL(url);
- },
-
- showMessageBox: function (winId, options) {
- let window = TopLevelWindow.fromId(winId);
- let showMessageBox = (dialog.showMessageBoxSync || dialog.showMessageBox);
- showMessageBox(window, options);
- },
-
- show: function (winId) {
- let window = TopLevelWindow.fromId(winId);
- window.show();
- }
-}
--- /dev/null
+'use strict';
+
+import { dialog, BrowserWindow } from 'electron';
+
+export default {
+ loadURL: function (winId: number, url: string) {
+ let window = BrowserWindow.fromId(winId);
+ window.loadURL(url);
+ },
+
+ showMessageBox: function (winId: number, options: Electron.MessageBoxOptions) {
+ let window = BrowserWindow.fromId(winId);
+ let showMessageBox = (dialog.showMessageBoxSync || dialog.showMessageBox);
+ showMessageBox(window, options);
+ },
+
+ show: function (winId: number) {
+ let window = BrowserWindow.fromId(winId);
+ window.show();
+ }
+};
'use strict';
-const moduleList = require('./module-list');
+import moduleList from './module-list';
-exports.memoizedGetter = (getter) => {
- let memoizedValue = null;
+export let memoizedGetter = (getter: () => any) => {
+ let memoizedValue: any = null;
return () => {
if (memoizedValue === null) {
}
}
-exports.defineProperties = function (targetExports) {
- const descriptors = {};
+export let defineProperties = function (targetExports: Object) {
+ const descriptors: PropertyDescriptorMap = {};
for (const module of moduleList) {
descriptors[module.name] = {
get: exports.memoizedGetter(() => require(`./modules/${module.file}`))
'use strict';
// Common add-on modules, please sort alphabetically
-module.exports = [
+export default [
{ name: 'options', file: 'options' }
];
'use strict';
-const fs = require('fs');
-const utils = require('../utils');
+import * as fs from 'fs';
+import * as path from 'path';
+import * as os from 'os';
+import utils from '../utils';
-const REPO_PATH = require('path').join(require('os').homedir(), 'data/electron/runtime_addon/');
+const REPO_PATH = path.join(os.homedir(), 'data/electron/runtime_addon/');
const DB_FILE = '_db.json';
-function getDBPath() {
+function getDBPath(): string {
const addon_name = utils.getPackageID();
if (addon_name) {
const DB_PATH = REPO_PATH + addon_name + DB_FILE;
}
}
-function get(key) {
+export function get(key: string) {
const DB_PATH = getDBPath();
if (!DB_PATH)
return undefined;
try {
- const fileContents = fs.readFileSync(DB_PATH);
+ const fileContents = fs.readFileSync(DB_PATH, 'utf-8');
const jsonObject = JSON.parse(fileContents);
console.log(`Read value: ${JSON.stringify(jsonObject)}`);
return jsonObject[key];
}
}
-function set(key, value) {
+export function set(key: string, value: any) {
const DB_PATH = getDBPath();
if (!DB_PATH)
return false;
try {
let jsonObject;
try {
- const fileContents = fs.readFileSync(DB_PATH);
+ const fileContents = fs.readFileSync(DB_PATH, 'utf-8');
jsonObject = JSON.parse(fileContents);
console.log(`Current value: ${JSON.stringify(jsonObject)}`);
} catch {
}
}
-function clearAll() {
+export function clearAll() {
const DB_PATH = getDBPath();
if (!DB_PATH)
return false;
}
return true;
}
-
-module.exports = {
- clearAll,
- get,
- set
-}
\ No newline at end of file
const keyTerm = 'globalapps';
const delimiter = '/';
-function extractID(line) {
+function extractID(line: string) {
let keyTermAt = line.indexOf(keyTerm);
let delimiterAt = line.indexOf(delimiter, keyTermAt);
return line.substr(delimiterAt + 1, 10);
}
-module.exports = {
+export default {
getPackageID() {
let stack = new Error().stack;
- stack = stack.split('\n');
- for (let i = 0; i < stack.length; ++i) {
- if (stack[i].indexOf(keyTerm) !== -1)
- return extractID(stack[i]);
+ let stack_list = stack ? stack.split('\n') : [];
+ for (let i = 0; i < stack_list.length; ++i) {
+ if (stack_list[i].indexOf(keyTerm) !== -1)
+ return extractID(stack_list[i]);
}
return '';
}
-}
\ No newline at end of file
+};
'use strict';
-const common = require('../common/addonapi');
-common.defineProperties(exports);
-
-const moduleList = require('./module-list');
+import * as common from '../common/addonapi';
+import moduleList from './module-list';
+common.defineProperties(exports);
for (const module of moduleList) {
Object.defineProperty(exports, module.name, {
get: common.memoizedGetter(() => require(`./modules/${module.file}`))
'use strict';
// Renderer side add-on modules, please sort alphabetically
-module.exports = [
+export default [
{ name: 'messaging', file: 'messaging' }
];
+++ /dev/null
-'use strict';
-
-const { ipcRenderer } = require('electron');
-
-module.exports = {
- on: function (channel, listener) {
- ipcRenderer.on(channel, listener);
- },
-
- send: function (...args) {
- ipcRenderer.send(args);
- }
-}
\ No newline at end of file
--- /dev/null
+'use strict';
+
+import { ipcRenderer } from 'electron';
+
+export const on = function (channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void) {
+ ipcRenderer.on(channel, listener);
+};
+
+export const send = function (channel: string, ...args: any[]) {
+ ipcRenderer.send(channel, args);
+};
* limitations under the License.
*/
-require('../common/init')
-const wrt = require('./wrt')
-require(wrt.getElectronPath() + '/browser/init')
+import '../common/init';
+import { wrt } from './wrt';
+import (wrt.getElectronPath() + '/browser/init');
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-require('../common/exception_handling');
-const {wrt} = process.wrtBinding('wrt');
-const {EventEmitter} = require('events');
-const util = require('util');
+import { EventEmitter } from 'events';
+import * as util from 'util';
+export const { wrt }: NativeWRTjs.WRTBinding = process.wrtBinding('wrt');
Object.setPrototypeOf(Object.getPrototypeOf(wrt), EventEmitter.prototype);
-module.exports = wrt;
-
-console.log = console.info = console.error = console.warn = function(...args) {
- wrt.log(util.format(...args));
+console.log = console.info = console.error = console.warn = function(format: any, ...param: any[]) {
+ wrt.log(util.format(format, ...param));
};
console.logd = console.logv = console.loge = console.log;
-process.stdout.write = process.stderr.write = function (chunk, encoding, callback) {
+function write(chunk: Uint8Array | string, encoding?: any, callback?: (err?: Error) => void): boolean {
if (Buffer.isBuffer(chunk)) {
chunk = chunk.toString(encoding);
}
- wrt.log(chunk);
+ wrt.log(chunk as string);
if (callback) {
callback();
}
return true;
}
+process.stdout.write = process.stderr.write = write;
* limitations under the License.
*/
-const binding = process.wrtBinding('wrt_web_contents')
-const { WRTWebContents } = binding
-const { WebContents: AtomWebContents } = process.wrtBinding('atom_browser_web_contents')
+const binding: NativeWRTjs.WRTWebContentsBinding = process.wrtBinding('wrt_web_contents');
+const { WRTWebContents } = binding;
+const { WebContents: AtomWebContents } = process.wrtBinding('atom_browser_web_contents');
-const parent = AtomWebContents.prototype
-AtomWebContents.prototype = WRTWebContents.prototype
+const parent = AtomWebContents.prototype;
+AtomWebContents.prototype = WRTWebContents.prototype;
-Object.setPrototypeOf(WRTWebContents.prototype, parent)
+Object.setPrototypeOf(WRTWebContents.prototype, parent);
WRTWebContents.prototype._init = function () {
- parent._init.call(this)
+ parent._init.call(this);
}
-module.exports = {
- create (options = {}) {
- return binding.create(options)
- }
+export const create = (options = {}) => {
+ return binding.create(options);
}
* limitations under the License.
*/
-const { WRTWindow } = process.wrtBinding('wrt_window')
-const { BrowserWindow } = require('electron')
-const WRTWebContents = require('../browser/wrt_web_contents');
+import { BrowserWindow } from 'electron';
+import * as WRTWebContents from '../browser/wrt_web_contents';
-Object.setPrototypeOf(WRTWindow.prototype, BrowserWindow.prototype)
+export const { WRTWindow } = process.wrtBinding('wrt_window');
+
+Object.setPrototypeOf(WRTWindow.prototype, BrowserWindow.prototype);
WRTWindow.prototype._init = function () {
- BrowserWindow.prototype._init.call(this)
+ (BrowserWindow.prototype as any)._init.call(this);
if (typeof this.setup === 'function')
- this.setup()
- this.constructor = BrowserWindow
- let self = this
- this.webContents.on('new-window', (event, url, frameName, disposition, options) => {
- event.preventDefault()
+ this.setup();
+ this.constructor = BrowserWindow;
+ let self = this;
+ this.webContents.on('new-window', (event: Electron.NewWindowWebContentsEvent,
+ url: string,
+ frameName: string,
+ disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'),
+ options: any) => {
+ event.preventDefault();
if (!options.webContents || options.webContents === self) {
- options.webContents = WRTWebContents()
- options.webContents.loadURL(url)
+ options.webContents = WRTWebContents.create();
+ options.webContents.loadURL(url);
}
- event.newGuest = new WRTWindow(options)
- })
+ event.newGuest = new WRTWindow(options);
+ });
}
-
-module.exports = WRTWindow
+++ /dev/null
-'use strict';
-
-const Module = require('module');
-const originalResolveFilename = Module._resolveFilename;
-const ADDONS_PATH = require('path').join(__dirname, '..', 'addon', process.type, 'addonapi.js');
-
-Module._resolveFilename = function (request, parent, isMain) {
- if (request === 'addonapi') {
- return ADDONS_PATH;
- } else {
- return originalResolveFilename(request, parent, isMain);
- }
-}
\ No newline at end of file
--- /dev/null
+'use strict';
+
+const Module = require('module');
+import * as path from 'path';
+
+const originalResolveFilename = Module._resolveFilename;
+Module._resolveFilename = function (request: string, parent: NodeModule, isMain: boolean) {
+ if (request === 'addonapi' && !process.type)
+ return path.join(__dirname, '..', 'addon', process.type as string, 'addonapi.js');
+ else
+ return originalResolveFilename(request, parent, isMain);
+}
\ No newline at end of file
+++ /dev/null
-process.wrtBinding = (name) => {
- try {
- return process._linkedBinding(name)
- } catch (error) {
- if (/No such module/.test(error.message)) {
- return process.binding(name)
- } else {
- throw error
- }
- }
-}
--- /dev/null
+import '../common/exception_handling';
+import '../common/config-search-paths';
+
+process.wrtBinding = (name) => {
+ try {
+ return process._linkedBinding(name);
+ } catch (error) {
+ if (/No such module/.test(error.message)) {
+ return process.binding(name);
+ } else {
+ throw error;
+ }
+ }
+}
const Module = require('module');
-const TimerManager = require('../service/timer_manager');
-const XWalkExtension = require('./wrt_xwalk_extension');
-const vm = require('vm');
-const wrt = require('../browser/wrt');
+import { TimerManager } from '../service/timer_manager';
+import * as XWalkExtension from './wrt_xwalk_extension';
+import * as vm from 'vm';
+import { wrt } from '../browser/wrt';
-let sandbox = {};
-let is_global_service = !!wrt.getStartServiceFile;
+interface ContextMap {
+ [id: string]: vm.Context;
+}
+
+interface ContextOption {
+ [key: string]: any;
+}
-function callFunctionInContext(name, sandbox) {
+let sandbox: ContextMap = {};
+let is_global_service: boolean = !!wrt.getStartServiceFile;
+
+function callFunctionInContext(name: string, sandbox: vm.Context) {
const script = `if (typeof ${name} === 'function') { ${name}(); }`;
vm.runInContext(script, sandbox);
}
-function startService(id, filename) {
+export function startService(id: string, filename?: string) {
if (sandbox[id] === undefined) {
XWalkExtension.initialize();
XWalkExtension.setRuntimeMessageHandler((type, data) => {
console: console,
module: new Module,
require: require,
- tizen: tizen,
+ tizen: global.tizen,
};
sandbox[id].module.exports.onStop = () => {
callFunctionInContext('module.exports.onExit', sandbox[id]);
};
if (wrt.tv) {
- sandbox[id].webapis = webapis;
+ sandbox[id].webapis = global.webapis;
sandbox[id].webapis.getServiceId = () => {
let service_id = id.split(':')[0];
return service_id;
}
sandbox[id].webapis.getPackageId = () => {
let service_id = id.split(':')[0];
- let app_info = tizen.application.getAppInfo(service_id);
+ let app_info = global.tizen.application.getAppInfo(service_id);
if (app_info)
return app_info.packageId;
return '';
for (let key in timer_api)
sandbox[id][key] = timer_api[key];
- let standard_object_list = [ Error, EvalError, RangeError, ReferenceError,
- SyntaxError, TypeError, URIError, Number, BigInt, Math, Date,
- String, RegExp, Array, Int8Array, Uint8Array, Uint8ClampedArray,
- Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array,
- Float64Array, BigInt64Array, BigUint64Array, Map, Set, WeakMap,
- WeakSet, ArrayBuffer, DataView, JSON, Promise, Reflect, Proxy,
- Intl, Intl.Collator, Intl.DateTimeFormat, Intl.NumberFormat, Intl.PluralRules,
- WebAssembly, WebAssembly.Module, WebAssembly.Instance, WebAssembly.Memory,
- WebAssembly.Table, WebAssembly.CompileError, WebAssembly.LinkError,
- WebAssembly.RuntimeError, Boolean, Function, Object, Symbol ];
- for (let idx in standard_object_list)
- sandbox[id][standard_object_list[idx].name] = standard_object_list[idx];
-
- let options = {};
+ let object_list = [ 'Error', 'EvalError', 'RangeError', 'ReferenceError',
+ 'SyntaxError', 'TypeError', 'URIError', 'Number', 'BigInt', 'Math', 'Date',
+ 'String', 'RegExp', 'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
+ 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array', 'Float32Array',
+ 'Float64Array', 'BigInt64Array', 'BigUint64Array', 'Map', 'Set', 'WeakMap',
+ 'WeakSet', 'ArrayBuffer', 'DataView', 'JSON', 'Promise', 'Reflect', 'Proxy',
+ 'Intl', 'WebAssembly', 'Boolean', 'Function', 'Object', 'Symbol' ];
+ for (let prop of object_list)
+ sandbox[id][prop] = global[prop];
+
+ let options: ContextOption = {};
let code;
if (is_global_service) {
options.filename = id;
if (wrt.tv) {
- let extension_resolver = function (module, file_path) {
+ let extension_resolver = function (module: any, file_path: string) {
console.log(`resolved path: ${file_path}`);
- let content = wrt.tv.decryptFile(id, file_path);
+ let content = (wrt.tv as NativeWRTjs.TVExtension).decryptFile(id, file_path);
if (content) {
// Remove BOM
if (content.charCodeAt(0) === 0xFEFF)
callFunctionInContext('app.onRequest', sandbox[id]);
}
-function stopService(id) {
+export function stopService(id: string) {
console.log('stopService')
if (sandbox[id]['stopped']) {
console.log(id + ' service has been already stopped.');
if (Object.keys(sandbox).length === 0)
XWalkExtension.cleanup();
}
-
-module.exports = {
- startService,
- stopService
-};
* limitations under the License.
*/
-require('./exception_handling');
+import './exception_handling';
-let instance;
-let api_ = {};
-let extensions_ = {};
+interface NativeXWalkExtension extends NativeWRTjs.XWalkExtension {
+ loaded?: boolean
+}
+
+let instance: XWalkExtension | undefined;
+let api_: { [key: string]: any } = {};
+let extensions_: { [key: string]: NativeXWalkExtension } = {};
class XWalkExtension {
constructor() {
- const binding = process.wrtBinding('wrt_xwalk_extension')
- var extensions = binding.getExtensions();
+ const binding: NativeWRTjs.XWalkExtensionBinding = process.wrtBinding('wrt_xwalk_extension')
+ var extensions: NativeXWalkExtension[] = binding.getExtensions();
for (var i = 0; i < extensions.length; i++) {
extensions[i].loaded = false;
console.log("Load extension : " + extensions[i].name);
* @param {Object} object
* @param {String} name
*/
- createNamespace(object, name) {
+ createNamespace(object: { [key: string]: any }, name: string) {
var obj = object;
var arr = name.split('.');
for (var i = 0; i < arr.length; i++) {
}
}
- exposeApi(ext) {
+ exposeApi(ext: NativeXWalkExtension) {
var i, entry_points, entry_point, tmp, parent_name, base_name;
// additional entry points are installed in global context by eval()
}
}
- runtimeMessageHandler(type, data, callback) {
+ static runtimeMessageHandler(type: string, data?: string, callback?: (message: string) => void): void {
console.log('This is prototype of runtimeMessageHandler');
}
/**
* @param {Object} ext
*/
- load(ext) {
+ load(ext: NativeXWalkExtension) {
if (ext.loaded)
return;
try {
var func = eval(jscode);
func({
- postMessage: function(msg) {
+ postMessage: function(msg: string) {
return ext.postMessage(msg);
},
- sendSyncMessage: function(msg) {
+ sendSyncMessage: function(msg: string) {
return ext.sendSyncMessage(msg);
},
- setMessageListener: function(fn) {
+ setMessageListener: function(fn: (message: string) => void) {
return ext.setMessageListener(fn);
},
- sendRuntimeMessage: function(type, data) {
+ sendRuntimeMessage: function(type: string, data?: string) {
return XWalkExtension.runtimeMessageHandler(type, data);
},
- sendRuntimeSyncMessage: function(type, data) {
+ sendRuntimeSyncMessage: function(type: string, data?: string) {
return XWalkExtension.runtimeMessageHandler(type, data);
},
- sendRuntimeAsyncMessage: function(type, data, callback) {
+ sendRuntimeAsyncMessage: function(type: string, data?: string, callback?: (message: string) => void) {
return XWalkExtension.runtimeMessageHandler(type, data, callback);
- },
- postData: function(msg, chunk) {
- return ext.postData(msg, chunk);
- },
- sendSyncData: function(msg, chunk) {
- return ext.sendSyncData(msg, chunk);
- },
- setDataListener: function(fn) {
- return ext.setDataListener(fn);
- },
- receiveChunkData: function(id, type) {
- return ext.receiveChunkData(id, type);
}
});
*
* @param {Object} ext
*/
- installTrampoline(ext) {
+ installTrampoline(ext: NativeXWalkExtension) {
var entry_points = [...new Set(ext.entry_points)];
entry_points.push(ext.name);
for (var i = 0; i < entry_points.length; i++) {
this.createNamespace(global, entry_points[i]);
Object.defineProperty(global[parent_name], base_name, {
- get: function (parent_name, base_name) {
- return function() {
+ get: function (this: XWalkExtension, parent_name: string, base_name: string) {
+ return function(this: XWalkExtension) {
try {
this.deleteTrampoline(ext);
this.load(ext);
}
}
- deleteTrampoline(ext) {
+ deleteTrampoline(ext: NativeXWalkExtension) {
var entry_points = [...new Set(ext.entry_points)];
entry_points.push(ext.name);
}
}
-const extension_api = {
- initialize: () => {
- if (!instance)
- instance = new XWalkExtension;
- },
- setRuntimeMessageHandler: (handler) => {
- XWalkExtension.runtimeMessageHandler = handler;
- },
- cleanup: () => {
- delete global.tizen;
- instance = undefined;
- },
- preventCleanup: () => {
- extension_api.cleanup = () => {};
- }
+export const initialize = () => {
+ if (!instance)
+ instance = new XWalkExtension;
+}
+
+export const setRuntimeMessageHandler = (handler: (type: string, data?: string, callback?: (message: string) => void) => void) => {
+ XWalkExtension.runtimeMessageHandler = handler;
}
-module.exports = extension_api;
+export let cleanup = () => {
+ delete global.tizen;
+ instance = undefined;
+}
+
+export const preventCleanup = () => {
+ cleanup = () => {};
+}
{
"name": "wrtjs",
"version": "1.0.0",
- "main": "src/main.js"
+ "main": "src/runtime.js"
}
+++ /dev/null
-/*
- * Copyright (c) 2019 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.
- */
-
-require('../common/init')
-require('../common/exception_handling');
-require('../common/config-search-paths');
-const wrtRenderer = require('./wrt_renderer');
-require(wrtRenderer.getElectronPath() + '/renderer/init')
* limitations under the License.
*/
-'use strict';
-
-require('../common/exception_handling');
-require('../common/config-search-paths');
-
-new (require('./runtime'))({
- devMode: false,
- noAddons: false
-});
+import '../common/init';
+import { wrtRenderer } from './wrt_renderer';
+import (wrtRenderer.getElectronPath() + '/renderer/init');
+++ /dev/null
-const {wrtRenderer} = process.wrtBinding('wrt_renderer');
-
-module.exports = wrtRenderer
--- /dev/null
+export const { wrtRenderer }: NativeWRTjs.WRTRendererBinding = process.wrtBinding('wrt_renderer');
-function initialize(permissions, sandbox) {
+import * as vm from 'vm';
+
+export function initialize(permissions: string[], sandbox: vm.Context) {
let tizen = sandbox.tizen;
if (!permissions.includes("http://tizen.org/privilege/alarm")) {
tizen.alarm.add =
}
// systeminfo : Runtime privilege validation is required, based on parameters
let getPropertyValue = tizen.systeminfo.getPropertyValue;
- tizen.systeminfo.getPropertyValue = function(type, onSuccessCallback, onErrorCallback) {
+ tizen.systeminfo.getPropertyValue = (type: string, onSuccessCallback: any, onErrorCallback: any) => {
if (type === "CELLULAR_NETWORK" && !permissions.includes("http://tizen.org/privilege/telephony")) {
console.log('The telephony permission is missing.');
return;
}
getPropertyValue.apply(tizen.systeminfo, arguments);
- }.bind(this);
-}
-
-module.exports = {
- initialize
+ };
}
+++ /dev/null
-const fs = require('fs');
-const wrt = require('../../browser/wrt');
-
-async function compileWasmForCaching(file_path) {
- console.log(`Requesting WASM compilation for building a cache, file_path:(${file_path})`);
- try {
- let source = fs.readFileSync(file_path);
- let file = new Uint8Array(source);
- await WebAssembly.compile(file);
- } catch (e) {
- console.error(`An error occurred while compiling a wasm module. error:(${e})`);
- }
-}
-
-function run(app_id) {
- console.log(`wasm_builder.js starts, app_id:(${app_id})`);
- wrt.tv.setWasmFlags();
- wrt.tv.setDiskCache(app_id);
- let files = wrt.tv.getWasmFiles(app_id);
- console.log(files);
- files.forEach((file_path) => {
- compileWasmForCaching(file_path);
- });
-}
-
-module.exports = {
- run
-}
--- /dev/null
+import * as fs from 'fs';
+import { wrt } from '../../browser/wrt';
+
+async function compileWasmForCaching(file_path: string) {
+ console.log(`Requesting WASM compilation for building a cache, file_path:(${file_path})`);
+ try {
+ let source = fs.readFileSync(file_path);
+ let file = new Uint8Array(source);
+ await WebAssembly.compile(file);
+ } catch (e) {
+ console.error(`An error occurred while compiling a wasm module. error:(${e})`);
+ }
+}
+
+export function run(app_id: string) {
+ console.log(`wasm_builder.js starts, app_id:(${app_id})`);
+ let tv = wrt.tv as NativeWRTjs.TVExtension;
+ tv.setWasmFlags();
+ tv.setDiskCache(app_id);
+ let files = tv.getWasmFiles(app_id);
+ console.log(files);
+ files.forEach((file_path) => {
+ compileWasmForCaching(file_path);
+ });
+}
'use strict';
-require('../common/init')
-const wrt = require('../browser/wrt');
-const ServiceManager = require('../common/service_manager');
+import '../common/init';
+import { wrt } from '../browser/wrt';
+import * as ServiceManager from '../common/service_manager';
wrt.on('start-service', (event, internal_id) => {
console.log('start service app : ' + internal_id);
+++ /dev/null
-class TimerManager {
- constructor() {
- this.interval_handlers = [];
- this.timeout_handlers = [];
- // Prevent contaminating global APIs
- this.timer_api = {};
- const _this = this;
-
- this.timer_api.clearInterval = function(handler) {
- const index = Object.keys(_this.interval_handlers)[Object.values(_this.interval_handlers).indexOf(handler)]
- clearInterval(_this.interval_handlers.splice(index, 1)[0]);
- }
- this.timer_api.clearTimeout = function(handler) {
- const index = Object.keys(_this.timeout_handlers)[Object.values(_this.timeout_handlers).indexOf(handler)]
- clearTimeout(_this.timeout_handlers.splice(index, 1)[0]);
- }
- this.timer_api.setInterval = function(func, delay) {
- _this.interval_handlers.push(setInterval(func, delay));
- }
- this.timer_api.setTimeout = function(func, delay) {
- _this.timeout_handlers.push(setTimeout(func, delay));
- }
- this.timer_api.setServiceInterval = function(func, after = 1000, repeat = 0) {
- if (typeof func !== "function") {
- console.log("Use function as the first parameter.");
- return;
- }
- let count = 1;
- let handler = this.timer_api.setInterval(function() {
- func();
- count++;
- if (count > repeat && repeat !== 0) {
- this.timer_api.clearInterval(handler);
- }
- }.bind(this), after);
- }.bind(this);
- }
- getTimerAPI() {
- return this.timer_api;
- }
- releaseRemainingTimers() {
- console.log('Remaining interval(s) : ' + this.interval_handlers.length);
- for (let index in this.interval_handlers) {
- const handler = this.interval_handlers.splice(index, 1)[0];
- clearInterval(handler);
- }
- console.log('Remaining timer(s) : ' + this.timeout_handlers.length);
- for (let index in this.timeout_handlers) {
- const handler = this.timeout_handlers.splice(index, 1)[0];
- clearTimeout(handler);
- }
- }
-}
-module.exports = TimerManager;
--- /dev/null
+interface TimerAPI {
+ clearInterval(id: NodeJS.Timeout): void;
+ clearTimeout(id: NodeJS.Timeout): void;
+ setInterval(callback: (...args: any[]) => void, ms: number): NodeJS.Timeout;
+ setTimeout(callback: (...args: any[]) => void, ms: number): NodeJS.Timeout;
+ setServiceInterval(callback: (...args: any[]) => void, after:number, repeat:number): void;
+}
+
+export class TimerManager {
+ interval_handlers: NodeJS.Timeout[] = [];
+ timeout_handlers: NodeJS.Timeout[] = [];
+ timer_api: TimerAPI;
+ constructor() {
+ this.timer_api = {
+ clearInterval: (handler) => {
+ const index = this.interval_handlers.indexOf(handler);
+ clearInterval(this.interval_handlers.splice(index, 1)[0]);
+ },
+ clearTimeout: (handler) => {
+ const index = this.timeout_handlers.indexOf(handler);
+ clearTimeout(this.timeout_handlers.splice(index, 1)[0]);
+ },
+ setInterval: (func, delay) => {
+ let id = setInterval(func, delay);
+ this.interval_handlers.push(id);
+ return id;
+ },
+ setTimeout: (func, delay) => {
+ let id = setTimeout(func, delay);
+ this.timeout_handlers.push(id);
+ return id;
+ },
+ setServiceInterval: (func, after = 1000, repeat = 0) => {
+ if (typeof func !== "function") {
+ console.log("Use function as the first parameter.");
+ return;
+ }
+ let count = 1;
+ let handler = this.timer_api.setInterval(() => {
+ func();
+ count++;
+ if (count > repeat && repeat !== 0) {
+ this.timer_api.clearInterval(handler);
+ }
+ }, after);
+ }
+ }
+ }
+ getTimerAPI() {
+ return this.timer_api;
+ }
+ releaseRemainingTimers() {
+ console.log('Remaining interval(s) : ' + this.interval_handlers.length);
+ for (let id of this.interval_handlers)
+ clearInterval(id);
+ this.interval_handlers = [];
+ console.log('Remaining timer(s) : ' + this.timeout_handlers.length);
+ for (let id of this.timeout_handlers)
+ clearTimeout(id);
+ this.timeout_handlers = [];
+ }
+}
+++ /dev/null
-/*
- * Copyright (c) 2019 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';
-
-const fs = require('fs');
-const path = require('path');
-const ADN_PATH =
- path.join(require('os').homedir(), 'data/electron/runtime_addon');
-const MANIFEST_FILE = 'manifest.json';
-const ADDONS_DB_FILE = 'addons_db.json';
-
-// A set of predefined events for addons
-const EventList = [
- 'lcPrelaunch', // An app is at just before launching
- 'lcResume', // An app is resumed
- 'lcSuspend', // An app is suspended
- 'lcQuit', // An app is quitted
- 'hwDownkey', // Down key is pressed
- 'hwUpkey', // Up key is pressed
- 'contentDidFinishLoad' // The navigation is done and 'onload' was dispatched
-];
-Object.freeze(EventList);
-
-const {BrowserWindow} = require('electron');
-const {EventEmitter} = require('events');
-
-class AddonManager {
- constructor() {
- this.addons_list_ = null;
- this.addons_ = null;
- this.evt_emitter_ = null;
- this.addons_listeners = {};
- }
-
- loadJsonDB(db_path) {
- if (!db_path) {
- db_path = path.join(ADN_PATH, ADDONS_DB_FILE);
- }
- var addons_list;
- try {
- addons_list = JSON.parse(fs.readFileSync(db_path));
- } catch (e) {
- console.error('LoadJsonDB - open error : ' + e);
- return false;
- // For DEBUG purpose (load addons from PATH, not via INSTALLER)
- //return this.loadAddonsListFromAdnPath();
- }
- this.addons_list_ = addons_list;
- return true;
- }
-
- build() {
- this.initEventListener();
- // 0. load addons_list_ from JSON DB
- this.loadJsonDB();
- var addons = [];
- // 1. load addons from addon_list_
- if (this.addons_list_) {
- for (var i in this.addons_list_) {
- var addon = this.addons_list_[i];
- if (addon.activate == false) {
- continue;
- }
- try {
- var manifest_obj, manifest_path = path.join(addon.path, MANIFEST_FILE);
- manifest_obj = JSON.parse(fs.readFileSync(manifest_path));
- console.log('manifest_obj : ' + JSON.stringify(manifest_obj));
-
- if (addons[manifest_obj.name]) {
- console.log('addons[' + manifest_obj.name + '] already registered : ' + addons[manifest_obj.name]);
- continue;
- }
- if (manifest_obj.main) {
- addons[manifest_obj.name] = path.join(addon.path, manifest_obj.main);
- } else {
- addons[manifest_obj.name] = addon.path;
- }
-
- console.log('addons[' + manifest_obj.name + '] = ' + addons[manifest_obj.name] + ' registered');
- } catch (e) {
- console.error('AddonManager.build error - ' + e);
- }
- }
- }
- if (this.addons_ != null) {
- delete this.addons_;
- this.addons_ = null;
- }
- this.addons_ = addons;
- return this.addons_;
- }
-
- activate(app, name) {
- if (!this.addons_) {
- return;
- }
- var addon, addon_path = null;
- if (this.addons_[name] !== undefined) {
- addon_path = this.addons_[name];
- console.log('activate: ' + addon_path + ' name:' + name);
- try {
- let Addon = require(addon_path);
- addon = new Addon(this.wrappedEventEmitter);
- } catch (e) {
- console.error('activate - error on require() : ' + e);
- return;
- }
- if (addon && addon.activate) {
- addon.activate();
- }
- else console.log('addon.activate not defined!');
- }
- }
-
- deactivate(app, name) {
- if (!this.addons_) {
- return;
- }
- console.log('deactivate: name:' + name);
- var addon, addon_path = null;
- if (this.addons_[name] !== undefined) {
- try {
- addon_path = this.addons_[name];
- } catch (e) {
- console.error('deactivate - error : ' + e);
- return;
- }
- console.log('deactivate: path:' + addon_path);
- try {
- let Addon = require(addon_path);
- addon = new Addon(this.wrappedEventEmitter);
- } catch (e) {
- console.error('deactivate - error on require() : ' + e);
- }
- if (addon && addon.deactivate) {
- addon.deactivate();
- } else {
- console.log('addon.deactivate not defined!');
- }
- }
- }
-
- activateAll(app) {
- if (!this.addons_) {
- console.log('activateAll - addons not built');
- return;
- }
- for (var name in this.addons_) {
- this.activate(app, name);
- }
- }
-
- deactivateAll(app) {
- if (!this.addons_) {
- console.log('deactivateAll - addons not built');
- return;
- }
- for (var name in this.addons_) {
- this.deactivate(app, name);
- }
- }
-
- isAddonAvailable() {
- return Object.keys(this.addons_).length != 0;
- }
-
- initEventListener() {
- const getSafeFunction = function(fn) {
- return function() {
- try {
- fn.apply(this, arguments);
- } catch (err) {
- console.log(`Exception from add-on: ${err.name} - ${err.message}`);
- }
- }
- }
- this.evt_emitter_ = new EventEmitter();
- this.wrappedEventEmitter = {
- on: (eventName, listener) => {
- if (EventList.indexOf(eventName) === -1) {
- console.log(`Invalid Event: ${eventName}`);
- } else if (!this.addons_listeners[listener]) {
- console.log(`A new listener for ${eventName} is added`);
- this.addons_listeners[listener] = getSafeFunction(listener);
- this.evt_emitter_.on(eventName, this.addons_listeners[listener]);
- } else console.log('Listener is already registered');
- },
- off: (eventName, listener) => {
- if (EventList.indexOf(eventName) === -1) {
- console.log(`Invalid Event: ${eventName}`);
- } else if (this.addons_listeners[listener]) {
- console.log(`A listener for ${eventName} is removed`);
- this.evt_emitter_.off(eventName, this.addons_listeners[listener]);
- delete this.addons_listeners[listener];
- } else console.log('Listener is not registered');
- },
- once: (eventName, listener) => {
- if (EventList.indexOf(eventName) === -1) {
- console.log(`Invalid Event: ${eventName}`);
- } else if (!this.addons_listeners[listener]) {
- console.log(`A new one-time listener for ${eventName} is added`);
- this.evt_emitter_.once(eventName, getSafeFunction(listener));
- } else console.log('Listener is already registered for the multiple-time use');
- }
- }
- }
-
- static getManifestFile() {
- return MANIFEST_FILE;
- }
-
- static getAddonsPath() {
- return ADN_PATH;
- }
-
- static checkAddon(path) {
- let tmpPath = require('path');
- let mani_path = tmpPath.join(path, MANIFEST_FILE);
- let tmpfs = require('fs');
- let json;
- let addon = null;
-
- try {
- json = JSON.parse(tmpfs.readFileSync(mani_path));
- } catch (e) {
- console.log('fail to read manifest');
- return addon;
- }
-
- addon = new Object();
- addon.name = json.name;
- addon.version = json.version;
- addon.settings = json.settings;
- addon.description = json.description;
- addon.path = path;
- let arr = path.split("/");
- let index = 0;
- for (let i in arr) {
- if (arr[i] == 'shared') {
- index = i - 1;
- break;
- }
- }
- addon.pkgid = arr[index];
- addon.type = 'WRT';
- addon.activate = true;
-
- return addon;
- }
-
- static updateDB(adnPkgs) {
- let tmpPath = require('path');
- let db_path = tmpPath.join(ADN_PATH, ADDONS_DB_FILE);
- let tmpfs = require('fs');
- let addon_list = [];
- try {
- addon_list = JSON.parse(tmpfs.readFileSync(db_path));
- } catch (e) {
- try {
- tmpfs.statSync(ADN_PATH);
- } catch (e) {
- console.log('db path not exist');
- let path_dep1 = tmpPath.join(require('os').homedir(),
- 'data/electron');
- let path_dep2 = tmpPath.join(path_dep1, 'runtime_addon');
- // fs.mkdirSync didn't work to create the directory recursively
- // even with recursive as true.
- // That's why creating a directory one by one here.
- tmpfs.mkdirSync(path_dep1, {recursive: true});
- tmpfs.mkdirSync(path_dep2, {recursive: true});
- }
- }
-
- let addons_pkgids_ = [];
- for (let addon of adnPkgs) {
- addons_pkgids_.push(addon.pkgid);
- for (let tmpAddon of addon_list) {
- if (tmpAddon.name == addon.name) {
- console.log('already in db');
- adnPkgs[adnPkgs.indexOf(addon)].activate =
- tmpAddon.activate;
- }
- }
- }
-
- // Removes obsolete add-on DB files
- try {
- const DB_FILES = '_db.json';
- tmpfs.readdirSync(ADN_PATH).forEach((file) => {
- if (file != ADDONS_DB_FILE && file.endsWith(DB_FILES)) {
- let id = file.substr(0, file.indexOf(DB_FILES));
- if (addons_pkgids_.indexOf(id) === -1) {
- try {
- tmpfs.unlinkSync(path.join(ADN_PATH, file));
- } catch (err) {
- console.log(`${file} deletion failed`);
- }
- }
- }
- });
- } catch (err) {
- console.log(`An error occurred: ${err}`);
- }
-
- var fd;
- try {
- fd = tmpfs.openSync(db_path, 'w');
- } catch (e) {
- console.log('db file open error');
- return false;
- }
- fs.writeSync(fd, JSON.stringify(adnPkgs));
- fs.closeSync(fd);
- fs.chmodSync(db_path, 0o666);
- return true;
- }
-}
-
-module.exports = AddonManager;
--- /dev/null
+/*
+ * Copyright (c) 2019 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 * as fs from 'fs';
+import * as os from 'os';
+import * as path from 'path';
+import { EventEmitter } from 'events';
+import { ipcMain } from 'electron';
+import { wrt } from '../browser/wrt';
+
+interface AddonModule {
+ activate: () => void;
+ deactivate: () => void;
+}
+
+interface AddonInfo {
+ name: string;
+ version: string;
+ path: string;
+ pkgid: string;
+ activate: boolean;
+ module?: AddonModule;
+}
+
+const ADN_PATH = path.join(os.homedir(), 'data/electron/runtime_addon');
+const MANIFEST_FILE = 'manifest.json';
+const ADDONS_DB_FILE = 'addons_db.json';
+
+// A set of predefined events for addons
+const EventList = [
+ 'lcPrelaunch', // An app is at just before launching
+ 'lcResume', // An app is resumed
+ 'lcSuspend', // An app is suspended
+ 'lcQuit', // An app is quitted
+ 'hwDownkey', // Down key is pressed
+ 'hwUpkey', // Up key is pressed
+ 'contentDidFinishLoad' // The navigation is done and 'onload' was dispatched
+];
+Object.freeze(EventList);
+
+const InternalEvent = {
+ installed: 'ipc:addons:installed',
+ uninstalled: 'ipc:addons:uninstalled',
+ activate: 'ipc:addons:activate',
+ deactivate: 'ipc:addons:deactivate'
+};
+
+class AddonEventManager {
+ private emitter = new EventEmitter();
+ private listeners = new Map();
+ private bindListener = (listener: (...args: any[]) => void, addon: AddonInfo): (...args: any[]) => void => {
+ return (...args) => {
+ listener.call(addon.module, args);
+ }
+ }
+ on = (eventName: string, listener: (...args: any[]) => void, addon: AddonInfo): void => {
+ if (EventList.indexOf(eventName) === -1) {
+ console.log(`Invalid Event: ${eventName}`);
+ } else if (!this.listeners.has(listener)) {
+ console.log(`A new listener for ${eventName} is added`);
+ let fn = this.bindListener(listener, addon);
+ this.listeners.set(listener, fn);
+ this.emitter.on(eventName, fn);
+ } else {
+ console.log('Listener is already registered');
+ }
+ }
+ off = (eventName: string, listener: (...args: any[]) => void): void => {
+ if (EventList.indexOf(eventName) === -1) {
+ console.log(`Invalid Event: ${eventName}`);
+ } else if (this.listeners.has(listener)) {
+ console.log(`A listener for ${eventName} is removed`);
+ this.emitter.off(eventName, this.listeners.get(listener));
+ this.listeners.delete(listener);
+ } else {
+ console.log('Listener is not registered');
+ }
+ }
+ once = (eventName: string, listener: (...args: any[]) => void, addon: AddonInfo): void => {
+ if (EventList.indexOf(eventName) === -1) {
+ console.log(`Invalid Event: ${eventName}`);
+ } else if (!this.listeners.has(listener)) {
+ console.log(`A new one-time listener for ${eventName} is added`);
+ this.emitter.once(eventName, this.bindListener(listener, addon));
+ } else {
+ console.log('Listener is already registered for the multiple-time use');
+ }
+ }
+ createBinder = (addon: AddonInfo) => {
+ return {
+ on: (eventName: string, listener: (...args: any[]) => void): void => {
+ this.on(eventName, listener, addon);
+ },
+ off: (eventName: string, listener: (...args: any[]) => void): void => {
+ this.off(eventName, listener);
+ },
+ once: (eventName: string, listener: (...args: any[]) => void): void => {
+ this.once(eventName, listener, addon);
+ }
+ }
+ }
+ emit = (eventName: string, ...args: any[]): void => {
+ this.emitter.emit(eventName, args);
+ }
+}
+
+class AddonManager {
+ private addonList: AddonInfo[] = [];
+ private eventManager = new AddonEventManager;
+
+ constructor() {
+ ipcMain.on(InternalEvent.installed, (sender, name) => {
+ console.log('Addon event : INSTALLED ' + name);
+ this.build();
+ 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);
+ return true;
+ });
+ ipcMain.on(InternalEvent.activate, (sender, name) => {
+ console.log('Addon event : ACTIVATE ' + name);
+ return this.activateByName(name);
+ });
+ ipcMain.on(InternalEvent.deactivate, (sender, name) => {
+ console.log('Addon event : DEACTIVATE ' + name);
+ return this.deactivateByName(name);
+ });
+ }
+
+ private loadJsonDB(dbPath?: string): void {
+ if (!dbPath)
+ dbPath = path.join(ADN_PATH, ADDONS_DB_FILE);
+ let list;
+ try {
+ list = JSON.parse(fs.readFileSync(dbPath, 'utf-8'));
+ } catch (e) {
+ console.error('LoadJsonDB - open error : ' + e);
+ list = [];
+ }
+ 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');
+ }
+ return this.isAddonAvailable();
+ }
+
+ private getModule(addon: AddonInfo): AddonModule | undefined {
+ if (addon.module)
+ return addon.module;
+ try {
+ const addonModule = require(addon.path);
+ let addonInstance = new addonModule(this.eventManager.createBinder(addon));
+ addon.module = addonInstance;
+ } 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);
+ 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!');
+ }
+
+ private getAddonInfo(name: string): AddonInfo | undefined {
+ for (let addon of this.addonList) {
+ if (addon.name === name)
+ return addon;
+ }
+ return undefined;
+ }
+
+ private activateByName(name: string): boolean {
+ let addon = this.getAddonInfo(name);
+ if (!addon)
+ return false;
+ this.activate(addon);
+ return true;
+ }
+
+ private deactivateByName(name: string): boolean {
+ let addon = this.getAddonInfo(name);
+ if (!addon)
+ return false;
+ this.deactivate(addon);
+ return true;
+ }
+
+ activateAll() {
+ for (let addon of this.addonList)
+ this.activate(addon);
+ }
+
+ deactivateAll() {
+ for (let addon of this.addonList)
+ this.deactivate(addon);
+ }
+
+ isAddonAvailable() {
+ return this.addonList.length != 0;
+ }
+
+ emit(eventName: string, ...args: any[]) {
+ this.eventManager.emit(eventName, args);
+ }
+
+ checkAddon(appPath: string) {
+ let manifest;
+ try {
+ let manifestPath = path.join(appPath, MANIFEST_FILE);
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
+ } catch (e) {
+ console.log('fail to read manifest');
+ return;
+ }
+
+ let arr = appPath.split("/");
+ let index = 0;
+ for (let i in arr) {
+ if (arr[i] == 'shared') {
+ index = Number(i) - 1;
+ break;
+ }
+ }
+
+ this.addonList.push({
+ name: manifest.name,
+ version: manifest.version,
+ path: path.join(appPath, manifest.main),
+ pkgid: arr[index],
+ activate: true
+ });
+ }
+
+ updateDB() {
+ let dbPath = path.join(ADN_PATH, ADDONS_DB_FILE);
+ let currentAddonList;
+ try {
+ currentAddonList = JSON.parse(fs.readFileSync(dbPath, 'utf-8'));
+ } catch (e) {
+ try {
+ fs.statSync(ADN_PATH);
+ } catch (e) {
+ console.log('db path not exist');
+ fs.mkdirSync(ADN_PATH, { recursive: true });
+ }
+ currentAddonList = [];
+ }
+
+ let pkgids: string[] = [];
+ for (let addon of this.addonList) {
+ pkgids.push(addon.pkgid);
+ for (let addonInDB of currentAddonList) {
+ if (addonInDB.name == addon.name)
+ addon.activate = addonInDB.activate;
+ }
+ }
+
+ // Removes obsolete add-on DB files
+ try {
+ const DB_FILES = '_db.json';
+ fs.readdirSync(ADN_PATH).forEach((file) => {
+ if (file != ADDONS_DB_FILE && file.endsWith(DB_FILES)) {
+ let id = file.substr(0, file.indexOf(DB_FILES));
+ if (pkgids.indexOf(id) === -1) {
+ try {
+ fs.unlinkSync(path.join(ADN_PATH, file));
+ } catch (err) {
+ console.log(`${file} deletion failed`);
+ }
+ }
+ }
+ });
+ } catch (err) {
+ console.log(`An error occurred: ${err}`);
+ }
+
+ let fd;
+ try {
+ fd = fs.openSync(dbPath, 'w');
+ } catch (e) {
+ console.log('db file open error');
+ return false;
+ }
+ let replacer = (key: string, value: any): any => {
+ if (key === 'module')
+ return undefined;
+ return value;
+ };
+ fs.writeSync(fd, JSON.stringify(this.addonList, replacer));
+ fs.closeSync(fd);
+ fs.chmodSync(dbPath, 0o666);
+ return true;
+ }
+}
+
+export const addonManager = new AddonManager;
+++ /dev/null
-/*
- * Copyright (c) 2019 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';
-
-module.exports = Object.freeze({
- ADDONS: {
- INSTALLED: 'ipc:addons:installed',
- UNINSTALLED: 'ipc:addons:uninstalled',
- ACTIVATE: 'ipc:addons:activate',
- DEACTIVATE: 'ipc:addons:deactivate'
- }
-});
+++ /dev/null
-/*
- * Copyright (c) 2019 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';
-
-const wrt = require('../browser/wrt'); // Load first for log
-const AddonManager = require('./addon_manager');
-const {app, ipcMain} = require('electron');
-const IPC_MESSAGE = require('./ipc_message');
-const WebApplication = require('./web_application');
-
-class Runtime {
- constructor(options) {
- this.webApplication = null;
- this.handleIpcMessages();
- this.addonManager = null;
- this.isLaunched = false;
- this.inspectorEnabledByVconf = false;
- this.addonPkgs = [];
-
- var _this = this;
- app.on('before-quit', function(event) {
- console.log('before-quit');
- if (_this.webApplication) {
- _this.webApplication.quit();
- }
- });
- app.on('will-quit', function(event) {
- console.log('will-quit');
- _this.addonManager.deactivateAll(app);
- });
- app.on('quit', function(event) {
- console.log('quit');
- if (_this.webApplication) {
- _this.webApplication.finalize();
- _this.webApplication = null;
- }
- wrt.exit();
- });
- app.on('browser-window-blur', function() {
- console.log('browser-window-blur');
- });
- app.on('browser-window-focus', function() {
- console.log('browser-window-focus');
- });
- app.on('browser-window-created', function() {
- console.log('browser-window-created');
- if (!_this.isLaunched) {
- _this.addonManager.activateAll(app);
- _this.isLaunched = true;
- }
- });
- app.on('gpu-process-crashed', function() {
- console.error('gpu-process-crashed');
- });
- app.on('window-all-closed', function(event) {
- console.log('window-all-closed');
- app.quit();
- });
- app.on('will-finish-launching', function(event) {
- console.log('will-finish-launching');
- });
- app.on('web-contents-created', function(event, webContents) {
- console.log('web-contents-created');
- if (wrt.tv)
- _this.setCookiePath();
- webContents.on('before-input-event', function(event, input) {
- if (_this.isLaunched && _this.webApplication) {
- _this.handleKeyEvents(input.key);
- }
- });
- });
- app.once('ready', function(event) {
- console.log('ready');
- _this.addonManager = new AddonManager();
- if (!options.noAddons) {
- let addonBuilt = _this.addonManager.build();
- console.log("addonBuild : " + addonBuilt.length);
- if (addonBuilt.length) {
- const XWalkExtension = require('./wrt_xwalk_extension');
- XWalkExtension.initialize();
- XWalkExtension.preventCleanup();
- }
- }
- if (wrt.tv) {
- wrt.tv.importCertificate('');
- wrt.tv.optimizeCache();
- wrt.tv.clearDeadMount();
- }
- });
- wrt.on('app-control', function(event, appControl) {
- console.log('app-control');
- let loadInfo = appControl.getLoadInfo();
- let src = loadInfo.getSrc();
-
- if (wrt.isElectronApp()) {
- console.log('Electron App launch');
- const Module = require('module');
- Module.globalPaths.push(wrt.getAppPath());
- let filePath = src[7] === '/' ? src.substr(8) : src.substr(7); // strip "file://"
- let pkgJson = require(filePath);
- let pos = filePath.lastIndexOf('/');
-
- let mainJsPath = (pos !== -1 ? filePath.substr(0, pos + 1) : '') +
- (pkgJson.main || 'index.js');
- console.log('loading path:', mainJsPath);
- Module._load(mainJsPath, Module, true);
- app.emit('ready');
- } else {
- console.log('Tizen Web App launch');
- let launchMode = appControl.getData('http://samsung.com/appcontrol/data/launch_mode');
- if (!_this.webApplication) {
- console.log('Creating WebApplication');
- options.isAddonAvailable = !options.noAddons &&
- _this.addonManager.isAddonAvailable();
- options.launchMode = launchMode;
- _this.webApplication = new WebApplication(options);
- _this.webApplication.addonEmitter =
- _this.addonManager.evt_emitter_;
- if (wrt.tv) {
- _this.inspectorEnabledByVconf = wrt.tv.needUseInspector();
- if (_this.inspectorEnabledByVconf && launchMode != 'backgroundExecution') {
- _this.webApplication.inspectorSrc = src;
- src = "about:blank";
- }
- let useDiskCache = appControl.getData('USE_DISKCACHE');
- let halfWindowOption = appControl.getData('http://samsung.com/appcontrol/data/half_window_support');
- wrt.tv.setDiskCache(useDiskCache);
- wrt.tv.handleAppControlData(launchMode, halfWindowOption);
- }
- _this.webApplication.mainWindow.loadURL(src);
- _this.webApplication.prelaunch(src);
- } else {
- console.log('Handling app-control event');
- if (_this.webApplication.preloadStatus == 'readyToShow') {
- _this.webApplication.show();
- } else {
- if (launchMode != 'backgroundAtStartup') {
- _this.webApplication.preloadStatus = 'none';
- }
- }
-
- 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;
- }
-
- let reload = loadInfo.getReload() || _this.webApplication.isAlwaysReload;
- if (!reload) {
- let originalUrl = _this.webApplication.mainWindow.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;
- }
- }
- // 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.
- if (reload && appControl.getOperation() == 'http://tizen.org/appcontrol/operation/main')
- reload = false;
- if (reload) {
- _this.webApplication.handleAppControlReload(src);
- } else {
- _this.webApplication.sendAppControlEvent();
- }
- }
- }
- _this.setCookiePath();
- _this.launchInspector(appControl);
- });
- wrt.on('suspend', function() {
- console.log('suspend');
- if (_this.webApplication)
- _this.webApplication.suspend();
- });
- wrt.on('resume', function() {
- console.log('resume');
- if (_this.webApplication)
- _this.webApplication.resume();
- });
- wrt.on('low-memory', function() {
- console.log('low-memory');
- if (_this.webApplication)
- _this.webApplication.lowMemory();
- });
- wrt.on('message', function(event, type, params) {
- console.log('message type(' + type + ') params : ' + params);
- const app_id = params[0];
- if (type === 'startService') {
- require('../common/service_manager').startService(app_id, params[1]);
- event.preventDefault();
- } else if (type === 'stopService') {
- require('../common/service_manager').stopService(app_id);
- event.preventDefault();
- }
- });
- wrt.on('ambient-tick', function() {
- if (_this.webApplication)
- _this.webApplication.ambientTick();
- });
- wrt.on('ambient-changed', function(event, ambient_mode) {
- console.log('ambient-changed , ambient_mode:' + ambient_mode);
- if (_this.webApplication)
- _this.webApplication.ambientChanged(ambient_mode);
- });
- wrt.on('addon-installed', function(event, path) {
- console.log('addon-installed at ' + path);
- let dbInfo = AddonManager.checkAddon(path);
- if (dbInfo) {
- _this.addonPkgs.push(dbInfo);
- }
- });
- wrt.on('addon-uninstalled', function(event, id) {
- console.log('addon-unistalled named ' + id);
- });
- wrt.on('wgt-checking-done', function(event) {
- console.log('wgt-checking-done');
- AddonManager.updateDB(_this.addonPkgs);
- });
- /* FIXME: will uncheck after chromium-efl released */
- if (wrt.getPlatformType() !== "product_tv") {
- wrt.getInstalledPkg();
- }
- }
- handleIpcMessages() {
- var _this = this;
- ipcMain.on(IPC_MESSAGE.ADDONS.INSTALLED, (sender, name) => {
- console.log('handleIpcMessages: INSTALLED ' + name);
- _this.addonManager.build();
- return _this.addonManager.activate(app, name);
- });
- ipcMain.on(IPC_MESSAGE.ADDONS.UNINSTALLED, (sender, name, pkg) => {
- console.log('handleIpcMessages: UNINSTALLED ' + name);
- _this.addonManager.deactivate(app, name);
- /* FIXME: will uncheck after chromium-efl released */
- if (wrt.getPlatformType() !== "product_tv") {
- wrt.reqUninstallPkg(pkg);
- }
- return true;
- });
- ipcMain.on(IPC_MESSAGE.ADDONS.ACTIVATE, (sender, name) => {
- console.log('handleIpcMessages: ACTIVATE ' + name);
- return _this.addonManager.activate(app, name);
- });
- ipcMain.on(IPC_MESSAGE.ADDONS.DEACTIVATE, (sender, name) => {
- console.log('handleIpcMessages: DEACTIVATE ' + name);
- return _this.addonManager.deactivate(app, name);
- });
- }
- checkInspectorCondition(appControl) {
- let bundleDebug = (appControl.getData('__AUL_DEBUG__') === "1");
- return (bundleDebug || this.inspectorEnabledByVconf);
- }
- setCookiePath() {
- this.setCookiePath = () => {}; // call once
- console.log('setCookiePath');
-
- // FIX ME : It must be supplemented to set a specific path
- wrt.setCookiePath();
- }
- launchInspector(appControl) {
- this.launchInspector = (param) => {}; // call once
- console.log('launchInspector');
-
- // AUL public key/Vconf - To support inspector
- if (this.checkInspectorCondition(appControl)) {
- var debugPort = wrt.startInspectorServer();
- var data = { "port" : [ debugPort.toString() ] };
- if (this.webApplication)
- this.webApplication.debugPort = debugPort;
- appControl.reply(data);
- }
- }
- handleKeyEvents(key) {
- let valid = false;
- let _this = this;
-
- console.log(key + ' is pressed');
- switch(key) {
- case "ArrowUp":
- case "Up":
- case "ArrowDown":
- case "Down":
- valid = true;
- break;
- default:
- console.log('No handler for the key ' + key);
- break;
- }
-
- if (valid) {
- _this.webApplication.keyEvent(key);
- }
- }
-}
-module.exports = Runtime;
--- /dev/null
+/*
+ * Copyright (c) 2019 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 { wrt } from '../browser/wrt'; // Load first for log
+import { addonManager } from './addon_manager';
+import { app } from 'electron';
+import { WebApplication } from './web_application';
+
+class Runtime {
+ webApplication?: WebApplication = undefined;
+ isLaunched = false;
+ inspectorEnabledByVconf = false;
+
+ constructor() {
+ app.on('before-quit', (event) => {
+ console.log('before-quit');
+ this.webApplication?.quit();
+ });
+ app.on('will-quit', (event) => {
+ console.log('will-quit');
+ addonManager.deactivateAll();
+ });
+ app.on('quit', (event) => {
+ console.log('quit');
+ if (this.webApplication) {
+ this.webApplication.finalize();
+ this.webApplication = undefined;
+ }
+ });
+ app.on('browser-window-created', () => {
+ console.log('browser-window-created');
+ if (!this.isLaunched) {
+ addonManager.activateAll();
+ this.isLaunched = true;
+ }
+ });
+ app.on('window-all-closed', () => {
+ console.log('window-all-closed');
+ app.quit();
+ });
+ app.on('web-contents-created', (event, webContents) => {
+ console.log('web-contents-created');
+ if (wrt.tv)
+ this.setCookiePath();
+ webContents.on('before-input-event', (event, input) => {
+ if (this.isLaunched && this.webApplication)
+ this.handleKeyEvents(input.key);
+ });
+ });
+ app.once('ready', (event) => {
+ console.log('ready');
+ let addonAvailable = addonManager.build();
+ console.log("addonBuild : " + addonAvailable);
+ if (addonAvailable) {
+ const XWalkExtension = require('../common/wrt_xwalk_extension');
+ XWalkExtension.initialize();
+ XWalkExtension.preventCleanup();
+ }
+ if (wrt.tv) {
+ wrt.tv.importCertificate('');
+ wrt.tv.optimizeCache();
+ wrt.tv.clearDeadMount();
+ }
+ });
+ wrt.on('app-control', (event, appControl) => {
+ console.log('app-control');
+ let loadInfo = appControl.getLoadInfo();
+ let src = loadInfo.getSrc();
+
+ if (wrt.isElectronApp()) {
+ console.log('Electron App launch');
+ const Module = require('module');
+ Module.globalPaths.push(wrt.getAppPath());
+ let filePath = src[7] === '/' ? src.substr(8) : src.substr(7); // strip "file://"
+ let pkgJson = require(filePath);
+ let pos = filePath.lastIndexOf('/');
+
+ let mainJsPath = (pos !== -1 ? filePath.substr(0, pos + 1) : '') +
+ (pkgJson.main || 'index.js');
+ console.log('loading path:', mainJsPath);
+ Module._load(mainJsPath, Module, true);
+ app.emit('ready');
+ } else {
+ console.log('Tizen Web App launch');
+ let launchMode = appControl.getData('http://samsung.com/appcontrol/data/launch_mode');
+ if (!this.webApplication) {
+ console.log('Creating WebApplication');
+ let options: RuntimeOption = {
+ isAddonAvailable: addonManager.isAddonAvailable(),
+ launchMode: launchMode
+ }
+ this.webApplication = new WebApplication(options);
+ if (wrt.tv) {
+ this.inspectorEnabledByVconf = wrt.tv.needUseInspector();
+ if (this.inspectorEnabledByVconf && launchMode != 'backgroundExecution') {
+ this.webApplication.inspectorSrc = src;
+ src = "about:blank";
+ }
+ let useDiskCache = appControl.getData('USE_DISKCACHE');
+ let halfWindowOption = appControl.getData('http://samsung.com/appcontrol/data/half_window_support');
+ wrt.tv.setDiskCache(useDiskCache);
+ wrt.tv.handleAppControlData(launchMode, halfWindowOption);
+ }
+ this.webApplication.mainWindow.loadURL(src);
+ this.webApplication.prelaunch(src);
+ } else {
+ console.log('Handling app-control event');
+ if (this.webApplication.preloadStatus == 'readyToShow') {
+ this.webApplication.show();
+ } else {
+ if (launchMode != 'backgroundAtStartup')
+ this.webApplication.preloadStatus = 'none';
+ }
+
+ 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;
+ }
+
+ let reload = loadInfo.getReload() || this.webApplication.isAlwaysReload;
+ if (!reload) {
+ let originalUrl = this.webApplication.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;
+ }
+ }
+ // 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.
+ if (reload && appControl.getOperation() == 'http://tizen.org/appcontrol/operation/main')
+ reload = false;
+ if (reload)
+ this.webApplication.handleAppControlReload(src);
+ else
+ this.webApplication.sendAppControlEvent();
+ }
+ }
+ this.setCookiePath();
+ this.launchInspector(appControl);
+ });
+ wrt.on('suspend', () => {
+ console.log('suspend');
+ this.webApplication?.suspend();
+ });
+ wrt.on('resume', () => {
+ console.log('resume');
+ this.webApplication?.resume();
+ });
+ wrt.on('low-memory', () => {
+ console.log('low-memory');
+ this.webApplication?.lowMemory();
+ });
+ wrt.on('message', (event, type, params) => {
+ console.log('message type(' + type + ') params : ' + params);
+ const app_id = params[0];
+ if (type === 'startService') {
+ require('../common/service_manager').startService(app_id, params[1]);
+ event.preventDefault();
+ } else if (type === 'stopService') {
+ require('../common/service_manager').stopService(app_id);
+ event.preventDefault();
+ }
+ });
+ wrt.on('ambient-tick', () => {
+ this.webApplication?.ambientTick();
+ });
+ wrt.on('ambient-changed', (event, ambient_mode) => {
+ console.log('ambient-changed , ambient_mode:' + ambient_mode);
+ this.webApplication?.ambientChanged(ambient_mode);
+ });
+ wrt.on('addon-installed', (event, path) => {
+ console.log('addon-installed at ' + path);
+ addonManager.checkAddon(path);
+ });
+ wrt.on('addon-uninstalled', (event, id) => {
+ console.log('addon-unistalled named ' + id);
+ });
+ wrt.on('wgt-checking-done', (event) => {
+ console.log('wgt-checking-done');
+ addonManager.updateDB();
+ });
+
+ /* FIXME: will uncheck after chromium-efl released */
+ if (wrt.getPlatformType() !== "product_tv")
+ wrt.getInstalledPkg();
+ }
+
+ private launchInspector(appControl: NativeWRTjs.AppControl) {
+ this.launchInspector = (param) => {}; // call once
+ console.log('launchInspector');
+
+ // AUL public key/Vconf - To support inspector
+ if (this.checkInspectorCondition(appControl)) {
+ let debugPort = wrt.startInspectorServer();
+ let data = { "port" : [ debugPort.toString() ] };
+ if (this.webApplication)
+ this.webApplication.debugPort = debugPort;
+ appControl.reply(data);
+ }
+ }
+
+ private checkInspectorCondition(appControl: NativeWRTjs.AppControl) {
+ let bundleDebug = (appControl.getData('__AUL_DEBUG__') === "1");
+ return (bundleDebug || this.inspectorEnabledByVconf);
+ }
+
+ private setCookiePath() {
+ this.setCookiePath = () => {}; // call once
+ console.log('setCookiePath');
+
+ // FIX ME : It must be supplemented to set a specific path
+ wrt.setCookiePath();
+ }
+
+ private handleKeyEvents(key: string) {
+ let valid = false;
+ console.log(key + ' is pressed');
+ switch(key) {
+ case "ArrowUp":
+ case "Up":
+ case "ArrowDown":
+ case "Down":
+ valid = true;
+ break;
+ default:
+ console.log('No handler for the key ' + key);
+ break;
+ }
+ if (valid)
+ this.webApplication?.keyEvent(key);
+ }
+}
+
+new Runtime();
+++ /dev/null
-/*
- * Copyright (c) 2019 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';
-
-const { app, protocol } = require('electron');
-const wrt = require('../browser/wrt');
-const WRTWebContents = require('../browser/wrt_web_contents');
-const WRTWindow = require('../browser/wrt_window');
-
-class WebApplication {
- constructor(options) {
- this.initialize(options);
- this.createMainWindow(options);
- }
- initialize(options) {
- this.pendingID = 0;
- this.pendingCallbacks = new Map();
- this.windowList = [];
- this.backgroundSupport = wrt.getBackgroundSupport();
- this.debugPort = 0;
- this.inspectorSrc = '';
- 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.accessiblePath = (wrt.tv ? wrt.tv.getAccessiblePath() : null);
- 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.loadFinished = false;
- this.runningStatus = 'none';
- this.addonEmitter = null;
-
- let self = this;
- app.on('browser-window-created', function(event, window) {
- if (self.windowList.length > 0)
- self.windowList[self.windowList.length - 1].hide();
- self.windowList.push(window);
- console.log(`window created : #${self.windowList.length}`);
-
- window.on('closed', function() {
- console.log(`window closed : #${self.windowList.length}`);
- let index = self.windowList.indexOf(window);
- self.windowList.splice(index, 1);
- if (index === self.windowList.length && self.windowList.length > 0)
- self.windowList[self.windowList.length - 1].show();
- });
- });
- app.on('web-contents-created', function(event, webContents) {
- webContents.on('crashed', function() {
- console.error('webContents crashed');
- app.exit(100);
- });
- webContents.session.setPermissionRequestHandler(function(webContents, permission, callback) {
- console.log(`handlePermissionRequests for ${permission}`);
- if (permission === 'notifications') {
- if (!self.notificationPermissionMap)
- self.notificationPermissionMap = new Map();
- else if (self.notificationPermissionMap.has(webContents)) {
- process.nextTick(callback, self.notificationPermissionMap.get(webContents));
- return;
- }
- const id = ++self.pendingID;
- console.log(`Raising a notification permission request with id: ${id}`);
- self.pendingCallbacks.set(id, (result) => {
- self.notificationPermissionMap.set(webContents, result);
- callback(result);
- });
- wrt.handleNotificationPermissionRequest(id, webContents);
- } else if (permission === 'media') {
- const id = ++self.pendingID;
- console.log(`Raising a media permission request with id: ${id}`);
- self.pendingCallbacks.set(id, callback);
- wrt.handleMediaPermissionRequest(id, webContents);
- } else if (permission === 'geolocation') {
- const id = ++self.pendingID;
- console.log(`Raising a geolocation permission request with id: ${id}`);
- self.pendingCallbacks.set(id, callback);
- wrt.handleGeolocationPermissionRequest(id, webContents);
- } else {
- /* electron by default allows permission for all if no request handler
- is there; so granting permission only temporarily to not have any
- side effects */
- callback(true);
- }
- });
- });
- app.on('certificate-error', function(event, webContents, url, error, certificate, callback) {
- console.log('A certificate error has occurred');
- event.preventDefault();
- if (certificate.data) {
- const id = ++self.pendingID;
- console.log(`Raising a certificate error response with id: ${id}`);
- self.pendingCallbacks.set(id, callback);
- wrt.handleCertificateError(id, webContents, certificate.data, url, error);
- } else {
- console.log('Certificate could not be opened');
- callback(false);
- }
- });
- app.on('login', function(event, webContents, request, authInfo, callback) {
- 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 {
- const id = ++self.pendingID;
- console.log(`Raising a login info request with id: ${id}`);
- self.pendingCallbacks.set(id, callback);
- wrt.handleAuthRequest(id, webContents);
- }
- });
- if (this.accessiblePath) {
- console.log(`accessiblePath: ${this.accessiblePath}`);
- protocol.interceptFileProtocol('file', (request, callback) => {
- if (request.url) {
- let access_path, parsed_info = new URL(request.url);
- access_path = parsed_info.host + decodeURI(parsed_info.pathname);
- console.log(`check path: : ${access_path}`);
- for (let p in self.accessiblePath) {
- if (access_path.startsWith(self.accessiblePath[p])) {
- callback(access_path);
- return;
- }
- }
- if (access_path.indexOf("/shared/res/") > -1) {
- callback(access_path);
- return;
- }
- else {
- console.log(`invalid accesspath: ${access_path}`);
- callback(403);
- }
- } else {
- console.log('request url is empty');
- callback(403);
- }
- }, (error) => {
- console.log(error);
- });
- }
- wrt.on('permission-response', function(event, id, result) {
- console.log(`permission-response for ${id} is ${result}`);
- let callback = self.pendingCallbacks.get(id);
- if (typeof callback === 'function') {
- console.log('calling permission response callback');
- callback(result);
- self.pendingCallbacks.delete(id);
- }
- });
- wrt.on('auth-response', function(event, id, submit, user, passwd) {
- let callback = self.pendingCallbacks.get(id);
- if (typeof callback === 'function') {
- console.log('calling auth response callback');
- if (submit) {
- callback(user, passwd);
- } else {
- callback();
- }
- self.pendingCallbacks.delete(id);
- }
- });
- wrt.on('app-status-changed', function(event, status) {
- console.log(`runningStatus: ${status}, ${self.loadFinished}`);
- if (!wrt.tv) {
- return;
- }
- self.runningStatus = status;
- if (self.runningStatus === 'DialogClose' && self.inspectorSrc) {
- console.log(`runningStatus is DialogClose, src is ${self.inspectorSrc}`);
- self.mainWindow.loadURL(self.inspectorSrc);
- self.inspectorSrc = '';
- } else if (self.runningStatus == 'behind' && self.loadFinished) {
- // TODO : Need to care this situation and decide to pass the addon event emitter to suspend()
- self.suspend();
- }
- });
- }
- backgroundRunnable() {
- return this.backgroundSupport || this.backgroundExecution;
- }
- getWindowOption(options) {
- return {
- fullscreen: false,
- backgroundColor: this.defaultBackgroundColor,
- transparent: this.defaultTransparent,
- show: false,
- webPreferences: {
- nodeIntegration: options.isAddonAvailable,
- nodeIntegrationInWorker: false
- },
- webContents: WRTWebContents.create(),
- 'web-preferences': {
- 'direct-write': true,
- 'subpixel-font-scaling': false,
- 'web-security': false
- }
- };
- }
- createMainWindow(options) {
- let winopt = this.getWindowOption(options);
- this.mainWindow = new WRTWindow(winopt);
- if (options.devMode) {
- this.mainWindow.webContents.openDevTools({
- detached: true
- });
- }
- this.initDisplayDelay(true);
- let self = this;
- this.mainWindow.once('ready-to-show', function() {
- console.log('mainWindow ready-to-show');
- if (self.showTimer)
- clearTimeout(self.showTimer);
- wrt.hideSplashScreen(0);
- self.firstRendered = true;
- if (self.preloadStatus == 'preload') {
- self.preloadStatus = 'readyToShow';
- console.log('preloading show is skipped!');
- return;
- }
- self.show();
- });
- this.mainWindow.webContents.on('did-start-loading', function() {
- console.log('webContents did-start-loading');
- self.loadFinished = false;
- });
- this.mainWindow.webContents.on('did-finish-load', function() {
- console.log('webContents did-finish-load');
- self.loadFinished = true;
- wrt.hideSplashScreen(1);
- if (wrt.isIMEWebApp()) {
- self.activateIMEWebHelperClient();
- } else if (wrt.tv) {
- if (self.inspectorSrc) {
- self.showInspectorGuide();
- } else {
- self.suspendByStatus();
- }
- }
- self.addonEmitter.emit('contentDidFinishLoad', self.mainWindow.id);
- });
- }
- initDisplayDelay(firstLaunch) {
- // TODO: On 6.0, this causes a black screen on relaunch
- if (firstLaunch)
- this.firstRendered = false;
- this.suspended = false;
- if (this.showTimer)
- clearTimeout(this.showTimer);
- let splashShown = firstLaunch && wrt.showSplashScreen();
- if (!splashShown && !wrt.tv) {
- let self = this;
- self.showTimer = setTimeout(() => {
- if (!self.suspended) {
- console.log('FrameRendered not obtained from engine. To show window, timer fired');
- self.mainWindow.emit('ready-to-show');
- }
- }, 2000);
- }
- if (!firstLaunch && !this.backgroundRunnable())
- this.mainWindow.setEnabled(true);
- }
- handleAppControlReload(src) {
- console.log('WebApplication : handleAppControlReload');
- this.closeWindows();
- this.initDisplayDelay(false);
- this.mainWindow.loadURL(src);
- }
- 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.notifyAppStatus('preload');
- }
- }
- showInspectorGuide() {
- console.log('WebApplication : showInspectorGuide');
- this.showInspectorGuide = () => {}; // call once
- let message = this.debugPort.toString() +
- "\r\nFast RWI is used, [about:blank] is loaded fist instead of \r\n[" +
- this.inspectorSrc +
- "]\r\nClick OK button will start the real loading.\r\nNotes:\r\nPlease " +
- "connect to RWI in PC before click OK button.\r\nThen you can get " +
- "network log from the initial loading.\r\nPlease click Record button " +
- "in Timeline panel in PC before click OK button,\r\nThen you can get " +
- "profile log from the initial loading."
- wrt.tv.showDialog(this.mainWindow.webContents, message);
-
- if (this.preloadStatus !== 'none') {
- setTimeout(() => {
- wrt.tv.cancelDialogs(this.mainWindow.webContents);
- }, 5000);
- }
- }
- suspend() {
- console.log('WebApplication : suspend');
- if (this.addonEmitter) {
- console.log('WebApplication : suspend - Found event emitter');
- this.addonEmitter.emit('lcSuspend', this.mainWindow.id);
- } else {
- console.log('WebApplication : suspend - Invalid event emitter');
- }
- this.suspended = true;
- this.windowList[this.windowList.length - 1].hide();
- 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);
- } else {
- this.windowList.forEach((window) => window.setEnabled(false));
- }
- }
- }
- resume() {
- console.log('WebApplication : resume');
- this.suspended = false;
- if (this.addonEmitter) {
- console.log('WebApplication : resume - Found event emitter');
- this.addonEmitter.emit('lcResume', this.mainWindow.id);
- } else {
- console.log('WebApplication : resume - Invalid event emitter');
- }
-
- if (!this.firstRendered) {
- console.log('WebApplication : resume firstRendered is false');
- return;
- }
- if (!this.backgroundRunnable()) {
- this.windowList.forEach((window) => window.setEnabled(true));
- }
- this.windowList[this.windowList.length - 1].show();
- }
- finalize() {
- console.log('WebApplication : finalize');
- if (wrt.tv) {
- this.inspectorSrc = '';
- wrt.tv.cancelDialogs(this.mainWindow.webContents);
- }
- this.flushData();
- if (this.debugPort) {
- console.log('stop inspector server');
- this.debugPort = 0;
- wrt.stopInspectorServer();
- }
- this.windowList.forEach((window) => {
- window.removeAllListeners();
- });
- }
- quit() {
- console.log('WebApplication : quit');
- if (this.addonEmitter) {
- console.log('WebApplication : quit - Found event emitter');
- this.addonEmitter.emit('lcQuit', this.mainWindow.id);
- } else {
- console.log('WebApplication : quit - Invalid event emitter');
- }
- }
- flushData() {
- console.log('WebApplication : FlushData');
- if (wrt.tv) {
- wrt.tv.flushCookie();
- this.windowList.forEach((window) => window.webContents.session.flushStorageData());
- }
- }
- sendAppControlEvent() {
- const kAppControlEventScript =
- '(function(){' +
- 'var __event = document.createEvent("CustomEvent");' +
- '__event.initCustomEvent("appcontrol", true, true, null);' +
- 'document.dispatchEvent(__event);' +
- 'for (var i=0; i < window.frames.length; i++)' +
- 'window.frames[i].document.dispatchEvent(__event);' +
- '})()';
- wrt.executeJS(this.mainWindow.webContents, kAppControlEventScript);
- }
- activateIMEWebHelperClient() {
- console.log('webApplication : activateIMEWebHelperClient');
- const kImeActivateFunctionCallScript =
- '(function(){WebHelperClient.impl.activate();})()';
- wrt.executeJS(this.mainWindow.webContents, kImeActivateFunctionCallScript);
- }
- show() {
- console.log('WebApplication : show');
- this.preloadStatus = 'none';
- if (this.backgroundExecution) {
- console.log('skip showing while backgroundExecution mode');
- } else if (!this.mainWindow.isVisible()) {
- console.log('show window');
- this.mainWindow.show();
- }
- }
- closeWindows() {
- if (wrt.tv)
- wrt.tv.clearSurface(this.mainWindow.webContents);
- this.windowList.forEach((window) => {
- if (window != this.mainWindow)
- window.destroy();
- });
- }
- keyEvent(key) {
- console.log('WebApplication : keyEvent');
- if (!this.addonEmitter) {
- console.log('Invalid event emitter for key hook');
- return;
- }
- console.log('key event is ' + key);
- switch(key) {
- case "ArrowUp":
- case "Up":
- this.addonEmitter.emit('hwUpkey', this.mainWindow.id);
- break;
- case "ArrowDown":
- case "Down":
- this.addonEmitter.emit('hwDownkey', this.mainWindow.id);
- break;
- default:
- console.log('No handler for ' + key);
- break;
- }
- }
- prelaunch(origURL) {
- console.log('WebApplication : prelaunch');
- if (this.addonEmitter) {
- console.log('WebApplication : prelaunch - Found event emitter');
- this.addonEmitter.emit('lcPrelaunch', this.mainWindow.id, origURL);
- } else {
- console.log('WebApplication : prelaunch - Invalid event emitter');
- }
- }
- lowMemory() {
- console.log('WebApplication : lowMemory to clearcache');
- if (wrt.tv) {
- this.windowList.forEach((window) => {
- //clear webframe cache
- wrt.tv.clearWebCache(window.webContents);
- window.webContents.session.clearCache(function() {
- console.log('clear session Cache complete');
- })
- });
- }
- }
- ambientTick() {
- const kAmbientTickEventScript =
- '(function(){' +
- 'var __event = document.createEvent(\"CustomEvent\");' +
- '__event.initCustomEvent(\"timetick\", true, true);' +
- 'document.dispatchEvent(__event);' +
- 'for (var i=0; i < window.frames.length; i++)' +
- '{ window.frames[i].document.dispatchEvent(__event); }' +
- '})()';
- wrt.executeJS(this.mainWindow.webContents, kAmbientTickEventScript);
- }
- ambientChanged(ambient_mode) {
- const kAmbientChangedEventScript =
- '(function(){' +
- 'var __event = document.createEvent(\"CustomEvent\");' +
- 'var __detail = {};' +
- '__event.initCustomEvent(\"ambientmodechanged\",true,true,__detail);' +
- '__event.detail.ambientMode = ' +
- (ambient_mode ? 'true' : 'false') + ';' +
- 'document.dispatchEvent(__event);' +
- 'for (var i=0; i < window.frames.length; i++)' +
- '{ window.frames[i].document.dispatchEvent(__event); }' +
- '})()';
- wrt.executeJS(this.mainWindow.webContents, kAmbientChangedEventScript);
- }
-}
-module.exports = WebApplication;
--- /dev/null
+/*
+ * Copyright (c) 2019 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 { app, protocol } 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';
+
+export class WebApplication {
+ accessiblePath?: string[];
+ backgroundExecution: boolean;
+ defaultBackgroundColor: string;
+ defaultTransparent: boolean;
+ isAlwaysReload: boolean;
+ mainWindow: Electron.BrowserWindow;
+ multitaskingSupport: boolean;
+ notificationPermissionMap?: Map<Electron.WebContents, boolean>;
+ preloadStatus: string;
+ showTimer?: NodeJS.Timeout;
+
+ backgroundSupport = wrt.getBackgroundSupport();
+ debugPort = 0;
+ firstRendered = false;
+ inspectorSrc = '';
+ loadFinished = false;
+ pendingCallbacks: Map<number, any> = new Map();
+ pendingID = 0;
+ runningStatus = 'none';
+ suspended = false;
+ windowList: Electron.BrowserWindow[] = [];
+
+ 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;
+ } else {
+ 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'));
+ this.defaultTransparent = (wrt.tv ? true : false);
+
+ this.setupEventListener(options);
+
+ this.mainWindow = new WRTWindow(this.getWindowOption(options));
+ this.initDisplayDelay(true);
+ this.setupMainWindowEventListener();
+ }
+
+ private setupEventListener(options: RuntimeOption) {
+ app.on('browser-window-created', (event, window) => {
+ if (this.windowList.length > 0)
+ this.windowList[this.windowList.length - 1].hide();
+ this.windowList.push(window);
+ console.log(`window created : #${this.windowList.length}`);
+
+ window.on('closed', () => {
+ console.log(`window closed : #${this.windowList.length}`);
+ let index = this.windowList.indexOf(window);
+ this.windowList.splice(index, 1);
+ if (index === this.windowList.length && this.windowList.length > 0)
+ this.windowList[this.windowList.length - 1].show();
+ });
+ });
+ app.on('web-contents-created', (event, webContents) => {
+ webContents.on('crashed', function() {
+ console.error('webContents crashed');
+ app.exit(100);
+ });
+ webContents.session.setPermissionRequestHandler((webContents, permission, callback) => {
+ console.log(`handlePermissionRequests for ${permission}`);
+ if (permission === 'notifications') {
+ if (!this.notificationPermissionMap)
+ this.notificationPermissionMap = new Map();
+ else if (this.notificationPermissionMap.has(webContents)) {
+ process.nextTick(callback, this.notificationPermissionMap.get(webContents));
+ return;
+ }
+ const id = ++this.pendingID;
+ console.log(`Raising a notification permission request with id: ${id}`);
+ this.pendingCallbacks.set(id, (result: boolean) => {
+ (this.notificationPermissionMap as Map<Electron.WebContents, boolean>).set(webContents, result);
+ callback(result);
+ });
+ wrt.handleNotificationPermissionRequest(id, webContents);
+ } else if (permission === 'media') {
+ const id = ++this.pendingID;
+ console.log(`Raising a media permission request with id: ${id}`);
+ this.pendingCallbacks.set(id, callback);
+ wrt.handleMediaPermissionRequest(id, webContents);
+ } else if (permission === 'geolocation') {
+ const id = ++this.pendingID;
+ console.log(`Raising a geolocation permission request with id: ${id}`);
+ this.pendingCallbacks.set(id, callback);
+ wrt.handleGeolocationPermissionRequest(id, webContents);
+ } else {
+ /* electron by default allows permission for all if no request handler
+ is there; so granting permission only temporarily to not have any
+ side effects */
+ callback(true);
+ }
+ });
+ });
+ app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
+ console.log('A certificate error has occurred');
+ event.preventDefault();
+ if (certificate.data) {
+ const id = ++this.pendingID;
+ console.log(`Raising a certificate error response with id: ${id}`);
+ this.pendingCallbacks.set(id, callback);
+ wrt.handleCertificateError(id, webContents, certificate.data, url, error);
+ } else {
+ console.log('Certificate could not be opened');
+ callback(false);
+ }
+ });
+ app.on('login', (event, webContents, request, authInfo, callback) => {
+ 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 {
+ const id = ++this.pendingID;
+ console.log(`Raising a login info request with id: ${id}`);
+ this.pendingCallbacks.set(id, callback);
+ wrt.handleAuthRequest(id, webContents);
+ }
+ });
+ if (this.accessiblePath) {
+ console.log(`accessiblePath: ${this.accessiblePath}`);
+ protocol.interceptFileProtocol('file', (request, callback) => {
+ 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) => {
+ console.log(error);
+ });
+ }
+ wrt.on('permission-response', (event, id, result) => {
+ console.log(`permission-response for ${id} is ${result}`);
+ let callback = this.pendingCallbacks.get(id);
+ if (typeof callback === 'function') {
+ console.log('calling permission response callback');
+ callback(result);
+ this.pendingCallbacks.delete(id);
+ }
+ });
+ wrt.on('auth-response', (event, id, submit, user, password) => {
+ let callback = this.pendingCallbacks.get(id);
+ if (typeof callback === 'function') {
+ console.log('calling auth response callback');
+ if (submit)
+ callback(user, password);
+ else
+ callback();
+ this.pendingCallbacks.delete(id);
+ }
+ });
+ wrt.on('app-status-changed', (event, status) => {
+ 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 {
+ return {
+ fullscreen: false,
+ backgroundColor: this.defaultBackgroundColor,
+ transparent: this.defaultTransparent,
+ show: false,
+ webPreferences: {
+ nodeIntegration: options.isAddonAvailable,
+ nodeIntegrationInWorker: false
+ },
+ webContents: WRTWebContents.create(),
+ };
+ }
+
+ private setupMainWindowEventListener() {
+ this.mainWindow.once('ready-to-show', () => {
+ console.log('mainWindow ready-to-show');
+ if (this.showTimer)
+ clearTimeout(this.showTimer);
+ wrt.hideSplashScreen(0);
+ this.firstRendered = true;
+ if (this.preloadStatus == 'preload') {
+ this.preloadStatus = 'readyToShow';
+ console.log('preloading show is skipped!');
+ return;
+ }
+ this.show();
+ });
+ this.mainWindow.webContents.on('did-start-loading', () => {
+ console.log('webContents did-start-loading');
+ this.loadFinished = false;
+ });
+ this.mainWindow.webContents.on('did-finish-load', () => {
+ console.log('webContents did-finish-load');
+ this.loadFinished = true;
+ wrt.hideSplashScreen(1);
+ if (wrt.isIMEWebApp()) {
+ this.activateIMEWebHelperClient();
+ } else if (wrt.tv) {
+ if (this.inspectorSrc)
+ this.showInspectorGuide();
+ else
+ this.suspendByStatus();
+ }
+ addonManager.emit('contentDidFinishLoad', this.mainWindow.id);
+ });
+ }
+
+ private initDisplayDelay(firstLaunch: boolean) {
+ // TODO: On 6.0, this causes a black screen on relaunch
+ if (firstLaunch)
+ this.firstRendered = false;
+ this.suspended = false;
+ if (this.showTimer)
+ clearTimeout(this.showTimer);
+ let splashShown = 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())
+ this.mainWindow.setEnabled(true);
+ }
+
+ private backgroundRunnable(): boolean {
+ return this.backgroundSupport || this.backgroundExecution;
+ }
+
+ handleAppControlReload(url: string) {
+ console.log('WebApplication : handleAppControlReload');
+ this.closeWindows();
+ this.initDisplayDelay(false);
+ this.mainWindow.loadURL(url);
+ }
+
+ 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);
+ }
+ }
+
+ suspend() {
+ console.log('WebApplication : suspend');
+ addonManager.emit('lcSuspend', this.mainWindow.id);
+ this.suspended = true;
+ this.windowList[this.windowList.length - 1].hide();
+ 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);
+ } else {
+ this.windowList.forEach((window) => window.setEnabled(false));
+ }
+ }
+ }
+
+ resume() {
+ console.log('WebApplication : resume');
+ this.suspended = false;
+ addonManager.emit('lcResume', this.mainWindow.id);
+
+ if (!this.firstRendered) {
+ console.log('WebApplication : resume firstRendered is false');
+ return;
+ }
+ if (!this.backgroundRunnable())
+ this.windowList.forEach((window) => window.setEnabled(true));
+ this.windowList[this.windowList.length - 1].show();
+ }
+
+ finalize() {
+ console.log('WebApplication : finalize');
+ if (wrt.tv) {
+ this.inspectorSrc = '';
+ wrt.tv.cancelDialogs(this.mainWindow.webContents);
+ }
+ this.flushData();
+ if (this.debugPort) {
+ console.log('stop inspector server');
+ this.debugPort = 0;
+ wrt.stopInspectorServer();
+ }
+ this.windowList.forEach((window) => window.removeAllListeners());
+ }
+
+ quit() {
+ console.log('WebApplication : quit');
+ addonManager.emit('lcQuit', this.mainWindow.id);
+ }
+
+ private flushData() {
+ console.log('WebApplication : FlushData');
+ if (wrt.tv) {
+ wrt.tv.flushCookie();
+ this.windowList.forEach((window) => window.webContents.session.flushStorageData());
+ }
+ }
+
+ sendAppControlEvent() {
+ const kAppControlEventScript = `(function(){
+ var __event = document.createEvent("CustomEvent");
+ __event.initCustomEvent("appcontrol", true, true, null);
+ document.dispatchEvent(__event);
+ for (var i=0; i < window.frames.length; i++)
+ window.frames[i].document.dispatchEvent(__event);
+})()`;
+ wrt.executeJS(this.mainWindow.webContents, kAppControlEventScript);
+ }
+
+ private activateIMEWebHelperClient() {
+ console.log('webApplication : activateIMEWebHelperClient');
+ const kImeActivateFunctionCallScript =
+ '(function(){WebHelperClient.impl.activate();})()';
+ wrt.executeJS(this.mainWindow.webContents, kImeActivateFunctionCallScript);
+ }
+
+ show() {
+ console.log('WebApplication : show');
+ this.preloadStatus = 'none';
+ if (this.backgroundExecution) {
+ console.log('skip showing while backgroundExecution mode');
+ } else if (!this.mainWindow.isVisible()) {
+ console.log('show window');
+ this.mainWindow.show();
+ }
+ }
+
+ private closeWindows() {
+ wrt.tv?.clearSurface(this.mainWindow.webContents);
+ this.windowList.forEach((window) => {
+ if (window != this.mainWindow)
+ window.destroy();
+ });
+ }
+
+ keyEvent(key: string) {
+ console.log(`WebApplication : keyEvent[${key}]`);
+ switch(key) {
+ case "ArrowUp":
+ case "Up":
+ addonManager.emit('hwUpkey', this.mainWindow.id);
+ break;
+ case "ArrowDown":
+ case "Down":
+ addonManager.emit('hwDownkey', this.mainWindow.id);
+ break;
+ default:
+ console.log('No handler for ' + key);
+ break;
+ }
+ }
+
+ prelaunch(url: string) {
+ console.log('WebApplication : prelaunch');
+ 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');
+ })
+ });
+ }
+
+ ambientTick() {
+ const kAmbientTickEventScript = `(function(){
+ var __event = document.createEvent("CustomEvent");
+ __event.initCustomEvent("timetick", true, true);
+ document.dispatchEvent(__event);
+ for (var i=0; i < window.frames.length; i++)
+ window.frames[i].document.dispatchEvent(__event);
+})()`;
+ wrt.executeJS(this.mainWindow.webContents, kAmbientTickEventScript);
+ }
+
+ ambientChanged(ambient_mode: boolean) {
+ const kAmbientChangedEventScript = `(function(){
+ var __event = document.createEvent(\"CustomEvent\");
+ var __detail = {};
+ __event.initCustomEvent(\"ambientmodechanged\",true,true,__detail);
+ __event.detail.ambientMode = ${ambient_mode ? 'true' : 'false'};
+ document.dispatchEvent(__event);
+ for (var i=0; i < window.frames.length; i++)
+ window.frames[i].document.dispatchEvent(__event);
+})()`;
+ wrt.executeJS(this.mainWindow.webContents, kAmbientChangedEventScript);
+ }
+}
--- /dev/null
+interface RuntimeOption {
+ launchMode: string;
+ isAddonAvailable: boolean;
+}
+
+interface Console {
+ logd(message?: any, ...optionalParams: any[]): void;
+ logv(message?: any, ...optionalParams: any[]): void;
+ loge(message?: any, ...optionalParams: any[]): void;
+}
+
+declare namespace NodeJS {
+ interface Process extends EventEmitter {
+ _linkedBinding(name: string): any;
+ binding(name: string): any;
+ wrtBinding(name: string): any;
+ }
+}
+
+declare namespace NodeJS {
+ interface Global {
+ [key: string]: any;
+ }
+}