mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to fx-team
This commit is contained in:
commit
a38f023bca
@ -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));
|
||||
},
|
||||
|
||||
|
765
b2g/components/PersistentDataBlock.jsm
Normal file
765
b2g/components/PersistentDataBlock.jsm
Normal file
@ -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'
|
||||
|
412
b2g/components/test/unit/file_persistentdatablock.js
Normal file
412
b2g/components/test/unit/file_persistentdatablock.js
Normal file
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
21
b2g/components/test/unit/test_persistentdatablock_gonk.js
Normal file
21
b2g/components/test/unit/test_persistentdatablock_gonk.js
Normal file
@ -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"/>
|
||||
|
@ -4752,7 +4752,7 @@ var CombinedStopReload = {
|
||||
var TabsProgressListener = {
|
||||
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
// Collect telemetry data about tab load times.
|
||||
if (aWebProgress.isTopLevel) {
|
||||
if (aWebProgress.isTopLevel && (!aRequest.originalURI || aRequest.originalURI.spec.scheme != "about")) {
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_START) {
|
||||
TelemetryStopwatch.start("FX_PAGE_LOAD_MS", aBrowser);
|
||||
|
@ -5,10 +5,10 @@ support-files =
|
||||
[browser_displayURI.js]
|
||||
skip-if = (os == "linux" && (debug || asan))
|
||||
[browser_popupNotification.js]
|
||||
skip-if = (os == "linux" && (debug || asan)) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
|
||||
skip-if = (os == "linux" && (debug || asan))
|
||||
[browser_popupNotification_2.js]
|
||||
skip-if = (os == "linux" && (debug || asan)) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
|
||||
skip-if = (os == "linux" && (debug || asan))
|
||||
[browser_popupNotification_3.js]
|
||||
skip-if = (os == "linux" && (debug || asan)) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
|
||||
skip-if = (os == "linux" && (debug || asan))
|
||||
[browser_popupNotification_4.js]
|
||||
skip-if = (os == "linux" && (debug || asan)) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
|
||||
skip-if = (os == "linux" && (debug || asan))
|
||||
|
@ -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
|
||||
|
@ -535,7 +535,7 @@ this.BrowserUITelemetry = {
|
||||
let paletteItems =
|
||||
CustomizableUI.getUnusedWidgets(aWindow.gNavToolbox.palette);
|
||||
let defaultRemoved = [];
|
||||
for (item of paletteItems) {
|
||||
for (let item of paletteItems) {
|
||||
if (DEFAULT_ITEMS.indexOf(item.id) != -1) {
|
||||
defaultRemoved.push(item.id);
|
||||
}
|
||||
|
6
config/external/moz.build
vendored
6
config/external/moz.build
vendored
@ -26,13 +26,10 @@ if CONFIG['MOZ_VORBIS']:
|
||||
if CONFIG['MOZ_TREMOR']:
|
||||
external_dirs += ['media/libtremor']
|
||||
|
||||
if CONFIG['MOZ_WEBM']:
|
||||
external_dirs += ['media/libnestegg']
|
||||
|
||||
if CONFIG['MOZ_WEBM_ENCODER']:
|
||||
external_dirs += ['media/libmkv']
|
||||
|
||||
if CONFIG['MOZ_VPX'] and not CONFIG['MOZ_NATIVE_LIBVPX']:
|
||||
if not CONFIG['MOZ_NATIVE_LIBVPX']:
|
||||
external_dirs += ['media/libvpx']
|
||||
|
||||
if not CONFIG['MOZ_NATIVE_PNG']:
|
||||
@ -50,6 +47,7 @@ if CONFIG['MOZ_WEBSPEECH_POCKETSPHINX']:
|
||||
external_dirs += [
|
||||
'media/kiss_fft',
|
||||
'media/libcubeb',
|
||||
'media/libnestegg',
|
||||
'media/libogg',
|
||||
'media/libopus',
|
||||
'media/libtheora',
|
||||
|
115
configure.in
115
configure.in
@ -92,8 +92,8 @@ _PTHREAD_LDFLAGS=""
|
||||
|
||||
dnl Do not allow objdir == srcdir builds.
|
||||
dnl ==============================================================
|
||||
_topsrcdir=`cd \`dirname $0\`; pwd -W 2>/dev/null || pwd`
|
||||
_objdir=`pwd`
|
||||
_topsrcdir=`cd \`dirname $0\`; pwd -W 2>/dev/null || pwd -P`
|
||||
_objdir=`pwd -P`
|
||||
|
||||
dnl TODO Don't exempt L10N builds once bug 842760 is resolved.
|
||||
if test "$_topsrcdir" = "$_objdir" -a "${with_l10n_base+set}" != set; then
|
||||
@ -134,7 +134,7 @@ EOF
|
||||
exit 1
|
||||
break
|
||||
fi
|
||||
MOZ_BUILD_ROOT=`pwd -W 2>/dev/null || pwd`
|
||||
MOZ_BUILD_ROOT=`pwd -W 2>/dev/null || pwd -P`
|
||||
DIST="$MOZ_BUILD_ROOT/dist"
|
||||
|
||||
MOZ_PYTHON
|
||||
@ -157,7 +157,7 @@ if test -n "$L10NBASEDIR"; then
|
||||
if test "$L10NBASEDIR" = "yes" -o "$L10NBASEDIR" = "no"; then
|
||||
AC_MSG_ERROR([--with-l10n-base must specify a path])
|
||||
elif test -d "$L10NBASEDIR"; then
|
||||
L10NBASEDIR=`cd "$L10NBASEDIR" && pwd`
|
||||
L10NBASEDIR=`cd "$L10NBASEDIR" && pwd -P`
|
||||
else
|
||||
AC_MSG_ERROR([Invalid value --with-l10n-base, $L10NBASEDIR doesn't exist])
|
||||
fi
|
||||
@ -3692,10 +3692,8 @@ fi
|
||||
MOZ_RAW=
|
||||
MOZ_VORBIS=
|
||||
MOZ_TREMOR=
|
||||
MOZ_WAVE=1
|
||||
MOZ_SAMPLE_TYPE_FLOAT32=
|
||||
MOZ_SAMPLE_TYPE_S16=
|
||||
MOZ_WEBM=1
|
||||
MOZ_GSTREAMER=
|
||||
MOZ_DIRECTSHOW=
|
||||
MOZ_WMF=
|
||||
@ -3715,7 +3713,6 @@ MOZ_SCTP=
|
||||
MOZ_ANDROID_OMX=
|
||||
MOZ_MEDIA_NAVIGATOR=
|
||||
MOZ_OMX_PLUGIN=
|
||||
MOZ_VPX=
|
||||
MOZ_VPX_ERROR_CONCEALMENT=
|
||||
MOZ_WEBSPEECH=1
|
||||
MOZ_WEBSPEECH_MODELS=
|
||||
@ -4998,7 +4995,6 @@ if test -n "$MOZ_WEBRTC"; then
|
||||
dnl opt/production builds (via MOZ_CRASH())
|
||||
AC_DEFINE(MOZ_WEBRTC_ASSERT_ALWAYS)
|
||||
MOZ_RAW=1
|
||||
MOZ_VPX=1
|
||||
MOZ_VPX_ERROR_CONCEALMENT=1
|
||||
|
||||
dnl enable once Signaling lands
|
||||
@ -5143,19 +5139,6 @@ if test "${ac_cv_c_attribute_aligned}" != "0"; then
|
||||
[${ac_cv_c_attribute_aligned}],[Maximum supported data alignment])
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Disable VP8 decoder support
|
||||
dnl ========================================================
|
||||
MOZ_ARG_DISABLE_BOOL(webm,
|
||||
[ --disable-webm Disable support for WebM media (VP8 video and Vorbis audio)],
|
||||
MOZ_WEBM=,
|
||||
MOZ_WEBM=1)
|
||||
|
||||
if test -n "$MOZ_WEBM"; then
|
||||
AC_DEFINE(MOZ_WEBM)
|
||||
MOZ_VPX=1
|
||||
fi;
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Apple platform decoder support
|
||||
dnl ========================================================
|
||||
@ -5361,54 +5344,49 @@ MOZ_ARG_WITH_BOOL(system-libvpx,
|
||||
MOZ_LIBVPX_CFLAGS=
|
||||
MOZ_LIBVPX_LIBS=
|
||||
|
||||
if test -n "$MOZ_VPX"; then
|
||||
AC_DEFINE(MOZ_VPX)
|
||||
if test -n "$MOZ_VPX_ERROR_CONCEALMENT" ; then
|
||||
AC_DEFINE(MOZ_VPX_ERROR_CONCEALMENT)
|
||||
fi
|
||||
|
||||
_SAVE_CFLAGS=$CFLAGS
|
||||
_SAVE_LIBS=$LIBS
|
||||
if test -n "$MOZ_NATIVE_LIBVPX"; then
|
||||
dnl ============================
|
||||
dnl === libvpx Version check ===
|
||||
dnl ============================
|
||||
dnl Check to see if we have a system libvpx package.
|
||||
PKG_CHECK_MODULES(MOZ_LIBVPX, vpx >= 1.3.0)
|
||||
|
||||
CFLAGS="$CFLAGS $MOZ_LIBVPX_CFLAGS"
|
||||
LIBS="$LIBS $MOZ_LIBVPX_LIBS"
|
||||
|
||||
MOZ_CHECK_HEADER([vpx/vpx_decoder.h], [],
|
||||
[AC_MSG_ERROR([Couldn't find vpx/vpx_decoder.h which is required for build with system libvpx. Use --without-system-libvpx to build with in-tree libvpx.])])
|
||||
|
||||
AC_CHECK_LIB(vpx, vpx_codec_dec_init_ver, [],
|
||||
[AC_MSG_ERROR([--with-system-libvpx requested but symbol vpx_codec_dec_init_ver not found])])
|
||||
|
||||
MOZ_CHECK_HEADER([vpx_mem/vpx_mem.h],
|
||||
[AC_CHECK_FUNC(vpx_mem_set_functions)])
|
||||
if test "$ac_cv_header_vpx_mem_vpx_mem_h" = no -o \
|
||||
"$ac_cv_func_vpx_mem_set_functions" = no; then
|
||||
AC_DEFINE(MOZ_VPX_NO_MEM_REPORTING)
|
||||
fi
|
||||
fi
|
||||
CFLAGS=$_SAVE_CFLAGS
|
||||
LIBS=$_SAVE_LIBS
|
||||
if test -n "$MOZ_VPX_ERROR_CONCEALMENT" ; then
|
||||
AC_DEFINE(MOZ_VPX_ERROR_CONCEALMENT)
|
||||
fi
|
||||
|
||||
_SAVE_CFLAGS=$CFLAGS
|
||||
_SAVE_LIBS=$LIBS
|
||||
if test -n "$MOZ_NATIVE_LIBVPX"; then
|
||||
dnl ============================
|
||||
dnl === libvpx Version check ===
|
||||
dnl ============================
|
||||
dnl Check to see if we have a system libvpx package.
|
||||
PKG_CHECK_MODULES(MOZ_LIBVPX, vpx >= 1.3.0)
|
||||
|
||||
CFLAGS="$CFLAGS $MOZ_LIBVPX_CFLAGS"
|
||||
LIBS="$LIBS $MOZ_LIBVPX_LIBS"
|
||||
|
||||
MOZ_CHECK_HEADER([vpx/vpx_decoder.h], [],
|
||||
[AC_MSG_ERROR([Couldn't find vpx/vpx_decoder.h which is required for build with system libvpx. Use --without-system-libvpx to build with in-tree libvpx.])])
|
||||
|
||||
AC_CHECK_LIB(vpx, vpx_codec_dec_init_ver, [],
|
||||
[AC_MSG_ERROR([--with-system-libvpx requested but symbol vpx_codec_dec_init_ver not found])])
|
||||
|
||||
MOZ_CHECK_HEADER([vpx_mem/vpx_mem.h],
|
||||
[AC_CHECK_FUNC(vpx_mem_set_functions)])
|
||||
if test "$ac_cv_header_vpx_mem_vpx_mem_h" = no -o \
|
||||
"$ac_cv_func_vpx_mem_set_functions" = no; then
|
||||
AC_DEFINE(MOZ_VPX_NO_MEM_REPORTING)
|
||||
fi
|
||||
fi
|
||||
CFLAGS=$_SAVE_CFLAGS
|
||||
LIBS=$_SAVE_LIBS
|
||||
|
||||
AC_SUBST(MOZ_NATIVE_LIBVPX)
|
||||
AC_SUBST_LIST(MOZ_LIBVPX_CFLAGS)
|
||||
AC_SUBST_LIST(MOZ_LIBVPX_LIBS)
|
||||
|
||||
if test "$MOZ_WEBM"; then
|
||||
if test "$MOZ_SAMPLE_TYPE_FLOAT32"; then
|
||||
MOZ_VORBIS=1
|
||||
else
|
||||
MOZ_TREMOR=1
|
||||
fi
|
||||
if test "$MOZ_SAMPLE_TYPE_FLOAT32"; then
|
||||
MOZ_VORBIS=1
|
||||
else
|
||||
MOZ_TREMOR=1
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_VPX" -a -z "$MOZ_NATIVE_LIBVPX"; then
|
||||
if test -z "$MOZ_NATIVE_LIBVPX"; then
|
||||
|
||||
dnl Detect if we can use an assembler to compile optimized assembly for libvpx.
|
||||
dnl We currently require yasm on all x86 platforms and require yasm 1.1.0 on Win32.
|
||||
@ -5510,18 +5488,6 @@ if test -n "$MOZ_VPX" -a -z "$MOZ_NATIVE_LIBVPX"; then
|
||||
AC_DEFINE(MOZ_VPX_NO_MEM_REPORTING)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Disable Wave decoder support
|
||||
dnl ========================================================
|
||||
MOZ_ARG_DISABLE_BOOL(wave,
|
||||
[ --disable-wave Disable Wave decoder support],
|
||||
MOZ_WAVE=,
|
||||
MOZ_WAVE=1)
|
||||
|
||||
if test -n "$MOZ_WAVE"; then
|
||||
AC_DEFINE(MOZ_WAVE)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Handle dependent MEDIA defines
|
||||
dnl ========================================================
|
||||
@ -8926,10 +8892,8 @@ AC_SUBST(NS_ENABLE_TSF)
|
||||
AC_SUBST(WIN32_CONSOLE_EXE_LDFLAGS)
|
||||
AC_SUBST(WIN32_GUI_EXE_LDFLAGS)
|
||||
|
||||
AC_SUBST(MOZ_WAVE)
|
||||
AC_SUBST(MOZ_VORBIS)
|
||||
AC_SUBST(MOZ_TREMOR)
|
||||
AC_SUBST(MOZ_WEBM)
|
||||
AC_SUBST(MOZ_WMF)
|
||||
AC_SUBST(MOZ_FFMPEG)
|
||||
AC_SUBST(MOZ_FMP4)
|
||||
@ -8939,7 +8903,6 @@ AC_SUBST(MOZ_ANDROID_OMX)
|
||||
AC_SUBST(MOZ_APPLEMEDIA)
|
||||
AC_SUBST(MOZ_OMX_PLUGIN)
|
||||
AC_SUBST(MOZ_VPX_ERROR_CONCEALMENT)
|
||||
AC_SUBST(MOZ_VPX)
|
||||
AC_SUBST(VPX_AS)
|
||||
AC_SUBST_LIST(VPX_ASFLAGS)
|
||||
AC_SUBST(VPX_AS_CONVERSION)
|
||||
|
@ -12,7 +12,7 @@ const { ActorPool, OriginalLocation, GeneratedLocation } = require("devtools/ser
|
||||
const { ObjectActor, createValueGrip, longStringGrip } = require("devtools/server/actors/object");
|
||||
const { DebuggerServer } = require("devtools/server/main");
|
||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
const { assert, dbg_assert, dumpn, update, fetch } = DevToolsUtils;
|
||||
const { assert, dumpn, update, fetch } = DevToolsUtils;
|
||||
const { dirname, joinURI } = require("devtools/shared/path");
|
||||
const promise = require("promise");
|
||||
const PromiseDebugging = require("PromiseDebugging");
|
||||
@ -354,8 +354,6 @@ EventLoop.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
dbg_assert(this._thread.state === "running", "Should be in the running state");
|
||||
|
||||
if (this._hooks.postNest) {
|
||||
this._hooks.postNest(nestData);
|
||||
}
|
||||
|
@ -183,8 +183,12 @@ exports.executeSoon = function executeSoon(aFn) {
|
||||
if (isWorker) {
|
||||
setImmediate(aFn);
|
||||
} else {
|
||||
let stack = components.stack;
|
||||
let executor = () => {
|
||||
Cu.callFunctionWithAsyncStack(aFn, stack, "DevToolsUtils.executeSoon");
|
||||
};
|
||||
Services.tm.mainThread.dispatch({
|
||||
run: exports.makeInfallible(aFn)
|
||||
run: exports.makeInfallible(executor)
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
};
|
||||
@ -457,24 +461,6 @@ exports.defineLazyGetter = function defineLazyGetter(aObject, aName, aLambda) {
|
||||
});
|
||||
};
|
||||
|
||||
// DEPRECATED: use DevToolsUtils.assert(condition, message) instead!
|
||||
let haveLoggedDeprecationMessage = false;
|
||||
exports.dbg_assert = function dbg_assert(cond, e) {
|
||||
if (!haveLoggedDeprecationMessage) {
|
||||
haveLoggedDeprecationMessage = true;
|
||||
const deprecationMessage = "DevToolsUtils.dbg_assert is deprecated! Use DevToolsUtils.assert instead!\n"
|
||||
+ Error().stack;
|
||||
dump(deprecationMessage);
|
||||
if (typeof console === "object" && console && console.warn) {
|
||||
console.warn(deprecationMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (!cond) {
|
||||
return e;
|
||||
}
|
||||
};
|
||||
|
||||
exports.defineLazyGetter(this, "AppConstants", () => {
|
||||
if (isWorker) {
|
||||
return {};
|
||||
|
47
devtools/shared/tests/unit/test_executeSoon.js
Normal file
47
devtools/shared/tests/unit/test_executeSoon.js
Normal file
@ -0,0 +1,47 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Client request stacks should span the entire process from before making the
|
||||
* request to handling the reply from the server. The server frames are not
|
||||
* included, nor can they be in most cases, since the server can be a remote
|
||||
* device.
|
||||
*/
|
||||
|
||||
var { executeSoon } = require("devtools/shared/DevToolsUtils");
|
||||
var promise = require("promise");
|
||||
var Services = require("Services");
|
||||
|
||||
var asyncStackEnabled =
|
||||
Services.prefs.getBoolPref("javascript.options.asyncstack");
|
||||
|
||||
do_register_cleanup(() => {
|
||||
Services.prefs.setBoolPref("javascript.options.asyncstack",
|
||||
asyncStackEnabled);
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
Services.prefs.setBoolPref("javascript.options.asyncstack", true);
|
||||
|
||||
yield waitForTick();
|
||||
|
||||
let stack = Components.stack;
|
||||
while (stack) {
|
||||
do_print(stack.name);
|
||||
if (stack.name == "waitForTick") {
|
||||
// Reached back to outer function before executeSoon
|
||||
ok(true, "Complete stack");
|
||||
return;
|
||||
}
|
||||
stack = stack.asyncCaller || stack.caller;
|
||||
}
|
||||
ok(false, "Incomplete stack");
|
||||
});
|
||||
|
||||
function waitForTick() {
|
||||
let deferred = promise.defer();
|
||||
executeSoon(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
@ -24,3 +24,4 @@ support-files =
|
||||
[test_require_lazy.js]
|
||||
[test_require.js]
|
||||
[test_stack.js]
|
||||
[test_executeSoon.js]
|
||||
|
@ -110,6 +110,8 @@ void
|
||||
DocumentTimeline::WillRefresh(mozilla::TimeStamp aTime)
|
||||
{
|
||||
MOZ_ASSERT(mIsObservingRefreshDriver);
|
||||
MOZ_ASSERT(GetRefreshDriver(),
|
||||
"Should be able to reach refresh driver from within WillRefresh");
|
||||
|
||||
bool needsTicks = false;
|
||||
nsTArray<Animation*> animationsToRemove(mAnimations.Count());
|
||||
@ -143,10 +145,13 @@ DocumentTimeline::WillRefresh(mozilla::TimeStamp aTime)
|
||||
}
|
||||
|
||||
if (!needsTicks) {
|
||||
// If another refresh driver observer destroys the nsPresContext,
|
||||
// nsRefreshDriver will detect it and we won't be called.
|
||||
// We already assert that GetRefreshDriver() is non-null at the beginning
|
||||
// of this function but we check it again here to be sure that ticking
|
||||
// animations does not have any side effects that cause us to lose the
|
||||
// connection with the refresh driver, such as triggering the destruction
|
||||
// of mDocument's PresShell.
|
||||
MOZ_ASSERT(GetRefreshDriver(),
|
||||
"Refresh driver should still be valid inside WillRefresh");
|
||||
"Refresh driver should still be valid at end of WillRefresh");
|
||||
GetRefreshDriver()->RemoveRefreshObserver(this, Flush_Style);
|
||||
mIsObservingRefreshDriver = false;
|
||||
}
|
||||
|
@ -96,7 +96,6 @@ KeyframeEffectReadOnly::KeyframeEffectReadOnly(
|
||||
, mPseudoType(aPseudoType)
|
||||
{
|
||||
MOZ_ASSERT(aTarget, "null animation target is not yet supported");
|
||||
ResetIsRunningOnCompositor();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@ -466,19 +465,6 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
KeyframeEffectReadOnly::IsPropertyRunningOnCompositor(
|
||||
nsCSSProperty aProperty) const
|
||||
{
|
||||
const auto& info = LayerAnimationInfo::sRecords;
|
||||
for (size_t i = 0; i < ArrayLength(mIsPropertyRunningOnCompositor); i++) {
|
||||
if (info[i].mProperty == aProperty) {
|
||||
return mIsPropertyRunningOnCompositor[i];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
KeyframeEffectReadOnly::IsRunningOnCompositor() const
|
||||
{
|
||||
@ -486,8 +472,8 @@ KeyframeEffectReadOnly::IsRunningOnCompositor() const
|
||||
// one property running on compositor.
|
||||
// Animation.IsRunningOnCompotitor will return more fine grained
|
||||
// information in bug 1196114.
|
||||
for (bool isPropertyRunningOnCompositor : mIsPropertyRunningOnCompositor) {
|
||||
if (isPropertyRunningOnCompositor) {
|
||||
for (const AnimationProperty& property : mProperties) {
|
||||
if (property.mIsRunningOnCompositor) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -498,19 +484,13 @@ void
|
||||
KeyframeEffectReadOnly::SetIsRunningOnCompositor(nsCSSProperty aProperty,
|
||||
bool aIsRunning)
|
||||
{
|
||||
static_assert(
|
||||
MOZ_ARRAY_LENGTH(LayerAnimationInfo::sRecords) ==
|
||||
MOZ_ARRAY_LENGTH(mIsPropertyRunningOnCompositor),
|
||||
"The length of mIsPropertyRunningOnCompositor should equal to"
|
||||
"the length of LayserAnimationInfo::sRecords");
|
||||
MOZ_ASSERT(nsCSSProps::PropHasFlags(aProperty,
|
||||
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),
|
||||
"Property being animated on compositor is a recognized "
|
||||
"compositor-animatable property");
|
||||
const auto& info = LayerAnimationInfo::sRecords;
|
||||
for (size_t i = 0; i < ArrayLength(mIsPropertyRunningOnCompositor); i++) {
|
||||
if (info[i].mProperty == aProperty) {
|
||||
mIsPropertyRunningOnCompositor[i] = aIsRunning;
|
||||
for (AnimationProperty& property : mProperties) {
|
||||
if (property.mProperty == aProperty) {
|
||||
property.mIsRunningOnCompositor = aIsRunning;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -523,8 +503,8 @@ KeyframeEffectReadOnly::~KeyframeEffectReadOnly()
|
||||
void
|
||||
KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
|
||||
{
|
||||
for (bool& isPropertyRunningOnCompositor : mIsPropertyRunningOnCompositor) {
|
||||
isPropertyRunningOnCompositor = false;
|
||||
for (AnimationProperty& property : mProperties) {
|
||||
property.mIsRunningOnCompositor = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -556,9 +536,7 @@ KeyframeEffectReadOnly::UpdateTargetRegistration()
|
||||
// Any effects not in the effect set will not be included in the set of
|
||||
// candidate effects for running on the compositor and hence they won't
|
||||
// have their compositor status updated so we should do that now.
|
||||
for (bool& isRunningOnCompositor : mIsPropertyRunningOnCompositor) {
|
||||
isRunningOnCompositor = false;
|
||||
}
|
||||
ResetIsRunningOnCompositor();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1822,7 +1800,7 @@ KeyframeEffectReadOnly::CanThrottle() const
|
||||
}
|
||||
|
||||
// First we need to check layer generation and transform overflow
|
||||
// prior to the IsPropertyRunningOnCompositor check because we should
|
||||
// prior to the property.mIsRunningOnCompositor check because we should
|
||||
// occasionally unthrottle these animations even if the animations are
|
||||
// already running on compositor.
|
||||
for (const LayerAnimationInfo::Record& record :
|
||||
@ -1854,7 +1832,7 @@ KeyframeEffectReadOnly::CanThrottle() const
|
||||
}
|
||||
|
||||
for (const AnimationProperty& property : mProperties) {
|
||||
if (!IsPropertyRunningOnCompositor(property.mProperty)) {
|
||||
if (!property.mIsRunningOnCompositor) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ struct AnimationPropertySegment
|
||||
|
||||
struct AnimationProperty
|
||||
{
|
||||
nsCSSProperty mProperty;
|
||||
nsCSSProperty mProperty = eCSSProperty_UNKNOWN;
|
||||
|
||||
// Does this property win in the CSS Cascade?
|
||||
//
|
||||
@ -136,16 +136,26 @@ struct AnimationProperty
|
||||
// For other properties, we make it always be true.
|
||||
// **NOTE 2**: This member is not included when comparing AnimationProperty
|
||||
// objects for equality.
|
||||
bool mWinsInCascade;
|
||||
bool mWinsInCascade = true;
|
||||
|
||||
// If true, the propery is currently being animated on the compositor.
|
||||
//
|
||||
// Note that when the owning Animation requests a non-throttled restyle, in
|
||||
// between calling RequestRestyle on its AnimationCollection and when the
|
||||
// restyle is performed, this member may temporarily become false even if
|
||||
// the animation remains on the layer after the restyle.
|
||||
bool mIsRunningOnCompositor = false;
|
||||
|
||||
InfallibleTArray<AnimationPropertySegment> mSegments;
|
||||
|
||||
// NOTE: This operator does *not* compare the mWinsInCascade member.
|
||||
// NOTE: This operator does *not* compare the mWinsInCascade member *or* the
|
||||
// mIsRunningOnCompositor member.
|
||||
// This is because AnimationProperty objects are compared when recreating
|
||||
// CSS animations to determine if mutation observer change records need to
|
||||
// be created or not. However, at the point when these objects are compared
|
||||
// the mWinsInCascade will not have been set on the new objects so we ignore
|
||||
// this member to avoid generating spurious change records.
|
||||
// neither the mWinsInCascade nor the mIsRunningOnCompositor will have been
|
||||
// set on the new objects so we ignore these members to avoid generating
|
||||
// spurious change records.
|
||||
bool operator==(const AnimationProperty& aOther) const {
|
||||
return mProperty == aOther.mProperty &&
|
||||
mSegments == aOther.mSegments;
|
||||
@ -279,8 +289,6 @@ public:
|
||||
// Any updated properties are added to |aSetProperties|.
|
||||
void ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
||||
nsCSSPropertySet& aSetProperties);
|
||||
// Returns true if |aProperty| is currently being animated on compositor.
|
||||
bool IsPropertyRunningOnCompositor(nsCSSProperty aProperty) const;
|
||||
// Returns true if at least one property is being animated on compositor.
|
||||
bool IsRunningOnCompositor() const;
|
||||
void SetIsRunningOnCompositor(nsCSSProperty aProperty, bool aIsRunning);
|
||||
@ -341,17 +349,6 @@ protected:
|
||||
|
||||
InfallibleTArray<AnimationProperty> mProperties;
|
||||
|
||||
// Parallel array corresponding to CommonAnimationManager::sLayerAnimationInfo
|
||||
// such that mIsPropertyRunningOnCompositor[x] is true only if this effect has
|
||||
// an animation of CommonAnimationManager::sLayerAnimationInfo[x].mProperty
|
||||
// that is currently running on the compositor.
|
||||
//
|
||||
// Note that when the owning Animation requests a non-throttled restyle, in
|
||||
// between calling RequestRestyle on its AnimationCollection and when the
|
||||
// restyle is performed, this member may temporarily become false even if
|
||||
// the animation remains on the layer after the restyle.
|
||||
bool mIsPropertyRunningOnCompositor[LayerAnimationInfo::kRecords];
|
||||
|
||||
private:
|
||||
nsIFrame* GetAnimationFrame() const;
|
||||
|
||||
|
@ -207,6 +207,9 @@ promise_test(function(t) {
|
||||
resolve();
|
||||
}));
|
||||
observer.observe(div, { animations: true, subtree: false });
|
||||
t.add_cleanup(function() {
|
||||
observer.disconnect();
|
||||
});
|
||||
div.style.animationDuration = "200s";
|
||||
}));
|
||||
}));
|
||||
|
@ -2117,7 +2117,15 @@ nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
|
||||
// Note that, since mTiming does not change during a reset, the
|
||||
// navigationStart time remains unchanged and therefore any future new
|
||||
// timeline will have the same global clock time as the old one.
|
||||
mDocumentTimeline = nullptr;
|
||||
if (mDocumentTimeline) {
|
||||
nsRefreshDriver* rd = mPresShell && mPresShell->GetPresContext() ?
|
||||
mPresShell->GetPresContext()->RefreshDriver() :
|
||||
nullptr;
|
||||
if (rd) {
|
||||
mDocumentTimeline->NotifyRefreshDriverDestroying(rd);
|
||||
}
|
||||
mDocumentTimeline = nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
|
||||
if (bag) {
|
||||
|
@ -64,6 +64,7 @@ skip-if = debug == false
|
||||
[test_worker_UnwrapArg.html]
|
||||
[test_unforgeablesonexpando.html]
|
||||
[test_crossOriginWindowSymbolAccess.html]
|
||||
[test_primitive_this.html]
|
||||
[test_callback_exceptions.html]
|
||||
[test_bug1123516_maplikesetlike.html]
|
||||
skip-if = debug == false
|
||||
|
45
dom/bindings/test/test_primitive_this.html
Normal file
45
dom/bindings/test/test_primitive_this.html
Normal file
@ -0,0 +1,45 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=603201
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 603201</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 603201 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
function runTest()
|
||||
{
|
||||
var nodes = document.body.childNodes;
|
||||
|
||||
Object.setPrototypeOf(Number.prototype, nodes);
|
||||
|
||||
Object.defineProperty(nodes, "getter", {get: function() {
|
||||
"use strict";
|
||||
is(this, 1);
|
||||
return "getter";
|
||||
}});
|
||||
Object.defineProperty(Object.getPrototypeOf(nodes), "getter2", {get: function() {
|
||||
"use strict";
|
||||
is(this, 1);
|
||||
return "getter2";
|
||||
}});
|
||||
|
||||
var number = 1;
|
||||
is(number.getter, "getter");
|
||||
is(number.getter2, "getter2");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="runTest();">
|
||||
<pre>Test</pre>
|
||||
</body>
|
||||
</html>
|
@ -25,11 +25,8 @@ NS_IMPL_NS_NEW_HTML_ELEMENT(Audio)
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
extern bool IsAudioAPIEnabled();
|
||||
|
||||
NS_IMPL_ELEMENT_CLONE(HTMLAudioElement)
|
||||
|
||||
|
||||
HTMLAudioElement::HTMLAudioElement(already_AddRefed<NodeInfo>& aNodeInfo)
|
||||
: HTMLMediaElement(aNodeInfo)
|
||||
{
|
||||
@ -79,13 +76,9 @@ HTMLAudioElement::Audio(const GlobalObject& aGlobal,
|
||||
nsresult HTMLAudioElement::SetAcceptHeader(nsIHttpChannel* aChannel)
|
||||
{
|
||||
nsAutoCString value(
|
||||
#ifdef MOZ_WEBM
|
||||
"audio/webm,"
|
||||
#endif
|
||||
"audio/ogg,"
|
||||
#ifdef MOZ_WAVE
|
||||
"audio/wav,"
|
||||
#endif
|
||||
"audio/*;q=0.9,"
|
||||
"application/ogg;q=0.7,"
|
||||
"video/*;q=0.6,*/*;q=0.5");
|
||||
|
@ -936,6 +936,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLInputElement,
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesAndDirectoriesPromise)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement,
|
||||
@ -944,6 +945,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement,
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesAndDirectoriesPromise)
|
||||
if (tmp->IsSingleLineTextControl(false)) {
|
||||
tmp->mInputData.mState->Unlink();
|
||||
}
|
||||
|
@ -113,9 +113,7 @@ HTMLVideoElement::GetAttributeMappingFunction() const
|
||||
nsresult HTMLVideoElement::SetAcceptHeader(nsIHttpChannel* aChannel)
|
||||
{
|
||||
nsAutoCString value(
|
||||
#ifdef MOZ_WEBM
|
||||
"video/webm,"
|
||||
#endif
|
||||
"video/ogg,"
|
||||
"video/*;q=0.9,"
|
||||
"application/ogg;q=0.7,"
|
||||
|
@ -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;
|
||||
|
@ -458,8 +458,31 @@ static void LogChannelRelevantInfo(nsIURI* aURI,
|
||||
LOG("Result principal origin: %s\n", resultPrincipalOrigin.get());
|
||||
}
|
||||
|
||||
// This is similar to nsIScriptSecurityManager.getChannelResultPrincipal
|
||||
// but taking signedPkg into account. The reason we can't rely on channel
|
||||
// loadContext/loadInfo is it's dangerous to mutate them on parent process.
|
||||
static already_AddRefed<nsIPrincipal>
|
||||
GetChannelPrincipalWithSingedPkg(nsIChannel* aChannel, const nsACString& aSignedPkg)
|
||||
{
|
||||
NeckoOriginAttributes neckoAttrs;
|
||||
NS_GetOriginAttributes(aChannel, neckoAttrs);
|
||||
|
||||
PrincipalOriginAttributes attrs;
|
||||
attrs.InheritFromNecko(neckoAttrs);
|
||||
attrs.mSignedPkg = NS_ConvertUTF8toUTF16(aSignedPkg);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal =
|
||||
BasePrincipal::CreateCodebasePrincipal(uri, attrs);
|
||||
|
||||
return principal.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::ShouldSwitchProcess(nsIChannel* aChannel)
|
||||
TabParent::ShouldSwitchProcess(nsIChannel* aChannel, const nsACString& aSignedPkg)
|
||||
{
|
||||
// If we lack of any information which is required to decide the need of
|
||||
// process switch, consider that we should switch process.
|
||||
@ -473,19 +496,18 @@ TabParent::ShouldSwitchProcess(nsIChannel* aChannel)
|
||||
NS_ENSURE_TRUE(loadingPrincipal, true);
|
||||
|
||||
// Prepare the channel result principal.
|
||||
nsCOMPtr<nsIPrincipal> resultPrincipal;
|
||||
nsContentUtils::GetSecurityManager()->
|
||||
GetChannelResultPrincipal(aChannel, getter_AddRefs(resultPrincipal));
|
||||
nsCOMPtr<nsIPrincipal> channelPrincipal =
|
||||
GetChannelPrincipalWithSingedPkg(aChannel, aSignedPkg);
|
||||
|
||||
// Log the debug info which is used to decide the need of proces switch.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
aChannel->GetURI(getter_AddRefs(uri));
|
||||
LogChannelRelevantInfo(uri, loadingPrincipal, resultPrincipal,
|
||||
LogChannelRelevantInfo(uri, loadingPrincipal, channelPrincipal,
|
||||
loadInfo->InternalContentPolicyType());
|
||||
|
||||
// Check if the signed package is loaded from the same origin.
|
||||
bool sameOrigin = false;
|
||||
loadingPrincipal->Equals(resultPrincipal, &sameOrigin);
|
||||
loadingPrincipal->Equals(channelPrincipal, &sameOrigin);
|
||||
if (sameOrigin) {
|
||||
LOG("Loading singed package from the same origin. Don't switch process.\n");
|
||||
return false;
|
||||
@ -516,7 +538,7 @@ void
|
||||
TabParent::OnStartSignedPackageRequest(nsIChannel* aChannel,
|
||||
const nsACString& aPackageId)
|
||||
{
|
||||
if (!ShouldSwitchProcess(aChannel)) {
|
||||
if (!ShouldSwitchProcess(aChannel, aPackageId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2347,7 +2369,7 @@ TabParent::RecvStartPluginIME(const WidgetKeyboardEvent& aKeyboardEvent,
|
||||
return true;
|
||||
}
|
||||
widget->StartPluginIME(aKeyboardEvent,
|
||||
(int32_t&)aPanelX,
|
||||
(int32_t&)aPanelX,
|
||||
(int32_t&)aPanelY,
|
||||
*aCommitted);
|
||||
return true;
|
||||
|
@ -495,7 +495,7 @@ protected:
|
||||
|
||||
// Decide whether we have to use a new process to reload the URI associated
|
||||
// with the given channel.
|
||||
bool ShouldSwitchProcess(nsIChannel* aChannel);
|
||||
bool ShouldSwitchProcess(nsIChannel* aChannel, const nsACString& aSignedPkg);
|
||||
|
||||
ContentCacheInParent mContentCache;
|
||||
|
||||
|
@ -12,15 +12,14 @@
|
||||
|
||||
#include "OggDecoder.h"
|
||||
#include "OggReader.h"
|
||||
#ifdef MOZ_WAVE
|
||||
|
||||
#include "WaveDecoder.h"
|
||||
#include "WaveReader.h"
|
||||
#endif
|
||||
#ifdef MOZ_WEBM
|
||||
|
||||
#include "WebMDecoder.h"
|
||||
#include "WebMReader.h"
|
||||
#include "WebMDemuxer.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_RAW
|
||||
#include "RawDecoder.h"
|
||||
#include "RawReader.h"
|
||||
@ -128,7 +127,6 @@ IsOggType(const nsACString& aType)
|
||||
return CodecListContains(gOggTypes, aType);
|
||||
}
|
||||
|
||||
#ifdef MOZ_WAVE
|
||||
// See http://www.rfc-editor.org/rfc/rfc2361.txt for the definitions
|
||||
// of WAVE media types and codec types. However, the audio/vnd.wave
|
||||
// MIME type described there is not used.
|
||||
@ -154,33 +152,24 @@ IsWaveType(const nsACString& aType)
|
||||
|
||||
return CodecListContains(gWaveTypes, aType);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WEBM
|
||||
static bool
|
||||
IsWebMSupportedType(const nsACString& aType,
|
||||
const nsAString& aCodecs = EmptyString())
|
||||
{
|
||||
return WebMDecoder::CanHandleMediaType(aType, aCodecs);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* static */ bool
|
||||
DecoderTraits::IsWebMTypeAndEnabled(const nsACString& aType)
|
||||
{
|
||||
#ifdef MOZ_WEBM
|
||||
return IsWebMSupportedType(aType);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DecoderTraits::IsWebMAudioType(const nsACString& aType)
|
||||
{
|
||||
#ifdef MOZ_WEBM
|
||||
return aType.EqualsASCII("audio/webm");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef MOZ_GSTREAMER
|
||||
@ -367,7 +356,6 @@ IsAACSupportedType(const nsACString& aType,
|
||||
/* static */
|
||||
bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType)
|
||||
{
|
||||
#ifdef MOZ_WAVE
|
||||
if (IsWaveType(nsDependentCString(aMIMEType))) {
|
||||
// We should not return true for Wave types, since there are some
|
||||
// Wave codecs actually in use in the wild that we don't support, and
|
||||
@ -376,7 +364,6 @@ bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType)
|
||||
// means.
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return CanHandleMediaType(aMIMEType, false, EmptyString()) != CANPLAY_NO;
|
||||
}
|
||||
|
||||
@ -394,11 +381,9 @@ DecoderTraits::CanHandleCodecsType(const char* aMIMEType,
|
||||
if (IsOggType(nsDependentCString(aMIMEType))) {
|
||||
codecList = MediaDecoder::IsOpusEnabled() ? gOggCodecsWithOpus : gOggCodecs;
|
||||
}
|
||||
#ifdef MOZ_WAVE
|
||||
if (IsWaveType(nsDependentCString(aMIMEType))) {
|
||||
codecList = gWaveCodecs;
|
||||
}
|
||||
#endif
|
||||
#if !defined(MOZ_OMX_WEBM_DECODER)
|
||||
if (IsWebMTypeAndEnabled(nsDependentCString(aMIMEType))) {
|
||||
if (IsWebMSupportedType(nsDependentCString(aMIMEType), aRequestedCodecs)) {
|
||||
@ -496,11 +481,9 @@ DecoderTraits::CanHandleMediaType(const char* aMIMEType,
|
||||
if (IsOggType(nsDependentCString(aMIMEType))) {
|
||||
return CANPLAY_MAYBE;
|
||||
}
|
||||
#ifdef MOZ_WAVE
|
||||
if (IsWaveType(nsDependentCString(aMIMEType))) {
|
||||
return CANPLAY_MAYBE;
|
||||
}
|
||||
#endif
|
||||
if (IsMP4TypeAndEnabled(nsDependentCString(aMIMEType))) {
|
||||
return CANPLAY_MAYBE;
|
||||
}
|
||||
@ -583,12 +566,10 @@ InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
|
||||
decoder = new OggDecoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#ifdef MOZ_WAVE
|
||||
if (IsWaveType(aType)) {
|
||||
decoder = new WaveDecoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_OMX_DECODER
|
||||
if (IsOmxSupportedType(aType)) {
|
||||
// we are discouraging Web and App developers from using those formats in
|
||||
@ -623,12 +604,12 @@ InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
|
||||
return decoder.forget();
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_WEBM
|
||||
|
||||
if (IsWebMSupportedType(aType)) {
|
||||
decoder = new WebMDecoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_DIRECTSHOW
|
||||
// Note: DirectShow should come before WMF, so that we prefer DirectShow's
|
||||
// MP3 support over WMF's.
|
||||
@ -682,11 +663,9 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
|
||||
if (IsOggType(aType)) {
|
||||
decoderReader = new OggReader(aDecoder);
|
||||
} else
|
||||
#ifdef MOZ_WAVE
|
||||
if (IsWaveType(aType)) {
|
||||
decoderReader = new WaveReader(aDecoder);
|
||||
} else
|
||||
#endif
|
||||
#ifdef MOZ_OMX_DECODER
|
||||
if (IsOmxSupportedType(aType)) {
|
||||
decoderReader = new MediaOmxReader(aDecoder);
|
||||
@ -698,13 +677,13 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
|
||||
decoderReader = new AndroidMediaReader(aDecoder, aType);
|
||||
} else
|
||||
#endif
|
||||
#ifdef MOZ_WEBM
|
||||
|
||||
if (IsWebMSupportedType(aType)) {
|
||||
decoderReader = Preferences::GetBool("media.format-reader.webm", true) ?
|
||||
static_cast<MediaDecoderReader*>(new MediaFormatReader(aDecoder, new WebMDemuxer(aDecoder->GetResource()))) :
|
||||
new WebMReader(aDecoder);
|
||||
} else
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_DIRECTSHOW
|
||||
if (IsDirectShowSupportedType(aType)) {
|
||||
decoderReader = new DirectShowReader(aDecoder);
|
||||
@ -735,9 +714,7 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
|
||||
(IsOmxSupportedType(aType) &&
|
||||
!IsB2GSupportOnlyType(aType)) ||
|
||||
#endif
|
||||
#ifdef MOZ_WEBM
|
||||
IsWebMSupportedType(aType) ||
|
||||
#endif
|
||||
#ifdef MOZ_GSTREAMER
|
||||
IsGStreamerSupportedType(aType) ||
|
||||
#endif
|
||||
|
@ -1641,21 +1641,17 @@ MediaDecoder::IsOggEnabled()
|
||||
return Preferences::GetBool("media.ogg.enabled");
|
||||
}
|
||||
|
||||
#ifdef MOZ_WAVE
|
||||
bool
|
||||
MediaDecoder::IsWaveEnabled()
|
||||
{
|
||||
return Preferences::GetBool("media.wave.enabled");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WEBM
|
||||
bool
|
||||
MediaDecoder::IsWebMEnabled()
|
||||
{
|
||||
return Preferences::GetBool("media.webm.enabled");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NECKO_PROTOCOL_rtsp
|
||||
bool
|
||||
|
@ -662,14 +662,9 @@ private:
|
||||
|
||||
static bool IsOggEnabled();
|
||||
static bool IsOpusEnabled();
|
||||
|
||||
#ifdef MOZ_WAVE
|
||||
static bool IsWaveEnabled();
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WEBM
|
||||
static bool IsWebMEnabled();
|
||||
#endif
|
||||
|
||||
#ifdef NECKO_PROTOCOL_rtsp
|
||||
static bool IsRtspEnabled();
|
||||
#endif
|
||||
|
@ -224,6 +224,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
mIsVideoPrerolling(false),
|
||||
mAudioCaptured(false, "MediaDecoderStateMachine::mAudioCaptured"),
|
||||
mAudioCompleted(false, "MediaDecoderStateMachine::mAudioCompleted"),
|
||||
mVideoCompleted(false, "MediaDecoderStateMachine::mVideoCompleted"),
|
||||
mNotifyMetadataBeforeFirstFrame(false),
|
||||
mDispatchedEventToDecode(false),
|
||||
mQuickBuffering(false),
|
||||
@ -355,6 +356,7 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
|
||||
mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated);
|
||||
mWatchManager.Watch(mState, &MediaDecoderStateMachine::UpdateNextFrameStatus);
|
||||
mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
|
||||
mWatchManager.Watch(mVideoCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
|
||||
mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
|
||||
mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
|
||||
mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
|
||||
@ -2352,10 +2354,8 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
// Play the remaining media. We want to run AdvanceFrame() at least
|
||||
// once to ensure the current playback position is advanced to the
|
||||
// end of the media, and so that we update the readyState.
|
||||
if (VideoQueue().GetSize() > 1 ||
|
||||
(HasAudio() && !mAudioCompleted) ||
|
||||
(mAudioCaptured && !mStreamSink->IsFinished()))
|
||||
{
|
||||
if ((HasVideo() && !mVideoCompleted) ||
|
||||
(HasAudio() && !mAudioCompleted)) {
|
||||
// Start playback if necessary to play the remaining media.
|
||||
MaybeStartPlayback();
|
||||
UpdatePlaybackPositionPeriodically();
|
||||
@ -2425,6 +2425,7 @@ MediaDecoderStateMachine::Reset()
|
||||
mDecodedVideoEndTime = -1;
|
||||
mDecodedAudioEndTime = -1;
|
||||
mAudioCompleted = false;
|
||||
mVideoCompleted = false;
|
||||
AudioQueue().Reset();
|
||||
VideoQueue().Reset();
|
||||
mFirstVideoFrameAfterSeek = nullptr;
|
||||
@ -2836,9 +2837,11 @@ void
|
||||
MediaDecoderStateMachine::OnMediaSinkVideoComplete()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(mInfo.HasVideo());
|
||||
VERBOSE_LOG("[%s]", __func__);
|
||||
|
||||
mMediaSinkVideoPromise.Complete();
|
||||
mVideoCompleted = true;
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
|
||||
@ -2846,9 +2849,11 @@ void
|
||||
MediaDecoderStateMachine::OnMediaSinkVideoError()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(mInfo.HasVideo());
|
||||
VERBOSE_LOG("[%s]", __func__);
|
||||
|
||||
mMediaSinkVideoPromise.Complete();
|
||||
mVideoCompleted = true;
|
||||
if (HasAudio()) {
|
||||
return;
|
||||
}
|
||||
@ -2858,11 +2863,11 @@ MediaDecoderStateMachine::OnMediaSinkVideoError()
|
||||
void MediaDecoderStateMachine::OnMediaSinkAudioComplete()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(mInfo.HasAudio());
|
||||
VERBOSE_LOG("[%s]", __func__);
|
||||
|
||||
mMediaSinkAudioPromise.Complete();
|
||||
// Set true only when we have audio.
|
||||
mAudioCompleted = mInfo.HasAudio();
|
||||
mAudioCompleted = true;
|
||||
// To notify PlaybackEnded as soon as possible.
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
@ -2870,11 +2875,11 @@ void MediaDecoderStateMachine::OnMediaSinkAudioComplete()
|
||||
void MediaDecoderStateMachine::OnMediaSinkAudioError()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(mInfo.HasAudio());
|
||||
VERBOSE_LOG("[%s]", __func__);
|
||||
|
||||
mMediaSinkAudioPromise.Complete();
|
||||
// Set true only when we have audio.
|
||||
mAudioCompleted = mInfo.HasAudio();
|
||||
mAudioCompleted = true;
|
||||
|
||||
// Make the best effort to continue playback when there is video.
|
||||
if (HasVideo()) {
|
||||
|
@ -1101,6 +1101,9 @@ private:
|
||||
// been written to the MediaStream.
|
||||
Watchable<bool> mAudioCompleted;
|
||||
|
||||
// True if all video frames are already rendered.
|
||||
Watchable<bool> mVideoCompleted;
|
||||
|
||||
// Set if MDSM receives dormant request during reading metadata.
|
||||
Maybe<bool> mPendingDormant;
|
||||
|
||||
|
@ -26,7 +26,6 @@ public:
|
||||
: mMutex("DecodedStreamGraphListener::mMutex")
|
||||
, mStream(aStream)
|
||||
, mLastOutputTime(aStream->StreamTimeToMicroseconds(aStream->GetCurrentTime()))
|
||||
, mStreamFinishedOnMainThread(false)
|
||||
{
|
||||
mFinishPromise = Move(aPromise);
|
||||
}
|
||||
@ -52,8 +51,6 @@ public:
|
||||
void DoNotifyFinished()
|
||||
{
|
||||
mFinishPromise.ResolveIfExists(true, __func__);
|
||||
MutexAutoLock lock(mMutex);
|
||||
mStreamFinishedOnMainThread = true;
|
||||
}
|
||||
|
||||
int64_t GetLastOutputTime()
|
||||
@ -70,18 +67,11 @@ public:
|
||||
mStream = nullptr;
|
||||
}
|
||||
|
||||
bool IsFinishedOnMainThread()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mStreamFinishedOnMainThread;
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex mMutex;
|
||||
// Members below are protected by mMutex.
|
||||
RefPtr<MediaStream> mStream;
|
||||
int64_t mLastOutputTime; // microseconds
|
||||
bool mStreamFinishedOnMainThread;
|
||||
// Main thread only.
|
||||
MozPromiseHolder<GenericPromise> mFinishPromise;
|
||||
};
|
||||
@ -119,7 +109,6 @@ public:
|
||||
DecodedStreamData(SourceMediaStream* aStream,
|
||||
MozPromiseHolder<GenericPromise>&& aPromise);
|
||||
~DecodedStreamData();
|
||||
bool IsFinished() const;
|
||||
int64_t GetPosition() const;
|
||||
void SetPlaying(bool aPlaying);
|
||||
|
||||
@ -180,12 +169,6 @@ DecodedStreamData::~DecodedStreamData()
|
||||
mStream->Destroy();
|
||||
}
|
||||
|
||||
bool
|
||||
DecodedStreamData::IsFinished() const
|
||||
{
|
||||
return mListener->IsFinishedOnMainThread();
|
||||
}
|
||||
|
||||
int64_t
|
||||
DecodedStreamData::GetPosition() const
|
||||
{
|
||||
@ -346,13 +329,14 @@ DecodedStream::OnEnded(TrackType aType)
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(mStartTime.isSome());
|
||||
|
||||
if (aType == TrackInfo::kAudioTrack) {
|
||||
if (aType == TrackInfo::kAudioTrack && mInfo.HasAudio()) {
|
||||
// TODO: we should return a promise which is resolved when the audio track
|
||||
// is finished. For now this promise is resolved when the whole stream is
|
||||
// finished.
|
||||
return mFinishPromise;
|
||||
} else if (aType == TrackInfo::kVideoTrack && mInfo.HasVideo()) {
|
||||
return mFinishPromise;
|
||||
}
|
||||
// TODO: handle video track.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -883,13 +867,6 @@ DecodedStream::GetPosition(TimeStamp* aTimeStamp) const
|
||||
return mStartTime.ref() + (mData ? mData->GetPosition() : 0);
|
||||
}
|
||||
|
||||
bool
|
||||
DecodedStream::IsFinished() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
return mData && mData->IsFinished();
|
||||
}
|
||||
|
||||
void
|
||||
DecodedStream::ConnectListener()
|
||||
{
|
||||
|
@ -123,7 +123,6 @@ public:
|
||||
void AddOutput(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
|
||||
void RemoveOutput(MediaStream* aStream);
|
||||
void SetSameOrigin(bool aSameOrigin);
|
||||
bool IsFinished() const;
|
||||
bool HasConsumers() const;
|
||||
|
||||
protected:
|
||||
|
@ -166,8 +166,29 @@ VideoSink::Start(int64_t aStartTime, const MediaInfo& aInfo)
|
||||
|
||||
if (mHasVideo) {
|
||||
mEndPromise = mEndPromiseHolder.Ensure(__func__);
|
||||
|
||||
// If the underlying MediaSink has an end promise for the video track (which
|
||||
// happens when mAudioSink refers to a DecodedStream), we must wait for it
|
||||
// to complete before resolving our own end promise. Otherwise, MDSM might
|
||||
// stop playback before DecodedStream plays to the end and cause
|
||||
// test_streams_element_capture.html to time out.
|
||||
RefPtr<GenericPromise> p = mAudioSink->OnEnded(TrackInfo::kVideoTrack);
|
||||
if (p) {
|
||||
RefPtr<VideoSink> self = this;
|
||||
mVideoSinkEndRequest.Begin(p->Then(mOwnerThread, __func__,
|
||||
[self] () {
|
||||
self->mVideoSinkEndRequest.Complete();
|
||||
self->TryUpdateRenderedVideoFrames();
|
||||
}, [self] () {
|
||||
self->mVideoSinkEndRequest.Complete();
|
||||
self->TryUpdateRenderedVideoFrames();
|
||||
}));
|
||||
}
|
||||
|
||||
ConnectListener();
|
||||
TryUpdateRenderedVideoFrames();
|
||||
// Run the render loop at least once so we can resolve the end promise
|
||||
// when video duration is 0.
|
||||
UpdateRenderedVideoFrames();
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,7 +204,8 @@ VideoSink::Stop()
|
||||
mUpdateScheduler.Reset();
|
||||
if (mHasVideo) {
|
||||
DisconnectListener();
|
||||
mEndPromiseHolder.Resolve(true, __func__);
|
||||
mVideoSinkEndRequest.DisconnectIfExists();
|
||||
mEndPromiseHolder.ResolveIfExists(true, __func__);
|
||||
mEndPromise = nullptr;
|
||||
}
|
||||
mVideoFrameEndTime = -1;
|
||||
@ -216,7 +238,7 @@ VideoSink::Shutdown()
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::OnVideoQueueEvent(RefPtr<MediaData>&& aSample)
|
||||
VideoSink::OnVideoQueuePushed(RefPtr<MediaData>&& aSample)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
// Listen to push event, VideoSink should try rendering ASAP if first frame
|
||||
@ -230,6 +252,18 @@ VideoSink::OnVideoQueueEvent(RefPtr<MediaData>&& aSample)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::OnVideoQueueFinished()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
// Run render loop if the end promise is not resolved yet.
|
||||
if (!mUpdateScheduler.IsScheduled() &&
|
||||
mAudioSink->IsPlaying() &&
|
||||
!mEndPromiseHolder.IsEmpty()) {
|
||||
UpdateRenderedVideoFrames();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::Redraw()
|
||||
{
|
||||
@ -260,7 +294,9 @@ VideoSink::ConnectListener()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
mPushListener = VideoQueue().PushEvent().Connect(
|
||||
mOwnerThread, this, &VideoSink::OnVideoQueueEvent);
|
||||
mOwnerThread, this, &VideoSink::OnVideoQueuePushed);
|
||||
mFinishListener = VideoQueue().FinishEvent().Connect(
|
||||
mOwnerThread, this, &VideoSink::OnVideoQueueFinished);
|
||||
}
|
||||
|
||||
void
|
||||
@ -268,6 +304,7 @@ VideoSink::DisconnectListener()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
mPushListener.Disconnect();
|
||||
mFinishListener.Disconnect();
|
||||
}
|
||||
|
||||
void
|
||||
@ -371,6 +408,13 @@ VideoSink::UpdateRenderedVideoFrames()
|
||||
}
|
||||
}
|
||||
|
||||
// All frames are rendered, Let's resolve the promise.
|
||||
if (VideoQueue().IsFinished() &&
|
||||
VideoQueue().GetSize() <= 1 &&
|
||||
!mVideoSinkEndRequest.Exists()) {
|
||||
mEndPromiseHolder.ResolveIfExists(true, __func__);
|
||||
}
|
||||
|
||||
RenderVideoFrames(mVideoQueueSendToCompositorSize, clockTime, nowTime);
|
||||
|
||||
// No next fame to render. There is no need to schedule next render
|
||||
|
@ -72,7 +72,8 @@ private:
|
||||
virtual ~VideoSink();
|
||||
|
||||
// VideoQueue listener related.
|
||||
void OnVideoQueueEvent(RefPtr<MediaData>&& aSample);
|
||||
void OnVideoQueuePushed(RefPtr<MediaData>&& aSample);
|
||||
void OnVideoQueueFinished();
|
||||
void ConnectListener();
|
||||
void DisconnectListener();
|
||||
|
||||
@ -129,6 +130,7 @@ private:
|
||||
|
||||
// Event listeners for VideoQueue
|
||||
MediaEventListener mPushListener;
|
||||
MediaEventListener mFinishListener;
|
||||
|
||||
// True if this sink is going to handle video track.
|
||||
bool mHasVideo;
|
||||
|
@ -12,10 +12,7 @@
|
||||
#include "mozilla/StateMirroring.h"
|
||||
#include "SourceBufferResource.h"
|
||||
#include "SourceBuffer.h"
|
||||
|
||||
#ifdef MOZ_WEBM
|
||||
#include "WebMDemuxer.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_FMP4
|
||||
#include "MP4Demuxer.h"
|
||||
@ -798,12 +795,10 @@ TrackBuffersManager::CreateDemuxerforMIMEType()
|
||||
{
|
||||
ShutdownDemuxers();
|
||||
|
||||
#ifdef MOZ_WEBM
|
||||
if (mType.LowerCaseEqualsLiteral("video/webm") || mType.LowerCaseEqualsLiteral("audio/webm")) {
|
||||
mInputDemuxer = new WebMDemuxer(mCurrentInputBuffer, true /* IsMediaSource*/ );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_FMP4
|
||||
if (mType.LowerCaseEqualsLiteral("video/mp4") || mType.LowerCaseEqualsLiteral("audio/mp4")) {
|
||||
|
@ -30,7 +30,9 @@ DIRS += [
|
||||
'ogg',
|
||||
'platforms',
|
||||
'systemservices',
|
||||
'wave',
|
||||
'webaudio',
|
||||
'webm',
|
||||
'webrtc',
|
||||
'webspeech',
|
||||
'webvtt',
|
||||
@ -40,12 +42,6 @@ DIRS += [
|
||||
if CONFIG['MOZ_RAW']:
|
||||
DIRS += ['raw']
|
||||
|
||||
if CONFIG['MOZ_WAVE']:
|
||||
DIRS += ['wave']
|
||||
|
||||
if CONFIG['MOZ_WEBM']:
|
||||
DIRS += ['webm']
|
||||
|
||||
if CONFIG['MOZ_GSTREAMER']:
|
||||
DIRS += ['gstreamer']
|
||||
|
||||
|
@ -68,7 +68,7 @@ OmxDataDecoder::OmxDataDecoder(const TrackInfo& aTrackInfo,
|
||||
, mOmxState(OMX_STATETYPE::OMX_StateInvalid, "OmxDataDecoder::mOmxState")
|
||||
, mTrackInfo(aTrackInfo.Clone())
|
||||
, mFlushing(false)
|
||||
, mShutdown(false)
|
||||
, mShuttingDown(false)
|
||||
, mCheckingInputExhausted(false)
|
||||
, mPortSettingsChanged(-1, "OmxDataDecoder::mPortSettingsChanged")
|
||||
, mAudioCompactor(mAudioQueue)
|
||||
@ -86,7 +86,6 @@ OmxDataDecoder::~OmxDataDecoder()
|
||||
{
|
||||
LOG("(%p)", this);
|
||||
mWatchManager.Shutdown();
|
||||
mOmxTaskQueue->AwaitShutdownAndIdle();
|
||||
}
|
||||
|
||||
void
|
||||
@ -207,12 +206,25 @@ OmxDataDecoder::Shutdown()
|
||||
{
|
||||
LOG("(%p)", this);
|
||||
|
||||
mShutdown = true;
|
||||
mShuttingDown = true;
|
||||
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(this, &OmxDataDecoder::DoAsyncShutdown);
|
||||
mOmxTaskQueue->Dispatch(r.forget());
|
||||
|
||||
{
|
||||
// DoAsyncShutdown() will be running for a while, it could be still running
|
||||
// when reader releasing the decoder and then it causes problem. To avoid it,
|
||||
// Shutdown() must block until DoAsyncShutdown() is completed.
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
while (mShuttingDown) {
|
||||
lock.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
mOmxTaskQueue->BeginShutdown();
|
||||
mOmxTaskQueue->AwaitShutdownAndIdle();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -273,9 +285,17 @@ OmxDataDecoder::DoAsyncShutdown()
|
||||
[self] () {
|
||||
LOG("DoAsyncShutdown: OMX_StateLoaded, it is safe to shutdown omx");
|
||||
self->mOmxLayer->Shutdown();
|
||||
|
||||
MonitorAutoLock lock(self->mMonitor);
|
||||
self->mShuttingDown = false;
|
||||
self->mMonitor.Notify();
|
||||
},
|
||||
[self] () {
|
||||
self->mOmxLayer->Shutdown();
|
||||
|
||||
MonitorAutoLock lock(self->mMonitor);
|
||||
self->mShuttingDown = false;
|
||||
self->mMonitor.Notify();
|
||||
});
|
||||
}
|
||||
|
||||
@ -400,12 +420,8 @@ OmxDataDecoder::FillAndEmptyBuffers()
|
||||
MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
|
||||
MOZ_ASSERT(mOmxState == OMX_StateExecuting);
|
||||
|
||||
// During the port setting changed, it is forbided to do any buffer operations.
|
||||
if (mPortSettingsChanged != -1 || mShutdown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFlushing) {
|
||||
// During the port setting changed, it is forbidden to do any buffer operation.
|
||||
if (mPortSettingsChanged != -1 || mShuttingDown || mFlushing) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -842,7 +858,6 @@ OmxDataDecoder::DoFlush()
|
||||
|
||||
// 1. Call OMX command OMX_CommandFlush in Omx TaskQueue.
|
||||
// 2. Remove all elements in mMediaRawDatas when flush is completed.
|
||||
RefPtr<OmxDataDecoder> self = this;
|
||||
mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
|
||||
->Then(mOmxTaskQueue, __func__, this,
|
||||
&OmxDataDecoder::FlushComplete,
|
||||
|
@ -163,7 +163,7 @@ protected:
|
||||
Atomic<bool> mFlushing;
|
||||
|
||||
// It is accessed in Omx/reader TaskQeueu.
|
||||
Atomic<bool> mShutdown;
|
||||
Atomic<bool> mShuttingDown;
|
||||
|
||||
// It is accessed in Omx TaskQeueu.
|
||||
bool mCheckingInputExhausted;
|
||||
|
@ -22,9 +22,10 @@ extern mozilla::LogModule* GetPDMLog();
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
extern void GetPortIndex(nsTArray<uint32_t>& aPortIndex);
|
||||
|
||||
OmxPromiseLayer::OmxPromiseLayer(TaskQueue* aTaskQueue, OmxDataDecoder* aDataDecoder)
|
||||
: mTaskQueue(aTaskQueue)
|
||||
, mFlushPortIndex(0)
|
||||
{
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION < 21
|
||||
mPlatformLayer = new GonkOmxPlatformLayer(aDataDecoder, this, aTaskQueue);
|
||||
@ -196,27 +197,48 @@ OmxPromiseLayer::EmptyFillBufferDone(OMX_DIRTYPE aType, BufferData::BufferID aID
|
||||
RefPtr<OmxPromiseLayer::OmxCommandPromise>
|
||||
OmxPromiseLayer::SendCommand(OMX_COMMANDTYPE aCmd, OMX_U32 aParam1, OMX_PTR aCmdData)
|
||||
{
|
||||
// No need to issue flush because of buffers are in client already.
|
||||
//
|
||||
// Some components fail to respond flush event when all of buffers are in
|
||||
// client.
|
||||
if (aCmd == OMX_CommandFlush) {
|
||||
bool needFlush = false;
|
||||
if ((aParam1 & OMX_DirInput && mInbufferHolders.Length()) ||
|
||||
(aParam1 & OMX_DirOutput && mOutbufferHolders.Length())) {
|
||||
needFlush = true;
|
||||
}
|
||||
if (!needFlush) {
|
||||
LOG("SendCommand: buffers are in client already, no need to flush");
|
||||
mRawDatas.Clear();
|
||||
return OmxCommandPromise::CreateAndResolve(OMX_CommandFlush, __func__);
|
||||
}
|
||||
}
|
||||
// It doesn't support another flush commands before previous one is completed.
|
||||
MOZ_RELEASE_ASSERT(!mFlushCommands.Length());
|
||||
|
||||
OMX_ERRORTYPE err = mPlatformLayer->SendCommand(aCmd, aParam1, aCmdData);
|
||||
if (err != OMX_ErrorNone) {
|
||||
OmxCommandFailureHolder failure(OMX_ErrorNotReady, aCmd);
|
||||
return OmxCommandPromise::CreateAndReject(failure, __func__);
|
||||
// Some coomponents don't send event with OMX_ALL, they send flush complete
|
||||
// event with input port and another event for output port.
|
||||
// In prupose of better compatibility, we inteprete the OMX_ALL to OMX_DirInput
|
||||
// and OMX_DirOutput flush separately.
|
||||
OMX_DIRTYPE types[] = {OMX_DIRTYPE::OMX_DirInput, OMX_DIRTYPE::OMX_DirOutput};
|
||||
for(const auto type : types) {
|
||||
if ((aParam1 == type) || (aParam1 == OMX_ALL)) {
|
||||
mFlushCommands.AppendElement(FlushCommand({type, aCmdData}));
|
||||
}
|
||||
|
||||
if (type == OMX_DirInput) {
|
||||
// Clear all buffered raw data.
|
||||
mRawDatas.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't overlay more than one fush command, some components can't overlay flush commands.
|
||||
// So here we send another flush after receiving the previous flush completed event.
|
||||
if (mFlushCommands.Length()) {
|
||||
OMX_ERRORTYPE err =
|
||||
mPlatformLayer->SendCommand(OMX_CommandFlush,
|
||||
mFlushCommands.ElementAt(0).type,
|
||||
mFlushCommands.ElementAt(0).cmd);
|
||||
if (err != OMX_ErrorNone) {
|
||||
OmxCommandFailureHolder failure(OMX_ErrorNotReady, OMX_CommandFlush);
|
||||
return OmxCommandPromise::CreateAndReject(failure, __func__);
|
||||
}
|
||||
} else {
|
||||
LOG("SendCommand: OMX_CommandFlush parameter error");
|
||||
OmxCommandFailureHolder failure(OMX_ErrorNotReady, OMX_CommandFlush);
|
||||
return OmxCommandPromise::CreateAndReject(failure, __func__);
|
||||
}
|
||||
} else {
|
||||
OMX_ERRORTYPE err = mPlatformLayer->SendCommand(aCmd, aParam1, aCmdData);
|
||||
if (err != OMX_ErrorNone) {
|
||||
OmxCommandFailureHolder failure(OMX_ErrorNotReady, aCmd);
|
||||
return OmxCommandPromise::CreateAndReject(failure, __func__);
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<OmxCommandPromise> p;
|
||||
@ -224,9 +246,6 @@ OmxPromiseLayer::SendCommand(OMX_COMMANDTYPE aCmd, OMX_U32 aParam1, OMX_PTR aCmd
|
||||
p = mCommandStatePromise.Ensure(__func__);
|
||||
} else if (aCmd == OMX_CommandFlush) {
|
||||
p = mFlushPromise.Ensure(__func__);
|
||||
mFlushPortIndex = aParam1;
|
||||
// Clear all buffered raw data.
|
||||
mRawDatas.Clear();
|
||||
} else if (aCmd == OMX_CommandPortEnable) {
|
||||
p = mPortEnablePromise.Ensure(__func__);
|
||||
} else if (aCmd == OMX_CommandPortDisable) {
|
||||
@ -248,8 +267,24 @@ OmxPromiseLayer::Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2)
|
||||
{
|
||||
if (cmd == OMX_CommandStateSet) {
|
||||
mCommandStatePromise.Resolve(OMX_CommandStateSet, __func__);
|
||||
} else if (cmd == OMX_CommandFlush && mFlushPortIndex == aData2) {
|
||||
mFlushPromise.Resolve(OMX_CommandFlush, __func__);
|
||||
} else if (cmd == OMX_CommandFlush) {
|
||||
MOZ_RELEASE_ASSERT(mFlushCommands.ElementAt(0).type == aData2);
|
||||
LOG("Event: OMX_CommandFlush completed port type %d", aData2);
|
||||
mFlushCommands.RemoveElementAt(0);
|
||||
|
||||
// Sending next flush command.
|
||||
if (mFlushCommands.Length()) {
|
||||
OMX_ERRORTYPE err =
|
||||
mPlatformLayer->SendCommand(OMX_CommandFlush,
|
||||
mFlushCommands.ElementAt(0).type,
|
||||
mFlushCommands.ElementAt(0).cmd);
|
||||
if (err != OMX_ErrorNone) {
|
||||
OmxCommandFailureHolder failure(OMX_ErrorNotReady, OMX_CommandFlush);
|
||||
mFlushPromise.Reject(failure, __func__);
|
||||
}
|
||||
} else {
|
||||
mFlushPromise.Resolve(OMX_CommandFlush, __func__);
|
||||
}
|
||||
} else if (cmd == OMX_CommandPortDisable) {
|
||||
mPortDisablePromise.Resolve(OMX_CommandPortDisable, __func__);
|
||||
} else if (cmd == OMX_CommandPortEnable) {
|
||||
@ -262,7 +297,7 @@ OmxPromiseLayer::Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2)
|
||||
if (cmd == OMX_CommandStateSet) {
|
||||
OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandStateSet);
|
||||
mCommandStatePromise.Reject(failure, __func__);
|
||||
} else if (cmd == OMX_CommandFlush && mFlushPortIndex == aData2) {
|
||||
} else if (cmd == OMX_CommandFlush) {
|
||||
OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandFlush);
|
||||
mFlushPromise.Reject(failure, __func__);
|
||||
} else if (cmd == OMX_CommandPortDisable) {
|
||||
|
@ -183,6 +183,11 @@ public:
|
||||
bool Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2);
|
||||
|
||||
protected:
|
||||
struct FlushCommand {
|
||||
OMX_DIRTYPE type;
|
||||
OMX_PTR cmd;
|
||||
};
|
||||
|
||||
BUFFERLIST* GetBufferHolders(OMX_DIRTYPE aType);
|
||||
|
||||
already_AddRefed<MediaRawData> FindAndRemoveRawData(OMX_TICKS aTimecode);
|
||||
@ -197,7 +202,7 @@ protected:
|
||||
|
||||
MozPromiseHolder<OmxCommandPromise> mFlushPromise;
|
||||
|
||||
OMX_U32 mFlushPortIndex;
|
||||
nsTArray<FlushCommand> mFlushCommands;
|
||||
|
||||
nsAutoPtr<OmxPlatformLayer> mPlatformLayer;
|
||||
|
||||
|
@ -50,11 +50,12 @@ DecoderFuzzingWrapper::Input(MediaRawData* aData)
|
||||
nsresult
|
||||
DecoderFuzzingWrapper::Flush()
|
||||
{
|
||||
DFW_LOGV("");
|
||||
DFW_LOGV("Calling mDecoder[%p]->Flush()", mDecoder.get());
|
||||
MOZ_ASSERT(mDecoder);
|
||||
// Flush may output some frames (though unlikely).
|
||||
// Flush may block a bit, it's ok if we output some frames in the meantime.
|
||||
nsresult result = mDecoder->Flush();
|
||||
DFW_LOGV("mDecoder[%p]->Flush() -> result=%u", mDecoder.get(), uint32_t(result));
|
||||
// Clear any delayed output we may have.
|
||||
mCallbackWrapper->ClearDelayedOutput();
|
||||
return result;
|
||||
@ -254,13 +255,28 @@ void
|
||||
DecoderCallbackFuzzingWrapper::ScheduleOutputDelayedFrame()
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
if (mDelayedOutputRequest.Exists()) {
|
||||
// A delayed output is already scheduled, no need for more than one timer.
|
||||
return;
|
||||
}
|
||||
RefPtr<DecoderCallbackFuzzingWrapper> self = this;
|
||||
mDelayedOutputTimer->WaitUntil(
|
||||
mPreviousOutput + mFrameOutputMinimumInterval,
|
||||
__func__)
|
||||
->Then(mTaskQueue, __func__,
|
||||
[self] () -> void { self->OutputDelayedFrame(); },
|
||||
[self] () -> void { self->OutputDelayedFrame(); });
|
||||
mDelayedOutputRequest.Begin(
|
||||
mDelayedOutputTimer->WaitUntil(
|
||||
mPreviousOutput + mFrameOutputMinimumInterval,
|
||||
__func__)
|
||||
->Then(mTaskQueue, __func__,
|
||||
[self] () -> void {
|
||||
if (self->mDelayedOutputRequest.Exists()) {
|
||||
self->mDelayedOutputRequest.Complete();
|
||||
self->OutputDelayedFrame();
|
||||
}
|
||||
},
|
||||
[self] () -> void {
|
||||
if (self->mDelayedOutputRequest.Exists()) {
|
||||
self->mDelayedOutputRequest.Complete();
|
||||
self->ClearDelayedOutput();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void
|
||||
@ -300,11 +316,16 @@ void
|
||||
DecoderCallbackFuzzingWrapper::ClearDelayedOutput()
|
||||
{
|
||||
if (!mTaskQueue->IsCurrentThreadIn()) {
|
||||
DFW_LOGV("(dispatching self)");
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::ClearDelayedOutput);
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
return;
|
||||
}
|
||||
DFW_LOGV("");
|
||||
// In case a timer hasn't lapsed yet, before destroying the timer and its
|
||||
// attached waitUntil() promise, the 'Then' request must be disconnected.
|
||||
mDelayedOutputRequest.DisconnectIfExists();
|
||||
mDelayedOutputTimer = nullptr;
|
||||
mDelayedOutput.clear();
|
||||
}
|
||||
@ -312,10 +333,16 @@ DecoderCallbackFuzzingWrapper::ClearDelayedOutput()
|
||||
void
|
||||
DecoderCallbackFuzzingWrapper::Shutdown()
|
||||
{
|
||||
DFW_LOGV("Shutting down mTaskQueue");
|
||||
CFW_LOGV("Clear delayed output (if any) before shutting down mTaskQueue");
|
||||
ClearDelayedOutput();
|
||||
// Await idle here, so that 'ClearDelayedOutput' runs to completion before
|
||||
// the task queue is shutdown (and tasks can't be queued anymore).
|
||||
mTaskQueue->AwaitIdle();
|
||||
|
||||
CFW_LOGV("Shutting down mTaskQueue");
|
||||
mTaskQueue->BeginShutdown();
|
||||
mTaskQueue->AwaitIdle();
|
||||
DFW_LOGV("mTaskQueue shut down");
|
||||
CFW_LOGV("mTaskQueue shut down");
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -81,6 +81,7 @@ private:
|
||||
typedef Pair<RefPtr<MediaData>, bool> MediaDataAndInputExhausted;
|
||||
std::deque<MediaDataAndInputExhausted> mDelayedOutput;
|
||||
RefPtr<MediaTimer> mDelayedOutputTimer;
|
||||
MozPromiseRequestHolder<MediaTimerPromise> mDelayedOutputRequest;
|
||||
// If draining, a 'DrainComplete' will be sent after all delayed frames have
|
||||
// been output.
|
||||
bool mDraining;
|
||||
|
@ -851,14 +851,6 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
||||
# The tests below contain backend-specific tests. Write backend independent
|
||||
# tests rather than adding to this list.
|
||||
[test_can_play_type_webm.html]
|
||||
skip-if = !webm
|
||||
[test_can_play_type_no_webm.html]
|
||||
skip-if = webm
|
||||
[test_can_play_type_wave.html]
|
||||
skip-if = !wave
|
||||
[test_can_play_type_no_wave.html]
|
||||
skip-if = wave
|
||||
[test_fragment_noplay.html]
|
||||
skip-if = !wave
|
||||
[test_fragment_play.html]
|
||||
skip-if = !wave
|
||||
|
@ -1,32 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=469247
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 469247: WAVE backend disabled</title>
|
||||
<script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469247">Mozill
|
||||
a Bug 469247</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
||||
<video id="v"></video>
|
||||
|
||||
<pre id="test">
|
||||
<script src="can_play_type_wave.js"></script>
|
||||
|
||||
check_wave(document.getElementById('v'), false);
|
||||
|
||||
mediaTestCleanup();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,32 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=566245
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 566245: WebM backend disabled</title>
|
||||
<script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=566245">Mozill
|
||||
a Bug 566245</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
||||
<video id="v"></video>
|
||||
|
||||
<pre id="test">
|
||||
<script src="can_play_type_webm.js"></script>
|
||||
|
||||
check_webm(document.getElementById('v'), false);
|
||||
|
||||
mediaTestCleanup();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -90,11 +90,11 @@ skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' &
|
||||
[test_peerConnection_basicScreenshare.html]
|
||||
# no screenshare on b2g/android
|
||||
# frequent timeouts/crashes on e10s (bug 1048455)
|
||||
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' || e10s # Bug 1141029 Mulet parity with B2G Desktop for TC
|
||||
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' # Bug 1141029 Mulet parity with B2G Desktop for TC
|
||||
[test_peerConnection_basicWindowshare.html]
|
||||
# no screenshare on b2g/android
|
||||
# frequent timeouts/crashes on e10s (bug 1048455)
|
||||
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' || e10s # Bug 1141029 Mulet parity with B2G Desktop for TC
|
||||
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' # Bug 1141029 Mulet parity with B2G Desktop for TC
|
||||
[test_peerConnection_basicH264Video.html]
|
||||
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || os == 'android' # bug 1043403 # Bug 1141029 Mulet parity with B2G Desktop for TC
|
||||
[test_peerConnection_bug822674.html]
|
||||
@ -161,16 +161,16 @@ skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' &
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g (Bug 1059867), android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
[test_peerConnection_twoAudioVideoStreams.html]
|
||||
# b2g(Bug 960442, video support for WebRTC is disabled on b2g), Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (os == 'linux' && debug && e10s) || android_version == '18'
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18'
|
||||
[test_peerConnection_twoAudioVideoStreamsCombined.html]
|
||||
# b2g(Bug 960442, video support for WebRTC is disabled on b2g), Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (os == 'linux' && debug && e10s) || android_version == '18'
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18'
|
||||
[test_peerConnection_twoVideoStreams.html]
|
||||
# b2g(Bug 960442, video support for WebRTC is disabled on b2g), Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (os == 'linux' && debug && e10s) || android_version == '18'
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18'
|
||||
[test_peerConnection_twoVideoTracksInOneStream.html]
|
||||
# b2g(Bug 960442, video support for WebRTC is disabled on b2g), Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (os == 'linux' && debug && e10s) || (android_version == '18' && debug)
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug)
|
||||
[test_peerConnection_addSecondAudioStream.html]
|
||||
skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
|
||||
[test_peerConnection_answererAddSecondAudioStream.html]
|
||||
@ -181,26 +181,26 @@ skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # B2G emulator
|
||||
skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # B2G emulator is too slow to finish a renegotiation test in under 5 minutes, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
[test_peerConnection_addSecondVideoStream.html]
|
||||
# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
skip-if = toolkit == 'gonk' || (os == 'linux' && debug && e10s) || android_version == '18'
|
||||
skip-if = toolkit == 'gonk' || android_version == '18'
|
||||
[test_peerConnection_removeVideoTrack.html]
|
||||
# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
skip-if = toolkit == 'gonk' || (os == 'linux' && debug && e10s) || (android_version == '18' && debug)
|
||||
skip-if = toolkit == 'gonk' || (android_version == '18' && debug)
|
||||
[test_peerConnection_removeThenAddVideoTrack.html]
|
||||
# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
skip-if = toolkit == 'gonk' || (os == 'linux' && debug && e10s) || (android_version == '18' && debug)
|
||||
skip-if = toolkit == 'gonk' || (android_version == '18' && debug)
|
||||
[test_peerConnection_replaceVideoThenRenegotiate.html]
|
||||
# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
skip-if = toolkit == 'gonk' || (os == 'linux' && debug && e10s) || (android_version == '18' && debug)
|
||||
skip-if = toolkit == 'gonk' || (android_version == '18' && debug)
|
||||
[test_peerConnection_addSecondAudioStreamNoBundle.html]
|
||||
skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # B2G emulator is too slow to finish a renegotiation test in under 5 minutes, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
[test_peerConnection_removeThenAddAudioTrackNoBundle.html]
|
||||
skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # B2G emulator is too slow to finish a renegotiation test in under 5 minutes, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
[test_peerConnection_addSecondVideoStreamNoBundle.html]
|
||||
# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
skip-if = toolkit == 'gonk' || (os == 'linux' && debug && e10s) || android_version == '18'
|
||||
skip-if = toolkit == 'gonk' || android_version == '18'
|
||||
[test_peerConnection_removeThenAddVideoTrackNoBundle.html]
|
||||
# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
|
||||
skip-if = toolkit == 'gonk' || (os == 'linux' && debug && e10s) || android_version == '18'
|
||||
skip-if = toolkit == 'gonk' || android_version == '18'
|
||||
[test_peerConnection_addDataChannel.html]
|
||||
skip-if = toolkit == 'gonk' # B2G emulator seems to be so slow that DTLS cannot establish properly
|
||||
[test_peerConnection_addDataChannelNoBundle.html]
|
||||
|
@ -2650,7 +2650,12 @@ Notification::CreateAndShow(nsIGlobalObject* aGlobal,
|
||||
MOZ_ASSERT(aGlobal);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init(aGlobal);
|
||||
if (NS_WARN_IF(!jsapi.Init(aGlobal)))
|
||||
{
|
||||
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
RefPtr<Notification> notification = CreateInternal(aGlobal, EmptyString(),
|
||||
|
@ -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]
|
||||
|
99
dom/tv/test/mochitest/mock_data.json
Normal file
99
dom/tv/test/mochitest/mock_data.json
Normal file
@ -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"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -1162,6 +1162,11 @@ public:
|
||||
int32_t limit = 0,
|
||||
int32_t allocLimit = 0);
|
||||
|
||||
/**
|
||||
* Make sure that the given buffer size doesn't exceed the allocation limit.
|
||||
*/
|
||||
static bool CheckBufferSize(int32_t bufSize);
|
||||
|
||||
/** Make sure the given dimension satisfies the CheckSurfaceSize and is
|
||||
* within 8k limit. The 8k value is chosen a bit randomly.
|
||||
*/
|
||||
|
@ -333,6 +333,10 @@ GetCairoSurfaceForSourceSurface(SourceSurface *aSurface,
|
||||
bool aExistingOnly = false,
|
||||
const IntRect& aSubImage = IntRect())
|
||||
{
|
||||
if (!aSurface) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IntRect subimage = IntRect(IntPoint(), aSurface->GetSize());
|
||||
if (!aSubImage.IsEmpty()) {
|
||||
MOZ_ASSERT(!aExistingOnly);
|
||||
@ -792,7 +796,7 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValid()) {
|
||||
if (!IsValid() || !aSurface) {
|
||||
gfxCriticalNote << "DrawSurface with bad surface " << cairo_surface_status(mSurface);
|
||||
return;
|
||||
}
|
||||
|
@ -264,6 +264,12 @@ Factory::AllowedSurfaceSize(const IntSize &aSize)
|
||||
return CheckSurfaceSize(aSize);
|
||||
}
|
||||
|
||||
bool
|
||||
Factory::CheckBufferSize(int32_t bufSize)
|
||||
{
|
||||
return !sConfig || bufSize < sConfig->mMaxAllocSize;
|
||||
}
|
||||
|
||||
bool
|
||||
Factory::CheckSurfaceSize(const IntSize &sz,
|
||||
int32_t extentLimit,
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "mozilla/layers/AsyncDragMetrics.h"
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
#include "mozilla/layers/CompositorTypes.h"
|
||||
#include "ImageTypes.h"
|
||||
#include "FrameMetrics.h"
|
||||
#include "FilterSupport.h"
|
||||
#include "mozilla/layers/GeckoContentController.h"
|
||||
@ -714,6 +715,7 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
|
||||
WriteParam(aMsg, aParam.mMaskLayerIndex);
|
||||
WriteParam(aMsg, aParam.mIsLayersIdRoot);
|
||||
WriteParam(aMsg, aParam.mUsesContainerScrolling);
|
||||
WriteParam(aMsg, aParam.mIsScrollInfoLayer);
|
||||
WriteParam(aMsg, aParam.GetContentDescription());
|
||||
}
|
||||
|
||||
@ -760,6 +762,7 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
|
||||
ReadParam(aMsg, aIter, &aResult->mMaskLayerIndex) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mIsLayersIdRoot) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mUsesContainerScrolling) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mIsScrollInfoLayer) &&
|
||||
ReadContentDescription(aMsg, aIter, aResult));
|
||||
}
|
||||
};
|
||||
@ -797,7 +800,7 @@ template<>
|
||||
struct ParamTraits<mozilla::layers::TextureInfo>
|
||||
{
|
||||
typedef mozilla::layers::TextureInfo paramType;
|
||||
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.mCompositableType);
|
||||
@ -827,6 +830,14 @@ struct ParamTraits<mozilla::gfx::SurfaceFormat>
|
||||
mozilla::gfx::SurfaceFormat::UNKNOWN>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::StereoMode>
|
||||
: public ContiguousEnumSerializer<
|
||||
mozilla::StereoMode,
|
||||
mozilla::StereoMode::MONO,
|
||||
mozilla::StereoMode::TOP_BOTTOM>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::layers::ScrollableLayerGuid>
|
||||
{
|
||||
@ -847,6 +858,7 @@ struct ParamTraits<mozilla::layers::ScrollableLayerGuid>
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::layers::ZoomConstraints>
|
||||
{
|
||||
|
@ -30,10 +30,10 @@ public:
|
||||
|
||||
virtual void Deallocate(ISurfaceAllocator*) override;
|
||||
|
||||
MemoryTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
||||
MemoryTextureData(const BufferDescriptor& aDesc,
|
||||
gfx::BackendType aMoz2DBackend,
|
||||
uint8_t* aBuffer, size_t aBufferSize)
|
||||
: BufferTextureData(aSize, aFormat, aMoz2DBackend)
|
||||
: BufferTextureData(aDesc, aMoz2DBackend)
|
||||
, mBuffer(aBuffer)
|
||||
, mBufferSize(aBufferSize)
|
||||
{
|
||||
@ -67,9 +67,9 @@ public:
|
||||
|
||||
virtual void Deallocate(ISurfaceAllocator* aAllocator) override;
|
||||
|
||||
ShmemTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
||||
ShmemTextureData(const BufferDescriptor& aDesc,
|
||||
gfx::BackendType aMoz2DBackend, mozilla::ipc::Shmem aShmem)
|
||||
: BufferTextureData(aSize, aFormat, aMoz2DBackend)
|
||||
: BufferTextureData(aDesc, aMoz2DBackend)
|
||||
, mShmem(aShmem)
|
||||
{
|
||||
MOZ_ASSERT(mShmem.Size<uint8_t>());
|
||||
@ -97,43 +97,46 @@ BufferTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
||||
}
|
||||
|
||||
BufferTextureData*
|
||||
BufferTextureData::CreateWithBufferSize(ISurfaceAllocator* aAllocator,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
size_t aSize,
|
||||
TextureFlags aTextureFlags)
|
||||
BufferTextureData::CreateInternal(ISurfaceAllocator* aAllocator,
|
||||
const BufferDescriptor& aDesc,
|
||||
gfx::BackendType aMoz2DBackend,
|
||||
int32_t aBufferSize,
|
||||
TextureFlags aTextureFlags)
|
||||
{
|
||||
if (aSize == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BufferTextureData* data;
|
||||
if (!aAllocator || aAllocator->IsSameProcess()) {
|
||||
uint8_t* buffer = new (fallible) uint8_t[aSize];
|
||||
uint8_t* buffer = new (fallible) uint8_t[aBufferSize];
|
||||
if (!buffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
data = new MemoryTextureData(gfx::IntSize(), aFormat, gfx::BackendType::NONE, buffer, aSize);
|
||||
return new MemoryTextureData(aDesc, aMoz2DBackend, buffer, aBufferSize);
|
||||
} else {
|
||||
ipc::Shmem shm;
|
||||
if (!aAllocator->AllocUnsafeShmem(aSize, OptimalShmemType(), &shm)) {
|
||||
if (!aAllocator->AllocUnsafeShmem(aBufferSize, OptimalShmemType(), &shm)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
data = new ShmemTextureData(gfx::IntSize(), aFormat, gfx::BackendType::NONE, shm);
|
||||
return new ShmemTextureData(aDesc, aMoz2DBackend, shm);
|
||||
}
|
||||
}
|
||||
|
||||
BufferTextureData*
|
||||
BufferTextureData::CreateForYCbCrWithBufferSize(ISurfaceAllocator* aAllocator,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
int32_t aBufferSize,
|
||||
TextureFlags aTextureFlags)
|
||||
{
|
||||
if (aBufferSize == 0 || !gfx::Factory::CheckBufferSize(aBufferSize)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Initialize the metadata with something, even if it will have to be rewritten
|
||||
// afterwards since we don't know the dimensions of the texture at this point.
|
||||
if (aFormat == gfx::SurfaceFormat::YUV) {
|
||||
YCbCrImageDataSerializer serializer(data->GetBuffer(), data->GetBufferSize());
|
||||
serializer.InitializeBufferInfo(gfx::IntSize(0,0), gfx::IntSize(0,0), StereoMode::MONO);
|
||||
} else {
|
||||
ImageDataSerializer serializer(data->GetBuffer(), data->GetBufferSize());
|
||||
serializer.InitializeBufferInfo(gfx::IntSize(0, 0), aFormat);
|
||||
}
|
||||
BufferDescriptor desc = YCbCrDescriptor(gfx::IntSize(), gfx::IntSize(),
|
||||
0, 0, 0, StereoMode::MONO);
|
||||
|
||||
return data;
|
||||
return CreateInternal(aAllocator, desc, gfx::BackendType::NONE, aBufferSize,
|
||||
aTextureFlags);
|
||||
}
|
||||
|
||||
BufferTextureData*
|
||||
@ -143,24 +146,41 @@ BufferTextureData::CreateForYCbCr(ISurfaceAllocator* aAllocator,
|
||||
StereoMode aStereoMode,
|
||||
TextureFlags aTextureFlags)
|
||||
{
|
||||
size_t bufSize = YCbCrImageDataSerializer::ComputeMinBufferSize(aYSize, aCbCrSize);
|
||||
BufferTextureData* texture = CreateWithBufferSize(aAllocator, gfx::SurfaceFormat::YUV,
|
||||
bufSize, aTextureFlags);
|
||||
if (!texture) {
|
||||
uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize(aYSize, aCbCrSize);
|
||||
if (bufSize == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
YCbCrImageDataSerializer serializer(texture->GetBuffer(), texture->GetBufferSize());
|
||||
serializer.InitializeBufferInfo(aYSize, aCbCrSize, aStereoMode);
|
||||
texture->mSize = aYSize;
|
||||
uint32_t yOffset;
|
||||
uint32_t cbOffset;
|
||||
uint32_t crOffset;
|
||||
ImageDataSerializer::ComputeYCbCrOffsets(aYSize.width, aYSize.height,
|
||||
aCbCrSize.width, aCbCrSize.height,
|
||||
yOffset, cbOffset, crOffset);
|
||||
|
||||
return texture;
|
||||
YCbCrDescriptor descriptor = YCbCrDescriptor(aYSize, aCbCrSize, yOffset, cbOffset,
|
||||
crOffset, aStereoMode);
|
||||
|
||||
return CreateInternal(aAllocator, descriptor, gfx::BackendType::NONE, bufSize,
|
||||
aTextureFlags);
|
||||
}
|
||||
|
||||
gfx::IntSize
|
||||
BufferTextureData::GetSize() const
|
||||
{
|
||||
return ImageDataSerializer::SizeFromBufferDescriptor(mDescriptor);
|
||||
}
|
||||
|
||||
gfx::SurfaceFormat
|
||||
BufferTextureData::GetFormat() const
|
||||
{
|
||||
return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor);
|
||||
}
|
||||
|
||||
bool
|
||||
BufferTextureData::SupportsMoz2D() const
|
||||
{
|
||||
switch (mFormat) {
|
||||
switch (GetFormat()) {
|
||||
case gfx::SurfaceFormat::YUV:
|
||||
case gfx::SurfaceFormat::NV12:
|
||||
case gfx::SurfaceFormat::UNKNOWN:
|
||||
@ -179,12 +199,17 @@ BufferTextureData::BorrowDrawTarget()
|
||||
return dt.forget();
|
||||
}
|
||||
|
||||
ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
|
||||
if (!serializer.IsValid()) {
|
||||
if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mDrawTarget = serializer.GetAsDrawTarget(mMoz2DBackend);
|
||||
const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
|
||||
|
||||
uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
|
||||
mDrawTarget = gfx::Factory::CreateDrawTargetForData(mMoz2DBackend,
|
||||
GetBuffer(), rgb.size(),
|
||||
stride, rgb.format());
|
||||
|
||||
if (mDrawTarget) {
|
||||
RefPtr<gfx::DrawTarget> dt = mDrawTarget;
|
||||
return dt.forget();
|
||||
@ -192,7 +217,12 @@ BufferTextureData::BorrowDrawTarget()
|
||||
|
||||
// TODO - should we warn? should we really fallback to cairo? perhaps
|
||||
// at least update mMoz2DBackend...
|
||||
mDrawTarget = serializer.GetAsDrawTarget(gfx::BackendType::CAIRO);
|
||||
if (mMoz2DBackend != gfx::BackendType::CAIRO) {
|
||||
mDrawTarget = gfx::Factory::CreateDrawTargetForData(gfx::BackendType::CAIRO,
|
||||
GetBuffer(), rgb.size(),
|
||||
stride, rgb.format());
|
||||
}
|
||||
|
||||
if (!mDrawTarget) {
|
||||
gfxCriticalNote << "BorrowDrawTarget failure, original backend " << (int)mMoz2DBackend;
|
||||
}
|
||||
@ -204,19 +234,16 @@ BufferTextureData::BorrowDrawTarget()
|
||||
bool
|
||||
BufferTextureData::BorrowMappedData(MappedTextureData& aData)
|
||||
{
|
||||
if (mFormat == gfx::SurfaceFormat::YUV) {
|
||||
if (GetFormat() == gfx::SurfaceFormat::YUV) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageDataDeserializer view(GetBuffer(), GetBufferSize());
|
||||
if (!view.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
gfx::IntSize size = GetSize();
|
||||
|
||||
aData.data = view.GetData();
|
||||
aData.size = view.GetSize();
|
||||
aData.stride = view.GetStride();
|
||||
aData.format = mFormat;
|
||||
aData.data = GetBuffer();
|
||||
aData.size = size;
|
||||
aData.format = GetFormat();
|
||||
aData.stride = ImageDataSerializer::ComputeRGBStride(aData.format, size.width);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -224,31 +251,32 @@ BufferTextureData::BorrowMappedData(MappedTextureData& aData)
|
||||
bool
|
||||
BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap)
|
||||
{
|
||||
if (mFormat != gfx::SurfaceFormat::YUV) {
|
||||
if (mDescriptor.type() != BufferDescriptor::TYCbCrDescriptor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
YCbCrImageDataDeserializer view(GetBuffer(), GetBufferSize());
|
||||
if (!view.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
|
||||
|
||||
aMap.stereoMode = view.GetStereoMode();
|
||||
aMap.metadata = GetBuffer();
|
||||
uint8_t* data = GetBuffer();
|
||||
auto ySize = desc.ySize();
|
||||
auto cbCrSize = desc.cbCrSize();
|
||||
|
||||
aMap.y.data = view.GetYData();
|
||||
aMap.y.size = view.GetYSize();
|
||||
aMap.y.stride = view.GetYStride();
|
||||
aMap.stereoMode = desc.stereoMode();
|
||||
aMap.metadata = nullptr;
|
||||
|
||||
aMap.y.data = data + desc.yOffset();
|
||||
aMap.y.size = ySize;
|
||||
aMap.y.stride = ySize.width;
|
||||
aMap.y.skip = 0;
|
||||
|
||||
aMap.cb.data = view.GetCbData();
|
||||
aMap.cb.size = view.GetCbCrSize();
|
||||
aMap.cb.stride = view.GetCbCrStride();
|
||||
aMap.cb.data = data + desc.cbOffset();
|
||||
aMap.cb.size = cbCrSize;
|
||||
aMap.cb.stride = cbCrSize.width;
|
||||
aMap.cb.skip = 0;
|
||||
|
||||
aMap.cr.data = view.GetCrData();
|
||||
aMap.cr.size = view.GetCbCrSize();
|
||||
aMap.cr.stride = view.GetCbCrStride();
|
||||
aMap.cr.data = data + desc.crOffset();
|
||||
aMap.cr.size = cbCrSize;
|
||||
aMap.cr.stride = cbCrSize.width;
|
||||
aMap.cr.skip = 0;
|
||||
|
||||
return true;
|
||||
@ -257,9 +285,15 @@ BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap)
|
||||
bool
|
||||
BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
|
||||
{
|
||||
ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
|
||||
if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) {
|
||||
return false;
|
||||
}
|
||||
const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
|
||||
|
||||
RefPtr<gfx::DataSourceSurface> surface = serializer.GetAsSurface();
|
||||
uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
|
||||
RefPtr<gfx::DataSourceSurface> surface =
|
||||
gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(), stride,
|
||||
rgb.size(), rgb.format());
|
||||
|
||||
if (!surface) {
|
||||
gfxCriticalError() << "Failed to get serializer as surface!";
|
||||
@ -304,6 +338,14 @@ BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BufferTextureData::SetDesciptor(const BufferDescriptor& aDescriptor)
|
||||
{
|
||||
MOZ_ASSERT(mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor);
|
||||
MOZ_ASSERT(mDescriptor.get_YCbCrDescriptor().ySize() == gfx::IntSize());
|
||||
mDescriptor = aDescriptor;
|
||||
}
|
||||
|
||||
bool
|
||||
MemoryTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
|
||||
{
|
||||
@ -312,14 +354,13 @@ MemoryTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
|
||||
return false;
|
||||
}
|
||||
|
||||
aOutDescriptor = SurfaceDescriptorMemory(reinterpret_cast<uintptr_t>(mBuffer),
|
||||
GetFormat());
|
||||
uintptr_t ptr = reinterpret_cast<uintptr_t>(mBuffer);
|
||||
aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(ptr));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool InitBuffer(uint8_t* buf, size_t bufSize,
|
||||
gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
||||
TextureAllocationFlags aAllocFlags)
|
||||
static bool InitBuffer(uint8_t* buf, size_t bufSize, TextureAllocationFlags aAllocFlags)
|
||||
{
|
||||
if (!buf) {
|
||||
gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize << " bytes";
|
||||
@ -333,8 +374,6 @@ static bool InitBuffer(uint8_t* buf, size_t bufSize,
|
||||
memset(buf, 0xFF, bufSize);
|
||||
}
|
||||
|
||||
ImageDataSerializer serializer(buf, bufSize);
|
||||
serializer.InitializeBufferInfo(aSize, aFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -344,24 +383,29 @@ MemoryTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
||||
TextureAllocationFlags aAllocFlags,
|
||||
ISurfaceAllocator*)
|
||||
{
|
||||
// Should have used CreateForYCbCr.
|
||||
MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
|
||||
|
||||
if (aSize.width <= 0 || aSize.height <= 0) {
|
||||
gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t bufSize = ImageDataSerializer::ComputeMinBufferSize(aSize, aFormat);
|
||||
uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
|
||||
if (!bufSize) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8_t* buf = new (fallible) uint8_t[bufSize];
|
||||
|
||||
if (InitBuffer(buf, bufSize, aSize, aFormat, aAllocFlags)) {
|
||||
GfxMemoryImageReporter::DidAlloc(buf);
|
||||
return new MemoryTextureData(aSize, aFormat, aMoz2DBackend, buf, bufSize);
|
||||
if (!InitBuffer(buf, bufSize, aAllocFlags)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
GfxMemoryImageReporter::DidAlloc(buf);
|
||||
|
||||
BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat);
|
||||
|
||||
return new MemoryTextureData(descriptor, aMoz2DBackend, buf, bufSize);
|
||||
}
|
||||
|
||||
void
|
||||
@ -378,7 +422,7 @@ MemoryTextureData::CreateSimilar(ISurfaceAllocator* aAllocator,
|
||||
TextureFlags aFlags,
|
||||
TextureAllocationFlags aAllocFlags) const
|
||||
{
|
||||
return MemoryTextureData::Create(mSize, mFormat, mMoz2DBackend,
|
||||
return MemoryTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend,
|
||||
aFlags, aAllocFlags, aAllocator);
|
||||
}
|
||||
|
||||
@ -390,7 +434,7 @@ ShmemTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
|
||||
return false;
|
||||
}
|
||||
|
||||
aOutDescriptor = SurfaceDescriptorShmem(mShmem, GetFormat());
|
||||
aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(mShmem));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -402,6 +446,9 @@ ShmemTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
||||
ISurfaceAllocator* aAllocator)
|
||||
{
|
||||
MOZ_ASSERT(aAllocator);
|
||||
// Should have used CreateForYCbCr.
|
||||
MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
|
||||
|
||||
if (!aAllocator) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -411,7 +458,7 @@ ShmemTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t bufSize = ImageDataSerializer::ComputeMinBufferSize(aSize, aFormat);
|
||||
uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
|
||||
if (!bufSize) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -422,11 +469,14 @@ ShmemTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
||||
}
|
||||
|
||||
uint8_t* buf = shm.get<uint8_t>();
|
||||
|
||||
if (InitBuffer(buf, bufSize, aSize, aFormat, aAllocFlags)) {
|
||||
return new ShmemTextureData(aSize, aFormat, aMoz2DBackend, shm);
|
||||
if (!InitBuffer(buf, bufSize, aAllocFlags)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat);
|
||||
|
||||
return new ShmemTextureData(descriptor, aMoz2DBackend, shm);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -435,7 +485,7 @@ ShmemTextureData::CreateSimilar(ISurfaceAllocator* aAllocator,
|
||||
TextureFlags aFlags,
|
||||
TextureAllocationFlags aAllocFlags) const
|
||||
{
|
||||
return ShmemTextureData::Create(mSize, mFormat, mMoz2DBackend,
|
||||
return ShmemTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend,
|
||||
aFlags, aAllocFlags, aAllocator);
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,6 @@
|
||||
#ifndef MOZILLA_LAYERS_BUFFERETEXTURE
|
||||
#define MOZILLA_LAYERS_BUFFERETEXTURE
|
||||
|
||||
#include "mozilla/layers/ImageDataSerializer.h"
|
||||
#include "mozilla/layers/YCbCrImageDataSerializer.h"
|
||||
#include "mozilla/layers/TextureClient.h"
|
||||
#include "mozilla/ipc/SharedMemory.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
@ -25,24 +23,27 @@ public:
|
||||
TextureAllocationFlags aAllocFlags,
|
||||
ISurfaceAllocator* aAllocator);
|
||||
|
||||
static BufferTextureData* CreateWithBufferSize(ISurfaceAllocator* aAllocator,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
size_t aSize,
|
||||
TextureFlags aTextureFlags);
|
||||
|
||||
static BufferTextureData* CreateForYCbCr(ISurfaceAllocator* aAllocator,
|
||||
gfx::IntSize aYSize,
|
||||
gfx::IntSize aCbCrSize,
|
||||
StereoMode aStereoMode,
|
||||
TextureFlags aTextureFlags);
|
||||
|
||||
// It is generally better to use CreateForYCbCr instead.
|
||||
// This creates a half-initialized texture since we don't know the sizes and
|
||||
// offsets in the buffer.
|
||||
static BufferTextureData* CreateForYCbCrWithBufferSize(ISurfaceAllocator* aAllocator,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
int32_t aSize,
|
||||
TextureFlags aTextureFlags);
|
||||
|
||||
virtual bool Lock(OpenMode aMode, FenceHandle*) override { return true; }
|
||||
|
||||
virtual void Unlock() override {}
|
||||
|
||||
virtual gfx::IntSize GetSize() const override { return mSize; }
|
||||
virtual gfx::IntSize GetSize() const override;
|
||||
|
||||
virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
|
||||
virtual gfx::SurfaceFormat GetFormat() const override;
|
||||
|
||||
virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
|
||||
|
||||
@ -59,19 +60,26 @@ public:
|
||||
// use TextureClient's default implementation
|
||||
virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
|
||||
|
||||
// Don't use this.
|
||||
void SetDesciptor(const BufferDescriptor& aDesc);
|
||||
|
||||
protected:
|
||||
static BufferTextureData* CreateInternal(ISurfaceAllocator* aAllocator,
|
||||
const BufferDescriptor& aDesc,
|
||||
gfx::BackendType aMoz2DBackend,
|
||||
int32_t aBufferSize,
|
||||
TextureFlags aTextureFlags);
|
||||
|
||||
virtual uint8_t* GetBuffer() = 0;
|
||||
virtual size_t GetBufferSize() = 0;
|
||||
|
||||
BufferTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, gfx::BackendType aMoz2DBackend)
|
||||
: mSize(aSize)
|
||||
, mFormat(aFormat)
|
||||
BufferTextureData(const BufferDescriptor& aDescriptor, gfx::BackendType aMoz2DBackend)
|
||||
: mDescriptor(aDescriptor)
|
||||
, mMoz2DBackend(aMoz2DBackend)
|
||||
{}
|
||||
|
||||
RefPtr<gfx::DrawTarget> mDrawTarget;
|
||||
gfx::IntSize mSize;
|
||||
gfx::SurfaceFormat mFormat;
|
||||
BufferDescriptor mDescriptor;
|
||||
gfx::BackendType mMoz2DBackend;
|
||||
};
|
||||
|
||||
|
@ -68,6 +68,7 @@ public:
|
||||
, mAllowVerticalScrollWithWheel(false)
|
||||
, mIsLayersIdRoot(false)
|
||||
, mUsesContainerScrolling(false)
|
||||
, mIsScrollInfoLayer(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -104,7 +105,8 @@ public:
|
||||
mClipRect == aOther.mClipRect &&
|
||||
mMaskLayerIndex == aOther.mMaskLayerIndex &&
|
||||
mIsLayersIdRoot == aOther.mIsLayersIdRoot &&
|
||||
mUsesContainerScrolling == aOther.mUsesContainerScrolling;
|
||||
mUsesContainerScrolling == aOther.mUsesContainerScrolling &&
|
||||
mIsScrollInfoLayer == aOther.mIsScrollInfoLayer;
|
||||
}
|
||||
bool operator!=(const FrameMetrics& aOther) const
|
||||
{
|
||||
@ -545,6 +547,13 @@ public:
|
||||
return mUsesContainerScrolling;
|
||||
}
|
||||
|
||||
void SetIsScrollInfoLayer(bool aIsScrollInfoLayer) {
|
||||
mIsScrollInfoLayer = aIsScrollInfoLayer;
|
||||
}
|
||||
bool IsScrollInfoLayer() const {
|
||||
return mIsScrollInfoLayer;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// The pres-shell resolution that has been induced on the document containing
|
||||
@ -732,6 +741,9 @@ private:
|
||||
// when containerful scrolling is eliminated.
|
||||
bool mUsesContainerScrolling;
|
||||
|
||||
// Whether or not this frame has a "scroll info layer" to capture events.
|
||||
bool mIsScrollInfoLayer;
|
||||
|
||||
// WARNING!!!!
|
||||
//
|
||||
// When adding new fields to FrameMetrics, the following places should be
|
||||
|
@ -4,70 +4,27 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ImageDataSerializer.h"
|
||||
#include <string.h> // for memcpy
|
||||
#include "gfx2DGlue.h" // for SurfaceFormatToImageFormat
|
||||
#include "mozilla/gfx/Point.h" // for IntSize
|
||||
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
|
||||
#include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory
|
||||
#include "mozilla/gfx/Logging.h" // for gfxDebug
|
||||
#include "mozilla/gfx/Tools.h" // for GetAlignedStride, etc
|
||||
#include "mozilla/gfx/Types.h"
|
||||
#include "mozilla/mozalloc.h" // for operator delete, etc
|
||||
#include "yuv_convert.h" // for ConvertYCbCrToRGB32, etc
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
namespace ImageDataSerializer {
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
// The Data is layed out as follows:
|
||||
//
|
||||
// +-------------------+ -++ --+ <-- ImageDataSerializerBase::mData pointer
|
||||
// | SurfaceBufferInfo | | |
|
||||
// +-------------------+ --+ | offset
|
||||
// | ... | |
|
||||
// +-------------------+ ------+
|
||||
// | |
|
||||
// | data |
|
||||
// | |
|
||||
// +-------------------+
|
||||
#define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
|
||||
|
||||
// Structure written at the beginning of the data blob containing the image
|
||||
// (as shown in the figure above). It contains the necessary informations to
|
||||
// read the image in the blob.
|
||||
namespace {
|
||||
struct SurfaceBufferInfo
|
||||
{
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
SurfaceFormat format;
|
||||
|
||||
static int32_t GetOffset()
|
||||
{
|
||||
return GetAlignedStride<16>(sizeof(SurfaceBufferInfo));
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static SurfaceBufferInfo*
|
||||
GetBufferInfo(uint8_t* aData, size_t aDataSize)
|
||||
{
|
||||
return aDataSize >= sizeof(SurfaceBufferInfo)
|
||||
? reinterpret_cast<SurfaceBufferInfo*>(aData)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
ImageDataSerializer::InitializeBufferInfo(IntSize aSize,
|
||||
SurfaceFormat aFormat)
|
||||
{
|
||||
SurfaceBufferInfo* info = GetBufferInfo(mData, mDataSize);
|
||||
MOZ_ASSERT(info); // OK to assert here, this method is client-side-only
|
||||
info->width = aSize.width;
|
||||
info->height = aSize.height;
|
||||
info->format = aFormat;
|
||||
Validate();
|
||||
}
|
||||
|
||||
static inline int32_t
|
||||
ComputeStride(SurfaceFormat aFormat, int32_t aWidth)
|
||||
int32_t
|
||||
ComputeRGBStride(SurfaceFormat aFormat, int32_t aWidth)
|
||||
{
|
||||
CheckedInt<int32_t> size = BytesPerPixel(aFormat);
|
||||
size *= aWidth;
|
||||
@ -79,9 +36,14 @@ ComputeStride(SurfaceFormat aFormat, int32_t aWidth)
|
||||
return GetAlignedStride<4>(size.value());
|
||||
}
|
||||
|
||||
int32_t
|
||||
GetRGBStride(const RGBDescriptor& aDescriptor)
|
||||
{
|
||||
return ComputeRGBStride(aDescriptor.format(), aDescriptor.size().width);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ImageDataSerializerBase::ComputeMinBufferSize(IntSize aSize,
|
||||
SurfaceFormat aFormat)
|
||||
ComputeRGBBufferSize(IntSize aSize, SurfaceFormat aFormat)
|
||||
{
|
||||
MOZ_ASSERT(aSize.height >= 0 && aSize.width >= 0);
|
||||
|
||||
@ -91,9 +53,8 @@ ImageDataSerializerBase::ComputeMinBufferSize(IntSize aSize,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t bufsize = GetAlignedStride<16>(ComputeStride(aFormat, aSize.width)
|
||||
* aSize.height)
|
||||
+ SurfaceBufferInfo::GetOffset();
|
||||
int32_t bufsize = GetAlignedStride<16>(ComputeRGBStride(aFormat, aSize.width)
|
||||
* aSize.height);
|
||||
|
||||
if (bufsize < 0) {
|
||||
// This should not be possible thanks to Factory::AllowedSurfaceSize
|
||||
@ -103,75 +64,121 @@ ImageDataSerializerBase::ComputeMinBufferSize(IntSize aSize,
|
||||
return bufsize;
|
||||
}
|
||||
|
||||
void
|
||||
ImageDataSerializerBase::Validate()
|
||||
{
|
||||
mIsValid = false;
|
||||
if (!mData) {
|
||||
return;
|
||||
}
|
||||
SurfaceBufferInfo* info = GetBufferInfo(mData, mDataSize);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
size_t requiredSize =
|
||||
ComputeMinBufferSize(IntSize(info->width, info->height), info->format);
|
||||
|
||||
mIsValid = !!requiredSize && requiredSize <= mDataSize;
|
||||
|
||||
// Minimum required shmem size in bytes
|
||||
uint32_t
|
||||
ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride,
|
||||
const gfx::IntSize& aCbCrSize, int32_t aCbCrStride)
|
||||
{
|
||||
MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
|
||||
|
||||
if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0 ||
|
||||
aYSize.width > aYStride || aCbCrSize.width > aCbCrStride ||
|
||||
aCbCrStride > aYStride || aCbCrSize.height > aYSize.height ||
|
||||
!gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height))) {
|
||||
return 0;
|
||||
}
|
||||
// Overflow checks are performed in AllowedSurfaceSize
|
||||
return MOZ_ALIGN_WORD(aYSize.height * aYStride)
|
||||
+ 2 * MOZ_ALIGN_WORD(aCbCrSize.height * aCbCrStride);
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
ImageDataSerializerBase::GetData()
|
||||
// Minimum required shmem size in bytes
|
||||
uint32_t
|
||||
ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, const gfx::IntSize& aCbCrSize)
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
return mData + SurfaceBufferInfo::GetOffset();
|
||||
return ComputeYCbCrBufferSize(aYSize, aYSize.width, aCbCrSize, aCbCrSize.width);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ImageDataSerializerBase::GetStride() const
|
||||
ComputeYCbCrBufferSize(uint32_t aBufferSize)
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
SurfaceBufferInfo* info = GetBufferInfo(mData, mDataSize);
|
||||
return ComputeStride(GetFormat(), info->width);
|
||||
return MOZ_ALIGN_WORD(aBufferSize);
|
||||
}
|
||||
|
||||
IntSize
|
||||
ImageDataSerializerBase::GetSize() const
|
||||
void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight,
|
||||
int32_t cbCrStride, int32_t cbCrHeight,
|
||||
uint32_t& outYOffset, uint32_t& outCbOffset, uint32_t& outCrOffset)
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
SurfaceBufferInfo* info = GetBufferInfo(mData, mDataSize);
|
||||
return IntSize(info->width, info->height);
|
||||
outYOffset = 0;
|
||||
outCbOffset = outYOffset + MOZ_ALIGN_WORD(yStride * yHeight);
|
||||
outCrOffset = outCbOffset + MOZ_ALIGN_WORD(cbCrStride * cbCrHeight);
|
||||
}
|
||||
|
||||
SurfaceFormat
|
||||
ImageDataSerializerBase::GetFormat() const
|
||||
gfx::SurfaceFormat FormatFromBufferDescriptor(const BufferDescriptor& aDescriptor)
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
return GetBufferInfo(mData, mDataSize)->format;
|
||||
}
|
||||
|
||||
already_AddRefed<DrawTarget>
|
||||
ImageDataSerializerBase::GetAsDrawTarget(gfx::BackendType aBackend)
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
RefPtr<DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(aBackend,
|
||||
GetData(), GetSize(),
|
||||
GetStride(), GetFormat());
|
||||
if (!dt) {
|
||||
gfxCriticalNote << "Failed GetAsDrawTarget " << IsValid() << ", " << hexa(size_t(mData)) << " + " << SurfaceBufferInfo::GetOffset() << ", " << GetSize() << ", " << GetStride() << ", " << (int)GetFormat();
|
||||
switch (aDescriptor.type()) {
|
||||
case BufferDescriptor::TRGBDescriptor:
|
||||
return aDescriptor.get_RGBDescriptor().format();
|
||||
case BufferDescriptor::TYCbCrDescriptor:
|
||||
return gfx::SurfaceFormat::YUV;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
return dt.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
ImageDataSerializerBase::GetAsSurface()
|
||||
gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor)
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
return Factory::CreateWrappingDataSourceSurface(GetData(),
|
||||
GetStride(),
|
||||
GetSize(),
|
||||
GetFormat());
|
||||
switch (aDescriptor.type()) {
|
||||
case BufferDescriptor::TRGBDescriptor:
|
||||
return aDescriptor.get_RGBDescriptor().size();
|
||||
case BufferDescriptor::TYCbCrDescriptor:
|
||||
return aDescriptor.get_YCbCrDescriptor().ySize();
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor)
|
||||
{
|
||||
return aBuffer + aDescriptor.yOffset();
|
||||
}
|
||||
|
||||
uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor)
|
||||
{
|
||||
return aBuffer + aDescriptor.cbOffset();
|
||||
}
|
||||
|
||||
uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor)
|
||||
{
|
||||
return aBuffer + aDescriptor.crOffset();
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
DataSourceSurfaceFromYCbCrDescriptor(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor)
|
||||
{
|
||||
gfx::IntSize ySize = aDescriptor.ySize();
|
||||
gfx::IntSize cbCrSize = aDescriptor.cbCrSize();
|
||||
int32_t yStride = ySize.width;
|
||||
int32_t cbCrStride = cbCrSize.width;
|
||||
|
||||
RefPtr<DataSourceSurface> result =
|
||||
Factory::CreateDataSourceSurface(ySize, gfx::SurfaceFormat::B8G8R8X8);
|
||||
if (NS_WARN_IF(!result)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (NS_WARN_IF(!result->Map(DataSourceSurface::MapType::WRITE, &map))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gfx::YUVType type = TypeFromSize(ySize.width, ySize.height,
|
||||
cbCrSize.width, cbCrSize.height);
|
||||
gfx::ConvertYCbCrToRGB32(GetYChannel(aBuffer, aDescriptor),
|
||||
GetCbChannel(aBuffer, aDescriptor),
|
||||
GetCrChannel(aBuffer, aDescriptor),
|
||||
map.mData,
|
||||
0, 0, //pic x and y
|
||||
ySize.width, ySize.height,
|
||||
yStride, cbCrStride,
|
||||
map.mStride, type);
|
||||
result->Unmap();
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
|
||||
} // namespace ImageDataSerializer
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
@ -24,72 +24,49 @@ class DrawTarget;
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class ImageDataSerializerBase
|
||||
{
|
||||
public:
|
||||
bool IsValid() const { return mIsValid; }
|
||||
namespace ImageDataSerializer {
|
||||
|
||||
uint8_t* GetData();
|
||||
uint32_t GetStride() const;
|
||||
gfx::IntSize GetSize() const;
|
||||
gfx::SurfaceFormat GetFormat() const;
|
||||
already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
|
||||
already_AddRefed<gfx::DrawTarget> GetAsDrawTarget(gfx::BackendType aBackend);
|
||||
// RGB
|
||||
|
||||
static uint32_t ComputeMinBufferSize(gfx::IntSize aSize,
|
||||
gfx::SurfaceFormat aFormat);
|
||||
int32_t ComputeRGBStride(gfx::SurfaceFormat aFormat, int32_t aWidth);
|
||||
|
||||
size_t GetBufferSize() const { return mDataSize; }
|
||||
int32_t GetRGBStride(const RGBDescriptor& aDescriptor);
|
||||
|
||||
protected:
|
||||
uint32_t ComputeRGBBufferSize(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
|
||||
|
||||
ImageDataSerializerBase(uint8_t* aData, size_t aDataSize)
|
||||
: mData(aData)
|
||||
, mDataSize(aDataSize)
|
||||
, mIsValid(false)
|
||||
{}
|
||||
|
||||
void Validate();
|
||||
// YCbCr
|
||||
|
||||
uint8_t* mData;
|
||||
size_t mDataSize;
|
||||
bool mIsValid;
|
||||
};
|
||||
///This function is meant as a helper to know how much shared memory we need
|
||||
///to allocate in a shmem in order to place a shared YCbCr image blob of
|
||||
///given dimensions.
|
||||
uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize,
|
||||
int32_t aYStride,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
int32_t aCbCrStride);
|
||||
uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize);
|
||||
|
||||
/**
|
||||
* A facility to serialize an image into a buffer of memory.
|
||||
* This is intended for use with the IPC code, in order to copy image data
|
||||
* into shared memory.
|
||||
* Note that there is a separate serializer class for YCbCr images
|
||||
* (see YCbCrImageDataSerializer.h).
|
||||
*/
|
||||
class MOZ_STACK_CLASS ImageDataSerializer : public ImageDataSerializerBase
|
||||
{
|
||||
public:
|
||||
ImageDataSerializer(uint8_t* aData, size_t aDataSize)
|
||||
: ImageDataSerializerBase(aData, aDataSize)
|
||||
{
|
||||
// a serializer needs to be usable before correct buffer info has been written to it
|
||||
mIsValid = !!mData;
|
||||
}
|
||||
void InitializeBufferInfo(gfx::IntSize aSize,
|
||||
gfx::SurfaceFormat aFormat);
|
||||
};
|
||||
uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize);
|
||||
|
||||
/**
|
||||
* A facility to deserialize image data that has been serialized by an
|
||||
* ImageDataSerializer.
|
||||
*/
|
||||
class MOZ_STACK_CLASS ImageDataDeserializer : public ImageDataSerializerBase
|
||||
{
|
||||
public:
|
||||
ImageDataDeserializer(uint8_t* aData, size_t aDataSize)
|
||||
: ImageDataSerializerBase(aData, aDataSize)
|
||||
{
|
||||
Validate();
|
||||
}
|
||||
void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight,
|
||||
int32_t cbCrStride, int32_t cbCrHeight,
|
||||
uint32_t& outYOffset, uint32_t& outCbOffset, uint32_t& outCrOffset);
|
||||
|
||||
};
|
||||
gfx::SurfaceFormat FormatFromBufferDescriptor(const BufferDescriptor& aDescriptor);
|
||||
|
||||
gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
|
||||
|
||||
uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
|
||||
|
||||
uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
|
||||
|
||||
uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
DataSourceSurfaceFromYCbCrDescriptor(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
|
||||
|
||||
} // ImageDataSerializer
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
@ -1,317 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* 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/layers/YCbCrImageDataSerializer.h"
|
||||
#include <string.h> // for memcpy
|
||||
#include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory
|
||||
#include "mozilla/gfx/BaseSize.h" // for BaseSize
|
||||
#include "mozilla/gfx/Logging.h" // for gfxDebug
|
||||
#include "mozilla/gfx/Types.h"
|
||||
#include "mozilla/mozalloc.h" // for operator delete
|
||||
#include "nsDebug.h" // for NS_WARN_IF
|
||||
#include "yuv_convert.h" // for ConvertYCbCrToRGB32, etc
|
||||
#include "nsDebug.h"
|
||||
|
||||
#define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
namespace layers {
|
||||
|
||||
// The Data is layed out as follows:
|
||||
//
|
||||
// +-----------------+ -++ --+ --+ <-- Beginning of the buffer
|
||||
// | YCbCrBufferInfo | | | |
|
||||
// +-----------------+ --+ | |
|
||||
// | data | | | YCbCrBufferInfo->[mY/mCb/mCr]Offset
|
||||
// +-----------------+ ------+ |
|
||||
// | data | |
|
||||
// +-----------------+ ----------+
|
||||
// | data |
|
||||
// +-----------------+
|
||||
//
|
||||
// There can be padding between the blocks above to keep word alignment.
|
||||
|
||||
// Structure written at the beginning og the data blob containing the image
|
||||
// (as shown in the figure above). It contains the necessary informations to
|
||||
// read the image in the blob.
|
||||
struct YCbCrBufferInfo
|
||||
{
|
||||
uint32_t mYOffset;
|
||||
uint32_t mCbOffset;
|
||||
uint32_t mCrOffset;
|
||||
uint32_t mYStride;
|
||||
uint32_t mYWidth;
|
||||
uint32_t mYHeight;
|
||||
uint32_t mCbCrStride;
|
||||
uint32_t mCbCrWidth;
|
||||
uint32_t mCbCrHeight;
|
||||
StereoMode mStereoMode;
|
||||
};
|
||||
|
||||
static YCbCrBufferInfo* GetYCbCrBufferInfo(uint8_t* aData, size_t aDataSize)
|
||||
{
|
||||
return aDataSize >= sizeof(YCbCrBufferInfo)
|
||||
? reinterpret_cast<YCbCrBufferInfo*>(aData)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
void YCbCrImageDataDeserializerBase::Validate()
|
||||
{
|
||||
mIsValid = false;
|
||||
if (!mData) {
|
||||
return;
|
||||
}
|
||||
YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
size_t requiredSize = ComputeMinBufferSize(
|
||||
IntSize(info->mYWidth, info->mYHeight),
|
||||
info->mYStride,
|
||||
IntSize(info->mCbCrWidth, info->mCbCrHeight),
|
||||
info->mCbCrStride);
|
||||
mIsValid = requiredSize <= mDataSize;
|
||||
|
||||
}
|
||||
|
||||
uint8_t* YCbCrImageDataDeserializerBase::GetYData()
|
||||
{
|
||||
YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize);
|
||||
return reinterpret_cast<uint8_t*>(info) + info->mYOffset;
|
||||
}
|
||||
|
||||
uint8_t* YCbCrImageDataDeserializerBase::GetCbData()
|
||||
{
|
||||
YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize);
|
||||
return reinterpret_cast<uint8_t*>(info) + info->mCbOffset;
|
||||
}
|
||||
|
||||
uint8_t* YCbCrImageDataDeserializerBase::GetCrData()
|
||||
{
|
||||
YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize);
|
||||
return reinterpret_cast<uint8_t*>(info) + info->mCrOffset;
|
||||
}
|
||||
|
||||
uint8_t* YCbCrImageDataDeserializerBase::GetData()
|
||||
{
|
||||
YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize);
|
||||
return (reinterpret_cast<uint8_t*>(info)) + MOZ_ALIGN_WORD(sizeof(YCbCrBufferInfo));
|
||||
}
|
||||
|
||||
uint32_t YCbCrImageDataDeserializerBase::GetYStride()
|
||||
{
|
||||
YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize);
|
||||
return info->mYStride;
|
||||
}
|
||||
|
||||
uint32_t YCbCrImageDataDeserializerBase::GetCbCrStride()
|
||||
{
|
||||
YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize);
|
||||
return info->mCbCrStride;
|
||||
}
|
||||
|
||||
gfx::IntSize YCbCrImageDataDeserializerBase::GetYSize()
|
||||
{
|
||||
YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize);
|
||||
return gfx::IntSize(info->mYWidth, info->mYHeight);
|
||||
}
|
||||
|
||||
gfx::IntSize YCbCrImageDataDeserializerBase::GetCbCrSize()
|
||||
{
|
||||
YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize);
|
||||
return gfx::IntSize(info->mCbCrWidth, info->mCbCrHeight);
|
||||
}
|
||||
|
||||
StereoMode YCbCrImageDataDeserializerBase::GetStereoMode()
|
||||
{
|
||||
YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize);
|
||||
return info->mStereoMode;
|
||||
}
|
||||
|
||||
// Offset in bytes
|
||||
static size_t ComputeOffset(uint32_t aHeight, uint32_t aStride)
|
||||
{
|
||||
return MOZ_ALIGN_WORD(aHeight * aStride);
|
||||
}
|
||||
|
||||
// Minimum required shmem size in bytes
|
||||
size_t
|
||||
YCbCrImageDataDeserializerBase::ComputeMinBufferSize(const gfx::IntSize& aYSize,
|
||||
uint32_t aYStride,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
uint32_t aCbCrStride)
|
||||
{
|
||||
MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
|
||||
if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0) {
|
||||
gfxDebug() << "Non-positive YCbCr buffer size request " << aYSize.height << "x" << aYSize.width << ", " << aCbCrSize.height << "x" << aCbCrSize.width;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (aYSize != IntSize() &&
|
||||
(!gfx::Factory::AllowedSurfaceSize(aYSize) ||
|
||||
aCbCrSize.width > aYSize.width ||
|
||||
aCbCrSize.height > aYSize.height)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ComputeOffset(aYSize.height, aYStride)
|
||||
+ 2 * ComputeOffset(aCbCrSize.height, aCbCrStride)
|
||||
+ MOZ_ALIGN_WORD(sizeof(YCbCrBufferInfo));
|
||||
}
|
||||
|
||||
// Minimum required shmem size in bytes
|
||||
size_t
|
||||
YCbCrImageDataDeserializerBase::ComputeMinBufferSize(const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize)
|
||||
{
|
||||
return ComputeMinBufferSize(aYSize, aYSize.width, aCbCrSize, aCbCrSize.width);
|
||||
}
|
||||
|
||||
// Offset in bytes
|
||||
static size_t ComputeOffset(uint32_t aSize)
|
||||
{
|
||||
return MOZ_ALIGN_WORD(aSize);
|
||||
}
|
||||
|
||||
// Minimum required shmem size in bytes
|
||||
size_t
|
||||
YCbCrImageDataDeserializerBase::ComputeMinBufferSize(uint32_t aSize)
|
||||
{
|
||||
return ComputeOffset(aSize) + MOZ_ALIGN_WORD(sizeof(YCbCrBufferInfo));
|
||||
}
|
||||
|
||||
void
|
||||
YCbCrImageDataSerializer::InitializeBufferInfo(uint32_t aYOffset,
|
||||
uint32_t aCbOffset,
|
||||
uint32_t aCrOffset,
|
||||
uint32_t aYStride,
|
||||
uint32_t aCbCrStride,
|
||||
const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
StereoMode aStereoMode)
|
||||
{
|
||||
YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize);
|
||||
MOZ_ASSERT(info); // OK to assert here, this method is client-side-only
|
||||
uint32_t info_size = MOZ_ALIGN_WORD(sizeof(YCbCrBufferInfo));
|
||||
info->mYOffset = info_size + aYOffset;
|
||||
info->mCbOffset = info_size + aCbOffset;
|
||||
info->mCrOffset = info_size + aCrOffset;
|
||||
info->mYStride = aYStride;
|
||||
info->mYWidth = aYSize.width;
|
||||
info->mYHeight = aYSize.height;
|
||||
info->mCbCrStride = aCbCrStride;
|
||||
info->mCbCrWidth = aCbCrSize.width;
|
||||
info->mCbCrHeight = aCbCrSize.height;
|
||||
info->mStereoMode = aStereoMode;
|
||||
Validate();
|
||||
}
|
||||
|
||||
void
|
||||
YCbCrImageDataSerializer::InitializeBufferInfo(uint32_t aYStride,
|
||||
uint32_t aCbCrStride,
|
||||
const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
StereoMode aStereoMode)
|
||||
{
|
||||
uint32_t yOffset = 0;
|
||||
uint32_t cbOffset = yOffset + MOZ_ALIGN_WORD(aYStride * aYSize.height);
|
||||
uint32_t crOffset = cbOffset + MOZ_ALIGN_WORD(aCbCrStride * aCbCrSize.height);
|
||||
return InitializeBufferInfo(yOffset, cbOffset, crOffset,
|
||||
aYStride, aCbCrStride, aYSize, aCbCrSize, aStereoMode);
|
||||
}
|
||||
|
||||
void
|
||||
YCbCrImageDataSerializer::InitializeBufferInfo(const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
StereoMode aStereoMode)
|
||||
{
|
||||
return InitializeBufferInfo(aYSize.width, aCbCrSize.width, aYSize, aCbCrSize, aStereoMode);
|
||||
}
|
||||
|
||||
static void CopyLineWithSkip(const uint8_t* src, uint8_t* dst, uint32_t len, uint32_t skip) {
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
*dst = *src;
|
||||
src += 1 + skip;
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
YCbCrImageDataSerializer::CopyData(const uint8_t* aYData,
|
||||
const uint8_t* aCbData, const uint8_t* aCrData,
|
||||
gfx::IntSize aYSize, uint32_t aYStride,
|
||||
gfx::IntSize aCbCrSize, uint32_t aCbCrStride,
|
||||
uint32_t aYSkip, uint32_t aCbCrSkip)
|
||||
{
|
||||
if (!IsValid() || GetYSize() != aYSize || GetCbCrSize() != aCbCrSize) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < aYSize.height; ++i) {
|
||||
if (aYSkip == 0) {
|
||||
// fast path
|
||||
memcpy(GetYData() + i * GetYStride(),
|
||||
aYData + i * aYStride,
|
||||
aYSize.width);
|
||||
} else {
|
||||
// slower path
|
||||
CopyLineWithSkip(aYData + i * aYStride,
|
||||
GetYData() + i * GetYStride(),
|
||||
aYSize.width, aYSkip);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < aCbCrSize.height; ++i) {
|
||||
if (aCbCrSkip == 0) {
|
||||
// fast path
|
||||
memcpy(GetCbData() + i * GetCbCrStride(),
|
||||
aCbData + i * aCbCrStride,
|
||||
aCbCrSize.width);
|
||||
memcpy(GetCrData() + i * GetCbCrStride(),
|
||||
aCrData + i * aCbCrStride,
|
||||
aCbCrSize.width);
|
||||
} else {
|
||||
// slower path
|
||||
CopyLineWithSkip(aCbData + i * aCbCrStride,
|
||||
GetCbData() + i * GetCbCrStride(),
|
||||
aCbCrSize.width, aCbCrSkip);
|
||||
CopyLineWithSkip(aCrData + i * aCbCrStride,
|
||||
GetCrData() + i * GetCbCrStride(),
|
||||
aCbCrSize.width, aCbCrSkip);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
YCbCrImageDataDeserializer::ToDataSourceSurface()
|
||||
{
|
||||
RefPtr<DataSourceSurface> result =
|
||||
Factory::CreateDataSourceSurface(GetYSize(), gfx::SurfaceFormat::B8G8R8X8);
|
||||
if (NS_WARN_IF(!result)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (NS_WARN_IF(!result->Map(DataSourceSurface::MapType::WRITE, &map))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gfx::YUVType type = TypeFromSize(GetYSize().width, GetYSize().height,
|
||||
GetCbCrSize().width, GetCbCrSize().height);
|
||||
gfx::ConvertYCbCrToRGB32(GetYData(), GetCbData(), GetCrData(),
|
||||
map.mData,
|
||||
0, 0, //pic x and y
|
||||
GetYSize().width, GetYSize().height,
|
||||
GetYStride(), GetCbCrStride(),
|
||||
map.mStride, type);
|
||||
result->Unmap();
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
@ -1,186 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* 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_LAYERS_BLOBYCBCRSURFACE_H
|
||||
#define MOZILLA_LAYERS_BLOBYCBCRSURFACE_H
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <stdint.h> // for uint8_t, uint32_t
|
||||
#include "ImageTypes.h" // for StereoMode
|
||||
#include "mozilla/Attributes.h" // for MOZ_STACK_CLASS
|
||||
#include "mozilla/RefPtr.h" // for already_AddRefed
|
||||
#include "mozilla/gfx/Point.h" // for IntSize
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
class DataSourceSurface;
|
||||
} // namespace gfx
|
||||
|
||||
namespace layers {
|
||||
|
||||
class Image;
|
||||
|
||||
/**
|
||||
* Convenience class to share code between YCbCrImageDataSerializer
|
||||
* and YCbCrImageDataDeserializer.
|
||||
* Do not use it.
|
||||
*/
|
||||
class YCbCrImageDataDeserializerBase
|
||||
{
|
||||
public:
|
||||
bool IsValid() const { return mIsValid; }
|
||||
|
||||
/**
|
||||
* Returns the Y channel data pointer.
|
||||
*/
|
||||
uint8_t* GetYData();
|
||||
/**
|
||||
* Returns the Cb channel data pointer.
|
||||
*/
|
||||
uint8_t* GetCbData();
|
||||
/**
|
||||
* Returns the Cr channel data pointer.
|
||||
*/
|
||||
uint8_t* GetCrData();
|
||||
|
||||
/**
|
||||
* Returns the Y channel stride.
|
||||
*/
|
||||
uint32_t GetYStride();
|
||||
/**
|
||||
* Returns the stride of the Cb and Cr channels.
|
||||
*/
|
||||
uint32_t GetCbCrStride();
|
||||
|
||||
/**
|
||||
* Returns the dimensions of the Y Channel.
|
||||
*/
|
||||
gfx::IntSize GetYSize();
|
||||
|
||||
/**
|
||||
* Returns the dimensions of the Cb and Cr Channel.
|
||||
*/
|
||||
gfx::IntSize GetCbCrSize();
|
||||
|
||||
/**
|
||||
* Stereo mode for the image.
|
||||
*/
|
||||
StereoMode GetStereoMode();
|
||||
|
||||
/**
|
||||
* Return a pointer to the begining of the data buffer.
|
||||
*/
|
||||
uint8_t* GetData();
|
||||
|
||||
/**
|
||||
* This function is meant as a helper to know how much shared memory we need
|
||||
* to allocate in a shmem in order to place a shared YCbCr image blob of
|
||||
* given dimensions.
|
||||
*/
|
||||
static size_t ComputeMinBufferSize(const gfx::IntSize& aYSize,
|
||||
uint32_t aYStride,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
uint32_t aCbCrStride);
|
||||
static size_t ComputeMinBufferSize(const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize);
|
||||
static size_t ComputeMinBufferSize(uint32_t aSize);
|
||||
|
||||
protected:
|
||||
YCbCrImageDataDeserializerBase(uint8_t* aData, size_t aDataSize)
|
||||
: mData (aData)
|
||||
, mDataSize(aDataSize)
|
||||
, mIsValid(false)
|
||||
{}
|
||||
|
||||
void Validate();
|
||||
|
||||
uint8_t* mData;
|
||||
size_t mDataSize;
|
||||
bool mIsValid;
|
||||
};
|
||||
|
||||
/**
|
||||
* A view on a YCbCr image stored with its metadata in a blob of memory.
|
||||
* It is only meant as a convenience to access the image data, and does not own
|
||||
* the data. The instance can live on the stack and used as follows:
|
||||
*
|
||||
* const YCbCrImage& yuv = sharedImage.get_YCbCrImage();
|
||||
* YCbCrImageDataDeserializer deserializer(yuv.data().get<uint8_t>());
|
||||
* if (!deserializer.IsValid()) {
|
||||
* // handle error
|
||||
* }
|
||||
* size = deserializer.GetYSize(); // work with the data, etc...
|
||||
*/
|
||||
class MOZ_STACK_CLASS YCbCrImageDataSerializer : public YCbCrImageDataDeserializerBase
|
||||
{
|
||||
public:
|
||||
YCbCrImageDataSerializer(uint8_t* aData, size_t aDataSize)
|
||||
: YCbCrImageDataDeserializerBase(aData, aDataSize)
|
||||
{
|
||||
// a serializer needs to be usable before correct buffer info has been written to it
|
||||
mIsValid = !!mData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the image informations in the buffer for given dimensions.
|
||||
* The provided pointer should point to the beginning of the (chunk of)
|
||||
* buffer on which we want to store the image.
|
||||
*/
|
||||
void InitializeBufferInfo(uint32_t aYOffset,
|
||||
uint32_t aCbOffset,
|
||||
uint32_t aCrOffset,
|
||||
uint32_t aYStride,
|
||||
uint32_t aCbCrStride,
|
||||
const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
StereoMode aStereoMode);
|
||||
void InitializeBufferInfo(uint32_t aYStride,
|
||||
uint32_t aCbCrStride,
|
||||
const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
StereoMode aStereoMode);
|
||||
void InitializeBufferInfo(const gfx::IntSize& aYSize,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
StereoMode aStereoMode);
|
||||
bool CopyData(const uint8_t* aYData,
|
||||
const uint8_t* aCbData, const uint8_t* aCrData,
|
||||
gfx::IntSize aYSize, uint32_t aYStride,
|
||||
gfx::IntSize aCbCrSize, uint32_t aCbCrStride,
|
||||
uint32_t aYSkip, uint32_t aCbCrSkip);
|
||||
};
|
||||
|
||||
/**
|
||||
* A view on a YCbCr image stored with its metadata in a blob of memory.
|
||||
* It is only meant as a convenience to access the image data, and does not own
|
||||
* the data. The instance can live on the stack and used as follows:
|
||||
*
|
||||
* const YCbCrImage& yuv = sharedImage.get_YCbCrImage();
|
||||
* YCbCrImageDataDeserializer deserializer(yuv.data().get<uint8_t>());
|
||||
* if (!deserializer.IsValid()) {
|
||||
* // handle error
|
||||
* }
|
||||
* size = deserializer.GetYSize(); // work with the data, etc...
|
||||
*/
|
||||
class MOZ_STACK_CLASS YCbCrImageDataDeserializer : public YCbCrImageDataDeserializerBase
|
||||
{
|
||||
public:
|
||||
YCbCrImageDataDeserializer(uint8_t* aData, size_t aDataSize)
|
||||
: YCbCrImageDataDeserializerBase(aData, aDataSize)
|
||||
{
|
||||
Validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the YCbCr data into RGB and return a DataSourceSurface.
|
||||
* This is a costly operation, so use it only when YCbCr compositing is
|
||||
* not supported.
|
||||
*/
|
||||
already_AddRefed<gfx::DataSourceSurface> ToDataSourceSurface();
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -28,7 +28,6 @@
|
||||
#include "nsPoint.h" // for nsIntPoint
|
||||
#include "nsThreadUtils.h" // for NS_IsMainThread
|
||||
#include "OverscrollHandoffState.h" // for OverscrollHandoffState
|
||||
#include "TaskThrottler.h" // for TaskThrottler
|
||||
#include "TreeTraversal.h" // for generic tree traveral algorithms
|
||||
#include "LayersLogging.h" // for Stringify
|
||||
#include "Units.h" // for ParentlayerPixel
|
||||
@ -86,11 +85,10 @@ struct APZCTreeManager::TreeBuildingState {
|
||||
/*static*/ const ScreenMargin
|
||||
APZCTreeManager::CalculatePendingDisplayPort(
|
||||
const FrameMetrics& aFrameMetrics,
|
||||
const ParentLayerPoint& aVelocity,
|
||||
double aEstimatedPaintDuration)
|
||||
const ParentLayerPoint& aVelocity)
|
||||
{
|
||||
return AsyncPanZoomController::CalculatePendingDisplayPort(
|
||||
aFrameMetrics, aVelocity, aEstimatedPaintDuration);
|
||||
aFrameMetrics, aVelocity);
|
||||
}
|
||||
|
||||
APZCTreeManager::APZCTreeManager()
|
||||
@ -111,11 +109,10 @@ APZCTreeManager::~APZCTreeManager()
|
||||
|
||||
AsyncPanZoomController*
|
||||
APZCTreeManager::NewAPZCInstance(uint64_t aLayersId,
|
||||
GeckoContentController* aController,
|
||||
TaskThrottler* aPaintThrottler)
|
||||
GeckoContentController* aController)
|
||||
{
|
||||
return new AsyncPanZoomController(aLayersId, this, mInputQueue,
|
||||
aController, aPaintThrottler, AsyncPanZoomController::USE_GESTURE_DETECTOR);
|
||||
aController, AsyncPanZoomController::USE_GESTURE_DETECTOR);
|
||||
}
|
||||
|
||||
TimeStamp
|
||||
@ -201,32 +198,6 @@ APZCTreeManager::UpdateHitTestingTree(CompositorParent* aCompositor,
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManager::InitializeForLayersId(uint64_t aLayersId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
auto throttlerInsertResult = mPaintThrottlerMap.insert(
|
||||
std::make_pair(aLayersId, RefPtr<TaskThrottler>()));
|
||||
if (throttlerInsertResult.second) {
|
||||
throttlerInsertResult.first->second = new TaskThrottler(
|
||||
GetFrameTime(), TimeDuration::FromMilliseconds(500));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManager::AdoptLayersId(uint64_t aLayersId, APZCTreeManager* aOldManager)
|
||||
{
|
||||
MOZ_ASSERT(aOldManager);
|
||||
if (aOldManager == this) {
|
||||
return;
|
||||
}
|
||||
auto iter = aOldManager->mPaintThrottlerMap.find(aLayersId);
|
||||
if (iter != aOldManager->mPaintThrottlerMap.end()) {
|
||||
mPaintThrottlerMap[aLayersId] = iter->second;
|
||||
aOldManager->mPaintThrottlerMap.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the clip region to be used for a layer with an APZC. This function
|
||||
// is only called for layers which actually have scrollable metrics and an APZC.
|
||||
static ParentLayerIntRegion
|
||||
@ -462,12 +433,7 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
|
||||
// a destroyed APZC and so we need to throw that out and make a new one.
|
||||
bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
|
||||
if (newApzc) {
|
||||
// Look up the paint throttler for this layers id, or create it if
|
||||
// this is the first APZC for this layers id.
|
||||
RefPtr<TaskThrottler> throttler = mPaintThrottlerMap[aLayersId];
|
||||
MOZ_ASSERT(throttler);
|
||||
|
||||
apzc = NewAPZCInstance(aLayersId, state->mController, throttler);
|
||||
apzc = NewAPZCInstance(aLayersId, state->mController);
|
||||
apzc->SetCompositorParent(aState.mCompositor);
|
||||
if (state->mCrossProcessParent != nullptr) {
|
||||
apzc->ShareFrameMetricsAcrossProcesses();
|
||||
@ -657,23 +623,10 @@ WillHandleInput(const PanGestureOrScrollWheelInput& aPanInput)
|
||||
void
|
||||
APZCTreeManager::FlushApzRepaints(uint64_t aLayersId)
|
||||
{
|
||||
// Previously, paints were throttled and therefore this method was used to
|
||||
// ensure any pending paints were flushed. Now, paints are flushed
|
||||
// immediately, so it is safe to simply send a notification now.
|
||||
APZCTM_LOG("Flushing repaints for layers id %" PRIu64, aLayersId);
|
||||
{ // scope lock
|
||||
MonitorAutoLock lock(mTreeLock);
|
||||
mTreeLock.AssertCurrentThreadOwns();
|
||||
|
||||
ForEachNode(mRootNode.get(),
|
||||
[aLayersId](HitTestingTreeNode* aNode)
|
||||
{
|
||||
if (aNode->IsPrimaryHolder()) {
|
||||
AsyncPanZoomController* apzc = aNode->GetApzc();
|
||||
MOZ_ASSERT(apzc);
|
||||
if (apzc->GetGuid().mLayersId == aLayersId) {
|
||||
apzc->FlushRepaintIfPending();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
|
||||
MOZ_ASSERT(state && state->mController);
|
||||
NS_DispatchToMainThread(NS_NewRunnableMethod(
|
||||
|
@ -52,7 +52,6 @@ class LayerMetricsWrapper;
|
||||
class InputQueue;
|
||||
class GeckoContentController;
|
||||
class HitTestingTreeNode;
|
||||
class TaskThrottler;
|
||||
|
||||
/**
|
||||
* ****************** NOTE ON LOCK ORDERING IN APZ **************************
|
||||
@ -137,17 +136,6 @@ public:
|
||||
uint64_t aOriginatingLayersId,
|
||||
uint32_t aPaintSequenceNumber);
|
||||
|
||||
/**
|
||||
* Do any per-layers-id setup needed. This will be called on the main thread,
|
||||
* and may be called multiple times for the same layers id.
|
||||
*/
|
||||
void InitializeForLayersId(uint64_t aLayersId);
|
||||
|
||||
/**
|
||||
* Move any per-layers-id state from the old APZCTreeManager to this one.
|
||||
*/
|
||||
void AdoptLayersId(uint64_t aLayersId, APZCTreeManager* aOldManager);
|
||||
|
||||
/**
|
||||
* Walk the tree of APZCs and flushes the repaint requests for all the APZCS
|
||||
* corresponding to the given layers id. Finally, sends a flush complete
|
||||
@ -286,8 +274,7 @@ public:
|
||||
*/
|
||||
static const ScreenMargin CalculatePendingDisplayPort(
|
||||
const FrameMetrics& aFrameMetrics,
|
||||
const ParentLayerPoint& aVelocity,
|
||||
double aEstimatedPaintDuration);
|
||||
const ParentLayerPoint& aVelocity);
|
||||
|
||||
/**
|
||||
* Set the dpi value used by all AsyncPanZoomControllers.
|
||||
@ -420,8 +407,7 @@ protected:
|
||||
|
||||
// Protected hooks for gtests subclass
|
||||
virtual AsyncPanZoomController* NewAPZCInstance(uint64_t aLayersId,
|
||||
GeckoContentController* aController,
|
||||
TaskThrottler* aPaintThrottler);
|
||||
GeckoContentController* aController);
|
||||
public:
|
||||
// Public hooks for gtests subclass
|
||||
virtual TimeStamp GetFrameTime();
|
||||
@ -536,10 +522,6 @@ private:
|
||||
/* Holds the zoom constraints for scrollable layers, as determined by the
|
||||
* the main-thread gecko code. */
|
||||
std::map<ScrollableLayerGuid, ZoomConstraints> mZoomConstraints;
|
||||
/* Stores a paint throttler for each layers id. There is one for each layers
|
||||
* id to ensure that one child process painting slowly doesn't hold up
|
||||
* another. */
|
||||
std::map<uint64_t, RefPtr<TaskThrottler>> mPaintThrottlerMap;
|
||||
/* This tracks the APZC that should receive all inputs for the current input event block.
|
||||
* This allows touch points to move outside the thing they started on, but still have the
|
||||
* touch events delivered to the same initial APZC. This will only ever be touched on the
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "InputBlockState.h" // for InputBlockState, TouchBlockState
|
||||
#include "InputQueue.h" // for InputQueue
|
||||
#include "OverscrollHandoffState.h" // for OverscrollHandoffState
|
||||
#include "TaskThrottler.h" // for TaskThrottler
|
||||
#include "Units.h" // for CSSRect, CSSPoint, etc
|
||||
#include "UnitTransforms.h" // for TransformTo
|
||||
#include "base/message_loop.h" // for MessageLoop
|
||||
@ -239,9 +238,6 @@ using mozilla::gfx::PointTyped;
|
||||
* rather than using the "stationary" multipliers.\n
|
||||
* Units: CSS pixels per millisecond
|
||||
*
|
||||
* \li\b apz.num_paint_duration_samples
|
||||
* Number of samples to store of how long it took to paint after the previous
|
||||
*
|
||||
* \li\b apz.overscroll.enabled
|
||||
* Pref that enables overscrolling. If this is disabled, excess scroll that
|
||||
* cannot be handed off is discarded.
|
||||
@ -303,12 +299,6 @@ using mozilla::gfx::PointTyped;
|
||||
* within this distance on scrollable frames.\n
|
||||
* Units: (real-world, i.e. screen) inches
|
||||
*
|
||||
* \li\b apz.use_paint_duration
|
||||
* Whether or not to use the estimated paint duration as a factor when projecting
|
||||
* the displayport in the direction of scrolling. If this value is set to false,
|
||||
* a constant 50ms paint time is used; the projection can be scaled as desired
|
||||
* using the \b apz.velocity_bias pref below.
|
||||
*
|
||||
* \li\b apz.velocity_bias
|
||||
* How much to adjust the displayport in the direction of scrolling. This value
|
||||
* is multiplied by the velocity and added to the displayport offset.
|
||||
@ -356,6 +346,12 @@ StaticAutoPtr<ComputedTimingFunction> gZoomAnimationFunction;
|
||||
*/
|
||||
StaticAutoPtr<ComputedTimingFunction> gVelocityCurveFunction;
|
||||
|
||||
/**
|
||||
* The estimated duration of a paint for the purposes of calculating a new
|
||||
* displayport, in milliseconds.
|
||||
*/
|
||||
static const double kDefaultEstimatedPaintDurationMs = 50;
|
||||
|
||||
/**
|
||||
* Returns true if this is a high memory system and we can use
|
||||
* extra memory for a larger displayport to reduce checkerboarding.
|
||||
@ -851,10 +847,8 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
|
||||
APZCTreeManager* aTreeManager,
|
||||
const RefPtr<InputQueue>& aInputQueue,
|
||||
GeckoContentController* aGeckoContentController,
|
||||
TaskThrottler* aPaintThrottler,
|
||||
GestureBehavior aGestures)
|
||||
: mLayersId(aLayersId),
|
||||
mPaintThrottler(aPaintThrottler),
|
||||
mGeckoContentController(aGeckoContentController),
|
||||
mRefPtrMonitor("RefPtrMonitor"),
|
||||
// mTreeManager must be initialized before GetFrameTime() is called
|
||||
@ -2695,9 +2689,14 @@ RedistributeDisplayPortExcess(CSSSize& aDisplayPortSize,
|
||||
/* static */
|
||||
const ScreenMargin AsyncPanZoomController::CalculatePendingDisplayPort(
|
||||
const FrameMetrics& aFrameMetrics,
|
||||
const ParentLayerPoint& aVelocity,
|
||||
double aEstimatedPaintDuration)
|
||||
const ParentLayerPoint& aVelocity)
|
||||
{
|
||||
if (aFrameMetrics.IsScrollInfoLayer()) {
|
||||
// Don't compute margins. Since we can't asynchronously scroll this frame,
|
||||
// we don't want to paint anything more than the composition bounds.
|
||||
return ScreenMargin();
|
||||
}
|
||||
|
||||
CSSSize compositionSize = aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels();
|
||||
CSSPoint velocity = aVelocity / aFrameMetrics.GetZoom();
|
||||
CSSPoint scrollOffset = aFrameMetrics.GetScrollOffset();
|
||||
@ -2712,8 +2711,7 @@ const ScreenMargin AsyncPanZoomController::CalculatePendingDisplayPort(
|
||||
|
||||
// Offset the displayport, depending on how fast we're moving and the
|
||||
// estimated time it takes to paint, to try to minimise checkerboarding.
|
||||
float estimatedPaintDurationMillis = (float)(aEstimatedPaintDuration * 1000.0);
|
||||
float paintFactor = (gfxPrefs::APZUsePaintDuration() ? estimatedPaintDurationMillis : 50.0f);
|
||||
float paintFactor = kDefaultEstimatedPaintDurationMs;
|
||||
CSSRect displayPort = CSSRect(scrollOffset + (velocity * paintFactor * gfxPrefs::APZVelocityBias()),
|
||||
displayPortSize);
|
||||
|
||||
@ -2746,11 +2744,7 @@ void AsyncPanZoomController::ScheduleComposite() {
|
||||
|
||||
void AsyncPanZoomController::ScheduleCompositeAndMaybeRepaint() {
|
||||
ScheduleComposite();
|
||||
|
||||
TimeDuration timePaintDelta = mPaintThrottler->TimeSinceLastRequest(GetFrameTime());
|
||||
if (timePaintDelta.ToMilliseconds() > gfxPrefs::APZPanRepaintInterval()) {
|
||||
RequestContentRepaint();
|
||||
}
|
||||
RequestContentRepaint();
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::FlushRepaintForOverscrollHandoff() {
|
||||
@ -2763,25 +2757,10 @@ void AsyncPanZoomController::FlushRepaintForNewInputBlock() {
|
||||
APZC_LOG("%p flushing repaint for new input block\n", this);
|
||||
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
// We need to send a new repaint request unthrottled, but that
|
||||
// will obsolete any pending repaint request in the paint throttler.
|
||||
// Therefore we should clear out the pending task and restore the
|
||||
// state of mLastPaintRequestMetrics to what it was before the
|
||||
// pending task was queued.
|
||||
mPaintThrottler->CancelPendingTask();
|
||||
mLastPaintRequestMetrics = mLastDispatchedPaintMetrics;
|
||||
|
||||
RequestContentRepaint(mFrameMetrics, false /* not throttled */);
|
||||
RequestContentRepaint(mFrameMetrics);
|
||||
UpdateSharedCompositorFrameMetrics();
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::FlushRepaintIfPending() {
|
||||
// Just tell the paint throttler to send the pending repaint request if
|
||||
// there is one.
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
mPaintThrottler->TaskComplete(GetFrameTime());
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::SnapBackIfOverscrolled() {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
// It's possible that we're already in the middle of an overscroll
|
||||
@ -2824,11 +2803,8 @@ void AsyncPanZoomController::RequestContentRepaint() {
|
||||
RequestContentRepaint(mFrameMetrics);
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics, bool aThrottled) {
|
||||
aFrameMetrics.SetDisplayPortMargins(
|
||||
CalculatePendingDisplayPort(aFrameMetrics,
|
||||
GetVelocityVector(),
|
||||
mPaintThrottler->AverageDuration().ToSeconds()));
|
||||
void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics) {
|
||||
aFrameMetrics.SetDisplayPortMargins(CalculatePendingDisplayPort(aFrameMetrics, GetVelocityVector()));
|
||||
aFrameMetrics.SetUseDisplayPortMargins();
|
||||
|
||||
// If we're trying to paint what we already think is painted, discard this
|
||||
@ -2852,19 +2828,8 @@ void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics,
|
||||
return;
|
||||
}
|
||||
|
||||
if (aThrottled) {
|
||||
mPaintThrottler->PostTask(
|
||||
FROM_HERE,
|
||||
UniquePtr<CancelableTask>(NewRunnableMethod(this,
|
||||
&AsyncPanZoomController::DispatchRepaintRequest,
|
||||
aFrameMetrics)),
|
||||
GetFrameTime());
|
||||
} else {
|
||||
DispatchRepaintRequest(aFrameMetrics);
|
||||
}
|
||||
|
||||
DispatchRepaintRequest(aFrameMetrics);
|
||||
aFrameMetrics.SetPresShellId(mLastContentPaintMetrics.GetPresShellId());
|
||||
mLastPaintRequestMetrics = aFrameMetrics;
|
||||
}
|
||||
|
||||
/*static*/ CSSRect
|
||||
@ -2881,18 +2846,21 @@ GetDisplayPortRect(const FrameMetrics& aFrameMetrics)
|
||||
void
|
||||
AsyncPanZoomController::DispatchRepaintRequest(const FrameMetrics& aFrameMetrics) {
|
||||
RefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
if (controller) {
|
||||
APZC_LOG_FM(aFrameMetrics, "%p requesting content repaint", this);
|
||||
LogRendertraceRect(GetGuid(), "requested displayport", "yellow", GetDisplayPortRect(aFrameMetrics));
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
controller->RequestContentRepaint(aFrameMetrics);
|
||||
} else {
|
||||
NS_DispatchToMainThread(NS_NewRunnableMethodWithArg<FrameMetrics>(
|
||||
controller, &GeckoContentController::RequestContentRepaint, aFrameMetrics));
|
||||
}
|
||||
mLastDispatchedPaintMetrics = aFrameMetrics;
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
APZC_LOG_FM(aFrameMetrics, "%p requesting content repaint", this);
|
||||
LogRendertraceRect(GetGuid(), "requested displayport", "yellow", GetDisplayPortRect(aFrameMetrics));
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
controller->RequestContentRepaint(aFrameMetrics);
|
||||
} else {
|
||||
NS_DispatchToMainThread(NS_NewRunnableMethodWithArg<FrameMetrics>(
|
||||
controller, &GeckoContentController::RequestContentRepaint, aFrameMetrics));
|
||||
}
|
||||
mExpectedGeckoMetrics = aFrameMetrics;
|
||||
mLastPaintRequestMetrics = aFrameMetrics;
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
|
||||
@ -2913,16 +2881,11 @@ bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
|
||||
if (mAnimation) {
|
||||
bool continueAnimation = mAnimation->Sample(mFrameMetrics, sampleTimeDelta);
|
||||
*aOutDeferredTasks = mAnimation->TakeDeferredTasks();
|
||||
if (continueAnimation) {
|
||||
if (mPaintThrottler->TimeSinceLastRequest(aSampleTime) >
|
||||
mAnimation->mRepaintInterval) {
|
||||
RequestContentRepaint();
|
||||
}
|
||||
} else {
|
||||
if (!continueAnimation) {
|
||||
mAnimation = nullptr;
|
||||
SetState(NOTHING);
|
||||
RequestContentRepaint();
|
||||
}
|
||||
RequestContentRepaint();
|
||||
UpdateSharedCompositorFrameMetrics();
|
||||
return true;
|
||||
}
|
||||
@ -3071,7 +3034,7 @@ Matrix4x4 AsyncPanZoomController::GetTransformToLastDispatchedPaint() const {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
|
||||
LayerPoint scrollChange =
|
||||
(mLastContentPaintMetrics.GetScrollOffset() - mLastDispatchedPaintMetrics.GetScrollOffset())
|
||||
(mLastContentPaintMetrics.GetScrollOffset() - mExpectedGeckoMetrics.GetScrollOffset())
|
||||
* mLastContentPaintMetrics.GetDevPixelsPerCSSPixel()
|
||||
* mLastContentPaintMetrics.GetCumulativeResolution();
|
||||
|
||||
@ -3081,7 +3044,7 @@ Matrix4x4 AsyncPanZoomController::GetTransformToLastDispatchedPaint() const {
|
||||
LayoutDeviceToParentLayerScale2D lastContentZoom =
|
||||
mLastContentPaintMetrics.GetZoom() / mLastContentPaintMetrics.GetDevPixelsPerCSSPixel();
|
||||
LayoutDeviceToParentLayerScale2D lastDispatchedZoom =
|
||||
mLastDispatchedPaintMetrics.GetZoom() / mLastDispatchedPaintMetrics.GetDevPixelsPerCSSPixel();
|
||||
mExpectedGeckoMetrics.GetZoom() / mExpectedGeckoMetrics.GetDevPixelsPerCSSPixel();
|
||||
gfxSize zoomChange = lastContentZoom / lastDispatchedZoom;
|
||||
|
||||
return Matrix4x4::Translation(scrollChange.x, scrollChange.y, 0).
|
||||
@ -3159,7 +3122,6 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
aLayerMetrics.GetCriticalDisplayPort() + aLayerMetrics.GetScrollOffset());
|
||||
}
|
||||
|
||||
mPaintThrottler->TaskComplete(GetFrameTime());
|
||||
bool needContentRepaint = false;
|
||||
bool viewportUpdated = false;
|
||||
if (FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().width, mFrameMetrics.GetCompositionBounds().width) &&
|
||||
@ -3190,16 +3152,13 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
if (aIsFirstPaint || isDefault) {
|
||||
// Initialize our internal state to something sane when the content
|
||||
// that was just painted is something we knew nothing about previously
|
||||
mPaintThrottler->ClearHistory();
|
||||
mPaintThrottler->SetMaxDurations(gfxPrefs::APZNumPaintDurationSamples());
|
||||
|
||||
CancelAnimation();
|
||||
|
||||
mFrameMetrics = aLayerMetrics;
|
||||
if (scrollOffsetUpdated) {
|
||||
AcknowledgeScrollUpdate();
|
||||
}
|
||||
mLastDispatchedPaintMetrics = aLayerMetrics;
|
||||
mExpectedGeckoMetrics = aLayerMetrics;
|
||||
ShareCompositorFrameMetrics();
|
||||
|
||||
if (mFrameMetrics.GetDisplayPortMargins() != ScreenMargin()) {
|
||||
@ -3252,6 +3211,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
mFrameMetrics.SetMaskLayerIndex(aLayerMetrics.GetMaskLayerIndex());
|
||||
mFrameMetrics.SetIsLayersIdRoot(aLayerMetrics.IsLayersIdRoot());
|
||||
mFrameMetrics.SetUsesContainerScrolling(aLayerMetrics.UsesContainerScrolling());
|
||||
mFrameMetrics.SetIsScrollInfoLayer(aLayerMetrics.IsScrollInfoLayer());
|
||||
|
||||
if (scrollOffsetUpdated) {
|
||||
APZC_LOG("%p updating scroll offset from %s to %s\n", this,
|
||||
@ -3261,13 +3221,13 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
// Send an acknowledgement with the new scroll generation so that any
|
||||
// repaint requests later in this function go through.
|
||||
// Because of the scroll generation update, any inflight paint requests are
|
||||
// going to be ignored by layout, and so mLastDispatchedPaintMetrics
|
||||
// going to be ignored by layout, and so mExpectedGeckoMetrics
|
||||
// becomes incorrect for the purposes of calculating the LD transform. To
|
||||
// correct this we need to update mLastDispatchedPaintMetrics to be the
|
||||
// correct this we need to update mExpectedGeckoMetrics to be the
|
||||
// last thing we know was painted by Gecko.
|
||||
mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
|
||||
AcknowledgeScrollUpdate();
|
||||
mLastDispatchedPaintMetrics = aLayerMetrics;
|
||||
mExpectedGeckoMetrics = aLayerMetrics;
|
||||
|
||||
// Cancel the animation (which might also trigger a repaint request)
|
||||
// after we update the scroll offset above. Otherwise we can be left
|
||||
@ -3299,7 +3259,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
// above.
|
||||
mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics);
|
||||
AcknowledgeScrollUpdate();
|
||||
mLastDispatchedPaintMetrics = aLayerMetrics;
|
||||
mExpectedGeckoMetrics = aLayerMetrics;
|
||||
|
||||
if (mState == SMOOTH_SCROLL && mAnimation) {
|
||||
APZC_LOG("%p updating destination on existing animation\n", this);
|
||||
@ -3438,9 +3398,7 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
|
||||
|
||||
endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
|
||||
endZoomToMetrics.SetDisplayPortMargins(
|
||||
CalculatePendingDisplayPort(endZoomToMetrics,
|
||||
ParentLayerPoint(0,0),
|
||||
0));
|
||||
CalculatePendingDisplayPort(endZoomToMetrics, ParentLayerPoint(0,0)));
|
||||
endZoomToMetrics.SetUseDisplayPortMargins();
|
||||
|
||||
StartAnimation(new ZoomAnimation(
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include "APZUtils.h"
|
||||
#include "Layers.h" // for Layer::ScrollDirection
|
||||
#include "LayersTypes.h"
|
||||
#include "TaskThrottler.h"
|
||||
#include "mozilla/gfx/Matrix.h"
|
||||
#include "nsRegion.h"
|
||||
|
||||
@ -104,7 +103,6 @@ public:
|
||||
APZCTreeManager* aTreeManager,
|
||||
const RefPtr<InputQueue>& aInputQueue,
|
||||
GeckoContentController* aController,
|
||||
TaskThrottler* aPaintThrottler,
|
||||
GestureBehavior aGestures = DEFAULT_GESTURES);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
@ -186,11 +184,6 @@ public:
|
||||
*/
|
||||
void NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint);
|
||||
|
||||
/**
|
||||
* Flush any pending repaint request.
|
||||
*/
|
||||
void FlushRepaintIfPending();
|
||||
|
||||
/**
|
||||
* The platform implementation must set the compositor parent so that we can
|
||||
* request composites.
|
||||
@ -269,8 +262,7 @@ public:
|
||||
*/
|
||||
static const ScreenMargin CalculatePendingDisplayPort(
|
||||
const FrameMetrics& aFrameMetrics,
|
||||
const ParentLayerPoint& aVelocity,
|
||||
double aEstimatedPaintDuration);
|
||||
const ParentLayerPoint& aVelocity);
|
||||
|
||||
nsEventStatus HandleDragEvent(const MouseInput& aEvent,
|
||||
const AsyncDragMetrics& aDragMetrics);
|
||||
@ -597,7 +589,7 @@ protected:
|
||||
* the paint throttler. In particular, the GeckoContentController::RequestContentRepaint
|
||||
* function will be invoked before this function returns.
|
||||
*/
|
||||
void RequestContentRepaint(FrameMetrics& aFrameMetrics, bool aThrottled = true);
|
||||
void RequestContentRepaint(FrameMetrics& aFrameMetrics);
|
||||
|
||||
/**
|
||||
* Actually send the next pending paint request to gecko.
|
||||
@ -651,7 +643,6 @@ protected:
|
||||
|
||||
uint64_t mLayersId;
|
||||
RefPtr<CompositorParent> mCompositorParent;
|
||||
RefPtr<TaskThrottler> mPaintThrottler;
|
||||
|
||||
/* Access to the following two fields is protected by the mRefPtrMonitor,
|
||||
since they are accessed on the UI thread but can be cleared on the
|
||||
@ -699,15 +690,12 @@ private:
|
||||
// the Gecko state, it should be used as a basis for untransformation when
|
||||
// sending messages back to Gecko.
|
||||
FrameMetrics mLastContentPaintMetrics;
|
||||
// The last metrics that we requested a paint for. These are used to make sure
|
||||
// that we're not requesting a paint of the same thing that's already drawn.
|
||||
// If we don't do this check, we don't get a ShadowLayersUpdated back.
|
||||
// The last metrics used for a content repaint request.
|
||||
FrameMetrics mLastPaintRequestMetrics;
|
||||
// The last metrics that we actually sent to Gecko. This allows us to transform
|
||||
// inputs into a coordinate space that Gecko knows about. This assumes the pipe
|
||||
// through which input events and repaint requests are sent to Gecko operates
|
||||
// in a FIFO manner.
|
||||
FrameMetrics mLastDispatchedPaintMetrics;
|
||||
// The metrics that we expect content to have. This is updated when we
|
||||
// request a content repaint, and when we receive a shadow layers update.
|
||||
// This allows us to transform events into Gecko's coordinate space.
|
||||
FrameMetrics mExpectedGeckoMetrics;
|
||||
|
||||
AxisX mX;
|
||||
AxisY mY;
|
||||
|
@ -1,173 +0,0 @@
|
||||
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
|
||||
/* vim: set sw=2 sts=2 ts=8 et 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 "TaskThrottler.h"
|
||||
|
||||
#include "mozilla/layers/APZThreadUtils.h"
|
||||
|
||||
#define TASK_LOG(...)
|
||||
// #define TASK_LOG(...) printf_stderr("TASK: " __VA_ARGS__)
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
TaskThrottler::TaskThrottler(const TimeStamp& aTimeStamp, const TimeDuration& aMaxWait)
|
||||
: mMonitor("TaskThrottler")
|
||||
, mOutstanding(false)
|
||||
, mQueuedTask(nullptr)
|
||||
, mStartTime(aTimeStamp)
|
||||
, mMaxWait(aMaxWait)
|
||||
, mMean(1)
|
||||
, mTimeoutTask(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
TaskThrottler::~TaskThrottler()
|
||||
{
|
||||
// The timeout task holds a strong reference to the TaskThrottler, so if the
|
||||
// TaskThrottler is being destroyed, there's no need to cancel the task.
|
||||
}
|
||||
|
||||
void
|
||||
TaskThrottler::PostTask(const tracked_objects::Location& aLocation,
|
||||
UniquePtr<CancelableTask> aTask, const TimeStamp& aTimeStamp)
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
TASK_LOG("%p got a task posted; mOutstanding=%d\n", this, mOutstanding);
|
||||
aTask->SetBirthPlace(aLocation);
|
||||
|
||||
if (mOutstanding) {
|
||||
CancelPendingTask(lock);
|
||||
if (TimeSinceLastRequest(aTimeStamp, lock) < mMaxWait) {
|
||||
mQueuedTask = Move(aTask);
|
||||
TASK_LOG("%p queued task %p\n", this, mQueuedTask.get());
|
||||
// Make sure the queued task is sent after mMaxWait time elapses,
|
||||
// even if we don't get a TaskComplete() until then.
|
||||
TimeDuration timeout = mMaxWait - TimeSinceLastRequest(aTimeStamp, lock);
|
||||
mTimeoutTask = NewRunnableMethod(this, &TaskThrottler::OnTimeout);
|
||||
APZThreadUtils::RunDelayedTaskOnCurrentThread(mTimeoutTask, timeout);
|
||||
return;
|
||||
}
|
||||
// we've been waiting for more than the max-wait limit, so just fall through
|
||||
// and send the new task already.
|
||||
}
|
||||
|
||||
mStartTime = aTimeStamp;
|
||||
aTask->Run();
|
||||
mOutstanding = true;
|
||||
}
|
||||
|
||||
void
|
||||
TaskThrottler::OnTimeout()
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
if (mQueuedTask) {
|
||||
RunQueuedTask(TimeStamp::Now(), lock);
|
||||
}
|
||||
// The message loop will delete the posted timeout task. Make sure we don't
|
||||
// keep a dangling pointer to it.
|
||||
mTimeoutTask = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
TaskThrottler::TaskComplete(const TimeStamp& aTimeStamp)
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
if (!mOutstanding) {
|
||||
return;
|
||||
}
|
||||
|
||||
mMean.insert(aTimeStamp - mStartTime);
|
||||
|
||||
if (mQueuedTask) {
|
||||
RunQueuedTask(aTimeStamp, lock);
|
||||
CancelTimeoutTask(lock);
|
||||
} else {
|
||||
mOutstanding = false;
|
||||
}
|
||||
}
|
||||
|
||||
TimeDuration
|
||||
TaskThrottler::AverageDuration()
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
return mMean.empty() ? TimeDuration() : mMean.mean();
|
||||
}
|
||||
|
||||
void
|
||||
TaskThrottler::RunQueuedTask(const TimeStamp& aTimeStamp,
|
||||
const MonitorAutoLock& aProofOfLock)
|
||||
{
|
||||
TASK_LOG("%p running task %p\n", this, mQueuedTask.get());
|
||||
mStartTime = aTimeStamp;
|
||||
mQueuedTask->Run();
|
||||
mQueuedTask = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
TaskThrottler::CancelPendingTask()
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
CancelPendingTask(lock);
|
||||
}
|
||||
|
||||
void
|
||||
TaskThrottler::CancelPendingTask(const MonitorAutoLock& aProofOfLock)
|
||||
{
|
||||
if (mQueuedTask) {
|
||||
TASK_LOG("%p cancelling task %p\n", this, mQueuedTask.get());
|
||||
mQueuedTask->Cancel();
|
||||
mQueuedTask = nullptr;
|
||||
CancelTimeoutTask(aProofOfLock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TaskThrottler::CancelTimeoutTask(const MonitorAutoLock& aProofOfLock)
|
||||
{
|
||||
if (mTimeoutTask) {
|
||||
mTimeoutTask->Cancel();
|
||||
mTimeoutTask = nullptr; // the MessageLoop will destroy it
|
||||
}
|
||||
}
|
||||
|
||||
TimeDuration
|
||||
TaskThrottler::TimeSinceLastRequest(const TimeStamp& aTimeStamp)
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
return TimeSinceLastRequest(aTimeStamp, lock);
|
||||
}
|
||||
|
||||
TimeDuration
|
||||
TaskThrottler::TimeSinceLastRequest(const TimeStamp& aTimeStamp,
|
||||
const MonitorAutoLock& aProofOfLock)
|
||||
{
|
||||
return aTimeStamp - mStartTime;
|
||||
}
|
||||
|
||||
void
|
||||
TaskThrottler::ClearHistory()
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
mMean.clear();
|
||||
}
|
||||
|
||||
void
|
||||
TaskThrottler::SetMaxDurations(uint32_t aMaxDurations)
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
if (aMaxDurations != mMean.maxValues()) {
|
||||
mMean = RollingMean<TimeDuration, TimeDuration>(aMaxDurations);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
@ -1,119 +0,0 @@
|
||||
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2; -*- */
|
||||
/* vim: set sw=4 ts=8 et 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_TaskThrottler_h
|
||||
#define mozilla_dom_TaskThrottler_h
|
||||
|
||||
#include <stdint.h> // for uint32_t
|
||||
#include "base/task.h" // for CancelableTask
|
||||
#include "mozilla/Monitor.h" // for Monitor
|
||||
#include "mozilla/mozalloc.h" // for operator delete
|
||||
#include "mozilla/RollingMean.h" // for RollingMean
|
||||
#include "mozilla/TimeStamp.h" // for TimeDuration, TimeStamp
|
||||
#include "mozilla/UniquePtr.h" // for UniquePtr
|
||||
#include "nsCOMPtr.h" // for nsCOMPtr
|
||||
#include "nsISupportsImpl.h" // for NS_INLINE_DECL_THREADSAFE_REFCOUNTING
|
||||
#include "nsTArray.h" // for nsTArray
|
||||
|
||||
namespace tracked_objects {
|
||||
class Location;
|
||||
} // namespace tracked_objects
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
/** The TaskThrottler prevents update event overruns. It is used in cases where
|
||||
* you're sending an async message and waiting for a reply. You need to call
|
||||
* PostTask to queue a task and TaskComplete when you get a response.
|
||||
*
|
||||
* The call to TaskComplete will run the most recent task posted since the last
|
||||
* request was sent, if any. This means that at any time there can be at most 1
|
||||
* outstanding request being processed and at most 1 queued behind it.
|
||||
*
|
||||
* However, to guard against task runs that error out and fail to call TaskComplete,
|
||||
* the TaskThrottler also has a max-wait timeout. If the caller requests a new
|
||||
* task be posted, and it has been greater than the max-wait timeout since the
|
||||
* last one was sent, then we send the new one regardless of whether or not the
|
||||
* last one was marked as completed.
|
||||
*
|
||||
* This is used in the context of repainting a scrollable region. While another
|
||||
* process is painting you might get several updates from the UI thread but when
|
||||
* the paint is complete you want to send the most recent.
|
||||
*/
|
||||
|
||||
class TaskThrottler {
|
||||
public:
|
||||
TaskThrottler(const TimeStamp& aTimeStamp, const TimeDuration& aMaxWait);
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TaskThrottler)
|
||||
|
||||
/** Post a task to be run as soon as there are no outstanding tasks, or
|
||||
* post it immediately if it has been more than the max-wait time since
|
||||
* the last task was posted.
|
||||
*
|
||||
* @param aLocation Use the macro FROM_HERE
|
||||
* @param aTask Ownership of this object is transferred to TaskThrottler
|
||||
* which will delete it when it is either run or becomes
|
||||
* obsolete or the TaskThrottler is destructed.
|
||||
*/
|
||||
void PostTask(const tracked_objects::Location& aLocation,
|
||||
UniquePtr<CancelableTask> aTask, const TimeStamp& aTimeStamp);
|
||||
/**
|
||||
* Mark the task as complete and process the next queued task.
|
||||
*/
|
||||
void TaskComplete(const TimeStamp& aTimeStamp);
|
||||
|
||||
/**
|
||||
* Calculate the average time between processing the posted task and getting
|
||||
* the TaskComplete() call back.
|
||||
*/
|
||||
TimeDuration AverageDuration();
|
||||
|
||||
/**
|
||||
* Cancel the queued task if there is one.
|
||||
*/
|
||||
void CancelPendingTask();
|
||||
|
||||
/**
|
||||
* Return the time elapsed since the last request was processed
|
||||
*/
|
||||
TimeDuration TimeSinceLastRequest(const TimeStamp& aTimeStamp);
|
||||
|
||||
/**
|
||||
* Clear average history.
|
||||
*/
|
||||
void ClearHistory();
|
||||
|
||||
/**
|
||||
* @param aMaxDurations The maximum number of durations to measure.
|
||||
*/
|
||||
|
||||
void SetMaxDurations(uint32_t aMaxDurations);
|
||||
|
||||
private:
|
||||
mutable Monitor mMonitor;
|
||||
bool mOutstanding;
|
||||
UniquePtr<CancelableTask> mQueuedTask;
|
||||
TimeStamp mStartTime;
|
||||
TimeDuration mMaxWait;
|
||||
RollingMean<TimeDuration, TimeDuration> mMean;
|
||||
CancelableTask* mTimeoutTask; // not owned because it's posted to a MessageLoop
|
||||
// which deletes it
|
||||
|
||||
~TaskThrottler();
|
||||
void RunQueuedTask(const TimeStamp& aTimeStamp,
|
||||
const MonitorAutoLock& aProofOfLock);
|
||||
void CancelPendingTask(const MonitorAutoLock& aProofOfLock);
|
||||
TimeDuration TimeSinceLastRequest(const TimeStamp& aTimeStamp,
|
||||
const MonitorAutoLock& aProofOfLock);
|
||||
void OnTimeout();
|
||||
void CancelTimeoutTask(const MonitorAutoLock& aProofOfLock);
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TaskThrottler_h
|
@ -154,8 +154,7 @@ public:
|
||||
|
||||
protected:
|
||||
AsyncPanZoomController* NewAPZCInstance(uint64_t aLayersId,
|
||||
GeckoContentController* aController,
|
||||
TaskThrottler* aPaintThrottler) override;
|
||||
GeckoContentController* aController) override;
|
||||
|
||||
TimeStamp GetFrameTime() override {
|
||||
return mcc->Time();
|
||||
@ -169,10 +168,9 @@ class TestAsyncPanZoomController : public AsyncPanZoomController {
|
||||
public:
|
||||
TestAsyncPanZoomController(uint64_t aLayersId, MockContentControllerDelayed* aMcc,
|
||||
TestAPZCTreeManager* aTreeManager,
|
||||
TaskThrottler* aPaintThrottler,
|
||||
GestureBehavior aBehavior = DEFAULT_GESTURES)
|
||||
: AsyncPanZoomController(aLayersId, aTreeManager, aTreeManager->GetInputQueue(),
|
||||
aMcc, aPaintThrottler, aBehavior)
|
||||
aMcc, aBehavior)
|
||||
, mWaitForMainThread(false)
|
||||
, mcc(aMcc)
|
||||
{}
|
||||
@ -261,11 +259,10 @@ private:
|
||||
|
||||
AsyncPanZoomController*
|
||||
TestAPZCTreeManager::NewAPZCInstance(uint64_t aLayersId,
|
||||
GeckoContentController* aController,
|
||||
TaskThrottler* aPaintThrottler)
|
||||
GeckoContentController* aController)
|
||||
{
|
||||
MockContentControllerDelayed* mcc = static_cast<MockContentControllerDelayed*>(aController);
|
||||
return new TestAsyncPanZoomController(aLayersId, mcc, this, aPaintThrottler,
|
||||
return new TestAsyncPanZoomController(aLayersId, mcc, this,
|
||||
AsyncPanZoomController::USE_GESTURE_DETECTOR);
|
||||
}
|
||||
|
||||
@ -297,9 +294,8 @@ protected:
|
||||
APZThreadUtils::SetControllerThread(MessageLoop::current());
|
||||
|
||||
mcc = new NiceMock<MockContentControllerDelayed>();
|
||||
mPaintThrottler = new TaskThrottler(mcc->Time(), TimeDuration::FromMilliseconds(500));
|
||||
tm = new TestAPZCTreeManager(mcc);
|
||||
apzc = new TestAsyncPanZoomController(0, mcc, tm, mPaintThrottler, mGestureBehavior);
|
||||
apzc = new TestAsyncPanZoomController(0, mcc, tm, mGestureBehavior);
|
||||
apzc->SetFrameMetrics(TestFrameMetrics());
|
||||
}
|
||||
|
||||
@ -382,7 +378,6 @@ protected:
|
||||
|
||||
AsyncPanZoomController::GestureBehavior mGestureBehavior;
|
||||
RefPtr<MockContentControllerDelayed> mcc;
|
||||
RefPtr<TaskThrottler> mPaintThrottler;
|
||||
RefPtr<TestAPZCTreeManager> tm;
|
||||
RefPtr<TestAsyncPanZoomController> apzc;
|
||||
};
|
||||
@ -819,7 +814,8 @@ protected:
|
||||
MakeApzcZoomable();
|
||||
|
||||
if (aShouldTriggerPinch) {
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
|
||||
// One repaint request for each gesture.
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2);
|
||||
} else {
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
|
||||
}
|
||||
@ -1141,7 +1137,7 @@ TEST_F(APZCBasicTester, ComplexTransform) {
|
||||
// sides.
|
||||
|
||||
RefPtr<TestAsyncPanZoomController> childApzc =
|
||||
new TestAsyncPanZoomController(0, mcc, tm, mPaintThrottler);
|
||||
new TestAsyncPanZoomController(0, mcc, tm);
|
||||
|
||||
const char* layerTreeSyntax = "c(c)";
|
||||
// LayerID 0 1
|
||||
@ -1248,7 +1244,8 @@ protected:
|
||||
void DoPanTest(bool aShouldTriggerScroll, bool aShouldBeConsumed, uint32_t aBehavior)
|
||||
{
|
||||
if (aShouldTriggerScroll) {
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
|
||||
// One repaint request for each pan.
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2);
|
||||
} else {
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
|
||||
}
|
||||
@ -1357,8 +1354,6 @@ TEST_F(APZCPanningTester, PanWithPreventDefault) {
|
||||
}
|
||||
|
||||
TEST_F(APZCBasicTester, Fling) {
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
|
||||
|
||||
int touchStart = 50;
|
||||
int touchEnd = 10;
|
||||
ParentLayerPoint pointOut;
|
||||
@ -2422,10 +2417,8 @@ TEST_F(APZHitTestingTester, HitTesting2) {
|
||||
// because we have already issued a paint request with it.
|
||||
EXPECT_EQ(ScreenPoint(25, 25), transformToGecko * ParentLayerPoint(12.5, 75));
|
||||
|
||||
// This second pan will move the APZC by another 50 pixels but since the paint
|
||||
// request dispatched above has not "completed", we will not dispatch another
|
||||
// one yet. Now we have an async transform on top of the pending paint request
|
||||
// transform.
|
||||
// This second pan will move the APZC by another 50 pixels.
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
|
||||
ApzcPanNoFling(apzcroot, mcc, 100, 50);
|
||||
|
||||
// Hit where layers[3] used to be. It should now hit the root.
|
||||
@ -2433,18 +2426,16 @@ TEST_F(APZHitTestingTester, HitTesting2) {
|
||||
EXPECT_EQ(apzcroot, hit.get());
|
||||
// transformToApzc doesn't unapply the root's own async transform
|
||||
EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc * ScreenPoint(75, 75));
|
||||
// transformToGecko unapplies the full async transform of -100 pixels, and then
|
||||
// reapplies the "D" transform of -50 leading to an overall adjustment of +50
|
||||
EXPECT_EQ(ScreenPoint(75, 125), transformToGecko * ParentLayerPoint(75, 75));
|
||||
// transformToGecko unapplies the full async transform of -100 pixels
|
||||
EXPECT_EQ(ScreenPoint(75, 75), transformToGecko * ParentLayerPoint(75, 75));
|
||||
|
||||
// Hit where layers[1] used to be. It should now hit the root.
|
||||
hit = GetTargetAPZC(ScreenPoint(25, 25));
|
||||
EXPECT_EQ(apzcroot, hit.get());
|
||||
// transformToApzc doesn't unapply the root's own async transform
|
||||
EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc * ScreenPoint(25, 25));
|
||||
// transformToGecko unapplies the full async transform of -100 pixels, and then
|
||||
// reapplies the "D" transform of -50 leading to an overall adjustment of +50
|
||||
EXPECT_EQ(ScreenPoint(25, 75), transformToGecko * ParentLayerPoint(25, 25));
|
||||
// transformToGecko unapplies the full async transform of -100 pixels
|
||||
EXPECT_EQ(ScreenPoint(25, 25), transformToGecko * ParentLayerPoint(25, 25));
|
||||
}
|
||||
|
||||
TEST_F(APZCTreeManagerTester, ScrollablePaintedLayers) {
|
||||
@ -3497,89 +3488,3 @@ public:
|
||||
private:
|
||||
TaskRunMetrics& mMetrics;
|
||||
};
|
||||
|
||||
class APZTaskThrottlerTester : public ::testing::Test {
|
||||
public:
|
||||
APZTaskThrottlerTester()
|
||||
{
|
||||
now = TimeStamp::Now();
|
||||
throttler = new TaskThrottler(now, TimeDuration::FromMilliseconds(100));
|
||||
}
|
||||
|
||||
protected:
|
||||
TimeStamp Advance(int aMillis = 5)
|
||||
{
|
||||
now = now + TimeDuration::FromMilliseconds(aMillis);
|
||||
return now;
|
||||
}
|
||||
|
||||
UniquePtr<CancelableTask> NewTask()
|
||||
{
|
||||
return MakeUnique<MockTask>(metrics);
|
||||
}
|
||||
|
||||
TimeStamp now;
|
||||
RefPtr<TaskThrottler> throttler;
|
||||
TaskRunMetrics metrics;
|
||||
};
|
||||
|
||||
TEST_F(APZTaskThrottlerTester, BasicTest) {
|
||||
// Check that posting the first task runs right away
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance()); // task 1
|
||||
EXPECT_EQ(1, metrics.GetAndClearRunCount());
|
||||
|
||||
// Check that posting the second task doesn't run until the first one is done
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance()); // task 2
|
||||
EXPECT_EQ(0, metrics.GetAndClearRunCount());
|
||||
throttler->TaskComplete(Advance()); // for task 1
|
||||
EXPECT_EQ(1, metrics.GetAndClearRunCount());
|
||||
EXPECT_EQ(0, metrics.GetAndClearCancelCount());
|
||||
|
||||
// Check that tasks are coalesced: dispatch 5 tasks
|
||||
// while there is still one outstanding, and ensure
|
||||
// that only one of the 5 runs
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance()); // task 3
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance()); // task 4
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance()); // task 5
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance()); // task 6
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance()); // task 7
|
||||
EXPECT_EQ(0, metrics.GetAndClearRunCount());
|
||||
EXPECT_EQ(4, metrics.GetAndClearCancelCount());
|
||||
|
||||
throttler->TaskComplete(Advance()); // for task 2
|
||||
EXPECT_EQ(1, metrics.GetAndClearRunCount());
|
||||
throttler->TaskComplete(Advance()); // for task 7 (tasks 3..6 were cancelled)
|
||||
EXPECT_EQ(0, metrics.GetAndClearRunCount());
|
||||
EXPECT_EQ(0, metrics.GetAndClearCancelCount());
|
||||
}
|
||||
|
||||
TEST_F(APZTaskThrottlerTester, TimeoutTest) {
|
||||
// Check that posting the first task runs right away
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance()); // task 1
|
||||
EXPECT_EQ(1, metrics.GetAndClearRunCount());
|
||||
|
||||
// Because we let 100ms pass, the second task should
|
||||
// run immediately even though the first one isn't
|
||||
// done yet
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance(100)); // task 2; task 1 is assumed lost
|
||||
EXPECT_EQ(1, metrics.GetAndClearRunCount());
|
||||
throttler->TaskComplete(Advance()); // for task 1, but TaskThrottler thinks it's for task 2
|
||||
throttler->TaskComplete(Advance()); // for task 2, TaskThrottler ignores it
|
||||
EXPECT_EQ(0, metrics.GetAndClearRunCount());
|
||||
EXPECT_EQ(0, metrics.GetAndClearCancelCount());
|
||||
|
||||
// This time queue up a few tasks before the timeout expires
|
||||
// and ensure cancellation still works as expected
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance()); // task 3
|
||||
EXPECT_EQ(1, metrics.GetAndClearRunCount());
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance()); // task 4
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance()); // task 5
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance()); // task 6
|
||||
EXPECT_EQ(0, metrics.GetAndClearRunCount());
|
||||
throttler->PostTask(FROM_HERE, NewTask(), Advance(100)); // task 7; task 3 is assumed lost
|
||||
EXPECT_EQ(1, metrics.GetAndClearRunCount());
|
||||
EXPECT_EQ(3, metrics.GetAndClearCancelCount()); // tasks 4..6 should have been cancelled
|
||||
throttler->TaskComplete(Advance()); // for task 7
|
||||
EXPECT_EQ(0, metrics.GetAndClearRunCount());
|
||||
EXPECT_EQ(0, metrics.GetAndClearCancelCount());
|
||||
}
|
||||
|
@ -41,26 +41,26 @@ static void
|
||||
AdjustDisplayPortForScrollDelta(mozilla::layers::FrameMetrics& aFrameMetrics,
|
||||
const CSSPoint& aActualScrollOffset)
|
||||
{
|
||||
// Correct the display-port by the difference between the requested scroll
|
||||
// offset and the resulting scroll offset after setting the requested value.
|
||||
ScreenPoint shift =
|
||||
(aFrameMetrics.GetScrollOffset() - aActualScrollOffset) *
|
||||
aFrameMetrics.DisplayportPixelsPerCSSPixel();
|
||||
ScreenMargin margins = aFrameMetrics.GetDisplayPortMargins();
|
||||
margins.left -= shift.x;
|
||||
margins.right += shift.x;
|
||||
margins.top -= shift.y;
|
||||
margins.bottom += shift.y;
|
||||
aFrameMetrics.SetDisplayPortMargins(margins);
|
||||
// Correct the display-port by the difference between the requested scroll
|
||||
// offset and the resulting scroll offset after setting the requested value.
|
||||
ScreenPoint shift =
|
||||
(aFrameMetrics.GetScrollOffset() - aActualScrollOffset) *
|
||||
aFrameMetrics.DisplayportPixelsPerCSSPixel();
|
||||
ScreenMargin margins = aFrameMetrics.GetDisplayPortMargins();
|
||||
margins.left -= shift.x;
|
||||
margins.right += shift.x;
|
||||
margins.top -= shift.y;
|
||||
margins.bottom += shift.y;
|
||||
aFrameMetrics.SetDisplayPortMargins(margins);
|
||||
}
|
||||
|
||||
static void
|
||||
RecenterDisplayPort(mozilla::layers::FrameMetrics& aFrameMetrics)
|
||||
{
|
||||
ScreenMargin margins = aFrameMetrics.GetDisplayPortMargins();
|
||||
margins.right = margins.left = margins.LeftRight() / 2;
|
||||
margins.top = margins.bottom = margins.TopBottom() / 2;
|
||||
aFrameMetrics.SetDisplayPortMargins(margins);
|
||||
ScreenMargin margins = aFrameMetrics.GetDisplayPortMargins();
|
||||
margins.right = margins.left = margins.LeftRight() / 2;
|
||||
margins.top = margins.bottom = margins.TopBottom() / 2;
|
||||
aFrameMetrics.SetDisplayPortMargins(margins);
|
||||
}
|
||||
|
||||
static CSSPoint
|
||||
@ -128,9 +128,20 @@ ScrollFrame(nsIContent* aContent,
|
||||
CSSPoint actualScrollOffset = ScrollFrameTo(sf, apzScrollOffset, scrollUpdated);
|
||||
|
||||
if (scrollUpdated) {
|
||||
// Correct the display port due to the difference between mScrollOffset and the
|
||||
// actual scroll offset.
|
||||
AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
|
||||
if (aMetrics.IsScrollInfoLayer()) {
|
||||
// In cases where the APZ scroll offset is different from the content scroll
|
||||
// offset, we want to interpret the margins as relative to the APZ scroll
|
||||
// offset except when the frame is not scrollable by APZ. Therefore, if the
|
||||
// layer is a scroll info layer, we leave the margins as-is and they will
|
||||
// be interpreted as relative to the content scroll offset.
|
||||
if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
|
||||
frame->SchedulePaint();
|
||||
}
|
||||
} else {
|
||||
// Correct the display port due to the difference between mScrollOffset and the
|
||||
// actual scroll offset.
|
||||
AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
|
||||
}
|
||||
} else {
|
||||
// For whatever reason we couldn't update the scroll offset on the scroll frame,
|
||||
// which means the data APZ used for its displayport calculation is stale. Fall
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "BasicLayersImpl.h" // for FillRectWithMask
|
||||
#include "TextureHostBasic.h"
|
||||
#include "mozilla/layers/Effects.h"
|
||||
#include "mozilla/layers/YCbCrImageDataSerializer.h"
|
||||
#include "nsIWidget.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "mozilla/layers/ISurfaceAllocator.h"
|
||||
#include "mozilla/layers/ImageDataSerializer.h"
|
||||
#include "mozilla/layers/TextureClientRecycleAllocator.h"
|
||||
#include "mozilla/layers/YCbCrImageDataSerializer.h"
|
||||
#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc
|
||||
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
|
||||
#include "ImageContainer.h" // for PlanarYCbCrData, etc
|
||||
@ -838,10 +837,10 @@ TextureClient::CreateForYCbCr(ISurfaceAllocator* aAllocator,
|
||||
|
||||
// static
|
||||
already_AddRefed<TextureClient>
|
||||
TextureClient::CreateWithBufferSize(ISurfaceAllocator* aAllocator,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
size_t aSize,
|
||||
TextureFlags aTextureFlags)
|
||||
TextureClient::CreateForYCbCrWithBufferSize(ISurfaceAllocator* aAllocator,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
size_t aSize,
|
||||
TextureFlags aTextureFlags)
|
||||
{
|
||||
// also test the validity of aAllocator
|
||||
MOZ_ASSERT(aAllocator && aAllocator->IPCOpen());
|
||||
@ -849,8 +848,9 @@ TextureClient::CreateWithBufferSize(ISurfaceAllocator* aAllocator,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TextureData* data = BufferTextureData::CreateWithBufferSize(aAllocator, aFormat, aSize,
|
||||
aTextureFlags);
|
||||
TextureData* data =
|
||||
BufferTextureData::CreateForYCbCrWithBufferSize(aAllocator, aFormat, aSize,
|
||||
aTextureFlags);
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -297,10 +297,10 @@ public:
|
||||
// pointers) with a certain buffer size. It's unfortunate that we need this.
|
||||
// providing format and sizes could let us do more optimization.
|
||||
static already_AddRefed<TextureClient>
|
||||
CreateWithBufferSize(ISurfaceAllocator* aAllocator,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
size_t aSize,
|
||||
TextureFlags aTextureFlags);
|
||||
CreateForYCbCrWithBufferSize(ISurfaceAllocator* aAllocator,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
size_t aSize,
|
||||
TextureFlags aTextureFlags);
|
||||
|
||||
// Creates and allocates a TextureClient of the same type.
|
||||
already_AddRefed<TextureClient>
|
||||
|
@ -13,10 +13,9 @@
|
||||
#include "mozilla/layers/CompositableTransactionParent.h" // for CompositableParentManager
|
||||
#include "mozilla/layers/Compositor.h" // for Compositor
|
||||
#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
|
||||
#include "mozilla/layers/ImageDataSerializer.h"
|
||||
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
|
||||
#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
|
||||
#include "mozilla/layers/YCbCrImageDataSerializer.h"
|
||||
#include "mozilla/layers/ImageDataSerializer.h"
|
||||
#include "nsAString.h"
|
||||
#include "mozilla/RefPtr.h" // for nsRefPtr
|
||||
#include "nsPrintfCString.h" // for nsPrintfCString
|
||||
@ -95,7 +94,8 @@ TextureHost::CreateIPDLActor(CompositableParentManager* aManager,
|
||||
LayersBackend aLayersBackend,
|
||||
TextureFlags aFlags)
|
||||
{
|
||||
if (aSharedData.type() == SurfaceDescriptor::TSurfaceDescriptorMemory &&
|
||||
if (aSharedData.type() == SurfaceDescriptor::TSurfaceDescriptorBuffer &&
|
||||
aSharedData.get_SurfaceDescriptorBuffer().data().type() == MemoryOrShmem::Tuintptr_t &&
|
||||
!aManager->IsSameProcess())
|
||||
{
|
||||
NS_ERROR("A client process is trying to peek at our address space using a MemoryTexture!");
|
||||
@ -202,8 +202,7 @@ TextureHost::Create(const SurfaceDescriptor& aDesc,
|
||||
TextureFlags aFlags)
|
||||
{
|
||||
switch (aDesc.type()) {
|
||||
case SurfaceDescriptor::TSurfaceDescriptorShmem:
|
||||
case SurfaceDescriptor::TSurfaceDescriptorMemory:
|
||||
case SurfaceDescriptor::TSurfaceDescriptorBuffer:
|
||||
case SurfaceDescriptor::TSurfaceDescriptorDIB:
|
||||
case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
|
||||
return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
|
||||
@ -252,19 +251,26 @@ CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
|
||||
{
|
||||
RefPtr<TextureHost> result;
|
||||
switch (aDesc.type()) {
|
||||
case SurfaceDescriptor::TSurfaceDescriptorShmem: {
|
||||
const SurfaceDescriptorShmem& descriptor = aDesc.get_SurfaceDescriptorShmem();
|
||||
result = new ShmemTextureHost(descriptor.data(),
|
||||
descriptor.format(),
|
||||
aDeallocator,
|
||||
aFlags);
|
||||
break;
|
||||
}
|
||||
case SurfaceDescriptor::TSurfaceDescriptorMemory: {
|
||||
const SurfaceDescriptorMemory& descriptor = aDesc.get_SurfaceDescriptorMemory();
|
||||
result = new MemoryTextureHost(reinterpret_cast<uint8_t*>(descriptor.data()),
|
||||
descriptor.format(),
|
||||
aFlags);
|
||||
case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
|
||||
const SurfaceDescriptorBuffer& bufferDesc = aDesc.get_SurfaceDescriptorBuffer();
|
||||
const MemoryOrShmem& data = bufferDesc.data();
|
||||
switch (data.type()) {
|
||||
case MemoryOrShmem::TShmem: {
|
||||
result = new ShmemTextureHost(data.get_Shmem(),
|
||||
bufferDesc.desc(),
|
||||
aDeallocator,
|
||||
aFlags);
|
||||
break;
|
||||
}
|
||||
case MemoryOrShmem::Tuintptr_t: {
|
||||
result = new MemoryTextureHost(reinterpret_cast<uint8_t*>(data.get_uintptr_t()),
|
||||
bufferDesc.desc(),
|
||||
aFlags);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef XP_WIN
|
||||
@ -368,15 +374,30 @@ TextureSource::~TextureSource()
|
||||
MOZ_COUNT_DTOR(TextureSource);
|
||||
}
|
||||
|
||||
BufferTextureHost::BufferTextureHost(gfx::SurfaceFormat aFormat,
|
||||
BufferTextureHost::BufferTextureHost(const BufferDescriptor& aDesc,
|
||||
TextureFlags aFlags)
|
||||
: TextureHost(aFlags)
|
||||
, mCompositor(nullptr)
|
||||
, mFormat(aFormat)
|
||||
, mUpdateSerial(1)
|
||||
, mLocked(false)
|
||||
, mNeedsFullUpdate(false)
|
||||
{
|
||||
mDescriptor = aDesc;
|
||||
switch (mDescriptor.type()) {
|
||||
case BufferDescriptor::TYCbCrDescriptor: {
|
||||
const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
|
||||
mSize = ycbcr.ySize();
|
||||
mFormat = gfx::SurfaceFormat::YUV;
|
||||
break;
|
||||
}
|
||||
case BufferDescriptor::TRGBDescriptor: {
|
||||
const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
|
||||
mSize = rgb.size();
|
||||
mFormat = rgb.format();
|
||||
break;
|
||||
}
|
||||
default: MOZ_CRASH();
|
||||
}
|
||||
if (aFlags & TextureFlags::COMPONENT_ALPHA) {
|
||||
// One texture of a component alpha texture pair will start out all white.
|
||||
// This hack allows us to easily make sure that white will be uploaded.
|
||||
@ -385,22 +406,6 @@ BufferTextureHost::BufferTextureHost(gfx::SurfaceFormat aFormat,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BufferTextureHost::InitSize()
|
||||
{
|
||||
if (mFormat == gfx::SurfaceFormat::YUV) {
|
||||
YCbCrImageDataDeserializer yuvDeserializer(GetBuffer(), GetBufferSize());
|
||||
if (yuvDeserializer.IsValid()) {
|
||||
mSize = yuvDeserializer.GetYSize();
|
||||
}
|
||||
} else if (mFormat != gfx::SurfaceFormat::UNKNOWN) {
|
||||
ImageDataDeserializer deserializer(GetBuffer(), GetBufferSize());
|
||||
if (deserializer.IsValid()) {
|
||||
mSize = deserializer.GetSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BufferTextureHost::~BufferTextureHost()
|
||||
{}
|
||||
|
||||
@ -512,7 +517,8 @@ BufferTextureHost::MaybeUpload(nsIntRegion *aRegion)
|
||||
bool
|
||||
BufferTextureHost::Upload(nsIntRegion *aRegion)
|
||||
{
|
||||
if (!GetBuffer()) {
|
||||
uint8_t* buf = GetBuffer();
|
||||
if (!buf) {
|
||||
// We don't have a buffer; a possible cause is that the IPDL actor
|
||||
// is already dead. This inevitably happens as IPDL actors can die
|
||||
// at any time, so we want to silently return in this case.
|
||||
@ -527,11 +533,11 @@ BufferTextureHost::Upload(nsIntRegion *aRegion)
|
||||
NS_WARNING("BufferTextureHost: unsupported format!");
|
||||
return false;
|
||||
} else if (mFormat == gfx::SurfaceFormat::YUV) {
|
||||
YCbCrImageDataDeserializer yuvDeserializer(GetBuffer(), GetBufferSize());
|
||||
MOZ_ASSERT(yuvDeserializer.IsValid());
|
||||
const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
|
||||
|
||||
if (!mCompositor->SupportsEffect(EffectTypes::YCBCR)) {
|
||||
RefPtr<gfx::DataSourceSurface> surf = yuvDeserializer.ToDataSourceSurface();
|
||||
RefPtr<gfx::DataSourceSurface> surf =
|
||||
ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(buf, mDescriptor.get_YCbCrDescriptor());
|
||||
if (NS_WARN_IF(!surf)) {
|
||||
return false;
|
||||
}
|
||||
@ -565,21 +571,20 @@ BufferTextureHost::Upload(nsIntRegion *aRegion)
|
||||
srcV = mFirstSource->GetNextSibling()->GetNextSibling()->AsDataTextureSource();
|
||||
}
|
||||
|
||||
|
||||
RefPtr<gfx::DataSourceSurface> tempY =
|
||||
gfx::Factory::CreateWrappingDataSourceSurface(yuvDeserializer.GetYData(),
|
||||
yuvDeserializer.GetYStride(),
|
||||
yuvDeserializer.GetYSize(),
|
||||
gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetYChannel(buf, desc),
|
||||
desc.ySize().width,
|
||||
desc.ySize(),
|
||||
gfx::SurfaceFormat::A8);
|
||||
RefPtr<gfx::DataSourceSurface> tempCb =
|
||||
gfx::Factory::CreateWrappingDataSourceSurface(yuvDeserializer.GetCbData(),
|
||||
yuvDeserializer.GetCbCrStride(),
|
||||
yuvDeserializer.GetCbCrSize(),
|
||||
gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCbChannel(buf, desc),
|
||||
desc.cbCrSize().width,
|
||||
desc.cbCrSize(),
|
||||
gfx::SurfaceFormat::A8);
|
||||
RefPtr<gfx::DataSourceSurface> tempCr =
|
||||
gfx::Factory::CreateWrappingDataSourceSurface(yuvDeserializer.GetCrData(),
|
||||
yuvDeserializer.GetCbCrStride(),
|
||||
yuvDeserializer.GetCbCrSize(),
|
||||
gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCrChannel(buf, desc),
|
||||
desc.cbCrSize().width,
|
||||
desc.cbCrSize(),
|
||||
gfx::SurfaceFormat::A8);
|
||||
// We don't support partial updates for Y U V textures
|
||||
NS_ASSERTION(!aRegion, "Unsupported partial updates for YCbCr textures");
|
||||
@ -602,13 +607,10 @@ BufferTextureHost::Upload(nsIntRegion *aRegion)
|
||||
regionToUpdate = nullptr;
|
||||
}
|
||||
}
|
||||
ImageDataDeserializer deserializer(GetBuffer(), GetBufferSize());
|
||||
if (!deserializer.IsValid()) {
|
||||
NS_ERROR("Failed to deserialize image!");
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<gfx::DataSourceSurface> surf = deserializer.GetAsSurface();
|
||||
RefPtr<gfx::DataSourceSurface> surf =
|
||||
gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
|
||||
ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
|
||||
if (!surf) {
|
||||
return false;
|
||||
}
|
||||
@ -630,35 +632,29 @@ BufferTextureHost::GetAsSurface()
|
||||
NS_WARNING("BufferTextureHost: unsupported format!");
|
||||
return nullptr;
|
||||
} else if (mFormat == gfx::SurfaceFormat::YUV) {
|
||||
YCbCrImageDataDeserializer yuvDeserializer(GetBuffer(), GetBufferSize());
|
||||
if (!yuvDeserializer.IsValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
result = yuvDeserializer.ToDataSourceSurface();
|
||||
result = ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
|
||||
GetBuffer(), mDescriptor.get_YCbCrDescriptor());
|
||||
if (NS_WARN_IF(!result)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
ImageDataDeserializer deserializer(GetBuffer(), GetBufferSize());
|
||||
if (!deserializer.IsValid()) {
|
||||
NS_ERROR("Failed to deserialize image!");
|
||||
return nullptr;
|
||||
}
|
||||
result = deserializer.GetAsSurface();
|
||||
RefPtr<gfx::DataSourceSurface> surf =
|
||||
gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
|
||||
ImageDataSerializer::GetRGBStride(mDescriptor.get_RGBDescriptor()),
|
||||
mSize, mFormat);
|
||||
}
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
ShmemTextureHost::ShmemTextureHost(const ipc::Shmem& aShmem,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
const BufferDescriptor& aDesc,
|
||||
ISurfaceAllocator* aDeallocator,
|
||||
TextureFlags aFlags)
|
||||
: BufferTextureHost(aFormat, aFlags)
|
||||
: BufferTextureHost(aDesc, aFlags)
|
||||
, mShmem(MakeUnique<ipc::Shmem>(aShmem))
|
||||
, mDeallocator(aDeallocator)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ShmemTextureHost);
|
||||
InitSize();
|
||||
}
|
||||
|
||||
ShmemTextureHost::~ShmemTextureHost()
|
||||
@ -705,13 +701,12 @@ size_t ShmemTextureHost::GetBufferSize()
|
||||
}
|
||||
|
||||
MemoryTextureHost::MemoryTextureHost(uint8_t* aBuffer,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
const BufferDescriptor& aDesc,
|
||||
TextureFlags aFlags)
|
||||
: BufferTextureHost(aFormat, aFlags)
|
||||
: BufferTextureHost(aDesc, aFlags)
|
||||
, mBuffer(aBuffer)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MemoryTextureHost);
|
||||
InitSize();
|
||||
}
|
||||
|
||||
MemoryTextureHost::~MemoryTextureHost()
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
|
||||
#include "mozilla/layers/FenceUtils.h" // for FenceHandle
|
||||
#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
|
||||
#include "mozilla/layers/LayersSurfaces.h"
|
||||
#include "mozilla/mozalloc.h" // for operator delete
|
||||
#include "mozilla/UniquePtr.h" // for UniquePtr
|
||||
#include "nsCOMPtr.h" // for already_AddRefed
|
||||
@ -37,6 +38,7 @@ class Shmem;
|
||||
|
||||
namespace layers {
|
||||
|
||||
class BufferDescriptor;
|
||||
class Compositor;
|
||||
class CompositableParentManager;
|
||||
class SurfaceDescriptor;
|
||||
@ -567,8 +569,7 @@ protected:
|
||||
class BufferTextureHost : public TextureHost
|
||||
{
|
||||
public:
|
||||
BufferTextureHost(gfx::SurfaceFormat aFormat,
|
||||
TextureFlags aFlags);
|
||||
BufferTextureHost(const BufferDescriptor& aDescriptor, TextureFlags aFlags);
|
||||
|
||||
~BufferTextureHost();
|
||||
|
||||
@ -605,15 +606,13 @@ protected:
|
||||
bool Upload(nsIntRegion *aRegion = nullptr);
|
||||
bool MaybeUpload(nsIntRegion *aRegion = nullptr);
|
||||
|
||||
void InitSize();
|
||||
|
||||
virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
|
||||
|
||||
BufferDescriptor mDescriptor;
|
||||
RefPtr<Compositor> mCompositor;
|
||||
RefPtr<DataTextureSource> mFirstSource;
|
||||
nsIntRegion mMaybeUpdatedRegion;
|
||||
gfx::IntSize mSize;
|
||||
// format of the data that is shared with the content process.
|
||||
gfx::SurfaceFormat mFormat;
|
||||
uint32_t mUpdateSerial;
|
||||
bool mLocked;
|
||||
@ -629,7 +628,7 @@ class ShmemTextureHost : public BufferTextureHost
|
||||
{
|
||||
public:
|
||||
ShmemTextureHost(const mozilla::ipc::Shmem& aShmem,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
const BufferDescriptor& aDesc,
|
||||
ISurfaceAllocator* aDeallocator,
|
||||
TextureFlags aFlags);
|
||||
|
||||
@ -664,7 +663,7 @@ class MemoryTextureHost : public BufferTextureHost
|
||||
{
|
||||
public:
|
||||
MemoryTextureHost(uint8_t* aBuffer,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
const BufferDescriptor& aDesc,
|
||||
TextureFlags aFlags);
|
||||
|
||||
protected:
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "CompositorD3D11.h"
|
||||
#include "gfxContext.h"
|
||||
#include "Effects.h"
|
||||
#include "mozilla/layers/YCbCrImageDataSerializer.h"
|
||||
#include "gfxWindowsPlatform.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxPrefs.h"
|
||||
@ -665,8 +664,7 @@ CreateTextureHostD3D11(const SurfaceDescriptor& aDesc,
|
||||
{
|
||||
RefPtr<TextureHost> result;
|
||||
switch (aDesc.type()) {
|
||||
case SurfaceDescriptor::TSurfaceDescriptorShmem:
|
||||
case SurfaceDescriptor::TSurfaceDescriptorMemory: {
|
||||
case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
|
||||
result = CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
|
||||
break;
|
||||
}
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include "gfxContext.h"
|
||||
#include "gfxImageSurface.h"
|
||||
#include "Effects.h"
|
||||
#include "mozilla/layers/YCbCrImageDataSerializer.h"
|
||||
#include "gfxWindowsPlatform.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxUtils.h"
|
||||
@ -53,8 +52,7 @@ CreateTextureHostD3D9(const SurfaceDescriptor& aDesc,
|
||||
{
|
||||
RefPtr<TextureHost> result;
|
||||
switch (aDesc.type()) {
|
||||
case SurfaceDescriptor::TSurfaceDescriptorShmem:
|
||||
case SurfaceDescriptor::TSurfaceDescriptorMemory: {
|
||||
case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
|
||||
result = CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
|
||||
break;
|
||||
}
|
||||
@ -192,7 +190,7 @@ TextureSourceD3D9::InitTextures(DeviceManagerD3D9* aDeviceManager,
|
||||
}
|
||||
|
||||
tmpTexture->GetSurfaceLevel(0, getter_AddRefs(aSurface));
|
||||
|
||||
|
||||
HRESULT hr = aSurface->LockRect(&aLockedRect, nullptr, 0);
|
||||
if (FAILED(hr) || !aLockedRect.pBits) {
|
||||
gfxCriticalError() << "Failed to lock rect initialize texture in D3D9 " << hexa(hr);
|
||||
|
@ -1546,10 +1546,6 @@ CompositorParent::RecvNotifyChildCreated(const uint64_t& child)
|
||||
void
|
||||
CompositorParent::NotifyChildCreated(const uint64_t& aChild)
|
||||
{
|
||||
if (mApzcTreeManager) {
|
||||
NS_DispatchToMainThread(NS_NewRunnableMethodWithArg<uint64_t>(
|
||||
mApzcTreeManager, &APZCTreeManager::InitializeForLayersId, aChild));
|
||||
}
|
||||
sIndirectLayerTreesLock->AssertCurrentThreadOwns();
|
||||
sIndirectLayerTrees[aChild].mParent = this;
|
||||
sIndirectLayerTrees[aChild].mLayerManager = mLayerManager;
|
||||
@ -1559,9 +1555,6 @@ bool
|
||||
CompositorParent::RecvAdoptChild(const uint64_t& child)
|
||||
{
|
||||
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
||||
if (mApzcTreeManager) {
|
||||
mApzcTreeManager->AdoptLayersId(child, sIndirectLayerTrees[child].mParent->mApzcTreeManager.get());
|
||||
}
|
||||
NotifyChildCreated(child);
|
||||
if (sIndirectLayerTrees[child].mLayerTree) {
|
||||
sIndirectLayerTrees[child].mLayerTree->mLayerManager = mLayerManager;
|
||||
@ -1633,7 +1626,6 @@ ScopedLayerTreeRegistration::ScopedLayerTreeRegistration(APZCTreeManager* aApzct
|
||||
: mLayersId(aLayersId)
|
||||
{
|
||||
EnsureLayerTreeMapReady();
|
||||
aApzctm->InitializeForLayersId(aLayersId);
|
||||
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
||||
sIndirectLayerTrees[aLayersId].mRoot = aRoot;
|
||||
sIndirectLayerTrees[aLayersId].mController = aController;
|
||||
@ -1649,9 +1641,6 @@ ScopedLayerTreeRegistration::~ScopedLayerTreeRegistration()
|
||||
CompositorParent::SetControllerForLayerTree(uint64_t aLayersId,
|
||||
GeckoContentController* aController)
|
||||
{
|
||||
if (APZCTreeManager* apzctm = GetAPZCTreeManager(aLayersId)) {
|
||||
apzctm->InitializeForLayersId(aLayersId);
|
||||
}
|
||||
// This ref is adopted by UpdateControllerForLayersId().
|
||||
aController->AddRef();
|
||||
CompositorLoop()->PostTask(FROM_HERE,
|
||||
|
@ -60,38 +60,38 @@ ISurfaceAllocator::Finalize()
|
||||
}
|
||||
|
||||
static inline uint8_t*
|
||||
GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor, size_t& aSize)
|
||||
GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor)
|
||||
{
|
||||
MOZ_ASSERT(IsSurfaceDescriptorValid(aDescriptor));
|
||||
MOZ_ASSERT(aDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorShmem ||
|
||||
aDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorMemory);
|
||||
if (aDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorShmem) {
|
||||
Shmem shmem(aDescriptor.get_SurfaceDescriptorShmem().data());
|
||||
aSize = shmem.Size<uint8_t>();
|
||||
return shmem.get<uint8_t>();
|
||||
MOZ_RELEASE_ASSERT(aDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorBuffer);
|
||||
|
||||
auto memOrShmem = aDescriptor.get_SurfaceDescriptorBuffer().data();
|
||||
if (memOrShmem.type() == MemoryOrShmem::TShmem) {
|
||||
return memOrShmem.get_Shmem().get<uint8_t>();
|
||||
} else {
|
||||
const SurfaceDescriptorMemory& image = aDescriptor.get_SurfaceDescriptorMemory();
|
||||
aSize = std::numeric_limits<size_t>::max();
|
||||
return reinterpret_cast<uint8_t*>(image.data());
|
||||
return reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DrawTarget>
|
||||
GetDrawTargetForDescriptor(const SurfaceDescriptor& aDescriptor, gfx::BackendType aBackend)
|
||||
{
|
||||
size_t size;
|
||||
uint8_t* data = GetAddressFromDescriptor(aDescriptor, size);
|
||||
ImageDataDeserializer image(data, size);
|
||||
return image.GetAsDrawTarget(aBackend);
|
||||
uint8_t* data = GetAddressFromDescriptor(aDescriptor);
|
||||
auto rgb = aDescriptor.get_SurfaceDescriptorBuffer().desc().get_RGBDescriptor();
|
||||
uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
|
||||
return gfx::Factory::CreateDrawTargetForData(gfx::BackendType::CAIRO,
|
||||
data, rgb.size(),
|
||||
stride, rgb.format());
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
GetSurfaceForDescriptor(const SurfaceDescriptor& aDescriptor)
|
||||
{
|
||||
size_t size;
|
||||
uint8_t* data = GetAddressFromDescriptor(aDescriptor, size);
|
||||
ImageDataDeserializer image(data, size);
|
||||
return image.GetAsSurface();
|
||||
uint8_t* data = GetAddressFromDescriptor(aDescriptor);
|
||||
auto rgb = aDescriptor.get_SurfaceDescriptorBuffer().desc().get_RGBDescriptor();
|
||||
uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
|
||||
return gfx::Factory::CreateWrappingDataSourceSurface(data, stride, rgb.size(),
|
||||
rgb.format());
|
||||
}
|
||||
|
||||
bool
|
||||
@ -116,12 +116,14 @@ ISurfaceAllocator::AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
|
||||
}
|
||||
gfx::SurfaceFormat format =
|
||||
gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aContent);
|
||||
size_t size = ImageDataSerializer::ComputeMinBufferSize(aSize, format);
|
||||
size_t size = ImageDataSerializer::ComputeRGBBufferSize(aSize, format);
|
||||
if (!size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MemoryOrShmem bufferDesc;
|
||||
if (IsSameProcess()) {
|
||||
uint8_t *data = new (std::nothrow) uint8_t[size];
|
||||
uint8_t* data = new (std::nothrow) uint8_t[size];
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
@ -133,7 +135,7 @@ ISurfaceAllocator::AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
|
||||
memset(data, 0, size);
|
||||
}
|
||||
#endif
|
||||
*aBuffer = SurfaceDescriptorMemory((uintptr_t)data, format);
|
||||
bufferDesc = reinterpret_cast<uintptr_t>(data);
|
||||
} else {
|
||||
|
||||
mozilla::ipc::SharedMemory::SharedMemoryType shmemType = OptimalShmemType();
|
||||
@ -142,19 +144,19 @@ ISurfaceAllocator::AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
|
||||
return false;
|
||||
}
|
||||
|
||||
*aBuffer = SurfaceDescriptorShmem(shmem, format);
|
||||
bufferDesc = shmem;
|
||||
}
|
||||
|
||||
uint8_t* data = GetAddressFromDescriptor(*aBuffer, size);
|
||||
ImageDataSerializer serializer(data, size);
|
||||
serializer.InitializeBufferInfo(aSize, format);
|
||||
|
||||
*aBuffer = SurfaceDescriptorBuffer(RGBDescriptor(aSize, format), bufferDesc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
ISurfaceAllocator::IsShmem(SurfaceDescriptor* aSurface)
|
||||
{
|
||||
return aSurface && (aSurface->type() == SurfaceDescriptor::TSurfaceDescriptorShmem);
|
||||
return aSurface && (aSurface->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer)
|
||||
&& (aSurface->get_SurfaceDescriptorBuffer().data().type() == MemoryOrShmem::TShmem);
|
||||
}
|
||||
|
||||
void
|
||||
@ -172,17 +174,18 @@ ISurfaceAllocator::DestroySharedSurface(SurfaceDescriptor* aSurface)
|
||||
if (!IPCOpen()) {
|
||||
return;
|
||||
}
|
||||
switch (aSurface->type()) {
|
||||
case SurfaceDescriptor::TSurfaceDescriptorShmem:
|
||||
DeallocShmem(aSurface->get_SurfaceDescriptorShmem().data());
|
||||
SurfaceDescriptorBuffer& desc = aSurface->get_SurfaceDescriptorBuffer();
|
||||
switch (desc.data().type()) {
|
||||
case MemoryOrShmem::TShmem: {
|
||||
DeallocShmem(desc.data().get_Shmem());
|
||||
break;
|
||||
case SurfaceDescriptor::TSurfaceDescriptorMemory:
|
||||
GfxMemoryImageReporter::WillFree((uint8_t*)aSurface->get_SurfaceDescriptorMemory().data());
|
||||
delete [] (uint8_t*)aSurface->get_SurfaceDescriptorMemory().data();
|
||||
break;
|
||||
case SurfaceDescriptor::Tnull_t:
|
||||
case SurfaceDescriptor::T__None:
|
||||
}
|
||||
case MemoryOrShmem::Tuintptr_t: {
|
||||
uint8_t* ptr = (uint8_t*)desc.data().get_uintptr_t();
|
||||
GfxMemoryImageReporter::WillFree(ptr);
|
||||
delete [] ptr;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NS_RUNTIMEABORT("surface type not implemented!");
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user