зеркало из https://github.com/mozilla/gecko-dev.git
merge b2g-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
8f8a18e826
|
@ -8,6 +8,8 @@
|
|||
|
||||
const DEVELOPER_HUD_LOG_PREFIX = 'DeveloperHUD';
|
||||
const CUSTOM_HISTOGRAM_PREFIX = 'DEVTOOLS_HUD_CUSTOM_';
|
||||
const APPNAME_IDX = 3;
|
||||
const HISTNAME_IDX = 4;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, 'devtools', function() {
|
||||
const {devtools} = Cu.import('resource://devtools/shared/Loader.jsm', {});
|
||||
|
@ -322,14 +324,16 @@ Target.prototype = {
|
|||
},
|
||||
|
||||
_getAddonHistogram(item) {
|
||||
let APPNAME_IDX = 3;
|
||||
let HISTNAME_IDX = 4;
|
||||
let appName = this._getAddonHistogramName(item, APPNAME_IDX);
|
||||
let histName = this._getAddonHistogramName(item, HISTNAME_IDX);
|
||||
|
||||
return Services.telemetry.getAddonHistogram(appName, CUSTOM_HISTOGRAM_PREFIX
|
||||
+ histName);
|
||||
},
|
||||
|
||||
_getAddonHistogramName(item, index) {
|
||||
let array = item.split('_');
|
||||
let appName = array[APPNAME_IDX].toUpperCase();
|
||||
let histName = array[HISTNAME_IDX].toUpperCase();
|
||||
return Services.telemetry.getAddonHistogram(appName,
|
||||
CUSTOM_HISTOGRAM_PREFIX + histName);
|
||||
return array[index].toUpperCase();
|
||||
},
|
||||
|
||||
_clearTelemetryData() {
|
||||
|
@ -357,11 +361,21 @@ Target.prototype = {
|
|||
payload.keyedHistograms[item] =
|
||||
Services.telemetry.getKeyedHistogramById(item).snapshot();
|
||||
});
|
||||
|
||||
// Package the registered hud custom histograms
|
||||
developerHUD._customHistograms.forEach(item => {
|
||||
payload.addonHistograms[item] = this._getAddonHistogram(item).snapshot();
|
||||
let appName = this._getAddonHistogramName(item, APPNAME_IDX);
|
||||
let histName = CUSTOM_HISTOGRAM_PREFIX +
|
||||
this._getAddonHistogramName(item, HISTNAME_IDX);
|
||||
let addonHist = Services.telemetry.getAddonHistogram(appName, histName).snapshot();
|
||||
if (!(appName in payload.addonHistograms)) {
|
||||
payload.addonHistograms[appName] = {};
|
||||
}
|
||||
// Do not include histograms with sum of 0.
|
||||
if (addonHist.sum > 0) {
|
||||
payload.addonHistograms[appName][histName] = addonHist;
|
||||
}
|
||||
});
|
||||
|
||||
shell.sendEvent(frame, 'advanced-telemetry-update', Cu.cloneInto(payload, frame));
|
||||
},
|
||||
|
||||
|
|
|
@ -0,0 +1,765 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* The Persistent Partition has this layout:
|
||||
*
|
||||
* Bytes: 32 4 4 <DATA_LENGTH> 1
|
||||
* Fields: [[DIGEST][MAGIC][DATA_LENGTH][ DATA ][OEM_UNLOCK_ENABLED]]
|
||||
*
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "PersistentDataBlock" ];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
// This is a marker that will be written after digest in the partition.
|
||||
const PARTITION_MAGIC = 0x19901873;
|
||||
// This is the limit in Android because of issues with Binder if blocks are > 100k
|
||||
// We dont really have this issues because we don't use Binder, but let's stick
|
||||
// to Android implementation.
|
||||
const MAX_DATA_BLOCK_SIZE = 1024 * 100;
|
||||
const DIGEST_SIZE_BYTES = 32;
|
||||
const HEADER_SIZE_BYTES = 8;
|
||||
const PARTITION_MAGIC_SIZE_BYTES = 4;
|
||||
const DATA_SIZE_BYTES = 4;
|
||||
const OEM_UNLOCK_ENABLED_BYTES = 1;
|
||||
// The position of the Digest
|
||||
const DIGEST_OFFSET = 0;
|
||||
const XPCOM_SHUTDOWN_OBSERVER_TOPIC = "xpcom-shutdown";
|
||||
// This property will have the path to the persistent partition
|
||||
const PERSISTENT_DATA_BLOCK_PROPERTY = "ro.frp.pst";
|
||||
const OEM_UNLOCK_PROPERTY = "sys.oem_unlock_allowed";
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "libcutils", function () {
|
||||
Cu.import("resource://gre/modules/systemlibs.js");
|
||||
return libcutils;
|
||||
});
|
||||
|
||||
var inParent = Cc["@mozilla.org/xre/app-info;1"]
|
||||
.getService(Ci.nsIXULRuntime)
|
||||
.processType === Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
||||
|
||||
function log(str) {
|
||||
dump("PersistentDataBlock.jsm: " + str + "\n");
|
||||
}
|
||||
|
||||
function debug(str) {
|
||||
DEBUG && log(str);
|
||||
}
|
||||
|
||||
function toHexString(data) {
|
||||
function toHexChar(charCode) {
|
||||
return ("0" + charCode.toString(16).slice(-2));
|
||||
}
|
||||
let hexString = "";
|
||||
if (typeof data === "string") {
|
||||
hexString = [toHexChar(data.charCodeAt(i)) for (i in data)].join("");
|
||||
} else if (typeof data === "array") {
|
||||
hexString = [toHexChar(data[i]) for (i in data)].join("");
|
||||
}
|
||||
return hexString;
|
||||
}
|
||||
|
||||
function arr2bstr(arr) {
|
||||
let bstr = "";
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
bstr += String.fromCharCode(arr[i]);
|
||||
}
|
||||
return bstr;
|
||||
}
|
||||
|
||||
this.PersistentDataBlock = {
|
||||
|
||||
/**
|
||||
* libc funcionality. Accessed via ctypes
|
||||
*/
|
||||
_libc: {
|
||||
handler: null,
|
||||
open: function() {},
|
||||
close: function() {},
|
||||
ioctl: function() {}
|
||||
},
|
||||
|
||||
/**
|
||||
* Component to access property_get/set functions
|
||||
*/
|
||||
_libcutils: null,
|
||||
|
||||
/**
|
||||
* The size of a device block. This is assigned by querying the kernel.
|
||||
*/
|
||||
_blockDeviceSize: -1,
|
||||
|
||||
/**
|
||||
* Data block file
|
||||
*/
|
||||
_dataBlockFile: "",
|
||||
|
||||
/**
|
||||
* Change the behavior of the class for some methods to testing mode. This will fake the return value of some
|
||||
* methods realted to native operations with block devices.
|
||||
*/
|
||||
_testing: false,
|
||||
|
||||
/*
|
||||
* *** USE ONLY FOR TESTING ***
|
||||
* This component will interface between Gecko and a special secure partition with no formatting, a raw partition.
|
||||
* This interaction requires a specific partition layout structure which emulators don't have so far. So for
|
||||
* our unit tests to pass, we need a way for some methods to behave differently. This method will change this
|
||||
* behavior at runtime so some low-level platform-specific operations will be faked:
|
||||
* - Getting the size of a partition: We can use any partition to get the size, is up to the test to choose
|
||||
* which partition to use. But, in testing mode we use files instead of partitions, so we need to fake the
|
||||
* return value of this method in this case.
|
||||
* - Wipping a partition: This will fully remove the partition as well as it filesystem type, so we cannot
|
||||
* test it on any existing emulator partition. Testing mode will skip this operation.
|
||||
*
|
||||
* @param enabled {Bool} Set testing mode. See _testing property.
|
||||
*/
|
||||
setTestingMode: function(enabled) {
|
||||
this._testing = enabled || false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the class.
|
||||
*
|
||||
*/
|
||||
init: function(mode) {
|
||||
debug("init()");
|
||||
|
||||
if (libcutils) {
|
||||
this._libcutils = libcutils;
|
||||
}
|
||||
|
||||
if (!this.ctypes) {
|
||||
Cu.import("resource://gre/modules/ctypes.jsm", this);
|
||||
}
|
||||
|
||||
if (this._libc.handler === null) {
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
try {
|
||||
this._libc.handler = this.ctypes.open(this.ctypes.libraryName("c"));
|
||||
this._libc.close = this._libc.handler.declare("close",
|
||||
this.ctypes.default_abi,
|
||||
this.ctypes.int,
|
||||
this.ctypes.int
|
||||
);
|
||||
this._libc.open = this._libc.handler.declare("open",
|
||||
this.ctypes.default_abi,
|
||||
this.ctypes.int,
|
||||
this.ctypes.char.ptr,
|
||||
this.ctypes.int
|
||||
);
|
||||
this._libc.ioctl = this._libc.handler.declare("ioctl",
|
||||
this.ctypes.default_abi,
|
||||
this.ctypes.int,
|
||||
this.ctypes.int,
|
||||
this.ctypes.unsigned_long,
|
||||
this.ctypes.unsigned_long.ptr);
|
||||
|
||||
} catch(ex) {
|
||||
log("Unable to open libc.so: ex = " + ex);
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
#else
|
||||
log("This component requires Gonk!");
|
||||
throw Cr.NS_ERROR_ABORT;
|
||||
#endif
|
||||
}
|
||||
|
||||
this._dataBlockFile = this._libcutils.property_get(PERSISTENT_DATA_BLOCK_PROPERTY);
|
||||
if (this._dataBlockFile === null) {
|
||||
log("init: ERROR: property " + PERSISTENT_DATA_BLOCK_PROPERTY + " doesn't exist!");
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
Services.obs.addObserver(this, XPCOM_SHUTDOWN_OBSERVER_TOPIC, false);
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
debug("uninit()");
|
||||
this._libc.handler.close();
|
||||
Services.obs.removeObserver(this, XPCOM_SHUTDOWN_OBSERVER_TOPIC);
|
||||
},
|
||||
|
||||
_checkLibcUtils: function() {
|
||||
debug("_checkLibcUtils");
|
||||
if (!this._libcutils) {
|
||||
log("No proper libcutils binding, aborting.");
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback mehtod for addObserver
|
||||
*/
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
debug("observe()");
|
||||
switch (aTopic) {
|
||||
case XPCOM_SHUTDOWN_OBSERVER_TOPIC:
|
||||
this.uninit();
|
||||
break;
|
||||
|
||||
default:
|
||||
log("Wrong observer topic: " + aTopic);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This method will format the persistent partition if it detects manipulation (digest calculation will fail)
|
||||
* or if the OEM Unlock Enabled byte is set to true.
|
||||
* We need to call this method on every boot.
|
||||
*/
|
||||
start: function() {
|
||||
debug("start()");
|
||||
return this._enforceChecksumValidity().then(() => {
|
||||
return this._formatIfOemUnlockEnabled().then(() => {
|
||||
return Promise.resolve(true);
|
||||
})
|
||||
}).catch(ex => {
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Computes the digest of the entire data block.
|
||||
* The digest is saved in the first 32 bytes of the block.
|
||||
*
|
||||
* @param isStoredDigestReturned {Bool} Tells the function to return the stored digest as well as the calculated.
|
||||
* True means to return stored digest and the calculated
|
||||
* False means to return just the calculated one
|
||||
*
|
||||
* @return Promise<digest> {Object} The calculated digest into the "calculated" property, and the stored
|
||||
* digest into the "stored" property.
|
||||
*/
|
||||
_computeDigest: function (isStoredDigestReturned) {
|
||||
debug("_computeDigest()");
|
||||
let digest = {calculated: "", stored: ""};
|
||||
let partition;
|
||||
debug("_computeDigest: _dataBlockFile = " + this._dataBlockFile);
|
||||
return OS.File.open(this._dataBlockFile, {existing:true, append:false, read:true}).then(_partition => {
|
||||
partition = _partition;
|
||||
return partition.read(DIGEST_SIZE_BYTES);
|
||||
}).then(digestDataRead => {
|
||||
// If storedDigest is passed as a parameter, the caller will likely compare the
|
||||
// one is already stored in the partition with the one we are going to compute later.
|
||||
if (isStoredDigestReturned === true) {
|
||||
debug("_computeDigest: get stored digest from the partition");
|
||||
digest.stored = arr2bstr(digestDataRead);
|
||||
}
|
||||
return partition.read();
|
||||
}).then(data => {
|
||||
// Calculate Digest with the data retrieved after the digest
|
||||
let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
|
||||
hasher.init(hasher.SHA256);
|
||||
hasher.update(data, data.byteLength);
|
||||
digest.calculated = hasher.finish(false);
|
||||
debug("_computeDigest(): Digest = " + toHexString(digest.calculated) +
|
||||
"(" + digest.calculated.length + ")");
|
||||
return partition.close();
|
||||
}).then(() => {
|
||||
return Promise.resolve(digest);
|
||||
}).catch(ex => {
|
||||
log("_computeDigest(): Failed to read partition: ex = " + ex);
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the size of a block from the undelaying filesystem
|
||||
*
|
||||
* @return {Number} The size of the block
|
||||
*/
|
||||
_getBlockDeviceSize: function() {
|
||||
debug("_getBlockDeviceSize()");
|
||||
|
||||
// See _testing property
|
||||
if (this._testing === true) {
|
||||
debug("_getBlockDeviceSize: No real block device size in testing mode!. Returning 1024.");
|
||||
return 1024;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
const O_READONLY = 0;
|
||||
const O_NONBLOCK = 1 << 11;
|
||||
/* Getting the correct values for ioctl() operations by reading the headers is not a trivial task, so
|
||||
* the better way to get the values below is by writting a simple test aplication in C that will
|
||||
* print the values to the output.
|
||||
* 32bits and 64bits value for ioctl() BLKGETSIZE64 operation is different. So we will fallback in
|
||||
* case ioctl() returns ENOTTY (22). */
|
||||
const BLKGETSIZE64_32_BITS = 0x80041272;
|
||||
const BLKGETSIZE64_64_BITS = 0x80081272;
|
||||
const ENOTTY = 25;
|
||||
|
||||
debug("_getBlockDeviceSize: _dataBlockFile = " + this._dataBlockFile);
|
||||
let fd = this._libc.open(this._dataBlockFile, O_READONLY | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
log("_getBlockDeviceSize: couldn't open partition!: errno = " + this.ctypes.errno);
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
let size = new this.ctypes.unsigned_long();
|
||||
let sizeAddress = size.address();
|
||||
let ret = this._libc.ioctl(fd, BLKGETSIZE64_32_BITS, sizeAddress);
|
||||
if (ret < 0) {
|
||||
if (this.ctypes.errno === ENOTTY) {
|
||||
log("_getBlockDeviceSize: errno is ENOTTY, falling back to 64 bit version of BLKGETSIZE64...");
|
||||
ret = this._libc.ioctl(fd, BLKGETSIZE64_64_BITS, sizeAddress);
|
||||
if (ret < 0) {
|
||||
this._libc.close(fd);
|
||||
log("_getBlockDeviceSize: BLKGETSIZE64 failed again!. errno = " + this.ctypes.errno);
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
} else {
|
||||
this._libc.close(fd);
|
||||
log("_getBlockDeviceSize: couldn't get block device size!: errno = " + this.ctypes.errno);
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
this._libc.close(fd);
|
||||
debug("_getBlockDeviceSize: size =" + size.value);
|
||||
return size.value;
|
||||
#else
|
||||
log("_getBlockDeviceSize: ERROR: This feature is only supported in Gonk!");
|
||||
return -1;
|
||||
#endif
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the byte into the partition which represents the OEM Unlock Enabled feature.
|
||||
* A value of "1" means that the user doesn't want to enable KillSwitch.
|
||||
* The byte is the last one byte into the device block.
|
||||
*
|
||||
* @param isSetOemUnlockEnabled {bool} If true, sets the OEM Unlock Enabled byte to 1.
|
||||
* Otherwise, sets it to 0.
|
||||
*/
|
||||
_doSetOemUnlockEnabled: function(isSetOemUnlockEnabled) {
|
||||
debug("_doSetOemUnlockEnabled()");
|
||||
let partition;
|
||||
return OS.File.open(this._dataBlockFile, {existing:true, append:false, write:true}).then(_partition => {
|
||||
partition = _partition;
|
||||
return partition.setPosition(this._getBlockDeviceSize() - OEM_UNLOCK_ENABLED_BYTES, OS.File.POS_START);
|
||||
}).then(() => {
|
||||
return partition.write(new Uint8Array([ isSetOemUnlockEnabled === true ? 1 : 0 ]));
|
||||
}).then(bytesWrittenLength => {
|
||||
if (bytesWrittenLength != 1) {
|
||||
log("_doSetOemUnlockEnabled: Error writting OEM Unlock Enabled byte!");
|
||||
return Promise.reject();
|
||||
}
|
||||
return partition.close();
|
||||
}).then(() => {
|
||||
let oemUnlockByte = (isSetOemUnlockEnabled === true ? "1" : "0");
|
||||
debug("_doSetOemUnlockEnabled: OEM unlock enabled written to " + oemUnlockByte);
|
||||
this._libcutils.property_set(OEM_UNLOCK_PROPERTY, oemUnlockByte);
|
||||
return Promise.resolve();
|
||||
}).catch(ex => {
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Computes the digest by reading the entire block of data and write it to the digest field
|
||||
*
|
||||
* @return true Promise<bool> Operation succeed
|
||||
* @return false Promise<bool> Operation failed
|
||||
*/
|
||||
_computeAndWriteDigest: function() {
|
||||
debug("_computeAndWriteDigest()");
|
||||
let digest;
|
||||
let partition;
|
||||
return this._computeDigest().then(_digest => {
|
||||
digest = _digest;
|
||||
return OS.File.open(this._dataBlockFile, {write:true, existing:true, append:false});
|
||||
}).then(_partition => {
|
||||
partition = _partition;
|
||||
return partition.setPosition(DIGEST_OFFSET, OS.File.POS_START);
|
||||
}).then(() => {
|
||||
return partition.write(new Uint8Array([digest.calculated.charCodeAt(i) for (i in digest.calculated)]));
|
||||
}).then(bytesWrittenLength => {
|
||||
if (bytesWrittenLength != DIGEST_SIZE_BYTES) {
|
||||
log("_computeAndWriteDigest: Error writting digest to partition!. Expected: " + DIGEST_SIZE_BYTES + " Written: " + bytesWrittenLength);
|
||||
return Promise.reject();
|
||||
}
|
||||
return partition.close();
|
||||
}).then(() => {
|
||||
debug("_computeAndWriteDigest: digest written to partition");
|
||||
return Promise.resolve(true);
|
||||
}).catch(ex => {
|
||||
log("_computeAndWriteDigest: Couldn't write digest in the persistent partion. ex = " + ex );
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Formats the persistent partition if the OEM Unlock Enabled field is set to true, and
|
||||
* write the Unlock Property accordingly.
|
||||
*
|
||||
* @return true Promise<bool> OEM Unlock was enabled, so the partition has been formated
|
||||
* @return false Promise<bool> OEM Unlock was disabled, so the partition hasn't been formated
|
||||
*/
|
||||
_formatIfOemUnlockEnabled: function () {
|
||||
debug("_formatIfOemUnlockEnabled()");
|
||||
return this.getOemUnlockEnabled().then(enabled => {
|
||||
this._libcutils.property_set(OEM_UNLOCK_PROPERTY,(enabled === true ? "1" : "0"));
|
||||
if (enabled === true) {
|
||||
return this._formatPartition(true);
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
}).then(result => {
|
||||
if (result === false) {
|
||||
return Promise.resolve(false);
|
||||
} else {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}).catch(ex => {
|
||||
log("_formatIfOemUnlockEnabled: An error ocurred!. ex = " + ex);
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Formats the persistent data partition with the proper structure.
|
||||
*
|
||||
* @param isSetOemUnlockEnabled {bool} If true, writes a "1" in the OEM Unlock Enabled field (last
|
||||
* byte of the block). If false, writes a "0".
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
_formatPartition: function(isSetOemUnlockEnabled) {
|
||||
debug("_formatPartition()");
|
||||
let partition;
|
||||
return OS.File.open(this._dataBlockFile, {write:true, existing:true, append:false}).then(_partition => {
|
||||
partition = _partition;
|
||||
return partition.write(new Uint8Array(DIGEST_SIZE_BYTES));
|
||||
}).then(bytesWrittenLength => {
|
||||
if (bytesWrittenLength != DIGEST_SIZE_BYTES) {
|
||||
log("_formatPartition Error writting zero-digest!. Expected: " + DIGEST_SIZE_BYTES + " Written: " + bytesWrittenLength);
|
||||
return Promise.reject();
|
||||
}
|
||||
return partition.write(new Uint32Array([PARTITION_MAGIC]));
|
||||
}).then(bytesWrittenLength => {
|
||||
if (bytesWrittenLength != PARTITION_MAGIC_SIZE_BYTES) {
|
||||
log("_formatPartition Error writting magic number!. Expected: " + PARTITION_MAGIC_SIZE_BYTES + " Written: " + bytesWrittenLength);
|
||||
return Promise.reject();
|
||||
}
|
||||
return partition.write(new Uint8Array(DATA_SIZE_BYTES));
|
||||
}).then(bytesWrittenLength => {
|
||||
if (bytesWrittenLength != DATA_SIZE_BYTES) {
|
||||
log("_formatPartition Error writting data size!. Expected: " + DATA_SIZE_BYTES + " Written: " + bytesWrittenLength);
|
||||
return Promise.reject();
|
||||
}
|
||||
return partition.close();
|
||||
}).then(() => {
|
||||
return this._doSetOemUnlockEnabled(isSetOemUnlockEnabled);
|
||||
}).then(() => {
|
||||
return this._computeAndWriteDigest();
|
||||
}).then(() => {
|
||||
return Promise.resolve();
|
||||
}).catch(ex => {
|
||||
log("_formatPartition: Failed to format block device!: ex = " + ex);
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Check digest validity. If it's not valid, formats the persistent partition
|
||||
*
|
||||
* @return true Promise<bool> The checksum is valid so the promise is resolved to true
|
||||
* @return false Promise<bool> The checksum is not valid, so the partition is going to be
|
||||
* formatted and the OEM Unlock Enabled field written to 0 (false).
|
||||
*/
|
||||
_enforceChecksumValidity: function() {
|
||||
debug("_enforceChecksumValidity");
|
||||
return this._computeDigest(true).then(digest => {
|
||||
if (digest.stored != digest.calculated) {
|
||||
log("_enforceChecksumValidity: Validation failed! Stored digest: " + toHexString(digest.stored) +
|
||||
" is not the same as the calculated one: " + toHexString(digest.calculated));
|
||||
return Promise.reject();
|
||||
}
|
||||
debug("_enforceChecksumValidity: Digest computation succeed.");
|
||||
return Promise.resolve(true);
|
||||
}).catch(ex => {
|
||||
log("_enforceChecksumValidity: Digest computation failed: ex = " + ex);
|
||||
log("_enforceChecksumValidity: Formatting FRP partition...");
|
||||
return this._formatPartition(false).then(() => {
|
||||
return Promise.resolve(false);
|
||||
}).catch(ex => {
|
||||
log("_enforceChecksumValidity: Error ocurred while formating the partition!: ex = " + ex);
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Reads the entire data field
|
||||
*
|
||||
* @return bytes Promise<Uint8Array> A promise resolved with the bytes read
|
||||
*/
|
||||
read: function() {
|
||||
debug("read()");
|
||||
let partition;
|
||||
let bytes;
|
||||
let dataSize;
|
||||
return this.getDataFieldSize().then(_dataSize => {
|
||||
dataSize = _dataSize;
|
||||
return OS.File.open(this._dataBlockFile, {read:true, existing:true, append:false});
|
||||
}).then(_partition => {
|
||||
partition = _partition;
|
||||
return partition.setPosition(DIGEST_SIZE_BYTES + HEADER_SIZE_BYTES, OS.File.POS_START);
|
||||
}).then(() => {
|
||||
return partition.read(dataSize);
|
||||
}).then(_bytes => {
|
||||
bytes = _bytes;
|
||||
if (bytes.byteLength < dataSize) {
|
||||
log("read: Failed to read entire data block. Bytes read: " + bytes.byteLength + "/" + dataSize);
|
||||
return Promise.reject();
|
||||
}
|
||||
return partition.close();
|
||||
}).then(() => {
|
||||
return Promise.resolve(bytes);
|
||||
}).catch(ex => {
|
||||
log("read: Failed to read entire data block. Exception: " + ex);
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes an entire block to the persistent partition
|
||||
*
|
||||
* @param data {Uint8Array}
|
||||
*
|
||||
* @return Promise<Number> Promise resolved to the number of bytes written.
|
||||
*/
|
||||
write: function(data) {
|
||||
debug("write()");
|
||||
// Ensure that we don't overwrite digest/magic/data-length and the last byte
|
||||
let maxBlockSize = this._getBlockDeviceSize() - (DIGEST_SIZE_BYTES + HEADER_SIZE_BYTES + 1);
|
||||
if (data.byteLength > maxBlockSize) {
|
||||
log("write: Couldn't write more than " + maxBlockSize + " bytes to the partition. " +
|
||||
maxBlockSize + " bytes given.");
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
let partition;
|
||||
return OS.File.open(this._dataBlockFile, {write:true, existing:true, append:false}).then(_partition => {
|
||||
let digest = new Uint8Array(DIGEST_SIZE_BYTES);
|
||||
let magic = new Uint8Array((new Uint32Array([PARTITION_MAGIC])).buffer);
|
||||
let dataLength = new Uint8Array((new Uint32Array([data.byteLength])).buffer);
|
||||
let bufferToWrite = new Uint8Array(digest.byteLength + magic.byteLength + dataLength.byteLength + data.byteLength );
|
||||
let offset = 0;
|
||||
bufferToWrite.set(digest, offset);
|
||||
offset += digest.byteLength;
|
||||
bufferToWrite.set(magic, offset);
|
||||
offset += magic.byteLength;
|
||||
bufferToWrite.set(dataLength, offset);
|
||||
offset += dataLength.byteLength;
|
||||
bufferToWrite.set(data, offset);
|
||||
partition = _partition;
|
||||
return partition.write(bufferToWrite);
|
||||
}).then(bytesWrittenLength => {
|
||||
let expectedWrittenLength = DIGEST_SIZE_BYTES + HEADER_SIZE_BYTES + data.byteLength;
|
||||
if (bytesWrittenLength != expectedWrittenLength) {
|
||||
log("write: Error writting data to partition!: Expected: " + expectedWrittenLength + " Written: " + bytesWrittenLength);
|
||||
return Promise.reject();
|
||||
}
|
||||
return partition.close();
|
||||
}).then(() => {
|
||||
return this._computeAndWriteDigest();
|
||||
}).then(couldComputeAndWriteDigest => {
|
||||
if (couldComputeAndWriteDigest === true) {
|
||||
return Promise.resolve(data.byteLength);
|
||||
} else {
|
||||
log("write: Failed to compute and write the digest");
|
||||
return Promise.reject();
|
||||
}
|
||||
}).catch(ex => {
|
||||
log("write: Failed to write to the persistent partition: ex = " + ex);
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Wipes the persistent partition.
|
||||
*
|
||||
* @return Promise If no errors, the promise is resolved
|
||||
*/
|
||||
wipe: function() {
|
||||
debug("wipe()");
|
||||
|
||||
if (this._testing === true) {
|
||||
log("wipe: No wipe() funcionality in testing mode");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
const O_READONLY = 0;
|
||||
const O_RDWR = 2;
|
||||
const O_NONBLOCK = 1 << 11;
|
||||
// This constant value is the same under 32 and 64 bits arch.
|
||||
const BLKSECDISCARD = 0x127D;
|
||||
// This constant value is the same under 32 and 64 bits arch.
|
||||
const BLKDISCARD = 0x1277;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let range = new this.ctypes.unsigned_long();
|
||||
let rangeAddress = range.address();
|
||||
let blockDeviceLength = this._getBlockDeviceSize();
|
||||
range[0] = 0;
|
||||
range[1] = blockDeviceLength;
|
||||
if (range[1] === 0) {
|
||||
log("wipe: Block device size is 0!");
|
||||
return reject();
|
||||
}
|
||||
let fd = this._libc.open(this._dataBlockFile, O_RDWR);
|
||||
if (fd < 0) {
|
||||
log("wipe: ERROR couldn't open partition!: error = " + this.ctypes.errno);
|
||||
return reject();
|
||||
}
|
||||
let ret = this._libc.ioctl(fd, BLKSECDISCARD, rangeAddress);
|
||||
if (ret < 0) {
|
||||
log("wipe: Something went wrong secure discarding block: errno: " + this.ctypes.errno + ": Falling back to non-secure discarding...");
|
||||
ret = this._libc.ioctl(fd, BLKDISCARD, rangeAddress);
|
||||
if (ret < 0) {
|
||||
this._libc.close(fd);
|
||||
log("wipe: CRITICAL: non-secure discarding failed too!!: errno: " + this.ctypes.errno);
|
||||
return reject();
|
||||
} else {
|
||||
this._libc.close(fd);
|
||||
log("wipe: non-secure discard used and succeed");
|
||||
return resolve();
|
||||
}
|
||||
}
|
||||
this._libc.close(fd);
|
||||
log("wipe: secure discard succeed");
|
||||
return resolve();
|
||||
});
|
||||
#else
|
||||
log("wipe: ERROR: This feature is only supported in Gonk!");
|
||||
return Promise.reject();
|
||||
#endif
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the OEM Unlock Enabled field (one byte at the end of the partition), to 1 or 0 depending on
|
||||
* the input parameter.
|
||||
*
|
||||
* @param enabled {bool} If enabled, we write a 1 in the last byte of the partition.
|
||||
*
|
||||
* @return Promise
|
||||
*
|
||||
*/
|
||||
setOemUnlockEnabled: function(enabled) {
|
||||
debug("setOemUnlockEnabled()");
|
||||
return this._doSetOemUnlockEnabled(enabled).then(() => {
|
||||
return this._computeAndWriteDigest();
|
||||
}).then(() => {
|
||||
return Promise.resolve();
|
||||
}).catch(ex => {
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the byte from the partition which represents the OEM Unlock Enabled state.
|
||||
*
|
||||
* @return true Promise<Bool> The user didn't activate KillSwitch.
|
||||
* @return false Promise<Bool> The user did activate KillSwitch.
|
||||
*/
|
||||
getOemUnlockEnabled: function() {
|
||||
log("getOemUnlockEnabled()");
|
||||
let ret = false;
|
||||
let partition;
|
||||
return OS.File.open(this._dataBlockFile, {existing:true, append:false, read:true}).then(_partition => {
|
||||
partition = _partition;
|
||||
return partition.setPosition(this._getBlockDeviceSize() - OEM_UNLOCK_ENABLED_BYTES, OS.File.POS_START);
|
||||
}).then(() => {
|
||||
return partition.read(OEM_UNLOCK_ENABLED_BYTES);
|
||||
}).then(data => {
|
||||
debug("getOemUnlockEnabled: OEM unlock enabled byte = '" + data[0] + "'");
|
||||
ret = (data[0] === 1 ? true : false);
|
||||
return partition.close();
|
||||
}).then(() => {
|
||||
return Promise.resolve(ret);
|
||||
}).catch(ex => {
|
||||
log("getOemUnlockEnabled: Error reading OEM unlock enabled byte from partition: ex = " + ex);
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the size of the data block by reading the data-length field
|
||||
*
|
||||
* @return Promise<Number> A promise resolved to the number of bytes os the data field.
|
||||
*/
|
||||
getDataFieldSize: function() {
|
||||
debug("getDataFieldSize()");
|
||||
let partition
|
||||
let dataLength = 0;
|
||||
return OS.File.open(this._dataBlockFile, {read:true, existing:true, append:false}).then(_partition => {
|
||||
partition = _partition;
|
||||
// Skip the digest field
|
||||
return partition.setPosition(DIGEST_SIZE_BYTES, OS.File.POS_START);
|
||||
}).then(() => {
|
||||
// Read the Magic field
|
||||
return partition.read(PARTITION_MAGIC_SIZE_BYTES);
|
||||
}).then(_magic => {
|
||||
let magic = new Uint32Array(_magic.buffer)[0];
|
||||
if (magic === PARTITION_MAGIC) {
|
||||
return partition.read(PARTITION_MAGIC_SIZE_BYTES);
|
||||
} else {
|
||||
log("getDataFieldSize: ERROR: Invalid Magic number!");
|
||||
return Promise.reject();
|
||||
}
|
||||
}).then(_dataLength => {
|
||||
if (_dataLength) {
|
||||
dataLength = new Uint32Array(_dataLength.buffer)[0];
|
||||
}
|
||||
return partition.close();
|
||||
}).then(() => {
|
||||
if (dataLength && dataLength != 0) {
|
||||
return Promise.resolve(dataLength);
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
}).catch(ex => {
|
||||
log("getDataFieldSize: Couldn't get data field size: ex = " + ex);
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the maximum possible size of a data field
|
||||
*
|
||||
* @return Promise<Number> A Promise resolved to the maximum number of bytes allowed for the data field
|
||||
*
|
||||
*/
|
||||
getMaximumDataBlockSize: function() {
|
||||
debug("getMaximumDataBlockSize()");
|
||||
return new Promise((resolve, reject) => {
|
||||
let actualSize = this._getBlockDeviceSize() - HEADER_SIZE_BYTES - OEM_UNLOCK_ENABLED_BYTES;
|
||||
resolve(actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// This code should ALWAYS be living only on the parent side.
|
||||
if (!inParent) {
|
||||
log("PersistentDataBlock should only be living on parent side.");
|
||||
throw Cr.NS_ERROR_ABORT;
|
||||
} else {
|
||||
this.PersistentDataBlock.init();
|
||||
}
|
|
@ -78,6 +78,10 @@ EXTRA_JS_MODULES += [
|
|||
'WebappsUpdater.jsm',
|
||||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'PersistentDataBlock.jsm'
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
|
||||
EXTRA_JS_MODULES += [
|
||||
'GlobalSimulatorScreen.jsm'
|
||||
|
|
|
@ -0,0 +1,412 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
// This constants must be synced with the ones in PersistentDataBlock.jsm
|
||||
const PARTITION_MAGIC = 0x19901873;
|
||||
const DIGEST_SIZE_BYTES = 32;
|
||||
const PARTITION_MAGIC_SIZE_BYTES = 4;
|
||||
const DATA_SIZE_BYTES = 4;
|
||||
const OEM_UNLOCK_ENABLED_BYTES = 1;
|
||||
|
||||
const CACHE_PARTITION = "/dev/block/mtdblock2";
|
||||
const PARTITION_FAKE_FILE = "/data/local/tmp/frp.test";
|
||||
const CACHE_PARTITION_SIZE = 69206016;
|
||||
|
||||
function log(str) {
|
||||
do_print("head_persistentdatablock: " + str + "\n");
|
||||
}
|
||||
|
||||
function toHexString(data) {
|
||||
function toHexChar(charCode) {
|
||||
return ("0" + charCode.toString(16).slice(-2));
|
||||
}
|
||||
let hexString = "";
|
||||
if (typeof data === "string") {
|
||||
hexString = [toHexChar(data.charCodeAt(i)) for (i in data)].join("");
|
||||
} else if (typeof data === "array") {
|
||||
hexString = [toHexChar(data[i]) for (i in data)].join("");
|
||||
}
|
||||
return hexString;
|
||||
}
|
||||
|
||||
function _prepareConfig(_args) {
|
||||
let args = _args || {};
|
||||
// This digest has been previously calculated given the data to be written later, and setting the OEM Unlocked Enabled byte
|
||||
// to 1. If we need different values, some tests will fail because this precalculated digest won't be valid then.
|
||||
args.digest = args.digest || new Uint8Array([0x00, 0x41, 0x7e, 0x5f, 0xe2, 0xdd, 0xaa, 0xed, 0x11, 0x90, 0x0e, 0x1d, 0x26,
|
||||
0x10, 0x30, 0xbd, 0x44, 0x9e, 0xcc, 0x4b, 0x65, 0xbe, 0x2e, 0x99, 0x9f, 0x86,
|
||||
0xf0, 0xfc, 0x5b, 0x33, 0x00, 0xd0]);
|
||||
args.dataLength = args.dataLength || 6;
|
||||
args.data = args.data || new Uint8Array(["P", "A", "S", "S", "W", "D"]);
|
||||
args.oem = args.oem === undefined ? true : args.oem;
|
||||
args.oemUnlockAllowed = args.oemUnlockAllowed === undefined ? true : args.oemUnlockAllowed;
|
||||
|
||||
log("_prepareConfig: args.digest = " + args.digest);
|
||||
log("_prepareConfig: args.dataLength = " + args.dataLength);
|
||||
log("_prepareConfig: args.data = " + args.data);
|
||||
log("_prepareConfig: args.oem = " + args.oem);
|
||||
log("_prepareConfig: args.oemUnlockAllowed = " + args.oemUnlockAllowed);
|
||||
|
||||
/* This function will be called after passing all native stuff tests, so we will write into a file instead of a real
|
||||
* partition. Obviously, there are some native operations like getting the device block size or wipping, that will not
|
||||
* work in a regular file, so we need to fake them. */
|
||||
PersistentDataBlock._libcutils.property_set("sys.oem_unlock_allowed", args.oemUnlockAllowed === true ? "true" : "false");
|
||||
PersistentDataBlock.setTestingMode(true);
|
||||
PersistentDataBlock._dataBlockFile = PARTITION_FAKE_FILE;
|
||||
// Create the test file with the same structure as the partition will be
|
||||
let tempFile;
|
||||
return OS.File.open(PersistentDataBlock._dataBlockFile, {write:true, append:false, truncate: true}).then(_tempFile => {
|
||||
log("_prepareConfig: Writing DIGEST...");
|
||||
tempFile = _tempFile;
|
||||
return tempFile.write(args.digest);
|
||||
}).then(bytes => {
|
||||
log("_prepareConfig: Writing the magic: " + PARTITION_MAGIC);
|
||||
return tempFile.write(new Uint32Array([PARTITION_MAGIC]));
|
||||
}).then(bytes => {
|
||||
log("_prepareConfig: Writing the length of data field");
|
||||
return tempFile.write(new Uint32Array([args.dataLength]));
|
||||
}).then(bytes => {
|
||||
log("_prepareConfig: Writing the data field");
|
||||
let data = new Uint8Array(PersistentDataBlock._getBlockDeviceSize() -
|
||||
(DIGEST_SIZE_BYTES + PARTITION_MAGIC_SIZE_BYTES + DATA_SIZE_BYTES + OEM_UNLOCK_ENABLED_BYTES));
|
||||
data.set(args.data);
|
||||
return tempFile.write(data);
|
||||
}).then(bytes => {
|
||||
return tempFile.write(new Uint8Array([ args.oem === true ? 1 : 0 ]));
|
||||
}).then(bytes => {
|
||||
return tempFile.close();
|
||||
}).then(() =>{
|
||||
return Promise.resolve(true);
|
||||
}).catch(ex => {
|
||||
log("_prepareConfig: ERROR: ex = " + ex);
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
}
|
||||
|
||||
function utils_getByteAt(pos) {
|
||||
let file;
|
||||
let byte;
|
||||
return OS.File.open(PersistentDataBlock._dataBlockFile, {read:true, existing:true, append:false}).then(_file => {
|
||||
file = _file;
|
||||
return file.setPosition(pos, OS.File.POS_START);
|
||||
}).then(() => {
|
||||
return file.read(1);
|
||||
}).then(_byte => {
|
||||
byte = _byte;
|
||||
return file.close();
|
||||
}).then(() => {
|
||||
return Promise.resolve(byte[0]);
|
||||
}).catch(ex => {
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
}
|
||||
|
||||
function utils_getHeader() {
|
||||
let file;
|
||||
let header = {};
|
||||
return OS.File.open(PersistentDataBlock._dataBlockFile, {read:true, existing:true, append:false}).then(_file => {
|
||||
file = _file;
|
||||
return file.read(DIGEST_SIZE_BYTES);
|
||||
}).then(digest => {
|
||||
header.digest = digest;
|
||||
return file.read(PARTITION_MAGIC_SIZE_BYTES);
|
||||
}).then(magic => {
|
||||
header.magic = magic;
|
||||
return file.read(DATA_SIZE_BYTES);
|
||||
}).then(dataLength => {
|
||||
header.dataLength = dataLength;
|
||||
return file.close();
|
||||
}).then(() => {
|
||||
return Promise.resolve(header);
|
||||
}).catch(ex => {
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
}
|
||||
|
||||
function utils_getData() {
|
||||
let file;
|
||||
let data;
|
||||
return OS.File.open(PersistentDataBlock._dataBlockFile, {read:true, existing:true, append:false}).then(_file => {
|
||||
file = _file;
|
||||
return file.setPosition(DIGEST_SIZE_BYTES + PARTITION_MAGIC_SIZE_BYTES, OS.File.POS_START);
|
||||
}).then(() => {
|
||||
return file.read(4);
|
||||
}).then(_dataLength => {
|
||||
let dataLength = new Uint32Array(_dataLength.buffer);
|
||||
log("utils_getData: dataLength = " + dataLength[0]);
|
||||
return file.read(dataLength[0]);
|
||||
}).then(_data => {
|
||||
data = _data;
|
||||
return file.close();
|
||||
}).then(() => {
|
||||
return Promise.resolve(data);
|
||||
}).catch(ex => {
|
||||
return Promise.reject(ex);
|
||||
});
|
||||
}
|
||||
|
||||
function _installTests() {
|
||||
// <NATIVE_TESTS> Native operation tests go first
|
||||
add_test(function test_getBlockDeviceSize() {
|
||||
// We will use emulator /cache partition to get it's size.
|
||||
PersistentDataBlock._dataBlockFile = CACHE_PARTITION;
|
||||
// Disable testing mode for this specific test because we can get the size of a real block device,
|
||||
// but we need to flip to testing mode after this test because we use files instead of partitions
|
||||
// and we cannot run this operation on files.
|
||||
PersistentDataBlock.setTestingMode(false);
|
||||
let blockSize = PersistentDataBlock._getBlockDeviceSize();
|
||||
ok(blockSize !== CACHE_PARTITION_SIZE, "test_getBlockDeviceSize: Block device size should be greater than 0");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_wipe() {
|
||||
// Turning into testing mode again.
|
||||
PersistentDataBlock.setTestingMode(true);
|
||||
PersistentDataBlock.wipe().then(() => {
|
||||
// We don't evaluate anything because in testing mode we always return ok!
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
// ... something went really really bad if this happens.
|
||||
ok(false, "test_wipe failed!: ex: " + ex);
|
||||
});
|
||||
});
|
||||
// </NATIVE_TESTS>
|
||||
|
||||
add_test(function test_computeDigest() {
|
||||
_prepareConfig().then(() => {
|
||||
PersistentDataBlock._computeDigest().then(digest => {
|
||||
// So in order to update this value in a future (should only happens if the partition data is changed), you just need
|
||||
// to launch this test manually, see the result in the logs and update this constant with that value.
|
||||
const _EXPECTED_VALUE = "0004107e05f0e20dd0aa0ed0110900e01d0260100300bd04409e0cc04b0650be02e09909f0860f00fc05b033000d0";
|
||||
let calculatedValue = toHexString(digest.calculated);
|
||||
strictEqual(calculatedValue, _EXPECTED_VALUE);
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_computeDigest failed!: ex: " + ex);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_getDataFieldSize() {
|
||||
PersistentDataBlock.getDataFieldSize().then(dataFieldLength => {
|
||||
log("test_getDataFieldSize: dataFieldLength is " + dataFieldLength);
|
||||
strictEqual(dataFieldLength, 6);
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_getOemUnlockedEnabled failed: ex:" + ex);
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_setOemUnlockedEnabledToTrue() {
|
||||
PersistentDataBlock.setOemUnlockEnabled(true).then(() => {
|
||||
return utils_getByteAt(PersistentDataBlock._getBlockDeviceSize() - 1);
|
||||
}).then(byte => {
|
||||
log("test_setOemUnlockedEnabledToTrue: byte = " + byte );
|
||||
strictEqual(byte, 1);
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_setOemUnlockedEnabledToTrue failed!: ex: " + ex);
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_setOemUnlockedEnabledToFalse() {
|
||||
PersistentDataBlock.setOemUnlockEnabled(false).then(() => {
|
||||
return utils_getByteAt(PersistentDataBlock._getBlockDeviceSize() - 1);
|
||||
}).then(byte => {
|
||||
log("test_setOemUnlockedEnabledToFalse: byte = " + byte );
|
||||
strictEqual(byte, 0);
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_setOemUnlockedEnabledToFalse failed!: ex: " + ex);
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_getOemUnlockedEnabledWithTrue() {
|
||||
// We first need to set the OEM Unlock Enabled byte to true so we can test
|
||||
// the getter properly
|
||||
PersistentDataBlock.setOemUnlockEnabled(true).then(() => {
|
||||
return PersistentDataBlock.getOemUnlockEnabled().then(enabled => {
|
||||
log("test_getOemUnlockedEnabledWithTrue: enabled is " + enabled);
|
||||
ok(enabled === true, "test_getOemUnlockedEnabledWithTrue: enabled value should be true");
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_getOemUnlockedEnabledWithTrue failed: ex:" + ex);
|
||||
});
|
||||
}).catch(ex => {
|
||||
ok(false, "test_getOemUnlockedEnabledWithTrue failed: An error ocurred while setting the OEM Unlock Enabled byte to true: ex:" + ex);
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_getOemUnlockedEnabledWithFalse() {
|
||||
// We first need to set the OEM Unlock Enabled byte to false so we can test
|
||||
// the getter properly
|
||||
PersistentDataBlock.setOemUnlockEnabled(false).then(() => {
|
||||
return PersistentDataBlock.getOemUnlockEnabled().then(enabled => {
|
||||
log("test_getOemUnlockedEnabledWithFalse: enabled is " + enabled);
|
||||
ok(enabled === false, "test_getOemUnlockedEnabledWithFalse: enabled value should be false");
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_getOemUnlockedEnabledWithFalse failed: ex:" + ex);
|
||||
});
|
||||
}).catch(ex => {
|
||||
ok(false, "test_getOemUnlockedEnabledWithFalse failed: An error ocurred while setting the OEM Unlock Enabled byte to false: ex:" + ex);
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_computeAndWriteDigest() {
|
||||
PersistentDataBlock._computeAndWriteDigest().then(() => {
|
||||
return utils_getHeader();
|
||||
}).then(header => {
|
||||
log("test_computeAndWriteDigest: header = " + header);
|
||||
let magicRead = new Uint32Array(header.magic.buffer);
|
||||
let magicSupposed = new Uint32Array([PARTITION_MAGIC]);
|
||||
strictEqual(magicRead[0], magicSupposed[0]);
|
||||
let dataLength = new Uint32Array([header.dataLength]);
|
||||
strictEqual(header.dataLength[0], 6);
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_computeAndWriteDigest failed!: ex: " + ex);
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_formatIfOemUnlockEnabledWithTrue() {
|
||||
_prepareConfig({oem:true}).then(() => {
|
||||
return PersistentDataBlock._formatIfOemUnlockEnabled();
|
||||
}).then(result => {
|
||||
ok(result === true, "test_formatIfOemUnlockEnabledWithTrue: result should be true");
|
||||
return utils_getByteAt(PersistentDataBlock._getBlockDeviceSize() - 1);
|
||||
}).then(byte => {
|
||||
// Check if the OEM Unlock Enabled byte is 1
|
||||
strictEqual(byte, 1);
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_formatIfOemUnlockEnabledWithTrue failed!: ex: " + ex);
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_formatIfOemUnlockEnabledWithFalse() {
|
||||
_prepareConfig({oem:false}).then(() => {
|
||||
return PersistentDataBlock._formatIfOemUnlockEnabled();
|
||||
}).then(result => {
|
||||
log("test_formatIfOemUnlockEnabledWithFalse: result = " + result);
|
||||
ok(result === false, "test_formatIfOemUnlockEnabledWithFalse: result should be false");
|
||||
return utils_getByteAt(PersistentDataBlock._getBlockDeviceSize() - 1);
|
||||
}).then(byte => {
|
||||
// Check if the OEM Unlock Enabled byte is 0
|
||||
strictEqual(byte, 0);
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_formatIfOemUnlockEnabledWithFalse failed!: ex: " + ex);
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_formatPartition() {
|
||||
// Restore a fullfilled partition so we can check if formatting works...
|
||||
_prepareConfig({oem:true}).then(() => {
|
||||
return PersistentDataBlock._formatPartition(true);
|
||||
}).then(() => {
|
||||
return utils_getByteAt(PersistentDataBlock._getBlockDeviceSize() - 1);
|
||||
}).then(byte => {
|
||||
// Check if the last byte is 1
|
||||
strictEqual(byte, 1);
|
||||
return utils_getHeader();
|
||||
}).then(header => {
|
||||
// The Magic number should exists in a formatted partition
|
||||
let magicRead = new Uint32Array(header.magic.buffer);
|
||||
let magicSupposed = new Uint32Array([PARTITION_MAGIC]);
|
||||
strictEqual(magicRead[0], magicSupposed[0]);
|
||||
// In a formatted partition, the digest field is always 32 bytes of zeros.
|
||||
let digestSupposed = new Uint8Array(DIGEST_SIZE_BYTES);
|
||||
strictEqual(header.digest.join(""), "94227253995810864198417798821014713171138121254110134189198178208133167236184116199");
|
||||
return PersistentDataBlock._formatPartition(false);
|
||||
}).then(() => {
|
||||
return utils_getByteAt(PersistentDataBlock._getBlockDeviceSize() - 1);
|
||||
}).then(byte => {
|
||||
// In this case OEM Unlock enabled byte should be set to 0 because we passed false to the _formatPartition method before.
|
||||
strictEqual(byte, 0);
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_formatPartition failed!: ex: " + ex);
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_enforceChecksumValidityWithValidChecksum() {
|
||||
// We need a valid partition layout to pass this test
|
||||
_prepareConfig().then(() => {
|
||||
PersistentDataBlock._enforceChecksumValidity().then(() => {
|
||||
ok(true, "test_enforceChecksumValidityWithValidChecksum passed");
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_enforceChecksumValidityWithValidChecksum failed!: ex: " + ex);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_enforceChecksumValidityWithInvalidChecksum() {
|
||||
var badDigest = new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1C, 0x1D, 0x1E, 0x1F, 0x20]);
|
||||
// We need a valid partition layout to pass this test
|
||||
_prepareConfig({digest: badDigest}).then(() => {
|
||||
PersistentDataBlock._enforceChecksumValidity().then(() => {
|
||||
return utils_getHeader();
|
||||
}).then(header => {
|
||||
// Check that we have a valid magic after formatting
|
||||
let magicRead = new Uint32Array(header.magic.buffer)[0];
|
||||
let magicSupposed = new Uint32Array([PARTITION_MAGIC])[0];
|
||||
strictEqual(magicRead, magicSupposed);
|
||||
// Data length field should be 0, because we formatted the partition
|
||||
let dataLengthRead = new Uint32Array(header.dataLength.buffer)[0];
|
||||
strictEqual(dataLengthRead, 0);
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_enforceChecksumValidityWithValidChecksum failed!: ex: " + ex);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_read() {
|
||||
// Before reading, let's write some bytes of data first.
|
||||
PersistentDataBlock.write(new Uint8Array([1,2,3,4])).then(() => {
|
||||
PersistentDataBlock.read().then(bytes => {
|
||||
log("test_read: bytes (in hex): " + toHexString(bytes));
|
||||
strictEqual(bytes[0], 1);
|
||||
strictEqual(bytes[1], 2);
|
||||
strictEqual(bytes[2], 3);
|
||||
strictEqual(bytes[3], 4);
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_read failed!: ex: " + ex);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
add_test(function test_write() {
|
||||
let data = new Uint8Array(['1','2','3','4','5']);
|
||||
PersistentDataBlock.write(data).then(bytesWrittenLength => {
|
||||
log("test_write: bytesWrittenLength = " + bytesWrittenLength);
|
||||
return utils_getData();
|
||||
}).then(data => {
|
||||
strictEqual(data[0], 1);
|
||||
strictEqual(data[1], 2);
|
||||
strictEqual(data[2], 3);
|
||||
strictEqual(data[3], 4);
|
||||
strictEqual(data[4], 5);
|
||||
run_next_test();
|
||||
}).catch(ex => {
|
||||
ok(false, "test_write failed!: ex: " + ex);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "libcutils", function () {
|
||||
Cu.import("resource://gre/modules/systemlibs.js");
|
||||
return libcutils;
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
Cu.import("resource://gre/modules/PersistentDataBlock.jsm");
|
||||
// We need to point to a valid partition for some of the tests. This is the /cache
|
||||
// partition in the emulator (x86-KitaKat).
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
_installTests();
|
|
@ -50,3 +50,9 @@ skip-if = (toolkit == "gonk")
|
|||
head = file_killswitch.js
|
||||
# Bug 1193677: disable on B2G ICS Emulator for intermittent failures with IndexedDB
|
||||
skip-if = ((toolkit != "gonk") || (toolkit == "gonk" && debug))
|
||||
|
||||
[test_persistentdatablock_gonk.js]
|
||||
head = file_persistentdatablock.js
|
||||
skip-if = (toolkit != "gonk")
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="14aefb2519becfa32f31bcc3c9c995693421f19c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3cf488a97ecaec43369f3e3d8a7dda52be019f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="14aefb2519becfa32f31bcc3c9c995693421f19c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3cf488a97ecaec43369f3e3d8a7dda52be019f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="14aefb2519becfa32f31bcc3c9c995693421f19c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3cf488a97ecaec43369f3e3d8a7dda52be019f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="14aefb2519becfa32f31bcc3c9c995693421f19c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3cf488a97ecaec43369f3e3d8a7dda52be019f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="14aefb2519becfa32f31bcc3c9c995693421f19c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3cf488a97ecaec43369f3e3d8a7dda52be019f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="14aefb2519becfa32f31bcc3c9c995693421f19c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3cf488a97ecaec43369f3e3d8a7dda52be019f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="14aefb2519becfa32f31bcc3c9c995693421f19c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3cf488a97ecaec43369f3e3d8a7dda52be019f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="14aefb2519becfa32f31bcc3c9c995693421f19c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3cf488a97ecaec43369f3e3d8a7dda52be019f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"git": {
|
||||
"git_revision": "14aefb2519becfa32f31bcc3c9c995693421f19c",
|
||||
"git_revision": "f3cf488a97ecaec43369f3e3d8a7dda52be019f9",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "e384f9eb4a149302c38df25a4aa772ddd1415f7e",
|
||||
"revision": "295225a48cf2607651e83cdcda92a57516f1509e",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="14aefb2519becfa32f31bcc3c9c995693421f19c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3cf488a97ecaec43369f3e3d8a7dda52be019f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="14aefb2519becfa32f31bcc3c9c995693421f19c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3cf488a97ecaec43369f3e3d8a7dda52be019f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="14aefb2519becfa32f31bcc3c9c995693421f19c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3cf488a97ecaec43369f3e3d8a7dda52be019f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -552,6 +552,9 @@
|
|||
@RESPATH@/components/SlowScriptDebug.manifest
|
||||
@RESPATH@/components/SlowScriptDebug.js
|
||||
|
||||
@RESPATH@/components/TVSimulatorService.js
|
||||
@RESPATH@/components/TVSimulatorService.manifest
|
||||
|
||||
#ifndef RELEASE_BUILD
|
||||
@RESPATH@/components/InterAppComm.manifest
|
||||
@RESPATH@/components/InterAppCommService.js
|
||||
|
|
|
@ -1498,7 +1498,12 @@ TabChild::ApplyShowInfo(const ShowInfo& aInfo)
|
|||
void
|
||||
TabChild::MaybeRequestPreinitCamera()
|
||||
{
|
||||
// Check if this tab will use the `camera` permission.
|
||||
// Check if this tab is an app (not a browser frame) and will use the
|
||||
// `camera` permission,
|
||||
if (IsBrowserElement()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
|
||||
if (NS_WARN_IF(!appsService)) {
|
||||
return;
|
||||
|
|
|
@ -1,532 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/TVServiceRunnables.h"
|
||||
#include "mozilla/dom/TVTypes.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "prtime.h"
|
||||
#include "FakeTVService.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(FakeTVService)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FakeTVService)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTuners)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannels)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrograms)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEITBroadcastedTimer)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScanCompleteTimer)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FakeTVService)
|
||||
tmp->Shutdown();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTuners)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannels)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrograms)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEITBroadcastedTimer)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mScanCompleteTimer)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(FakeTVService)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(FakeTVService)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FakeTVService)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITVService)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
FakeTVService::FakeTVService()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
FakeTVService::~FakeTVService()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
FakeTVService::Init()
|
||||
{
|
||||
const char* sourceTypes1[2] = {"dvb-t", "dvb-c"};
|
||||
nsCOMPtr<nsITVTunerData> tunerData1 = MockTuner(NS_LITERAL_STRING("1"), 2, sourceTypes1);
|
||||
mTuners.AppendElement(tunerData1);
|
||||
const char* sourceTypes2[1] = {"dvb-s"};
|
||||
nsCOMPtr<nsITVTunerData> tunerData2 = MockTuner(NS_LITERAL_STRING("2"), 1, sourceTypes2);
|
||||
mTuners.AppendElement(tunerData2);
|
||||
|
||||
nsCOMPtr<nsITVChannelData> channelData1 =
|
||||
MockChannel(NS_LITERAL_STRING("networkId1"), NS_LITERAL_STRING("transportStreamId1"),
|
||||
NS_LITERAL_STRING("serviceId1"), NS_LITERAL_STRING("tv"),
|
||||
NS_LITERAL_STRING("1"), NS_LITERAL_STRING("name1"), true, true);
|
||||
mChannels.AppendElement(channelData1);
|
||||
nsCOMPtr<nsITVChannelData> channelData2 =
|
||||
MockChannel(NS_LITERAL_STRING("networkId2"), NS_LITERAL_STRING("transportStreamId2"),
|
||||
NS_LITERAL_STRING("serviceId2"), NS_LITERAL_STRING("radio"),
|
||||
NS_LITERAL_STRING("2"), NS_LITERAL_STRING("name2"), true, true);
|
||||
mChannels.AppendElement(channelData2);
|
||||
|
||||
uint64_t now = PR_Now();
|
||||
const char* audioLanguages1[2] = {"eng", "jpn"};
|
||||
const char* subtitleLanguages1[2] = {"fre", "spa"};
|
||||
nsCOMPtr<nsITVProgramData> programData1 =
|
||||
MockProgram(NS_LITERAL_STRING("eventId1"), NS_LITERAL_STRING("title1"),
|
||||
now - 1, 3600000,
|
||||
NS_LITERAL_STRING("description1"), NS_LITERAL_STRING("rating1"),
|
||||
2, audioLanguages1, 2, subtitleLanguages1);
|
||||
mPrograms.AppendElement(programData1);
|
||||
nsCOMPtr<nsITVProgramData> programData2 =
|
||||
MockProgram(NS_LITERAL_STRING("eventId2"), NS_LITERAL_STRING("title2"),
|
||||
now + 3600000 , 3600000,
|
||||
NS_LITERAL_STRING(""), NS_LITERAL_STRING(""),
|
||||
0, nullptr, 0, nullptr);
|
||||
mPrograms.AppendElement(programData2);
|
||||
}
|
||||
|
||||
void
|
||||
FakeTVService::Shutdown()
|
||||
{
|
||||
if (mEITBroadcastedTimer) {
|
||||
mEITBroadcastedTimer->Cancel();
|
||||
}
|
||||
if (mScanCompleteTimer) {
|
||||
mScanCompleteTimer->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::GetSourceListener(nsITVSourceListener** aSourceListener)
|
||||
{
|
||||
if (!mSourceListener) {
|
||||
*aSourceListener = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*aSourceListener = mSourceListener;
|
||||
NS_ADDREF(*aSourceListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::SetSourceListener(nsITVSourceListener* aSourceListener)
|
||||
{
|
||||
mSourceListener = aSourceListener;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::GetTuners(nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMutableArray> tunerDataList = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!tunerDataList) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mTuners.Length(); i++) {
|
||||
tunerDataList->AppendElement(mTuners[i], false);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, tunerDataList);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::SetSource(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mTuners.Length(); i++) {
|
||||
nsString tunerId;
|
||||
mTuners[i]->GetId(tunerId);
|
||||
if (aTunerId.Equals(tunerId)) {
|
||||
uint32_t sourceTypeCount;
|
||||
char** sourceTypes;
|
||||
mTuners[i]->GetSupportedSourceTypes(&sourceTypeCount, &sourceTypes);
|
||||
for (uint32_t j = 0; j < sourceTypeCount; j++) {
|
||||
nsString sourceType;
|
||||
sourceType.AssignASCII(sourceTypes[j]);
|
||||
if (aSourceType.Equals(sourceType)) {
|
||||
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(sourceTypeCount, sourceTypes);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, nullptr);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
}
|
||||
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(sourceTypeCount, sourceTypes);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, nullptr, nsITVServiceCallback::TV_ERROR_FAILURE);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
class EITBroadcastedCallback final : public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
EITBroadcastedCallback(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
nsITVSourceListener* aSourceListener,
|
||||
nsITVChannelData* aChannelData)
|
||||
: mTunerId(aTunerId)
|
||||
, mSourceType(aSourceType)
|
||||
, mSourceListener(aSourceListener)
|
||||
, mChannelData(aChannelData)
|
||||
{}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Notify(nsITimer* aTimer) override
|
||||
{
|
||||
// Notify mock EIT broadcasting.
|
||||
nsITVProgramData** programDataList =
|
||||
static_cast<nsITVProgramData **>(moz_xmalloc(1 * sizeof(nsITVProgramData*)));
|
||||
programDataList[0] = new TVProgramData();
|
||||
programDataList[0]->SetEventId(NS_LITERAL_STRING("eventId"));
|
||||
programDataList[0]->SetTitle(NS_LITERAL_STRING("title"));
|
||||
programDataList[0]->SetStartTime(PR_Now() + 3600000);
|
||||
programDataList[0]->SetDuration(3600000);
|
||||
programDataList[0]->SetDescription(NS_LITERAL_STRING("description"));
|
||||
programDataList[0]->SetRating(NS_LITERAL_STRING("rating"));
|
||||
programDataList[0]->SetAudioLanguages(0, nullptr);
|
||||
programDataList[0]->SetSubtitleLanguages(0, nullptr);
|
||||
nsresult rv = mSourceListener->NotifyEITBroadcasted(mTunerId, mSourceType,
|
||||
mChannelData,
|
||||
programDataList, 1);
|
||||
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(1, programDataList);
|
||||
return rv;
|
||||
}
|
||||
|
||||
private:
|
||||
~EITBroadcastedCallback() {}
|
||||
|
||||
nsString mTunerId;
|
||||
nsString mSourceType;
|
||||
nsCOMPtr<nsITVSourceListener> mSourceListener;
|
||||
nsCOMPtr<nsITVChannelData> mChannelData;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(EITBroadcastedCallback, nsITimerCallback)
|
||||
|
||||
class ScanCompleteCallback final : public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
ScanCompleteCallback(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
nsITVSourceListener* aSourceListener)
|
||||
: mTunerId(aTunerId)
|
||||
, mSourceType(aSourceType)
|
||||
, mSourceListener(aSourceListener)
|
||||
{}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Notify(nsITimer* aTimer) override
|
||||
{
|
||||
return mSourceListener->NotifyChannelScanComplete(mTunerId, mSourceType);
|
||||
}
|
||||
|
||||
private:
|
||||
~ScanCompleteCallback() {}
|
||||
|
||||
nsString mTunerId;
|
||||
nsString mSourceType;
|
||||
nsCOMPtr<nsITVSourceListener> mSourceListener;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ScanCompleteCallback, nsITimerCallback)
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::StartScanningChannels(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, nullptr);
|
||||
nsresult rv = NS_DispatchToCurrentThread(runnable);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (IsAllowed(aTunerId, aSourceType)) {
|
||||
rv = mSourceListener->NotifyChannelScanned(aTunerId, aSourceType, mChannels[0]);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set a timer. |notifyEITBroadcasted| will be called after the timer
|
||||
// fires (10ms). (The timer could be canceled if |StopScanningChannels| gets
|
||||
// called before firing.)
|
||||
mEITBroadcastedTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(mEITBroadcastedTimer, NS_ERROR_OUT_OF_MEMORY);
|
||||
RefPtr<EITBroadcastedCallback> eitBroadcastedCb =
|
||||
new EITBroadcastedCallback(aTunerId, aSourceType, mSourceListener, mChannels[0]);
|
||||
rv = mEITBroadcastedTimer->InitWithCallback(eitBroadcastedCb, 10,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set a timer. |notifyChannelScanComplete| will be called after the timer
|
||||
// fires (20ms). (The timer could be canceled if |StopScanningChannels| gets
|
||||
// called before firing.)
|
||||
mScanCompleteTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(mScanCompleteTimer, NS_ERROR_OUT_OF_MEMORY);
|
||||
RefPtr<ScanCompleteCallback> scanCompleteCb =
|
||||
new ScanCompleteCallback(aTunerId, aSourceType, mSourceListener);
|
||||
rv = mScanCompleteTimer->InitWithCallback(scanCompleteCb, 20,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::StopScanningChannels(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (mEITBroadcastedTimer) {
|
||||
mEITBroadcastedTimer->Cancel();
|
||||
mEITBroadcastedTimer = nullptr;
|
||||
}
|
||||
if (mScanCompleteTimer) {
|
||||
mScanCompleteTimer->Cancel();
|
||||
mScanCompleteTimer = nullptr;
|
||||
}
|
||||
nsresult rv = mSourceListener->NotifyChannelScanStopped(aTunerId, aSourceType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, nullptr);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::ClearScannedChannelsCache()
|
||||
{
|
||||
// Fake service doesn't support channel cache, so there's nothing to do here.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::SetChannel(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
const nsAString& aChannelNumber,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMutableArray> channelDataList = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!channelDataList) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (IsAllowed(aTunerId, aSourceType)) {
|
||||
for (uint32_t i = 0; i < mChannels.Length(); i++) {
|
||||
nsString channelNumber;
|
||||
mChannels[i]->GetNumber(channelNumber);
|
||||
if (aChannelNumber.Equals(channelNumber)) {
|
||||
channelDataList->AppendElement(mChannels[i], false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t length;
|
||||
nsresult rv = channelDataList->GetLength(&length);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new TVServiceNotifyRunnable(
|
||||
aCallback,
|
||||
(length == 1) ? channelDataList : nullptr,
|
||||
(length == 1) ? nsITVServiceCallback::TV_ERROR_OK : nsITVServiceCallback::TV_ERROR_FAILURE
|
||||
);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::GetChannels(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMutableArray> channelDataList = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!channelDataList) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (IsAllowed(aTunerId, aSourceType)) {
|
||||
for (uint32_t i = 0; i < mChannels.Length(); i++) {
|
||||
channelDataList->AppendElement(mChannels[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, channelDataList);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::GetPrograms(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType,
|
||||
const nsAString& aChannelNumber,
|
||||
uint64_t startTime,
|
||||
uint64_t endTime,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMutableArray> programDataList = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!programDataList) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// Only return mock programs for the first channel.
|
||||
nsString channelNumber;
|
||||
mChannels[0]->GetNumber(channelNumber);
|
||||
if (IsAllowed(aTunerId, aSourceType) && aChannelNumber.Equals(channelNumber)) {
|
||||
for (uint32_t i = 0; i < mPrograms.Length(); i++) {
|
||||
programDataList->AppendElement(mPrograms[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, programDataList);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/* virtual */ NS_IMETHODIMP
|
||||
FakeTVService::GetOverlayId(const nsAString& aTunerId,
|
||||
nsITVServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMutableArray> overlayIds = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!overlayIds) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// TODO Implement in follow-up patches.
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new TVServiceNotifyRunnable(aCallback, overlayIds);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
bool
|
||||
FakeTVService::IsAllowed(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType)
|
||||
{
|
||||
// Only allow for the first source of the first tuner.
|
||||
nsString tunerId;
|
||||
mTuners[0]->GetId(tunerId);
|
||||
if (!aTunerId.Equals(tunerId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t sourceTypeCount;
|
||||
char** sourceTypes;
|
||||
mTuners[0]->GetSupportedSourceTypes(&sourceTypeCount, &sourceTypes);
|
||||
nsString sourceType;
|
||||
sourceType.AssignASCII(sourceTypes[0]);
|
||||
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(sourceTypeCount, sourceTypes);
|
||||
if (!aSourceType.Equals(sourceType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<nsITVTunerData>
|
||||
FakeTVService::MockTuner(const nsAString& aId,
|
||||
uint32_t aSupportedSourceTypeCount,
|
||||
const char** aSupportedSourceTypes)
|
||||
{
|
||||
nsCOMPtr<nsITVTunerData> tunerData = new TVTunerData();
|
||||
tunerData->SetId(aId);
|
||||
tunerData->SetSupportedSourceTypes(aSupportedSourceTypeCount, aSupportedSourceTypes);
|
||||
return tunerData.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsITVChannelData>
|
||||
FakeTVService::MockChannel(const nsAString& aNetworkId,
|
||||
const nsAString& aTransportStreamId,
|
||||
const nsAString& aServiceId,
|
||||
const nsAString& aType,
|
||||
const nsAString& aNumber,
|
||||
const nsAString& aName,
|
||||
bool aIsEmergency,
|
||||
bool aIsFree)
|
||||
{
|
||||
nsCOMPtr<nsITVChannelData> channelData = new TVChannelData();
|
||||
channelData->SetNetworkId(aNetworkId);
|
||||
channelData->SetTransportStreamId(aTransportStreamId);
|
||||
channelData->SetServiceId(aServiceId);
|
||||
channelData->SetType(aType);
|
||||
channelData->SetNumber(aNumber);
|
||||
channelData->SetName(aName);
|
||||
channelData->SetIsEmergency(aIsEmergency);
|
||||
channelData->SetIsFree(aIsFree);
|
||||
return channelData.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsITVProgramData>
|
||||
FakeTVService::MockProgram(const nsAString& aEventId,
|
||||
const nsAString& aTitle,
|
||||
uint64_t aStartTime,
|
||||
uint64_t aDuration,
|
||||
const nsAString& aDescription,
|
||||
const nsAString& aRating,
|
||||
uint32_t aAudioLanguageCount,
|
||||
const char** aAudioLanguages,
|
||||
uint32_t aSubtitleLanguageCount,
|
||||
const char** aSubtitleLanguages)
|
||||
{
|
||||
nsCOMPtr<nsITVProgramData> programData = new TVProgramData();
|
||||
programData->SetEventId(aEventId);
|
||||
programData->SetTitle(aTitle);
|
||||
programData->SetStartTime(aStartTime);
|
||||
programData->SetDuration(aDuration);
|
||||
programData->SetDescription(aDescription);
|
||||
programData->SetRating(aRating);
|
||||
programData->SetAudioLanguages(aAudioLanguageCount, aAudioLanguages);
|
||||
programData->SetSubtitleLanguages(aSubtitleLanguageCount, aSubtitleLanguages);
|
||||
return programData.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -1,84 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_FakeTVService_h
|
||||
#define mozilla_dom_FakeTVService_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsITVService.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#define FAKE_TV_SERVICE_CONTRACTID \
|
||||
"@mozilla.org/tv/faketvservice;1"
|
||||
#define FAKE_TV_SERVICE_CID \
|
||||
{ 0x60fb3c53, 0x017f, 0x4340, { 0x91, 0x1b, 0xd5, 0x5c, 0x31, 0x28, 0x88, 0xb6 } }
|
||||
|
||||
class nsITimer;
|
||||
class nsITVTunerData;
|
||||
class nsITVChannelData;
|
||||
class nsITVProgramData;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FakeTVService final : public nsITVService
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(FakeTVService)
|
||||
NS_DECL_NSITVSERVICE
|
||||
|
||||
FakeTVService();
|
||||
|
||||
private:
|
||||
~FakeTVService();
|
||||
|
||||
void Init();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
bool IsAllowed(const nsAString& aTunerId,
|
||||
const nsAString& aSourceType);
|
||||
|
||||
already_AddRefed<nsITVTunerData> MockTuner(const nsAString& aId,
|
||||
uint32_t aSupportedSourceTypeCount,
|
||||
const char** aSupportedSourceTypes);
|
||||
|
||||
already_AddRefed<nsITVChannelData> MockChannel(const nsAString& aNetworkId,
|
||||
const nsAString& aTransportStreamId,
|
||||
const nsAString& aServiceId,
|
||||
const nsAString& aType,
|
||||
const nsAString& aNumber,
|
||||
const nsAString& aName,
|
||||
bool aIsEmergency,
|
||||
bool aIsFree);
|
||||
|
||||
already_AddRefed<nsITVProgramData> MockProgram(const nsAString& aEventId,
|
||||
const nsAString& aTitle,
|
||||
uint64_t aStartTime,
|
||||
uint64_t aDuration,
|
||||
const nsAString& aDescription,
|
||||
const nsAString& aRating,
|
||||
uint32_t aAudioLanguageCount,
|
||||
const char** aAudioLanguages,
|
||||
uint32_t aSubtitleLanguageCount,
|
||||
const char** aSubtitleLanguages);
|
||||
|
||||
nsCOMPtr<nsITVSourceListener> mSourceListener;
|
||||
|
||||
// The real implementation may want to use more efficient data structures.
|
||||
nsTArray<nsCOMPtr<nsITVTunerData>> mTuners;
|
||||
nsTArray<nsCOMPtr<nsITVChannelData>> mChannels;
|
||||
nsTArray<nsCOMPtr<nsITVProgramData>> mPrograms;
|
||||
nsCOMPtr<nsITimer> mEITBroadcastedTimer;
|
||||
nsCOMPtr<nsITimer> mScanCompleteTimer;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FakeTVService_h
|
|
@ -4,7 +4,6 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/FakeTVService.h"
|
||||
#include "mozilla/dom/TVListeners.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsITVService.h"
|
||||
|
@ -15,26 +14,14 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/* static */ already_AddRefed<FakeTVService>
|
||||
TVServiceFactory::CreateFakeTVService()
|
||||
{
|
||||
RefPtr<FakeTVService> service = new FakeTVService();
|
||||
return service.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsITVService>
|
||||
TVServiceFactory::AutoCreateTVService()
|
||||
{
|
||||
nsresult rv;
|
||||
nsresult rv = NS_OK;
|
||||
nsCOMPtr<nsITVService> service = do_CreateInstance(TV_SERVICE_CONTRACTID);
|
||||
if (!service) {
|
||||
if (Preferences::GetBool("dom.ignore_webidl_scope_checks", false)) {
|
||||
// Fallback to the fake service.
|
||||
service = do_CreateInstance(FAKE_TV_SERVICE_CONTRACTID, &rv);
|
||||
} else {
|
||||
// Fallback to the TV Simulator Service
|
||||
service = do_CreateInstance(TV_SIMULATOR_SERVICE_CONTRACTID, &rv);
|
||||
}
|
||||
// Fallback to the TV Simulator Service
|
||||
service = do_CreateInstance(TV_SIMULATOR_SERVICE_CONTRACTID, &rv);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
|
|
|
@ -14,13 +14,9 @@ class nsITVService;
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FakeTVService;
|
||||
|
||||
class TVServiceFactory
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<FakeTVService> CreateFakeTVService();
|
||||
|
||||
static already_AddRefed<nsITVService> AutoCreateTVService();
|
||||
};
|
||||
|
||||
|
|
|
@ -13,9 +13,11 @@ const Ci = Components.interfaces;
|
|||
const Cr = Components.returnCode;
|
||||
|
||||
Cu.importGlobalProperties(["File"]);
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const TV_SIMULATOR_DUMMY_DIRECTORY = "dummy";
|
||||
const TV_SIMULATOR_DUMMY_FILE = "settings.json";
|
||||
const TV_SIMULATOR_DUMMY_DIRECTORY = "dummy";
|
||||
const TV_SIMULATOR_DUMMY_FILE = "settings.json";
|
||||
const TV_SIMULATOR_MOCK_DATA = Services.prefs.getCharPref("dom.testing.tv_mock_data");
|
||||
|
||||
// See http://seanyhlin.github.io/TV-Manager-API/#idl-def-TVSourceType
|
||||
const TV_SOURCE_TYPES = ["dvb-t","dvb-t2","dvb-c","dvb-c2","dvb-s",
|
||||
|
@ -49,36 +51,16 @@ TVSimulatorService.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
// Load the setting file from local JSON file.
|
||||
// Synchrhronous File Reading.
|
||||
let file = Cc["@mozilla.org/file/local;1"]
|
||||
.createInstance(Ci.nsILocalFile);
|
||||
|
||||
file.initWithPath(this._getFilePath(TV_SIMULATOR_DUMMY_FILE));
|
||||
|
||||
let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Ci.nsIFileInputStream);
|
||||
let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
|
||||
.createInstance(Ci.nsIConverterInputStream);
|
||||
|
||||
let settingStr = "";
|
||||
|
||||
try {
|
||||
fstream.init(file, -1, 0, 0);
|
||||
cstream.init(fstream,
|
||||
"UTF-8",
|
||||
1024,
|
||||
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
||||
|
||||
let str = {};
|
||||
while (cstream.readString(0xffffffff, str) != 0) {
|
||||
settingStr += str.value;
|
||||
if (TV_SIMULATOR_MOCK_DATA) {
|
||||
settingStr = TV_SIMULATOR_MOCK_DATA;
|
||||
} else {
|
||||
settingStr = this._getDummyData();
|
||||
}
|
||||
} catch(e) {
|
||||
debug("Error occurred : " + e );
|
||||
return;
|
||||
} finally {
|
||||
cstream.close();
|
||||
}
|
||||
|
||||
let settingsObj;
|
||||
|
@ -281,10 +263,11 @@ TVSimulatorService.prototype = {
|
|||
}
|
||||
|
||||
this._scanCompleteTimer = null;
|
||||
let notifyResult = this._sourceListener.notifyChannelScanComplete(
|
||||
this._scanningWrapTunerData.tuner.id,
|
||||
this._scanningWrapTunerData.sourceType);
|
||||
this._scanningWrapTunerData = null;
|
||||
return this._sourceListener.notifyChannelScanComplete(
|
||||
this._scanningWrapTunerData.tuner.id,
|
||||
this._scanningWrapTunerData.sourceType);
|
||||
return notifyResult;
|
||||
},
|
||||
|
||||
stopScanningChannels: function TVSimStopScanningChannels(aTunerId, aSourceType, aCallback) {
|
||||
|
@ -434,6 +417,41 @@ TVSimulatorService.prototype = {
|
|||
return videoBlobURL;
|
||||
},
|
||||
|
||||
_getDummyData : function TVSimGetDummyData() {
|
||||
// Load the setting file from local JSON file.
|
||||
// Synchrhronous File Reading.
|
||||
let file = Cc["@mozilla.org/file/local;1"]
|
||||
.createInstance(Ci.nsILocalFile);
|
||||
|
||||
let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Ci.nsIFileInputStream);
|
||||
let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
|
||||
.createInstance(Ci.nsIConverterInputStream);
|
||||
|
||||
let settingsStr = "";
|
||||
|
||||
try {
|
||||
file.initWithPath(this._getFilePath(TV_SIMULATOR_DUMMY_FILE));
|
||||
fstream.init(file, -1, 0, 0);
|
||||
cstream.init(fstream,
|
||||
"UTF-8",
|
||||
1024,
|
||||
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
||||
|
||||
let str = {};
|
||||
while (cstream.readString(0xffffffff, str) != 0) {
|
||||
settingsStr += str.value;
|
||||
}
|
||||
} catch(e) {
|
||||
debug("Catch the Exception when reading the dummy file:" + e );
|
||||
throw e;
|
||||
} finally {
|
||||
cstream.close();
|
||||
}
|
||||
|
||||
return settingsStr;
|
||||
},
|
||||
|
||||
_getTunerMapKey: function TVSimGetTunerMapKey(aTunerId, aSourceType) {
|
||||
return JSON.stringify({'tunerId': aTunerId, 'sourceType': aSourceType});
|
||||
},
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'FakeTVService.h',
|
||||
'TVChannel.h',
|
||||
'TVListeners.h',
|
||||
'TVManager.h',
|
||||
|
@ -20,7 +19,6 @@ EXPORTS.mozilla.dom += [
|
|||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'FakeTVService.cpp',
|
||||
'TVChannel.cpp',
|
||||
'TVListeners.cpp',
|
||||
'TVManager.cpp',
|
||||
|
|
|
@ -9,8 +9,20 @@ function setupPrefsAndPermissions(callback) {
|
|||
}
|
||||
|
||||
function setupPrefs(callback) {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.tv.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true]]}, function() {
|
||||
let xhr = new XMLHttpRequest;
|
||||
let data;
|
||||
|
||||
xhr.open("GET", "./mock_data.json", false);
|
||||
xhr.send(null);
|
||||
if (xhr.status == 200) {
|
||||
data = xhr.responseText;
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.tv.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true],
|
||||
["dom.testing.tv_mock_data", data]
|
||||
]}, function() {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
[DEFAULT]
|
||||
support-files = head.js
|
||||
skip-if = (os == 'mac' && debug) || asan # Bug 1125477
|
||||
support-files =
|
||||
head.js
|
||||
mock_data.json
|
||||
|
||||
[test_tv_non_permitted_app.html]
|
||||
[test_tv_permitted_app.html]
|
||||
[test_tv_get_tuners.html]
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
{
|
||||
"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": "tv1.ogv",
|
||||
"programs": [
|
||||
{"eventId":"734475972", "title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Morning News", "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": "tv2.ogv",
|
||||
"programs": [
|
||||
{"eventId":"931109607","title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Provide news in morning", "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": "tv1.ogv",
|
||||
"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":"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": "tv2.ogv",
|
||||
"programs": [
|
||||
{"eventId":"931109607","title":"News of Morning", "startTime":"1430686800", "duration":"10800", "description":"Provide news in morning", "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": "tv1.ogv",
|
||||
"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":"533612446", "title":"Entertainment Program", "startTime":"1431270000", "duration":"7200", "description":"Midnight entertainment program", "rating":"0", "audioLanguages":["eng"], "subtitleLanguages":["eng"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -241,7 +241,6 @@ static void Shutdown();
|
|||
#include "nsITelephonyService.h"
|
||||
#include "nsIVoicemailService.h"
|
||||
|
||||
#include "mozilla/dom/FakeTVService.h"
|
||||
#include "mozilla/dom/TVServiceFactory.h"
|
||||
#include "mozilla/dom/TVTypes.h"
|
||||
#include "nsITVService.h"
|
||||
|
@ -389,8 +388,6 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelephonyService,
|
|||
NS_CreateTelephonyService)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIVoicemailService,
|
||||
NS_CreateVoicemailService)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(FakeTVService,
|
||||
TVServiceFactory::CreateFakeTVService)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(TVTunerData)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(TVChannelData)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(TVProgramData)
|
||||
|
@ -850,7 +847,6 @@ NS_DEFINE_NAMED_CID(NS_SYNTHVOICEREGISTRY_CID);
|
|||
#ifdef ACCESSIBILITY
|
||||
NS_DEFINE_NAMED_CID(NS_ACCESSIBILITY_SERVICE_CID);
|
||||
#endif
|
||||
NS_DEFINE_NAMED_CID(FAKE_TV_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(TV_TUNER_DATA_CID);
|
||||
NS_DEFINE_NAMED_CID(TV_CHANNEL_DATA_CID);
|
||||
NS_DEFINE_NAMED_CID(TV_PROGRAM_DATA_CID);
|
||||
|
@ -1154,7 +1150,6 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
|
|||
{ &kTELEPHONY_SERVICE_CID, false, nullptr, nsITelephonyServiceConstructor },
|
||||
{ &kNS_MOBILE_CONNECTION_SERVICE_CID, false, NULL, nsIMobileConnectionServiceConstructor },
|
||||
{ &kNS_VOICEMAIL_SERVICE_CID, false, nullptr, nsIVoicemailServiceConstructor },
|
||||
{ &kFAKE_TV_SERVICE_CID, false, nullptr, FakeTVServiceConstructor },
|
||||
{ &kTV_TUNER_DATA_CID, false, nullptr, TVTunerDataConstructor },
|
||||
{ &kTV_CHANNEL_DATA_CID, false, nullptr, TVChannelDataConstructor },
|
||||
{ &kTV_PROGRAM_DATA_CID, false, nullptr, TVProgramDataConstructor },
|
||||
|
@ -1319,7 +1314,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
|
|||
{ "@mozilla.org/accessibleRetrieval;1", &kNS_ACCESSIBILITY_SERVICE_CID },
|
||||
#endif
|
||||
{ TELEPHONY_SERVICE_CONTRACTID, &kTELEPHONY_SERVICE_CID },
|
||||
{ FAKE_TV_SERVICE_CONTRACTID, &kFAKE_TV_SERVICE_CID },
|
||||
{ TV_TUNER_DATA_CONTRACTID, &kTV_TUNER_DATA_CID },
|
||||
{ TV_CHANNEL_DATA_CONTRACTID, &kTV_CHANNEL_DATA_CID },
|
||||
{ TV_PROGRAM_DATA_CONTRACTID, &kTV_PROGRAM_DATA_CID },
|
||||
|
|
|
@ -472,6 +472,9 @@
|
|||
@BINPATH@/components/PACGenerator.js
|
||||
@BINPATH@/components/PACGenerator.manifest
|
||||
|
||||
@BINPATH@/components/TVSimulatorService.js
|
||||
@BINPATH@/components/TVSimulatorService.manifest
|
||||
|
||||
; Modules
|
||||
@BINPATH@/modules/*
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче