Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2015-12-22 12:28:35 +01:00
commit a38f023bca
253 changed files with 3663 additions and 3968 deletions

View File

@ -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));
},

View 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();
}

View File

@ -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'

View 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);
});
});
}

View 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();

View File

@ -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")

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"
}

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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);

View File

@ -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))

View File

@ -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

View File

@ -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);
}

View File

@ -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',

View File

@ -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)

View File

@ -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);
}

View File

@ -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 {};

View 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;
}

View File

@ -24,3 +24,4 @@ support-files =
[test_require_lazy.js]
[test_require.js]
[test_stack.js]
[test_executeSoon.js]

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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";
}));
}));

View File

@ -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) {

View File

@ -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

View 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>

View File

@ -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");

View File

@ -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();
}

View File

@ -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,"

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()) {

View File

@ -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;

View File

@ -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()
{

View File

@ -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:

View File

@ -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

View File

@ -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;

View File

@ -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")) {

View File

@ -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']

View File

@ -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,

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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]

View File

@ -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(),

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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();
};

View File

@ -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});
},

View File

@ -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',

View File

@ -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();
});
}

View File

@ -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]

View 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"]}
]
}
]
}
]
}
]
}

View File

@ -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.
*/

View File

@ -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;
}

View File

@ -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,

View File

@ -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>
{

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -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(

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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());
}

View File

@ -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

View File

@ -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"

View File

@ -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;
}

View File

@ -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>

View File

@ -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()

View File

@ -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:

View File

@ -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;
}

View File

@ -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);

View File

@ -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,

View File

@ -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