Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2014-07-30 14:22:07 +02:00
Родитель 42210fb773 4e808ef3a7
Коммит e61f4dab12
303 изменённых файлов: 5754 добавлений и 2696 удалений

Просмотреть файл

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

Просмотреть файл

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

117
content/media/eme/CDMCaps.h Normal file
Просмотреть файл

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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше