зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to fx-team
This commit is contained in:
Коммит
e61f4dab12
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
|
||||
|
@ -131,7 +131,7 @@
|
|||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="683623c76338dccd65e698bfb5c4cfee8808d799"/>
|
||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="2cd3c7277621902b3749797eaaab618c4c80a58b"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="1adbb04024c6181f1089f3a12ee46f26663ea1db"/>
|
||||
<project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
|
||||
<project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
|
||||
</manifest>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="999e945b85c578c503ad445c2285940f16aacdae">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
|
||||
|
@ -120,7 +120,7 @@
|
|||
<!-- Flame specific things -->
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="e8a318f7690092e639ba88891606f4183e846d3f"/>
|
||||
<project name="device/qcom/common" path="device/qcom/common" revision="34ed8345250bb97262d70a052217a92e83444ede"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="a927b19531c293dca1e80e081e0ab5715f4d1bfe"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="d7e5ed0a081a39419932b8b9fdefc9b2d903850d"/>
|
||||
<project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="30d40a5636ff28a554f1d8e9d975bfd04c2463d3"/>
|
||||
<project name="kernel_lk" path="bootable/bootloader/lk" remote="b2g" revision="2b1d8b5b7a760230f4c94c02e733e3929f44253a"/>
|
||||
<project name="platform_bootable_recovery" path="bootable/recovery" remote="b2g" revision="e81502511cda303c803e63f049574634bc96f9f2"/>
|
||||
|
@ -143,7 +143,7 @@
|
|||
<project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
|
||||
<project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
|
||||
<project name="platform/system/core" path="system/core" revision="bbf7212289fc8311e43f9d11e10788e310d36a08"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="2cd3c7277621902b3749797eaaab618c4c80a58b"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="1adbb04024c6181f1089f3a12ee46f26663ea1db"/>
|
||||
<project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
|
||||
<project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4ae5df252123591d5b941191790e7abed1bce5a4"/>
|
||||
<project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="ce18b47b4a4f93a581d672bbd5cb6d12fe796ca9"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "4024e28dbc44b11d3297378484c2474dcee425fa",
|
||||
"revision": "549d40d59aed92eb33c912e0a8c7ea0faf0e7a18",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
|
||||
|
@ -127,7 +127,7 @@
|
|||
<project name="device-mako" path="device/lge/mako" remote="b2g" revision="78d17f0c117f0c66dd55ee8d5c5dde8ccc93ecba"/>
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
|
||||
<project name="device/lge/mako-kernel" path="device/lge/mako-kernel" revision="d1729e53d71d711c8fde25eab8728ff2b9b4df0e"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="2cd3c7277621902b3749797eaaab618c4c80a58b"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="1adbb04024c6181f1089f3a12ee46f26663ea1db"/>
|
||||
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
|
||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
|
||||
<project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="0e1929fa3aa38bf9d40e9e953d619fab8164c82e"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c527178b78a5bc85b76f89d6ba7f0bb464963b79"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="25e998814ba89f30fe44cd2fdfbb44d160a04641"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -23,7 +23,7 @@ browser.jar:
|
|||
* skin/classic/browser/browser.css
|
||||
* skin/classic/browser/browser-lightweightTheme.css
|
||||
skin/classic/browser/click-to-play-warning-stripes.png
|
||||
* skin/classic/browser/content-contextmenu.svg
|
||||
skin/classic/browser/content-contextmenu.svg
|
||||
* skin/classic/browser/engineManager.css
|
||||
skin/classic/browser/fullscreen-darknoise.png
|
||||
skin/classic/browser/Geolocation-16.png
|
||||
|
|
|
@ -23,7 +23,7 @@ browser.jar:
|
|||
* skin/classic/browser/browser.css (browser.css)
|
||||
* skin/classic/browser/browser-lightweightTheme.css
|
||||
skin/classic/browser/click-to-play-warning-stripes.png
|
||||
* skin/classic/browser/content-contextmenu.svg
|
||||
skin/classic/browser/content-contextmenu.svg
|
||||
* skin/classic/browser/engineManager.css (engineManager.css)
|
||||
skin/classic/browser/fullscreen-darknoise.png
|
||||
skin/classic/browser/Geolocation-16.png
|
||||
|
|
|
@ -25,7 +25,7 @@ browser.jar:
|
|||
* skin/classic/browser/browser.css
|
||||
* skin/classic/browser/browser-lightweightTheme.css
|
||||
skin/classic/browser/click-to-play-warning-stripes.png
|
||||
* skin/classic/browser/content-contextmenu.svg
|
||||
skin/classic/browser/content-contextmenu.svg
|
||||
* skin/classic/browser/engineManager.css
|
||||
skin/classic/browser/fullscreen-darknoise.png
|
||||
skin/classic/browser/Geolocation-16.png
|
||||
|
@ -437,7 +437,7 @@ browser.jar:
|
|||
* skin/classic/aero/browser/browser.css (browser-aero.css)
|
||||
* skin/classic/aero/browser/browser-lightweightTheme.css
|
||||
skin/classic/aero/browser/click-to-play-warning-stripes.png
|
||||
* skin/classic/aero/browser/content-contextmenu.svg
|
||||
skin/classic/aero/browser/content-contextmenu.svg
|
||||
* skin/classic/aero/browser/engineManager.css
|
||||
skin/classic/aero/browser/fullscreen-darknoise.png
|
||||
skin/classic/aero/browser/Geolocation-16.png
|
||||
|
|
|
@ -12,7 +12,7 @@ ac_add_app_options x86_64 --target=x86_64-apple-darwin$DARWIN_VERSION
|
|||
ac_add_app_options i386 --with-unify-dist=../x86_64/dist
|
||||
ac_add_app_options x86_64 --with-unify-dist=../i386/dist
|
||||
|
||||
ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.6.sdk
|
||||
ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.7.sdk
|
||||
|
||||
. $topsrcdir/build/macosx/mozconfig.common
|
||||
|
||||
|
|
|
@ -266,7 +266,7 @@ class RemoteAutomation(Automation):
|
|||
messages = []
|
||||
for line in lines:
|
||||
# This passes the line to the logger (to be logged or buffered)
|
||||
# and returns a list of structured messages (dict) or None, depending on the log
|
||||
# and returns a list of structured messages (dict)
|
||||
parsed_messages = self.messageLogger.write(line)
|
||||
for message in parsed_messages:
|
||||
if message['action'] == 'test_start':
|
||||
|
|
|
@ -25,17 +25,28 @@ public class FennecMochitestAssert implements Assert {
|
|||
// Measure the time it takes to run test case
|
||||
private long mStartTime = 0;
|
||||
|
||||
public FennecMochitestAssert() {
|
||||
}
|
||||
// Structured logger
|
||||
private StructuredLogger mLogger;
|
||||
|
||||
/** Write information to a logfile and logcat */
|
||||
public void dumpLog(String message) {
|
||||
FennecNativeDriver.log(FennecNativeDriver.LogLevel.INFO, message);
|
||||
mLogger.info(message);
|
||||
}
|
||||
|
||||
public void dumpLog(String message, Throwable t) {
|
||||
mLogger.error(message + " - " + t.toString());
|
||||
}
|
||||
|
||||
/** Write information to a logfile and logcat */
|
||||
public void dumpLog(String message, Throwable t) {
|
||||
FennecNativeDriver.log(FennecNativeDriver.LogLevel.INFO, message, t);
|
||||
static class DumpLogCallback implements StructuredLogger.LoggerCallback {
|
||||
public void call(String output) {
|
||||
FennecNativeDriver.log(FennecNativeDriver.LogLevel.INFO, output);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public FennecMochitestAssert() {
|
||||
mLogger = new StructuredLogger("robocop", new DumpLogCallback());
|
||||
}
|
||||
|
||||
/** Set the filename used for dumpLog. */
|
||||
|
@ -44,15 +55,13 @@ public class FennecMochitestAssert implements Assert {
|
|||
|
||||
String message;
|
||||
if (!mLogStarted) {
|
||||
dumpLog("SimpleTest START");
|
||||
mLogger.info("SimpleTest START");
|
||||
mLogStarted = true;
|
||||
}
|
||||
|
||||
if (mLogTestName != "") {
|
||||
long diff = SystemClock.uptimeMillis() - mStartTime;
|
||||
message = "TEST-END | " + mLogTestName;
|
||||
message += " | finished in " + diff + "ms";
|
||||
dumpLog(message);
|
||||
mLogger.testEnd(mLogTestName, "OK", "finished in " + diff + "ms");
|
||||
mLogTestName = "";
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +71,7 @@ public class FennecMochitestAssert implements Assert {
|
|||
mLogTestName = nameParts[nameParts.length - 1];
|
||||
mStartTime = SystemClock.uptimeMillis();
|
||||
|
||||
dumpLog("TEST-START | " + mLogTestName);
|
||||
mLogger.testStart(mLogTestName);
|
||||
}
|
||||
|
||||
class testInfo {
|
||||
|
@ -81,21 +90,22 @@ public class FennecMochitestAssert implements Assert {
|
|||
|
||||
}
|
||||
|
||||
private void _logMochitestResult(testInfo test, String passString, String failString) {
|
||||
/** Used to log a subtest's result.
|
||||
* test represents the subtest (an assertion).
|
||||
* passStatus and passExpected are the actual status and the expected status if the assertion is true.
|
||||
* failStatus and failExpected are the actual status and the expected status otherwise.
|
||||
*/
|
||||
private void _logMochitestResult(testInfo test, String passStatus, String passExpected, String failStatus, String failExpected) {
|
||||
boolean isError = true;
|
||||
String resultString = failString;
|
||||
if (test.mResult || test.mTodo) {
|
||||
isError = false;
|
||||
}
|
||||
if (test.mResult)
|
||||
{
|
||||
resultString = passString;
|
||||
mLogger.testStatus(mLogTestName, test.mName, passStatus, passExpected, test.mDiag);
|
||||
} else {
|
||||
mLogger.testStatus(mLogTestName, test.mName, failStatus, failExpected, test.mDiag);
|
||||
}
|
||||
String diag = test.mName;
|
||||
if (test.mDiag != null) diag += " - " + test.mDiag;
|
||||
|
||||
String message = resultString + " | " + mLogTestName + " | " + diag;
|
||||
dumpLog(message);
|
||||
|
||||
if (test.mInfo) {
|
||||
// do not count TEST-INFO messages
|
||||
|
@ -107,6 +117,8 @@ public class FennecMochitestAssert implements Assert {
|
|||
mPassed++;
|
||||
}
|
||||
if (isError) {
|
||||
String message = "TEST-UNEXPECTED-" + failStatus + " | " + mLogTestName + " | "
|
||||
+ test.mName + " - " + test.mDiag;
|
||||
junit.framework.Assert.fail(message);
|
||||
}
|
||||
}
|
||||
|
@ -116,27 +128,20 @@ public class FennecMochitestAssert implements Assert {
|
|||
|
||||
if (mLogTestName != "") {
|
||||
long diff = SystemClock.uptimeMillis() - mStartTime;
|
||||
message = "TEST-END | " + mLogTestName;
|
||||
message += " | finished in " + diff + "ms";
|
||||
dumpLog(message);
|
||||
mLogger.testEnd(mLogTestName, "OK", "finished in " + diff + "ms");
|
||||
mLogTestName = "";
|
||||
}
|
||||
|
||||
message = "TEST-START | Shutdown";
|
||||
dumpLog(message);
|
||||
message = "Passed: " + Integer.toString(mPassed);
|
||||
dumpLog(message);
|
||||
message = "Failed: " + Integer.toString(mFailed);
|
||||
dumpLog(message);
|
||||
message = "Todo: " + Integer.toString(mTodo);
|
||||
dumpLog(message);
|
||||
message = "SimpleTest FINISHED";
|
||||
dumpLog(message);
|
||||
mLogger.info("TEST-START | Shutdown");
|
||||
mLogger.info("Passed: " + Integer.toString(mPassed));
|
||||
mLogger.info("Failed: " + Integer.toString(mFailed));
|
||||
mLogger.info("Todo: " + Integer.toString(mTodo));
|
||||
mLogger.info("SimpleTest FINISHED");
|
||||
}
|
||||
|
||||
public void ok(boolean condition, String name, String diag) {
|
||||
testInfo test = new testInfo(condition, name, diag, false, false);
|
||||
_logMochitestResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL");
|
||||
_logMochitestResult(test, "PASS", "PASS", "FAIL", "PASS");
|
||||
mTestList.add(test);
|
||||
}
|
||||
|
||||
|
@ -193,7 +198,7 @@ public class FennecMochitestAssert implements Assert {
|
|||
|
||||
public void todo(boolean condition, String name, String diag) {
|
||||
testInfo test = new testInfo(condition, name, diag, true, false);
|
||||
_logMochitestResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL");
|
||||
_logMochitestResult(test, "PASS", "FAIL", "FAIL", "FAIL");
|
||||
mTestList.add(test);
|
||||
}
|
||||
|
||||
|
@ -245,7 +250,6 @@ public class FennecMochitestAssert implements Assert {
|
|||
}
|
||||
|
||||
public void info(String name, String message) {
|
||||
testInfo test = new testInfo(true, name, message, false, true);
|
||||
_logMochitestResult(test, "TEST-INFO", "INFO FAILED?");
|
||||
mLogger.info(name + " | " + message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,10 @@ public class StructuredLogger {
|
|||
this(name, component, new StandardLoggerCallback());
|
||||
}
|
||||
|
||||
public StructuredLogger(String name, LoggerCallback callback) {
|
||||
this(name, null, callback);
|
||||
}
|
||||
|
||||
public StructuredLogger(String name) {
|
||||
this(name, null, new StandardLoggerCallback());
|
||||
}
|
||||
|
|
|
@ -1263,7 +1263,6 @@ X11/Xlocale.h
|
|||
X11/Xos.h
|
||||
X11/Xutil.h
|
||||
zmouse.h
|
||||
speex/speex_resampler.h
|
||||
soundtouch/SoundTouch.h
|
||||
#if MOZ_NATIVE_PNG==1
|
||||
png.h
|
||||
|
@ -1325,6 +1324,7 @@ vpx_mem/vpx_mem.h
|
|||
vorbis/codec.h
|
||||
theora/theoradec.h
|
||||
tremor/ivorbiscodec.h
|
||||
speex/speex_resampler.h
|
||||
ogg/ogg.h
|
||||
ogg/os_types.h
|
||||
nestegg/nestegg.h
|
||||
|
|
23
configure.in
23
configure.in
|
@ -3844,6 +3844,7 @@ MOZ_PAY=
|
|||
MOZ_AUDIO_CHANNEL_MANAGER=
|
||||
NSS_NO_LIBPKIX=
|
||||
MOZ_CONTENT_SANDBOX=
|
||||
MOZ_GMP_SANDBOX=
|
||||
JSGC_USE_EXACT_ROOTING=1
|
||||
JSGC_GENERATIONAL=
|
||||
|
||||
|
@ -6405,6 +6406,28 @@ fi
|
|||
|
||||
AC_SUBST(MOZ_CONTENT_SANDBOX)
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Gecko Media Plugin sandboxing
|
||||
dnl ========================================================
|
||||
case $OS_TARGET in
|
||||
WINNT)
|
||||
MOZ_GMP_SANDBOX=1
|
||||
;;
|
||||
Linux)
|
||||
case $CPU_ARCH in
|
||||
x86_64|x86)
|
||||
MOZ_GMP_SANDBOX=1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -n "$MOZ_GMP_SANDBOX"; then
|
||||
AC_DEFINE(MOZ_GMP_SANDBOX)
|
||||
fi
|
||||
|
||||
AC_SUBST(MOZ_GMP_SANDBOX)
|
||||
|
||||
dnl ========================================================
|
||||
dnl =
|
||||
dnl = Module specific options
|
||||
|
|
|
@ -1746,7 +1746,7 @@ Element::DispatchClickEvent(nsPresContext* aPresContext,
|
|||
nsIFrame*
|
||||
Element::GetPrimaryFrame(mozFlushType aType)
|
||||
{
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
nsIDocument* doc = GetComposedDoc();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -1516,7 +1516,7 @@ struct nsIDocument::FrameRequest
|
|||
int32_t mHandle;
|
||||
};
|
||||
|
||||
static already_AddRefed<mozilla::dom::NodeInfo> nullNodeInfo(nullptr);
|
||||
static already_AddRefed<mozilla::dom::NodeInfo> nullNodeInfo;
|
||||
|
||||
// ==================================================================
|
||||
// =
|
||||
|
|
|
@ -327,12 +327,19 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsIScriptElement *script = aRequest->mElement;
|
||||
if (aScriptFromHead &&
|
||||
!(script && (script->GetScriptAsync() || script->GetScriptDeferred()))) {
|
||||
nsCOMPtr<nsIHttpChannelInternal>
|
||||
internalHttpChannel(do_QueryInterface(channel));
|
||||
if (internalHttpChannel)
|
||||
nsCOMPtr<nsIHttpChannelInternal>
|
||||
internalHttpChannel(do_QueryInterface(channel));
|
||||
|
||||
if (internalHttpChannel) {
|
||||
if (aScriptFromHead &&
|
||||
!(script && (script->GetScriptAsync() || script->GetScriptDeferred()))) {
|
||||
// synchronous head scripts block lading of most other non js/css
|
||||
// content such as images
|
||||
internalHttpChannel->SetLoadAsBlocking(true);
|
||||
} else if (!(script && script->GetScriptDeferred())) {
|
||||
// other scripts are neither blocked nor prioritized unless marked deferred
|
||||
internalHttpChannel->SetLoadUnblocked(true);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
|
||||
|
|
|
@ -650,3 +650,4 @@ support-files = file_bug503473-frame.sjs
|
|||
[test_bug1011748.html]
|
||||
skip-if = buildapp == 'b2g' || e10s
|
||||
support-files = file_bug1011748_redirect.sjs file_bug1011748_OK.sjs
|
||||
[test_bug1025933.html]
|
||||
|
|
|
@ -1,18 +1,37 @@
|
|||
function setOurState(data) {
|
||||
x = { data: data, QueryInterface: function(iid) { return this } };
|
||||
x.wrappedJSObject = x;
|
||||
setObjectState("bug602838", x);
|
||||
}
|
||||
|
||||
function getOurState() {
|
||||
var data;
|
||||
getObjectState("bug602838", function(x) {
|
||||
// x can be null if no one has set any state yet
|
||||
if (x) {
|
||||
data = x.wrappedJSObject.data;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
if (request.queryString) {
|
||||
let blockedResponse = null;
|
||||
getObjectState("bug602838", function(x) { blockedResponse = x.wrappedJSObject.r });
|
||||
blockedResponse.finish();
|
||||
setObjectState("bug602838", null);
|
||||
let blockedResponse = getOurState();
|
||||
if (typeof(blockedResponse) == "object") {
|
||||
blockedResponse.finish();
|
||||
setOurState(null);
|
||||
} else {
|
||||
setOurState("unblocked");
|
||||
}
|
||||
return;
|
||||
}
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
response.setHeader("Content-Type", "text/javascript", false);
|
||||
response.write("ok(asyncRan, 'Async script should have run first.'); firstRan = true;");
|
||||
response.processAsync();
|
||||
|
||||
x = { r: response, QueryInterface: function(iid) { return this } };
|
||||
x.wrappedJSObject = x;
|
||||
setObjectState("bug602838", x);
|
||||
if (getOurState() != "unblocked") {
|
||||
response.processAsync();
|
||||
setOurState(response);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1025933
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1025933</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 1025933 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function test() {
|
||||
var s = document.getElementById("host").createShadowRoot();
|
||||
s.innerHTML = '<div style="width:100px;height:100px;background:red"></div>';
|
||||
var el = s.firstElementChild;
|
||||
is(el.clientWidth, 100);
|
||||
is(el.clientHeight, 100);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="test()">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1025933">Mozilla Bug 1025933</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<div id="host"></div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -2647,12 +2647,15 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
|
|||
// Tell the decoder about its MediaResource now so things like principals are
|
||||
// available immediately.
|
||||
mDecoder->SetResource(aStream);
|
||||
aDecoder->SetAudioChannel(mAudioChannel);
|
||||
mDecoder->SetAudioChannel(mAudioChannel);
|
||||
mDecoder->SetAudioCaptured(mAudioCaptured);
|
||||
mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
|
||||
mDecoder->SetPreservesPitch(mPreservesPitch);
|
||||
mDecoder->SetPlaybackRate(mPlaybackRate);
|
||||
|
||||
if (mMediaKeys) {
|
||||
mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
|
||||
}
|
||||
if (mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
|
||||
mDecoder->SetMinimizePrerollUntilPlaybackStarts();
|
||||
}
|
||||
|
@ -3974,6 +3977,9 @@ HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
|
|||
if (mMediaKeys != aMediaKeys) {
|
||||
mMediaKeys = aMediaKeys;
|
||||
}
|
||||
if (mDecoder) {
|
||||
mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
|
||||
}
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
return promise.forget();
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ class ReentrantMonitor;
|
|||
class VideoFrameContainer;
|
||||
class TimedMetadata;
|
||||
class MediaDecoderOwner;
|
||||
class CDMProxy;
|
||||
|
||||
typedef nsDataHashtable<nsCStringHashKey, nsCString> MetadataTags;
|
||||
|
||||
|
@ -137,6 +138,9 @@ public:
|
|||
uint32_t& mParsed;
|
||||
uint32_t& mDecoded;
|
||||
};
|
||||
|
||||
virtual nsresult SetCDMProxy(CDMProxy* aProxy) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual CDMProxy* GetCDMProxy() { return nullptr; }
|
||||
};
|
||||
|
||||
class MetadataEventRunner : public nsRunnable
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "AudioNodeEngine.h"
|
||||
#include "AudioNodeExternalInputStream.h"
|
||||
#include "AudioChannelFormat.h"
|
||||
#include "speex/speex_resampler.h"
|
||||
#include "mozilla/dom/MediaStreamAudioSourceNode.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "AudioMixer.h"
|
||||
#include "AudioChannelFormat.h"
|
||||
#include "Latency.h"
|
||||
#include "speex/speex_resampler.h"
|
||||
#include <speex/speex_resampler.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
|
|
@ -1660,6 +1660,25 @@ bool MediaDecoder::CanPlayThrough()
|
|||
stats.mDownloadPosition > stats.mPlaybackPosition + readAheadMargin;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaDecoder::SetCDMProxy(CDMProxy* aProxy)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mProxy = aProxy;
|
||||
// Awaken any readers waiting for the proxy.
|
||||
NotifyWaitingForResourcesStatusChanged();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
CDMProxy*
|
||||
MediaDecoder::GetCDMProxy()
|
||||
{
|
||||
GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
MOZ_ASSERT(OnDecodeThread() || NS_IsMainThread());
|
||||
return mProxy;
|
||||
}
|
||||
|
||||
#ifdef MOZ_RAW
|
||||
bool
|
||||
MediaDecoder::IsRawEnabled()
|
||||
|
|
|
@ -190,6 +190,7 @@ destroying the MediaDecoder object.
|
|||
#include "MediaStreamGraph.h"
|
||||
#include "AbstractMediaDecoder.h"
|
||||
#include "necko-config.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
|
||||
class nsIStreamListener;
|
||||
class nsIPrincipal;
|
||||
|
@ -849,6 +850,12 @@ public:
|
|||
// The decoder monitor must be held.
|
||||
bool IsLogicallyPlaying();
|
||||
|
||||
// This takes the decoder monitor.
|
||||
virtual nsresult SetCDMProxy(CDMProxy* aProxy) MOZ_OVERRIDE;
|
||||
|
||||
// Decoder monitor must be held.
|
||||
virtual CDMProxy* GetCDMProxy() MOZ_OVERRIDE;
|
||||
|
||||
#ifdef MOZ_RAW
|
||||
static bool IsRawEnabled();
|
||||
#endif
|
||||
|
@ -1099,6 +1106,8 @@ private:
|
|||
// The |RestrictedAccessMonitor| member object.
|
||||
RestrictedAccessMonitor mReentrantMonitor;
|
||||
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
|
||||
protected:
|
||||
// Data about MediaStreams that are being fed by this decoder.
|
||||
nsTArray<OutputStreamData> mOutputStreams;
|
||||
|
|
|
@ -137,6 +137,12 @@ public:
|
|||
// Called by the media decoder object, on the main thread,
|
||||
// when the connection between Rtsp server and client gets lost.
|
||||
virtual void ResetConnectionState() = 0;
|
||||
|
||||
// Dispatches a "needkey" event to the HTMLMediaElement, with the
|
||||
// provided init data.
|
||||
// Main thread only.
|
||||
virtual void DispatchNeedKey(const nsTArray<uint8_t>& aInitData,
|
||||
const nsAString& aInitDataType) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -176,7 +176,7 @@ public:
|
|||
// Indicates if the media is seekable.
|
||||
// ReadMetada should be called before calling this method.
|
||||
virtual bool IsMediaSeekable() = 0;
|
||||
|
||||
|
||||
protected:
|
||||
virtual ~MediaDecoderReader();
|
||||
|
||||
|
|
|
@ -454,7 +454,8 @@ private:
|
|||
// shutdown notification and stop Read Thread.
|
||||
nsContentUtils::RegisterShutdownObserver(this);
|
||||
|
||||
nsRefPtr<nsIRunnable> event = new ExtractRunnable(this);
|
||||
already_AddRefed<Session> session(this); // addref in MediaRecorder::Start
|
||||
nsRefPtr<nsIRunnable> event = new ExtractRunnable(Move(session));
|
||||
if (NS_FAILED(mReadThread->Dispatch(event, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Failed to dispatch ExtractRunnable at beginning");
|
||||
}
|
||||
|
@ -472,7 +473,8 @@ private:
|
|||
MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
|
||||
}
|
||||
// Destroy this session object in main thread.
|
||||
if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) {
|
||||
already_AddRefed<Session> session(this); // addref in MediaRecorder::Start
|
||||
if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(Move(session))))) {
|
||||
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include "DOMMediaStream.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "speex/speex_resampler.h"
|
||||
#ifdef MOZ_WEBRTC
|
||||
#include "AudioOutputObserver.h"
|
||||
#endif
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "VideoSegment.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "nsAutoRef.h"
|
||||
#include "speex/speex_resampler.h"
|
||||
#include <speex/speex_resampler.h>
|
||||
#include "AudioMixer.h"
|
||||
#include "mozilla/dom/AudioChannelBinding.h"
|
||||
|
||||
|
|
|
@ -48,6 +48,46 @@ MediaTaskQueue::Dispatch(TemporaryRef<nsIRunnable> aRunnable)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
class MediaTaskQueueSyncRunnable : public nsRunnable {
|
||||
public:
|
||||
MediaTaskQueueSyncRunnable(TemporaryRef<nsIRunnable> aRunnable)
|
||||
: mRunnable(aRunnable)
|
||||
, mMonitor("MediaTaskQueueSyncRunnable")
|
||||
, mDone(false)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
nsresult rv = mRunnable->Run();
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mDone = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult WaitUntilDone() {
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
while (!mDone) {
|
||||
mon.Wait();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
RefPtr<nsIRunnable> mRunnable;
|
||||
Monitor mMonitor;
|
||||
bool mDone;
|
||||
};
|
||||
|
||||
nsresult
|
||||
MediaTaskQueue::SyncDispatch(TemporaryRef<nsIRunnable> aRunnable) {
|
||||
RefPtr<MediaTaskQueueSyncRunnable> task(new MediaTaskQueueSyncRunnable(aRunnable));
|
||||
nsresult rv = Dispatch(task);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return task->WaitUntilDone();
|
||||
}
|
||||
|
||||
void
|
||||
MediaTaskQueue::AwaitIdle()
|
||||
{
|
||||
|
|
|
@ -33,6 +33,8 @@ public:
|
|||
|
||||
nsresult Dispatch(TemporaryRef<nsIRunnable> aRunnable);
|
||||
|
||||
nsresult SyncDispatch(TemporaryRef<nsIRunnable> aRunnable);
|
||||
|
||||
// Removes all pending tasks from the task queue, and blocks until
|
||||
// the currently running task (if any) finishes.
|
||||
void Flush();
|
||||
|
|
|
@ -0,0 +1,284 @@
|
|||
/* -*- 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/CDMCallbackProxy.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/dom/MediaKeys.h"
|
||||
#include "mozilla/dom/MediaKeySession.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "nsContentCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "EMELog.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
CDMCallbackProxy::CDMCallbackProxy(CDMProxy* aProxy)
|
||||
: mProxy(aProxy)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class NewSessionTask : public nsRunnable {
|
||||
public:
|
||||
NewSessionTask(CDMProxy* aProxy,
|
||||
uint32_t aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
: mProxy(aProxy)
|
||||
, mPid(aPromiseId)
|
||||
, mSid(NS_ConvertUTF8toUTF16(aSessionId))
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mProxy->OnResolveNewSessionPromise(mPid, mSid);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
dom::PromiseId mPid;
|
||||
nsString mSid;
|
||||
};
|
||||
|
||||
void
|
||||
CDMCallbackProxy::ResolveNewSessionPromise(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
nsRefPtr<nsIRunnable> task(new NewSessionTask(mProxy,
|
||||
aPromiseId,
|
||||
aSessionId));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::ResolvePromise(uint32_t aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
// Note: CDMProxy proxies this from non-main threads to main thread.
|
||||
mProxy->ResolvePromise(aPromiseId);
|
||||
}
|
||||
|
||||
class RejectPromiseTask : public nsRunnable {
|
||||
public:
|
||||
RejectPromiseTask(CDMProxy* aProxy,
|
||||
uint32_t aPromiseId,
|
||||
nsresult aException,
|
||||
const nsCString& aMessage)
|
||||
: mProxy(aProxy)
|
||||
, mPid(aPromiseId)
|
||||
, mException(aException)
|
||||
, mMsg(NS_ConvertUTF8toUTF16(aMessage))
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mProxy->OnRejectPromise(mPid, mException, mMsg);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
dom::PromiseId mPid;
|
||||
nsresult mException;
|
||||
nsString mMsg;
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
CDMCallbackProxy::RejectPromise(uint32_t aPromiseId,
|
||||
nsresult aException,
|
||||
const nsCString& aMessage)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = new RejectPromiseTask(mProxy,
|
||||
aPromiseId,
|
||||
aException,
|
||||
aMessage);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
class SessionMessageTask : public nsRunnable {
|
||||
public:
|
||||
SessionMessageTask(CDMProxy* aProxy,
|
||||
const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aMessage,
|
||||
const nsCString& aDestinationURL)
|
||||
: mProxy(aProxy)
|
||||
, mSid(NS_ConvertUTF8toUTF16(aSessionId))
|
||||
, mURL(NS_ConvertUTF8toUTF16(aDestinationURL))
|
||||
{
|
||||
mMsg.AppendElements(aMessage);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mProxy->OnSessionMessage(mSid, mMsg, mURL);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
dom::PromiseId mPid;
|
||||
nsString mSid;
|
||||
nsTArray<uint8_t> mMsg;
|
||||
nsString mURL;
|
||||
};
|
||||
|
||||
void
|
||||
CDMCallbackProxy::SessionMessage(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aMessage,
|
||||
const nsCString& aDestinationURL)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = new SessionMessageTask(mProxy,
|
||||
aSessionId,
|
||||
aMessage,
|
||||
aDestinationURL);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
class ExpirationChangeTask : public nsRunnable {
|
||||
public:
|
||||
ExpirationChangeTask(CDMProxy* aProxy,
|
||||
const nsCString& aSessionId,
|
||||
GMPTimestamp aExpiryTime)
|
||||
: mProxy(aProxy)
|
||||
, mSid(NS_ConvertUTF8toUTF16(aSessionId))
|
||||
, mTimestamp(aExpiryTime)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mProxy->OnExpirationChange(mSid, mTimestamp);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
nsString mSid;
|
||||
GMPTimestamp mTimestamp;
|
||||
};
|
||||
|
||||
void
|
||||
CDMCallbackProxy::ExpirationChange(const nsCString& aSessionId,
|
||||
GMPTimestamp aExpiryTime)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = new ExpirationChangeTask(mProxy,
|
||||
aSessionId,
|
||||
aExpiryTime);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::SessionClosed(const nsCString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethodWithArg<nsString>(mProxy,
|
||||
&CDMProxy::OnSessionClosed,
|
||||
NS_ConvertUTF8toUTF16(aSessionId));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
class SessionErrorTask : public nsRunnable {
|
||||
public:
|
||||
SessionErrorTask(CDMProxy* aProxy,
|
||||
const nsCString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsCString& aMessage)
|
||||
: mProxy(aProxy)
|
||||
, mSid(NS_ConvertUTF8toUTF16(aSessionId))
|
||||
, mException(aException)
|
||||
, mSystemCode(aSystemCode)
|
||||
, mMsg(NS_ConvertUTF8toUTF16(aMessage))
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mProxy->OnSessionError(mSid, mException, mSystemCode, mMsg);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
dom::PromiseId mPid;
|
||||
nsString mSid;
|
||||
nsresult mException;
|
||||
uint32_t mSystemCode;
|
||||
nsString mMsg;
|
||||
};
|
||||
|
||||
void
|
||||
CDMCallbackProxy::SessionError(const nsCString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsCString& aMessage)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = new SessionErrorTask(mProxy,
|
||||
aSessionId,
|
||||
aException,
|
||||
aSystemCode,
|
||||
aMessage);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::KeyIdUsable(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
caps.SetKeyUsable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId));
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::KeyIdNotUsable(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
caps.SetKeyUnusable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId));
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::SetCaps(uint64_t aCaps)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
caps.SetCaps(aCaps);
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::Decrypted(uint32_t aId,
|
||||
GMPErr aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
mProxy->gmp_Decrypted(aId, aResult, aDecryptedData);
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::Terminated()
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
mProxy->gmp_Terminated();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,69 @@
|
|||
/* -*- 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 CDMCallbackProxy_h_
|
||||
#define CDMCallbackProxy_h_
|
||||
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "gmp-decryption.h"
|
||||
#include "GMPDecryptorProxy.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Proxies call backs from the CDM on the GMP thread back to the MediaKeys
|
||||
// object on the main thread.
|
||||
class CDMCallbackProxy : public GMPDecryptorProxyCallback {
|
||||
public:
|
||||
virtual void ResolveNewSessionPromise(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId) MOZ_OVERRIDE;
|
||||
|
||||
virtual void ResolvePromise(uint32_t aPromiseId) MOZ_OVERRIDE;
|
||||
|
||||
virtual void RejectPromise(uint32_t aPromiseId,
|
||||
nsresult aException,
|
||||
const nsCString& aSessionId) MOZ_OVERRIDE;
|
||||
|
||||
virtual void SessionMessage(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aMessage,
|
||||
const nsCString& aDestinationURL) MOZ_OVERRIDE;
|
||||
|
||||
virtual void ExpirationChange(const nsCString& aSessionId,
|
||||
GMPTimestamp aExpiryTime) MOZ_OVERRIDE;
|
||||
|
||||
virtual void SessionClosed(const nsCString& aSessionId) MOZ_OVERRIDE;
|
||||
|
||||
virtual void SessionError(const nsCString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsCString& aMessage) MOZ_OVERRIDE;
|
||||
|
||||
virtual void KeyIdUsable(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId) MOZ_OVERRIDE;
|
||||
|
||||
virtual void KeyIdNotUsable(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId) MOZ_OVERRIDE;
|
||||
|
||||
virtual void SetCaps(uint64_t aCaps) MOZ_OVERRIDE;
|
||||
|
||||
virtual void Decrypted(uint32_t aId,
|
||||
GMPErr aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData) MOZ_OVERRIDE;
|
||||
|
||||
virtual void Terminated() MOZ_OVERRIDE;
|
||||
|
||||
~CDMCallbackProxy() {}
|
||||
|
||||
private:
|
||||
friend class CDMProxy;
|
||||
explicit CDMCallbackProxy(CDMProxy* aProxy);
|
||||
|
||||
// Warning: Weak ref.
|
||||
CDMProxy* mProxy;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // CDMCallbackProxy_h_
|
|
@ -0,0 +1,187 @@
|
|||
/* -*- 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 "CDMCaps.h"
|
||||
#include "gmp-decryption.h"
|
||||
#include "EMELog.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
CDMCaps::CDMCaps()
|
||||
: mMonitor("CDMCaps")
|
||||
, mCaps(0)
|
||||
{
|
||||
}
|
||||
|
||||
CDMCaps::~CDMCaps()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::Lock()
|
||||
{
|
||||
mMonitor.Lock();
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::Unlock()
|
||||
{
|
||||
mMonitor.Unlock();
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::HasCap(uint64_t aCap)
|
||||
{
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
return (mCaps & aCap) == aCap;
|
||||
}
|
||||
|
||||
CDMCaps::AutoLock::AutoLock(CDMCaps& aInstance)
|
||||
: mData(aInstance)
|
||||
{
|
||||
mData.Lock();
|
||||
}
|
||||
|
||||
CDMCaps::AutoLock::~AutoLock()
|
||||
{
|
||||
mData.Unlock();
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::SetCaps(uint64_t aCaps)
|
||||
{
|
||||
EME_LOG("SetCaps()");
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
mData.mCaps = aCaps;
|
||||
for (size_t i = 0; i < mData.mWaitForCaps.Length(); i++) {
|
||||
NS_DispatchToMainThread(mData.mWaitForCaps[i], NS_DISPATCH_NORMAL);
|
||||
}
|
||||
mData.mWaitForCaps.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::CallOnMainThreadWhenCapsAvailable(nsIRunnable* aContinuation)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
if (mData.mCaps) {
|
||||
NS_DispatchToMainThread(aContinuation, NS_DISPATCH_NORMAL);
|
||||
MOZ_ASSERT(mData.mWaitForCaps.IsEmpty());
|
||||
} else {
|
||||
mData.mWaitForCaps.AppendElement(aContinuation);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::IsKeyUsable(const CencKeyId& aKeyId)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
const auto& keys = mData.mUsableKeyIds;
|
||||
for (size_t i = 0; i < keys.Length(); i++) {
|
||||
if (keys[i].mId == aKeyId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::SetKeyUsable(const CencKeyId& aKeyId,
|
||||
const nsString& aSessionId)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
mData.mUsableKeyIds.AppendElement(UsableKey(aKeyId, aSessionId));
|
||||
auto& waiters = mData.mWaitForKeys;
|
||||
size_t i = 0;
|
||||
while (i < waiters.Length()) {
|
||||
auto& w = waiters[i];
|
||||
if (w.mKeyId == aKeyId) {
|
||||
if (waiters[i].mTarget) {
|
||||
EME_LOG("SetKeyUsable() notified waiter.");
|
||||
w.mTarget->Dispatch(w.mContinuation, NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
w.mContinuation->Run();
|
||||
}
|
||||
waiters.RemoveElementAt(i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::SetKeyUnusable(const CencKeyId& aKeyId,
|
||||
const nsString& aSessionId)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
auto& keys = mData.mUsableKeyIds;
|
||||
for (size_t i = 0; i < keys.Length(); i++) {
|
||||
if (keys[i].mId == aKeyId &&
|
||||
keys[i].mSessionId == aSessionId) {
|
||||
keys.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::CallWhenKeyUsable(const CencKeyId& aKey,
|
||||
nsIRunnable* aContinuation,
|
||||
nsIThread* aTarget)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
MOZ_ASSERT(!IsKeyUsable(aKey));
|
||||
MOZ_ASSERT(aContinuation);
|
||||
mData.mWaitForKeys.AppendElement(WaitForKeys(aKey, aContinuation, aTarget));
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::DropKeysForSession(const nsAString& aSessionId)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
auto& keys = mData.mUsableKeyIds;
|
||||
size_t i = 0;
|
||||
while (i < keys.Length()) {
|
||||
if (keys[i].mSessionId == aSessionId) {
|
||||
keys.RemoveElementAt(i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::AreCapsKnown()
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
return mData.mCaps != 0;
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::CanDecryptAndDecodeAudio()
|
||||
{
|
||||
return mData.HasCap(GMP_EME_CAP_DECRYPT_AND_DECODE_AUDIO);
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::CanDecryptAndDecodeVideo()
|
||||
{
|
||||
return mData.HasCap(GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO);
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::CanDecryptAudio()
|
||||
{
|
||||
return mData.HasCap(GMP_EME_CAP_DECRYPT_AUDIO);
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::CanDecryptVideo()
|
||||
{
|
||||
return mData.HasCap(GMP_EME_CAP_DECRYPT_VIDEO);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,117 @@
|
|||
/* -*- 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 CDMCaps_h_
|
||||
#define CDMCaps_h_
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
typedef nsTArray<uint8_t> CencKeyId;
|
||||
|
||||
// CDM capabilities; what keys a CDMProxy can use, and whether it can decrypt, or
|
||||
// decrypt-and-decode on a per stream basis. Must be locked to access state.
|
||||
class CDMCaps {
|
||||
public:
|
||||
CDMCaps();
|
||||
~CDMCaps();
|
||||
|
||||
// Locks the CDMCaps. It must be locked to access its shared state.
|
||||
// Threadsafe when locked.
|
||||
class MOZ_STACK_CLASS AutoLock {
|
||||
public:
|
||||
explicit AutoLock(CDMCaps& aKeyCaps);
|
||||
~AutoLock();
|
||||
|
||||
// Returns true if the capabilities of the CDM are known, i.e. they have
|
||||
// been reported by the CDM to Gecko.
|
||||
bool AreCapsKnown();
|
||||
|
||||
bool IsKeyUsable(const CencKeyId& aKeyId);
|
||||
|
||||
void SetKeyUsable(const CencKeyId& aKeyId, const nsString& aSessionId);
|
||||
void SetKeyUnusable(const CencKeyId& aKeyId, const nsString& aSessionId);
|
||||
|
||||
void DropKeysForSession(const nsAString& aSessionId);
|
||||
|
||||
// Sets the capabilities of the CDM. aCaps is the logical OR of the
|
||||
// GMP_EME_CAP_* flags from gmp-decryption.h.
|
||||
void SetCaps(uint64_t aCaps);
|
||||
|
||||
bool CanDecryptAndDecodeAudio();
|
||||
bool CanDecryptAndDecodeVideo();
|
||||
|
||||
bool CanDecryptAudio();
|
||||
bool CanDecryptVideo();
|
||||
|
||||
void CallOnMainThreadWhenCapsAvailable(nsIRunnable* aContinuation);
|
||||
|
||||
// Calls aContinuation on aTarget thread when key become usable.
|
||||
// Pass aTarget=nullptr and runnable will be called on the GMP thread
|
||||
// when key becomes usable.
|
||||
void CallWhenKeyUsable(const CencKeyId& aKey,
|
||||
nsIRunnable* aContinuation,
|
||||
nsIThread* aTarget = nullptr);
|
||||
|
||||
private:
|
||||
// Not taking a strong ref, since this should be allocated on the stack.
|
||||
CDMCaps& mData;
|
||||
};
|
||||
|
||||
private:
|
||||
void Lock();
|
||||
void Unlock();
|
||||
bool HasCap(uint64_t);
|
||||
|
||||
struct WaitForKeys {
|
||||
WaitForKeys(const CencKeyId& aKeyId,
|
||||
nsIRunnable* aContinuation,
|
||||
nsIThread* aTarget)
|
||||
: mKeyId(aKeyId)
|
||||
, mContinuation(aContinuation)
|
||||
, mTarget(aTarget)
|
||||
{}
|
||||
CencKeyId mKeyId;
|
||||
nsRefPtr<nsIRunnable> mContinuation;
|
||||
nsCOMPtr<nsIThread> mTarget;
|
||||
};
|
||||
|
||||
Monitor mMonitor;
|
||||
|
||||
struct UsableKey {
|
||||
UsableKey(const CencKeyId& aId,
|
||||
const nsString& aSessionId)
|
||||
: mId(aId)
|
||||
, mSessionId(aSessionId)
|
||||
{}
|
||||
UsableKey(const UsableKey& aOther)
|
||||
: mId(aOther.mId)
|
||||
, mSessionId(aOther.mSessionId)
|
||||
{}
|
||||
CencKeyId mId;
|
||||
nsString mSessionId;
|
||||
};
|
||||
nsTArray<UsableKey> mUsableKeyIds;
|
||||
|
||||
nsTArray<WaitForKeys> mWaitForKeys;
|
||||
|
||||
nsTArray<nsRefPtr<nsIRunnable>> mWaitForCaps;
|
||||
uint64_t mCaps;
|
||||
|
||||
// It is not safe to copy this object.
|
||||
CDMCaps(const CDMCaps&) MOZ_DELETE;
|
||||
CDMCaps& operator=(const CDMCaps&) MOZ_DELETE;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -12,16 +12,19 @@
|
|||
#include "nsContentCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "MainThreadUtils.h"
|
||||
|
||||
// TODO: Change the functions in this file to do IPC via the Gecko Media
|
||||
// Plugins API. In the meantime, the code here merely implements the
|
||||
// interface we expect will be required when the IPC is working.
|
||||
#include "mozilla/EMELog.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "prenv.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/CDMCallbackProxy.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
|
||||
: mKeys(aKeys)
|
||||
, mKeySystem(aKeySystem)
|
||||
, mCDM(nullptr)
|
||||
, mDecryptionJobCount(0)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_COUNT_CTOR(CDMProxy);
|
||||
|
@ -36,6 +39,15 @@ void
|
|||
CDMProxy::Init(PromiseId aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult rv = mKeys->GetOrigin(mOrigin);
|
||||
if (NS_FAILED(rv)) {
|
||||
RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
EME_LOG("Creating CDMProxy for origin='%s'",
|
||||
NS_ConvertUTF16toUTF8(GetOrigin()).get());
|
||||
|
||||
if (!mGMPThread) {
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mps =
|
||||
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
||||
|
@ -50,12 +62,53 @@ CDMProxy::Init(PromiseId aPromiseId)
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Dispatch task to GMPThread to initialize CDM via IPC.
|
||||
|
||||
mKeys->OnCDMCreated(aPromiseId);
|
||||
nsRefPtr<nsIRunnable> task(NS_NewRunnableMethodWithArg<uint32_t>(this, &CDMProxy::gmp_Init, aPromiseId));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
static int sFakeSessionIdNum = 0;
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
CDMProxy::IsOnGMPThread()
|
||||
{
|
||||
return NS_GetCurrentThread() == mGMPThread;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
CDMProxy::gmp_Init(uint32_t aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mps =
|
||||
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
||||
if (!mps) {
|
||||
RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<nsCString> tags;
|
||||
tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
|
||||
nsresult rv = mps->GetGMPDecryptor(&tags, GetOrigin(), &mCDM);
|
||||
if (NS_FAILED(rv) || !mCDM) {
|
||||
RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
} else {
|
||||
mCallback = new CDMCallbackProxy(this);
|
||||
mCDM->Init(mCallback);
|
||||
nsRefPtr<nsIRunnable> task(NS_NewRunnableMethodWithArg<uint32_t>(this, &CDMProxy::OnCDMCreated, aPromiseId));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnCDMCreated(uint32_t aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mKeys.IsNull()) {
|
||||
mKeys->OnCDMCreated(aPromiseId);
|
||||
} else {
|
||||
NS_WARNING("CDMProxy unable to reject promise!");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::CreateSession(dom::SessionType aSessionType,
|
||||
|
@ -66,14 +119,38 @@ CDMProxy::CreateSession(dom::SessionType aSessionType,
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mGMPThread);
|
||||
|
||||
// TODO: Dispatch task to GMPThread to call CDM CreateSession via IPC.
|
||||
nsAutoPtr<CreateSessionData> data(new CreateSessionData());
|
||||
data->mSessionType = aSessionType;
|
||||
data->mPromiseId = aPromiseId;
|
||||
data->mInitDataType = NS_ConvertUTF16toUTF8(aInitDataType);
|
||||
data->mInitData.AppendElements(aInitData.Data(), aInitData.Length());
|
||||
|
||||
// Make a fake session id. We'll get this from the CDM normally.
|
||||
nsAutoString id;
|
||||
id.AppendASCII("FakeSessionId_");
|
||||
id.AppendInt(sFakeSessionIdNum++);
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<CreateSessionData>>(this, &CDMProxy::gmp_CreateSession, data));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
mKeys->OnSessionActivated(aPromiseId, id);
|
||||
GMPSessionType
|
||||
ToGMPSessionType(dom::SessionType aSessionType) {
|
||||
switch (aSessionType) {
|
||||
case dom::SessionType::Temporary: return kGMPTemporySession;
|
||||
case dom::SessionType::Persistent: return kGMPPersistentSession;
|
||||
default: return kGMPTemporySession;
|
||||
};
|
||||
};
|
||||
|
||||
void
|
||||
CDMProxy::gmp_CreateSession(nsAutoPtr<CreateSessionData> aData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mCDM) {
|
||||
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mCDM->CreateSession(aData->mPromiseId,
|
||||
aData->mInitDataType,
|
||||
aData->mInitData,
|
||||
ToGMPSessionType(aData->mSessionType));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -83,25 +160,51 @@ CDMProxy::LoadSession(PromiseId aPromiseId,
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mGMPThread);
|
||||
|
||||
// TODO: Dispatch task to GMPThread to call CDM LoadSession via IPC.
|
||||
// make MediaKeys::mPendingSessions CC'd
|
||||
nsAutoPtr<SessionOpData> data(new SessionOpData());
|
||||
data->mPromiseId = aPromiseId;
|
||||
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_LoadSession, data));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
mKeys->OnSessionActivated(aPromiseId, aSessionId);
|
||||
void
|
||||
CDMProxy::gmp_LoadSession(nsAutoPtr<SessionOpData> aData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
if (!mCDM) {
|
||||
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mCDM->LoadSession(aData->mPromiseId, aData->mSessionId);
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::SetServerCertificate(PromiseId aPromiseId,
|
||||
const Uint8Array& aCertData)
|
||||
const Uint8Array& aCert)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mGMPThread);
|
||||
|
||||
// TODO: Dispatch task to GMPThread to call CDM SetServerCertificate via IPC.
|
||||
|
||||
ResolvePromise(aPromiseId);
|
||||
nsAutoPtr<SetServerCertificateData> data;
|
||||
data->mPromiseId = aPromiseId;
|
||||
data->mCert.AppendElements(aCert.Data(), aCert.Length());
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<SetServerCertificateData>>(this, &CDMProxy::gmp_SetServerCertificate, data));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
static int sUpdateCount = 0;
|
||||
void
|
||||
CDMProxy::gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mCDM) {
|
||||
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mCDM->SetServerCertificate(aData->mPromiseId, aData->mCert);
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::UpdateSession(const nsAString& aSessionId,
|
||||
|
@ -112,15 +215,26 @@ CDMProxy::UpdateSession(const nsAString& aSessionId,
|
|||
MOZ_ASSERT(mGMPThread);
|
||||
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
|
||||
|
||||
// TODO: Dispatch task to GMPThread to call CDM UpdateSession via IPC.
|
||||
nsAutoPtr<UpdateSessionData> data(new UpdateSessionData());
|
||||
data->mPromiseId = aPromiseId;
|
||||
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
|
||||
data->mResponse.AppendElements(aResponse.Data(), aResponse.Length());
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<UpdateSessionData>>(this, &CDMProxy::gmp_UpdateSession, data));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
nsAutoCString str(NS_LITERAL_CSTRING("Update_"));
|
||||
str.AppendInt(sUpdateCount++);
|
||||
nsTArray<uint8_t> msg;
|
||||
msg.AppendElements(str.get(), str.Length());
|
||||
session->DispatchKeyMessage(msg, NS_LITERAL_STRING("http://bogus.url"));
|
||||
ResolvePromise(aPromiseId);
|
||||
void
|
||||
CDMProxy::gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mCDM) {
|
||||
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mCDM->UpdateSession(aData->mPromiseId,
|
||||
aData->mSessionId,
|
||||
aData->mResponse);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -130,17 +244,27 @@ CDMProxy::CloseSession(const nsAString& aSessionId,
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
|
||||
|
||||
// TODO: Dispatch task to GMPThread to call CDM CloseSession via IPC.
|
||||
{
|
||||
CDMCaps::AutoLock caps(Capabilites());
|
||||
caps.DropKeysForSession(aSessionId);
|
||||
}
|
||||
nsAutoPtr<SessionOpData> data(new SessionOpData());
|
||||
data->mPromiseId = aPromiseId;
|
||||
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_CloseSession, data));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
|
||||
// Pretend that the CDM actually does close the session...
|
||||
// "...the [MediaKeySession's] closed attribute promise is resolved
|
||||
// when the session is closed."
|
||||
session->OnClosed();
|
||||
|
||||
// "The promise is resolved when the request has been processed."
|
||||
ResolvePromise(aPromiseId);
|
||||
void
|
||||
CDMProxy::gmp_CloseSession(nsAutoPtr<SessionOpData> aData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mCDM) {
|
||||
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mCDM->CloseSession(aData->mPromiseId, aData->mSessionId);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -148,12 +272,29 @@ CDMProxy::RemoveSession(const nsAString& aSessionId,
|
|||
PromiseId aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
|
||||
|
||||
// TODO: Dispatch task to GMPThread to call CDM RemoveSession via IPC.
|
||||
{
|
||||
CDMCaps::AutoLock caps(Capabilites());
|
||||
caps.DropKeysForSession(aSessionId);
|
||||
}
|
||||
nsAutoPtr<SessionOpData> data(new SessionOpData());
|
||||
data->mPromiseId = aPromiseId;
|
||||
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_RemoveSession, data));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
// Assume CDM immediately removes session's data, then close the session
|
||||
// as per the spec.
|
||||
CloseSession(aSessionId, aPromiseId);
|
||||
void
|
||||
CDMProxy::gmp_RemoveSession(nsAutoPtr<SessionOpData> aData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mCDM) {
|
||||
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mCDM->RemoveSession(aData->mPromiseId, aData->mSessionId);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -196,4 +337,158 @@ CDMProxy::ResolvePromise(PromiseId aId)
|
|||
}
|
||||
}
|
||||
|
||||
const nsAString&
|
||||
CDMProxy::GetOrigin() const
|
||||
{
|
||||
return mOrigin;
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnResolveNewSessionPromise(uint32_t aPromiseId,
|
||||
const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mKeys->OnSessionCreated(aPromiseId, aSessionId);
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnSessionMessage(const nsAString& aSessionId,
|
||||
nsTArray<uint8_t>& aMessage,
|
||||
const nsAString& aDestinationURL)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
if (session) {
|
||||
session->DispatchKeyMessage(aMessage, aDestinationURL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnExpirationChange(const nsAString& aSessionId,
|
||||
GMPTimestamp aExpiryTime)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_WARNING("CDMProxy::OnExpirationChange() not implemented");
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnSessionClosed(const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_WARNING("CDMProxy::OnSessionClosed() not implemented");
|
||||
}
|
||||
|
||||
static void
|
||||
LogToConsole(const nsAString& aMsg)
|
||||
{
|
||||
nsCOMPtr<nsIConsoleService> console(
|
||||
do_GetService("@mozilla.org/consoleservice;1"));
|
||||
if (!console) {
|
||||
NS_WARNING("Failed to log message to console.");
|
||||
return;
|
||||
}
|
||||
nsAutoString msg(aMsg);
|
||||
console->LogStringMessage(msg.get());
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnSessionError(const nsAString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsAString& aMsg)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
if (session) {
|
||||
session->DispatchKeyError(aSystemCode);
|
||||
}
|
||||
LogToConsole(aMsg);
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnRejectPromise(uint32_t aPromiseId,
|
||||
nsresult aDOMException,
|
||||
const nsAString& aMsg)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RejectPromise(aPromiseId, aDOMException);
|
||||
LogToConsole(aMsg);
|
||||
}
|
||||
|
||||
const nsString&
|
||||
CDMProxy::KeySystem() const
|
||||
{
|
||||
return mKeySystem;
|
||||
}
|
||||
|
||||
CDMCaps&
|
||||
CDMProxy::Capabilites() {
|
||||
return mCapabilites;
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::Decrypt(mp4_demuxer::MP4Sample* aSample,
|
||||
DecryptionClient* aClient)
|
||||
{
|
||||
nsAutoPtr<DecryptJob> job(new DecryptJob(aSample, aClient));
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<DecryptJob>>(this, &CDMProxy::gmp_Decrypt, job));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::gmp_Decrypt(nsAutoPtr<DecryptJob> aJob)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
MOZ_ASSERT(aJob->mClient);
|
||||
MOZ_ASSERT(aJob->mSample);
|
||||
|
||||
if (!mCDM) {
|
||||
aJob->mClient->Decrypted(NS_ERROR_FAILURE, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
aJob->mId = ++mDecryptionJobCount;
|
||||
nsTArray<uint8_t> data;
|
||||
data.AppendElements(aJob->mSample->data, aJob->mSample->size);
|
||||
mCDM->Decrypt(aJob->mId, aJob->mSample->crypto, data);
|
||||
mDecryptionJobs.AppendElement(aJob.forget());
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::gmp_Decrypted(uint32_t aId,
|
||||
GMPErr aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
|
||||
DecryptJob* job = mDecryptionJobs[i];
|
||||
if (job->mId == aId) {
|
||||
if (aDecryptedData.Length() != job->mSample->size) {
|
||||
NS_WARNING("CDM returned incorrect number of decrypted bytes");
|
||||
}
|
||||
PodCopy(job->mSample->data,
|
||||
aDecryptedData.Elements(),
|
||||
std::min<size_t>(aDecryptedData.Length(), job->mSample->size));
|
||||
nsresult rv = GMP_SUCCEEDED(aResult) ? NS_OK : NS_ERROR_FAILURE;
|
||||
job->mClient->Decrypted(rv, job->mSample.forget());
|
||||
mDecryptionJobs.RemoveElementAt(i);
|
||||
return;
|
||||
} else {
|
||||
NS_WARNING("GMPDecryptorChild returned incorrect job ID");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::gmp_Terminated()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
EME_LOG("CDM terminated");
|
||||
if (mCDM) {
|
||||
mCDM->Close();
|
||||
mCDM = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -9,22 +9,30 @@
|
|||
|
||||
#include "nsString.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "mozilla/dom/MediaKeys.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
|
||||
class nsIThread;
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "nsIThread.h"
|
||||
#include "GMPDecryptorProxy.h"
|
||||
#include "mozilla/CDMCaps.h"
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class CDMCallbackProxy;
|
||||
|
||||
namespace dom {
|
||||
class MediaKeySession;
|
||||
}
|
||||
|
||||
// A placeholder proxy to the CDM.
|
||||
// TODO: The functions here need to do IPC to talk to the CDM via the
|
||||
// Gecko Media Plugin API, which we'll need to extend for H.264 and EME
|
||||
// content.
|
||||
class DecryptionClient {
|
||||
public:
|
||||
virtual ~DecryptionClient() {}
|
||||
virtual void Decrypted(nsresult aResult,
|
||||
mp4_demuxer::MP4Sample* aSample) = 0;
|
||||
};
|
||||
|
||||
// Proxies calls GMP/CDM, and proxies calls back.
|
||||
// Note: Promises are passed in via a PromiseId, so that the ID can be
|
||||
// passed via IPC to the CDM, which can then signal when to reject or
|
||||
// resolve the promise using its PromiseId.
|
||||
|
@ -95,8 +103,124 @@ public:
|
|||
// Main thread only.
|
||||
void Shutdown();
|
||||
|
||||
// Threadsafe.
|
||||
const nsAString& GetOrigin() const;
|
||||
|
||||
// Main thread only.
|
||||
void OnResolveNewSessionPromise(uint32_t aPromiseId,
|
||||
const nsAString& aSessionId);
|
||||
|
||||
// Main thread only.
|
||||
void OnSessionMessage(const nsAString& aSessionId,
|
||||
nsTArray<uint8_t>& aMessage,
|
||||
const nsAString& aDestinationURL);
|
||||
|
||||
// Main thread only.
|
||||
void OnExpirationChange(const nsAString& aSessionId,
|
||||
GMPTimestamp aExpiryTime);
|
||||
|
||||
// Main thread only.
|
||||
void OnSessionClosed(const nsAString& aSessionId);
|
||||
|
||||
// Main thread only.
|
||||
void OnSessionError(const nsAString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsAString& aMsg);
|
||||
|
||||
// Main thread only.
|
||||
void OnRejectPromise(uint32_t aPromiseId,
|
||||
nsresult aDOMException,
|
||||
const nsAString& aMsg);
|
||||
|
||||
// Threadsafe.
|
||||
void Decrypt(mp4_demuxer::MP4Sample* aSample,
|
||||
DecryptionClient* aSink);
|
||||
|
||||
// Reject promise with DOMException corresponding to aExceptionCode.
|
||||
// Can be called from any thread.
|
||||
void RejectPromise(PromiseId aId, nsresult aExceptionCode);
|
||||
|
||||
// Resolves promise with "undefined".
|
||||
// Can be called from any thread.
|
||||
void ResolvePromise(PromiseId aId);
|
||||
|
||||
// Threadsafe.
|
||||
const nsString& KeySystem() const;
|
||||
|
||||
// GMP thread only.
|
||||
void gmp_Decrypted(uint32_t aId,
|
||||
GMPErr aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData);
|
||||
|
||||
// GMP thread only.
|
||||
void gmp_Terminated();
|
||||
|
||||
CDMCaps& Capabilites();
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsOnGMPThread();
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
// GMP thread only.
|
||||
void gmp_Init(uint32_t aPromiseId);
|
||||
|
||||
// Main thread only.
|
||||
void OnCDMCreated(uint32_t aPromiseId);
|
||||
|
||||
struct CreateSessionData {
|
||||
dom::SessionType mSessionType;
|
||||
PromiseId mPromiseId;
|
||||
nsAutoCString mInitDataType;
|
||||
nsTArray<uint8_t> mInitData;
|
||||
};
|
||||
// GMP thread only.
|
||||
void gmp_CreateSession(nsAutoPtr<CreateSessionData> aData);
|
||||
|
||||
struct SessionOpData {
|
||||
PromiseId mPromiseId;
|
||||
nsAutoCString mSessionId;
|
||||
};
|
||||
// GMP thread only.
|
||||
void gmp_LoadSession(nsAutoPtr<SessionOpData> aData);
|
||||
|
||||
struct SetServerCertificateData {
|
||||
PromiseId mPromiseId;
|
||||
nsTArray<uint8_t> mCert;
|
||||
};
|
||||
// GMP thread only.
|
||||
void gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData);
|
||||
|
||||
struct UpdateSessionData {
|
||||
PromiseId mPromiseId;
|
||||
nsAutoCString mSessionId;
|
||||
nsTArray<uint8_t> mResponse;
|
||||
};
|
||||
// GMP thread only.
|
||||
void gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData);
|
||||
|
||||
// GMP thread only.
|
||||
void gmp_CloseSession(nsAutoPtr<SessionOpData> aData);
|
||||
|
||||
// GMP thread only.
|
||||
void gmp_RemoveSession(nsAutoPtr<SessionOpData> aData);
|
||||
|
||||
struct DecryptJob {
|
||||
DecryptJob(mp4_demuxer::MP4Sample* aSample,
|
||||
DecryptionClient* aClient)
|
||||
: mId(0)
|
||||
, mSample(aSample)
|
||||
, mClient(aClient)
|
||||
{}
|
||||
uint32_t mId;
|
||||
nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
|
||||
nsAutoPtr<DecryptionClient> mClient;
|
||||
};
|
||||
// GMP thread only.
|
||||
void gmp_Decrypt(nsAutoPtr<DecryptJob> aJob);
|
||||
|
||||
class RejectPromiseTask : public nsRunnable {
|
||||
public:
|
||||
RejectPromiseTask(CDMProxy* aProxy,
|
||||
|
@ -117,13 +241,6 @@ private:
|
|||
nsresult mCode;
|
||||
};
|
||||
|
||||
// Reject promise with DOMException corresponding to aExceptionCode.
|
||||
// Can be called from any thread.
|
||||
void RejectPromise(PromiseId aId, nsresult aExceptionCode);
|
||||
// Resolves promise with "undefined".
|
||||
// Can be called from any thread.
|
||||
void ResolvePromise(PromiseId aId);
|
||||
|
||||
~CDMProxy();
|
||||
|
||||
// Helper to enforce that a raw pointer is only accessed on the main thread.
|
||||
|
@ -164,8 +281,26 @@ private:
|
|||
// Gecko Media Plugin thread. All interactions with the out-of-process
|
||||
// EME plugin must come from this thread.
|
||||
nsRefPtr<nsIThread> mGMPThread;
|
||||
|
||||
nsAutoString mOrigin;
|
||||
|
||||
GMPDecryptorProxy* mCDM;
|
||||
CDMCaps mCapabilites;
|
||||
nsAutoPtr<CDMCallbackProxy> mCallback;
|
||||
|
||||
// Decryption jobs sent to CDM, awaiting result.
|
||||
// GMP thread only.
|
||||
nsTArray<nsAutoPtr<DecryptJob>> mDecryptionJobs;
|
||||
|
||||
// Number of buffers we've decrypted. Used to uniquely identify
|
||||
// decryption jobs sent to CDM. Note we can't just use the length of
|
||||
// mDecryptionJobs as that shrinks as jobs are completed and removed
|
||||
// from it.
|
||||
// GMP thread only.
|
||||
uint32_t mDecryptionJobCount;
|
||||
};
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // CDMProxy_h_
|
||||
|
|
|
@ -4,32 +4,45 @@
|
|||
* 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 EME_LOG_H_
|
||||
#define EME_LOG_H_
|
||||
|
||||
#include "prlog.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
|
||||
#ifndef EME_LOG
|
||||
PRLogModuleInfo* GetEMELog();
|
||||
#define EME_LOG(...) PR_LOG(GetEMELog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#endif
|
||||
#ifndef EME_LOG
|
||||
PRLogModuleInfo* GetEMELog();
|
||||
#define EME_LOG(...) PR_LOG(GetEMELog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef EME_VERBOSE_LOG
|
||||
PRLogModuleInfo* GetEMEVerboseLog();
|
||||
#define EME_VERBOSE_LOG(...) PR_LOG(GetEMEVerboseLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#ifndef EME_VERBOSE_LOG
|
||||
PRLogModuleInfo* GetEMEVerboseLog();
|
||||
#define EME_VERBOSE_LOG(...) PR_LOG(GetEMEVerboseLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#ifndef EME_LOG
|
||||
#define EME_LOG(...)
|
||||
#endif
|
||||
|
||||
#ifndef EME_VERBOSE_LOG
|
||||
#define EME_VERBOSE_LOG(...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef EME_LOG
|
||||
#define EME_LOG(...)
|
||||
#endif
|
||||
#ifndef EME_LOG
|
||||
#define EME_LOG(...)
|
||||
#endif
|
||||
|
||||
#ifndef EME_VERBOSE_LOG
|
||||
#define EME_VERBOSE_LOG(...)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#ifndef EME_VERBOSE_LOG
|
||||
#define EME_VERBOSE_LOG(...)
|
||||
#endif
|
||||
|
||||
#endif // PR_LOGGING
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // EME_LOG_H_
|
||||
|
|
|
@ -16,6 +16,7 @@ MediaKeyError::MediaKeyError(EventTarget* aOwner, uint32_t aSystemCode)
|
|||
, mSystemCode(aSystemCode)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
InitEvent(NS_LITERAL_STRING("error"), false, false);
|
||||
}
|
||||
|
||||
MediaKeyError::~MediaKeyError()
|
||||
|
|
|
@ -67,7 +67,13 @@ MediaKeySession::GetKeySystem(nsString& aKeySystem) const
|
|||
void
|
||||
MediaKeySession::GetSessionId(nsString& aSessionId) const
|
||||
{
|
||||
aSessionId = mSessionId;
|
||||
aSessionId = GetSessionId();
|
||||
}
|
||||
|
||||
const nsString&
|
||||
MediaKeySession::GetSessionId() const
|
||||
{
|
||||
return mSessionId;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
|
@ -166,7 +172,7 @@ MediaKeySession::Remove(ErrorResult& aRv)
|
|||
|
||||
void
|
||||
MediaKeySession::DispatchKeyMessage(const nsTArray<uint8_t>& aMessage,
|
||||
const nsString& aURL)
|
||||
const nsAString& aURL)
|
||||
{
|
||||
nsRefPtr<MediaKeyMessageEvent> event(
|
||||
MediaKeyMessageEvent::Constructor(this, aURL, aMessage));
|
||||
|
|
|
@ -53,6 +53,8 @@ public:
|
|||
|
||||
void GetSessionId(nsString& aRetval) const;
|
||||
|
||||
const nsString& GetSessionId() const;
|
||||
|
||||
// Number of ms since epoch at which expiration occurs, or NaN if unknown.
|
||||
// TODO: The type of this attribute is still under contention.
|
||||
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=25902
|
||||
|
@ -68,7 +70,7 @@ public:
|
|||
already_AddRefed<Promise> Remove(ErrorResult& aRv);
|
||||
|
||||
void DispatchKeyMessage(const nsTArray<uint8_t>& aMessage,
|
||||
const nsString& aURL);
|
||||
const nsAString& aURL);
|
||||
|
||||
void DispatchKeyError(uint32_t system_code);
|
||||
|
||||
|
|
|
@ -12,8 +12,13 @@
|
|||
#include "mozilla/dom/MediaKeySession.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "mozilla/EMELog.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "EMELog.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#ifdef XP_WIN
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -32,8 +37,9 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeys)
|
|||
NS_INTERFACE_MAP_END
|
||||
|
||||
MediaKeys::MediaKeys(nsPIDOMWindow* aParent, const nsAString& aKeySystem)
|
||||
: mParent(aParent),
|
||||
mKeySystem(aKeySystem)
|
||||
: mParent(aParent)
|
||||
, mKeySystem(aKeySystem)
|
||||
, mCreatePromiseId(0)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
@ -76,6 +82,18 @@ MediaKeys::SetServerCertificate(const Uint8Array& aCert, ErrorResult& aRv)
|
|||
return promise.forget();
|
||||
}
|
||||
|
||||
static bool
|
||||
IsSupportedKeySystem(const nsAString& aKeySystem)
|
||||
{
|
||||
return aKeySystem.EqualsASCII("org.w3.clearkey") ||
|
||||
#ifdef XP_WIN
|
||||
(aKeySystem.EqualsASCII("com.adobe.access") &&
|
||||
IsVistaOrLater() &&
|
||||
Preferences::GetBool("media.eme.adobe-access.enabled", false)) ||
|
||||
#endif
|
||||
false;
|
||||
}
|
||||
|
||||
/* static */
|
||||
IsTypeSupportedResult
|
||||
MediaKeys::IsTypeSupported(const GlobalObject& aGlobal,
|
||||
|
@ -84,10 +102,10 @@ MediaKeys::IsTypeSupported(const GlobalObject& aGlobal,
|
|||
const Optional<nsAString>& aContentType,
|
||||
const Optional<nsAString>& aCapability)
|
||||
{
|
||||
// TODO: Query list of known CDMs and their supported content types.
|
||||
// TODO: Should really get spec changed to this is async, so we can wait
|
||||
// for user to consent to running plugin.
|
||||
return IsTypeSupportedResult::Maybe;
|
||||
return IsSupportedKeySystem(aKeySystem) ? IsTypeSupportedResult::Maybe
|
||||
: IsTypeSupportedResult::_empty;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
|
@ -115,6 +133,7 @@ MediaKeys::StorePromise(Promise* aPromise)
|
|||
already_AddRefed<Promise>
|
||||
MediaKeys::RetrievePromise(PromiseId aId)
|
||||
{
|
||||
MOZ_ASSERT(mPromises.Contains(aId));
|
||||
nsRefPtr<Promise> promise;
|
||||
mPromises.Remove(aId, getter_AddRefs(promise));
|
||||
return promise.forget();
|
||||
|
@ -138,6 +157,11 @@ MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode)
|
|||
|
||||
MOZ_ASSERT(NS_FAILED(aExceptionCode));
|
||||
promise->MaybeReject(aExceptionCode);
|
||||
|
||||
if (mCreatePromiseId == aId) {
|
||||
// Note: This will probably destroy the MediaKeys object!
|
||||
Release();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -148,10 +172,24 @@ MediaKeys::ResolvePromise(PromiseId aId)
|
|||
NS_WARNING("MediaKeys tried to resolve a non-existent promise");
|
||||
return;
|
||||
}
|
||||
// We should not resolve CreateSession or LoadSession calls via this path,
|
||||
// OnSessionActivated() should be called instead.
|
||||
MOZ_ASSERT(!mPendingSessions.Contains(aId));
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
if (mPendingSessions.Contains(aId)) {
|
||||
// We should only resolve LoadSession calls via this path,
|
||||
// not CreateSession() promises.
|
||||
nsRefPtr<MediaKeySession> session;
|
||||
if (!mPendingSessions.Get(aId, getter_AddRefs(session)) ||
|
||||
!session ||
|
||||
session->GetSessionId().IsEmpty()) {
|
||||
NS_WARNING("Received activation for non-existent session!");
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
mPendingSessions.Remove(aId);
|
||||
return;
|
||||
}
|
||||
mPendingSessions.Remove(aId);
|
||||
mKeySessions.Put(session->GetSessionId(), session);
|
||||
promise->MaybeResolve(session);
|
||||
} else {
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -174,13 +212,25 @@ MediaKeys::Create(const GlobalObject& aGlobal,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (!aKeySystem.EqualsASCII("org.w3.clearkey")) {
|
||||
if (!IsSupportedKeySystem(aKeySystem)) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
keys->mProxy = new CDMProxy(keys, aKeySystem);
|
||||
keys->mProxy->Init(keys->StorePromise(promise));
|
||||
|
||||
// The CDMProxy's initialization is asynchronous. The MediaKeys is
|
||||
// refcounted, and its instance is returned to JS by promise once
|
||||
// it's been initialized. No external refs exist to the MediaKeys while
|
||||
// we're waiting for the promise to be resolved, so we must hold a
|
||||
// reference to the new MediaKeys object until it's been created,
|
||||
// or its creation has failed. Store the id of the promise returned
|
||||
// here, and hold a self-reference until that promise is resolved or
|
||||
// rejected.
|
||||
MOZ_ASSERT(!keys->mCreatePromiseId, "Should only be created once!");
|
||||
keys->mCreatePromiseId = keys->StorePromise(promise);
|
||||
keys->AddRef();
|
||||
keys->mProxy->Init(keys->mCreatePromiseId);
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
@ -195,6 +245,9 @@ MediaKeys::OnCDMCreated(PromiseId aId)
|
|||
}
|
||||
nsRefPtr<MediaKeys> keys(this);
|
||||
promise->MaybeResolve(keys);
|
||||
if (mCreatePromiseId == aId) {
|
||||
Release();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
|
@ -224,9 +277,10 @@ MediaKeys::LoadSession(const nsAString& aSessionId, ErrorResult& aRv)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// Proxy owns session object until resolving promise.
|
||||
mProxy->LoadSession(StorePromise(promise),
|
||||
aSessionId);
|
||||
session->Init(aSessionId);
|
||||
auto pid = StorePromise(promise);
|
||||
mPendingSessions.Put(pid, session);
|
||||
mProxy->LoadSession(pid, aSessionId);
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
@ -262,7 +316,7 @@ MediaKeys::CreateSession(const nsAString& initDataType,
|
|||
}
|
||||
|
||||
void
|
||||
MediaKeys::OnSessionActivated(PromiseId aId, const nsAString& aSessionId)
|
||||
MediaKeys::OnSessionCreated(PromiseId aId, const nsAString& aSessionId)
|
||||
{
|
||||
nsRefPtr<Promise> promise(RetrievePromise(aId));
|
||||
if (!promise) {
|
||||
|
@ -272,16 +326,17 @@ MediaKeys::OnSessionActivated(PromiseId aId, const nsAString& aSessionId)
|
|||
MOZ_ASSERT(mPendingSessions.Contains(aId));
|
||||
|
||||
nsRefPtr<MediaKeySession> session;
|
||||
if (!mPendingSessions.Get(aId, getter_AddRefs(session)) || !session) {
|
||||
bool gotSession = mPendingSessions.Get(aId, getter_AddRefs(session));
|
||||
// Session has completed creation/loading, remove it from mPendingSessions,
|
||||
// and resolve the promise with it. We store it in mKeySessions, so we can
|
||||
// find it again if we need to send messages to it etc.
|
||||
mPendingSessions.Remove(aId);
|
||||
if (!gotSession || !session) {
|
||||
NS_WARNING("Received activation for non-existent session!");
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Session has completed creation/loading, remove it from mPendingSessions,
|
||||
// and resolve the promise with it. We store it in mKeySessions, so we can
|
||||
// find it again if we need to send messages to it etc.
|
||||
mPendingSessions.Remove(aId);
|
||||
session->Init(aSessionId);
|
||||
mKeySessions.Put(aSessionId, session);
|
||||
promise->MaybeResolve(session);
|
||||
|
@ -303,5 +358,27 @@ MediaKeys::GetSession(const nsAString& aSessionId)
|
|||
return session.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaKeys::GetOrigin(nsString& aOutOrigin)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// TODO: Bug 1035637, return a combination of origin and URL bar origin.
|
||||
|
||||
nsIPrincipal* principal = nullptr;
|
||||
nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(GetParentObject());
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
|
||||
do_QueryInterface(pWindow);
|
||||
if (scriptPrincipal) {
|
||||
principal = scriptPrincipal->GetPrincipal();
|
||||
}
|
||||
NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
|
||||
|
||||
nsresult res = nsContentUtils::GetUTFOrigin(principal, aOutOrigin);
|
||||
|
||||
EME_LOG("EME Origin = '%s'", NS_ConvertUTF16toUTF8(aOutOrigin).get());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -83,7 +83,7 @@ public:
|
|||
// Called once a Create() operation succeeds.
|
||||
void OnCDMCreated(PromiseId aId);
|
||||
// Called once a CreateSession or LoadSession succeeds.
|
||||
void OnSessionActivated(PromiseId aId, const nsAString& aSessionId);
|
||||
void OnSessionCreated(PromiseId aId, const nsAString& aSessionId);
|
||||
// Called once a session has closed.
|
||||
void OnSessionClosed(MediaKeySession* aSession);
|
||||
|
||||
|
@ -101,6 +101,8 @@ public:
|
|||
// Resolves promise with "undefined".
|
||||
void ResolvePromise(PromiseId aId);
|
||||
|
||||
nsresult GetOrigin(nsString& aOutOrigin);
|
||||
|
||||
private:
|
||||
|
||||
// Removes promise from mPromises, and returns it.
|
||||
|
@ -115,6 +117,7 @@ private:
|
|||
KeySessionHashMap mKeySessions;
|
||||
PromiseHashMap mPromises;
|
||||
PendingKeySessionsHashMap mPendingSessions;
|
||||
PromiseId mCreatePromiseId;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -13,10 +13,15 @@ EXPORTS.mozilla.dom += [
|
|||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'CDMCallbackProxy.h',
|
||||
'CDMCaps.h',
|
||||
'CDMProxy.h',
|
||||
'EMELog.h'
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'CDMCallbackProxy.cpp',
|
||||
'CDMCaps.cpp',
|
||||
'CDMProxy.cpp',
|
||||
'EMELog.cpp',
|
||||
'MediaKeyError.cpp',
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "MP4Reader.h"
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
|
@ -23,6 +25,23 @@ MediaDecoderStateMachine* MP4Decoder::CreateStateMachine()
|
|||
return new MediaDecoderStateMachine(this, new MP4Reader(this));
|
||||
}
|
||||
|
||||
nsresult
|
||||
MP4Decoder::SetCDMProxy(CDMProxy* aProxy)
|
||||
{
|
||||
nsresult rv = MediaDecoder::SetCDMProxy(aProxy);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
{
|
||||
// The MP4Reader can't decrypt EME content until it has a CDMProxy,
|
||||
// and the CDMProxy knows the capabilities of the CDM. The MP4Reader
|
||||
// remains in "waiting for resources" state until then.
|
||||
CDMCaps::AutoLock caps(aProxy->Capabilites());
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethod(this, &MediaDecoder::NotifyWaitingForResourcesStatusChanged));
|
||||
caps.CallOnMainThreadWhenCapsAvailable(task);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
MP4Decoder::GetSupportedCodecs(const nsACString& aType,
|
||||
char const *const ** aCodecList)
|
||||
|
|
|
@ -24,6 +24,8 @@ public:
|
|||
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine();
|
||||
|
||||
virtual nsresult SetCDMProxy(CDMProxy* aProxy) MOZ_OVERRIDE;
|
||||
|
||||
// Returns true if aType is a MIME type that we can render with the
|
||||
// a MP4 platform decoder backend. If aCodecList is non null,
|
||||
// it is filled with a (static const) null-terminated list of strings
|
||||
|
|
|
@ -105,6 +105,8 @@ MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
|
|||
, mVideo("MP4 video decoder data", Preferences::GetUint("media.mp4-video-decode-ahead", 2))
|
||||
, mLastReportedNumDecodedFrames(0)
|
||||
, mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
|
||||
, mDemuxerInitialized(false)
|
||||
, mIsEncrypted(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
MOZ_COUNT_CTOR(MP4Reader);
|
||||
|
@ -168,6 +170,8 @@ MP4Reader::InitLayersBackendType()
|
|||
mLayersBackendType = layerManager->GetCompositorBackendType();
|
||||
}
|
||||
|
||||
static bool sIsEMEEnabled = false;
|
||||
|
||||
nsresult
|
||||
MP4Reader::Init(MediaDecoderReader* aCloneDonor)
|
||||
{
|
||||
|
@ -185,34 +189,148 @@ MP4Reader::Init(MediaDecoderReader* aCloneDonor)
|
|||
SharedThreadPool::Get(NS_LITERAL_CSTRING("MP4 Video Decode")));
|
||||
NS_ENSURE_TRUE(mVideo.mTaskQueue, NS_ERROR_FAILURE);
|
||||
|
||||
static bool sSetupPrefCache = false;
|
||||
if (!sSetupPrefCache) {
|
||||
sSetupPrefCache = true;
|
||||
Preferences::AddBoolVarCache(&sIsEMEEnabled, "media.eme.enabled", false);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class DispatchKeyNeededEvent : public nsRunnable {
|
||||
public:
|
||||
DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder,
|
||||
nsTArray<uint8_t>& aInitData,
|
||||
const nsString& aInitDataType)
|
||||
: mDecoder(aDecoder)
|
||||
, mInitData(aInitData)
|
||||
, mInitDataType(aInitDataType)
|
||||
{
|
||||
}
|
||||
NS_IMETHOD Run() {
|
||||
// Note: Null check the owner, as the decoder could have been shutdown
|
||||
// since this event was dispatched.
|
||||
MediaDecoderOwner* owner = mDecoder->GetOwner();
|
||||
if (owner) {
|
||||
owner->DispatchNeedKey(mInitData, mInitDataType);
|
||||
}
|
||||
mDecoder = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<AbstractMediaDecoder> mDecoder;
|
||||
nsTArray<uint8_t> mInitData;
|
||||
nsString mInitDataType;
|
||||
};
|
||||
|
||||
bool MP4Reader::IsWaitingMediaResources()
|
||||
{
|
||||
nsRefPtr<CDMProxy> proxy;
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
if (!mIsEncrypted) {
|
||||
// Not encrypted, no need to wait for CDMProxy.
|
||||
return false;
|
||||
}
|
||||
proxy = mDecoder->GetCDMProxy();
|
||||
if (!proxy) {
|
||||
// We're encrypted, we need a CDMProxy to decrypt file.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// We'll keep waiting if the CDM hasn't informed Gecko of its capabilities.
|
||||
{
|
||||
CDMCaps::AutoLock caps(proxy->Capabilites());
|
||||
LOG("MP4Reader::IsWaitingMediaResources() capsKnown=%d", caps.AreCapsKnown());
|
||||
return !caps.AreCapsKnown();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MP4Reader::ExtractCryptoInitData(nsTArray<uint8_t>& aInitData)
|
||||
{
|
||||
MOZ_ASSERT(mDemuxer->Crypto().valid);
|
||||
const nsTArray<mp4_demuxer::PsshInfo>& psshs = mDemuxer->Crypto().pssh;
|
||||
for (uint32_t i = 0; i < psshs.Length(); i++) {
|
||||
aInitData.AppendElements(psshs[i].data);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
MP4Reader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
{
|
||||
bool ok = mDemuxer->Init();
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
||||
if (!mDemuxerInitialized) {
|
||||
bool ok = mDemuxer->Init();
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
||||
|
||||
mInfo.mAudio.mHasAudio = mAudio.mActive = mDemuxer->HasValidAudio();
|
||||
const AudioDecoderConfig& audio = mDemuxer->AudioConfig();
|
||||
// If we have audio, we *only* allow AAC to be decoded.
|
||||
if (mInfo.mAudio.mHasAudio && strcmp(audio.mime_type, "audio/mp4a-latm")) {
|
||||
return NS_ERROR_FAILURE;
|
||||
mInfo.mAudio.mHasAudio = mAudio.mActive = mDemuxer->HasValidAudio();
|
||||
const AudioDecoderConfig& audio = mDemuxer->AudioConfig();
|
||||
// If we have audio, we *only* allow AAC to be decoded.
|
||||
if (mInfo.mAudio.mHasAudio && strcmp(audio.mime_type, "audio/mp4a-latm")) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mInfo.mVideo.mHasVideo = mVideo.mActive = mDemuxer->HasValidVideo();
|
||||
const VideoDecoderConfig& video = mDemuxer->VideoConfig();
|
||||
// If we have video, we *only* allow H.264 to be decoded.
|
||||
if (mInfo.mVideo.mHasVideo && strcmp(video.mime_type, "video/avc")) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mIsEncrypted = mDemuxer->Crypto().valid;
|
||||
}
|
||||
|
||||
// Remember that we've initialized the demuxer, so that if we're decoding
|
||||
// an encrypted stream and we need to wait for a CDM to be set, we don't
|
||||
// need to reinit the demuxer.
|
||||
mDemuxerInitialized = true;
|
||||
}
|
||||
if (mDemuxer->Crypto().valid) {
|
||||
if (!sIsEMEEnabled) {
|
||||
// TODO: Need to signal DRM/EME required somehow...
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mInfo.mVideo.mHasVideo = mVideo.mActive = mDemuxer->HasValidVideo();
|
||||
const VideoDecoderConfig& video = mDemuxer->VideoConfig();
|
||||
// If we have video, we *only* allow H.264 to be decoded.
|
||||
if (mInfo.mVideo.mHasVideo && strcmp(video.mime_type, "video/avc")) {
|
||||
return NS_ERROR_FAILURE;
|
||||
// We have encrypted audio or video. We'll need a CDM to decrypt and
|
||||
// possibly decode this. Wait until we've received a CDM from the
|
||||
// JavaScript player app.
|
||||
nsRefPtr<CDMProxy> proxy;
|
||||
nsTArray<uint8_t> initData;
|
||||
ExtractCryptoInitData(initData);
|
||||
if (initData.Length() == 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!mInitDataEncountered.Contains(initData)) {
|
||||
mInitDataEncountered.AppendElement(initData);
|
||||
NS_DispatchToMainThread(new DispatchKeyNeededEvent(mDecoder, initData, NS_LITERAL_STRING("cenc")));
|
||||
}
|
||||
if (IsWaitingMediaResources()) {
|
||||
return NS_OK;
|
||||
}
|
||||
MOZ_ASSERT(!IsWaitingMediaResources());
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
proxy = mDecoder->GetCDMProxy();
|
||||
}
|
||||
MOZ_ASSERT(proxy);
|
||||
|
||||
mPlatform = PlatformDecoderModule::CreateCDMWrapper(proxy,
|
||||
HasAudio(),
|
||||
HasVideo(),
|
||||
GetTaskQueue());
|
||||
NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
|
||||
} else {
|
||||
mPlatform = PlatformDecoderModule::Create();
|
||||
NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
mPlatform = PlatformDecoderModule::Create();
|
||||
NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
|
||||
|
||||
if (HasAudio()) {
|
||||
const AudioDecoderConfig& audio = mDemuxer->AudioConfig();
|
||||
mInfo.mAudio.mRate = audio.samples_per_second;
|
||||
mInfo.mAudio.mChannels = audio.channel_count;
|
||||
mAudio.mCallback = new DecoderCallback(this, kAudio);
|
||||
|
@ -225,6 +343,7 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
|
|||
}
|
||||
|
||||
if (HasVideo()) {
|
||||
const VideoDecoderConfig& video = mDemuxer->VideoConfig();
|
||||
mInfo.mVideo.mDisplay =
|
||||
nsIntSize(video.display_width, video.display_height);
|
||||
mVideo.mCallback = new DecoderCallback(this, kVideo);
|
||||
|
@ -292,9 +411,9 @@ MP4Reader::PopSample(TrackType aTrack)
|
|||
return mDemuxer->DemuxAudioSample();
|
||||
|
||||
case kVideo:
|
||||
if (mQueuedVideoSample)
|
||||
if (mQueuedVideoSample) {
|
||||
return mQueuedVideoSample.forget();
|
||||
|
||||
}
|
||||
return mDemuxer->DemuxVideoSample();
|
||||
|
||||
default:
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "PlatformDecoderModule.h"
|
||||
#include "mp4_demuxer/mp4_demuxer.h"
|
||||
#include "MediaTaskQueue.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
|
||||
#include <deque>
|
||||
#include "mozilla/Monitor.h"
|
||||
|
@ -55,8 +56,12 @@ public:
|
|||
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered,
|
||||
int64_t aStartTime) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool IsWaitingMediaResources() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
void ExtractCryptoInitData(nsTArray<uint8_t>& aInitData);
|
||||
|
||||
// Destroys all decoder resources.
|
||||
void Shutdown();
|
||||
|
||||
|
@ -141,7 +146,7 @@ private:
|
|||
};
|
||||
DecoderData mAudio;
|
||||
DecoderData mVideo;
|
||||
// Queued frame extracted by the demuxer, but not yet sent to the platform
|
||||
// Queued samples extracted by the demuxer, but not yet sent to the platform
|
||||
// decoder.
|
||||
nsAutoPtr<mp4_demuxer::MP4Sample> mQueuedVideoSample;
|
||||
|
||||
|
@ -152,11 +157,17 @@ private:
|
|||
uint64_t mLastReportedNumDecodedFrames;
|
||||
|
||||
DecoderData& GetDecoderData(mp4_demuxer::TrackType aTrack);
|
||||
MP4SampleQueue& SampleQueue(mp4_demuxer::TrackType aTrack);
|
||||
MediaDataDecoder* Decoder(mp4_demuxer::TrackType aTrack);
|
||||
|
||||
layers::LayersBackend mLayersBackendType;
|
||||
|
||||
nsTArray<nsTArray<uint8_t>> mInitDataEncountered;
|
||||
|
||||
// True if we've read the streams' metadata.
|
||||
bool mDemuxerInitialized;
|
||||
|
||||
// Synchronized by decoder monitor.
|
||||
bool mIsEncrypted;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
#include "FFmpegRuntimeLinker.h"
|
||||
#endif
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "EMEDecoderModule.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "SharedThreadPool.h"
|
||||
#include "MediaTaskQueue.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -35,12 +39,66 @@ PlatformDecoderModule::Init()
|
|||
"media.fragmented-mp4.use-blank-decoder");
|
||||
Preferences::AddBoolVarCache(&sFFmpegDecoderEnabled,
|
||||
"media.fragmented-mp4.ffmpeg.enabled", false);
|
||||
|
||||
#ifdef XP_WIN
|
||||
WMFDecoderModule::Init();
|
||||
#endif
|
||||
}
|
||||
|
||||
class CreateTaskQueueTask : public nsRunnable {
|
||||
public:
|
||||
NS_IMETHOD Run() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
|
||||
return NS_OK;
|
||||
}
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
};
|
||||
|
||||
static already_AddRefed<MediaTaskQueue>
|
||||
CreateTaskQueue()
|
||||
{
|
||||
// We must create the MediaTaskQueue/SharedThreadPool on the main thread.
|
||||
nsRefPtr<CreateTaskQueueTask> t(new CreateTaskQueueTask());
|
||||
nsresult rv = NS_DispatchToMainThread(t, NS_DISPATCH_SYNC);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
return t->mTaskQueue.forget();
|
||||
}
|
||||
|
||||
/* static */
|
||||
PlatformDecoderModule*
|
||||
PlatformDecoderModule::CreateCDMWrapper(CDMProxy* aProxy,
|
||||
bool aHasAudio,
|
||||
bool aHasVideo,
|
||||
MediaTaskQueue* aTaskQueue)
|
||||
{
|
||||
bool cdmDecodesAudio;
|
||||
bool cdmDecodesVideo;
|
||||
{
|
||||
CDMCaps::AutoLock caps(aProxy->Capabilites());
|
||||
cdmDecodesAudio = caps.CanDecryptAndDecodeAudio();
|
||||
cdmDecodesVideo = caps.CanDecryptAndDecodeVideo();
|
||||
}
|
||||
|
||||
nsAutoPtr<PlatformDecoderModule> pdm;
|
||||
if ((!cdmDecodesAudio && aHasAudio) || (!cdmDecodesVideo && aHasVideo)) {
|
||||
// The CDM itself can't decode. We need to wrap a PDM to decode the
|
||||
// decrypted output of the CDM.
|
||||
pdm = Create();
|
||||
if (!pdm) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("CDM that decodes not yet supported!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new EMEDecoderModule(aProxy,
|
||||
pdm.forget(),
|
||||
cdmDecodesAudio,
|
||||
cdmDecodesVideo,
|
||||
CreateTaskQueue());
|
||||
}
|
||||
|
||||
/* static */
|
||||
PlatformDecoderModule*
|
||||
PlatformDecoderModule::Create()
|
||||
|
|
|
@ -31,6 +31,7 @@ class MediaDataDecoder;
|
|||
class MediaDataDecoderCallback;
|
||||
class MediaInputQueue;
|
||||
class MediaTaskQueue;
|
||||
class CDMProxy;
|
||||
typedef int64_t Microseconds;
|
||||
|
||||
// The PlatformDecoderModule interface is used by the MP4Reader to abstract
|
||||
|
@ -65,6 +66,15 @@ public:
|
|||
// This is called on the decode thread.
|
||||
static PlatformDecoderModule* Create();
|
||||
|
||||
// Creates a PlatformDecoderModule that uses a CDMProxy to decrypt or
|
||||
// decrypt-and-decode EME encrypted content. If the CDM only decrypts and
|
||||
// does not decode, we create a PDM and use that to create MediaDataDecoders
|
||||
// that we use on on aTaskQueue to decode the decrypted stream.
|
||||
static PlatformDecoderModule* CreateCDMWrapper(CDMProxy* aProxy,
|
||||
bool aHasAudio,
|
||||
bool aHasVideo,
|
||||
MediaTaskQueue* aTaskQueue);
|
||||
|
||||
// Called to shutdown the decoder module and cleanup state. This should
|
||||
// block until shutdown is complete. This is called after Shutdown() has
|
||||
// been called on all MediaDataDecoders created from this
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "EMEDecoderModule.h"
|
||||
#include "mtransport/runnable_utils.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "prsystem.h"
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "mozilla/EMELog.h"
|
||||
#include "MediaTaskQueue.h"
|
||||
#include "SharedThreadPool.h"
|
||||
#include "mozilla/EMELog.h"
|
||||
#include <string>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class EMEDecryptor : public MediaDataDecoder {
|
||||
typedef mp4_demuxer::MP4Sample MP4Sample;
|
||||
|
||||
public:
|
||||
|
||||
EMEDecryptor(MediaDataDecoder* aDecoder,
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
CDMProxy* aProxy)
|
||||
: mDecoder(aDecoder)
|
||||
, mCallback(aCallback)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mProxy(aProxy)
|
||||
{
|
||||
}
|
||||
|
||||
virtual nsresult Init() MOZ_OVERRIDE {
|
||||
return mTaskQueue->SyncDispatch(
|
||||
NS_NewRunnableMethod(mDecoder,
|
||||
&MediaDataDecoder::Init));
|
||||
}
|
||||
|
||||
class RedeliverEncryptedInput : public nsRunnable {
|
||||
public:
|
||||
RedeliverEncryptedInput(EMEDecryptor* aDecryptor,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MP4Sample* aSample)
|
||||
: mDecryptor(aDecryptor)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mSample(aSample)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
RefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
|
||||
&EMEDecryptor::Input,
|
||||
mSample.forget());
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
mTaskQueue = nullptr;
|
||||
mDecryptor = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<EMEDecryptor> mDecryptor;
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
nsAutoPtr<MP4Sample> mSample;
|
||||
};
|
||||
|
||||
class DeliverDecrypted : public DecryptionClient {
|
||||
public:
|
||||
DeliverDecrypted(EMEDecryptor* aDecryptor, MediaTaskQueue* aTaskQueue)
|
||||
: mDecryptor(aDecryptor)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
{}
|
||||
virtual void Decrypted(nsresult aResult,
|
||||
mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE {
|
||||
if (NS_FAILED(aResult)) {
|
||||
mDecryptor->mCallback->Error();
|
||||
delete aSample;
|
||||
} else {
|
||||
RefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
|
||||
&EMEDecryptor::Decrypted,
|
||||
aSample);
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
mTaskQueue = nullptr;
|
||||
mDecryptor = nullptr;
|
||||
}
|
||||
}
|
||||
private:
|
||||
nsRefPtr<EMEDecryptor> mDecryptor;
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
};
|
||||
|
||||
virtual nsresult Input(MP4Sample* aSample) MOZ_OVERRIDE {
|
||||
// We run the PDM on its own task queue. We can't run it on the decode
|
||||
// task queue, because that calls into Input() in a loop and waits until
|
||||
// output is delivered. We need to defer some Input() calls while we wait
|
||||
// for keys to become usable, and once they do we need to dispatch an event
|
||||
// to run the PDM on the same task queue, but since the decode task queue
|
||||
// is waiting in MP4Reader::Decode() for output our task would never run.
|
||||
// So we dispatch tasks to make all calls into the wrapped decoder.
|
||||
{
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
if (!caps.IsKeyUsable(aSample->crypto.key)) {
|
||||
EME_LOG("Encountered a non-usable key, waiting");
|
||||
nsRefPtr<nsIRunnable> task(new RedeliverEncryptedInput(this,
|
||||
mTaskQueue,
|
||||
aSample));
|
||||
caps.CallWhenKeyUsable(aSample->crypto.key, task);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
mProxy->Decrypt(aSample, new DeliverDecrypted(this, mTaskQueue));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Decrypted(mp4_demuxer::MP4Sample* aSample) {
|
||||
mTaskQueue->Dispatch(
|
||||
NS_NewRunnableMethodWithArg<mp4_demuxer::MP4Sample*>(
|
||||
mDecoder,
|
||||
&MediaDataDecoder::Input,
|
||||
aSample));
|
||||
}
|
||||
|
||||
virtual nsresult Flush() MOZ_OVERRIDE {
|
||||
mTaskQueue->SyncDispatch(
|
||||
NS_NewRunnableMethod(
|
||||
mDecoder,
|
||||
&MediaDataDecoder::Flush));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual nsresult Drain() MOZ_OVERRIDE {
|
||||
mTaskQueue->Dispatch(
|
||||
NS_NewRunnableMethod(
|
||||
mDecoder,
|
||||
&MediaDataDecoder::Drain));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE {
|
||||
mTaskQueue->SyncDispatch(
|
||||
NS_NewRunnableMethod(
|
||||
mDecoder,
|
||||
&MediaDataDecoder::Shutdown));
|
||||
mDecoder = nullptr;
|
||||
mTaskQueue->Shutdown();
|
||||
mTaskQueue = nullptr;
|
||||
mProxy = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
nsRefPtr<MediaDataDecoder> mDecoder;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
};
|
||||
|
||||
EMEDecoderModule::EMEDecoderModule(CDMProxy* aProxy,
|
||||
PlatformDecoderModule* aPDM,
|
||||
bool aCDMDecodesAudio,
|
||||
bool aCDMDecodesVideo,
|
||||
already_AddRefed<MediaTaskQueue> aTaskQueue)
|
||||
: mProxy(aProxy)
|
||||
, mPDM(aPDM)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mCDMDecodesAudio(aCDMDecodesAudio)
|
||||
, mCDMDecodesVideo(aCDMDecodesVideo)
|
||||
{
|
||||
}
|
||||
|
||||
EMEDecoderModule::~EMEDecoderModule()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
EMEDecoderModule::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
if (mPDM) {
|
||||
return mPDM->Shutdown();
|
||||
}
|
||||
mTaskQueue->Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MediaDataDecoder*
|
||||
EMEDecoderModule::CreateH264Decoder(const VideoDecoderConfig& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
if (mCDMDecodesVideo) {
|
||||
NS_WARNING("Support for CDM that decodes video not yet supported");
|
||||
return nullptr;
|
||||
} else {
|
||||
nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateH264Decoder(aConfig,
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
aVideoTaskQueue,
|
||||
aCallback));
|
||||
if (!decoder) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new EMEDecryptor(decoder,
|
||||
aCallback,
|
||||
mTaskQueue,
|
||||
mProxy);
|
||||
}
|
||||
}
|
||||
|
||||
MediaDataDecoder*
|
||||
EMEDecoderModule::CreateAACDecoder(const AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
if (mCDMDecodesAudio) {
|
||||
NS_WARNING("Support for CDM that decodes audio not yet supported");
|
||||
return nullptr;
|
||||
} else {
|
||||
nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateAACDecoder(aConfig,
|
||||
aAudioTaskQueue,
|
||||
aCallback));
|
||||
if (!decoder) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new EMEDecryptor(decoder,
|
||||
aCallback,
|
||||
mTaskQueue,
|
||||
mProxy);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,62 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#if !defined(EMEDecoderModule_h_)
|
||||
#define EMEDecoderModule_h_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "gmp-decryption.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class CDMProxy;
|
||||
class MediaTaskQueue;
|
||||
|
||||
class EMEDecoderModule : public PlatformDecoderModule {
|
||||
private:
|
||||
typedef mp4_demuxer::AudioDecoderConfig AudioDecoderConfig;
|
||||
typedef mp4_demuxer::VideoDecoderConfig VideoDecoderConfig;
|
||||
|
||||
public:
|
||||
EMEDecoderModule(CDMProxy* aProxy,
|
||||
PlatformDecoderModule* aPDM,
|
||||
bool aCDMDecodesAudio,
|
||||
bool aCDMDecodesVideo,
|
||||
already_AddRefed<MediaTaskQueue> aDecodeTaskQueue);
|
||||
|
||||
virtual ~EMEDecoderModule();
|
||||
|
||||
// Called when the decoders have shutdown. Main thread only.
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
// Decode thread.
|
||||
virtual MediaDataDecoder*
|
||||
CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
|
||||
|
||||
// Decode thread.
|
||||
virtual MediaDataDecoder* CreateAACDecoder(
|
||||
const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
// Will be null if CDM has decoding capability.
|
||||
nsAutoPtr<PlatformDecoderModule> mPDM;
|
||||
// We run the PDM on its own task queue.
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
bool mCDMDecodesAudio;
|
||||
bool mCDMDecodesVideo;
|
||||
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // EMEDecoderModule_h_
|
|
@ -5,6 +5,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS += [
|
||||
'eme/EMEDecoderModule.h',
|
||||
'MP4Decoder.h',
|
||||
'MP4Reader.h',
|
||||
'PlatformDecoderModule.h',
|
||||
|
@ -12,6 +13,7 @@ EXPORTS += [
|
|||
|
||||
UNIFIED_SOURCES += [
|
||||
'BlankDecoderModule.cpp',
|
||||
'eme/EMEDecoderModule.cpp',
|
||||
'PlatformDecoderModule.cpp',
|
||||
]
|
||||
|
||||
|
@ -21,24 +23,24 @@ SOURCES += [
|
|||
]
|
||||
|
||||
if CONFIG['MOZ_WMF']:
|
||||
DIRS += [ 'wmf' ];
|
||||
DIRS += [ 'wmf' ];
|
||||
|
||||
if CONFIG['MOZ_FFMPEG']:
|
||||
EXPORTS += [
|
||||
'ffmpeg/FFmpegRuntimeLinker.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'ffmpeg/FFmpegLog.cpp',
|
||||
'ffmpeg/FFmpegRuntimeLinker.cpp',
|
||||
]
|
||||
DIRS += [
|
||||
'ffmpeg/libav53',
|
||||
'ffmpeg/libav54',
|
||||
'ffmpeg/libav55',
|
||||
]
|
||||
LOCAL_INCLUDES += [
|
||||
'ffmpeg',
|
||||
]
|
||||
EXPORTS += [
|
||||
'ffmpeg/FFmpegRuntimeLinker.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'ffmpeg/FFmpegLog.cpp',
|
||||
'ffmpeg/FFmpegRuntimeLinker.cpp',
|
||||
]
|
||||
DIRS += [
|
||||
'ffmpeg/libav53',
|
||||
'ffmpeg/libav54',
|
||||
'ffmpeg/libav55',
|
||||
]
|
||||
LOCAL_INCLUDES += [
|
||||
'ffmpeg',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ using mozilla::dom::CrashReporterChild;
|
|||
#if defined(XP_WIN)
|
||||
#define TARGET_SANDBOX_EXPORTS
|
||||
#include "mozilla/sandboxTarget.h"
|
||||
#elif defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
|
||||
#include "mozilla/Sandbox.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -98,6 +100,13 @@ GMPChild::LoadPluginLibrary(const std::string& aPluginPath)
|
|||
|
||||
nsAutoCString nativePath;
|
||||
libFile->GetNativePath(nativePath);
|
||||
|
||||
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
|
||||
// Enable sandboxing here -- we know the plugin file's path, but
|
||||
// this process's execution hasn't been affected by its content yet.
|
||||
mozilla::SetMediaPluginSandbox(nativePath.get());
|
||||
#endif
|
||||
|
||||
mLib = PR_LoadLibrary(nativePath.get());
|
||||
if (!mLib) {
|
||||
return false;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define GMPDecryptorProxy_h_
|
||||
|
||||
#include "GMPCallbackBase.h"
|
||||
#include "gmp-decryption.h"
|
||||
|
||||
namespace mp4_demuxer {
|
||||
class CryptoSample;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script>
|
||||
|
||||
var ac = new AudioContext(1, 1354, 44100);
|
||||
var shaper = ac.createWaveShaper();
|
||||
var biquad = ac.createBiquadFilter();
|
||||
shaper.connect(biquad.frequency);
|
||||
biquad.getFrequencyResponse(new Float32Array(55785),
|
||||
new Float32Array(62876),
|
||||
new Float32Array(45111));
|
||||
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
|
@ -0,0 +1,45 @@
|
|||
<html class="reftest-wait">
|
||||
<head>
|
||||
<script>
|
||||
function boom() {
|
||||
var Context0= new window.AudioContext();
|
||||
var BufferSource0=Context0.createBufferSource();
|
||||
BufferSource0.start(0);
|
||||
BufferSource0.playbackRate.value = 1.0/128.0;
|
||||
|
||||
setTimeout(
|
||||
function(){
|
||||
BufferSource0.buffer=
|
||||
function(){
|
||||
var length=1;
|
||||
var Buffer=Context0.createBuffer(1,length,Context0.sampleRate);
|
||||
var bufferData= Buffer.getChannelData(0);
|
||||
for (var i = 0; i < length; ++i) {
|
||||
bufferData[i] = Math.sin(i*(626))
|
||||
};return Buffer;
|
||||
}();
|
||||
setTimeout(
|
||||
function(){
|
||||
document.documentElement.removeAttribute("class");
|
||||
}, 0)
|
||||
},4)
|
||||
|
||||
BufferSource0.buffer=
|
||||
function(){
|
||||
const resample_filter_length = 64;
|
||||
// Small enough so that resampler tail latency is triggered immediately,
|
||||
// but large enough that skip_zeros does not consume resampler tail
|
||||
// latency.
|
||||
var length=resample_filter_length/2 + 1;
|
||||
var Buffer=Context0.createBuffer(1,length,Context0.sampleRate);
|
||||
var bufferData= Buffer.getChannelData(0);
|
||||
for (var i = 0; i < length; ++i) {
|
||||
bufferData[i] = Math.sin(i*(311980))
|
||||
};
|
||||
return Buffer;
|
||||
}();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="boom();">
|
||||
</body>
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<script>
|
||||
var ctx = new AudioContext();
|
||||
var source = ctx.createOscillator();
|
||||
source.start(0);
|
||||
|
||||
function appendMerger(src) {
|
||||
const inputCount = 18;
|
||||
|
||||
var merger = ctx.createChannelMerger(32);
|
||||
|
||||
for (var i = 0; i < inputCount; ++i) {
|
||||
src.connect(merger, 0, i);
|
||||
}
|
||||
|
||||
return merger;
|
||||
}
|
||||
|
||||
for (var i = 0; i < 6; ++i) {
|
||||
source = appendMerger(source);
|
||||
}
|
||||
</script>
|
|
@ -63,8 +63,11 @@ load 925619-2.html
|
|||
load 926619.html
|
||||
load 933151.html
|
||||
load 933156.html
|
||||
load 944851.html
|
||||
load 952756.html
|
||||
load 966636.html
|
||||
load 986901.html
|
||||
load 990794.html
|
||||
load buffer-source-ended-1.html
|
||||
load offline-buffer-source-ended-1.html
|
||||
HTTP load media-element-source-seek-1.html
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "AudioNodeStream.h"
|
||||
#include "AudioDestinationNode.h"
|
||||
#include "AudioParamTimeline.h"
|
||||
#include "speex/speex_resampler.h"
|
||||
#include <limits>
|
||||
|
||||
namespace mozilla {
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "AudioNodeEngine.h"
|
||||
#include "AudioNodeStream.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "speex/speex_resampler.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "AudioNodeStream.h"
|
||||
#include "AudioParamTimeline.h"
|
||||
#include "blink/HRTFDatabaseLoader.h"
|
||||
#include "speex/speex_resampler.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
#include "HRTFElevation.h"
|
||||
|
||||
#include "speex/speex_resampler.h"
|
||||
#include <speex/speex_resampler.h>
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "AudioSampleFormat.h"
|
||||
|
||||
|
|
|
@ -305,7 +305,7 @@ PicoCallbackRunnable::Run()
|
|||
} else {
|
||||
// If we already fed all the text to the engine, send a zero length buffer
|
||||
// and quit.
|
||||
DispatchSynthDataRunnable(already_AddRefed<SharedBuffer>(nullptr), 0);
|
||||
DispatchSynthDataRunnable(already_AddRefed<SharedBuffer>(), 0);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -4371,6 +4371,21 @@ nsGlobalWindow::IsChromeWindow(JSContext* aCx, JSObject* aObj)
|
|||
return xpc::WindowOrNull(aObj)->IsChromeWindow();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsGlobalWindow::IsShowModalDialogEnabled(JSContext*, JSObject*)
|
||||
{
|
||||
static bool sAddedPrefCache = false;
|
||||
static bool sIsDisabled;
|
||||
static const char sShowModalDialogPref[] = "dom.disable_window_showModalDialog";
|
||||
|
||||
if (!sAddedPrefCache) {
|
||||
Preferences::AddBoolVarCache(&sIsDisabled, sShowModalDialogPref, false);
|
||||
sAddedPrefCache = true;
|
||||
}
|
||||
|
||||
return !sIsDisabled;
|
||||
}
|
||||
|
||||
nsIDOMOfflineResourceList*
|
||||
nsGlobalWindow::GetApplicationCache(ErrorResult& aError)
|
||||
{
|
||||
|
@ -9127,7 +9142,7 @@ nsGlobalWindow::ShowModalDialog(const nsAString& aUrl, nsIVariant* aArgument,
|
|||
(aUrl, aArgument, aOptions, aError), aError,
|
||||
nullptr);
|
||||
|
||||
if (Preferences::GetBool("dom.disable_window_showModalDialog", false)) {
|
||||
if (!IsShowModalDialogEnabled()) {
|
||||
aError.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -512,6 +512,9 @@ public:
|
|||
|
||||
static bool IsChromeWindow(JSContext* /* unused */, JSObject* aObj);
|
||||
|
||||
static bool IsShowModalDialogEnabled(JSContext* /* unused */ = nullptr,
|
||||
JSObject* /* unused */ = nullptr);
|
||||
|
||||
bool DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
JS::Handle<jsid> aId,
|
||||
JS::MutableHandle<JSPropertyDescriptor> aDesc);
|
||||
|
|
|
@ -710,10 +710,6 @@ DumpString(const nsAString &str)
|
|||
#define JS_OPTIONS_DOT_STR "javascript.options."
|
||||
|
||||
static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR;
|
||||
static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict";
|
||||
#ifdef DEBUG
|
||||
static const char js_strict_debug_option_str[] = JS_OPTIONS_DOT_STR "strict.debug";
|
||||
#endif
|
||||
#ifdef JS_GC_ZEAL
|
||||
static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal";
|
||||
static const char js_zeal_frequency_str[] = JS_OPTIONS_DOT_STR "gczeal.frequency";
|
||||
|
@ -724,34 +720,11 @@ static const char js_memnotify_option_str[] = JS_OPTIONS_DOT_STR "mem.notify";
|
|||
void
|
||||
nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
|
||||
{
|
||||
nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
|
||||
JSContext *cx = context->mContext;
|
||||
|
||||
sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str);
|
||||
sPostGCEventsToObserver = Preferences::GetBool(js_memnotify_option_str);
|
||||
|
||||
JS::ContextOptionsRef(cx).setExtraWarnings(Preferences::GetBool(js_strict_option_str));
|
||||
|
||||
// The vanilla GetGlobalObject returns null if a global isn't set up on
|
||||
// the context yet. We can sometimes be call midway through context init,
|
||||
// So ask for the member directly instead.
|
||||
nsIScriptGlobalObject *global = context->GetGlobalObjectRef();
|
||||
|
||||
// XXX should we check for sysprin instead of a chrome window, to make
|
||||
// XXX components be covered by the chrome pref instead of the content one?
|
||||
nsCOMPtr<nsIDOMWindow> contentWindow(do_QueryInterface(global));
|
||||
nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(global));
|
||||
|
||||
#ifdef DEBUG
|
||||
// In debug builds, warnings are enabled in chrome context if
|
||||
// javascript.options.strict.debug is true
|
||||
if (Preferences::GetBool(js_strict_debug_option_str) &&
|
||||
(chromeWindow || !contentWindow)) {
|
||||
JS::ContextOptionsRef(cx).setExtraWarnings(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
|
||||
int32_t zeal = Preferences::GetInt(js_zeal_option_str, -1);
|
||||
int32_t frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
|
||||
if (zeal >= 0)
|
||||
|
|
|
@ -43,8 +43,8 @@ function runTest() {
|
|||
}
|
||||
|
||||
addEventListener('testready', function() {
|
||||
// Cause the CPU wake lock taken on behalf of this new process to time out
|
||||
// after 1s.
|
||||
// Cause the grace period of priority privilege for this new process to time
|
||||
// out after 1s.
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{set: [["dom.ipc.systemMessageCPULockTimeoutSec", 1]]},
|
||||
runTest);
|
||||
|
|
|
@ -57,9 +57,9 @@ function runTest() {
|
|||
}
|
||||
|
||||
addEventListener('testready', function() {
|
||||
// We don't want this wake lock to time out during the test; if it did, then
|
||||
// we might see BACKGROUND priority instead of BACKGROUND_PERCEIVABLE. So
|
||||
// set the timeout to a large value.
|
||||
// We don't want the grace period of priority privilege to time out during the
|
||||
// test; should it really happen, we would see BACKGROUND priority instead of
|
||||
// BACKGROUND_PERCEIVABLE. So set the timeout to a large value.
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{set: [["dom.ipc.systemMessageCPULockTimeoutSec", 99999]]},
|
||||
runTest);
|
||||
|
|
|
@ -69,8 +69,8 @@ function runTest() {
|
|||
addEventListener('testready', function() {
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{set: [
|
||||
/* Cause the CPU wake lock taken on behalf of the high-priority process
|
||||
* to time out after 1s. */
|
||||
/* Cause the grace period of priority privilege for the high-priority
|
||||
* process to time out after 1s. */
|
||||
["dom.ipc.systemMessageCPULockTimeoutSec", 1],
|
||||
["dom.wakelock.enabled", true]
|
||||
]},
|
||||
|
|
|
@ -65,8 +65,8 @@ function runTest() {
|
|||
}
|
||||
|
||||
addEventListener('testready', function() {
|
||||
// Cause the CPU wake lock taken on behalf of the high-priority process never
|
||||
// to time out during this test.
|
||||
// Cause the grace period of priority privilege for the high-priority process
|
||||
// never to time out during this test.
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{set: [["dom.ipc.systemMessageCPULockTimeoutSec", 1000]]},
|
||||
runTest);
|
||||
|
|
|
@ -26,7 +26,7 @@ WebGLExtensionInstancedArrays::DrawArraysInstancedANGLE(GLenum mode, GLint first
|
|||
GLsizei count, GLsizei primcount)
|
||||
{
|
||||
if (mIsLost)
|
||||
return mContext->ErrorInvalidOperation("drawArraysInstancedANGLE: Extension is lost.");
|
||||
return mContext->GenerateWarning("drawArraysInstancedANGLE: Extension is lost.");
|
||||
|
||||
mContext->DrawArraysInstanced(mode, first, count, primcount);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ WebGLExtensionInstancedArrays::DrawElementsInstancedANGLE(GLenum mode, GLsizei c
|
|||
GLsizei primcount)
|
||||
{
|
||||
if (mIsLost)
|
||||
return mContext->ErrorInvalidOperation("drawElementsInstancedANGLE: Extension is lost.");
|
||||
return mContext->GenerateWarning("drawElementsInstancedANGLE: Extension is lost.");
|
||||
|
||||
mContext->DrawElementsInstanced(mode, count, type, offset, primcount);
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ void
|
|||
WebGLExtensionInstancedArrays::VertexAttribDivisorANGLE(GLuint index, GLuint divisor)
|
||||
{
|
||||
if (mIsLost)
|
||||
return mContext->ErrorInvalidOperation("vertexAttribDivisorANGLE: Extension is lost.");
|
||||
return mContext->GenerateWarning("vertexAttribDivisorANGLE: Extension is lost.");
|
||||
|
||||
mContext->VertexAttribDivisor(index, divisor);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ WebGLExtensionVertexArray::~WebGLExtensionVertexArray()
|
|||
already_AddRefed<WebGLVertexArray> WebGLExtensionVertexArray::CreateVertexArrayOES()
|
||||
{
|
||||
if (mIsLost) {
|
||||
mContext->ErrorInvalidOperation("createVertexArrayOES: Extension is lost. Returning NULL.");
|
||||
mContext->GenerateWarning("createVertexArrayOES: Extension is lost. Returning null.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ already_AddRefed<WebGLVertexArray> WebGLExtensionVertexArray::CreateVertexArrayO
|
|||
void WebGLExtensionVertexArray::DeleteVertexArrayOES(WebGLVertexArray* array)
|
||||
{
|
||||
if (mIsLost)
|
||||
return mContext->ErrorInvalidOperation("deleteVertexArrayOES: Extension is lost.");
|
||||
return mContext->GenerateWarning("deleteVertexArrayOES: Extension is lost.");
|
||||
|
||||
mContext->DeleteVertexArray(array);
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ void WebGLExtensionVertexArray::DeleteVertexArrayOES(WebGLVertexArray* array)
|
|||
bool WebGLExtensionVertexArray::IsVertexArrayOES(WebGLVertexArray* array)
|
||||
{
|
||||
if (mIsLost) {
|
||||
mContext->ErrorInvalidOperation("isVertexArrayOES: Extension is lost. Returning false.");
|
||||
mContext->GenerateWarning("isVertexArrayOES: Extension is lost. Returning false.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ bool WebGLExtensionVertexArray::IsVertexArrayOES(WebGLVertexArray* array)
|
|||
void WebGLExtensionVertexArray::BindVertexArrayOES(WebGLVertexArray* array)
|
||||
{
|
||||
if (mIsLost)
|
||||
return mContext->ErrorInvalidOperation("bindVertexArrayOES: Extension is lost.");
|
||||
return mContext->GenerateWarning("bindVertexArrayOES: Extension is lost.");
|
||||
|
||||
mContext->BindVertexArray(array);
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ unpackFromFloat16(uint16_t v)
|
|||
return f32Value;
|
||||
}
|
||||
|
||||
f32Bits |= uint32_t(exp + (-15 + 127)) << 10;
|
||||
f32Bits |= uint32_t(exp + (-15 + 127)) << 23;
|
||||
f32Bits |= uint32_t(v & 0x03FF) << 13;
|
||||
|
||||
return f32Value;
|
||||
|
|
|
@ -58,6 +58,26 @@ const CB_DCS_LANG_GROUP_2 = [
|
|||
null, null, null, null, null, null
|
||||
];
|
||||
|
||||
const CB_MAX_CONTENT_PER_PAGE_7BIT = Math.floor((CB_MESSAGE_SIZE_GSM - 6) * 8 / 7);
|
||||
const CB_MAX_CONTENT_PER_PAGE_UCS2 = Math.floor((CB_MESSAGE_SIZE_GSM - 6) / 2);
|
||||
|
||||
const DUMMY_BODY_7BITS = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
+ "@@@@@@@@@@@@@"; // 93 ascii chars.
|
||||
const DUMMY_BODY_7BITS_IND = DUMMY_BODY_7BITS.substr(3);
|
||||
const DUMMY_BODY_UCS2 = "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
|
||||
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
|
||||
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
|
||||
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
|
||||
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
|
||||
+ "\u0000"; // 41 unicode chars.
|
||||
const DUMMY_BODY_UCS2_IND = DUMMY_BODY_UCS2.substr(1);
|
||||
|
||||
is(DUMMY_BODY_7BITS.length, CB_MAX_CONTENT_PER_PAGE_7BIT, "DUMMY_BODY_7BITS.length");
|
||||
is(DUMMY_BODY_7BITS_IND.length, CB_MAX_CONTENT_PER_PAGE_7BIT - 3, "DUMMY_BODY_7BITS_IND.length");
|
||||
is(DUMMY_BODY_UCS2.length, CB_MAX_CONTENT_PER_PAGE_UCS2, "DUMMY_BODY_UCS2.length");
|
||||
is(DUMMY_BODY_UCS2_IND.length, CB_MAX_CONTENT_PER_PAGE_UCS2 - 1, "DUMMY_BODY_UCS2_IND.length");
|
||||
|
||||
/**
|
||||
* Compose input number into specified number of semi-octets.
|
||||
*
|
||||
|
|
|
@ -5,4 +5,5 @@ qemu = true
|
|||
|
||||
[test_cellbroadcast_etws.js]
|
||||
[test_cellbroadcast_gsm.js]
|
||||
[test_cellbroadcast_multi_sim.js]
|
||||
[test_cellbroadcast_multi_sim.js]
|
||||
[test_cellbroadcast_umts.js]
|
|
@ -1,29 +1,9 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_TIMEOUT = 90000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
const CB_MAX_CONTENT_7BIT = Math.floor((CB_MESSAGE_SIZE_GSM - 6) * 8 / 7);
|
||||
const CB_MAX_CONTENT_UCS2 = Math.floor((CB_MESSAGE_SIZE_GSM - 6) / 2);
|
||||
|
||||
const BODY_7BITS = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
+ "@@@@@@@@@@@@@"; // 93 ascii chars.
|
||||
const BODY_7BITS_IND = BODY_7BITS.substr(3);
|
||||
const BODY_UCS2 = "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
|
||||
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
|
||||
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
|
||||
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
|
||||
+ "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
|
||||
+ "\u0000"; // 41 unicode chars.
|
||||
const BODY_UCS2_IND = BODY_UCS2.substr(1);
|
||||
|
||||
is(BODY_7BITS.length, CB_MAX_CONTENT_7BIT, "BODY_7BITS.length");
|
||||
is(BODY_7BITS_IND.length, CB_MAX_CONTENT_7BIT - 3, "BODY_7BITS_IND.length");
|
||||
is(BODY_UCS2.length, CB_MAX_CONTENT_UCS2, "BODY_UCS2.length");
|
||||
is(BODY_UCS2_IND.length, CB_MAX_CONTENT_UCS2 - 1, "BODY_UCS2_IND.length");
|
||||
|
||||
function testReceiving_GSM_MessageAttributes() {
|
||||
log("Test receiving GSM Cell Broadcast - Message Attributes");
|
||||
|
||||
|
@ -159,13 +139,13 @@ function testReceiving_GSM_Language_and_Body() {
|
|||
|
||||
switch (aDcsInfo.encoding) {
|
||||
case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
|
||||
is(aMessage.body, aDcsInfo.indicator ? BODY_7BITS_IND : BODY_7BITS, "aMessage.body");
|
||||
is(aMessage.body, aDcsInfo.indicator ? DUMMY_BODY_7BITS_IND : DUMMY_BODY_7BITS, "aMessage.body");
|
||||
break;
|
||||
case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
|
||||
ok(aMessage.body == null, "aMessage.body");
|
||||
break;
|
||||
case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
|
||||
is(aMessage.body, aDcsInfo.indicator ? BODY_UCS2_IND : BODY_UCS2, "aMessage.body");
|
||||
is(aMessage.body, aDcsInfo.indicator ? DUMMY_BODY_UCS2_IND : DUMMY_BODY_UCS2, "aMessage.body");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -296,7 +276,7 @@ function testReceiving_GSM_Multipart() {
|
|||
let numParts = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
||||
|
||||
let verifyCBMessage = (aMessage, aNumParts) => {
|
||||
is(aMessage.body.length, (aNumParts * CB_MAX_CONTENT_7BIT),
|
||||
is(aMessage.body.length, (aNumParts * CB_MAX_CONTENT_PER_PAGE_7BIT),
|
||||
"aMessage.body");
|
||||
};
|
||||
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
MARIONETTE_TIMEOUT = 10000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
const BODY_7BITS = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
+ "@@@@@@@@@@@@@"; // 93 ascii chars.
|
||||
|
||||
function testReceiving_MultiSIM() {
|
||||
log("Test receiving GSM Cell Broadcast - Multi-SIM");
|
||||
|
||||
|
@ -15,7 +11,7 @@ function testReceiving_MultiSIM() {
|
|||
|
||||
let verifyCBMessage = (aMessage, aServiceId) => {
|
||||
log("Verify CB message received from serviceId: " + aServiceId);
|
||||
is(aMessage.body, BODY_7BITS, "Checking message body.");
|
||||
is(aMessage.body, DUMMY_BODY_7BITS, "Checking message body.");
|
||||
is(aMessage.serviceId, aServiceId, "Checking serviceId.");
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,450 @@
|
|||
MARIONETTE_TIMEOUT = 90000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
const CB_UMTS_MESSAGE_TYPE_CBS = 1;
|
||||
const CB_UMTS_MESSAGE_PAGE_SIZE = 82;
|
||||
|
||||
function testReceiving_UMTS_MessageAttributes() {
|
||||
log("Test receiving UMTS Cell Broadcast - Message Attributes");
|
||||
|
||||
let verifyCBMessage = (aMessage) => {
|
||||
// Attributes other than `language` , `body` and `data` should always be assigned.
|
||||
ok(aMessage.gsmGeographicalScope != null, "aMessage.gsmGeographicalScope");
|
||||
ok(aMessage.messageCode != null, "aMessage.messageCode");
|
||||
ok(aMessage.messageId != null, "aMessage.messageId");
|
||||
ok(aMessage.messageClass != null, "aMessage.messageClass");
|
||||
ok(aMessage.timestamp != null, "aMessage.timestamp");
|
||||
ok('etws' in aMessage, "aMessage.etws");
|
||||
if (aMessage.etws) {
|
||||
ok('warningType' in aMessage.etws, "aMessage.etws.warningType");
|
||||
ok(aMessage.etws.emergencyUserAlert != null, "aMessage.etws.emergencyUserAlert");
|
||||
ok(aMessage.etws.popup != null, "aMessage.etws.popup");
|
||||
}
|
||||
ok(aMessage.cdmaServiceCategory != null, "aMessage.cdmaServiceCategory");
|
||||
};
|
||||
|
||||
// Here we use a single UMTS message for test.
|
||||
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
|
||||
+ buildHexStr(0, 10) // skip msg_id, sn, dcs
|
||||
+ buildHexStr(1, 2) // set num_of_pages to 1
|
||||
+ buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
|
||||
+ buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
|
||||
|
||||
return sendMultipleRawCbsToEmulatorAndWait([pdu])
|
||||
.then((aMessage) => verifyCBMessage(aMessage));
|
||||
}
|
||||
|
||||
function testReceiving_UMTS_GeographicalScope() {
|
||||
log("Test receiving UMTS Cell Broadcast - Geographical Scope");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let verifyCBMessage = (aMessage, aGsName) => {
|
||||
is(aMessage.gsmGeographicalScope, aGsName,
|
||||
"aMessage.gsmGeographicalScope");
|
||||
};
|
||||
|
||||
CB_GSM_GEOGRAPHICAL_SCOPE_NAMES.forEach(function(aGsName, aIndex) {
|
||||
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
|
||||
+ buildHexStr(0, 4) // skip msg_id
|
||||
+ buildHexStr(((aIndex & 0x03) << 14), 4) // set SN
|
||||
+ buildHexStr(0, 2) // skip dcs
|
||||
+ buildHexStr(1, 2) // set num_of_pages to 1
|
||||
+ buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
|
||||
+ buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aGsName));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_UMTS_MessageCode() {
|
||||
log("Test receiving UMTS Cell Broadcast - Message Code");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
// Message Code has 10 bits, and is ORed into a 16 bits 'serial' number. Here
|
||||
// we test every single bit to verify the operation doesn't go wrong.
|
||||
let messageCodes = [
|
||||
0x000, 0x001, 0x002, 0x004, 0x008, 0x010, 0x020, 0x040,
|
||||
0x080, 0x100, 0x200, 0x251
|
||||
];
|
||||
|
||||
let verifyCBMessage = (aMessage, aMsgCode) => {
|
||||
is(aMessage.messageCode, aMsgCode, "aMessage.messageCode");
|
||||
};
|
||||
|
||||
messageCodes.forEach(function(aMsgCode) {
|
||||
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
|
||||
+ buildHexStr(0, 4) // skip msg_id
|
||||
+ buildHexStr(((aMsgCode & 0x3FF) << 4), 4) // set SN
|
||||
+ buildHexStr(0, 2) // skip dcs
|
||||
+ buildHexStr(1, 2) // set num_of_pages to 1
|
||||
+ buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
|
||||
+ buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMsgCode));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_UMTS_MessageId() {
|
||||
log("Test receiving UMTS Cell Broadcast - Message Identifier");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
// Message Identifier has 16 bits, but no bitwise operation is needed.
|
||||
// Test some selected values only.
|
||||
let messageIds = [
|
||||
0x0000, 0x0001, 0x0010, 0x0100, 0x1000, 0x1111, 0x8888, 0x8811,
|
||||
];
|
||||
|
||||
let verifyCBMessage = (aMessage, aMessageId) => {
|
||||
is(aMessage.messageId, aMessageId, "aMessage.messageId");
|
||||
ok(aMessage.etws == null, "aMessage.etws");
|
||||
};
|
||||
|
||||
messageIds.forEach(function(aMessageId) {
|
||||
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
|
||||
+ buildHexStr((aMessageId & 0xFFFF), 4) // set msg_id
|
||||
+ buildHexStr(0, 4) // skip SN
|
||||
+ buildHexStr(0, 2) // skip dcs
|
||||
+ buildHexStr(1, 2) // set num_of_pages to 1
|
||||
+ buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
|
||||
+ buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMessageId));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_UMTS_Language_and_Body() {
|
||||
log("Test receiving UMTS Cell Broadcast - Language & Body");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let testDcs = [];
|
||||
dcs = 0;
|
||||
while (dcs <= 0xFF) {
|
||||
try {
|
||||
let dcsInfo = { dcs: dcs };
|
||||
[ dcsInfo.encoding, dcsInfo.language,
|
||||
dcsInfo.indicator, dcsInfo.messageClass ] = decodeGsmDataCodingScheme(dcs);
|
||||
testDcs.push(dcsInfo);
|
||||
} catch (e) {
|
||||
// Unsupported coding group, skip.
|
||||
let dcs = (dcs & PDU_DCS_CODING_GROUP_BITS) + 0x10;
|
||||
}
|
||||
dcs++;
|
||||
}
|
||||
|
||||
let verifyCBMessage = (aMessage, aDcsInfo) => {
|
||||
if (aDcsInfo.language) {
|
||||
is(aMessage.language, aDcsInfo.language, "aMessage.language");
|
||||
} else if (aDcsInfo.indicator) {
|
||||
is(aMessage.language, "@@", "aMessage.language");
|
||||
} else {
|
||||
ok(aMessage.language == null, "aMessage.language");
|
||||
}
|
||||
|
||||
switch (aDcsInfo.encoding) {
|
||||
case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
|
||||
is(aMessage.body,
|
||||
aDcsInfo.indicator ? DUMMY_BODY_7BITS_IND : DUMMY_BODY_7BITS,
|
||||
"aMessage.body");
|
||||
break;
|
||||
case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
|
||||
ok(aMessage.body == null, "aMessage.body");
|
||||
break;
|
||||
case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
|
||||
is(aMessage.body,
|
||||
aDcsInfo.indicator ? DUMMY_BODY_UCS2_IND : DUMMY_BODY_UCS2,
|
||||
"aMessage.body");
|
||||
break;
|
||||
}
|
||||
|
||||
is(aMessage.messageClass, aDcsInfo.messageClass, "aMessage.messageClass");
|
||||
};
|
||||
|
||||
testDcs.forEach(function(aDcsInfo) {
|
||||
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
|
||||
+ buildHexStr(0, 4) // skip msg_id
|
||||
+ buildHexStr(0, 4) // skip SN
|
||||
+ buildHexStr(aDcsInfo.dcs, 2) // set dcs
|
||||
+ buildHexStr(1, 2) // set num_of_pages to 1
|
||||
+ buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
|
||||
+ buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aDcsInfo));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_UMTS_Timestamp() {
|
||||
log("Test receiving UMTS Cell Broadcast - Timestamp");
|
||||
|
||||
let verifyCBMessage = (aMessage) => {
|
||||
// Cell Broadcast messages do not contain a timestamp field (however, ETWS
|
||||
// does). We only check the timestamp doesn't go too far (60 seconds) here.
|
||||
let msMessage = aMessage.timestamp;
|
||||
let msNow = Date.now();
|
||||
ok(Math.abs(msMessage - msNow) < (1000 * 60), "aMessage.timestamp");
|
||||
};
|
||||
|
||||
// Here we use a single UMTS message for test.
|
||||
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
|
||||
+ buildHexStr(0, 10) // skip msg_id, sn, dcs
|
||||
+ buildHexStr(1, 2) // set num_of_pages to 1
|
||||
+ buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
|
||||
+ buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
|
||||
|
||||
return sendMultipleRawCbsToEmulatorAndWait([pdu])
|
||||
.then((aMessage) => verifyCBMessage(aMessage));
|
||||
}
|
||||
|
||||
function testReceiving_UMTS_WarningType() {
|
||||
log("Test receiving UMTS Cell Broadcast - Warning Type");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let messageIds = [];
|
||||
for (let i = CB_GSM_MESSAGEID_ETWS_BEGIN; i <= CB_GSM_MESSAGEID_ETWS_END; i++) {
|
||||
messageIds.push(i);
|
||||
}
|
||||
|
||||
let verifyCBMessage = (aMessage, aMessageId) => {
|
||||
is(aMessage.messageId, aMessageId, "aMessage.messageId");
|
||||
ok(aMessage.etws != null, "aMessage.etws");
|
||||
|
||||
let offset = aMessageId - CB_GSM_MESSAGEID_ETWS_BEGIN;
|
||||
if (offset < CB_ETWS_WARNING_TYPE_NAMES.length) {
|
||||
is(aMessage.etws.warningType, CB_ETWS_WARNING_TYPE_NAMES[offset],
|
||||
"aMessage.etws.warningType");
|
||||
} else {
|
||||
ok(aMessage.etws.warningType == null, "aMessage.etws.warningType");
|
||||
}
|
||||
};
|
||||
|
||||
messageIds.forEach(function(aMessageId) {
|
||||
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
|
||||
+ buildHexStr((aMessageId & 0xFFFF), 4) // set msg_id
|
||||
+ buildHexStr(0, 4) // skip SN
|
||||
+ buildHexStr(0, 2) // skip dcs
|
||||
+ buildHexStr(1, 2) // set num_of_pages to 1
|
||||
+ buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
|
||||
+ buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMessageId));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_UMTS_EmergencyUserAlert() {
|
||||
log("Test receiving UMTS Cell Broadcast - Emergency User Alert");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let emergencyUserAlertMasks = [0x2000, 0x0000];
|
||||
|
||||
let verifyCBMessage = (aMessage, aMask) => {
|
||||
is(aMessage.messageId, CB_GSM_MESSAGEID_ETWS_BEGIN, "aMessage.messageId");
|
||||
ok(aMessage.etws != null, "aMessage.etws");
|
||||
is(aMessage.etws.emergencyUserAlert, aMask != 0, "aMessage.etws.emergencyUserAlert");
|
||||
};
|
||||
|
||||
emergencyUserAlertMasks.forEach(function(aMask) {
|
||||
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
|
||||
+ buildHexStr(CB_GSM_MESSAGEID_ETWS_BEGIN, 4) // set msg_id
|
||||
+ buildHexStr(aMask, 4) // set SN
|
||||
+ buildHexStr(0, 2) // skip dcs
|
||||
+ buildHexStr(1, 2) // set num_of_pages to 1
|
||||
+ buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
|
||||
+ buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMask));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_UMTS_Popup() {
|
||||
log("Test receiving UMTS Cell Broadcast - Popup");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let popupMasks = [0x1000, 0x0000];
|
||||
|
||||
let verifyCBMessage = (aMessage, aMask) => {
|
||||
is(aMessage.messageId, CB_GSM_MESSAGEID_ETWS_BEGIN, "aMessage.messageId");
|
||||
ok(aMessage.etws != null, "aMessage.etws");
|
||||
is(aMessage.etws.popup, aMask != 0, "aMessage.etws.popup");
|
||||
};
|
||||
|
||||
popupMasks.forEach(function(aMask) {
|
||||
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
|
||||
+ buildHexStr(CB_GSM_MESSAGEID_ETWS_BEGIN, 4) // set msg_id
|
||||
+ buildHexStr(aMask, 4) // set SN
|
||||
+ buildHexStr(0, 2) // skip dcs
|
||||
+ buildHexStr(1, 2) // set num_of_pages to 1
|
||||
+ buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
|
||||
+ buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aMask));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_UMTS_Multipart() {
|
||||
log("Test receiving UMTS Cell Broadcast - Multipart Messages");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
// According to 9.4.2.2.5 CB Data in TS 23.041,
|
||||
// Number-of-Pages is equal to or less than 15.
|
||||
// However, the size of out_buff of android modem in emulator is fixed to 1024.
|
||||
// The maximal number of CBS pages we can test is limited to 6.
|
||||
// Another xpc shell test case in ril_worker will address this instead.
|
||||
let numOfPages = [1, 2, 3, 4, 5, 6];
|
||||
|
||||
let verifyCBMessage = (aMessage, aNumOfPages) => {
|
||||
is(aMessage.body.length, (aNumOfPages * CB_MAX_CONTENT_PER_PAGE_7BIT),
|
||||
"aMessage.body");
|
||||
};
|
||||
|
||||
numOfPages.forEach(function(aNumOfPages) {
|
||||
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
|
||||
+ buildHexStr(0, 4) // skip msg_id
|
||||
+ buildHexStr(0, 4) // skip SN
|
||||
+ buildHexStr(0, 2) // skip dcs
|
||||
+ buildHexStr(aNumOfPages, 2); // set num_of_pages
|
||||
for (let i = 1; i <= aNumOfPages; i++) {
|
||||
pdu = pdu + buildHexStr(0, CB_UMTS_MESSAGE_PAGE_SIZE * 2)
|
||||
+ buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2); // msg_info_length
|
||||
}
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aNumOfPages));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_UMTS_PaddingCharacters() {
|
||||
log("Test receiving UMTS Cell Broadcast - Padding Characters <CR>");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
let testContents = [
|
||||
{ pdu:
|
||||
// CB PDU with GSM 7bit encoded text of
|
||||
// "The quick brown fox jumps over the lazy dog
|
||||
// \r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r
|
||||
// \r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r
|
||||
// \r\r\r\r\r\r\r\r"
|
||||
buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) + // msg_type
|
||||
buildHexStr(0, 4) + // skip msg_id
|
||||
buildHexStr(0, 4) + // skip SN
|
||||
buildHexStr(0, 2) + // set dcs
|
||||
buildHexStr(1, 2) + // set num_of_pages to 1
|
||||
"54741914AFA7C76B9058FEBEBB41E637" +
|
||||
"1EA4AEB7E173D0DB5E9683E8E832881D" +
|
||||
"D6E741E4F7B9D168341A8D46A3D16834" +
|
||||
"1A8D46A3D168341A8D46A3D168341A8D" +
|
||||
"46A3D168341A8D46A3D168341A8D46A3" +
|
||||
"D100" +
|
||||
buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2), // msg_info_length
|
||||
text:
|
||||
"The quick brown fox jumps over the lazy dog"
|
||||
},
|
||||
{ pdu:
|
||||
// CB PDU with UCS2 encoded text of
|
||||
// "The quick brown fox jumps over\r\r\r\r\r\r\r\r\r\r\r"
|
||||
buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) + // msg_type
|
||||
buildHexStr(0, 4) + // skip msg_id
|
||||
buildHexStr(0, 4) + // skip SN
|
||||
buildHexStr(72, 2) + // set dcs
|
||||
buildHexStr(1, 2) + // set num_of_pages to 1
|
||||
"00540068006500200071007500690063" +
|
||||
"006b002000620072006f0077006e0020" +
|
||||
"0066006f00780020006a0075006d0070" +
|
||||
"00730020006f007600650072000D000D" +
|
||||
"000D000D000D000D000D000D000D000D" +
|
||||
"000D" +
|
||||
buildHexStr(CB_UMTS_MESSAGE_PAGE_SIZE, 2), // msg_info_length
|
||||
text:
|
||||
"The quick brown fox jumps over"
|
||||
}
|
||||
];
|
||||
|
||||
let verifyCBMessage = (aMessage, aText) => {
|
||||
is(aMessage.body, aText, "aMessage.body");
|
||||
};
|
||||
|
||||
testContents.forEach(function(aTestContent) {
|
||||
promise = promise
|
||||
.then(() => sendMultipleRawCbsToEmulatorAndWait([aTestContent.pdu]))
|
||||
.then((aMessage) => verifyCBMessage(aMessage, aTestContent.text));
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function testReceiving_UMTS_MessageInformationLength() {
|
||||
log("Test receiving UMTS Cell Broadcast - Message Information Length");
|
||||
|
||||
let testText = "The quick brown fox jumps over the lazy dog";
|
||||
|
||||
let verifyCBMessage = (aMessage) => {
|
||||
is(aMessage.body, testText, "aMessage.body");
|
||||
};
|
||||
|
||||
// CB PDU with GSM 7bit encoded text of
|
||||
// "The quick brown fox jumps over the lazy dog
|
||||
// \r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r
|
||||
// \r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r
|
||||
// \r\r\r\r\r\r\r\r"
|
||||
// We set msg_info_length to the number of used octets.
|
||||
let pdu = buildHexStr(CB_UMTS_MESSAGE_TYPE_CBS, 2) // msg_type
|
||||
+ buildHexStr(0, 4) // skip msg_id
|
||||
+ buildHexStr(0, 4) // skip SN
|
||||
+ buildHexStr(0, 2) // set dcs
|
||||
+ buildHexStr(1, 2) // set num_of_pages to 1
|
||||
+ "54741914AFA7C76B9058FEBEBB41E637"
|
||||
+ "1EA4AEB7E173D0DB5E9683E8E832881D"
|
||||
+ "D6E741E4F7B9D168341A8D46A3D16834"
|
||||
+ "1A8D46A3D168341A8D46A3D168341A8D"
|
||||
+ "46A3D168341A8D46A3D168341A8D46A3"
|
||||
+ "D100"
|
||||
+ buildHexStr(Math.ceil(testText.length * 7 / 8), 2); // msg_info_length
|
||||
|
||||
return sendMultipleRawCbsToEmulatorAndWait([pdu])
|
||||
.then((aMessage) => verifyCBMessage(aMessage));
|
||||
}
|
||||
|
||||
startTestCommon(function testCaseMain() {
|
||||
return testReceiving_UMTS_MessageAttributes()
|
||||
.then(() => testReceiving_UMTS_GeographicalScope())
|
||||
.then(() => testReceiving_UMTS_MessageCode())
|
||||
.then(() => testReceiving_UMTS_MessageId())
|
||||
.then(() => testReceiving_UMTS_Language_and_Body())
|
||||
.then(() => testReceiving_UMTS_Timestamp())
|
||||
.then(() => testReceiving_UMTS_WarningType())
|
||||
.then(() => testReceiving_UMTS_EmergencyUserAlert())
|
||||
.then(() => testReceiving_UMTS_Popup())
|
||||
.then(() => testReceiving_UMTS_Multipart())
|
||||
.then(() => testReceiving_UMTS_PaddingCharacters())
|
||||
.then(() => testReceiving_UMTS_MessageInformationLength());
|
||||
});
|
|
@ -2495,7 +2495,7 @@ EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame,
|
|||
nsIntPoint overflow;
|
||||
aScrollableFrame->ScrollBy(actualDevPixelScrollAmount,
|
||||
nsIScrollableFrame::DEVICE_PIXELS,
|
||||
mode, &overflow, origin);
|
||||
mode, &overflow, origin, aEvent->isMomentum);
|
||||
|
||||
if (!scrollFrameWeak.IsAlive()) {
|
||||
// If the scroll causes changing the layout, we can think that the event
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
@ -28,6 +30,32 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Headers)
|
|||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
// static
|
||||
bool
|
||||
Headers::PrefEnabled(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
using mozilla::dom::workers::WorkerPrivate;
|
||||
using mozilla::dom::workers::GetWorkerPrivateFromContext;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
static bool sPrefCacheInit = false;
|
||||
static bool sPrefEnabled = false;
|
||||
if (sPrefCacheInit) {
|
||||
return sPrefEnabled;
|
||||
}
|
||||
Preferences::AddBoolVarCache(&sPrefEnabled, "dom.fetch.enabled");
|
||||
sPrefCacheInit = true;
|
||||
return sPrefEnabled;
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
if (!workerPrivate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return workerPrivate->DOMFetchEnabled();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Headers>
|
||||
Headers::Constructor(const GlobalObject& aGlobal,
|
||||
|
|
|
@ -54,6 +54,8 @@ public:
|
|||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
static bool PrefEnabled(JSContext* cx, JSObject* obj);
|
||||
|
||||
static already_AddRefed<Headers>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit,
|
||||
|
|
|
@ -12,6 +12,10 @@ UNIFIED_SOURCES += [
|
|||
'Headers.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../workers',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
MSVC_ENABLE_PGO = True
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>Properties of the window object</title>
|
||||
<link rel="author" title="Ms2ger" href="ms2ger@gmail.com">
|
||||
<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
|
||||
<link rel="help" href="http://ecma-international.org/ecma-262/5.1/#sec-15.1">
|
||||
<link rel="help" href="http://dev.w3.org/2006/webapi/WebIDL/#interface-prototype-object">
|
||||
<link rel="help" href="http://dev.w3.org/2006/webapi/WebIDL/#es-attributes">
|
||||
<link rel="help" href="http://dev.w3.org/2006/webapi/WebIDL/#es-operations">
|
||||
<link rel="help" href="http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#eventtarget">
|
||||
<link rel="help" href="http://heycam.github.io/webidl/#interface-prototype-object">
|
||||
<link rel="help" href="http://heycam.github.io/webidl/#es-attributes">
|
||||
<link rel="help" href="http://heycam.github.io/webidl/#es-operations">
|
||||
<link rel="help" href="http://dom.spec.whatwg.org/#eventtarget">
|
||||
<link rel="help" href="http://www.whatwg.org/html/#window">
|
||||
<link rel="help" href="http://www.whatwg.org/html/#windowtimers">
|
||||
<link rel="help" href="http://www.whatwg.org/html/#windowbase64">
|
||||
|
@ -64,7 +64,7 @@ var methods = [
|
|||
"confirm",
|
||||
"prompt",
|
||||
"print",
|
||||
"showModalDialog",
|
||||
// See below: "showModalDialog",
|
||||
"postMessage",
|
||||
|
||||
// WindowBase64
|
||||
|
@ -90,6 +90,12 @@ var methods = [
|
|||
"scrollBy"
|
||||
];
|
||||
|
||||
// We would like to remove showModalDialog from the platform,
|
||||
// see <https://www.w3.org/Bugs/Public/show_bug.cgi?id=26437>.
|
||||
if ("showModalDialog" in window) {
|
||||
methods.push("showModalDialog");
|
||||
}
|
||||
|
||||
var readonlyAttributes = [
|
||||
"history",
|
||||
"parent",
|
||||
|
|
|
@ -918,7 +918,7 @@ ContentChild::RecvSetProcessSandbox()
|
|||
// at some point; see bug 880808.
|
||||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
#if defined(XP_LINUX)
|
||||
SetCurrentProcessSandbox();
|
||||
SetContentProcessSandbox();
|
||||
#elif defined(XP_WIN)
|
||||
mozilla::SandboxTarget::Instance()->StartSandbox();
|
||||
#endif
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#include "mozilla/dom/PFileDescriptorSetParent.h"
|
||||
#include "mozilla/dom/PCycleCollectWithLogsParent.h"
|
||||
#include "mozilla/dom/PMemoryReportRequestParent.h"
|
||||
#include "mozilla/dom/power/PowerManagerService.h"
|
||||
#include "mozilla/dom/DOMStorageIPC.h"
|
||||
#include "mozilla/dom/bluetooth/PBluetoothParent.h"
|
||||
#include "mozilla/dom/PFMRadioParent.h"
|
||||
|
@ -84,7 +83,6 @@
|
|||
#include "nsIClipboard.h"
|
||||
#include "nsICycleCollectorListener.h"
|
||||
#include "nsIDOMGeoGeolocation.h"
|
||||
#include "mozilla/dom/WakeLock.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIExternalProtocolService.h"
|
||||
#include "nsIGfxInfo.h"
|
||||
|
@ -185,7 +183,6 @@ using base::KillProcess;
|
|||
using namespace mozilla::dom::bluetooth;
|
||||
using namespace mozilla::dom::devicestorage;
|
||||
using namespace mozilla::dom::indexedDB;
|
||||
using namespace mozilla::dom::power;
|
||||
using namespace mozilla::dom::mobilemessage;
|
||||
using namespace mozilla::dom::telephony;
|
||||
using namespace mozilla::hal;
|
||||
|
@ -839,6 +836,95 @@ ContentParent::AnswerBridgeToChildProcess(const uint64_t& id)
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class SystemMessageHandledListener MOZ_FINAL
|
||||
: public nsITimerCallback
|
||||
, public LinkedListElement<SystemMessageHandledListener>
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
SystemMessageHandledListener() {}
|
||||
|
||||
static void OnSystemMessageHandled()
|
||||
{
|
||||
if (!sListeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
SystemMessageHandledListener* listener = sListeners->popFirst();
|
||||
if (!listener) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Careful: ShutDown() may delete |this|.
|
||||
listener->ShutDown();
|
||||
}
|
||||
|
||||
void Init(ContentParent* aContentParent)
|
||||
{
|
||||
MOZ_ASSERT(!mContentParent);
|
||||
MOZ_ASSERT(!mTimer);
|
||||
|
||||
// mTimer keeps a strong reference to |this|. When this object's
|
||||
// destructor runs, it will remove itself from the LinkedList.
|
||||
|
||||
if (!sListeners) {
|
||||
sListeners = new LinkedList<SystemMessageHandledListener>();
|
||||
ClearOnShutdown(&sListeners);
|
||||
}
|
||||
sListeners->insertBack(this);
|
||||
|
||||
mContentParent = aContentParent;
|
||||
|
||||
mTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
|
||||
uint32_t timeoutSec =
|
||||
Preferences::GetInt("dom.ipc.systemMessageCPULockTimeoutSec", 30);
|
||||
mTimer->InitWithCallback(this, timeoutSec * 1000,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
NS_IMETHOD Notify(nsITimer* aTimer)
|
||||
{
|
||||
// Careful: ShutDown() may delete |this|.
|
||||
ShutDown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~SystemMessageHandledListener() {}
|
||||
|
||||
static StaticAutoPtr<LinkedList<SystemMessageHandledListener> > sListeners;
|
||||
|
||||
void ShutDown()
|
||||
{
|
||||
ProcessPriorityManager::ResetProcessPriority(mContentParent, false);
|
||||
|
||||
nsRefPtr<SystemMessageHandledListener> kungFuDeathGrip = this;
|
||||
|
||||
if (mContentParent) {
|
||||
mContentParent = nullptr;
|
||||
}
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<ContentParent> mContentParent;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
};
|
||||
|
||||
StaticAutoPtr<LinkedList<SystemMessageHandledListener> >
|
||||
SystemMessageHandledListener::sListeners;
|
||||
|
||||
NS_IMPL_ISUPPORTS(SystemMessageHandledListener,
|
||||
nsITimerCallback)
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
/*static*/ TabParent*
|
||||
ContentParent::CreateBrowserOrApp(const TabContext& aContext,
|
||||
Element* aFrameElement,
|
||||
|
@ -1043,7 +1129,20 @@ ContentParent::CreateBrowserOrApp(const TabContext& aContext,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
p->MaybeTakeCPUWakeLock(aFrameElement);
|
||||
// Request a higher priority above BACKGROUND if the child process is
|
||||
// "critical" and probably has system messages coming soon. (A CPU wake lock
|
||||
// may already be controlled by the B2G process in SystemMessageInternal.js
|
||||
// for message handling.) This privilege is revoked once the message is
|
||||
// delivered, or the grace period is up, whichever comes first.
|
||||
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aFrameElement);
|
||||
if (browserFrame && browserFrame->GetIsExpectingSystemMessage()) {
|
||||
ProcessPriorityManager::ResetProcessPriority(p, true);
|
||||
|
||||
// This object's Init() function keeps it alive.
|
||||
nsRefPtr<SystemMessageHandledListener> listener =
|
||||
new SystemMessageHandledListener();
|
||||
listener->Init(p);
|
||||
}
|
||||
|
||||
return static_cast<TabParent*>(browser);
|
||||
}
|
||||
|
@ -1107,117 +1206,6 @@ ContentParent::Init()
|
|||
NS_ASSERTION(observer, "FileUpdateDispatcher is null");
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class SystemMessageHandledListener MOZ_FINAL
|
||||
: public nsITimerCallback
|
||||
, public LinkedListElement<SystemMessageHandledListener>
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
SystemMessageHandledListener() {}
|
||||
|
||||
static void OnSystemMessageHandled()
|
||||
{
|
||||
if (!sListeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
SystemMessageHandledListener* listener = sListeners->popFirst();
|
||||
if (!listener) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Careful: ShutDown() may delete |this|.
|
||||
listener->ShutDown();
|
||||
}
|
||||
|
||||
void Init(WakeLock* aWakeLock)
|
||||
{
|
||||
MOZ_ASSERT(!mWakeLock);
|
||||
MOZ_ASSERT(!mTimer);
|
||||
|
||||
// mTimer keeps a strong reference to |this|. When this object's
|
||||
// destructor runs, it will remove itself from the LinkedList.
|
||||
|
||||
if (!sListeners) {
|
||||
sListeners = new LinkedList<SystemMessageHandledListener>();
|
||||
ClearOnShutdown(&sListeners);
|
||||
}
|
||||
sListeners->insertBack(this);
|
||||
|
||||
mWakeLock = aWakeLock;
|
||||
|
||||
mTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
|
||||
uint32_t timeoutSec =
|
||||
Preferences::GetInt("dom.ipc.systemMessageCPULockTimeoutSec", 30);
|
||||
mTimer->InitWithCallback(this, timeoutSec * 1000,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
NS_IMETHOD Notify(nsITimer* aTimer)
|
||||
{
|
||||
// Careful: ShutDown() may delete |this|.
|
||||
ShutDown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~SystemMessageHandledListener() {}
|
||||
|
||||
static StaticAutoPtr<LinkedList<SystemMessageHandledListener> > sListeners;
|
||||
|
||||
void ShutDown()
|
||||
{
|
||||
nsRefPtr<SystemMessageHandledListener> kungFuDeathGrip = this;
|
||||
|
||||
ErrorResult rv;
|
||||
mWakeLock->Unlock(rv);
|
||||
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<WakeLock> mWakeLock;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
};
|
||||
|
||||
StaticAutoPtr<LinkedList<SystemMessageHandledListener> >
|
||||
SystemMessageHandledListener::sListeners;
|
||||
|
||||
NS_IMPL_ISUPPORTS(SystemMessageHandledListener,
|
||||
nsITimerCallback)
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
ContentParent::MaybeTakeCPUWakeLock(Element* aFrameElement)
|
||||
{
|
||||
// Take the CPU wake lock on behalf of this processs if it's expecting a
|
||||
// system message. We'll release the CPU lock once the message is
|
||||
// delivered, or after some period of time, which ever comes first.
|
||||
|
||||
nsCOMPtr<nsIMozBrowserFrame> browserFrame =
|
||||
do_QueryInterface(aFrameElement);
|
||||
if (!browserFrame ||
|
||||
!browserFrame->GetIsExpectingSystemMessage()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<PowerManagerService> pms = PowerManagerService::GetInstance();
|
||||
nsRefPtr<WakeLock> lock =
|
||||
pms->NewWakeLockOnBehalfOfProcess(NS_LITERAL_STRING("cpu"), this);
|
||||
|
||||
// This object's Init() function keeps it alive.
|
||||
nsRefPtr<SystemMessageHandledListener> listener =
|
||||
new SystemMessageHandledListener();
|
||||
listener->Init(lock);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority)
|
||||
{
|
||||
|
|
|
@ -332,12 +332,6 @@ private:
|
|||
|
||||
void Init();
|
||||
|
||||
// If the frame element indicates that the child process is "critical" and
|
||||
// has a pending system message, this function acquires the CPU wake lock on
|
||||
// behalf of the child. We'll release the lock when the system message is
|
||||
// handled or after a timeout, whichever comes first.
|
||||
void MaybeTakeCPUWakeLock(Element* aFrameElement);
|
||||
|
||||
// Set the child process's priority and then check whether the child is
|
||||
// still alive. Returns true if the process is still alive, and false
|
||||
// otherwise. If you pass a FOREGROUND* priority here, it's (hopefully)
|
||||
|
|
|
@ -131,6 +131,12 @@ public:
|
|||
ProcessPriority aPriority,
|
||||
uint32_t aBackgroundLRU = 0);
|
||||
|
||||
/**
|
||||
* This function implements ProcessPriorityManager::ResetProcessPriority.
|
||||
*/
|
||||
void ResetProcessPriority(ContentParent* aContentParent,
|
||||
bool aHandleSystemMessage);
|
||||
|
||||
/**
|
||||
* If a magic testing-only pref is set, notify the observer service on the
|
||||
* given topic with the given data. This is used for testing
|
||||
|
@ -287,6 +293,8 @@ public:
|
|||
|
||||
void ShutDown();
|
||||
|
||||
void SetHandlesSystemMessage(bool aHandlesSystemMessage);
|
||||
|
||||
private:
|
||||
void FireTestOnlyObserverNotification(
|
||||
const char* aTopic,
|
||||
|
@ -302,6 +310,7 @@ private:
|
|||
ProcessCPUPriority mCPUPriority;
|
||||
bool mHoldsCPUWakeLock;
|
||||
bool mHoldsHighPriorityWakeLock;
|
||||
bool mHandlesSystemMessage;
|
||||
|
||||
/**
|
||||
* Used to implement NameWithComma().
|
||||
|
@ -496,6 +505,17 @@ ProcessPriorityManagerImpl::SetProcessPriority(ContentParent* aContentParent,
|
|||
pppm->SetPriorityNow(aPriority, aBackgroundLRU);
|
||||
}
|
||||
|
||||
void
|
||||
ProcessPriorityManagerImpl::ResetProcessPriority(ContentParent* aContentParent,
|
||||
bool aHandleSystemMessage)
|
||||
{
|
||||
MOZ_ASSERT(aContentParent);
|
||||
nsRefPtr<ParticularProcessPriorityManager> pppm =
|
||||
GetParticularProcessPriorityManager(aContentParent);
|
||||
pppm->SetHandlesSystemMessage(aHandleSystemMessage);
|
||||
pppm->ResetPriorityNow();
|
||||
}
|
||||
|
||||
void
|
||||
ProcessPriorityManagerImpl::ObserveContentParentCreated(
|
||||
nsISupports* aContentParent)
|
||||
|
@ -647,6 +667,7 @@ ParticularProcessPriorityManager::ParticularProcessPriorityManager(
|
|||
, mCPUPriority(PROCESS_CPU_PRIORITY_NORMAL)
|
||||
, mHoldsCPUWakeLock(false)
|
||||
, mHoldsHighPriorityWakeLock(false)
|
||||
, mHandlesSystemMessage(false)
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
LOGP("Creating ParticularProcessPriorityManager.");
|
||||
|
@ -969,7 +990,7 @@ ParticularProcessPriorityManager::CurrentPriority()
|
|||
ProcessPriority
|
||||
ParticularProcessPriorityManager::ComputePriority()
|
||||
{
|
||||
if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
|
||||
if ((mHandlesSystemMessage || mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
|
||||
HasAppType("critical")) {
|
||||
return PROCESS_PRIORITY_FOREGROUND_HIGH;
|
||||
}
|
||||
|
@ -990,7 +1011,7 @@ ParticularProcessPriorityManager::ComputePriority()
|
|||
PROCESS_PRIORITY_FOREGROUND;
|
||||
}
|
||||
|
||||
if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
|
||||
if ((mHandlesSystemMessage || mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
|
||||
IsExpectingSystemMessage()) {
|
||||
return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
|
||||
}
|
||||
|
@ -1137,6 +1158,12 @@ ParticularProcessPriorityManager::ShutDown()
|
|||
mContentParent = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
ParticularProcessPriorityManager::SetHandlesSystemMessage(bool aHandlesSystemMessage)
|
||||
{
|
||||
mHandlesSystemMessage = aHandlesSystemMessage;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessPriorityManagerImpl::FireTestOnlyObserverNotification(
|
||||
const char* aTopic,
|
||||
|
@ -1473,6 +1500,19 @@ ProcessPriorityManager::SetProcessPriority(ContentParent* aContentParent,
|
|||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ProcessPriorityManager::ResetProcessPriority(ContentParent* aContentParent,
|
||||
bool aHandleSystemMessage)
|
||||
{
|
||||
MOZ_ASSERT(aContentParent);
|
||||
|
||||
ProcessPriorityManagerImpl* singleton =
|
||||
ProcessPriorityManagerImpl::GetSingleton();
|
||||
if (singleton) {
|
||||
singleton->ResetProcessPriority(aContentParent, aHandleSystemMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ProcessPriorityManager::RemoveFromBackgroundLRUPool(
|
||||
ContentParent* aContentParent)
|
||||
|
|
|
@ -59,6 +59,20 @@ public:
|
|||
static void SetProcessPriority(dom::ContentParent* aContentParent,
|
||||
hal::ProcessPriority aPriority);
|
||||
|
||||
/**
|
||||
* Reset the process priority of a given ContentParent's process in
|
||||
* consideration of system message handling.
|
||||
*
|
||||
* Note that because this method takes a ContentParent*, you can only set the
|
||||
* priority of your subprocesses. In fact, because we don't support nested
|
||||
* content processes (bug 761935), you can only call this method from the
|
||||
* main process.
|
||||
*
|
||||
* The process priority manager will determine a new appropriate priority.
|
||||
*/
|
||||
static void ResetProcessPriority(dom::ContentParent* aContentParent,
|
||||
bool aHandleSystemMessage);
|
||||
|
||||
/**
|
||||
* Returns true iff this process's priority is FOREGROUND*.
|
||||
*
|
||||
|
|
|
@ -74,8 +74,7 @@ skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabl
|
|||
[test_peerConnection_bug1013809.html]
|
||||
skip-if = (toolkit == 'gonk' && debug) # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
|
||||
[test_peerConnection_bug1042791.html]
|
||||
skip-if = true # disabled until we can resolve plugin installation issues
|
||||
#skip-if = toolkit == 'gonk' || toolkit == 'android' # no openh264 on b2g/android
|
||||
skip-if = buildapp == 'b2g' || os == 'android' # bug 1043403
|
||||
[test_peerConnection_close.html]
|
||||
[test_peerConnection_errorCallbacks.html]
|
||||
[test_peerConnection_offerRequiresReceiveAudio.html]
|
||||
|
|
|
@ -28,10 +28,10 @@
|
|||
test.chain.append([[
|
||||
"PC_LOCAL_VERIFY_H264_OFFER",
|
||||
function (test) {
|
||||
ok(!test.pcLocal._last_offer.sdp.contains("profile-level-id=0x42e00c"),
|
||||
"H264 offer does not contain profile-level-id=0x42e00c");
|
||||
ok(test.pcLocal._last_offer.sdp.contains("profile-level-id=42e00c"),
|
||||
"H264 offer contains profile-level-id=42e00c");
|
||||
ok(!test.pcLocal._last_offer.sdp.toLowerCase().contains("profile-level-id=0x42e0"),
|
||||
"H264 offer does not contain profile-level-id=0x42e0");
|
||||
ok(test.pcLocal._last_offer.sdp.toLowerCase().contains("profile-level-id=42e0"),
|
||||
"H264 offer contains profile-level-id=42e0");
|
||||
test.next();
|
||||
}
|
||||
]]);
|
||||
|
|
|
@ -39,6 +39,16 @@ try {
|
|||
kMaxPendingMessages = 5;
|
||||
}
|
||||
|
||||
//Limit the duration to hold the CPU wake lock.
|
||||
let kCpuWakeLockTimeoutSec;
|
||||
try {
|
||||
kCpuWakeLockTimeoutSec =
|
||||
Services.prefs.getIntPref("dom.ipc.systemMessageCPULockTimeoutSec");
|
||||
} catch (e) {
|
||||
// getIntPref throws when the pref is not set.
|
||||
kCpuWakeLockTimeoutSec = 30;
|
||||
}
|
||||
|
||||
const kMessages =["SystemMessageManager:GetPendingMessages",
|
||||
"SystemMessageManager:HasPendingMessages",
|
||||
"SystemMessageManager:Register",
|
||||
|
@ -149,7 +159,7 @@ SystemMessageInternal.prototype = {
|
|||
debug("Releasing the CPU wake lock because the system messages " +
|
||||
"were not handled by its registered page before time out.");
|
||||
this._cancelCpuWakeLock(aPageKey);
|
||||
}.bind(this), 30000, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}.bind(this), kCpuWakeLockTimeoutSec * 1000, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
},
|
||||
|
||||
_releaseCpuWakeLock: function _releaseCpuWakeLock(aPageKey, aHandledCount) {
|
||||
|
|
|
@ -108,10 +108,8 @@ SystemMessageManager.prototype = {
|
|||
this._dispatchMessage(aType, aDispatcher, aDispatcher.messages.shift());
|
||||
} else {
|
||||
// No more messages that need to be handled, we can notify the
|
||||
// ContentChild to release the CPU wake lock grabbed by the ContentParent
|
||||
// (i.e. NewWakeLockOnBehalfOfProcess()) and reset the process's priority.
|
||||
//
|
||||
// TODO: Bug 874353 - Remove SystemMessageHandledListener in ContentParent
|
||||
// ContentChild to propogate the event, so that the ContentParent can
|
||||
// reset the process's priority.
|
||||
Services.obs.notifyObservers(/* aSubject */ null,
|
||||
"handle-system-messages-done",
|
||||
/* aData */ null);
|
||||
|
@ -249,11 +247,8 @@ SystemMessageManager.prototype = {
|
|||
pageURL: this._pageURL,
|
||||
handledCount: messages.length });
|
||||
|
||||
// We also need to notify the ContentChild to release the CPU wake lock
|
||||
// grabbed by the ContentParent (i.e. NewWakeLockOnBehalfOfProcess()) and
|
||||
// reset the process's priority.
|
||||
//
|
||||
// TODO: Bug 874353 - Remove SystemMessageHandledListener in ContentParent
|
||||
// We also need to notify the ContentChild to propogate the event, so that
|
||||
// the ContentParent can reset the process's priority.
|
||||
Services.obs.notifyObservers(/* aSubject */ null,
|
||||
"handle-system-messages-done",
|
||||
/* aData */ null);
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче