Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

9
gaia/build/.jshintrc Executable file
View file

@ -0,0 +1,9 @@
{
"extends": "../.jshintrc",
"predef": [
"suiteSetup",
"suiteTeardown"
],
"node": true,
"mocha": true
}

View file

@ -0,0 +1,354 @@
'use strict';
/* global require, exports, Promise */
const { Cc, Ci, Cu} = require('chrome');
Cu.import('resource://gre/modules/FileUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/Promise.jsm');
var utils = require('./utils');
var dm = require('./download-manager');
var config;
var gaiaDir;
var stageDir;
var AMO_URL =
'https://services.addons.mozilla.org/en-US/firefox/api/1.5/addon/';
function logLine(msg) {
utils.log('additional-extensions', msg);
}
var InstallationManager = {
// utils.processEvents tends to end thread earlier than
// the time we start downloading extensions,
// because we asynchonously get correct urls to download from.
// So we add this flag to prevent thread ended too early.
_processing: false,
_onTheFly: [],
_downloadedExtensions: {},
_callbacksOnAllFinished: [],
_removeUrl: function im_remove_url(url) {
var index = this._onTheFly.indexOf(url);
if (index > -1) {
this._onTheFly.splice(index, 1);
}
if (this._onTheFly.length > 0) {
this._processing = true;
} else {
while (this._callbacksOnAllFinished.length > 0) {
var cb = this._callbacksOnAllFinished.pop();
cb(this._downloadedExtensions);
}
this._processing = false;
}
},
_getExtensionIdFromInstallRdf:
function im_get_extension_id_from_install_rdf(doc) {
var elementsOfNameOfEmId = doc.getElementsByTagName('em:id');
var result;
var index = 0;
var elem;
for (index = 0; index < elementsOfNameOfEmId.length; index += 1) {
elem = elementsOfNameOfEmId[index];
if (elem.parentNode && elem.parentNode.tagName === 'Description') {
result = elem.innerHTML;
break;
}
}
if (!result) {
throw new Error('No extension ID found');
} else {
return result;
}
},
_getUnpackFromInstallRdf: function im_get_unpack_from_install_rdf(doc) {
var elems = doc.getElementsByTagName('em:unpack');
var result = false;
if (elems && elems.length > 0) {
result = doc.getElementsByTagName('em:unpack')[0].innerHTML === 'true';
}
return result;
},
_installFromUrl: function im_install_from_url(sourceUrl, filePath) {
var zipReader;
var file;
var sourceFileName;
var sourceUrlTokens;
var extractedDir;
var installRdf;
var installRdfContent;
var extensionId;
var unpack;
var entryEnumerator;
var extensionDir;
sourceUrlTokens = sourceUrl.split('/');
sourceFileName = sourceUrlTokens[sourceUrlTokens.length - 1];
zipReader =
Cc['@mozilla.org/libjar/zip-reader;1'].createInstance(Ci.nsIZipReader);
file = utils.getFile(filePath);
zipReader.open(file);
extractedDir = utils.getTempFolder(sourceFileName);
installRdf = utils.getFile(extractedDir.path, 'install.rdf');
// peek install.rdf and look for what we need: extionsion id and unpack
zipReader.extract('install.rdf', installRdf);
installRdfContent = utils.getXML(installRdf);
extensionId =
InstallationManager._getExtensionIdFromInstallRdf(installRdfContent);
unpack =
InstallationManager._getUnpackFromInstallRdf(installRdfContent);
if (unpack) {
// case 1: extract content of xpi file to
// build_stage/additional-extensions/<extensionId>/
extensionDir =
utils.getFile(stageDir, 'additional-extensions', extensionId);
utils.ensureFolderExists(extensionDir);
entryEnumerator = zipReader.findEntries('*');
while (entryEnumerator.hasMore()) {
try {
var zipEntryName = entryEnumerator.getNext();
var zipEntry = zipReader.getEntry(zipEntryName);
var targetFile = utils.getFile(
stageDir, 'additional-extensions', extensionId, zipEntryName);
var targetFileType = zipEntry.isDirectory ?
Ci.nsIFile.DIRECTORY_TYPE : Ci.nsIFile.NORMAL_FILE_TYPE;
if (!targetFile.exists()) {
targetFile.create(targetFileType, parseInt('0644', 8));
zipReader.extract(zipEntryName, targetFile);
}
} catch (e) {
logLine('Error extracting ' + zipEntryName + ' due to ' + e.message);
}
}
} else {
// case 2: put xpi file directly at build+stage/additional-extensions
extensionDir = utils.getFile(stageDir, 'additional-extensions');
utils.ensureFolderExists(extensionDir);
file.copyTo(extensionDir, extensionId + '.xpi');
}
logLine(sourceUrl + ' downloaded');
zipReader.close();
InstallationManager.finish(sourceUrl);
},
init: function im_init(downloadedExtensions) {
if (downloadedExtensions) {
this._downloadedExtensions = downloadedExtensions;
}
this._processing = true;
},
startInstalling: function im_start_installing(url) {
var self = this;
if (self._onTheFly.indexOf(url) < 0) {
self._onTheFly.push(url);
}
dm.getDownloadManager().download(
url, null, null, self._installFromUrl,
function(url) {
logLine('error downloading ' + url);
self.failOrCancel(url);
});
},
failOrCancel: function im_failOrCancel() {
this._removeUrl();
},
finish: function im_finish(url) {
this._downloadedExtensions[url] = 'done';
this._removeUrl(url);
},
whenAllFinished: function im_when_all_finished(callback) {
if (callback && typeof callback === 'function') {
this._callbacksOnAllFinished.push(callback);
}
},
isBusy: function im_is_busy() {
return this.howManyAreOnTheFly() > 0 ||
dm.getDownloadManager().count > 0 ||
this._processing;
},
howManyAreOnTheFly: function im_how_many_are_on_the_fly() {
return this._onTheFly.length;
}
};
var AdditionalExtensions = (function() {
// utility to merge two extensions objects (both in JSON format)
// if extensions1 and extensions2 both have a property with
// the same name, use the one in extensions2
function merge(extensions1, extensions2) {
var result = extensions1 || {};
Object.keys(extensions2).forEach(function(key) {
result[key] = extensions2[key];
});
return result;
}
function loadDownloadedExtensions() {
var file;
var downloadedExtensions;
logLine('load downloaded extensions');
try {
file = utils.getFile(
stageDir, 'additional-extensions', 'downloaded.json');
downloadedExtensions = utils.getJSON(file);
} catch (e) {
downloadedExtensions = {};
}
return downloadedExtensions;
}
function loadCustomExtensions() {
var file;
var customExtensions;
logLine('load custom extensions');
try {
file = utils.getFile(gaiaDir, 'build', 'config',
'custom-extensions.json');
customExtensions = utils.getJSON(file);
} catch (e) {
customExtensions = {};
}
return customExtensions;
}
function loadAdditionalExtensions() {
var file;
var additionalExtensions;
logLine('load additional extensions');
try {
file = utils.getFile(gaiaDir, 'build', 'config',
'additional-extensions.json');
additionalExtensions = utils.getJSON(file);
} catch (e) {
additionalExtensions = {};
}
return additionalExtensions;
}
function writeDownloadedExtensions(downloaded) {
var dir;
var file;
var content;
// output as an array (it's the original design of additional-extensions.py)
content = JSON.stringify(Object.keys(downloaded), undefined, 2);
dir = utils.getFile(stageDir, 'additional-extensions');
utils.ensureFolderExists(dir);
file = utils.getFile(stageDir, 'additional-extensions', 'downloaded.json');
utils.writeContent(file, content);
}
function getExtensionUrl(extension) {
var deferred = Promise.defer();
var url;
try {
url = extension.url;
if (url) {
deferred.resolve(url);
} else {
getUrlFromAMO(extension.amo).then(function(amoUrl) {
deferred.resolve(amoUrl);
});
}
} catch (e) {
deferred.reject(e);
}
return deferred.promise;
}
function getUrlFromAMO(amoId) {
var deferred = Promise.defer();
function extractDownloadUrlFromAMO(sourceUrl, filePath) {
var xmlDoc;
var elems;
try {
xmlDoc = utils.getXML(utils.getFile(filePath));
elems = xmlDoc.getElementsByTagName('install');
deferred.resolve(elems[0].innerHTML);
} catch (e) {
deferred.reject(e);
}
}
dm.getDownloadManager().download(AMO_URL + amoId,
null,
null,
extractDownloadUrlFromAMO,
function() {
deferred.reject('No Download URL found for AMO ID: ' + amoId);
});
return deferred.promise;
}
// main function
function execute(options) {
logLine(JSON.stringify(options, null, ' '));
config = options;
gaiaDir = config.GAIA_DIR;
stageDir = config.STAGE_DIR;
var extensions;
var downloadedExtensions;
var keys;
function downloadAndInstall(url) {
try {
if (downloadedExtensions && downloadedExtensions[url]) {
logLine(' already downloaded');
} else {
logLine('download from ' + url);
InstallationManager.startInstalling(url);
}
} catch (e) {
InstallationManager.failOrCancel(url);
logLine(' failed');
}
}
downloadedExtensions = loadDownloadedExtensions();
extensions = merge(loadAdditionalExtensions(), loadCustomExtensions());
InstallationManager.init(downloadedExtensions);
InstallationManager.whenAllFinished(function(exts) {
writeDownloadedExtensions(exts);
});
keys = Object.keys(extensions);
if (keys.length > 0) {
keys.forEach(function(prop) {
logLine('Installing ' + prop + '...');
getExtensionUrl(extensions[prop]).then(downloadAndInstall);
});
} else {
InstallationManager.failOrCancel();
}
utils.processEvents(function exitFunc() {
return {
wait: InstallationManager.isBusy()
};
});
}
return {
execute: execute
};
}());
exports.execute = AdditionalExtensions.execute;

139
gaia/build/app.js Executable file
View file

@ -0,0 +1,139 @@
'use strict';
/* global require, exports, quit */
var utils = require('utils');
var nodeHelper = new utils.NodeHelper();
function getAppRegExp(options) {
var appRegExp;
try {
appRegExp = utils.getAppNameRegex(options.BUILD_APP_NAME);
} catch (e) {
utils.log('utils', 'Using an invalid regular expression for APP ' +
'environment variable, APP=' + options.BUILD_APP_NAME);
throw e;
}
return appRegExp;
}
function buildApps(options) {
var processes = [];
var gaia = utils.gaia.getInstance(options);
// A workaround for bug 1093267 in order to handle callscreen's l10n broken.
// Callscreen will generate incorrect multilocale strings if
// build_stage/communications/dialer/locales is removed by webapp-optimize.
// After bug 1093267 has been resolved, we're going to get rid of this.
var callscreen;
var communications;
var webapps = gaia.rebuildWebapps.filter(function(app) {
var path = app.appDirPath;
if (path.indexOf('callscreen') !== -1) {
callscreen = app;
return false;
} else {
return true;
}
});
if (callscreen) {
webapps.push(callscreen);
}
webapps.forEach(function(app) {
let appDir = app.appDirPath;
let appDirFile = utils.getFile(appDir);
let appOptions = utils.cloneJSON(options);
let stageAppDir = utils.getFile(options.STAGE_DIR, appDirFile.leafName);
appOptions.APP_DIR = appDirFile.path;
appOptions.STAGE_APP_DIR = stageAppDir.path;
let buildFile = utils.getFile(appDir, 'build', 'build.js');
// A workaround for bug 1093267
if (buildFile.exists()) {
utils.log('app', 'building ' + appDirFile.leafName + ' app...');
if (parseInt(options.P) > 0) {
// A workaround for bug 1093267
if (appDir.indexOf('communications') !== -1) {
communications = utils.spawnProcess('build-app', appOptions);
processes.push({
name: 'communications',
instance: communications
});
} else {
processes.push({
name: appDirFile.leafName,
instance: utils.spawnProcess('build-app', appOptions)
});
}
} else {
require('./build-app').execute(appOptions);
}
}
// Do not spawn a new process since too many processes will slow it down
else {
// A workaround for bug 1093267
if (appDir.indexOf('callscreen') !== -1) {
if (communications) {
utils.processEvents(function () {
return { wait: utils.processIsRunning(communications) };
});
}
}
utils.copyToStage(appOptions);
appOptions.webapp = app;
nodeHelper.require('./post-app', appOptions);
}
});
utils.processEvents(function () {
return {
wait: processes.some(function(proc) {
return utils.processIsRunning(proc.instance);
})
};
});
var failed = false;
processes.forEach(function(proc) {
var exitValue = utils.getProcessExitCode(proc.instance);
if (exitValue !== 0) {
failed = true;
utils.log('failed', 'building ' + proc.name +
' app failed with exit code ' + exitValue);
}
});
if (failed) {
quit(1);
}
}
exports.execute = function(options) {
var stageDir = utils.getFile(options.STAGE_DIR);
utils.ensureFolderExists(stageDir);
if (options.BUILD_APP_NAME === '*') {
options.rebuildAppDirs = nodeHelper.require('./rebuild', options);
} else {
options.rebuildAppDirs = options.GAIA_APPDIRS.split(' ')
.filter(function(appDir) {
let appDirFile = utils.getFile(appDir);
return getAppRegExp(options).test(appDirFile.leafName);
});
}
nodeHelper.require('pre-app', options);
// Wait for all pre app tasks to be done before proceeding.
utils.processEvents(function () {
return { wait: false };
});
buildApps(options);
};
exports.buildApps = buildApps;

53
gaia/build/build-app.js Executable file
View file

@ -0,0 +1,53 @@
'use strict';
var utils = require('./utils');
var nodeHelper = new utils.NodeHelper();
exports.execute = function(options) {
var webapp = utils.getWebapp(options.APP_DIR, options);
var appDir = utils.getFile(webapp.appDirPath);
options.webapp = webapp;
var nodeScripts = [
'bluetooth',
'calendar',
'camera',
'clock',
'communications',
'costcontrol',
//'email',
'findmydevice',
'ftu',
'gallery',
'homescreen',
'keyboard',
'music',
'operatorvariant',
'search',
'settings',
'sms',
'system',
'verticalhome',
'wallpaper',
'wappush',
'music-oga',
'browser',
'smart-system'
];
var appName = appDir.leafName;
if (options.RUN_ON_NODE === '1' && nodeScripts.indexOf(appName) !== -1) {
var buildDir = utils.joinPath(options.GAIA_DIR, 'build');
var appBuildScriptPath = utils.joinPath(appDir.path, 'build/build');
var requirePath = utils.relativePath(buildDir, appBuildScriptPath);
nodeHelper.require(requirePath, options);
} else {
require(appDir.leafName + '/build').execute(options);
}
// Wait for all app tasks to be done before proceeding.
utils.processEvents(function () {
return { wait: false };
});
nodeHelper.require('./post-app', options);
};

67
gaia/build/build-test.js Executable file
View file

@ -0,0 +1,67 @@
'use strict';
/**
* This script is used to run test case for gaia build system.
*/
var utils = require('./utils');
var Mocha = require('mocha');
exports.execute = function(options) {
var TEST_TYPE = utils.getEnv('TEST_TYPE');
var REPORTER = utils.getEnv('REPORTER');
var TRY_ENV = utils.getEnv('TRY_ENV');
var TEST_FILES = utils.getEnv('TEST_FILES');
var TIMEOUT = '600000';
var BUILD_TEST_PATH = 'build/test/';
var thisChunk = utils.getEnv('THIS_CHUNK') || 1;
var totalChunks = utils.getEnv('TOTAL_CHUNKS') || 1;
var mocha = new Mocha({
ui: 'tdd',
reporter: REPORTER,
timeout: TIMEOUT
});
// Specify build test libraries path for build script testing
utils.setEnv('NODE_PATH', '$NODE_PATH:' + BUILD_TEST_PATH + TEST_TYPE);
if (TRY_ENV === '1') {
// Pass MOZ_DISABLE_NONLOCAL_CONNECTIONS=1 to try server in order to fail
// testing if there are network connections.
utils.setEnv('MOZ_DISABLE_NONLOCAL_CONNECTIONS', '1');
// Exclude tests if mocha test description marks [Network Required]
mocha.grep(/\[Network Required\]/).invert();
}
var testDir = utils.getFile(options.GAIA_DIR);
var files = [];
var pattern;
if (TEST_FILES) {
pattern = new RegExp(TEST_FILES.trim().replace(' ', '|'));
} else {
pattern = new RegExp('^' + options.GAIA_DIR +
'(/build/test/|/apps/\\S+/test/build/)' + TEST_TYPE + '/\\S+_test\\.js$');
}
utils.ls(testDir, true).forEach(function(file) {
if (pattern.test(file.path)) {
files.push(file.path);
}
});
// Sort test files to ensure the order of files will be the same everytime.
files = files.sort();
// Calculate chunks for task cluster to split tests
if (files.length >= totalChunks) {
let chunkLength = parseInt(Math.ceil(files.length / totalChunks));
let chunk = parseInt((thisChunk - 1) * chunkLength);
files = files.slice(chunk, chunk + chunkLength);
}
// Use Mocha to run test cases.
files.forEach(mocha.addFile, mocha);
mocha.run(utils.exit);
};

27
gaia/build/clean-stage-app.js Executable file
View file

@ -0,0 +1,27 @@
'use strict';
var utils = require('./utils');
function remove(stageDir, appName) {
var stageAppDir = utils.getFile(stageDir, appName);
if (stageAppDir.exists()) {
stageAppDir.remove(true);
}
}
exports.execute = function(options) {
if (options.BUILD_APP_NAME !== '*') {
remove(options.STAGE_DIR, options.BUILD_APP_NAME);
return;
}
options.GAIA_APPDIRS.split(' ').forEach(function(appdirPath) {
try {
var appName = utils.getFile(appdirPath).leafName;
remove(options.STAGE_DIR, appName);
} catch (e) {
// getFile will raise an exception if initial nsIFile with first argument
// doesn't exist, add an empty exception handler to catch it.
}
});
};

28
gaia/build/common.mk Executable file
View file

@ -0,0 +1,28 @@
APP_DIR := $(CURDIR)
ifneq (,$(findstring MINGW32_,$(SYS)))
APP_DIR:=$(shell pwd -W | sed -e 's|/|\\\\|g')
endif
export APP_DIR
define run-build-coverage
TEST_FILES_DIR=$1 node --harmony node_modules$(SEP)istanbul$(SEP)lib$(SEP)cli.js \
cover build$(SEP)test$(SEP)coverage-checker.js
endef
# rwildcard is used to recursive wildcard, it will travel all files in
# directory by argument 1 and filter by argument 2
#
# Usage: $(rwildcard, DIR, FILTER)
rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
define run-js-command
$(2) $(XULRUNNERSDK) $(XPCSHELLSDK) \
-f "$(GAIA_DIR)$(SEP)build$(SEP)xpcshell-commonjs.js" \
-e "run('$(strip $1)');"
endef
define run-node-command
$(2) node --harmony -e 'require("./build/$(strip $1).js").execute($(BUILD_CONFIG))'
endef

View file

@ -0,0 +1,5 @@
{
"Screen Reader Simulator": {
"url" : "http://addons.mozilla.org/firefox/downloads/latest/440614/addon-440614-latest.xpi"
}
}

View file

@ -0,0 +1,6 @@
{
"rebuildBlacklist": [
"apps/calendar/js/presets.js",
"dev_apps/test-agent/config.json"
]
}

View file

@ -0,0 +1,292 @@
{
"accessibility.invert": false,
"accessibility.screenreader": false,
"accessibility.screenreader-ftu-timeout-seconds": 15,
"achievements": [{
"issuer": "fxos-customizer",
"criteria": "achievements/customize-champ"
}, {
"issuer": "fxos-customizer",
"criteria": "achievements/add-on-all-star"
}, {
"issuer": "fxos-directory",
"criteria": "achievements/fab-finder"
}, {
"issuer": "fxos-directory",
"criteria": "achievements/content-commander"
}, {
"issuer": "fxos-sharing",
"criteria": "achievements/sharing-is-caring"
}, {
"issuer": "Studio",
"criteria": "achievements/creative-kind"
}],
"addons.auto_update": true,
"addons.update_notify": false,
"alarm.enabled": false,
"app-transition.enabled": true,
"app.cards_view.screenshots.enabled": true,
"app-suspending.enabled": true,
"app-themecolor.enabled": true,
"app.launch_path.blacklist": [],
"app.reportCrashes": "ask",
"app.update.channel": "",
"app.update.interval": 86400,
"app.update.url": "",
"app.update.battery-threshold.unplugged": 25,
"app.update.battery-threshold.plugged": 25,
"app.update.wifi-prioritized": true,
"apz.overscroll.enabled": true,
"audio.volume.alarm": 8,
"audio.volume.bt_sco": 15,
"audio.volume.dtmf": 15,
"audio.volume.content": 8,
"audio.volume.master": 5,
"audio.volume.notification": 8,
"audio.volume.tts": 15,
"audio.volume.telephony": 5,
"audio.volume.cemaxvol": 11,
"bluetooth.enabled": false,
"bluetooth.debugging.enabled": false,
"bluetooth.snoop.enabled": false,
"bluetooth.suspended": false,
"bluetooth.device.connected": null,
"browser.private.default": false,
"camera.sound.enabled": true,
"camera.recording.preferredSizes": [],
"cdn.url": "https://fxos.cdn.mozilla.net",
"clear.remote-windows.data": false,
"cmas.enabled": [true, true],
"copypaste.enabled": true,
"multiscreen.enabled": false,
"debug.console.enabled": false,
"developer.menu.enabled": false,
"dev.gaia.pinning_the_web": true,
"debug.oop.disabled": false,
"debug.fps.enabled": false,
"debug.ttl.enabled": false,
"debug.log-animations.enabled": false,
"debug.paint-flashing.enabled": false,
"metrics.selectedMetrics.level": "Basic",
"debug.gaia.enabled": false,
"deviceinfo.build_number": "",
"deviceinfo.firmware_revision": "",
"deviceinfo.hardware": "",
"deviceinfo.mac": "",
"deviceinfo.os": "",
"deviceinfo.previous_os": "",
"deviceinfo.platform_build_id": "",
"deviceinfo.platform_version": "",
"deviceinfo.product_device": "",
"deviceinfo.product_manufacturer": "",
"deviceinfo.product_model": "",
"deviceinfo.software": "",
"device.storage.writable.name": "sdcard",
"devtools.overlay": false,
"devtools.overlay.system": false,
"devtools.eventlooplag.threshold": 100,
"devtools.remote.wifi.visible": false,
"devtools.remote.wifi.enabled": false,
"devtools.unrestricted": false,
"feedback.url": "https://input.allizom.org/api/v1/feedback/",
"gaia.system.checkForUpdates": false,
"gaia.ui.menu": "#f97c17",
"gaia.ui.menutext": "#ffffff",
"gaia.ui.infobackground": "#343e40",
"gaia.ui.infotext": "#686868",
"gaia.ui.window": "#ffffff",
"gaia.ui.windowtext": "#000000",
"gaia.ui.highlight": "#b2f2ff",
"geolocation.debugging.enabled": false,
"geolocation.debugging.gps-locations-ignored": false,
"geolocation.enabled": true,
"geolocation.suspended": false,
"gfx.layerscope.enabled": false,
"hud.hide": true,
"hud.logging": true,
"hud.warnings": true,
"hud.errors": true,
"hud.security": true,
"hud.reflows": false,
"hud.reflows.duration": 10,
"hud.jank": false,
"hud.uss": false,
"hud.appmemory": false,
"hud.jsobjects": false,
"hud.jsstrings": false,
"hud.jsother": false,
"hud.style": false,
"hud.dom": false,
"hud.other": false,
"icc.applications": null,
"icc.data": null,
"icc.displayTextTimeout": 40000,
"icc.inputTextTimeout": 40000,
"icc.toneDefaultTimeout": 5000,
"icc.goBackTimeout": 4000,
"icc.selectTimeout": 150000,
"identity.fxaccounts.reset-password.url": "https://accounts.firefox.com/reset_password",
"in-app-sheet.enabled": false,
"keyboard.vibration": true,
"keyboard.clicksound": false,
"keyboard.dynamic-inputs": {},
"keyboard.autocorrect": true,
"keyboard.wordsuggestion": true,
"keyboard.handwriting.strokeWidth": 10,
"keyboard.handwriting.responseTime": 200,
"keyboard.3rd-party-app.enabled": true,
"keyboard.launch-on-boot": true,
"langpack.channel": "2.5",
"language.current": "en-US",
"latecustomization.url": "https://marketplace.firefox.com/api/v2/late-customization/",
"layers.draw-borders": false,
"layers.dump": false,
"layers.enable-tiles": true,
"layout.display-list.dump": false,
"locale.hour12": null,
"lockscreen.passcode-lock.code": "0000",
"lockscreen.passcode-lock.timeout": 0,
"lockscreen.passcode-lock.enabled": false,
"lockscreen.notifications-preview.enabled": true,
"notifications.resend": true,
"lockscreen.enabled": true,
"lockscreen.locked": false,
"lockscreen.unlock-sound.enabled": false,
"message.sent-sound.enabled": true,
"mms.debugging.enabled": false,
"network.debugging.enabled": false,
"nfc.enabled": false,
"nfc.suspended": false,
"nfc.debugging.enabled": false,
"operatorvariant.mcc": "000",
"operatorvariant.mnc": "00",
"phone.ring.keypad": true,
"phone.dtmf.type": "long",
"powersave.enabled": false,
"powersave.threshold": -1,
"privacy.donottrackheader.value": "-1",
"privacy.donottrackheader.enabled": false,
"ril.telephony.defaultServiceId": 0,
"ril.voicemail.defaultServiceId": 0,
"ril.sms.defaultServiceId": 0,
"ril.mms.defaultServiceId": 0,
"ril.data.defaultServiceId": 0,
"ril.data.suspended": false,
"ril.iccInfo.mbdn": "",
"ril.sms.requestStatusReport.enabled": false,
"ril.mms.requestReadReport.enabled": false,
"ril.cellbroadcast.searchlist": "",
"ril.callwaiting.enabled": null,
"ril.cf.enabled": false,
"ril.data.enabled": false,
"ril.data.apn": "",
"ril.data.carrier": "",
"ril.data.passwd": "",
"ril.data.httpProxyHost": "",
"ril.data.httpProxyPort": 0,
"ril.data.mmsc": "",
"ril.data.mmsproxy": "",
"ril.data.mmsport": 0,
"ril.data.roaming_enabled": false,
"ril.data.roaming_enabled.warningDialog.enabled": true,
"ril.data.enabled.warningDialog.enabled": true,
"ril.data.user": "",
"ril.data.authtype": "notDefined",
"ril.mms.apn": "",
"ril.mms.carrier": "",
"ril.mms.httpProxyHost": "",
"ril.mms.httpProxyPort": "",
"ril.mms.mmsc": "",
"ril.mms.mmsport": "",
"ril.mms.mmsproxy": "",
"ril.mms.passwd": "",
"ril.mms.user": "",
"ril.mms.retrieval_mode": "automatic-home",
"ril.mms.authtype": "notDefined",
"dom.mms.operatorSizeLimitation": 307200,
"airplaneMode.enabled": false,
"airplaneMode.status": "disabled",
"ril.radio.preferredNetworkType": "",
"ril.radio.disabled": false,
"ril.supl.apn": "",
"ril.supl.carrier": "",
"ril.supl.httpProxyHost": "",
"ril.supl.httpProxyPort": "",
"ril.supl.passwd": "",
"ril.supl.user": "",
"ril.supl.authtype": "notDefined",
"ril.sms.strict7BitEncoding.enabled": false,
"ril.cellbroadcast.disabled": [false, false],
"ril.data.apnSettings": "",
"ril.data.cp.apns": "",
"ril.callerId": "CLIR_DEFAULT",
"ril.debugging.enabled": false,
"appsearch.url": "https://appsearch.services.mozilla.com/partners/1.0/{resource}/",
"search.marketplace.url": "https://marketplace.firefox.com/api/v2/apps/search/rocketbar/?q={q}&limit={limit}&lang={lang}&region=restofworld",
"screen.automatic-brightness": false,
"screen.brightness": 1,
"screen.timeout": 60,
"search.suggestions.enabled": true,
"software-button.enabled": false,
"homegesture.enabled": false,
"edgesgesture.enabled": true,
"continuous-transition.enabled": false,
"statusbar.show-am-pm": false,
"support.onlinesupport.title": "",
"support.onlinesupport.href": "",
"support.callsupport1.title": "",
"support.callsupport1.href": "",
"support.callsupport2.title": "",
"support.callsupport2.href": "",
"sync.collections.history.enabled": true,
"sync.collections.passwords.enabled": false,
"sync.collections.bookmarks.enabled": false,
"sync.collections.history.readonly": true,
"sync.collections.passwords.readonly": true,
"sync.collections.bookmarks.readonly": true,
"sync.collections.history.limit": 50,
"sync.collections.passwords.limit": 2000,
"sync.collections.bookmarks.limit": 2000,
"sync.fxa.audience": "https://token.services.mozilla.com/",
"sync.scheduler.interval": 3600,
"sync.scheduler.wifionly": false,
"sync.server.url": "https://syncto.dev.mozaws.net/v1/",
"telephony.speaker.enabled": false,
"tethering.usb.enabled": false,
"tethering.usb.ip": "192.168.0.1",
"tethering.usb.prefix": "24",
"tethering.usb.dhcpserver.startip": "192.168.0.10",
"tethering.usb.dhcpserver.endip": "192.168.0.30",
"tethering.wifi.enabled": false,
"tethering.wifi.ip": "192.168.1.1",
"tethering.wifi.prefix": "24",
"tethering.wifi.dhcpserver.startip": "192.168.1.10",
"tethering.wifi.dhcpserver.endip": "192.168.1.30",
"tethering.wifi.ssid": "FirefoxHotspot",
"tethering.wifi.security.type": "wpa2-psk",
"tethering.wifi.security.password": "",
"tethering.wifi.connectedClients": 0,
"tethering.usb.connectedClients": 0,
"theme.selected": "app://default_theme.gaiamobile.org/manifest.webapp",
"time.clock.automatic-update.enabled": true,
"time.timezone.automatic-update.enabled": true,
"time.timezone": null,
"ums.enabled": false,
"ums.mode": 0,
"ums.status": 0,
"ums.volume.sdcard.enabled": true,
"ums.volume.sdcard1.enabled": true,
"ums.volume.extsdcard.enabled": true,
"ums.volume.external.enabled": true,
"usb.transfer": "auto",
"volume.external.unrecognised": false,
"vibration.enabled": true,
"wifi.enabled": true,
"wifi.screen_off_timeout": 600000,
"wifi.sleepMode": false,
"wifi.suspended": false,
"wifi.disabled_by_wakelock": false,
"wifi.notification": false,
"wifi.connect_via_settings": false,
"wap.push.enabled": true
}

View file

@ -0,0 +1,48 @@
{
"facebook": {
"redirectURI":
"https://www.facebook.com/connect/login_success.html",
"loginPage":
"https://m.facebook.com/dialog/oauth/?",
"applicationId":
"123456",
"scope":
["friends_about_me", "friends_birthday", "friends_hometown",
"friends_location", "friends_work_history", "read_stream"],
"redirectMsg":
"http://intense-tundra-4122.herokuapp.com/fbowd/oauth2_new/dialogs_end.html",
"redirectLogout":
"http://intense-tundra-4122.herokuapp.com/fbowd/oauth2_new/dialogs_end.html",
"imgDetailWidth":
200,
"imgThumbSize":
120
},
"live": {
"redirectURI":
"https://www.mozilla.org/index.html",
"loginPage":
"https://login.live.com/oauth20_authorize.srf?",
"applicationId":
"123456",
"scope":
["wl.basic", "wl.contacts_emails", "wl.contacts_phone_numbers",
"wl.contacts_birthday", "wl.contacts_postal_addresses"],
"logoutUrl":
"https://login.live.com/logout.srf"
},
"gmail": {
"redirectURI":
"https://serene-cove-3587.herokuapp.com/liveowd/oauth2_new/flow_live.html",
"loginPage":
"https://accounts.google.com/o/oauth2/auth?",
"applicationId":
"664741361278.apps.googleusercontent.com",
"scope":
["https://www.google.com/m8/feeds/"],
"logoutUrl":
"https://accounts.google.com/Logout"
}
}

View file

@ -0,0 +1,7 @@
/* global pref */
// This pref is only for firefox tests which contain web components and
// debugging/testing portions of gaia in firefox nightly. We can remove this
// pref once webcomponents are ready for the entire web (and not just certified
// apps seee bug 1000199)
pref('dom.webcomponents.enabled', true);

4
gaia/build/config/gps-prefs.js Executable file
View file

@ -0,0 +1,4 @@
/* global pref */
pref('geo.gps.supl_server', 'supl.izatcloud.net');
pref('geo.gps.supl_port', 22024);

View file

@ -0,0 +1,302 @@
{
"layout" : {
"af": [
{"layoutId": "af", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"am": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"ar": [
{"layoutId": "ar", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"as": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"bg": [
{"layoutId": "bg-BDS", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"bm": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"bn-BD": [
{"layoutId": "bn-Avro", "app": ["apps", "keyboard"]},
{"layoutId": "bn-Probhat", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"bn-IN": [
{"layoutId": "bn-Avro", "app": ["apps", "keyboard"]},
{"layoutId": "bn-Probhat", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"bs": [
{"layoutId": "bs", "app": ["apps", "keyboard"]}
],
"ca": [
{"layoutId": "ca", "app": ["apps", "keyboard"]}
],
"cs": [
{"layoutId": "cs", "app": ["apps", "keyboard"]}
],
"cy": [
{"layoutId": "cy", "app": ["apps", "keyboard"]}
],
"da": [
{"layoutId": "da", "app": ["apps", "keyboard"]}
],
"de": [
{"layoutId": "de", "app": ["apps", "keyboard"]}
],
"dsb": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"ee": [
{"layoutId": "en-Africa", "app": ["apps", "keyboard"]}
],
"el": [
{"layoutId": "el", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"en-GB": [
{"layoutId": "en-GB", "app": ["apps", "keyboard"]}
],
"en-US": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"eo": [
{"layoutId": "eo", "app": ["apps", "keyboard"]}
],
"es": [
{"layoutId": "es", "app": ["apps", "keyboard"]}
],
"et": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"eu": [
{"layoutId": "eu", "app": ["apps", "keyboard"]},
{"layoutId": "es", "app": ["apps", "keyboard"]},
{"layoutId": "fr", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"fa": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"ff": [
{"layoutId": "ff", "app": ["apps", "keyboard"]},
{"layoutId": "fr", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"fi": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"fr": [
{"layoutId": "fr", "app": ["apps", "keyboard"]}
],
"fy-NL": [
{"layoutId": "fy", "app": ["apps", "keyboard"]}
],
"ga-IE": [
{"layoutId": "ga", "app": ["apps", "keyboard"]}
],
"gd": [
{"layoutId": "gd", "app": ["apps", "keyboard"]}
],
"gl": [
{"layoutId": "es", "app": ["apps", "keyboard"]}
],
"ha": [
{"layoutId": "en-Africa", "app": ["apps", "keyboard"]}
],
"he": [
{"layoutId": "he", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"hi-IN": [
{"layoutId": "hi", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"hr": [
{"layoutId": "hr", "app": ["apps", "keyboard"]}
],
"hsb": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"ht": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"hu": [
{"layoutId": "hu", "app": ["apps", "keyboard"]}
],
"hy-AM": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"id": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"ig": [
{"layoutId": "en-Africa", "app": ["apps", "keyboard"]}
],
"it": [
{"layoutId": "it", "app": ["apps", "keyboard"]}
],
"ja": [
{"layoutId": "jp-kanji", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"ko": [
{"layoutId": "ko", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"lg": [
{"layoutId": "en-Africa", "app": ["apps", "keyboard"]}
],
"lij": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"ln": [
{"layoutId": "en-Africa", "app": ["apps", "keyboard"]}
],
"lv": [
{"layoutId": "lv", "app": ["apps", "keyboard"]}
],
"mk": [
{"layoutId": "mk", "app": ["apps", "keyboard"]}
],
"ml": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"mg": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"mr": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"my": [
{"layoutId": "my", "app": ["apps", "keyboard"]}
],
"nb": [
{"layoutId": "nb", "app": ["apps", "keyboard"]}
],
"nl": [
{"layoutId": "nl", "app": ["apps", "keyboard"]}
],
"or": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"pa": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"pl": [
{"layoutId": "pl", "app": ["apps", "keyboard"]}
],
"pt-BR": [
{"layoutId": "pt-BR", "app": ["apps", "keyboard"]}
],
"pt-PT": [
{"layoutId": "pt-PT", "app": ["apps", "keyboard"]}
],
"fr-x-psaccent": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"ar-x-psbidi": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"ro": [
{"layoutId": "ro", "app": ["apps", "keyboard"]}
],
"ru": [
{"layoutId": "ru", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"sk": [
{"layoutId": "sk", "app": ["apps", "keyboard"]}
],
"sl": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"son": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"sq": [
{"layoutId": "sq", "app": ["apps", "keyboard"]}
],
"sr": [
{"layoutId": "sr-Cyrl", "app": ["apps", "keyboard"]},
{"layoutId": "sr-Latn", "app": ["apps", "keyboard"]}
],
"sr-Cyrl": [
{"layoutId": "sr-Cyrl", "app": ["apps", "keyboard"]}
],
"sr-Latn": [
{"layoutId": "sr-Latn", "app": ["apps", "keyboard"]}
],
"sv-SE": [
{"layoutId": "sv", "app": ["apps", "keyboard"]}
],
"sw": [
{"layoutId": "en-Africa", "app": ["apps", "keyboard"]}
],
"ta": [
{"layoutId": "ta", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"te": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"th": [
{"layoutId": "th", "app": ["apps", "keyboard"]}
],
"tn": [
{"layoutId": "en-Africa", "app": ["apps", "keyboard"]}
],
"tl": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"tr": [
{"layoutId": "tr-Q", "app": ["apps", "keyboard"]},
{"layoutId": "tr-F", "app": ["apps", "keyboard"]}
],
"uk": [
{"layoutId": "uk", "app": ["apps", "keyboard"]}
],
"ur": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"vi": [
{"layoutId": "vi-Typewriter", "app": ["apps", "keyboard"]},
{"layoutId": "fr", "app": ["apps", "keyboard"]}
],
"wo": [
{"layoutId": "wo", "app": ["apps", "keyboard"]}
],
"xh": [
{"layoutId": "en-Africa", "app": ["apps", "keyboard"]}
],
"yo": [
{"layoutId": "en-Africa", "app": ["apps", "keyboard"]}
],
"zam": [
{"layoutId": "es-Americas", "app": ["apps", "keyboard"]},
{"layoutId": "es", "app": ["apps", "keyboard"]}
],
"zh-CN": [
{"layoutId": "zh-Hans-Pinyin", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"zh-TW": [
{"layoutId": "zh-Hant-Zhuyin", "app": ["apps", "keyboard"]},
{"layoutId": "en", "app": ["apps", "keyboard"]}
],
"zu": [
{"layoutId": "en-Africa", "app": ["apps", "keyboard"]}
]
},
"langIndependentLayouts": [
{"layoutId": "number", "app": ["apps", "keyboard"]}
],
"fallbackLayouts": [
{"layoutId": "en", "app": ["apps", "keyboard"]}
]
}

View file

@ -0,0 +1,19 @@
{
"CONCAT_LOCALES_BLACKLIST": {
"pdfjs": "*"
},
"JS_AGGREGATION_BLACKLIST": {
"pdfjs": "*"
},
"INLINE_GLOBAL_VAR_WHITELIST": {
"system": [ "net_error.html" ]
},
"INLINE_OPTIMIZE_WHITELIST": {
"system": [ "net_error.html" ],
"smart-system": [ "net_error.html" ]
},
"PRETRANSLATION_BLACKLIST": {
"pdfjs": "*",
"keyboard": ["index.html"]
}
}

View file

@ -0,0 +1,25 @@
/*global pref*/
pref('dom.payment.skipHTTPSCheck', true);
pref('dom.payment.debug', true);
pref('dom.payment.provider.1.name', 'firefoxmarketdev');
pref('dom.payment.provider.1.description', 'marketplace-dev.allizom.org');
pref('dom.payment.provider.1.uri',
'https://marketplace-dev.allizom.org/mozpay/?req=');
pref('dom.payment.provider.1.type', 'mozilla-dev/payments/pay/v1');
pref('dom.payment.provider.1.requestMethod', 'GET');
pref('dom.payment.provider.2.name', 'firefoxmarketstage');
pref('dom.payment.provider.2.description', 'marketplace.allizom.org');
pref('dom.payment.provider.2.uri',
'https://marketplace.allizom.org/mozpay/?req=');
pref('dom.payment.provider.2.type', 'mozilla-stage/payments/pay/v1');
pref('dom.payment.provider.2.requestMethod', 'GET');
pref('dom.payment.provider.3.name', 'mockpayprovider');
pref('dom.payment.provider.3.description', 'Mock Payment Provider');
pref('dom.payment.provider.3.uri',
'http://ferjm.github.io/gaia-mock-payment-provider/index.html?req=');
pref('dom.payment.provider.3.type', 'tests/payments/pay/v1');
pref('dom.payment.provider.3.requestMethod', 'GET');

View file

@ -0,0 +1,8 @@
/* global pref */
pref('dom.payment.provider.0.name', 'firefoxmarket');
pref('dom.payment.provider.0.description', 'marketplace.firefox.com');
pref('dom.payment.provider.0.uri',
'https://marketplace.firefox.com/mozpay/?req=');
pref('dom.payment.provider.0.type', 'mozilla/payments/pay/v1');
pref('dom.payment.provider.0.requestMethod', 'GET');

View file

@ -0,0 +1,5 @@
apps/*
outoftree_apps/*
hosted_apps/*
dev_apps/crystalskull
dev_apps/cubevid

View file

@ -0,0 +1,4 @@
apps/*
external-dogfood-apps/*
outoftree_apps/*
hosted_apps/*

View file

@ -0,0 +1,40 @@
apps/*
outoftree_apps/*
hosted_apps/*
dev_apps/marketplace-dev.allizom.org
dev_apps/in_app_pay_test
dev_apps/marketplace.allizom.org
dev_apps/mochitest
dev_apps/ds-test
dev_apps/geoloc
dev_apps/l20n-app
dev_apps/membuster
dev_apps/test-ime
dev_apps/share-receiver
dev_apps/template
dev_apps/test-agent
dev_apps/test-container
dev_apps/test-iac-publisher
dev_apps/test-iac-subscriber
dev_apps/test-otasp
dev_apps/test-receiver-1
dev_apps/test-receiver-2
dev_apps/test-receiver-inline
dev_apps/test-wappush
dev_apps/uitest
dev_apps/uitest-privileged
dev_apps/contacts-manager
dev_apps/contacts-ds-provider1
dev_apps/contacts-ds-provider2
dev_apps/mobile-wallet
dev_apps/theme-test-1
dev_apps/theme-test-2
dev_apps/theme-test-3
dev_apps/nfc-api-test
dev_apps/music-oga
dev_apps/upnp-test
apps/settings/test/fixtures/addon1
apps/settings/test/fixtures/addon2
apps/settings/test/fixtures/addon3
distros/raptor/apps/test-startup-limit

View file

@ -0,0 +1,37 @@
apps/bluetooth
apps/bookmark
apps/calendar
apps/callscreen
apps/camera
apps/clock
apps/communications
apps/costcontrol
apps/default_theme
apps/download
apps/email
apps/emergency-call
apps/findmydevice
apps/fl
apps/fm
apps/ftu
apps/gallery
apps/homescreen
apps/keyboard
apps/marketplace.firefox.com
apps/music
apps/network-alerts
apps/operatorvariant
apps/pdfjs
apps/ringtones
apps/search
apps/setringtone
apps/settings
apps/sms
apps/sync
apps/system
apps/verticalhome
apps/video
apps/wallpaper
apps/wappush
outoftree_apps/*
hosted_apps/*

View file

@ -0,0 +1,4 @@
/*global user_pref*/
user_pref('devtools.responsiveUI.customWidth', 320);
user_pref('devtools.responsiveUI.customHeight', 480);
user_pref('devtools.responsiveUI.currentPreset', 'custom');

View file

@ -0,0 +1 @@
FIREFOX_SYNC ?= 1

View file

@ -0,0 +1,3 @@
{
"homescreen.appName": "verticalhome"
}

View file

@ -0,0 +1,24 @@
apps/bluetooth
apps/bookmark
apps/calendar
apps/camera
apps/clock
apps/communications
apps/default_theme
apps/email
apps/fl
apps/ftu
apps/gallery
apps/keyboard
apps/marketplace.firefox.com
apps/music
apps/pdfjs
apps/ringtones
apps/search
apps/setringtone
apps/settings
apps/system
apps/verticalhome
apps/video
apps/wallpaper
outoftree_apps/*

View file

@ -0,0 +1,24 @@
apps/bluetooth
apps/bookmark
apps/calendar
apps/camera
apps/clock
apps/communications
apps/default_theme
apps/email
apps/fl
apps/ftu
apps/gallery
apps/keyboard
apps/marketplace.firefox.com
apps/music
apps/pdfjs
apps/ringtones
apps/search
apps/setringtone
apps/settings
apps/system
apps/verticalhome
apps/video
apps/wallpaper
outoftree_apps/*

View file

@ -0,0 +1,5 @@
/*global user_pref*/
user_pref('devtools.responsiveUI.customWidth', 1280);
user_pref('devtools.responsiveUI.customHeight', 800);
user_pref('devtools.responsiveUI.currentPreset', 'custom');
user_pref('devtools.useragent.device_type', 'Tablet');

View file

@ -0,0 +1,20 @@
{
"homescreens": [
[
["apps", "search"],
["apps", "marketplace.firefox.com"],
["apps", "camera"]
], [
["apps", "gallery"],
["apps", "music"],
["apps", "video"],
["apps", "settings"],
["apps", "clock"],
["apps", "email"],
["apps", "calendar"]
]
],
"preferences": {
"grid.cols": 3
}
}

View file

@ -0,0 +1,5 @@
{
"app.launch_path.blacklist": ["/dialer/index.html#keyboard-view"],
"wap.push.enabled": false,
"layers.composer2d.enabled": false
}

View file

@ -0,0 +1,2 @@
{
}

View file

@ -0,0 +1,20 @@
'use strict';
/* jslint node: true */
/* global TestAgent, server */
var fsPath = require('path');
// All require paths must be absolute -- use __dirname
var Agent = TestAgent,
Apps = Agent.server,
Suite = Agent.Suite,
testSuite = new Suite({
paths: [fsPath.resolve(__dirname + '/../../apps/'),
fsPath.resolve(__dirname + '/../../tv_apps/'),
fsPath.resolve(__dirname + '/../../dev_apps/')],
strictMode: false,
testDir: '/test/unit/',
libDir: 'js/',
testSuffix: '_test.js'
});
server.use(Apps.Suite, testSuite);

View file

@ -0,0 +1,22 @@
tv_apps/smart-system
tv_apps/smart-settings
tv_apps/smart-home
tv_apps/browser
tv_apps/tv-deck
tv_apps/tv-epg
tv_apps/app-deck
tv_apps/device-deck
tv_apps/dashboard
tv_apps/dlna-player
tv_apps/fling-player
tv_apps/notification-receiver
tv_apps/weather-widget
tv_apps/remote-control
tv_apps/remote-control-client
tv_apps/fling-tutorial
tv_apps/tv-ftu
apps/sync
apps/keyboard
outoftree_apps/*
dev_apps/test-agent
dev_apps/uitest

View file

@ -0,0 +1,12 @@
tv_apps/smart-system
tv_apps/smart-home
tv_apps/browser
tv_apps/tv-deck
tv_apps/app-deck
tv_apps/fling-player
tv_apps/notification-receiver
tv_apps/fling-tutorial
tv_apps/remote-control
tv_apps/remote-control-client
outoftree_apps/*
apps/sync

View file

@ -0,0 +1,28 @@
/*global user_pref*/
user_pref('devtools.responsiveUI.customWidth', 1366);
user_pref('devtools.responsiveUI.customHeight', 768);
user_pref('devtools.responsiveUI.currentPreset', 'custom');
user_pref('b2g.system_startup_url',
'app://smart-system.gaiamobile.org/index.html');
user_pref('b2g.system_manifest_url',
'app://smart-system.gaiamobile.org/manifest.webapp');
user_pref('b2g.neterror.url',
'app://smart-system.gaiamobile.org/net_error.html');
// Collaborate with meta-viewport to fulfill more complete web experience
user_pref('apz.allow_zooming', true);
// Larger value to provide better experience for apps/pages which targets on
// TV but uses embed tag with hard-coded width/height
user_pref('browser.viewport.desktopWidth', 1920);
user_pref('dom.presentation.enabled', true);
user_pref('devtools.useragent.device_type', 'TV');
user_pref('dom.apps.customization.enabled', false);
// Remote Control default setting, enable service and pairing
user_pref('remotecontrol.service.enabled', true);
user_pref('remotecontrol.service.pairing_required', true);
// Remote control URL. Gecko loads its required static file in this app.
user_pref('remotecontrol.client_page.prepath',
'app://remote-control-client.gaiamobile.org');
user_pref('remotecontrol.client_page.blacklist', '/client.html,/pairing.html');

1
gaia/build/config/tv/device.mk Executable file
View file

@ -0,0 +1 @@
FIREFOX_SYNC ?= 1

View file

@ -0,0 +1,7 @@
{
"dom.presentation.discoverable": true,
"homescreen.appName": "smart-home",
"sync.collections.bookmarks.enabled": true,
"sync.collections.history.enabled": false,
"screen.timeout":0
}

View file

@ -0,0 +1,739 @@
{
"tuners": [
{
"id":"1",
"supportedType": ["dvb-t"],
"sources": [
{
"type": "dvb-t",
"channels": [
{
"networkId": "32112",
"transportStreamId": "32112",
"serviceId": "40960",
"type": "tv",
"name": "TV #1",
"number" : 1,
"isEmergency": false,
"isFree" : true,
"videoFilePath": "elephants-dream.webm",
"programs": [
{"eventId":"734475972", "title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"586216742", "title":"Information Program", "startTime":"1430697600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"861120908", "title":"Cooking", "startTime":"1430704800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"57540864", "title":"Afternoon Information", "startTime":"1430708400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"329867743", "title":"Movie Program", "startTime":"1430715600", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"572563927", "title":"Television drama", "startTime":"1430722800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"363286425", "title":"News of Afternoon", "startTime":"1430726400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"910176581", "title":"Entertainment Program", "startTime":"1430733600", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"566633346", "title":"Music Program", "startTime":"1430740800", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"951603614", "title":"News of Midnight", "startTime":"1430748000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"773101560", "title":"Entertainment Program", "startTime":"1430751600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"185460210", "title":"News of Morning", "startTime":"1430773200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"797456853", "title":"Information Program", "startTime":"1430784000", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"976825440", "title":"Cooking", "startTime":"1430791200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"420857949", "title":"Afternoon Information", "startTime":"1430794800", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"749006350", "title":"Movie Program", "startTime":"1430802000", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"927193", "title":"Television drama", "startTime":"1430809200", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"47696273", "title":"News of Afternoon", "startTime":"1430812800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"231995539", "title":"Entertainment Program", "startTime":"1430820000", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"501545076", "title":"Music Program", "startTime":"1430827200", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"263172731", "title":"News of Midnight", "startTime":"1430834400", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"658267606", "title":"Entertainment Program", "startTime":"1430838000", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"431111532", "title":"News of Morning", "startTime":"1430859600", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"363586806", "title":"Information Program", "startTime":"1430870400", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"775168902", "title":"Cooking", "startTime":"1430877600", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"839588503", "title":"Afternoon Information", "startTime":"1430881200", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"65487862", "title":"Movie Program", "startTime":"1430888400", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"508594474", "title":"Television drama", "startTime":"1430895600", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"331004789", "title":"News of Afternoon", "startTime":"1430899200", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"47225257", "title":"Entertainment Program", "startTime":"1430906400", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"555267371", "title":"Music Program", "startTime":"1430913600", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"627197565", "title":"News of Midnight", "startTime":"1430920800", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"772640894", "title":"Entertainment Program", "startTime":"1430924400", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"631125294", "title":"News of Morning", "startTime":"1430946000", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"44026761", "title":"Information Program", "startTime":"1430956800", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"132622366", "title":"Cooking", "startTime":"1430964000", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"680336718", "title":"Afternoon Information", "startTime":"1430967600", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"904310744", "title":"Movie Program", "startTime":"1430974800", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"796724154", "title":"Television drama", "startTime":"1430982000", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"281676124", "title":"News of Afternoon", "startTime":"1430985600", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"990618634", "title":"Entertainment Program", "startTime":"1430992800", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"200131511", "title":"Music Program", "startTime":"1431000000", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"352281886", "title":"News of Midnight", "startTime":"1431007200", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"293989564", "title":"Entertainment Program", "startTime":"1431010800", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"306546887", "title":"News of Morning", "startTime":"1431032400", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771172746", "title":"Information Program", "startTime":"1431043200", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"999039097", "title":"Cooking", "startTime":"1431050400", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"530278315", "title":"Afternoon Information", "startTime":"1431054000", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"350663877", "title":"Movie Program", "startTime":"1431061200", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"499245733", "title":"Television drama", "startTime":"1431068400", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"152123632", "title":"News of Afternoon", "startTime":"1431072000", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"982508510", "title":"Entertainment Program", "startTime":"1431079200", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"652088739", "title":"Music Program", "startTime":"1431086400", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"148961430", "title":"News of Midnight", "startTime":"1431093600", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771074023", "title":"Entertainment Program", "startTime":"1431097200", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"306546888", "title":"News of Morning", "startTime":"1431118800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771172745", "title":"Information Program", "startTime":"1431129600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"999039096", "title":"Cooking", "startTime":"1431136800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"530278314", "title":"Afternoon Information", "startTime":"1431140400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"350663876", "title":"Movie Program", "startTime":"1431147600", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"499245732", "title":"Television drama", "startTime":"1431154800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"152123631", "title":"News of Afternoon", "startTime":"1431072000", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"982508511", "title":"Entertainment Program", "startTime":"1431165600", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"652088738", "title":"Music Program", "startTime":"1431172800", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"148961431", "title":"News of Midnight", "startTime":"1431180000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771074022", "title":"Entertainment Program", "startTime":"1431183600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"28753297", "title":"News of Morning", "startTime":"1431205200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"73100316", "title":"Information Program", "startTime":"1431216000", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"89241810", "title":"Cooking", "startTime":"1431223200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"201048955", "title":"Afternoon Information", "startTime":"1431226800", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"93917998", "title":"Movie Program", "startTime":"1431234000", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"506404406", "title":"Television drama", "startTime":"1431241200", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"523969994", "title":"News of Afternoon", "startTime":"1431244800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"636968480", "title":"Entertainment Program", "startTime":"1431252000", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"883912480", "title":"Music Program", "startTime":"1431259200", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"422158917", "title":"News of Midnight", "startTime":"1431266400", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"533612446", "title":"Entertainment Program", "startTime":"1431270000", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]}
]
},
{
"_comment": "Channel Data",
"networkId": "32122",
"transportStreamId": "32122",
"serviceId": "40990",
"type": "tv",
"name": "TV #2",
"number" : 2,
"isEmergency": false,
"isFree" : true,
"videoFilePath": "elephants-dream.webm",
"programs": [
{"eventId":"931109607","title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Provide news in morning", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"652081589","title":"Information Program", "startTime":"1430697600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"50732923","title":"Cooking", "startTime":"1430704800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"454916869","title":"Afternoon Information", "startTime":"1430708400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"834076468","title":"Movie Program", "startTime":"1430715600", "duration":"7200", "description":"Classic Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"866266707","title":"Television drama", "startTime":"1430722800", "duration":"3600", "description":"Movie season 2", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"886652749","title":"News of Afternoon", "startTime":"1430726400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"228542771","title":"Entertainment Program", "startTime":"1430733600", "duration":"7200", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"619509509","title":"Music Program", "startTime":"1430740800", "duration":"7200", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"191123325","title":"News of Midnight", "startTime":"1430748000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"742793900","title":"Entertainment Program", "startTime":"1430751600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"750089","title":"News of Morning", "startTime":"1430773200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"721906757","title":"Information Program", "startTime":"1430784000", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"38470096","title":"Cooking", "startTime":"1430791200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"293545622","title":"Afternoon Information", "startTime":"1430794800", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"204855790","title":"Movie Program", "startTime":"1430802000", "duration":"7200", "description":"Classic Movie #2", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"541163301","title":"Television drama", "startTime":"1430809200", "duration":"3600", "description":"Movie season 3", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"371250895","title":"News of Afternoon", "startTime":"1430812800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"294390215","title":"Quiz show", "startTime":"1430820000", "duration":"7200", "description":"Quiz show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"784527971","title":"Documentary", "startTime":"1430827200", "duration":"3600", "description":"Documentary", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"666368711","title":"News of Midnight", "startTime":"1430830800", "duration":"7200", "description":"News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"22754175","title":"Shopping", "startTime":"1430838000", "duration":"7200", "description":"Shopping", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"613868651","title":"News of Morning", "startTime":"1430859600", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"176355635","title":"Information Program", "startTime":"1430870400", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"37461026","title":"Cooking", "startTime":"1430877600", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"187748556","title":"Afternoon Information", "startTime":"1430881200", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"188444168","title":"Movie Program", "startTime":"1430888400", "duration":"7200", "description":"Foreign movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"638623604","title":"Television drama", "startTime":"1430895600", "duration":"3600", "description":"Movie Final episode", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"275870355","title":"News of Afternoon", "startTime":"1430899200", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"682802847","title":"Information Program", "startTime":"1430906400", "duration":"7200", "description":"World wide Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"174619712","title":"Movie", "startTime":"1430913600", "duration":"7200", "description":"French movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"375371806","title":"News of Midnight", "startTime":"1430920800", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"935860948","title":"Entertainment Program", "startTime":"1430924400", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"511731712","title":"News of Morning", "startTime":"1430946000", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"12656422","title":"Information Program", "startTime":"1430956800", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"514993914","title":"Cooking", "startTime":"1430964000", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"420663304","title":"Afternoon Information", "startTime":"1430967600", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"559457772","title":"Movie Program", "startTime":"1430974800", "duration":"7200", "description":"Foreign movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"731385902","title":"Television drama", "startTime":"1430982000", "duration":"3600", "description":"Movie season 3", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"340663640","title":"News of Afternoon", "startTime":"1430985600", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"633526889","title":"Baseball", "startTime":"1430992800", "duration":"7200", "description":"Live", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"73917208","title":"Information program", "startTime":"1431000000", "duration":"7200", "description":"Entertainment", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"350618661","title":"News of Midnight", "startTime":"1431007200", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"190241230","title":"Movie", "startTime":"1431010800", "duration":"7200", "description":"Asia Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"627256776","title":"News of Morning", "startTime":"1431032400", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"27672509","title":"Information Program", "startTime":"1431043200", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"994916158","title":"Cooking", "startTime":"1431050400", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"424933580","title":"Afternoon Information", "startTime":"1431054000", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"104913352","title":"Movie Program", "startTime":"1431061200", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"407112701","title":"Television drama", "startTime":"1431068400", "duration":"3600", "description":"Movie season 4", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"813297989","title":"News of Afternoon", "startTime":"1431072000", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"727142931","title":"Succer", "startTime":"1431079200", "duration":"7200", "description":"Live", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"254336897","title":"Documentary #2", "startTime":"1431086400", "duration":"7200", "description":"Documentary", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"470904867","title":"News of Midnight", "startTime":"1431093600", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"519072420","title":"Shopping", "startTime":"1431097200", "duration":"7200", "description":"Shopping", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"323719613","title":"News of Morning", "startTime":"1431118800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"427256319","title":"Animation", "startTime":"1431129600", "duration":"7200", "description":"Animation", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"66267485","title":"Sports", "startTime":"1431136800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"729245832","title":"Afternoon Information", "startTime":"1431140400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"878389413","title":"Racing", "startTime":"1431147600", "duration":"7200", "description":"Racing.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"227030461","title":"Television drama", "startTime":"1431154800", "duration":"3600", "description":"Movie season 5", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"276580860","title":"News of Afternoon", "startTime":"1431158400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"52665079","title":"Quiz program", "startTime":"1431165600", "duration":"7200", "description":"Qiuz", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"735468872","title":"Movie", "startTime":"1431172800", "duration":"7200", "description":"Foreign Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"199513380","title":"Weekly News", "startTime":"1431180000", "duration":"3600", "description":"Weekly News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"988358549","title":"Discussion program", "startTime":"1431183600", "duration":"14400", "description":"Live", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"367745497","title":"News of Morning", "startTime":"1431205200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"136382033","title":"Information Program", "startTime":"1431216000", "duration":"7200", "description":"Information Program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"609470770","title":"Cooking", "startTime":"1431223200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"898683475","title":"Travel Program", "startTime":"1431226800", "duration":"7200", "description":"Travel Program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"722995606","title":"Golf", "startTime":"1431234000", "duration":"7200", "description":"World Golf", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"284154504","title":"Television drama", "startTime":"1431241200", "duration":"3600", "description":"Movie season 6", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"939817891","title":"News of Afternoon", "startTime":"1431244800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"180794533","title":"Educational Program", "startTime":"1431252000", "duration":"3600", "description":"Educational program。", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"32132348","title":"Ceremony program", "startTime":"1431255600", "duration":"3600", "description":"Ceremony program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"297834220","title":"Weekly News", "startTime":"1431259200", "duration":"10800", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"866886159","title":"Weekly news", "startTime":"1431270000", "duration":"7200", "description":"Information program on Monday", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]}
]
},
{
"_comment": "Channel Data",
"networkId": "32132",
"transportStreamId": "32132",
"serviceId": "41020",
"type": "tv",
"name": "TV #3",
"number" : 3,
"isEmergency": false,
"isFree" : true,
"videoFilePath": "gizmo2.mp4",
"programs": [
{"eventId":"734475972", "title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"586216742", "title":"Information Program", "startTime":"1430697600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"861120908", "title":"Cooking", "startTime":"1430704800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"57540864", "title":"Afternoon Information", "startTime":"1430708400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"329867743", "title":"Movie Program", "startTime":"1430715600", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"572563927", "title":"Television drama", "startTime":"1430722800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"363286425", "title":"News of Afternoon", "startTime":"1430726400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"910176581", "title":"Entertainment Program", "startTime":"1430733600", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"566633346", "title":"Music Program", "startTime":"1430740800", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"951603614", "title":"News of Midnight", "startTime":"1430748000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"773101560", "title":"Entertainment Program", "startTime":"1430751600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"185460210", "title":"News of Morning", "startTime":"1430773200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"797456853", "title":"Information Program", "startTime":"1430784000", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"976825440", "title":"Cooking", "startTime":"1430791200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"420857949", "title":"Afternoon Information", "startTime":"1430794800", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"749006350", "title":"Movie Program", "startTime":"1430802000", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"927193", "title":"Television drama", "startTime":"1430809200", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"47696273", "title":"News of Afternoon", "startTime":"1430812800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"231995539", "title":"Entertainment Program", "startTime":"1430820000", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"501545076", "title":"Music Program", "startTime":"1430827200", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"263172731", "title":"News of Midnight", "startTime":"1430834400", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"658267606", "title":"Entertainment Program", "startTime":"1430838000", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"431111532", "title":"News of Morning", "startTime":"1430859600", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"363586806", "title":"Information Program", "startTime":"1430870400", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"775168902", "title":"Cooking", "startTime":"1430877600", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"839588503", "title":"Afternoon Information", "startTime":"1430881200", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"65487862", "title":"Movie Program", "startTime":"1430888400", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"508594474", "title":"Television drama", "startTime":"1430895600", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"331004789", "title":"News of Afternoon", "startTime":"1430899200", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"47225257", "title":"Entertainment Program", "startTime":"1430906400", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"555267371", "title":"Music Program", "startTime":"1430913600", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"627197565", "title":"News of Midnight", "startTime":"1430920800", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"772640894", "title":"Entertainment Program", "startTime":"1430924400", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"631125294", "title":"News of Morning", "startTime":"1430946000", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"44026761", "title":"Information Program", "startTime":"1430956800", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"132622366", "title":"Cooking", "startTime":"1430964000", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"680336718", "title":"Afternoon Information", "startTime":"1430967600", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"904310744", "title":"Movie Program", "startTime":"1430974800", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"796724154", "title":"Television drama", "startTime":"1430982000", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"281676124", "title":"News of Afternoon", "startTime":"1430985600", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"990618634", "title":"Entertainment Program", "startTime":"1430992800", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"200131511", "title":"Music Program", "startTime":"1431000000", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"352281886", "title":"News of Midnight", "startTime":"1431007200", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"293989564", "title":"Entertainment Program", "startTime":"1431010800", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"306546889", "title":"News of Morning", "startTime":"1431032400", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771172746", "title":"Information Program", "startTime":"1431043200", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"999039097", "title":"Cooking", "startTime":"1431050400", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"530278314", "title":"Afternoon Information", "startTime":"1431054000", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"350663877", "title":"Movie Program", "startTime":"1431061200", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"499245732", "title":"Television drama", "startTime":"1431068400", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"152123632", "title":"News of Afternoon", "startTime":"1431072000", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"982508510", "title":"Entertainment Program", "startTime":"1431079200", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"652088739", "title":"Music Program", "startTime":"1431086400", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"148961430", "title":"News of Midnight", "startTime":"1431093600", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771074023", "title":"Entertainment Program", "startTime":"1431097200", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"306546886", "title":"News of Morning", "startTime":"1431118800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771172745", "title":"Information Program", "startTime":"1431129600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"999039096", "title":"Cooking", "startTime":"1431136800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"530278315", "title":"Afternoon Information", "startTime":"1431140400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"350663876", "title":"Movie Program", "startTime":"1431147600", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"499245733", "title":"Television drama", "startTime":"1431154800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"152123631", "title":"News of Afternoon", "startTime":"1431072000", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"982508511", "title":"Entertainment Program", "startTime":"1431165600", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"652088738", "title":"Music Program", "startTime":"1431172800", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"148961431", "title":"News of Midnight", "startTime":"1431180000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771074022", "title":"Entertainment Program", "startTime":"1431183600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"28753297", "title":"News of Morning", "startTime":"1431205200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"73100316", "title":"Information Program", "startTime":"1431216000", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"89241810", "title":"Cooking", "startTime":"1431223200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"201048955", "title":"Afternoon Information", "startTime":"1431226800", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"93917998", "title":"Movie Program", "startTime":"1431234000", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"506404406", "title":"Television drama", "startTime":"1431241200", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"523969994", "title":"News of Afternoon", "startTime":"1431244800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"636968480", "title":"Entertainment Program", "startTime":"1431252000", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"883912480", "title":"Music Program", "startTime":"1431259200", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"422158917", "title":"News of Midnight", "startTime":"1431266400", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"533612446", "title":"Entertainment Program", "startTime":"1431270000", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]}
]
},
{
"_comment": "Channel Data",
"networkId": "32142",
"transportStreamId": "32142",
"serviceId": "41040",
"type": "tv",
"name": "TV #5",
"number" : 4,
"isEmergency": false,
"isFree" : true,
"videoFilePath": "elephants-dream.webm",
"programs": [
{"eventId":"931109607","title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Provide news in morning", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"652081589","title":"Information Program", "startTime":"1430697600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"50732923","title":"Cooking", "startTime":"1430704800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"454916869","title":"Afternoon Information", "startTime":"1430708400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"834076468","title":"Movie Program", "startTime":"1430715600", "duration":"7200", "description":"Classic Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"866266707","title":"Television drama", "startTime":"1430722800", "duration":"3600", "description":"Movie season 2", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"886652749","title":"News of Afternoon", "startTime":"1430726400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"228542771","title":"Entertainment Program", "startTime":"1430733600", "duration":"7200", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"619509509","title":"Music Program", "startTime":"1430740800", "duration":"7200", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"191123325","title":"News of Midnight", "startTime":"1430748000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"742793900","title":"Entertainment Program", "startTime":"1430751600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"750089","title":"News of Morning", "startTime":"1430773200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"721906757","title":"Information Program", "startTime":"1430784000", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"38470096","title":"Cooking", "startTime":"1430791200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"293545622","title":"Afternoon Information", "startTime":"1430794800", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"204855790","title":"Movie Program", "startTime":"1430802000", "duration":"7200", "description":"Classic Movie #2", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"541163301","title":"Television drama", "startTime":"1430809200", "duration":"3600", "description":"Movie season 3", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"371250895","title":"News of Afternoon", "startTime":"1430812800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"294390215","title":"Quiz show", "startTime":"1430820000", "duration":"7200", "description":"Quiz show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"784527971","title":"Documentary", "startTime":"1430827200", "duration":"3600", "description":"Documentary", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"666368711","title":"News of Midnight", "startTime":"1430830800", "duration":"7200", "description":"News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"22754175","title":"Shopping", "startTime":"1430838000", "duration":"7200", "description":"Shopping", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"613868651","title":"News of Morning", "startTime":"1430859600", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"176355635","title":"Information Program", "startTime":"1430870400", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"37461026","title":"Cooking", "startTime":"1430877600", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"187748556","title":"Afternoon Information", "startTime":"1430881200", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"188444168","title":"Movie Program", "startTime":"1430888400", "duration":"7200", "description":"Foreign movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"638623604","title":"Television drama", "startTime":"1430895600", "duration":"3600", "description":"Movie Final episode", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"275870355","title":"News of Afternoon", "startTime":"1430899200", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"682802847","title":"Information Program", "startTime":"1430906400", "duration":"7200", "description":"World wide Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"174619712","title":"Movie", "startTime":"1430913600", "duration":"7200", "description":"French movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"375371806","title":"News of Midnight", "startTime":"1430920800", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"935860948","title":"Entertainment Program", "startTime":"1430924400", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"511731712","title":"News of Morning", "startTime":"1430946000", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"12656422","title":"Information Program", "startTime":"1430956800", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"514993914","title":"Cooking", "startTime":"1430964000", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"420663304","title":"Afternoon Information", "startTime":"1430967600", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"559457772","title":"Movie Program", "startTime":"1430974800", "duration":"7200", "description":"Foreign movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"731385902","title":"Television drama", "startTime":"1430982000", "duration":"3600", "description":"Movie season 3", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"340663640","title":"News of Afternoon", "startTime":"1430985600", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"633526889","title":"Baseball", "startTime":"1430992800", "duration":"7200", "description":"Live", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"73917208","title":"Information program", "startTime":"1431000000", "duration":"7200", "description":"Entertainment", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"350618661","title":"News of Midnight", "startTime":"1431007200", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"190241230","title":"Movie", "startTime":"1431010800", "duration":"7200", "description":"Asia Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"627256776","title":"News of Morning", "startTime":"1431032400", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"27672509","title":"Information Program", "startTime":"1431043200", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"994916158","title":"Cooking", "startTime":"1431050400", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"424933580","title":"Afternoon Information", "startTime":"1431054000", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"104913352","title":"Movie Program", "startTime":"1431061200", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"407112701","title":"Television drama", "startTime":"1431068400", "duration":"3600", "description":"Movie season 4", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"813297989","title":"News of Afternoon", "startTime":"1431072000", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"727142931","title":"Succer", "startTime":"1431079200", "duration":"7200", "description":"Live", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"254336897","title":"Documentary #2", "startTime":"1431086400", "duration":"7200", "description":"Documentary", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"470904867","title":"News of Midnight", "startTime":"1431093600", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"519072420","title":"Shopping", "startTime":"1431097200", "duration":"7200", "description":"Shopping", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"323719613","title":"News of Morning", "startTime":"1431118800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"427256319","title":"Animation", "startTime":"1431129600", "duration":"7200", "description":"Animation", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"66267485","title":"Sports", "startTime":"1431136800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"729245832","title":"Afternoon Information", "startTime":"1431140400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"878389413","title":"Racing", "startTime":"1431147600", "duration":"7200", "description":"Racing.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"227030461","title":"Television drama", "startTime":"1431154800", "duration":"3600", "description":"Movie season 5", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"276580860","title":"News of Afternoon", "startTime":"1431158400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"52665079","title":"Quiz program", "startTime":"1431165600", "duration":"7200", "description":"Quiz", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"735468872","title":"Movie", "startTime":"1431172800", "duration":"7200", "description":"Foreign Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"199513380","title":"Weekly News", "startTime":"1431180000", "duration":"3600", "description":"Weekly News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"988358549","title":"Discussion program", "startTime":"1431183600", "duration":"14400", "description":"Live", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"367745497","title":"News of Morning", "startTime":"1431205200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"136382033","title":"Information Program", "startTime":"1431216000", "duration":"7200", "description":"Information Program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"609470770","title":"Cooking", "startTime":"1431223200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"898683475","title":"Travel Program", "startTime":"1431226800", "duration":"7200", "description":"Travel Program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"722995606","title":"Golf", "startTime":"1431234000", "duration":"7200", "description":"World Golf", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"284154504","title":"Television drama", "startTime":"1431241200", "duration":"3600", "description":"Movie season 6", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"939817891","title":"News of Afternoon", "startTime":"1431244800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"180794533","title":"Educational Program", "startTime":"1431252000", "duration":"3600", "description":"Educational program。", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"32132348","title":"Ceremony program", "startTime":"1431255600", "duration":"3600", "description":"Ceremony program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"297834220","title":"Weekly News", "startTime":"1431259200", "duration":"10800", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"866886159","title":"Weekly news", "startTime":"1431270000", "duration":"7200", "description":"Information program on Monday", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]}
]
},
{
"_comment": "Channel Data",
"networkId": "32152",
"transportStreamId": "32152",
"serviceId": "41060",
"type": "tv",
"name": "TV #5",
"number" : 5,
"isEmergency": false,
"isFree" : true,
"videoFilePath": "gizmo2.mp4",
"programs": [
{"eventId":"734475972", "title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"586216742", "title":"Information Program", "startTime":"1430697600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"861120908", "title":"Cooking", "startTime":"1430704800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"57540864", "title":"Afternoon Information", "startTime":"1430708400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"329867743", "title":"Movie Program", "startTime":"1430715600", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"572563927", "title":"Television drama", "startTime":"1430722800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"363286425", "title":"News of Afternoon", "startTime":"1430726400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"910176581", "title":"Entertainment Program", "startTime":"1430733600", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"566633346", "title":"Music Program", "startTime":"1430740800", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"951603614", "title":"News of Midnight", "startTime":"1430748000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"773101560", "title":"Entertainment Program", "startTime":"1430751600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"185460210", "title":"News of Morning", "startTime":"1430773200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"797456853", "title":"Information Program", "startTime":"1430784000", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"976825440", "title":"Cooking", "startTime":"1430791200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"420857949", "title":"Afternoon Information", "startTime":"1430794800", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"749006350", "title":"Movie Program", "startTime":"1430802000", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"927193", "title":"Television drama", "startTime":"1430809200", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"47696273", "title":"News of Afternoon", "startTime":"1430812800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"231995539", "title":"Entertainment Program", "startTime":"1430820000", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"501545076", "title":"Music Program", "startTime":"1430827200", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"263172731", "title":"News of Midnight", "startTime":"1430834400", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"658267606", "title":"Entertainment Program", "startTime":"1430838000", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"431111532", "title":"News of Morning", "startTime":"1430859600", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"363586806", "title":"Information Program", "startTime":"1430870400", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"775168902", "title":"Cooking", "startTime":"1430877600", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"839588503", "title":"Afternoon Information", "startTime":"1430881200", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"65487862", "title":"Movie Program", "startTime":"1430888400", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"508594474", "title":"Television drama", "startTime":"1430895600", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"331004789", "title":"News of Afternoon", "startTime":"1430899200", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"47225257", "title":"Entertainment Program", "startTime":"1430906400", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"555267371", "title":"Music Program", "startTime":"1430913600", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"627197565", "title":"News of Midnight", "startTime":"1430920800", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"772640894", "title":"Entertainment Program", "startTime":"1430924400", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"631125294", "title":"News of Morning", "startTime":"1430946000", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"44026761", "title":"Information Program", "startTime":"1430956800", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"132622366", "title":"Cooking", "startTime":"1430964000", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"680336718", "title":"Afternoon Information", "startTime":"1430967600", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"904310744", "title":"Movie Program", "startTime":"1430974800", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"796724154", "title":"Television drama", "startTime":"1430982000", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"281676124", "title":"News of Afternoon", "startTime":"1430985600", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"990618634", "title":"Entertainment Program", "startTime":"1430992800", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"200131511", "title":"Music Program", "startTime":"1431000000", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"352281886", "title":"News of Midnight", "startTime":"1431007200", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"293989564", "title":"Entertainment Program", "startTime":"1431010800", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"306546885", "title":"News of Morning", "startTime":"1431032400", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771172746", "title":"Information Program", "startTime":"1431043200", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"999039097", "title":"Cooking", "startTime":"1431050400", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"530278314", "title":"Afternoon Information", "startTime":"1431054000", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"350663877", "title":"Movie Program", "startTime":"1431061200", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"499245733", "title":"Television drama", "startTime":"1431068400", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"152123632", "title":"News of Afternoon", "startTime":"1431072000", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"982508510", "title":"Entertainment Program", "startTime":"1431079200", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"652088739", "title":"Music Program", "startTime":"1431086400", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"148961430", "title":"News of Midnight", "startTime":"1431093600", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771074023", "title":"Entertainment Program", "startTime":"1431097200", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"306546884", "title":"News of Morning", "startTime":"1431118800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771172745", "title":"Information Program", "startTime":"1431129600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"999039096", "title":"Cooking", "startTime":"1431136800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"530278315", "title":"Afternoon Information", "startTime":"1431140400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"350663876", "title":"Movie Program", "startTime":"1431147600", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"499245732", "title":"Television drama", "startTime":"1431154800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"152123631", "title":"News of Afternoon", "startTime":"1431072000", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"982508511", "title":"Entertainment Program", "startTime":"1431165600", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"652088738", "title":"Music Program", "startTime":"1431172800", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"148961431", "title":"News of Midnight", "startTime":"1431180000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771074022", "title":"Entertainment Program", "startTime":"1431183600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"28753297", "title":"News of Morning", "startTime":"1431205200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"73100316", "title":"Information Program", "startTime":"1431216000", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"89241810", "title":"Cooking", "startTime":"1431223200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"201048955", "title":"Afternoon Information", "startTime":"1431226800", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"93917998", "title":"Movie Program", "startTime":"1431234000", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"506404406", "title":"Television drama", "startTime":"1431241200", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"523969994", "title":"News of Afternoon", "startTime":"1431244800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"636968480", "title":"Entertainment Program", "startTime":"1431252000", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"883912480", "title":"Music Program", "startTime":"1431259200", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"422158917", "title":"News of Midnight", "startTime":"1431266400", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"533612446", "title":"Entertainment Program", "startTime":"1431270000", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]}
]
},
{
"_comment": "Channel Data",
"networkId": "32162",
"transportStreamId": "32162",
"serviceId": "41080",
"type": "tv",
"name": "TV #6",
"number" : 6,
"isEmergency": false,
"isFree" : true,
"videoFilePath": "elephants-dream.webm",
"programs": [
{"eventId":"931109607","title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Provide news in morning", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"652081589","title":"Information Program", "startTime":"1430697600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"50732923" ,"title":"Cooking", "startTime":"1430704800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"454916869","title":"Afternoon Information", "startTime":"1430708400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"834076468","title":"Movie Program", "startTime":"1430715600", "duration":"7200", "description":"Classic Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"866266707","title":"Television drama", "startTime":"1430722800", "duration":"3600", "description":"Movie season 2", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"886652749","title":"News of Afternoon", "startTime":"1430726400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"228542771","title":"Entertainment Program", "startTime":"1430733600", "duration":"7200", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"619509509","title":"Music Program", "startTime":"1430740800", "duration":"7200", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"191123325","title":"News of Midnight", "startTime":"1430748000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"742793900","title":"Entertainment Program", "startTime":"1430751600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"750089", "title":"News of Morning", "startTime":"1430773200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"721906757","title":"Information Program", "startTime":"1430784000", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"38470096", "title":"Cooking", "startTime":"1430791200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"293545622","title":"Afternoon Information", "startTime":"1430794800", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"204855790","title":"Movie Program", "startTime":"1430802000", "duration":"7200", "description":"Classic Movie #2", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"541163301","title":"Television drama", "startTime":"1430809200", "duration":"3600", "description":"Movie season 3", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"371250895","title":"News of Afternoon", "startTime":"1430812800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"294390215","title":"Quiz show", "startTime":"1430820000", "duration":"7200", "description":"Quiz show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"784527971","title":"Documentary", "startTime":"1430827200", "duration":"3600", "description":"Documentary", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"666368711","title":"News of Midnight", "startTime":"1430830800", "duration":"7200", "description":"News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"22754175", "title":"Shopping", "startTime":"1430838000", "duration":"7200", "description":"Shopping", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"613868651","title":"News of Morning", "startTime":"1430859600", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"176355635","title":"Information Program", "startTime":"1430870400", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"37461026", "title":"Cooking", "startTime":"1430877600", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"187748556","title":"Afternoon Information", "startTime":"1430881200", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"188444168","title":"Movie Program", "startTime":"1430888400", "duration":"7200", "description":"Foreign movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"638623604","title":"Television drama", "startTime":"1430895600", "duration":"3600", "description":"Movie Final episode", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"275870355","title":"News of Afternoon", "startTime":"1430899200", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"682802847","title":"Information Program", "startTime":"1430906400", "duration":"7200", "description":"World wide Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"174619712","title":"Movie", "startTime":"1430913600", "duration":"7200", "description":"French movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"375371806","title":"News of Midnight", "startTime":"1430920800", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"935860948","title":"Entertainment Program", "startTime":"1430924400", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"511731712","title":"News of Morning", "startTime":"1430946000", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"12656422", "title":"Information Program", "startTime":"1430956800", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"514993914","title":"Cooking", "startTime":"1430964000", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"420663304","title":"Afternoon Information", "startTime":"1430967600", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"559457772","title":"Movie Program", "startTime":"1430974800", "duration":"7200", "description":"Foreign movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"731385902","title":"Television drama", "startTime":"1430982000", "duration":"3600", "description":"Movie season 3", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"340663640","title":"News of Afternoon", "startTime":"1430985600", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"633526889","title":"Baseball", "startTime":"1430992800", "duration":"7200", "description":"Live", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"73917208", "title":"Information program", "startTime":"1431000000", "duration":"7200", "description":"Entertainment", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"350618661","title":"News of Midnight", "startTime":"1431007200", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"190241230","title":"Movie", "startTime":"1431010800", "duration":"7200", "description":"Asia Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"627256776","title":"News of Morning", "startTime":"1431032400", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"27672509", "title":"Information Program", "startTime":"1431043200", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"994916158","title":"Cooking", "startTime":"1431050400", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"424933580","title":"Afternoon Information", "startTime":"1431054000", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"104913352","title":"Movie Program", "startTime":"1431061200", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"407112701","title":"Television drama", "startTime":"1431068400", "duration":"3600", "description":"Movie season 4", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"813297989","title":"News of Afternoon", "startTime":"1431072000", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"727142931","title":"Succer", "startTime":"1431079200", "duration":"7200", "description":"Live", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"254336897","title":"Documentary #2", "startTime":"1431086400", "duration":"7200", "description":"Documentary", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"470904867","title":"News of Midnight", "startTime":"1431093600", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"519072420","title":"Shopping", "startTime":"1431097200", "duration":"7200", "description":"Shopping", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"323719613","title":"News of Morning", "startTime":"1431118800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"427256319","title":"Animation", "startTime":"1431129600", "duration":"7200", "description":"Animation", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"66267485","title":"Sports", "startTime":"1431136800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"729245832","title":"Afternoon Information", "startTime":"1431140400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"878389413","title":"Racing", "startTime":"1431147600", "duration":"7200", "description":"Racing.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"227030461","title":"Television drama", "startTime":"1431154800", "duration":"3600", "description":"Movie season 5", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"276580860","title":"News of Afternoon", "startTime":"1431158400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"52665079","title":"Quiz program", "startTime":"1431165600", "duration":"7200", "description":"Quiz", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"735468872","title":"Movie", "startTime":"1431172800", "duration":"7200", "description":"Foreign Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"199513380","title":"Weekly News", "startTime":"1431180000", "duration":"3600", "description":"Weekly News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"988358549","title":"Discussion program", "startTime":"1431183600", "duration":"14400", "description":"Live", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"367745497","title":"News of Morning", "startTime":"1431205200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"136382033","title":"Information Program", "startTime":"1431216000", "duration":"7200", "description":"Information Program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"609470770","title":"Cooking", "startTime":"1431223200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"898683475","title":"Travel Program", "startTime":"1431226800", "duration":"7200", "description":"Travel Program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"722995606","title":"Golf", "startTime":"1431234000", "duration":"7200", "description":"World Golf", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"284154504","title":"Television drama", "startTime":"1431241200", "duration":"3600", "description":"Movie season 6", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"939817891","title":"News of Afternoon", "startTime":"1431244800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"180794533","title":"Educational Program", "startTime":"1431252000", "duration":"3600", "description":"Educational program。", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"32132348","title":"Ceremony program", "startTime":"1431255600", "duration":"3600", "description":"Ceremony program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"297834220","title":"Weekly News", "startTime":"1431259200", "duration":"10800", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"866886159","title":"Weekly news", "startTime":"1431270000", "duration":"7200", "description":"Information program on Monday", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]}
]
},
{
"_comment": "Channel Data",
"networkId": "32172",
"transportStreamId": "32172",
"serviceId": "41100",
"type": "tv",
"name": "TV #7",
"number" : 7,
"isEmergency": false,
"isFree" : true,
"videoFilePath": "gizmo2.mp4",
"programs": [
{"eventId":"734475972", "title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"586216742", "title":"Information Program", "startTime":"1430697600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"861120908", "title":"Cooking", "startTime":"1430704800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"57540864", "title":"Afternoon Information", "startTime":"1430708400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"329867743", "title":"Movie Program", "startTime":"1430715600", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"572563927", "title":"Television drama", "startTime":"1430722800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"363286425", "title":"News of Afternoon", "startTime":"1430726400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"910176581", "title":"Entertainment Program", "startTime":"1430733600", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"566633346", "title":"Music Program", "startTime":"1430740800", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"951603614", "title":"News of Midnight", "startTime":"1430748000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"773101560", "title":"Entertainment Program", "startTime":"1430751600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"185460210", "title":"News of Morning", "startTime":"1430773200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"797456853", "title":"Information Program", "startTime":"1430784000", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"976825440", "title":"Cooking", "startTime":"1430791200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"420857949", "title":"Afternoon Information", "startTime":"1430794800", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"749006350", "title":"Movie Program", "startTime":"1430802000", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"927193", "title":"Television drama", "startTime":"1430809200", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"47696273", "title":"News of Afternoon", "startTime":"1430812800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"231995539", "title":"Entertainment Program", "startTime":"1430820000", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"501545076", "title":"Music Program", "startTime":"1430827200", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"263172731", "title":"News of Midnight", "startTime":"1430834400", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"658267606", "title":"Entertainment Program", "startTime":"1430838000", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"431111532", "title":"News of Morning", "startTime":"1430859600", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"363586806", "title":"Information Program", "startTime":"1430870400", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"775168902", "title":"Cooking", "startTime":"1430877600", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"839588503", "title":"Afternoon Information", "startTime":"1430881200", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"65487862", "title":"Movie Program", "startTime":"1430888400", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"508594474", "title":"Television drama", "startTime":"1430895600", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"331004789", "title":"News of Afternoon", "startTime":"1430899200", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"47225257", "title":"Entertainment Program", "startTime":"1430906400", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"555267371", "title":"Music Program", "startTime":"1430913600", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"627197565", "title":"News of Midnight", "startTime":"1430920800", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"772640894", "title":"Entertainment Program", "startTime":"1430924400", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"631125294", "title":"News of Morning", "startTime":"1430946000", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"44026761", "title":"Information Program", "startTime":"1430956800", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"132622366", "title":"Cooking", "startTime":"1430964000", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"680336718", "title":"Afternoon Information", "startTime":"1430967600", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"904310744", "title":"Movie Program", "startTime":"1430974800", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"796724154", "title":"Television drama", "startTime":"1430982000", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"281676124", "title":"News of Afternoon", "startTime":"1430985600", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"990618634", "title":"Entertainment Program", "startTime":"1430992800", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"200131511", "title":"Music Program", "startTime":"1431000000", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"352281886", "title":"News of Midnight", "startTime":"1431007200", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"293989564", "title":"Entertainment Program", "startTime":"1431010800", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"306546883", "title":"News of Morning", "startTime":"1431032400", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771172746", "title":"Information Program", "startTime":"1431043200", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"999039097", "title":"Cooking", "startTime":"1431050400", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"530278314", "title":"Afternoon Information", "startTime":"1431054000", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"350663877", "title":"Movie Program", "startTime":"1431061200", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"499245733", "title":"Television drama", "startTime":"1431068400", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"152123632", "title":"News of Afternoon", "startTime":"1431072000", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"982508510", "title":"Entertainment Program", "startTime":"1431079200", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"652088739", "title":"Music Program", "startTime":"1431086400", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"148961430", "title":"News of Midnight", "startTime":"1431093600", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771074023", "title":"Entertainment Program", "startTime":"1431097200", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"306546882", "title":"News of Morning", "startTime":"1431118800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771172745", "title":"Information Program", "startTime":"1431129600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"999039096", "title":"Cooking", "startTime":"1431136800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"530278315", "title":"Afternoon Information", "startTime":"1431140400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"350663876", "title":"Movie Program", "startTime":"1431147600", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"499245732", "title":"Television drama", "startTime":"1431154800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"152123631", "title":"News of Afternoon", "startTime":"1431072000", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"982508511", "title":"Entertainment Program", "startTime":"1431165600", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"652088738", "title":"Music Program", "startTime":"1431172800", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"148961431", "title":"News of Midnight", "startTime":"1431180000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"771074022", "title":"Entertainment Program", "startTime":"1431183600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"28753297", "title":"News of Morning", "startTime":"1431205200", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"73100316", "title":"Information Program", "startTime":"1431216000", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"89241810", "title":"Cooking", "startTime":"1431223200", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"201048955", "title":"Afternoon Information", "startTime":"1431226800", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"93917998", "title":"Movie Program", "startTime":"1431234000", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"506404406", "title":"Television drama", "startTime":"1431241200", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"523969994", "title":"News of Afternoon", "startTime":"1431244800", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"636968480", "title":"Entertainment Program", "startTime":"1431252000", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"883912480", "title":"Music Program", "startTime":"1431259200", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"422158917", "title":"News of Midnight", "startTime":"1431266400", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"533612446", "title":"Entertainment Program", "startTime":"1431270000", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]}
]
}
]
}
]
},
{
"_comment": "Tuner Data",
"id":"3",
"supportedType": ["isdb-t"],
"sources": [
{
"_comment": "Source Data",
"type": "isdb-t",
"channels": [
{
"_comment": "Channel Data",
"networkId": "32182",
"transportStreamId": "32182",
"serviceId": "51100",
"type": "tv",
"name": "CS #1",
"number" : 1,
"isEmergency": false,
"isFree" : true,
"videoFilePath": "gizmo2.mp4",
"programs": [
{"eventId":"734475972", "title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"586216742", "title":"Information Program", "startTime":"1430697600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"861120908", "title":"Cooking", "startTime":"1430704800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"57540864", "title":"Afternoon Information", "startTime":"1430708400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"329867743", "title":"Movie Program", "startTime":"1430715600", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"572563927", "title":"Television drama", "startTime":"1430722800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"363286425", "title":"News of Afternoon", "startTime":"1430726400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"910176581", "title":"Entertainment Program", "startTime":"1430733600", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"566633346", "title":"Music Program", "startTime":"1430740800", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"951603614", "title":"News of Midnight", "startTime":"1430748000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"773101560", "title":"Entertainment Program", "startTime":"1430751600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]}
]
},
{
"_comment": "Channel Data",
"networkId": "32184",
"transportStreamId": "32184",
"serviceId": "51200",
"type": "tv",
"name": "CS #2",
"number" : 2,
"isEmergency": false,
"isFree" : true,
"videoFilePath": "elephants-dream.webm",
"programs": [
{"eventId":"734475972", "title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"586216742", "title":"Information Program", "startTime":"1430697600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"861120908", "title":"Cooking", "startTime":"1430704800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"57540864", "title":"Afternoon Information", "startTime":"1430708400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"329867743", "title":"Movie Program", "startTime":"1430715600", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"572563927", "title":"Television drama", "startTime":"1430722800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"363286425", "title":"News of Afternoon", "startTime":"1430726400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"910176581", "title":"Entertainment Program", "startTime":"1430733600", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"566633346", "title":"Music Program", "startTime":"1430740800", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"951603614", "title":"News of Midnight", "startTime":"1430748000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"773101560", "title":"Entertainment Program", "startTime":"1430751600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]}
]
},
{
"_comment": "Channel Data",
"networkId": "32290",
"transportStreamId": "32290",
"serviceId": "51250",
"type": "tv",
"name": "CS #3",
"number" : 3,
"isEmergency": false,
"isFree" : true,
"videoFilePath": "gizmo2.mp4",
"programs": [
{"eventId":"734475972", "title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Morning News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"586216742", "title":"Information Program", "startTime":"1430697600", "duration":"7200", "description":"Provide program Information.", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"861120908", "title":"Cooking", "startTime":"1430704800", "duration":"3600", "description":"Today's Cooking", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"57540864", "title":"Afternoon Information", "startTime":"1430708400", "duration":"7200", "description":"Aftenoon program Information", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"329867743", "title":"Movie Program", "startTime":"1430715600", "duration":"7200", "description":"Today's Movie", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"572563927", "title":"Television drama", "startTime":"1430722800", "duration":"3600", "description":"Educational program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"363286425", "title":"News of Afternoon", "startTime":"1430726400", "duration":"7200", "description":"Provide Today's News", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"910176581", "title":"Entertainment Program", "startTime":"1430733600", "duration":"3600", "description":"Entertainment show", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"566633346", "title":"Music Program", "startTime":"1430740800", "duration":"3600", "description":"Poopular Music program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"951603614", "title":"News of Midnight", "startTime":"1430748000", "duration":"3600", "description":"Summary of today news", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]},
{"eventId":"773101560", "title":"Entertainment Program", "startTime":"1430751600", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]}
]
}
]
}
]
}
]
}

BIN
gaia/build/config/wallpaper.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

View file

@ -0,0 +1,47 @@
'use strict';
/**
* This module contains all the build time generation logic needed to
* use the import functionality both by the FTU and Comms Applications
*
* The Functions exported by this module are used by the build scripts
* of Comms and FTU apps
*/
const utils = require('./utils');
// Generates the communications services configuration as JS file
// loaded by the application (parameters.js)
function generateServicesConfig(config) {
var importServicesFile = utils.resolve(config.CONTACTS_IMPORT_SERVICES_PATH,
config.GAIA_DIR);
if (!importServicesFile.exists()) {
   throw new Error('file not found: ' + importServicesFile.path);
 }
var importServices = utils.getJSON(importServicesFile);
// Bug 883344 Only use default facebook app id if is mozilla partner build
if (config.OFFICIAL === '1') {
importServices.facebook.applicationId = '395559767228801';
importServices.live.applicationId = '00000000440F8B08';
}
var resultFile = utils.resolve(utils.joinPath('shared', 'pages', 'import',
'js', 'parameters.js'), config.GAIA_DIR);
var jsPrefix = 'var oauthflow = this.oauthflow || {}; oauthflow.params = ';
var content = jsPrefix + JSON.stringify(importServices) + ';';
if (resultFile.exists()) {
let prev = utils.getFileContent(resultFile);
if (prev === content) {
return;
}
}
utils.writeContent(resultFile, content);
}
exports.execute = function(config) {
generateServicesConfig(config);
};

View file

@ -0,0 +1,92 @@
'use strict';
/**
* This script is to copy external app from build_stage to profile folder,
* which may rename the name of stage folder to uuid name.
* This task will do three things.
* 1. Copy manifest to profile: generally we got manifest from
* webapp-manifest.js unless manifest is generated from Makefile of app.
* so we will copy manifest.webapp if it's avaiable in build_stage/ .
* 2. Copy external app to profile dir.
* 3. Generate webapps.json from webapps_stage.json and copy to profile dir.
*/
var utils = require('./utils');
function moveExternalApp(webapp, source, destination) {
// In case of packaged app, just copy `application.zip` and `update.webapp`
if (webapp.pckManifest) {
let updateManifest = utils.getFile(source.path, 'update.webapp');
if (!updateManifest.exists()) {
throw 'External packaged webapp `' + webapp.domain + ' is ' +
'missing an `update.webapp` file. This JSON file ' +
'contains a `package_path` attribute specifying where ' +
'to download the application zip package from the origin ' +
'specified in `metadata.json` file.';
}
let appPackage = utils.getFile(source.path, 'application.zip');
utils.copyFileTo(appPackage, destination, 'application.zip');
utils.copyFileTo(updateManifest, destination, 'update.webapp');
} else {
var manifestFile = utils.getFile(webapp.manifestFilePath);
utils.copyFileTo(manifestFile, destination, 'manifest.webapp');
// This is an hosted app. Check if there is an offline cache.
let srcCacheFolder = utils.getFile(source.path, 'cache');
if (srcCacheFolder.exists()) {
let cacheManifest = utils.getFile(srcCacheFolder.path,
'manifest.appcache');
if (!cacheManifest.exists()) {
throw 'External webapp `' + webapp.domain +
'` has a cache directory without `manifest.appcache`' +
' file.';
}
// If it has a cache, it should also have a resources_metadata.json file
let resourcesMetadata = utils.getFile(source.path,
'resources_metadata.json');
if (!resourcesMetadata.exists()) {
throw 'External webapp `' + webapp.domain +
'` has a cache directory without an associated `resources.metadata`' +
' file.';
}
utils.copyFileTo(resourcesMetadata, destination,
'resources_metadata.json');
// Copy recursively the whole cache folder to webapp folder
let targetCacheFolder = utils.getFile(destination, 'cache');
utils.copyRec(srcCacheFolder, targetCacheFolder);
}
}
}
function execute(options) {
var webapp = options.webapp;
var webappManifest = utils.getFile(webapp.buildDirectoryFilePath,
'manifest.webapp');
var updateManifest = utils.getFile(webapp.buildDirectoryFilePath,
'update.webapp');
var stageManifest = webappManifest.exists() ? webappManifest : updateManifest;
if (!stageManifest.exists()) {
return;
}
var profileDirectoryFile = utils.getFile(webapp.profileDirectoryFilePath);
utils.ensureFolderExists(profileDirectoryFile);
if (utils.isExternalApp(webapp)) {
var appSource = utils.getFile(webapp.buildDirectoryFilePath);
moveExternalApp(webapp, appSource, profileDirectoryFile.path);
return;
}
// We'll remove it once bug 968666 is merged.
var targetManifest = utils.getFile(webapp.profileDirectoryFilePath);
utils.copyFileTo(stageManifest, targetManifest.path, 'manifest.webapp');
}
exports.execute = execute;

64
gaia/build/copy-common-files.js Executable file
View file

@ -0,0 +1,64 @@
'use strict';
/**
* Copy common files such as webapps.json
*/
var utils = require('./utils');
function cleanProfile(webappsDir) {
// Profile can contain folders with a generated uuid that need to be deleted
// or apps will be duplicated.
var expreg = /^{[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}}$/;
utils.ls(webappsDir, false).forEach(function(dir) {
if (expreg.test(dir.leafName)) {
dir.remove(true);
}
});
}
/**
* Generate data for webapps.json.
*/
function genWebappJSON(config) {
var configItems = ['origin', 'installOrigin', 'receipt', 'installTime',
'updateTime', 'manifestURL', 'removable', 'localId',
'etag', 'packageEtag', 'appStatus'];
var resultJSON = {};
configItems.forEach(function(key) {
if (key in config) {
resultJSON[key] = config[key];
}
});
return resultJSON;
}
function execute(options) {
var webappsJSON = {};
var gaia = utils.gaia.getInstance(options);
var webappsBaseDir = utils.getFile(options.COREWEBAPPS_DIR, 'webapps');
var stageDir = gaia.stageDir;
var webappsJSONFile = utils.getFile(stageDir.path, 'webapps_stage.json');
var webappsStageJSON = utils.getJSON(webappsJSONFile);
if (webappsBaseDir.exists()) {
// remove all external app with uuid folder name
cleanProfile(webappsBaseDir);
} else {
utils.ensureFolderExists(webappsBaseDir);
}
gaia.webapps.forEach(function(app) {
// Preparing webapps.json content
var appConfig = webappsStageJSON[app.sourceDirectoryName];
var webappTargetDirName = appConfig.webappTargetDirName;
webappsJSON[webappTargetDirName] = genWebappJSON(appConfig);
});
var manifestFile = utils.getFile(webappsBaseDir.path, 'webapps.json');
utils.writeContent(manifestFile, JSON.stringify(webappsJSON, null, 2) + '\n');
}
exports.execute = execute;

543
gaia/build/csslint.js Executable file
View file

@ -0,0 +1,543 @@
'use strict';
/* global require, exports, dump, Services */
/* exported debug */
const utils = require('./utils');
const { Cc, Ci, Cu } = require('chrome');
Cu.import('resource://gre/modules/Services.jsm');
function debug(msg) {
dump('-*- csslint.js: ' + msg + '\n');
}
const domUtils = Cc['@mozilla.org/inspector/dom-utils;1']
.getService(Ci.inIDOMUtils);
const domParser = Cc['@mozilla.org/xmlextras/domparser;1']
.createInstance(Ci.nsIDOMParser);
let CSSLint = null;
function execute(config) {
let gaia = utils.gaia.getInstance(config);
let files = [];
// Starts by parsing the CSS files from apps/
gaia.webapps.forEach(function getAllFilesFor(webapp) {
var sourceDirectoryFile = utils.getFile(webapp.sourceDirectoryFilePath);
if (sourceDirectoryFile.parent.leafName != 'apps') {
return;
}
if (config.BUILD_APP_NAME != '*' &&
webapp.sourceDirectoryName != config.BUILD_APP_NAME) {
return;
}
files = files.concat(getCSSFilesFor(webapp.sourceDirectoryFilePath));
});
if (config.BUILD_APP_NAME == '*') {
// And then parse the CSS files from shared/
files = files.concat(getCSSFilesFor(gaia.sharedFolder));
}
files = files.map(function toRelativePath(path) {
return path.slice(config.GAIA_DIR.length + 1);
});
let reportKnownErrorsOrWarnings = false;
let quit = require('xpcshell').quit;
return quit(lint(config.GAIA_DIR, files.join(' '),
reportKnownErrorsOrWarnings));
}
function lint(root, files, reportKnownErrorsOrWarnings = true) {
const xfailFilePath = 'build/csslint/xfail.list';
let hasNewErrorsOrWarnings = 0;
let fileHasNewErrorsOrWarnings = false;
function lessErrorsOrWarnings(filename, type, previous, current) {
hasNewErrorsOrWarnings = 1;
fileHasNewErrorsOrWarnings = true;
dump('You rock! ' + (previous - current) + ' ' + type + ' has been ' +
'removed from ' + filename + '. Please update ' + xfailFilePath +
' with the updated number of errors (' + current + ') or remove ' +
'the file from ' + xfailFilePath + '\n');
}
function moreErrorsOrWarnings(filename, type, previous, current) {
hasNewErrorsOrWarnings = 1;
fileHasNewErrorsOrWarnings = true;
dump(':( ' + (previous - current) + ' ' + type + ' has been added to ' +
filename + '.\n');
}
function lessWarnings(filename, previous, current) {
lessErrorsOrWarnings(filename, 'warnings', previous, current);
}
function moreWarnings(filename, previous, current) {
moreErrorsOrWarnings(filename, 'warnings', previous, current);
}
function lessErrors(filename, previous, current) {
lessErrorsOrWarnings(filename, 'errors', previous, current);
}
function moreErrors(filename, previous, current) {
moreErrorsOrWarnings(filename, 'errors', previous, current);
}
try {
setupLinters(root);
// Get the list of ignored files and build a map of:
// filename: { errors: x, warnings: y }
//
// This map is used to check that no new errors or warnings are introduced
// into a file.
// Once an error/warning has been fixed the xfail.list file also needs
// to be updated and so the number of errors/warnings should be smaller
// and smaller...
let xfail = {};
let content = utils.getFileContent(utils.getFile(root, xfailFilePath));
content.split('\n').forEach(function buildXfailMap(line) {
// ignore lines starting with #.
if (line[0] === '#') {
return;
}
let [filename, errors, warnings] = line.split(' ');
xfail[filename] = {
errors: errors,
warnings: warnings
};
});
// Now lint the css of each files and reports any errors/warnings.
files.split(/\s/).forEach(function parseCSSFor(filename) {
fileHasNewErrorsOrWarnings = false;
let file = utils.getFile(root, filename);
if (!/^(apps|shared)\//.test(filename)) {
return;
}
if (/\/(tests|docs)\//.test(file.path)) {
return;
}
if (!/\.css$/.test(file.leafName)) {
return;
}
let messages = parseCSS(file);
let errorsCount = messages.errors.length;
let warningsCount = messages.warnings.length;
if (!errorsCount && !warningsCount && !(filename in xfail)) {
return;
}
if (!(filename in xfail)) {
hasNewErrorsOrWarnings = 1;
fileHasNewErrorsOrWarnings = true;
dump(filename + ' has new errors/warnings.\n');
printWarningsAndErrors(messages.errors, messages.warnings, file);
return;
}
let rules = xfail[filename];
if (errorsCount > rules.errors) {
moreErrors(filename, rules.errors, errorsCount);
} else if (errorsCount < rules.errors) {
lessErrors(filename, rules.errors, errorsCount);
}
if (warningsCount > rules.warnings) {
moreWarnings(filename, rules.warnings, warningsCount);
} else if (warningsCount < rules.warnings) {
lessWarnings(filename, rules.warnings, warningsCount);
}
if (reportKnownErrorsOrWarnings || fileHasNewErrorsOrWarnings) {
printWarningsAndErrors(messages.errors, messages.warnings, file);
}
});
return hasNewErrorsOrWarnings;
} catch(e) {
dump('Error while trying to run csslint on files: ' + e + '\n');
return 1;
}
}
function setupLinters(root) {
// Activate more CSS properties so those are not considered errors by
// the parser.
Services.prefs.setBoolPref('layout.css.sticky.enabled', true);
Services.prefs.setBoolPref('layout.css.will-change.enabled', true);
// Load the third-party CSS Linter.
let scope = {};
let url =
'file:///' + root + '/build/csslinter.js?reload=' + Date.now();
Services.scriptloader.loadSubScript(url, scope);
CSSLint = scope.CSSLint;
}
function getCSSFilesFor(path) {
return utils.listFiles(path, utils.FILE_TYPE_FILE, true, /^(docs|tests?)$/)
.filter(function(path) {
return path.endsWith('.css');
});
}
function printWarningsAndErrors(errors, warnings, file) {
function printMessage(msg) {
dump('\t' + msg.message + ' line ' + msg.line + ', col ' + msg.col + ' \n');
}
// If any errors, print them to the console.
if (errors.length) {
dump('Errors in ' + file.path + '\n');
for (let i = 0; i < errors.length; i++) {
printMessage(errors[i]);
}
}
// If any warnings, print them to the console.
if (warnings.length) {
dump('Warnings in ' + file.path + '\n');
for (let i = 0; i < warnings.length; i++) {
printMessage(warnings[i]);
}
}
dump('\n');
}
function parseCSS(file) {
let content = utils.getFileContent(file);
let parserErrors = checkForParsingErrors(content);
// If any parserErrors, print them to the console.
if (parserErrors.length) {
dump('Parsing errors in ' + file.path + '\n');
for (let i = 0; i < parserErrors.length; i++) {
let JSWarnRegexp = /\[JavaScript Warning: "(.+)"}\]/;
let filenameRegexp = /" {file: "moz-nullprincipal:\{[a-f0-9-]+\}"/;
let error = parserErrors[i].message
.replace(JSWarnRegexp, '$1"')
.replace(filenameRegexp, '');
dump('\t' + error + '\n');
}
}
let [errors, warnings] = checkForGoodPractices(content);
return { errors: parserErrors.concat(errors), warnings: warnings };
}
function checkForParsingErrors(content) {
// Create a dummy dom document.
let html = '<html><head><style></style></head></html>';
let document = domParser.parseFromString(html, 'text/html');
// Ignore @import rules.
content = content.replace(/@import url('?.*'?);/g, '');
// Ignore -moz-osx-font-smoothing rules.
content = content.replace(/-moz-osx-font-smoothing: grayscale;/g, '');
// Clear any possible leftovers in the console.
Services.console.reset();
// Load the CSS into the dummy document stylesheet.
let sheet = document.styleSheets.item(0);
domUtils.parseStyleSheet(sheet, content);
// Get messages from the console and reset it.
let messages = Services.console.getMessageArray();
Services.console.reset();
// Temporary fix for bug 1133971.
// Allow CSS properties which we support in later versions of B2G.
// Once we have bug 1089710 landed we can remove this workaround.
messages = messages.filter(function(message) {
var msg = message.message;
if (msg.indexOf('offset-inline') !== -1 ||
msg.indexOf('offset-block') !== -1) {
return false;
}
return true;
});
return messages;
}
function checkForGoodPractices(content) {
let rules = {
/*** Errors ***/
// Certain properties don't play well with certain display values.
// * - float should not be used with inline-block
// * - height, width, margin-top, margin-bottom, float should not be used
// with inline
// * - vertical-align should not be used with block
// * - margin, float should not be used with table-*
'display-property-grouping': 1,
// Duplicate properties must appear one after the other.
// If an already-defined property appears somewhere else in the rule, then
// it's likely an error.
'duplicate-properties': 1,
// Style rules without any properties defined should be removed.
'empty-rules': 1,
// Properties should be known (listed in CSS3 specification) or
// be a vendor-prefixed property.
//
// This one is not enabled, because the linter codebase is oriented for
// cross browsers compatibility, and is also lagging behind Gecko
// implementation of those properties. Unknow properties will be caught in
// a much more robust way by the step 1 of the CSS parsing which directly
// use the Gecko engine to find unknow properties.
'known-properties': 0,
};
let messages = CSSLint.verify(content, rules).messages;
let errors = [];
for (let i = 0; i < messages.length; i++) {
let message = messages[i];
// It's sad but a lot of 'errors' are just parser bugs. It would be nice
// to fix them in the linter and upstream those changes.
if (message.type !== 'warning') {
continue;
}
errors.push(message);
}
rules = {
/*** Errors ***/
// Don't use width or height when using padding or border.
//
// This one is not enabled, since it is related to how the box model works
// and it is used to prevent general errors, but is not an errors by itself.
// It is probably a good thing to use it, but since the codebase used a lot
// of width/height/padding/border rules for the same selector, it is a lot
// of efforts to get rid of it, and probably regression prone.
'box-model': 0,
/*** Maintainability & Duplication ***/
// You shouldn't use more than 10 floats. If you do, there's probably
// room for some abstraction.
'floats': 1,
// You shouldn't need more than 9 font-size declarations.
'font-sizes': 1,
// Make sure !important is not overused, this could lead to specificity
// war. Display a warning on !important declarations, an error if it's
// used more at least 10 times.
'important': 1,
// Don't use IDs for selectors.
//
// This one is not enabled. There is a lot of discussions on the web around
// using ids as selectors or not. Not enabling it is not any rationale
// decision based on if they should be used or not, but the codebase is
// already using them a lot, and changing that is a lot of work, with a lot
// of possible regressions.
'ids': 0,
/*** Compatibility ***/
// Don't use adjoining classes (.foo.bar).
//
// This one is not enabled, as the main reason to enable it is to be
// compatible with IE 6 and lesser. There is nothing against browser
// compatibily here, but its doubtful that Gaia will support those one day.
'adjoining-classes': 0,
// box-sizing doesn't work in IE6 and IE7.
//
// This one is not enabled for the same reason as 'adjoining-classes'
'box-sizing': 0,
// Include all compatible vendor prefixes.
//
// This one is not enabled, since Gecko dumps some warnings in the console
// when it does not recognized a CSS property, and this spew the console.
// But there is less and less prefixes for many properties and hopefully
// when Gaia will run on top of those others browser vendors, most of the
// prefixes will be removed anyway.
'compatible-vendor-prefixes': 0,
// When using a vendor-prefixed gradient, make sure to use them all.
//
// This one is not enabled for the same reason as
// 'compatible-vendor-prefixes'.
'gradients': 0,
// Don't use text-indent for image replacement if you need to support rtl.
'text-indent': 1,
// When using a vendor-prefixed property, make sure to
// include the standard one.
//
// This one is not enabled since Gecko will spew a warning in the console if
// there is a property that does not work without prefix and this is just
// some noise in the console. Also some properties, like hyphens are
// prefixed in all known browsers and that noise for that is painful.
'vendor-prefix': 0,
// Require fallback colors.
//
// This one is not enabled for the same reason as 'adjoining-classes'.
// (Except that it not supported by < IE8 instead of < IE6.
'fallback-colors': 0,
// Don't use property with a star prefix.
'star-property-hack': 1,
// Don't use properties with a underscore prefix.
'underscore-property-hack': 1,
// Use the bulletproof @font-face syntax to avoid 404's in old IE.
//
// This one is not enabled for the same reason as 'adjoining-classes'
'bulletproof-font-face': 0,
// Warn people with approaching the IE 4095 limit.
'selector-max-approaching': 1,
// Warn people past the IE 4095 limit.
'selector-max': 1,
/*** Accessibility ***/
// outline: none or outline: 0 should only be used in a :focus rule
// and only if there are other properties in the same rule.
'outline': 1,
/*** Performance ***/
// Avoid too many @font-face declarations in the same stylesheet.
'font-faces': 1,
// Don't use @import, use <link> instead.
//
// This one is not enabled since its not clear that it has any real impact
// on locally installed app, which is the first target of Gaia.
'import': 0,
// Disallow duplicate background-images (using url).
//
// This one is not enabled since its not clear that it has any real impact
// on locally installed app, which is the first target of Gaia. Also it
// seems like there is some valid use cases to reuse an url when there
// is multiple background images on an element, that varies based on some
// classes. So maybe there is better way of doing it, or maybe this is
// a linter bug.
'duplicate-background-images': 0,
// Selectors that look like regular expressions are slow and should be
// avoided.
//
// This one is not enabled since it is used heavily in Gaia. It would be
// nice to remove the usage of such selectors but that seems like a lot of
// work and very regressions prone.
'regex-selectors': 0,
// Don't use universal selector because it's slow.
//
// This one is not enabled for the same reason as 'regex-selectors'
'universal-selector': 0,
// Don't use unqualified attribute selectors because they're just like
// universal selectors.
//
// This one is not enabled for the same reason as 'universal-selectors'
'unqualified-attributes': 0,
// You don't need to specify units when a value is 0.
'zero-units': 1,
// Don't use classes or IDs with elements (a.foo or a#foo).
//
// This one is not enabled, not because it is believed to be useless but the
// codebase is already heavily using that and fixing it is a lot of work and
// can create some possible regressions.
'overqualified-elements': 0,
// Use shorthand properties where possible.
'shorthand': 1,
/*** OOCSS ***/
// Headings (h1-h6) should not be qualified (namespaced).
//
// This one is not enabled, not because people does not want to do OOCSS,
// but mostly because this is already heavily used in the codebase and
// fixing it is a lot of efforts, and may generate regressions.
'qualified-headings': 0,
// Headings (h1-h6) should be defined only once.
//
// This one is not enabled for the same reason than 'qualified-headings'.
'unique-headings': 0,
/*** Misc. ***/
// There should be no syntax errors. (Duh.)
'errors': 1,
// Total number of rules should not exceed x
'rules-count': 1
};
messages = CSSLint.verify(content, rules).messages;
let warnings = [];
for (let i = 0; i < messages.length; i++) {
let message = messages[i];
// It's sad but a lot of 'errors' are just parser bugs. It would be nice
// to fix them in the linter and upstream those changes.
if (message.type !== 'warning') {
continue;
}
warnings.push(message);
}
return [errors, warnings];
}
exports.execute = execute;
exports.lint = lint;

158
gaia/build/csslint/xfail.list Executable file
View file

@ -0,0 +1,158 @@
# This file contains the list of files that are known to have css issues.
# Each line contains 3 informations:
# - the filename, the numbers or known errors, the numbers of known warnings
#
apps/bluetooth/style/lists.css 5 0
apps/bluetooth/style/settings.css 2 1
apps/calendar/style/event_view.css 2 0
apps/calendar/style/modify_account_view.css 1 0
apps/calendar/style/modify_event_view.css 4 4
apps/calendar/style/month.css 4 2
apps/calendar/style/multi_day.css 9 0
apps/calendar/style/settings.css 3 1
apps/calendar/style/ui.css 2 1
apps/callscreen/style/conference_group_ui.css 1 0
apps/callscreen/style/lockscreen.css 2 0
apps/callscreen/style/oncall.css 20 1
apps/callscreen/style/oncall_status_bar.css 4 0
apps/camera/bower_components/gaia-theme/gaia-theme.css 5 0
apps/camera/style/notification.css 1 0
apps/camera/style/overlay.css 0 4
apps/camera/style/preview-gallery.css 0 2
apps/clock/style/alarm.css 0 4
apps/clock/style/onring.css 0 1
apps/clock/style/picker/picker.css 0 1
apps/communications/contacts/style/app.css 8 5
apps/communications/contacts/style/contacts.css 10 6
apps/communications/contacts/style/fb_extensions.css 2 0
apps/communications/contacts/style/matching_contacts.css 0 3
apps/communications/contacts/style/overlay.css 0 1
apps/communications/contacts/style/status.css 0 1
apps/communications/dialer/style/buttons.css 7 0
apps/communications/dialer/style/call_info.css 1 0
apps/communications/dialer/style/call_log.css 2 0
apps/communications/dialer/style/suggestion.css 3 0
apps/communications/dialer/style/toolbar.css 0 1
apps/costcontrol/style/app.css 0 3
apps/costcontrol/style/views/balance.css 2 2
apps/costcontrol/style/views/datausage.css 5 0
apps/costcontrol/style/views/settings.css 1 0
apps/costcontrol/style/views/telephony.css 2 0
apps/costcontrol/style/widget.css 2 0
apps/email/style/common.css 8 5
apps/email/style/folder_cards.css 9 0
apps/email/style/message_cards.css 16 1
apps/email/style/setup_cards.css 8 1
apps/fl/style/pick.css 0 1
apps/fm/style/fm.css 0 1
apps/ftu/style/overlay.css 0 1
apps/ftu/style/responsive.css 1 0
apps/ftu/style/status.css 0 1
apps/ftu/style/style.css 15 10
apps/ftu/style/tutorial.css 0 1
apps/gallery/style/gallery.css 0 7
apps/gallery/style/gallery_tablet.css 0 4
apps/gallery/style/info.css 0 2
apps/gallery/style/open.css 0 1
apps/homescreen/bower_components/base/base.css 4 3
apps/homescreen/bower_components/gaia-fonts/style.css 0 1
apps/homescreen/bower_components/gaia-theme/gaia-theme.css 5 0
apps/homescreen/style/apps-panel.css 1 0
apps/homescreen/style/window.css 2 0
# keyboard.css: csslint don't parse CSS variables
apps/keyboard/style/keyboard.css 4 3
apps/keyboard/style/settings.css 2 1
apps/music/components/dom-scheduler/bower_components/base/base.css 4 3
apps/music/components/dom-scheduler/bower_components/fast-list/examples/sections/css/ul.css 1 1
apps/music/components/dom-scheduler/bower_components/gaia-fonts/style.css 0 1
apps/music/components/dom-scheduler/bower_components/gaia-theme/gaia-theme.css 5 0
apps/music/components/dom-scheduler/demo-app/css/ul.css 1 1
apps/music/components/gaia-theme/gaia-theme.css 5 0
apps/music/css/view.css 1 1
apps/music/css/app.css 0 2
apps/music/node_modules/nws/node_modules/connect/lib/public/style.css 4 10
apps/music/views/home/view.css 3 0
apps/pdfjs/content/web/viewer.css 0 1
apps/ringtones/style/pick.css 0 1
apps/search/style/newtab.css 3 0
apps/search/style/search.css 12 2
apps/settings/style/apps.css 21 4
apps/settings/style/downloads.css 1 6
apps/settings/style/homescreens.css 3 0
apps/settings/style/icc.css 2 0
apps/settings/style/icons.css 5 0
apps/settings/style/lists.css 12 1
apps/settings/style/screen_lock.css 1 2
apps/settings/style/settings.css 4 5
apps/settings/style/settings_large.css 12 1
apps/sms/views/conversation/style/conversation.css 4 0
apps/sms/views/conversation/style/message.css 7 0
apps/sms/views/conversation/style/report_view.css 6 0
apps/sms/views/inbox/style/edit-mode.css 1 0
apps/sms/views/inbox/style/inbox.css 1 2
apps/sms/views/new_message/style/recipients.css 4 0
apps/sms/views/shared/style/common.css 0 1
apps/sms/views/shared/style/composer.css 5 0
apps/sms/views/shared/style/edit-mode.css 1 0
apps/sms/views/shared/style/sms.css 1 2
apps/system/fxa/style/fxa.css 1 0
apps/system/lockscreen/style/lockscreen.css 3 0
apps/system/lockscreen/style/notifications.css 9 0
apps/system/mobile_id/style/mobile_id.css 0 16
apps/system/style/action_menu/action_menu_extended.css 2 0
apps/system/style/app_install_manager/app_install_manager.css 2 0
apps/system/style/bluetooth_transfer/bluetooth_transfer.css 1 0
apps/system/style/cards_view/cards_view.css 5 0
apps/system/style/chrome/chrome.css 12 0
apps/system/style/emergency_callback_manager/emergency_callback_manager.css 4 0
apps/system/style/ime_menu.css 2 0
apps/system/style/media_recording/media_recording.css 3 0
apps/system/style/modal_dialog/modal_dialog.css 2 1
apps/system/style/modal_dialog/prompt.css 1 0
apps/system/style/net_error.css 2 0
apps/system/style/notifications/notifications.css 8 0
apps/system/style/permission_manager/permission_manager.css 5 0
apps/system/style/rocketbar/rocketbar.css 2 0
apps/system/style/sound_manager/sound_manager.css 5 3
apps/system/style/statusbar/statusbar.css 1 0
apps/system/style/storage_watcher/storage_watcher.css 2 0
apps/system/style/system/keyboard.css 2 0
apps/system/style/system/system.css 4 0
apps/system/style/themes/default/buttons.css 3 0
apps/system/style/themes/default/core.css 4 5
apps/system/style/ttlview/ttlview.css 1 0
apps/system/style/update_manager/update_manager.css 4 0
apps/system/style/utility_tray/utility_tray.css 4 0
apps/system/style/window.css 0 3
apps/system/style/wrapper/wrapper.css 0 1
apps/system/style/zindex.css 0 5
apps/video/style/info.css 0 2
apps/video/style/video.css 0 9
apps/video/style/video_tablet.css 0 11
apps/wappush/style/wappush.css 0 1
shared/elements/gaia_checkbox/style.css 5 0
shared/elements/gaia_confirm/style.css 4 0
shared/elements/gaia_grid/style.css 0 5
shared/elements/gaia_menu/style.css 3 0
shared/elements/gaia_radio/style.css 2 0
shared/elements/gaia_sim_picker/style.css 1 0
shared/elements/gaia_switch/style.css 3 0
shared/elements/gaia-theme/gaia-theme.css 5 0
shared/pages/import/style/import.css 0 4
shared/pages/import/style/search.css 0 3
shared/style/action_menu.css 3 1
shared/style/buttons.css 7 0
shared/style/confirm.css 7 0
shared/style/contacts/contacts_buttons.css 0 7
shared/style/date_selector.css 1 0
shared/style/dialer/dialer.css 0 1
shared/style/dialer/keypad.css 1 0
shared/style/edit_mode.css 1 0
shared/style/input_areas.css 1 0
shared/style/lists.css 5 0
shared/style/object_menu.css 2 0
shared/style/progress_activity.css 1 0
shared/style/tabs.css 1 0
shared/style/time_selector.css 1 0
shared/style/switches.css 1 0
shared/style/value_selector.css 1 1

9259
gaia/build/csslinter.js Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
taskcluster/gaia-taskenv

View file

@ -0,0 +1,10 @@
from taskcluster/tester:0.4.5
maintainer Aus Lacroix <aus@mozilla.com>
# configure git identity
run git config --global user.email "gaia@mozilla.com"
run git config --global user.name "gaia-taskenv"
# run some more root commands which change frequently
copy ./bin/entrypoint /home/worker/bin/entrypoint
run chmod a+x /home/worker/bin/entrypoint

View file

@ -0,0 +1 @@
2.0.5

View file

@ -0,0 +1,62 @@
#! /bin/bash -e
buildbot_step_help() {
echo "Run a command and wrap it in buildbot step format"
echo " $0 - <buildbot step> [bash args...]"
}
bb_time() {
# Parser dies if this is longer then 6 chars long.
nano=$(date +%N | cut -c1-6)
echo "$(date '+%Y-%m-%d %H:%M:%S.')$nano"
}
bb_echo() {
echo "========= $1 ========="
}
bb_line() {
local type=$1 # Finished/Started
local step=$2 # Name of step
local code=$3 # Exit code
local duration=$4 # Elapsed time in seconds
local time=$(bb_time)
bb_echo "$type $step (results: $code, elapsed: $duration secs) (at $time)"
}
# Intentionally the full name of this executable so sourcing this file also
# works as expected...
buildbot_step() {
local step=$1
local command=${@:2}
local start_time=$(date +%s)
local exit_code=0
bb_line "Started" "$step" 0 0
if eval "$command";
then
exit_code=$?
else
exit_code=$?
fi
local end_time=$(date +%s)
local duration=$(($end_time-$start_time))
bb_line "Finished" "$step" $exit_code $duration
return $exit_code
}
# When this script is not being sourced invoke command directly.
if [ "$(basename $0)" == "buildbot_step" ];
then
if [ $# -lt 2 ];
then
buildbot_step_help
exit 1
fi
buildbot_step "$@"
exit $?
fi

View file

@ -0,0 +1,49 @@
#! /bin/bash -e
source ./bin/buildbot_step
Xvfb :0 -nolisten tcp -screen 0 1600x1200x24 2>/dev/null &
export DISPLAY=:0
checkout_path="$PWD/git_checkout"
# Handle initial clone and caching of the initial clone...
step_checkout() {
# Extract the cached clone (this is kept as a tarball to always keep a clean
# working state without worrying about tree issues).
if [ "$GITHUB_PULL_REQUEST" == "1" ];
then
tc-vcs checkout $checkout_path \
$GITHUB_BASE_GIT \
$GITHUB_HEAD_GIT \
$GITHUB_HEAD_BRANCH
else
tc-vcs checkout $checkout_path \
$GITHUB_BASE_GIT \
$GITHUB_HEAD_GIT
fi
}
# Set-up our npm-cache.
step_npm_cache() {
taskcluster-npm-cache-get --namespace gaia.npm_cache ./package.json
}
buildbot_step 'git checkout' step_checkout
# From here on we to operate inside of the actual checkout mostly for
# convenience.
cd $checkout_path
# Current revision...
buildbot_step 'git revision...' git --no-pager log -n 1
# node modules
buildbot_step 'npm-cache-get' step_npm_cache
# virtualenv setup so any python packages can be abused without root access
buildbot_step 'virtualenv' virtualenv $HOME/.venv --system-site-packages
source $HOME/.venv/bin/activate
# run whatever command was intended on the branch (root of the repo)
buildbot_step "run $*" $@

View file

@ -0,0 +1,2 @@
#! /bin/bash -vex
docker build -t $(cat DOCKER_TAG):$(cat VERSION) $PWD

View file

@ -0,0 +1,4 @@
#! /bin/bash -e
./build
docker run $@ -v $PWD/bin:/home/worker/bin -i -t $(cat DOCKER_TAG):$(cat VERSION) /bin/bash

129
gaia/build/download-manager.js Executable file
View file

@ -0,0 +1,129 @@
/* global exports, require */
'use strict';
/**
* DownloadManager is a tool for downloading file from internet.
* When downloads a https url which uses self-signed certificate,
* DownloadManager fallbacks to "http" protocol to download it.
*
*/
var utils = require('./utils');
const { Cc, Ci, Cu, Cr } = require('chrome');
const { Services } = Cu.import('resource://gre/modules/Services.jsm');
var DownloadManager = {
debug: false,
count: 0,
download: function dm_download(url, targetFolder, targetName,
finishedCallback, errorCallback) {
var tmpFile;
var isTemp;
var isHttps = url.substring(0, 5) === 'https';
function logLine(msg) {
if (DownloadManager.debug) {
utils.log('download-manager', msg);
}
}
function downloadFinished() {
logLine(url + ' is successfully downloaded.');
try {
if (finishedCallback) {
finishedCallback(url, tmpFile.path);
}
} finally {
DownloadManager.count -= 1;
}
}
function downloadError(reason) {
try {
if (isHttps) {
// If we failed with https protocol, it may be self-signed
// certificate. We fallback to use http protocol to download it.
// XXX find a way to bypass the self-signed certificate error.
var httpUrl = 'http' + url.substring(5);
logLine('fallback to ' + httpUrl);
DownloadManager.download(httpUrl, targetFolder, targetName,
finishedCallback, errorCallback);
} else if (errorCallback) {
logLine('Unable to download ' + url + '\n' + reason);
errorCallback(url, tmpFile.path);
} else {
logLine('Unable to download ' + url + '\n' + reason);
}
} finally {
DownloadManager.count -= 1;
}
}
// normalize file for saving.
if (targetFolder && targetName) {
isTemp = false;
utils.ensureFolderExists(utils.getFile(targetFolder));
tmpFile = utils.getFile(targetFolder, targetName);
} else {
isTemp = true;
tmpFile = DownloadManager._getTempFolder('gaia').clone();
tmpFile.append((new Date()).getTime() + '.tmp');
tmpFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt('0644', 8));
}
DownloadManager.count += 1;
var listener = {
QueryInterface: function(iid) {
if (!iid.equals(Ci.nsIDownloadObserver) &&
!iid.equals(Ci.nsISupports)) {
throw Cr.NS_ERROR_NO_INTERFACE;
}
return this;
},
onDownloadComplete: function(downloader, request, ctxt, status, file) {
if (!file || status !== 0) {
downloadError('failed to download "' + url + '" with error code: ' +
status);
} else {
downloadFinished();
}
}
};
var downloader = Cc['@mozilla.org/network/downloader;1']
.createInstance(Ci.nsIDownloader);
downloader.init(listener, tmpFile);
var principal = Services.scriptSecurityManager.getSystemPrincipal();
let channel = Services.io.newChannel2(url,
null,
null,
null, // aLoadingNode
principal,
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_OTHER);
channel.asyncOpen(downloader, null);
},
// private functions and variables
_tempFolder: null,
_getTempFolder: function dm_get_temp_folder(name) {
if (!DownloadManager._tempFolder) {
var file = Cc['@mozilla.org/file/directory_service;1']
.getService(Ci.nsIProperties).get('TmpD', Ci.nsIFile);
file.append(name);
file.createUnique(Ci.nsIFile.DIRECTORY_TYPE, parseInt('0755', 8));
DownloadManager._tempFolder = file;
}
return DownloadManager._tempFolder;
}
};
exports.getDownloadManager = function() {
return DownloadManager;
};

7
gaia/build/escodegen.js Executable file

File diff suppressed because one or more lines are too long

298
gaia/build/eslint/xfail.list Executable file
View file

@ -0,0 +1,298 @@
apps/calendar/build/calendar.build.js # line 3, col 10, Error - Unexpected token (this is a JSON file)
apps/calendar/js/templates/multi_day.js # line 22, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/utils/dom.js # line 43, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/views/advanced_settings.js # line 149, col 7, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/views/advanced_settings.js # line 165, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/views/create_account.js # line 58, col 7, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/views/modify_event.js # line 138, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/views/modify_event.js # line 679, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/views/month_day_agenda.js # line 88, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/views/month_day.js # line 50, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/views/multi_day.js # line 159, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/views/settings.js # line 228, col 7, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/views/single_month.js # line 39, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/views/view_selector.js # line 59, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/oauth_window_test.js # line 27, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/templates/duration_time_test.js # line 38, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/templates/duration_time_test.js # line 61, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/template_test.js # line 309, col 11, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/views/create_account_test.js # line 22, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/views/event_base_test.js # line 35, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/views/modify_account_test.js # line 95, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/views/month_day_agenda_test.js # line 21, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/views/month_test.js # line 27, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/views/settings_test.js # line 72, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/views/time_header_test.js # line 30, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/view_test.js # line 13, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/test/unit/view_test.js # line 140, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/bower_components/gaia-component/gaia-component.js # line 135, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/bower_components/gaia-component/gaia-component.js # line 199, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/bower_components/gaia-header/dist/gaia-header.js # line 136, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/bower_components/gaia-header/dist/gaia-header.js # line 200, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/bower_components/gaia-header/dist/gaia-header.js # line 280, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/bower_components/gaia-header/dist/gaia-header-es5.js # line 215, col 15, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/bower_components/gaia-header/dist/gaia-header-es5.js # line 501, col 13, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/bower_components/gaia-header/gaia-header.js # line 37, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/bower_components/gaia-theme/lib/gaia-theme-selector.js # line 12, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/confirm.js # line 29, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/controls.js # line 43, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/countdown.js # line 28, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/face.js # line 22, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/faces.js # line 21, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/focus.js # line 25, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/hud.js # line 24, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/indicators.js # line 24, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/loading-screen.js # line 16, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/notification.js # line 59, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/overlay.js # line 28, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/preview-gallery.js # line 335, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/preview-gallery.js # line 46, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/setting.js # line 53, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/setting-options.js # line 48, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/settings.js # line 55, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/viewfinder.js # line 38, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/camera/js/views/zoom-bar.js # line 39, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/js/banner/main.js # line 73, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/js/panels/alarm/alarm_list.js # line 102, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/js/panels/alarm/clock_view.js # line 131, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/js/panels/alarm/clock_view.js # line 152, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/js/panels/alarm_edit/main.js # line 18, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/js/panels/alarm/main.js # line 13, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/js/panels/stopwatch/main.js # line 219, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/js/panels/stopwatch/main.js # line 36, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/js/panels/timer/main.js # line 38, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/js/picker/spinner.js # line 113, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/js/ring_view.js # line 122, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/js/tmpl.js # line 9, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/test/unit/form_button_test.js # line 119, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/test/unit/form_button_test.js # line 154, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/clock/test/unit/utils_test.js # line 25, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/js/cache.js # line 152, col 11, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/js/views/list.js # line 2313, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/contacts_buttons_test.js # line 66, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/contacts_tag_test.js # line 16, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/image_loader_test.js # line 29, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/import/import_contacts_test.js # line 110, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/import/import_test.js # line 104, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/import/import_test.js # line 482, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/link_test.js # line 77, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/mock_multiple_select_dom.js # line 10, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/navigation_test.js # line 18, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/search_test.js # line 52, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/utilities/tagSelector_test.js # line 35, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/views/details_test.js # line 170, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/views/form_test.js # line 103, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/views/ice_settings_test.js # line 107, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/views/ice_test.js # line 63, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/views/ice_test.js # line 76, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/views/multiple_select_test.js # line 22, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/views/settings_test.js # line 134, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/communications/contacts/test/unit/views/settings_test.js # line 475, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/costcontrol/js/settings/settings.js # line 170, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/costcontrol/test/unit/formatting_test.js # line 274, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/costcontrol/test/unit/formatting_test.js # line 46, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/email/build/email.build.js # line 3, col 10, Error - Unexpected token (this is a JSON file)
apps/email/build/gelam_worker.build.js # line 3, col 10, Error - Unexpected token (this is a JSON file)
apps/email/build/main-frame-setup.build.js # line 3, col 6, Error - Unexpected token (this is a JSON file)
apps/email/js/ext/ext/bleach.js/index.js # line 8, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/email/js/html_cache_restore.js # line 397, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/email/js/template.js # line 177, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/email/js/template.js # line 50, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/email/js/tmpl.js # line 8, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/email/test/unit/message_reader_card_test.js # line 121, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/emergency-call/js/keypad.js # line 294, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/emergency-call/js/keypad.js # line 302, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/emergency-call/js/keypad.js # line 315, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/ftu/js/ui.js # line 560, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/ftu/js/wifi.js # line 333, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/ftu/test/unit/language_test.js # line 21, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/ftu/test/unit/language_test.js # line 34, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/ftu/test/unit/navigation_test.js # line 116, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/ftu/test/unit/navigation_test.js # line 129, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/gallery/test/marionette/apps/galleryactivitytester/js/galleryactivitytester.js # line 32, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/gallery/test/marionette/apps/galleryactivitytester/js/galleryactivitytester.js # line 33, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/gallery/test/marionette/apps/galleryactivitytester/js/galleryactivitytester.js # line 34, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/keyboard/build/settings-config.js # line 61, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/keyboard/build/settings-config.js # line 72, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/keyboard/js/imes/handwriting/hwr/emscripten_zinnia.js # line 0, col 0, Error - Maximum call stack size exceeded
apps/keyboard/js/imes/jspinyin/libpinyin.js # line 0, col 0, Error - Maximum call stack size exceeded
apps/keyboard/js/imes/jspinyin/tests/init.js # line 103, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/keyboard/js/views/emoji_key_view.js # line 22, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/keyboard/js/views/key_view.js # line 115, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/keyboard/js/views/key_view.js # line 124, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/keyboard/js/views/key_view.js # line 131, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/keyboard/js/views/key_view.js # line 136, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/music/components/dom-scheduler/bower_components/gaia-component/gaia-component.js # line 45, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/music/components/dom-scheduler/bower_components/gaia-theme/lib/gaia-theme-selector.js # line 12, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/music/components/gaia-component/gaia-component.js # line 45, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/music/components/gaia-fast-list/gaia-fast-list.js # line 1134, col 7, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/music/components/gaia-header/dist/gaia-header-es5.js # line 205, col 15, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/music/components/gaia-header/dist/gaia-header.js # line 168, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/music/components/gaia-theme/lib/gaia-theme-selector.js # line 12, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/music/components/gaia-toolbar/gaia-toolbar.js # line 26, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/music/components/poplar/poplar.js # line 260, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/pdfjs/content/build/pdf.worker.js # line 0, col 0, Error - Maximum call stack size exceeded
apps/ringtones/js/tone_list.js # line 51, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/settings/js/modules/messaging.js # line 196, col 13, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/settings/js/panels/frame/panel.js # line 67, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/settings/js/panels/languages/languages.js # line 21, col 11, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/settings/js/panels/simcard_manager/simcard_manager.js # line 296, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/settings/js/panels/simpin/simpin.js # line 80, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sharedtest/test/unit/elements/gaia_tabs/gaia_tabs_test.js # line 14, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sharedtest/test/unit/lazy_loader_test.js # line 15, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sharedtest/test/unit/lazy_loader_test.js # line 179, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/js/attachment_renderer.js # line 134, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/js/conversation.js # line 1431, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/js/conversation.js # line 1513, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/js/conversation.js # line 1568, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/js/conversation.js # line 1778, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/js/information.js # line 151, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/js/information.js # line 495, col 11, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/js/recipients.js # line 406, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/js/recipients.js # line 509, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/test/unit/conversation_test.js # line 263, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/test/unit/conversation_test.js # line 4168, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/test/unit/conversation_test.js # line 6202, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/conversation/test/unit/subject_composer_test.js # line 138, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/inbox/js/inbox.js # line 748, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/inbox/test/unit/inbox_test.js # line 105, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/shared/js/contact_renderer.js # line 275, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/shared/test/unit/selection_handler_test.js # line 24, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/sms/views/shared/test/unit/time_headers_test.js # line 60, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/activity_window.js # line 200, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/app_chrome.js # line 1076, col 9, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/app_chrome.js # line 824, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/attention_window.js # line 114, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/base_icon.js # line 113, col 9, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/base_ui.js # line 28, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/bluetooth_transfer.js # line 405, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/call_forwarding_icon.js # line 29, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/callscreen_window.js # line 89, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/entry_sheet.js # line 63, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/global_overlay_window.js # line 86, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/roaming_icon.js # line 22, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/signal_icon.js # line 22, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/system_dialog.js # line 143, col 7, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/time_icon.js # line 59, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/js/ttl_view.js # line 133, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/mobile_id/js/ui.js # line 169, col 13, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/mobile_id/js/ui.js # line 340, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/app_install_manager_test.js # line 109, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/app_install_manager_test.js # line 136, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/app_install_manager_test.js # line 159, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/app_install_manager_test.js # line 178, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/app_install_manager_test.js # line 193, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/app_modal_dialog_test.js # line 28, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/app_text_selection_dialog_test.js # line 41, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/battery_overlay_test.js # line 82, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/crash_reporter_test.js # line 74, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/device_storage_watcher_test.js # line 47, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/text_selection_dialog_test.js # line 40, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/update_manager_test.js # line 149, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/update_manager_test.js # line 157, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/update_manager_test.js # line 166, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/update_manager_test.js # line 186, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/system/test/unit/value_selector_test.js # line 74, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/video/js/thumbnail_date_group.js # line 57, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/video/js/thumbnail_item.js # line 67, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/video/test/unit/mock_thumbnail_group.js # line 7, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
build/l10n/l20n.js # line 468, col 8, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
build/r.js # line 0, col 0, Error - Maximum call stack size exceeded
build/test/unit/webapp_optimize_test.js # line 175, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
build/test/unit/webapp_optimize_test.js # line 200, col 7, Error - Unsafe assignment to outerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
build/utils-node.js # line 40, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
build/utils-xpc.js # line 175, col 16, Error - Unexpected token
build/webapp-optimize.js # line 192, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
build/webapp-optimize.js # line 215, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
build/webapp-optimize.js # line 381, col 5, Error - Unsafe assignment to outerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
build/webapp-optimize.js # line 429, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
build/webapp-optimize.js # line 461, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
build/webapp-optimize.js # line 669, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/elements/gaia-component/gaia-component.js # line 289, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/elements/gaia-component/gaia-component.js # line 330, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/elements/gaia-component/gaia-component.js # line 33, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/elements/gaia_grid/script.js # line 248, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/elements/gaia-header/dist/gaia-header.js # line 168, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/elements/gaia-header/dist/gaia-header.js # line 424, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/elements/gaia-header/dist/gaia-header.js # line 465, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/elements/gaia-site-icon/script.js # line 559, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/elements/gaia_sim_picker/script.js # line 23, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/elements/gaia_textinput/script.js # line 63, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/elements/gaia-theme/lib/gaia-theme-selector.js # line 12, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/component_utils.js # line 21, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/contacts/search.js # line 196, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/contacts/search.js # line 202, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/contacts/utilities/templates.js # line 219, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/homescreens/confirm_dialog_helper.js # line 32, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/html_imports.js # line 27, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/html_imports.js # line 39, col 13, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/l10n.js # line 1952, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/intl/l20n.js # line 43, col 11, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/intl/l20n-client.js # line 43, col 11, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/lazy_loader.js # line 55, col 11, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/smart-screen/shared_utils.js # line 29, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/template.js # line 160, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/js/sync/ext/kinto.min.js # line 217, col 1044, Error - Using postMessage() with wildcard targets is not allowed. (no-wildcard-postmessage/no-wildcard-postmessage)
shared/js/utilities.js # line 145, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
shared/test/unit/load_body_html_helper.js # line 38, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tests/**
tv_apps/browser/js/authentication_dialog.js # line 140, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/awesomescreen.js # line 1089, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/awesomescreen.js # line 1106, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/awesomescreen.js # line 1248, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/awesomescreen.js # line 1259, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/awesomescreen.js # line 1270, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/awesomescreen.js # line 1284, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/awesomescreen.js # line 365, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/awesomescreen.js # line 523, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/awesomescreen.js # line 568, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/browser_dialog.js # line 178, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/search_result.js # line 174, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/search_result.js # line 176, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/search_result.js # line 185, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/search_result.js # line 187, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/settings.js # line 446, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/toolbar.js # line 526, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/toolbar.js # line 567, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/toolbar.js # line 654, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/toolbar.js # line 655, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/toolbar.js # line 669, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/toolbar.js # line 670, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/browser/js/tooltip.js # line 161, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/fling-player/bower_components/gaia-component/gaia-component.js # line 283, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/fling-player/bower_components/gaia-component/gaia-component.js # line 324, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/fling-player/bower_components/gaia-component/gaia-component.js # line 33, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/fling-player/bower_components/gaia-theme/lib/gaia-theme-selector.js # line 12, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-home/js/home.js # line 796, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/fxa/js/fxam_error_overlay.js # line 39, col 6, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/fxa/js/fxam_errors.js # line 77, col 6, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/fxa/js/screens/fxam_enter_email.js # line 77, col 4, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/fxa/js/screens/fxam_enter_password.js # line 131, col 4, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/fxa/js/screens/fxam_set_password.js # line 94, col 4, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/fxa/js/screens/fxam_signup_success.js # line 26, col 4, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/system_dialog.js # line 139, col 4, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/activity_window.js # line 215, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/app_chrome.js # line 485, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/app_chrome.js # line 668, col 9, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/app_install_manager.js # line 378, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/attention_indicator.js # line 28, col 7, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/attention_window.js # line 114, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/base_ui.js # line 28, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/entry_sheet.js # line 61, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/ime_menu.js # line 38, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/ime_menu.js # line 91, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/modal_dialog.js # line 377, col 5, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/permission_manager.js # line 544, col 9, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/ttlview.js # line 133, col 7, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/value_selector/value_selector.js # line 126, col 7, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/value_selector/value_selector.js # line 398, col 9, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/value_selector/value_selector.js # line 403, col 9, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/smart-system/js/browser_context_menu.js # line 189, col 5, Error - Unsafe call to insertAdjacentHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/weather-widget/web_components/smart-weather.js # line 11, col 3, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/remote-control/bower_components/qrcodejs/qrcode.js # line 255, col 4, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
tv_apps/remote-control-client/bower_components/l20n/dist/webcompat/l20n.js # line 2546, col 8, Error - Unsafe assignment to innerHTML (no-unsafe-innerhtml/no-unsafe-innerhtml)
apps/calendar/js/ext/threads.js # line 110, col 20, Error - Using postMessage() with wildcard targets is not allowed. (no-wildcard-postmessage/no-wildcard-postmessage)
apps/clock/js/panels/alarm/post_message_proxy.js # line 35, col 15, Error - Using postMessage() with wildcard targets is not allowed. (no-wildcard-postmessage/no-wildcard-postmessage)
apps/keyboard/js/imes/jszhuyin/lib/jszhuyin_server.js # line 42, col 7, Error - Using postMessage() with wildcard targets is not allowed. (no-wildcard-postmessage/no-wildcard-postmessage)
apps/sync/js/ext/kinto.min.js # line 217, col 1044, Error - Using postMessage() with wildcard targets is not allowed. (no-wildcard-postmessage/no-wildcard-postmessage)
shared/js/setImmediate.js # line 137, col 13, Error - Using postMessage() with wildcard targets is not allowed. (no-wildcard-postmessage/no-wildcard-postmessage)

78
gaia/build/esomin.js Executable file
View file

@ -0,0 +1,78 @@
'use strict';
/**
* Handles running a JS minifier over a directory, only picking out the .js
* files for minification, or allowing a minification of a specfic file in
* place. By default, uses an AST-backed minifier, in astMinify, but the file
* methods allow passing in a function that handles the actual minification.
*/
var utils = require('./utils');
var esprima = require('esprima');
var escodegen = require('escodegen');
var jsSuffix = /\.js$/;
/**
* Minifies all .js files found in the given directory path string. Can pass
* a custom minifyFn that does the minification. If no minifyFn is passed, then
* the default of astMinify will be used.
*/
exports.minifyDir = function(dirPath, minifyFn) {
utils.listFiles(dirPath, utils.FILE_TYPE_FILE, true)
.forEach(function(filePath) {
if (jsSuffix.test(filePath)) {
exports.minifyFile(filePath, minifyFn);
}
});
};
/**
* Minifies the file at the given filePath string. If it has a problem minifying
* the file, it will just skip it. Can pass a custom minifyFn that does the
* minification. If no minifyFn is passed, then the default of astMinify with
* its default options will be used. To use jsmin:
*
* minifyFile(filePath, function(contents) {
* return require('jsmin')(contents).code;
* });
*/
exports.minifyFile = function(filePath, minifyFn) {
minifyFn = minifyFn || exports.astMinify;
try {
var file = utils.getFile(filePath);
var contents = utils.getFileContent(file);
utils.writeContent(file, minifyFn(contents));
} catch(e) {
utils.log('esomin', 'Unable to minify, skipping ' + filePath);
}
};
/*
* Does some minification, via an AST. Allows some es6 features to be used, like
* arrow functions and template strings. Currently uglify cannot handle them.
*
* Long term want to use Reflect.parse to get a speed boost, but there are some
* incompatibilities with its AST and the escodegen/esmangle tools, tracked in
* bug 913617.
*
* Also, this module only does comment removal right now. That is the biggest
* minification win, but over time want to use esmangle, see esmangle notes
* below.
*
* More research details here:
* https://github.com/jrburke/esomin
*/
exports.astMinify = function(contents, options) {
var ast = esprima.parse(contents);
// Long term TODO, use esmangle, https://github.com/Constellation/esmangle
// to do name mangling. Unfortunately it is not able to handle the es6-ish
// AST yet.
// There are some options fo escodegen, like removing line returns, but
// not using them to preserve line returns to give better debugging
// experience. Options are here:
// https://github.com/Constellation/escodegen/wiki/API
return escodegen.generate(ast, options);
};

5743
gaia/build/esprima.js Executable file

File diff suppressed because one or more lines are too long

153
gaia/build/homescreen-manager.js Executable file
View file

@ -0,0 +1,153 @@
'use strict';
const utils = require('./utils');
const BASE_ICON_SIZE = 60;
function isAbsoluteURL(url) {
return url.indexOf('data:') === 0 ||
url.indexOf('app://') === 0 ||
url.indexOf('http://') === 0 ||
url.indexOf('https://') === 0;
}
// c.f. the corresponding implementation in the Homescreen app.
function bestMatchingIcon(config, manifest, origin) {
var icons = manifest.icons;
if (!icons) {
return undefined;
}
var preferredIconSize = Number.MAX_VALUE;
var max = 0;
for (var size in icons) {
size = parseInt(size, 10);
if (size > max) {
max = size;
}
if (size >= config.preferredIconSize && size < preferredIconSize) {
preferredIconSize = size;
}
}
// If there is an icon matching the preferred size, we return the result,
// if there isn't, we will return the maximum available size.
if (preferredIconSize === Number.MAX_VALUE) {
preferredIconSize = max;
}
var url = icons[preferredIconSize];
if (!url) {
return undefined;
}
// If the icon path is not an absolute URL, prepend the app's origin.
if (!isAbsoluteURL(url)) {
url = origin + url;
}
return url;
}
function getAppMetadata(config, directory, appName, entryPoint) {
var origin = null;
var manifestURL = null;
if (!config.webappsMapping[appName]) {
utils.log('homescreen-manager', 'Warning: Can not find application ' +
appName + ' at ' + directory + '\n');
return;
}
var manifest = config.webappsMapping[appName].originalManifest;
let entryPoints = manifest.entry_points;
if (entryPoint && entryPoints && entryPoints[entryPoint]) {
manifest = entryPoints[entryPoint];
}
origin = config.webappsMapping[appName].origin;
manifestURL = config.webappsMapping[appName].manifestURL;
let descriptor = {
entry_point: entryPoint
};
descriptor.name = manifest.name;
descriptor.manifestURL = manifestURL;
descriptor.icon = bestMatchingIcon(config, manifest, origin);
return descriptor;
}
function customizeHomescreen(config, homescreen) {
var content = {
grid: homescreen.homescreens.map(
function mapHomescreens(applist) {
var output = [];
for (var i = 0; i < applist.length; i++) {
if (applist[i] !== null) {
var meta = getAppMetadata(config, applist[i][0],
applist[i][1], applist[i][2]);
meta && output.push(meta);
}
}
return output;
}
),
preferences: homescreen.preferences || {}
};
return content;
}
function loadHomescreen(config, homescreen) {
homescreen = homescreen || 'verticalhome';
var defaultConfig = utils.getFile(config.GAIA_DIR, 'apps', homescreen,
'build', 'default-homescreens.json');
var customize = utils.getJSON(defaultConfig);
// Load device specific configuration
var deviceConfig = utils.getFile(config.GAIA_DIR, 'build', 'config',
config.GAIA_DEVICE_TYPE, 'homescreens.json');
if (deviceConfig.exists()) {
customize = utils.getJSON(deviceConfig);
}
// Load distribution specific configuration
if (config.GAIA_DISTRIBUTION_DIR) {
var distributionCustom =
JSON.parse(utils.getDistributionFileContent('homescreens',
customize, config.GAIA_DISTRIBUTION_DIR));
customize = customizeSettings(customize, distributionCustom);
}
return customize;
}
function customizeSettings(origin, custom) {
Object.keys(custom).forEach(function(key) {
origin[key] = custom[key];
});
return origin;
}
function configure(config) {
config.preferredIconSize = BASE_ICON_SIZE *
parseFloat(config.GAIA_DEV_PIXELS_PER_PX);
var mappingFile = utils.getFile(config.STAGE_DIR, 'webapps_stage.json');
if (!mappingFile.exists()) {
throw new Error('build_stage/webapps_stage.json not found.');
}
config.webappsMapping = utils.getJSON(mappingFile);
config.displayHeight = config.GAIA_DEV_DISPLAY_HEIGHT;
}
exports.getHomescreen = function(config, homescreen) {
configure(config);
var rawHomescreen = loadHomescreen(config, homescreen);
return customizeHomescreen(config, rawHomescreen);
};

93
gaia/build/import-config.js Executable file
View file

@ -0,0 +1,93 @@
'use strict';
/**
* This module contains all the build time generation logic needed to
* use the import functionality both by the FTU and Comms Applications
*
* The Functions exported by this module are used by the build scripts
* of Comms and FTU apps
*
*/
var utils = require('./utils');
// Returns the communications services configuration as a JS object
function getCommsServices(gaiaDir) {
var content = JSON.parse(utils.getFileContent(utils.getFile(
gaiaDir, 'build', 'config', 'communications_services.json')));
var custom = utils.getDistributionFileContent('communications_services',
content);
return JSON.parse(custom);
}
// Generates a manifest object with the proper redirections established
// This manifest object will have to be serialized and persisted by the caller
function generateManifest (webapp, gaia) {
var manifestObject;
var manifestFile = utils.getFile(webapp.manifestFilePath);
var manifestContent = utils.getFileContent(manifestFile);
manifestObject = JSON.parse(manifestContent);
var commsServices = getCommsServices(gaia.gaiaDir);
var redirects = manifestObject.redirects;
var indexedRedirects = {};
redirects.forEach(function(aRedirect) {
indexedRedirects[aRedirect.from] = aRedirect.to;
});
var mappingParameters = {
'facebook': 'redirectURI',
'live': 'redirectURI',
'gmail': 'redirectURI',
'facebook_dialogs': 'redirectMsg',
'facebook_logout': 'redirectLogout'
};
var newRedirects = [];
redirects.forEach(function(aRedirect) {
var from = aRedirect.from;
var service = commsServices[from.split('_')[0] || from] || commsServices;
newRedirects.push({
from: service[mappingParameters[from]],
to: indexedRedirects[from]
});
});
manifestObject.redirects = newRedirects;
return manifestObject;
}
// Generates the configuration to be used by the importing processes
function generateConfig (app, destination, gaia) {
var config = utils.getFile(gaia.stageDir.path, destination, 'config.json');
var defaultConfig = {
'defaultContactsOrder': 'givenName',
'facebookEnabled': true,
'operationsTimeout': 25000,
'logLevel': 'DEBUG',
'facebookSyncPeriod': 24,
'testToken': '',
'defaultImage': true
};
utils.writeContent(config, utils.getDistributionFileContent(app,
defaultConfig, gaia.distributionDir));
// Images configuration
var imageConfig= utils.getFile(gaia.stageDir.path, '', 'config-images.json');
var defaultImageConfig = {
'thumbnail' : {
'format': 'image/jpeg',
'size': 65,
'quality': 1.0
}
};
utils.writeContent(imageConfig, utils.getDistributionFileContent(app,
defaultImageConfig, gaia.distributionDir));
}
exports.generateManifest = generateManifest;
exports.generateConfig = generateConfig;

11
gaia/build/jsdoc/nostrict.js Executable file
View file

@ -0,0 +1,11 @@
/*global exports*/
/*
* plugin to remove strict mode before parsing to prevent getter/setter
* parsing error.
*/
'use strict';
exports.handlers = {
beforeParse: function(e) {
e.source = e.source.replace(/['"]use strict['"]/g, '//"use strict"');
}
};

50
gaia/build/jshint/reporter.js Executable file
View file

@ -0,0 +1,50 @@
/* jshint node: true */
'use strict';
module.exports = {
reporter: function(results, data, opts) {
var len = results.length;
var redErrors = '';
var lastFile;
var fileCount = 0;
opts = opts || {};
// filter the results down to only those that should be "red"
var red = results.filter(function(result) {
var file = result.file;
var error = result.error;
var str = '';
if (lastFile && (file !== lastFile)) {
fileCount = 0;
}
lastFile = file;
fileCount++;
str += file + ': line ' + error.line + ', col ' +
error.character + ', ' + error.reason;
if (opts.verbose) {
str += ' (' + error.code + ')';
}
str += ' (ERROR)';
redErrors += str + '\n';
return true;
});
var redCount = red.length;
process.stdout.write(
// show redErrors first always
redErrors + '\n' +
redCount + ' error' + ((redCount === 1) ? '' : 's') +
'\n'
);
// interesting - if we modify 'results' to be 0 in length, jshint exits
// with 0 :)
results.splice.apply(results, [0, len].concat(red));
}
};

463
gaia/build/jsmin.js Executable file
View file

@ -0,0 +1,463 @@
/*
node-jsmin2
2012-10-07
Copyright (c) 2012 Todd Wolfson (todd@twolfson.com)
jsmin.c
2012-07-02
Copyright (c) 2002 Douglas Crockford (www.crockford.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
function JSMin(input) {
const EOF = -1;
var stdin = {
'index': 0,
'end': input.length,
'read': function (len) {
// For now only handle len = 1
if (len !== 1) {
throw new Error('You can only read one character from input at a time.');
}
// If we are at the end, return EOF (-1)
var index = stdin.index;
if (index === stdin.end) {
return EOF;
}
// Read the input at our index
var char = input[index];
// Increment our index
stdin.index = index + 1;
// Return char
return char;
},
'getIndex': function () {
return stdin.index;
},
// Helper for verifying during dev
'charAt': function (index) {
return (index >= stdin.end) ? EOF : input.charAt(index);
}
};
var map = {};
var output = '';
options = {
'error': '',
'stdout': {
'write': function (str) {
// Add the string to output
output += str;
},
'writeFromIndex': function (index) {
var char = '\n';
// If we are at a positive index
if (index >= 0) {
// Grab the char from its index
char = input.charAt(index);
// Get the length of the current output
var len = output.length;
// Map the sourceIndex to the destIndex
map[index] = len;
}
// Output the char to output
output += char;
}
},
'stderr': function (err) {
// Add the error to output
options.error += err;
},
'exit': function (code) {
// Throw the collective error
throw new Error(options.error);
}
};
// Run jsminc
jsminc(stdin, options);
// Return the output and map
var retObj = {
'code': output,
'codeMap': map
};
return retObj;
}
/* jsmin.c
2012-07-02
Copyright (c) 2002 Douglas Crockford (www.crockford.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
function jsminc(stdin, options) {
// Fallback options
options = options || {};
// Grab stdout, stderr, exit, argc, and argv
var stdout = options.stdout || console.log,
stderr = options.stderr || console.error,
exit = options.exit || process.exit,
argc = options.argc || 0,
argv = options.argv || [];
// Generate fputs, fputc, getc, putc, fprintf
var fputs = fputc = putc = function (str, stream) {
stream.write(str);
};
var fprintf = function (stream, formatStr, input) {
var outStr = formatStr.replace('%s', input);
fputs(outStr, stream);
};
var getc = function () {
return stdin.read(1);
};
function puti(index, stream) {
stream.writeFromIndex(index);
}
// Begin normal jsmin.c code
var EOF = -1;
var theA;
var theB;
var theLookahead = EOF;
var theX = EOF;
var theY = EOF;
var getIndex = -1;
var peekIndex = -1;
var nextIndex = -1;
var theAIndex = -1;
var theBIndex = -1;
function geti(stream) {
return stream.getIndex();
}
function getChar(index, stream) {
var retVal = '\n';
if (index >= 0) {
retVal = stream.charAt(index);
}
return retVal;
}
function error(s) {
fputs("JSMIN Error: ", stderr);
fputs(s, stderr);
fputc('\n', stderr);
exit(1);
}
/* isAlphanum -- return true if the character is a letter, digit, underscore,
dollar sign, or non-ASCII character.
*/
function isAlphanum(c) {
return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') || c === '_' || c === '$' || c === '\\' ||
c > 126);
}
/* get -- return the next character from stdin. Watch out for lookahead. If
the character is a control character, translate it to a space or
linefeed.
*/
function get() {
var c = theLookahead;
theLookahead = EOF;
if (c === EOF) {
getIndex = geti(stdin);
c = getc(stdin);
theY = theX;
theX = c;
}
if (c >= ' ' || c === '\n' || c === EOF) {
return c;
}
if (c === '\r') {
return '\n';
}
return ' ';
}
/* peek -- get the next character without getting it. */
function peek() {
theLookahead = get();
peekIndex = getIndex;
return theLookahead;
}
/* next -- get the next character, excluding comments. peek() is used to see
if a '/' is followed by a '/' or '*'.
*/
function next() {
var c = get();
nextIndex = getIndex;
if (c === '/') {
switch (peek()) {
case '/':
for (;;) {
c = get();
nextIndex = getIndex;
if (c <= '\n') {
return c;
}
}
case '*':
get();
nextIndex = getIndex;
for (;;) {
switch (get()) {
case '*':
if (peek() === '/') {
get();
nextIndex = getIndex;
return ' ';
}
break;
case EOF:
error("Unterminated comment.");
}
nextIndex = getIndex;
}
default:
return c;
}
}
return c;
}
/* action -- do something! What you do is determined by the argument:
1 Output A. Copy B to A. Get the next B.
2 Copy B to A. Get the next B. (Delete A).
3 Get the next B. (Delete B).
action treats a string as a single character. Wow!
action recognizes a regular expression if it is preceded by ( or , or =.
*/
function action(d) {
switch (d) {
case 1:
puti(theAIndex, stdout);
if (theA === theB && (theA === '+' || theA === '-') && theY !== theA) {
putc(' ', stdout);
}
case 2:
theA = theB;
theAIndex = theBIndex;
if (theA === '\'' || theA === '"' || theA === '`') {
for (;;) {
puti(theAIndex, stdout);
theA = get();
theAIndex = getIndex;
if (theA === theB) {
break;
}
if (theA === '\\') {
puti(theAIndex, stdout);
theA = get();
theAIndex = getIndex;
}
if (theA === EOF) {
error("Unterminated string literal.");
}
}
}
case 3:
theB = next();
theBIndex = nextIndex;
if (theB === '/' && (theA === '(' || theA === ',' || theA === '=' ||
theA === ':' || theA === '[' || theA === '!' ||
theA === '&' || theA === '|' || theA === '?' ||
theA === '{' || theA === '}' || theA === ';' ||
theA === '\n')) {
puti(theAIndex, stdout);
puti(theBIndex, stdout);
for (;;) {
theA = get();
theAIndex = getIndex;
if (theA === '[') {
for (;;) {
puti(theAIndex, stdout);
theA = get();
theAIndex = getIndex;
if (theA === ']') {
break;
}
if (theA === '\\') {
puti(theAIndex, stdout);
theA = get();
theAIndex = getIndex;
}
if (theA === EOF) {
error("Unterminated set in Regular Expression literal.");
}
}
} else if (theA === '/') {
break;
} else if (theA ==='\\') {
puti(theAIndex, stdout);
theA = get();
theAIndex = getIndex;
}
if (theA === EOF) {
error("Unterminated Regular Expression literal.");
}
puti(theAIndex, stdout);
}
theB = next();
theBIndex = nextIndex;
}
}
}
/* jsmin -- Copy the input to the output, deleting the characters which are
insignificant to JavaScript. Comments will be removed. Tabs will be
replaced with spaces. Carriage returns will be replaced with linefeeds.
Most spaces and linefeeds will be removed.
*/
function jsmin() {
if (peek() === 0xEF) {
get();
get();
get();
}
theA = '\n';
action(3);
while (theA !== EOF) {
switch (theA) {
case ' ':
if (isAlphanum(theB)) {
action(1);
} else {
action(2);
}
break;
case '\n':
switch (theB) {
case '{':
case '[':
case '(':
case '+':
case '-':
case '!':
case '~':
action(1);
break;
case ' ':
action(3);
break;
default:
if (isAlphanum(theB)) {
action(1);
} else {
action(2);
}
}
break;
default:
switch (theB) {
case ' ':
if (isAlphanum(theA)) {
action(1);
break;
}
action(3);
break;
case '\n':
switch (theA) {
case '}':
case ']':
case ')':
case '+':
case '-':
case '"':
case '\'':
case '`':
action(1);
break;
default:
if (isAlphanum(theA)) {
action(1);
} else {
action(3);
}
}
break;
default:
action(1);
break;
}
}
}
}
jsmin();
}
// Support commonjs.
if (module && module.exports) {
module.exports = JSMin;
}

27
gaia/build/jsonlint.js Executable file
View file

@ -0,0 +1,27 @@
'use strict';
var utils = require('./utils');
var RE_JSON = /\.json$/;
exports.execute = function(options) {
var jsons = [];
var dirs = ['apps', 'dev_apps', 'build', 'customization', 'dev_apps',
'locales', 'shared'];
dirs.forEach(function(dir) {
var dirFile = utils.getFile(options.GAIA_DIR, dir);
var files = utils.ls(dirFile, true).filter(function(f) {
return RE_JSON.test(f.leafName);
});
jsons.push(...files);
});
jsons.forEach(function(json) {
try {
JSON.parse(utils.getFileContent(json));
} catch (e) {
utils.log('jsonlint', 'JSON lint error: ' + json.path + '\n');
throw e;
}
});
};

110
gaia/build/keyboard-layouts.js Executable file
View file

@ -0,0 +1,110 @@
'use strict';
/**
* Generate the default keyboard layout config
* (shared/resources/keyboard_layouts.json)
* which is used to setup which layouts we should enable for each language
*/
var utils = require('./utils');
// To get the manifestURL of an app from webapps collection.
// Note: right now directory is used only for logging the directory info.
// For now, we would not allow multiple apps with the same appName.
function getManifestURL(webappsMapping, appName) {
if (!webappsMapping[appName]) {
utils.log('keyboard-layouts', 'Can not find application ' + appName +
' in webappsMapping');
return '';
}
return webappsMapping[appName].manifestURL;
}
// Helper function to return a layout entry which represents a layout
// in the keyboard_layouts.json.
function getLayoutEntry(layout, webappsMapping) {
return {
layoutId: layout.layoutId,
appManifestURL: getManifestURL(webappsMapping, layout.app[1])
};
}
// Generate the default layout mapping from language -> keyboard layouts
// config: build config
// webappsMapping: all the webapps mapping
// allLayouts: all the preloaded keyboard layouts
function genDefaultLayouts(config, webappsMapping, allLayouts) {
let layoutDefFile = utils.resolve(config.KEYBOARD_LAYOUTS_PATH,
config.GAIA_DIR);
if (!layoutDefFile.exists()) {
throw new Error('file not found: ' + layoutDefFile.path);
}
let defaultKeyboards = utils.getJSON(layoutDefFile);
let result = {
layout: {},
langIndependentLayouts: []
};
// Handle language -> layouts mapping
let mapping = defaultKeyboards.layout;
Object.keys(defaultKeyboards.layout).forEach(function(lang) {
// Remove those mappings without the layouts preloaded
var filteredLayouts = mapping[lang].filter(function(layoutEntry) {
return (allLayouts.indexOf(layoutEntry.layoutId) != -1);
});
// Put a fallback layout to avoid an empty layout set
if (filteredLayouts.length === 0) {
filteredLayouts = defaultKeyboards.fallbackLayouts;
}
result.layout[lang] = filteredLayouts.map(function parseLayout(layout) {
return getLayoutEntry(layout, webappsMapping);
});
});
// Handle language-independent layouts
let langIndLayouts = defaultKeyboards.langIndependentLayouts;
langIndLayouts.forEach(function parseLayout(layout) {
result.langIndependentLayouts.push(getLayoutEntry(layout, webappsMapping));
});
// Write the result to file
let content = JSON.stringify(result, null, 2);
let resultFile = utils.resolve(utils.joinPath('shared', 'resources',
'keyboard_layouts.json'), config.GAIA_DIR);
if (resultFile.exists()) {
let prev = utils.getFileContent(resultFile);
if (prev === content) {
return;
}
}
utils.writeContent(resultFile, content);
}
function execute(options) {
// Get web apps mapping file
let stageFolder = options.STAGE_DIR;
let webappsMappingFile = utils.resolve(
utils.joinPath(stageFolder, 'webapps_stage.json'),
options.GAIA_DIR);
if (!webappsMappingFile.exists()) {
throw new Error('webapps mapping file not found, you should use' +
' webapp-manifests.js to create it first, path: ' +
webappsMappingFile.path);
}
let webappsMapping = utils.getJSON(webappsMappingFile);
let allLayouts = options.GAIA_KEYBOARD_LAYOUTS.split(',');
genDefaultLayouts(options, webappsMapping, allLayouts);
}
exports.execute = execute;
exports.genDefaultLayouts = genDefaultLayouts;

2859
gaia/build/l10n/l20n.js Executable file

File diff suppressed because it is too large Load diff

98
gaia/build/l10n/xfail.list Executable file
View file

@ -0,0 +1,98 @@
apps/bluetooth/js/deviceList.js
apps/bluetooth/js/settings.js
apps/bluetooth/js/views/device_picker_panel.js
apps/bluetooth/js/views/pair_view.js
apps/bluetooth/js/views/pair_view_v1.js
apps/bluetooth/test/unit/modules/pair_manager_v1_test.js
apps/bookmark/js/activity_handler.js
apps/bookmark/js/bookmark_editor.js
apps/bookmark/js/bookmark_remover.js
apps/calendar/js/provider/caldav.js
apps/calendar/js/provider/local.js
apps/calendar/js/template.js
apps/calendar/js/templates/alarm.js
apps/calendar/js/view.js
apps/calendar/js/views/modify_event.js
apps/calendar/js/views/month_day.js
apps/calendar/js/views/view_event.js
apps/calendar/test/unit/view_test.js
apps/calendar/test/unit/views/view_event_test.js
apps/communications/contacts/js/export/bt.js
apps/communications/contacts/js/tag_options.js
apps/communications/contacts/js/utilities/normalizer.js
apps/communications/contacts/js/views/details.js
apps/communications/contacts/js/views/form.js
apps/communications/contacts/js/views/list.js
apps/communications/contacts/test/unit/contacts_action_menu_test.js
apps/communications/contacts/test/unit/contacts_activities_test.js
apps/communications/contacts/test/unit/export/bt_test.js
apps/communications/contacts/test/unit/export/generic_export_test.js
apps/communications/contacts/test/unit/import_vcard_test.js
apps/communications/contacts/test/unit/multiple_contact_del_test.js
apps/communications/contacts/test/unit/views/form_test.js
apps/communications/contacts/views/form/js/form_controller.js
apps/communications/contacts/views/form/js/form_ui.js
apps/communications/contacts/views/matching/js/matching_ui.js
apps/communications/dialer/js/mmi.js
apps/communications/dialer/js/mmi_ui.js
apps/costcontrol/js/common.js
apps/costcontrol/js/utils/toolkit.js
apps/costcontrol/test/unit/mock_common.js
apps/email/js/attachment_name.js
apps/email/js/cards.js
apps/email/js/cards/compose.js
apps/email/js/cards/message_list.js
apps/email/js/cards/settings_account_credentials.js
apps/email/js/cards/setup_account_prefs.js
apps/email/js/model_init.js
apps/email/js/toaster.js
apps/fl/js/utils.js
apps/homescreen/test/marionette/lib/homescreen.js
apps/keyboard/js/settings/layout_item_view.js
apps/pdfjs/content/web/viewer.js
apps/ringtones/js/built_in_ringtones.js
apps/ringtones/js/null_ringtone.js
apps/ringtones/js/sd_card_ringtones.js
apps/search/js/providers/marketplace.js
apps/search/js/providers/places.js
apps/sharedtest/test/unit/l10n/l10n_test.js
apps/system/js/accessibility.js
apps/system/js/app_install_manager.js
apps/system/js/bluetooth_transfer.js
apps/system/js/browser_context_menu.js
apps/system/js/cell_broadcast_system.js
apps/system/js/crash_reporter.js
apps/system/js/download/download_notification.js
apps/system/js/ime_menu.js
apps/system/js/keyboard_manager.js
apps/system/js/nfc_handover_manager.js
apps/system/js/permission_manager.js
apps/system/js/quick_settings.js
apps/system/js/screenshot.js
apps/system/js/sleep_menu.js
apps/system/js/device_storage_watcher.js
apps/system/js/update_manager.js
apps/system/test/unit/fxa_test/screens/fxam_signup_success_test.js
apps/system/test/unit/permission_manager_test.js
apps/system/test/unit/device_storage_watcher_test.js
apps/system/test/unit/updatable_test.js
apps/verticalhome/test/marionette/lib/verticalhome.js
dev_apps/feedback/js/feedback.js
dev_apps/geoloc/js/geoloc.js
shared/js/l10n_date.js
tv_apps/smart-home/js/edit.js
tv_apps/smart-home/js/home.js
tv_apps/smart-system/fxa/js/fxam_errors.js
tv_apps/smart-system/fxa/js/screens/fxam_enter_email.js
tv_apps/smart-system/fxa/js/screens/fxam_enter_password.js
tv_apps/smart-system/js/accessibility.js
tv_apps/smart-system/js/app_install_manager.js
tv_apps/smart-system/js/app_modal_dialog.js
tv_apps/smart-system/js/bluetooth_transfer.js
tv_apps/smart-system/js/browser_context_menu.js
tv_apps/smart-system/js/external_storage_monitor.js
tv_apps/smart-system/js/ime_menu.js
tv_apps/smart-system/js/remote_control.js
tv_apps/smart-system/js/update_manager.js
tv_apps/smart-system/js/value_selector/spin_date_picker.js
tv_apps/smart-system/js/value_selector/value_selector.js

78
gaia/build/media-resolution.js Executable file
View file

@ -0,0 +1,78 @@
'use strict';
/**
* Filter images/video by GAIA_DEV_PIXELS_PER_PX.
*/
var utils = require('./utils');
var MediaResolution = function() {
this.options = null;
};
MediaResolution.prototype.setOptions = function(options) {
this.options = options;
};
// If options.GAIA_DEV_PIXELS_PER_PX is not 1 and the file is a bitmap or video
// let's check if there is a bigger version in the directory.
// If so let's ignore the file in order to use the bigger version later.
MediaResolution.prototype.pickMediaByResolution = function(file) {
if (!/\.(png|gif|jpg|webm|mp4|m4v|ogg|ogv)$/.test(file.path)) {
return;
}
var gaiaPixelsPerPx = this.options.GAIA_DEV_PIXELS_PER_PX;
var suffix = '@' + gaiaPixelsPerPx + 'x';
var matchResult = /@([0-9]+\.?[0-9]*)x/.exec(file.path);
if ((gaiaPixelsPerPx === '1' && matchResult) ||
(matchResult && matchResult[1] !== gaiaPixelsPerPx) && file.exists()) {
file.remove(true);
}
if (gaiaPixelsPerPx !== '1') {
if (matchResult && matchResult[1] === gaiaPixelsPerPx) {
// Save the hidpi file to the zip, strip the name to be more generic.
utils.copyFileTo(file.path, file.parent.path,
utils.basename(file.path).replace(suffix, ''));
if (file.exists()) {
file.remove(true);
}
} else {
// Check if there a hidpi file. If yes, let's ignore this file since
// it will be loaded later (or it has already been loaded, depending on
// how the OS organize files.
var hqfile = utils.getFile(
file.path.replace(/(\.[a-z]+$)/, suffix + '$1'));
if (hqfile.exists() && file.exists()) {
file.remove(true);
}
}
}
};
MediaResolution.prototype.execute = function(options) {
this.setOptions(options);
// Sort listing by path to ensure hidpi files are processed *after* the
// corresponding 1x file
var buildDir = utils.getFile(options.webapp.buildDirectoryFilePath);
var files = utils.ls(buildDir, true).sort(function(a, b) {
if(a.path < b.path) {
return -1;
}
if(a.path > b.path) {
return 1;
}
return 0;
});
files.forEach(this.pickMediaByResolution, this);
};
function execute(options) {
(new MediaResolution()).execute(options);
}
exports.execute = execute;
exports.MediaResolution = MediaResolution;

552
gaia/build/multilocale.js Executable file
View file

@ -0,0 +1,552 @@
'use strict';
const utils = require('./utils');
const l20n = require('./l10n/l20n');
const RE_PROPERTY_LINE = /(.*)\s*[:=]\s*(.*)/;
const MODNAME = 'multilocale';
// This is the source locale. We will use it as a reference locale for others
// and use it for locales that lack localization data
const GAIA_SOURCE_LOCALE = 'en-US';
function L10nManager(gaiaDir,
localesFilePath,
localeBasedir,
subject) {
if (arguments.length < 3 || arguments.length > 4) {
throw new TypeError('Illegal constructor');
}
// subject is for optional arguments and can be omitted
subject = subject || {};
var self = this;
var localesFile = utils.resolve(localesFilePath, gaiaDir);
var baseDir = null;
if (localeBasedir) {
baseDir = utils.resolve(localeBasedir, gaiaDir);
}
[utils.getFile(gaiaDir), localesFile, baseDir]
.forEach(function(file) {
if (file && !file.exists()) {
throw new Error('file not found: ' + file.path);
}
});
this.locales = Object.keys(utils.getJSON(localesFile));
this.localeBasedir = baseDir ? baseDir.path : null;
this.gaiaDir = gaiaDir;
this.official = subject.official;
this.deviceType = subject.deviceType;
this.defaultLocale = subject.defaultLocale;
/**
* Copy l10n resources required by the .html file to build stage directory
*
* The resources will be copied either from app's source directory or
* from l10n repository.
*
* @param {nsIFile[]} file - HTML file
* @param {Object} webapp - A webapp object for specific app
* @param {nsIDocument} doc - document object
*/
function getL10nResources(file, webapp, doc) {
var isOfficialBranding;
// get all <link rel="localization">
var links = doc.querySelectorAll('link[rel="localization"]');
Array.prototype.forEach.call(links, function(link) {
var resURL = link.getAttribute('href');
var realURL = resURL;
// if the resource URL is a subject to branding, then
// add official/unofficial to the path
if (utils.isSubjectToBranding(utils.dirname(resURL))) {
isOfficialBranding = self.official === '1';
realURL = utils.joinPath(utils.dirname(resURL),
isOfficialBranding ? 'official' : 'unofficial',
utils.basename(resURL));
} else {
isOfficialBranding = false;
}
// XXX: We should use @formFactor for device specific L10N support,
// isSubjectToDeviceType should be removed after bug 936532 landed.
if (utils.isSubjectToDeviceType(resURL)) {
realURL = utils.joinPath(utils.dirname(resURL),
self.deviceType,
utils.basename(resURL));
}
for (var key in self.locales) {
var loc = self.locales[key];
var isAbsolute = realURL[0] === '/';
var relPathInApp = isAbsolute ? '' :
utils.dirname(file.path).substr(webapp.buildDirectoryFilePath.length);
var resFile =
getResourceFile(webapp, relPathInApp,
realURL, loc, isOfficialBranding);
var destFile = utils.getFile(webapp.buildDirectoryFilePath,
relPathInApp, realURL.replace('{locale}', loc));
if (!resFile.exists()) {
if (self.localeBasedir !== null) {
utils.log(MODNAME, 'Resource file not found: ' + resFile.path);
}
continue;
}
var parentOfDestFile = utils.getFile(destFile.path, '..');
utils.ensureFolderExists(parentOfDestFile);
utils.copyFileTo(resFile, parentOfDestFile.path,
destFile.leafName);
}
});
}
/**
* Get l10n resource file for a given locale.
*
* @param {Object} webapp - A webapp object for specific app
* @param {String} relPathInApp - Relative path of the html file
* For example: /contacts
* @param {nsIFile[]} resURL - URL to the resource
* @param {String} loc - Locale code
* @param {Boolean} isOfficialBranding - Is the file part of the branding
* @returns {nsIFile[]} resFile - L10n resource file object
*/
function getResourceFile(webapp,
relPathInApp,
resURL,
loc,
isOfficialBranding) {
function cleanPath(str) {
// removes locales/ and {locale}
// so transforms:
// locales/foo.{locale}.res => foo.res
// ./shared/locales/foo/bar.{locale}.res => ./shared/foo/bar.res
// foo.properties
str = str.replace(/locales\//, '');
str = str.replace('{locale}.', '');
return utils.normalizePath(str);
}
var isShared = /^\.?\/?shared\//.test(resURL);
var paths = [];
// this flag defines if for the given locale we will take resources
// from the source directory or from LOCALE_BASEDIR directory
var useSourceDir = false;
if (self.localeBasedir === null) {
useSourceDir = true;
}
// for GAIA_SOURCE_LOCALE use source directory
if (loc === GAIA_SOURCE_LOCALE) {
useSourceDir = true;
}
// if the file is a part of the branding and we are official
// use source directory
if (isOfficialBranding) {
useSourceDir = true;
loc = GAIA_SOURCE_LOCALE;
}
if (useSourceDir) {
if (isShared) {
paths.push(self.gaiaDir);
} else {
paths.push(webapp.sourceDirectoryFilePath);
paths.push(relPathInApp);
}
paths.push(resURL.replace('{locale}', loc));
} else {
paths.push(self.localeBasedir);
paths.push(loc);
if (!isShared) {
var sourceDirectoryFile = utils.getFile(webapp.sourceDirectoryFilePath);
var parentOfSourceDirFile =
utils.getFile(webapp.sourceDirectoryFilePath, '..');
paths.push(parentOfSourceDirFile.leafName);
paths.push(sourceDirectoryFile.leafName);
paths.push(relPathInApp);
}
paths.push(cleanPath(resURL));
}
var resFile = utils.getFile.apply(null, paths);
return resFile;
}
/**
* Localize all manifest and copy properties files.
*
* @param {nsIFile[]} htmlFiles - all files in webapp source tree
* @param {Object} webapp - A webapp object for specific app
*/
function localize(htmlFiles, webapp) {
// Localize webapp's manifest.webapp file.
localizeManifest(webapp);
htmlFiles.forEach(function(htmlFile) {
var content = utils.getFileContent(htmlFile);
// if there is no localization word in the file, don't even parse it
// exit early
if (content.indexOf('localization') === -1) {
return;
}
var doc = utils.getDocument(content);
buildL10nMeta(htmlFile, doc);
if (self.localeBasedir) {
// Copy resource files into build_stage directory
getL10nResources(htmlFile, webapp, doc);
}
});
}
function getTimestamp(date) {
var chunks = [
date.getFullYear(),
date.getMonth(),
date.getDate(),
date.getHours(),
date.getMinutes()
];
return chunks.map(function(c) {
return c < 10 ? ('0' + c) : c.toString();
}).join('');
}
function createMeta(doc, name) {
var meta = doc.createElement('meta');
meta.setAttribute('name', name);
var head = doc.querySelector('head');
return head.appendChild(meta);
}
function buildL10nMeta(file, doc) {
var metas = {
availableLanguages: doc.querySelector('meta[name="availableLanguages"]'),
defaultLanguage: doc.querySelector('meta[name="defaultLanguage"]'),
appVersion: doc.querySelector('meta[name="appVersion"]')
};
if ((!metas.availableLanguages || !metas.defaultLanguage) &&
doc.querySelector('link[rel="manifest"]')) {
// So, the app is using obsolete l10n meta model with a link to the
// manifest.
// Let's warn the user.
utils.log(MODNAME,
'WARNING: \n In HTML file: ' + file.path + ', ' +
'obsolete link to w3c manifest is used for l10n meta. '+
'Please, replace with:\n' +
' <meta name="availableLanguages" content="en-US">\n' +
' <meta name="defaultLanguage" content="en-US">\n' +
' Read more at: http://bugzil.la/1115807');
}
// ... and save him... for now.
if (!metas.availableLanguages) {
metas.availableLanguages = createMeta(doc, 'availableLanguages');
}
if (!metas.defaultLanguage) {
metas.defaultLanguage = createMeta(doc, 'defaultLanguage');
}
if (!metas.appVersion) {
metas.appVersion = createMeta(doc, 'appVersion');
}
metas.defaultLanguage.setAttribute('content', self.defaultLocale);
var timestamp = getTimestamp(new Date());
metas.availableLanguages.setAttribute('content',
self.locales.map(function(loc) {
return loc + ':' + timestamp;
}).join(', '));
var settingsFile = utils.getFile(self.gaiaDir, 'build', 'config',
'common-settings.json');
var settings = utils.getJSON(settingsFile);
metas.appVersion.setAttribute('content', settings['langpack.channel']);
var str = utils.serializeDocument(doc);
utils.writeContent(file, str);
}
/**
* Localize manifest.webapp file.
* Propagate locale codes into manifest's
* `locales` key and `entry_points[].locales`
*
* @param {Object} webapp - A webapp object for specific app
*/
function localizeManifest(webapp) {
var buildManifestFile = utils.getFile(webapp.buildManifestFilePath);
var manifest = utils.getJSON(buildManifestFile);
if (manifest.default_locale) {
manifest.default_locale = self.defaultLocale;
}
// If manifest.webapp does not have `locales` key, return early
if (!manifest.locales) {
return;
}
// Build locale properties based on GAIA_SOURCE_LOCALE data
// from manifest.webapp
var sourceLocaleProps = buildSourceLocaleProps(manifest, webapp);
// Reset `locales` key
manifest.locales = {};
if (manifest.entry_points) {
for (var name in manifest.entry_points) {
manifest.entry_points[name].locales = {};
}
}
self.locales.forEach(function(locale) {
var manifestProps;
if (locale === GAIA_SOURCE_LOCALE) {
manifestProps = sourceLocaleProps;
} else if (locale in l20n.pseudo) {
manifestProps = l20n.walkValue(
sourceLocaleProps, l20n.pseudo[locale].process);
} else {
manifestProps = getManifestProperties(webapp, locale);
}
manifest.locales[locale] = buildLocalizedManifestEntry(
manifestProps,
sourceLocaleProps,
'default'
);
if (manifest.entry_points) {
for (var name in manifest.entry_points) {
var ep = manifest.entry_points[name];
ep.locales[locale] = buildLocalizedManifestEntry(
manifestProps,
sourceLocaleProps,
'entry_points',
name
);
}
}
});
utils.writeContent(buildManifestFile, JSON.stringify(manifest));
}
/**
* Build an object with localization metadata, taken from the manifest file
* for the GAIA_SOURCE_LOCALE locale
*
* It may look like this:
* {
* default: {
* name: "App",
* description: "App's description"
* },
* entry_points: {
* dialer: {
* name: "App's Dialer",
* description: "App's Dialer's description"
* }
* }
* }
*
* This data will be used as a reference point for localization of the
* manifest data to other locales.
*
* @param {Object} manifest - Manifest.webapp's data object
* @param {Object} webapp - A webapp object for specific app
*/
function buildSourceLocaleProps(manifest, webapp) {
manifest = utils.cloneJSON(manifest);
if (!manifest.locales[GAIA_SOURCE_LOCALE]) {
utils.log(MODNAME,
'In manifest file: ' + webapp.buildManifestFilePath + ', ' +
'missing locales key for locale: ' + GAIA_SOURCE_LOCALE);
}
var sourceLocaleProps = {
default: manifest.locales[GAIA_SOURCE_LOCALE],
entry_points: {}
};
if (manifest.entry_points) {
for (var name in manifest.entry_points) {
sourceLocaleProps.entry_points[name] =
manifest.entry_points[name].locales[GAIA_SOURCE_LOCALE];
}
}
return sourceLocaleProps;
}
/**
* Creates an l10n data object for a given locale based on the
* keys from the source locale.
*
* If the localization object lacks any of the keys, they are taken
* from the source locale as well.
*
* @param {Object} manifestProps - L10n strings for a locale
* @param {Object} sourceProps - L10n strings for a source locale
* @param {String} type - `default` or `entry_points`
* @param {String} name - in `entry_points` case, name of the
* entry point
* @returns {Object} val - Result l10n strings for a locale
*/
function buildLocalizedManifestEntry(manifestProps,
sourceProps,
type,
name) {
function traverseObject(node, key) {
if (node &&
key in node) {
return node[key];
} else {
return null;
}
}
var val = {};
var sourceRoot = sourceProps[type];
var manifestRoot = traverseObject(manifestProps, type);
if (type === 'entry_points') {
sourceRoot = sourceRoot[name];
manifestRoot = traverseObject(manifestRoot, name);
}
for (var key in sourceRoot) {
if (manifestRoot &&
key in manifestRoot) {
val[key] = manifestRoot[key];
} else {
val[key] = sourceRoot[key];
}
}
return val;
}
/**
* Creates an l10n data object for a given locale based on the
* keys from the source locale.
*
* If the localization object lacks any of the keys, they are taken
* from the source locale as well.
*
* @param {Object} webapp - A webapp object for specific app
* @param {String} locale - Locale code
* @returns {Object} res - Manifest l10n resource
*/
function getManifestProperties(webapp, locale) {
var parent = utils.getFile(webapp.sourceDirectoryFilePath, '..');
var propFile = utils.getFile(self.localeBasedir, locale, parent.leafName,
webapp.sourceDirectoryName, 'manifest.properties');
if (!propFile.exists()) {
return null;
}
var content = utils.getFileContent(propFile);
return parseManifestProperties(content);
}
/**
* parsing a properties file for manifest localization to an object.
*
* @param {String} content - content of properties file.
* @returns {Object} object - an object with "default" and "entry_points"
* fields, Special keys with one dot, like
* "dialer.name = Foo" will be store in entrypoints
* as "dialer" attribute refering to the object
* {"name": "foo"}. And all other regular keys are
* returned as a dictionary in default attribute.
*/
function parseManifestProperties(content) {
var prop = {
'default': {},
'entry_points': {}
};
content.split('\n').forEach(function(line) {
var matched = line.match(RE_PROPERTY_LINE);
if (!matched || line.trim().indexOf('#') === 0) {
return;
}
var value = matched[2].trim();
var parts = matched[1].split('.');
var entryPoint, key;
// if we got a key with "." such as dialer.name, create an new
// entry_point for it.
if (parts.length === 2) {
entryPoint = parts[0];
key = parts[1];
if (!prop.entry_points[entryPoint]) {
prop.entry_points[entryPoint] = {};
}
prop.entry_points[entryPoint][key.trim()] = value;
} else if (parts.length > 2) {
throw new Error('More than one dot in an entry: ' + line);
} else {
key = matched[1];
prop['default'][key.trim()] = value;
}
});
return prop;
}
this.localize = localize;
this.localizeManifest = localizeManifest;
}
function execute(options) {
var webapp = options.webapp;
var localeBasedir = null;
if (options.LOCALE_BASEDIR) {
// Bug 952901: remove getLocaleBasedir() if bug 952900 fixed.
localeBasedir = utils.getLocaleBasedir(options.LOCALE_BASEDIR);
}
var l10nManager = new L10nManager(
options.GAIA_DIR,
options.LOCALES_FILE,
localeBasedir,
{
official: options.OFFICIAL,
defaultLocale: options.GAIA_DEFAULT_LOCALE,
deviceType: options.GAIA_DEVICE_TYPE,
});
if (utils.isExternalApp(webapp)) {
return;
}
var buildDirectoryFile = utils.getFile(webapp.buildDirectoryFilePath);
var excluded = new RegExp(webapp.buildDirectoryFilePath + '.*\/tests?');
var files = utils.ls(buildDirectoryFile, true).filter(function(file) {
return !(excluded.test(file.path));
});
l10nManager.localize(files.filter(function(file) {
return /\.html$/.test(file.path);
}), webapp);
}
exports.execute = execute;
exports.L10nManager = L10nManager;

21
gaia/build/post-app.js Executable file
View file

@ -0,0 +1,21 @@
'use strict';
exports.execute = function(options) {
require('./media-resolution').execute(options);
require('./post-manifest').execute(options);
require('./multilocale').execute(options);
require('./copy-build-stage-data').execute(options);
if (options.RAPTOR_TRANSFORM === '1') {
require('./raptor-test-transformer').execute(options);
}
require('./webapp-optimize').execute(options).then(function() {
if (options.DEBUG === '0') {
require('./webapp-zip').execute(options);
}
});
};

33
gaia/build/post-manifest.js Executable file
View file

@ -0,0 +1,33 @@
'use strict';
/**
* Updates manifest.webapp
*/
var utils = require('./utils');
function execute(options) {
var webapp = options.webapp;
if (utils.isExternalApp(webapp)) {
return;
}
var buildManifestFile = utils.getFile(webapp.buildManifestFilePath);
var manifest = utils.getJSON(buildManifestFile);
// Forces the presence of `origin` field in order to help WebIDE overriding
// the app, with the same origin.
manifest.origin = webapp.url;
// Get the Gaia version and set it as an app version in manifest.webapp.
// It's used by Langpack API
var settingsFile = utils.getFile(options.GAIA_DIR, 'build', 'config',
'common-settings.json');
var settings = utils.getJSON(settingsFile);
manifest.version = settings['langpack.channel'];
utils.writeContent(buildManifestFile, JSON.stringify(manifest));
}
exports.execute = execute;

28
gaia/build/pre-app.js Executable file
View file

@ -0,0 +1,28 @@
'use strict';
function execute(options) {
require('./clean-stage-app').execute(options);
require('./svoperapps').execute(options);
require('./webapp-manifests').execute(options);
require('./contacts-import-services').execute(options);
require('./search-provider').execute(options);
require('./keyboard-layouts').execute(options);
require('./preferences').execute(options);
if (options.BUILD_APP_NAME == '*') {
require('./settings').execute(options);
}
require('./webapp-shared').execute(options);
require('./copy-common-files').execute(options);
}
exports.execute = execute;

313
gaia/build/preferences.js Executable file
View file

@ -0,0 +1,313 @@
'use strict';
/**
* Generate user.js
*/
var utils = require('./utils');
// Optional partner provided preference files. They will be added
// after the ones on the extenedPrefFiles and they will be read
// from the GAIA_DISTRIBUTION_DIR directory
var PARTNER_PREF_FILES = ['partner-prefs.js'];
var PreferencesBuilder = function() {
};
PreferencesBuilder.prototype.setConfig = function(config) {
this.config = config;
this.prefs = {};
this.userPrefs = {};
this.gaia = utils.gaia.getInstance(this.config);
// Optional files that may be provided to extend the set of default
// preferences installed for gaia. If the preferences in these files
// conflict, the result is undefined.
this.extenedPrefFiles = ['custom-prefs.js', 'gps-prefs.js',
'payment-prefs.js'];
if (config.GAIA_APP_TARGET === 'engineering') {
this.extenedPrefFiles.push('payment-dev-prefs.js');
} else if (config.GAIA_APP_TARGET === 'dogfood') {
this.extenedPrefFiles.push('dogfood-prefs.js');
}
if (config.DEBUG === '1') {
this.extenedPrefFiles.push('debug-prefs.js');
}
};
PreferencesBuilder.prototype.execute = function(config) {
this.setConfig(config);
this.preparePref();
this.loadBuildPrefs();
if (this.gaia.engine === 'xpcshell') {
let jsPath = utils.getFile(this.config.PROFILE_DIR, 'user.js').path;
this.writePrefs(jsPath, this.prefs, this.userPrefs);
} else if (this.gaia.engine === 'b2g') {
this.setPrefs();
}
this.writeDefaultUserJs();
};
PreferencesBuilder.prototype.writeDefaultUserJs = function() {
if (this.config.BUILD_APP_NAME !== '*') {
return;
}
// Create a folder to store data for B2G, this folder will copy to b2g output
// folder.
let prefDir = utils.getFile(this.config.PROFILE_DIR, 'defaults', 'pref');
utils.ensureFolderExists(prefDir);
let allPrefs = utils.cloneJSON(this.prefs);
for (let pref in this.userPrefs) {
allPrefs[pref] = this.userPrefs[pref];
}
this.writePrefs(utils.joinPath(prefDir.path, 'user.js'), allPrefs, {});
};
PreferencesBuilder.prototype.loadBuildPrefs = function() {
var buildConfigDir = utils.getFile(this.config.GAIA_DIR, 'build', 'config');
this.loadPrefFile(buildConfigDir, this.extenedPrefFiles);
var deviceConfigDir = utils.getFile(this.config.GAIA_DIR, 'build', 'config',
this.config.GAIA_DEVICE_TYPE);
this.loadPrefFile(deviceConfigDir, this.extenedPrefFiles);
try {
var distDir = utils.getFile(this.config.GAIA_DISTRIBUTION_DIR);
this.loadPrefFile(distDir, PARTNER_PREF_FILES);
} catch (e) {
// The utils.getFile will throw exception if GAIA_DISTRIBUTION_DIR does not
// exist. In this case we just don't override preferences by
// PARTNER_PREF_FILES.
}
};
PreferencesBuilder.prototype.loadPrefFile = function(srcDir, srcPrefFiles) {
// Scope object for hosting functions and variables.
var self = this;
var scope = {
user_pref: function(key, value) {
self.userPrefs[key] = self.customizePrefValue(key, value);
},
pref: function fillPref(key, value) {
self.prefs[key] = self.customizePrefValue(key, value);
}
};
// Read all files
srcPrefFiles.forEach(function(filename) {
var srcFile = utils.getFile(srcDir.path, filename);
if (srcFile.exists()) {
utils.scriptLoader.load(srcFile.path, scope, true);
}
});
};
PreferencesBuilder.prototype.customizePrefValue = function(key, value) {
switch(key) {
case 'devtools.responsiveUI.customWidth':
// The responsive UI has padding and border at width.
// width = 320 + 15(horizontal padding) * 2 + 1(vertical border) * 2
return value + 32;
case 'devtools.responsiveUI.customHeight':
// The responsive UI has padding and border at width.
// height = 480 + 60(top padding) + 1(top border)
return value + 61;
}
return value;
};
PreferencesBuilder.prototype.preparePref = function() {
this.system = this.config.SYSTEM;
this.userPrefs['b2g.system_manifest_url'] = this.system + '/manifest.webapp';
this.userPrefs['b2g.neterror.url'] = this.system + '/net_error.html';
if (this.system.substring(0, 6) == 'app://') { // B2G bug 773884
this.system += '/index.html';
}
this.userPrefs['b2g.system_startup_url'] = this.system;
this.domains = [this.config.GAIA_DOMAIN, 'theme.' + this.config.GAIA_DOMAIN];
this.config.GAIA_ALLAPPDIRS.split(' ').forEach(function(appdir) {
this.domains.push(utils.getFile(appdir).leafName + '.' +
this.config.GAIA_DOMAIN);
}, this);
this.userPrefs['network.http.max-connections-per-server'] = 15;
this.userPrefs['dom.mozInputMethod.enabled'] = true;
this.userPrefs['layout.css.scroll-behavior.enabled'] = true;
this.userPrefs['layout.css.sticky.enabled'] = true;
this.userPrefs['intl.uidirection.ar-x-psbidi'] = 'rtl';
// For https://bugzilla.mozilla.org/show_bug.cgi?id=811605 to let user know
// what prefs is for ril debugging
this.userPrefs['ril.debugging.enabled'] = false;
// Gaia has no vCard/vCalendar for now. Override MMS version to v1.1:
// TODO: remove this override after having vCard/vCalendar implemented in
// Gaia.
// @see bug 885683 - [Messages] MMS doesn't support sending and receiving
// vCard attachments.
// @see bug 905548 - [Messages] MMS doesn't support sending and receiving
// vCalendar attachments.
this.userPrefs['dom.mms.version'] = 0x11;
// TODO: Once platform enabled unsafe WPA-EAP, we have to remove this flag
// here.
// @see Bug 790056 - Enable WPA Enterprise
this.userPrefs['b2g.wifi.allow_unsafe_wpa_eap'] = true;
if (this.config.LOCAL_DOMAINS === '1') {
this.setLocalDomainPref();
}
if (this.config.DESKTOP === '1') {
this.setDesktopPref();
}
if (this.config.DEBUG === '1') {
this.setDebugPref();
}
if (this.config.DEVICE_DEBUG === '1') {
this.setDeviceDebugPref();
}
// Turn on user timing output to logcat
if (this.config.PERF_LOGGING === '1') {
this.userPrefs['dom.performance.enable_user_timing_logging'] = true;
}
// Turn off hardware composing
if (this.config.HARDWARE_COMPOSER === '0') {
this.userPrefs['layers.composer2d.enabled'] = false;
}
// If this is already equal to the profile directory, no need to
// write the preference
if (this.config.COREWEBAPPS_DIR !== this.config.PROFILE_DIR) {
this.userPrefs['b2g.coreappsdir'] =
this.config.COREWEBAPPS_DIR;
}
};
PreferencesBuilder.prototype.setLocalDomainPref = function() {
this.userPrefs['network.dns.localDomains'] = this.domains.join(',');
};
PreferencesBuilder.prototype.setDesktopPref = function() {
this.userPrefs['extensions.autoDisableScopes'] = 0;
this.userPrefs['xpinstall.signatures.required'] = false;
};
PreferencesBuilder.prototype.setDebugPref = function() {
this.userPrefs['docshell.device_size_is_page_size'] = true;
this.userPrefs['marionette.defaultPrefs.enabled'] = true;
this.userPrefs['nglayout.debug.disable_xul_cache'] = true;
this.userPrefs['nglayout.debug.disable_xul_fastload'] = true;
this.userPrefs['javascript.options.showInConsole'] = true;
this.userPrefs['browser.dom.window.dump.enabled'] = true;
this.userPrefs['dom.report_all_js_exceptions'] = true;
this.userPrefs['dom.w3c_touch_events.enabled'] = 1;
this.userPrefs['dom.promise.enabled'] = true;
this.userPrefs['dom.wakelock.enabled'] = true;
this.userPrefs['image.mozsamplesize.enabled'] = true;
this.userPrefs['webgl.verbose'] = true;
// Turn off unresponsive script dialogs so test-agent can keep running...
// https://bugzilla.mozilla.org/show_bug.cgi?id=872141
this.userPrefs['dom.max_script_run_time'] = 0;
// Identity debug messages
this.userPrefs['toolkit.identity.debug'] = true;
// Preferences for httpd
// (Use JSON.stringify in order to avoid taking care of `\` escaping)
this.userPrefs['extensions.gaia.dir'] = this.config.GAIA_DIR;
this.userPrefs['extensions.gaia.domain'] = this.config.GAIA_DOMAIN;
this.userPrefs['extensions.gaia.port'] =
parseInt(this.config.GAIA_PORT.replace(/:/g, ''), 10);
this.userPrefs['extensions.gaia.appdirs'] = this.config.GAIA_APPDIRS;
this.userPrefs['extensions.gaia.allappdirs'] = this.config.GAIA_ALLAPPDIRS;
this.userPrefs['extensions.gaia.locales_debug_path'] =
this.config.GAIA_LOCALES_PATH;
this.userPrefs['extensions.gaia.official'] = Boolean(this.config.OFFICIAL);
this.userPrefs['extensions.gaia.locales_file'] = this.config.LOCALES_FILE;
// Bug 952901: remove getLocaleBasedir() if bug 952900 fixed.
this.userPrefs['extensions.gaia.locale_basedir'] = utils.getLocaleBasedir(
this.config.LOCALE_BASEDIR);
var suffix = this.config.GAIA_DEV_PIXELS_PER_PX === '1' ?
'' : '@' + this.config.GAIA_DEV_PIXELS_PER_PX + 'x';
this.userPrefs['extensions.gaia.device_pixel_suffix'] = suffix;
this.userPrefs['extensions.autoDisableScopes'] = 0;
this.userPrefs['xpinstall.signatures.required'] = false;
// electrolysis breaks the app:// protocol as registered by httpd.js
// see Bug 1097912
this.userPrefs['browser.tabs.remote.autostart'] = false;
this.userPrefs['browser.tabs.remote.autostart.1'] = false;
this.userPrefs['browser.tabs.remote.autostart.2'] = false;
// Disable dialog asking to set firefox as default OS browser
this.userPrefs['browser.shell.checkDefaultBrowser'] = false;
};
PreferencesBuilder.prototype.setDeviceDebugPref = function() {
this.userPrefs['dom.apps.developer_mode'] = true;
this.userPrefs['network.disable.ipc.security'] = true;
this.userPrefs['dom.webcomponents.enabled'] = true;
this.userPrefs['devtools.debugger.prompt-connection'] = false;
this.userPrefs['devtools.debugger.forbid-certified-apps'] = false;
// Bug 1001348: This optimization prevents debugger to fetch script sources
// of certified apps as well as chrome code:
this.userPrefs['javascript.options.discardSystemSource'] = false;
this.userPrefs['b2g.adb.timeout'] = 0;
};
PreferencesBuilder.prototype.writePrefs = function(jsPath, prefs, userPrefs) {
var userJs = utils.getFile(jsPath);
var content = '';
var pref;
// Output pref
for (pref in prefs) {
content += 'pref(\'' + pref + '\', ' + JSON.stringify(prefs[pref]) + ');\n';
}
// Output user_pref
for (pref in userPrefs) {
content += 'user_pref(\'' + pref + '\', ' +
JSON.stringify(userPrefs[pref]) + ');\n';
}
utils.writeContent(userJs, content + '\n');
};
PreferencesBuilder.prototype.setPrefs = function() {
[this.prefs, this.userPrefs].forEach(function(prefs) {
for (var pref in prefs) {
switch(typeof prefs[pref]) {
case 'string':
utils.Services.prefs.setCharPref(pref, prefs[pref]);
break;
case 'boolean':
utils.Services.prefs.setBoolPref(pref, prefs[pref]);
break;
case 'number':
utils.Services.prefs.setIntPref(pref, prefs[pref]);
break;
default:
throw new Error('Unsupported pref type: ' + typeof prefs[pref]);
}
}
});
};
exports.execute = function(config) {
if (config.BUILD_APP_NAME !== '*') {
return;
}
(new PreferencesBuilder()).execute(config);
};
exports.PreferencesBuilder = PreferencesBuilder;

33
gaia/build/preload.js Executable file
View file

@ -0,0 +1,33 @@
'use strict';
var utils = require('./utils');
var python = new utils.Commander('python');
exports.execute = function(opts) {
var GAIA_DISTRIBUTION_DIR = utils.getEnv('GAIA_DISTRIBUTION_DIR');
if (GAIA_DISTRIBUTION_DIR === '') {
return;
}
var GAIA_DIR = utils.getEnv('GAIA_DIR');
var remoteList;
try {
remoteList = utils.getFile(GAIA_DISTRIBUTION_DIR, 'remote.list');
} catch(e) {}
if (!remoteList || !remoteList.exists()) {
return;
}
var appFolder = utils.getFile(GAIA_DISTRIBUTION_DIR, 'outoftree_apps/');
utils.deleteFile(appFolder.path, true);
utils.ensureFolderExists(appFolder);
utils.copyFileTo(remoteList, appFolder.path, 'list');
var preload = utils.getFile(GAIA_DIR, 'tools/preload.py').path;
python.initPath(utils.getEnvPath());
return python.run([preload, '--root=' + appFolder.path]);
};

151
gaia/build/preprocessor.js Executable file
View file

@ -0,0 +1,151 @@
'use strict';
var utils = require('./utils');
const jsSuffix = /\.js$/;
const htmlSuffix = /\.(html|htm)$/;
const cssSuffix = /\.css$/;
function removeFiles(stagePath, list) {
list.forEach((fileName) => {
fileName.unshift(stagePath);
var filePath = utils.joinPath.apply(this, fileName);
utils.log('preprocessor', 'remove file:', filePath);
utils.deleteFile(filePath);
});
}
function processContent(flag, enable, content, type) {
var replaced;
var regexp;
if (enable) {
if (type === 'html') {
regexp = new RegExp(
'<!--IFNDEF_' + flag + '[^]*?ENDIF_' + flag + '-->', 'mg'
);
replaced = content.replace(regexp, '');
replaced = replaced.
replace('<!--IFDEF_' + flag, '', 'g').
replace('ENDIF_' + flag + '-->', '', 'g');
} else if (type === 'js') {
regexp = new RegExp(
'//IFNDEF_' + flag + '[^]*?//ENDIF_' + flag, 'mg'
);
replaced = content.replace(regexp, '');
replaced = replaced.
replace('//IFDEF_' + flag, '', 'g').
replace('//ENDIF_' + flag, '', 'g');
} else if (type === 'css') {
regexp = new RegExp(
'/*IFNDEF_' + flag + '[^]*?/*ENDIF_' + flag, 'mg'
);
replaced = content.replace(regexp, '');
replaced = replaced.
replace('/*IFDEF_' + flag + '*/', '', 'g').
replace('/*ENDIF_' + flag + '*/', '', 'g');
}
} else {
if (type === 'html') {
regexp = new RegExp(
'<!--IFDEF_' + flag + '[^]*?ENDIF_' + flag + '-->', 'mg'
);
replaced = content.replace(regexp, '');
replaced = replaced.
replace('<!--IFNDEF_' + flag, '', 'g').
replace('ENDIF_' + flag + '-->', '', 'g');
} else if (type === 'js') {
regexp = new RegExp(
'//IFDEF_' + flag + '[^]*?//ENDIF_' + flag, 'mg'
);
replaced = content.replace(regexp, '');
replaced = replaced.
replace('//IFNDEF_' + flag, '', 'g').
replace('//ENDIF_' + flag, '', 'g');
} else if (type === 'css') {
regexp = new RegExp(
'\/\*IFDEF_' + flag + '\*\/[^]*?\/\*ENDIF_' + flag + '\*\/', 'mg'
);
replaced = content.replace(regexp, '');
replaced = replaced.
replace('/*IFNDEF_' + flag + '*/', '', 'g').
replace('/*ENDIF_' + flag + '*/', '', 'g');
}
}
return replaced;
}
function processFiles(flag, enable, list, stagePath) {
list.forEach((fileName) => {
fileName.unshift(stagePath);
utils.log('preprocessor', flag, enable, fileName);
var file = utils.getFile.apply(this, fileName);
var fileContent = utils.getFileContent(file);
var type;
if (htmlSuffix.test(file.path)) {
type = 'html';
} else if (jsSuffix.test(file.path)) {
type = 'js';
} else if (cssSuffix.test(file.path)) {
type = 'css';
}
var replacedContent = processContent(flag, enable, fileContent, type);
utils.writeContent(file, replacedContent);
});
}
/*
Preprocessor module provides a C/C++ style preprocessor mechanism to
customize HTML/Javascript/CSS source codes at build stage with a build flag.
Here is the file list example:
var fileList = {
process:[ // Process all IFDEF/IFNDEF/ENDIF tag for each source code in
// the array.
['index.html'],
['elements', 'root.html']
],
remove: {
"ifndef": [ // Remove all files in the array when the flag is disabled.
['js', 'example.js'],
['elements', 'example.html']
],
"ifdef": [ // Remove all the files in the array when the flag is enabled.
['js', 'other_example.js'],
['elements', 'other_example.html']
]
}
};
// Enable:
// $ EXAMPLE_FLAG=1 make
preprocessor.execute(options, 'EXAMPLE_FLAG', fileList);
*/
exports.execute = function(options, flag, list) {
var stagePath = options.STAGE_APP_DIR;
var enable = options[flag] === '1';
processFiles(flag, enable, list.process, stagePath);
if (list.remove) {
// For backwards compatibility - if the the remove list is an array, remove
// if the flag is NOT enabled.
if (Array.isArray(list.remove)) {
if (!enable) {
removeFiles(stagePath, list.remove);
}
} else {
if (enable) {
if (list.remove.ifdef) {
removeFiles(stagePath, list.remove.ifdef);
}
} else {
if (list.remove.ifndef) {
removeFiles(stagePath, list.remove.ifndef);
}
}
}
}
};

238
gaia/build/push-to-device.js Executable file
View file

@ -0,0 +1,238 @@
'use strict';
var utils = require('./utils');
var sh = new utils.Commander('sh');
var Q = utils.Q;
sh.initPath(utils.getEnvPath());
/**
* detect if b2g process needs to be restarted
* @param {string} appName app name
* @return {bool} true if needed
*/
function needsB2gRestart(appName) {
// b2g should be restarted if these app name is assigned by APP=appname
var appList = ['system', 'callscreen'];
return (appList.indexOf(appName) !== -1);
}
function pushToDevice(profileFolder, remotePath, adb) {
// MingGW on Windows takes '/remote/src' as 'c:\remote\src' which is
// not right, so we use two slash before the remote path to prevent it.
var webapps_path = '/' + remotePath + '/webapps';
var indexedDbFile;
var queue = Q.defer();
queue.resolve();
return queue.promise.then(function() {
return sh.run(['-c', adb + ' shell rm -r ' + webapps_path]);
}).then(function() {
return sh.run(['-c', adb + ' shell rm //data/local/user.js']);
}).then(function() {
// adb push /gaia/profile/webapps /system/b2g/webapps
return sh.run(['-c', adb + ' push "' + utils.joinPath(profileFolder,
'webapps') + '" ' + webapps_path]);
}).then(function() {
// adb push /gaia/profile/user.js /data/local/user.js
return sh.run(['-c', adb + ' push "' + utils.joinPath(profileFolder,
'user.js') + '" //data/local/user.js']);
}).then(function() {
indexedDbFile = utils.getFile(profileFolder, 'indexedDB');
}).then(function() {
if (indexedDbFile.exists() && indexedDbFile.isDirectory()) {
// adb push /gaia/profile/indexedDB /data/local/indexedDB
return sh.run(['-c', adb + ' push "' + indexedDbFile.path +
'" //data/local/indexedDB']);
}
});
}
function installSvoperapps(profileFolder, adb) {
var svoperappsUrl = '//data/local/svoperapps';
var queue = Q.defer();
queue.resolve();
return queue.promise.then(function() {
return sh.run(['-c', adb + ' shell rm -r ' + svoperappsUrl]);
}).then(function() {
return sh.run(['-c', adb + ' push "' + utils.joinPath(profileFolder,
'svoperapps') + '" ' + svoperappsUrl]);
});
}
function installOneApp(targetFolder, buildAppName,
remotePath, gaiaDomain,
adb) {
// Instead of push to remote path directly, we push to temp folder and then
// cat to overwrite the file. This way the original file will not be deleted
// and reading from the already opened fd will get updated content. So even we
// can't clear the zip cache, will still have updated app launched next time.
var queue = Q.defer();
queue.resolve();
return queue.promise.then(function() {
// "adb push /gaia/profile/webapps/SOME_APP.gaiamobile.org/manifest.webapp
// /data/local/tmp/pushgaia/SOME_APP.gaiamobile.org/manifest.webapp"
return sh.run(['-c',
adb + ' push "' + utils.joinPath(targetFolder, 'manifest.webapp') +
'" //data/local/tmp/pushgaia/' + buildAppName + '.' + gaiaDomain +
'/manifest.webapp']);
}).then(function() {
// "adb push /gaia/profile/webapps/SOME_APP.gaiamobile.org/application.zip
// /data/local/tmp/pushgaia/SOME_APP.gaiamobile.org/application.zip"
return sh.run(['-c',
adb + ' push "' + utils.joinPath(targetFolder, 'application.zip') +
'" //data/local/tmp/pushgaia/' + buildAppName + '.' + gaiaDomain +
'/application.zip']);
}).then(function() {
// "adb shell cat /data/local/tmp/pushgaia/SOME_APP.gaiamobile.org/manifes
// t.webapp > /system/b2g/webapps/SOME_APP.gaiamobile.org/manifest.webapp"
return sh.run(['-c',
adb + ' shell "cat /data/local/tmp/pushgaia/' + buildAppName +
'.' + gaiaDomain + '/manifest.webapp > ' + remotePath + '/webapps/' +
buildAppName + '.' + gaiaDomain + '/manifest.webapp"']);
}).then(function() {
// "adb shell cat /data/local/tmp/pushgaia/SOME_APP.gaiamobile.org/applica
// tion.zip > /system/b2g/webapps/SOME_APP.gaiamobile.org/application.zip"
return sh.run(['-c',
adb + ' shell "cat /data/local/tmp/pushgaia/' + buildAppName +
'.' + gaiaDomain + '/application.zip > ' + remotePath + '/webapps/' +
buildAppName + '.' + gaiaDomain + '/application.zip"']);
}).then(function() {
// "adb shell rm -rf /data/local/tmp/pushgaia"
return sh.run(['-c', adb + ' shell rm -rf //data/local/tmp/pushgaia']);
});
}
function getRemoteInstallPath(adb) {
var tempDirName = 'pushGaia' + Math.random().toString(36).substr(2, 8);
var tempDir = utils.getTempFolder(tempDirName);
var tempFile = tempDir.clone();
tempFile.append('webapps.json');
// Use |adb shell cat| instead of |adb pull| so we don't run into
// error and exit when the file does not exist.
sh.run(['-c', adb + ' shell cat /data/local/webapps/webapps.json > ' +
tempFile.path]);
// Read the file as JSON
// If there were no webapps ever installed on the device (likely purged in
// the previous step), default to /system/b2g
var content;
try {
content = utils.getJSON(tempFile);
} catch (e) {
return '/system/b2g';
}
// Remove the entire temp directory
tempDir.remove(true);
// If any of the preload Gaia app was installed at /system/b2g,
// we should overwrite them in /system/b2g
for (var app in content) {
if (content[app].basePath === '/system/b2g/webapps') {
return '/system/b2g';
}
}
return '/data/local';
}
function execute(options) {
const buildAppName = options.BUILD_APP_NAME;
const gaiaDir = options.GAIA_DIR;
const profileFolder = options.PROFILE_DIR;
const gaiaDomain = options.GAIA_DOMAIN;
var remotePath = options.GAIA_INSTALL_PARENT;
var mainQ = Q.defer();
var targetFolder;
var adb = options.ADB;
var restartB2g = needsB2gRestart(buildAppName);
if (restartB2g) {
utils.log('push-to-device', 'b2g process will be restarted for ' +
'installing ' + buildAppName + ' app, see bug 1000049 for ' +
'more information.');
}
mainQ.resolve();
return mainQ.promise.then(function() {
var profile = utils.getFile(profileFolder);
if (!profile.isDirectory()) {
throw new Error(' -*- build/push-to-device.js: cannot locate' +
'profile folder in ' + options.PROFILE_DIR);
}
}).then(function() {
return sh.run(['-c', adb + ' start-server']);
}).then(function() {
utils.log('push', 'Waiting for device ...');
return sh.run(['-c', adb + ' wait-for-device']);
}).then(function() {
if (buildAppName === '*' || restartB2g) {
return sh.run(['-c', adb + ' shell stop b2g']);
}
}).then(function() {
return sh.run(['-c', adb + ' shell rm -r //cache/*']);
}).then(function() {
if (!remotePath) {
utils.log('push', 'GAIA_INSTALL_PARENT unset; ' +
'probing previous installation location...');
remotePath = getRemoteInstallPath(adb);
}
utils.log('push', 'Install webapp(s) to ' + remotePath + '...');
if (/^\/system\//.test(remotePath)) {
return sh.run(['-c', adb + ' remount']);
}
}).then(function() {
if (buildAppName === '*') {
return pushToDevice(profileFolder, remotePath, adb);
} else {
targetFolder = utils.joinPath(
profileFolder, 'webapps',
buildAppName + '.' + gaiaDomain);
return installOneApp(targetFolder, buildAppName,
remotePath, gaiaDomain, adb);
}
}).then(function() {
if (options.VARIANT_PATH) {
return installSvoperapps(profileFolder, adb);
}
}).then(function() {
if (buildAppName === '*') {
return sh.run(['-c', adb + ' push ' +
'"' + options.DEFAULT_GAIA_ICONS_FONT + '"' +
' //system/fonts/hidden/gaia-icons.ttf']);
}
}).then(function() {
if (buildAppName === '*') {
return sh.run(['-c', adb + ' push ' +
'"' + options.DEFAULT_KEYBOAD_SYMBOLS_FONT + '"' +
' //system/fonts/hidden/Keyboard-Symbols.ttf']);
}
}).then(function() {
if (buildAppName === '*' || restartB2g) {
utils.log('push', 'Restarting B2G...');
sh.run(['-c', adb + ' shell start b2g']);
} else {
var Q3 = Q.defer();
var manifest;
Q3.resolve();
return Q3.promise.then(function() {
// Some app folder name is different with the process name,
// ex. sms -> Messages
manifest = utils.readZipManifest(utils.getFile(
targetFolder));
}).then(function() {
utils.log('push', 'Restarting ' + manifest.name + '...');
utils.killAppByPid(manifest.name, gaiaDir);
});
}
});
}
exports.execute = execute;
exports.installSvoperapps = installSvoperapps;
exports.getRemoteInstallPath = getRemoteInstallPath;
exports.pushToDevice = pushToDevice;

30580
gaia/build/r.js Executable file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,31 @@
'use strict';
/**
* This is a controller: it would invoke the sub-espect file for each test.
* Each Espect file will match all files and to see if it can be applied on
* that, so each Espect module need to filter file path by their own.
*
* And it's a Node.js file.
*/
var fs = require('fs');
var path = require('path');
module.exports = function(filepath, outpath) {
var requirepath;
if (process.env.RAPTOR_TRANSFORMER_PATH) {
requirepath = process.env.RAPTOR_TRANSFORMER_PATH + '/node_modules/';
} else {
// We guess we're in Gaia and could find it according to such relative path
requirepath = __dirname + '/../node_modules/debuguy/node_modules/';
}
var rulesdir = path.resolve(process.env.RAPTOR_TRANSFORM_RULES);
fs.readdirSync(rulesdir).forEach(function(esfile) {
if (!esfile.match(/.esp.js$/)) {
return;
}
// Scan all *.esp files in the directory, and then require and
// apply them to transform the code.
var subesp = require(rulesdir + '/' + esfile);
subesp(filepath, outpath, requirepath);
});
};

View file

@ -0,0 +1,32 @@
'use strict';
/**
* Overview:
*
* 1. Find all '.js' files in the app directory
* 2. Replace the path with the '.esp.js' one
* 3. Check if such file exists
* 4. Run this transformer + espect to apply the esp file on to target file
*/
var utils = require('./utils');
exports.execute = function(options) {
var webapp = options.webapp;
var debuguypath = options.RAPTOR_TRANSFORMER_PATH;
if (!debuguypath) {
debuguypath = utils.joinPath(options.GAIA_DIR, 'node_modules', 'debuguy');
}
var file = utils.getFile(debuguypath);
if (!file.exists()) {
throw new Error('Cannot find debuguy: set the path of tool as DEBUGUY');
}
var debuguyclipath = utils.joinPath(debuguypath, 'cli.js');
var node = new utils.Commander('node');
node.initPath(utils.getEnvPath());
node.run([debuguyclipath, 'autolog', '--x-espect=' +
utils.joinPath(options.GAIA_DIR, 'build', 'raptor-test-transformer.esp.js'),
'--r',
webapp.buildDirectoryFilePath]);
};

154
gaia/build/rebuild.js Executable file
View file

@ -0,0 +1,154 @@
'use strict';
var utils = require('./utils');
var config = require('./config/build-config.json');
function getTimestamp(dirPaths) {
let timestamp = {};
dirPaths.forEach(function(dirPath) {
timestamp[dirPath] = {};
let dir = utils.getFile(dirPath);
if (dir.exists() && dir.isDirectory()) {
utils.ls(dir, true).filter(isFileWatched).forEach(function(file) {
try {
let relativePath = utils.relativePath(dir.path, file.path);
timestamp[dirPath][relativePath] = file.lastModifiedTime;
} catch(e) {
timestamp = Date.now();
}
});
}
});
return timestamp;
}
function isFileWatched(file) {
let path = file.path;
let ignores = [];
let result = true;
// Ignore all hidden files (ex: .filename)
if (/^\.\S+|\/\.\S+$/.test(path)) {
return false;
}
config.rebuildBlacklist.forEach(function(pattern) {
ignores.push(pattern);
});
ignores.forEach(function(pattern) {
if (path.indexOf(pattern) !== -1) {
result = false;
return;
}
});
return result;
}
function dirChanged(previous, current, dir) {
for (let filepath in current) {
if (!previous[filepath] || current[filepath] > previous[filepath]) {
utils.log('rebuild', 'file has been changed: ' + dir + '/' + filepath);
return true;
}
}
return false;
}
function buildConfigChanged(previous, current) {
if (current.REBUILD == 1) {
utils.log('rebuild', 'rebuild forced by use of special env like' +
'LOCALE_BASEDIR, GAIA_DISTRIBUTION_DIR or REBUILD');
return true;
}
let flags = [];
// We don't detect FLAGS in blacklist because we don't have to rebuild again
// if someone specifies them.
let blacklist = ['REBUILD', 'P', 'VERBOSE'];
for (let flag in current) {
if (blacklist.indexOf(flag) === -1 && current[flag] !== previous[flag]) {
flags.push(flag);
}
}
if (flags.length > 0) {
utils.log('rebuild', 'build config has been changed: ' + flags.join(', '));
}
return flags.length > 0;
}
exports.execute = function(options) {
var scanningDirs = options.GAIA_APPDIRS.split(' ');
var sharedPath = utils.getFile(options.GAIA_DIR, 'shared').path;
scanningDirs.push(sharedPath);
var current = getTimestamp(scanningDirs);
var timestampFile = utils.getFile(options.STAGE_DIR, 'timestamp.json');
var rebuildAppDirs = [];
if (timestampFile.exists()) {
let record = utils.getJSON(timestampFile);
let previous = record.timestamp;
let sharedChanged =
dirChanged(previous[sharedPath] || {}, current[sharedPath], sharedPath);
let configChanged =
buildConfigChanged(record.build_config, options);
let webappDir = utils.joinPath(options.PROFILE_DIR, 'webapps');
let webappsExist = utils.fileExists(webappDir);
// Rebuild everything if any BUILD_CONFIG attribute / shared file changed or
// PROFILE_DIR/webapps does not exist
if (configChanged || sharedChanged || !webappsExist) {
rebuildAppDirs = scanningDirs;
}
// Rebuild any app that has any of its source file modified or external app
else {
for (let appDir in current) {
if (dirChanged(previous[appDir] || {}, current[appDir], appDir)) {
rebuildAppDirs.push(appDir);
}
}
// Force rebuilding all apps with custom build.js file or unpakcage
// external apps which are always named by random uuid
// These uuid apps may clean up later in bug 1020259
scanningDirs.forEach(function(appDir) {
if (rebuildAppDirs.indexOf(appDir) !== -1 || appDir === sharedPath) {
return;
}
var buildFile = utils.getFile(appDir, 'build', 'build.js');
var webapp = utils.getWebapp(appDir, options);
if (!webapp) {
// Some leftover folders may still be in source tree,
// without any valid app, like apps/browser that has been removed.
return;
}
if (buildFile.exists() ||
(utils.isExternalApp(webapp) && !webapp.pckManifest) ||
!utils.fileExists(webapp.profileDirectoryFilePath)) {
rebuildAppDirs.push(appDir);
}
});
}
} else {
rebuildAppDirs = scanningDirs;
}
utils.writeContent(timestampFile, JSON.stringify({
build_config: options,
timestamp: current
}, null, 2));
utils.log('rebuild', 'rebuildAppDirs: ' + JSON.stringify(rebuildAppDirs));
return rebuildAppDirs;
};
exports.getTimestamp = getTimestamp;
exports.isFileWatched = isFileWatched;
exports.dirChanged = dirChanged;
exports.buildConfigChanged = buildConfigChanged;

98
gaia/build/scan-appdir.js Executable file
View file

@ -0,0 +1,98 @@
'use strict';
/* global require, exports, dump */
const utils = require('./utils');
var ScanAppdir = function() {
var GAIA_APP_CONFIG = utils.getEnv('GAIA_APP_CONFIG');
var GAIA_DIR = utils.getEnv('GAIA_DIR');
var GAIA_DISTRIBUTION_DIR = utils.getEnv('GAIA_DISTRIBUTION_DIR');
var GAIA_APP_SRCDIRS = utils.getEnv('GAIA_APP_SRCDIRS');
var appListFile = utils.getFile(GAIA_APP_CONFIG);
this.appList = utils.getFileContent(appListFile.exists() ?
appListFile : utils.getFile(GAIA_APP_SRCDIRS));
this.gaiaDirPath = GAIA_DIR;
this.gaiaDistributionPath = GAIA_DISTRIBUTION_DIR;
this.allAppList = [];
};
ScanAppdir.prototype.scanAppAndDistributionFolder = function(src, recursive) {
var gaiaAppFolder = utils.getFile(this.gaiaDirPath, src);
var distributionAppFolder;
try {
distributionAppFolder = utils.getFile(this.gaiaDistributionPath, src);
} catch (e) {
distributionAppFolder = null;
}
var srcAppFolder;
if (recursive) {
utils.ls(distributionAppFolder, false).forEach(function(subFolder) {
this.pushPathSrc(subFolder);
}, this);
utils.ls(gaiaAppFolder, false).forEach(function(subFolder) {
this.pushPathSrc(subFolder);
}, this);
try {
srcAppFolder = utils.getFile(src);
utils.ls(srcAppFolder, false).forEach(function(subFolder) {
this.pushPathSrc(subFolder);
}, this);
} catch (e) {
// Calling getFile with no existing filepath or dump message will affect
// the output of GAIA_APPDIRS, so we simply catch the error without doing
// anything.
}
} else {
if (!this.pushPathSrc(distributionAppFolder)) {
if (!this.pushPathSrc(gaiaAppFolder)) {
this.pushPathSrc(src);
}
}
}
};
ScanAppdir.prototype.pushPathSrc = function(srcFolder, path) {
try {
if (!srcFolder.exists() || !srcFolder.isDirectory()) {
return false;
}
var appFolder = path ? utils.getFile(srcFolder.path, path) : srcFolder;
if (appFolder.exists() && appFolder.isDirectory()) {
this.allAppList.push(appFolder.path);
return true;
}
return false;
} catch (e) {
return false;
}
};
ScanAppdir.prototype.execute = function() {
var baseSrc;
this.appList.split('\n').forEach(function(src) {
src = src.trim();
if (!src || src === '') {
return;
}
if (src[src.length - 1] === '*') {
baseSrc = src.slice(0, src.length - 2);
this.scanAppAndDistributionFolder(baseSrc, true);
} else {
this.scanAppAndDistributionFolder(src, false);
}
}, this);
};
function execute() {
var scanAppDir = new ScanAppdir();
scanAppDir.execute();
if (utils.getOsType().indexOf('WIN') !== -1) {
dump(scanAppDir.allAppList.join(' ').replace(/\\/g, '\\\\'));
} else {
dump(scanAppDir.allAppList.join(' '));
}
}
exports.execute = execute;

40
gaia/build/search-provider.js Executable file
View file

@ -0,0 +1,40 @@
'use strict';
/**
* This module generates 'search_providers.json' used by the Search and
* Settings Applications
*/
var utils = require('./utils');
const IN_FILE = 'search_providers_input.json';
const OUT_FILE = 'search_providers.json';
function generateSearchProviderJson(options) {
var searchProviderJson =
utils.getJSON(utils.getFile(options.GAIA_DIR, 'shared', 'js', IN_FILE));
if (options.VARIANT_PATH) {
var variantJson = utils.getJSON(utils.getFile(options.VARIANT_PATH));
if ('search_partner_code' in variantJson) {
searchProviderJson.partner_code = variantJson.search_partner_code;
}
}
var content = JSON.stringify(searchProviderJson);
var resultFile = utils.getFile(options.GAIA_DIR, 'shared', 'js', OUT_FILE);
if (resultFile.exists()) {
let prev = utils.getFileContent(resultFile);
if (prev === content) {
return;
}
}
utils.writeContent(resultFile, content);
}
function execute(options) {
generateSearchProviderJson(options);
}
exports.execute = execute;

355
gaia/build/settings.js Executable file
View file

@ -0,0 +1,355 @@
'use strict';
var utils = require('./utils');
function setWallpaper(settings, config) {
// Grab the default wallpaper and convert it into a base64 string
let devpixels = '';
if (config.GAIA_DEV_PIXELS_PER_PX != '1') {
devpixels = '@' + config.GAIA_DEV_PIXELS_PER_PX + 'x';
}
let wallpaper = utils.resolve(
utils.joinPath(config.GAIA_DISTRIBUTION_DIR, 'wallpapers',
'default' + devpixels + '.jpg'), config.GAIA_DIR);
if (!wallpaper.exists()) {
wallpaper = utils.resolve(
utils.joinPath(config.GAIA_DISTRIBUTION_DIR, 'wallpapers', 'default.jpg'),
config.GAIA_DIR);
}
if (!wallpaper.exists()) {
wallpaper = utils.resolve(
utils.joinPath('build', 'config', 'wallpaper_' +
config.GAIA_DEVICE_TYPE + devpixels + '.jpg'), config.GAIA_DIR);
}
if (!wallpaper.exists()) {
wallpaper = utils.resolve(
utils.joinPath('build', 'config', 'wallpaper_' +
config.GAIA_DEVICE_TYPE + '.jpg'), config.GAIA_DIR);
}
if (!wallpaper.exists()) {
wallpaper = utils.resolve(
utils.joinPath('build', 'config', 'wallpaper' + devpixels + '.jpg'),
config.GAIA_DIR);
}
if (!wallpaper.exists()) {
wallpaper = utils.resolve(
utils.joinPath('build', 'config', 'wallpaper.jpg'),
config.GAIA_DIR);
}
settings['wallpaper.image'] = utils.getFileAsDataURI(wallpaper);
}
function setTone(settings, config, toneType, name, l10nID) {
let settingsKey, dir;
switch (toneType) {
case 'ringtone':
settingsKey = 'dialer.ringtone';
dir = 'shared/resources/media/ringtones/';
break;
case 'alerttone':
settingsKey = 'notification.ringtone';
dir = 'shared/resources/media/notifications/';
break;
default:
throw new Error('unknown tone type: ' + toneType);
}
let tone = utils.resolve(dir + name, config.GAIA_DIR);
settings[settingsKey] = utils.getFileAsDataURI(tone);
settings[settingsKey + '.name'] = {l10nID: l10nID};
settings[settingsKey + '.id'] = settings[settingsKey + '.default.id'] =
'builtin:' + toneType + '/' + name.replace(/\.\w+$/, '');
}
function setMediatone(settings, config) {
// Grab notifier_firefox.opus and convert it into a base64 string
let mediatone_name = 'shared/resources/media/notifications/' +
'notifier_firefox.opus';
let mediatone = utils.resolve(mediatone_name,
config.GAIA_DIR);
settings['media.ringtone'] = utils.getFileAsDataURI(mediatone);
}
function setAlarmtone(settings, config) {
// Grab ac_awake.opus and convert it into a base64 string
let alarmtone_name = 'shared/resources/media/alarms/' +
'ac_awake.opus';
let alarmtone = utils.resolve(alarmtone_name,
config.GAIA_DIR);
settings['alarm.ringtone'] = utils.getFileAsDataURI(alarmtone);
}
function setRingtone(settings, config) {
// Grab ringer_firefox.opus and convert it into a base64 string
let ringtone_name = 'ringer_firefox.opus';
let ringtone_l10nID = 'ringer_firefox2';
setTone(settings, config, 'ringtone', ringtone_name, ringtone_l10nID);
}
function setNotification(settings, config) {
// Grab notifier_firefox.opus and convert it into a base64 string
let notification_name = 'notifier_firefox.opus';
let notification_l10nID = 'notifier_firefox2';
setTone(settings, config, 'alerttone', notification_name,
notification_l10nID);
}
/* Setup the default keyboard layouts according to the current language */
function setDefaultKeyboardLayouts(lang, settings, config) {
let layoutConfigFile = utils.resolve(
utils.joinPath('shared', 'resources', 'keyboard_layouts.json'),
config.GAIA_DIR);
let layoutConfig = utils.getJSON(layoutConfigFile);
let keyboardLayouts = layoutConfig.layout;
if (!keyboardLayouts) {
utils.log('default keyboard layouts are not defined: ' +
layoutConfigFile.path + '\n');
return;
}
// Get the default layouts for the specified language
let defaultLayoutList = keyboardLayouts[lang];
if (!defaultLayoutList) {
utils.log('Cannot find default layout list for language: ' + lang + '\n');
defaultLayoutList = keyboardLayouts['en-US'];
}
let keyboardSettings = {};
function addLayoutEntry(layout) {
let manifestURL = layout.appManifestURL;
if (!keyboardSettings[manifestURL]) {
keyboardSettings[manifestURL] = {};
}
keyboardSettings[manifestURL][layout.layoutId] = true;
}
defaultLayoutList.forEach(addLayoutEntry);
// Also add language-independent layouts into the sets
let langIndependentLayoutList = layoutConfig.langIndependentLayouts;
langIndependentLayoutList.forEach(addLayoutEntry);
settings['keyboard.enabled-layouts'] = keyboardSettings;
settings['keyboard.default-layouts'] = keyboardSettings;
}
function deviceTypeSettings(settings, config) {
// See if any override file exists and eventually override settings
let override = utils.getFile(config.GAIA_DIR,
'build', 'config', config.GAIA_DEVICE_TYPE, 'settings.json');
if (override.exists()) {
let content = utils.getJSON(override);
for (let key in content) {
settings[key] = content[key];
}
}
}
function overrideRingtoneSettings(content, key) {
// Override ringtone if ringtone, ringtone name, and ringtone ID properties
// are available.
if (content[key] && content[key + '.name'] && content[key + '.id']) {
content[key + '.default.id'] = content[key + '.id'];
} else if (content[key] || content[key + '.name'] || content[key + '.id']) {
delete content[key];
delete content[key + '.name'];
delete content[key + '.id'];
delete content[key + '.default.id'];
throw new Error('ringtone not overridden because ' + key + ', ' +
key + '.name, or ' + key + '.id not found in custom ' +
'\'settings.json\'. All properties must be set.');
}
}
function overrideSettings(settings, config) {
// See if any override file exists and eventually override settings
let override = utils.resolve(config.SETTINGS_PATH,
config.GAIA_DIR);
if (override.exists()) {
let content = utils.getJSON(override);
overrideRingtoneSettings(content, 'dialer.ringtone');
overrideRingtoneSettings(content, 'notification.ringtone');
for (let key in content) {
settings[key] = content[key];
}
}
}
function setHomescreenURL(settings, config) {
// 'homescreen' as default value of homescreen.appName
let appName = 'homescreen';
if (typeof(settings['homescreen.appName']) !== 'undefined') {
appName = settings['homescreen.appName'];
let homescreenExists = utils.existsInAppDirs(config.GAIA_APPDIRS, appName);
if (!homescreenExists) {
throw new Error('homescreen APP not found: ' + appName);
}
// no longer to use this settings so remove it.
delete settings['homescreen.appName'];
}
settings['homescreen.manifestURL'] = utils.gaiaManifestURL(appName,
config.GAIA_SCHEME, config.GAIA_DOMAIN, config.GAIA_PORT);
}
function writeSettings(settings, config) {
// Finally write the settings file
let profileDir = utils.getFile(config.PROFILE_DIR);
let settingsFile = utils.getFile(config.PROFILE_DIR, 'settings.json');
let defaultsSettings = utils.getFile(
config.PROFILE_DIR, 'defaults', 'settings.json');
utils.ensureFolderExists(profileDir);
let content = JSON.stringify(settings);
utils.writeContent(settingsFile, content + '\n');
utils.log('settings.js', 'Writing settings file: ' + settingsFile.path);
var defaultSettingFolder = utils.getFile(profileDir.path, 'defaults');
utils.ensureFolderExists(defaultSettingFolder);
utils.writeContent(defaultsSettings, content + '\n');
utils.log('settings.js', 'Writing settings file: ' + defaultsSettings.path);
}
function execute(config) {
var settingsFile = utils.getFile(config.GAIA_DIR, 'build', 'config',
'common-settings.json');
if (!settingsFile.exists()) {
throw new Error('file not found: ' + settingsFile.path);
}
var settings = utils.getJSON(settingsFile);
if (config.TARGET_BUILD_VARIANT != 'user') {
// We want the console to be disabled
// for device builds using the user variant.
settings['debug.console.enabled'] = true;
// Activate developer menu under the system menu when long pressing
// the power button by default for devs.
settings['developer.menu.enabled'] = true;
}
// Set the ftu manifest URL
if (config.NOFTU === '0') {
settings['ftu.manifestURL'] = utils.gaiaManifestURL('ftu',
config.GAIA_SCHEME, config.GAIA_DOMAIN, config.GAIA_PORT);
}
// Set the ftu ping URL -- we set this regardless of NOFTU for now
settings['ftu.pingURL'] = config.FTU_PING_URL;
// The selected value for the level of data to share.
settings['metrics.selectedMetrics.level'] = 'Basic';
// Set the rocketbar URL
settings['rocketbar.searchAppURL'] = utils.gaiaOriginURL('search',
config.GAIA_SCHEME, config.GAIA_DOMAIN, config.GAIA_PORT) + '/index.html';
// Set the new tab-page URL
settings['rocketbar.newTabAppURL'] = utils.gaiaOriginURL('search',
config.GAIA_SCHEME, config.GAIA_DOMAIN, config.GAIA_PORT) + '/index.html';
settings['debugger.remote-mode'] = config.REMOTE_DEBUGGER === '1' ?
'adb-only' : 'disabled';
if (config.PRODUCTION === '1') {
settings['feedback.url'] = 'https://input.mozilla.org/api/v1/feedback/';
settings['debugger.remote-mode'] = 'disabled';
settings['gaia.system.checkForUpdates'] = true;
}
if (config.PRODUCTION === '0') {
settings['dom.mozApps.signed_apps_installable_from'] =
'https://marketplace.firefox.com,https://marketplace.allizom.org';
settings['devtools.pseudolocalization.enabled'] = true;
}
if (config.DOGFOOD === '1') {
settings['debug.performance_data.dogfooding'] = true;
settings['metrics.appusage.reportInterval'] = 24 * 60 * 60 * 1000;
settings['metrics.selectedMetrics.level'] = 'Enhanced';
settings['hud.hide'] = true;
settings['devtools.overlay'] = true;
}
settings['language.current'] = config.GAIA_DEFAULT_LOCALE;
if (config.DEVICE_DEBUG === '1') {
settings['debugger.remote-mode'] = 'adb-devtools';
}
if (config.NO_LOCK_SCREEN === '1') {
settings['lockscreen.enabled'] = false;
settings['lockscreen.locked'] = false;
}
var screenTimeout = parseInt(config.SCREEN_TIMEOUT, 10);
if (screenTimeout >= 0) {
settings['screen.timeout'] = screenTimeout;
}
setDefaultKeyboardLayouts(config.GAIA_DEFAULT_LOCALE, settings, config);
var queue = utils.Q.defer();
queue.resolve();
var wait = true;
var result = queue.promise.then(function() {
setWallpaper(settings, config);
}).then(function() {
setMediatone(settings, config);
}).then(function() {
setAlarmtone(settings, config);
}).then(function() {
setRingtone(settings, config);
}).then(function() {
setNotification(settings, config);
}).then(function() {
deviceTypeSettings(settings, config);
}).then(function() {
overrideSettings(settings, config);
}).then(function() {
setHomescreenURL(settings, config);
}).then(function() {
writeSettings(settings, config);
wait = false;
return settings;
});
// Ensure not quitting xpcshell before all asynchronous code is done
utils.processEvents(function(){
return {
wait : wait
};
});
return result;
}
exports.execute = execute;
exports.setWallpaper = setWallpaper;
exports.setMediatone = setMediatone;
exports.setAlarmtone = setAlarmtone;
exports.setRingtone = setRingtone;
exports.setNotification = setNotification;
exports.deviceTypeSettings = deviceTypeSettings;
exports.overrideSettings = overrideSettings;
exports.writeSettings = writeSettings;
exports.setDefaultKeyboardLayouts = setDefaultKeyboardLayouts;
exports.setHomescreenURL = setHomescreenURL;

326
gaia/build/svoperapps.js Executable file
View file

@ -0,0 +1,326 @@
'use strict';
var utils = require('./utils');
var SvoperappsBuilder = function() {
};
SvoperappsBuilder.prototype.STAGE_SVOPERAPPS_FOLDER = '__svoperapps';
SvoperappsBuilder.prototype.APPS_DATA_FOLDER = 'cached';
SvoperappsBuilder.prototype.APPS_CONF_FOLDER = 'conf';
SvoperappsBuilder.prototype.PROFILE_APPS_FOLDER = 'svoperapps';
SvoperappsBuilder.prototype.APPS_CONF_FILENAME = 'singlevariantconf.json';
SvoperappsBuilder.prototype.DEFAULT_EMPTY_SCREENS = 2;
SvoperappsBuilder.prototype.MAX_ICONS_PER_PAGE = 4 * 4;
SvoperappsBuilder.prototype.errorObject = null;
/*
* the count of current issued and unprocessed app requests.
*/
SvoperappsBuilder.prototype.appRequest = 0;
/*
* check and cut the path parts out from url
* if the url does not conform to {scheme}://{net_loc}/, this function returns
* null.
*/
SvoperappsBuilder.prototype.getOrigin = function(url, defaultOrigin) {
if (!url) {
return null;
} else {
var matched = url.match(/^([a-zA-Z]+:\/\/.[^/]+).*/);
return (matched && matched.length > 1) ? matched[1] : defaultOrigin;
}
};
/*
* check if the downloaded app folder contains the following files:
* 1. metadata.json, 2. update.webapp, 3. application.zip, 4. manifest.webapp.
*/
SvoperappsBuilder.prototype.checkDownloadedApp = function(path) {
var files = utils.listFiles(path, utils.FILE_TYPE_FILE, false);
var metadataFound = false;
var updateFound = false;
var applicationFound = false;
var manifestFound = false;
for (var i = 0; i < files.length; i++) {
if (files[i].match(/[\/\\]metadata\.json$/)) {
metadataFound = true;
} else if (files[i].match(/[\/\\]update\.webapp$/)) {
updateFound = true;
} else if (files[i].match(/[\/\\]application\.zip$/)) {
applicationFound = true;
} else if (files[i].match(/[\/\\]manifest\.webapp$/)) {
manifestFound = true;
}
}
return metadataFound && ((updateFound && applicationFound )|| manifestFound);
};
/*
* Download the packaged app from manifest.package_path to cache path.
*/
SvoperappsBuilder.prototype.downloadPackageApp =
function(manifest, metadata, appPath, cb, errCb) {
utils.log('svoperapps.js',
'download packaged app from: ' + manifest.package_path);
// write update.webapp
utils.writeContent(utils.getFile(appPath, 'update.webapp'),
JSON.stringify(manifest));
// write metadata.json
utils.writeContent(utils.getFile(appPath, 'metadata.json'),
JSON.stringify(metadata));
// download application
var origin = utils.getNewURI(metadata.manifestURL, null, null);
var absolutePkgPath = origin.resolve(manifest.package_path);
utils.download(absolutePkgPath, appPath + 'application.zip', cb,
function err(url, path) {
errCb(new Error('unable to download: ' + url));
});
};
SvoperappsBuilder.prototype.prepareHostedApp =
function(manifest, metadata, appPath, callback, errCallback) {
// Bug 936028 - appcache needs to be removed from manifest
if (manifest.appcache_path) {
delete manifest.appcache_path;
}
// write manifest
utils.writeContent(utils.getFile(appPath, 'manifest.webapp'),
JSON.stringify(manifest));
// write metadata
utils.writeContent(utils.getFile(appPath, 'metadata.json'),
JSON.stringify(metadata));
callback();
};
SvoperappsBuilder.prototype.downloadApp =
function(manifest, metadata, appPath, callback, errCallback) {
// Determine if application is hosted or packaged and save manifest
if (manifest.package_path) { // packaged app
this.downloadPackageApp(manifest, metadata, appPath, callback, errCallback);
} else if (metadata.origin) { // hosted app
this.prepareHostedApp(manifest, metadata, appPath, callback, errCallback);
} else { // error case
errCallback(new Error('installOrigin required for app ' + manifest.name +
' in local_apps.json configuration file'));
}
};
SvoperappsBuilder.prototype.processApp =
function(appId, manifestUrl, origin, installOrigin, appPath, cached, done) {
var self = this;
var destPath = utils.joinPath(this.profilePath, this.PROFILE_APPS_FOLDER);
// When everything is ready, we call this function to copy all files in
// appPath to profilePath.
function copyAppPathToProfile() {
try {
utils.copyFileTo(appPath, destPath, appId);
} catch (ex) {
utils.log('svoperapps.js', 'copy file error: ' + ex.message);
self.errorObject = ex;
} finally {
done();
}
}
function handleError() {
self.errorObject = new Error('failed to download manifest from ' +
manifestUrl);
done();
}
utils.log('svoperapps.js', 'downloading manifest: ' + manifestUrl);
utils.downloadJSON(manifestUrl, function mandifestHandler(json) {
if (!json) {
if (cached) {
// if json is not downloaded but we already had everything, just copy
// it.
copyAppPathToProfile();
} else {
handleError();
}
return;
}
// prepare the folders for this app
utils.mkdirs(appPath);
// if the app is cached, we need to compare the manifest file.
if (cached) {
// If we already have everything, we still need to compare the
// update/manifest with downloaded content. If they are identical, we
// can view them as the same. If not, we need to download and rebuild
// them.
var updatePath = utils.joinPath(appPath, 'update.webapp');
var manifestFile = utils.fileExists(updatePath) ?
'update.webapp' : 'manifest.webapp';
var fileJSON = utils.readJSONFromPath(utils.joinPath(appPath,
manifestFile));
// Bug 936028 - Comparation has to be done removing appcache + "\n");
if (json.appcache_path) {
delete json.appcache_path;
}
if (JSON.stringify(json) === JSON.stringify(fileJSON)) {
copyAppPathToProfile();
return;
}
}
try {
// if it is cached or the manifest file is not the same, we need to
// download that app.
var metadata = {
'id': appId,
'installOrigin': installOrigin,
'manifestURL': manifestUrl
};
metadata.origin = json.package_path ?
self.getOrigin(json.package_path, installOrigin) :
(origin || self.getOrigin(json.launch_path));
self.downloadApp(json, metadata, appPath,
copyAppPathToProfile, handleError);
} catch (ex) {
// error found
self.errorObject = ex;
done();
}
});
};
SvoperappsBuilder.prototype.fetchApps = function(done) {
utils.log('svoperapps.js', 'fetch apps');
var apps = this.variant.apps;
var distPath = utils.joinPath(this.svoperappsStagePath,
this.APPS_DATA_FOLDER);
var cachedApps = utils.listFiles(distPath, utils.FILE_TYPE_DIRECTORY, false);
var self = this;
function checkApp(cachedApp) {
if (cachedApp.match(new RegExp('[\\\/]' + app + '$'))) {
cachedApps.splice(cachedApps.indexOf(cachedApp), 1);
//compare everything.
if (self.checkDownloadedApp(appPath)) {
utils.log('svoperapps.js', app + ' is cached.');
// path exists and everything is ok, make it as cached
appCached = true;
} else {
// path exists but some files are missing, mark it as not cached.
appCached = false;
utils.deleteFile(appPath, true);
}
return false;
} else {
return true;
}
}
function processAppCallback() {
self.appRequest--;
if (self.appRequest === 0) {
// all apps are processed, clean all left cached apps, those are not
// found with the app list.
for (var i = 0; i < cachedApps.length; i++) {
utils.log('svoperapps.js', 'remove useless cache: ' + cachedApps[i]);
utils.deleteFile(cachedApps[i], true);
}
done();
}
}
for (var app in apps) {
var appPath = utils.joinPath(distPath, app);
var appCached = false;
// check if the appPath already exists in cachedApp list
cachedApps.every(checkApp);
var origin = apps[app].origin || null;
var installOrigin = apps[app].installOrigin || null;
this.appRequest++;
this.processApp(app, apps[app].manifestURL, origin, installOrigin,
appPath, appCached, processAppCallback);
}
if (Object.keys(apps).length === 0) {
done();
}
};
SvoperappsBuilder.prototype.prepareDir = function() {
var profilePath = this.options.PROFILE_DIR;
var distributionPath = this.options.GAIA_DISTRIBUTION_DIR;
var svoperappsStagePath = utils.joinPath(this.options.GAIA_DIR, 'build_stage',
this.STAGE_SVOPERAPPS_FOLDER);
utils.mkdirs(svoperappsStagePath);
// create the temp folders.
utils.mkdirs(utils.joinPath(svoperappsStagePath, this.APPS_DATA_FOLDER));
// clear profile folder
utils.deleteFile(utils.joinPath(profilePath, this.PROFILE_APPS_FOLDER), true);
utils.mkdirs(utils.joinPath(profilePath, this.PROFILE_APPS_FOLDER));
this.profilePath = profilePath;
this.distributionPath = distributionPath;
this.svoperappsStagePath = svoperappsStagePath;
};
SvoperappsBuilder.prototype.setOptions = function(options) {
this.options = options;
this.variant = utils.readJSONFromPath(options.VARIANT_PATH);
};
SvoperappsBuilder.prototype.generateConfig = function() {
var file = utils.getFile(this.options.PROFILE_DIR, this.PROFILE_APPS_FOLDER,
this.APPS_CONF_FILENAME);
var config = {};
if (this.variant.operators) {
this.variant.operators.forEach(function(operator) {
var apps = operator.apps.map(function(app) {
return app.id;
});
operator['mcc-mnc'].forEach(function(mccmnc) {
config[mccmnc] = apps;
});
});
}
utils.writeContent(file, JSON.stringify(config));
};
SvoperappsBuilder.prototype.execute = function(options) {
if (!options.PROFILE_DIR || !options.GAIA_DISTRIBUTION_DIR ||
!options.VARIANT_PATH) {
utils.log('svoperapps.js', 'PROFILE_DIR, GAIA_DISTRIBUTION_DIR,' +
' VARIANT_PATH are all required');
return;
}
this.setOptions(options);
this.prepareDir();
this.generateConfig();
// set done flag and start everything.
var done = false;
// read variant.json
// download all apps
this.fetchApps(function() {
done = true;
});
// Ensure everything is finished before exit this module
utils.processEvents(function exitFunc() {
return {
'wait': !done,
'error': this.errorObject
};
}.bind(this));
};
exports.execute = function(options) {
(new SvoperappsBuilder()).execute(options);
};

2
gaia/build/test/bin/fake-adb Executable file
View file

@ -0,0 +1,2 @@
#!/bin/bash
echo $@ >> ./build/test/integration/result

View file

@ -0,0 +1,38 @@
'use strict';
var Mocha = require('mocha');
var dive = require('diveSync');
var mocha = new Mocha({
harmony: true,
reporter: 'spec',
ui: 'tdd',
timeout: 0
});
function validFile(file, dir) {
if (dir) {
return true;
}
return (file.indexOf('.test.js') !== -1);
}
function run(callback) {
dive(process.env.TEST_FILES_DIR, { filter: validFile }, function(err, file) {
if (err) {
return callback(err);
}
mocha.addFile(file);
});
callback();
}
run(function(err) {
if (err) {
throw err;
}
mocha.run(function(failures) {
process.exit(failures);
});
});

View file

@ -0,0 +1,12 @@
{
"name": "Custom origin",
"description": "Gaia Test custom origin",
"type": "privileged",
"developer": {
"name": "The Gaia Team",
"url": "https://github.com/mozilla-b2g/gaia"
},
"default_locale": "en-US",
"orientation": "default",
"origin": "app://test.mozilla.com"
}

1
gaia/build/test/fixtures/shared_css.list vendored Executable file
View file

@ -0,0 +1 @@
build/test/fixtures/test-shared-css-app

View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<link rel="manifest" href="manifest.webapp">
<link rel="localization" href="locales/test.{locale}.properties">
<script type="text/javascript" defer src="/shared/js/l10n.js"></script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,2 @@
entity1=My Entity
entity1=My Entity

View file

@ -0,0 +1,12 @@
{
"name": "Test L10n Duplicates",
"description": "Test L10n: Duplicate string detection",
"type": "certified",
"launch_path": "/index.html",
"developer": {
"name": "The Gaia Team",
"url": "https://github.com/mozilla-b2g/gaia"
},
"default_locale": "en-US",
"orientation": "default"
}

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<meta name="defaultLanguage" content="en-US">
<meta name="availableLanguages" content="en-US">
<link rel="localization" href="locales/test.{locale}.properties">
<script type="text/javascript" defer src="/shared/js/l10n.js"></script>
</head>
<body>
<p data-l10n-id="entity0"></p>
</body>
</html>

View file

@ -0,0 +1 @@
entity1=My Entity

View file

@ -0,0 +1,12 @@
{
"name": "Test L10n Missing",
"description": "Test L10n: Missing string detection",
"type": "certified",
"launch_path": "/index.html",
"developer": {
"name": "The Gaia Team",
"url": "https://github.com/mozilla-b2g/gaia"
},
"default_locale": "en-US",
"orientation": "default"
}

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
<meta name="defaultLanguage" content="en-US">
<meta name="availableLanguages" content="en-US">
<link rel="localization" href="locales/test.{locale}.properties">
<link rel="localization" href="locales/test.{locale}.l20n">
<script type="text/javascript" defer src="/shared/js/intl/l20n.js"></script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,2 @@
<entity1 "Entity 1">
<entity2 "Entity 2">

View file

@ -0,0 +1 @@
entity1=My Entity

Some files were not shown because too many files have changed in this diff Show more